Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2998819
  • 博文数量: 674
  • 博客积分: 17881
  • 博客等级: 上将
  • 技术积分: 4849
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 10:15
文章分类

全部博文(674)

文章存档

2013年(34)

2012年(146)

2011年(197)

2010年(297)

分类: LINUX

2010-12-08 22:32:22

驱动在/kernel/drivers/char/s3c2410-ts.c 文件中。
该驱动总要有以下重要数据结构:
 1.的file_operations
static struct file_operations s3c2410_fops={
    owner: THIS_MODULE,
    open: s3c2410_ts_open,
    read: s3c2410_ts_read,
    release: s3c2410_ts_release,
#ifdef USE_ASYNC
    fasync: s3c2410_ts_fasync,
#endif
    poll: s3c2410_ts_poll,
};
 2.全局变量TS_DEV结构体,用来保存的相关参数、等待处理的消息队列、当前采样数据、上一次采样数据等信息
typedef struct {
    unsigned int penStatus;
    TS_RET buf[MAX_TS_BUF];
    unsigned int head, tail;
    wait_queue_head_t wq; /
 wait_down_int(); 99touch触摸屏资讯网

     函数request_irq 是Linux 系统中驱动程序注册中断的方法。irq 为所要申请的硬件中断号,handler 为系统所注册的中断处理子程序,irq_flags 为申请时的选项,devname 为指向设备名称的字符指针,dev_id 为申请时告诉系统的设备标识。若中断申请成功则返回0,失败则返回负值。
ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT,
           DEVICE_NAME, s3c2410_isr_adc);
    调用该函数来进行A/D转换的中断注册,所要申请的硬件中断号为IRQ_ADC_DONE(62),在arch/irq s.h中定义;系统所注册的中断处理子程序为s3c2410_isr_adc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。

ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT,
          DEVICE_NAME, s3c2410_isr_tc);
    接着继续调用该函数来进行触摸的中断注册,所要申请的硬件中断号为IRQ_TC(61);系统所注册的中断处理子程序为s3c2410_isr_tc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。

wait_down_int();
    调用该宏函数来设置为等待中断模式【笔按下产生中断】,具体定义如下:
#define wait_down_int() { ADCTSC = DOWN_INT | XP_PULL_UP_EN | \
    XP_AIN | XM_HIZ | YP_AIN | YM_GND | \
    XP_PST(WAIT_INT_MODE); }
    用该宏函数来设置ADC 控制寄存器,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
DOWN_INT = 1<<8 * 0  该位保留且应该设为0 【笔按下或笔抬起中断信号控制位,设为0 表示笔按下产生中断信号】
XP_PULL_UP_EN = 1<<3 * 0  上拉开关使能,设为0 表示XP 引脚上拉使能
XP_AIN = 1<<4 * 1  选择nXPON 引脚输出值,设为1 表示nXPON 引脚输出1,则XP 引脚连接AIN[7] 引脚
XM_HIZ = 1<<5 * 0  选择XMON 引脚输出值,设为0 表示XMON 引脚输出0,则XM 引脚为高阻态
YP_AIN = 1<<6 * 1  选择nYPON 引脚输出值,设为1 表示nYPON 引脚输出1,则YP 引脚连接AIN[5] 引脚

99touch触摸屏

YM_GND = 1<<7 * 1  选择YMON 引脚输出值,设为1 表示YMON 引脚输出1,则YM 引脚为接地
XP_PST(WAIT_INT_MODE); = 3  X坐标Y坐标手动测量设置,设为3 表示等待中断模式 触摸屏资讯网99touch.com

#ifdef CONFIG_DEVFS_FS
devfs_ts_dir = devfs_mk_dir(NULL, "touchscreen", NULL);
devfs_tsraw = devfs_register(devfs_ts_dir, "0raw", DEVFS_FL_DEFAULT,
       tsMajor, TSRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
       &s3c2410_fops, NULL);
