1设备驱动程序的一般编写步骤
(1) 确定设备的主设备号
(2) 填充static struct file_operation这样的结构
(3) 定义一个类似__init mydriver_init(void)这样的注册函数,大体如下:
int __init mydriver_init(void)
{
int rc;
rc = register_chrdev(mydevice_Major, "mydev", &mydriver_fops);
if (rc < 0) {
printk(KERN_WARNING "mydevice: can/'t get Major %d//n",
mydevice_Major);
return rc;
}
printk("mydevice Driver Support.//n");
return 0;
}
其中mydev表示的设备文件为/dev/mydev X X,即习惯上起名为mydev mydev01 mydev02……如had hda1 hda2 hda3。不过名字是可以随意的。如果成功就返回0,否则返回-1
2 填充驱动程序子函数集struct file_operation结构
(1)驱动程序子函数集struct file_operation结构
struct file_operation结构共有11个成员,分别表示一函数指针,内容及作用说明如下:
struct file_operations {
/*需要改变文件位置时调用*/
loff_t (*llseek) (struct file *, loff_t, int);
/*执行read(2)系统调用时调用*/
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
/*执行write(2)系统调用时调用*/
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
/*读目录内容时调用*/
int (*readdir) (struct file *, void *, filldir_t);
/*执行poll 系统调用时调用*/
unsigned int (*poll) (struct file *, struct poll_table_struct *);
/*执行ioctl(2) 系统调用时调用*/
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
/*执行mmap(2) 系统调用时调用*/
int (*mmap) (struct file *, struct vm_area_struct *);
/*打开设备时调用*/
int (*open) (struct inode *, struct file *);
/*只在file的使用计数为0时调用,它用来释放file结构*/
int (*release) (struct inode *, struct file *);
/*执行fsync(2)系统调用时调用*/
int (*fsync) (struct file *, struct dentry *);
/*执行fcntl(2)系统调用时调用*/
int (*fasync) (int, struct file *, int);
};
(2)根据需要,按以下方法构造上述函数,如:
static int ReadPower(struct inode * inode, struct file * file, char * buffer, int count)
{
if (!Ready)
return -EAGAIN;
if (count != sizeof(int))
return -EINVAL;
copy_to_user(buffer, &myvar, sizeof(int));
return sizeof(int);
}
这里定义成static是因为该驱动程序子函数在文件外是通过上述的间接方法被调用的,文件外不需要该符号。
(3)把相关操作对应的函数名填入mydevice_operations中
填充static struct file_operations mydevice_operations时,用以上构造的函数名填充相应成员,例如:
struct file_operations TouchPanel_fops = {
read: ReadTouchPanel,
poll: TouchPanelSelect,
ioctl: TouchPanelIoctl,
open: OpenTouchPanel,
release: CloseTouchPanel,
};
3 中断处理
首先,中断不是必须的。如果设备要用中断模式,就要构造中断处理函数,并注册中断处理函数。
Linux中断处理过程由中断子系统完成;而中断处理函数只是处理具体操作,即读写数据等。成功注册该中断处理函数,就挂接到中断子系统上了。
中断处理一般由两个主要部分组成:
(1)构造中断处理函数void MyDriverInterrupt(int irq, void *dev_id, struct pt_regs *regs),
一般说来,Linux中断处理函数完成工作一方面把设备来的数据读出并保存起来,另一方面把内核中的数据写入设备。这样,驱动程序只需从内核读出数据,把用户提交的数据写入内核;这里可以使用一个简单的数组。
中断处理函数时要防止其他中断的干扰,可用"save_flags(flags); cli();"来关闭中断,相关处理完毕,用"restore_flags(flags)"来恢复。
(2) 注册中断可在mydriver_init()中,也 可在 mydriver_open()中,例如:
if (request_irq(
MYDEVICE_IRQ_NUM,
MyDeviceInterrupt,
IRQ_FLG_STD,
"mydevice",
NULL
)
) {
printk("TouchPanel: Cannot register interrupt.//n");
return -EBUSY;
}
其中:
MYDEVICE_IRQ_NUM: 注册中断号码
MyDeviceInterrupt: 中断处理程序
mydevice: 中断说明(可随便写)
如果返回0,表示成功,否则失败。
4 时间处理
在驱动程序中可以使用定时器来调度函数在未来某个特定时间执行。定时器包括它的超时值(timeout, 单位是jiffies)和超时时调用的函数。定时器列表的数据结构如下:
struct timer_list {
struct list_head list;
unsigned long expires; /*timeout超时值,以jiffies值为单位*/
unsigned long data; /*传递给定时器处理程序的参数*/
void (*function)(unsigned long); /*超时调用的定时器处理程序*/
};
定时器的timeout是个"jiffy"值,当jiffies值大于等于timer->expires时,timer->function函数就会执行,timeout值是个绝对数值,它与当前时间无关,不需要更新。
操作定时器有下面几个函数:
void init_timer(struct timer_list *timer);
该函数初始化新分配的定时器队列。
void add_timer(struct timer_list *timer);
该函数将定时器插入挂起的定时器的全局队列。
void del_timer(struct timer_list *timer);
如果需要在定时器超时前将它从列表中删除,应调用del_timer函数。但当定时器超时时,系统会自动地将它从列表中删除。