Path for Linux Device Driver engineers-advanced character device drivers
K-Style
Reprinted please indicate from Hengyang Normal College 08 electric 2 K-Style http://blog.csdn.net/ayangke,QQ:843308498 mailbox: yangkeemail@qq.com
The advanced character device driver adds the ioctl method, blocking non-blocking read/write, poll method, and automatic creation of device files based on the simple character driver.
I. Important knowledge points
1. ioctl
Ioctl command: defines an ioctl command using four fields, including
Type: magic number, which is generally defined by one character and unique in the kernel.
Number: ordinal number.
Direction: data transmission direction. This field is invalid when data transmission is not involved.
Size: the size of the user data involved. This field is invalid when data transmission is not involved.
_ IOC_NONE
_ IOC_READ
_ IOC_WRITE
Possible value of the "direction" field. "Read" and "write" are different bits. You can use "OR" to specify the read/write operations.
_ IOC (dir, type, size)
_ IO (type, nr)
_ IOR (type, nr, size)
_ IOW (type, nr, size)
Macro used to produce IOCTL commands
_ Ioc_dir (CMD)
_ Ioc_type (CMD)
_ Ioc_nr (CMD)
_ Ioc_size (CMD)
Macro used to decode the ioctl command
Intaccess_ OK (INT type, const void * ADDR, unsigned Long SIZE)
This function verifies whether the pointer to the user space is available. If access is allowed, access_ OK returns a non-0 value.
Int put_user (datum, PTR)
Int get_user (local, PTR)
Int _ put_user (datum, PTR)
Int _ get_user (local, PTR)
Macro used to save (or retrieve) a single data item to (or from) the user space. The number of transmitted bytes is determined by sizeof (* PTR. The first two must call access_ OK, and the last two (_ put_user and _ get_user) are assumed that access_ OK has been called.
2. Blocked I/O
Typedef struct {/*..... */} Wait_queue_head_t
Void init_waitqueue_head (wait_queue_head_t * queue)
DECLARE_WAIT_QUEUE_HEAD (queue)
Pre-defined Linux kernel waiting queue type. The wait_queue_head_t type must be explicitly initialized. The initialization method can call init_waitqueue_head at runtime, or DECLARE_WAIT_QUEUE_HEAD at compilation.
Void wait_event (wait_queue_head_t q, intcondition)
Int wait_event_interruptible (wait_queue_head_tq, int condition)
Int wait_event_timeout (wait_queue_head_t q, int condition, int time)
Int wait_event_interruptible_timeout (wait_queue_head_tq, int condition, int time)
Sleep the process on the specified queue until the given condition value is true.
Void wake_up (struct wait_queue ** q)
Void wake_up_interruptible (structwait_queue ** q)
These functions wake up the processes that sleep on queue q. _ Interruptible functions can only wake up interrupted processes. In practice, it is agreed that wae_up is used when wait_event is used, and wake_up_interruptible is used when wait_event_interruptible is used.
3. poll Method
The poll method is processed in two steps. The first step is to call poll_wait to specify the waiting queue, and the second step returns the operable mask.
POLLIN indicates the readable mask of the device, and POLLRDORM indicates the readable mask of the data. POLLOUT indicates the device writable mask, and POLLWRNORM indicates the data readable mask. Generally, both POLLIN and POLLRDORM, POLLOUT, and POLLWRNORM are returned.
4. select system call
The prototype is intselect (int mafdp1, fd_set * restrict readfds, fd_set * restrict writefds, fd_set * restrict limit TFDs, struct timeval * restrict tvptr)
Returned value: the number of ready descriptors. If the number of ready descriptors times out, 0 is returned. If an error occurs,-1 is returned.
Void fd_isset (int fd, fd_set * fdset)
Void fd_clr (int fd, fd_set * fdset)
Void fd_set (int fd, fd_set * fdset)
Void fd_zero (fd_set * fdset)
Call fd_zero to set all bits of a specified fd_set variable to 0. Call fd_set to set an fd_set variable. Call fd_clr to clear the handler. Finally, call fd_isset to test whether or not to set the token.
5. automatically create a device file
Struct class * class_create (struct module * owner, const char * name)
Struct device * device_create (struct class * class, struct device * parent, dev_t devt, const char * FMT ,...)
These two functions can be used to create a character device file node. The first parameter "class_create" specifies the owner, and the second parameter specifies the class name. Class_device_create the first parameter specifies the class to which the device to be created belongs. The second parameter is the parent device of the device. If not, null is specified, the third parameter is the device number, and the fourth parameter is the device name.
Ii. driver code
# Include <Linux/module. h> # include <Linux/types. h> # include <Linux/Fs. h> # include <Linux/errno. h> # include <Linux/mm. h> # include <Linux/sched. h> # include <Linux/init. h> # include <Linux/cdev. h> # include <ASM/Io. h> # include <ASM/system. h> # include <ASM/uaccess. h> # include <Linux/IOCTL. h> # include <Linux/Wait. h> # include <Linux/poll. h> # include <Linux/device. h> # define memdev_major 251 # define memdev_num 2 # define me Mdev_size 1024 // define the device IOCTL command # define memdev_ioc_magic 'K' # define memdev_ioc_nr 2 # define memory (memdev_ioc_magic, 0) # define memory (memdev_ioc_magic, 1, INT) # define evaluate (memdev_ioc_magic, 2, char) struct mem_dev {unsignedint size; char * data; structsemaphore SEM; wait_queue_head_t inque ;}; static int mem_major = memdev_major; struct cdev mem_cdev; struct mem_dev * m Em_devp; bool havedata = false; static int mem_open (struct inode * inode, struct file * filp) {structmem_dev * dev; unsignedint num; printk ("mem_open. \ n "); num = minor (inode-> I _rdev); // obtain the next device number if (Num> (memdev_num-1 )) // return-enodev; Dev = & mem_devp [num]; filp-> private_data = dev; // Save the device structure as private data return0 ;} static int mem_release (struct inode * inode, struct file * filp) {printk ("mem_release. \ n "); Re Turn0;} static ssize_t mem_read (struct file * filp, char _ User * Buf, size_t size, loff_t * PPOs) {intret = 0; structmem_dev * dev; unsignedlong P; unsignedlong count; printk ("mem_read. \ n "); Dev = filp-> private_data; // obtain the device structure COUNT = size; P = * PPOs; // check the validity of offset and data size if (P> memdev_size) return0; If (count> (MEMDEV_SIZE-p) Count = memdev_size-P; if (down_interruptible (& Dev-> SEM) // lock the mutex semaphores return-erestartsys; While (! Havedata) {up (& Dev-> SEM); If (filp-> f_flags & o_nonblock) Return-eagain; printk ("readyto go sleep "); if (wait_event_interruptible (Dev-> inque, havedata) // wait for the data return-erestartsys; If (down_interruptible (& Dev-> SEM) Return-erestartsys ;} // read data to the user space if (copy_to_user (BUF, Dev-> Data + P, count) {ret =-efault; printk ("copyfrom User Failed \ n");} else {* PPOs + = count; ret = count; printk ("read % LD bytes from Dev \ n ", Count); havedata = false; // data read} Up (& Dev-> SEM); // unlock mutex semaphores returnret ;} static ssize_t mem_write (struct file * filp, const char _ User * Buf, size_t size, loff_t * PPOs) // Note: The second parameter is different from the read method {intret = 0; structmem_dev * dev; unsignedlong P; unsignedlong count; printk ("mem_write. \ n "); Dev = filp-> private_data; Count = size; P = * PPOs; If (P> memdev_size) return0; If (count> (MEMDEV_SIZE-p) Count = memdev_siz E-P; If (down_interruptible (& Dev-> SEM) // lock the mutex semaphores return-erestartsys; If (copy_from_user (Dev-> Data + P, Buf, count )) {ret =-efault; printk ("copyfrom User Failed \ n");} else {* PPOs + = count; ret = count; printk ("write % LD bytes to Dev \ n", count); havedata = true; wake_up_interruptible (& Dev-> inque ); // wake up the queue waiting for data} Up (& Dev-> SEM); // unlock the mutex semaphore returnret;} static loff_t mem_llseek (struct file * filp, loff_t offset, in T whence) {intnewpos; printk ("mem_llseek. \ n "); Switch (whence) {case0: // start from the file header newpos = offset; break; case1: // start from the current position of the file newpos = filp-> f_pos + offset; break; case2: // start from the end of the file newpos = memdev_size-1 + offset; break; default: return-einval;} If (newpos <0) | (newpos> (memdev_size-1) Return-einval; filp-> f_pos = newpos; returnnewpos ;} static int mem_ioctl (struct inode * inode, struct file * filp, unsig Ned int cmd, unsigned long Arg) {interr = 0, ret = 0; intioarg = 0; charrdarg = '0'; // check the parameter if (_ ioc_type (CMD )! = Memdev_ioc_magic) // return-enotty for parameter type check; If (_ ioc_nr (CMD)> memdev_ioc_nr) // return-enotty for parameter command number check; // check if (_ ioc_dir (CMD) & _ ioc_read) Err =! Access_ OK (verify_write, (void _ User *) Arg, _ ioc_size (CMD); elseif (_ ioc_dir (CMD) & _ ioc_write) Err =! Access_ OK (verify_write, (void _ User *) Arg, _ ioc_size (CMD); If (ERR) Return-enotty; // execute the switch (CMD) operation according to the command) {Case memdev_ioc_print: printk ("memdevictl print excuting... \ n "); break; casememdev_ioc_rd: ioarg = 1024; ret = _ put_user (ioarg, (int *) Arg ); // The user space obtains data from the kernel space printk ("memdevictl read excuting... \ n "); break; casememdev_ioc_wt: ret = _ get_user (rdarg, (char *) Arg); // The user space transmits data printk (" memdev IOCTL write excuting... arg: % C \ n ", rdarg); break; default: Return-enotty;} returnret;} static unsigned int mem_poll (struct file * filp, poll_table * Wait) {structmem_dev * dev; unsignedint mask = 0; Dev = filp-> private_data; If (down_interruptible (& Dev-> SEM) // lock the mutex semaphores return-erestartsys; poll_wait (filp, & Dev-> inque, wait); If (havedata) mask | = Pollin | pollrdnorm; // returns the readable mask up (& Dev-> SEM ); // release the semaphore returnmask ;} Static const struct file_operationsmem_fops = {. owner = this_module ,. open = mem_open ,. write = mem_write ,. read = mem_read ,. release = mem_release ,. llseek = mem_llseek ,. IOCTL = mem_ioctl ,. poll = mem_poll,}; static int _ init memdev_init (void) {intresult; interr; Inti; structclass * memdev_class; // apply for the device number dev_tdevno = mkdev (mem_major, 0 ); if (mem_major) Result = register_chrdev_region (devno, memdev_num, "Memdev"); // pay attention to the difference between the dev_t Parameter Applied For statically and the dynamic dev_t parameter else {// directly transmits the variable statically, and dynamically transmits the variable pointer result = alloc_chrdev_region (& devno, 0, memdev_num, "memdev"); mem_major = major (devno);} If (result <0) {printk ("can't get Major devno: % d \ n ", mem_major); returnresult;} // register the device driver cdev_init (& mem_cdev, & mem_fops); role = this_module; err = cdev_add (& mem_cdev, mkdev (mem_major, 0 ), memdev_num); // if there are n devices, add n device numbers if (ERR) printk ("add Cdev faild, err is % d \ n ", err); // allocate the device memory mem_devp = kmalloc (memdev_num * (sizeof (struct mem_dev), gfp_kernel); If (! Mem_devp) {result =-enomem; goto fail_malloc;} memset (mem_devp, 0, memdev_num * (sizeof (struct mem_dev); for (I = 0; I <memdev_num; I ++) {mem_devp [I]. size = memdev_size; mem_devp [I]. data = kmalloc (memdev_size, gfp_kernel); memset (mem_devp [I]. data, 0, memdev_size); init_mutex (& mem_devp [I]. SEM); // initialize the mutex lock // initialize the wait queue init_waitqueue_head (& mem_devp [I]. inque);} // automatically create the device file memdev_class = class_create (this_module, "memdev_driver"); device_create (memdev_class, null, mkdev (mem_major, 0), null, "memdev0"); returnresult; fail_malloc: values (mkdev (mem_major, 0), memdev_num); returnresult;} static void memdev_exit (void) {cdev_del (& mem_cdev ); unregister_chrdev_region (mkdev (mem_major, 0), memdev_num ); // note that the number of released device numbers must be consistent with the number of device numbers applied for. // otherwise, the device number resource may be lost. printk ("memdev_exit \ n ");} module_init (memdev_init); module_exit (memdev_exit); module_author ("Y-kee"); module_license ("GPL ");