在用户空间实现Linux字符驱动程序

6
我正在尝试为嵌入式Linux系统构建自定义的操纵杆/游戏手柄设备。我正在寻找一个库或系统API,可以让我从用户空间代码中创建/dev/input下的节点。
我需要这个功能是因为:
- 自定义硬件可以使用现有的SPI或I2C驱动程序与系统通信(这是我的硬件设计,所以我可以做任何最合理的技术决策)。 - 内核模块不能利用其他驱动程序,只能利用其他模块导出的符号。
我了解到制作内核模块的唯一方法是使用内核代码并将其编译为内核模块。我不想通过用户空间代码实现内核模块。
我正在寻找一个API,可以让我创建一个文件,当该文件被读取或写入时,函数会被调用。这就是字符驱动程序的一般概念。我不需要内核提供或强制执行的权限或限制。
一定有一种方法可以模拟文件I/O,而不涉及编写全新的内核模块。
谢谢!

你知道 udev 吗? - Basile Starynkevitch
我知道udev。我了解它会自动创建设备节点以响应内核事件。你是在暗示这个程序能够从uinput创建的内容中创建我需要的设备文件吗?如果是这样,你能指引我一个展示这种高级配置的指南吗?你有完整地阅读我的问题吗?我已经使用udev守护进程来监视内核事件并检查/proc等。我不是这方面的专家,所以我不知道如何操作/sys和/proc条目。 - Kevin Ward
我相信udev可以配置成创建一个/dev/input/js0(可能是一个符号链接)。 - Basile Starynkevitch
很有趣。这似乎是一个可行的方向,但您知道我是否可以从uinput创建的文件中实现这一点吗?我不知道uinput是否会生成udev需要捕获的事件。如果没有掌握udev规则系统,我不知道能否弄清楚这一点。虽然提到符号链接给了我一些想法。我将稍后使用工作设备检查它,并查看/dev/input/js0是否为符号链接。 - Kevin Ward
我认为你应该调查和学习udev规则系统。如果可能的话,避免在内核中进行操作... - Basile Starynkevitch
我同意,这就是为什么我正在寻找用户空间解决方案的原因。 - Kevin Ward
6个回答

5
旧问题,但我想补充一点信息,以便查看本文的人不会有错误的想法。大约三到四年前,添加了一个小框架来扩展FUSE文件系统边缘,提供一个沙箱解决方案,精确地做到了提问者所要求的。这被称为CUSE,它允许字符驱动程序由那些在内核中启用了FUSE和CUSE的FUSE组成员实例化。只需要一个适当的应用程序(例如您分发中的OSS适配守护程序)。
当时“你不能”等回答并没有真正考虑问题,也不是很有帮助...甚至是错误的。
CUSE的接受程度没有FUSE那么高,因此在形式上很难使用的绑定中执行操作的帮助要少得多。但它仍然存在。我来到这个线程的原因是我正在寻找更好的答案,如果有可能的话,在那方面得到“是的,如果你能做Python...”(pycuse)的答案-如果你无法在那里做Python,那就靠自己。好吧...我从pycuse学习,并在需要使用的语言的时间制作一个C++/Go等绑定。
至于其他的......下次请准备得更充分。 在这件事上你显然没有做好充分准备。

1
谢谢。为了明确一点 - CUSE已在2.6.31中合并,比2012年提问早了3年。顺便问一句,你找到了什么好的资源吗? - domen
1
你好。目前还没有这样的东西……PyCuse是唯一的存在。既然我需要类似的东西,我可能会成为其他语言中第一个使用它的人。支持是有的。人们只是不知道整个框架的存在,即使你在现代发行版上使用应用程序进行OSS支持,你也在使用它。 - Svartalf

5

uinput一开始是我首选,但由于使用uinput时发现了一些限制,所以我提出了这个问题。这个限制是我找不到一种使用uinput来创建游戏手柄设备的方法,因为它似乎只能创建鼠标和键盘设备文件。然而,通过研究你给出的示例,那可能正是我需要的。谢谢! - Kevin Ward

1

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

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

希望这可以帮助到您。


