[Linux驱动]字符设备驱动学习笔记(一)
2014-05-27 14:59
495 查看
一,主设备号和次设备号代表的含义?linu内核是如果根据主设备号找驱动,次设备号找设备的。
答:通常一个主设备号代表一个驱动,比如在block设备中,一个主设备号代表一个emmc设备,不同次设备号代表的是不同的分区
Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。内核维护者一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个次设备驱动程序组成的数组的指针(次设备共享主设备号)
二,编写字符设备的一般顺序
一,调用kmalloc memset函数对相关结构体(比如设备结构体)进行初始化的动作
二,注册相应的驱动,如平台驱动则调用platform_driver_register(&driver)进行注册,driver是一个全部静态变量。
三,接来下会调用到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)
三,阻塞型I/O
如果在调用字符设备的read write方法中,设备没有准备好可能导致用户层要去读取的进程阻塞(默认),将其置入休眠直到请求可以继续。将进程置入休眠要注意的两点
(1)不要再原子上下文中进行休眠,驱动程序不能再拥有自旋锁,RCU锁时候休眠,拥有信号量的进程休眠是合法的,但是等待此信号量的其它进程也必须休眠,因此拥有信号量的进程休眠时间要足够短
(2)当进程唤醒的时候不知道发生过什么,所以检查以确保我们等待的条件真正为真。
让进程休眠的方法:
wait_event()相关的函数
唤醒进程方法
wake_up()相关的函数
如何实现非阻塞的I/O操作
答:填充filp->f_flags中的O_NONBLOCK flag,如果以非阻塞方式打开,如果此时设备没有就绪好的数据,那么会返回-EAGAIN错误。
四,设备文件的访问控制
1,让一个进程独享设备,通过维护一个原子变量
使用实例:adb的驱动程序,每次只让adbd一个进程使用该设备。
2,限制每次只由一个用户访问
答:通常一个主设备号代表一个驱动,比如在block设备中,一个主设备号代表一个emmc设备,不同次设备号代表的是不同的分区
Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。内核维护者一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个次设备驱动程序组成的数组的指针(次设备共享主设备号)
二,编写字符设备的一般顺序
一,调用kmalloc memset函数对相关结构体(比如设备结构体)进行初始化的动作
二,注册相应的驱动,如平台驱动则调用platform_driver_register(&driver)进行注册,driver是一个全部静态变量。
struct test_dev{ struct cdev cdev; char *test_devname; char actname[32]; unsigned int index; struct semphare sem; } static struct platform_driver test_driver = { .probe = test_probe, .remove = test_remove, .driver = { .name = "test_char", .owner = THIS_MODULE, }, }; #define TEST_NUM 10 static struct test_dev *test_devices; static int __init test_init(void) { int result; test_devices=kmalloc(TEST_NUM*sizeof(struct test_dev),GFP_KERNEL); if(!test_devices) { result = -ENOMEM printk(“alloc test_devices fail\n”); goto fail_malloc; } memset(test_devices,0,TEST_NUM*sizeof(struct test_dev)); result = platform_driver_register(&test_driver); if (result) { printk("fail to register test_driver"); goto fail_driver_register; } return 0 fail_driver_register: return result; fail_malloc: return result; } 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)
int test_probe(struct platform_device *dev) { int result; dev_t devno; result = alloc_chrdev_region(&devno,0,TEST_NUM,"testchar"); if(result<0) { printk(); goto fail_alloc_chrdev; } major = MAJOR(devno); for(int i=0;i<TEST_NUM;I++) { devno=MKDEV(major,i); cdev_init(&test_devices[i].cdev,&test_fops); test_devices[i].cdev.owner = THIS_MODULE; test_devices[i].cdev.ops = &test_fops; result = cdev_add(&test_devices[i].cedv,devno,1); if(result) { printk("cdev add fail\n"); goto fail_register_chrdev; } } return 0; fail_register_chrdev: cdev_del(&test_devices[i].cdev); unregister_chrdev_region(MKDEV(major,0),TEST_NUM); }
三,阻塞型I/O
如果在调用字符设备的read write方法中,设备没有准备好可能导致用户层要去读取的进程阻塞(默认),将其置入休眠直到请求可以继续。将进程置入休眠要注意的两点
(1)不要再原子上下文中进行休眠,驱动程序不能再拥有自旋锁,RCU锁时候休眠,拥有信号量的进程休眠是合法的,但是等待此信号量的其它进程也必须休眠,因此拥有信号量的进程休眠时间要足够短
(2)当进程唤醒的时候不知道发生过什么,所以检查以确保我们等待的条件真正为真。
让进程休眠的方法:
wait_event()相关的函数
唤醒进程方法
wake_up()相关的函数
如何实现非阻塞的I/O操作
答:填充filp->f_flags中的O_NONBLOCK flag,如果以非阻塞方式打开,如果此时设备没有就绪好的数据,那么会返回-EAGAIN错误。
四,设备文件的访问控制
1,让一个进程独享设备,通过维护一个原子变量
使用实例:adb的驱动程序,每次只让adbd一个进程使用该设备。
static int scull_s_poen(struct inode*inode,struct file*filp) { struct scull_dev *dev = &scull_s_device; if(!atomic_dec_and_test(&scull_s_available)){ atomic_inc(&scull_s_available); return -EBUSY; } if((filp->flags & O_ACCMODE)==O_WRONLY) SCULL_trim(dev); filp->priate_data=dev; return 0; }
2,限制每次只由一个用户访问
spin_lock(&scull_u_lock); //scull_u_lock是全局变量,所以用自旋锁,自选锁使用过程不能睡眠 if(scull_u_count && (scull_u_owner != current->uid) &&(scull_u_owner != current->euid) && !capable(CAP_DAC_OVERRIDE) ) { spin_unlock(&scull_u_lock); return -EBUSY; } if(scull_u_count == 0) { scull_u_owner=current->uid; //第一个访问设备的用户为属主用户 } scull_u_count++; spin_unlock(&scull_u_lock);
相关文章推荐
- Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动
- linux字符设备驱动学习笔记(一):简单的字符设备驱动
- [Linux驱动]字符设备驱动学习笔记(三)———高级
- 嵌入式linux学习笔记4之字符设备驱动
- linux字符设备驱动学习笔记2
- [Linux驱动]字符设备驱动学习笔记(三)———高级
- linux字符设备驱动学习笔记1
- [Linux驱动]字符设备驱动学习笔记(二)———实例
- linux字符设备驱动-重新学习-笔记-1
- Linux设备驱动程序学习笔记 高级字符驱动学习--阻塞型I/0
- linux字符设备驱动学习笔记3
- linux字符设备驱动-重新学习-笔记-2
- linux 学习笔记--字符设备驱动相关数据结构
- [Linux驱动]字符设备驱动学习笔记(二)———实例
- Linux 驱动学习笔记3 -- 字符设备驱动实例(driver+client)
- [Linux驱动]字符设备驱动学习笔记(一)
- linux驱动学习之字符设备驱动模板
- linux 下块设备驱动开发学习笔记 2(sbull驱动在vmware上测试)
- Linux设备驱动开发学习(1)--字符设备驱动
- Linux网络设备驱动学习笔记(-)