#endif
   这里调用了devfs_mk_dir 函数,在设备文件系统中创建了一个名为touchscreen 的目录,并返回一个带有目录结构的数据结构变量devfs_ts_dir。将该变量作为下一步devfs_register 函数的参数,该参数在调用设备文件系统注册清除函数devfs_unregister 时也要作为参数传入。
    调用devfs_register 函数后,会在刚才创建的touchscreen 目录下再创建一个名为0raw 的设备文件节点。该函数的参数中,DEVFS_FL_DEFAULT 为该函数的标志选项,tsMajor 为注册字符设备时系统自动分配的主设备号,TSRAW_MINOR(1)为次设备号,S_IFCHR | S_IRUSR | S_IWUSR 为默认的文件模式,&s3c2410_fops 为传入内核的file_operations 结构中的函数接口,私有数据指针为空。返回一个devfs_handle_t 数据结构的变量devfs_tsraw,这会在调用设备文件系统注册清除函数devfs_unregister 时作为参数传入。

99触摸屏

 
99Touch.com中国触摸屏


   模块的退出函数为s3c2410_ts_exit,该函数的工作就是清除已注册的字符设备,中断以及设备文件系统。
static void __exit s3c2410_ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS
   devfs_unregister(devfs_tsraw);
   devfs_unregister(devfs_ts_dir);
#endif
   unregister_chrdev(tsMajor, DEVICE_NAME);
#ifdef CONFIG_PM
   pm_unregister(tsdev.pm_dev);
#endif
   free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
   free_irq(IRQ_TC, s3c2410_isr_tc);
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_tsraw);
devfs_unregister(devfs_ts_dir);
#endif
    这里首先清除原先后一步创建设备文件节点0raw 的结构变量devfs_tsraw,然后再清除创建touchscreen 目录的结构变量devfs_ts_dir。
unregister_chrdev(tsMajor, DEVICE_NAME);
   接下来删除字符设备的注册信息。
void free_irq(unsigned int irq, void *dev_id)
    函数free_irq 与函数request_irq 相对应,通常在模块被卸载时调用,负责注销一个已经申请的中断。

free_irq(IRQ_ADC_DONE, s3c2410_isr_adc); 中国触摸屏网
free_irq(IRQ_TC, s3c2410_isr_tc);
    最后依次注销A/D转换和定时器这两个已经申请的中断

接下来看一下A/D转换的中断处理函数:
static void s3c2410_isr_adc(int irq, void *dev_id, struct pt_regs *reg)
    其中参数irq 为中断号,dev_id 为申请中断时告诉系统的设备标识,regs 为中断发生时寄存器内容。该函数在中断产生时由系统来调用,调用时以上参数已经由系统传入。

在/kernel/include/linux/spinlock.h 文件中:

#define spin_lock_irq(lock) do{local_irq_disable();spin_lock(lock);}while (0)
#define spin_unlock_irq(lock) do{spin_unlock(lock);local_irq_enable();}while(0)

#define DEBUG_SPINLOCKS 0
#if (DEBUG_SPINLOCKS < 1)
  typedef struct { } spinlock_t;
  #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
#define spin_lock_init(lock) do { } while(0)
#define spin_lock(lock)  (void)(lock)
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
可见上面这四个宏函数都是空函数,这样的话spin_lock_irq(lock)和spin_unlock_irq(lock)这两个宏函数就相当于分别只调用了local_irq_disable();和local_irq_enable();两个宏函数。关于自旋锁的作用和概念可以参考一篇《Linux内核的同步机制》文章的相关章节。 99touch触摸屏资讯网

在/kernel/include/asm-arm/system.h 文件中:
#define local_irq_disable() __cli()
#define local_irq_enable() __sti()
在/kernel/include/asm-arm/proc-armo/system.h 文件中:

