Creation of proc entrt using proc_create_data in kernel version 3.10 and above.

In the post “Creating read only proc entry in kernel versions above 3.10. ” we saw how to create a proc entry using the function proc_create.

Note: The following module is valid only of kernel version 3.10 and above.

Another function available in kernel version 3.10 and above for creation of proc entry is proc_create_data which is defined in proc_fs.hs as

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,struct proc_dir_entry *parent,const struct file_operations *proc_fops,void *data);

Where

name: The name of the proc entry
mode: The access mode for proc entry
parent: The name of the parent directory under /proc
proc_fops: The structure in which the file operations for the proc entry will be created.
data: If any data needs to be passed to the proc entry.

For example to create a proc entry by the name “hello” under /proc the above functions will be defined are

proc=proc_create_data(“hello”,0,NULL,&proc_fops,msg);

Along with creation of the entry we are also passing a function to the proc entry in using the porinter “msg”.

Now we need to create file_operations structure proc_fops in which we can map the read function for the proc entry.

struct file_operations proc_fops = { read: read_proc };

Next we need to add the function read_proc which will give to the user space the data that we want to export from the kernel space.

int read_proc(struct file *filp,char *buf,size_t count,loff_t *offp ) { char *data; data=PDE_DATA(file_inode(filp)); if(!(data)){ printk(KERN_INFO “Null data”); return 0; } if(count>temp) { count=temp; } temp=temp-count; copy_to_user(buf,data, count); if(count==0) temp=len; return count; }

To access the data in the proc_dir_structure we need to make use of the function PDE_DATA to which we pass the file pointer. The function in turn returs a pointer to the data that was passed during the creation of the proc entry.

The message that we want to display will be defined in the function create_new_proc_entry in which we will also call the fuction for creation of proc entry.

void create_new_proc_entry() { msg=”Hello World “; proc=proc_create_data(“hello”,0,NULL,&proc_fops,msg); len=strlen(msg); temp=len; }

The init and clean up functions for the module are

int proc_init (void) { create_new_proc_entry(); return 0; } void proc_cleanup(void) { remove_proc_entry(“hello”,NULL); }

The full code for creation of proc entry using proc_create_data is

proc_read_data.c

#include #include #include #include #include #include #include #include struct proc_dir_entry *proc; int len,temp; char *msg; int read_proc(struct file *filp,char *buf,size_t count,loff_t *offp ) { char *data; data=PDE_DATA(file_inode(filp)); if(!(data)){ printk(KERN_INFO “Null data”); return 0; } if(count>temp) { count=temp; } temp=temp-count; copy_to_user(buf,data, count); if(count==0) temp=len; return count; } struct file_operations proc_fops = { read: read_proc }; void create_new_proc_entry() { msg=”Hello World “; proc=proc_create_data(“hello”,0,NULL,&proc_fops,msg); len=strlen(msg); temp=len; } int proc_init (void) { create_new_proc_entry(); return 0; } void proc_cleanup(void) { remove_proc_entry(“hello”,NULL); } MODULE_LICENSE(“GPL”); module_init(proc_init); module_exit(proc_cleanup);

The makefile for the compilation of the modules are

ifneq ($(KERNELRELEASE),) obj-m := proc_read_data_3_10.o else #KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= /home/nitin/Desktop/src/linux-3.11.1/ PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

Compile and load the module using

$ make $ sudo insmod proc_read_3_10.ko

We can see the output by using the cat command $ cat /proc/hello Hello World

Related Posts:

Creating read only proc entry in kernel versions above 3.10.

For kernel version before 3.10

Creating a proc entry – 1

Creating a proc read entry

Category: Linux | Comments Off on Creation of proc entrt using proc_create_data in kernel version 3.10 and above.

Creating read only proc entry in kernel versions above 3.10.

The creation of proc entries has undergone a considerable change in kernel version 3.10 and above. In this post we will see one of the methods we can use in linux kernel version 3.10 and above let us see how we can create proc entries in version 3.10 and above.

The function is defined in proc_fs.h as

static inline struct proc_dir_entry *proc_create(const char *name, umode_t mode,struct proc_dir_entry *parent, const struct file_operations *proc_fops)

Where:

name: The name of the proc entry
mode: The access mode for proc entry
parent: The name of the parent directory under /proc
proc_fops: The structure in which the file operations for the proc entry will be created.

For example to create a proc entry by the name “hello” under /proc the above function will be defined are

proc_create(“hello”,0,NULL,&proc_fops);

Now we need to create file_operations structure proc_fops in which we can map the read function for the proc entry.

struct file_operations proc_fops = { read: read_proc };

Next we need to add the function read_proc which will give to the user space the data that we want to export from the kernel space.

int read_proc(struct file *filp,char *buf,size_t count,loff_t *offp ) { if(count>temp) { count=temp; } temp=temp-count; copy_to_user(buf,msg, count); if(count==0) temp=len; return count; }

The message that we want to display will be defined in the function create_new_proc_entry in which we will also call the fuction for creation of proc entry.

void create_new_proc_entry() { proc_create(“hello”,0,NULL,&proc_fops); msg=” Hello World “; len=strlen(msg); temp=len; printk(KERN_INFO “1.len=%d”,len); }

The init and clean up functions for the module are

int proc_init (void) { create_new_proc_entry(); return 0; } void proc_cleanup(void) { remove_proc_entry(“hello”,NULL); }

The full code of module for creation of proc entry using proc_create is

#include #include #include #include#include int len,temp; char *msg; int read_proc(struct file *filp,char *buf,size_t count,loff_t *offp ) { if(count>temp) { count=temp; } temp=temp-count; copy_to_user(buf,msg, count); if(count==0) temp=len; return count; } struct file_operations proc_fops = { read: read_proc }; void create_new_proc_entry() { proc_create(“hello”,0,NULL,&proc_fops); msg=” Hello World “; len=strlen(msg); temp=len; printk(KERN_INFO “1.len=%d”,len); } int proc_init (void) { create_new_proc_entry(); return 0; } void proc_cleanup(void) { remove_proc_entry(“hello”,NULL); } MODULE_LICENSE(“GPL”); module_init(proc_init); module_exit(proc_cleanup);

The makefile for the compilation of the module is

ifneq ($(KERNELRELEASE),) obj-m := proc_read_3_10.o else #KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= /home/nitin/Desktop/src/linux-3.11.1/ PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

