技术文摘

input子系统(一)——GSC3280触摸屏驱动和注册handler












一、GSC3280触摸屏驱动简介


ADC与触摸屏控制器通过SPI接口挂在GSC3280的SPI0总线上,支持4线电阻式触摸屏或当ADC输入使用。

控制器可以接四线电阻式触摸屏,最大支持4000像素的屏,市面上绝大部分四线电阻式触摸屏都能支持。所接触摸屏的输出阻抗不要大于2K欧姆,否则会影响测量精度。当控制器四根引脚(XP、YP、XN、YN)接四线电阻式触摸屏时,掉电及屏没有被按下时,控制器的中断信号INT_PEN保持为低电平。当触摸屏被按下时,控制器会置起INT_PEN信号来通知CPU,然后在中断服务程序里,CPU可以通过SPI0接口发出触摸屏相关命令进行操作。接触摸屏时,屏被按下时就有中断,当CPU往SPI_DIN 线上发测量命令时,控制器自动撤掉中断,无需CPU清除。CPU取到有效数据(即在SPI_DOUT线上返回状态0,此时为有效数据)时,如果此时触摸屏还是被按下的(可能是上次按下没来得及撤掉,也可能是再次被按下的),INT_PEN会再次被置起。



二、GSC3280四线触摸屏驱动


2.1、平台设备注册


分析一个驱动,一般都是从模块初始化函数开始,程序如下:

1.  #if defined(CONFIG_TOUCHSCREEN_GSC3280)

2.  static struct resource gsc3280_ts_resources[] = {

3.      {

4.                  .name = "adc-ts-irq",

5.                  .start = EXT_GSC3280_ADC_IRQ,

6.                  .end = EXT_GSC3280_ADC_IRQ,

7.                  .flags = IORESOURCE_IRQ,

8.          },

9.   

10.};

11.static struct platform_device gsc3280_ts_device = {

12.        .name            = "adc-ts-irq",

13.        .id                = -1,

14.        .resource        = gsc3280_ts_resources,

15.        .num_resources    = ARRAY_SIZE(gsc3280_ts_resources),

16.};

17.#endif

18.static struct platform_driver gsc3280_ts_driver = {

19.     .driver    = {

20.         .name    = "adc-ts-irq",

21.         .owner    = THIS_MODULE,

22.     },

23.     .probe    = gsc3280_ts_probe,

24.     .remove    = __devexit_p(gsc3280_ts_remove),

25.};

26.static int __init gsc3280_ts_init(void)

27.{

28.    int ret = 0;

29. 

30.    ret = platform_driver_register(&gsc3280_ts_driver);

31.    if (ret)

32.        printk(KERN_ERR "!!!!!!gsc ts init register error!!!!!!\n");

33.    return ret;

34.}

35.static void __exit gsc3280_ts_exit(void)

36.{

37.    platform_driver_unregister(&gsc3280_ts_driver);

38.}

39.subsys_initcall(gsc3280_ts_init);

40.module_exit(gsc3280_ts_exit);

说明:

1) 此处模块初始化使用的是subsys_initcall(),优先级高于module_init(),为什么使用subsys_initcall(),将在第三篇中讲述。
2) 将GSC3280的触摸屏驱动当作平台设备,根据(一)中的特性,此处的触摸屏和SPI0挂接在一起,所以此处涉及的资源只有中断,没有寄存器地址。




2.2、探测函数gsc3280_ts_probe()


程序如下:

1.  static int __devinit gsc3280_ts_probe(struct platform_device *pdev)