#define __sti()     \
do {     \
   unsigned long temp;   \
   __asm__ __volatile__(   \
" mov %0, pc  @ sti\n" \
" bic %0, %0, #0x08000000\n"  \
" teqp %0, #0\n"   \
   : "=r" (temp)    \
   :     \
   : "memory");    \
} while(0)

#define __cli()     \
do {     \
   unsigned long temp;   \
   __asm__ __volatile__(   \
" mov %0, pc  @ cli\n" \
" orr %0, %0, #0x08000000\n"  \
" teqp %0, #0\n"   \


   : "=r" (temp)    \
   :     \
   : "memory");    \
} while(0)

最后用ARM 汇编指令实现了对IRQ 的使能和禁止。
spin_lock_irq(&(tsdev.lock));
    这样调用spin_lock_irq 宏函数,实际上只是做了local_irq_disable();一步,就是禁止IRQ 中断。

if (tsdev.penStatus == PEN_UP)
   s3c2410_get_XY();
    然后根据变量tsdev.penStatus 所处的状态,若为笔抬起则调用s3c2410_get_XY 函数来取得A/D转换得到的坐标值,该函数会在后面说明。
#ifdef HOOK_FOR_DRAG
else
   s3c2410_get_XY();
#endif
    这里表示如果定义了笔拖曳,且在笔没有抬起的情况下,继续调用s3c2410_get_XY 函数来得到最新的坐标值。
spin_unlock_irq(&(tsdev.lock));
    最后调用spin_unlock_irq 宏函数,相当于只做了local_irq_enable();一步,来重新使能IRQ 中断。最后退出这个中断服务子程序。


    继续来看一下另一个中断处理函数,即触摸中断处理函数:


static void s3c2410_isr_tc(int irq, void *dev_id, struct pt_regs *reg)
    该函数的参数和上面A/D转换中断处理函数的定义一样,不再累赘。
spin_lock_irq(&(tsdev.lock));

    也同上面的意思一样,首先禁止IRQ 中断。
if (tsdev.penStatus == PEN_UP) {
   start_ts_adc();
}
    接着根据变量tsdev.penStatus 的状态值判断是否进行A/D转换。若笔抬起,则调用函数start_ts_adc 来进行A/D转换,该函数会在后面说明。
else {
   tsdev.penStatus = PEN_UP;
   DPRINTK("PEN UP: x: d, y: d\n", x, y);
   wait_down_int();
   tsEvent();
}
    如果变量tsdev.penStatus 的状态值不是笔抬起,则先将该变量状态设为笔抬起,然后调用宏函数wait_down_int()。该宏函数已在前面说明,用来设置为等待中断模式。最后调用tsEvent 函数指针所指的函数,在模块初始化函数s3c2410_ts_init 中,tsEvent 指向的是一个空函数tsEvent_dummy,而在打开设备函数s3c2410_ts_open 中,tsEvent 会指向tsEvent_raw 函数,该函数负责填充缓冲区,并唤醒等待的进程。该函数也会在后面加以说明。

中国触摸屏网99touch.com


spin_unlock_irq(&(tsdev.lock));
    中断处理函数的最后一步都一样,重新使能IRQ 中断。退出中断服务子程序。


    下面先来看启动A/D转换的函数:
static inline void start_ts_adc(void)
adc_state = 0;
mode_x_axis();
start_adc_x();
    简简单单的3步。
    第一步,对A/D转换的状态变量清零。
    第二步,调用mode_x_axis 宏函数,具体定义如下:
#define mode_x_axis() { ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
    XP_PULL_UP_DIS | XP_PST(X_AXIS_MODE); }


    该宏函数用来设置ADC控制寄存器为测量X坐标模式,参考S3C2410 芯datasheet 中关于的章节,具体设置参数如下:
