LINUX驱动编程 scull 字符驱动 第一个可以使用的驱动(源码)
代码说明:通过修改函数中file_operations 来实现不同结构dev结构
三种分别是 小型结构 dev结构只接了 一个字符数组(量子)(测试通过 可以读写)
中型结构 dev结构接链表(量子表)(测试通过 可以读写)
大型结构 dev结构接链表(量子集)(测试不通过 是LDD3 原版代码 自行查找修改 根据修改小型结构的经验问题出在第一次open时trim函数的初始化)
编译会产生 warning 因为 选择一种结构 另外两个结构就是未使用,有兴趣可以自行修改成参数选择的方式。(位于544 546两行 修改对应的 xx_fops就可以使用 前面的代码命名很乱,尽量不要修改底层)
驱动在 内核3.160-30下编译通过
1 #include <linux/init.h> 2 #include <linux/module.h> 3 4 #include <linux/errno.h>// error code 5 #include <linux/types.h>//for struct dev_t 6 #include <linux/kdev_t.h> 7 #include <linux/fs.h>//for register major and minor 8 #include <linux/cdev.h>//for [zi fu] device , struct cdev 9 #include <linux/kernel.h>//for open() 10 #include <linux/slab.h>//for kfree() 11 #include <asm/uaccess.h>//for copy() 12 #include <linux/mutex.h> 13 #include <asm/switch_to.h> 14 15 16 #ifndef DF_QUANTUM 17 #define DF_QUANTUM 1024 18 #endif // DF_QUANTUM 自己使用 完整代码 19 20 #ifndef DF_QSET 21 #define DF_QSET 1000 22 #endif // DF_QSET 自己使用 完整代码 23 24 #ifndef SCLL_DEV_MEM_LEN 25 #define SCLL_DEV_MEM_LEN 4000 26 #endif 27 28 29 MODULE_LICENSE("Dual BSD/GPL"); 30 31 32 33 int maj=0,minx=0;// for unregister 34 35 int pk_times=0; 36 37 38 int pk_add(int n) 39 { 40 41 printk(KERN_ALERT"p_w\n"); 42 if(n>=2048) 43 { 44 printk(KERN_ALERT"write 2048! \n"); 45 return 0; 46 } 47 else 48 return n+1; 49 } 50 51 52 53 //---------------struct for cde--------------------------------------------- 54 struct scull_qset{ 55 void **data; 56 struct scull_qset *next; 57 }; 58 struct sc_qset//中级结构 二级测试使用 59 { 60 char *data; 61 struct sc_qset *next; 62 }; 63 struct Hello_dev{ 64 struct scull_qset *data;//大型复杂结构 未测 65 char *test_data;//小型简单结构 测试通过 可以使用(未测试bug 数据量超过 4/8 * 4000 可能崩溃) 66 struct sc_qset *head;//中级结构 测试通过 67 int quantum; 68 int qset; 69 unsigned long size; 70 unsigned long access_key; 71 struct semaphore sem; 72 struct cdev cdev; 73 }; 74 75 struct Hello_dev *my_dev; 76 77 78 //========================================================================== 79 //--------------------------functions for fops------------------------------- 80 int char_data_trim(struct Hello_dev *dev) 81 { 82 if (!dev) 83 return -1; 84 85 printk(KERN_ALERT"do kfree %p\n",dev->test_data); 86 87 if(dev->test_data) 88 kfree(dev->test_data); 89 90 printk(KERN_ALERT"finish kfree \n"); 91 92 dev->size = 0 ; 93 dev->quantum = DF_QUANTUM ; 94 dev->qset = DF_QSET ; 95 dev->data = NULL ; 96 dev->test_data = NULL ; 97 98 printk(KERN_ALERT"finish trim \n"); 99 return 0; 100 101 } 102 103 int zj_trim(struct Hello_dev *dev) 104 { 105 106 struct sc_qset *dir,*temp;//指针代换 107 108 109 if(!dev) 110 return 0; 111 112 dir = dev->head; 113 114 while(dir) //清除链表 115 { 116 if(dir->data) 117 kfree(dir->data);//如果分配了量子 那么清除 118 119 temp=dir; //下一个循环 120 dir=dir->next; 121 kfree(temp);//清除当前量子集 122 } 123 124 dev->size = 0 ; 125 dev->quantum = DF_QUANTUM ; 126 dev->qset = DF_QSET ; 127 dev->data = NULL ; 128 dev->test_data = NULL ; 129 dev->head = NULL; 130 return 0; 131 } 132 133 int h_trim(struct Hello_dev *dev) 134 { 135 struct scull_qset *next,*dptr; 136 int qset = dev->qset; 137 int i; 138 for (dptr=dev->data;dptr;dptr=next) 139 { 140 if(dptr->data) 141 { 142 for(i=0;i<qset;i++) 143 kfree(dptr->data[i]); 144 kfree(dptr->data); 145 dptr->data = NULL; 146 } 147 next = dptr->next; 148 kfree(dptr); 149 } 150 dev->size = 0; 151 dev->quantum = DF_QUANTUM; 152 dev->qset = DF_QSET; 153 dev->data = NULL; 154 return 0; 155 156 } 157 static struct sc_qset *zj_follow(struct Hello_dev *dev,int n) 158 { 159 struct sc_qset *qs; 160 161 qs =dev->head; 162 163 if(!qs) 164 { 165 qs = dev->head = kmalloc(sizeof(struct sc_qset),GFP_KERNEL); 166 if(!qs) 167 return NULL; 168 memset(qs,0,sizeof(struct sc_qset)); 169 } 170 while (n--) 171 { 172 if(!qs->next) 173 { 174 qs->next = kmalloc(sizeof(struct sc_qset),GFP_KERNEL); 175 if(!qs) 176 return NULL; 177 memset(qs->next,0,sizeof(struct sc_qset)); 178 } 179 qs=qs->next; 180 continue; 181 } 182 return qs; 183 184 } 185 static struct scull_qset *h_follow(struct Hello_dev *dev,int n) 186 { 187 struct scull_qset *qs = dev->data; 188 189 if(!qs)//if qs dont exist 190 { 191 qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL); 192 if(qs==NULL) 193 return NULL; 194 memset(qs,0,sizeof(struct scull_qset)); 195 } 196 while(n--) 197 { 198 if(!qs->next) 199 { 200 qs->next = kmalloc(sizeof(struct scull_qset),GFP_KERNEL); 201 if(qs->next == NULL) 202 return NULL; 203 memset(qs->next,0,sizeof(struct scull_qset)); 204 } 205 qs = qs->next; 206 continue; 207 } 208 return qs; 209 } 210 211 //************************************ 212 // Method: h_open 213 // FullName: h_open 214 // Access: public static 215 // Returns: int 216 // Qualifier: for Hello_module , fix it later 217 // Parameter: struct inode * inode 218 // Parameter: struct file * filp 219 //************************************ 220 static int h_open(struct inode *inode,struct file *filp) 221 { 222 struct Hello_dev *temp_h_dev; 223 temp_h_dev = container_of(inode->i_cdev,struct Hello_dev,cdev); 224 filp->private_data = temp_h_dev;// for other methods 225 if((filp->f_flags & O_ACCMODE) == O_WRONLY) 226 { 227 228 h_trim(temp_h_dev); 229 230 } 231 return 0; 232 } 233 234 static int sc_open(struct inode *inode,struct file *filp) 235 { 236 struct Hello_dev *temp_h_dev; 237 temp_h_dev = container_of(inode->i_cdev,struct Hello_dev,cdev); 238 filp->private_data = temp_h_dev;// for other methods 239 240 printk(KERN_ALERT" p of (temp) my_dev : %p \n",temp_h_dev); 241 242 if((filp->f_flags & O_ACCMODE) == O_WRONLY) 243 { 244 printk(KERN_ALERT" do trim \n"); 245 char_data_trim(temp_h_dev); /* ignore errors */ 246 } 247 248 printk(KERN_ALERT"finish op successfully!\n"); 249 return 0; 250 } 251 252 static int zj_open(struct inode *inode,struct file *filp) 253 { 254 struct Hello_dev *temp_h_dev; 255 temp_h_dev = container_of(inode->i_cdev,struct Hello_dev,cdev); 256 filp->private_data = temp_h_dev;// for other methods 257 258 if((filp->f_flags & O_ACCMODE) == O_WRONLY) 259 { 260 zj_trim(temp_h_dev); /* ignore errors */ 261 } 262 263 printk(KERN_ALERT"finish zj op successfully!\n"); 264 return 0; 265 } 266 static int h_relaese(struct inode *inode,struct file *filp) 267 { 268 return 0; 269 } 270 271 //测试函数正确性 内存泄漏解决 测试存储结构 272 static ssize_t sc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 273 { 274 struct Hello_dev *dev = filp->private_data; 275 ssize_t retval = 0; 276 277 278 if (down_interruptible(&dev->sem)) 279 return -ERESTARTSYS; 280 if (*f_pos >= dev->size) 281 goto out; 282 if (*f_pos + count > dev->size) 283 count = dev->size - (*f_pos); 284 if (copy_to_user(buf, dev->test_data, count)) { 285 retval = -EFAULT; 286 goto out; 287 } 288 *f_pos += count; 289 retval = count; 290 printk(KERN_WARNING "DEBUG read \n"); 291 out: 292 up(&dev->sem); 293 return retval; 294 } 295 static ssize_t zj_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 296 { 297 int x=0,y=0;//x是链表对应位置 从0开始 y是数组对应位置 298 struct Hello_dev *dev = filp->private_data; 299 ssize_t retval = 0; 300 struct sc_qset *dir ; 301 x=(long)*f_pos/DF_QSET; //这个位置就是开始读写的位置,第一个字符从这个位置开始读 302 y=(long)*f_pos%DF_QSET; 303 304 if (down_interruptible(&dev->sem)) 305 return -ERESTARTSYS; 306 307 printk(KERN_ALERT"read 1 || x: %d ; y: %d \n",x,y); 308 309 dir = zj_follow(dev,x); 310 311 312 if(!dir) 313 goto out; 314 315 //检查读取位置是否还在设备内部 并且读取位置上有数据 316 if (*f_pos >= dev->size || !dir->data) 317 goto out; 318 319 //超出设备部分不读取 320 if (*f_pos + count > dev->size) 321 count = dev->size - (*f_pos); 322 323 printk(KERN_ALERT"read 2 || %d\n",count); 324 325 //超出量子部分不读取 326 if(y + count > DF_QSET) 327 count = DF_QSET - y ; 328 329 printk(KERN_ALERT"read 3 || %d\n",count); 330 331 //复制到用户空间 332 if (copy_to_user(buf, dir->data+y, count)) { 333 retval = -EFAULT; 334 printk(KERN_ALERT"read 4 ||read failed count = %d ,*f_pos = %d \n",(int)count,(int)*f_pos); 335 goto out; 336 } 337 338 *f_pos += count; 339 retval = count; 340 printk(KERN_WARNING "DEBUG zj read || x = %d , y = %d ,count = %d ,*f_pos = %d \n",x,y,(int)count,(int)*f_pos); 341 out: 342 up(&dev->sem); 343 return retval; 344 } 345 static ssize_t sc_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 346 { 347 struct Hello_dev *dev = filp->private_data; 348 ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ 349 350 if (down_interruptible(&dev->sem)) 351 return -ERESTARTSYS; 352 353 354 if (!dev->test_data){ 355 dev->test_data = kmalloc(SCLL_DEV_MEM_LEN * sizeof(char *), GFP_KERNEL); 356 if (!dev->test_data) 357 goto out; 358 memset(dev->test_data, 0, SCLL_DEV_MEM_LEN * sizeof(char *)); 359 } 360 361 if (copy_from_user(dev->test_data, buf, count)) { 362 retval = -EFAULT; 363 goto out; 364 } 365 *f_pos += count; 366 retval = count; 367 368 printk(KERN_WARNING "DEBUG write \n"); 369 /* update the size */ 370 if (dev->size < *f_pos) 371 dev->size = *f_pos; 372 373 374 out: 375 up(&dev->sem); 376 return retval; 377 } 378 379 static ssize_t zj_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 380 { 381 int x=0,y=0;//x是链表对应位置 从0开始 y是数组对应位置 382 struct Hello_dev *dev = filp->private_data; 383 ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ 384 struct sc_qset *dir ; 385 x=(long)*f_pos/DF_QSET; //这个位置就是开始读写的位置,第一个字符从这个位置开始读 386 y=(long)*f_pos%DF_QSET; 387 388 if (down_interruptible(&dev->sem)) 389 return -ERESTARTSYS; 390 391 dir = zj_follow(dev,x); 392 393 if(!dir) 394 goto out; 395 396 if (!dir->data) 397 { 398 dir->data = kmalloc(DF_QSET * sizeof(char), GFP_KERNEL); 399 if(dir->data) 400 memset(dir->data, 0, DF_QSET * sizeof(char)); 401 } 402 403 if( y + count > DF_QSET) 404 count=DF_QSET - y; 405 406 if (copy_from_user(dir->data+y, buf, count)) { 407 retval = -EFAULT; 408 goto out; 409 } 410 *f_pos += count; 411 retval = count; 412 413 414 /* update the size */ 415 if (dev->size < *f_pos) 416 dev->size = *f_pos; 417 out: 418 up(&dev->sem); 419 return retval; 420 } 421 422 static ssize_t h_read(struct file *filp,char __user *buff,size_t count,loff_t *f_pos) 423 { 424 struct Hello_dev *dev = filp->private_data; 425 struct scull_qset *dptr; 426 int quantum = dev->quantum; 427 int qset =dev->qset; 428 int item_size =quantum * qset; 429 int item,s_pos,q_pos,reset; 430 ssize_t retval = 0 ; 431 432 if(down_interruptible(&dev->sem)) 433 return -ERESTARTSYS; 434 if(*f_pos >= dev->size) 435 goto out; 436 if(*f_pos + count > dev->size) 437 count = dev->size - *f_pos; 438 item = (long)*f_pos / item_size; 439 reset = (long)*f_pos % item_size; 440 s_pos = reset / quantum; 441 q_pos = reset % quantum; 442 443 dptr = h_follow(dev,item); 444 445 if(dptr == NULL || ! dptr->data || ! dptr->data[s_pos]) 446 goto out; 447 if(count >quantum-q_pos) 448 count = quantum -q_pos; 449 if(copy_to_user(buff,dptr->data[s_pos] + q_pos,count)) 450 { 451 retval = -EFAULT; 452 goto out; 453 } 454 *f_pos += count; 455 retval = count; 456 out: 457 up(&dev->sem); 458 return retval; 459 } 460 static ssize_t h_write(struct file *filp,const char __user *buf ,size_t count ,loff_t *f_pos) 461 { 462 struct Hello_dev *dev = filp->private_data; 463 struct scull_qset *dptr; 464 int quantum = dev->quantum; 465 int qset =dev->qset; 466 int item_size =quantum * qset; 467 int item,s_pos,q_pos,reset; 468 ssize_t retval = -ENOMEM; 469 if(down_interruptible(&dev->sem)) 470 return -ERESTARTSYS; 471 item = (long)*f_pos / item_size; 472 reset = (long)*f_pos % item_size; 473 s_pos = reset / quantum; 474 q_pos = reset % quantum; 475 476 dptr = h_follow(dev,item); 477 if(dptr == NULL) 478 goto out; 479 if(!dptr->data) 480 { 481 dptr->data = kmalloc(qset * sizeof(char *),GFP_KERNEL); 482 if(!dptr->data) 483 goto out; 484 memset(dptr->data,0,qset*sizeof(char *)); 485 } 486 if(!dptr->data[s_pos]) 487 { 488 dptr->data[s_pos] = kmalloc(quantum,GFP_KERNEL); 489 if(!dptr->data[s_pos]) 490 goto out; 491 } 492 if(count > quantum -q_pos) 493 count =quantum -q_pos; 494 if(copy_to_user(dptr->data[s_pos]+q_pos,buf,count)) 495 { 496 retval = -EFAULT; 497 goto out; 498 499 } 500 pk_times=pk_add(pk_times); 501 502 *f_pos += count; 503 retval = count; 504 505 if(dev->size < *f_pos) 506 dev->size = *f_pos; 507 out: 508 up(&dev->sem); 509 return retval; 510 511 } 512 //=========================================================================== 513 //---------------struct for fops-------------------------------------------- 514 static struct file_operations Hello_fops= 515 { 516 .owner = THIS_MODULE, 517 .read = h_read, 518 .write = h_write, 519 .open = h_open, 520 .release = h_relaese, 521 }; 522 static struct file_operations sc_fops= 523 { 524 .owner = THIS_MODULE, 525 .read = sc_read, 526 .write = sc_write, 527 .open = sc_open, 528 .release = h_relaese, 529 }; 530 static struct file_operations zj_fops= 531 { 532 .owner = THIS_MODULE, 533 .read = zj_read, 534 .write = zj_write, 535 .open = zj_open, 536 .release = h_relaese, 537 }; 538 //========================================================================== 539 //----------------functions for dev setup----------------------------------- 540 static int holle_setup_cdev(struct Hello_dev *dev , int index) 541 { 542 int err,first = MKDEV(maj,minx + index); 543 sema_init(&dev->sem,1); 544 cdev_init(&dev->cdev,&zj_fops); 545 dev->cdev.owner = THIS_MODULE; 546 dev->cdev.ops = &zj_fops; 547 err = cdev_add(&dev->cdev,first,1); 548 if(err) 549 printk(KERN_ALERT"fail to add %d,err code %d \n",index,err); 550 else 551 printk(KERN_ALERT"add successfully !!!! \n"); 552 return 0; 553 } 554 //=========================================================================== 555 static void hello_exit(void) 556 { 557 if(!my_dev) 558 { 559 char_data_trim(my_dev); 560 cdev_del(&my_dev->cdev); 561 kfree(my_dev); 562 } 563 unregister_chrdev_region(MKDEV(maj,minx),1); 564 printk(KERN_ALERT"unreg successfully !!!! \n"); 565 printk(KERN_ALERT"==================goodbye,cruel world!==================\n"); 566 } 567 568 569 static int hello_init(void) 570 { 571 char name[]="hello"; //设备名称 572 dev_t sec; 573 int isregsu=0,isregsu_a=0; 574 575 printk(KERN_ALERT"-----------------Hello world-----------------\n"); 576 577 578 // 申请设备的内存空间 并初始化地址 579 my_dev = kmalloc(sizeof(struct Hello_dev),GFP_KERNEL); 580 my_dev->data=NULL;// 此处必须清指针,如果不清指针kfree必崩溃 。且menset不行,原因不明白 581 my_dev->test_data=NULL; // 同上 可能是随机的指针指向了不能访问的内存页导致的 582 my_dev->head=NULL; 583 584 if(!my_dev) 585 { 586 return -12; 587 } 588 589 // 动态申请设备号 此处未提供静态设备号接口 590 isregsu_a = alloc_chrdev_region(&sec,0,1,name); 591 592 if(isregsu_a==0) 593 printk(KERN_ALERT"reg successfully !!!! \n"); 594 else 595 { 596 printk(KERN_ALERT"fail to reg,error code %d !!\n",isregsu_a); 597 return -1; 598 } 599 600 maj = MAJOR(sec); 601 minx = MINOR(sec); 602 603 604 // 向内核注册设备 605 isregsu_a = holle_setup_cdev(my_dev,0); 606 if(!isregsu_a) 607 printk(KERN_ALERT"setup dev ,op code %d !!\n",isregsu_a); 608 else 609 { 610 printk(KERN_ALERT"fail to setup,op code %d !!",isregsu_a); 611 goto fail_out; 612 } 613 return 0; 614 615 fail_out:// 初始化失败出口函数 616 hello_exit(); 617 return isregsu; 618 619 } 620 621 622 623 624 625 626 627 module_init(hello_init); 628 module_exit(hello_exit);

浙公网安备 33010602011771号