一、GSC3280觸摸屏驅(qū)動簡介 | |||||
ADC與觸摸屏控制器通過SPI接口掛在GSC3280的SPI0總線上,支持4線電阻式觸摸屏或當(dāng)ADC輸入使用。 控制器可以接四線電阻式觸摸屏,最大支持4000像素的屏,市面上絕大部分四線電阻式觸摸屏都能支持。所接觸摸屏的輸出阻抗不要大于2K歐姆,否則會影響測量精度。當(dāng)控制器四根引腳(XP、YP、XN、YN)接四線電阻式觸摸屏?xí)r,掉電及屏沒有被按下時,控制器的中斷信號INT_PEN保持為低電平。當(dāng)觸摸屏被按下時,控制器會置起INT_PEN信號來通知CPU,然后在中斷服務(wù)程序里,CPU可以通過SPI0接口發(fā)出觸摸屏相關(guān)命令進行操作。接觸摸屏?xí)r,屏被按下時就有中斷,當(dāng)CPU往SPI_DIN 線上發(fā)測量命令時,控制器自動撤掉中斷,無需CPU清除。CPU取到有效數(shù)據(jù)(即在SPI_DOUT線上返回狀態(tài)0,此時為有效數(shù)據(jù))時,如果此時觸摸屏還是被按下的(可能是上次按下沒來得及撤掉,也可能是再次被按下的),INT_PEN會再次被置起。 | |||||
二、GSC3280四線觸摸屏驅(qū)動 | |||||
2.1、平臺設(shè)備注冊 | |||||
分析一個驅(qū)動,一般都是從模塊初始化函數(shù)開始,程序如下: 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(),優(yōu)先級高于module_init(),為什么使用subsys_initcall(),將在第三篇中講述。 | |||||
2.2、探測函數(shù)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) 首先申請結(jié)構(gòu)體內(nèi)存gsc3280_ts_s和input設(shè)備內(nèi)存。 2) 申請中斷資源和注冊中斷函數(shù)。 3) 初始化成員變量。 4) 注冊input_dev。 5) 移除函數(shù)主要就是釋放探測函數(shù)中申請的資源。 | |||||
2.3、中斷函數(shù)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) 進入中斷后,調(diào)用adc_cmd()函數(shù)測量觸摸點位置和按鍵力度信息。 2) 根據(jù)公式計算絕對坐標(biāo),具體公式原理可以查看input子系統(tǒng)(二)--GSC3280一線觸摸屏驅(qū)動。 3) 判斷測量數(shù)據(jù)是否正確,如果正確,報告相應(yīng)位置。 4) flg是報告標(biāo)志,即是否向input子系統(tǒng)報告了位置信息,該變量還用來做是否是第一次觸摸判斷,如果該變量為0,表示第一次觸摸,如果為1,表示不是第一次觸摸。 5) mdelay(10);的作用是除抖。 6) 除抖后判斷觸摸是否按下,如果按下,還需要判斷是否是第一次觸摸,如果是第一次觸摸,而且按鍵是抬起狀態(tài),則表示此次是抖動,直接退出。如果不是第一次觸摸,此時觸摸是抬起狀態(tài),則直接進入抬起流程。 7) 調(diào)用gsc_report_data(gsc);報告信息。 8) 再次判斷觸摸狀態(tài),如果是按下,進入開始流程。如果是抬起,進入抬起流程。 9) 抬起流程中,判斷是否報告了觸摸按下信息,如果報告了,就報告觸摸抬起信息。 | |||||
2.4、觸摸點信息測量函數(shù) | |||||
程序如下: 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()函數(shù)的形參為具體的命令。 2) adc_cmd()函數(shù)首先通過SPI0發(fā)送命令,然后根據(jù)協(xié)議讀取數(shù)據(jù)。 | |||||
2.5、信息報告函數(shù) | |||||
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) 根據(jù)宏標(biāo)志判斷是否打印測量信息,用于測試。 2) 向input核心報告位置信息。 | |||||
三、注冊input_handler | |||||
handler中的成員主要作為回調(diào)函數(shù),具體如下: | |||||
3.1、模塊注冊函數(shù) | |||||
程序如下: 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) 調(diào)用input_register_handler()注冊tsdev_handler。input_register_handler()函數(shù)將在第三篇文章中講述。 2) 模塊退出函數(shù)就是注銷tsdev_handler。 | |||||
3.2、連接和釋放連接函數(shù) | |||||
程序如下: 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) 此函數(shù)作為handler的回調(diào)函數(shù),具體調(diào)用地點將在第三篇文章中講述。 2) 在數(shù)組指針tsdev_table中找到還沒有使用的數(shù)組索引編號。 2) 申請結(jié)構(gòu)體內(nèi)存,然后對其成員進行初始化。 3) 設(shè)置設(shè)備名稱,此名稱就是顯示在“/dev”目錄下的名稱。 4) 計算設(shè)備號。 5) 調(diào)用input_register_handle()函數(shù)注冊handle。 6) 將結(jié)構(gòu)體指針放入指針數(shù)組tsdev_table中。 7) 釋放連接函數(shù)主要是注銷連接函數(shù)中注冊的handle。 | |||||
3.3、事件函數(shù) | |||||
程序如下: 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) 事件函數(shù)也是一個回調(diào)函數(shù),具體調(diào)用地點將在第三篇文章中介紹。 2) 首先是一個switch語句,根據(jù)不同情況,進入不同的分支。各個分支主要是根據(jù)情況的不同,賦值觸摸信息。 3) 判斷是否是同步事件,如果不是,直接退出。 4) 如果是同步事件,將觸摸信息賦值給list結(jié)構(gòu)體成員,list結(jié)構(gòu)體成員值將會被傳遞到應(yīng)用層,接下來會講述。 5) 喚醒等待隊列,標(biāo)志有數(shù)據(jù)可讀 | |||||
3.4、函數(shù)操作集tsdev_fops | |||||
操作集具體內(nèi)容如下: 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函數(shù)
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) 首先計算設(shè)備節(jié)點的次設(shè)備號,此即接下來申請的結(jié)構(gòu)體內(nèi)存地址存放索引號。 2) 申請list結(jié)構(gòu)體內(nèi)存,對其成員變量初始化。 3) 此處list->head和list->tail兩個成員變量比較重要,通過這兩個變量來標(biāo)識有多少個數(shù)據(jù)可讀。 4) 釋放函數(shù)主要就是釋放open函數(shù)中申請到的資源。 2、讀函數(shù) 應(yīng)用層操作“/dev/ts0”設(shè)備節(jié)點讀數(shù)據(jù)時,就是調(diào)用此函數(shù),程序如下: 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) 首先判斷是否有數(shù)據(jù),此時就是通過list->head和list->tail來判斷的。 2) 如果沒有數(shù)據(jù),則表達(dá)式(list->head != list->tail)不滿足,睡眠等待。 3) 如果有數(shù)據(jù),將數(shù)據(jù)拷貝到應(yīng)用層,在操作數(shù)據(jù)過程中,使用自旋鎖保護,函數(shù)返回值為應(yīng)用層接收到的數(shù)據(jù)長度。 3、poll函數(shù) 當(dāng)應(yīng)用層使用select或者poll函數(shù)時,調(diào)用的就是底層驅(qū)動的poll函數(shù),此處poll函數(shù)的程序如下: 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) 首先調(diào)用poll_wait()函數(shù)等待。 2) 當(dāng)有數(shù)據(jù)時,喚醒等待隊列,從上面發(fā)現(xiàn),喚醒函數(shù)在3.3事件函數(shù)tsdev_event()中。 3) 如果有數(shù)據(jù),則返回值為可讀,否則返回0。 4、fasync函數(shù) 程序如下: 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函數(shù)供上層調(diào)用。 2) 在3.3中調(diào)用了kill_fasync(&list->fasync, SIGIO, POLL_IN); 函數(shù),其表示向應(yīng)用層發(fā)送數(shù)據(jù),應(yīng)用層進程中哪個函數(shù)調(diào)用fasync_helper()函數(shù)就向它發(fā)送數(shù)據(jù),也就是說應(yīng)用層哪個進程調(diào)用tsdev_fasync()函數(shù)就向它發(fā)送數(shù)據(jù)。 | |||||
原文參見:http://blog.chinaunix.net/uid-25445243-id-4042727.html | |||||