Learn about Linux driver development. Article 8: scull in chapter 3 of ldd3

Source: Internet
Author: User

# Include <Linux/module. h> <br/> # include <Linux/moduleparam. h> <br/> # include <Linux/init. h> <br/> # include <Linux/kernel. h>/* printk () */<br/> # include <Linux/slab. h>/* kmalloc () */<br/> # include <Linux/Fs. h>/* everything... */<br/> # include <Linux/errno. h>/* error codes */<br/> # include <Linux/types. h>/* size_t */<br/> # include <Linux/fcntl. h>/* o_accmode */<br/> # include <Linux/cdev. h> <br/> # includ E <ASM/system. h>/* CLI (), * _ flags */<br/> # include <ASM/uaccess. h>/* Copy _ * _ User */<br/> # include "scull. H "/* local definitions */<br/>/* <br/> * our parameters which can be set at load time. <br/> */<br/> // master device number <br/> int scull_major = scull_major; <br/> // device number <br/> int scull_minor = 0; <br/> // number of consecutive device numbers requested <br/> int scull_nr_devs = scull_nr_devs; /* Number of bare scull devices */<br/> // quantum size <br/> int Scull_quantum = scull_quantum; <br/> // large or small quantum set <br/> int scull_qset = scull_qset; <br/> module_param (scull_major, Int, s_irugo ); <br/> module_param (scull_minor, Int, s_irugo); <br/> module_param (inflow, Int, s_irugo); <br/> module_param (scull_quantum, Int, s_irugo ); <br/> module_param (scull_qset, Int, s_irugo); <br/> struct scull_dev * scull_devices;/* allocated in scull_init_module */<br/>/* <br/> * Empty out the scull device; must be called with the device <br/> * semaphore held. <br/> */<br/>/* <br/> * release the entire data zone, traverse the list, and release any quantum and quantum sets it discovers. <Br/> * called when the scull_open file is opened for writing. <Br/> * the semaphore must be held when this function is called. <Br/> */<br/> int scull_trim (struct scull_dev * Dev) <br/>{< br/> struct scull_qset * Next, * dptr; <br/> // large or small quantum set <br/> int qset = Dev-> qset;/* "Dev" is not-null */<br/> int I; <br/> for (dptr = Dev-> data; dptr = NEXT) {/* All the list items */<br/> If (dptr-> data) {// data in the quantum set <br/> // traverses and releases each quantum in the current quantum set. The quantum set size is qset <br/> for (I = 0; I <qset; I ++) <br/> kfree (dptr-> data [I]); <br/> // release the quantum array pointer <br/> kfree (DP Tr-> data); <br/> dptr-> DATA = NULL; <br/>}< br/> // next gets the next quantum set, release the current quantum set <br/> next = dptr-> next; <br/> kfree (dptr ); <br/>}< br/> // clear the variable value in struct scull_dev <br/> Dev-> size = 0; <br/> Dev-> quantum = scull_quantum; <br/> Dev-> qset = scull_qset; <br/> Dev-> DATA = NULL; <br/> return 0; <br/>}< br/>/* <br/> * Open and Close <br/> */<br/> int scull_open (struct inode * inode, struct file * filp) <br/>{< br/> stru CT scull_dev * dev;/* device information */<br/> Dev = container_of (inode-> I _cdev, struct scull_dev, cdev ); <br/> filp-> private_data = dev; /* for other methods */<br/>/* Now trim to 0 the length of the device if open was write-only */<br/> // The file is in read-only mode. when opening, truncated to 0 <br/> If (filp-> f_flags & o_accmode) = o_wronly) {<br/> If (down_interruptible (& Dev-> SEM )) <br/> return-erestartsys; <br/> scull_trim (Dev ); /* Ignore errors */<br/> up (& Dev-> SEM); <br/>}< br/> return 0; /* success */<br/>}< br/> int scull_release (struct inode * inode, struct file * filp) <br/>{< br/> return 0; <br/>}< br/>/* <br/> * Follow the list <br/> */<br/> // return the nth quantum set of the device Dev pointer, apply for a new quantum set if n is not enough <br/> struct scull_qset * scull_follow (struct scull_dev * Dev, int N) <br/>{< br/> // pointer to the First Quantum set <br/> struct scull_qset * Qs = Dev-> data; <br/>/* allocat E first qset explicitly if need be */<br/> // if the current device does not have a quantum set, the First Quantum set is allocated. <br/> If (! Qs) {<br/> Qs = Dev-> DATA = kmalloc (sizeof (struct scull_qset), gfp_kernel); <br/> If (Qs = NULL) <br/> return NULL;/* Never mind */<br/> memset (QS, 0, sizeof (struct scull_qset )); <br/>}< br/>/* then follow the list */<br/> // traverse n steps of the quantum set linked list of the current device, apply for a new <br/> while (n --) {<br/> If (! QS-> next) {<br/> QS-> next = kmalloc (sizeof (struct scull_qset), gfp_kernel); <br/> If (QS-> next = NULL) <br/> return NULL;/* Never mind */<br/> memset (QS-> next, 0, sizeof (struct scull_qset )); <br/>}< br/> Qs = QS-> next; <br/> continue; <br/>}< br/> return QS; <br/>}< br/>/* <br/> * Data Management: Read and Write <br/> */<br/> ssize_t scull_read (struct file * filp, // file structure corresponding to the device <br/> char _ User * Buf ,// Read the user space <br/> size_t count, // number of bytes <br/> loff_t * f_pos) // the location to be read, offset in filp private data <br/>{< br/> struct scull_dev * Dev = filp-> private_data; <br/> struct scull_qset * dptr; /* The first listitem */<br/> // large or small quantum set <br/> int quantum = Dev-> quantum, qset = Dev-> qset; <br/> // number of bytes in a quantum set <br/> int itemsize = quantum * qset;/* How many bytes in the listitem */<br/> int item, s_pos, q_pos, rest; <br/> ssize_t retval = 0; <Br/> If (down_interruptible (& Dev-> SEM) <br/> return-erestartsys; <br/> // The position to be read exceeds the total data volume <br/> If (* f_pos> = Dev-> size) <br/> goto out; <br/> // If (* f_pos + count> Dev-> size) <br/> COUNT = Dev-> size-* f_pos; <br/>/* Find listitem, qset index, and offset in the quantum */<br/> // locate the read/write position in the quantum/quantum set: the First Quantum set, offset in the quantum field <br/> // Number of quantum sets <br/> item = (long) * f_pos/itemsize; <br/> // Offset in the quantum set <br/> rest = (long) * f_pos % itemsize; <br/> // Number of the First Quantum, offset in the quantum field <br/> s_pos = rest/quantum; q_pos = rest % quantum; <br/>/* Follow the list up to the right position (defined elsewhere) */<br/> // read the pointer to the quantum set to be read <br/> dptr = scull_follow (Dev, item ); <br/> // read error handling <br/> If (dptr = NULL |! Dptr-> data |! Dptr-> data [s_pos]) <br/> goto out; /* don't fill holes */<br/>/* read only up to the end of this quantum */<br/> // read only in one quantum: if count exceeds the current quantum, truncation count <br/> If (count> quantum-q_pos) <br/> COUNT = quantum-q_pos; <br/> // copy the Count bytes of the content to be read to the Buf of the user space. <br/> 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; <br /> Out: <br/> up (& Dev-> SEM); <br/> return retval; <br/>}< br/> ssize_t scull_write (struct file * filp, const char _ User * Buf, size_t count, <br/> loff_t * f_pos) <br/>{< br/> struct scull_dev * Dev = filp-> private_data; <br/> struct scull_qset * dptr; <br/> // quantum, quantum set size <br/> int quantum = Dev-> quantum, qset = Dev-> qset; <br/> // The total number of bytes in a quantum set <br/> int itemsize = quantum * qset; <br/> int item, s_pos, q_pos, rest; <Br/> ssize_t retval =-enomem;/* value used in "Goto out" statements */<br/> If (down_interruptible (& Dev-> SEM )) <br/> return-erestartsys; <br/>/* Find listitem, qset index and offset in the quantum */<br/> // Number of quantum sets <br/> item = (long) * f_pos/itemsize; <br/> // offset in the quantum set <br/> rest = (long) * f_pos % itemsize; <br/> // The First Quantum in the quantum set. offset in the quantum <br/> s_pos = rest/quantum; q_pos = rest % quantum; <br/>/* fol Low the list up to the right position */<br/> // returns the pointer to the quantum set <br/> dptr = scull_follow (Dev, item ); <br/> If (dptr = NULL) <br/> goto out; <br/> // if the data of this quantum set is null, apply for a new memory <br/> If (! Dptr-> data) {<br/> dptr-> DATA = kmalloc (qset * sizeof (char *), gfp_kernel); <br/> If (! Dptr-> data) <br/> goto out; <br/> memset (dptr-> data, 0, qset * sizeof (char *)); <br/>}< br/> // If the s_pos quantum is null, apply for a new memory. <br/> If (! Dptr-> data [s_pos]) {<br/> dptr-> data [s_pos] = kmalloc (quantum, gfp_kernel); <br/> If (! Dptr-> data [s_pos]) <br/> goto out; <br/>}< br/>/* write only up to the end of this quantum */<br/> // write only in one quantum, if the Count value exceeds the current quantum, It is truncated. <br/> If (count> quantum-q_pos) <br/> COUNT = quantum-q_pos; <br/> // copy data from the user space to the kernel space. If no data is copied, the number of bytes is returned, 0 <br/> If (copy_from_user (dptr-> data [s_pos] + q_pos, Buf, count) {<br/> retval =-efault; <br/> goto out; <br/>}< br/> * f_pos + = count; <br/> retval = count; <br/>/* update t He size */<br/> // update the total number of bytes <br/> If (Dev-> size <* f_pos) <br/> Dev-> size = * f_pos; <br/> out: <br/> up (& Dev-> SEM); <br/> return retval; <br/>}</P> <p> struct file_operations scull_fops ={< br/>. owner = this_module, <br/>. read = scull_read, <br/>. write = scull_write, <br/>. open = scull_open, <br/>. release = scull_release, <br/>}; <br/>/* <br/> * Finally, the module stuff <br/> */<br/>/* <br/> * The Cl Eanup function is used to handle initialization failures as well. <br/> * thefore, it must be careful to work correctly even if some of the items <br/> * have not been initialized <br/> */<br/> void scull_cleanup_module (void) <br/>{< br/> int I; <br/> // combine the Primary and Secondary device numbers into a dev_t structure, that is, the device number <br/> dev_t devno = mkdev (scull_major, scull_minor); <br/>/* Get Rid Of our char Dev entries */<br/> If (scull_devices) {<br/> // Facilitate the release of the data zone of each device <br/> for (I = 0; I <scull_nr_devs; I ++) {<br/> // release the data zone <br/> scull_trim (scull_devices + I ); <br/> // remove cdev <br/> cdev_del (& scull_devices [I]. cdev); <br/>}< br/> // release scull_devices itself <br/> kfree (scull_devices ); <br/>}< br/>/* cleanup_module is never called if registering failed */<br/> unregister_chrdev_region (devno, scull_nr_devs ); <br/>}</P> <p>/* <br/> * set up the char_dev structure fo R this device. <br/> */<br/> // create the char_dev structure <br/> static void scull_setup_cdev (struct scull_dev * Dev, int index) <br/>{< br/> int err, devno = mkdev (scull_major, scull_minor + index); </P> <p> cdev_init (& Dev-> cdev, & scull_fops); <br/> Dev-> cdev. owner = this_module; <br/> // Dev-> cdev. ops = & scull_fops; <br/> // Add the character device Dev-> cdev, which takes effect immediately <br/> err = cdev_add (& Dev-> cdev, devno, 1 ); <br/>/* fail gracefully if need Be */<br/> If (ERR) <br/> printk (kern_notice "error % d adding scull % d", err, index ); <br/>}</P> <p> int scull_init_module (void) <br/>{< br/> int result, I; <br/> dev_t Dev = 0; <br/>/* <br/> * Get a range of minor numbers to work with, asking for a dynamic <br/> * Major unless directed otherwise at load time. <br/> */<br/> // apply for a device ID. If no primary device ID is specified during loading, the device ID is dynamically allocated. <br/> If (scull_major) {<br/> Dev = mkdev (scull_majo R, scull_minor); <br/> result = register_chrdev_region (Dev, scull_nr_devs, "scull"); <br/>} else {<br/> result = alloc_chrdev_region (& Dev, scull_minor, scull_nr_devs, <br/> "scull"); <br/> scull_major = major (Dev); <br/>}< br/> If (result <0) {<br/> printk (kern_warning "scull: Can't Get Major % d/N", scull_major); <br/> return result; <br/>}< br/>/* <br/> * allocate the devices -- we can't have them s Tatic, as the number <br/> * can be specified at load time <br/> */<br/> // apply for memory for the scull_dev object <br/> scull_devices = kmalloc (scull_nr_devs * sizeof (struct scull_dev ), gfp_kernel); <br/> If (! Scull_devices) {<br/> result =-enomem; <br/> goto fail;/* make this more graceful */<br/>}< br/> memset (scull_devices, 0, scull_nr_devs * sizeof (struct scull_dev); <br/>/* initialize each device. */<br/> for (I = 0; I <scull_nr_devs; I ++) {<br/> scull_devices [I]. quantum = scull_quantum; <br/> scull_devices [I]. qset = scull_qset; <br/> // initialize the mutex lock and set the semaphores SEM to 1. <br/> sema_init (& scull_devices [I]. SEM, 1); <br/> // init_mutex (& scull_devices [I]. SEM); <br/> // create the char_dev structure <br/> scull_setup_cdev (& scull_devices [I], I); <br/>}< br/> return 0; /* succeed */<br/> fail: <br/> scull_cleanup_module (); <br/> return result; <br/>}< br/> module_init (scull_init_module ); <br/> module_exit (scull_cleanup_module); <br/> module_author ("tekkamanninja"); <br/> module_license ("dual BSD/GPL ");

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.