嵌入式驱动笔记

Linux驱动开发面试总结

1.Linux设备驱动

(1)字符设备

Linux字符设备是一种按字节访问的设备,字符驱动通常实现open、close、read、write系统调用 例:串口 LED 按键等

应用程序通过设备文件(/dev/xxx)可以使用相应的字符驱动来控制字符设备

创建字符设备文件:
(1)使用命令 mknod /dev/文件名 c 主设备号 次设备号
(1 安装驱动模块:insmod MemDev.ko //dmesg
(2 查看主设备号:cat /proc/devices(查找memdev对应的主设备号)
(3 创建设备文件:mknod /dev/memdev0 c 主设备号 0

(2) 使用函数 mknod(const char *pathname, mode_t mode, dev_t dev)

文件系统与字符设备驱动联系
(1)在Linux系统中,每一个打开的文件,在内核中都会关联一个struct file结构,它是由内核在打开文件时创建,在文件关闭后释放。
  struct file结构中的重要成员
  * struct file_operations* f_op;  //文件操作函数集
  * loff_t f_pos;         //文件读写指针
(2)每一个存在于文件系统中的文件都会关联一个inode结构,该结构主要用来记录文件物理上的信息。因此,它和代表打开文件的file结构是不同的,一个文件没有被打开时不会关联file结构,但是会关联一个inode结构(存于磁盘,操作文件时在内存中建立相应的映射结构)
(3)系统实质上是把字符设备的注册表看成了文件。chrdevs[]

在任何一种驱动模型中,设备都会用内核中的一种结构来描述。字符设备在内核中使用struct cdev来描述
Linux内核中使用dev_t类型来定义设备号,dev_t其实质为32位unsigned int类型,其中高12位为主设备号,低20位为次设备号。
(1)MKDEV(主设备号,次设备号)
//主设备号:主设备号标识设备对应的驱动程序。虽然现代的linux内核允许多个驱动程序共享主设备号,但我们看待的大多数设备仍然按照“一个主设备对应一个驱动程序”的原则组织。
次设备号:次设备号由内核使用,用于正确确定设备文件所指的设备。依赖于驱动程序的编写方式,我们可以通过次设备号获得一个指向内核设备的
(2)MAJOR(dev_t dev)
(3)MINOR(dev_t dev)
注:字符设备文件与字符设备驱动是通过主设备号建立对应关系;驱动程序用次设备号来区分同类型的设备

设备号的申请与注销
(1)静态申请:开发者自己选择一个数字作为主设备号,通过函数 register_chardev_region 向内核申请
(2)动态分配:使用 alloc_chrdev_region 由内核分配一个可用的主设备号(推荐使用)
(3)不论使用何种方法分配设备号,都应该在驱动退出时,使用 unregister_chrdev_region 函数释放这些设备

设计Linux字符设备驱动程序的主要工作:
(1)根据外部设备的特点,实现file_operations结构所需要的函数
(2)调用函数cdev_alloc()函数向系统动态申请一个cdev结构实例
(3)调用函数cdev_init()初始化cdev实例,并建立cdev实例与file_operations实例之间的连接
//void cdev_init(struct cdev *cdev, struct file_operations *fops)
该注册函数可以将cdev结构嵌入到自己的设备特定的结构中。cdev是一个指向结构体cdev的指针,而fops是指向一个类似于file_operations结构(可以是file_operations结构,但不限于该结构)的指针.
(4)调用函数alloc_chrdev_region()向系统申请一个设备号
//int register_chrdev(unsigned int major, const char *namem , struct file)operations *fopen);
该注册函数是早期的注册函数,major是设备的主设备号,name是驱动程序的名称,而fops是默认的file_operations结构(这是只限于file_operations结构)。对于register_chrdev的调用将为给定的主设备号注册0-255作为次设备号,并为每个设备建立一个对应的默认cdev结构。
(5)调用函数cdev_add()向系统添加一个设备
(6)调用函数cdev_del()从系统删除一个cdev结构实例

字符设备简单实例

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

/* We suppose this is the two device's registers */
int dev1_registers[5];
int dev2_registers[5];

struct cdev cdev; 
dev_t devno;

/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);
    
    if (num == 0)
        filp->private_data = dev1_registers;
    else if(num == 1)
        filp->private_data = dev2_registers;
    else
        return -ENODEV;  //无效的次设备号
    
    return 0; 
}

/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
    return 0;
}

/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/

    /*判断读位置是否有效*/
    if (p >= 5 * sizeof(int))
        return 0;
    if (count > 5 * sizeof(int) - p)
        count = 5 * sizeof(int) - p;

  /*读数据到用户空间*/
    if (copy_to_user(buf, register_addr + p, count))
    {
        ret = -EFAULT;
    }
    else
    {
        *ppos += count;
        ret = count;
    }
    
    return ret;
}

/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
    unsigned long p =  *ppos;
    unsigned int count = size;
    int ret = 0;
    int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
  
    /*分析和获取有效的写长度*/
    if (p >= 5*sizeof(int))
        return 0;
    
    if (count > 5 * sizeof(int
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值