
/* -*- linux-c -*- */

/* 
 * Driver for USB Touchscreen (Microtech - IBM SurePos 4820)
 *
 * Copyright (C) 2000 Wojciech Woziwodzki
 * Written by Radoslaw Garbacz
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Originally based upon scanner.c (David E. Nelson).
 *
 * History
 *
 *  0.1  06/05/2000 (RGA)
 *    Development work was begun.
 *
 *  0.2  09/05/2000 (RGA)
 *    Documentation about MicroTouch controller was arrived.
 *
 */

#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>

#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>       /* used in new tty drivers */
#include <linux/signal.h>       /* used in new tty drivers */
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/termios.h>
#include <linux/if.h>

#ifdef CONFIG_KERNELD
#   include <linux/kerneld.h>
#endif

//#define DEBUG
#define TSCRN_IOCTL
//#define __TEST_NO_DEVICE__

#define TSCRN_MAX_MNR 16        /* We're allocated 16 minors */
#define TSCRN_BASE_MNR 48       /* USB Scanners start at minor 48 */

#define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
#define IS_EP_INTR(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_INT ? 1 : 0)
#define IS_EP_INTR_IN(ep) (IS_EP_INTR(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
#define IS_EP_INTR_OUT(ep) (IS_EP_INTR(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)

#define USB_TSCRN_MINOR(X) MINOR((X)->i_rdev) - TSCRN_BASE_MNR

#ifdef DEBUG
#   define TSCRN_DEBUG(X) X
#else
#   define TSCRN_DEBUG(X)
#endif

#include <linux/usb.h>
#include <linux/input.h>
#include "touchscreen.h"

struct tscrn_usb_data {
   struct usb_device *dev;
   wait_queue_head_t wait;
   struct urb irq;
   struct urb ctrlin;
   struct urb ctrlout;
   devrequest *setup_packet;    // for control transfers 
   unsigned int ifnum;          // Interface number of the USB device 
   kdev_t minor;                // TouchScreen minor - used in disconnect() 
   unsigned char data[TSCRN_USB_RAPORT_SIZE_DATA];      // Data buffer X-Y, and 
                                                        // button 
   int nLoopCounter;            // number of data packet last received +1
   char isopen;                 // Not zero if the device is open 
   char present;                // Not zero if device is present 
   unsigned char *obuf, *ibuf;  // transfer buffers 
   unsigned char *pToRead, *pToWrite;   // pointers to toRead cell of buffer
                                        // and to toWrite one 
   char intr_ep;                // Endpoint assignments 
   struct input_dev input_dev;  // to work as an input device driver
};

static struct tscrn_usb_data *p_tscrn_table[TSCRN_MAX_MNR] =
   { NULL, /* ... */  };

MODULE_AUTHOR("Radoslaw Garbacz, garbacz@posexperts.com.pl");
MODULE_DESCRIPTION("USB touchscreen driver");

static __s32 vendor = -1, product = -1;

MODULE_PARM(vendor, "i");
MODULE_PARM_DESC(vendor, "User specified USB idVendor");

MODULE_PARM(product, "i");
MODULE_PARM_DESC(product, "User specified USB idProduct");

/* Forward declarations */
static struct usb_driver touchscreen_driver;

/* Procedures */

/**
 * Runs after ctrl requests.
 */
static void ctrl_touchscreen(struct urb *urb)
{
   struct tscrn_usb_data *tscrn = urb->context;

   dbg("ctrl_touchscreen(%d): status=%d", tscrn->minor, urb->status);

   if(waitqueue_active(&tscrn->wait))
      wake_up_interruptible(&tscrn->wait);
   return;
};

/**
 * Runs on new data received from device
 * The buffer should keep the last state of device, thus the buffer overflow
 * can occurre
 * The client can't read data when pToRead == pToWrite
 */
