Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1264581
  • 博文数量: 404
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 5382
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-03 16:29
文章存档

2010年(40)

2009年(140)

2008年(224)

我的朋友

分类: LINUX

2008-10-07 08:56:33

 
触摸屏驱动1+1
触摸屏驱动解析
by good02xaut

开发触摸屏驱动,最好的范例莫过于mc68328digi.c的实现。在没有看到原文之前,我把其中用到的结构解析一下。

1。struct ts_pen_info  
该结构是触摸屏的核心数据结构。用户程序和驱动程序的交互就是通过该数据结构完成的。结构体里面的x,y坐标和状态是gui中事件驱动的原始数据源。

2。环形队列
gui程序通过read完成对ts_pen_info  的提取。而在内核中维护了一个环形队列,只要队列不为空,将立即返回数据给应用程序。

3。中断驱动机制
触摸屏是输入设备,因此使用的是中断驱动机制。只要有触摸事件发生,即向环形队列里面填充一项。

4。定时器的必要性
触摸屏的中断处理函数必然启动一个定时器。定时器的使用是为了检测出Drag操作。当按下触摸屏一直没有松开时,中断只会相应一次。这和触发方式关系不大,不是沿触发和电平触发的问题。主要是触摸屏的中断处理函数没有处理到松开是不会开放中断的。在这段时间内,就是通过定时器不停的启动,检测触摸屏的新坐标的。

5。驱动
触摸屏采用结构的驱动。

这5点是触摸屏驱动的核心,理解这些,阅读和编写自己的触摸屏驱动都会游刃有余。后面给出程序的源码,供有兴趣的朋友研究:)
 
mc68328digi.c解析1
文件说明部分,给出了硬件的接线方式

/*----------------------------------------------------------------------------*/
/* mc68328digi.c,v 1.2 /02/12 11:14:10 pney Exp
*
* linux/drivers/char/mc68328digi.c - Touch screen driver.
*
* Author: Philippe Ney
* (C) SMARTDATA <
*
* This program is free ; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* Thanks to:
*    Kenneth Albanowski for is first work on a touch screen driver.
*    Alessandro Rubini for is "Linux drivers" book.
*    Ori Pomerantz for is "Linux Kernel " guide.
*
* Updates:
*   -03-07 Pascal bauermeister
*              - Adapted to work ok on xcopilot; yet untested on real Palm.
*              - Added check for version in ioctl()
*
*   -03-21 Pascal bauermeister
*              - bypass real hw uninits for xcopilot. Now xcopilot is
*                happy. It even no longer generates pen irq while this
*                irq is disabled (I'd like to understand why, so that
*                I can do the same in mc68328digi_init() !).
*
*   -10-23 Daniel Potts
*              - for uCdimm/68VZ328. I use SPI 2 which maps to the
*                same addresses as for 68EZ328 and requries less changes to
*                the code. For CS I use PF_A22; CSD0/CAS0 is not made
*                available off the uCdimm.
*                PF1 (/IRQ5) is already used on uCdimm by the ethernet
*                controller. We use /IRQ6 instead (PD7).
*              - kill_fasync and request_irq fixes for 2.4.x () .
*
*
* :       -EZ
*                Burr-Brown Touch Screen Controller
*                Rikei REW-A1 Touch Screen
*
* OS:            uClinux version 2.0.38.1pre3
*
* Connectivity:  Burr-Brown        
*                PENIRQ     ---->  PF1  &  PD4 (with a )
*                BUSY       ---->  PF5
*                CS         ---->  PB4
*                DIN        ---->  PE0
*                DOUT       ---->  PE1
*                DCLK       ---->  PE2
*
*     : /PENIRQ ----+----[ ]---- EZ328: PD4
*                          |
*                          +----------------------- EZ328: /IRQ5
*
* States graph:
*
*         ELAPSED & PEN_UP
*         (re-init)             +------+
*     +------------------------>| IDLE |<------+
*     |                         +------+       |
*     |                           |            | ELAPSED
*     |  +------------------------+            | (re-init)
*     |  |       PEN_IRQ                       |
*     |  |       (disable pen_irq)             |
*     |  |       (enable & set timer)          |
*     |  |                                     |
*     | \/                                     |
*   +------+                                  
 
 
 
mc68328digi.c解析2
头文件和全局变量

#include      /* We're doing kernel work */
#include      /* Specifically, a */
#include   /* We want interrupts */

