linux设备驱动day3-驱动介绍,注册和加载实现

linux设备的分类:
字符设备:
串口、终端、触摸屏、键盘
查看方式:ls -l /dev 以c开头的文件都是字符设备文件。
块设备:
Flash、ramdisk(内存磁盘)、harddisk(硬盘)
查看方式:ls -l /dev 以b开头的文件
网络设备:
ifconfig -a

Linux抽象了对硬件的处理,所有的硬件设备都可以看作普通文件一样看待,可以使用和操作文件相同的,标准的系统调用接口来完成对设备的打开,关闭,读写和IO控制操作

字符设备和块设备是通过文件节点访问的,在Linux文件系统中,可以找到或者mknod创建设备对应的文件名,称为设备文件。
块设备和字符设备有两个数字分别是主设备号和次设备号,普通文件只有文件大小。
同一个驱动程序可以管理多个设备,依靠次设备号来区别。
主设备号标识该设备的种类,也标识了该设备所使用的驱动程序。
次设备号:标识使用同一设备驱动程序的不同硬件设备。次设备号只能在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。

设备驱动程序有三个接口:
(加载接口)系统引导接口:系统各设备初始化, 激活,运行驱动程序
(控制硬件接口)交互接口:驱动程序与具体设备间进行交互
(系统调用接口)实现接口:操作系统内核 通过数据结构file_operations实现具体的函数功能

软中断切换用户态进入内核态,地址空间不一样,不能直接对地址操作

Linux内核加载驱动程序:
1、系统启动时,通过代码自身加载模块,称为静态编译入内核,驱动程序开发完毕口一般这么使用
2、Linux系统启动后,通过insmod命令加载模块,称为动态加载。

驱动标识:文件节点,设备号

接口的实现:
编写驱动要遵守一定的协议:
MODULE_AUTHOR(” “);
MODULE_LICENSE(“GPL”); //这是必须的
MODULE_DESCRIPTION(” “);
MODULE_ALIAS(” “);
加载接口:int __init hello_init(void) (加载)、void __exit hello_exit(void)(卸载)(表示运行一次)
注册接口:module_init(hello_init);//模块加载函数
module_exit(hello_exit);//模块卸载函数
在内核里打印用printk,不能用printf函数
编译驱动程序要用Makefile来编译
生成的xxxx.ko文件放到板子上进行加载
加载命令:insmod xxxxx.ko 把内核模块加入到系统中, 会调用之前定义的init函数
查看动态加载的内核模块:lsmod 可以看到刚刚加载的内核模块
驱动程序打印的信息会打印到系统日志中 /var/log/syslog
卸载模块:rmmod xxxx, 会调用定义的exit函数
查看某个模块的信息:modinfo xxxxx.ko

申请设备号(在init时申请):register_chrdev(arg1, arg2, arg3);//注册字符设备,返回设备号
arg1 = 0 表示由系统分配设备号,arg1 > 0 表示自己规定一个,但不能冲突
arg2 是一个字符串,表示设备的名字
arg3 是一个结构体指针,(struct file_operations)结构体成员是一系列函数指针, 要给成员函数赋值,定义出相关函数(系统调用相关接口)

定义出一系列的函数实际就是在实现系统调用接口(open, release, read, write)

释放设备号(在exit时释放):unregister_chrdev(arg1, arg2);
arg1是要释放的主设备号
arg2 是出册时的设备名字
结构体的定义包含在头文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

MODULE_AUTHOR("CYW");
MODULE_LICENSE("GPL"); //必须要写的遵守协议
MODULE_DESCRIPTION("A simple test for kernel module");
MODULE_ALIAS("a simple module");

struct file_operations hello_fops = { //定义一个结构体
    .open = hello_open,
    .release = hello_release,
    .read = hello_read,
    .write = hello_write,
}; //部分成员初始化

#define DEVICE_NAME   "Hello_Driver" //注册成功会出现在/proc/devices中
int major;

static int __init hello_init(void) //加载驱动
{
    printk(DEVICE_NAME":Hello world in kernel module\n"); //不能用printf

    major = register_chrdev(0, DEVICE_NAME, &hello_fops);//向内核申请主设备号,需要结构体地址参数

    if(major < 0){ //申请失败
        printk(DEVICE_NAME":register %s fail\n", DEVICE_NAME);
        return major;
    }
    printk(DEVICE_NAME":got major number: %d\n", major);
    return 0;
}

static void __exit hello_exit(void) //卸载驱动
{
    printk(DEVICE_NAME":Hello world in exit\n"); //不能用printf
    unregister_chrdev(major, DEVICE_NAME); //释放资源,根据主设备号和名字
}

module_init(hello_init);//模块加载所用函数
module_exit(hello_exit);//模块卸载所用函数


int hello_open(struct inode *pinode, struct file *pfile) //打开驱动
{
    printk(DEVICE_NAME":hello_open\n");
    wr_pos = 0;
    rd_pos = 0;
    pbuff =(unsigned char *)kmalloc(BUFF_SIZE, 0);
    if(pbuff == NULL){
        printk(DEVICE_NAME":kmalloc fail\n");
        return 1;
    }
    return 0;
}
int hello_release(struct inode *pinode, struct file *pfile) //释放驱动
{
    printk(DEVICE_NAME":hello_release\n");
    kfree(pbuff);
    return 0;
}
/*
 *__user *buff, 用户空间内存地址
 *
 */

ssize_t hello_read(struct file *pfile, char __user *buff, size_t bufsize, loff_t *poffset) //读驱动
{
    int i = 0;
    for(i = 0; i < bufsize && wr_pos - rd_pos > 0; i++){ //循环拷贝一个字节到用户空间
        put_user(pbuff[rd_pos % BUFF_SIZE] , buff+i);
        rd_pos++;
    }
    if(i >= 0)
        return i;
#if 0
    int len = wr_pos - rd_pos;
    printk(DEVICE_NAME":hello_read\n");
    if( len > 0){
        int err = copy_to_user(buff, pbuff+(rd_pos % BUFF_SIZE), len > bufsize ? bufsize : len); //把内核空间的数据读到用户空间
        rd_pos += len > bufsize ? bufsize : len;
        if(!err)
            return len > bufsize ? bufsize : len;
    }
#endif
    return -1;
}

ssize_t hello_write(struct file *pfile, const char __user *buff, size_t bufsize, loff_t *poffset) //写驱动
{
    int i = 0;
    for(i = 0; i < bufsize && free_size() > 0; i++){ //拷贝一个字节到内核空间
        get_user(pbuff[wr_pos % BUFF_SIZE] , buff+i);
        wr_pos++;
    }
    if(i >= bufsize)
        return bufsize;

#if 0
    int free_len = BUFF_SIZE - (wr_pos - rd_pos);
    printk(DEVICE_NAME":hello_write\n");
    if( free_len > bufsize){
        int err = copy_from_user(pbuff+wr_pos % BUFF_SIZE, buff, bufsize);//把用户空间的数据写道内核空间
        wr_pos += bufsize;
        if(!err)
            return bufsize;
    }
#endif
    return -1;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值