Logo Search packages:      
Sourcecode: linux version File versions  Download package

h3600_ts_input.c

/*
 * $Id: h3600_ts_input.c,v 1.4 2002/01/23 06:39:37 jsimmons Exp $
 *
 *  Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com
 *
 *  Sponsored by Transvirtual Technology.
 *
 *  Derived from the code in h3600_ts.[ch] by Charles Flynn
 */

/*
 * Driver for the h3600 Touch Screen and other Atmel controlled devices.
 */

/*
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Should you need to contact me, the author, you can do so by
 * e-mail - mail your message to <jsimmons@transvirtual.com>.
 */

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/delay.h>

/* SA1100 serial defines */
#include <asm/arch/hardware.h>
#include <asm/arch/irqs.h>

#define DRIVER_DESC     "H3600 touchscreen driver"

MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

/*
 * Definitions & global arrays.
 */

/* The start and end of frame characters SOF and EOF */
#define CHAR_SOF                0x02
#define CHAR_EOF                0x03
#define FRAME_OVERHEAD          3       /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */

/*
        Atmel events and response IDs contained in frame.
        Programmer has no control over these numbers.
        TODO there are holes - specifically  1,7,0x0a
*/
#define VERSION_ID              0       /* Get Version (request/respose) */
#define KEYBD_ID                2       /* Keyboard (event) */
#define TOUCHS_ID               3       /* Touch Screen (event)*/
#define EEPROM_READ_ID          4       /* (request/response) */
#define EEPROM_WRITE_ID         5       /* (request/response) */
#define THERMAL_ID              6       /* (request/response) */
#define NOTIFY_LED_ID           8       /* (request/response) */
#define BATTERY_ID              9       /* (request/response) */
#define SPI_READ_ID             0x0b    /* ( request/response) */
#define SPI_WRITE_ID            0x0c    /* ( request/response) */
#define FLITE_ID                0x0d    /* backlight ( request/response) */
#define STX_ID                  0xa1    /* extension pack status (req/resp) */

#define MAX_ID                  14

#define H3600_MAX_LENGTH 16
#define H3600_KEY 0xf

#define H3600_SCANCODE_RECORD 1      /* 1 -> record button */
#define H3600_SCANCODE_CALENDAR 2    /* 2 -> calendar */
#define H3600_SCANCODE_CONTACTS 3    /* 3 -> contact */
#define H3600_SCANCODE_Q      4      /* 4 -> Q button */
#define     H3600_SCANCODE_START    5      /* 5 -> start menu */
#define     H3600_SCANCODE_UP 6      /* 6 -> up */
#define H3600_SCANCODE_RIGHT  7      /* 7 -> right */
#define H3600_SCANCODE_LEFT   8      /* 8 -> left */
#define H3600_SCANCODE_DOWN   9      /* 9 -> down */

/*
 * Per-touchscreen data.
 */
struct h3600_dev {
      struct input_dev *dev;
      struct serio *serio;
      unsigned char event;    /* event ID from packet */
      unsigned char chksum;
      unsigned char len;
      unsigned char idx;
      unsigned char buf[H3600_MAX_LENGTH];
      char phys[32];
};

static irqreturn_t action_button_handler(int irq, void *dev_id)
{
      int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1;
      struct input_dev *dev = (struct input_dev *) dev_id;

      input_report_key(dev, KEY_ENTER, down);
      input_sync(dev);

      return IRQ_HANDLED;
}

static irqreturn_t npower_button_handler(int irq, void *dev_id)
{
      int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1;
      struct input_dev *dev = (struct input_dev *) dev_id;

      /*
       * This interrupt is only called when we release the key. So we have
       * to fake a key press.
       */
      input_report_key(dev, KEY_SUSPEND, 1);
      input_report_key(dev, KEY_SUSPEND, down);
      input_sync(dev);

      return IRQ_HANDLED;
}

#ifdef CONFIG_PM

static int flite_brightness = 25;

enum flite_pwr {
      FLITE_PWR_OFF = 0,
      FLITE_PWR_ON = 1
};

/*
 * h3600_flite_power: enables or disables power to frontlight, using last bright */
unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
{
      unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;
      struct h3600_dev *ts = input_get_drvdata(dev);

      /* Must be in this order */
      ts->serio->write(ts->serio, 1);
      ts->serio->write(ts->serio, pwr);
      ts->serio->write(ts->serio, brightness);
      return 0;
}

#endif

/*
 * This function translates the native event packets to linux input event
 * packets. Some packets coming from serial are not touchscreen related. In
 * this case we send them off to be processed elsewhere.
 */
