字符设备

字符设备

  1. 设备号 = 主设备号(占高字节)+次设备号(占低字节)。
  2. 在/proc/devices列举出所有已经注册的设备号。
  3. 文件操作对象,提供open、write、read。

框架

  1. 获取并注册设备号
    不同linux版本对应字节数不一样,一般用内核提供的函数获取 MKDEV(ma,mi)。
    方式1:
    register_chrdev 第一个参数为0则动态注册,第一个参数非0则静态注册
    卸载1:
    unregister_chrdev
    方式2:
    静态获取:register_chrdev_region(设备号通过MKDEV(ma,mi)获取)
    动态注册:alloc_chrdev_region
    cdev_init()//声明文件io操作结构体
    cdev_add();
    卸载2:
    cdev_del(&led->dev);
    unregister_chrdev_region(led->dev_no, 1);
  2. 创建设备节点(/dev目录下)
    手动:mknod /dev/设备 类型 主设备号 次设备号;这种在断电后则失效
    自动(通过udev/mdev机制):class_create、device_class;创建类后创建设备文件
  3. 初始化文件io操作函数
    struct file_operations my_ops={};
    copy_to_user();//内核到用户,对应于用户read
    copy_from_user();//用户到内核,对应于用户write
    驱动中的read参数除了第四个其他都与用户态的一致,非安全操作中驱动中直接使用*buf直接赋值,但是若buf为NULL指针则会造成内核崩溃,对于这两个函数其实是一个安全措施,会对用户中所传进来的参数进行判断。返回值大于0表示出错,剩下多少个没有拷贝成功,成功返回0。
  4. 通过ioremap地址转换控制外设,返回虚拟地址,iounmap取消映射。
  5. 内核中对寄存器操作使用 readl、writel,一般不使用直接解引用赋值。

关键部分讲解

  1. 对于inode对象与file对象的重要属性应用:
    inode: 其成员i_rdev保存设备号,i_cdev保存字符设备结构体。
    file:其成员private_data,类型为void*,是驱动私有的,内核保证不会使用该指针。
  2. 对于注册字符串部分,推荐使用方式2,方式1只能注册一个设备号,其字符设备对象在内核帮我们注册了,一个字符设备对应一个设备号,而方式2可以注册多个设备号,即一个字符设备对象对应多个设备,例如串口一般有多个,若调用方式1则需重复调用多次注册设备号,主设备号相同,次设备号递增+1,一个字符对象对应多个设备。
    具体实现(1对2):
dev_t dev;
dev = MKDEV(主设备号,次设备号);
register_chrdev_region(dev, 2, "设备名");
cdev_init(&字符设备, &my_fops);
字符设备.owner = THIS_MODULE;
cdev_add(&字符设备, dev, 2);
  1. 对于不同设备对应同一个驱动的区分,设备的唯一身份就是设备号,而设备号在inode对象中的成员i_rdev中记录,其中他们一般主设备号相同,次设备号不同,我们可以通过open中内核传入的inode对象判断次设备号来区分不同设备,并将得到的设备放入file对象中的私有数据中,在其他io函数中可取出来使用。
    具体实现如下:
static int int my_open(struct inode *no, struct file *fp)
{
	switch(MINOR(no->i_rdev)){
		default:
		case 0:
			filp->private_data = 设备0结构体
		case 1:
			filp->private_data = 设备1结构体
	}
	return 0;
}
  1. 对于一个驱动对应多个设备的另一实现:一个字符设备对象对应一个设备,定义多组。
    对于不同设备而言,他们对文件io传入的参数均不同,即可直接通过file私有数据空间的不同来进行差异化操作。
dev_t dev;
dev = MKDEV(主设备号,次设备号);
register_chrdev_region(dev, 2, "设备名");
for(i=0; i<2;i++){
	cdev_init(&字符设备i, &my_fops);
	字符设备i.owner = THIS_MODULE;
	cdev_add(&字符设备i, dev+i, 1);
}

还有一个问题就是如何将差异化数据保存在file对象的私有数据空间中:
linux内核中定义了一个宏 container_of,他能够根据结构体成员的地址方向得到结构体的起始地址,通过字符设备对象调用该宏获取到自定义的设备对象保存在file对象私有数据空间中,则在其他io中即可通过file对象使用,使用实例:

file->private_data = container_of(inode->i_cdev, 自定义设备对象类型, cdev);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值