Compile and load the module using

$ make $ sudo insmod proc_read_3_10.ko

We can see the output by using the cat command $ cat /proc/hello Hello World

We can see that the message that we passed in the read function of the module is being displayed when the proc entry is read.

Related Posts : For kernel version before 3.10 Creating a proc entry – 1 Creating a proc read entry

Category: Linux | Comments Off on Creating read only proc entry in kernel versions above 3.10.

Using Spinlock in linux : Example

In the post we see the various functions available for spinlock in Linux. Let us look at an example and see how it can be used in Linux.

In the following example we create two threads, thread1 and thread2. We initialize a spinlock “my_lock” and both the threads try to acquire the lock .

Thread1 is made to get to the lock first. After acquiring the lock thread1 waits for one minute before releasing the lock.

During this time thread2 attempts to acquire the lock too. We have used the function spin_trylock, so if the thread2 is unable to hold the spinlock it will return immediately with a return value of non zero.

Thus we will define a spinlock using

static spinlock_t my_lock=SPIN_LOCK_UNLOCKED

Starting with the spinlock in unlocked state.

We will create the two threads thread_fn1 and thread_fn2 using the function kthread_create.

The first thread function thread_fn1 will hold the lock and loop for approximately on minute the code for which is

int thread_fn1() { unsigned long j0,j1; int delay = 60*HZ; j0 = jiffies; j1 = j0 + delay; spin_lock(&my_lock); while (time_before(jiffies, j1)) schedule(); printk(KERN_INFO “In thread1”); spin_unlock(&my_lock); return 0; }

The second thread function thread_fn2 will try to hold try to hold the lock using spin_trylock.A sleep of 100ms is added at the beginning to make sure that the first thread gets to the lock first.Thus the code for the second thread function will be

int thread_fn2() { int ret=0; msleep(100); ret=spin_trylock(&my_lock); if(!ret) { printk(KERN_INFO “Unable to hold lock”); return 0; } else { printk(KERN_INFO “Lock acquired”); spin_unlock(&my_lock); return 0; } }

If the thread_fn2 is able to get the lock we will see the message “Lock acquired” in the kernel log. If the thread_fn2 is unable to get the lock we will see the message “Unable to hold lock” in the kernel log.

The complete code will be

spinlock_example.c

#include #include #include #include // for threads #include // for task_struct #include #include static struct task_struct *thread1,*thread2; static spinlock_t my_lock = SPIN_LOCK_UNLOCKED; int thread_fn1() { unsigned long j0,j1; int delay = 60*HZ; j0 = jiffies; j1 = j0 + delay; spin_lock(&my_lock); while (time_before(jiffies, j1)) schedule(); spin_unlock(&my_lock); return 0; } int thread_fn2() { int ret=0; msleep(100); ret=spin_trylock(&my_lock); if(!ret) { printk(KERN_INFO “Unable to hold lock”); return 0; } else { printk(KERN_INFO “Lock acquired”); spin_unlock(&my_lock); return 0; } } int thread_init (void) { char name[8]=”thread1″; char name1[8]=”thread2″; thread1 = kthread_create(thread_fn1,NULL,name); if((thread1)) { wake_up_process(thread1); } thread2 = kthread_create(thread_fn2,NULL,name1); if((thread2)) { wake_up_process(thread2); } return 0; } void thread_cleanup(void) { int ret,ret1; ret = kthread_stop(thread1); if(!ret) printk(KERN_INFO “Thread stopped”); } MODULE_LICENSE(“GPL”); module_init(thread_init); module_exit(thread_cleanup);

Save the file as spinlock_example.c

Makefile required for compilation.

ifneq ($(KERNELRELEASE),) obj-m := spinlock_example.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

Compile and load the file

$ make $ insmod spinlock_example.ko

To check if second thread was able to get the lock run the command dmesg

$ dmesg Unable to hold lock

Thus we can see that the spin_trylock faile to acquire the lock and returned the error message. Instead of spin_trylock if we use spin_lock, the second thread will wait on the lock as long as the thread does not become available.

Note: Do not try changing the code from spin_trylock to spin_lock if you have a uniprocessor system. It might freeze your system and force you to do hard reboot.

Category: Linux | Comments Off on Using Spinlock in linux : Example

pipe.c of ldd3 modified for 3.x.x kernel version