XP_EXTVLT = 1<<4 * 0  选择nXPON 引脚输出值,设为0 表示nXPON 引脚输出0,则XP 引脚为接外部电压
XM_GND = 1<<5 * 1  选择XMON 引脚输出值,设为1 表示XMON 引脚输出1,则XM 引脚为接地
YP_AIN = 1<<6 * 1  选择nYPON 引脚输出值,设为1 表示nYPON 引脚输出1,则YP 引脚连接AIN[5] 引脚 中国触摸屏网99touch.com
YM_HIZ = 1<<7 * 0  选择YMON 引脚输出值,设为0 表示YMON 引脚输出0,则YM 引脚为高阻态
XP_PULL_UP_DIS = 1<<3 * 1  上拉开关使能,设为1 表示XP 引脚上拉禁止
XP_PST(X_AXIS_MODE); = 1  X坐标Y坐标手动测量设置,设为1 表示X坐标测量模式
    第三步,调用start_adc_x 宏函数,具体定义如下:
#define start_adc_x() { ADCCON = PRESCALE_EN | PRSCVL(49) | \
    ADC_INPUT(ADC_IN5) | ADC_START_BY_RD_EN | \
    ADC_NORMAL_MODE; \
     ADCDAT0; }
    该宏函数用来设置ADC控制寄存器启动X坐标的A/D转换,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
PRESCALE_EN = 1<<14 * 1  A/D转换器使能,设为1 表示使能A/D转换器
PRSCVL(49) = 49<<6  A/D转换器值,设为49
ADC_INPUT(ADC_IN5) = 5<<3  选择模拟输入通道,设为5 表示AIN[5] 引脚作为模拟输入通道
ADC_START_BY_RD_EN = 1<<1 * 1  A/D转换通过读启动,设为1 表示通过读操作启动A/D转换使能 99Touch.com中国触摸屏
ADC_NORMAL_MODE; = 1<<2 * 0  选择待命模式,设为0 表示正常操作模式
ADCDAT0;  读取X坐标的ADC转换数据寄存器
    由于设置了A/D转换通过读启动,则该ADCCON 寄存器的最低位ENABLE_START 启动A/D转换位就无效了。在最后一步读取ADCDAT0 寄存器这一操作时就启动了A/D转换。


static inline void s3c2410_get_XY(void)
    这就是获取A/D转换所得到的坐标值的函数。
if (adc_state == 0)
{
  adc_state = 1;
  disable_ts_adc();
  y = (ADCDAT0 & 0x3ff);
  mode_y_axis();
  start_adc_y();
}
    这里首先查看A/D转换的状态变量,若为0 表示进行过X坐标的A/D转换,将该变量设为1。然后调用宏函数disable_ts_adc,该宏函数定义如下:
#define disable_ts_adc() { ADCCON &= ~(ADCCON_READ_START); }
    这个宏函数主要工作就是禁止通过读操作启动A/D转换,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
ADCCON_READ_START = 1<<1  A/D转换通过读启动,设为0 表示通过读操作启动A/D转换禁止 99Touch
    然后y = (ADCDAT0 & 0x3ff); 这一步将X坐标的ADC转换数据寄存器的D9~D0 这10为读出到变量y(这里由于是竖屏,参考原理图后知道,硬件连线有过改动,将XP,XM 和YP,YM 进行了对换,这样ADCDAT0 里读出的是YP,YM 方向电阻导通的值,也就是y轴坐标值)。这个mode_y_axis 宏函数定义如下:
#define mode_y_axis() { ADCTSC = XP_AIN | XM_HIZ | YP_EXTVLT | YM_GND | \
    XP_PULL_UP_DIS | XP_PST(Y_AXIS_MODE); }
    该宏函数用来设置ADC控制寄存器为测量Y坐标模式,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