#include /* for _register() and _deregister() */

#include          /* for struct 'file_operations' */

#include      /* for timeout interrupts */
#include      /* for HZ. HZ = 100 and the timer step is 1/100 */
#include      /* for jiffies definition. jiffies is incremented
                              * once for each clock tick; thus it's incremented
         * HZ times per secondes.*/
#include         /* for verify_area */
#include     /* for kmalloc */
#include

#include          /* For IRQ_MACHSPEC */

/*----------------------------------------------------------------------------*/

#if defined(CONFIG_M68328)
/* These are missing in MC68328.h. I took them from MC68EZ328.h temporarily,
* but did not dare modifying MC68328.h yet (don't want to break things and
* have no time now to do regression testings)
*   - Pascal Bauermeister
*/
#define ICR_ADDR        0xfffff302
#define ICR_POL5        0x0080  /* Polarity for IRQ5 */
#define IMR_MIRQ5       (1 << IRQ5_IRQ_NUM)     /* Mask IRQ5 */
#define IRQ5_IRQ_NUM    20      /* IRQ5 */
#define PBPUEN          BYTE_REF(PBPUEN_ADDR)
#define PBPUEN_ADDR     0xfffff40a              /* B Pull-Up enable reg */
#define PB_CSD0_CAS0    0x10    /* Use CSD0/CAS0 as PB[4] */
#define PDSEL           BYTE_REF(PDSEL_ADDR)
#define PDSEL_ADDR      0xfffff41b              /* D Register */
#define PD_IRQ1         0x10    /* Use IRQ1 as PD[4] */
#define PF_A22          0x20    /* Use A22       as PF[5] */
#define PF_IRQ5         0x02    /* Use IRQ5      as PF[1] */
#define SPIMCONT        WORD_REF(SPIMCONT_ADDR)
#define SPIMCONT_ADDR   0xfffff802
#define SPIMCONT_SPIMEN         SPIMCONT_RSPIMEN

#elif defined(CONFIG_M68EZ328)
# include   /* bus, and irq definition of EZ */
#elif defined(CONFIG_M68VZ328)
# include
#else
# error "mc68328digi: a known machine not defined."
#endif

#include

/*----------------------------------------------------------------------------*/
/* Debug */

#if 0
#if 0
# define DPRINTK printk("%0: [%03d,%03d] ", __file__, \
                        current_pos.x, current_pos.y); \
                        printk
#else
# define DPRINTK ____
  static void ____(const * fmt, ...) {}
#endif
#endif
/*----------------------------------------------------------------------------*/

static const * __file__ = __FILE__;

/*----------------------------------------------------------------------------*//*
* Kernel compatibility.
* Kernel > 2.2.3, include/linux/version.h contain a macro for KERNEL_VERSION
*/
#include
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c)  (((a) << 16) + ((b) << 8) + (c))
#endif

/*
* Conditional compilation. LINUX_VERSION_CODE is the code of this version
* (as per KERNEL_VERSION)
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
#include     /* for put_user     */
#include      /* for polling fnct */
#endif

/*
* time limit
* Used for a whole conversion (x & y) and to clock the Burr-Brown to fall the
* BUSY signal down (otherwise, pen cannot generate irq anymore).
* If this limit run out, an error is generated. In the first case, the driver
* could recover his normal state. In the second, the BUSY signal cannot be
* fallen down and the have to be reseted.
* This limit is defined as approximatively the double of the deglitch one.
*/
#define CONV_TIME_LIMIT    50      /* ms */

