Some time ago, I have been engaged in the application software of the GPS navigation system. Recently, it has come to an end. I continue to pick up the driver of the Linux device!
The last time I recorded the module initialization code, I checked the code for uninstalling the module this time.
Void scull_cleanup_module (void) </P> <p >{</P> <p> int I; </P> <p> dev_t devno = mkdev (scull_major, scull_minor ); // you have seen this! Obtain the device ID of the current module </P> <p> If (scull_devices) {</P> <p> for (I = 0; I <scull_nr_devs; I ++) {</P> <p> scull_trim (scull_devices + I); // analyze this function below </P> <p> cdev_del (& scull_devices [I]. cdev);/* system call. Remove a character device from the system. This function is the same as cdev_alloc, cdev_init, the cdev_add functions constitute the functions required for character device registration */</P> <p >}</P> <p> kfree (scull_devices ); /* this time corresponds to the previous kmalloc function, releasing the memory space of the device (here the device itself is a piece of memory, haha) */</P> <p >}< br/>
Let's take a look at scull_trim.
/*
* Empty out the scull device; must be called with the device
* Semaphore held.
*/
This function is used to traverse the entire data zone and release all the found quantum and quantum sets. The content is also very simple, not explained.
Int scull_trim (struct scull_dev * Dev) </P> <p >{</P> <p> struct scull_qset * Next, * dptr; </P> <p> int qset = Dev-> qset; /* "Dev" is not-null */</P> <p> for (dptr = Dev-> data; dptr = NEXT) // traverse the entire linked list </P> <p >{</P> <p> If (dptr-> data) </P> <p >{</P> <p> for (I = 0; I <qset; I ++) </P> <p> kfree (dptr-> data [I]); </P> <p> kfree (dptr-> data ); </P> <p> dptr-> DATA = NULL; </P> <p >}</P> <p> next = dptr-> next; </P> <p> kfree (dptr); </P> <p >}</P> <p> Dev-> size = 0; </P> <p> Dev-> quantum = scull_quantum; </P> <p> Dev-> qset = scull_qset; </P> <p> Dev-> DATA = NULL; </P> <p> return 0; </P> <p >}< br/>
The scull Device Driver implements several most important device methods. The file_operations structure is as follows:
Struct file_operations scull_fops = {<br/>. owner = this_module, <br/>. llseek = scull_llseek, <br/>. read = scull_real, <br/>. write = scull_write, <br/>. IOCTL = scull_ioctl, <br/>. open = scull_open, <br/>. release = scull_release, <br/> };
These methods must be implemented by the Driver Designer. Each device driver needs the designer to implement these methods.
For example, the driver user calls the scull_open function. I think it can be understood that the interfaces of all drivers are the same, but the internal implementations are different.
Reference books on the functions of open and release methods include page62 and page63.
The prototype of the read and write methods is as follows:
Ssize_t read (stuct file * filp, char _ User * buff, size_t count, loff_t OFFP );
Ssize_t write (struct file * filp, const char _ User * buff, size_t count, loff_t * OFFP );
The essence of these two methods is data copying. The read method copies data to the user address space, and the write method copies the data to the kernel address space. The kernel functions used to implement these two copies are as follows (the core part of most read and write methods ):
Unsigned long copy_to_user (void _ User * To, const void * From, unsigned Long Count );
Unsigned long copy_from_user (void * To, const void _ User * From, unsigned Long Count );
Analyze the scull open method:
Int scull_open (struct inode * inode, struct file * filp) <br/>{< br/> struct scull_dev * dev; /* device information */</P> <p> Dev = container_of (inode-> I _cdev, struct scull_dev, cdev ); /* because the I _cdev field of inode only contains a pointer to the struct cdev structure, we need to use the scull_dev structure pointer. Here we use the container_of macro, used to obtain the scull_dev structure containing the cdev structure */<br/> filp-> private_data = dev;/* Save the obtained scull_dev structure pointer for future access. */</P> <p>/* only when the device is enabled with "write, 0 x/<br/> If (filp-> f_flags & o_accmode) = o_wronly) {<br/> If (down_interruptible (& Dev-> SEM )) /* obtain the semaphores. If the semaphores are successfully obtained, the system continues. If the semaphores fail, the system returns-erestarsys */<br/> return-erestartsys; <br/> scull_trim (Dev ); /* ignore errors */<br/> up (& Dev-> SEM);/* release semaphore */<br/>}< br/> return 0; /* success */<br/>}
Analyze scull's release method: the Basic scull does not have hardware to be closed, so it returns directly.
Int scull_release (struct inode * inode, struct file * filp) <br/>{< br/> return 0; <br/>}
Analyze the scull read method:
Ssize_t scull_read (struct file * filp, char _ User * Buf, size_t count, <br/> loff_t * f_pos) <br/> {<br/> struct scull_dev * Dev = filp-> private_data;/* obtain the scull_dev structure pointer that is allocated when open is obtained and saved in filp-> private_data. */<Br/> struct scull_qset * dptr;/* The first listitem */<br/> int quantum = Dev-> quantum, qset = Dev-> qset; /* obtain the quantum size (4000 bytes) and the quantum set size (1000 quantum) */<br/> int itemsize = quantum * qset; /* How many bytes in the listitem, the size of each item in the Linked List (4 000 000 bytes) */<br/> int item, s_pos, q_pos, rest; <br/> ssize_t retval = 0; </P> <p> If (down_interruptible (& Dev-> SEM)/* the following operations are mutually exclusive, so here we need to obtain the semaphore */<br/> return -Erestartsys; <br/> If (* f_pos> = Dev-> size)/* If the offset to be read exceeds the size of the device file, it cannot be read, directly jump out */<br/> goto out; <br/> If (* f_pos + count> Dev-> size) /* If the content to be read exceeds the size of the device file, it is truncated, read-only to the end of the file. */<br/> COUNT = Dev-> size-* f_pos; </P> <p>/* Find listitem, qset index, and offset in the quantum */<br/> item = (long) * f_pos/itemsize; <br/> rest = (long) * f_pos % itemsize; <br/> s_pos = rest/quantum; q_pos = rest % Qu Antum; </P> <p>/* Follow the list up to the right position (defined elsewhere) */<br/> dptr = scull_follow (Dev, item ); </P> <p> If (dptr = NULL |! Dptr-> data |! Dptr-> data [s_pos]) <br/> goto out; /* don't fill holes */</P> <p>/* read only up to the end of this quantum */<br/> If (count> quantum-q_pos) <br/> COUNT = quantum-q_pos; </P> <p> If (copy_to_user (BUF, dptr-> data [s_pos] + q_pos, count )) {<br/> retval =-efault; <br/> goto out; <br/>}< br/> * f_pos + = count; <br/> retval = count; </P> <p> out: <br/> up (& Dev-> SEM); <br/> return retval; <br/>}< br/>