[Linux驱动]字符设备驱动学习笔记(一)

一,主设备号和次设备号代表的含义?linu内核是如果根据主设备号找驱动,次设备号找设备的。

答:通常一个主设备号代表一个驱动,比如在block设备中,一个主设备号代表一个emmc设备,不同次设备号代表的是不同的分区

Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。内核维护者一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个次设备驱动程序组成的数组的指针(次设备共享主设备号)

二,编写字符设备的一般顺序

一,调用kmalloc memset函数对相关结构体(比如设备结构体)进行初始化的动作
二,注册相应的驱动,如平台驱动则调用platform_driver_register(&driver)进行注册,driver是一个全部静态变量。

[cpp] view plaincopy

  1. struct test_dev{
  2. struct cdev cdev;
  3. char *test_devname;
  4. char actname[32];
  5. unsigned int index;
  6. struct semphare sem;
  7. }
  8. static struct platform_driver test_driver = {
  9. .probe = test_probe,
  10. .remove = test_remove,
  11. .driver = {
  12. .name = "test_char",
  13. .owner = THIS_MODULE,
  14. },
  15. };
  16. #define TEST_NUM 10
  17. static struct test_dev *test_devices;
  18. static int __init test_init(void)
  19. {
  20. int result;
  21. test_devices=kmalloc(TEST_NUM*sizeof(struct test_dev),GFP_KERNEL);
  22. if(!test_devices)
  23. {
  24. result = -ENOMEM
  25. printk(“alloc test_devices fail\n”);
  26. goto fail_malloc;
  27. }
  28. memset(test_devices,0,TEST_NUM*sizeof(struct test_dev));
  29. result = platform_driver_register(&test_driver);
  30. if (result)
  31. {
  32. printk("fail to register test_driver");
  33. goto fail_driver_register;
  34. }
  35. return 0
  36. fail_driver_register:
  37. return result;
  38. fail_malloc:
  39. return result;
  40. }
  41. module_init(test_init);

三,接来下会调用到test_probe()函数,该函数首先alloc_chrdev_region()函数分配主设备号和次设备号,然后调用cdev_init()函数来注册真正的字符设备,void cdev_init(struct cdev*cdev,struct file_operations *fops)最后调用cdev_add()函数来告诉内核该结构体的信息init cdev_add(struct cdev*cdev,dev_t num,unsigned int count)

[cpp] view plaincopy

  1. int test_probe(struct platform_device *dev)
  2. {
  3. int result;
  4. dev_t devno;
  5. result = alloc_chrdev_region(&devno,0,TEST_NUM,"testchar");
  6. if(result<0)
  7. {
  8. printk();
  9. goto fail_alloc_chrdev;
  10. }
  11. major = MAJOR(devno);
  12. for(int i=0;i<TEST_NUM;I++)
  13. {
  14. devno=MKDEV(major,i);
  15. cdev_init(&test_devices[i].cdev,&test_fops);
  16. test_devices[i].cdev.owner = THIS_MODULE;
  17. test_devices[i].cdev.ops = &test_fops;
  18. result = cdev_add(&test_devices[i].cedv,devno,1);
  19. if(result)
  20. {
  21. printk("cdev add fail\n");
  22. goto fail_register_chrdev;
  23. }
  24. }
  25. return 0;
  26. fail_register_chrdev:
  27. cdev_del(&test_devices[i].cdev);
  28. unregister_chrdev_region(MKDEV(major,0),TEST_NUM);
  29. }

三,阻塞型I/O

如果在调用字符设备的read write方法中,设备没有准备好可能导致用户层要去读取的进程阻塞(默认),将其置入休眠直到请求可以继续。将进程置入休眠要注意的两点
(1)不要再原子上下文中进行休眠,驱动程序不能再拥有自旋锁,RCU锁时候休眠,拥有信号量的进程休眠是合法的,但是等待此信号量的其它进程也必须休眠,因此拥有信号量的进程休眠时间要足够短
(2)当进程唤醒的时候不知道发生过什么,所以检查以确保我们等待的条件真正为真。
让进程休眠的方法:
wait_event()相关的函数
唤醒进程方法
wake_up()相关的函数

