SkyEye TouchScreen For S3C2410 驱动

/*skyeye_ts_drv.h*/

/*----------------------------------------------------------------------------*/
/* $Id: mc68328digi.h,v 1.1 2003/03/06 10:37:43 davidm Exp $
 *
 * linux/drivers/char/mc68328digi.h - Touch screen driver.
 *                                    Header file.
 *
 * Author: Philippe Ney <[email protected]>
 * Copyright (C) 2001 SMARTDATA <www.smartdata.ch>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software 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 device drivers" book.
 *   Ori Pomerantz for is "Linux Kernel Module Programming" guide.
 *
 * Updates:
 *   2001-03-12 Pascal bauermeister
 *              - Modified the semantics of ts_drv_params;
 *              - Added check for version in ioctl()
 *              - included a sample code in the end of this file as comment
 *
 *----------------------------------------------------------------------------
 * Known bugs:
 *   - read() returns zero
 *
 *----------------------------------------------------------------------------
 * HOW TO SETUP THE PARAMETERS FOR COORDINATES SCALING
 *
 * The driver does scaling as follows, in this order:
 *   o for x and y raw samples:
 *       - raw position is multiplied by ratio_num
 *       - result is divided by ratio_den
 *       - offset is added to the result
 *       - result is bounded by min and max
 *   o x and y results are swapped if xy_swap is non-zero
 *
 * Appropriate parameter combinations allow things like:
 *   o rotations in multiples of 90 degrees
 *   o coordinate mirroring
 *   o negative coordinates
 *   o etc.
 *
 * How to calculate the parameters:
 *   o Depending on your hardware, you may need to have x and y swapped. If
 *     so, set xy_swap to 1, always.
 *   o Calibration phase:
 *     choose two physical points, preferrably diagonally opposite corners of
 *     the touch panel. Measure one first time with num=den=1 and offset=0,
 *     i.e. do an ioctl() to set these parameters, and some read() to read
 *     these points.
 *
 *   o Let mx1 be the value measured at the left edge of your touchscreen
 *         mx2 be the value measured at the right edge of your touchscreen
 *         ux1 be the value you want your userland programs to see for mx1
 *         ux2 be the value you want your userland programs to see for mx2
 *   o For X, calculate:
 *         x_ratio_num = ux1 - ux2
 *         x_ratio_den = mx1 - mx2
 *         x_offset = ux1 - mx1*x_ratio_num/x_ratio_den  (or the same with x2)
 *   o Do the same for Y
 *   o Then set the min and max according to the bounds you desire for userland
 *     values
 *
 * Notes:
 *   o We assume linearity between the touchscreen coordinates space and the
 *     userland coordinate space
 *   o During calibration, the points may be any pair of points, but best
 *     results are obtained if they are as distant as possible
 * 
 *
 *----------------------------------------------------------------------------
 *
 * Event generated:
 *                __________      ______________
 *   1) /PENIRQ            |     |
 *                         -------
 *
 *                         |     |
 *                         |     +-> generate PEN_UP
 *                         |
 *                         +-------> generate PEN_DOWN
 *
 *                __________               ______________
 *   2) /PENIRQ            |              |
 *                         ------#####-----
 *
 *                         |     |||||    |
 *                         |     |||||    +-> generate PEN_UP
 *                         |     |||||
 *                         |     ||||+------> generate PEN_MOVE
 *                         |     |||+-------> generate PEN_MOVE
 *                         |     ||+--------> generate PEN_MOVE
 *                         |     |+---------> generate PEN_MOVE
 *                         |     +----------> generate PEN_MOVE
 *                         |
 *                         -----------------> generate PEN_DOWN
 *
 */
/*----------------------------------------------------------------------------*/

#ifndef _MC68328DIGI_H
#define _MC68328DIGI_H

#ifdef __KERNEL__
#include <linux/time.h>   /* for timeval struct */
#include <linux/ioctl.h>  /* for the _IOR macro to define the ioctl commands  */
#endif

/*----------------------------------------------------------------------------*/
/* Used to check the driver vs. userland program.
 *
 * If they have the same number, it means that 'struct ts_pen_info' and
 * 'struct ts_drv_params' have compatible semantics on both side. Otherwise
 * one side is outdated.
 *
 * The number has to be incremented if and only if updates in the driver lead
 * to any change in the semantics of these interface data structures (and not
 * if some internal mechanism of the driver are changed).
 */

#define MC68328DIGI_VERSION 2

