字符设备文件本身就是字符设备硬件。
字符设备文件只能存在于根文件系统必要目录dev目录下。
将来访问字符设备硬件只需要访问对应的字符设备文件即可。
int fd = open("/dev/ttySAC0", O_RDWR);
char buf[1024] = {0};
//读数据
read(fd, buf, sizeof(buf));
//写数据
write(fd, "hello", 5);
//关闭设备
close(fd);
再次明确:驱动程序属于Linux内核的一部分。也就是内核的其他代码是可以调用自己写的驱动程序的函数或者变量,也就是内核代码可以互相直接调用。
再次明确:不管是什么驱动。都必须有两个核心内容:
结论:通过以上信息,发现字符设备驱动本身就是一个“事物”,这个事物本身还包含一了一些属性(设备号)和方法(硬件操作接口函数)。所以驱动必然有对应的数据结构(类)。
struct char_device {
char *name;//驱动的名称
dev_t dev;//驱动申请的设备号
int count;//驱动申请的次设备号的个数
int (*open)(...);//提供打开硬件接口
int (*close)(...);//提供关闭硬件的接口
int (*read)(...);//提供读硬件数据接口
int (*write)(...);//提供向硬件写入数据接口
};
*· 缺点:将来根据用户的需求经常要改动操作接口,一会加了lseek,一会加个mmap等,不便于维护,提取出来单独管理。
//声明描述字符设备驱动的数据结构
strucet file_operations{
int (*open)(..);
int (*close)(...);
int (*read)(...);
int (*write)(...);
};
//声明描述字符设备驱动的数据结构
struct char_device
{
char *name;//驱动的名称
dev_t dev;//驱动申请到的设备号。
int count;//驱动申请的次设备号的个数。
struct file_operations *ops;//给字符设备驱动定义的结构体。
};
}
struct file_operations led_fops = {
.open = led_open, //打开设备
.close = led_close,//关闭设备
.read = led_read,//读设备
.write = led_write//写设备
};
struct char_device led_dev = {
.name = "led",
.dev = (申请好的设备号)(alloc_chrdev_region)
.count = 次设备号的个数、
.ops = &led_fops //添加硬件操作接口
};
void char_device_init(struct char_device *dev, struct file_operations *fops)
{
dev->ops = fops; //两者结合,提供操作接口。
}
//优化之后的程序使用:
char_device_init(&led_dev, &led_fops);
void char_device_add(struct char_device *dev, dev_t dev, int count, char *name)
{
//1、先将其余没有初始化的字段初始化
dev->name = name;
dev->dev = dev;
dev->count = count;
//2、向内核注册登记
//思路:事先在内核中准备一个大数组,数组的下标就是主设备号,数组元素就是字符设备对象的地址。
//结论:以主设备号为下标,将要注册的字符设备对象的地址放到这个大数组中即可。
//联想:应用如何调用底层驱动提供的操作接口函数。应用open->C库open ->软中断->内核sys_open->应用open,这里要明确:内核的sys_open只有一个。
//问:内核唯一的函数sys_open如何能找到众多驱动中对应的open接口呢?
//应用open->软中断->内核sys_open->驱动的beep_open(如何找到beep_open呢?)
//答:由于字符设备文件本身包含了主设备号,当应用open时也可以获取主设备号,最终跑到内核的sys_open,进程在内核的sys_open中找到内核事前准备好的大数组,然后以主设备号为下标找到应用的字符设备驱动对象,例如led_dev,一旦内核的sys_open找到字符设备驱动对象led_dev然后直接调用代码即可:int 内核:sys_open(...){
//1、在大数组中以主设备号为下标找字符设备对象地址,
//2、直接调用字符设备对象中的操作函数接口。
&led_dev->ops->open();//最终调用驱动的led_dev的函数
}
//以此类推:
}
* 结论:一旦注册成功,将来这个驱动静静在内存中等待着应用程序利用系统调用函数来访问驱动注册的接口函数
4、踏踏实实的根据用户需求编写各个操作接口函数:
5、设计一个函数将字符设备对象从内核卸载,也就是从大数组中删除
void char_device_del(struct char_device *dev)
{
//1、以dev->dev的主设备号为下标,以大数组中删除字符设备对象即可。
}
一旦删除,除非重启,否则不可恢复。
struct file_operations{
open,
close,
read,
write,
};
struct cdev{
const struct file_operations *ops;//硬件操作接口结构对象
dev_t dev;//保存申请的设备号
unsigned int count;//次设备号的个数。
...
};
struct file_operations A={
.opern = xxx_open,
.close = xxx_close,
......
}
struct cdev B;//定义
cdev_init(&B, &A);//初始化
cdev_add(&B, 申请号的设备号,次设备号的个数);
cdev_del(&B);
int xxx_open()
{
//打开设备
}
...
mkdir /opt/drivers/day03/1.0 -p
cd /opt/drivers/day03/1.0
vim led_drv.c //驱动程序
vim led_test.c //应用程序
vim Makefile
make
arm... gcc -o led_test led_test.c
cp led_drv.ko led_test /opt/rootfs/home/drivers
cd /home/drivers
insmod led_drv.ko //调用入口函数
cat /proc/devices //查看申请到的主设备号
mknod /dev/myled c 主设备号 0 //创建设备文件,代表LED0
./led_test
//open device fail 测试失败
cd /home/drivers
insmod led_drv.ko //调用入口函数
cat /proc/devices //查看申请到的主设备号
character devices://当前系统支持的字符设备,主设备号和设备名称
1 mem
5 /dev/tty
5 /dev/console
5 /dev/ptmx
...
244 myled //LED驱动申请到的主设备号就是244,设备名称为myled
...
mknod /dev/myled c 主设备号 0 //创建设备文件,它代表LED设备。
./led_test //再次执行
#include
#include
#include
#inlcude
#include // struct cdev
#include //struct file_operations
//声明描述LED硬件信息的数据结构
struct led_resource{
char *name; //名称
int gpio; //gpio编号
};
//定义初始化四个LED灯的硬件信息对象
static struct led_resource led_info[] = {
{
.name = "LED1",
.gpio = PAD_GPIO_C+12
},
{
.name = "LED2",
.gpio = PAD_GPIO_C+17
},
{
.name = "LED3",
.gpio = PAD_GPIO_C+11
},
{
.name = "LED4",
.gpio = PAD_GPIO_B+26
}
};
//打开设备接口:
//执行成功返回0,失败返回负值
static int led_open(struct inode *, struct file *)
{
//开灯
int i;
for(i = 0; i
#include
#include
#include
#include
int main()
{
int fd;
//应用open->软中断->内核sys_open->驱动led_open
fd = open("/dev/myled", O_RDWR);
if(fd < 0){
printf("open led device failed.\n");
return -1;
}
sleep(3);
//应用close->软中断->内核sys_close->驱动led_close
close(fd);
return 0;
}
obj-m += led_drv.o
all:
make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
make -C /opt/kernel SUBDIRS=$(PWD) clean
先搭建驱动框架:
各种该:
填充入口和出口
最后编写各个接口函数
ssize_t write(int fd, const void *buf, size_t count);
struct file_operations {
int (*open)(struct inode *, struct file *);
int (*release)(struct inode *, struct file *);
ssize_t (*write) (struct file *, const char __user *buf, size_t count, loff_t *ppos);
}
注意:
int sys_open(...)
{
if(驱动的open接口是否为NULL)
return fd > 0 // 永远成功
else
xxx->ops->open();//调用驱动的open接口
}
如果用户对open/close没有要求,底层驱动open/close可以不用初始化!
应用write->C库的write->软中断->内核的sys_write->驱动write接口->应用write返回。
write接口的功能:向硬件设备写入数据
ssize_t (*write) (struct file *, const char __user *buf, size_t count, loff_t *ppos);
参数说明:
注意:此种写法极其危险,两种危险情况
int copy_from_user(void *to, const void __user *from, int n);
#include
#include
#include
#include
int main()
{
int fd;
int cmd = 0;
if(argc != 2){
printf("usage : %s \n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if(fd < 0){
printf("open led device failed.\n);
return -1;
}
if(!strcmp(argv[1], "on"))
{
cmd = 1;
}
else if(!strcmp(argv[1], "off"))
{
cmd = 0;
}
//向设备驱动文件写cmd
write(fd, &cmd, sizeof(cmd));
close(fd);
return 0;
}
#include
#include
#include
#include
#include
#include
//声明描述LED硬件信息数据结构
struct led_resource{
char *name;
int gpio;
};
//初始化LED硬件信息对象
static struct led_resource led_info[] = {
{
.name = "LED1",
.gpio = PAD_GPIO_C + 12
},
{
.name = "LED2",
.gpio = PAD_GPIO_C + 17
},
{
.name = "LED3",
.gpio = PAD_GPIO_C + 11
},
{
.name = "LED4",
.gpio = PAD_GPIO_B + 26
}
};
//向硬件设备写入数据接口
//参数对应关系
//write的fd <->led_write file; write的buf <-> led_write buf;
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int i;
//分配内核缓冲区,暂存从用户缓冲区获取的数据
int kcmd;
//拷贝用户缓冲区的数据到内核缓冲区中
//kcmd = *(int *)buf; <---危险操作,使用操作函数
copy_from_user(&cmd, buf, sizeof(kcmd));
//操作硬件
for(i = 0; i< ARRAY_SIZE(led_info); i++)
{
gpio_set_value(led_info[i].gpio, !kcmd);
printk("%s: %s 第 %d 个灯", __func, kcmd ? "开":"关", i+1);
}
return count
}
//定义初始化LED硬件操作接口对象
static struct file_operations led_fops = {
.write = led_write//向硬件写入数据
}
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
static int led_init(void)
{
int i;
//申请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);
}
//申请设备号.
alloc_chrdev_region(&dev, 0, 1, "myled");
//初始化字符设备对象,添加操作接口
cdev_init(&led_cdev, &led_fops);
//向内核注册字符设备对象。
cdev_add(&led_cdev, dev, 1);
return 0;
}
static void led_exit(void)
{
//从内核卸载字符设备对象
cdev_del(&led_cdev);
//释放设备号
unregister_chrdev_region(dev, 1);
//释放GPIO资源,输出1
for(i = 0; i < ARRAY_SIZE(led_info); i++)
{
gpio_set_value(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include
//声明描述LED操作信息的数据结构
struct led_event {
int cmd;//开关灯命令,1-开灯,0-关灯。
int index;//灯编号。1、2、3、4
};
int main()
{
int fd;
struct led_event led;
if(argc != 3){
printf("usage: %s <1|2|3|4> \n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if(fd < 0)
return -1;
if(!strcmp(argv[1], "on"))
led.cmd = 1;
else if(!strcmp(argv[1], "off"))
led.cmd = 0;
led.index = strtoul(argv[2], NULL, 0);
write(fd, &led, sizeof(led));
close(fd);
return 0;
}
ssize_t read(int fd, void *buf, size_t count);
ssize_t (*read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);
#include
#include
#include
#include
//声明描述led操作信息的数据结构
struct led_event {
int cmd;//开灯命令,1-开灯,0-关灯。
int index; //灯编号
};
//声明描述led等状态的数据结构
struct led_state{
int index;//灯编号。
int state;//灯状态,1-关灯,0-开灯。
};
int main()
{
int fd;
struct led_event led;//分配用户缓冲区,保存灯的操作信息。
struct led_state ledst;//分配用户缓冲区,保存灯的状态。
if(argc != 3){
printf("usage: %s <1|2|3|4> \n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if(fd < 0)
return -1;
if(!strcmp(argv[1], "on"))
led.cmd = 1;
else if(!strcmp(argv[1], "off"))
led.cmd = 0;
led.index = strtoul(argv[2], NULL, 0);
//将LED操作信息发生给驱动,开关灯。
write(fd, &led, sizeof(led));
//获取灯的状态
//为ledst 赋值。
ledst.index = strtol(argv[2], NULL, 0);
read(fd, &ledst, sizeof(ledst));
//打印状态
printf("%d led state is %s \n", ledst.index, ledst.state? "close":"open");
close(fd);
return 0;
}
#include
#include
#include
#include
#include
#include
//声明描述led操作信息的数据结构
struct led_event {
int cmd;//开灯命令,1-开灯,0-关灯。
int index; //灯编号
};
//声明描述led等状态的数据结构
struct led_state{
int index;//灯编号。
int state;//灯状态,1-关灯,0-开灯。
};
//声明描述LED硬件信息数据结构
struct led_resource{
char *name;
int gpio;
};
//初始化LED硬件信息对象
static struct led_resource led_info[] = {
{
.name = "LED1",
.gpio = PAD_GPIO_C + 12
},
{
.name = "LED2",
.gpio = PAD_GPIO_C + 17
},
{
.name = "LED3",
.gpio = PAD_GPIO_C + 11
},
{
.name = "LED4",
.gpio = PAD_GPIO_B + 26
}
};
//向硬件设备写入数据接口
//参数对应关系
//write的fd <->led_write file; write的buf <-> led_write buf;
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int i;
//分配内核缓冲区,暂存从用户缓冲区获取的数据
int kcmd;
//拷贝用户缓冲区的数据到内核缓冲区中
//kcmd = *(int *)buf; <---危险操作,使用操作函数
copy_from_user(&cmd, buf, sizeof(kcmd));
//操作硬件
for(i = 0; i< ARRAY_SIZE(led_info); i++)
{
gpio_set_value(led_info[i].gpio, !kcmd);
printk("%s: %s 第 %d 个灯", __func, kcmd ? "开":"关", i+1);
}
return count
}
//读硬件接口
static ssize_t led_read(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
//1、分配内核缓冲区,暂存数据
struct led_state kledst;
//2.先从用户缓存区拷贝数据到内核缓冲区
copy_from_user(&kledst, buf, sizeof(kledst));
kledst.state = gpio_get_value(led_info[kledst.index-1].gpio);
//将内核缓存区中的有效数据拷贝到用户缓冲区
copy_to_user(buf, &kledst, sizeof(kledst));
return count;
}
//定义初始化LED硬件操作接口对象
static struct file_operations led_fops = {
.write = led_write//向硬件写入数据
.read = led_read;//从硬件读取数据
}
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
static int led_init(void)
{
int i;
//申请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);
}
//申请设备号.
alloc_chrdev_region(&dev, 0, 1, "myled");
//初始化字符设备对象,添加操作接口
cdev_init(&led_cdev, &led_fops);
//向内核注册字符设备对象。
cdev_add(&led_cdev, dev, 1);
return 0;
}
static void led_exit(void)
{
//从内核卸载字符设备对象
cdev_del(&led_cdev);
//释放设备号
unregister_chrdev_region(dev, 1);
//释放GPIO资源,输出1
for(i = 0; i < ARRAY_SIZE(led_info); i++)
{
gpio_set_value(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
int ioctl(int fd, int request, …);
struct file_operations {
long (*unlocked_ioctl)(struct file *file , unsigned int cmd, unsigned long buf);
}
cd /home/drivers
insmod led_drv.ko
cat /proc/devices
mknod /dev/myled c 244 0
./led_test on
./led_test off
./led_test on 1
./led_test off 1
#include
#include
#include
#include
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//声明描述led操作信息的数据结构
struct led_event {
int cmd;//开灯命令,1-开灯,0-关灯。
int index; //灯编号
};
//声明描述led等状态的数据结构
struct led_state{
int index;//灯编号。
int state;//灯状态,1-关灯,0-开灯。
};
int main()
{
int fd;
struct led_event led;//分配用户缓冲区,保存灯的操作信息。
struct led_state ledst;//分配用户缓冲区,保存灯的状态。
if(argc != 3){
printf("usage: %s <1|2|3|4> \n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if(fd < 0)
return -1;
if(!strcmp(argv[1], "on"))
ioctl(fd, LED_ON);
else if(!strcmp(argv[1], "off"))
ioctl(fd, LED_OFF);
close(fd);
return 0;
}
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//控制操作接口
static long led_ioctl(struct file *file, unsigned long cmd, unsigned long buf)
{
int i;
switch(cmd){
case LED_ON:
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_set_value(led_info[i].gpio, 0);
printk("%s : turn on %d led. \n", __func__, i+1);
}
break;
case LED_OFF:
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_set_value(led_info[i].gpio, 1);
printk("%s : turn off %d led. \n", __func__, i+1);
}
break;
default:
printk("command is invalid\n");
return -1;
}
retrun 0;
}
//部分代码:
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl
};
#include
#include
#include
#include
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//声明描述led操作信息的数据结构
struct led_event {
int cmd;//开灯命令,1-开灯,0-关灯。
int index; //灯编号
};
//声明描述led等状态的数据结构
struct led_state{
int index;//灯编号。
int state;//灯状态,1-关灯,0-开灯。
};
int main()
{
int fd;
int index;
struct led_event led;//分配用户缓冲区,保存灯的操作信息。
struct led_state ledst;//分配用户缓冲区,保存灯的状态。
if(argc != 3){
printf("usage: %s <1|2|3|4> \n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if(fd < 0)
return -1;
//获取灯编号。
index = strtoul(argv[2], NULL, 0);
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;
}
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//控制操作接口
static long led_ioctl(struct file *file, unsigned long cmd, unsigned long buf)
{
int i;
//分配内核缓冲区
int kindex;
copy_from_user(&kindex, (int *)buf, sizeof(kindex));
switch(cmd){
case LED_ON:
gpio_set_value(led_info[kindex - 1].gpio, 0);
printk("%s : turn on %d led. \n", __func__, kindex);
break;
case LED_OFF:
gpio_set_value(led_info[kindex - 1].gpio, 1);
printk("%s : turn off %d led. \n", __func__, kindex);
break;
default:
printk("command is invalid\n");
return -1;
}
retrun 0;
}
//部分代码:
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl
};
struct inode{
umode_t i_mode;
unsigned short i_opflags;
uid_t i_uid;
gid_t i_gid;
unsigned int i_flags;
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_spacc *i_mapping;
void *i_security;
....
dev_t i_rdev;//如果此文件是设备文件,i_rdev保存对应的设备号。
struct cdev *i_cdev;//指向设备文件对应的字符设备驱动对象。
....
};
int led_open(struct inode *inode, ...)
{
printk("主设备号 %d, 次设备号 %d\n", MAJOR(inode->i_rdev, MINOR(indoe->i_rdev));
return 0;
}
struct file{
struct file_operations *f_op;
}
long led_ioctl(struct file *file, cmd, buf)
{
struct inode *inode = file->f_path.dentry->d_inode;
int minor = MINOR(inode->i_rdev);
led_info(minor);
}
#include
#include
#include
#include
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//声明描述led操作信息的数据结构
struct led_event {
int cmd;//开灯命令,1-开灯,0-关灯。
int index; //灯编号
};
//声明描述led等状态的数据结构
struct led_state{
int index;//灯编号。
int state;//灯状态,1-关灯,0-开灯。
};
int main()
{
int fd0, fd1, fd2, fd3;
int index;
struct led_event led;//分配用户缓冲区,保存灯的操作信息。
struct led_state ledst;//分配用户缓冲区,保存灯的状态。
if(argc != 3){
printf("usage: %s <1|2|3|4> \n", argv[0]);
return -1;
}
fd0 = open("/dev/myled0", O_RDWR);
if(fd0 < 0)
return -1;
fd1 = open("/dev/myled1", O_RDWR);
if(fd1 < 0)
return -1;
fd2 = open("/dev/myled2", O_RDWR);
if(fd2 < 0)
return -1;
fd3 = open("/dev/myled3", O_RDWR);
if(fd3 < 0)
return -1;
while(1){
ioctl(fd0, LED_ON);
sleep(1);
ioctl(fd1, LED_ON);
sleep(1);
ioctl(fd2, LED_ON);
sleep(1);
ioctl(fd3, LED_ON);
sleep(1);
ioctl(fd0, LED_OFF);
sleep(1);
ioctl(fd1, LED_OFF);
sleep(1);
ioctl(fd2, LED_OFF);
sleep(1);
ioctl(fd3, LED_OFF);
sleep(1);
}
close(fd0);
close(fd1);
close(fd2);
close(fd3);
return 0;
}
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//控制操作接口
static long led_ioctl(struct file *file, unsigned long cmd, unsigned long buf)
{
int i;
//通过file找到对应inode
struct inode *inode = file->f_path.dentry->d_inode;
int minor = MINOR(inode->i_rdev);
//分配内核缓冲区
switch(cmd){
case LED_ON:
gpio_set_value(led_info[minor].gpio, 0);
printk("%s : turn on %d led. \n", __func__, minor + 1);
break;
case LED_OFF:
gpio_set_value(led_info[minor].gpio, 1);
printk("%s : turn off %d led. \n", __func__, minor + 1);
break;
default:
printk("command is invalid\n");
return -1;
}
retrun 0;
}
//定义初始化LED硬件操作接口对象
//共用
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl;
};
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
static int led_init(void)
{
int i;
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);
}
//申请设备号
//次设备号0,1,2,3
alloc_chrdev_region(&dev, 0, 4, "myled");
cdev_init(&led_cdev, &led_fops);
cdev_add(&led_cdev, dev, 4);
return 0;
}
static void led_exit(void)
{
int i;
cdev_del(&led_cdev);
unregister_chrdev_region(dev, 4);
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_direction_output(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
}
module_init(led_init);
module_exit(led_exit);
MODULE_LINCESE("GPL");
保证根文件系统rootfs必要脚本文件rcS中添加以下两句话:
mount -a:就是为了执行fstab文件。
echo /sbin/mdev > /proc/sys/kernel/hotplug
保证根文件系统rootfs必须有mdev命令,执行在下位机执行:which is mdev查看是否存在即可。
保证根文件系统rootfs必要配置文件fstab中必须有以下两句话:
struct class *cls; //创建一个设备类指针,类似苹果上长一个嫩芽。
//cls指向创建的设备对象。
cls = class_create(THIS_MODULE, "tarena1");
//正式创建设备文件,本质是将来调用mdev来创建设备文件。
//dev:设备文件的设备号,myled:设备文件名
device_create(cls, NULL, dev, NULL, "myled");
//删除设备文件
device_destroy(cls, dev);
//删除设备对象。
class_destroy(cls);
//自定义两个命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//控制操作接口
static long led_ioctl(struct file *file, unsigned long cmd, unsigned long buf)
{
int i;
//通过file找到对应inode
struct inode *inode = file->f_path.dentry->d_inode;
int minor = MINOR(inode->i_rdev);
//分配内核缓冲区
switch(cmd){
case LED_ON:
gpio_set_value(led_info[minor].gpio, 0);
printk("%s : turn on %d led. \n", __func__, minor + 1);
break;
case LED_OFF:
gpio_set_value(led_info[minor].gpio, 1);
printk("%s : turn off %d led. \n", __func__, minor + 1);
break;
default:
printk("command is invalid\n");
return -1;
}
retrun 0;
}
//定义初始化LED硬件操作接口对象
//共用
static struct file_operations led_fops = {
.unlocked_ioctl = led_ioctl;
};
//定义设备号对象
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
struct class *cls; //创建一个设备类指针,类似苹果上长一个
static int led_init(void)
{
int i;
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);
}
//申请设备号
//次设备号0,1,2,3
alloc_chrdev_region(&dev, 0, 4, "myled");
cdev_init(&led_cdev, &led_fops);
cdev_add(&led_cdev, dev, 4);
//5.创建设备类对象
cls = class_create(THIS_MODULE, "tarena1");
//6、自动创建设备文件
device_create(cls, NULL, \
MKDEV(MAJOR(dev), 0), NULL, "myled0");
device_create(cls, NULL, \
MKDEV(MAJOR(dev), 1), NULL, "myled1");
device_create(cls, NULL, \
MKDEV(MAJOR(dev), 2), NULL, "myled2");
device_create(cls, NULL, \
MKDEV(MAJOR(dev), 3), NULL, "myled3");
return 0;
}
static void led_exit(void)
{
int i;
cdev_del(&led_cdev);
unregister_chrdev_region(dev, 4);
for(i = 0; i < ARRAY_SIZE(led_info); i++){
gpio_direction_output(led_info[i].gpio, 1);
gpio_free(led_info[i].gpio);
}
//删除设备文件,
device_destroy(cls, \
MKDEV(MAJOR(dev), 0), 0);
device_destroy(cls, \
MKDEV(MAJOR(dev), 1), 1);
device_destroy(cls, \
MKDEV(MAJOR(dev), 2), 2);
device_destroy(cls, \
MKDEV(MAJOR(dev), 3), 3);
//删除设备类对象
class_destroy(cls);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LINCESE("GPL");