(学习)linux驱动程序之字符驱动

首先讲述一下驱动程序的概念。

驱动程序实际上就是硬件与应用程序之间的中间层。驱动程序工作在内核空间,应用程序一般运行于用户态。在内核态下,CPU可执行任何指令,在用户态下CPU只能执行非特权指令。当CPU处于内核态,可以随意进入用户态,而当CPU处于用户态,只能通过特殊的方式进入内核态,比如linux操作系统中的系统调用。系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。

Linux支持三类硬件设备:字符设备、块设备及网络设备。首先学习一下字符设备驱动的编写。

一、字符设备注册

在linux 2.6内核中采用的是以下函数进行字符设备注册,而对于新版本的字符设备注册改成新的注册方式。

int register_chrdev(unsigned int major, const char *name, struct file_operations 
*fops);

新的注册方式:

(1)在linux内核中使用结构体cdev结构来描述字符设备,在驱动程序中必须将已分配到的设备号以及设备操作接口赋予struct cdev结构变量。首先使用cdev_alloc()函数向系统

申请分配struct cdev结构,函数原型为:

struct cdev *cdev_alloc(void);

(2)初始化struct cdev,并与file_operations结构关联起来,即赋值cdev.owner=THIS_MODULE,函数原型为:

void cdev_init(struct cdev *cdev,struct file_operations *fops);

(3)调用cdev_add()函数将设备号与struct cdev结构进行关联并向内核正式报告新设备的注册。

int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
//成功返回0,出错返回-1
//cdev:需要初始化/注册/删除的struct cdev结构
//fops:该字符设备的file_opertions结构
//num:系统给该设备分配的第一个设备号
//count:该设备对应的设备号数量

(4)删除一个设备则要调用cdev_del()函数。

void cdev_del(struct cdev *dev);

二、设备的打开和释放

打开设备的函数接口是open,函数原型为:

static int device_open(struct inode *inode, struct file *file)

主要完成如下工作:

(1)递增计数器,检查错误;

(2)如果未初始化,则进行初始化;

(3)识别次设备号,如果必要,更新f_op指针;

(4)分配并填写被置于filp->private_data的数据结构

其中递增计数器是用于设备计数的。由于设备在使用时通常会打开多次,也可以由不同的进程所使用,所以若有一进程想要删除该设备,则必须保证其他设备没有使用该设备。

释放设备设备的函数接口是release,函数原型为:

static int device_release(struct inode *inode, struct file *file)

主要完成工作:

(1)递减计数器;

(2)释放打开设备时系统所分配的内存空间;

(3)在最后一次释放设备操作时关闭设备

三、读写设备

读写设备的主要任务就是把内核空间的数据复制到用户空间,或者从用户空间复制到内核空间,函数原型为:

ssize_t (*read)(struct file *filp, char *buff, size_t count, loff_t *offp);

ssize_t (*write)(struct file *filp,const char *buff, size_t count, loff_t *offp);

这里重点说明一下buff指的是用户空间指针,不能被内核代码直接引用。有如下理由:

(1)依赖于你的驱动运行的体系,用户空间指针当运行于内核模式可能根本是无效的,可能没有那个地址的映射,或者它可能指向一些其他的随机数据。

(2)就算这个指针在内核空间是同样的东西,用户空间内存是分页的,在做系统调用时,这个内存可能没有在RAM中。试图直接引用用户空间内存可能产生一个页面错,这是内核代码不允许做的事情,导致进行系统调用的进程死亡。

其中的读设备的意思就是把数据从内核空间复制到用户空间,而写设备是把数据从用户空间复制到内核空间,这里用到的函数原型为(在asm/uaccess.h)中定义:

unsigned long copy_to_user(void *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void *from, unsigned long count);
int put_user(dataum,ptr);
int get_user(local,ptr);
//内核空间和用户空间的单值交互(如char、int、long)

四、IO控制函数

IO控制函数包括对设备的所有操作,包括设置设备、读写设备等等,这样最大的好处就是给了应用程序一个统一的接口,让应用程序编写非常简单而且易懂。函数原型为:

static int device_ioctl(struct inode *inode,    /* see include/linux/fs.h */
         struct file *file,    /* ditto */
         unsigned int ioctl_num,    /* number and param for ioctl */
         unsigned long ioctl_param)

ioctl_num表示IO控制的类型,可以为IOCTL_SET_MSG、IOCTL_SET_DISP_WAIT、IOCTL_GET_MSG、IOCTL_GET_NTH_BYTE等等,这里仅仅举个例子。

ioctl_param参数表示传入的参数。

这里有一个比较重要的概念就是ioctl命令号。

ioctl命令号

ioctl命令号是这个函数中最重要的参数,它描述的ioctl要处理的命令。linux中使用一个32位的数据来编码ioctl命令,它包含四个部分:dir,type,nr,size

  • dir:

代表数据传输的方向,占2位,可以是_IOC_NONE(无数据传输,OU),_IOC_WRITE(向设备写数据)或_IOC_READ(从设备读数据)或者他们的逻辑组合,当然这里只有_IOC_WRITE和_IOC_READ的组合才有意义。

  • type:

描述ioctl命令的类型,8bit。每种设备或系统都可以指定自己的一个类型号,ioctl用这个类型来表示ioctl命令所属的设备或驱动。

  • nr

ioctl命令序号,一般8bit,对于一个指定的设备驱动,可以对他的ioctl命令做一个顺序编码,一般从零开始,这个编码就是ioctl命令的序号。

  • size

ioctl命令的参数大小,一般为14位。ioctl命令号的这个数据成员不是强制使用的,你可以不使用它,但是建议你指定这个数据成员,通过它可以检查用户空间数据的大小以避免错误的数据操作,也可以实现兼容旧版本的ioctl命令。

对于自己写的驱动,如果需要使用特定的ioctl命令,则必须创建ioctl命令以及编号。内核中给出了创建ioctl命令的宏。

/* used to create numbers */
#define _IO(type,nr)            _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)      _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)     _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

总结:上述是基本的字符驱动编写基本知识,针对具体的字符驱动,涉及到具体硬件的一些设置,里面的读写函数以及设置函数都有很多区别,而且大部分都要用到延迟,因为所有的串行硬件设备都是工作在KHZ的频率上,而CPU已经工作在GHZ的水平,所以,这里需要进行软延迟。另外可能还有就是中断方式。

转载于:https://www.cnblogs.com/yingfang18/archive/2011/10/19/2217854.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值