/* Userland programs can get this version number in params.version after
 * ioctl(fd, TS_PARAMS_GET, &params). Userland programs must specify their
 * version compatibility by setting version_req to MC68328DIGI_VERSION before
 * doing  ioctl(fd, TS_PARAMS_SET, &params).
 *
 * If version_req does not match, ioctl(fd, TS_PARAMS_SET, &params) would return
 * EBADRQC ('Invalid request code').
 *
 * Note:
 *   it is not possible to check the compatibility for 'struct ts_pen_info'
 *   without doing an ioctl(fd, TS_PARAMS_SET, &params). So, please, do an
 *   ioctl(fd, TS_PARAMS_SET, &params)!
 */

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

/* Pen events */
#define EV_PEN_DOWN    0
#define EV_PEN_UP      1
#define EV_PEN_MOVE    2

/* Pen states */
/* defined through the 2 lsb of an integer variable. If an error occure,
 * the driver will recover the state PEN_UP and the error bit will be set.
 */
#define ST_PEN_DOWN    (0<<0)   /* bit 0 at 0 = the pen is down            */
#define ST_PEN_UP      (1<<0)   /* bit 0 at 1 = the pen is up              */
#define ST_PEN_ERROR   (1<<1)   /* bit 1 at 1 means that an error occure   */

/* Definition for the ioctl of the driver */
/* Device is type misc then major=10 */
#define MC68328DIGI_MAJOR  10

#define TS_PARAMS_GET _IOR(MC68328DIGI_MAJOR, 0, struct ts_drv_params)
#define TS_PARAMS_SET _IOR(MC68328DIGI_MAJOR, 1, struct ts_drv_params)
 
/*----------------------------------------------------------------------------*/

/* Available info from pen position and status */
struct ts_pen_info {
  int x,y;    /* pen position                                      */
  int dx,dy;  /* delta move from last position                     */
  int event;  /* event from pen (DOWN,UP,CLICK,MOVE)               */
  int state;  /* state of pen (DOWN,UP,ERROR)                      */
  int ev_no;  /* no of the event                                   */
  unsigned long ev_time;  /* time of the event (ms) since ts_open  */
};

/* Structure that define touch screen parameters */
struct ts_drv_params {
  int version;     /* version number for check, returned by TS_PARAMS_GET
            */
  int version_req; /* version number for check, that MUST be set to
            * MC68328DIGI_VERSION prior to do TS_PARAMS_SET,
            * or to -1 to bypass checking (do not do this usually)
            */
  int x_ratio_num; /*                        */
  int x_ratio_den; /*                        */
  int y_ratio_num; /*                        */
  int y_ratio_den; /*                        */
  int x_offset;    /*                        */
  int y_offset;    /*                        */
  int xy_swap;     /*                        */
  int x_min;       /*                        */
  int y_min;       /*                        */
  int x_max;       /*                        */
  int y_max;       /*                        */
  int mv_thrs;     /* minimum delta move to considere the pen start to move */
  int follow_thrs; /* minimum delta move to follow the pen move when the pen
                    * is moving
            */
  int sample_ms;   /* time between sampling (ms) */
  int deglitch_on; /* whether to filter glitches at pen down */
  int event_queue_on; /* switch on and off the event queue */
};


