 |
s3c2410触摸屏在linux下的驱动分析 三 |
|
 |
|
s3c2410触摸屏在linux下的驱动分析 三 |
|
副标题:s3c2410触摸屏在linux下的驱动分析 三 |
| 日期:2008-3-24 22:22:42 来源:雪竹博客 编辑:51Touch |
|
由于上面这个wake_up_interruptible 函数的作用还不是很明确,所以需要分析一下打开设备文件这个函数。触摸屏打开设备文件函数定义如下: static int s3c2410_ts_open(struct inode *inode, struct file *filp) tsdev.head = tsdev.tail = 0; tsdev.penStatus = PEN_UP; 首先是最简单的将变量tsdev.head 和tsdev.tail 这两个表示buf 头尾位置的值清零,然后将变量tsdev.penStatus 的状态值初始化为笔抬起。 #ifdef HOOK_FOR_DRAG init_timer(&ts_timer); ts_timer.function = ts_timer_handler; #endif 如果定义了笔拖曳,先调用init_timer 函数来初始化一个定时器,变量ts_timer 为struct timer_list 数据结构,然后将定时调用的函数指针指向ts_timer_handler 函数。 tsEvent = tsEvent_raw; 将函数指针tsEvent 指向tsEvent_raw 函数。在/kernel/include/linux/wait.h 文件中: static inline void init_waitqueue_head(wait_queue_head_t *q) { #if WAITQUEUE_DEBUG if (!q) WQ_BUG(); #endif q->lock = WAITQUEUE_RW_LOCK_UNLOCKED; INIT_LIST_HEAD(&q->task_list); #if WAITQUEUE_DEBUG q->__magic = (long)&q->__magic; q->__creator = (long)current_text_addr(); #endif } 该函数初始化一个已经存在的等待队列头,它将整个队列设置为"未上锁"状态,并将链表指针prev和next指向它自身。 init_waitqueue_head(&(tsdev.wq)); 在这个s3c2410_ts_open 函数中,调用init_waitqueue_head 函数来初始化一个定义在变量tsdev 中的等待队列头的成员结构。 MOD_INC_USE_COUNT; return 0; 最后调用MOD_INC_USE_COUNT; 来对设备文件计数器加一计数,并返回再来分析一下用户层要调用的读取设备文件的接口函数: static ssize_t s3c2410_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) 这个函数实现的任务是将事件队列从设备缓存中读到用户空间的数据缓存中。实现的过程主要是通过一个循环,只有在事件队列的头、尾指针不重合时,才能成功的从tsdev.tail指向的队列尾部读取到一组触摸信息数据,并退出循环。否则调用读取函数的进程就要进入睡眠。 TS_RET ts_ret; retry: if (tsdev.head != tsdev.tail) { int count; count = tsRead(&ts_ret); if (count) copy_to_user(buffer, (char *)&ts_ret, count); return count; } 这个函数中,首先通过变量tsdev.head 和tsdev.tail 是否相等来判断环形缓冲区是否为空。若不相等则表示环形缓冲区中有触摸屏数据,调用tsRead 函数将触摸屏数据读入TS_RET 数据结构的ts_ret 变量中,该函数会在后面说明。 接下来调用copy_to_user 函数来把内核空间的数据复制到用户空间,在这里就是把驱动程序里的变量ts_ret 中的数据复制到用户程序的buffer 中,然后返回复制的数据长度。 else { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&(tsdev.wq)); if (signal_pending(current)) return -ERESTARTSYS; goto retry; } 若变量tsdev.head 和tsdev.tail 相等,则表示环形缓冲区为空,首先根据file->f_flags 与上O_NONBLOCK 值来进行判断,若为O_NONBLOCK 值,则表示采用非阻塞的文件IO方法,立即返回,否则才会调用interruptible_sleep_on 函数,调用该函数的进程将会进入睡眠,直到被唤醒。关于O_NONBLOCK 值的含义在我总结的《IIS音频驱动程序分析》一文中有详细说明。在/kernel/kernel/sched.c 文件中: void interruptible_sleep_on(wait_queue_head_t *q) { SLEEP_ON_VAR current->state = TASK_INTERRUPTIBLE; SLEEP_ON_HEAD schedule(); SLEEP_ON_TAIL } 常用的睡眠操作有interruptible_sleep_on和sleep_on。两个函数类似,只不过前者将进程的状态从就绪态(TASK_RUNNING)设置为TASK_INTERRUPTIBLE,允许通过发送signal唤醒它(即可中断的睡眠状态);而后者将进程的状态设置为TASK_UNINTERRUPTIBLE,在这种状态下,不接收任何singal。关于interruptible_sleep_on 和wake_up_interruptible 函数详细的用法可以参考一篇《关于linux内核中等待队列的问题》文档。如果进程被唤醒,则会继续跳到retry: 循环读取环形缓冲区,直到读取到一组触摸信息数据才会退出。 static int tsRead(TS_RET * ts_ret) 这个函数主要将环形缓冲区的x轴y轴坐标数据和笔的状态数据传入该函数形式参数所指的指针变量中。 spin_lock_irq(&(tsdev.lock)); 上文已经解释过了,调用该宏函数来禁止IRQ 中断。 ts_ret->x = BUF_TAIL.x; ts_ret->y = BUF_TAIL.y; ts_ret->pressure = BUF_TAIL.pressure; tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF); 接着把变量tsdev 的环形缓冲区中相关数据赋值给该函数形式参数所指的指针变量中,并将表示环形缓冲区队列尾部的变量tsdev.tail 加一,这就意味着从环形队列中读取一组数据,尾指针加一。 spin_unlock_irq(&(tsdev.lock)); return sizeof(TS_RET); 上文已经解释过了,调用该宏函数来重新使能IRQ 中断,然后返回。再来看一个定时器定时调用的函数: #ifdef HOOK_FOR_DRAG static void ts_timer_handler(unsigned long data) { spin_lock_irq(&(tsdev.lock)); if (tsdev.penStatus == PEN_DOWN) { start_ts_adc(); } spin_unlock_irq(&(tsdev.lock)); } #endif 这个函数需要定义过笔拖曳才有效。首先调用宏函数spin_lock_irq 来禁止IRQ 中断。 然后在变量tsdev.penStatus 状态为笔按下的时候,调用宏函数start_ts_adc 来启动A/D转换,转换的是X轴的坐标。 最后再调用宏函数spin_unlock_irq 来重新使能IRQ 中断 最后再来看一个释放设备文件的函数: static int s3c2410_ts_release(struct inode *inode, struct file *filp) #ifdef HOOK_FOR_DRAG del_timer(&ts_timer); #endif MOD_DEC_USE_COUNT; return 0; 其实也很简单,在定义了笔拖曳的情况下,调用del_timer 函数来删除定时器,变量ts_timer 为struct timer_list 数据结构,然后调用MOD_DEC_USE_COUNT; 将设备文件计数器减一计数,并返回。经过对整个触摸屏驱动程序的流程,以及S3C2410 芯片数据手册里的相关章节进行分析后,下面来总结一下触摸屏驱动程序的大致流程。首先在驱动模块初始化函数中,除了对驱动的字符设备的注册外,还要对中断进行申请。这里申请了两个触摸屏相关的中断,一个是IRQ_TC 中断,查阅了数据手册后了解到,该中断在笔按下时,由XP 管脚产生表示中断的低电平信号,而笔抬起是没有中断信号产生的。另一个是IRQ_ADC_DONE 中断,该中断是当芯片内部A/D转换结束后,通知中断控制器产生中断,这时就可以去读取转换得到的数据。当触摸屏按下后,就会出发中断,这时会调用申请中断时附带的s3c2410_isr_tc 中断回调函数,该函数中判断若为笔抬起则启动x轴坐标的A/D转换。当转换完毕后就会产生ADC中断,这时就会调用申请中断时附带的s3c2410_isr_adc 中断回调函数,在该函数中进行判断,若x轴坐标转换结束马上进行y轴坐标的A/D转换转换;若y轴坐标转换结束,则重新回到等待中断模式,然后将坐标值写入环形缓冲区,并环形等待队列中的进程。
|
|
|
| 对这篇文章您有更多的想法?
请去论坛发表意见吧
|
 |
“s3c2410触摸屏在linux下的驱动分析 三 ” 的相关新闻 |
 |
|
|
|
|
|
 |
站内搜索 |
 |
|