How to access uart from linux kernel? - c

I'm trying to write simple kernel module that uses a device connected to the system via serial line, but I'm having trouble actually accessing the UART. I think I know how to communicate with the device with kernel_read and kernel_write:
struct file* serial_file;
char buf[32];
lsoff_t off = 0;
serial_file = filp_open("/dev/ttyS0", O_RDWR, 0);
kernel_write(serial_file, "test\n", 5, &off);
kernel_read(serial_file, buf, 32, &off);
The file seems to be open (IS_ERR(serial_file) is 0), but reads and writes return EIO - probably because of the wrong baudrate. Since there is no kernel_ioctl, there's no obvious way to fix this.
Answer to the problem above would be nice, but since this is more of a hack (from my understanding filesystem shouldn't really be accessed from kernel in this fashion), even nicer would be explanation how to do this properly? I'd guess it would involve calling functions from tty_struct directly, but how to get it from file (or from path, or from whatever it should be generated from)?

Related

Use read() C function without stopping code

I'm trying to read data from a joystick using C code. I have found online the way to do it.
This solution seems to work fine but with a problem. When the code reachers the the read() function, it stops and waits until there is change in the joystick device:
int read_event(int fd, struct js_event *event)
{
ssize_t bytes;
bytes = read(fd, event, sizeof(*event));
if (bytes == sizeof(*event))
return 0;
return -1;
}
I'm trying to find a way to make the code run continously and if there is no change in the control device, just return the previous state. So far I did not succeed. Maybe someone can help.
Given that you are opening the joystick device via open() and reading from it via read(), I infer that you are coding for a machine that implements a POSIX interface. For most files, the POSIX read() function blocks until it can transfer at least one byte, and that is indeed the behavior you observe.
One way to avoid that is to open the file in non-blocking mode, or to put it in non-blocking mode after opening it but before reading it. You can do the former by adding the O_NONBLOCK flag to your open options:
js = open(device, O_RDONLY | O_NONBLOCK);
That should interact cleanly and favorably with your existing code for reading joystick events. In the case that no event is available to read, your read() call should return immediately, with return value -1, and errno set to EAGAIN. Otherwise, it will return the number of bytes transferred.
Note that in general, read() is not guaranteed to transfer the full number of bytes you request -- it may successfully read fewer, which you can determine from its return value, and if you don't account for that then you can come to grief. I don't think you'll see any short reads from the joystick device, however; you should get either a complete event or nothing.
I'm not sure about the actual API (system call) function, but my idea should work.
Use something like GetNumberOfInputEvents (a WinAPI function) to determine whether your read will get stuck (with nothing to read). Then if you judge that it'll get stuck, skip it.
See Checking the stdin buffer if it's empty for more information.
Pseudo code:
read_event(Event* event){
if (is_event_buffer_empty())
// Do nothing because `read` is stuck
else
SystemAPI_ReadEvent(event);
}

Get a FILE* from a bluetooth COM port on Windows

A C library I use requires a FILE* opened to a COM port to work with it, but I have to set up the port (connection speed, etc) before passing it to a library. On Windows, this is done using SetCommState(HANDLE, DCB*).
Transformation between HANDLE and FILE* is a solved problem (How make FILE* from HANDLE in WinApi? and How do I get the file HANDLE from the fopen FILE structure?), but, surprisingly, it fails in different ways when applied to COM ports obtained via Bluetooth (RFCOMM/SPP).
If I first open a HANDLE and set COM port parameters, _open_osfhandle always fails for me:
HANDLE qc9200_handle = CreateFile(T("\\\\.\\COM9"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
/* this returns a valid value like 0x30 */
SetCommState(qc9200_handle, &qc9200_dcb); /* also succeeds */
int qc9200_fd = _open_osfhandle((intptr_t)qc9200_handle, _O_RDWR|_O_BINARY);
/* returns -1 and sets errno=EINVAL (22) */
I tried setting various combinations of flags from _O_APPEND (the only one which is both mentioned in the docs and makes sense with my application) to _O_RDWR|_O_BINARY (which I initially thought should have worked), but the errno is always EINVAL.
If I try to start with a FILE* or int file_descriptor to produce a HANDLE from it later, both _topen(path, _O_RDWR|_O_BINARY) and _tfopen(path, "r+b") fail right away and set errno=EACCES (13) no matter what access mode I request, even when I launch my application with Administrator rights. They don't fail immediately, though, it takes about a second, and I can see the light flashing on my USB dongle before the failure. Trying to open(COMPORT, "+<", "\\\\.\\COM9") the port from Perl also fails with "access denied".
Fishing for events with ProcessMonitor, surprisingly, only gives a bunch of RegQueryValue to HKLM\System\CurrentControlSet\Enum\BTHENUM_LOCALMFG\Device parameters\{Port Name, Authenticated, Encrypted} and one IRP_MJ_READ to %WINDIR%\system32\ucrtbased.dll while fopen is on the stack.
The COM port in question is obtained using ordinary Windows 7 Bluetooth settings, and every terminal program capable of working with COM ports does access it, too, even if not run as administrator. When I do same things to a virtual COM port (com0com), everything works as expected; the errors persist only when I'm using a Bluetooth COM port.
UPD: As GetFileType(HANDLE) reveals, the handle to a Bluetooth COM port is not a file-HANDLE, nor it is anything GetFileType knows about. That's why C runtime library refuses to return a file descriptor to the handle and that's probably why fopen refuses to open the port. I'll have to implement an fprintf-like function which accepts HANDLEs instead and #ifdef and use it on Windows.
Thanks to a Russian language forum, I was able to solve this.
After opening a HANDLE to the virtual COM port, I called GetFileType() on it,
printf("handle=%d\nGetFileType=%lu\n", handle, GetFileType(handle));
Which resulted in
handle=36
GetFileType=0
with GetLastError returning 0.
GetFileType(HANDLE) == 0x0000 means FILE_TYPE_UNKNOWN.
It seems that CRT doesn't know how to properly produce FILE* from unknown-type HANDLES, so my only way to run a vfprintf-like function to write to the serial port would be to implement such a function for HANDLEs myself.
Given that it's a bad idea to use FILE* with serial ports because the standard library would issue seek() calls between reads and writes, and serial ports are unseekable, I had to drop FILE* completely and use plain int file descriptors on POSIX and HANDLEs on Windows.

rdma connection manager driver pattern

I'm using the OFED 3.18r2 implementation of Infiniband drivers for my application. In particular I'm using the rdma connection manager wrapper functions. To understand better what's going on under the hood I'm used to look at the source code. Doing this I came into something that looks like a pattern but I cannot understand it. Let's make an example. The rdma connection manager functions are in cma.c. Looking for example at the rdma_listen call (this is common to almost every functions defined in the library that start with "rdma_"):
int rdma_listen(struct rdma_cm_id *id, int backlog)
{
struct ucma_abi_listen cmd;
struct cma_id_private *id_priv;
int ret;
CMA_INIT_CMD(&cmd, sizeof cmd, LISTEN);
id_priv = container_of(id, struct cma_id_private, id);
cmd.id = id_priv->handle;
cmd.backlog = backlog;
ret = write(id->channel->fd, &cmd, sizeof cmd);
if (ret != sizeof cmd)
return (ret >= 0) ? ERR(ENODATA) : -1;
if (af_ib_support)
return ucma_query_addr(id);
else
return ucma_query_route(id);
}
Here you can see the pattern I mentioned before:
ret = write(id->channel->fd, &cmd, sizeof cmd);
the first argument to the write call is the file descriptor associated with the /dev/infiniband/rdma_cm , but what I cannot understand the usage of the cmd arguments. I dig into the source only to find that cmd is a struct that comes for the ABI definition of the rdma cm function calls. I really don't understand if this is a common pattern to execute device driver calls and how it works, where is the real code associated with the cmd argument.
Could you please help me?
Using a write() system call to execute commands is a common method for executing commands in the RDMA subsystem. It is used among others by the rdma_ucm module and by the ib_uverbs module. The kernel code associate with rdma_ucm can be found in the drivers/infiniband/core/ucma.c file. Specifically, the write() system call for this device is implemented in the ucma_write() function.
I don't think there is a lot of documentation on this method of calling into the driver. The user_verbs.txt document in the kernel documentation states:
Commands are sent to the kernel via write()s on these device files.
The ABI is defined in drivers/infiniband/include/ib_user_verbs.h.
The structs for commands that require a response from the kernel
contain a 64-bit field used to pass a pointer to an output buffer.
Status is returned to userspace as the return value of the write()
system call.
I think it may be a small abuse of the write() system call that implements something more similar to a ioctl().
Edit: Note that I added a link to the upstream kernel module, but the OFED source structure is similar.
Edit: Added some documentation pointers.

How to attach to device context in another driver at Linux

Example: I use sensor TMP421 which driver is linux/drivers/hwmon/tmp421.c. It will export to /sys/class/hwmon/hwon0/.... And user can use cat command to read the temperatures. But my is request: I want to read it at kernel space to control some thing when the temperature reach to MAX values (example).
So how I can attach to get the device context to use the function
tmp421_data *tmp421_update_device(struct device *dev)
to read the temperature ?
Or is there another way ?
I don't want to use the cat command at my code.
Thanks
Just do bus_find_device_by_name on an i2c_bus_type. It should give you the correct device pointer relatively easily. You will also have to change the tmp421_update_device function from "static" to "exported" (and move tmp421_data structure to an external header).
If you don't want to alter drivers not of your own, you can still try to emulate the approach sysfs takes when accessing device information. The sysfs dirent will be accessible to you at the dev->kobj.sd field and it's a fairly simple data structure.
Critically, you need to call put_device() on the received device handle after you've finished with it (otherwise you will end up with kernel lock-up down the line because of "unreleasable" objects). If you're using the kobj.sd accessor, then sysfs_get()/sysfs_put() on it will also be required.
As said in https://stackoverflow.com/a/4407051/196561 ("How to use sysfs inside kernel module?") by shodanex
it is a Bad Idea (tm)
with link http://www.linuxjournal.com/article/8110
Driving Me Nuts - Things You Never Should Do in the Kernel - From Issue #133 Linux Journal, 2005 By Greg Kroah-Hartman
The article says that it is possible to use sys_open and sys_read from modules to open and read files:
old_fs = get_fs();
set_fs(KERNEL_DS);
fd = sys_open(filename, O_RDONLY, 0);
if (fd >= 0) {
/* read the file here */
sys_close(fd);
}
set_fs(old_fs);
Don't know will it work with files in /sys or not.
And Greg warns us:
I Never Told You about This.
In conclusion, reading and writing a file from within the kernel is a bad, bad thing to do. Never do it. Ever.
Better way may be to learn inter module communications, possibly with modifying the hwmon/tmp421.c.

Getting exclusive access to a tty device from a root program on Linux

I have a program running as root on Linux, talking to a tty (actually an LCD implemented as a tty). The device for what it's worth is /dev/ttyUSB0. I'd like to have my program that writes to this device be able to have exclusive access to the device, so as to not have any interference from other instances of the program running at the same time.
I see that there's a ioctl option called TIOCEXCL which will prevent additonal open's of the device to wit "multiple open() calls to the same file will succeed unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned processes." I tested this and it works just as advertised: if a non-root user tries to open /dev/ttyUSB0 (once I changed the permissions) then the open fails with something like "device busy" and if a root user tries to open it, it works.
What I ideally want is a way to this exclusive access to the tty to work for root users. So I'd have multiple root users using the program that writes to the LCD, but somehow their access to the LCD (tty) would be serialized. Apparently the TIOCEXCL ioctl option will not work for me since it doesn't stop root users from opening an already-opened tty device.
I guess there are a number of options here, but I am reaching out to all ya'll to see if you might have other thoughts or suggestions.
Maybe I'm missing something about using TIOCEXCL...
Maybe there's some other way via open() or ioctl() or what-not to get exclusive access.
If there was some way I could detect that some other process has the device open, I could just wait and retry. I know about lsof but I'm loath to invoke it from inside this program just to learn this. And there are race conditions with that. (Maybe I can get over that? :) )
I could implement locking like apparently used to be done to get exclusive access to tty devices.
Update 1:
Since the only program writing to the LCD device is mine, I am inclined to do something like the following (pseudo-code) to lock inside the code:
f = open("/dev/ttyUSB0", O_RDWR)
flock(f, LOCK_EX)
// do any ioctl's, etc.
// do any write's
// sleep a tad to not flash messages too fast on LCD
nanosleep({0, 250000000}, NULL)
flock(f, LOCK_UN)
close(f)
Perhaps this discussion on LKML: [TTY] exclusive mode question can help you!
i have some issue like you.
If you can hack you linux kernel, you can to do dirty hack like this.
In file
linux/drivers/char/tty_io.c
add new command to function
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
...
case 0x54FF:
return put_user(tty->count, (int __user *)p);
...
}
The value of tty->count is a count of current open descriptors.
In your application you can try this code
int fd, count, res;
fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NONBLOCK);
if (fd >= 0)
{
count = 0;
res = ioctl(fd, 0x54FF, &count);
if (res>=0)
{
if (count > 1)
{
printf("Device already in use.\n")
close(fd);
fd = -1;
}
}
}
Answer: Root always has access. Always.
Perhaps if you said more about what else was grabbing the device, or what you fear might grab the device...
I would encourage you to look into UUCP locking. There should be a library on Linux that implements it, but if not, it's fairly easy to implement. I've used it extensively in similar situations where I don't want multiple instances of the same program to step on each other.
As a side note, perhaps you should rethink the architecture of your solution. The process that accesses the LCD/ttyUSB0 could act as a server and handle messages from the client processes that need to write to the LCD. This would require some form of IPC. It may be overkill for your project.
Keep in mind that any solution you come up with will only work if all processes that access the device conform to the protocol. If you are worried about a rogue process running as root then you may be stuck with hacking the kernel to get the solution you want.
I don't know if you have already found a solution or you just changed your design but here's a work around in the "roots land":
Create another file in your system with the same name of your port or something including it like ttyUSB0_islocked.
when you open the port have your process look for/create and open this file and write some info like it's process Id in it.
before opening the port in other programs check if this file exists and there's a process with the same process id within the file and then proceed if there's no such process other wise wait or exit.

Resources