zoukankan      html  css  js  c++  java
  • 【驱动】linux设备驱动·字符设备驱动开发

    Preface

       前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段!

       《linux设备驱动入门篇》:http://infohacker.blog.51cto.com/6751239/1218461

       《linux设备驱动扫盲篇》:http://infohacker.blog.51cto.com/6751239/1218747

       《fedora下的字符设备驱动开发http://infohacker.blog.51cto.com/6751239/1155153


    开发一个基本的字符设备驱动

       在Linux内核驱动中,字符设备是最基本的设备驱动。

    字符设备包括了设备最基本的操作,如打开设备、关闭设备、I/O控制等。

    功能:❶建立一个名为GlobalChar的虚拟设备,设备内部只有一个全局变量供用户操作。设备提供了❷读函数读取全局变量的值并且返回给用户,❸写函数把用户设定的值写入全局变量。

    代码如下:

    //GlobalCharDev.c
    #include <linux/fs.h>                
    #include <linux/module.h>
    #include <asm/uaccess.h>             
    #include <linux/init.h>
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("mystery");
    #define DEV_NAME "GlobalChar"
    static ssize_t GlobalRead(struct file *, char *, size_t, loff_t *);
    static ssize_t GlobalWrite(struct file *, const char *, size_t, loff_t *);
                                                                                                                                                                                                                                                                                               
    static int char_major = 0;                   
    static int GlobalData = 0;  // "GlobalData" 设备的全局变量
    //初始化字符设备驱动的file_operations结构体
    static const struct file_operations globalchar_fops =
    {
        .read = GlobalRead,
        .write = GlobalWrite
    }; //注意分号啊!!!
    //模块初始化函数
    static int __init GlobalChar_init(void)
    {
        int ret;
        ret = register_chrdev(char_major, DEV_NAME, &globalchar_fops);  //注册设备驱动
        if (ret < 0)
        {
            printk(KERN_ALERT "GlobalChar Reg Fail ! 
    ");
        }
        else
        {
            printk(KERN_ALERT "GlobalChar Reg Success ! 
    ");
            char_major = ret;
            printk(KERN_ALTER "Major = %d 
    ",char_major);
        }
        return ret;
    }
    //模块卸载函数
    static void __exit GlobalChar_exit(void)
    {
        unregister_chrdev(char_major, DEV_NAME);    //注销设备驱动
        return;
    }
    //设备驱动读函数
    static ssize_t GlobalRead(struct file *filp, char *buf, size_t len, loff_t *off)
    {
        if(copy_to_user(buf, &GlobalData, sizeof(int))) //从内核空间复制GlobalData到用户空间
        {
            return -EFAULT;
        }
        return sizeof(int);
    }
    //设备驱动写函数
    static ssize_t GlobalWrite(struct file *filp, const char *buf, size_t len, loff_t *off)
    {
        if(copy_from_user(&GlobalData, buf, sizeof(int)))   //从用户空间复制GlobalData到内核空间
        {
            return -EFAULT;
        }
        return sizeof(int);
    }
    module_init(GlobalChar_init);
    module_exit(GlobalChar_exit);

    在内核中操作数据要区分数据的来源,对于用户空间的数据要使用copy_from_user()函数复制,使用copy_to_user()函数回写,不能直接操作用户空间的数据,否则会产生内存访问错误。

    ②编写Makefile

    obj-m := GlobalCharDev.o
    KDIR := /lib/modules/$(shell uname -r)/build
    SRCPWD := $(shell pwd)
    all:
        make -C $(KDIR) M=$(SRCPWD) modules

    ③编译并加载内核模块

    ④查看内核分配的主设备号

    ⑤使用mknod命令建立一个设备文件

    mknod命令使用-m参数指定GlobalChar设备可以被所有用户访问。

    249即上面查询的主设备号。

       到这里,我们就已经正确地添加了一个字符设备到内核,下面需要测试一下驱动程序能否正常工作。


    测试字符设备驱动

       为了测试编写的字符设备是否能正常工作,我们编写一个应用程序测试一下能否正常读写字符设备。

    测试代码:

    //GlobalCharTest.c
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <fcntl.h>
    #define DEV_NAME "/dev/GlobalChar"
    int main()
    {
            int fd,num;
                                                                                                               
            fd = open(DEV_NAME, O_RDWR, S_IRUSR | S_IWUSR); //打开设备文件
            if (fd < 0)
            {
                printf("Open Deivece Fail ! 
    ");
                return -1;
            }
            read(fd, &num, sizeof(int));
            printf("The GlobalChar is %d 
    ", num); //获取当前设备数值
            printf("Please input a number written to GlobalChar: ");
            scanf("%d", &num);
            write(fd, &num, sizeof(int));   //写入新的数值
            read(fd, &num, sizeof(int));
            printf("The GlobalChar is %d 
    ", num); //重新读取设备数值
            close(fd);
            return 0;
    }

    程序首先使用open函数打开设备文件,然后使用read()函数读取字符设备的值,open()系统调用最终会被解释为字符设备注册的read调用。

       测试结果:

       从程序输出结果来看,最初从设备得到的数值是0,输入520后写入到字符设备,重新读出的数值也是520,与设置相同,表示设备驱动程序功能正确。

     


    总结

       linux字符设备驱动也不过如此嘛,嘿嘿,虽然只实现了read和write两个函数,不过其它函数也大同小异。

       重点:实践再实践!!!

    本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1219217

  • 相关阅读:
    机器学习理解
    如何让Vim成为我们的神器
    新一代 Linux 文件系统 btrfs 简介 【转载】
    第一次写Linux下Makefile的笔记
    UEFI boot sequence
    #pragma pack
    Linux makefile sample
    解决ScrollView中嵌套ListView滚动效果冲突问题
    Android SqLite升级
    Android 播放gif图片
  • 原文地址:https://www.cnblogs.com/lcw/p/3159384.html
Copyright © 2011-2022 走看看