字符数神奇驱动程序的使用
1、编译/安装驱动
(1)Makefile编写
obj-m := memdev.o KDIR := /home/S5-driver/lesson7/linux-tiny6410/ all: make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
(2)编写内核程序文件
#include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/uaccess.h> int dev1_registers[5]; //模拟出来两个寄存器进行操作 int dev2_registers[5]; struct cdev cdev; dev_t devno; /*文件打开函数*/ int mem_open(struct inode *inode, struct file *filp) { /*获取次设备号*/ int num = MINOR(inode->i_rdev); if (num==0) filp->private_data = dev1_registers; else if(num == 1) filp->private_data = dev2_registers; else return -ENODEV; //无效的次设备号 return 0; } /*文件释放函数*/ int mem_release(struct inode *inode, struct file *filp) { return 0; } /*读函数*/ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/ /*判断读位置是否有效*/ if (p >= 5*sizeof(int)) return 0; if (count > 5*sizeof(int) - p) count = 5*sizeof(int) - p; /*读数据到用户空间*/ if (copy_to_user(buf, register_addr+p, count)) { ret = -EFAULT; } else { *ppos += count; ret = count; } return ret; } /*写函数*/ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /*获取设备的寄存器地址*/ /*分析和获取有效的写长度*/ if (p >= 5*sizeof(int)) return 0; if (count > 5*sizeof(int) - p) count = 5*sizeof(int) - p; /*从用户空间写入数据*/ if (copy_from_user(register_addr + p, buf, count)) ret = -EFAULT; else { *ppos += count; ret = count; } return ret; } /* seek文件定位函数 */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos; switch(whence) { case SEEK_SET: newpos = offset; break; case SEEK_CUR: newpos = filp->f_pos + offset; break; case SEEK_END: newpos = 5*sizeof(int)-1 + offset; break; default: return -EINVAL; } if ((newpos<0) || (newpos>5*sizeof(int))) return -EINVAL; filp->f_pos = newpos; return newpos; } /*文件操作结构体*/ static const struct file_operations mem_fops = { .llseek = mem_llseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, }; /*设备驱动模块加载函数*/ static int memdev_init(void) { /*初始化cdev结构*/ cdev_init(&cdev, &mem_fops); /* 注册字符设备 */ alloc_chrdev_region(&devno, 0, 2, "memdev"); cdev_add(&cdev, devno, 2); } /*模块卸载函数*/ static void memdev_exit(void) { cdev_del(&cdev); /*注销设备*/ unregister_chrdev_region(devno, 2); /*释放设备号*/ } MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
2、创建设备文件
(3)直接make即可生成memdev.ko
(4)insmod memdev.ko
(5)rmmod memdev.ko
(6)应用程序如何找到这个设备驱动程序呢??
字符设备文件是连接用户态和内核态的桥梁。用户态可以把数据发送给设备文件,设备文件再发送控制给内核驱动。
创建字符设备文件的方式:、
①mknod /dev/123456 c 主设备号 次设备号
②使用函数在驱动程序中创建。
③如何查看驱动程序的主设备号呢?
cat /proc/devices
3、访问设备
(7)编写用户态程序
write-mem.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = 0; int src = 2013; /*打开设备文件*/ fd = open("/dev/memdev0",O_RDWR); /*写入数据*/ write(fd, &src, sizeof(int)); /*关闭设备*/ close(fd); return 0; }
注意,交叉编译的时候需要使用静态编译链接的方式。
read-mem.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = 0; int dst = 0; /*打开设备文件*/ fd = open("/dev/memdev0",O_RDWR); /*写入数据*/ read(fd, &dst, sizeof(int)); printf("dst is %d\n",dst); /*关闭设备*/ close(fd); return 0; }