Here is the pipe.c code of scull, which is a part of Linux Device Drivers by Jonathan Corbet, modified for the kernel version 3.x.x. The code has been tested on 3.7.5 /* * pipe.c — fifo driver for scull * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O’Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book “Linux Device * Drivers” by Alessandro Rubini and Jonathan Corbet, published * by O’Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * */ #include #include #include /* printk(), min() */ #include /* kmalloc() */ #include /* everything… */ #include #include /* error codes */ #include /* size_t */ #include #include #include #include #include #include “scull.h” /* local definitions */ struct scull_pipe { wait_queue_head_t inq, outq; /* read and write queues */ char *buffer, *end; /* begin of buf, end of buf */ int buffersize; /* used in pointer arithmetic */ char *rp, *wp; /* where to read, where to write */ int nreaders, nwriters; /* number of openings for r/w */ struct fasync_struct *async_queue; /* asynchronous readers */ struct semaphore sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ }; /* parameters */ static int scull_p_nr_devs = SCULL_P_NR_DEVS; /* number of pipe devices */ int scull_p_buffer = SCULL_P_BUFFER; /* buffer size */ dev_t scull_p_devno; /* Our first device number */ module_param(scull_p_nr_devs, int, 0); /* FIXME check perms */ module_param(scull_p_buffer, int, 0); static struct scull_pipe *scull_p_devices; static int scull_p_fasync(int fd, struct file *filp, int mode); static int spacefree(struct scull_pipe *dev); /* * Open and close */ static int scull_p_open(struct inode *inode, struct file *filp) { struct scull_pipe *dev; dev = container_of(inode->i_cdev, struct scull_pipe, cdev); filp->private_data = dev; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (!dev->buffer) { /* allocate the buffer */ dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL); if (!dev->buffer) { up(&dev->sem); return -ENOMEM; } } dev->buffersize = scull_p_buffer; dev->end = dev->buffer + dev->buffersize; dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */ /* use f_mode,not f_flags: it’s cleaner (fs/open.c tells why) */ if (filp->f_mode & FMODE_READ) dev->nreaders++; if (filp->f_mode & FMODE_WRITE) dev->nwriters++; up(&dev->sem); return nonseekable_open(inode, filp); } static int scull_p_release(struct inode *inode, struct file *filp) { struct scull_pipe *dev = filp->private_data; /* remove this filp from the asynchronously notified filp’s */ scull_p_fasync(-1, filp, 0); down(&dev->sem); if (filp->f_mode & FMODE_READ) dev->nreaders–; if (filp->f_mode & FMODE_WRITE) dev->nwriters–; if (dev->nreaders + dev->nwriters == 0) { kfree(dev->buffer); dev->buffer = NULL; /* the other fields are not checked on open */ } up(&dev->sem); return 0; } /* * Data management: read and write */ static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_pipe *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; while (dev->rp == dev->wp) { /* nothing to read */ up(&dev->sem); /* release the lock */ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; PDEBUG(“\”%s\” reading: going to sleep\n”, current->comm); if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } /* ok, data is there, return something */ if (dev->wp > dev->rp) count = min(count, (size_t)(dev->wp – dev->rp)); else /* the write pointer has wrapped, return data up to dev->end */ count = min(count, (size_t)(dev->end – dev->rp)); if (copy_to_user(buf, dev->rp, count)) { up (&dev->sem); return -EFAULT; } dev->rp += count; if (dev->rp == dev->end) dev->rp = dev->buffer; /* wrapped */ up (&dev->sem); /* finally, awake any writers and return */ wake_up_interruptible(&dev->outq); PDEBUG(“\”%s\” did read %li bytes\n”,current->comm, (long)count); return count; } /* Wait for space for writing; caller must hold device semaphore. On * error the semaphore will be released before returning. */ static int scull_getwritespace(struct scull_pipe *dev, struct file *filp) { while (spacefree(dev) == 0) { /* full */ DEFINE_WAIT(wait); up(&dev->sem); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; PDEBUG(“\”%s\” writing: going to sleep\n”,current->comm); prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE); if (spacefree(dev) == 0) schedule(); finish_wait(&dev->outq, &wait); if (signal_pending(current)) return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } return 0; } /* How much space is free? */ static int spacefree(struct scull_pipe *dev) { if (dev->rp == dev->wp) return dev->buffersize – 1; return ((dev->rp + dev->buffersize – dev->wp) % dev->buffersize) – 1; } static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scull_pipe *dev = filp->private_data; int result; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* Make sure there’s space to write */ result = scull_getwritespace(dev, filp); if (result) return result; /* scull_getwritespace called up(&dev->sem) */ /* ok, space is there, accept something */ count = min(count, (size_t)spacefree(dev)); if (dev->wp >= dev->rp) count = min(count, (size_t)(dev->end – dev->wp)); /* to end-of-buf */ else /* the write pointer has wrapped, fill up to rp-1 */ count = min(count, (size_t)(dev->rp – dev->wp – 1)); PDEBUG(“Going to accept %li bytes to %p from %p\n”, (long)count, dev->wp, buf); if (copy_from_user(dev->wp, buf, count)) { up (&dev->sem); return -EFAULT; } dev->wp += count; if (dev->wp == dev->end) dev->wp = dev->buffer; /* wrapped */ up(&dev->sem); /* finally, awake any reader */ wake_up_interruptible(&dev->inq); /* blocked in read() and select() */ /* and signal asynchronous readers, explained late in chapter 5 */ if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); PDEBUG(“\”%s\” did write %li bytes\n”,current->comm, (long)count); return count; } static unsigned int scull_p_poll(struct file *filp, poll_table *wait) { struct scull_pipe *dev = filp->private_data; unsigned int mask = 0; /* * The buffer is circular; it is considered full * if “wp” is right behind “rp” and empty if the * two are equal. */ down(&dev->sem); poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if (dev->rp != dev->wp) mask |= POLLIN | POLLRDNORM; /* readable */ if (spacefree(dev)) mask |= POLLOUT | POLLWRNORM; /* writable */ up(&dev->sem); return mask; } static int scull_p_fasync(int fd, struct file *filp, int mode) { struct scull_pipe *dev = filp->private_data; return fasync_helper(fd, filp, mode, &dev->async_queue); } /* FIXME this should use seq_file */ #ifdef SCULL_DEBUG static void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len) { if (*offset == 0) return; if (*offset >= *len) { /* Not there yet */ *offset -= *len; *len = 0; } else { /* We’re into the interesting stuff now */ *start = buf + *offset; *offset = 0; } } static int scull_read_p_mem(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int i, len; struct scull_pipe *p; #define LIMIT (PAGE_SIZE-200) /* don’t print any more after this size */ *start = buf; len = sprintf(buf, “Default buffersize is %i\n”, scull_p_buffer); for(i = 0; iinq, p->outq);*/ len += sprintf(buf+len, ” Buffer: %p to %p (%i bytes)\n”, p->buffer, p->end, p->buffersize); len += sprintf(buf+len, ” rp %p wp %p\n”, p->rp, p->wp); len += sprintf(buf+len, ” readers %i writers %i\n”, p->nreaders, p->nwriters); up(&p->sem); scullp_proc_offset(buf, start, &offset, &len); } *eof = (len cdev, &scull_pipe_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE “Error %d adding scullpipe%d”, err, index); } /* * Initialize the pipe devs; return how many we did. */ int scull_p_init(dev_t firstdev) { int i, result; result = register_chrdev_region(firstdev, scull_p_nr_devs, “scullp”); if (result < 0) { printk(KERN_NOTICE "Unable to get scullp region, error %d\n", result); return 0; } scull_p_devno = firstdev; scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe), GFP_KERNEL); if (scull_p_devices == NULL) { unregister_chrdev_region(firstdev, scull_p_nr_devs); return 0; } memset(scull_p_devices, 0, scull_p_nr_devs * sizeof(struct scull_pipe)); for (i = 0; i < scull_p_nr_devs; i++) { init_waitqueue_head(&(scull_p_devices[i].inq)); init_waitqueue_head(&(scull_p_devices[i].outq)); sema_init(&scull_p_devices[i].sem,1); scull_p_setup_cdev(scull_p_devices + i, i); } #ifdef SCULL_DEBUG create_proc_read_entry("scullpipe", 0, NULL, scull_read_p_mem, NULL); #endif return scull_p_nr_devs; } /* * This is called by cleanup_module or on failure. * It is required to never fail, even if nothing was initialized first */ void scull_p_cleanup(void) { int i; #ifdef SCULL_DEBUG remove_proc_entry("scullpipe", NULL); #endif if (!scull_p_devices) return; /* nothing else to release */ for (i = 0; i < scull_p_nr_devs; i++) { cdev_del(&scull_p_devices[i].cdev); kfree(scull_p_devices[i].buffer); } kfree(scull_p_devices); unregister_chrdev_region(scull_p_devno, scull_p_nr_devs); scull_p_devices = NULL; /* pedantic */ }