static void irq_touchscreen(struct urb *urb)
{
   /*
    * data raports...
    */
   struct tscrn_usb_data *tscrn = urb->context;
   struct tscrn_usb_data_report *data =
      (struct tscrn_usb_data_report *) tscrn->data;

   if(urb->status)
      return;

   // return when driver was clesed 
   if(!tscrn->isopen)
      return;

   // increase loop counter
   // !!! Unfortunately the data was lost to seldom !!!
   // if((int)GET_LOOP(data) != tscrn->nLoopCounter)
   // warn("warn:Lost data new loop %d previous loop
   // %d",(int)GET_LOOP(data),(int)tscrn->nLoopCounter);

   input_report_key(&tscrn->input_dev, BTN_LEFT, IS_TOUCHED(data));
   input_report_abs(&tscrn->input_dev, ABS_X, GET_XC(data));
   input_report_abs(&tscrn->input_dev, ABS_Y, GET_YC(data));

   tscrn->nLoopCounter = (GET_LOOP(data)) + 1;

   // store data to buffer
   memcpy(tscrn->pToWrite, data, TSCRN_USB_RAPORT_SIZE_DATA);
   // next data to next cell
   if(tscrn->pToRead == NULL)
      tscrn->pToRead = tscrn->pToWrite;

   tscrn->pToWrite += TSCRN_USB_RAPORT_SIZE_DATA;
   if(tscrn->pToWrite >= tscrn->obuf + OBUF_SIZE)
      tscrn->pToWrite = tscrn->obuf;

   if(waitqueue_active(&tscrn->wait))
      wake_up_interruptible(&tscrn->wait);

   // dbg("irq_touchscreen(): GET_Data
   // 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x status=%d",
   // (int)data[0],(int)data[1],(int)data[2],(int)data[3],(int)data[4],(int)data[5],(int)data[6],(int)data[7],(int)data[8],(int)data[9],(int)data[10], 
   // tscrn->ctrlout.status);

   return;
}

static int open_touchscreen(struct inode *inode, struct file *file)
{
   struct tscrn_usb_data *tscrn;
   struct usb_device *dev;

   kdev_t minor;

#ifdef __TEST_NO_DEVICE__
   return -ENODEV;
#endif

   minor = USB_TSCRN_MINOR(inode);

   dbg("open_touchscreen: tscrn_minor:%d", minor);

   if(!p_tscrn_table[minor]) {
      err("open_touchscreen(%d): invalid tscrn_minor", minor);
      return -ENODEV;
   }

   tscrn = p_tscrn_table[minor];

   dev = tscrn->dev;

   if(!dev) {
      return -ENODEV;
   }

   if(!tscrn->present) {
      return -ENODEV;
   }

   if(tscrn->isopen) {
      return -EBUSY;
   }
   // clear up the data buffer
   memset(tscrn->obuf, 0x0, OBUF_SIZE);
   tscrn->pToRead = NULL;
   tscrn->pToWrite = tscrn->obuf;

   tscrn->isopen = 1;

   file->private_data = tscrn;  /* Used by the read and write metheds */

   MOD_INC_USE_COUNT;

   return 0;
}

static int close_touchscreen(struct inode *inode, struct file *file)
{
   struct tscrn_usb_data *tscrn;

   kdev_t minor;

#ifdef __TEST_NO_DEVICE__
   return -ENODEV;
#endif

   minor = USB_TSCRN_MINOR(inode);

   dbg("close_touchscreen: tscrn_minor:%d", minor);

   if(!p_tscrn_table[minor]) {
      err("close_touchscreen(%d): invalid tscrn_minor", minor);
      return -ENODEV;
   }

   tscrn = p_tscrn_table[minor];

   tscrn->isopen = 0;

   file->private_data = NULL;

   MOD_DEC_USE_COUNT;

   return 0;
}

