zoukankan      html  css  js  c++  java
  • 字符设备驱动模块与测试代码编写。

    设备驱动程序:以内核模块的形式存在也就是*.ko

    设备驱动程序和系统调用关系.
    系统调用:应有程序和操作系统(内核) 之间的接口(应用程序与内核的交互)
    设备驱动程序:内核和设备硬件之间接口(内核与硬件的交互)

    整个过程实现了应用程序间接访问了底层的硬件。

    test.c中调用open-----》系统调用(sys_open())----->file_operation{.open = led_open}:调用驱动函数中的自定义的open

    ---------------------------------------------------------------

    linux设备驱动的三种类型:

    1.字符设备:LED,显卡,声卡,键盘,触摸屏。。。
    2.块设备:硬盘,nandflash,SD,U盘....
    3.网络设备:网卡(无线,有线)

    ---------------------------------------------------------------

    二。字符设备特点
    1.应用程序和设备进行数据交互时,是以字节单位进行的。
    2。访问的数据是连续时,实时。
    3.不带缓存,块设备驱动带有缓存.

    三。字符设备的描述
    1。cdev结构
    struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list; //内核链表
    dev_t dev;
    unsigned int count;//当前设备下有多少个次设备
    };
    cdev是描述一个字符设备,每写一字符设备,创建一个cdev结构,每个字符设备都有自已的cdev

    --struct kobject kobj;
    不关心的,是由内核管理设备使用,
    --struct module *owner;
    cdev属于那个体module,一般写THIS_MODULE;
    --const struct file_operations *ops;
    操作集(设备驱动),是应用程序访问设备的驱动接口

    --struct list_head list;
    内核链表:将当前cdev放到一链表,方便内核管理cdev

    --dev_t dev;
    typedef u_long dev_t;
    typedef unsigned long u_long;
    设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形
    --unsigned int count;
    在当设备下有多少个次设备...

    struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    -----------------------------------------------
    };

    ------------------------------------------------------------------

    cdev中的设备号:

    2.1定义
    typedef u_long dev_t;
    typedef unsigned long u_long;
    设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形。

    2.2设备号由主设备号和次设备号组成:
    主设备号:某类型类的设备 [31:20]
    次设备号:该类型的设备下,具体那个设备实例.[19:0]

    2.3相关函数(宏):由设备号的得到主次设备号

    #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
    #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

      由主次设备号得到设备号:

    #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
    ma-->主设备号
    mi-->次设备号

      向内核中注册设备号:

    定义一个CDEV时候,首先在内核中申请一个设备号,获得设备号的方法有两种。

    1、向内核中静态注册一个设备号:自已定制一个设备号,向内核注册(申请),如果内核中没有占用设备号,代注册成功。

    * register_chrdev_region() - register a range of device numbers
    * @from: the first in the desired range of device numbers; must include
    * the major number.
    * @count: the number of consecutive device numbers required
    * @name: the name of the device or driver.
    *
    * Return value is zero on success, a negative error code on failure.
    */
    int register_chrdev_region(dev_t from, unsigned count, const char *name)
    参数说明:
    from-->设备号的值
    count-->次设备的数量
    name->设备的名称.
    返回值:
    成功:0
    失败:负数

    例:
    [@GEC2103 /]# ls /dev/s3c241* -l
    crw-rw---- 1 0 0 204, 64 Jan 1 00:00 /dev/s3c2410_serial0
    crw-rw---- 1 0 0 204, 65 Jan 1 00:00 /dev/s3c2410_serial1
    crw-rw---- 1 0 0 204, 66 Jan 1 00:00 /dev/s3c2410_serial2
    crw-rw---- 1 0 0 204, 67 Jan 1 00:00 /dev/s3c2410_serial3

    register_chrdev_region(MKDEV(204,64),4,“s3c2410_serial”)


    #cat /proc/devices
    204 s3c2410_serial

    2、让内核动态分配一个空闲设备号

    /**
    * alloc_chrdev_region() - register a range of char device numbers
    * @dev: output parameter for first assigned number
    * @baseminor: first of the requested range of minor numbers
    * @count: the number of minor numbers required
    * @name: the name of the associated device or driver
    *
    * Allocates a range of char device numbers. The major number will be
    * chosen dynamically, and returned (along with the first minor number)
    * in @dev. Returns zero or a negative error code.
    */
    int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
    参数:
    *dev:-->申请设备号
    baseminor-->申请设备号第一个次设备号
    count-->申请次设备的数量
    name-->设备名称

    返回值:
    成功:0
    失败:负数

    注意:一定判断申请是成功


    dev_t det;
    alloc_chrdev_region(&det,0,2,"test");

    3、注销设备号

    3.3注销一个已经注册的设备号
    当驱动程序移除时,一定先注销设备号
    /**
    * unregister_chrdev_region() - return a range of device numbers
    * @from: the first in the range of numbers to unregister
    * @count: the number of device numbers to unregister
    *
    * This function will unregister a range of @count device numbers,
    * starting with @from. The caller should normally be the one who
    * allocated those numbers in the first place...
    */
    void unregister_chrdev_region(dev_t from, unsigned count)
    参数:from-->设备号的值,第一个设备号
    count-->次设备号的数量

    --------------------------------------------------------------------------

    3.文件操作集
    struct file_operations

    文件操作集是内核管理cdev那设备的驱动接口, 驱动程序提供应用程序一个接口

    int testopen(struct inode *inode, struct file *file)
    {



    }
    int testclose (struct inode *inode, struct file *file);
    {


    }
    ssize_t testread(struct file *, char __user *, size_t, loff_t *)
    {


    }

    struct file_operations fops= --->结构体初始化 .---???????????????
    { .owner=THIS_MODULE,
    .open=testopen,
    .read=testread,
    .release=testclose,
    }

    ----------------------------------------------------------------------

    4.cdev初始化和注册
    4.1.cdev的初始化函数
    /**
    * cdev_init() - initialize a cdev structure
    * @cdev: the structure to initialize
    * @fops: the file_operations for this device
    *
    * Initializes @cdev, remembering @fops, making it ready to add to the
    * system with cdev_add().
    */
    void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    参数:
    cdev-->要初始化cdev
    fops-->cdev的文件操作集


    4.2.注册cdev到内核的函数
    /**
    * cdev_add() - add a char device to the system
    * @p: the cdev structure for the device
    * @dev: the first device number for which this device is responsible
    * @count: the number of consecutive minor numbers corresponding to this
    * device
    *
    * cdev_add() adds the device represented by @p to the system, making it
    * live immediately. A negative error code is returned on failure.
    */
    int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    参数:p --->注册那个cdev
    dev--->设备号
    count-->次设备个数


    返回值:
    成功:0
    失败:负数

    例:
    strcut cdev cd;
    cdev_init(&cd,&fops)
    cd.owner=THIS_MODULE;
    cdev_add(&cd,dev,1);

    --------------------------------------------------------------

    4.3cdev的注销函数
    /**
    * cdev_del() - remove a cdev from the system
    * @p: the cdev structure to be removed
    *
    * cdev_del() removes @p from the system, possibly freeing the structure
    * itself.
    */
    void cdev_del(struct cdev *p)
    p-->要注销那个cdev .

    注意:在linux驱动设计时候,我们使用的函数,一般都成对,申请<--->释放 注册--->注销

    ------------------------------------------------------------------------

    5。字符设备的设计流程
    5.1定义一个cdev
    struct cdev chrdev3;
    5.2申请一个设备号
    例:
    unsigned int TestMajor=0;
    unsigned int TestMinor=0;
    dev_t dev_no;
    int ret;
    dev_no =MKDEV(TestMajor,TestMinor)


    if(dev_no>0)
    {
    ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

    }
    else //动态申请设备号
    {

    alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

    }


    if(ret<0)
    {
    return ret;
    }

    5.3定义文件操作集
    int testopen(struct inode *inode, struct file *file)
    {



    }
    int testclose (struct inode *inode, struct file *file);
    {


    }
    ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
    {


    }

    struct file_operations fops= --->结构体初始化 .---???????????????
    { .owner=THIS_MODULE,
    .open=testopen,
    .write=testwrite,
    .release=testclose,
    }

    5.4cdev初始化
    cdev_init(&chrdev3,&fops);

    5.5cdev注册到内核
    cdev_add(&chrdev3,dev_no,1);先初始化设备号,然后注册cdev

    5.6设备号和cdev注销
    unregister_chrdev_region(dev_no, 1);
    cdev_dev(&chrdev3);

    -------------------------------------------------------------------------

    .结合模块规则:写字符设备驱动程序
    test.c

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include<linux/cdev.h>

    struct cdev chrdev3;
    unsigned int TestMajor=0;
    unsigned int TestMinor=0;
    dev_t dev_no;

    int testopen(struct inode *inode, struct file *file)
    {
    //LED输出
    printk("led init ");

    }
    int testclose (struct inode *inode, struct file *file);
    {
    printk("close");
    return 0;

    }
    ssize_t testwrtie(struct file *, char __user *usr, size_t len, loff_t *offf)
    {
    char buf[12];
    copy_from_user(buf,usr,);
    buf[12];
    //if(buf[]=='1')
    // led点明
    printk(,buf);


    }
    ssize_t testread(struct file *, char __user *usr, size_t len, loff_t *);
    {
    char buf='r';
    read_led;
    copy_to_user(usr,buf,);
    }

    struct file_operations fops= --->结构体初始化 .---???????????????
    { .owner=THIS_MODULE,
    .open=testopen,
    .write=testwrite,
    .release=testclose,
    }


    static int __init test_init(void) //入口函数
    {
    printk("hello world! "); //相当于printf()
    int ret;
    dev_no =MKDEV(TestMajor,TestMinor)


    if(dev_no>0)
    {
    ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

    }
    else //动态申请设备号
    {

    alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

    }


    if(ret<0)
    {
    return ret;
    }


    cdev_init(&chrdev3,&fops);
    cdev.owner=THIS_MODULE;
    cdev_add(&chrdev3,dev_no,1);
    return 0;
    }

    static void __exit test_exit(void) //出口函数
    {
    unregister_chrdev_region(dev_no, 1);
    cdev_del(&chrdev3);
    }


    module_init(test_init); //驱动的入口 #insmod *.ko
    module_exit(test_exit); //驱动的出口 #rmmod *.ko

    //#modinfo *.ko 可以查看module的信息
    MODULE_AUTHOR("fbx@GEC");
    MODULE_DESCRIPTION("the first module of drivers");
    MODULE_LICENSE("GPL");
    MODULE_VERSION("V1.0");

    ---------------------------------------------------------------------

    8.linux用户空间与内核空间交互函数。
    8.1将内核空间数据copy用户空间
    unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

    8.2用户空间数据copy内核空间
    unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) ---> write


    练习:应用调用write写一个字符到内核空间,内核那驱动程序的.write---》 PRINTK();
    应用调用read把一个数据从内核读到用户,用户空间打印出来

    chrdev.c

     1 #include<linux/module.h>
     2 #include<linux/kernel.h>
     3 #include<linux/cdev.h>
     4 #include<linux/fs.h>
     5 #include<linux/kdev_t.h>
     6 #include<linux/types.h>
     7 #include<linux/uaccess.h>
     8 #include<linux/string.h>
     9 struct cdev chrdev;
    10 unsigned int TestMajor=0;
    11 unsigned int TestMinor=0;
    12 dev_t dev_no;
    13 int ret;
    14 
    15 int testopen(struct inode *inode,struct file *file)
    16 {
    17     printk("cdev init
    ");
    18     return 0;
    19     
    20 }
    21 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
    22 {
    23     char buf[12];
    24     
    25     copy_from_user(buf,usr,strlen(usr));
    26     printk("%s
    ",buf);
    27 
    28 }
    29 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
    30 {
    31     char *buf = "hello,user!";
    32     copy_to_user(usr,buf,20);
    33 
    34 
    35 }
    36 int testrelease(struct inode *inode, struct file *file)
    37 {
    38     printk("close
    ");
    39     return 0;
    40 
    41 }
    42 
    43 struct file_operations fops=
    44 {
    45     .owner=THIS_MODULE,
    46     .open = testopen,
    47     .write = testwrite,
    48     .read = testread,
    49     .release = testrelease,
    50 };
    51 static int __init test_init(void)
    52 {
    53     dev_no = MKDEV(TestMajor,TestMinor);
    54     if(dev_no>0)
    55     {
    56         ret = register_chrdev_region(dev_no,1,"chrdev_test");    
    57     }
    58     else
    59     {
    60         alloc_chrdev_region(&dev_no,0,1,"chrdev_test");
    61     }
    62     if(ret<0)
    63     {
    64         return ret;
    65     }
    66     cdev_init(&chrdev,&fops);
    67     chrdev.owner=THIS_MODULE;
    68     cdev_add(&chrdev,dev_no,1);
    69     return 0;
    70 }
    71 
    72 static int __exit test_exit(void)
    73 {
    74     unregister_chrdev_region(dev_no,1);
    75     cdev_del(&chrdev);
    76     
    77     return 0;
    78 }
    79 
    80 module_init(test_init);
    81 module_exit(test_exit);
    82 
    83 
    84 MODULE_AUTHOR("FENG");
    85 MODULE_DESCRIPTION("the first module of char drivers");
    86 MODULE_LICENSE("GPL");
    87 MODULE_VERSION("V1.0");

    7.字符设备的调试
    7。1安装驱动
    [@GEC2103 /]# insmod chrdev.ko
    7.2查看分配的设备号
    [@GEC2103 /]# cat /proc/devices

    250 chrdev_test

    7.3手动创建设备文件
    mknod /dev/chrdev c 250 0

    [@GEC2103 /]# ls /dev/chrdev -l
    crw-r--r-- 1 0 0 250, 0 Jan 1 00:45 /dev/chrdev
    7.4应用程序来/dev/chrdev设备文件,等效操作设备文件对应那个设备

    int main()
    {
    fd=open("/dev/chrdev",O_RDWR);
    write(fd,usrbuf,sizeof(usrbuf))
    close(fd);

    }

    --------------------------------------------------------------------------------

    test.c

     1 #include<stdio.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <errno.h>
     5 #include <unistd.h>
     6 #include <string.h>
     7 
     8 int main()
     9 {
    10 char buf[20];
    11 char buf1[20];
    12 int fd = open("/dev/chrdev_test",O_RDWR);
    13 if(fd<0)
    14 perror("open() error!
    ");
    15 bzero(buf1,20);
    16 strcpy(buf1,"hello kernel!");
    17 write(fd,buf1,strlen(buf1)+1);
    18 sleep(1);
    19 read(fd,buf,20);
    20 printf("%s
    ",buf);
    21 }

    9。字符设备设计老的方法
    优点:比新简洁,比较好理解.


    1.使用函数
    1)注册字符设备
    static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
    参数:
    major--->主设备号,如果major>0,静态注册
    =0 动态分配

    name-->设备名称 /proc/devices


    fops-->文件操作集


    返回值:
    major >0 成功:0
    失败:负数
    major =0 成功:分配后主设备号
    失败:负数


    2)注销字符设备
    static inline void unregister_chrdev(unsigned int major, const char *name)
    major->主设备号
    name-->设备名称

    2.使用旧方法设计字符设备驱动的流程
    2.1unsigned int Testmajor =0;//251

    2.2定义文件操作集
    int testopen(struct inode *inode, struct file *file)
    {



    }
    int testclose (struct inode *inode, struct file *file);
    {


    }
    ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
    {


    }

    struct file_operations fops= --->结构体初始化 .---???????????????
    { .owner=THIS_MODULE,
    .open=testopen,
    .write=testwrite,
    .release=testclose,
    }

    2.3注册一个字符设备

    ret=register_chrdev(Testmajor,"chrdev_test",&fops);//注册字符设备到内核
    if(ret<0)
    {
    printk("register error")
    }

    if(Testmajor==0)
    {
    Testmajor =ret;

    }

     1 #include<linux/module.h>
     2 #include<linux/kernel.h>
     3 #include<linux/cdev.h>
     4 #include<linux/fs.h>
     5 #include<linux/kdev_t.h>
     6 #include<linux/types.h>
     7 #include<linux/uaccess.h>
     8 #include<linux/string.h>
     9 
    10 dev_t dev_no = 0;
    11 int ret;
    12 
    13 int testopen(struct inode *inode,struct file *file)
    14 {
    15     printk("cdev init
    ");
    16     return 0;
    17     
    18 }
    19 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
    20 {
    21     char buf[12];
    22     
    23     copy_from_user(buf,usr,len);
    24     printk("%s
    ",buf);
    25 
    26 }
    27 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
    28 {
    29     char buf[20] = "hello,user!";
    30     copy_to_user(usr,buf,len);
    31 
    32 
    33 }
    34 int testrelease(struct inode *inode, struct file *file)
    35 {
    36     printk("close
    ");
    37     return 0;
    38 
    39 }
    40 
    41 struct file_operations fops=
    42 {
    43     .owner=THIS_MODULE,
    44     .open = testopen,
    45     .write = testwrite,
    46     .read = testread,
    47     .release = testrelease,
    48 };
    49 static int __init test_init(void)
    50 {
    51     ret = register_chrdev(dev_no,"chrdev_test",&fops);
    52     if(ret < 0)
    53     {
    54         printk("register error!
    ");
    55     }
    56     if(dev_no==0)
    57     {
    58         dev_no = ret;
    59     }
    60 
    61     return 0;
    62 }
    63 
    64 static int __exit test_exit(void)
    65 {
    66     unregister_chrdev(dev_no,"chrdev_test");
    67     
    68     return 0;
    69 }
    70 
    71 module_init(test_init);
    72 module_exit(test_exit);
    73 
    74 
    75 MODULE_AUTHOR("FENG");
    76 MODULE_DESCRIPTION("the first module of char drivers");
    77 MODULE_LICENSE("GPL");
    78 MODULE_VERSION("V1.0");
  • 相关阅读:
    【翻译】在Sencha应用程序中使用插件和混入
    Codeforces Round #306 (Div. 2) A
    自己定义控件-仿iphone之ToggleButton&amp;VoiceSeekBar
    .m文件导入C++头文件带来的错误
    Permutations
    ceph命令拷屏
    Azure 3 月新公布
    Azure 本月最新活动,速度Mark!
    直接刷脸?一元就能搞定会议签到!
    Azure SQL的DTU和eDTU到底是个什么鬼
  • 原文地址:https://www.cnblogs.com/defen/p/5462247.html
Copyright © 2011-2022 走看看