Linux内核字符设备文件的自动创建
前面已经描述过通过mknod命令可以手动创建字符设备文件,那么如何实现自动创建设备文件呢?
自动创建字符设备文件概述
字符设备文件的自动创建只需要“三个保证”+“四个函数”
三个保证
- 保证根文件系统rootfs中必须有mdev可执行程序,例如:执行命令
#确认根文件系统中有mdev,mdev可以帮助自动创建设备文件
which is mdev
- 保证根文件系统rootfs的启动脚本rcS中必须有以下内容:
#将来解析fstab文件
mount -a
#驱动程序解析hotplug来确认谁来帮驱动创建设备文件(需要mdev可执行程序)
echo /sbin/mdev > /proc/sys/kernel/hotplug
- 保证根文件系统rootfs的配置文件fstab中必须有以下三句话:
- /proc作为procfs虚拟文件系统的入口
- /sys作为sysfs虚拟文件系统的入口
- /dev作为tmpfs虚拟文件系统的入口
- 这三种文件系统的内容都是由内核创建并且创建在内存中,掉电丢失。
proc /proc proc defaults 0 0
sysfs /sys syfs defaluts 0 0
tmpfs /dev tmpfs defulats 0 0
四个函数
驱动程序接下来只需要调用以下四个函数即可完成设备文件的创建:
(编写驱动时记得添加头文件 #include <linux/device.h>)
- 定义一个设备类指针:
struct class *cls;
- 创建一个设备类对象,cls指向这个对象,并且设定这个对象类名:
cls = class_create(THIS_MODULE, "myclass");
- 正式创建设备文件,(暂时还是利用led等示例来说明)设备文件为/dev/myled,其中dev为设备号,myled为设备文件名。
device_create(cls, NULL, dev, NULL, "myled");
- 删除设备文件(在卸载驱动的时候自动删除设备文件)
device_destroy(cls, dev);
- 删除设备类(卸载驱动时用到)。
class_destroy(cls);
修改之前LED驱动代码
- led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h> //copy_from_user声明
#include <linux/device.h> //设备文件的自动创建
//声明描述LED硬件信息的数据结构
struct led_resource {
int gpio; //GPIO编号
char *name; //LED名称
};
//定义初始化LED的硬件信息对象
static struct led_resource led_info[] = {
{
.name = "LED1",
.gpio = PAD_GPIO_C+12
},
{
.name = "LED2",
.gpio = PAD_GPIO_C+7
},
{
.name = "LED3",
.gpio = PAD_GPIO_C+11
},
{
.name = "LED4",
.gpio = PAD_GPIO_B+26
}
};
//调用关系:应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
//例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯
//参数关系:
// fd<---->file 亲戚关系
// cmd=LED_ON或者cmd=LED_OFF
// arg=(unsigned long)&index
#define LED_ON 0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
static long led_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
//1.分配内核缓冲区,保存用法发送过来的灯的编号
int kindex;
//2.拷贝用户缓冲区数据到内核缓冲区
//kindex = *(int *)arg; //相当危险
copy_from_user(&kindex, (int *)arg, sizeof(kindex));
//3.解析用户发送过来的命令
switch(cmd) {
case LED_ON:
gpio_set_value(led_info[kindex-1].gpio, 0);
printk("%s:开第%d个灯", __func__, kindex);
break;
case LED_OFF:
gpio_set_value(led_info[kindex-1].gpio, 1);
printk("%s:关第%d个灯", __func__, kindex);
break;
default:
printk("无效命令!\n");
return -1;
}
return 0; //执行成功返回0,执行失败返回负值
}
//定义初始化LED的硬件操作接口对象
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl, //不仅仅向硬件设备发送控制命令,还能和设备进行读写操作
};
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
//定义设备类指针
static struct class *cls;
static int led_init(void)
{
int i;
//1.申请设备号
alloc_chrdev_region(&dev, 0, 1, "myled");
//2.初始化字符设备对象,本质就是给字符设备添加操作接口
cdev_init(&led_cdev, &led_fops);
//3.向内核注册字符设备对象并且提供硬件操作接口
cdev_add(&led_cdev, dev, 1);
//4.申请GPIO资源配置为输出,输出1
for(i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_request(led_info[i].gpio,
led_info[i].name);
gpio_direction_output(led_info[i].gpio, 1);
}
//5.创建设备类对象,类似长树枝
//将来会创建:rootfs/sys/class/zhangsan
cls = class_create(THIS_MODULE, "zhangsan");
//6.创建设备文件/dev/myled,类似长苹果
//dev:就是提供创建设备文件时所需的设备号
//myled:就是提供创建设备文件是所需的设备文件名
device_create(cls, NULL, dev, NULL, "myled");
return 0;
}
static void led_exit(void)
{
int i;
//1.输出1,释放GPIO资源
for (i = 0; i < ARRAY_SIZE(led_info); i++) {
gpio_set_value(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
//2.释放设备号
unregister_chrdev_region(dev, 1);
//3.卸载字符设备对象
cdev_del(&led_cdev);
//4.删除设备文件(摘苹果)和设备类(砍树枝)
device_destroy(cls, dev);
class_destroy(cls);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
- led_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#define LED_ON 0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
int main(int argc, char *argv[])
{
int fd;
int index; //分配用户缓冲区,保存操作灯的编号
if(argc != 3) {
printf("用法:%s <on|off> <1|2|3|4>\n", argv[0]);
return -1;
}
//打开设备
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
printf("打开设备失败!\n");
return -1;
}
//"1"->1
index = strtoul(argv[2], NULL, 0);
//应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
if(!strcmp(argv[1], "on"))
ioctl(fd, LED_ON, &index);
else if(!strcmp(argv[1], "off"))
ioctl(fd, LED_OFF, &index);
//关闭设备
close(fd);
return 0;
}
- Makefile
obj-m += led_drv.o
all:
make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
make -C /opt/kernel SUBDIRS=$(PWD) clean
运行结果
加载驱动:
insmod led_drv.ko