驱动开发-模块

驱动开发-


atexit – 程序终止时释放空间

void function(void)
{
    printf("1\n");
}
void function1(void)
{
    printf("2\n");
}
int main(int argc,char \*argv[])
{
    printf("duang\n");
    atexit(function);
    printf("fang\n");
    atexit(function1);
    return 0;
}

输出结果
duang
fang
2
1

exit比return 0多一个刷新缓冲区功能

exit–>释放空间

_exit–>不释放空间


应用程序和模块区别

  • 入口函数
应用程序模块函数
mainmodule_init

- 运行空间

应用程序模块
用户空间内核空间

- 调用函数

应用程序模块函数
c库或者系统调用内核提供的函数

- 资源释放

应用程序模块函数
自动释放或者手动释放手动释放

什么程序可以编译生成模块

  • 在linux下,有三大设备驱动
    1. 字符设备 –> led, beep,key,adc,mpu6050,usb,lcd
      • 一般按字节的形式访问,顺序访问 char
    2. 块设备 –> emmc,nand flash,nor flash,sd
      • 固定大小随机访问,linux系统下block 512byte
    3. 网络设备驱动 –> 网卡
      • 有自己的协议

如何编写模块

如果想在内核中编译模块,必须在make menuconfig 中配置驱动程序才可以编译生成模块:make modules .o –> .ko

1. 模块分成内部模块和外部模块
2. 编译模块的条件
    - 必须是已经 编译过的内核 --> make
    - 必须是已经 配置过的内核 --> make menuconfig
3. 在内核的源码目录外编译
4. 采用模块化编程
  1. 模块的入口函数 –> 申请资源

    module_init() --> 模块的入口函数
    // 函数的返回值
    typedef init (\*initcall_t)(void);
  2. 模块的出口函数 –> 释放资源

    module_exit() --> 模块的出口函数
    // 函数的返回值
    typedef void (\*exitcall_t)(void);

C程序编程

    //编写入口函数和出口函数
    int hello_init(void)
    {
        //资源的申请
    }
    void hello_exit(void)
    {
        //资源的释放
    }

    module_init();
    module_exit();

    ```

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

### 查看内核和安装模块

- 查看内核  <br/>
    1. 添加文件索引
    `make tags`  <br/>
    2. 搜索函数名
    `vi -t module_init`  <br/>

- 模块的命令
    1. 安装模块  <br/>
        `insmod 文件名`
    2. 卸载模块  <br/>
        `rmmod 模块名`
    2. 查看模块  <br/>
        `lsmod `
    3. 查看内核后台的打印信息 <br/>
        `dmesg`
    4. 清空内核后台的打印信息  <br/>
        `dmesg -c`


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

### 字符设备驱动

> 应用程序是通过设备号找到驱动程序,在linux系统设备号是唯一的。

- 内核定义设备号

通过设备号可以找到对应的驱动程序,驱动程序可以绑定设备号

- 设备号 是32bit的无符号整数
    - 其中高12bit表示的是主设备号,低20位表示的是次设备号
    - 主设备号:表示哪一类设备
    - 次设备号:表示这一类设备的第几个设备
- 申请设备号 -- 静态申请和动态申请
    - 静态申请

        int marjor = 250;
        int minor = 0;

        int decno = major << 20 | minor

        内核申请设备号的函数
        dev_t devno = MAKDEV(marjor,minor);
        MAKDEV

    - 注册字符设备号
    ```c
        register_chrdev_region();

        int register_chrdev_region(dev_t from, unsigned count,const char \*name);
        参数
        dev_t from 设备号
        unsigned count 申请设备号的个数
        const char * name 设备或者驱动的名字

        返回值
        成功返回0,失败返回错误码

        int ret;
        ret  register_chrdev_region("devno",1,"hello");
        if(0 != ret)
        {
            return -EBUSY;
        }
    ```
    - 释放设备号
    ```c
        unregister_chrdev_region();

        void unregister_chrdev_region(dev_t from,unsigned count);
        参数
        dev_t from 设备号
        unsigned count 设备号的个数

        unregister_chrdev_region(devno,1);
    ```

    - 查看系统中注册的设备号  <br/>
    `cat /proc/devices`

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

### 初始化cdev结构体

- 查看系统中已经注册的设备号  <br/>
`cat /proc/devices`

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

1. 初始化cdev结构体

    - **cdev_init()函数**
```c
555  * @fops: the file_operations for this device
556  *
557  * Initializes @cdev, remembering @fops, making it ready to add to the
558  * system with cdev_add();
559  *
560 void cdev_init(struct cdev \*cdev, const struct file_operations \*fops);
参数:
      struct  cdev *cdev:cdev结构体
      struct  file_operations  *  
      ops:操作函数的接口




<div class="se-preview-section-delimiter"></div>
- **struct cdev \*cdev**
    struct cdev{
    struct kobject kobj;   // 内嵌的kobject对象
    struct module \*owner;  // 所属模块
    const struct file_operations \*ops; // 文件操作结构体
    struct list_head list;        //linux内核所维护的链表指针
    dev_t dev;               //设备号
    unsigned int count;
};




<div class="se-preview-section-delimiter"></div>

- **struct file_operations**

struct file_operations {
struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES
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);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
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 *); //关闭
int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据
int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据
int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};

Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。


- **cdev_add()函数**

c
464 **
465 * cdev_add(); - add a char device to the system
466 * @p: the cdev structure for the device
467 * @dev: the first device number for which this device is responsible
468 * @count: the number of consecutive minor numbers corresponding to this
469 * device
470 *
471 * cdev_add(); adds the device represented by @p to the system, making it
472 * live immediately. A negative error code is returned on failure.
473 *
474 int cdev_add(struct cdev \*p, dev_t dev, unsigned count)
参数: struct cdev * p:字符设备
dev_t devno;设备号
unsigned count :次设备号的个数


- **删除cdev结构体**
496 **
497  * cdev_del(); - remove a cdev from the system
498  * @p: the cdev structure to be removed
499  *                                                                                                                               
500  * cdev_del(); removes @p from the system, possibly freeing the structure
501  * itself.
502  *
503 void cdev_del(struct cdev \*p)
504 {
505     cdev_unmap(p->dev, p->count);
506     kobject_put(&p->kobj);
507 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值