/*
* Length of the data structure
*/
#define DATA_LENGTH       sizeof(struct ts_pen_info)

/*
* Size of the buffer for the event queue
*/
#define TS_BUF_SIZE        32*DATA_LENGTH

/*
* Minor for the touch screen . Major is 10 because of the
* type of the driver.
*/
#define MC68328DIGI_MINOR    9

/*
* SPIMCONT fields for this app (not defined in asm/MC68EZ328.h).
*/
#define SPIMCONT_DATARATE ( 7 <#define SPIMCONT_BITCOUNT (15<< 0)                      /* 16 bits transfert  */

/*
* SPIMCONT used values.
*/
#define SPIMCONT_INIT (SPIMCONT_DATARATE    | \
                       SPIMCONT_ENABLE      | \
                       SPIMCONT_XCH      *0 | \
                       SPIMCONT_IRQ      *0 | \
                       SPIMCONT_IRQEN       | \
                       SPIMCONT_
 
 
mc68328digi.c解析3
/*----------------------------------------------------------------------------*/
/* Init & release functions --------------------------------------------------*/

static void init_ts_state(void) {
  DISABLE_SPIM_IRQ;      /* Disable SPIM interrupt */

  ts_drv_state = TS_DRV_IDLE;
  state_counter = 0;

  ENABLE_PEN_IRQ;        /* Enable interrupt IRQ5  */
}

static void init_ts_pen_s(void) {
  ts_pen.x = 0;ts_pen.y = 0;
  ts_pen.dx = 0;ts_pen.dy = 0;
  ts_pen.event = EV_PEN_UP;
  ts_pen.state &= 0;ts_pen.state |= ST_PEN_UP;

  ts_pen_prev.x = 0;ts_pen_prev.y = 0;
  ts_pen_prev.dx = 0;ts_pen_prev.dy = 0;
  ts_pen_prev.event = EV_PEN_UP;
  ts_pen_prev.state &= 0;ts_pen_prev.state |= ST_PEN_UP;
  
  new_pen_state = 0;
  ev_counter = 0;
  do_gettimeofday(&first_tick);
}

static void init_ts_timer(struct timer_list *timer) {
  init_timer(timer);
  timer->function = (void *)handle_timeout;
}

static void release_ts_timer(struct timer_list *timer) {
  del_timer(timer);
}

/*
* Set default s for the params of the driver.
*/
static void init_ts_settings(void) {
  current_params.version = MC68328DIGI_VERSION;
  current_params.version_req = 0;
  current_params.x_ratio_num = 1;
  current_params.x_ratio_den = 1;
  current_params.y_ratio_num = 1;
  current_params.y_ratio_den = 1;
  current_params.x_offset = 0;
  current_params.y_offset = 0;
  current_params.xy_swap = 0;
  current_params.x_min = 0;
  current_params.x_max = ;
  current_params.y_min = 0;
  current_params.y_max = ;
  current_params.mv_thrs = 50;
  current_params.follow_thrs = 5;    /* to eliminate the conversion noise */
  current_params.sample_ms = 20;
  current_params.deglitch_on = 1;
  current_params.event_queue_on = 1;
  sample_ticks = TICKS(current_params.sample_ms);
}

