4.2 字符设备驱动之 字符设备的注册(第二种注册字符设备的方法) ----编写访问内核缓存的设备驱动

demo.c
1 /* head file */ 2 #include <linux/init.h> 3 #include <linux/module.h> 4 #include <linux/fs.h> 5 #include <linux/cdev.h> 6 #include <linux/uaccess.h> 7 #include <linux/slab.h> 8 9 #define DEVNAME "scull"//设备名:内存设备scull 10 #define CNT 200//创建次设备的数目 11 12 #define SZ 512 13 14 static unsigned int major = 0; 15 static unsigned int minor = 0; 16 static dev_t devnum;//定义字符设备名变量!是字符设备号吧!!! 17 18 struct scull_t { //定义对应字符设备的结构体 19 char *kbuf; /*指向用户要访问的内核空间的一块内存的地址*/ 20 size_t sz; //内核层数据大小 21 struct cdev cdev; 22 }*sculls; //struct scull_t *sculls; 23 24 static int mill_open (struct inode *inodp, struct file *filp) 25 { 26 //求该字符设备的结构体的地址,用于一套驱动多个设备 27 filp->private_data = container_of(inodp->i_cdev, struct scull_t, cdev); 28 29 return 0; 30 } 31 32 static ssize_t 33 mill_read (struct file *filp, char __user *buf, size_t cnt, loff_t *fpos) 34 { 35 struct scull_t *ptr = filp->private_data;//定义指向该结构体的一个指针 36 37 if (cnt == 0) { //用户层传下来数据字节数为0 38 return cnt; //错误返回 39 } 40 41 if (!ptr->sz) { //内核没有读到自己内存空间数据立即返回错误负值(无阻塞) 42 return -EINVAL; 43 } 44 45 cnt = min(cnt, ptr->sz);//得出用户层需要读取的数据字节数(比如512个字节)和内核实际字节数的最小值 46 //读操作:将内核空间ptr->kbuf上的数据传递cnt(从用户层传下来的)个到用户层buf 47 if (copy_to_user(buf, ptr->kbuf, cnt)) { 48 return -EINVAL; 49 } 50 51 //后面挪到前面去读 52 memmove(ptr->kbuf, ptr->kbuf + cnt, ptr->sz - cnt); 53 ptr->sz -= cnt;//更新内核指定空间区域的数据字节数/每读一次减一次cnt个字节(比如512个字节) 54 55 return cnt;//返回从内核读取的数据字节数 56 } 57 58 static ssize_t 59 mill_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *fpos) 60 { 61 struct scull_t *ptr = filp->private_data; 62 63 if (cnt == 0) { //从内核层传下来要写的数据字节数为0/不对了。。。 64 return cnt; //返回0个/错误返回 65 } 66 67 if (ptr->sz == SZ) { //若内核层内存空间数据等于SZ,则满了,不能写了。。 68 return -EINVAL; 69 } 70 71 cnt = min(cnt, SZ- ptr->sz);//得出用户层需要写的数据字节数(比如512个字节)和内核实际字节数的最小值 72 73 //写操作:从用户层传递数据到内核指定空间 74 if (copy_from_user(ptr->kbuf+ptr->sz, buf, cnt)) { 75 return -EINVAL; 76 } 77 78 ptr->sz += cnt;//更新内核层内存空间数据字节数 79 //ptr->sz = ptr->sz + cnt; 80 81 return cnt; 82 } 83 84 static int mill_release (struct inode *inodp, struct file *filp) 85 { 86 87 return 0; 88 } 89 90 static struct file_operations fops = { //驱动函数集 91 .owner = THIS_MODULE, 92 .open = mill_open, 93 .read = mill_read, 94 .write = mill_write, 95 .release = mill_release, 96 }; 97 98 99 /* driver module entry */ 100 static int __init demo_init(void) 101 { 102 int ret; 103 int i; 104 105 /*首先*/ 106 if (!major) { 107 //第二种申请设备号方法:内核自动(动态)申请设备号 108 ret = alloc_chrdev_region(&devnum, minor, CNT, DEVNAME); 109 major = MAJOR(devnum); 110 }else { 111 devnum = MKDEV(major, minor); 112 //注册字符设备驱动 113 ret = register_chrdev_region(devnum, CNT, DEVNAME); 114 } 115 116 if (ret < 0) { 117 return -EINVAL; 118 } 119 120 //给struct scull_t 申请空间 121 sculls = kzalloc(CNT*sizeof(*sculls), GFP_KERNEL); 122 if (NULL == sculls) { 123 ret = -ENOMEM; //申请失败返回-ENOMEM 124 goto error0;//跳到去执行移除字符设备驱动 125 } 126 127 /*其次*/ 128 //给每个次设备申请地址空间 129 for (i = 0; i < CNT; ++i) { 130 sculls[i].kbuf = kzalloc(SZ, GFP_KERNEL); 131 if (NULL == sculls[i].kbuf) { 132 ret = -ENOMEM;//申请失败返回-ENOMEM 133 goto error1;// 134 } 135 136 cdev_init(&sculls[i].cdev, &fops);//字符设备次设备初始化 137 ret = cdev_add(&sculls[i].cdev, devnum+i, CNT);//增加200个次设备/ 138 if (ret < 0) { 139 kfree(sculls[i].kbuf);//最后释放一次第200个次设备的空间 140 goto error1; 141 } 142 } 143 144 return 0; 145 146 error1: 147 //释放所有199个次设备之前的空间 148 while (i--) { 149 kfree(sculls[i].kbuf); 150 cdev_del(&sculls[i].cdev);//删除所有200个次设备 151 } 152 //释放大的struct scull_t 结构体之前的空间 153 kfree(sculls); 154 155 error0: 156 //移除之前注册的字符设备驱动 157 unregister_chrdev_region(devnum, CNT); 158 159 return ret; 160 } 161 162 module_init(demo_init); 163 164 /* driver module exit */ 165 static void __exit demo_exit(void) 166 { 167 int i; 168 //移除驱动模块时:释放所有次设备之前的空间 169 for (i = 0; i < CNT; ++i) { 170 kfree(sculls[i].kbuf); 171 cdev_del(&sculls[i].cdev); 172 } 173 //释放大的struct scull_t 结构体之前的空间 174 kfree(sculls); 175 //移除之前注册的字符设备驱动 176 unregister_chrdev_region(devnum, CNT); 177 } 178 module_exit(demo_exit); 179 180 /* driver module description */ 181 MODULE_LICENSE("GPL"); 182 183 MODULE_AUTHOR("millet9527"); 184 MODULE_VERSION("millet plus 18"); 185 MODULE_DESCRIPTION("example for driver module arch");
makefile
1 obj-m := demo.o 2 3 KERNEL := /linux-3.5 4 5 all: 6 make -C $(KERNEL) M=`pwd` 7 clean: 8 make -C $(KERNEL) M=`pwd` clean 9
====================================================================
write.c
1 #include <fcntl.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/stat.h> 6 #include <assert.h> 7 8 #define LEN 512 9 10 int main(int argc, char **argv) 11 { 12 int ret; 13 14 if (argc != 3) { 15 fprintf(stderr, "Usage: %s devfile file\n", argv[0]); 16 exit(1); 17 } 18 19 int fd_dev = open(argv[1], O_WRONLY | O_NDELAY); 20 assert(fd_dev > 0); 21 22 int fd_file = open(argv[2], O_RDONLY | O_NDELAY); 23 assert(fd_file > 0); 24 25 char buf[LEN]; 26 ret = read(fd_file, buf, LEN);// 27 assert(ret > 0); 28 29 ret = write(fd_dev, buf, ret);//写 30 assert(ret >= 0); 31 32 return 0; 33 }
read.c
1 #include <fcntl.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/stat.h> 6 #include <assert.h> 7 8 #define LEN 512 9 10 int main(int argc, char **argv) 11 { 12 int ret; 13 14 if (argc != 2) { 15 fprintf(stderr, "Usage: %s devfile\n", argv[0]); 16 exit(1); 17 } 18 19 int fd = open(argv[1], O_RDONLY | O_NDELAY);//非阻塞型读取方式 20 assert(fd > 0); 21 22 char buf[LEN]; 23 24 ret = read(fd, buf, LEN);//只读512个字节 25 assert(fd > 0); 26 27 buf[ret] = '\0';//读完了,读到的字节数据末尾加'\0' 28 printf("%s\n", buf); 29 30 return 0; 31 }
makefile
1 CC=arm-linux-gcc





 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号