创建一个字符设备1.2
2025-02-17 来源:cnblogs
本文接着《创建一个字符设备1.1》讲如何创建字符设备模型、怎么创建设备文件
=============================================================================================
创建字符设备流程
1.定义一个cdev
2.申请设备号
.静态注册
.MKDEV
.register_chrdev_region
.动态注册
.alloc_chrdev_region
3.定义file_operations,并且初始化
4.cdev初始化
.cdev_init
5.将cdev加入到内核
.cdev_add
6.创建设备文件
.手动创建设备文件,去/dev目录进行创建
.自动创建
前面《创建一个字符设备1.1》已经讲了初始化驱动函数、卸载驱动函数。
这里多了设备、设备号(主次设备号)、为应用层提供操作设备驱动的方法集、设备文件。
------------------------------------------------------------------------------------------------------------
针对上面的内容,下面会一一讲解。
1.我们先看设备文件,在/dev 目录下可查看当前挂载的设备文件
[root@Mr.Jin Fa /dev]# ls -l
c: 字符设备 10主设备号 242次设备号 CEC设备文件名
crw-rw---- 1 root root 10, 242 Jan 1 16:04 CEC
crw-rw---- 1 root root 10, 243 Jan 1 16:04 HPD
crw-rw---- 1 root root 14, 12 Jan 1 16:04 adsp
设备文件是驱动层用来给应用层调用(调用应用层调用open()获取文件描述符时)
2.设备是一个由硬件抽象出来的“实体”,它具有完成驱动程序功能。用lsmod查看当前挂载的设备
[root@Mr.Jin Fa /]# lsmod
rtnet3070ap 24124 0 - Live 0xbf0d3000
rt3070ap 488261 1 rtnet3070ap, Live 0xbf044000 (P)
led_drv 1203 0 - Live 0xbf028000
buzzer_drv 1488 0 - Live 0xbf022000
........
3.设备号是作为区分驱动的标识符,它如进程的PID。
主设备号:用来区分不同硬件类型,如网络设备和串口设备它们的主设备号很大程度是不同的
(为什么这样说呢,因为还是一个原则保证设备号不同就好了)。
次设备号:用来区分同一硬件类型下的不同硬件,如串口0和串口1硬件类型相同,它们的主设备号
相同,但是次设备号不会相同。
设备号:通过调用MKDEV(adc_major,adc_minor)函数输入想要申请的主设备号、次设备号。返回设备号
4.用于层操作驱动方法集和
static const struct file_operations gec210_led_fops = {
.owner = THIS_MODULE, 申明这个驱动属于本内核
.write = gec210_led_write, 定义用作写函数
.open = gec210_led_open, 定义打开驱动函数
.release = gec210_led_close, 定义用于关闭驱动函数
};
=========================================================================
下面贴代码细讲
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 static struct cdev gec210_led_drv; // 定义一个设备 11 unsigned int major=238,minor=0; // 定义并初始化表示主次设备号的变量 12 unsigned int led_num=0; // 定义设备号 13 int rt=0; // 定义表示错误返回码 14 15 static struct resource *led_res; // ioremap()后的虚拟地址的首地址 16 static void __iomem * gpj2con_va; // 表示gpj2con_va虚拟地址变量 17 static void __iomem * gpj2dat_va; // 表示gpj2dat_va 虚拟地址变量 18 19 20 int gec210_led_open (struct inode * inode, struct file *file) 21 { 22 printk('gec210_led_openn'); 23 //将GPJ2CON配置为输出模式 24 *(unsigned int *)gpj2con_va &=~0xFFFF; 25 *(unsigned int *)gpj2con_va |= 0x1111; 26 *(unsigned int *)gpj2dat_va |= 0xF; 27 28 return 0; 29 } 30 31 32 int gec210_led_close (struct inode *inode, struct file *file) 33 { 34 printk('gec210_led_closen'); 35 return 0; 36 37 } 38 39 ssize_t gec210_led_write (struct file *file, const char __user *buf, size_t len, loff_t *offset) 40 { 41 char kbuf[2]={0}; 42 if(len > 2) 43 return -EINVAL; 44 rt = copy_from_user(kbuf,buf,len); 45 if(rt !=0){ 46 printk('copy_from_user failn'); 47 return -EFAULT; 48 } 49 if(kbuf[1]) 50 { 51 *(unsigned int *)gpj2dat_va &=~(1< 53 else 54 { 55 *(unsigned int *)gpj2dat_va|= (1< 57 } 58 59 60 printk('gec210_led_write,kbuf[0]=%d,kbuf[1]=%dn',kbuf[0],kbuf[1]); 61 return len; 62 } 63 64 static const struct file_operations gec210_led_fops={ 65 .owner = THIS_MODULE, // 代表改驱动适用于本内核 66 .open = gec210_led_open, // 定义为应用层提供的打开驱动函数 67 .release = gec210_led_close, // 定义为应用层提供的关闭驱动函数 68 .write = gec210_led_write, // 定义为应用层提供的写函数 69 }; 70 71 int __init gec210_led_init(void) 72 { 73 //申请设备号 74 led_num=MKDEV(led_major,led_minor); 75 rt = register_chrdev_region(led_num,1,'gec210_led'); 76 if(rt < 0) 77 { 78 printk('register_chrdev_region failn'); 79 rt = alloc_chrdev_region(&led_num,0,1,'gec210_led'); //动态注册设备号 80 if(rt < 0) 81 { 82 printk('alloc_chrdev_region failn'); 83 return rt; 84 } 85 led_major = MAJOR(led_num); // 获取主设备号 86 led_minor = MINOR(led_num); // 获取次设备号 87 printk('led_major=%d,led_minor=%dn',led_major,led_minor); 88 } 89 90 //字符设备初始化 91 cdev_init(&gec210_led_cdev,&gec210_led_fops); 92 //字符设备加入内核 93 rt = cdev_add(&gec210_led_cdev,led_num,1); 94 if(rt < 0){ 95 goto fail_cdev_add; 96 97 } 98 //申请物理内存区,起始地址为0xE0200280,申请内存空间为8个字节,登记名字为:GPJ2_LED 99 led_res=request_mem_region(0xE0200280,8,'GPJ2_LED'); 100 if(led_res == NULL){ 101 printk('request_mem_region 0xE0200280 failn'); 102 rt = -EBUSY; 103 goto fail_request_mem_region; 104 } 105 106 //IO内存的动态映射 107 gpj2con_va=ioremap(0xE0200280,8); 108 if(gpj2con_va == NULL){ 109 printk('ioremap 0xE0200280 failn'); 110 rt = -EBUSY; 111 goto fail_ioremap; 112 } 113 114 gpj2dat_va = gpj2con_va + 4; 115 printk('hello gec210 led driver,gpj2con_va=%p,gpj2dat_va=%pn',gpj2con_va,gpj2dat_va); 116 return 0; 117 118 fail_ioremap: 119 release_mem_region(0xE0200280,8); 120 121 fail_request_mem_region: 122 cdev_del(&gec210_led_cdev); 123 124 fail_cdev_add: 125 unregister_chrdev_region(led_num,1); 126 127 return rt; 128 } 129 130 void __exit gec210_led_exit(void) 131 { 132 iounmap(gpj2con_va); 133 release_mem_region(0xE0200280,8); 134 cdev_del(&gec210_led_cdev); 135 unregister_chrdev_region(led_num,1); 136 printk('exit gec210 led drivern'); 137 } 138 139 140 module_init(gec210_led_init); //insmod 141 module_exit(gec210_led_exit); //rmmod 142 143 MODULE_AUTHOR('stephen.wen'); 144 MODULE_DESCRIPTION('S5PV210 LED driver'); 145 MODULE_LICENSE('GPL'); =================================================================================================== 应用层代码 1 #include 2 #include 3 4 int main(void) 5 { 6 int fd; 7 int ret; 8 char led0_on_buf[2]={0,1}; //0,led的号码;1,亮 9 char led0_off_buf[2]={0,0}; //0,led的号码;0,灭 10 fd = open('/dev/led_drv', O_RDWR); 11 if(fd < 0){ 12 perror('open /dev/'); 13 return -1; 14 } 15 while(1) 16 { 17 //亮 18 write(fd,led0_on_buf,2); 19 sleep(1); 20 21 //灭 22 write(fd,led0_off_buf,2); 23 sleep(1); 24 } 25 close(fd); 26 return 0; 27 } 由于我们没有做杂项设备、类设备所以我们必须在/dev 目录下新建设备文件 先看加载驱动 insmod [root@Mr.Jin Fa /]# insmod LedDrv.ko [ 4480.789028] hello gec210 led driver,gpj2con_va=e09de280,gpj2dat_va=e09de284 在/dev 目录下建设备文件 [root@Mr.Jin Fa /]# insmod /dev/led_drv c 250 0 [root@Mr.Jin Fa /dev]# ls -l | grep 'led_drv' crw-r--r-- 1 root root 250, 0 Jan 12 19:13 led_drv 然后我们之间运行程序就好了, 下面是效果: [root@GEC210 /]# chmod 777 led_test [root@GEC210 /]# ./led_test [ 4835.601157] gec210_led_open [ 4835.601205] gec210_led_write,kbuf[0]=0,kbuf[1]=1 [ 4836.601335] gec210_led_write,kbuf[0]=0,kbuf[1]=0 [ 4837.601422] gec210_led_write,kbuf[0]=0,kbuf[1]=1 [ 4838.601510] gec210_led_write,kbuf[0]=0,kbuf[1]=0 [ 4839.601599] gec210_led_write,kbuf[0]=0,kbuf[1]=1 [ 4840.601686] gec210_led_write,kbuf[0]=0,kbuf[1]=0 ^C[ 4841.497062] gec210_led_close
上一篇:创建一个字符设备1.1
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 蓝牙信道探测技术原理与开发套件实践
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析