/*------------------------------------------------------------------------------
** The following sample code illustrates the usage of the ts driver:
**
** #include <string.h>
** #include <errno.h>
** #include <fcntl.h>
** #include <stdio.h>
** #include <linux/mc68328digi.h>
**
**
**
**
** const char *ts_device_name = "/dev/ts";
**
**
**
**
**
**
** void tsdrv_params_fprint(FILE *f, struct ts_drv_params *p)
** {
**   fprintf(f, "version     : %d/n",    p->version);
**   fprintf(f, "version_req : %d/n",    p->version_req);
**   fprintf(f, "x ratio     : %d/%d/n", p->x_ratio_num, p->x_ratio_den);
**   fprintf(f, "y ratio     : %d/%d/n", p->y_ratio_num, p->y_ratio_den);
**   fprintf(f, "x limits    : %d/%d/n", p->x_min, p->x_max);
**   fprintf(f, "y limits    : %d/%d/n", p->y_min, p->y_max);
**   fprintf(f, "x offset    : %d/n",    p->x_offset);
**   fprintf(f, "y offset    : %d/n",    p->y_offset);
**   fprintf(f, "invert xy   : %d/n",    p->xy_swap);
**   fprintf(f, "mv thrs     : %d/n",    p->mv_thrs);
**   fprintf(f, "follow thrs : %d/n",    p->follow_thrs);
**   fprintf(f, "sample ms   : %d/n",    p->sample_ms);
**   fprintf(f, "dglitch on  : %d/n",    p->deglitch_on);
**   fprintf(f, "evt Q on    : %d/n",    p->event_queue_on);
** }
**
**
** int tsdrv_init(int argc, char *argv[])
** {
**   int ts_fd;
**   int err;
**   struct ts_drv_params  drv_params;
**   int mx1, mx2, my1, my2;
**   int ux1, ux2, uy1, uy2;
**
**   ts_fd = open(ts_device_name, O_RDWR);
**   if(ts_fd==0) {
**     fprintf(stderr, "%s: error: could not open device %s/n",
**         argv[0], ts_device_name);
**     return 0;
**   }
**
**   err = ioctl(ts_fd, TS_PARAMS_GET, &drv_params);
**   if(err)  {
**     fprintf(stderr, "%s: ioctl TS_PARAMS_GET error: %s/n",
**         argv[0], strerror(errno));
**     close(ts_fd);
**     return 0;
**   }
**
**   printf("/nDefault driver settings:/n");
**   tsdrv_params_fprint(stdout, &drv_params);
**
**
**   drv_params.version_req    = MC68328DIGI_VERSION;
**   drv_params.event_queue_on = 1;
**   drv_params.deglitch_on    = 0;
**   drv_params.sample_ms      = 10;
**   drv_params.follow_thrs    = 0;
**   drv_params.mv_thrs        = 2;
**   drv_params.y_max          = 159 + 66;
**   drv_params.y_min          = 0;
**   drv_params.x_max          = 159;
**   drv_params.x_min          = 0;
**   drv_params.xy_swap        = 0;
**
** 
** 
**   mx1 = 508; ux1 =   0;
**   my1 = 508; uy1 =   0;
**   mx2 = 188; ux2 = 159;
**   my2 = 188; uy2 = 159;
**
** 
**   drv_params.x_ratio_num    = ux1 - ux2;
**   drv_params.x_ratio_den    = mx1 - mx2;
**   drv_params.x_offset       =
**     ux1 - mx1 * drv_params.x_ratio_num / drv_params.x_ratio_den;
**
**   drv_params.y_ratio_num    = uy1 - uy2;
**   drv_params.y_ratio_den    = my1 - my2;
**   drv_params.y_offset       =
**     uy1 - my1 * drv_params.y_ratio_num / drv_params.y_ratio_den;
**
**
**   printf("/nNew driver settings:/n");
**   tsdrv_params_fprint(stdout, &drv_params);
**
**   err = ioctl(ts_fd, TS_PARAMS_SET, &drv_params);
**   if(err)  {
**     fprintf(stderr, "%s: ioctl TS_PARAMS_SET error: %s/n",
**         argv[0], strerror(errno));
**     close(ts_fd);
**     return 0;
**   }
**
**   return ts_fd;
** }
**
**
**
**
**
** main(int argc, char *argv[])
** {
**   int                   ts_fd = 0;
**   struct ts_pen_info    pen_info;
**   int                   bytes_transfered = 0;
**
**
**   ts_fd = tsdrv_init(argc, argv);
**   if(ts_fd==0)
**     exit(1);
**
**
**   while(1) {
**     char whats_up;
**     int x, y;
**     bytes_transfered = read(ts_fd, (void*)&pen_info, sizeof(pen_info));
**     x = pen_info.x;
**     y = pen_info.y;
**
**     switch(pen_info.event) {
**
**     case EV_PEN_UP:
**       whats_up = 'U';
**       break;
**
**     case EV_PEN_DOWN:
**       whats_up = 'D';
**       break;
**
**     case EV_PEN_MOVE:
**       whats_up = 'M';
**       break;
**
**     default:
**       whats_up = '?';
**     }
**
**     printf("%c(%i,%i) ", whats_up, x, y);
**     printf("ev_no:%d ", pen_info.ev_no);
**     printf("ev_usec:%ld ", pen_info.ev_time);
**     printf("bytes_transfered:%d ", bytes_transfered);
**     printf("/n");
**   }
**
** 
**   close(ts_fd);
**   exit(0);
** }
**
**
**
** <<< End of example ----------------------------------------------------------
*/


#endif /* _MC68328DIGI_H */

 

 

 

 

/*skyeye_ts_drv.c*/

/*

 *

 * linux/drivers/char/skyeye_ts.c - Touch screen driver for touch screen simulated on skyeye.

*/



#include <linux/kernel.h>     /* We're doing kernel work */

#include <linux/module.h>     /* Specifically, a module */

#include <linux/interrupt.h>  /* We want interrupts */



#include <linux/miscdevice.h> /* for misc_register() and misc_deregister() */



#include <linux/fs.h>         /* for struct 'file_operations' */



#include <linux/timer.h>     /* for timeout interrupts */

