I am trying to implement a system call interception for sys_open() call via kernel module and for that I have defined a miscellaneous device driver MyDevice which can be inserted as kernel module. Below is the code for my kernel module:
#include <linux/version.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/highmem.h>
#include <asm/unistd.h>
MODULE_LICENSE("GPL");
// IOCTL commands
#define IOCTL_PATCH_TABLE 0x00000001
#define IOCTL_FIX_TABLE 0x00000004
//Global variables
int in_use = 0; //set to 1 in open handler and reset to zero in release handler
int is_set = 0; // flag to detect system call interception
unsigned long *sys_call_table = (unsigned long*)0xffffffff81801400; //hard coded address of sys_call_table from /boot/System.map
//function pointer to original sys_open
asmlinkage int (*real_open)(const char* __user, int, int);
//Replacement of original call with modified system call
asmlinkage int custom_open(const char* __user file_name, int flags, int mode)
{
printk("interceptor: open(\"%s\", %X, %X)\n", file_name,flags,mode);
return real_open(file_name,flags,mode);
}
/*
Make the memory page writable
This is little risky as directly arch level protection bit is changed
*/
int make_rw(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
if(pte->pte &~ _PAGE_RW)
pte->pte |= _PAGE_RW;
return 0;
}
/* Make the page write protected */
int make_ro(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte = pte->pte &~ _PAGE_RW;
return 0;
}
/* This function will be invoked each time a user process attempts
to open my device. You should keep in mind that the prototype
of this function may change along different kernel versions. */
static int my_open(struct inode *inode, struct file *file)
{
/*Do not allow multiple processes to open this device*/
if(in_use)
return -EBUSY;
in_use++;
printk("MyDevice opened\n");
return 0;
}
/* This function, in turn, will be called when a process closes our device */
static int my_release(struct inode *inode, struct file *file)
{
in_use--;
printk("MyDevice closed\n");
return 0;
}
/*This static function handles ioctl calls performed on MyDevice*/
static int my_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int retval = 0;
switch(cmd)
{
case IOCTL_PATCH_TABLE:
make_rw((unsigned long)sys_call_table);
real_open = (void*)*(sys_call_table + __NR_open);
*(sys_call_table + __NR_open) = (unsigned long)custom_open;
make_ro((unsigned long)sys_call_table);
is_set=1;
break;
case IOCTL_FIX_TABLE:
make_rw((unsigned long)sys_call_table);
*(sys_call_table + __NR_open) = (unsigned long)real_open;
make_ro((unsigned long)sys_call_table);
is_set=0;
break;
default:
printk("sys_open not executed\n");
break;
}
return retval;
}
//populate data struct for file operations
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = &my_open,
.release = &my_release,
.unlocked_ioctl = (void*)&my_ioctl,
.compat_ioctl = (void*)&my_ioctl
};
//populate miscdevice data structure
static struct miscdevice my_device = {
MISC_DYNAMIC_MINOR,
"MyDevice",
&my_fops
};
static int __init init_my_module(void)
{
int retval;
printk(KERN_INFO "Inside kernel space\n");
retval = misc_register(&my_device);
return retval;
}
static void __exit cleanup_my_module(void)
{
if (is_set)
{
make_rw((unsigned long)sys_call_table);
*(sys_call_table + __NR_open) = (unsigned long)real_open;
make_ro((unsigned long)sys_call_table);
}
misc_deregister(&my_device);
printk(KERN_INFO "Exiting kernel space\n");
return;
}
module_init(init_my_module);
module_exit(cleanup_my_module);
The code for my test file is as follows:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* Define ioctl commands */
#define IOCTL_PATCH_TABLE 0x00000001
#define IOCTL_FIX_TABLE 0x00000004
int main(void)
{
int device = open("/dev/MyDevice", O_RDWR);
printf("%d\n",device);
ioctl(device, IOCTL_PATCH_TABLE);
sleep(2);
ioctl(device, IOCTL_FIX_TABLE);
close(device);
return 0;
}
The problem is that in my test file open("/dev/MyDevice", O_RDWR); is always returning -1, why is it so ? Where am I going wrong ? I checked with ls -l /dev/MyDevice, MyDevice has been successfully registered with following details: crw------- 1 root root 10, 56 Dec 9 19:33 /dev/MyDevice
Sorry, seriously stupid mistake, that's what happens when rookies do things. I just needed to grant the read and write permissions for my miscellaneous char device driver.
sudo chmod a+r+w /dev/MyDevice
Related
I'm trying to complete a hooking sample attachment in a program for my uni assignment. The task requires to get a system call sys_rt_sigaction hooked when initiating a loadable module in Linux kernel (I use Ubuntu 18.04 LTS, kernel version is 5.0.0-23-generic). So, the case I'm struggling originates from an error could not insert module <module name>: Unknown symbol in module once I started sudo insmod <my module name>.ko.
After some googling, I see clear this problem arises due to missing sys_call_table export to run inserting as smoothly as well. Following this post, I want to cope that invoking kallsyms_lookup_name call before kicking off init procedure.
There is .c-file which provides with definitions of operations accessible by module (file name is buffer.c):
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/unistd.h>
void * sys_call_table = (void *) kallsyms_lookup_name("sys_call_table");// some wrongness here, but what exactly?
MODULE_LICENSE("GPL");
int (*real_rt_sigaction)(const char * path); // true syscall prototype
static int __init buffer_init_module(void);
static void __exit buffer_exit_module(void);
static int device_open(struct inode *, struct file *); // driver file opening
static int device_release(struct inode *, struct file *); // return of system resource control
static ssize_t device_read(struct file *, char *, size_t, loff_t *); // reading from driver file
static ssize_t device_write(struct file *, const char *, size_t, loff_t *); // writing into driver file
#define DEVICE_NAME "buffer"
#define BUF_LEN 80
// to be called instead
int alter_rt_sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact, size_t sigsetsize) {
printk(KERN_INFO "Syscall function hooked - you've lost control of your experience");
return 0;
}
static int Major;
static int Device_Open = 0;
static int total_open = 1;
static char Buf[BUF_LEN + 1] = "Buffer is empty, add some input\n";
static char *Msg_ptr;
static int Buf_Char = 50;
static int Bytes_Read = 0;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static int __init buffer_init_module(void)
{
printk(KERN_INFO
"Device initializing in progress...");
Major = register_chrdev(0, DEVICE_NAME, &fops);
if(Major < 0) {
printk("Major number hasn't been assigned - Driver registration failed\n");
return Major;
}
printk(KERN_INFO "Registration success - device major number: %d\n", Major);
real_rt_sigaction=sys_call_table[__NR_rt_sigaction];
sys_call_table[__NR_rt_sigaction]=alter_rt_sigaction; // hooking implementation
return 0;
}
static void __exit buffer_exit_module(void)
{
unregister_chrdev(Major, DEVICE_NAME);
printk(KERN_INFO "Outside the module - exit successfully completed\n");
sys_call_table[__NR_rt_sigaction]=real_rt_sigaction; // original call reset
}
static int device_open(struct inode *inode, struct file *file)
{
if(Device_Open)
return -EBUSY;
Device_Open++;
printk(KERN_INFO "Device file has been accessed %d time(s)\n", total_open++);
Msg_ptr = Buf;
try_module_get(THIS_MODULE);
Bytes_Read = 0;
return 0;
}
static int device_release(struct inode * node, struct file * filep)
{
Device_Open--;
module_put(THIS_MODULE);
printk(KERN_INFO "Device file gets close\n");
return 0;
}
static ssize_t device_read(struct file * filep, char * buffer, size_t len, loff_t * offset)
{
int got_read = Bytes_Read;
if(Bytes_Read >= Buf_Char)
return 0;
while(len && (Bytes_Read < Buf_Char)) {
put_user(Msg_ptr[Bytes_Read], buffer+Bytes_Read);
len--;
Bytes_Read++;
}
return Bytes_Read-got_read;
}
static ssize_t device_write(struct file * filep, const char * buffer, size_t len, loff_t * offset)
{
Buf_Char = 0;
if(Buf_Char >= BUF_LEN) {
return 0;
}
while(len && (Buf_Char < BUF_LEN))
{
get_user(Msg_ptr[Buf_Char], buffer+Buf_Char);
len--;
Buf_Char++;
}
return Buf_Char;
}
module_init(buffer_init_module);
module_exit(buffer_exit_module);
Additively, there is code in Makefile:
obj-m += buffer.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
The painful moment here is an error message initializer element is not constant whenever I was trying to build module via sudo make in my project folder. As I follow the beginner's tutorials and need for some basic insight, it might be highly appreciable to see any help with solution or even some ideas how to handle the same problem more effectively, indeed.
I wrote a small device driver for a "coin" device. I create an entry in /drivers/char/Kconfig
and corresponding Makefile, then selected built-in option in menuconfig. The kernel compiled fine (built-in.o file was created). But I still can't access the device (/dev/coin was not created) and there was no entry under /proc/devices.
Please help!!
I am cross-compiling for powerpc
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/random.h>
#include <linux/debugfs.h>
#include <linux/init.h>
#define DEVNAME "coin"
#define LEN 20
enum values {HEAD, TAIL};
struct dentry *dir, *file;
int file_value;
int stats[2] = {0, 0};
char *msg[2] = {"head\n", "tail\n"};
static int major;
static struct class *class_coin;
static struct device *dev_coin;
static ssize_t r_coin(struct file *f, char __user *b,
size_t cnt, loff_t *lf)
{
char *ret;
u32 value = random32() % 2;
ret = msg[value];
stats[value]++;
return simple_read_from_buffer(b, cnt,
lf, ret,
strlen(ret));
}
static struct file_operations fops = { .read = r_coin };
#ifdef CONFIG_COIN_STAT
static ssize_t r_stat(struct file *f, char __user *b,
size_t cnt, loff_t *lf)
{
char buf[LEN];
snprintf(buf, LEN, "head=%d tail=%d\n",
stats[HEAD], stats[TAIL]);
return simple_read_from_buffer(b, cnt,
lf, buf,
strlen(buf));
}
static struct file_operations fstat = { .read = r_stat };
#endif
static int __init coin_init(void)
{
void *ptr_err;
major = register_chrdev(0, DEVNAME, &fops);
if (major < 0)
return major;
class_coin = class_create(THIS_MODULE,
DEVNAME);
if (IS_ERR(class_coin)) {
ptr_err = class_coin;
goto err_class;
}
dev_coin = device_create(class_coin, NULL,
MKDEV(major, 0),
NULL, DEVNAME);
if (IS_ERR(dev_coin))
goto err_dev;
#ifdef CONFIG_COIN_STAT
dir = debugfs_create_dir("coin", NULL);
file = debugfs_create_file("stats", 0644,
dir, &file_value,
&fstat);
#endif
return 0;
err_dev:
ptr_err = class_coin;
class_destroy(class_coin);
err_class:
unregister_chrdev(major, DEVNAME);
return PTR_ERR(ptr_err);
}
static void __exit coin_exit(void)
{
#ifdef CONFIG_COIN_STAT
debugfs_remove(file);
debugfs_remove(dir);
#endif
device_destroy(class_coin, MKDEV(major, 0));
class_destroy(class_coin);
return unregister_chrdev(major, DEVNAME);
}
module_init(coin_init);
module_exit(coin_exit);
What if you manually insert module into kernel using insmod? Does it work? Any messages in dmesg?
As I remember entries in /dev (/dev/coin) should be created manually using mknod, but you need major number of registered device. Just printk it after register_chrdev().
I am working on parallel port driver. Now i had seen the methods to get interrupt from parallel port.
by one of them,
First make 4th pin of control reg 1(IRQ).
then make nACK low.
so i make a switch in between data pin 8 and nACK. so, if i write some data which has msb 1 then it will be interrupted, if that switch is on. Now i have a problem. If i disconnected that switch and again connect then it will not give me interrupt.
So, how can i do that thing by which i got interrupt by means of switch is connect or not.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/parport.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <linux/kthread.h>
#define DEVICE_NAME "parlelport"
struct pardevice *pdev;
static int dummy;
int ret;
static irqreturn_t recv_handler(int irq, void *dev_id)
{
printk("we inside if isr");
return 0;
}
int led_open(struct inode *inode, struct file *file)
{
printk("1\n");
printk("Device File Opened\n");
char byte1;
byte1=inb(0x37A);
printk("%d \n",byte1);
return 0;
}
ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
printk("2\n");
char byte=inb(0x37A);
printk("%d",byte);
byte = byte | 0x10; // 0x10= 00010000, 4th pin of CTRL reg
outb(byte, 0x37A); //which enable IRQ
char kbuf;
copy_from_user(&kbuf, buf, 1);
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, kbuf); /* Write to the device */
//parport_release (pdev);
return count;
}
int led_release(struct inode *inode, struct file *file)
{
printk("3\n");
printk("Device File Released\n");
char byte;
byte=inb(0x37A);
printk("%d", byte);
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int led_preempt(void *handle)
{
printk("4\n");
return 1;
}
static void led_attach(struct parport *port)
{
printk("5\n");
pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL);
printk("Port attached\n");
char byte1;
byte1=inb(0x37A);
printk("%d \n",byte1);
}
static void led_detach(struct parport *port)
{
printk("6\n");
parport_unregister_device (pdev);
printk("Port Deattached\n");
}
static struct parport_driver led_driver = {
.name= "led",
.attach = led_attach,
.detach = led_detach,
};
int __init led_init(void)
{
printk("7\n");
if (register_chrdev(89, DEVICE_NAME, &led_fops))
{
printk("Can't register device\n");
return -1;
}
char byte=inb(0x37A);
printk("%d",byte);
byte = byte | 0x10;
outb(byte, 0x37A);
char byte1;
byte1=inb(0x37A);
printk("%d %d \n",byte,byte1);
parport_register_driver(&led_driver);
ret= request_irq(7, recv_handler, IRQF_SHARED, "parlelport", &dummy);
printk("%d",ret);
return 0;
}
void __exit led_cleanup(void)
{
printk("8\n");
unregister_chrdev(89, DEVICE_NAME);
if(!ret)
free_irq(7, &dummy);
parport_unregister_driver(&led_driver);
printk("LED Driver unregistered.\n");
return;
}
module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vikrant Patel");
Test.c file
int main()
{
int fd=open("/dev/parlelport",O_RDWR);
char byte;
printf("Enter Value to send on parallel port");
scanf("%c",&byte);
printf("Byte value is %c\n",byte);
if(write(fd,&byte,sizeof(char)))
{
printf("\nSuccessfully written on port");
}
getchar();
getchar();
close(fd);
}
I got it.
First make a thread
put the Enable IRQ code in that thread
so it will continuously execute it
whenever i connect pins at my hardware then it will be interrupted.
check this code for your ref.
#include <linux/module.h>
#include <linux/parport.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#define DEVICE_NAME "parlelport"
#define DATA 0x378
#define STATUS 0x379
#define CONTROL 0x37A
struct pardevice *pdev;
struct task_struct *ts1, *ts2;
int dummy;
char buf1='1',buf2='2';
char byte='0';
int thread1(void *data)
{
while(1)
{
outb(byte, CONTROL); /* 0x30 = 0011 0000 , makes IRQ pin(5th bit) enable */
printk("Thread1\n");
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, buf1); /* Write to the device */
parport_release(pdev); /* Release the port */
msleep(4000);
if (kthread_should_stop())
break;
}
return 0;
}
int thread2(void *data)
{
while(1)
{
outb(byte,CONTROL); /* 0x30 = 0011 0000 , makes IRQ pin(5th bit) enable */
printk("Thread2\n");
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, buf2); /* Write to the device */
parport_release(pdev); /* Release the port */
msleep(4000);
if (kthread_should_stop())
break;
}
return 0;
}
int led_open(struct inode *inode, struct file *file)
{
printk("Device File Opened\n");
ts1=kthread_run(thread1,NULL,"kthread"); /* Initiation of thread 1 */
msleep(2000);
ts2=kthread_run(thread2,NULL,"kthread"); /* Initiation of thread 2 */
return 0;
}
ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
return count;
}
int led_release(struct inode *inode, struct file *file)
{
printk("Device File Released\n");
kthread_stop(ts1);
kthread_stop(ts2);
buf1='1';
buf2='2';
outb_p(0x00,DATA);
return 0;
}
static irqreturn_t recv_handler(int irq, void *unused)
{
printk("we inside of isr");
buf1= buf1 ^ 0x7F;
buf2= buf2 ^ 0x7F;
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int led_preempt(void *handle)
{
return 1;
}
static void led_attach(struct parport *port)
{
pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL);
printk("Port attached\n");
}
static void led_detach(struct parport *port)
{
parport_unregister_device (pdev);
printk("Port Deattached\n");
}
static struct parport_driver led_driver = {
.name= "led",
.attach = led_attach,
.detach = led_detach,
};
int __init led_init(void)
{
/*Register our ISR with the kernel for PARALLEL_IRQ */
if (request_irq(7, recv_handler, IRQF_SHARED, DEVICE_NAME ,&dummy))
{
printk("Registering ISR failed\n");
return -ENODEV;
}
/*Register Character Device Driver at 89 Major number*/
if (register_chrdev(89, DEVICE_NAME, &led_fops))
{
printk("Can't register device\n");
return -1;
}
/*Register parallel port driver with parport structure led_driver*/
parport_register_driver(&led_driver);
return 0;
}
void __exit led_cleanup(void)
{
unregister_chrdev(89, DEVICE_NAME); /* Unregister char driver */
free_irq(7, &dummy); /* Free the ISR from IRQ7 */
parport_unregister_driver(&led_driver); /* Unregister the parallel port driver */
printk("LED Driver unregistered.\n");
return;
}
module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vikrant Patel");
I have a simple sysfs device attribute which shows up under my sysfs directory, and on a call to read returns the value of a kernelspace variable. I want to call poll on this attribute to allow my userspace thread to block until the value shown by the attribute changes.
My problem is that poll doesn't seem to block on my attribute -- it keeps returning POLLPRI even though the value shown by the attribute does not change. In fact, I have no calls at all to sysfs_notify in the kernel module, yet the userspace call poll still does not block.
Perhaps I should be checking for a return value of something other than POLLPRI -- but according to the documentation in the Linux kernel, sysfs_poll should return POLLERR|POLLPRI:
/* ... When the content changes (assuming the
* manager for the kobject supports notification), poll will
* return POLLERR|POLLPRI ...
*/
Is there something I'm forgetting to do with poll?
The device attribute is located at: /sys/class/vilhelm/foo/blah.
I load a kernel module called foo which registers a device, and creates a class and this device attribute.
The userspace application called bar spawns a thread that calls poll on the device attribute, checking for POLLPRI.
If poll returns a positive number, POLLPRI has been returned.
Use fopen and fscan to read the value from the device attribute file.
If the value is 42, print FROM THREAD!!!.
The problem is that the message is printed nonstop when I'm expecting the call to poll to block indefinitely. The problem must lie with poll (the other calls successfully acquire the correct value of 42 from the device attribute).
userspace app - bar.c:
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <unistd.h>
static void handle_val(unsigned val, FILE *fp);
void * start_val_service(void *arg);
int main(void){
pthread_t val_serv;
pthread_create(&val_serv, NULL, &start_val_service, NULL);
pthread_exit(NULL);
return 0;
}
static void handle_val(unsigned val, FILE *fp){
switch(val){
case 42:
{
printf("FROM THREAD!!!\n");
break;
}
default:
break;
}
}
void * start_val_service(void *arg){
struct pollfd fds;
fds.fd = open("/sys/class/vilhelm/foo/blah", O_RDONLY);
fds.events = POLLPRI;
do{
int ret = poll(&fds, 1, -1);
if(ret > 0){
FILE *fp = fopen("/sys/class/vilhelm/foo/blah", "r");
unsigned val;
fscanf(fp, "%u", &val);
handle_val(val, fp);
fclose(fp);
}
}while(1);
close(fds.fd);
pthread_exit(NULL);
}
kernel module - foo.c:
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
static dev_t foo_dev;
static struct class *vilhelm;
static unsigned myvar = 42;
static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf);
struct unsigned_device_attribute{
struct device_attribute dev_attr;
unsigned *ptr;
};
static struct unsigned_device_attribute unsigned_dev_attr_blah = {
.dev_attr = __ATTR(blah, S_IRUGO, unsigned_dev_attr_show, NULL)
};
static int __init foo_init(void){
int retval = 0;
printk(KERN_INFO "HELLO FROM MODULE 1");
if(alloc_chrdev_region(&foo_dev, 0, 1, "vilhelm") < 0){
printk(KERN_ERR "foo: unable to register device");
retval = -1;
goto out_alloc_chrdev_region;
}
vilhelm = class_create(THIS_MODULE, "vilhelm");
if(IS_ERR(vilhelm)){
printk(KERN_ERR "foo: unable to create device class");
retval = PTR_ERR(vilhelm);
goto out_class_create;
}
struct device *foo_device = device_create(vilhelm, NULL, foo_dev, NULL, "foo");
if(IS_ERR(foo_device)){
printk(KERN_ERR "foo: unable to create device file");
retval = PTR_ERR(foo_device);
goto out_device_create;
}
unsigned_dev_attr_blah.ptr = &myvar;
retval = device_create_file(foo_device, &unsigned_dev_attr_blah.dev_attr);
if(retval){
printk(KERN_ERR "foo: unable to create device attribute files");
goto out_create_foo_dev_attr_files;
}
return 0;
out_create_foo_dev_attr_files:
device_destroy(vilhelm, foo_dev);
out_device_create:
class_destroy(vilhelm);
out_class_create:
unregister_chrdev_region(foo_dev, 1);
out_alloc_chrdev_region:
return retval;
}
static void __exit foo_exit(void){
printk(KERN_INFO "BYE FROM MODULE 1");
device_destroy(vilhelm, foo_dev);
class_destroy(vilhelm);
unregister_chrdev_region(foo_dev, 1);
}
static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf){
struct unsigned_device_attribute *tmp = container_of(attr, struct unsigned_device_attribute, dev_attr);
unsigned value = *(tmp->ptr);
return scnprintf(buf, PAGE_SIZE, "%u\n", value);
}
module_init(foo_init);
module_exit(foo_exit);
MODULE_LICENSE("GPL");
See also
Using the Linux sysfs_notify call
To quote some more from the comment you quoted:
Once poll/select indicates that the value has changed, you
need to close and re-open the file, or seek to 0 and read again.
But you do nothing with fds.fd.
Also, do a dummy read() before calling poll();
any newly opened file is considered changed.
I am trying to communicate asynchronously between a kernel driver and a user-space program (I know there are lots of questions here that ask for similar information, but I could find none that deal with sysfs_notify).
I am leaving Vilhelm's edit here, but adding the source to both a simple driver utilizing sysfs and a user-space program to poll it. The driver works fine (I got most of it from the net; it is missing the credits, but I couldn't find them when I went back to add them).
Unfortunately, the polling program does not work. It always returns success immediately. Interestingly, if I don't perform the two reads prior to the poll, the revents members are set to POLLERR | POLLIN instead of just POLLIN as seen in the program output.
Program output:
root#ubuntu:/home/wmulcahy/demo# ./readhello
triggered
Attribute file value: 74 (t) [0]
revents[0]: 00000001
revents[1]: 00000001
Here is the driver: hello.c (you can see where I started out...)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
struct my_attr {
struct attribute attr;
int value;
};
static struct my_attr notify = {
.attr.name="notify",
.attr.mode = 0644,
.value = 0,
};
static struct my_attr trigger = {
.attr.name="trigger",
.attr.mode = 0644,
.value = 0,
};
static struct attribute * myattr[] = {
¬ify.attr,
&trigger.attr,
NULL
};
static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
printk( "hello: show called (%s)\n", a->attr.name );
return scnprintf(buf, PAGE_SIZE, "%s: %d\n", a->attr.name, a->value);
}
static struct kobject *mykobj;
static ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
sscanf(buf, "%d", &a->value);
notify.value = a->value;
printk("sysfs_notify store %s = %d\n", a->attr.name, a->value);
sysfs_notify(mykobj, NULL, "notify");
return sizeof(int);
}
static struct sysfs_ops myops = {
.show = show,
.store = store,
};
static struct kobj_type mytype = {
.sysfs_ops = &myops,
.default_attrs = myattr,
};
static struct kobject *mykobj;
static int __init hello_module_init(void)
{
int err = -1;
printk("Hello: init\n");
mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
if (mykobj) {
kobject_init(mykobj, &mytype);
if (kobject_add(mykobj, NULL, "%s", "hello")) {
err = -1;
printk("Hello: kobject_add() failed\n");
kobject_put(mykobj);
mykobj = NULL;
}
err = 0;
}
return err;
}
static void __exit hello_module_exit(void)
{
if (mykobj) {
kobject_put(mykobj);
kfree(mykobj);
}
printk("Hello: exit\n");
}
module_init(hello_module_init);
module_exit(hello_module_exit);
MODULE_LICENSE("GPL");
And here is the poll program: readhello.c
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#define TEST_SYSFS_TRIGGER "/sys/hello/trigger"
#define TEST_SYSFS_NOTIFY "/sys/hello/notify"
int main(int argc, char **argv)
{
int cnt, notifyFd, triggerFd, rv;
char attrData[100];
struct pollfd ufds[2];
// Open a connection to the attribute file.
if ((notifyFd = open(TEST_SYSFS_NOTIFY, O_RDWR)) < 0)
{
perror("Unable to open notify");
exit(1);
}
// Open a connection to the attribute file.
if ((triggerFd = open(TEST_SYSFS_TRIGGER, O_RDWR)) < 0)
{
perror("Unable to open trigger");
exit(1);
}
ufds[0].fd = notifyFd;
ufds[0].events = POLLIN;
ufds[1].fd = triggerFd;
ufds[1].events = POLLIN;
// Someone suggested dummy reads before the poll() call
cnt = read( notifyFd, attrData, 100 );
cnt = read( triggerFd, attrData, 100 );
ufds[0].revents = 0;
ufds[1].revents = 0;
if (( rv = poll( ufds, 2, 10000)) < 0 )
{
perror("poll error");
}
else if (rv == 0)
{
printf("Timeout occurred!\n");
}
else if (ufds[0].revents & POLLIN)
{
printf("triggered\n");
cnt = read( notifyFd, attrData, 1 );
printf( "Attribute file value: %02X (%c) [%d]\n", attrData[0], attrData[0], cnt );
}
printf( "revents[0]: %08X\n", ufds[0].revents );
printf( "revents[1]: %08X\n", ufds[1].revents );
close( triggerFd );
close( notifyFd );
}
Some upcoming sysfs enhancements.
Internally, the patch adds a wait queue head to every kobject on the
system; that queue is inserted into a poll table in response to a
poll() call. The sysfs code has no way of knowing, however, when the
value of any given sysfs attribute has changed, so the subsystem
implementing a pollable attribute must make explicit calls to:
void sysfs_notify(struct kobject *kobj, char *dir, char *attr);
Thanks,
Lee
The blocking poll is from the user side. User code can simply tell the kernel which attributes it's interested in, then block in a poll() until one of them has changed.
The sysfs_notify() is a kernel-side call that releases the user-space poll(). After you adjust your kernel attribute value, just call sysfs_notify() to allow any user-space applications to respond to their outstanding poll().
Think of the poll() as "subscribing" to notices of a change in an attribute of interest, and sysfs_notify() as "publishing" the change to any subscribers.