2.  {

3.      int ret = 0;

4.      struct input_dev *input = NULL;

5.      struct gsc3280_ts_s *gsc = NULL;

6.   

7.      DBG("############\n");

8.      printk(KERN_INFO "gsc3280 touch screen probe start.\n");

9.      gsc = kzalloc(sizeof(struct gsc3280_ts_s), GFP_KERNEL);

10.    input = input_allocate_device();

11.    if (!gsc || !input) {

12.        ret = -ENOMEM;

13.        goto err_free_mem;

14.    }

15.    gsc->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

16.    if (gsc->irq == NULL) {

17.        DBG("!!!!no irq resource specified!\n");

18.        ret = -ENOENT;

19.        goto err_free_mem;

20.    }

21.    ret = request_threaded_irq(gsc->irq->start, NULL, gsc3280_ts_irq, IRQF_ONESHOT, "adcirq", gsc);

22.    if (ret) {

23.        goto err_free_mem;

24.    }

25.    writel(0x01, (volatile unsigned int *)0xbc04a0ac);    /* enable ts */

26. 

27.    spin_lock_init(&gsc->slock);

28.    gsc->dev = &pdev->dev;

29.    gsc->x = 0;

30.    gsc->y = 0;

31.    gsc->z = 0;

32.    gsc->con_cnt = 0;

33.    snprintf(gsc->phys, sizeof(gsc->phys), "%s/input0", dev_name(gsc->dev));

34.    input->name = "h3600_ts";

35.    input->phys = gsc->phys;

36.    input->dev.parent = gsc->dev;

37.    input->id.vendor = 0x00;

38.    input->id.version = 0x00;

39.    input->id.product = 0x03;

40.    input->id.bustype = BUS_HOST;

41. 

42.    input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

43.    input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

44.    input_set_abs_params(input, ABS_X, 0, 0x3FF, 0, 0);

45.    input_set_abs_params(input, ABS_Y, 0, 0x3FF, 0, 0);

46.    gsc->input = input;

47.    input_set_abs_params(gsc->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0);

48.    input_set_abs_params(gsc->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0);

49.    input_set_abs_params(gsc->input, ABS_PRESSURE, GSC_PRESSURE_MIN, 

50.                            GSC_PRESSURE_MAX, 0, 0);

51.    ret = input_register_device(gsc->input);

52.    if (ret) {

53.        DBG("!!!!input register device error!\n");

54.        goto err_free_irq;

55.    }

56.    INIT_LIST_HEAD(&gsc->device_entry);

57.    strlcpy(gsc->name, GSC3280_TS_NAME, GSC3280_TS_NAME_SIZE);

58.    

59.    mutex_lock(&gsc3280_ts_list_lock);

60.    list_add(&gsc->device_entry, &gsc3280_ts_list);

61.    mutex_unlock(&gsc3280_ts_list_lock);

62.    

63.    platform_set_drvdata(pdev, gsc);

64.    printk(KERN_INFO "gsc3280 touch screen probe success.\n");

65.    DBG("############\n");

66.    return 0;

67. 

68.err_free_irq:

69.    free_irq(gsc->irq->start, gsc);

70.err_free_mem:

71.    input_free_device(input);

72.    kfree(gsc);

73.    printk(KERN_INFO "!!!!gsc3280 touch screen probe error!\n");

74.    return ret;

75.}

76.static int __devexit gsc3280_ts_remove(struct platform_device *pdev)

77.{

78.    struct gsc3280_ts_s *gsc = platform_get_drvdata(pdev);

79.    

80.    free_irq(gsc->irq->start, gsc);

81.    input_unregister_device(gsc->input);

82.    kfree(gsc);

83.    platform_set_drvdata(pdev, NULL);

84.    printk(KERN_INFO "gsc3280 touch screen remove\n");

85.    return 0;

86.}


说明:

1) 首先申请结构体内存gsc3280_ts_s和input设备内存。

2) 申请中断资源和注册中断函数。

3) 初始化成员变量。

4) 注册input_dev。

5) 移除函数主要就是释放探测函数中申请的资源。



2.3、中断函数gsc3280_ts_irq()


程序如下:

1.  static int test_ts_state(void)

2.  {

3.      u32 state = *((volatile unsigned int *)(ICTL_RAW_STATUS_REG)) ;

4.      

5.      if (state & ICTL_SPI0_BITS) {

6.          return TS_PRESS_DOWN;

7.      } else

8.          return TS_PRESS_UP;

9.  }

10. static irqreturn_t gsc3280_ts_irq(int irq, void *dev_id)