#include <linux/param.h>     /* for HZ. HZ = 100 and the timer step is 1/100 */

#include <linux/sched.h>     /* for jiffies definition. jiffies is incremented

                              * once for each clock tick; thus it's incremented

                  * HZ times per secondes.*/

#include <linux/mm.h>        /* for verify_area */

#include <linux/slab.h>    /* for kmalloc */

#include <linux/init.h>



#include <asm/irq.h>         /* For IRQ_MACHSPEC */



#include <linux/version.h>



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







//#include "mc68328digi.h" //struct ts_pen_info was defined in this head file

#include "skyeye_ts_drv.h"

//#include "ep7312_sys.h"

//#include "clps7110.h"



#define IRQ_FLG_STD   (0x8000)        /* internally used              */



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



/* Available info from pen position and status */



//struct ts_pen_info {

  //int x,y;    /* pen position                                      */

  //int dx,dy;  /* delta move from last position                     */

  //int event;  /* event from pen (DOWN,UP,CLICK,MOVE)               */

  //int state;  /* state of pen (DOWN,UP,ERROR)                      */

  //int ev_no;  /* no of the event                                   */

  //unsigned long ev_time;  /* time of the event (ms) since ts_open  */

//};





static const char* __file__ = __FILE__;



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

 * Kernel compatibility.

 * Kernel > 2.2.3, include/linux/version.h contain a macro for KERNEL_VERSION

 */

#include <linux/version.h>

#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 <asm/uaccess.h>    /* for put_user     */

#include <linux/poll.h>     /* for polling fnct */

#endif



/*

 * 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 number for the touch screen device. Major is 10 because of the misc

 * type of the driver.

 */

//#define MC68328DIGI_MINOR    9

#define SKYEYE_TS_MINOR    9

#define SKYEYE_TS_MAJOR    10







/*

 * ADS7843 fields (BURR-BROWN Touch Screen Controller).

 */

#define ADS7843_START_BIT (1<<7)

#define ADS7843_A2        (1<<6)

#define ADS7843_A1        (1<<5)

#define ADS7843_A0        (1<<4)

#define ADS7843_MODE      (1<<3)  /* HIGH = 8, LOW = 12 bits conversion */

#define ADS7843_SER_DFR   (1<<2)  /* LOW = differential mode            */

#define ADS7843_PD1       (1<<1)  /* PD1,PD0 = 11  PENIRQ disable       */

#define ADS7843_PD0       (1<<0)  /* PD1,PD0 = 00  PENIRQ enable        */



/*

 * Conversion status.

 */

#define CONV_ERROR      -1   /* error during conversion flow        */

#define CONV_END         0   /* conversion ended (= pen is up)      */

#define CONV_LOOP        1   /* conversion continue (= pen is down) */



/*

 * Useful masks.

 */

#define PEN_IRQ_NUM    SMDK2410_TS_IRQ

//#define PEN_IRQ_NUM    IRQ6_IRQ_NUM



//#define PENIRQ_DATA     PDDATA

//#define PENIRQ_PUEN    PDPUEN

//#define PENIRQ_DIR    PDDIR

//#define PENIRQ_SEL    PDSEL

//#define ICR_PENPOL    ICR_POL6

//#define IMR_MPEN    IMR_MIRQ6



/*

 * Touch screen driver states.

 */

#define TS_DRV_ERROR       -1

#define TS_DRV_IDLE         0

#define TS_DRV_WAIT         1

#define TS_DRV_ASKX         2

#define TS_DRV_ASKY         3

#define TS_DRV_READX        4

#define TS_DRV_READY        5



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

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

//#define ENABLE_PEN_IRQ    { INTSR &= ~EINT2; }

//#define DISABLE_PEN_IRQ   { INTSR |= EINT2; }



//#define ENABLE_PEN_IRQ    { INTSR &= ~EINT2; }

//#define DISABLE_PEN_IRQ   { INTSR |= EINT2; }
#define ENABLE_PEN_IRQ

#define DISABLE_PEN_IRQ

#include <asm/io.h>
#include "asm/arch/smdk2410.h"

#ifndef MOD_DEC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif

#ifndef MOD_INC_USE_COUNT
#define MOD_INC_USE_COUNT
#endif

static inline int __deprecated verify_area(int type, const void __user *
addr,
unsigned long size)
{
        return access_ok(type,addr,size) ? 0 : -EFAULT;
}


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





/* predefinitions ------------------------------------------------------------*/



static irqreturn_t handle_pen_irq(int irq, void *dev_id, struct pt_regs *regs);



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

/* structure -----------------------------------------------------------------*/



