【问题标题】:Implementing a Linux Character Driver in User-space在用户空间实现 Linux 字符驱动程序
【发布时间】:2012-12-12 15:20:50
【问题描述】:

我正在尝试为嵌入式 Linux 系统构建自定义游戏杆/游戏手柄设备。我正在寻找允许我从用户空间代码在 /dev/input 中创建节点的库或系统 API。

我想要这个,因为:

  • 自定义硬件可以使用现有的 SPI 或 I2C 驱动程序与系统通信(这是我的硬件设计,所以我可以做任何最符合技术意义的事情)

  • 内核模块不能使用其他驱动,只能从其他模块导出符号

我知道制作内核模块的唯一方法是使用内核代码并将其编译为内核模块。我不是想用用户空间代码来实现内核模块。

我正在寻找允许我创建文件的 API,当读取或写入该文件时,函数会被调用。这是字符驱动程序的一般概念。我不需要内核提供或强加的特权或限制。

必须有某种方法来模拟文件 I/O,而无需编写全新的内核模块。

谢谢!

【问题讨论】:

  • 你知道udev吗?
  • 我知道 udev。我知道它会自动创建设备节点以响应内核事件。您是否建议该程序能够从 uinput 创建的设备文件中创建我需要的设备文件?如果是这种情况,您能否向我指出一个显示这种高级配置的指南?你读过我的整个问题吗?我已经使用 udev 守护进程来监视内核事件并检查 /proc 等。我不是这方面的专家,所以我不知道如何操作 /sys 和 /proc 条目。
  • 我相信udev可以配置成/dev/input/js0(可能是符号链接)
  • 这很有趣。这似乎是一条可行的途径,但你知道我是否可以从 uinput 创建的文件中实现这一点吗?我不知道 uinput 是否会生成 udev 需要获取的事件。如果没有掌握 udev 规则系统,我不知道我是否能弄清楚。尽管提到符号链接确实给了我一些想法。稍后我将不得不使用工作设备检查一下,看看 /dev/input/js0 是否是符号链接。
  • 我只是相信你应该调查和学习udev 规则系统。如果可能,避免在内核中做事......

标签: linux input kernel drivers


【解决方案1】:

老问题,但我想我会添加一个花絮,这样看这个的人就不会得到错误的想法。在过去的 3 到 4 年里,一直存在这个小框架,它被添加以扩展 FUSE 文件系统边缘,提供沙盒解决方案来准确地完成提问者的要求。

它被称为 CUSE,它允许字符驱动程序由属于 FUSE 组的人员在内核中启用了 FUSE 和 CUSE 的系统上进行实例化。只需要一个适当的应用程序(您的发行版上的 OSS 适配守护进程就是这样一个应用程序,FWIW...)

当时的“你不能”之类的答案根本没有帮助,没有真正考虑到问题,而且……呵呵……相当错误总的来说……即便如此。

CUSE 没有 FUSE 那么多人接受,因此以漂亮且易于使用的绑定的形式在那里做事的帮助较少,但它仍然存在。把我带到这个话题的是我在寻找一个“更好”的答案,如果有关于这个主题的答案的话。答案是“是的,如果你能做 Python……”(pycuse)——如果你不能在那里做 Python,你只能靠自己了。嗯...我从来没有接受过这种事情...所以我要向 pycuse 学习并制作 C++/Go/etc。当我找到它们时绑定,并且在我需要的时候需要一个新的用于我正在使用的语言。

至于其余的……呵呵……下次你的鸭子多排一排。你肯定没有上这个。

【讨论】:

  • 谢谢。为了清楚起见 - CUSE 在 2.6.31 中合并,比 2012 年提出这个问题的时间早了 3 年。顺便说一句,你找到什么好的资源了吗?
  • 欢迎您。不是这样......但是...... PyCuse 是唯一的野兽。因为我需要一些类似的东西,所以我可能会成为其他语言的第一人。支持就在那里。人们只是不知道整个框架的存在,即使您正在为 Modern Distro 上的应用程序提供 OSS 支持,您也正在使用它。
【解决方案2】:

你可以使用用户空间输入子系统做你想做的事,见:

http://thiemonge.org/getting-started-with-uinput

举个例子:

https://xboxdrv.gitlab.io/

【讨论】:

  • uinput 最初是我的首选,但由于我发现使用 uinput 存在局限性,我问了这个问题。这个限制是我找不到使用 uinput 制作操纵杆设备的方法,它似乎只能制作鼠标和键盘设备文件。但是,通过研究您提供的示例,这可能正是我所需要的。谢谢!
【解决方案3】:

尝试创建自己的 char 设备,然后编写一个与驱动程序通信的用户空间应用程序(我建议在这种情况下使用 netlink,因为它可能在其他驱动程序不导出符号时用作后门,但它们向用户空间开放功能,在这种情况下,具有 netlink 帮助的用户空间可以用作网关)。

在您的自定义字符设备中,您可以让用户告诉您要创建的设备的路径是什么。即 char 设备创建具有固定名称的初始 char 设备,然后用户应用程序可以使用 netlink(或 ioctl)告诉该 char 设备创建另一个具有自定义名称的 char 设备。

希望对你有帮助