11. {

12.     u8 flg = 0;

13.     u32 x = 0, y = 0, z = 0;

14.    struct gsc3280_ts *ts = dev_id;

15.    

16.begin:

17.    x = adc_cmd(ADC_CMD_MEASUREX);

18.    y = adc_cmd(ADC_CMD_MEASUREY);

19.    z = adc_cmd(ADC_CMD_MEASUREZ);

20.    x = ((x -0x83) * 800) / (0xf5d - 0x83);

21.    y = ((0xeea - y) * 480) / (0xeea - 0xcc);

22. 

23.    if ((z < 700) && (z > 10) && (x > 0) && (x < 800) && (y > 0) && (y < 480)) {

24.        if (flg == 0) {

25.            flg = 1;

26.            gsc3280_report_event(ts, x, y, 0);

27.        }

28.        gsc3280_report_event(ts, x, y, z);

29.        msleep(10);

30.    }    

31.    if (test_ts_state() == TS_PRESS_UP)

32.        goto Up;

33.    else

34.        goto begin;

35.Up:

36.    if (flg == 0)

37.        return IRQ_HANDLED;

38.    if ((x == 0) || (y == 0))

39.        return IRQ_HANDLED;

40.    gsc3280_report_event(ts, x, y, 0);

41.    return IRQ_HANDLED;

42.}


说明:

1) 进入中断后,调用adc_cmd()函数测量触摸点位置和按键力度信息。

2) 根据公式计算绝对坐标,具体公式原理可以查看input子系统(二)--GSC3280一线触摸屏驱动

3) 判断测量数据是否正确,如果正确,报告相应位置。

4) flg是报告标志,即是否向input子系统报告了位置信息,该变量还用来做是否是第一次触摸判断,如果该变量为0,表示第一次触摸,如果为1,表示不是第一次触摸。

5) mdelay(10);的作用是除抖。

6) 除抖后判断触摸是否按下,如果按下,还需要判断是否是第一次触摸,如果是第一次触摸,而且按键是抬起状态,则表示此次是抖动,直接退出。如果不是第一次触摸,此时触摸是抬起状态,则直接进入抬起流程。

7) 调用gsc_report_data(gsc);报告信息。

8) 再次判断触摸状态,如果是按下,进入开始流程。如果是抬起,进入抬起流程。

9) 抬起流程中,判断是否报告了触摸按下信息,如果报告了,就报告触摸抬起信息。



2.4、触摸点信息测量函数


程序如下:

1.  void spi0_write(unsigned short v)

2.  {

3.      int cnt=0;    

4.      DBG("enter adc_write()\n");

5.      do {

6.          while (SPI0_STATUS_REG & 0x10)

7.              if (cnt++ > 10000) 

8.                  break;

9.      } while(0);

10.    cnt = 0;

11.    /*spi0 fifo can write, transmit fifo empty */    

12.        while (SPI0_STATUS_REG & SPI_RX_FULL)

13.                   if (cnt++ > 1000000)    

14.                break; 

15.    SPI0_DATA_REG = v;

16.    DBG("leave adc_write()\n");

17.}

18.unsigned short spi0_read(void)

19.{

20.    int cnt= 0;

21.    DBG("enter adc_read()\n");

22.    do {

23.        while (SPI0_STATUS_REG & 0x10)

24.            if (cnt++ > 10000) 

25.                break;

26.    } while(0);

27.    cnt = 0;

28.    /*spi0 fifo receive not empty*/

29.    while (!(SPI0_STATUS_REG & SPI_RX_N_EMPTY))

30.        if (cnt++>10000000)

31.            break;

32.    DBG("leave adc_read()\n");

33.    return (unsigned short)(SPI0_DATA_REG); 

34.}

35.unsigned short adc_cmd(unsigned short cmd)

36.{

37.    unsigned short res = 0;

38.    DBG("enter adc_cmd\n");

39.    spi0_write( cmd );

40.    while (1) {

41.        res = spi0_read();

42.        if ((res == 0xF000) || (res == (0x8000 | (cmd >> 12)))) {

43.            spi0_write(0xF000);

44.        }

45.        else if ( res < 0x1000 ) { //data

46.            char buf[20] ;

47.            sprintf(buf, "adc_cmd %0x result %0x\n", cmd, res);

48.            DBG(buf);

49.            return res;                            

50.        }

51.        else{

52.            spi0_write( cmd );

53.        }

54.    }

55.    DBG("leave adc_cmd\n");

56.    return res;

57.}

说明:

1) adc_cmd()函数的形参为具体的命令。

2) adc_cmd()函数首先通过SPI0发送命令,然后根据协议读取数据。



2.5、信息报告函数


1.  static void gsc3280_report_event(struct gsc3280_ts *ts, u32 x, u32 y, u32 z)