struct ts_pen_position { int x,y; };



struct ts_queue {

  unsigned long head;

  unsigned long tail;

  wait_queue_head_t proc_list;

  struct fasync_struct *fasync;

  unsigned char buf[TS_BUF_SIZE];

};



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

/* Variables -----------------------------------------------------------------*/



/*

 * driver state variable.

 */

static int ts_drv_state;



/*

 * first tick initiated at ts_open.

 */

static struct timeval   first_tick;



/*

 * pen state.

 */

static int new_pen_state;



/*

 * event counter.

 */

static int ev_counter;



/*

 * counter to differentiate a click from a move.

 */

static int state_counter;



static struct timer_list         ts_wake_time;



/*

 * drv parameters.

 */

static struct ts_drv_params      current_params;

static int                       sample_ticks;

/*

 * pen info variables.

 */

static struct ts_pen_info        ts_pen;

static struct ts_pen_info        ts_pen_prev;

//static struct ts_pen_position    current_pos;

static struct ts_pen_info    current_pos;

static struct ts_pen_info     *buff;



/*

 * driver management.

 */

static struct ts_queue *queue;

static int    device_open = 0;   /* number of open device to prevent concurrent

                                  * access to the same device

                  */





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

/* 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_values(void) {

 

}





/*

 * Set default values for the params of the driver.

 */

static void init_ts_settings(void) {

 

}



static void init_ts_hardware(void) {

}



static void init_ts_drv(void) {

 

}



static void release_ts_drv(void) {

 

}



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

/* xcopilot compatibility hacks ----------------------------------------------*/

#ifdef CONFIG_XCOPILOT_BUGS



/* xcopilot has the following bugs:

 *

 * - Disabling the penirq has no effect; we keep on getting irqs even when

 *   penirq is disabled; this is not too annoying: we just trash pen irq events

 *   that come when disabled.

 *

 * - SPIM interrupt is not simulated; this is not too annoying: we just read

 *   SPI data immediately and bypass a couple of states related to SPI irq.

 *

 * - We do not get mouse drag events; this is a known xcopilot bug.

 *   This is annoying: we miss all moves ! You should patch and recompile

 *   your xcopilot.

 *

 *   This has been reported as Debian bug #64367, and there is a patch there:

 *     http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=64367&repeatmerged=yes

 *   which is:

 *   +-----------------------------------------------------------------------+

 *   | --- display.c   Tue Aug 25 14:56:02 1998

 *   | +++ display2.c  Fri May 19 16:07:52 2000

 *   | @@ -439,7 +439,7 @@

 *   | static void HandleDigitizer(Widget w, XtPointer client_data, XEvent/

 *   | *event, Boolean *continue_to_dispatch)

 *   | {

 *   | -  Time start;

 *   | +  static Time start;

 *   |

 *   | *continue_to_dispatch = True;

 *   +-----------------------------------------------------------------------+

 *   In short, add 'static' to the declaration of 'start' in HandleDigitizer()

 *   in display.c

 *

 *   With this patch, all works perfectly !

 *

 * - The pen state can be read only in IPR, not in port D; again, we're doing

 *   the workaround.

 *

 * With all these workarounds in this file, and with the patch on xcopilot,

 * things go perfectly smoothly.

 *

 */



/*

 * Ugly stuff to read the mouse position on xcopilot

 *

 * We get the position of the last mouse up/down only, we get no moves !!! :-(

 */

 

static void xcopilot_read_now(void) {

    PFDATA &= ~0xf; PFDATA |= 6;

    SPIMCONT |= SPIMCONT_XCH | SPIMCONT_IRQEN;

    current_pos.x = SPIMDATA;

    rescale_xpos(&current_pos.x);



    PFDATA &= ~0xf; PFDATA |= 9;

    SPIMCONT |= SPIMCONT_XCH | SPIMCONT_IRQEN;

    current_pos.y = SPIMDATA;

    rescale_ypos(&current_pos.y);



    swap_xy(&current_pos.x, &current_pos.y);

}

#endif





//****** by ywc 2004-04-09 ***************

static void skyeye_read_now(void){

    long * TS_Buf_start_addr;

    

    TS_Buf_start_addr=vSMDK2410_TS_IO+0x300;

    current_pos.x=TS_Buf_start_addr[0];

    current_pos.y=TS_Buf_start_addr[1];

    current_pos.dx=0;

    current_pos.dy=0;

    current_pos.event=TS_Buf_start_addr[4];

    current_pos.state=0;

    current_pos.ev_no=TS_Buf_start_addr[6];//1--new data come,0--no new data--set by ts_read

    current_pos.ev_time=0;

//    printk("x=%d, y=%d, event=%d/n", current_pos.x, current_pos.y, current_pos.event);

//    printk("&TS_Buf_start_addr[3]==%p/n",&TS_Buf_start_addr[3]);

//    printk("ts driver: skyeye_read_now:");

//    printk("current_pos.x=%d,current_pos.y=%d/n/n",current_pos.x,current_pos.y);

}

