如何在 Mac 插件中使用 ioctl() 设置 RTS?

Posted

技术标签:

【中文标题】如何在 Mac 插件中使用 ioctl() 设置 RTS?【英文标题】:How can I set the RTS with ioctl() in a Mac plugin? 【发布时间】:2013-02-04 19:08:16 【问题描述】:

我可以在一个小型终端应用程序中使用 ioctl 设置 RTS,但不能在我的 Mac 插件代码中设置,尽管两者都运行相同的代码。在插件中,我只能“获取”串行端口标志/引脚,但不能“设置”它们。在终端应用程序中,我可以“获取”和“设置”它们。我得到了 ENODEV 的 errno。错误号是 19,消息是“设备不支持操作。”

如果这是一个安全问题(在浏览器的上下文中),是否有办法获得使用 ioctl 修改标志的权限?我有一个连接到 USB 端口的串行设备。我正在使用 FTDI vcp(虚拟 com 端口)驱动程序。 Windows 端的一切都很顺利。顺便说一句,我使用 Safari 和 Firefox 得到了相同的结果。以下是我的代码:

int disableRTS ()

    char fd, ret, flags;

    // open device
    if ((fd = open("/dev/cu.mydevice", O_RDWR | O_NDELAY)) < 0)
    
        fprintf(stderr, "failed to open device");
        return -1;
    

    // Get the current state of the bits
    ioctl(fd, TIOCMGET, &flags);
    fprintf(stderr, "Flags are %x.\n", flags);    

    flags &= ~TIOCM_RTS;  // Disable the RTS bit
    ret = ioctl(fd, TIOCMSET, &flags);

    if (ret == -1)
        fprintf(stderr, "TIOCMSET failed\n");
    else
        fprintf(stderr, "TIOCMSET succeeded. flags: %x.\n", flags);

    return 0;

=========

更新

如果我刷新浏览器页面,强制再次执行代码,ioctl()返回0,表示成功。不幸的是,我需要它第一次工作。即使我使用 usleep() 方法编写一个循环并暂时暂停,并进行后续尝试,它也会失败。但是,当我刷新它成功时。我还在 Mozilla 提供的第二个专门的 NPAPI 项目“BasicPlugin.xcodeproj”中复制了这个问题。我的第一个插件项目是 Firebreath 项目。他们都首先失败,然后在页面重新加载时成功。我还有 2 个独立的 Mac 应用程序可以正常工作。一种是 SerialTools,它使用与我的终端应用程序和插件完全相同的方法来设置 RTS 开启(和 DTR 关闭)。

========

更新 2

我已经获得了 Apple 对此的代码级支持,因此可能即将推出解决方案。工程师说,代码在插件中的运行方式与在插件之外的方式不同,这“很奇怪”,并且正在与 Safari 插件工程师交谈。

【问题讨论】:

fprintf 错误案例更改为对 perror("TIOCMSET") 的调用,然后查看错误内容。 对不起,我忘了提。错误消息是“设备不支持操作”。它的错误号是 19。ENODEV。 至于悬赏即将到期还没有正确答案,我不知道该怎么办。我想我会失去它,即使苹果工程师已经接受了这个问题并试图找到解决方案。说插件代码与非插件代码的工作方式不同是“奇怪的”。至少我可以为任何可能有帮助的答案添加正确的答案。 您是否收到过 Apple 的回复? Safari 是否有可能与独立应用程序具有不同的权利?似乎这可能会干扰...尝试从命令行以 root 身份运行 Safari? 【参考方案1】:

您可以制作一个 AppleScript 插件,该插件将在特定时间或登录期间自动运行,或在后台持续运行。

AppleScript 可以运行终端代码。因此,您可以轻松地让它自动运行您当前在终端中运行的获取/设置代码。

AppleScript 插件的代码将是这样的 ::

tell application "Finder"
    display dialog explanation buttons "GET", "SET" default button "GET"
    if result = button returned:"GET" then
        tell application "Terminal"
                 set status to (do shell script "terminal code for getting the RTS here")
            end tell
    else if result = button returned:"SET" then
            tell application "Terminal"
                 set status to (do shell script "terminal code for setting the RTS here")
            end tell
    end if
end tell 

【讨论】:

【参考方案2】:

答案是,每当在调用 ioctl() 时使用 TIOCMSET 或 TIOCMGET 时,第三个参数必须是 int。我正在使用一个字符。呃。不敢相信我错过了这个。 TIOCMSET 和 TIOCMGET 定义如下:

  #define   TIOCMSET    _IOW('t', 109, int) /* set all modem bits */
  …
  #define   TIOCMGET    _IOR('t', 106, int) /* get all modem bits */

因此不难想象,使用 char 作为我的“标志”变量的类型可能会导致不可预知的行为。

【讨论】:

以上是关于如何在 Mac 插件中使用 ioctl() 设置 RTS?的主要内容,如果未能解决你的问题,请参考以下文章

Python - 使用 C 类型和本机 ioctl() 获取 Mac 地址会产生未知结果

Mac 沙盒和文件 ioctl

如何从 Mac OS X 中完全删除 Eclipse(包括设置和插件)?

Mac使用技巧--如何给safari安装翻译插件

如何使用带有第二个参数设置 0xFACE 的 ioctl 来调用特定函数?

在 C 中使用 ioctl() 设置不可变标志