XP_AIN = 1<<4 * 1  选择nXPON 引脚输出值,设为1 表示nXPON 引脚输出1,则XP 引脚连接AIN[7] 引脚
XM_HIZ = 1<<5 * 0  选择XMON 引脚输出值,设为0 表示XMON 引脚输出0,则XM 引脚为高阻态
YP_EXTVLT = 1<<6 * 0  选择nYPON 引脚输出值,设为0 表示nYPON 引脚输出0,则YP 引脚为接外部电压
YM_GND = 1<<7 * 1  选择YMON 引脚输出值,设为1 表示YMON 引脚输出1,则YM 引脚为接地 99Touch
XP_PULL_UP_DIS = 1<<3 * 1  上拉开关使能,设为1 表示XP 引脚上拉禁止
XP_PST(Y_AXIS_MODE); = 2  X坐标Y坐标手动测量设置,设为2 表示Y坐标测量模式
    最后调用start_adc_y 宏函数,具体定义如下:
#define start_adc_y() { ADCCON = PRESCALE_EN | PRSCVL(49) | \
    ADC_INPUT(ADC_IN7) | ADC_START_BY_RD_EN | \
    ADC_NORMAL_MODE; \
     ADCDAT1; }
   该宏函数用来设置ADC控制寄存器启动Y坐标的A/D转换,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
PRESCALE_EN = 1<<14 * 1  A/D转换器使能,设为1 表示使能A/D转换器
PRSCVL(49) = 49<<6  A/D转换器值,设为49
ADC_INPUT(ADC_IN7) = 7<<3  选择模拟输入通道,设为7 表示AIN[7] 引脚作为模拟输入通道
ADC_START_BY_RD_EN = 1<<1 * 1  A/D转换通过读启动,设为1 表示通过读操作启动A/D转换使能
ADC_NORMAL_MODE; = 1<<2 * 0  选择待命模式,设为0 表示正常操作模式

99Touch.com中国触摸屏


ADCDAT1;  读取Y坐标的ADC转换数据寄存器

else if (adc_state == 1)
{
  adc_state = 0;
  disable_ts_adc();
  x = (ADCDAT1 & 0x3ff);
  tsdev.penStatus = PEN_DOWN;
  DPRINTK("PEN DOWN: x: d, y: d\n", x, y);
  wait_up_int();
  tsEvent();
}
    若查看A/D转换的状态变量,若为1 表示进行过Y坐标的A/D转换,将该变量设为0。然后调用宏函数disable_ts_adc 来禁止通过读操作启动A/D转换。
    接着将x = (ADCDAT1 & 0x3ff); 这一步将Y坐标的ADC转换数据寄存器的D9~D0 这10为读出到变量x(这里由于是竖屏,参考原理图后知道,硬件连线有过改动,将XP,XM 和YP,YM 进行了对换,这样ADCDAT1 里读出的是XP,XM 方向电阻导通的值,也就是x轴坐标值)。
    随后将变量tsdev.penStatus 的状态值改为笔按下,并调用wait_up_int 宏函数来设置为等待中断模式【笔抬起产生中断】,具体定义如下:
#define wait_up_int() { ADCTSC = UP_INT | XP_PULL_UP_EN | XP_AIN | XM_HIZ | \ 中国触摸屏网99touch.com
    YP_AIN | YM_GND | XP_PST(WAIT_INT_MODE); }
    用该宏函数来设置ADC 控制寄存器,参考S3C2410 芯片datasheet 中关于的章节,具体设置参数如下:
UP_INT = 1<<8 * 1  该位保留且应该设为0,这里设为1 不知道为什么 【笔按下或笔抬起中断信号控制位,设为1 表示笔抬起产生中断信号】
XP_PULL_UP_EN = 1<<3 * 0  上拉开关使能,设为0 表示XP 引脚上拉使能
XP_AIN = 1<<4 * 1  选择nXPON 引脚输出值,设为1 表示nXPON 引脚输出1,则XP 引脚连接AIN[7] 引脚
XM_HIZ = 1<<5 * 0  选择XMON 引脚输出值,设为0 表示XMON 引脚输出0,则XM 引脚为高阻态
YP_AIN = 1<<6 * 1  选择nYPON 引脚输出值,设为1 表示nYPON 引脚输出1,则YP 引脚连接AIN[5] 引脚
YM_GND = 1<<7 * 1  选择YMON 引脚输出值,设为1 表示YMON 引脚输出1,则YM 引脚为接地
XP_PST(WAIT_INT_MODE); = 3  X坐标Y坐标手动测量设置,设为3 表示等待中断模式
    最后调用函数指针tsEvent 所指向的函数。在s3c2410_get_XY 函数里面,应该表示这个驱动的设备文件已经打开,在打开设备文件函数中,tsEvent 函数指针就指向了tsEvent_raw 这个函数,也就是说,下面执行的是tsEvent_raw 函数。tsEvent_raw 函数负责填充缓冲区,并唤醒等待的进程,该函数会在后面说明。

99touch.com中国触摸屏



------------------------------------------------------------------------
static void tsEvent_raw(void)
    来看一下tsEvent_raw 这个函数。
if (tsdev.penStatus == PEN_DOWN)
{
  BUF_HEAD.x = x;
  BUF_HEAD.y = y;
  BUF_HEAD.pressure = PEN_DOWN;
   
    一上来就根据变量tsdev.penStatus 的状态值进行判断,若为笔按下,将从A/D转换器中采集的x轴y轴坐标以及笔按下的状态存入变量tsdev 中的buf 成员中的相应变量中。

#ifdef HOOK_FOR_DRAG
  ts_timer.expires = jiffies + TS_TIMER_DELAY;
  add_timer(&ts_timer);
#endif
    如果定义了笔拖曳,先将定时器的定时溢出值更新,然后调用add_timer 函数重新增加定时器计时,变量ts_timer 为struct timer_list 数据结构。
}
else
{
#ifdef HOOK_FOR_DRAG
  del_timer(&ts_timer);
#endif

    如果定义了笔拖曳,调用del_timer 函数来删除定时器,变量ts_timer 为struct timer_list 数据结构。 中国触摸屏网99touch.com

  BUF_HEAD.x = 0;
  BUF_HEAD.y = 0;
  BUF_HEAD.pressure = PEN_UP;
}
    若变量tsdev.penStatus 的状态值不是笔按下,则x轴y轴坐标写为0和笔抬起的状态一起存入变量tsdev 中的buf 成员中的相应变量中。
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
    其中INCBUF 宏函数定义如下:
#define INCBUF(x,mod)  ((++(x)) & ((mod) - 1))
由于这里MAX_TS_BUF=16,这样(mod) - 1)就为15(0x0F),所以这个宏函数相当于就是将变量tsdev.head 这个表示buf 头位置的值加1,然后取模16,即指向下一个buf ,形成一个在环形缓冲区上绕环的头指针。

在/kernel/include/linux/sched.h 文件中:
#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE, 1)
该宏函数定义为__wake_up 函数,参数TASK_INTERRUPTIBLE 为1,表示要唤醒的任务的状态为中断模式,参数1 表示要唤醒的互斥进程数目为1。
对应的唤醒操作包括wake_up_interruptible和wake_up。wake_up函数不仅可以唤醒状态为TASK_UNINTERRUPTIBLE的进程,而且可以唤醒状态为TASK_INTERRUPTIBLE的进程。wake_up_interruptible只负责唤醒状态为TASK_INTERRUPTIBLE的进程。关于interruptible_sleep_on 和wake_up_interruptible 函数详细的用法可以参考一篇《关于linux内核中等待队列的问题》文档。 99触摸屏
在/kernel/kernel/sched.c 文件中:
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr)
{
if (q) {
  unsigned long flags;
  wq_read_lock_irqsave(&q->lock, flags);
  __wake_up_common(q, mode, nr, 0);
  wq_read_unlock_irqrestore(&q->lock, flags);
}
}
宏函数wq_read_lock_irqsave 的作用主要就是保存IRQ 和FIQ 的中断使能状态,并禁止IRQ 中断;而宏函数wq_read_unlock_irqrestore 的作用就是恢复IRQ 和FIQ 的中断使能状态。现在可以得知__wake_up 这个函数的作用,它首先保存IRQ 和FIQ 的中断使能状态,并禁止IRQ 中断,接着调用__wake_up_common 函数来唤醒等待q 队列的进程,最后再恢复IRQ 和FIQ 的中断使能状态。