Category: Linux | Comments Off on pipe.c of ldd3 modified for 3.x.x kernel version

access.c of ldd3 modified for 3.x.x kernel version

Here is the access.c code of scull, which is a part of Linux Device Drivers by Jonathan Corbet, modified for the kernel version 3.x.x. The code has been tested on 3.7.5 /* * access.c — the files with access control on open * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O’Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book “Linux Device * Drivers” by Alessandro Rubini and Jonathan Corbet, published * by O’Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: access.c,v 1.17 2004/09/26 07:29:56 gregkh Exp $ */ /* FIXME: cloned devices as a use for kobjects? */ #include /* printk() */ #include #include /* kmalloc() */ #include /* everything… */ #include /* error codes */ #include /* size_t */ #include #include #include #include #include #include #include “scull.h” /* local definitions */ static dev_t scull_a_firstdev; /* Where our range begins */ /* * These devices fall back on the main scull operations. They only * differ in the implementation of open() and close() */ /************************************************************************ * * The first device is the single-open one, * it has an hw structure and an open count */ static struct scull_dev scull_s_device; static atomic_t scull_s_available = ATOMIC_INIT(1); static int scull_s_open(struct inode *inode, struct file *filp) { struct scull_dev *dev = &scull_s_device; /* device information */ if (! atomic_dec_and_test (&scull_s_available)) { atomic_inc(&scull_s_available); return -EBUSY; /* already open */ } /* then, everything else is copied from the bare scull device */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) scull_trim(dev); filp->private_data = dev; return 0; /* success */ } static int scull_s_release(struct inode *inode, struct file *filp) { atomic_inc(&scull_s_available); /* release the device */ return 0; } /* * The other operations for the single-open device come from the bare device */ struct file_operations scull_sngl_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_s_open, .release = scull_s_release, }; /************************************************************************ * * Next, the “uid” device. It can be opened multiple times by the * same user, but access is denied to other users if the device is open */ static struct scull_dev scull_u_device; static int scull_u_count; /* initialized to 0 by default */ static uid_t scull_u_owner; /* initialized to 0 by default */ static spinlock_t scull_u_lock; static int scull_u_open(struct inode *inode, struct file *filp) { struct scull_dev *dev = &scull_u_device; /* device information */ spin_lock(&scull_u_lock); printk(KERN_INFO “%d”,current_uid()); if (scull_u_count && (scull_u_owner != current_uid()) && /* allow user */ (scull_u_owner != current_euid()) && /* allow whoever did su */ !capable(CAP_DAC_OVERRIDE)) { /* still allow root */ spin_unlock(&scull_u_lock); return -EBUSY; /* -EPERM would confuse the user */ } if (scull_u_count == 0) scull_u_owner = current_uid(); /* grab it */ scull_u_count++; spin_unlock(&scull_u_lock); /* then, everything else is copied from the bare scull device */ if ((filp->f_flags & O_ACCMODE) == O_WRONLY) scull_trim(dev); filp->private_data = dev; return 0; /* success */ } static int scull_u_release(struct inode *inode, struct file *filp) { spin_lock(&scull_u_lock); scull_u_count–; /* nothing else */ spin_unlock(&scull_u_lock); return 0; } /* * The other operations for the device come from the bare device */ struct file_operations scull_user_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_u_open, .release = scull_u_release, }; /************************************************************************ * * Next, the device with blocking-open based on uid */ static struct scull_dev scull_w_device; static int scull_w_count; /* initialized to 0 by default */ static uid_t scull_w_owner; /* initialized to 0 by default */ static DECLARE_WAIT_QUEUE_HEAD(scull_w_wait); static spinlock_t scull_w_lock;// = SPIN_LOCK_UNLOCKED; static inline int scull_w_available(void) { return scull_w_count == 0 || scull_w_owner == current_uid() || scull_w_owner == current_euid() || capable(CAP_DAC_OVERRIDE); } static int scull_w_open(struct inode *inode, struct file *filp) { struct scull_dev *dev = &scull_w_device; /* device information */ spin_lock(&scull_w_lock); while (! scull_w_available()) { spin_unlock(&scull_w_lock); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible (scull_w_wait, scull_w_available())) return -ERESTARTSYS; /* tell the fs layer to handle it */ spin_lock(&scull_w_lock); } if (scull_w_count == 0) scull_w_owner = current_uid(); /* grab it */ scull_w_count++; spin_unlock(&scull_w_lock); /* then, everything else is copied from the bare scull device */ if ((filp->f_flags & O_ACCMODE) == O_WRONLY) scull_trim(dev); filp->private_data = dev; return 0; /* success */ } static int scull_w_release(struct inode *inode, struct file *filp) { int temp; spin_lock(&scull_w_lock); scull_w_count–; temp = scull_w_count; spin_unlock(&scull_w_lock); if (temp == 0) wake_up_interruptible_sync(&scull_w_wait); /* awake other uid’s */ return 0; } /* * The other operations for the device come from the bare device */ struct file_operations scull_wusr_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_w_open, .release = scull_w_release, }; /************************************************************************ * * Finally the `cloned’ private device. This is trickier because it * involves list management, and dynamic allocation. */ /* The clone-specific data structure includes a key field */ struct scull_listitem { struct scull_dev device; dev_t key; struct list_head list; }; /* The list of devices, and a lock to protect it */ static LIST_HEAD(scull_c_list); static spinlock_t scull_c_lock;// = SPIN_LOCK_UNLOCKED; /* A placeholder scull_dev which really just holds the cdev stuff. */ static struct scull_dev scull_c_device; /* Look for a device or create one if missing */ static struct scull_dev *scull_c_lookfor_device(dev_t key) { struct scull_listitem *lptr; list_for_each_entry(lptr, &scull_c_list, list) { if (lptr->key == key) return &(lptr->device); } /* not found */ lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL); if (!lptr) return NULL; /* initialize the device */ memset(lptr, 0, sizeof(struct scull_listitem)); lptr->key = key; scull_trim(&(lptr->device)); /* initialize it */ sema_init(&lptr->device.sem,1); /* place it in the list */ list_add(&lptr->list, &scull_c_list); return &(lptr->device); } static int scull_c_open(struct inode *inode, struct file *filp) { struct scull_dev *dev; dev_t key; if (!current->signal->tty) { PDEBUG(“Process \”%s\” has no ctl tty\n”, current->comm); return -EINVAL; } key = tty_devnum(current->signal->tty); /* look for a scullc device in the list */ spin_lock(&scull_c_lock); dev = scull_c_lookfor_device(key); spin_unlock(&scull_c_lock); if (!dev) return -ENOMEM; /* then, everything else is copied from the bare scull device */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) scull_trim(dev); filp->private_data = dev; return 0; /* success */ } static int scull_c_release(struct inode *inode, struct file *filp) { /* * Nothing to do, because the device is persistent. * A `real’ cloned device should be freed on last close */ return 0; } /* * The other operations for the device come from the bare device */ struct file_operations scull_priv_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_c_open, .release = scull_c_release, }; /************************************************************************ * * And the init and cleanup functions come last */ static struct scull_adev_info { char *name; struct scull_dev *sculldev; struct file_operations *fops; } scull_access_devs[] = { { “scullsingle”, &scull_s_device, &scull_sngl_fops }, { “sculluid”, &scull_u_device, &scull_user_fops }, { “scullwuid”, &scull_w_device, &scull_wusr_fops }, { “sullpriv”, &scull_c_device, &scull_priv_fops } }; #define SCULL_N_ADEVS 4 /* * Set up a single device. */ static void scull_access_setup (dev_t devno, struct scull_adev_info *devinfo) { struct scull_dev *dev = devinfo->sculldev; int err; /* Initialize the device structure */ dev->quantum = scull_quantum; dev->qset = scull_qset; sema_init(&dev->sem,1); /* Do the cdev stuff. */ cdev_init(&dev->cdev, devinfo->fops); kobject_set_name(&dev->cdev.kobj, devinfo->name); dev->cdev.owner = THIS_MODULE; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) { printk(KERN_NOTICE “Error %d adding %s\n”, err, devinfo->name); kobject_put(&dev->cdev.kobj); } else printk(KERN_NOTICE “%s registered at %x\n”, devinfo->name, devno); } int scull_access_init(dev_t firstdev) { int result, i; /* Get our number space */ result = register_chrdev_region (firstdev, SCULL_N_ADEVS, “sculla”); if (result < 0) { printk(KERN_WARNING "sculla: device number registration failed\n"); return 0; } scull_a_firstdev = firstdev; /* Set up each device. */ for (i = 0; i < SCULL_N_ADEVS; i++) scull_access_setup (firstdev + i, scull_access_devs + i); return SCULL_N_ADEVS; } /* * This is called by cleanup_module or on failure. * It is required to never fail, even if nothing was initialized first */ void scull_access_cleanup(void) { struct scull_listitem *lptr, *next; int i; /* Clean up the static devs */ for (i = 0; i < SCULL_N_ADEVS; i++) { struct scull_dev *dev = scull_access_devs[i].sculldev; cdev_del(&dev->cdev); scull_trim(scull_access_devs[i].sculldev); } /* And all the cloned devices */ list_for_each_entry_safe(lptr, next, &scull_c_list, list) { list_del(&lptr->list); scull_trim(&(lptr->device)); kfree(lptr); } /* Free up our number space */ unregister_chrdev_region(scull_a_firstdev, SCULL_N_ADEVS); return; }