static void init_ts_hardware(void) {
  /* Set bit 2 of F for interrupt (IRQ5 for pen_irq)                 */
#if 0
  PENIRQ_PUEN |= PEN_MASK;     /* F as pull-up   */
#endif
  PENIRQ_DIR &= ~PEN_MASK;    /* PF1 as input                                 */
  PENIRQ_SEL &= ~PEN_MASK;    /* PF1 internal chip function is connected
          */
#if 0
  /* Set polarity of IRQ5 as low (interrupt occure when signal is low)    */
  ICR &= ~ICR_PENPOL;
#endif

  /* Init stuff for E. The 3 LSB are multiplexed with the SPIM       */
  PESEL &= ~PESEL_MASK;
  
  /* Set bit 4 of B for the CARD_SELECT signal of as . */
#ifndef CONFIG_XCOPILOT_BUGS
  CS_PUEN |= CS_MASK;       /* B as pull-up                           */
  CS_DIR  |= CS_MASK;       /* PB4 as                                */
  CS_DATA |= CS_MASK;       /* PB4 inactive (=high)                        */
  CS_SEL  |= CS_MASK;       /* PB4 as I/O not dedicated                    */
#endif
  
  /* Set bit 5 of F for the BUSY signal of as input          */
  PFPUEN |= BUSY_MASK;     /* F as pull-up                           */
  PFDIR  &= ~BUSY_MASK;    /* PF5 as input                                */
  PFSEL  |= BUSY_MASK;     /* PF5 I/O function pin is connected      */

#ifdef USE_PENIRQ_PULLUP
  /* Set bit 4 of D for the pen irq pull-up of as .    */
  PDDIR  |= PEN_MASK_2;    /* PD4 as                                */
  PDDATA |= PEN_MASK_2;    /* PD4 up (pull-up)                            */
  PDPUEN |= PEN_MASK_2;    /* PD4 as pull-up                              */
#ifndef CONFIG_XCOPILOT_BUGS
  PDSEL  |= PEN_MASK_2;    /* PD4 I/O function pin is connected      */
#endif
#endif
}