//****** by ywc 2004-04-09 ***************





/*

 * Get one char from the queue buffer.

 * AND the head with 'TS_BUF_SIZE -1' to have the loopback

 */

static unsigned char get_char_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.

 * Test if there is place for an event = the head cannot be just one event

 * length after the queue.

 */

static void put_in_queue(char *in, int len) {

  unsigned long head    = queue->head;

  unsigned long maxhead = (queue->tail - len) & (TS_BUF_SIZE - 1);

  int i;

 

  if(head != maxhead)              /* There is place for the event */

    for(i=0;i<len;i++) {

      queue->buf[head] = in[i];

      head++;

      head &= (TS_BUF_SIZE - 1);

    }

  //else printk("%0: Queue is full !!!!/n", __file__);

  queue->head = head;

}



/*

 * Test if queue is empty.

 */

static inline int queue_empty(void) {

  return queue->head == queue->tail;

}



/*

 * Test 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;

}



static void cause_event(int conv) {



  switch(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_pen.event = EV_PEN_UP;     /* event = pen go to up */

    ts_pen.x = ts_pen_prev.x;     /* get previous coord as current to by-pass */

    ts_pen.y = ts_pen_prev.y;     /* possible errors                          */

    ts_pen.dx = 0;

    ts_pen.dy = 0;

    ts_pen.ev_no = ev_counter++;    /* get no of event   */

    //ts_pen.ev_time = set_ev_time(); /* get time of event */

    copy_info();                    /* remember info */

    if(current_params.event_queue_on)

      put_in_queue((char *)&ts_pen,DATA_LENGTH);  /* queue event */



    /* tell user space progs that a new state occure */

    new_pen_state = 1;

    /* signal asynchronous readers that data arrives */

    if(queue->fasync)

      kill_fasync(&queue->fasync, SIGIO, POLL_IN);

    wake_up_interruptible(&queue->proc_list);  /* tell user space progs */

    break;

   

  case CONV_LOOP:  /* pen is down, conversion continue */   

    ts_pen.state &= 0;          /* clear */

    ts_pen.state &= ST_PEN_DOWN;



    switch(state_counter) {       /* It could be a move   */

       

    case 1:    /* the pen just went down, it's a new state */

      ts_pen.x = current_pos.x;

      ts_pen.y = current_pos.y;

      ts_pen.dx = 0;

      ts_pen.dy = 0;

      ts_pen.event = EV_PEN_DOWN;      /* event = pen go to down */

      ts_pen.ev_no = ev_counter++;     /* get no of event        */

      //ts_pen.ev_time = set_ev_time();  /* get time of event */

      copy_info();                     /* remember info */

      if(current_params.event_queue_on)

        put_in_queue((char *)&ts_pen,DATA_LENGTH);  /* queue event */



      /* tell user space progs that a new state occure */

      new_pen_state = 1;

      /* signal asynchronous readers that data arrives */

      if(queue->fasync)

        kill_fasync(&queue->fasync, SIGIO, POLL_IN);

      wake_up_interruptible(&queue->proc_list);  /* tell user space progs */

      break;



    case 2:    /* the pen is down for at least 2 loop of the state machine */



      if(is_moving()) {

        ts_pen.event = EV_PEN_MOVE;

        ts_pen.x = current_pos.x;

        ts_pen.y = current_pos.y;

        ts_pen.dx = ts_pen.x - ts_pen_prev.x;

        ts_pen.dy = ts_pen.y - ts_pen_prev.y;

        ts_pen.ev_no = ev_counter++;    /* get no of event   */

       // ts_pen.ev_time = set_ev_time(); /* get time of event */

        copy_info();                    /* remember info */

        if(current_params.event_queue_on)

          put_in_queue((char *)&ts_pen,DATA_LENGTH);  /* queue event */



        /*

     * pen is moving, then it's anyway a good reason to tell it to

     * user space progs

     */

        new_pen_state = 1;

        /* signal asynchronous readers that data arrives */

        if(queue->fasync)

          kill_fasync(&queue->fasync, SIGIO, POLL_IN);

        wake_up_interruptible(&queue->proc_list);

      }

      else {

        if(ts_pen_prev.event == EV_PEN_MOVE)  /* if previous state was moving */

      ts_pen.event = EV_PEN_MOVE;         /* -> keep it!                  */

        ts_pen.x = ts_pen_prev.x;       /* No coord passing to     */

        ts_pen.y = ts_pen_prev.y;       /* avoid Parkinson effects */

        ts_pen.dx = 0;

    ts_pen.dy = 0;  /* no wake-up interruptible because nothing new */

        copy_info();    /* remember info */

      }

      break;

    }

    break;



  case CONV_END:   /* pen is up, conversion ends */

    ts_pen.state &= 0;         /* clear */

    ts_pen.state |= ST_PEN_UP;

    ts_pen.event = EV_PEN_UP;

    ts_pen.x = current_pos.x;

    ts_pen.y = current_pos.y;

    ts_pen.dx = ts_pen.x - ts_pen_prev.x;

    ts_pen.dy = ts_pen.y - ts_pen_prev.y;

  //  ts_pen.ev_time = set_ev_time();  /* get time of event */

    ts_pen.ev_no = ev_counter++;     /* get no of event   */

    copy_info();                     /* remember info */

    if(current_params.event_queue_on)

      put_in_queue((char *)&ts_pen,DATA_LENGTH);  /* queue event */



    /* tell user space progs that a new state occure */

    new_pen_state = 1;

    /* signal asynchronous readers that data arrives */

    if(queue->fasync)

      kill_fasync(&queue->fasync, SIGIO, POLL_IN);

    wake_up_interruptible(&queue->proc_list);

    break;

  }

}





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

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