Category: Linux | Comments Off on access.c of ldd3 modified for 3.x.x kernel version

main.c of ldd3 modified for 3.x.x kernel version

Here is the main.c code of scull, which is a part of Linux Device Drivers by Jonathan Corbet, modified for the kernel version 3.x.x. The code has been tested on 3.7.5 /* * main.c — the bare scull char module * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O’Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book “Linux Device * Drivers” by Alessandro Rubini and Jonathan Corbet, published * by O’Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * */ #include #include #include #include /* printk() */ #include /* kmalloc() */ #include /* everything… */ #include /* error codes */ #include /* size_t */ #include #include /* O_ACCMODE */ #include #include #include /* copy_*_user */ #include “scull.h” /* local definitions */ #define SUCLL_DEBUG 1 /* * Our parameters which can be set at load time. */ int scull_major = SCULL_MAJOR; int scull_minor = 0; int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */ int scull_quantum = SCULL_QUANTUM; int scull_qset = SCULL_QSET; module_param(scull_major, int, S_IRUGO); module_param(scull_minor, int, S_IRUGO); module_param(scull_nr_devs, int, S_IRUGO); module_param(scull_quantum, int, S_IRUGO); module_param(scull_qset, int, S_IRUGO); MODULE_AUTHOR(“Alessandro Rubini, Jonathan Corbet”); MODULE_LICENSE(“Dual BSD/GPL”); struct scull_dev *scull_devices; /* allocated in scull_init_module */ /* * Empty out the scull device; must be called with the device * semaphore held. */ int scull_trim(struct scull_dev *dev) { struct scull_qset *next, *dptr; int qset = dev->qset; /* “dev” is not-null */ int i; for (dptr = dev->data; dptr; dptr = next) { /* all the list items */ if (dptr->data) { for (i = 0; i < qset; i++) kfree(dptr->data[i]); kfree(dptr->data); dptr->data = NULL; } next = dptr->next; kfree(dptr); } dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; dev->data = NULL; return 0; } #ifdef SCULL_DEBUG /* use proc only if debugging */ /* * The proc filesystem: function to read and entry */ int scull_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int i, j, len = 0; int limit = count – 80; /* Don’t print more than this */ char *info; struct scull_dev *pr=&scull_devices[0]; struct scull_qset *prd = pr->data; for (i = 0; i < scull_nr_devs && len data; if (down_interruptible(&d->sem)) return -ERESTARTSYS; len += sprintf(buf+len,”\nDevice %i: qset %i, q %i, sz %li\n”, i, d->qset, d->quantum, d->size); info = prd->data[0]; printk(KERN_INFO “in proc %s”,info); for (; qs && len next) { /* scan the list */ len += sprintf(buf + len, ” item at %p, qset at %p\n”, qs, qs->data); if (qs->data && !qs->next) /* dump only the last item */ for (j = 0; j < d->qset; j++) { if (qs->data[j]) len += sprintf(buf + len, ” % 4i: %8p\n”, j, qs->data[j]); } } up(&scull_devices[i].sem); } *eof = 1; return len; } /* * For now, the seq_file implementation will exist in parallel. The * older read_procmem function should maybe go away, though. */ /* * Here are our sequence iteration methods. Our “position” is * simply the device number. */ static void *scull_seq_start(struct seq_file *s, loff_t *pos) { if (*pos >= scull_nr_devs) return NULL; /* No more to read */ return scull_devices + *pos; } static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; if (*pos >= scull_nr_devs) return NULL; return scull_devices + *pos; } static void scull_seq_stop(struct seq_file *s, void *v) { /* Actually, there’s nothing to do here */ } static int scull_seq_show(struct seq_file *s, void *v) { struct scull_dev *dev = (struct scull_dev *) v; struct scull_qset *d; int i; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; seq_printf(s, “\nDevice %i: qset %i, q %i, sz %li\n”, (int) (dev – scull_devices), dev->qset, dev->quantum, dev->size); for (d = dev->data; d; d = d->next) { /* scan the list */ seq_printf(s, ” item at %p, qset at %p\n”, d, d->data); if (d->data && !d->next) /* dump only the last item */ for (i = 0; i < dev->qset; i++) { if (d->data[i]) seq_printf(s, ” % 4i: %8p\n”, i, d->data[i]); } } up(&dev->sem); return 0; } /* * Tie the sequence operators up. */ static struct seq_operations scull_seq_ops = { .start = scull_seq_start, .next = scull_seq_next, .stop = scull_seq_stop, .show = scull_seq_show }; /* * Now to implement the /proc file we need only make an open * method which sets up the sequence operators. */ static int scull_proc_open(struct inode *inode, struct file *file) { return seq_open(file, &scull_seq_ops); } /* * Create a set of file operations for our proc file. */ static struct file_operations scull_proc_ops = { .owner = THIS_MODULE, .open = scull_proc_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release }; /* * Actually create (and remove) the /proc file(s). */ static void scull_create_proc(void) { struct proc_dir_entry *entry; create_proc_read_entry(“scullmem”, 0 /* default mode */, NULL /* parent dir */, scull_read_procmem, NULL /* client data */); entry = create_proc_entry(“scullseq”, 0, NULL); if (entry) entry->proc_fops = &scull_proc_ops; } static void scull_remove_proc(void) { /* no problem if it was not registered */ remove_proc_entry(“scullmem”, NULL /* parent dir */); remove_proc_entry(“scullseq”, NULL); } #endif /* SCULL_DEBUG */ /* * Open and close */ int scull_open(struct inode *inode, struct file *filp) { struct scull_dev *dev; /* device information */ dev = container_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; /* for other methods */ /* now trim to 0 the length of the device if open was write-only */ if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { if (down_interruptible(&dev->sem)) return -ERESTARTSYS; scull_trim(dev); /* ignore errors */ up(&dev->sem); } return 0; /* success */ } int scull_release(struct inode *inode, struct file *filp) { return 0; } /* * Follow the list */ struct scull_qset *scull_follow(struct scull_dev *dev, int n) { struct scull_qset *qs = dev->data; /* Allocate first qset explicitly if need be */ if (! qs) { qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs == NULL) return NULL; /* Never mind */ memset(qs, 0, sizeof(struct scull_qset)); } /* Then follow the list */ while (n–) { if (!qs->next) { qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs->next == NULL) return NULL; /* Never mind */ memset(qs->next, 0, sizeof(struct scull_qset)); } qs = qs->next; continue; } return qs; } /* * Data management: read and write */ ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; /* the first listitem */ int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; /* how many bytes in the listitem */ int item, s_pos, q_pos, rest; ssize_t retval = 0; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos >= dev->size) goto out; if (*f_pos + count > dev->size) count = dev->size – *f_pos; /* find listitem, qset index, and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position (defined elsewhere) */ dptr = scull_follow(dev, item); if (dptr == NULL || !dptr->data || ! dptr->data[s_pos]) goto out; /* don’t fill holes */ /* read only up to the end of this quantum */ if (count > quantum – q_pos) count = quantum – q_pos; if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; out: printk(KERN_INFO “ret = %d”,retval); up(&dev->sem); return retval; } ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev = filp->private_data; struct scull_qset *dptr; int quantum = dev->quantum, qset = dev->qset; int itemsize = quantum * qset; int item, s_pos, q_pos, rest; ssize_t retval = -ENOMEM; /* value used in “goto out” statements */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* find listitem, qset index and offset in the quantum */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* follow the list up to the right position */ dptr = scull_follow(dev, item); if (dptr == NULL) goto out; if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dptr->data) goto out; memset(dptr->data, 0, qset * sizeof(char *)); } if (!dptr->data[s_pos]) { dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dptr->data[s_pos]) goto out; } /* write only up to the end of this quantum */ if (count > quantum – q_pos) count = quantum – q_pos; if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { retval = -EFAULT; goto out; } *f_pos += count; retval = count; /* update the size */ if (dev->size < *f_pos) dev->size = *f_pos; printk(KERN_INFO “ret = %d”,retval); out: up(&dev->sem); return retval; } /* * The ioctl() implementation */ long scull_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) { int err = 0, tmp; int retval = 0; struct scull_dev *sr=&scull_devices[0]; struct scull_qset *srd=sr->data; char *info; /* * extract the type and number bitfields, and don’t decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type’ is user-oriented, while * access_ok is kernel-oriented, so the concept of “read” and * “write” is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; switch(cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_quantum = arg; break; case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */ retval = __put_user(scull_quantum, (int __user *)arg); break; case SCULL_IOCQQUANTUM: /* Query: return it (it’s positive) */ return scull_quantum; case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; retval = __get_user(scull_quantum, (int __user *)arg); if (retval == 0) retval = __put_user(tmp, (int __user *)arg); break; case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_quantum; scull_quantum = arg; return tmp; case SCULL_IOCSQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; retval = __get_user(scull_qset, (int __user *)arg); break; case SCULL_IOCTQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; scull_qset = arg; break; case SCULL_IOCGQSET: retval = __put_user(scull_qset, (int __user *)arg); break; case SCULL_IOCQQSET: return scull_qset; case SCULL_IOCXQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_qset; retval = __get_user(scull_qset, (int __user *)arg); if (retval == 0) retval = put_user(tmp, (int __user *)arg); break; case SCULL_IOCHQSET: if (! capable (CAP_SYS_ADMIN)) return -EPERM; tmp = scull_qset; scull_qset = arg; return tmp; /* * The following two change the buffer size for scullpipe. * The scullpipe device uses this same ioctl method, just to * write less code. Actually, it’s the same driver, isn’t it? */ case SCULL_P_IOCTSIZE: scull_p_buffer = arg; break; case SCULL_P_IOCQSIZE: return scull_p_buffer; break; case SCULL_SORT: printk(KERN_INFO “entering sort”); retval = __get_user(tmp,(int __user *)arg); info = srd->data[tmp]; printk(KERN_INFO “in case %s”,info); break; default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return retval; } /* * The “extended” operations — only seek */ loff_t scull_llseek(struct file *filp, loff_t off, int whence) { struct scull_dev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = dev->size + off; break; default: /* can’t happen */ return -EINVAL; } if (newpos < 0) return -EINVAL; filp->f_pos = newpos; return newpos; } struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = scull_llseek, .read = scull_read, .write = scull_write, .unlocked_ioctl = scull_ioctl, .open = scull_open, .release = scull_release, }; /* * Finally, the module stuff */ /* * The cleanup function is used to handle initialization failures as well. * Thefore, it must be careful to work correctly even if some of the items * have not been initialized */ void scull_cleanup_module(void) { int i; dev_t devno = MKDEV(scull_major, scull_minor); /* Get rid of our char dev entries */ if (scull_devices) { for (i = 0; i < scull_nr_devs; i++) { scull_trim(scull_devices + i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); } #ifdef SCULL_DEBUG /* use proc only if debugging */ scull_remove_proc(); #endif /* cleanup_module is never called if registering failed */ unregister_chrdev_region(devno, scull_nr_devs); /* and call the cleanup functions for friend devices */ scull_p_cleanup(); scull_access_cleanup(); } /* * Set up the char_dev structure for this device. */ static void scull_setup_cdev(struct scull_dev *dev, int index) { int err, devno = MKDEV(scull_major, scull_minor + index); cdev_init(&dev->cdev, &scull_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &scull_fops; err = cdev_add (&dev->cdev, devno, 1); /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE “Error %d adding scull%d”, err, index); } int scull_init_module(void) { int result, i; dev_t dev = 0; /* * Get a range of minor numbers to work with, asking for a dynamic * major unless directed otherwise at load time. */ if (scull_major) { dev = MKDEV(scull_major, scull_minor); result = register_chrdev_region(dev, scull_nr_devs, “scull”); } else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, “scull”); scull_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "scull: can't get major %d\n", scull_major); return result; } /* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); if (!scull_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev)); /* Initialize each device. */ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; scull_devices[i].qset = scull_qset; sema_init(&scull_devices[i].sem,1); scull_setup_cdev(&scull_devices[i], i); } /* At this point call the init function for any friend device */ dev = MKDEV(scull_major, scull_minor + scull_nr_devs); dev += scull_p_init(dev); dev += scull_access_init(dev); #ifdef SCULL_DEBUG /* only when debugging */ scull_create_proc(); #endif return 0; /* succeed */ fail: scull_cleanup_module(); return result; } module_init(scull_init_module); module_exit(scull_cleanup_module);