/*
* Reset bits used in each register to their default .
*/
static void release_ts_hardware(void) {
                          /* Register   default */
#ifndef CONFIG_XCOPILOT_BUGS
  PESEL  |= PESEL_MASK;   /* PESEL      0xFF          */
  CS_DIR  &= ~CS_MASK;    /* PBDIR      0x00          */
  CS_DATA &= ~CS_MASK;    /* PBDATA     0x00          */
  CS_PUEN |= CS_MASK;     /* PBPUEN     0xFF          */
  CS_SEL  |= CS_MASK;     /* PBSEL      0xFF      
 
 
mc68328digi.c解析4
/*----------------------------------------------------------------------------*/
/* flow functions ------------------------------------------------------------*/

/*
* Set timer irq to now + delay ticks.
*/
static void set_timer_irq(struct timer_list *timer,int delay) {
  del_timer(timer);
  timer->expires = jiffies + delay;
  add_timer(timer);
}

/*
* Set and SPIM parameters for transfer.
*/
static void set_SPIM_transfert(void) {
  /* drive I/O 1 and I/O 2 LOW to reverse bias the PENIRQ
   * of the
   */
#ifdef USE_PENIRQ_PULLUP
  PDDATA &= ~PEN_MASK_2;        /* Pull down I/0 2 of PENIRQ */
#endif
  PENIRQ_DIR  |= PEN_MASK;      /* I/O 1 of PENIRQ as */
  PENIRQ_DATA &= ~PEN_MASK;     /* Pull down I/O 2 of PENIRQ */

  /* Initiate selection and parameters for using the Burr-Brown
   * and the SPIM.
   */
  CARD_SELECT;          /* .     */
  SPIM_ENABLE;          /* Enable SPIM         */
  SPIM_INIT;            /* Set SPIM parameters */
}

/*
* Clock the Burr-Brown to fall the AD_BUSY.
* With a 'start' bit and PD1,PD0 = 00 to enable PENIRQ.
* Used for the first pen irq after booting. And when error occures during
* conversion that need an initialisation.
*/
static void fall_BUSY_enable_PENIRQ(void) {
  SET_SPIMDATA(SPIMDATA_NOP);
  SPIMCONT |= SPIMCONT_XCH;     /* initiate exchange */
}

/*
* Release transfer settings.
*/
static void release_SPIM_transfert(void) {
  SPIM_DISABLE;
  CARD_DESELECT;

#ifdef USE_PENIRQ_PULLUP
  PDDATA |= PEN_MASK_2;       /* Pull up I/0 2 of PENIRQ   */
#endif
  PENIRQ_DIR  &= ~PEN_MASK;   /* I/O 1 of PENIRQ as input  */
  PENIRQ_DATA |= PEN_MASK;    /* Pull down I/O 2 of PENIRQ */
}

/*
* Ask for an X conversion.
*/
static void ask_x_conv(void) {
  SET_SPIMDATA(SPIMDATA_ASKX);
  SPIMCONT |= SPIMCONT_XCH;     /* initiate exchange */
}

/*
* Ask for an Y conversion.
*/
static void ask_y_conv(void) {
  SET_SPIMDATA(SPIMDATA_ASKY);
  SPIMCONT |= SPIMCONT_XCH;     /* initiate exchange */
}

/*
* Get the X conversion. Re-enable the PENIRQ.
*/
static void read_x_conv(void) {
  current_pos.x = (SPIMDATA & 0x7FF8) >> 3;
  /*
   * The first clock is used to fall the BUSY and the following 15 clks
   * to transfert the 12 bits of the conversion, MSB first.
   * The result will then be in the SPIM register in the bits 14 to 3.
   */
  SET_SPIMDATA(SPIMDATA_NOP);   /* nop with a start bit to re-enable */
  SPIMCONT |= SPIMCONT_XCH;     /* the pen irq.                      */

  rescale_xpos(¤t_pos.x);
}

/*
* Get the Y result. Clock the Burr-Brown to fall the BUSY.
*/
static void read_y_conv(void) {
  current_pos.y = (SPIMDATA & 0x7FF8) >> 3;
  /* Same remark as upper. */
  SET_SPIMDATA(SPIMDATA_NULL);  /* No start bit to fall the BUSY without */
  SPIMCONT |= SPIMCONT_XCH;     /* initiating another BUSY...            */

  rescale_ypos(¤t_pos.y);
}

/*
* Get one from the queue buffer.
* AND the with 'TS_BUF_SIZE -1' to have the loopback
*/
static unsigned get__from_queue(void) {
  unsigned int result;

  result = queue->buf[queue->tail];
  queue->tail = (queue->tail + 1) & (TS_BUF_SIZE - 1);
  return result;
}

/*
* Write one event in the queue buffer.
* if there is place for an event = the cannot be just one event
* length after the queue.
*/
static void put_in_queue( *in, int len) {
  unsigned     = queue->;
  unsigned max = (queue->tail - len) & (TS_BUF_SIZE - 1);
  int i;
  
  if( != max)              /* There is place for the event */
    for(i=0;i      queue->buf[] = in[i];
      ++;
       &= (TS_BUF_SIZE - 1);
    }
  //else printk("%0: Queue is full !!!!\n", __file__);
  queue-> = ;
}

/*
* if queue is empty.
*/
static in int queue_empty(void) {
  return queue-> == queue->tail;
}

/*
* if the delta move of the pen is enough big to say that this is a really
* move and not due to noise.
*/
static int is_moving(void) {
  int threshold;
  int dx, dy;
  
  threshold=((ts_pen_prev.event & EV_PEN_MOVE) > 0 ?
             current_params.follow_thrs : current_params.mv_thrs);
  dx = current_pos.x-ts_pen_prev.x;
  dy = current_pos.y-ts_pen_prev.y;
  if(dx < 0) dx = -dx; /* abs() */
  if(dy < 0) dy = -dy;
  return (dx > threshold ? 1 :
   (dy > threshold ? 1 : 0));
}

static void copy_info(void) {
  ts_pen_prev.x = ts_pen.x;
  ts_pen_prev.y = ts_pen.y;
  ts_pen_prev.dx = ts_pen.dx;
  ts_pen_prev.dy = ts_pen.dy;
  ts_pen_prev.event = ts_pen.event;
  ts_pen_prev.state = ts_pen.state;
  ts_pen_prev.ev_no = ts_pen.ev_no;
  ts_pen_prev.ev_time = ts_pen.ev_time;
}

unsigned set_ev_time(void) {
  struct timeval now;

  do_gettimeofday(&now);
  return (now.tv_sec -first_tick.tv_sec )* +
         (now.tv_usec-first_tick.tv_usec)/;
}

static void cause_event(int conv) {

  (conv) {

  case CONV_ERROR: /* error occure during conversion */
    ts_pen.state &= 0;            /* clear */
    ts_pen.state |= ST_PEN_UP;    /* recover a stable state for the drv.      */
    ts_pen.state |= ST_PEN_ERROR; /* tell that the state is due to an error   */
    ts_
 
 
mc68328digi.c解析5
中断函数,一个是触摸屏中断,另一个是timer

/*----------------------------------------------------------------------------*/
/* Interrupt functions -------------------------------------------------------*/

/*
* pen irq.
*/
static void handle_pen_irq(int irq, void *dev_id, struct pt_regs *regs) {
  unsigned flags;
  int bypass_initial_timer = 0;

  /* if unwanted interrupts come, trash them */
  if(!device_open)
    return;

  PENIRQ_DATA &= ~PEN_MASK;

#ifdef CONFIG_XCOPILOT_BUGS
  if(IMR&IMR_MPEN) {
    return;
  }
#endif
  save_flags(flags);     /* disable interrupts */
  cli();

  (ts_drv_state) {

  case TS_DRV_IDLE:  
    DISABLE_PEN_IRQ;
    ts_drv_state++;      /* update driver state */
    if(current_params.deglitch_on)
      set_timer_irq(&ts_wake_time,sample_ticks);
    else
      bypass_initial_timer = 1;
    break;

  default: /* Error */
    /* PENIRQ is enable then just init the driver
     * Its not necessary to pull down the busy signal
     */
    init_ts_state();
    break;
  }    
  restore_flags(flags);  /* Enable interrupts */

  /* if deglitching is off, we haven't started the deglitching timeout
   * so we do the inital sampling immediately:
   */
  if(bypass_initial_timer)
    handle_timeout();
}

/*
* timer irq.
*/
static void handle_timeout(void) {
  unsigned flags;

  /* if unwanted interrupts come, trash them */
  if(!device_open)
    return;
  
  save_flags(flags);     /* disable interrupts */
  cli();
  
  (ts_drv_state) {

  case TS_DRV_IDLE:  /* Error in the idle state of the driver */
    printk("%s: Error in the idle state of the driver\n", __file__);
    goto treat_error;
  case TS_DRV_ASKX:  /* Error in release SPIM   */
    printk("%s: Error in release SPIM\n", __file__);
    goto treat_error;
  case TS_DRV_ASKY:  /* Error in ask Y coord    */
    printk("%s: Error in ask Y coord\n", __file__);
    goto treat_error;
  case TS_DRV_READX: /* Error in read X coord   */
    printk("%s: Error in read X coord\n", __file__);
    goto treat_error;
  case TS_DRV_READY: /* Error in read Y coord   */
    printk("%s: Error in read Y coord\n", __file__);
    goto treat_error;
  treat_error:
    /* Force re-enable of PENIRQ because of an abnormal termination. Limit this
     * initialization with a timer to define a more FATAL error ...
     */
    set_timer_irq(&ts_wake_time,TICKS(CONV_TIME_LIMIT));
    ts_drv_state = TS_DRV_ERROR;
    CLEAR_SPIM_IRQ;              /* Consume residual irq from spim */
    ENABLE_SPIM_IRQ;
    fall_BUSY_enable_PENIRQ();
    break;
    
  case TS_DRV_WAIT:
    if(state_counter) {
      cause_event(CONV_LOOP);  /* after one loop */
    }
    if(IS_PEN_DOWN) {
      if(state_counter < 2) state_counter++;
      set_timer_irq(&ts_wake_time,TICKS(CONV_TIME_LIMIT));
      set_SPIM_transfert();
      ts_drv_state++;
      CLEAR_SPIM_IRQ;            /* Consume residual irq from spim */
      ENABLE_SPIM_IRQ;
      fall_BUSY_enable_PENIRQ();
    }
    else {
      if(state_counter) {
#ifdef CONFIG_XCOPILOT_BUGS
/* on xcopilot, we read the last mouse position now, because we
  * missed the moves, during which the position should have been
  * read
  */
xcopilot_read_now();
#endif
cause_event(CONV_END);
      }
      init_ts_state();
    }
    break;
    
  case TS_DRV_ERROR:  /* Busy doesn't want to fall down -> reboot? */
    panic(__FILE__": cannot fall pull BUSY signal\n");

  default:
    init_ts_state();
  }
  restore_flags(flags);  /* Enable interrupts */

#ifdef CONFIG_XCOPILOT_BUGS
  if (ts_drv_state==TS_DRV_ASKX) {
    handle_spi_irq();
  }
  PENIRQ_DATA |= PEN_MASK;
#endif
}

/*
* spi irq.
*/
static void handle_spi_irq(void) {
  unsigned flags;

  /* if unwanted interrupts come, trash them */
  if(!device_open)
    return;
  
  save_flags(flags);     /* disable interrupts */
  cli();
  
  CLEAR_SPIM_IRQ;       /* clear source of interrupt */

  (ts_drv_state) {


  case TS_DRV_ASKX:
#ifdef CONFIG_XCOPILOT_BUGS
    /* for xcopilot we bypass all SPI interrupt-related stuff
     * and read the pos immediately
     */
    xcopilot_read_now();
    ts_drv_state = TS_DRV_WAIT;
    DISABLE_SPIM_IRQ;
    release_SPIM_transfert();
    set_timer_irq(&ts_wake_time,sample_ticks);
    break;
#endif
    if(IS_BUSY_ENDED) {     /* If first BUSY down, then */
      ask_x_conv();         /* go on with conversion    */
      ts_drv_state++;
    }
    else  fall_BUSY_enable_PENIRQ();  /* else, re-loop  */
    break;

  case TS_DRV_ASKY:
    ask_y_conv();
    ts_drv_state++;
 
mc68328digi.c解析6
fops的定义,这个不用解释了吧

/*----------------------------------------------------------------------------*/
/* file_operations functions -------------------------------------------------*/

/*
* Called whenever a attempts to read the it has already open.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
static ssize_t ts_read (struct file *file,
                         *buff,       /* the buffer to fill with data */
   size_t len,       /* the length of the buffer.    */
   loff_t *offset) { /* Our offset in the file       */
#else
static int ts_read(struct inode *inode, struct file *file,
      *buff,            /* the buffer to fill with data */
     int len) {             /* the length of the buffer.    */
                            /* NO WRITE beyond that !!!     */
#endif

   *p_buffer;
   c;
  int  i;
  int  err;

  while(!new_pen_state) {    /* noting to read */
    if(file->f_flags & O_NONBLOCK)
      return -EAGAIN;
    interruptible_sleep_on(&queue->proc_list);

#if (LINUX_VERSION_CODE >= 0x020100)
    if(signal_pending(current))
#else
    if(current->signal & ~current->blocked)  /* a signal arrived */
#endif
      return -ERESTARTSYS;    /* tell the fs layer to handle it  */
    /* otherwise loop */
  }

  /* Force length to the one available */
  len = DATA_LENGTH;

  /* verify that the user space address to write is valid */
  err = verify_area(VERIFY_WRITE,buff,len);
  if(err) return err;

  /*
   * The events are anyway put in the queue.
   * But the read could choose to get the last event or the next in queue.
   * This decision is controlled through the ioctl function.
   * When the read doesn't flush the queue, this last is always full.
   */
  if(current_params.event_queue_on) {
    i = len;
    while((i>0) && !queue_empty()) {
      c = get__from_queue();
      put_user(c,buff++);
      i--;
    }
  }
  else {
    p_buffer = ( *)&ts_pen;
    for(i=len-1;i>=0;i--)   put_user(p_buffer[i],buff+i);
/*    for(i=0;i  }

  if(queue_empty() || !current_params.event_queue_on)
    new_pen_state = 0;  /* queue events fully consumned */
  else
    new_pen_state = 1;  /* queue not empty              */

  if(len-i) {
      file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* nfi */
      return len-i;
  }

  return 0;

}

/*
* file operation seems to become poll since version 2.2 of the kernel
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
static unsigned int ts_poll(struct file *file, poll_table *table) {

  poll_wait(file,&queue->proc_list,table);
  if (new_pen_state)
    return POLLIN | POLLRDNORM;
  return 0;

}
#else
static int ts_(struct inode *inode, struct file *file,
                     int mode, _table *table) {
  if(mode != SEL_IN)
    return 0;                   /* the is readable */
  if(new_pen_state)
    return 1;                   /* there is some new stuff to read */
  _wait(&queue->proc_list,table);    /* wait for data */
  return 0;
}
#endif

/*
* Called whenever a attempts to do an ioctl to the it has
* already open.
*/
static int ts_ioctl(struct inode *inode, struct file *file,
      unsigned int cmd,     /* the command to the ioctl */
      unsigned arg) {  /* the parameter to it      */

  struct ts_drv_params  new_params;
  int  err;
  int  i;
   *p_in;
   *p_out;

  (cmd) {
  
  case TS_PARAMS_GET:  /* Get internal params. First check if user space area
                        * to write is valid.
   */
    err = verify_area(VERIFY_WRITE,( *)arg,sizeof(current_params));
    if(err) return err;
    p_in  = ( *)¤t_params;
    p_out = ( *)arg;
    for(i=0;i      put_user(p_in[i],p_out+i);
    return 0;

  case TS_PARAMS_SET:  /* Set internal params. First check if user space area
                        * to read is valid.
   */
    err = verify_area(VERIFY_READ,( *)arg,sizeof(new_params));
    if(err) {
      return err;
    }

    /* ok */
    p_in  = ( *)&new_params;
    p_out = ( *)arg;
    for(i=0;i#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
      get_user(p_in[i],p_out+i);
#else
      p_in[i] = get_user(p_out+i);
#endif
    }
    
    /* All the params are set with minimal . */
    memcpy((void*)&
 
 
mc68328digi.c解析7
驱动注册,其他文中有详细介绍。

/*
* structure for driver registration.
*/
static struct mc68328_digi = {
  MC68328DIGI_MINOR,"mc68328 digitizer",&ts_fops
};

/*
* Initialize the driver.
*/
static int __init mc68328digi_init(void) {
  int err;

  printk("%s: MC68328DIGI touch screen driver\n", __file__);

  /* Register the driver */
  err = _register(&mc68328_digi);
  if(err<0)
    printk("%s: Error registering the \n", __file__);
  else
    printk("%s: register with : %s and : %d %d\n",
    __file__, mc68328_digi., MC68328DIGI_MAJOR, mc68328_digi.minor);

  /* Init prameters settings at boot time */
  init_ts_settings();

  return err;     /* A non zero means that init_module failed */
}

/*
* Cleanup - undid whatever mc68328digi_init did.
*/
void mc68328digi_cleanup(void) {
  /* Unregister driver */
  _deregister(&mc68328_digi);
}  

module_init(mc68328digi_init);
module_exit(mc68328digi_cleanup);
 
 
触摸屏驱动结束语
 触摸屏驱动结束语
by good02xaut

     触摸屏不是什么复杂的输入设备,它和键盘()的复杂度根本就不可同日而语。因此大家开发触摸屏驱动,是件十分轻松的事情。
    能否写出一个高效的触摸屏驱动,取决于对前面5点的认识。不论你使用哪种触摸屏,也不管是否使用,总之,只要能够提供给gui合适的Pen信息就是好的。
   顺便提一下GUI。目前minigui,qt都默认支持触摸屏作为输入设备,这和fb是一个道理。只有你的驱动是规范化的,才可以不加修改的应用到gui中。
      本介绍只是抛砖引玉,满足一般的需要。真正编写,最好多参考其他的源码。
阅读(1234) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~