/*

 * pen irq.

 */

static irqreturn_t handle_pen_irq(int irq, void *dev_id, struct pt_regs *regs) {

  unsigned long flags;

  int bypass_initial_timer = 0;



  /* if unwanted interrupts come, trash them */

  if(!device_open)

    return;



  //PENIRQ_DATA &= ~PEN_MASK;

/* - Disabling the penirq has no effect; we keep on getting irqs even when

 *   penirq is disabled; this is not too annoying: we just trash pen irq events

 *   that come when disabled.

 */

  // printk("enter the pen irq ,INTSR=%x",INTSR);

//   if(!(INTSR&EINT2)) {

//    return;

//  }



  save_flags(flags);     /* disable interrupts */

  cli();

 

  skyeye_read_now(); //ywc 2004-04-11



  switch(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;

  }

  DISABLE_PEN_IRQ;  

  restore_flags(flags);  /* Enable interrupts */

 

   return IRQ_HANDLED;

}



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

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



/*

 * Called whenever a process attempts to read the device it has already open.

 */

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

static ssize_t ts_read (struct file *file,

                        char *buff0,       /* 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,

           char *buff0,            /* the buffer to fill with data */

           int len) {             /* the length of the buffer.    */

                                  /* NO WRITE beyond that !!!     */

#endif



  char *p_buffer;

  char c;

  int  i;

  int  err;

  int  havedata=0;

  // printk("!!!enter ts_read/n");

 /*

  while(!current_pos.state) {   //no new data come

    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,buff0,len);

  if (err) {

    printk("verify_area error ,err=%d",err);

    return err;

  }

 

  /*

  for(i=0;i<len;i++){

  buff[i]=*((char *)&current_pos+i);

  }

  */

    //another copy way???

   // struct ts_pen_info * buff;

 if(current_pos.ev_no==1){

  buff=(struct ts_pen_info *)buff0;

  buff->x = current_pos.x;

  buff->y = current_pos.y;

  buff->dx = current_pos.dx;

  buff->dy = current_pos.dy;

  buff->event = current_pos.event;

  buff->state = current_pos.state;

  buff->ev_no = current_pos.ev_no;

  buff->ev_time = current_pos.ev_time;

 

//  printk("current_pos.x=%d,buff->x=%d/n",current_pos.x,buff->x);

//  printk("current_pos.y=%d,buff->y=%d/n",current_pos.y,buff->y);

 

  havedata=1;

  }



  current_pos.ev_no=0;// ywc 2004-04-12 data have been read ,set it to 0

//  printk("!!!leave ts_read/n");

  return havedata;



}



           

/*

 * Called whenever a process attempts to open the device file.

 */

