The source code is as follows:
#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>//#include "scull.h"#define SCULL_MAJOR 0 /* dynamic major by default */#define SCULL_NR_DEVS 4 /* scull0 through scull3 */#define SCULL_P_NR_DEVS 4 /* scullpipe0 through scullpipe3 */#define SCULL_QUANTUM 4000#define SCULL_QSET 1000struct scull_qset {void **data;struct scull_qset *next;};struct scull_dev {struct scull_qset *data; /* Pointer to first quantum set */int quantum; /* the current quantum size */int qset; /* the current array size */unsigned long size; /* amount of data stored here */unsigned int access_key; /* used by sculluid and scullpriv */struct semaphore sem; /* mutual exclusion semaphore */struct cdev cdev; /* Char device structure*/};intscull_major = SCULL_MAJOR;intscull_minor = 0;intscull_nr_devs = SCULL_NR_DEVS;intscull_quantum = SCULL_QUANTUM;intscull_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("BG2BKK");MODULE_LICENSE("Dual BSD/GPL");struct scull_dev *scull_devices;intscull_trim(struct scull_dev *dev){structscull_qset *next,*dptr;intqset = dev->qset;int i;for(dptr = dev->data; dptr;dptr = next){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;}intscull_open(struct inode *inode, struct file *filp){struct scull_dev *dev;dev = container_of(inode->i_cdev,struct scull_dev, cdev);filp->private_data= dev;if((filp->f_flags & O_ACCMODE) == O_WRONLY){scull_trim(dev);}return 0;}intscull_release(struct inode *inode,struct file *filp){printk(KERN_ALERT "scullrelease\n");return 0;}struct scull_qset *scull_follow(struct scull_dev *dev,int n){struct scull_qset *qs = dev->data;if(!qs){qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);if(qs == NULL)return NULL;memset(qs,0,sizeof(struct scull_qset));}while(n--){if( !qs->next ){qs->next = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);if(qs->next == NULL)return NULL;memset(qs->next, 0 ,sizeof(struct scull_qset));}qs = qs->next;continue;}return qs;}ssize_tscull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){structscull_dev *dev = filp->private_data;structscull_qset *dptr;intquantum = dev->quantum, qset = dev->qset;intitemsize = quantum*qset;intitem,s_pos,q_pos,rest;ssize_tretval = 0;if(down_interruptible(&dev->sem))return -ERESTARTSYS;//printk("f_pos= %d\n",*f_pos);//printk("count= %d\n",count);if( *f_pos >= dev->size)goto out;if( *f_pos + count > dev->size)count = dev->size - *f_pos;item= (long)*f_pos / itemsize;rest= (long)*f_pos % itemsize;s_pos= rest / quantum;q_pos= rest % quantum;dptr= scull_follow(dev,item);if(dptr == NULL || !dptr->data || !dptr->data[s_pos])goto out;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;}//printk("read scull: %d\n",count);*f_pos += count;retval = count;out:up(&dev->sem);return retval;}ssize_tscull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){structscull_dev *dev = filp->private_data;structscull_qset *dptr;intquantum = dev->quantum,qset = dev->qset;intitemsize = quantum * qset;intitem,s_pos,q_pos,rest;ssize_tretval = -ENOMEM;if(down_interruptible(&dev->sem))return -ERESTARTSYS;item= (long)*f_pos / itemsize;rest= (long)*f_pos % itemsize;s_pos= rest / quantum;q_pos= rest % quantum;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;}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;if(dev->size < *f_pos)dev->size = *f_pos;out:up(&dev->sem);return retval;}intscull_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){return 0;}loff_tscull_llseek(struct file *filp,loff_t off,int whence){struct scull_dev *dev = filp->private_data;loff_t newpos;switch(whence){case 0:newpos = off;break;case 1:newpos = filp->f_pos + off;break;case 2:newpos = dev->size + off;break;default: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,.ioctl= scull_ioctl,.open= scull_open,.release= scull_release,};void scull_cleanup_module(void){int i;dev_t devno = MKDEV(scull_major,scull_minor);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);}#ifdefSCULL_DEBUG//scull_remove_proc();#endifunregister_chrdev_region(devno,scull_nr_devs);//scull_p_cleanup();//scull_access_cleanup();}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);if(err)printk(KERN_NOTICE "Error %d adding scull%d",err,index);}intscull_init_module(void){intresult,i;dev_t dev=0;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;}printk(KERN_ALERT "hello scullmajor %d\n minor %d\n",scull_major,scull_minor);scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev),GFP_KERNEL);if(!scull_devices){result = -ENOMEM;goto fail;}memset(scull_devices, 0,scull_nr_devs*sizeof(struct scull_dev));for(i=0; i< scull_nr_devs; i++){scull_devices[i].quantum = scull_quantum;scull_devices[i].qset = scull_qset;init_MUTEX(&scull_devices[i].sem);scull_setup_cdev(&scull_devices[i],i);}dev=MKDEV(scull_major,scull_minor+scull_nr_devs);//dev += scull_p_init(dev);//dev += scull_access_init(dev);#ifdefSCULL_DEBUGscull_create_proc();#endifreturn 0;fail:scull_cleanup_module();return result;}module_init(scull_init_module);module_exit(scull_cleanup_module)
The test code is as follows:
/************************************************************************* *fileName: test.c *description: test the myscull.c *author: Hzc *create time: 2007-04-20 *modify info: -*************************************************************************/#include <stdio.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/types.h>/* device path */char path[] = "/dev/sculldev";int buf[40];int rbuf[40];int i;int t=1;int main(){int f = open(path, O_WRONLY);if (f == -1){printf("device open error!\n");return 1;}printf("Input a string to write device \n");//scanf("%s", buf);for(i=0;i<40;i++){buf[i]=i;write(f, buf+i,1 ); /* device wirte */}close(f);f= open(path,O_RDONLY);printf("Read the string from device...\n");if (f == -1){printf("device open error!\n");return 1;}for(i=0;i<40;i++){read(f, rbuf+i, 1); /* device wirte */}for(i=0;i<40;i++){printf("%d\t",rbuf[i]);if((i+1)%10 == 0) printf("\n");}close(f);}
The driver loading script is as follows:
#!/bin/shinsmod scull.komknod /dev/sculldev c 252 0
The driver uninstall script is as follows:
#!/bin/shrmmod scull.korm /dev/sculldev