2.  {

3.  #ifdef CONFIG_GSC3280_POS_PRINT

4.      printk(KERN_INFO "x = %d\n", x);

5.      printk(KERN_INFO "y = %d\n", y);

6.      printk(KERN_INFO "z = %d\n", z);

7.  #endif

8.   

9.      input_report_abs(ts->input, ABS_PRESSURE, z);

10.    input_report_abs(ts->input, ABS_X, x);

11.    input_report_abs(ts->input, ABS_Y, y);

12.    if (z > 0)

13.        input_report_key(ts->input, BTN_TOUCH, 1);

14.    else

15.        input_report_key(ts->input, BTN_TOUCH, 0);

16.    input_sync(ts->input);

17.}

说明:

1) 根据宏标志判断是否打印测量信息,用于测试。

2) 向input核心报告位置信息。






三、注册input_handler


handler中的成员主要作为回调函数,具体如下:


3.1、模块注册函数


程序如下:

1.  static struct input_handler tsdev_handler = {

2.      .event        = tsdev_event,

3.      .connect        = tsdev_connect,

4.      .disconnect    = tsdev_disconnect,

5.      .fops        = &tsdev_fops,

6.      .minor        = TSDEV_MINOR_BASE,

7.      .name        = "tsdev",

8.      .id_table        = tsdev_ids,

9.  };

10.static int __init tsdev_init(void)

11.{

12.    input_register_handler(&tsdev_handler);

13.    return 0;

14.}

15.static void __exit tsdev_exit(void)

16.{

17.    input_unregister_handler(&tsdev_handler);

18.}

19.module_init(tsdev_init);

20.module_exit(tsdev_exit);


说明:

1) 调用input_register_handler()注册tsdev_handler。input_register_handler()函数将在第三篇文章中讲述。

2) 模块退出函数就是注销tsdev_handler。



3.2、连接和释放连接函数


程序如下:

1.  static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,

2.                              const struct input_device_id *id)

3.  {

4.      int minor = 0, ret = 0;

5.      struct ts_dev *ts = NULL;

6.      for (minor = 0; ((minor < (TSDEV_MINOR_MAX >> 1)) && tsdev_table[minor]); minor++) {

7.          ;

8.      }

9.      if (minor >= (TSDEV_MINOR_MAX >> 1)) {

10.        DBG("!!!!You have way too many touchscreens!\n");

11.        return -EBUSY;

12.    }

13.    if (!(ts = kzalloc(sizeof(struct ts_dev), GFP_KERNEL))) {

14.        DBG("!!!!kmalloc error!\n");

15.        return -ENOMEM;

16.    }

17.    INIT_LIST_HEAD(&ts->list);

18.    init_waitqueue_head(&ts->wait);

19.    wake_up_interruptible(&ts_wait_queue);

20.    dev_set_name(&ts->dev, "ts%d", minor);

21.    ts->exist = 1;

22.    ts->minor = minor;

23.    ts->handle.dev = input_get_device(dev);

24.    ts->handle.name = dev_name(&ts->dev);;

25.    ts->handle.handler = handler;

26.    ts->handle.private = ts;

27.    ts->dev.class = &input_class;

28.    if (dev) {

29.        ts->dev.parent = &dev->dev;

30.    }

31.    ts->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);

32.    ts->dev.release = tsdev_free;

33.    device_initialize(&ts->dev);

34.    ret = input_register_handle(&ts->handle);

35.    if (ret) {

36.        DBG("!!!!register handler tsdev error!\n");

37.        return -EFAULT;

38.    }

39.    tsdev_table[minor] = ts;

40.    ret = device_add(&ts->dev);

41.    if (ret) {

42.        DBG("!!!!add tsdev class error!\n");

43.        return -EFAULT;

44.    }

45.    return 0;

46.}

47.static void tsdev_disconnect(struct input_handle *handle)