static int ts_open(struct inode *inode, struct file *file) {

     int err;

  /* To talk to only one device at a time */

  printk("!!!enter ts_open/n");

  if(device_open) return -EBUSY;



  /* IRQ registration have to be done before the hardware is instructed to

   * generate interruptions.

   */

  /* IRQ for pen */

  err = request_irq(PEN_IRQ_NUM, handle_pen_irq,

            IRQ_FLG_STD,"touch_screen",NULL);

  if(err) {

    printk("%s: Error. Cannot attach IRQ %d to touch screen device/n",

       __file__, PEN_IRQ_NUM);

    return err;

  }

  printk("%s: IRQ %d attached to touch screen/n",

     __file__, PEN_IRQ_NUM);



  init_ts_drv();

 

  printk("skyeye touch screen driver: init hardware done../n/n");



  /* Init the queue */

  queue = (struct ts_queue *) kmalloc(sizeof(*queue),GFP_KERNEL);

  memset(queue,0,sizeof(*queue));

  queue->head = queue->tail = 0;

  init_waitqueue_head(&queue->proc_list);



  /* Increment the usage count (number of opened references to the module)

   * to be sure the module couldn't be removed while the file is open.

   */

  MOD_INC_USE_COUNT;



  /* And my own counter too */

  device_open++;

  //ts_read(0,0,0,0);

  //file->f_op->read(1,1,1,1);

 

  return 0;  /* open succeed */

}



#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

static int ts_fasync(int inode, struct file *file, int mode) {

#else

static int ts_fasync(struct inode *inode, struct file *file, int mode) {

#endif

 

  int retval;

 

  retval = fasync_helper(inode,file,mode,&queue->fasync);

  if(retval < 0)

    return retval;

   

  return 0;

 

}



/*

 * Called whenever a process attempts to close the device file.

 */

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

static int ts_release(struct inode *inode, struct file *file) {

#else

static void ts_release(struct inode *inode, struct file *file) {

#endif



  /* Remove this file from the asynchronously notified file's */

  ts_fasync(inode,file,0);



  /* Release hardware */

  release_ts_drv();



  /* Free the allocated memory for the queue */

  kfree(queue);



  /* IRQ have to be freed after the hardware is instructed not to interrupt

   * the processor any more.

   */

  //free_irq(SPI_IRQ_NUM,NULL);

  free_irq(PEN_IRQ_NUM,NULL);



  /* Decrement the usage count, otherwise once the file is open we'll never

   * get rid of the module.

   */

  MOD_DEC_USE_COUNT;



  /* And my own counter too */

  device_open--;



#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

  return 0;

#endif

}





static struct file_operations ts_fops = {

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0))

  owner:          THIS_MODULE,

  read:           ts_read,

  //poll:           ts_poll,

  //ioctl:          ts_ioctl,

  open:           ts_open,

  release:        ts_release,

  fasync:         ts_fasync,

#else



  NULL,       /* lseek              */

  ts_read,    /* read               */

  NULL,       /* write              */

  NULL,       /* readdir            */

#  if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

  ts_poll,    /* poll               */

#  else

  ts_select,  /* select             */

#  endif

  ts_ioctl,   /* ioctl              */

  NULL,       /* mmap               */

  ts_open,    /* open               */

#  if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))

  NULL,       /* flush              */

#  endif

  ts_release, /* release (close)    */

  NULL,       /* fsync              */

  ts_fasync   /* async notification */



#endif

};



/*

 * miscdevice structure for misc driver registration.

 */

static struct miscdevice mc68328_digi = {

//  MC68328DIGI_MINOR,"mc68328 digitizer",&ts_fops

  SKYEYE_TS_MINOR,"skyeye touch screen",&ts_fops

};



/*

 * Initialize the driver.

 */

static int __init mc68328digi_init(void) {

  int err;

 

  int ts;



  printk("%s: SkyEye touch screen driver initialize/n", __file__);



  /* Register the misc device driver */

  err = misc_register(&mc68328_digi);

 if(err<0)

    printk("%s: Error registering the device/n", __file__); 

 else

    printk("%s: Device register with name: %s and number: %d %d/n",

       __file__, mc68328_digi.name, SKYEYE_TS_MAJOR, mc68328_digi.minor);



  /* Init prameters settings at boot time */

  init_ts_settings();

//*******************  test by ywc 2004-3-27 *****************

     /*

     * open up the touch-panel device.

     * Return the fd if successful, or negative if unsuccessful.

     */

/*  opened by application on ts_driver!

    ts = ts_open("/dev/ts", O_NONBLOCK | O_RDWR);

    if (ts < 0) {

        printk("Error opening touch panel/n");

        return ts;

    }

*/

//*******************  test by ywc 2004-3-27 *****************





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

}



/*

 * Cleanup - undid whatever mc68328digi_init did.

 */

void mc68328digi_cleanup(void) {

  /* Unregister device driver */

  misc_deregister(&mc68328_digi);





module_init(mc68328digi_init);

module_exit(mc68328digi_cleanup);

你可能感兴趣的:(SkyEye TouchScreen For S3C2410 驱动)