Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264950
  • 博文数量: 25
  • 博客积分: 329
  • 博客等级: 一等列兵
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-24 09:43
文章分类

全部博文(25)

文章存档

2014年(4)

2013年(12)

2012年(9)

分类: LINUX

2012-08-24 09:58:32

MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0

写字符设备驱动的基本步骤;

A. 确定设备号

B. 分配一个struct cdev结构

C. 设置

D. 注册

1.设备号

设备号分为主设备号,次设备号。主设备号是用来标识与设备文件相连的驱动程序;次设备号是驱动程序辨别操作的是那个设备。

2.主设备号的确定

有两种方式可以获得主设备号:一种是在开发板上运行 cat /proc/devices命令,找出没有使用的主设备号,然后使用register_chrdev_region函数静态申请;另一种方法是只用alloc_chrdev_region动态获取。

3.分配一个cdev结构

可以使用cdev_alloc函数动态分配,也可以使用直接定义。

3.设置cdev结构

使用函数cdev_init,它具有两个参数,一个是cdev结构的地址,一个是file_operations结构的地址,故在初始化cdev时应创建一个file_operations结构体,并实现里面的操作函数集。其中的成员很多有open,read,write,release函数,当应用程序调用open,read,write,close函数时,最终就会调用到file_operations结构中与之对应的函数。

下面简单介绍file_operations结构中的几个操作函数:

1).ioctl 函数的作用是对硬件有控制能力,比如设置波特率。通过调用ioctl函数可 以从应用程序传一些参数到的驱动程序中去。

在用户空间,使用ioctl 系统调用来控制设备,原型如下:

int ioctl(int fd,unsigned long cmd,...)

在内核空间,定义ioctl函数的在file_operations结构中的原型是:

int (*ioctl)(struct inode *inode,struct file *filp,

unsigned int cmd,unsigned long arg)

cmd参数从用户空间传下来,可选的参数arg 以一个unsigned long 的形式传递,不管它是一个整数或一个指针。如果cmd命令不涉及数据传输,则第3 个参数arg的值无任何意义

实现的方发可分为两步:

a. 定义命令;    

b. 实现命令。

ioctl函数的实现通常是通常是一个条件判断的语句,当传入的命令与驱动的实现的命令相同时,执行该部分的代码。当命令号不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)。

2).poll 函数的作用是监视文件,对应的系统调用有select 和 poll函数。实现的概括为两个:a. 将进程放入等待队列;b. 返回掩码。

poll系统调用返回值有如下几种情况:

1.正常情况下返回满足要求的文件描述符个数;

2.经过了timeout等待后仍无文件满足要求,返回值为0;

3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。

4.如果出错,返回-1并设置相应的errno。

poll驱动函数的模板为(以按键为例):

static unsigned int bottons_poll(struct file *file,

struct poll_table_struct *wait)

{

int mask = 0;

/* 将把进程添加到等待队列 */

poll_wait( file, &button_waitq, wait);

/* 当有按键按下时,返回掩码 */

if(ev_press)

 mask |= POLLIN | POLLRDNORM;

return mask;

}

使用poll系统调用时:

/* 定义结构体数组 */

struct pollfd fds[1]; 

/* 设置struct pollfd中的成员 */

fds[0].fd     = fd;       //设置监视文件的文件描述符

fds[0].events = POLLIN;   //设置监视文件的属性

while(1)

{

/*

 * 第一个参数传入结构体数组,第二个参数传入监视文件的个数,   * 第三个参数设置超时时间单位ms

 */

res = poll(fds, 1, 5000); 

if( 0 == res)

printf("time out!\n");

else

{

read(fd, &keynum, 1);

printf("keyval = 0x%x\n", keynum);

}

}

使用select系统调用时:

Select函数的原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

参数:

nfds:文件描述符的范围,比待检测的最大文件描述符大1

readfs:   被读监控的文件描述符集

writefds: 被写监控的文件描述符集

exceptfds:被异常监控的文件描述符集

timeout:  定时器

系统提供了4个宏对描述符集进行操作:

#include  

void FD_SET(int fd, fd_set *fdset)

void FD_CLR(int fd, fd_set *fdset)

void FD_ZERO(fd_set *fdset)

void FD_ISSET(int fd, fd_set *fdset)

宏FD_SET将文件描述符fd添加到文件描述符集fdset中;

宏FD_CLR从文件描述符集fdset中清除文件描述符fd;

宏FD_ZERO清空文件描述符集fdset;

在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件

fd发生了变化。

应用程序代码:

/* 设置超时时间 */

static struct timeval time = {

.tv_sec = 5,  //秒

.tv_usec = 0, //微秒

};

while(1)

{

FD_SET(fd, &set); //将要监视的文件的描述符放入文件描述符集中

res = select(fd+1, &set, NULL, NULL, &time);

if( 0 == res)

printf("time out!\n");

else

{

read(fd, &keynum, 1);

printf("keyval = 0x%x\n", keynum);

}

time.tv_sec = 5; // 必须重新设置超时时间,否则第一次超时后,  // 超时时间就变成了零,故必须重新设置。

}

4.注册

使用cdev_add函数注册字符设备,可以理解为将cdev结构添加到内核的链表中。

在入口函数中注册字符设备,在出口函数中就应释放字符设备;

在入口函数中申请了设备号,在出口函数中就应释放设备号;

如果是动态分配到cdev结构应该在出口函数中释放,防止内存泄露。

在2.6的内核中还经常见到使用register_chrdev注册字符设备驱动,该函数有三个参数,主设备号(为零时表示让系统自动分配),名字,file_operations结构。这种方式注册的字符设备只能通主设备号来区分,占用大量的设备号。但是使用时简单。

MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0

5.自动创建设备节点

使用class_create函数创建一个类

使用device_create函数在类下创建一个设备


以led驱动程序为例


以上内容全是本人凭个人理解写出的:如有错误请多包涵,若能指出,本人将万分感激。


阅读(1555) | 评论(0) | 转发(0) |
0

上一篇:S3C2440裸机部分

下一篇:按键驱动

给主人留下些什么吧!~~