48.{

49.    struct ts_dev *ts = handle->private;

50. 

51.    ts->exist = 0;

52.    device_del(&ts->dev);

53.    if (ts->minor != (TSDEV_MINOR_MAX >>1))

54.        input_unregister_handle(&ts->handle);

55.    put_device(&ts->dev);

56.}

 1) 此函数作为handler的回调函数,具体调用地点将在第三篇文章中讲述。

 2) 在数组指针tsdev_table中找到还没有使用的数组索引编号。

 2) 申请结构体内存,然后对其成员进行初始化。

 3) 设置设备名称,此名称就是显示在“/dev”目录下的名称。

 4) 计算设备号。

 5) 调用input_register_handle()函数注册handle。

 6) 将结构体指针放入指针数组tsdev_table中。

 7) 释放连接函数主要是注销连接函数中注册的handle。



3.3、事件函数


程序如下:

1.  static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)

2.  {

3.      unsigned long flags;

4.      struct timeval time;

5.      struct tsdev_list *list;

6.      struct ts_dev *ts = handle->private;

7.   

8.      switch (type) {

9.      case EV_ABS:

10.        switch (code) {

11.        case ABS_X:

12.            ts->x = value;

13.            break;

14.        case ABS_Y:

15.            ts->y = value;

16.            break;

17.        case ABS_PRESSURE:

18.            ts->pressure = value;

19.            break;

20.        }

21.        break;

22.    case EV_REL:

23.    switch (code) {

24.    case REL_X:

25.        ts->x += value;

26.        if (ts->x < 0)

27.            ts->x = 0;

28.        else if (ts->x > gXres)

29.            ts->x = gXres;

30.        break;

31.    case REL_Y:

32.        ts->y += value;

33.        if (ts->y < 0)

34.            ts->y = 0;

35.        else if (ts->y > gYres)

36.            ts->y = gYres;

37.        break;

38.    }

39.    break;

40.    case EV_KEY:

41.        if (code == BTN_TOUCH || code == BTN_MOUSE) {

42.            switch (value) {

43.            case 0:

44.                ts->pressure = 0;

45.                break;

46.            case 1:

47.                if (!ts->pressure)

48.                    ts->pressure = 1;

49.                break;

50.            }

51.        }

52.        break;

53.    } 

54.    if (type != EV_SYN)

55.        return;

56.    list_for_each_entry(list, &ts->list, node) {

57.        if (list) {

58.            spin_lock_irqsave(&list->lock, flags);     

59.            do_gettimeofday(&time);

60.            list->event[list->head].pressure = ts->pressure;

61.            list->event[list->head].x = ts->x;

62.            list->event[list->head].y = ts->y;

63.            list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);                

64.            kill_fasync(&list->fasync, SIGIO, POLL_IN);                 

65.            spin_unlock_irqrestore(&list->lock, flags);

66.        }

67.    }

68.    wake_up_interruptible(&ts_wait_queue);

69.}

说明:

1) 事件函数也是一个回调函数,具体调用地点将在第三篇文章中介绍。

2) 首先是一个switch语句,根据不同情况,进入不同的分支。各个分支主要是根据情况的不同,赋值触摸信息。

3) 判断是否是同步事件,如果不是,直接退出。

4) 如果是同步事件,将触摸信息赋值给list结构体成员,list结构体成员值将会被传递到应用层,接下来会讲述。

5) 唤醒等待队列,标志有数据可读



3.4、函数操作集tsdev_fops


操作集具体内容如下:

1.  struct file_operations tsdev_fops = {

2.      .owner = THIS_MODULE,

3.      .open = tsdev_open,

4.      .release = tsdev_release,

5.      .read = tsdev_read,

6.      .poll = tsdev_poll,

7.      .fasync = tsdev_fasync,

8.  };


1、open和release函数


程序如下:

1.  static int tsdev_open(struct inode *inode, struct file *file)

2.  {

3.      struct tsdev_list *list;

4.      int ret = 0, i = iminor(inode) - TSDEV_MINOR_BASE;

5.   

6.      if ((i >= TSDEV_MINOR_MAX) || (!tsdev_table[i & TSDEV_MINOR_MASK])) {

7.          DBG("!!!!tsdev minor error!\n");

8.          return -ENODEV;

9.      }

10.    list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL);

11.    if (!list) {

12.        DBG("!!!!kzalloc error!\n");

13.        return -ENOMEM;

14.    }

15.    list->raw = (i >= (TSDEV_MINOR_MAX >> 1)) ? 1 : 0;

16.    i &= TSDEV_MINOR_MASK;

17.    list->tsdev = tsdev_table[i];

18.    list_add_tail(&list->node, &list->tsdev->list);