static ssize_t read_touchscreen(struct file *file, char *buffer,
                                size_t count, loff_t * ppos)
{
   struct tscrn_usb_data *tscrn;
   struct usb_device *dev;

   ssize_t bytes_read;          /* Overall count of bytes_read */
   ssize_t ret;

   kdev_t minor;

   int partial;                 /* Number of bytes successfully read */
   int this_read;               /* Max number of bytes to read */

   // int result;

   unsigned char *buf, *pToRead;

#ifdef __TEST_NO_DEVICE__
   return -ENODEV;
#endif

   tscrn = file->private_data;
   minor = tscrn->minor;
   buf = tscrn->obuf;
   pToRead = tscrn->pToRead;
   dev = tscrn->dev;

   bytes_read = 0;
   ret = 0;

   if((tscrn->pToWrite == tscrn->pToRead) || (tscrn->pToRead == NULL))
      return 0;                 // no new data

   if(signal_pending(current)) {
      ret = -EINTR;
      return ret;
   }

   this_read =
      (count >=
       TSCRN_USB_RAPORT_SIZE_DATA) ? TSCRN_USB_RAPORT_SIZE_DATA : count;

   partial = this_read;
   // dbg("read stats(%d): result:%d this_read:%d partial:%d", minor, result,
   // this_read, partial);
   dbg("read count=%d", count);

   if(partial) {                /* Data returned */
      if(copy_to_user(buffer, tscrn->pToRead, this_read)) {
         ret = -EFAULT;
         return ret;            // break;
      }
      count -= partial;
      bytes_read += partial;
      buffer += partial;
      tscrn->pToRead += partial;
      if(tscrn->pToRead >= tscrn->obuf + OBUF_SIZE)
         tscrn->pToRead = tscrn->obuf;
   }

   dbg("read ended bytes_read(%d)", bytes_read);

   return ret ? ret : bytes_read;
}

/**
* Zmiana dla jądra 2.2.x z <code>select</code> na <code>poll</code>
*/
static unsigned int poll_touchscreen(struct file *file,
                                     struct poll_table_struct *_wait)
{
   struct usb_device *dev;
   struct tscrn_usb_data *tscrn;
   struct inode *inode;
   struct dentry *dentry;

   kdev_t minor;

   dentry = file->f_dentry;
   inode = dentry->d_inode;
   minor = USB_TSCRN_MINOR(inode);

   dbg("poll_touchscreen(%d): entered", minor);

   if(!p_tscrn_table[minor]) {
      err("poll_touchscreen(%d): invalid minor", minor);
      return 0;
      // return -ENODEV;
   };

   tscrn = p_tscrn_table[minor];
   dev = tscrn->dev;

   // if(_selType != SEL_IN)
   // return -EPERM;

   if((tscrn->pToWrite == tscrn->pToRead) || (tscrn->pToRead == NULL)) {
      poll_wait(file, &tscrn->wait, _wait);
      return 0;
   };

   return 1;
};