如何实现非阻塞的I/O操作
答:填充filp->f_flags中的O_NONBLOCK flag,如果以非阻塞方式打开,如果此时设备没有就绪好的数据,那么会返回-EAGAIN错误。

四,设备文件的访问控制

1,让一个进程独享设备,通过维护一个原子变量

使用实例:adb的驱动程序,每次只让adbd一个进程使用该设备。

[cpp] view plaincopy

  1. static int scull_s_poen(struct inode*inode,struct file*filp)
  2. {
  3. struct scull_dev *dev = &scull_s_device;
  4. if(!atomic_dec_and_test(&scull_s_available)){
  5. atomic_inc(&scull_s_available);
  6. return -EBUSY;
  7. }
  8. if((filp->flags & O_ACCMODE)==O_WRONLY)
  9. SCULL_trim(dev);
  10. filp->priate_data=dev;
  11. return 0;
  12. }

2,限制每次只由一个用户访问

[cpp] view plaincopy

    1. spin_lock(&scull_u_lock);       //scull_u_lock是全局变量,所以用自旋锁,自选锁使用过程不能睡眠
    2. if(scull_u_count && (scull_u_owner != current->uid) &&(scull_u_owner != current->euid) && !capable(CAP_DAC_OVERRIDE) )
    3. {
    4. spin_unlock(&scull_u_lock);
    5. return -EBUSY;
    6. }
    7. if(scull_u_count == 0)
    8. {
    9. scull_u_owner=current->uid;  //第一个访问设备的用户为属主用户
    10. }
    11. scull_u_count++;
    12. spin_unlock(&scull_u_lock);
时间: 2024-12-19 14:54:56

[Linux驱动]字符设备驱动学习笔记(一)的相关文章

Linux实现字符设备驱动的基础步骤

Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现. 1.先在rootfs中的 /dev/ 下生成一个字符设备.注意主设备号 和 从设备号.可用例如以下shell脚本生成: if [ ! -e audioIN ];then sudo mknod audioIN c 240 0 fi 生成的设备为 /dev/audioIN ,主设备号240,从设备号0. 2.写audioINdriver.ko ,audioINdriver.c 基本代码

[Linux驱动]字符设备驱动学习笔记(二)———实例

一,注册字符设备 [cpp] view plaincopy #define GLOBALMEM_MAJOR 256 #define GLOBALMEM_SIZE 0X1000 //4k static int char_major=GLOBALMEM_MAJOR;//主设备号 struct chartest_dev { struct cdev cdev; unsigned char mem[GLOBALMEM_SIZE]; }; struct chartest_dev glob_char_dev;

Linux高级字符设备驱动 poll方法(select多路监控原理与实现)

1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程.      int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)     Select系统调用(参数)     1)Maxfd:           文件描述符的范围,比

linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int fd,unsinged long cmd,...) fd-文件描述符 cmd-对设备的发出的控制命令 ...表示这是一个可选的参数,存在与否依赖于cmd,如cmd为修改波特率,那么....就表示波特率的值.如果cmd表示关闭,则不需要参数 内核函数原型 file_operations结构体里面long

[Linux驱动]字符设备驱动学习笔记(三)———高级

一,ioctl使用实例 ioctl使用实例 驱动程序.h文件  memdev.h [cpp] view plaincopy /* 定义幻数 */ #define MEMDEV_IOC_MAGIC  'k' /* 定义命令 */ #define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1) #define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int) #define MEMDEV_IOCSETDATA _I

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

[kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关

linux驱动学习(1)——字符设备驱动开发

(一)驱动程序介绍 (a)Linux驱动程序学习 知识结构: 1. Linux驱动程序设计模式(40%) 2. 内核相关知识(30%) 3. 硬件相关知识(30%) (b)驱动分类: ①字符设备: 字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现 open, close,read和 write 系统调用. ②块设备: 在大部分的 Unix 系统, 块设备不能按字节处理数据,只能一次传送一个或多个长度是512字节( 或一个更大的 2 次幂的数 )的整块数据,而Lin

Linux嵌入式驱动学习之路⑩字符设备驱动-my_led

首先贴上代码: 字符设备驱动代码: /** *file name: led.c */#include <linux/sched.h> #include <linux/signal.h> #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> #include <linux/init