static void h3600ts_process_packet(struct h3600_dev *ts)
{
      struct input_dev *dev = ts->dev;
      static int touched = 0;
      int key, down = 0;

      switch (ts->event) {
            /*
               Buttons - returned as a single byte
                  7 6 5 4 3 2 1 0
                  S x x x N N N N

               S       switch state ( 0=pressed 1=released)
               x       Unused.
               NNNN    switch number 0-15

               Note: This is true for non interrupt generated key events.
            */
            case KEYBD_ID:
                  down = (ts->buf[0] & 0x80) ? 0 : 1;

                  switch (ts->buf[0] & 0x7f) {
                        case H3600_SCANCODE_RECORD:
                              key = KEY_RECORD;
                              break;
                        case H3600_SCANCODE_CALENDAR:
                              key = KEY_PROG1;
                                        break;
                        case H3600_SCANCODE_CONTACTS:
                              key = KEY_PROG2;
                              break;
                        case H3600_SCANCODE_Q:
                              key = KEY_Q;
                              break;
                        case H3600_SCANCODE_START:
                              key = KEY_PROG3;
                              break;
                        case H3600_SCANCODE_UP:
                              key = KEY_UP;
                              break;
                        case H3600_SCANCODE_RIGHT:
                              key = KEY_RIGHT;
                              break;
                        case H3600_SCANCODE_LEFT:
                              key = KEY_LEFT;
                              break;
                        case H3600_SCANCODE_DOWN:
                              key = KEY_DOWN;
                              break;
                        default:
                              key = 0;
                  }
                  if (key)
                        input_report_key(dev, key, down);
                  break;
            /*
             * Native touchscreen event data is formatted as shown below:-
             *
             *      +-------+-------+-------+-------+
             *      | Xmsb  | Xlsb  | Ymsb  | Ylsb  |
             *      +-------+-------+-------+-------+
             *       byte 0    1       2       3
             */
            case TOUCHS_ID:
                  if (!touched) {
                        input_report_key(dev, BTN_TOUCH, 1);
                        touched = 1;
                  }

                  if (ts->len) {
                        unsigned short x, y;

                        x = ts->buf[0]; x <<= 8; x += ts->buf[1];
                        y = ts->buf[2]; y <<= 8; y += ts->buf[3];

                        input_report_abs(dev, ABS_X, x);
                        input_report_abs(dev, ABS_Y, y);
                  } else {
                        input_report_key(dev, BTN_TOUCH, 0);
                        touched = 0;
                  }
                  break;
            default:
                  /* Send a non input event elsewhere */
                  break;
      }

      input_sync(dev);
}

/*
 * h3600ts_event() handles events from the input module.
 */
static int h3600ts_event(struct input_dev *dev, unsigned int type,
                   unsigned int code, int value)
{
#if 0
      struct h3600_dev *ts = input_get_drvdata(dev);

      switch (type) {
            case EV_LED: {
            //    ts->serio->write(ts->serio, SOME_CMD);
                  return 0;
            }
      }
      return -1;
#endif
      return 0;
}

/*
        Frame format
  byte    1       2               3              len + 4
        +-------+---------------+---------------+--=------------+
        |SOF    |id     |len    | len bytes     | Chksum        |
        +-------+---------------+---------------+--=------------+
  bit   0     7  8    11 12   15 16

        +-------+---------------+-------+
        |SOF    |id     |0      |Chksum | - Note Chksum does not include SOF
        +-------+---------------+-------+
  bit   0     7  8    11 12   15 16

*/

static int state;

/* decode States  */
#define STATE_SOF       0       /* start of FRAME */
#define STATE_ID        1       /* state where we decode the ID & len */
#define STATE_DATA      2       /* state where we decode data */
#define STATE_EOF       3       /* state where we decode checksum or EOF */

static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,
                                     unsigned int flags)
{
      struct h3600_dev *ts = serio_get_drvdata(serio);

      /*
       * We have a new frame coming in.
       */
      switch (state) {
            case STATE_SOF:
                  if (data == CHAR_SOF)
                        state = STATE_ID;
                  break;
            case STATE_ID:
                  ts->event = (data & 0xf0) >> 4;
                  ts->len = (data & 0xf);
                  ts->idx = 0;
                  if (ts->event >= MAX_ID) {
                        state = STATE_SOF;
                        break;
                  }
                  ts->chksum = data;
                  state = (ts->len > 0) ? STATE_DATA : STATE_EOF;
                  break;
            case STATE_DATA:
                  ts->chksum += data;
                  ts->buf[ts->idx]= data;
                  if (++ts->idx == ts->len)
                        state = STATE_EOF;
                  break;
            case STATE_EOF:
                  state = STATE_SOF;
                  if (data == CHAR_EOF || data == ts->chksum)
                        h3600ts_process_packet(ts);
                  break;
            default:
                  printk("Error3\n");
                  break;
      }

      return IRQ_HANDLED;
}

