本文接着《创建一个字符设备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 <linux/errno.h> 2 #include <linux/kernel.h> 3 #include <linux/module.h> 4 #include <linux/cdev.h> 5 #include <linux/fs.h> 6 #include <linux/uaccess.h> 7 #include <linux/ioport.h> 8 #include <linux/io.h> 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_open\n"); 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_close\n"); 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 fail\n"); 47 return -EFAULT; 48 } 49 if(kbuf[1]) 50 { 51 *(unsigned int *)gpj2dat_va &=~(1<<kbuf[0]); 52 } 53 else 54 { 55 *(unsigned int *)gpj2dat_va|= (1<<kbuf[0]); 56 57 } 58 59 60 printk("gec210_led_write,kbuf[0]=%d,kbuf[1]=%d\n",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 fail\n"); 79 rt = alloc_chrdev_region(&led_num,0,1,"gec210_led"); //动态注册设备号 80 if(rt < 0) 81 { 82 printk("alloc_chrdev_region fail\n"); 83 return rt; 84 } 85 led_major = MAJOR(led_num); // 获取主设备号 86 led_minor = MINOR(led_num); // 获取次设备号 87 printk("led_major=%d,led_minor=%d\n",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 fail\n"); 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 fail\n"); 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=%p\n",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 driver\n"); 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 <stdio.h> 2 #include <fcntl.h> 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