zoukankan      html  css  js  c++  java
  • linux驱动初探之字符驱动

    关键字:字符驱动、动态生成设备节点、helloworld

    linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~

      此驱动程序基于linux3.0的内核,exynos4412开发板。

    首先,打开平台文件,此开发板的平台文件是archarmmach-exynosmach-itop4412.c,不同平台位置是不一样的。

      申明一下设备信息,这里以编译进kernel为例

    1 #ifdef CONFIG_HELLO_CHAR_CTL
    2 struct platform_device s3c_device_hello_char_ctl = {
    3         .name   = "Hello_Jni_Char",
    4         .id             = -1,
    5 };
    6 #endif

    然后在struct platform_device *smdk4x12_devices[]数组里注册设备信息

    1 #ifdef CONFIG_HELLO_CHAR_CTL
    2     &s3c_device_hello_char_ctl,
    3 #endif

    至此,平台文件配置完毕。

    接下来,开始大餐,驱动程序的编写!

    注意:在linux的每一个驱动程序里面都需要加上如下信息,表明开源

    1 MODULE_LICENSE("Dual BSD/GPL");
    2 MODULE_AUTHOR("pngcui");

    每一个驱动程序都是从init函数开始的,同时也需要编写exit函数,在初始化函数中需要生成主设备号与从设备号,因为linux是根据主从设备号来寻找相应的硬件的

     1 #define DEV_MAJOR 0
     2 #define DEV_MINOR 0
     3 
     4 int numdev_major = DEV_MAJOR;
     5 int numdev_minor = DEV_MINOR;
     6 
     7 
     8 /*输入主设备号*/
     9 module_param(numdev_major,int,S_IRUSR);
    10 /*输入次设备号*/
    11 module_param(numdev_minor,int,S_IRUSR);

    进入驱动程序中的init函数中,进行注册设备号,生成设备节点DEVICE_NAME

    1 #define DEVICE_NAME "chardevnode"

    注意:此驱动由于控制了两个gpio引脚所以出现了gpio_init(),在这里可以忽略,对于gpio的控制,在下一篇博客中会写到。

     1 static int scdev_init(void)
     2 {
     3     int ret = 0,i;
     4     dev_t num_dev;
     5     
     6     
     7     printk(KERN_EMERG "numdev_major is %d!
    ",numdev_major);
     8     printk(KERN_EMERG "numdev_minor is %d!
    ",numdev_minor);
     9     
    10     if(numdev_major){
    11         num_dev = MKDEV(numdev_major,numdev_minor);
    12         ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
    13     }
    14     else{
    15         /*动态注册设备号*/
    16         ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
    17         /*获得主设备号*/
    18         numdev_major = MAJOR(num_dev);
    19         printk(KERN_EMERG "adev_region req %d !
    ",numdev_major);
    20     }
    21     if(ret<0){
    22         printk(KERN_EMERG "register_chrdev_region req %d is failed!
    ",numdev_major);        
    23     }
    24     myclass = class_create(THIS_MODULE,DEVICE_NAME);
    25     
    26     
    27     my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
    28     if(!my_devices){
    29         ret = -ENOMEM;
    30         goto fail;
    31     }
    32     memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
    33     
    34     /*设备初始化*/
    35     for(i=0;i<DEVICE_MINOR_NUM;i++){
    36         my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
    37         memset(my_devices[i].data,0,REGDEV_SIZE);
    38         /*设备注册到系统*/
    39         reg_init_cdev(&my_devices[i],i);
    40         
    41         /*创建设备节点*/
    42         device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
    43     }
    44     
    45     ret = gpio_init();
    46     if(ret){
    47         printk(KERN_EMERG "gpio_init failed!
    ");
    48     }    
    49         
    50     printk(KERN_EMERG "scdev_init!
    ");
    51     return 0;
    52 
    53 fail:
    54     /*注销设备号*/
    55     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
    56     printk(KERN_EMERG "kmalloc is fail!
    ");
    57     
    58     return ret;
    59 }
    60 
    61 static void scdev_exit(void)
    62 {
    63     int i;
    64     printk(KERN_EMERG "scdev_exit!
    ");
    65     
    66     /*除去字符设备*/
    67     for(i=0;i<DEVICE_MINOR_NUM;i++){
    68         cdev_del(&(my_devices[i].cdev));
    69         /*摧毁设备节点函数*/
    70         device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
    71     }
    72     /*释放设备class*/
    73     class_destroy(myclass);
    74     /*释放内存*/
    75     kfree(my_devices);
    76     
    77     /*释放GPIO*/
    78     for(i=0;i<LED_NUM;i++){
    79         gpio_free(led_gpios[i]);
    80     }
    81         
    82     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
    83 }
    84 
    85 
    86 module_init(scdev_init);
    87 /*初始化函数*/
    88 module_exit(scdev_exit);
    89 /*卸载函数*/

    然后把设备注册到系统

     1 static void reg_init_cdev(struct reg_dev *dev,int index){
     2     int err;
     3     int devno = MKDEV(numdev_major,numdev_minor+index);
     4 
     5     /*数据初始化*/
     6     cdev_init(&dev->cdev,&my_fops);
     7     dev->cdev.owner = THIS_MODULE;
     8     dev->cdev.ops = &my_fops;
     9     
    10     /*注册到系统*/
    11     err = cdev_add(&dev->cdev,devno,1);
    12     if(err){
    13         printk(KERN_EMERG "cdev_add %d is fail! %d
    ",index,err);
    14     }
    15     else{
    16         printk(KERN_EMERG "cdev_add %d is success!
    ",numdev_minor+index);
    17     }
    18 }
    dev->cdev.ops = &my_fops;这一句就指明了此驱动程序的函数接口
    1 struct file_operations my_fops = {
    2     .owner = THIS_MODULE,
    3     .open = chardevnode_open,
    4     .release = chardevnode_release,
    5     .unlocked_ioctl = chardevnode_ioctl,
    6     .read = chardevnode_read,
    7     .write = chardevnode_write,
    8     .llseek = chardevnode_llseek,
    9 };

    在这里的所有函数接口必须都实现,否则会编译报错。

    其中最重要的两个函数是open、ioctl这两个函数,open函数用来打开设备节点,然后才可以对设备进行驱动。

    然后是ioctl函数,一般上层应用都是通过调用此函数来对设备进行操作的。

     1 /*打开操作*/
     2 static int chardevnode_open(struct inode *inode, struct file *file){
     3     printk(KERN_EMERG "chardevnode_open is success!
    ");
     4     
     5     return 0;
     6 }
     7 /*关闭操作*/
     8 static int chardevnode_release(struct inode *inode, struct file *file){
     9     printk(KERN_EMERG "chardevnode_release is success!
    ");
    10     
    11     return 0;
    12 }
    13 /*IO操作*/
    14 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
    15     
    16     switch(cmd)
    17     {
    18         case 0:
    19         case 1:
    20             if (arg > LED_NUM) {
    21                 return -EINVAL;
    22             }
    23 
    24             gpio_set_value(led_gpios[arg], cmd);
    25             break;
    26 
    27         default:
    28             return -EINVAL;
    29     }
    30     
    31     printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d 
    ",cmd,arg);
    32     
    33     return 0;
    34 }
    35 
    36 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
    37     return 0;
    38 }
    39 
    40 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
    41     return 0;
    42 }
    43 
    44 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){
    45     return 0;
    46 }

    到这里一个完整的字符驱动就编写完毕了。

    最后附上完整的驱动程序

      1 /*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
      2 #include <linux/init.h>
      3 /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
      4 #include <linux/module.h>
      5 /*定义module_param module_param_array的头文件*/
      6 #include <linux/moduleparam.h>
      7 /*定义module_param module_param_array中perm的头文件*/
      8 #include <linux/stat.h>
      9 /*三个字符设备函数*/
     10 #include <linux/fs.h>
     11 /*MKDEV转换设备号数据类型的宏定义*/
     12 #include <linux/kdev_t.h>
     13 /*定义字符设备的结构体*/
     14 #include <linux/cdev.h>
     15 /*分配内存空间函数头文件*/
     16 #include <linux/slab.h>
     17 /*包含函数device_create 结构体class等头文件*/
     18 #include <linux/device.h>
     19 
     20 /*自定义头文件*/
     21 #include "char_driver_leds.h"
     22 
     23 /*Linux中申请GPIO的头文件*/
     24 #include <linux/gpio.h>
     25 /*三星平台的GPIO配置函数头文件*/
     26 /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
     27 #include <plat/gpio-cfg.h>
     28 /*三星平台4412平台,GPIO宏定义头文件*/
     29 #include <mach/gpio-exynos4.h>
     30 
     31 
     32 MODULE_LICENSE("Dual BSD/GPL");
     33 /*声明是开源的,没有内核版本限制*/
     34 MODULE_AUTHOR("iTOPEET_dz");
     35 /*声明作者*/
     36 
     37 static int led_gpios[] = {
     38     EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
     39 };
     40 #define LED_NUM        ARRAY_SIZE(led_gpios)
     41 
     42 #define DEVICE_NAME "chardevnode"
     43 
     44 #define DEVICE_MINOR_NUM 1
     45 
     46 #define DEV_MAJOR 0
     47 #define DEV_MINOR 0
     48 #define REGDEV_SIZE 3000
     49 
     50 
     51 struct reg_dev
     52 {
     53     char *data;
     54     unsigned long size;
     55     
     56     struct cdev cdev;
     57 };
     58 
     59 
     60 int numdev_major = DEV_MAJOR;
     61 int numdev_minor = DEV_MINOR;
     62 
     63 /*输入主设备号*/
     64 module_param(numdev_major,int,S_IRUSR);
     65 /*输入次设备号*/
     66 module_param(numdev_minor,int,S_IRUSR);
     67 
     68 static struct class *myclass;
     69 struct reg_dev *my_devices;
     70 
     71 /*打开操作*/
     72 static int chardevnode_open(struct inode *inode, struct file *file){
     73     printk(KERN_EMERG "chardevnode_open is success!
    ");
     74     
     75     return 0;
     76 }
     77 /*关闭操作*/
     78 static int chardevnode_release(struct inode *inode, struct file *file){
     79     printk(KERN_EMERG "chardevnode_release is success!
    ");
     80     
     81     return 0;
     82 }
     83 /*IO操作*/
     84 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
     85     
     86     switch(cmd)
     87     {
     88         case 0:
     89         case 1:
     90             if (arg > LED_NUM) {
     91                 return -EINVAL;
     92             }
     93 
     94             gpio_set_value(led_gpios[arg], cmd);
     95             break;
     96 
     97         default:
     98             return -EINVAL;
     99     }
    100     
    101     printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d 
    ",cmd,arg);
    102     
    103     return 0;
    104 }
    105 
    106 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){
    107     return 0;
    108 }
    109 
    110 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){
    111     return 0;
    112 }
    113 
    114 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){
    115     return 0;
    116 }
    117 struct file_operations my_fops = {
    118     .owner = THIS_MODULE,
    119     .open = chardevnode_open,
    120     .release = chardevnode_release,
    121     .unlocked_ioctl = chardevnode_ioctl,
    122     .read = chardevnode_read,
    123     .write = chardevnode_write,
    124     .llseek = chardevnode_llseek,
    125 };
    126 
    127 
    128 /*设备注册到系统*/
    129 static void reg_init_cdev(struct reg_dev *dev,int index){
    130     int err;
    131     int devno = MKDEV(numdev_major,numdev_minor+index);
    132 
    133     /*数据初始化*/
    134     cdev_init(&dev->cdev,&my_fops);
    135     dev->cdev.owner = THIS_MODULE;
    136     dev->cdev.ops = &my_fops;
    137     
    138     /*注册到系统*/
    139     err = cdev_add(&dev->cdev,devno,1);
    140     if(err){
    141         printk(KERN_EMERG "cdev_add %d is fail! %d
    ",index,err);
    142     }
    143     else{
    144         printk(KERN_EMERG "cdev_add %d is success!
    ",numdev_minor+index);
    145     }
    146 }
    147 
    148 static int gpio_init(void){
    149     int i=0,ret;
    150     
    151     for(i=0;i<LED_NUM;i++){
    152         ret = gpio_request(led_gpios[i], "LED");
    153         if (ret) {
    154             printk("%s: request GPIO %d for LED failed, ret = %d
    ", DEVICE_NAME,i,ret);
    155             return -1;
    156         }
    157         else{
    158             s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
    159             gpio_set_value(led_gpios[i], 1);            
    160         }
    161     }
    162     
    163     return 0;
    164 }
    165 
    166 
    167 static int scdev_init(void)
    168 {
    169     int ret = 0,i;
    170     dev_t num_dev;
    171     
    172     
    173     printk(KERN_EMERG "numdev_major is %d!
    ",numdev_major);
    174     printk(KERN_EMERG "numdev_minor is %d!
    ",numdev_minor);
    175     
    176     if(numdev_major){
    177         num_dev = MKDEV(numdev_major,numdev_minor);
    178         ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
    179     }
    180     else{
    181         /*动态注册设备号*/
    182         ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
    183         /*获得主设备号*/
    184         numdev_major = MAJOR(num_dev);
    185         printk(KERN_EMERG "adev_region req %d !
    ",numdev_major);
    186     }
    187     if(ret<0){
    188         printk(KERN_EMERG "register_chrdev_region req %d is failed!
    ",numdev_major);        
    189     }
    190     myclass = class_create(THIS_MODULE,DEVICE_NAME);
    191     
    192     
    193     my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);
    194     if(!my_devices){
    195         ret = -ENOMEM;
    196         goto fail;
    197     }
    198     memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));
    199     
    200     /*设备初始化*/
    201     for(i=0;i<DEVICE_MINOR_NUM;i++){
    202         my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
    203         memset(my_devices[i].data,0,REGDEV_SIZE);
    204         /*设备注册到系统*/
    205         reg_init_cdev(&my_devices[i],i);
    206         
    207         /*创建设备节点*/
    208         device_create(myclass,NULL,MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NAME"%d",i);
    209     }
    210     
    211     return 0;
    212 
    213 fail:
    214     /*注销设备号*/
    215     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
    216     printk(KERN_EMERG "kmalloc is fail!
    ");
    217     
    218     return ret;
    219 }
    220 
    221 static void scdev_exit(void)
    222 {
    223     int i;
    224     printk(KERN_EMERG "scdev_exit!
    ");
    225     
    226     /*除去字符设备*/
    227     for(i=0;i<DEVICE_MINOR_NUM;i++){
    228         cdev_del(&(my_devices[i].cdev));
    229         /*摧毁设备节点函数d*/
    230         device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
    231     }
    232     /*释放设备class*/
    233     class_destroy(myclass);
    234     /*释放内存*/
    235     kfree(my_devices);
    236     
    237     /*释放GPIO*/
    238     for(i=0;i<LED_NUM;i++){
    239         gpio_free(led_gpios[i]);
    240     }
    241         
    242     unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
    243 }
    244 
    245 
    246 module_init(scdev_init);
    247 /*初始化函数*/
    248 module_exit(scdev_exit);
    249 /*卸载函数*/
    View Code

    以及完整的测试程序

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include <sys/ioctl.h>
     6 #include <stdio.h>
     7 
     8 int main(){
     9     
    10     int fd;
    11     
    12     if((fd = open("/dev/chardevnode0",O_RDWR|O_NDELAY)) < 0)
    13         printf("Hello_Jni open failed!
    ");
    14     
    15     ioctl(fd,0,0);
    16     ioctl(fd,0,1);
    17     
    18     close(fd);
    19     
    20     return 0;
    21 }
  • 相关阅读:
    Django 模版当中使用中文 UnicodeDecodeError at / 问题
    sql中索引不会被用到的几种情况
    Django安装
    分享一个webapi接口性能测试的工具
    orleans 2.0 进阶之自定义持久化储存
    centos7 .net core 使用supervisor守护进程,可以后台运行
    将.net core 发布到Linux上的一些坑
    .net core 部署在Linux系统上运行的环境搭建总结
    .net core 基于NPOI 的excel导入导出类,支持自定义导出哪些字段,和判断导入是否有失败的记录
    基于Ace Admin 的菜单栏实现
  • 原文地址:https://www.cnblogs.com/pngcui/p/4766504.html
Copyright © 2011-2022 走看看