scull0字符设备驱动示例(参考《Linux设备驱动程序》)
2012-02-19 22:52
369 查看
参考《Linux设备驱动程序》及其代码,写了一个简单的字符设备驱动,相比作者提供的要简单很多,我在ubuntu10.04的系统上已测试,能运行起来。因水平有限,只实现了open,release,write及read四个基本的接口,且使用测试程序运行时存在明显的bug,望志同道合的朋友一起完善。
/*头文件*/ #ifndef __SCULL_H__ #define __SCULL_H__ #ifndef SCULL_MAJOR #define SCULL_MAJOR 0 #endif #ifndef SCULL_MINOR #define SCULL_MINOR 0 #endif typedef struct _scull_dev_ { void **data; int quantum; int qset; unsigned long size; struct semaphore sem; struct cdev cdev; } SCULL_DEV,pSCULL_DEV; #endif /*驱动程序*/ /* * name:scull.c * function:scull character device driver * time:2012-2-19 * * author:txgcwm * mail:txgcwm@163.com * reference:Linux设备驱动程序(第三版) */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> #include "scull.h" /* local definitions */ int scull_major = SCULL_MAJOR; int scull_minor = SCULL_MINOR; int scull_mn_dev = 1; int scull_quantum = 4000; /*每个指针所指向存储单元的大小*/ int scull_qset = 1000; /*指针数组的大小*/ static pSCULL_DEV *pscull_dev = NULL; int scull_trim(SCULL_DEV *dev) { int i = 0; int qset = dev->qset; if (dev->data) { for(i = 0; i < qset; i++) { if (dev->data[i]) kfree(dev->data[i]); } kfree(dev->data); dev->data=NULL; } dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; return 0; } int scull_open(struct inode *inode, struct file *filp) { SCULL_DEV *dev = NULL; dev = container_of(inode->i_cdev,pSCULL_DEV,cdev); filp->private_data = dev; if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { scull_trim(dev); } return 0; } int scull_release(struct inode *inode, struct file *filp) { return 0; } ssize_t scull_read(struct file *filp, char *buf, size_t count,loff_t *f_pos) { SCULL_DEV *dev = filp->private_data; int quantum = dev->quantum; int qset = dev->qset; int s_pos, q_pos; ssize_t ret = 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; s_pos = (long)*f_pos / quantum; q_pos = (long)*f_pos % quantum; if(s_pos > qset) goto out; if(!dev->data) goto out; if(!dev->data[s_pos]) goto out; if(count > quantum - q_pos) count = quantum - q_pos; if(copy_to_user(buf, dev->data[s_pos]+q_pos, count)) /*将内核空间的数据拷贝到用户空间*/ { ret = -EFAULT; goto out; } *f_pos += count; ret = count; out: up(&dev->sem); return ret; } ssize_t scull_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos) { SCULL_DEV *dev = filp->private_data; int quantum = dev->quantum; int qset = dev->qset; int s_pos,q_pos; ssize_t ret = -ENOMEM; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; s_pos = (long)*f_pos / quantum; q_pos = (long)*f_pos % quantum; if(!dev->data) { dev->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); if (!dev->data) goto out; memset(dev->data, 0, qset * sizeof(char *)); } if(!dev->data[s_pos]) { dev->data[s_pos] = kmalloc(quantum, GFP_KERNEL); if (!dev->data[s_pos]) goto out; } if(count > quantum - q_pos) count = quantum - q_pos; if(copy_from_user(dev->data[s_pos]+q_pos, buf, count)) /*将用户空间的数据拷贝到内核空间*/ { ret = -EFAULT; goto out; } *f_pos += count; ret = count; if(dev->size < *f_pos) dev->size = *f_pos; out: up(&dev->sem); return ret; } int scull_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg) { return 0; } loff_t scull_llseek(struct file *filp, loff_t off, int whence) { return 0; } 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, }; static void scull_cleanup_module(void) { dev_t dev; dev = MKDEV(scull_major,scull_minor); cdev_del(&pscull_dev->cdev); unregister_chrdev_region(dev,scull_mn_dev); scull_trim(pscull_dev); kfree(pscull_dev); pscull_dev = NULL; printk(KERN_EMERG"Leave World!\n"); return; } static int scull_init_module(void) { dev_t dev; int result = -1; pscull_dev = kmalloc(sizeof(SCULL_DEV),GFP_KERNEL); if(pscull_dev == NULL) { printk(KERN_WARNING"scull: can't kmalloc memory\n"); result = -ENOMEM; return result; } memset(pscull_dev,0,sizeof(SCULL_DEV)); pscull_dev->data = NULL; pscull_dev->quantum = scull_quantum; pscull_dev->qset = scull_qset; sema_init(&pscull_dev->sem, 1); if(scull_major) { dev = MKDEV(scull_major,scull_minor); result = register_chrdev_region(dev,scull_mn_dev,"scull"); } else { result = alloc_chrdev_region(&dev,scull_minor,scull_mn_dev,"scull"); scull_major = MAJOR(dev); } if(result < 0) { printk(KERN_WARNING"scull: can't get major %d\n",scull_major); return result; } cdev_init(&pscull_dev->cdev,&scull_fops); pscull_dev->cdev.owner = THIS_MODULE; pscull_dev->cdev.ops = &scull_fops; result = cdev_add(&pscull_dev->cdev,dev,1); if(result) { printk(KERN_NOTICE"Error %d add scull device\n",result); goto fail; } printk(KERN_EMERG"Hello World!\n"); return 0; fail: scull_cleanup_module(); return result; } module_init(scull_init_module); module_exit(scull_cleanup_module); MODULE_AUTHOR("txgcwm"); MODULE_VERSION("scull_v1.0"); MODULE_LICENSE("GPL"); /*Makefile*/ ifneq ($(KERNELRELEASE),) obj-m := scull.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: make -C $(KERNELDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers endif /*加载脚本*/ #!/bin/sh module="scull" device="scull" mode="664" if grep '^staff:' /etc/group > /dev/null; then #并非所有的发行版本都有staff组,有些有wheel组 group="staff" else group="wheel" fi /sbin/insmod -f ./$module.ko $* || exit 1 #调用insmod,并将所有传入该脚本的参数传给模块,同时使用路径名来指定模块的位置,因为新的modutils默认不会在当前的目录中查找模块 major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` rm -f /dev/${device}[0-3] #删除原有节点 mknod /dev/${device}0 c $major 0 #创建新的节点 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /dev/${device}3 c $major 3 #ln -sf ${device}0 /dev/${device} chgrp $group /dev/${device}[0-3] #给定适当的组属性及许可,并修改属组 chmod $mode /dev/${device}[0-3] /*卸载脚本*/ #!/bin/sh module="scull" device="scull" /sbin/rmmod $module $* || exit 1 rm -f /dev/${device} /dev/${device}[0-3] /*测试程序*/ #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main(int argc,char **argv) { int fd = -1; int count = 0; char buf[30]; char *data = "it just for test scull0 "; fd = open("/dev/scull0",O_RDWR); if(fd < 0) { printf("error %d:can't open device scull0\n",fd); return -1; } else printf("fd:%d\n",fd); while(1) { memset(buf,'\0',sizeof(buf)); count = write(fd,data,strlen(data)); if(count < 0) { printf("error%d,can't write data to device scull0\n",count); continue; } count = read(fd,buf,strlen(data)); if(count < 0) { printf("can't read data from device scull0,count:%d\n",count); } else { printf("num:%d,read data:%s \n",count,buf); } sleep(1); } }
相关文章推荐
- 简单字符设备驱动示例
- linux字符设备驱动示例
- 宋宝华 《Linux设备驱动开发详解》示例代码之fifo字符设备驱动
- [linux设备驱动程序]scull字符设备驱动编译在新内核编译问题解决方案
- 【原创】《Linux设备驱动程序》学习之循序渐进 --- 字符设备驱动
- 《Linux设备驱动程序》学习2—高级字符设备驱动ioctl
- ceph存储 字符设备驱动示例x86Linux平台
- tony之linux driver_LDD3_scull字符设备驱动编译在新内核编译问题
- 【原创】《Linux设备驱动程序》学习之循序渐进 --- 字符设备驱动
- 宋宝华 《Linux设备驱动开发详解》示例代码之基本字符设备驱动
- 嵌入式 字符设备驱动经典示例
- 《LINUX设备驱动程序》学习之字符设备驱动实例
- 编写Linux并行接口字符设备驱动
- linux设备驱动--字符设备驱动注册与驱动参数传递
- 字符设备驱动(一)
- Linux设备驱动开发基础---字符设备驱动程序开发之基于中断的按键驱动
- linux字符驱动之自动创建设备节点
- linux字符设备驱动初识
- 字符设备驱动之RTC驱动分析
- linux驱动开发之字符设备--自动创建设备节点