Implementing a Linux Character Driver in User-space
Asked Answered
L

6

6

I am trying to build a custom joystick/gamepad device for an embedded Linux system. I am looking for a library or system API that will allow me to create a node in /dev/input from userspace code.

I would like this because:

  • The custom hardware can communicate with the system using the existing SPI or I2C drivers (it's my hardware design so I can do whatever makes the most technical sense)

  • Kernel modules can't utilize other drivers, only exported symbols from other modules

I understand the only way to make a kernel module is with kernel code and compiling that as a kernel module. I am not trying to achieve a kernel module with userspace code.

I am looking for an API that allows me to create a file, and when that file is read from or written to, functions get called. That's the general concept of a character driver. I don't need the privileges or restrictions that the kernel provides or imposes.

There must be some way to emulate file I/O that doesn't involve writing a brand new kernel module.

Thanks!

Leverage answered 27/12, 2012 at 7:46 Comment(6)
Do you know about udev ?Civilization
I am aware of udev. I understand that it automatically creates device nodes in response to kernel events. Are you suggesting that this program is capable of creating the devices files I need from what uinput creates? If that is the case can you point me to a guide that shows demonstrates that sort of advanced configuration? Did you read my entire question? I have used the udev daemon to monitor kernel events and checked /proc and such. I'm no wizard at this stuff so I don't know how to manipulate the /sys and /proc entries.Leverage
I believe that udev can be configured to make a /dev/input/js0 (which may be a symlink)Civilization
That's interesting. That seems like an avenue to go down but do you know if I can achieve this from the files created by uinput? I don't know if uinput would generate the events that udev would need to pick up. Without mastering the udev rule system I don't know if I could figure that out. Although that mention of a symlink does give me some ideas. I'll have to check that out with a working device later and see if /dev/input/js0 is a symlink.Leverage
I just believe you should investigate and learn the udev rule system. If possible, avoid doing things in kernel....Civilization
I agree, that's why I'm looking for a userspace solution.Leverage
S
-3

You can't.

A "character device" refers to an in-kernel character-oriented interface.

You could do what LIUB suggests and create a fuse-like interface that marshalls the kernel-like API back into userspace, and you will need to do something like this if you absolutely need HID-style devices produced in /dev/input.

Yet if you don't actually need HID devices and since it is just for your hardware, and you "don't need kernel access" as you can actually communicate with the low-level hardware from userspace, you might consider some other options:

  • You could use XSendEvent or the XTEST protocol to synthesise local events.
  • You could build a network server (or one that uses multicast unix-domain socket to efficiently distribute data) that allows clients to connect.
  • If it's really just read() you want clients to do, you could use a fifo. When your program write()s packets that are evenly divisible into a PIPE_BUF (512 bytes) you can be guaranteed they cannot be accidentally interleaved in another packet.
Schick answered 29/12, 2012 at 9:51 Comment(2)
Thank you for actually considering the problem, although I don't understand the big bold "you can't" if you then give me options. I don't see how a network server would work; a client can't use open() on a server at least not without modification to the code, which I'm avoiding. I really just need to be able to dump 8-bytes at a time to this file. The rest of the software knows what to do with those bytes. Anyway, I'll accept your answer.Leverage
CUSE. YOU CAN. Before you remark, the current (and this was at the time you offered this "insight") the OSS sound subsystem adapter had been moved to using CUSE and is the first example of it's use. Simply put, I'm...a bit shocked...at how WRONG people were on this. I found this little convo, by the by, looking for a library much like the FUSE dev bindings solutions for CUSE, folks- which should be a hint as to just how wrong you lot were.Banquet
O
5

You can use the user-space input subsystem to do what you want, see:

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

and for an example usage:

https://xboxdrv.gitlab.io/

Obey answered 7/1, 2013 at 3:44 Comment(1)
uinput was my top candidate at first, but I asked this question due to limitations I found with using uinput. That limitation is that I couldn't find a way to make a joystick device using uinput, which seems only to be capable of making mouse and keyboard device files. However, with a study of the example you gave, that just might be what I need. Thanks!Leverage
B
5

Old question, but I thought I'd add a tidbit so people looking at this won't get the wrong ideas. For about the last 3-4 years now, there's been this little framework, that was added to extend the FUSE filesystem edge that provides a sandboxed solution to do precisely what the questioner is asking for.

It's called CUSE, which allows character drivers to be instantiated by people that are part of the FUSE group on a system that has had FUSE and CUSE turned on in the kernel. All it takes is an appropriate application (your OSS adaptation daemon on your distribution is just one such application, FWIW...)

Answers of "you can't" and the like back then weren't at-all helpful, didn't actually consider the problem, and were...heh...quite wrong overall...even then.

CUSE hasn't had as much uptake as FUSE so there's less help to do things there in the form of nifty easy-to-use bindings, but it's still there. What brought me to this thread was me looking for a "better" answer if there was one to be had on that subject. Answer there's turning out to be "yes, if you can do Python..." (pycuse)- and if you can't do Python there, you're on your own. Well...I've never been one to accept that sort of thing...so I'm going to learn from pycuse and make a C++/Go/etc. binding as I get to them and need a new one for the language I'm working with at the time I need it.

As for the rest...heh...next time have your ducks more in a row. You certainly didn't on this one.

Banquet answered 22/4, 2016 at 17:52 Comment(2)
Thanks. And just to make it clear - CUSE was merged in 2.6.31, 3 years before this question had been asked in 2012. Btw, have you found any nice resources?Unicellular
Your welcome. Not as such...yet... PyCuse is the only beastie yet. Being that I need something along those lines I may just get to be the first on the other Languages. The support's there. People just don't know that the whole framework exists, even though if you're doing OSS support with an application on a Modern Distro, you're USING IT.Banquet
J
1

try to create your own char device, and then write a user space application that communicates with the driver (I recommend using netlink in this case, as it may be used as backdoor when other drivers don't export symbols, but they do open the functionality to user space, in this case the user space with netlink help can function as a gateway).

in your custom char device, you can let the user tell you what is the path of the device you want to create. i.e. the char device creates initial char device with fixed name, then the user app can use netlink (or ioctl) to tell this char device to create another char device with a custom name.

hope this helps

Jennie answered 28/12, 2012 at 2:30 Comment(6)
You're basically saying I should write a full blown device driver module, right? I'm not worried about my custom kernel driver showing up with the right device file; I assume that would be a small hurdle that goes along with learning the appropriate Kernel programming techniques. I'm looking for a library or maybe an API supplied by Linux that will allow me to only use userspace code, avoiding writing a custom module altogether. A driver and then a user program is more than overkill. The actual device and the interface my driver should present is trivial. Thanks for your answer, though!Leverage
Hi Kevin, There is no user space API in Linux that lets you create any kernel device, the API for creating custom kernel devices is in the kernel only. you may find examples (like macvtap, vconfig, bonding, etc..) utilities that connect to the kernel and create their own (network) device.. but this method doesn't let you create any arbitrary device you want.. and they also follow the same method I suggested earlier (where the user space talks with a kernel module that does the job)Jennie
I'm sorry, I still don't think you're understanding my question. I know I can't create a kernel module from userspace code; I don't want a kernel module at all. I want a userspace only solution that will allow me to create a file that when read from or written to calls a function in my userspace code. Device files do this, and they are implemented with Kernel modules because usually that code needs privileges for accessing hardware that only the Kernel has. I don't need those privileges. I'm not trying to make a program that makes arbitrary types of devices from user input either.Leverage
Hi Kevin, I guess you used incorrect termonolgy in your original post, the functionality you are referring to is in the filesystem level, and you are looking for something that allows you read/write the file descriptor similar to the kernel sysfs. Please check this file system called fuse, see the hello world example: fuse.sourceforge.netJennie
Ok Kevin, I hope you find a solution of what you are looking for. Best of luck.Jennie
LIUB: While this is "old" you'd be wrong and have been wrong when you posted your comment. CUSE allows just precisely what you said was "not possible". Strongly suggest you revise your knowledge before posting to Stack Overflow and elsewhere- you're off in left field.Banquet
M
1

You're exactly describing the viewos virtual machine

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

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

this VM can hijack every syscall directed to your kernel to your user space module (It's thinked to make linux a little less monolithic)

you start a umview instance with $umview xterm command

so every program that run inside the new spawned xterm is traced

now you can simply do (inside a umview instance) a

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

so you can write a module that can intercept every read/write/... to your /dev/virtualJoystick file and do what you want

(the module syntax is very simple)

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,
};

(the umdev_testmodules dir in the umview source is very helpful as a little tutorial! ;) )

Makeshift answered 1/3, 2013 at 12:31 Comment(1)
That is a pretty interesting suggestion. My only problem with that is it sounds like a fair amount of work for the end-user, which would not necessarily be as skilled as a developer. The process could be automated, obviously, but then that would require a dependency on the viewos package, which may not be available for, for example, Raspbian at this time.Leverage
I
1

With some limitation you can make think the applications that you can, with LD_PRELOAD and a library, overriding the common calls to the device.

see here for more details

Irrefutable answered 11/8, 2013 at 4:28 Comment(0)
S
-3

You can't.

A "character device" refers to an in-kernel character-oriented interface.

You could do what LIUB suggests and create a fuse-like interface that marshalls the kernel-like API back into userspace, and you will need to do something like this if you absolutely need HID-style devices produced in /dev/input.

Yet if you don't actually need HID devices and since it is just for your hardware, and you "don't need kernel access" as you can actually communicate with the low-level hardware from userspace, you might consider some other options:

  • You could use XSendEvent or the XTEST protocol to synthesise local events.
  • You could build a network server (or one that uses multicast unix-domain socket to efficiently distribute data) that allows clients to connect.
  • If it's really just read() you want clients to do, you could use a fifo. When your program write()s packets that are evenly divisible into a PIPE_BUF (512 bytes) you can be guaranteed they cannot be accidentally interleaved in another packet.
Schick answered 29/12, 2012 at 9:51 Comment(2)
Thank you for actually considering the problem, although I don't understand the big bold "you can't" if you then give me options. I don't see how a network server would work; a client can't use open() on a server at least not without modification to the code, which I'm avoiding. I really just need to be able to dump 8-bytes at a time to this file. The rest of the software knows what to do with those bytes. Anyway, I'll accept your answer.Leverage
CUSE. YOU CAN. Before you remark, the current (and this was at the time you offered this "insight") the OSS sound subsystem adapter had been moved to using CUSE and is the first example of it's use. Simply put, I'm...a bit shocked...at how WRONG people were on this. I found this little convo, by the by, looking for a library much like the FUSE dev bindings solutions for CUSE, folks- which should be a hint as to just how wrong you lot were.Banquet

© 2022 - 2024 — McMap. All rights reserved.