imx6ull字符设备驱动(1)

字符设备驱动(1)

基于imx6ull以及其使用手册

linux驱动

外设主要分为

  • 字符设备
    感觉就是简单的i/o?
    • 以字节驱动为单位,顺序访问,字符设备面向流设备,
    • 蜂鸣器,led,鼠标,键盘
  • 块设备
    • 访问块设备时候,是以扇区过着块为基础,属于无序访问
  • 网络设备
    • 网络设备就是网络适配器等用来上网的设备
    • 网卡分为有线和无线两种

字符设备驱动

  • 字符设备驱动在linux系统中有其规定的框架

常规字符设备驱动

为设备定义一个相关结构体

初始化函数 xxx_init

  • 向系统申请设备号
  • 申请设备内存
  • 调用cdev_init()初始化
  • 调用cdev_add()向系统注册设备

卸载函数 xxx_exit的定义

  • 释放设备号
  • 调用cdev_add()

定义 file_operations

  • 实现write()
  • 实现read()
  • 根据需要实现其他函数
//机构体模块
struct A{
    struct cdev cdev;
    
};

struct A *dev;//创建一个设备结构体指针
dev_t devno;//设备号结构体

//读设备
ssize_t xxx_read(struct file *p,char __user *buf,size_t count,loff_t* f_pos)
{
    copy_to_user(buf, , , );
}

//写设备
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos)
{
    copy_from_user(..., buf, ...);
}

//文件操作函数file_operations
struct file_operations xxx_fops = {
    .owner = THIS_MODULE,
    .read  = xxx_read,
    .write = xxx_write,
};


//设备驱动模块加载函数
static int __init xxx_init(void)
{
...
devno = MKDEV(xxx_major, 0);
//(1)申请设备号
if(xxx_major)
{
register_chrdev_region(devno, 1, "xxx_dev");
}
else
{
alloc_chrdev_region(&devno, 0, 1, "xxx_dev");
}
//(2)为设备结构体申请内存(推荐使用devm_kzalloc)
    
    
dev = kzalloc(sizeof(struct xxx_dev_t), GFP_KERNEL);
//(3)初始化cdev
cdev_init(&dev.cdev, &xxx_fops);
dev.cdev.owner = THIS_MODULE;
//(4)向系统注册设备
cdev_add(dev.cdev, dev_no, 1);
}
module_init(xxx_init);
//设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
//释放设备号
unregister_chrdev_region(dev_no, 1);
//注销设备
cdev_del(&dev.cdev);
...
}
module_exit(xxx_exit);
MODULE_LICENSE("GPL v2");
代码解释
  • 首先定义了机构体,直接定义了结构体cdev

    struct cdev {
    struct kobject kobj; //内嵌的kobject 对象
    struct module *owner; //所属模块
    const struct file_operations *ops; //文件操作结构体
    struct list_head list;
    dev_t dev; //设备号
    unsigned int count;
    };
    
  • 然后定义了设备号机构体,并在第37行申请了设备号、

  • 实现了文件操作函数总的部分功能。

    file_operations中的成员函数似乎字符设备驱动程序的主要内容

  • 设备驱动模块加载函数

  • 为此函数注册为设备驱动的入口函数

  • 初始化cdev,向系统注册设备

简化版字符设备驱动框架

可以使用这个两个函数对上面的函数进行简化

static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
//注册字符设备,返回申请的主设备号
static inline void unregister_chrdev(unsigned int major, const char *name)
//注销字符设备   
//通过这两个函数就不用一步一步的去注册设备了

两个文件就实现注册设备了,具体文件于fs/char_dev.c

设备号

· 各种设备都以文件的形式放在/dev 目录下,称为设备文件,应用程序可以打开,关闭,读写这些设备文件,完成对应的设备的操作。

组成
  • 一个字符设备或者块设备都有一个主次设备号。

    • 主设备号都用来表示一个特定的驱动程序。

    • 次设备号永安里表示该驱动程序的各个设备

  • dev_t的数据类型表示设备号

    typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t;

  • dev_t是一个32位变量

    linux设备号范围是0 - 4095

  • 分配

    • 静态分配设备号

      • 驱动程序开发者通过静态指定一个设备号
      • 内核设备号再内核源码documentation/ devices.txt里面
      • cat /proc/devices查看所有设备号
    • 动态分配设备号

      用于解决设备号存在冲突问题,因此建议使用动态分配设备号

      • 注册字符设备之前申请一个设备号
    • 不用了卸载即可

      驱动模块的安装与卸载

  • 写好驱动之后,运行驱动

    • 驱动编译到linux内核中,linux内核运行时候会自动运行驱动程序

    • 驱动程序编译后缀为.ko的模块文件

      • 安装和卸载.ko的命令

        insmod xxx.ko安装驱动

        rmmod xxx.ko卸载驱动

        实际上是调用module_init函数,这个函数会注册一个加载函数模块,使用上述命令的时候就调用了函数

      • 安装和卸载驱动

        modprobe xxx.ko //安装模块

        modprobe -r xxx.ko//卸载模块

        这个命令在加载模块时候,会同时加载该模块所依赖的其他模块

实验课题

(2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值