Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1806908
  • 博文数量: 134
  • 博客积分: 2488
  • 博客等级: 大尉
  • 技术积分: 7554
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-26 21:19
个人简介

1.每日自省; 2.享受人生; 3.尽力而为; 4.坚持不懈; 5.切莫急躁; 6.慎言敏行; 7.动心忍性; 8.上善若水。

文章分类

全部博文(134)

文章存档

2014年(38)

2013年(42)

2012年(15)

2011年(39)

分类: 嵌入式

2013-12-18 15:52:05

一、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. static struct platform_device gsc3280_ts_device = {
  11.         .name            = "adc-ts-irq",
  12.         .id                = -1,
  13.         .resource        = gsc3280_ts_resources,
  14.         .num_resources    = ARRAY_SIZE(gsc3280_ts_resources),
  15. };
  16. #endif
  17. static struct platform_driver gsc3280_ts_driver = {
  18.      .driver    = {
  19.          .name    = "adc-ts-irq",
  20.          .owner    = THIS_MODULE,
  21.      },
  22.      .probe    = gsc3280_ts_probe,
  23.      .remove    = __devexit_p(gsc3280_ts_remove),
  24. };
  25. static int __init gsc3280_ts_init(void)
  26. {
  27.     int ret = 0;

  28.     ret = platform_driver_register(&gsc3280_ts_driver);
  29.     if (ret)
  30.         printk(KERN_ERR "!!!!!!gsc ts init register error!!!!!!\n");
  31.     return ret;
  32. }
  33. static void __exit gsc3280_ts_exit(void)
  34. {
  35.     platform_driver_unregister(&gsc3280_ts_driver);
  36. }
  37. subsys_initcall(gsc3280_ts_init);
  38. 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.     DBG("############\n");
  7.     printk(KERN_INFO "gsc3280 touch screen probe start.\n");
  8.     gsc = kzalloc(sizeof(struct gsc3280_ts_s), GFP_KERNEL);
  9.     input = input_allocate_device();
  10.     if (!gsc || !input) {
  11.         ret = -ENOMEM;
  12.         goto err_free_mem;
  13.     }
  14.     gsc->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  15.     if (gsc->irq == NULL) {
  16.         DBG("!!!!no irq resource specified!\n");
  17.         ret = -ENOENT;
  18.         goto err_free_mem;
  19.     }
  20.     ret = request_threaded_irq(gsc->irq->start, NULL, gsc3280_ts_irq, IRQF_ONESHOT, "adcirq", gsc);
  21.     if (ret) {
  22.         goto err_free_mem;
  23.     }
  24.     writel(0x01, (volatile unsigned int *)0xbc04a0ac);    /* enable ts */

  25.     spin_lock_init(&gsc->slock);
  26.     gsc->dev = &pdev->dev;
  27.     gsc->x = 0;
  28.     gsc->y = 0;
  29.     gsc->z = 0;
  30.     gsc->con_cnt = 0;
  31.     snprintf(gsc->phys, sizeof(gsc->phys), "%s/input0", dev_name(gsc->dev));
  32.     input->name = "h3600_ts";
  33.     input->phys = gsc->phys;
  34.     input->dev.parent = gsc->dev;
  35.     input->id.vendor = 0x00;
  36.     input->id.version = 0x00;
  37.     input->id.product = 0x03;
  38.     input->id.bustype = BUS_HOST;

  39.     input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
  40.     input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
  41.     input_set_abs_params(input, ABS_X, 0, 0x3FF, 0, 0);
  42.     input_set_abs_params(input, ABS_Y, 0, 0x3FF, 0, 0);
  43.     gsc->input = input;
  44.     input_set_abs_params(gsc->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0);
  45.     input_set_abs_params(gsc->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0);
  46.     input_set_abs_params(gsc->input, ABS_PRESSURE, GSC_PRESSURE_MIN,
  47.                             GSC_PRESSURE_MAX, 0, 0);
  48.     ret = input_register_device(gsc->input);
  49.     if (ret) {
  50.         DBG("!!!!input register device error!\n");
  51.         goto err_free_irq;
  52.     }
  53.     INIT_LIST_HEAD(&gsc->device_entry);
  54.     strlcpy(gsc->name, GSC3280_TS_NAME, GSC3280_TS_NAME_SIZE);
  55.     
  56.     mutex_lock(&gsc3280_ts_list_lock);
  57.     list_add(&gsc->device_entry, &gsc3280_ts_list);
  58.     mutex_unlock(&gsc3280_ts_list_lock);
  59.     
  60.     platform_set_drvdata(pdev, gsc);
  61.     printk(KERN_INFO "gsc3280 touch screen probe success.\n");
  62.     DBG("############\n");
  63.     return 0;

  64. err_free_irq:
  65.     free_irq(gsc->irq->start, gsc);
  66. err_free_mem:
  67.     input_free_device(input);
  68.     kfree(gsc);
  69.     printk(KERN_INFO "!!!!gsc3280 touch screen probe error!\n");
  70.     return ret;
  71. }
  72. static int __devexit gsc3280_ts_remove(struct platform_device *pdev)
  73. {
  74.     struct gsc3280_ts_s *gsc = platform_get_drvdata(pdev);
  75.     
  76.     free_irq(gsc->irq->start, gsc);
  77.     input_unregister_device(gsc->input);
  78.     kfree(gsc);
  79.     platform_set_drvdata(pdev, NULL);
  80.     printk(KERN_INFO "gsc3280 touch screen remove\n");
  81.     return 0;
  82. }

        说明:

        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.     if ((z < 700) && (z > 10) && (x > 0) && (x < 800) && (y > 0) && (y < 480)) {
  23.         if (flg == 0) {
  24.             flg = 1;
  25.             gsc3280_report_event(ts, x, y, 0);
  26.         }
  27.         gsc3280_report_event(ts, x, y, z);
  28.         msleep(10);
  29.     }    
  30.     if (test_ts_state() == TS_PRESS_UP)
  31.         goto Up;
  32.     else
  33.         goto begin;
  34. Up:
  35.     if (flg == 0)
  36.         return IRQ_HANDLED;
  37.     if ((x == 0) || (y == 0))
  38.         return IRQ_HANDLED;
  39.     gsc3280_report_event(ts, x, y, 0);
  40.     return IRQ_HANDLED;
  41. }

        说明:
        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.     input_report_abs(ts->input, ABS_PRESSURE, z);
  9.     input_report_abs(ts->input, ABS_X, x);
  10.     input_report_abs(ts->input, ABS_Y, y);
  11.     if (z > 0)
  12.         input_report_key(ts->input, BTN_TOUCH, 1);
  13.     else
  14.         input_report_key(ts->input, BTN_TOUCH, 0);
  15.     input_sync(ts->input);
  16. }
        说明:

        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.     ts->exist = 0;
  51.     device_del(&ts->dev);
  52.     if (ts->minor != (TSDEV_MINOR_MAX >>1))
  53.         input_unregister_handle(&ts->handle);
  54.     put_device(&ts->dev);
  55. }

        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.     switch (type) {
  8.     case EV_ABS:
  9.         switch (code) {
  10.         case ABS_X:
  11.             ts->x = value;
  12.             break;
  13.         case ABS_Y:
  14.             ts->y = value;
  15.             break;
  16.         case ABS_PRESSURE:
  17.             ts->pressure = value;
  18.             break;
  19.         }
  20.         break;
  21.     case EV_REL:
  22.     switch (code) {
  23.     case REL_X:
  24.         ts->x += value;
  25.         if (ts->x < 0)
  26.             ts->x = 0;
  27.         else if (ts->x > gXres)
  28.             ts->x = gXres;
  29.         break;
  30.     case REL_Y:
  31.         ts->y += value;
  32.         if (ts->y < 0)
  33.             ts->y = 0;
  34.         else if (ts->y > gYres)
  35.             ts->y = gYres;
  36.         break;
  37.     }
  38.     break;
  39.     case EV_KEY:
  40.         if (code == BTN_TOUCH || code == BTN_MOUSE) {
  41.             switch (value) {
  42.             case 0:
  43.                 ts->pressure = 0;
  44.                 break;
  45.             case 1:
  46.                 if (!ts->pressure)
  47.                     ts->pressure = 1;
  48.                 break;
  49.             }
  50.         }
  51.         break;
  52.     }
  53.     if (type != EV_SYN)
  54.         return;
  55.     list_for_each_entry(list, &ts->list, node) {
  56.         if (list) {
  57.             spin_lock_irqsave(&list->lock, flags);    
  58.             do_gettimeofday(&time);
  59.             list->event[list->head].pressure = ts->pressure;
  60.             list->event[list->head].x = ts->x;
  61.             list->event[list->head].y = ts->y;
  62.             list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);                
  63.             kill_fasync(&list->fasync, SIGIO, POLL_IN);                
  64.             spin_unlock_irqrestore(&list->lock, flags);
  65.         }
  66.     }
  67.     wake_up_interruptible(&ts_wait_queue);
  68. }

        说明:

        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.     if ((i >= TSDEV_MINOR_MAX) || (!tsdev_table[i & TSDEV_MINOR_MASK])) {
  6.         DBG("!!!!tsdev minor error!\n");
  7.         return -ENODEV;
  8.     }
  9.     list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL);
  10.     if (!list) {
  11.         DBG("!!!!kzalloc error!\n");
  12.         return -ENOMEM;
  13.     }
  14.     list->raw = (i >= (TSDEV_MINOR_MAX >> 1)) ? 1 : 0;
  15.     i &= TSDEV_MINOR_MASK;
  16.     list->tsdev = tsdev_table[i];
  17.     list_add_tail(&list->node, &list->tsdev->list);
  18.     list->num++;
  19.     list->head = list->tail = 0;        
  20.     spin_lock_init(&list->lock);
  21.     file->private_data = list;
  22.     if (!list->tsdev->open++)
  23.         if (list->tsdev->exist)
  24.             ret = input_open_device(&list->tsdev->handle);
  25.     return ret;
  26. }
  27. static int tsdev_release(struct inode *inode, struct file *file)
  28. {
  29.     struct tsdev_list *list = file->private_data;
  30.     tsdev_fasync(-1, file, 0);
  31.     list_del(&list->node);
  32.     if (!--list->tsdev->open) {
  33.         if (list->tsdev->exist)
  34.             input_close_device(&list->tsdev->handle);
  35.         else
  36.             tsdev_free(&(list->tsdev->dev));
  37.     }
  38.     kfree(list);
  39.     return 0;
  40. }

        说明:

        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.     if ((list->head == list->tail) && (file->f_flags & O_NONBLOCK) )
  7.         return -EAGAIN;
  8.     ret = wait_event_interruptible(ts_wait_queue, (list->head != list->tail));
  9.     if (ret)
  10.         return ret;
  11.     spin_lock_irqsave(&list->lock, flags);
  12.     while ((list->head != list->tail) && (ret + sizeof (struct ts_event) <= count)) {
  13.         if (copy_to_user (buffer + ret, list->event + list->tail, sizeof (struct ts_event))) {
  14.             spin_unlock_irqrestore(&list->lock, flags);
  15.             return ret;
  16.         }    
  17.         list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
  18.         ret += sizeof (struct ts_event);
  19.     }
  20.     spin_unlock_irqrestore(&list->lock, flags);
  21.     return ret;
  22. }

        说明:

        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.     poll_wait(file, &ts_wait_queue, wait);
  6.     spin_lock_irqsave(&list->lock, flags);
  7.     if (list->head != list->tail) {
  8.         spin_unlock_irqrestore(&list->lock, flags);
  9.         return POLLIN | POLLRDNORM;
  10.     }
  11.     spin_unlock_irqrestore(&list->lock, flags);
  12.     return 0;
  13. }

        说明:

        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.     ret = fasync_helper(fd, file, on, &list->fasync);
  6.     return ret < 0 ? ret : 0;
  7. }

        说明:

        1)  fasync函数供上层调用。

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

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

CU博客助理2014-03-12 16:37:39

嘉宾点评:非常不错的分享,apple_guet Linux设备驱动深度解析教程和时间笔记,从学习笔记到实践举例非常全面,内容专业,条理清晰,可以看出博主非常专注于嵌入式Linux驱动开发,相信作者又很强的设备驱动开发经验。这是一系列不错的实践教材,绝对值得推荐,建议多提供可以编译的源代码和提供好参考的硬件设备信息,平台信息,让完全不懂的菜鸟也可以很快入手。(感谢您参与“原创博文评选”获奖结果即将公布)