本文接着《創建一個字符設備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