
Linux系统中,应用程序访问外设是通过文件的形式来进行的,Linux将所有的外设都看做文件,统一存放在/dev目录下。
应用程序使用内核提供的标准系统调用来与内核中的驱动程序进行通讯,这些系统调用有:
open(), read(), write(), ioctl(), close() 等等。
file_operations重要的成员
struct inode 结构代表一个实实在在文件,每个文件只对应一个inode;
struct file 结构代表一个打开的文件,同一个文件可以对应多个file结构;
struct file_operations结构代表底层 *** 作硬件函数的集合
找到 first_drv的主设备号是249,如下图
进阶字符设备写法请看下一篇文章
姓名:王芷若 学号:19020100180学院:电子工程学院
【嵌牛导读】:本篇文章整理Linux知识点—Linux字符型设备驱动初步。
【嵌牛鼻子】:Linux设备类型,结构体,驱动模块
【嵌牛提问】:Linux设备有什么类型?关键函数有哪些?
【嵌牛内容】–linux字符型设备驱动初步
一、Linux字符设备驱动初步
1、Linux设备类型
(1)字符设备:只能一个字节一个字节的读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后顺序进行。字符设备是面向流的设备,常见的字符设备如鼠标、键盘、串口、控制台、LED等。
(2)块设备:是指可以从设备的任意位置读取一定长度的数据设备。块设备如硬盘、磁盘、U盘和SD卡等存储设备。
(3)网络设备:网络设备比较特殊,不在是对文件进行 *** 作,而是由专门的网络接口来实现。应用程序不能直接访问网络设备驱动程序。在/dev目录下也没有文件来表示网络设备。
2、开发流程
在这里插入图片描述
3、关键函数讲解(以2.6以下版本内核为例)
(1)驱动模块注册register_chrdev()函数
原型:register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
major:主设备号,该值为 0 时,自动运行分配。而实际值不是 0 ;
name:设备名称;
fops: *** 作函数,实现驱动定义的open、read、write、close等内核函数与应用程序调用的open、read、write、close间的映射;
返回值:
major 值为 0 ,正常注册后,返回分配的主设备号。如果分配失败,返回 EBUSY 的负值 ( -EBUSY ) 。major 值若大于 linux/major.h (2.4内核)中声明的最大值 (#define MAX_CHRDEV 255) ,则返回EINVAL 的负值 (-EINVAL) 。指定 major 值后,若有注册的设备,返回 EBUSY 的负值 (-EBUSY)。若正常注册,则返回 0 值
(2)驱动注销unregister_chrdev()函数
原型:
#include <linux.fs.h>
int unregister_chrdev (unsigned int major, const char *name)
变量:
major 主设备号
name 设备文件
返回值:
major 值若大于 linux/major.h (2.4 内核)中声明的最大值 (#define MAX_CHRDEV 255),返回 EINVAL的负值 (-EINVAL)。指定了 major的值后,若将要注销的 major 值并不是注册的设备驱动程序,返回 EINVAL的负值 ( -EINVAL )。正常注销则返回 0值。
(3)File_operation结构体
file_operations结构是建立驱动程序和设备编号的连接,内部是一组函数指针,每个打开的文件,也就是file结构,和一组函数关联,这些 *** 作主要用来实现系统调用的
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设备驱动程序》USB骨架程序(usb-skeleton),是USB驱动程序的基础,通过对它源码的学习和理解,可以使我们迅速地了解USB驱动架构,迅速地开发我们自己的USB硬件的驱动。
前言
在上篇《Linux下的硬件驱动--USB设备(上)(驱动配制部分)》中,我们知道了在Linux下如何去使用一些最常见的USB设备。但对于做系统设计的程序员来说,这是远远不够的,我们还需要具有驱动程序的阅读、修改和开发能力。在此下篇中,就是要通过简单的USB驱动的例子,随您一起进入 USB驱动开发的世界。
USB驱动开发
在掌握了USB设备的配置后,对于程序员,我们就可以尝试进行一些简单的USB驱动的修改和开发了。这一段落,我们会讲解一个最基础USB框架的基础上,做两个小的USB驱动的例子。
USB骨架
在Linux kernel源码目录中driver/usb/usb-skeleton.c为我们提供了一个最基础的USB驱动程序。我们称为USB骨架。通过它我们仅需要修改极少的部分,就可以完成一个USB设备的驱动。我们的USB驱动开发也是从她开始的。
那些linux下不支持的USB设备几乎都是生产厂商特定的产品。如果生产厂商在他们的产品中使用自己定义的协议,他们就需要为此设备创建特定的驱动程序。当然我们知道,有些生产厂商公开他们的USB协议,并帮助Linux驱动程序的开发,然而有些生产厂商却根本不公开他们的USB协议。因为每一个不同的协议都会产生一个新的驱动程序,所以就有了这个通用的USB驱动骨架程序, 它是以pci 骨架为模板的。
如果你准备写一个linux驱动程序,首先要熟悉USB协议规范。USB主页上有它的帮助。一些比较典型的驱动可以在上面发现,同时还介绍了USB urbs的概念,而这个是usb驱动程序中最基本的。
Linux USB 驱动程序需要做的第一件事情就是在Linux USB 子系统里注册,并提供一些相关信息,例如这个驱动程序支持那种设备,当被支持的设备从系统插入或拔出时,会有哪些动作。所有这些信息都传送到USB 子系统中,在usb骨架驱动程序中是这样来表示的:
static struct usb_driver skel_driver = {
name: "skeleton",
probe: skel_probe,
disconnect: skel_disconnect,
fops: &skel_fops,
minor: USB_SKEL_MINOR_BASE,
id_table: skel_table,
}
变量name是一个字符串,它对驱动程序进行描述。probe 和disconnect 是函数指针,当设备与在id_table 中变量信息匹配时,此函数被调用。
fops和minor变量是可选的。大多usb驱动程序钩住另外一个驱动系统,例如SCSI,网络或者tty子系统。这些驱动程序在其他驱动系统中注册,同时任何用户空间的交互 *** 作通过那些接口提供,比如我们把SCSI设备驱动作为我们USB驱动所钩住的另外一个驱动系统,那么我们此USB设备的 read、write等 *** 作,就相应按SCSI设备的read、write函数进行访问。但是对于扫描仪等驱动程序来说,并没有一个匹配的驱动系统可以使用,那我们就要自己处理与用户空间的read、write等交互函数。Usb子系统提供一种方法去注册一个次设备号和file_operations函数指针,这样就可以与用户空间实现方便地交互。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)