static int ioctl_touchscreen(struct inode *inode, struct file *file,
                             unsigned int cmd, unsigned long arg)
{
   struct usb_device *dev;
   struct tscrn_usb_data *tscrn;

   int nRet;

   kdev_t minor;

#ifdef __TEST_NO_DEVICE__
   return -ENODEV;
#endif
   minor = USB_TSCRN_MINOR(inode);

   dbg("ioctl_touchscreen(%d): entered cmd=%d", minor, cmd);

   if(!p_tscrn_table[minor]) {
      err("ioctl_touchscreen(%d): invalid minor", minor);
      return -ENODEV;
   }

   tscrn = p_tscrn_table[minor];
   dev = tscrn->dev;

   switch (cmd) {
      case TSCRN_USER_REQUEST:
      {
         /*
          * sends reset command 
          */
         struct {
            __u8 data;
            __u8 request;
            __u16 value;
            __u16 index;
         } args;

         if(copy_from_user(&args, (void *) arg, sizeof(args)))
            return -EFAULT;

         // for the present a soft reset
         tscrn->setup_packet->requesttype = args.data;
         tscrn->setup_packet->request = args.request;
         tscrn->setup_packet->value = args.value;
         tscrn->setup_packet->index = args.index;
         tscrn->setup_packet->length = 0;
         FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                          usb_sndctrlpipe(dev, 0),
                          (unsigned char *) tscrn->setup_packet, NULL, 0,
                          ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlout.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): cmd status=%d nRet=%d", minor,
             tscrn->ctrlout.status, nRet);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): cmd status=%d", minor,
             tscrn->ctrlout.status);
         return tscrn->ctrlout.status;
      };
      case TSCRN_CLEAR_HALT:
      {
         // when end_point is STALLED always returned -1 at first 
         // and OK on second exec, then end_point returns to normal state
         return usb_clear_halt(dev, 0x00);      // 0x80
      };
      case TSCRN_HARD_RESET:
      {
         tscrn->setup_packet->requesttype =
            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_RESET;
         tscrn->setup_packet->value = TSCRN_USB_PARAM_HARD_RESET;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = 0;
         FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                          usb_sndctrlpipe(dev, 0),
                          (unsigned char *) tscrn->setup_packet, NULL, 0,
                          ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlout.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d nRet=%d", minor,
             tscrn->ctrlout.status, nRet);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlout.status);

         return tscrn->ctrlout.status;
      };
      case TSCRN_SOFT_RESET:
      {
         tscrn->setup_packet->requesttype =
            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_RESET;
         tscrn->setup_packet->value = TSCRN_USB_PARAM_SOFT_RESET;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = 0;
         FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                          usb_sndctrlpipe(dev, 0),
                          (unsigned char *) tscrn->setup_packet, NULL, 0,
                          ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlout.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d nRet=%d", minor,
             tscrn->ctrlout.status, nRet);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlout.status);

         return tscrn->ctrlout.status;
      };
      case TSCRN_CONTROLLER_STATUS:
      {
         tscrn->setup_packet->requesttype =
            USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_STATUS;
         tscrn->setup_packet->value = 0;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = TSCRN_USB_RAPORT_SIZE_STATUS;
         memset(tscrn->ibuf, 0x0, IBUF_SIZE);
         FILL_CONTROL_URB(&tscrn->ctrlin, dev,
                          usb_rcvctrlpipe(dev, 0x80),
                          (unsigned char *) tscrn->setup_packet, tscrn->ibuf,
                          TSCRN_USB_RAPORT_SIZE_STATUS, ctrl_touchscreen,
                          tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlin))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlin.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlin.status);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): gets 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], (int) tscrn->ibuf[2], (int) tscrn->ibuf[3], (int) tscrn->ibuf[4], (int) tscrn->ibuf[5], (int) tscrn->ibuf[6], (int) tscrn->ibuf[7], tscrn->ctrlin.status, nRet);

         if(tscrn->ctrlin.status == 0)
            if(copy_to_user
               ((void *) arg, tscrn->ibuf, TSCRN_USB_RAPORT_SIZE_STATUS))
               return -EFAULT;

         return tscrn->ctrlin.status;
      };
      case TSCRN_CONTROLLER_ID:
      {
         tscrn->setup_packet->requesttype =
            USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_CONTROLLER_ID;
         tscrn->setup_packet->value = 0;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = TSCRN_USB_RAPORT_SIZE_ID;
         memset(tscrn->ibuf, 0x0, IBUF_SIZE);
         FILL_CONTROL_URB(&tscrn->ctrlin, dev,
                          usb_rcvctrlpipe(dev, 0x80),
                          (unsigned char *) tscrn->setup_packet, tscrn->ibuf,
                          TSCRN_USB_RAPORT_SIZE_ID, ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlin))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlin.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlin.status);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): gets 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], (int) tscrn->ibuf[2], (int) tscrn->ibuf[3], (int) tscrn->ibuf[4], (int) tscrn->ibuf[5], (int) tscrn->ibuf[6], (int) tscrn->ibuf[7], tscrn->ctrlin.status, nRet);

         if(tscrn->ctrlin.status == 0)
            if(copy_to_user
               ((void *) arg, tscrn->ibuf, TSCRN_USB_RAPORT_SIZE_ID))
               return -EFAULT;

         return tscrn->ctrlin.status;
      };
      case TSCRN_CALIBRATION:
      {
         __u16 type;

         if(copy_from_user(&type, (void *) arg, sizeof(type))) {
            err("ioctl_touchscreen(%d): copy from user error arg=%d.", minor,
                *((int *) arg));
            return -EFAULT;
         };

         switch (type) {
            case TSCRN_EXTENDED_CALIBRATION_TYPE:
            case TSCRN_CORNER_CALIBRATION_TYPE:
               break;
            default:
               err("ioctl_touchscreen(%d): unknown calibration type %d.", minor,
                   type);
               return -EFAULT;

         };
         tscrn->setup_packet->requesttype =
            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_CALIBRATION;
         tscrn->setup_packet->value = type;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = 0;
         FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                          usb_sndctrlpipe(dev, 0x0),
                          (unsigned char *) tscrn->setup_packet, NULL, 0,
                          ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlout.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d nRet=%d", minor,
             tscrn->ctrlout.status, nRet);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlout.status);

         return tscrn->ctrlout.status;
      };
      case TSCRN_RESTORE_DEFAULTS:
      {
         tscrn->setup_packet->requesttype =
            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
         tscrn->setup_packet->request = TSCRN_USB_REQUEST_RESTORE_DEFAULTS;
         tscrn->setup_packet->value = 0;
         tscrn->setup_packet->index = 0;
         tscrn->setup_packet->length = 0;
         FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                          usb_sndctrlpipe(dev, 0x0),
                          (unsigned char *) tscrn->setup_packet, NULL, 0,
                          ctrl_touchscreen, tscrn);

         if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
            err("ioctl_touchscreen(%d): errror=%d status=%d.", minor, nRet,
                tscrn->ctrlout.status);
            return nRet;
         }
         dbg("ioctl_touchscreen(%d): status=%d nRet=%d", minor,
             tscrn->ctrlout.status, nRet);
         interruptible_sleep_on(&tscrn->wait);
         dbg("ioctl_touchscreen(%d): status=%d", minor, tscrn->ctrlout.status);

         return tscrn->ctrlout.status;
      };
      default:
         return -ENOIOCTLCMD;
   }
   return 0;
}

