文章目录
1.实现步骤
首先想要实现一个字符设备,需要以下3步
- 注册设备号
- 初始化字符设备
- 实现需要的文件操作
1.1 注册设备号
1.1.1 主次设备号以及设备ID
一般情况下,设备的注册是在模块加载入口所指向的函数中完成的。
1)先将主次设备号合成为设备ID,这里需要用到LINUX内核提供的宏MKDEV
2)通过设备ID在内核中对设备进行注册
【注意】 加载函数中的申请资源和卸载函数中的释放资源要一一配对
//把主次设备号合并生成设备ID
dev_t devno = MKDEV(LED_MA, LED_MI);
//注册设备号:
//"newled"是设备的名字,注册成功后可以在 /proc/devices文件中看到这名字
//ret返回值用于判断是否成功
ret =register_chrdev_region(devno, LED_NUM, "newled");
cdev_del(&led); //删除设备
unregister_chrdev_region(devno, LED_NUM); //取消注册
1.1.2 MKDEV宏的实现
/*****内核中MKDEV宏的实现*******/
// 文件/include/linux/kdev_t.h
#define MINORBITS 20
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
//作用:将主设备号MA左移20位与次设备号MI相或,最后得到设备ID
/*
| 31~20位 | 19~0位 |
| 主设备号 | 次设备号 |
*/
1.2 初始化字符设备
1)声明字符设备cdev类型的变量
2)声明文件操作函数指针结构体,将函数指针指向相应的文件操作函数
3)将设备和文件操作进行关联
4)将字符设备添加到系统之中
/*定义字符设备*/
struct cdev led;
/*设备的文件操作函数指针结构体*/
struct file_operations led_fps={
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl,
};
cdev_init(&led,&led_fps); //将设备和文件操作关联起来
ret = cdev_add(&led, devno, LED_NUM); //添加字符设备到系统中
1.3 实现需要的文件操作
1.3.1物理地址映射
由于硬件的物理地址和内核的地址是两种不同的地址,所以在内核中是不能直接访问硬件的物理地址的,需要将硬件物理地址映射成内核的虚地址。
/*LED实际的物理地址*/
#define LED2_CON 0x11000c40
/*ioremap映射后的地址*/
unsigned int *led2con=NULL;
/*把物理地址映射为内核虚地址*/
led2con =ioremap(LED2_CON,4);
if(led2con == NULL){
printk("ioremap led2con error\n");
return -1;
}
1.3.2 实现ioctl
- 当上层应用调用ioctl的时候,最终会调用到驱动中的unlocked_ioctl指向的函数,根据上层应用传入来的cmd,可以执行定制个性化的命令 (驱动提供机制,应用提供策略)
- 驱动提供机制其实就是通过查芯片手册,对寄存器的值进行相应修改 (这一步和裸机编程是一样的)
- 修改和读取寄存器一般使用writel和readl函数。
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//printk(" led_ioctl go\n");
switch(cmd)
{
case LED2_ON:
writel((readl(led2dat)& (~(0x1 << 7))) | (0x1<<7),led2dat);
break;
case LED2_OFF:
writel(readl(led2dat)&(~(0x1<<7)),led2dat);
break;
case LED3_ON:
writel((readl(led3dat)& (~(0x1 << 0))) | (0x1<<0),led3dat);
break;
case LED3_OFF:
writel(readl(led3dat) &(~(0x1<<0)),led3dat);
break;
case LED4_ON:
writel((readl(led4dat)& (~(0x1 << 4))) | (0x1<<4),led4dat);
break;
case LED4_OFF:
writel(readl(led4dat)&(~(0x1<<4)),led4dat);
break;
default:
printk(" no found cmd = %d\n",cmd);
break;
}
return 0;
}