Category: Linux | Comments Off on main.c of ldd3 modified for 3.x.x kernel version

kbleds.c for linux kernel version 3.7.5

The example of blinking led given at “http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html#AEN1201 ” works fine for kernel versions before 2.6.36, but for version above that it fails to compile due the changes in the structure vc_data and ioctl call of tty_driver.

Here is the modified code tested and working on 3.7.5.

The changes are

1. change the the vc_tty pointer to port.tty

vc_cons[fg_console].d->vc_tty to vc_cons[fg_console].d->port.tty

2. Remove the argument “NULL” being passed to the ioctl call of tty driver.

(my_driver->ops->ioctl) (vc_cons[fg_console].d->port.tty, NULL, KDSETLED,*pstatus); to (my_driver->ops->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED, *pstatus);

Full code:

/* * kbleds.c − Blink keyboard leds until the module is unloaded.(modified for 3.7.5) */ #include #include #include #include /* For fg_console, MAX_NR_CONSOLES */ #include /* For KDSETLED */ #include #include /* For vc_cons */ #include MODULE_DESCRIPTION(“Example module illustrating the use of Keyboard LEDs.”); MODULE_LICENSE(“GPL”); struct timer_list my_timer; struct tty_driver *my_driver; char kbledstatus = 0; #define BLINK_DELAY HZ/5 #define ALL_LEDS_ON 0x07 #define RESTORE_LEDS 0xFF /* * Function my_timer_func blinks the keyboard LEDs periodically by invoking * command KDSETLED of ioctl() on the keyboard driver. To learn more on virtual * terminal ioctl operations, please see file: * /usr/src/linux/drivers/char/vt_ioctl.c, function vt_ioctl(). * * The argument to KDSETLED is alternatively set to 7 (thus causing the led * mode to be set to LED_SHOW_IOCTL, and all the leds are lit) and to 0xFF * (any value above 7 switches back the led mode to LED_SHOW_FLAGS, thus * the LEDs reflect the actual keyboard status). To learn more on this, * please see file: * /usr/src/linux/drivers/char/keyboard.c, function setledstate(). * */ static void my_timer_func(unsigned long ptr) { int *pstatus = (int *)ptr; if (*pstatus == ALL_LEDS_ON) *pstatus = RESTORE_LEDS; else *pstatus = ALL_LEDS_ON; (my_driver->ops->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED, *pstatus); my_timer.expires = jiffies + BLINK_DELAY; add_timer(&my_timer); } static int __init kbleds_init(void) { int i; printk(KERN_INFO “kbleds: loading\n”); printk(KERN_INFO “kbleds: fgconsole is %x\n”, fg_console); for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons[i].d) break; printk(KERN_INFO "poet_atkm: console[%i/%i] #%i, tty %lx\n", i, MAX_NR_CONSOLES, vc_cons[i].d->vc_num, (unsigned long)vc_cons[i].d->port.tty); } printk(KERN_INFO “kbleds: finished scanning consoles\n”); my_driver = vc_cons[fg_console].d->port.tty->driver; printk(KERN_INFO “kbleds: tty driver magic %x\n”, my_driver->magic); /* * Set up the LED blink timer the first time */ init_timer(&my_timer); my_timer.function = my_timer_func; my_timer.data = (unsigned long)&kbledstatus; my_timer.expires = jiffies + BLINK_DELAY; add_timer(&my_timer); return 0; } static void __exit kbleds_cleanup(void) { printk(KERN_INFO “kbleds: unloading…\n”); del_timer(&my_timer); (my_driver->ops->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED, RESTORE_LEDS); } module_init(kbleds_init); module_exit(kbleds_cleanup);

