How to attach to device context in another driver at Linux - c

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.

Related

How does the libc function isatty() work?

I searched the linux repo and couldn't find a definition for it, so I guess it's something that comes with C or something like that? I'm trying to understand how isatty is able to tell whether a file descriptor is a terminal or not, and what it even means to "be a terminal," in technical language.
I'm not really sure where to find its implementation, and if it's in assembly I won't really be able to follow along with it very easily.
The general strategy for implementing isatty is to attempt a tty-specific ioctl operation on the file descriptor, and check for ENOTTY error result. Traditionally, TCGETS, which is the backend for the tcgetattr function, is used, but this is slightly dangerous since the ioctl number for it on Linux clashes with legacy OSS sound devices, and if the file descriptor actually refers to a certain type of MIDI device, it will make changes to the device. In musl libc, we use TIOCGWINSZ, the "get window size" operation, whose number was not inadvertently reused for any other types of devices and which reliably fails with ENOTTY for non-tty devices.
In theory, you could do it with fstat and check the st_rdev field for the device major number, but this would require a hard-coded list of all device majors which are ttys, and would have broken when new types were added (e.g. USB-serial/ACM devices, uartlite devices, etc.).
isatty(3) is a library function (you won't find anything about in the linux kernel), and is usually implemented by calling tcgetattr(3) and checking its return value.
For example, in the GNU C library (glibc):
/* Return 1 if FD is a terminal, 0 if not. */
int
__isatty (int fd)
{
struct termios term;
return __tcgetattr (fd, &term) == 0;
}
tcgetattr(3) itself will resolve to some ioctl like TCGETA or TCGETS.
Notice that isatty(3) will also return true for a master side of a pseudo-tty, which isn't really a tty -- most tty related ops performed on it will actually apply to its slave side.
On linux, isatty(3) will also return true for /dev/console, which again, isn't a real tty (it cannot be made the controlling tty of a process).
On linux, you can obtain a list of all the tty drivers on your system with their major and minor numbers via cat /proc/tty/drivers. Of course, that only reflects the modules which have been loaded.

how to get a device class specific descriptor with libusb

I'm working with libusb and I need to get the device class specific descriptor for the hardware I'm working on.
I didn't find any function that could help me, does anyone have a clue about how to do this?
Thanks.
I came across the same situation in checking the ways to get class specific descriptors and saw this question was unanswered.
libusb doesn't have any API or Data Structures to retrieve the class specific descriptors. This may be because libusb is designed only to address the basic USB specification. To get the class specific descriptor details using libusb, there are few extra fields added in each descriptor.
Fields: copied from libusb.h
/** Extra descriptors. If libusb encounters unknown endpoint descriptors,
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
/** Length of the extra descriptors, in bytes. */
int extra_length;
Both "extra" and "extra_length" is added in libusb_endpoint_descriptor, libusb_interface_descriptor, libusb_config_descriptor. You have to manually decode this content according to your class codes (either at configuration level or interface level descriptor).
You can refer lsusb.c in usbutils package for better understanding on decoding.
If libusb does not have a specialized function for getting the data that you want, you should be able to use the libusb_control_transfer function (or the asynchronous version of that function) to fetch it. The USB descriptors are all fetched via control transfers, so you can just do the appropriate control transfer using that function.

How Callback is maintained from Userspace to Kernel Space

I am learning about the driver and looking into the watchdog driver code where some value is being written to /sys/devices/virtual/wdc_per now I guess this is the logic how driver gets its value from userspace and exposed file in user space is
"sys/devices/virtual/wdc_per"
But now how actually this value from wdc_per is reached to driver, there must some callback maintained
In My case its GPIO based Watchdog driver and gpio_wdt.c may be having this callback.
But I really could not figure out how it actually happens
Anybody can help me find out this userspace to kernel space link.
First of all, this driver, gpio_wdt.c, doesn't seem to exist in the mainline kernel as of this date, so it's hard to comment it.
Sysfs (usually mounted at /sys) is actually very easy to use. This is a great example of how to create Sysfs attributes. Basically, you create attributes (will become the Sysfs file names) and register them with two defined operations (callbacks): store and show, which are the equivalent of resp. write and read. The show callback is called everytime the Sysfs file (attribute) is read and store when it's written.
When writing a device driver that belongs to an existing class (most likely your situation), you will rarely need to do that yourself. This is because the standard Linux device classes already have a working set of Sysfs attributes that your driver will use more or less indirectly.
For example, the leds class (LED devices), of which you will find the devices in /sys/class/leds, has a bunch of Sysfs attributes per LED so that a user may read/modify them from userspace (brightness, maximum brightness, trigger, etc.). Now, if you look at LED specific drivers in /drivers/leds, you won't find manual Sysfs attributes creations. You will find, however, a call to led_classdev_register when the driver is probed, which takes a struct led_classdev* as a parameter. This structure has a brightness_set callback member the specific driver needs to provide. When a user writes to /sys/class/leds/whatever-led/brightness, the leds class' store Sysfs callback gets called which in turn calls the specific driver's brightness_set callback.
My point is: make sure you really know your device class before manually adding Sysfs attributes. Anyway, when submitting your driver to the LKML, you will know fast enough if it was a good decision.

uart buffer is not read

I am trying to read binary data from a serial device in c on linux.
The problem is, that sometimes there are chars in the driver's internal buffer, but polling (with select(2)) returns saying the device is not ready to be read.
I have read and re-read the man of termios and all the related man and searched over the internet. I believe I set all the flags correctly (namely VTIME, VMIN) and unset ICANON.
I tried using the function "tcmakeraw", as well, but it didn't solve the problem.
Do you guys have any ideas about what should I do?
Kind regards & Thanks in advance
Yannay
You should show us the code. I would start with using cfmakeraw on the serial port.
Once you have things working in raw mode, you can make modification and see how it works.
Here is a list of question or things you could check :
after modifying the attribute, using for example cfmakeraw, do you call tcsetattr(...) to
apply your change ?
How do you prove there is still data in the driver receive buffer ?
do you check your system call for errors ?
what is the result of stracing your program ?
Edit based on your comments :
Your protocol "guarantee" .... => check your assumption ! Unchecked, crystal clear guarantee are a good coandidate for "impossible error"
Basically : either select is broken, or your serial driver. Reason for serial driver being broken is a hardware fifo not being full enough to trigger un interrupt, or loosing an interrupt.
What happens when you read directly (not through C) /dev/ttyS0 (or equiv) after you setserial your parameters. Are you able to get the needed data outside of the select()?

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