LDD3到第三章的组织结构改变的scull驱动
scull.h文件---驱动头文件
/********************************************** * Author: lewiyon@hotmail.com * File name: scull.h * Description: define scull * Date: 2012-07-4 *********************************************/ #ifndef __SCULL_H #define __SCULL_H #include <linux/semaphore.h> #include <linux/cdev.h> #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 #endif #ifndef SCULL_NR_DEVS #define SCULL_NR_DEVS 1 #endif #ifndef SCULL_QUANTUM #define SCULL_QUANTUM 4096 #endif #ifndef SCULL_QSET #define SCULL_QSET 4096 #endif /* * parameters of module */ extern int scull_major; extern int scull_quantum; extern int scull_qset; struct scull_qset { void **data; struct scull_qset *next; }; /* * define scull_dev struct */ struct scull_dev { struct scull_qset *data; /* point first child set */ int quantum; /* size of current set */ int qset; /* size of array */ unsigned long size; /* save total quantity */ unsigned int access_key; /* used by scullid and scullpriv */ struct semaphore sem; /* mutex */ struct cdev cdev; /* character device */ }; extern int scull_trim(struct scull_dev *dev); #endif
main.c -- 驱动模块实现源码
/********************************************** * Author: lewiyon@hotmail.com * File name: scullmod.c * Description: initialize and release function for scull * Date: 2012-07-4 *********************************************/ #include <linux/init.h> /* module */ #include <linux/module.h> /* module */ #include <linux/moduleparam.h> /* module */ #include <linux/errno.h> /* error codes */ #include <linux/kernel.h> /* printk */ #include <linux/slab.h> /* kmalloc kfree */ #include <linux/types.h> /* dev_t */ /* local head files */ #include "scull.h" #include "file.h" /* default parameters of module */ int scull_major = SCULL_MAJOR; int scull_minor = 0; int scull_nr_devs = SCULL_NR_DEVS; int scull_quantum = SCULL_QUANTUM; int scull_qset = SCULL_QSET; /* input parameters of module */ 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); struct scull_dev *scull_devices; /* * scull_trim - 遍历链表,并释放所有找到的量子和量子集 * @dev: scull设备 */ int scull_trim(struct scull_dev *dev) { int i, qset; struct scull_qset *next, *dptr; qset = dev->qset; 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; } static void scull_setup_cdev(struct scull_dev *dev, int index) { int err; int devno; 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); } /* * initialze scull module */ void scull_cleanup_module(void) { int i; dev_t devno; devno = MKDEV(scull_major, scull_minor); /* delete each entry */ 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); } /* unregister */ unregister_chrdev_region(devno, scull_nr_devs); printk(KERN_WARNING "scull: Bye!\n"); } /* * initialze scull module */ int scull_init_module(void) { int i, res; dev_t dev = 0; if (scull_major) { dev = MKDEV(scull_major, scull_minor); res = register_chrdev_region(dev, scull_nr_devs, "scull"); } else { res = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } if (res < 0) { printk(KERN_WARNING "scull: can't get major %d\n", scull_major); return res; } /* * allocate the device struct cache */ scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); if (NULL == scull_devices) { res = -ENOMEM; printk(KERN_WARNING "scull: NOMEM for scull!\n"); goto fail; } memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev)); /* initialize each device */ for (i = 0; i < scull_nr_devs; i++) { scull_devices[i].quantum = scull_quantum; scull_devices[i].qset = scull_qset; sema_init(&scull_devices[i].sem, 1); scull_setup_cdev(&scull_devices[i], i); } printk(KERN_INFO "scull: OK!\n"); return 0; fail: scull_cleanup_module(); return res; } module_init(scull_init_module); module_exit(scull_cleanup_module); MODULE_AUTHOR("lewiyon@hotmail.com"); MODULE_LICENSE("GPL");
file.h -- 文件操作函数头文件
/********************************************** * Author: lewiyon@hotmail.com * File name: file.h * Description: file header * Date: 2012-07-4 *********************************************/ #ifndef __SCULL_FILE_H #define __SCULL_FILE_H extern const struct file_operations scull_fops; #endif
file.c -- 文件操作函数源码
/********************************************** * Author: lewiyon@hotmail.com * File name: file.c * Description: realize cull file ops * Date: 2012-07-04 *********************************************/ #include <linux/module.h> /* THIS_MODULE */ #include <linux/kernel.h> /* printk & container */ #include <linux/uaccess.h> /* cp_to/from_user */ #include <linux/types.h> /* size_t */ #include <linux/fs.h> /* inode st. */ #include "scull.h" /* * scull_follow - 在指定的scull_dev中查找指定的量子集 * @dev: scull_dev设备结构体指针 * @n: 量子集在scull_dev中的位置 * * return: * @对应量子集结构指针 - 成功; * @NULL - 失败; */ struct scull_qset *scull_follow(struct scull_dev *dev, int n) { struct scull_qset *qs; qs = dev->data; if (!qs) { qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (qs == NULL) { printk(KERN_WARNING "scull_follow_if_fail\n"); return NULL; } memset(qs, 0, sizeof(struct scull_qset)); } /* 创建其他的量子集 */ while (n--) { if (!qs->next) { qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); if (NULL == qs->next) { printk(KERN_WARNING "scull_follow_n_%d\n", n); return NULL; } memset(qs->next, 0, sizeof(struct scull_qset)); } qs = qs->next; } return qs; } /* * scull_read - 从scull_dev中的文件读取数据 */ ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev; struct scull_qset *dptr; int quantum, qset, itemsize; int item, rest, s_pos, q_pos; ssize_t retval; dev = filp->private_data; quantum = dev->quantum; qset = dev->qset; itemsize = quantum * qset; retval = 0; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos >= dev->size) goto out; if (*f_pos + count > dev->size) count = dev->size - *f_pos; /* 查找listitem中量子集的索引以及量子偏移位 */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* 在dev中查找量子集item */ dptr = scull_follow(dev, item); if (NULL == dptr || !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; } *f_pos += count; retval = count; out: up(&dev->sem); return retval; } /* * scull_write - 往scull_dev中的文件写入数据 */ ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct scull_dev *dev; struct scull_qset *dptr; int quantum , qset; int itemsize, item, s_pos, q_pos, rest; ssize_t retval; dev = filp->private_data; quantum = dev->quantum; qset = dev->qset; itemsize = quantum * qset; retval = -ENOMEM; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* 查找listitem中量子集的索引以及量子偏移位 */ item = (long)*f_pos / itemsize; rest = (long)*f_pos % itemsize; s_pos = rest / quantum; q_pos = rest % quantum; /* 在dev中查找量子集item */ dptr = scull_follow(dev, item); if (dptr == NULL) { printk(KERN_WARNING "scull_follow_fail\n"); goto out; } if (!dptr->data) { dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dptr->data) { printk(KERN_WARNING "km_dptr->data_fail\n"); 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]) { printk(KERN_WARNING "km_dptr->data[s_pos]_fail\n"); 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; printk(KERN_WARNING "copy_fail\n"); goto out; } *f_pos += count; retval = count; /* 更新scull_dev数据大小记录 */ if (dev->size < *f_pos) dev->size = *f_pos; out: up(&dev->sem); return retval; } /* * scull_llseek - 往scull_dev中的文件写入数据 */ loff_t scull_llseek(struct file *filp, loff_t off, int whence) { struct scull_dev *dev; loff_t newpos; dev = filp->private_data; switch(whence) { case SEEK_SET: newpos = off; break; case SEEK_CUR: newpos = filp->f_pos + off; break; case SEEK_END: newpos = dev->size + off; break; default: return -EINVAL; } if (newpos < 0) return -EINVAL; filp->f_pos = newpos; return newpos; } /* * scull_open - 打开scull_dev中的文件 */ int scull_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 (O_WRONLY == (filp->f_flags & O_ACCMODE)) { if (down_interruptible(&dev->sem)) return -ERESTARTSYS; scull_trim(dev); up(&dev->sem); } return 0; } /* * scull_release - 关闭scull_dev中的文件 */ int scull_release(struct inode *inode, struct file *filp) { return 0; } const 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, };
makefile文件
obj-m += scull.o scull-objs := main.o file.o KERNELBUILD := /lib/modules/$(shell uname -r)/build default: $(MAKE) -C $(KERNELBUILD) M=$(shell pwd) modules clean: -rm -rf *.o .*.cmd *.ko* *.mod.c .tmp_versions -rm -rf modules.order Module.symvers
测试文件:
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <errno.h> int main() { int fp0; char Buf[4096]; /* 初始化Buf */ strcpy(Buf,"Scull is char dev!"); printf("BUF: %s\n",Buf); /* 打开设备文件 */ fp0 = open("/dev/scull0", O_RDWR); if (fp0 < 0) { printf("Open scull Error!\n"); return -1; } /* 写入设备 */ strcpy(Buf,"Scull write!"); write(fp0, Buf, sizeof(Buf)); /* 重新定位文件位置 */ lseek(fp0,0,SEEK_SET); /* 清除Buf */ strcpy(Buf,"Buf is NULL!"); /* 读出设备 */ read(fp0, Buf, sizeof(Buf)); /* 检测结果 */ printf("BUF: %s success\n",Buf); return 0; }
参考文献:(部分代码摘自下列网站)
http://www.cnblogs.com/adolph-suyu/archive/2011/12/04/2275990.html