Now save the file as kbleds.c

Compile it using the makefile

obj-m += kbleds.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

Insert it into the kernel using insmod

$ insmod kbleds.ko

The key board leds should start to blink as soon as the module is inserted and will continue to blink as long as we do not remove the module using rmmmod

$ rmmmod kbleds

or reboot the system.

Category: Linux | Comments Off on kbleds.c for linux kernel version 3.7.5

Implementing an ioctl call (for kernel versions above 2.6.39)

In the post “Creating an ioctl command in linux ” we saw the basics of ioctl, and how we can create our own ioctl commands. The functions used to create ioctls have undergone a change from the kernel version 2.6.39,and the previous functions are no longer valid.

Before 2.6.39, the ioctl calls would lock the BKL (Big kernel lock) and then execute the required functions. This was unnecessary with the new fine grained locking available in the kernel. Thus to improve the implementation of ioctl, a new operation called unlocked_ioctl was introduced and all the existing ioctls were migrated to this new implementation.

Thus stating 2.6.39 the older implementation is no longer available.

The changes required in implementation of the ioctl are.

The function in the driver has changed from

int ioctl_funcs(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)

long ioctl(struct file *filp,unsigned int cmd, unsigned long arg)

The inode is no longer passed as an argument.
The fops uses the name unlocked_ioctl

