欧美亚洲一区二区三区,午夜欧美艳情视频免费看,欧美XXXXX又粗又大,国精品产露脸偷拍视频

技術(shù)文摘

input子系統(tǒng)(一)——GSC3280觸摸屏驅(qū)動和注冊handler












一、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) 將GSC3280的觸摸屏驅(qū)動當(dāng)作平臺設(shè)備,根據(jù)(一)中的特性,此處的觸摸屏和SPI0掛接在一起,所以此處涉及的資源只有中斷,沒有寄存器地址。




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ù),程序如下:
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) 首先判斷是否有數(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ù)的程序如下:
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) 首先調(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