驱动分类
1 常规分类
1.1 字符设备:以字节为最小访问单位的设备,通常支持open,close,read,write系统调用。如串口、LED、按键
1.2 块设备:以块为最小访问单位的设备(块一般为512字节或512字节的倍数),linux中允许块设备传送任意字节数。如硬盘、flash、SD卡。
1.3 网络接口:负责发送和接收数据报文,可以是硬件设备,如网卡,也可以是纯软件设备,如回环接口(lo)
2 总线分类法
1.1 USB设备
1.2 PCI设备
1.3 平台总线设备
学习方法
1 构建驱动模型
2 添加硬件操作
3 驱动测试
硬件访问
1 地址映射
1.1 静态映射
void *ioremap(physaddr,size)
physaddr:待映射的物理地址
size:映射的区域长度
返回值:映射后的虚拟地址
1.2 动态映射
用户事先指定映射关系,在内核启动时自动将物理地址映射为虚拟地址。
struct map_desc
{
unsigned long virtual;/映射后的虚拟地址
unsigned long pfn;/物理地址所在的页帧号
unsigned long length;/映射长度
unsigned int type;/映射的设备类型
}
2 寄存器读写
unsigned ioread8(void *addr)
unsigned ioread16(void *addr)
unsigned readb(address)
unsigned readw(address)
unsigned readl(address)
void iowrite8(u8 value,void *addr)
void iowrite16(u16 value,void *addr)
void iowrite32(u32 value,void *addr)
void writeb(unsigned value, address)
void writew(unsigned value, address)
void writel(unsigned value, address)
驱动的运用
1 编译安装驱动
1.1 创建Makefile
obj-m := memdev.o
KDIR := /home/…/linux-tq2440
all:
make–C $( KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
1.2 拷贝驱动(内核模块)到/…/rootfs
1.3 安装驱动程序
insmod memdev.ko
1.4 查看 lsmod
2 字符设备文件
2.1 查看主设备号
cat /proc/devices
主设备号 设备名
2.2 创建设备文件
mknod /dev/文件名 c 设备号 次设备号(0~255)
3 应用程序
3.1 打开字符设备文件
fd = open(“/dev/memdev0”,O_RDWR);
3.2 操作字符设备文件
write(fd,&buf,sizeof(int));
read(fd,&buf,sizeof(int));
3.3 关闭字符设备文件
close(fd);
3.4 编译(静态编译)
arm-linux-gcc –static 文件名.c –O文件名
字符设备驱动模型
1 设备描述结构cdev
1.1 结构定义
struct cdev
{
struct kobject kobl;
struct module *owner;
const struct file operations *ops ;//设备操作集
struct lis thead list;
dev_t dev;//设备号。
unsigned int count;设备数
}
1.2 设备号类型dev_t
1.2.1 dev_t介绍:实质为32位的unsigned int,高十二位为主设备号,低二十位为次设备号。
1.2.2 操作设备号:
dev_t dev=MKDEV(主设备号,次设备号)
主设备号=MAJOR(dev_t dev)
次设备号=MINOR(dev_t dev)
1.2.3 申请设备号:
静态申请:intregister_chrdev_region(dev_t first, unsigned int count, char *name);
first: 待分配的起始设备编号,通常为0;
count: 连续设备编号的总数
name:设备名(cat /proc/devices)
动态申请:intalloc_chrdev_region(dev_t *dev,unsigned int -firstminor,unsigned int -count,char*name)
dev:得到的设备号保存位置
-firstminor: 待分配的起始设备编号,通常为0;
-count: 连续设备编号的总数
name: 设备名(cat /proc/devices)
1.2.4 注销设备号:unregister_chardev_region
1.3 操作集struct file operations
1.3.1 structfile operations介绍:函数指针的集合,定义能在设备上进行的操作。对于不支持的操作设置为NULL
1.3.2 例:struct file operations dev_fops =
{
.llseek = NULL;
.read= dev_read,
.write= dev_write,
.ioctl= dev_inctl,
.open= dev_open,
.release= dev_release,
};
2 驱动初始化
2.1 分配设备描述结构
静态分配:structcdev mdev;
动态分配:structcdev *pdev = cdev_alloc();
2.2 初始化设备描述结构
cdev_init(struct cdev*cdev,const struct file_operations *fops)
*cdev:待初始化的cdev结构/*fops:设备对应的操作函数集
2.3 注册设备描述结构
cdev_add(struct cdev *p,dev_t dev,unsignedcount)
p:待添加到内核的字符设备结构
dev:设备号
count:该类设备的设备个数
2.4 硬件初始化
3 设备操作(设备方法)
3.1 int (*open) (struct inode *,struct file *) 打开设备,响应open系统
3.1.1 structfile:
3.1.1.1 linux系统中,每个打开的文件,都会关联一个structfile,由内核在文件打开时创建,文件关闭后释放
3.1.1.2 重要成员
loff_t f_pos //文件读写指针
struct file_operation*f_op //文件对应的操作
3.1.2 structinode
3.1.2.1 每个存在于文件系统的文件都会关联一个inode结构,记录文件物理信息。
3.1.2.2 重要成员:
dev_t i_rdev //设备号
3.2 int (*release) (struct inode *,struct file*) 关闭设备,响应close系统
3.3 loff_t (*llseek) (struct file *,loff_t,int) 重定位读写指针,响应lseek系统调用
3.4 ssize_t(*read) (struct file *,char _user *,size_t,loff_t *) 从设备读取数据,响应read系统调用
3.4.1 参数分析:
filp:与字符设备文件关联的file结构指针,由内核创建
buff:从设备读到的数据需要保存的位置,由read系统调用提供
count:请求传输的数据量,由read系统调用提供
offp:文件读写位置,由内核从file结构中取出传递进来
3.4.2 从设备中读取数据(硬件访问类操作)
3.4.3 将读到的数据返回给应用程序
3.5 ssize_t(*write) (struct file *,const char _user *,size_t,loff_t *) 向设备写入数据,响应write系统调用
3.5.1 参数分析:
filp:与字符设备文件关联的file结构指针,由内核创建
buff:从设备读到的数据需要保存的位置,由read系统调用提供
count:请求传输的数据量,由read系统调用提供
offp:文件读写位置,由内核从file结构中取出传递进来
3.5.2 从应用程序提供的地址中取出数据
3.5.3 将数据写入设备(硬件访问类操作)
4 驱动注销
4.1 用cdev_del函数卸载驱动程序