zoukankan      html  css  js  c++  java
  • 20150216简单的Linux字符设备驱动程序

    20150216简单的Linux字符设备驱动程序

    2015-02-16 李海沿

    关于字符设备驱动程序详细的知识点,本文就不再介绍了,很多同志,看了知识点,还是一头雾水,写不出来,所以,本文从实战出发,带领各位同胞们来实现一个字符设备驱动程序,改程序可作为字符设备的通用模板。

    好了废话不多说,先上驱动程序,在驱动程序中加入详细注释:

     
      1 /******************************
      2     linux 字符设备驱动程序
      3  *****************************/
      4 #include <linux/module.h>
      5 #include <linux/init.h>
      6 #include <linux/kernel.h>
      7 #include <linux/delay.h>
      8 #include <linux/types.h>
      9 #include <linux/ioctl.h>    
     10 #include <linux/gpio.h>
     11 #include <linux/fs.h>
     12 #include <linux/device.h>    //包含了用于自动创建设备节点的函数device_create
     13 #include <linux/uaccess.h>  //包含了copy_to_user 函数等
     14 
     15 #define Driver_NAME "key_query"
     16 #define DEVICE_NAME "key_query"
     17 
     18 //command in ioctl        
     19 #define version        0
     20 
     21 //用于保存主设备号
     22 static int major=0;
     23 
     24 //用于自动创建设备节点 代替了手动敲mknod命令
     25 static struct class *drv_class = NULL;
     26 static struct class_device *drv_class_dev = NULL;
     27 
     28 
     29 /* 应用程序对设备文件/dev/key_query执行open(...)时,
     30  * 就会调用key_open函数*/
     31 static int key_open(struct inode *inode, struct file *file)
     32 {
     33     printk("<0>function open!
    
    ");
     34     
     35     return 0;
     36 }
     37 
     38 /*当应用程序中read(fd,buff,sizeof(buff))时调用此key_read函数*/
     39 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
     40 {
     41     printk("<0>function read!
    
    ");
     42     return 0;
     43 }
     44 
     45 /* 当应用程序中使用write函数时,调用此函数**/
     46 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
     47 {
     48     printk("<0>function write!
    
    ");
     49     
     50     return 1;
     51 }
     52 
     53 static int  key_release(struct inode *inode, struct file *filp)
     54 {
     55     printk("<0>function release!
    
    ");
     56     return 0;
     57 }
     58 /* 当用户调用ioctl(fd,version,NULL);时,会进入此函数,
     59  * 在SWITCH中配对command,然后执行相应的语句 
     60  * 注意command 一定为整数,需在前面定义*/
     61 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)
     62 {
     63     printk("<0>function ioctl!
    
    ");
     64     switch (command) {
     65         case version:
     66             printk("<0>hello,the driver version is 0.1.0
    
    ");
     67             break;
     68         default:
     69               printk("<0>command error 
    ");
     70             printk("<0>ioctl(fd, (unsigned int)command, (unsigned long) arg;
    ");
     71             printk("<0>command: <version>
    
    ");
     72             return -1;
     73     }
     74     return 0;    
     75 }
     76 
     77 /* 这个结构是字符设备驱动程序的核心
     78  * 当应用程序操作设备文件时所调用的open、read、write等函数,
     79  * 最终会调用这个结构中指定的对应函数
     80  */
     81 static struct file_operations key_fops = {
     82     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
     83     .open   =   key_open,     
     84     .read    =    key_read,       
     85     .write    =    key_write,       
     86     .release=   key_release,
     87     .ioctl  =   key_ioctl,    
     88 };
     89     
     90 /*
     91  * 执行insmod命令时就会调用这个函数 
     92  */
     93 static int __init key_init(void)
     94 {
     95     printk("<0>
    Hello,this is %s module!
    
    ",Driver_NAME);
     96     //register and mknod
     97     //注册字符设备,系统会自动分配一个主设备号,保存在major中
     98     major = register_chrdev(0,Driver_NAME,&key_fops);
     99     //自动在/dev/目录下创建设备节点
    100     drv_class = class_create(THIS_MODULE,Driver_NAME);
    101     drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);    /*/dev/key_query*/
    102 
    103     return 0;
    104 }
    105 
    106 /*
    107  * 执行rmmod命令时就会调用这个函数 
    108  */
    109 static void __exit key_exit(void)
    110 {
    111     printk("<0>
    Goodbye,%s!
    
    ",Driver_NAME);
    112 
    113     //卸载字符设备,释放主设备号
    114     unregister_chrdev(major,Driver_NAME);
    115     //卸载字符设备的设备节点
    116     device_unregister(drv_class_dev);
    117     class_destroy(drv_class);
    118 
    119 }
    120 
    121 /* 这两行指定驱动程序的初始化函数和卸载函数 */
    122 module_init(key_init);
    123 module_exit(key_exit);
    124 
    125 /* 描述驱动程序的一些信息,不是必须的 */
    126 MODULE_AUTHOR("Lover雪儿");
    127 MODULE_VERSION("0.1.0");
    128 MODULE_DESCRIPTION("IMX257 key Driver");
    129 MODULE_LICENSE("GPL");
    View Code

    以下是个人理解,仅代表个人思想:

    static int __init key_init(void)

    如果单片机程序的角度来理解字符设备,那么上面程序的其实就是相当于我们的main函数。

    在linux系统中,我们使用insmod 加载驱动时,

    最先调用的,最先进入的就是__init key_init函数,所以我们所有初始化的代码都可以放在这个函数里,

    比如注册设备,创建设备驱动,申请内存,如果说是功能有关GPIO的话,那么GPIO的引脚的初始化也可以放在此函数中,所以总结的一句话,

    __init key_init函数就是负责初始化的函数。

    static void __exit key_exit(void)

    相应的,与__init key_init函数的功能相反,当我们不要用这个驱动的时候,系统就会调用这个函数。

    前面我们的__init key_init函数实现了初始化,注册设备,申请的内存等功能,为了资源的合理利用,所有的系统资源,当我们不要用的时候,我们就应该释放。好比说,借了别人的东西暂时使用,不用的时候,就应该归还别人。

    所有我们要释放的代码,那就是放在这个函数中执行。

    比较简单理解。

    module_init(key_init);

    module_exit(key_exit);

    当我们写两个函数key_init key_exit 函数时,操作系统没有那么智能的就知道这两个就是初始化和退出函数

    这两行代码就是指定我们的初始化和退出的函数,

    也就是把我们前面写的两个函数告诉linux操作系统,我们初始化和退出的函数

    static int key_open(struct inode *inode, struct file *file)

    但我们在应用程序中使用代码

    open("dev/xxx",O_RDWR);

    打开设备时,就会调用这个函数,这个函数中可以实现我们设备被打开时要执行的一些操作。如果没什么操作,可以不写。

    static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

    当应用程序中read(fd,buff,sizeof(buff))时调用此key_read函数

    所以,在此函数中,我们可以使用copy_to_user函数,来把内核空间中的数据传递到我们应用程序中。

    static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

    当我们应用程序中执行写入操作时,这个函数就发挥作用了,当然

    在此函数中还需要使用Copy_from_user来配合,将应用程序中的数据传递到内核空间,

    因为应用程序空间和内核空间是相互独立的,不能互相访问,必须借助于copy_from_use 和 copy_to_user 这两个函数。

    static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)

    当我们的驱动程序可以实现多中会互相冲突的功能时,可以在此函数中使用command来区分不同的功能,

    然后再switch函数中实现不同功能的代码。

    应用程序中使用ioctl(fd,version,NULL); 会进入此函数。

    比如说,当我们的驱动程序可以实现流水灯,花样灯等不同的功能时,我们就可以定义两个整形COMMAND,在此函数实现该功能。

    static struct file_operations key_fops = {

        .owner = THIS_MODULE,

        .open = key_open,

        .read    =    key_read,    

        .write    =    key_write,    

    .release= key_release,

    .ioctl = key_ioctl,    

    };

    这个结构是字符设备驱动程序的核心

    当应用程序操作设备文件时所调用的open、read、write等函数,

    最终会调用这个结构中指定的对应函数

    通俗来讲,这个结构体就是告诉linux操作系统,我们哪些已经实现的函数分别是驱动程序的 open,write,read,ioctl。

    当我们实现了上面的几个函数时,也就是实现了一个简单的驱动程序了。

    下面我们附上 Makefile的代码

     1 ifeq ($(KERNELRELEASE),)
     2     KERNELDIR ?= /home/study/system/linux-2.6.31
     3     PWD := $(shell pwd)
     4 modules:
     5     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
     6 modules_install:
     7     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
     8 clean:
     9     rm -rf *.o *~ core .depend  *cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers
    10 
    11 else
    12     obj-m := key.o
    13 endif
    View Code

     接下来,我们就在此基础上实现一个gpio的按键输入程序。

  • 相关阅读:
    GlobalUpdate script for service model: RetailServer on machine: <server> d365
    Configure the test environment to trust the connection RSAT with Dynamics 365
    Run a Child Flow with PowerAutomate
    Platform Power On You
    Demo website PowerPlatform Power Virtual Agents
    Azure DevOps is a Microsoft product that provides version control for Dynamics 365 CRM
    Forward:Stale statistics on a newly created temporary table in a stored procedure can lead to poor performance
    How to: Grant Permission on Development Severs as Batch for Windows Authentications
    Sharepoint 2013配置失败:SQL 实例没有将“MaxDegree of Parallelism”设置为1
    Special Offer: Watch Three InDepth SQL Server Courses by SQLskills (FREE)
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4294494.html
Copyright © 2011-2022 走看看