你的意思是我应该编写一个完整的设备驱动模块,对吧?我并不担心我的自定义内核驱动程序能否出现在正确的设备文件中;我认为这只是学习适当的内核编程技术所需要克服的小障碍。我正在寻找一个由Linux提供的库或API,它将允许我仅使用用户空间代码,完全避免编写自定义模块。一个驱动程序再加上一个用户程序已经远远超过需求了。实际设备和我驱动程序应该呈现的接口是微不足道的。感谢您的回答! - Kevin Ward
嗨Kevin, 在Linux中没有用户空间API可以让你创建任何内核设备,创建自定义内核设备的API仅存在于内核中。你可以找到一些示例(如macvtap、vconfig、bonding等)的实用工具,它们连接到内核并创建自己的(网络)设备。但是这种方法不能让你创建任意想要的设备,并且它们也遵循我之前建议的同样方法(用户空间与执行任务的内核模块进行通信)。 - LIUB
抱歉,我仍然觉得你没有理解我的问题。我知道不能从用户空间代码创建内核模块;我根本不想要内核模块。我只需要一个纯用户空间的解决方案,可以让我创建一个文件,在读取或写入该文件时调用我的用户空间代码中的函数。设备文件可以实现这一点,通常使用内核模块来实现,因为该代码需要访问硬件所需的特权只有内核才有。而我并不需要那些特权。我也不打算根据用户输入创建任意类型的设备程序。 - Kevin Ward
1
嗨,Kevin,我猜你在原帖中使用了不正确的术语,你所指的功能位于文件系统级别,并且你正在寻找一些类似于内核sysfs的允许你读/写文件描述符的东西。请检查这个名为fuse的文件系统,参见hello world示例:http://fuse.sourceforge.net/ - LIUB
好的 Kevin,希望你能找到你正在寻找的解决方案,祝你好运。 - LIUB
LIUB:虽然这已经是“老”话题了,但当你发表评论时,你错了。CUSE正好允许你所说的“不可能”的事情。强烈建议在发布到Stack Overflow和其他地方之前修正你的知识——你的观点是错误的。 - Svartalf

1

1

你描述的正是 viewos 虚拟机

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

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

这个虚拟机可以劫持所有针对内核的系统调用并将其重定向到用户空间模块(旨在使Linux内核变得不那么单olithic)。
您可以使用$umview xterm命令启动一个umview实例。
因此,新生成的xterm中运行的每个程序都会被跟踪。
现在,您可以在umview实例中简单地执行以下操作:
$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)不可用。 - Kevin Ward

-3

无法。

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

您可以像LIUB建议的那样创建类似于Fuse的接口,将内核API转换回用户空间,如果您绝对需要在/ dev / input 中生成HID样式设备,则需要执行此操作。

但是,如果您实际上不需要HID设备,并且由于它仅适用于您的硬件,并且您“不需要内核访问权限”,因为您实际上可以从用户空间与低级硬件进行通信,则可能要考虑其他选项:

  • 您可以使用XSendEventXTEST协议来合成本地事件。
  • 您可以构建一个网络服务器(或使用多播Unix域套接字以有效地分发数据),允许客户端连接。
  • 如果您只是想让客户端执行read(),您可以使用fifo。当您的程序write()均匀地将数据包写入到PIPE_BUF(512字节)中时,您可以确保它们不会意外地与另一个数据包交错。

谢谢您真正考虑这个问题,尽管我不理解大胆的“你不能”,如果您随后给我选项。我不知道网络服务器如何工作;客户端无法使用open()在服务器上,至少不需要修改代码,这是我要避免的。我只需要能够每次转储8字节到这个文件中。软件的其余部分知道如何处理这些字节。无论如何,我会接受您的答案。 - Kevin Ward
2
CUSE。你可以。在你发表评论之前,当前(在你提供这个“见解”的时候)OSS声音子系统适配器已经被移动到使用CUSE,并且这是它的第一个实例。简而言之,我有点震惊......人们对此是多么的错误。顺便说一下,我找到了这个小对话,是在寻找类似于CUSE的FUSE开发绑定解决方案的库,这应该是一个提示,让你们知道你们错了多少。 - Svartalf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接