# 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 ");