struct file_operations fops = { open: open, unlocked_ioctl: ioctl_funcs, release: release };

The basic working of ioctl remains same.
Here is a code that implements the above mentioned changes if the kernel version is more than 2.6.39, else uses the older interface.

Module: ioctl_basic.c

#include #include #include // required for various structures related to files liked fops. #include #include #include “ioctl_basic.h” #include static int Major; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) #define UNLOCKED 1 #endif int open(struct inode *inode, struct file *filp) { printk(KERN_INFO “Inside open \n”); return 0; } int release(struct inode *inode, struct file *filp) { printk (KERN_INFO “Inside close \n”); return 0; } #ifdef UNLOCKED long ioctl_funcs(struct file *filp,unsigned int cmd, unsigned long arg) { int ret=0; switch(cmd) { case IOCTL_HELLO: printk(KERN_INFO “Hello ioctl world”); break; } return ret; } struct file_operations fops = { open: open, unlocked_ioctl: ioctl_funcs, release: release }; #else int ioctl_funcs(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int data=10,ret; switch(cmd) { case IOCTL_HELLO: printk(KERN_INFO “Hello ioctl world”); break; } return ret; } struct file_operations fops = { open: open, ioctl: ioctl_funcs, release: release }; #endif struct cdev *kernel_cdev; int char_arr_init (void) { int ret; dev_t dev_no,dev; kernel_cdev = cdev_alloc(); kernel_cdev->ops = &fops; kernel_cdev->owner = THIS_MODULE; printk (” Inside init module\n”); ret = alloc_chrdev_region( &dev_no , 0, 1,”char_arr_dev”); if (ret < 0) { printk("Major number allocation is failed\n"); return ret; } Major = MAJOR(dev_no); dev = MKDEV(Major,0); printk (" The major number for your device is %d\n", Major); ret = cdev_add( kernel_cdev,dev,1); if(ret < 0 ) { printk(KERN_INFO "Unable to allocate cdev"); return ret; } return 0; } void char_arr_cleanup(void) { printk(KERN_INFO " Inside cleanup_module\n"); cdev_del(kernel_cdev); unregister_chrdev_region(Major, 1); } MODULE_LICENSE("GPL"); module_init(char_arr_init); module_exit(char_arr_cleanup); The steps of compiling and testing the module are same as shown in the post “Creating an ioctl command in linux ”

Category: Linux | Comments Off on Implementing an ioctl call (for kernel versions above 2.6.39)

using printk_ratelimit

printk is used in kernel programming to log messages and is very useful while debugging the kernel programs.
But the kernel log being though big, is restricted and unnecessary logging at times can lead to loosing relevant messages .

Thus kernel provides a function, printk_ratelimit , to restrict the logging using which we can set a limit on the number of prints that we want our program to do.

The limit on the number of prints is set in the file /proc/sys/kernel/printk_ratelimit_burst

$ cat /proc/sys/kernel/printk_ratelimit_burst 10

The printk_ratelimit function will allow 10 prints before it starts blocking the further prints.
The printk_ratelimit, returns 1 as long as the number of prints do not exceed the limit. Once the limit is reached it starts returning 0.
Thus the function can be used as a condition for an “if” statement to decide whether to print a message or not.
The printk will be enabled again after a time interval in seconds mentioned in the file /proc/sys/kernel/printk_ratelimit

$ cat /proc/sys/kernel/printk_ratelimit 5

Which means, printk will be disabled for 5 seconds after 10 messages by the code which uses the printk_ratelimit function.

In the example module below we put 20 prints in the init function using a for loop.
Before each print, printk_ratelimit is called to decide whether to print or not.
When the module gets inserted, we will notice that only the first 10 prints are printed and the rest gets suppressed.

printk_limit.c :

#include #include int rate_limit_init(void) { int i; for(i=0;i

Category: Linux | Comments Off on using printk_ratelimit