本文接着《创建一个字符设备1.1》讲如何创建字符设备模型、怎么创建设备文件
=============================================================================================
创建字符设备流程
1.定义一个cdev
2.申请设备号 .静态注册 .MKDEV .register_chrdev_region.动态注册
.alloc_chrdev_region3.定义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 0xbf0d3000rt3070ap 488261 1 rtnet3070ap, Live 0xbf044000 (P)led_drv 1203 0 - Live 0xbf028000buzzer_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 #include2 #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_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<
===================================================================================================
应用层代码
1 #include2 #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