static inline void __wake_up_common (wait_queue_head_t *q, unsigned int mode,
          int nr_exclusive, const int sync)
该函数的作用是唤醒在等待当前等待队列的进程。参数q 表示要操作的等待队列,mode 表示要唤醒任务的状态,如TASK_UNINTERRUPTIBLE 或TASK_INTERRUPTIBLE 等。nr_exclusive 是要唤醒的互斥进程数目,在这之前遇到的非互斥进程将被无条件唤醒。sync表示???

99touch触摸屏



在/kernel/include/linux/wait.h 文件中:
struct __wait_queue_head {
wq_lock_t lock;
struct list_head task_list;
#if WAITQUEUE_DEBUG
long __magic;
long __creator;
#endif
};
typedef struct __wait_queue_head wait_queue_head_t;
这是等待队列数据结构。
# define wq_read_lock_irqsave spin_lock_irqsave
# define wq_read_unlock_irqrestore spin_unlock_irqrestore
看到这里可以知道其实宏函数wq_read_lock_irqsave 和wq_read_unlock_irqrestore 等价于宏函数spin_lock_irqsave 和spin_unlock_irqrestore,并直接将自己的参数传了下去。

在/kernel/include/linux/spinlock.h 文件中:

#define spin_lock_irqsave(lock, flags)  do {local_irq_save(flags);spin_lock(lock);}while (0)
#define spin_unlock_irqrestore(lock, flags)  do {spin_unlock(lock);  local_irq_restore(flags); } while (0)
在这两个宏函数中,前面已经提到spin_lock 和spin_unlock 其实都为空函数,那么实际只执行了local_irq_save 和local_irq_restore 这两个宏函数。

触摸屏资讯网99touch.com



在/kernel/include/asm-arm/system.h 文件中:

#define local_irq_save(x) __save_flags_cli(x)
#define local_irq_restore(x) __restore_flags(x)
这里local_irq_save 和local_irq_restore 这两个宏函数又分别等价于__save_flags_cli 和__restore_flags 这两个宏函数。

在/kernel/include/asm-arm/proc-armo/system.h 文件中:


#define __save_flags_cli(x)    \
do {      \
   unsigned long temp;    \
   __asm__ __volatile__(    \
" mov %0, pc  @ save_flags_cli\n" \
" orr %1, %0, #0x08000000\n"   \
" and %0, %0, #0x0c000000\n"   \
" teqp %1, #0\n"    \
   : "=r" (x), "=r" (temp)   \
   :      \
   : "memory");     \
} while (0)


#define __restore_flags(x)    \

中国触摸屏网99touch.com


do {      \
   unsigned long temp;    \
   __asm__ __volatile__(    \
" mov %0, pc  @ restore_flags\n" \
" bic %0, %0, #0x0c000000\n"   \
" orr %0, %0, %1\n"    \
" teqp %0, #0\n"    \
   : "=&r" (temp)    \
   : "r" (x)     \
   : "memory");     \
} while (0)
最后用ARM 汇编指令实现了对当前程序状态寄存器CPSR 中的IRQ 和FIQ 中断使能状态的保存和恢复。而且在__save_flags_cli 宏函数中,除了对IRQ 和FIQ 中断使能状态的保存外,还禁止了IRQ 中断。

wake_up_interruptible(&(tsdev.wq));
    在这个tsEvent_raw 函数最后,调用wake_up_interruptible 函数来以中断模式唤醒等待tsdev.wq 队列的进程。

阅读(1168) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~