【讨论】:

  • 你基本上是在说我应该写一个完整的设备驱动模块,对吧?我不担心我的自定义内核驱动程序会显示正确的设备文件;我认为这将是学习适当的内核编程技术的一个小障碍。我正在寻找一个库或 Linux 提供的 API,它允许我只使用用户空间代码,完全避免编写自定义模块。一个驱动程序,然后一个用户程序是多余的。我的驱动程序应该呈现的实际设备和接口是微不足道的。不过,感谢您的回答!
  • 嗨 Kevin,Linux 中没有用户空间 API 可让您创建任何内核设备,用于创建 自定义 内核设备的 API 仅在内核中。您可能会找到连接到内核并创建自己的(网络)设备的示例(如 macvtap、vconfig、bonding 等)实用程序。但这种方法不允许您随意创建 any您想要的设备.. 他们也遵循我之前建议的相同方法(用户空间与完成这项工作的内核模块进行对话)
  • 对不起,我仍然认为您没有理解我的问题。我知道我无法从用户空间代码创建内核模块;我根本不想要内核模块。我想要一个仅限用户空间的解决方案,它允许我创建一个文件,在读取或写入时调用我的用户空间代码中的函数。设备文件执行此操作,并且它们是使用内核模块实现的,因为通常该代码需要访问只有内核才有的硬件的权限。我不需要那些特权。我也没有尝试制作一个程序,可以根据用户输入制作任意类型的设备。
  • 嗨,Kevin,我猜您在原始帖子中使用了不正确的术语,您所指的功能是在文件系统级别,并且您正在寻找允许您读/写类似文件描述符的东西到内核​​ sysfs。请检查这个名为 fuse 的文件系统,参见 hello world 示例:fuse.sourceforge.net
  • 好的,凯文,我希望你能找到你正在寻找的解决方案。祝你好运。
【解决方案4】:

您所描述的正是viewos 虚拟机

http://wiki.virtualsquare.org/wiki/index.php/Main_Page

http://wiki.virtualsquare.org/wiki/index.php/UMview#Modules

这个虚拟机可以劫持每个指向你内核的系统调用到你的用户空间模块(它被认为可以让 linux 不那么单一)

你启动一个 umview 实例 $umview xterm 命令

所以在新生成的 xterm 中运行的每个程序都会被跟踪

现在您可以简单地(在 umview 实例中)a

$um_add_service umdev
$mount -t umdevJoystick none <your file, for example /dev/virtualJoystick>

这样您就可以编写一个模块,该模块可以拦截您的/dev/virtualJoystick 文件中的每个读/写/...并做您想做的事情

(模块语法很简单)

static int joystick_read(char type, dev_t device, struct dev_info *di){
    /*my operation...probably a XTestEvent() or something like that*/
}
static int joystick_write(char type, dev_t device, struct dev_info *di){
    /*my operation...probably a XTestEvent() or something like that*/
}
/*...*/
struct umdev_operations umdev_ops={
    /*hijacking table*/
    .read=joystick_read,
    .write=joystick_write,
};

(umview 源代码中的umdev_testmodules 目录作为一个小教程非常有用!;))

【讨论】:

  • 这是一个非常有趣的建议。我唯一的问题是,对于最终用户来说,这听起来像是相当多的工作,而最终用户不一定像开发人员那样熟练。显然,该过程可以自动化,但这需要对 viewos 包的依赖,例如,此时 Raspbian 可能无法使用该包。
【解决方案5】:

有一些限制,您可以考虑使用 LD_PRELOAD 和库覆盖对设备的常见调用的应用程序。

see here for more details

【讨论】:

    【解决方案6】:

    你不能。

    “字符设备”是指内核中面向字符的接口。

    您可以按照 LIUB 的建议创建一个类似熔断器的接口,将类似内核的 API 编组回用户空间,如果您绝对需要 HID 样式,您将需要执行类似操作在/dev/input生产的设备。

    然而,如果您实际上并不需要 HID 设备,并且因为它仅适用于您的硬件,并且您实际上可以“不需要内核访问”从用户空间与低级硬件通信,您可以考虑一些其他选项:

    • 您可以使用XSendEventXTEST 协议来合成本地事件。
    • 您可以构建一个允许客户端连接的网络服务器(或使用multicast unix-domain socket 高效分发数据的服务器)。
    • 如果您希望客户执行的只是read(),您可以使用fifo。当您的程序write()s 数据包被均匀地分割成PIPE_BUF(512 字节)时,您可以是guaranteed,它们不会意外地交错到另一个数据包中。

    【讨论】:

    • 感谢您实际考虑这个问题,尽管如果您给我选项,我不明白大粗体“你不能”。我看不出网络服务器是如何工作的;客户端至少不能在不修改代码的情况下在服务器上使用 open() ,这是我正在避免的。我真的只需要能够一次转储 8 个字节到这个文件。软件的其余部分知道如何处理这些字节。无论如何,我会接受你的回答。
    • CUSE。你可以。在您评论之前,当前(这是在您提供此“见解”时)OSS 声音子系统适配器已转移到使用 CUSE 并且是它使用的第一个示例。简而言之,我......有点震惊......人们对此的错误。顺便说一句,我发现了这个小团队,正在寻找一个类似于 CUSE 的 FUSE 开发绑定解决方案的库,伙计们,这应该是一个提示,你的错误有多大。
    猜你喜欢
    • 1970-01-01
    • 2021-12-19
    • 2013-10-02
    • 1970-01-01
    • 2016-07-18
    • 2011-01-17
    • 1970-01-01
    • 2013-02-23
    • 2018-02-27
    相关资源
    最近更新 更多