《LINUX设备驱动程序》学习之字符设备驱动实例
2012-10-25 14:57 Chung-shu 阅读(636) 评论(0) 收藏 举报 Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()、close ()、read ()、write () 等。
Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。下面假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,设备的名为"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。
1、编写globalvar.c文件,代码如下:
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/slab.h> MODULE_LICENSE("GPL"); int globalvar_open(struct inode *, struct file *); int globalvar_release(struct inode *, struct file *); ssize_t globalvar_read(struct file *, char *, size_t, loff_t *); ssize_t globalvar_write(struct file *, const char *, size_t, loff_t *); int dev_major=0; int dev_minor=0; struct file_operations globalvar_fos= { .owner=THIS_MODULE, .open=globalvar_open, .release=globalvar_release, .read=globalvar_read, .write=globalvar_write, }; struct globalvar_dev { int global_var; //代表要操作的设备 struct cdev cdev; //内核中表示字符设备的结构 }; struct globalvar_dev *my_dev; static void __exit globalvar_exit(void) //退出模块时的操作 { dev_t devno=MKDEV(dev_major, dev_minor); cdev_del(&my_dev->cdev); //从系统中移除一个字符设备 kfree(my_dev); //释放自定义的设备结构 unregister_chrdev_region(devno, 1); //注销已注册的驱动程序 printk("globalvar unregister success!\n"); } static int __init globalvar_init(void) //初始化模块的操作 { int ret, err; dev_t devno=MKDEV(dev_major, dev_minor); //注册设备号 if(dev_major) { ret=register_chrdev_region(devno, 1, "globalvar"); } else { ret=alloc_chrdev_region(&devno, dev_minor, 1, "globalvar"); dev_major=MAJOR(devno); } if(ret<0) { printk("globalvar register failure!\n"); globalvar_exit(); //如果注册设备号失败就退出。这个有问题? return ret; } else { printk("globalvar register success!\n"); } //为设备分配内核空间 my_dev=kmalloc(sizeof(struct globalvar_dev), GFP_KERNEL); if(!my_dev) { ret=-ENOMEM; printk("create device failed!\n"); } else //初始化设备,添加设备 { my_dev->global_var=0; //设备变量初始化为0 cdev_init(&my_dev->cdev, &globalvar_fos); //初始化设备中的cdev结构 my_dev->cdev.owner=THIS_MODULE; //初始化cdev中的所有者字段 //向内核添加cdev结构,注意到此时用到了devno err=cdev_add(&my_dev->cdev, devno, 1); if(err<0) //如果添加字符设备失败,打印错误信息 printk("add charater device failure!\n"); else printk("add charater device success!\n"); } return ret; } //打开操作 int globalvar_open(struct inode *inode, struct file *filp) { struct globalvar_dev *dev; //根据inode结构的cdev字段,获得整个设备结构的指针 dev=container_of(inode->i_cdev, struct globalvar_dev, cdev); //分配并填写置于filp->private_data里的数据结构,private_data是跨系统调用时 //保存状态信息的非常有用的资源 filp->private_data=dev; return 0; } //释放操作 int globalvar_release(struct inode *inode, struct file *filp) { //为什么不释放filp->private_data??? return 0; } //读操作 ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off) { struct globalvar_dev *dev=filp->private_data; //获取已指向分配数据的指针 //将设备变量值复制到用户空间 if(copy_to_user(buf, &dev->global_var, sizeof(int))) return -EFAULT; return sizeof(int); //返回读取数据字节数 } //写操作 ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off) { struct globalvar_dev *dev=filp->private_data; //获取已指向分配数据的指针 //将用户空间值复制到设备变量 if(copy_from_user(&dev->global_var, buf, sizeof(int))) return -EFAULT; return sizeof(int); } module_init(globalvar_init); module_exit(globalvar_exit);
2、编写 Makefile 文件,内容如下:
obj-m:=globalvar.o KERNELDIR:=/usr/src/linux-headers-2.6.38-8-generic PWD:=$(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
3、当前工作目录开始编译模块
~/linux_study/CharacterDevice$ make
4、加载模块,并用 lsmod 或 dmesg 查看
~/linux_study/CharacterDevice$ sudo insmod globalvar.ko ~/linux_study/CharacterDevice$ lsmod Module Size Used by globalvar 12630 0 binfmt_misc 13213 1
5、查看动态生成的设备号(主设备号)
~/linux_study/CharacterDevice$ cat /proc/devices Character devices: ... 189 usb_device 226 drm 250 globalvar
6、使用 mknod 命令创建设备节点文件
~/linux_study/CharacterDevice$ sudo mknod /dev/globalvar c 250 0 ~/linux_study/CharacterDevice$ sudo chgrp staff /dev/globalvar ~/linux_study/CharacterDevice$ sudo chmod 664 /dev/globalvar
7、编写测试程序
//test.c #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> main() { int fd, num; //可读写方式打开设备 fd=open("/dev/globalvar", O_RDWR, S_IRWXU|S_IRWXG); if(fd!=-1) { read(fd, &num, sizeof(int)); //读取设备变量 printf("The globalvar is %d.\n", num); printf("Please input the num written to globalvar:\n"); scanf("%d", &num); write(fd, &num, sizeof(int)); //写设备变量 read(fd, &num, sizeof(int)); //再次读取设备变量 printf("The globalvar is %d now.\n", num); close(fd); //关闭设备文件 } else printf("Device open failure!\n"); }
8、编译 test.c
~/linux_study/CharacterDevice$ gcc -o test test.c ~/linux_study/CharacterDevice$ sudo ./test The globalvar is 0. Please input the num written to globalvar: 20 The globalvar is 20 now.
如果可以出现上面的的提示则证明字符设备驱动编写成功。
以上流程在内核2.6.38.8下调试通过。
浙公网安备 33010602011771号