/*
 * h3600ts_connect() is the routine that is called when someone adds a
 * new serio device that supports H3600 protocol and registers it as
 * an input device.
 */
static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
{
      struct h3600_dev *ts;
      struct input_dev *input_dev;
      int err;

      ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL);
      input_dev = input_allocate_device();
      if (!ts || !input_dev) {
            err = -ENOMEM;
            goto fail1;
      }

      ts->serio = serio;
      ts->dev = input_dev;
      snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys);

      input_dev->name = "H3600 TouchScreen";
      input_dev->phys = ts->phys;
      input_dev->id.bustype = BUS_RS232;
      input_dev->id.vendor = SERIO_H3600;
      input_dev->id.product = 0x0666;  /* FIXME !!! We can ask the hardware */
      input_dev->id.version = 0x0100;
      input_dev->dev.parent = &serio->dev;

      input_set_drvdata(input_dev, ts);

      input_dev->event = h3600ts_event;

      input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
            BIT_MASK(EV_LED) | BIT_MASK(EV_PWR);
      input_dev->ledbit[0] = BIT_MASK(LED_SLEEP);
      input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0);
      input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0);

      set_bit(KEY_RECORD, input_dev->keybit);
      set_bit(KEY_Q, input_dev->keybit);
      set_bit(KEY_PROG1, input_dev->keybit);
      set_bit(KEY_PROG2, input_dev->keybit);
      set_bit(KEY_PROG3, input_dev->keybit);
      set_bit(KEY_UP, input_dev->keybit);
      set_bit(KEY_RIGHT, input_dev->keybit);
      set_bit(KEY_LEFT, input_dev->keybit);
      set_bit(KEY_DOWN, input_dev->keybit);
      set_bit(KEY_ENTER, input_dev->keybit);
      set_bit(KEY_SUSPEND, input_dev->keybit);
      set_bit(BTN_TOUCH, input_dev->keybit);

      /* Device specific stuff */
      set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES);
      set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);

      if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
                  IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
            printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
            err = -EBUSY;
            goto fail2;
      }

      if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
                  IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
            printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
            err = -EBUSY;
            goto fail3;
      }

      serio_set_drvdata(serio, ts);

      err = serio_open(serio, drv);
      if (err)
            return err;

      //h3600_flite_control(1, 25);     /* default brightness */
      input_register_device(ts->dev);

      return 0;

fail3:      free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
fail2:      free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
fail1:      serio_set_drvdata(serio, NULL);
      input_free_device(input_dev);
      kfree(ts);
      return err;
}

/*
 * h3600ts_disconnect() is the opposite of h3600ts_connect()
 */

static void h3600ts_disconnect(struct serio *serio)
{
      struct h3600_dev *ts = serio_get_drvdata(serio);

      free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
      free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev);
      input_get_device(ts->dev);
      input_unregister_device(ts->dev);
      serio_close(serio);
      serio_set_drvdata(serio, NULL);
      input_put_device(ts->dev);
      kfree(ts);
}

/*
 * The serio driver structure.
 */

static struct serio_device_id h3600ts_serio_ids[] = {
      {
            .type = SERIO_RS232,
            .proto      = SERIO_H3600,
            .id   = SERIO_ANY,
            .extra      = SERIO_ANY,
      },
      { 0 }
};

MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids);

static struct serio_driver h3600ts_drv = {
      .driver           = {
            .name = "h3600ts",
      },
      .description      = DRIVER_DESC,
      .id_table   = h3600ts_serio_ids,
      .interrupt  = h3600ts_interrupt,
      .connect    = h3600ts_connect,
      .disconnect = h3600ts_disconnect,
};

/*
 * The functions for inserting/removing us as a module.
 */

static int __init h3600ts_init(void)
{
      return serio_register_driver(&h3600ts_drv);
}

static void __exit h3600ts_exit(void)
{
      serio_unregister_driver(&h3600ts_drv);
}

module_init(h3600ts_init);
module_exit(h3600ts_exit);

Generated by  Doxygen 1.6.0   Back to index