19.    list->num++;

20.    list->head = list->tail = 0;        

21.    spin_lock_init(&list->lock);

22.    file->private_data = list;

23.    if (!list->tsdev->open++)

24.        if (list->tsdev->exist)

25.            ret = input_open_device(&list->tsdev->handle);

26.    return ret;

27.}

28.static int tsdev_release(struct inode *inode, struct file *file)

29.{

30.    struct tsdev_list *list = file->private_data;

31.    tsdev_fasync(-1, file, 0);

32.    list_del(&list->node);

33.    if (!--list->tsdev->open) {

34.        if (list->tsdev->exist)

35.            input_close_device(&list->tsdev->handle);

36.        else

37.            tsdev_free(&(list->tsdev->dev));

38.    }

39.    kfree(list);

40.    return 0;

41.}


说明:

1) 首先计算设备节点的次设备号,此即接下来申请的结构体内存地址存放索引号。

2) 申请list结构体内存,对其成员变量初始化。

3) 此处list->head和list->tail两个成员变量比较重要,通过这两个变量来标识有多少个数据可读。

4) 释放函数主要就是释放open函数中申请到的资源。

2、读函数

应用层操作“/dev/ts0”设备节点读数据时,就是调用此函数,程序如下:
1.  static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)

2.  {

3.      int ret = 0;

4.      unsigned long flags = 0;

5.      struct tsdev_list *list = file->private_data;

6.   

7.      if ((list->head == list->tail) && (file->f_flags & O_NONBLOCK) )

8.          return -EAGAIN;

9.      ret = wait_event_interruptible(ts_wait_queue, (list->head != list->tail));

10.    if (ret)

11.        return ret;

12.    spin_lock_irqsave(&list->lock, flags);

13.    while ((list->head != list->tail) && (ret + sizeof (struct ts_event) <= count)) {

14.        if (copy_to_user (buffer + ret, list->event + list->tail, sizeof (struct ts_event))) {

15.            spin_unlock_irqrestore(&list->lock, flags);

16.            return ret;

17.        }     

18.        list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);

19.        ret += sizeof (struct ts_event);

20.    }

21.    spin_unlock_irqrestore(&list->lock, flags); 

22.    return ret;

23.}


说明:

1) 首先判断是否有数据,此时就是通过list->head和list->tail来判断的。

2) 如果没有数据,则表达式(list->head != list->tail)不满足,睡眠等待。

3) 如果有数据,将数据拷贝到应用层,在操作数据过程中,使用自旋锁保护,函数返回值为应用层接收到的数据长度。

3、poll函数

当应用层使用select或者poll函数时,调用的就是底层驱动的poll函数,此处poll函数的程序如下:
1.  static unsigned int tsdev_poll(struct file *file, poll_table * wait)

2.  {

3.      unsigned long flags = 0;

4.      struct tsdev_list *list = file->private_data;

5.   

6.      poll_wait(file, &ts_wait_queue, wait);

7.      spin_lock_irqsave(&list->lock, flags);

8.      if (list->head != list->tail) {

9.          spin_unlock_irqrestore(&list->lock, flags);

10.        return POLLIN | POLLRDNORM;

11.    } 

12.    spin_unlock_irqrestore(&list->lock, flags);

13.    return 0;

14.}


说明:

1) 首先调用poll_wait()函数等待。

2) 当有数据时,唤醒等待队列,从上面发现,唤醒函数在3.3事件函数tsdev_event()中。

3) 如果有数据,则返回值为可读,否则返回0。

4、fasync函数

程序如下:

1.  static int tsdev_fasync(int fd, struct file *file, int on)

2.  {

3.      int ret = 0;

4.      struct tsdev_list *list = file->private_data;

5.   

6.      ret = fasync_helper(fd, file, on, &list->fasync);

7.      return ret < 0 ? ret : 0;

8.  }


说明:

1) fasync函数供上层调用。

2) 在3.3中调用了kill_fasync(&list->fasync, SIGIO, POLL_IN); 函数,其表示向应用层发送数据,应用层进程中哪个函数调用fasync_helper()函数就向它发送数据,也就是说应用层哪个进程调用tsdev_fasync()函数就向它发送数据。









原文参见:http://blog.chinaunix.net/uid-25445243-id-4042727.html