///////////////////////////////////////////////////////////////////////////
static void *probe_touchscreen(struct usb_device *dev, unsigned int ifnum)
{
   struct tscrn_usb_data *tscrn;
   struct usb_interface_descriptor *interface;
   struct usb_endpoint_descriptor *endpoint;

#ifdef DEBUG
   int nRet, nCounter;
#endif
   // devrequest *setup_packet;

   int ep_cnt;

   kdev_t minor;

   char valid_device = 0;
   char have_intr;

   if(vendor != -1 && product != -1) {
      info
         ("probe_touchscreen: User specified USB touchscreen -- Vendor:Product - %x:%x",
          vendor, product);
   }

   /*
    * 1. Check Vendor/Product
    * 2. Determine/Assign Bulk Endpoints
    * 3. Determine/Assign Intr Endpoint
    */

   /*
    *
    * NOTE: Just because a product is supported here does not mean that
    * applications exist that support the product.  It's in the hopes
    * that this will allow developers a means to produce applications
    * that will support USB products.
    *
    * Until we detect a device which is pleasing, we silently punt.
    */

   if(dev->descriptor.idVendor == MICROTOUCH_VENDOR_ID
      && dev->descriptor.idProduct == MICROTOUCH_PRODUCT_ID)
      valid_device = 1;
   // no user specification devices !!!
   // -- else
   // -- /* User specified */
   // -- if (dev->descriptor.idVendor == vendor
   // -- && dev->descriptor.idProduct == product)
   // -- valid_device = 1;

   if(!valid_device)
      return NULL;              /* We didn't find anything pleasing */

   if(ifnum != 0) {
      info("probe_touchscreen: not correct interface no.");
      return NULL;
   }
   /*
    * After this point we can be a little noisy about what we are trying to
    *  configure.
    */

   if(dev->descriptor.bNumConfigurations != 1) {
      info("probe_touchscreen: Only one device configuration is supported.");
      return NULL;
   }

   if(dev->config[0].bNumInterfaces != 1) {
      info("probe_touchscreen: Only one device interface is supported.");
      return NULL;
   }

   interface = dev->config[0].interface[ifnum].altsetting;
   // interface = dev->actconfig[0].interface[ifnum].altsetting;
   endpoint = interface[ifnum].endpoint;

   /*
    * Start checking for two bulk endpoints OR two bulk endpoints *and* one
    * interrupt endpoint. If we have an interrupt endpoint go ahead and
    * setup the handler. FIXME: This is a future enhancement...
    */

   dbg("probe_touchscreen: Number of Endpoints:%d",
       (int) interface->bNumEndpoints);

   if(interface->bNumEndpoints != 1) {
      info("probe_touchscreen: Only one endpoints supported.");
      return NULL;
   }
   // ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0;
   ep_cnt = have_intr = 0;

   if(IS_EP_INTR(endpoint[ep_cnt])) {
      have_intr = ++ep_cnt;
      dbg("probe_touchscreen: intr_ep:%d", have_intr);
   } else {
      info("probe_touchscreen: Undetected endpoint. Notify the maintainer.");
      return NULL;              /* Shouldn't ever get here unless we have
                                 * something weird */
   }

   /*
    * Determine a minor number and initialize the structure associated
    * with it.  The problem with this is that we are counting on the fact
    * that the user will sequentially add device nodes for the touchscreen
    * devices.  */

   /*
    * Use firs free file devices
    */
   for(minor = 0; minor < TSCRN_MAX_MNR; minor++) {
      if(!p_tscrn_table[minor])
         break;
   }

   /*
    * Check to make sure that the last slot isn't already taken 
    */
   if(p_tscrn_table[minor]) {
      err("probe_touchscreen: No more minor devices remaining.");
      return NULL;
   }

   dbg("probe_touchscreen: Allocated minor:%d", minor);

   if(!(tscrn = kmalloc(sizeof(struct tscrn_usb_data), GFP_KERNEL))) {
      err("probe_touchscreen: Out of memory.");
      return NULL;
   }
   memset(tscrn, 0, sizeof(struct tscrn_usb_data));
   dbg("probe_touchscreen(%d): Address of tscrn:%p", minor, tscrn);

   /*
    * Ok, now initialize all the relevant values 
    */
   if(!(tscrn->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
      err("probe_touchscreen(%d): Not enough memory for the output buffer.",
          minor);
      kfree(tscrn);
      return NULL;
   }
   dbg("probe_touchscreen(%d): obuf address:%p", minor, tscrn->obuf);

   if(!(tscrn->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
      err("probe_touchscreen(%d): Not enough memory for the input buffer.",
          minor);
      kfree(tscrn->obuf);
      kfree(tscrn);
      return NULL;
   }
   dbg("probe_touchscreen(%d): ibuf address:%p", minor, tscrn->ibuf);

   if(!
      (tscrn->setup_packet =
       (void *) kmalloc(sizeof(*tscrn->setup_packet), GFP_KERNEL))) {
      err("probe_touchscreen(%d): Not enough memory for the statuc.", minor);
      kfree(tscrn->ibuf);
      kfree(tscrn->obuf);
      kfree(tscrn);
      return NULL;
   }
   dbg("probe_touchscreen(%d): setup_packet address:%p, size=%d size2=%d",
       minor, tscrn->setup_packet, sizeof(*tscrn->setup_packet),
       sizeof(devrequest));
   memset((char *) tscrn->setup_packet, 0, sizeof(*tscrn->setup_packet));

   // Initiation input_dev structure 
   tscrn->input_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
   tscrn->input_dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT);    // |
                                                                // BIT(BTN_RIGHT) 
                                                                // |
                                                                // BIT(BTN_MIDDLE);
   tscrn->input_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);

   // Initialization toRead and toWrite variables
   tscrn->pToWrite = tscrn->obuf;
   tscrn->pToRead = NULL;

   // Initiation wait queue
   dbg("probe_touchscreen(%d): initialization wait_queue", minor);
   init_waitqueue_head(&tscrn->wait);
   dbg("probe_touchscreen(%d): wait_queue initialized", minor);

#ifdef DEBUG
   // /////////////////////////////////////////////////////////////////////////////////
   // Only for testing, not for normal useing of device driver
   // Register the interface
   if(!(nRet = usb_interface_claimed(&dev->actconfig[0].interface[ifnum]))) {
      dbg("probe_touchscreen(%d): interface_claimed=%d", minor, nRet);
      usb_driver_claim_interface(&touchscreen_driver,
                                 &dev->actconfig[0].interface[ifnum], tscrn);
   } else
      dbg("probe_touchscreen(%d): error interface_claimed=%d", minor, nRet);
   dbg("probe_touchscreen(%d): Configuring CTRL handler for intr EP:%d", minor,
       have_intr);
   /*
    * sends reset command 
    */
   // receive answer data
   tscrn->setup_packet->requesttype =
      USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
   tscrn->setup_packet->request = USB_REQ_GET_CONFIGURATION;
   tscrn->setup_packet->value = 0x0;
   tscrn->setup_packet->index = 0x0;    // ifnum;
   tscrn->setup_packet->length = 1;
   FILL_CONTROL_URB(&tscrn->ctrlin, dev,
                    usb_rcvctrlpipe(dev, 0x80),
                    (unsigned char *) tscrn->setup_packet, tscrn->ibuf,
                    sizeof(tscrn->ibuf), ctrl_touchscreen, tscrn);

   if((nRet = usb_submit_urb(&tscrn->ctrlin))) {
      err("probe_touchscreen(%d): GET_CONFIGURATION:Get smth error=%d status=%d.", minor, nRet, tscrn->ctrlin.status);
      kfree(tscrn->ibuf);
      kfree(tscrn->obuf);
      kfree(tscrn->setup_packet);
      kfree(tscrn);
      return NULL;
   }
   nCounter = 10;
   dbg("probe_touchscreen(%d): GET_CONFIGURATION:wait for data: status=%d nRet=%d", minor, tscrn->ctrlin.status, nRet);
   interruptible_sleep_on(&tscrn->wait);
   dbg("probe_touchscreen(%d): GET_CONFIGURATION:gets 0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], tscrn->ctrlin.status, nRet);
   if(tscrn->ctrlin.status == (-32)) {
      dbg("probe_touchscreen(%d): get error -32 status=%d - reset pipe", minor,
          tscrn->ctrlin.status);
      nRet = usb_clear_halt(dev, 0x00);
      dbg("probe_touchscreen(%d): get error -32 status=%d - reset pipe nRet=%d",
          minor, tscrn->ctrlin.status, nRet);
   }
   // 
   // receive answer data
   tscrn->setup_packet->requesttype =
      USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT;
   tscrn->setup_packet->request = USB_REQ_GET_STATUS;
   tscrn->setup_packet->value = 0x0;
   tscrn->setup_packet->index = 0x81;
   tscrn->setup_packet->length = 2;
   FILL_CONTROL_URB(&tscrn->ctrlin, dev,
                    usb_rcvctrlpipe(dev, 0x80),
                    (unsigned char *) tscrn->setup_packet, tscrn->ibuf,
                    sizeof(tscrn->ibuf), ctrl_touchscreen, tscrn);

   if((nRet = usb_submit_urb(&tscrn->ctrlin))) {
      err("probe_touchscreen(%d): GET_STATUS:Get smth error=%d status=%d .",
          minor, nRet, tscrn->ctrlin.status);
      kfree(tscrn->ibuf);
      kfree(tscrn->obuf);
      kfree(tscrn->setup_packet);
      kfree(tscrn);
      return NULL;
   }
   dbg("probe_touchscreen(%d): GET_STATUS: status=%d nRet=%d", minor,
       tscrn->ctrlin.status, nRet);
   interruptible_sleep_on(&tscrn->wait);
   dbg("probe_touchscreen(%d): GET_STATUS:gets 0x%x,0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], (int) tscrn->ibuf[2], tscrn->ctrlin.status, nRet);

   // -- // get controller id report
   tscrn->setup_packet->requesttype =
      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
   tscrn->setup_packet->request = TSCRN_USB_REQUEST_CONTROLLER_ID;
   tscrn->setup_packet->value = 0;
   tscrn->setup_packet->index = 0;
   tscrn->setup_packet->length = TSCRN_USB_RAPORT_SIZE_ID;

   memset((char *) tscrn->ibuf, 0, IBUF_SIZE);

   FILL_CONTROL_URB(&tscrn->ctrlout, dev,
                    usb_rcvctrlpipe(dev, 0x80),
                    (unsigned char *) tscrn->setup_packet, tscrn->ibuf,
                    TSCRN_USB_RAPORT_SIZE_ID, ctrl_touchscreen, tscrn);

   if((nRet = usb_submit_urb(&tscrn->ctrlout))) {
      err("probe_touchscreen(%d): GET_ID:error=%d status=%d .", minor, nRet,
          tscrn->ctrlout.status);
      kfree(tscrn->ibuf);
      kfree(tscrn->obuf);
      kfree(tscrn->setup_packet);
      kfree(tscrn);
      return NULL;
   }
   dbg("probe_touchscreen(%d): GET_ID gets 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], (int) tscrn->ibuf[2], (int) tscrn->ibuf[3], (int) tscrn->ibuf[4], (int) tscrn->ibuf[5], (int) tscrn->ibuf[6], (int) tscrn->ibuf[7], tscrn->ctrlout.status, nRet);
   interruptible_sleep_on(&tscrn->wait);
   dbg("probe_touchscreen(%d): GET_ID gets 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x status=%d nRet=%d", minor, (int) tscrn->ibuf[0], (int) tscrn->ibuf[1], (int) tscrn->ibuf[2], (int) tscrn->ibuf[3], (int) tscrn->ibuf[4], (int) tscrn->ibuf[5], (int) tscrn->ibuf[6], (int) tscrn->ibuf[7], tscrn->ctrlout.status, nRet);
#endif
   // //////////////////////////////////////////////////////////////////////////////////
   /*
    * Ok, if we detected an interrupt EP, setup a handler for it 
    */
   if(have_intr) {
      dbg("probe_touchscreen(%d): Configuring IRQ handler for intr EP:%d",
          minor, have_intr);
      FILL_INT_URB(&tscrn->irq, dev, usb_rcvintpipe(dev, 0x81), // endpoint[(int 
                                                                // )(have_intr-1)].bEndpointAddress),
                   tscrn->data, TSCRN_USB_RAPORT_SIZE_DATA, irq_touchscreen,
                   tscrn, endpoint[(int) (have_intr - 1)].bInterval);
      // 2);

      if(usb_submit_urb(&tscrn->irq)) {
         err("probe_touchscreen(%d): Unable to allocate INT URB.", minor);
         kfree(tscrn->ibuf);
         kfree(tscrn->obuf);
         kfree(tscrn->setup_packet);
         kfree(tscrn);
         return NULL;
      }
      // the status will be -115 -> thist means EINPROGRESS, but it should be
      // that.
      dbg("probe_touchscreen(%d): IRQ done interval=%d status=%d", minor,
          endpoint[(int) (have_intr - 1)].bInterval, tscrn->irq.status);
   }

   tscrn->intr_ep = have_intr;
   tscrn->present = 1;
   tscrn->dev = dev;
   tscrn->minor = minor;
   tscrn->isopen = 0;

   input_register_device(&tscrn->input_dev);

   return p_tscrn_table[minor] = tscrn;
}

static void disconnect_touchscreen(struct usb_device *dev, void *ptr)
{
   struct tscrn_usb_data *tscrn = (struct tscrn_usb_data *) ptr;

   // if(tscrn->intr_ep)
   // {
   if(tscrn->irq.status != 0) {
      dbg("disconnect_touchscreen(%d): Unlinking IRQ URB", tscrn->minor);
      usb_unlink_urb(&tscrn->irq);
   }
   // }
   if(tscrn->ctrlin.status != 0) {
      dbg("disconnect_touchscreen(%d): Unlinking CTRLIN URB", tscrn->minor);
      usb_unlink_urb(&tscrn->ctrlin);
   }
   if(tscrn->ctrlout.status != 0) {
      dbg("disconnect_touchscreen(%d): Unlinking CTRLOUT URB", tscrn->minor);
      usb_unlink_urb(&tscrn->ctrlout);
   }

   input_unregister_device(&tscrn->input_dev);
   usb_driver_release_interface(&touchscreen_driver,
                                &tscrn->dev->actconfig->interface[tscrn->
                                                                  ifnum]);

   kfree(tscrn->ibuf);
   kfree(tscrn->obuf);
   kfree(tscrn->setup_packet);

   dbg("disconnect_touchscreen: De-allocating minor:%d", tscrn->minor);
   p_tscrn_table[tscrn->minor] = NULL;
   kfree(tscrn);
}

static struct file_operations usb_touchscreen_fops = {
 read:read_touchscreen,
   // write: write_touchscreen,
 poll:poll_touchscreen,
 ioctl:ioctl_touchscreen,
 open:open_touchscreen,
 release:close_touchscreen,
};

static struct usb_driver touchscreen_driver = {
   "touchscreen",
   probe_touchscreen,
   disconnect_touchscreen,
   {NULL, NULL},
   &usb_touchscreen_fops,
   TSCRN_BASE_MNR
};

void __exit usb_touchscreen_exit(void)
{
   usb_deregister(&touchscreen_driver);
}

int __init usb_touchscreen_init(void)
{
   if(usb_register(&touchscreen_driver) < 0)
      return -1;

   info("USB Touchscreen support registered.");
   return 0;
}

module_init(usb_touchscreen_init);
module_exit(usb_touchscreen_exit);
