对Linux来说,设备驱动也是文件。驱动控制硬件的过程,实际上是对驱动文件的读写操作。
对内核来说,如何获取唯一的文件标识呢?当然是通过file结构体中的,inode结构体识别应用层打开的到底是哪一个设备文件。


在串口工具进行输入:
echo 1 > /dev/myled0 ---->led1灯点亮
echo 0 > /dev/myled0 ---->led1灯熄灭
echo 1 > /dev/myled1 ---->led1灯点亮
echo 0 > /dev/myled1 ---->led1灯熄灭
echo 1 > /dev/myled2 ---->led1灯点亮
echo 0 > /dev/myled2 ---->led1灯熄灭
linux@ubuntu:/sys/class/myled$ ls /dev/myled* -ll
crw------- 1 root root 236, 0 Nov 18 14:55 /dev/myled0 ----->控制PE10(LED1)
crw------- 1 root root 236, 1 Nov 18 14:55 /dev/myled1----->控制PF10(LED2)
crw------- 1 root root 236, 2 Nov 18 14:55 /dev/myled2----->控制PE8(LED3)
2.驱动:
open:在open函数中获取到次设备号,用私有数据传参,传递给write函数
write:在write函数,判断次设备号,就知道操作的是哪盏灯
3.要求:
1)分部实现注册字符设备驱动
2)自动创建设备节点
3)通过结构体对led灯地址进行映射
4)次设备号完成私有数据传参
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include "led.h"
- #define CNAME "myled"
- unsigned int major=0;
- int minor=0;
- struct class *cls;
- //指向字符设备号
- struct device *dev;
- const int count=3;
- //指向字符设备驱动结构体
- struct cdev *cdev;
- volatile unsigned int* virt_rcc;
- volatile gpio_t* virt_gpioe;
- volatile gpio_t* virt_gpiof;
- int mycdev_open(struct inode *inode,struct file *file)
- {
- //取出次设备号
- int mino;
- mino=MINOR(inode->i_rdev);
- //将次设备号放到file结构体私有数据中
- file->private_data = (void*)mino;
-
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- ssize_t mycdev_read(struct file*file,char __user *ubuf,size_t size,loff_t *loffs)
- {
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
-
- }
- //ssize_t mycdev_write(struct file *file,char __user *ubuf,size_t size,loff_t *loff)错误写法
- ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
- {
- //私有数据传参
- //读取用户空间数据到内核(传输数据以内核空间大小为准)
- char kbuf[128]="0";
- int ret=0;
- if(size>sizeof(kbuf)) size= sizeof(kbuf);
- ret=copy_from_user(kbuf,ubuf,size);
- if(ret)
- {
- printk("copy_from_user is failed\n");
- return -EIO;
- }
- //取出次设备号
- minor = (int)file->private_data;
- switch(minor)
- {
- case LED1:
- if(kbuf[0]=='0')
- {
- virt_gpioe->ODR &=(~(0x1<<10));
- printk("LED1 on\n");
- }else
- {
- virt_gpioe->ODR |=(0x1<<10);
- printk("LED1 off\n");
- }
- break;
- case LED2:
- if(kbuf[0]=='0')
- {
- virt_gpiof->ODR &=(~(0x1<<10));
- printk("LED2 on\n");
- }else{
- virt_gpiof->ODR |=(0x1<<10);
- printk("LED2 off\n");
- }
- break;
- case LED3:
- if(kbuf[0]=='0')
- {
- virt_gpioe->ODR &=(~(0x1<<8));
- printk("LED3 on\n");
- }
- else
- virt_gpioe->ODR |=(0x1<<8);
- break;
-
- }
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- //此处返回从用户空间获取到的大小,如果获取为0;copy_from_user会一直等待
- return size;
- }
- int mycdev_close(struct inode* inode,struct file *file)
- {
- printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
- return 0;
- }
- const struct file_operations fops={
- .open=mycdev_open,
- .read=mycdev_read,
- .write=mycdev_write,
- .release=mycdev_close,
- };
- static int __init mycdev_init(void)
- {
- int ret;
- dev_t devno;
- int i;
- //1.分步注册字符设备驱动
- //1)分配dev结构体
- cdev=cdev_alloc();
- if(NULL==cdev)
- {
- printk("cdev alloc is failed\n");
- return -EIO;
- }
- //2)初始化cdev结构体
- cdev_init(cdev,&fops);
- //3)动态申请设备号,成功返回0,失败返回错误码
- ret=alloc_chrdev_region(&devno,0,count,CNAME);
- if(ret)
- {
- printk("alloc_chrdev_region is failed\n");
- return -ENOMEM;
- }
- //成功申请到设备号,调函数获取主次设备号(12+20)
- major=MAJOR(devno);
- minor=MINOR(devno);
- //3)驱动注册
- ret=cdev_add(cdev,MKDEV(major,minor),count);
- if(ret)
- {
- printk("dev add is failed\n");
- return -EIO;
- }
- //2.自动创建设备节点
- //1)向上层提交目录信息
- cls=class_create(THIS_MODULE,CNAME);
- if(IS_ERR(cls))
- {
- return PTR_ERR(cls);
- }
- //2.向上层提交设备节点
- for(i=0;i
- {
- dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
- if(IS_ERR(dev))
- {
- return PTR_ERR(dev);
- }
- }
-
-
- //3.通过结构体对LED灯进行映射
- //将GPIOE和GPIOF物理地址映射为虚拟地址
- //1)将rcc地址进行映射
- virt_rcc=ioremap(PHY_RCC,4);
- if(NULL==virt_rcc)
- {
- printk("rcc ioremap failed\n");
- return -ENOMEM;
- }
- //2)将gpioe地址进行映射
- virt_gpioe=ioremap(PHY_GPIOE,sizeof(gpio_t));
- if(NULL==virt_gpioe)
- {
- printk("virt_gpioe ioremap failed\n");
- return -ENOMEM;
- }
- //2)将gpiof地址进行映射
- virt_gpiof=ioremap(PHY_GPIOF,sizeof(gpio_t));
- if(NULL==virt_gpiof)
- {
- printk("virt_gpiof ioremap failed\n");
- return -ENOMEM;
- }
- //灯的初始化
- //LED1--PE10
- *virt_rcc |= (0x1<<4);
- virt_gpioe->MODER &= (~(0x3<<20));
- virt_gpioe->MODER |= (0x1<<20);
- //设置默认输出低电平
- virt_gpioe->ODR |= (~(0x1<<10));
- //LED2--PF10
- *virt_rcc |= (0x1<<5);
- virt_gpiof->MODER &= (~(0x3<<20));
- virt_gpiof->MODER |= (0x1<<20);
- virt_gpiof->ODR &= (~(0x1<<10));
- //LED3--PE8
- virt_gpioe->MODER &= (~(0x3<<16));
- virt_gpioe->MODER |= (0x1<<16);
- virt_gpioe->ODR &= (~(0x1<<8));
-
- return 0;
-
- }
- static void __exit mycdev_exit(void)
- {
- int i;
- iounmap(virt_rcc);
- iounmap(virt_gpioe);
- iounmap(virt_gpiof);
- //销毁节点
- for(i=0;i
- {
- device_destroy(cls,MKDEV(major,i));
- }
-
- //销毁目录信息
- class_destroy(cls);
- //驱动注销
- cdev_del(cdev);
- //注销设备号
- unregister_chrdev_region(MKDEV(major,minor),count);
- //5.释放dev结构体
- kfree(cdev);
-
- }
- module_init(mycdev_init);
- module_exit(mycdev_exit);
- MODULE_LICENSE("GPL");
测试:
1.查看到3个设备节点

case判断条件写反了,所以结果显示是反的。
-
相关阅读:
Python入坑系列-pyside6桌面编程之认识并设置理想字体效果
PHP生成图形验证码
Spring项目整合 XXL-JOB分布式任务调度平台
Java SE 9 多版本兼容 JAR 包示例
web安全最亲密的战友Burp Suite—网络攻防常用工具介绍--burp suit工具初体验一
掌握AI助手的魔法工具:解密`Prompt`(提示)在AIGC时代的应用(下篇)
错字修改 | 布署1个中文文文本拼蟹纠错模型
【Java杂谈】#1 【MCA JAVA后端架构师】
玩机教程:阿里云无影云电脑怎么使用?
企业级大数据平台智能运维好帮手——星环科技多模数据平台监控软件Aquila Insight
-
原文地址:https://blog.csdn.net/m0_68004652/article/details/127955728