iMON Touch LCD

Linux support for Soundgraph iMON USB IR/VFD modules used in Ahanix, Silverstone, Uneed, Accent and other cases.

Moderator: Venky

Post Reply
rehar
Posts: 3
Joined: Sun Sep 07, 2008 12:12 pm

iMON Touch LCD

Post by rehar » Mon Sep 08, 2008 5:26 pm

Hi all,

I friend of mine owns a Moneual case with an Soundgraph VGA LCD, touchscreen, front panel and iMON PAD remote control.
He ask me if i could help to get this device working.
So now I want to share my experience with this device. I hope this is the right place, because here I started looking for a solution.

The iMON PAD was recognized by the usbhid driver of the 2.6.25 kernel, and the mouse was working right out of the box. However if you push a different button, for example the play button, the driver stops working and only a reload of the driver reactivated the mouse functionality.

With a modified version of the actual (CVS) lirc_imon driver I got most of the RC keys and the frontpanel working, but no luck with the touchscreen and mouse pad.
After a while analyzing this device I figured the following out:

lsusb recognize it as “ ID 15c2:0034 SoundGraph Inc.”
This device has 2 USB interfaces, the first one for the mouse pad, all the button which are around this pad (backspace, enter, right and left mouse button, space etc.) and the numbers (1,2,3 etc). The second interface is for the rest of the RC, front panel and touchscreen. That's why the lirc_imon driver creates two LIRC devices and for a working remote and frontpanel you have to open both LIRC devices, otherwise you will get nothing.
The codes from the remote control are encoded on board
The mouse almost acts like a normal HID

So I started to write a patch for the lirc_imon driver to get this device working.

I wanted the following features:
  • The PAD should act like a normal mouse in X (working)
  • able to switch the PAD between mouse functionality and keyboard arrows (LIRC) (working)
  • working remote (LIRC) maybe as keyboard in X (working LIRC, keyboard TODO)
  • working frontpanel (LIRC) (working)
  • working touchscreen input or at least with LIRC (irxevent button clicks) (kind of working with LIRC and a lower resolution(64 Buttons), still quite buggy)
  • only one LIRC device for all codes (working)
After a while I recognized that to get both interfaces working under one LIRC device a had to change most of the lirc_imon structure, so I decided to put everything in a separate driver.

This is my result (updated, Oct 06, 2008 ):

Code: Select all

/*
    lirc_imontouch - LIRC plugin and input device driver for the Soundgraph IMON touchscreen and remote control
    Copyright (C) 2008 Rene Harder (rehar@saweb.de)
                 based on lirc_imon by Venky Raju(dev@venky.ws) http://www.venky.ws/projects/imon
   
    lirc_imontouch 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 3 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, see <http://www.gnu.org/licenses/>.
*/

#include <linux/version.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#include <linux/autoconf.h>

#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/usb/input.h>
#include <linux/timer.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
#include <asm/uaccess.h>
#else
#include <linux/uaccess.h>
#endif

#include <linux/usb.h>

#include <drivers/kcompat.h>
#include <drivers/lirc.h>
#include <drivers/lirc_dev/lirc_dev.h>

#define MOD_AUTHOR   "Rene Harder <rehar@saweb.de>"
#define MOD_DESC   "Driver for Soundgraph iMON Touchscreen and Remote Control iMON PAD"
#define MOD_NAME   "lirc_imontouch"
#define MOD_VERSION   "0.1b"

#define BUF_CHUNK_SIZE   8
#define BUF_SIZE   128

#define SUCCESS      0
#define   TRUE      1
#define FALSE      0

/*Prototypes*/

static int imontouch_probe(struct usb_interface *interface, const struct usb_device_id *id);
static void imontouch_disconnect(struct usb_interface *interface);

static void usb_rx_callback_intf0(struct urb *urb);
static void usb_rx_callback_intf1(struct urb *urb);


static DECLARE_MUTEX(disconnect_sem);
static int lircdev_open(void *data);
static void lircdev_close(void *data);

/*Module init and exit */
static int __init imontouch_init(void);
static void __exit imontouch_exit(void);
struct semaphore lock;

/* Globals*/


struct imontouch_context {
   struct usb_device *dev_intf0; /*display has two USB interfaces*/
   struct usb_device *dev_intf1;
   int lirc_isopen;         /* LIRC port open   */
   int lirc_isassociating;      /* LIRC port open for association */
   int dev_present_intf0;      /* USB interface 0 presence */
   int dev_present_intf1;      /* USB interface 1 presence */
   struct semaphore sem;      /* to lock this object */


   struct lirc_plugin *plugin;
   struct usb_endpoint_descriptor *rx_endpoint_intf0, *rx_endpoint_intf1;
   struct urb *rx_urb_intf0, *rx_urb_intf1;

   unsigned char usb_rx_buf[8];

   struct rx_data {
      int count;      /* length of 0 or 1 sequence */
      int prev_bit;      /* logic level of sequence */
      int initial_space;   /* initial space flag */
   } rx;
   struct input_dev *mouse;
   struct input_dev *touch;
   int is_mouse;
   int touch_x;
   int touch_y;
   char name[128];
   char phys[64];
   struct timer_list timer;
};

#define LOCK_CONTEXT   down(&context->sem)
#define UNLOCK_CONTEXT   up(&context->sem)
#define LOCK_DRIVER   down(&lock)
#define UNLOCK_DRIVER   up(&lock)
#define TOUCH_TIMEOUT 	(HZ/30)

static struct usb_device_id imontouch_usb_id_table[] = {
   { USB_DEVICE(0x15c2, 0x0034) },
   {}
};

/* USB Device data */
static struct usb_driver imontouch_driver = {
   LIRC_THIS_MODULE(.owner = THIS_MODULE)
   .name      = MOD_NAME,
   .probe      = imontouch_probe,
   .disconnect   = imontouch_disconnect,
   .id_table   = imontouch_usb_id_table,
};

static int debug;
static int use_lirc;
static int touch_passthrough;

/* Module */

MODULE_AUTHOR(MOD_AUTHOR);
MODULE_DESCRIPTION(MOD_DESC);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, imontouch_usb_id_table);

module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");

module_param(use_lirc, int, 1);
MODULE_PARM_DESC(use_lirc, "Use LIRC device as output for RC and Touchscreen: 0=no, 1=yes(default: yes) (not yet active");

module_param(touch_passthrough, int, 0);
MODULE_PARM_DESC(touch_passthrough, "passtrough the raw touchscreen data, otherwise make a lower resolution matrix for LIRC: 0=no, 1=yes (raw touchscreen data) default:no");
static inline void delete_context(struct imontouch_context *context)
{
   
   lirc_buffer_free(context->plugin->rbuf);
   kfree(context->plugin->rbuf);
   kfree(context->plugin);
   kfree(context);
   kfree(context->mouse);
   kfree(context->touch);

   if (debug)
      info("%s: context deleted", __FUNCTION__);
}
static inline void deregister_from_lirc(struct imontouch_context *context)
{
   int retval;
   int minor = context->plugin->minor;

   retval = lirc_unregister_plugin(minor);
   if (retval)
      err("%s: unable to deregister from lirc(%d)",
         __FUNCTION__, retval);
   else
      info("Deregistered iMON plugin(minor:%d)", minor);

}

static inline void incoming_lirc_packet(struct imontouch_context *context,  struct urb *urb, int intf){
   int len = urb->actual_length;
   unsigned char *buf = urb->transfer_buffer;
   char rel_x, rel_y;

   if(!context->is_mouse){
      /* generated PAD key codes:
         Mouse_N                  0x69 02 81 B7 00 68 1401
         Mouse_S                  0x68 82 91 B7 00 68 1401
         Mouse_W                  0x6A 82 81 B7 00 68 1401
         Mouse_E                  0x68 8A 81 B7 00 68 1401
         mouse buttons :
         MouseRightClick          0x68 84 81 B7 00 68 1401
         MouseLeftClick           0x68 83 81 B7 00 68 1401
      */
      if(buf[0] & 0x01  && !buf[1] && len==5){
      rel_x=buf[2];
      rel_y=buf[3];
      len=8;
         if (abs(abs(rel_x) - abs(rel_y)) < 10)
            return;   
         if(rel_x >= 14)
            {buf[0]=0x68; buf[1]=0x8A;buf[2]=0x1B;buf[3]=0xb7;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;}
         else if (rel_x <= -14)
            {buf[0]=0x6a; buf[1]=0x82;buf[2]=0x81;buf[3]=0xB7;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;}
         else if (rel_y >= 14)
            {buf[0]=0x68; buf[1]=0x82;buf[2]=0x91;buf[3]=0xB7;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;}
         else if (rel_y <= -14)
            {buf[0]=0x69; buf[1]=0x02;buf[2]=0x81;buf[3]=0x70;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;}
      
        }else if (buf[0] & 0x01  && buf[1]==0x01 && len==5){ //left mouse click
		len=8;
		buf[0]=0x68; buf[1]=0x83;buf[2]=0x81;buf[3]=0xB7;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;
	}else if (buf[0] & 0x01  && buf[1]==0x02 && len==5){ //right mouse click
		len=8;
		buf[0]=0x68; buf[1]=0x84;buf[2]=0x81;buf[3]=0xB7;buf[4]=0x00;buf[5]=0x68;buf[6]=0x14;buf[7]=0x01;
	}
      
      }
   if (len == 5) {
      buf[5]=0x00;
      buf[6]=0x00;
      buf[7]=0x00;
      len=8;
   }
   if (len != 8) {
      warn("%s: invalid incoming packet size(%d)",
           __FUNCTION__, len);
      return;
   }
   if (!buf[1] && !buf[2] && !buf[3] && !buf[4] && !buf[5] && !buf[6]  && !buf[7] && !intf )
      return;
   if (((buf[2]>>6) & 0x01) && intf && (buf[7] != 0x86))
      return;
   if(buf[6]==0x14 && buf[7]==0x86 && intf){ /*handle touchscreen, generate button events for LIRC, in lower resolution */
      if (!touch_passthrough){
         /*generate higher resolution touchscreen to generate button events (256 buttons)
        buf[0]=buf[0]>>4; buf[1]=0x00;buf[2]=buf[2]>>4;buf[3]=0x00;buf[4]=0x00;buf[5]=0x00;buf[6]=0x14;buf[7]=0xff;   */

        /*generate lower resolution touchscreen to generate button events (64 buttons)*/
        buf[0]=buf[0]>>5; buf[1]=0x00;buf[2]=buf[2]>>5;buf[3]=0x00;buf[4]=0x00;buf[5]=0x00;buf[6]=0x14;buf[7]=0xff;
      }
   }
   lirc_buffer_write_1(context->plugin->rbuf, buf);
   wake_up(&context->plugin->rbuf->wait_poll);
   return;


}

static void display_timeout(struct imontouch_context *context){
	struct input_dev *touch=NULL;

	touch = context->touch;	
	if (!context->touch) /*no touchscreen input generated, something went wrong*/
		return;
	input_report_abs(touch, ABS_X, context->touch_x);
	input_report_abs(touch, ABS_Y, context->touch_y);
	input_report_key(touch, BTN_TOUCH, 0x00);
	input_sync(touch);
}

static void usb_rx_callback_intf0(struct urb *urb){
   struct imontouch_context *context;
   unsigned char *buf = urb->transfer_buffer;
     char rel_x, rel_y;
   int len = urb->actual_length;
   struct input_dev *mouse=NULL;
   int i;
     
   if (len == 5) {
      buf[5]=0x00;
      buf[6]=0x00;
      buf[7]=0x00;
      len=8;
   }
   if (len != 8) {
      warn("%s: invalid incoming packet size(%d)",
           __FUNCTION__, len);
      return;
   }      
     if (!urb)
        return;
   
   context = (struct imontouch_context *) urb->context;
   
     if (!context){
      
        goto exit;
   }
   mouse = context->mouse;

   if (!context->mouse){ /*no mouse input generated, something went wrong*/
      
        goto exit;
   }
   switch (urb->status){

   case -ENOENT:   /* usb unlink*/
      
      return;
   case SUCCESS:

   if(debug){
           printk("lirc_imontouch %s incomming packet: ",__FUNCTION__ );
           for(i=0;i<8;i++)
           {
                    printk("%02x ",buf[i]);
           }
		   printk("\n");
     }	                       
      if(buf[0] & 0x01  && context->is_mouse){ /* handle IMON PAD to generate mouse events*/

         input_report_key(mouse, BTN_LEFT,   buf[1] & 0x01);
         input_report_key(mouse, BTN_RIGHT,  buf[1] >> 2 & 0x01);
         rel_x=buf[2];
         rel_y=buf[3];
         
         input_report_rel(mouse, REL_X,     rel_x);
         input_report_rel(mouse, REL_Y,     rel_y);
         input_sync(mouse);
      }else{
         if (context->lirc_isopen)
            incoming_lirc_packet(context, urb,0);

         
      }
      break;
   default :
      warn("%s unknown urb status", __FUNCTION__);
      break;
   }
exit:
   usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
   usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
   
}

static void usb_rx_callback_intf1(struct urb *urb){
   struct imontouch_context *context;
   unsigned char *buf = urb->transfer_buffer;
   int len = urb->actual_length;
   struct input_dev *touch=NULL;
   int i;
   
   if (len != 8) {
      warn("%s: invalid incoming packet size(%d)",
           __FUNCTION__, len);
      return;
   }   
   if (!urb)
        return;
   
     context = (struct imontouch_context *) urb->context;
     if (!context){
      
        goto exit;
   }
   touch = context->touch;   
   if (!context->touch){ /*no touchscreen input generated, something went wrong*/
      
        goto exit;
   }

   switch (urb->status){

   case -ENOENT:   /* usb unlink*/
      
      return;
   case SUCCESS:
        if(debug){
           printk("lirc_imontouch %s incomming packet: ",__FUNCTION__ );
           for(i=0;i<8;i++)
           {
                    printk("%02x ",buf[i]);
           }
		   printk("\n");
     }
      if(buf[0] ==0x29 && buf[1]==0x91 && (buf[2]==0x35 || buf[2]==0x15) && buf[3]==0xb7 && !buf[4] && !buf[5] &&buf[6]==0x14 && buf[7]==0x01 ){ /*switch mouse/keyboard*/
         context->is_mouse= (~context->is_mouse) & 0x01;
         if(debug)
                  info ("%s tongle mouse %d",__FUNCTION__,context->is_mouse);
                  
         goto exit;
         
      }
      
      if(buf[6]==0x14 && buf[7]==0x86){ /*handle touchscreen, map to input device */

			mod_timer(&context->timer,jiffies+TOUCH_TIMEOUT);

			context->touch_x=buf[0]<<4 | (buf[1]>>4);
			context->touch_y=0xfff-((buf[2]<<4) | (buf[1]&0xf));
			
			input_report_abs(touch, ABS_X, context->touch_x);
			input_report_abs(touch, ABS_Y, context->touch_y);
			input_report_key(touch, BTN_TOUCH, 0x01);
			input_sync(touch);
      }
      if (context->lirc_isopen)
         incoming_lirc_packet(context, urb,1);

         
      break;
   default :
      warn("%s unknown urb status", __FUNCTION__);
      break;
   }
exit:
   usb_submit_urb(context->rx_urb_intf1, GFP_ATOMIC);
   usb_submit_urb(context->rx_urb_intf0, GFP_ATOMIC);
   
}


static int imontouch_probe(struct usb_interface *interface, const struct usb_device_id *id){
   
   struct usb_device *dev = NULL;
   struct usb_host_interface *iface_desc = NULL;
   struct urb *rx_urb = NULL;
   struct lirc_plugin *plugin = NULL;
   struct lirc_buffer *rbuf = NULL;
   struct usb_interface *first_if;
   int ifnum;
   int err;
   int lirc_minor=-1;
   int retval;
   int minor_first_intf=-1;
   int num_endpoints;
   int alloc_status;
   struct imontouch_context *context = NULL;
   struct imontouch_context *first_if_context=NULL;
   
   dev = usb_get_dev(interface_to_usbdev(interface));
   iface_desc = interface->cur_altsetting;
   num_endpoints = iface_desc->desc.bNumEndpoints;
   ifnum=iface_desc->desc.bInterfaceNumber;
   info("%s: found IMON device, %d. interface", __FUNCTION__, ifnum);
   
   alloc_status=SUCCESS;
   if(!ifnum)
     first_if=usb_ifnum_to_if(dev,1);
   else
     first_if=usb_ifnum_to_if(dev,0);

   LOCK_DRIVER;

   first_if_context=(struct imontouch_context *)usb_get_intfdata(first_if);
   
   if(!first_if_context){
      info("%s: first usb interface not yet registered", __FUNCTION__);
      
      context = kmalloc(sizeof(struct imontouch_context), GFP_KERNEL);
      if (!context) {
         err("%s: kmalloc failed for context", __FUNCTION__);
         alloc_status = 1;
         goto alloc_status_switch;
      }
      plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL);
      if (!plugin) {
         err("%s: kmalloc failed for lirc_plugin", __FUNCTION__);
         alloc_status = 2;
         goto alloc_status_switch;
      }
      rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
      if (!rbuf) {
         err("%s: kmalloc failed for lirc_buffer", __FUNCTION__);
         alloc_status = 3;
         goto alloc_status_switch;
      }
      if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
         err("%s: lirc_buffer_init failed", __FUNCTION__);
         alloc_status = 4;
         goto alloc_status_switch;
      }
      
      rx_urb = usb_alloc_urb(0, GFP_KERNEL);
      if (!rx_urb) {
         err("%s: usb_alloc_urb failed for IR urb", __FUNCTION__);
         alloc_status = 5;
         goto alloc_status_switch;
      }
       
   
      memset(context, 0, sizeof(struct imontouch_context));
      init_MUTEX(&context->sem);

      memset(plugin, 0, sizeof(struct lirc_plugin));
   
      strcpy(plugin->name, MOD_NAME);
      plugin->minor = -1;
      plugin->code_length = 64;
      plugin->sample_rate = 0;
      plugin->features = LIRC_CAN_REC_LIRCCODE;
      plugin->data = context;
      plugin->rbuf = rbuf;
      plugin->set_use_inc = lircdev_open;
      plugin->set_use_dec = lircdev_close;
      context->lirc_isopen=0;
      plugin->owner = THIS_MODULE;
      LOCK_CONTEXT;
      context->plugin = plugin;
      context->is_mouse=1;
      init_timer(&context->timer);
      context->timer.data=(unsigned long)context;
      context->timer.function=display_timeout;
      
      lirc_minor = lirc_register_plugin(plugin);
      if (lirc_minor < 0) {
         err("%s: lirc_register_plugin failed", __FUNCTION__);
         alloc_status = 5;
         UNLOCK_CONTEXT;
         goto alloc_status_switch;
      } else
         info("%s: Registered imontouch plugin(minor:%d)",__FUNCTION__, lirc_minor);

      plugin->minor = lirc_minor;
      UNLOCK_CONTEXT;
      

      
   }else{
          if(first_if_context->plugin){
         minor_first_intf=first_if_context->plugin->minor;
         info("%s: plugin dectected, LIRC minor: %d", __FUNCTION__,minor_first_intf);
         
         rx_urb = usb_alloc_urb(0, GFP_KERNEL);
         if (!rx_urb) {
            err("%s: usb_alloc_urb failed for urb", __FUNCTION__);
            alloc_status = 5;
            goto alloc_status_switch;
         }
         
         context=first_if_context;
         
         }
   }
   
   info("%s: imontouch device connected, minor %d", __FUNCTION__, context->plugin->minor);
   LOCK_CONTEXT;
   if(!ifnum)  {
      context->dev_intf0 = dev;
      context->dev_present_intf0 = TRUE;
      context->rx_urb_intf0 = rx_urb;
      context->rx_endpoint_intf0= &iface_desc->endpoint[0].desc;

      
      usb_fill_int_urb(rx_urb, context->dev_intf0, usb_rcvintpipe(context->dev_intf0,   context->rx_endpoint_intf0->bEndpointAddress), context->usb_rx_buf, sizeof(context->usb_rx_buf),usb_rx_callback_intf0, context, context->rx_endpoint_intf0->bInterval);
      retval = usb_submit_urb(context->rx_urb_intf0, GFP_KERNEL);
      context->mouse= input_allocate_device();
      snprintf(context->name, sizeof(context->name), "iMON PAD IR Mouse %04x:%04x", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct));
      usb_make_path(dev, context->phys, sizeof(context->phys));
      strlcat(context->phys, "/input0", sizeof(context->phys));

      context->mouse->phys=context->phys;
      context->mouse->name=context->name;
      context->mouse->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
      context->mouse->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
      context->mouse->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
      context->mouse->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
      context->mouse->relbit[0] |= BIT_MASK(REL_WHEEL);
      //context->mouse.private = context; // maybe better
      input_set_drvdata(context->mouse, context);

      usb_to_input_id(dev, &context->mouse->id);
      context->mouse->dev.parent = &interface->dev;
      //context->mouse.dev.dev = &dev->dev;
      input_register_device(context->mouse);
      
      

   }else{
      context->dev_intf1 = dev;
      context->dev_present_intf1 = TRUE;
      context->rx_urb_intf1 = rx_urb;
      context->rx_endpoint_intf1= &iface_desc->endpoint[0].desc;

      usb_fill_int_urb(rx_urb, context->dev_intf1, usb_rcvintpipe(context->dev_intf1,   context->rx_endpoint_intf1->bEndpointAddress), context->usb_rx_buf, sizeof(context->usb_rx_buf),usb_rx_callback_intf1, context, context->rx_endpoint_intf1->bInterval);
      retval = usb_submit_urb(context->rx_urb_intf1, GFP_KERNEL);

      context->touch= input_allocate_device();
      snprintf(context->name, sizeof(context->name), "USB Touchscreen %04x:%04x", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct));
      usb_make_path(dev, context->phys, sizeof(context->phys));
      strlcat(context->phys, "/input0", sizeof(context->phys));

      context->touch->phys=context->phys;
      context->touch->name=context->name;
      context->touch->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
      context->touch->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
      input_set_abs_params(context->touch, ABS_X, 0x00, 0xfff, 0, 0);
      input_set_abs_params(context->touch, ABS_Y, 0x00, 0xfff, 0, 0);
      
      input_set_drvdata(context->touch, context);
      
      usb_to_input_id(dev, &context->touch->id);
      context->touch->dev.parent = &interface->dev;
      err=input_register_device(context->touch);
   
      if (err) {
         info("%s - input_register touchscreen failed, err: %d", __FUNCTION__, err);
         
      }
   }
   //context->rx_endpoint = rx_endpoint;
   
   if (retval)
        err("%s: usb_submit_urb failed for module probe (%d)",
             __FUNCTION__, retval);
     


   usb_set_intfdata(interface, context);

   UNLOCK_CONTEXT;

alloc_status_switch:

   switch (alloc_status) {
   case 5:
      lirc_buffer_free(rbuf);
   case 4:
      kfree(rbuf);
   case 3:
      kfree(plugin);
   case 2:
      kfree(context);
      context = NULL;
   case 1:
      retval = -ENOMEM;
   case SUCCESS:
      ;
   }


UNLOCK_DRIVER;   
return 0;   
}


static void imontouch_disconnect(struct usb_interface *interface){
   struct imontouch_context *context;
   int ifnum;
   /* prevent races with ir_open()/vfd_open() */
   LOCK_DRIVER;


   context = usb_get_intfdata(interface);
   ifnum=interface->cur_altsetting->desc.bInterfaceNumber;
   LOCK_CONTEXT;


   if(!ifnum){
      
      context->dev_present_intf0 = FALSE;
      usb_kill_urb(context->rx_urb_intf0);
      input_unregister_device (context->mouse);
      if (context->dev_present_intf1){
         if(debug)   
            info ("interface 1 still activated");
         
         UNLOCK_CONTEXT;
         
      }else{
         /* De-register from lirc_dev if IR port is not open */
         if (!context->lirc_isopen)
            deregister_from_lirc(context);
            
         del_timer_sync(&context->timer);
         UNLOCK_CONTEXT;

         info("%s: imontouch device disconnected", __FUNCTION__);
      }
   }else{

      context->dev_present_intf1 = FALSE;
      usb_kill_urb(context->rx_urb_intf1);
      input_unregister_device (context->touch);
      if (context->dev_present_intf0){
         if(debug)
            info ("interface 0 still activated");
         UNLOCK_CONTEXT;
         
      }else{
         /* De-register from lirc_dev if IR port is not open */
         if (!context->lirc_isopen)
            deregister_from_lirc(context);
            
         del_timer_sync(&context->timer);                        
         UNLOCK_CONTEXT;

         info("%s: imontouch device disconnected", __FUNCTION__);
      }





   }
   

   usb_set_intfdata(interface, NULL);   
   UNLOCK_DRIVER;

}

static int lircdev_open(void *data){
   struct imontouch_context *context;
   int retval = SUCCESS;

   LOCK_DRIVER;
   context = (struct imontouch_context *) data;
   LOCK_CONTEXT;

   if (context->lirc_isopen) {
      err("%s: LIRC port is already open", __FUNCTION__);
      retval = -EBUSY;
      goto exit;
   }
   /* initial IR protocol decode variables */
   context->rx.count = 0;
   context->rx.initial_space = 1;
   context->rx.prev_bit = 0;
   MOD_INC_USE_COUNT;
   context->lirc_isopen = TRUE;
   info("LIRC port opened");


exit:
   UNLOCK_CONTEXT;
   UNLOCK_DRIVER;
   return retval;


};
static void lircdev_close(void *data){
struct imontouch_context *context;

   context = (struct imontouch_context *) data;
   if (!context) {
      err("%s: no context for device", __FUNCTION__);
      return;
   }
   LOCK_CONTEXT;
   context->lirc_isopen = FALSE;
   info("LIRC port closed");
   MOD_DEC_USE_COUNT;
   

   if (!context->dev_present_intf0 & !context->dev_present_intf1) {
      /* Device disconnected while LIRC port was
       * still open. Plugin was not deregistered
       * at disconnect time, so do it now. */
      usb_kill_urb(context->rx_urb_intf1);
      input_unregister_device (context->touch);
      usb_kill_urb(context->rx_urb_intf0);
      input_unregister_device (context->mouse);
      deregister_from_lirc(context);
   }


   UNLOCK_CONTEXT;
   return;

};

static int __init imontouch_init(void)
{
   int resource;

   info(MOD_DESC ", v" MOD_VERSION);
   info(MOD_AUTHOR);
   init_MUTEX(&lock);
   resource = usb_register(&imontouch_driver);
   if (resource) {
      err("%s: usb register failed(%d)", __FUNCTION__, resource);
      return -ENODEV;
   }
   return SUCCESS;
}

static void __exit imontouch_exit(void)
{
   usb_deregister(&imontouch_driver);
   info("module removed.");
}


module_init(imontouch_init);
module_exit(imontouch_exit);

NOTE:
  • This driver works with linux kernel 2.6.25 (other not yet tested )and is still buggy. Every now and then if you unload the module you get a segmentation fault or the X-Server crashes. I'm working on it.
  • You have to deactivate the usbhid driver for this device I use “modprobe usbhid quirks=0x15c2:0x0034:0x0004” otherwise the usbhid driver owns the device and this driver will not work.
My next goal is to get the driver more stable (the unload problem) and get a working input driver for the touchscreen. I'm still analyzing the touchscreen data. I could determine two byte which are related to the x and y coordinates but the rest is still mysterious.

At the end I like to Thank:
  • Venky for a great lirc_imon driver which most of this code is based on
  • DataPath the hole mouse handling is based on your imon_mouse patch.
Update, Oct 06, 2008
  • touchscreen input device support added
  • added another remote control for mouse/keyboard switch (seems like there are a couple of different iMon PAD remote control revisions)
  • fixed the unload problem
  • added support for kernel 2.6.27 (updated lirc-dev)
Attachments
lirc_imontouch-0.1b.tar.gz
(16.89 KiB) Downloaded 1185 times
Last edited by rehar on Mon Oct 06, 2008 7:05 pm, edited 3 times in total.

rikardo
Posts: 1
Joined: Tue Sep 16, 2008 4:50 am

Re: iMON Touch LCD

Post by rikardo » Fri Oct 03, 2008 5:19 am

hello, i tried your driver and it works, but only part of what you described works. the only part
that is working for me is that driver doesn't stop working, it receives commands after i touch any
non mouse buttons - remote buttons. but when i switch to keyboard mode arrow keys do not work and remote
buttons also do not work. what could be the problem

thanks, rikardo

simon
Posts: 2
Joined: Wed Sep 17, 2008 9:13 pm

Re: iMON Touch LCD

Post by simon » Fri Oct 03, 2008 7:09 am

Thanks for your work so far on this, I'll be trying it sometime in the next week. Keep us up to date on any further progress and let me know if you need any help testing.

rehar
Posts: 3
Joined: Sun Sep 07, 2008 12:12 pm

Re: iMON Touch LCD

Post by rehar » Mon Oct 06, 2008 6:45 pm

@rikardo
So you will not get the arrow keycodes after you pressed the mouse/keyboard switch, is this right? I found out that there are a couple different revisions of the iMon PAD remote control. Maybe you have one which uses a different code. Can you record the code for the mouse/keyboard button with irrecord? If, then let me know which code it is so that i can add it.
In the updated driver I already added a second remote control maybe that's yours.

@simon
Great, please let me know if the driver worked for you.

rk111
Posts: 3
Joined: Sat Nov 01, 2008 4:53 am

Re: iMON Touch LCD

Post by rk111 » Fri Nov 21, 2008 9:16 am

finally this made my zalman HD160XT Plus working 100% on top of mythbuntu 8.10.
I can now use the touchscreen as mouse, use the the front buttons as well as the imon remote.

thx a lot for that great work.

I had to use the "plpevtch" xorg driver (http://www.plop.at/de/touchscreen.html) instead of the default "evtouch" one to let the touchscreen work as mouse under x.
evtouch did not like it for whatever reason. Also some minor code tweaks were required because the zahlman did send a bit different events for switching the remote between mouse and cursor and other stuff.

simon
Posts: 2
Joined: Wed Sep 17, 2008 9:13 pm

Re: iMON Touch LCD

Post by simon » Fri Nov 28, 2008 1:23 am

Can you post the code changes that you needed to make? I think I am running into similar issues.

volle
Posts: 1
Joined: Tue Feb 03, 2009 11:31 am

Re: iMON Touch LCD

Post by volle » Fri Feb 06, 2009 12:00 pm

Hi all,
I have Zalman HD160XT PLUS case with 7" lcd display and I running it on Mythbuntu 8.10 with kernel 2.6.27-11. I have tried to make the touch screen, the remote control and front panel buttons to work, but far with little success.
First of all, I am bit confused what exactly I should need to install. lsusb recognize my device as Bus 004 Device 004: ID 15c2:0034 SoundGraph Inc, so I tried to compile lirc_imontouch and finally succeed. I can move the mouse cursor on the screen with my remote control, but that's all. Any ideas what to next? Do I need lcdproc? How many lircs there should be? How do I know that I have installed everything OK?

I would be more than happy about any instructions.

I've have some experience using both linux and unix systems, but compiling drivers and other stuff is bit new to me.

rk111
Posts: 3
Joined: Sat Nov 01, 2008 4:53 am

Re: iMON Touch LCD

Post by rk111 » Sat Feb 07, 2009 9:11 am

simon, attached you can find the diff that was required for my zalman HD160XT PLUS to get
a) the touchscreen working
b) the remote working correctly

(this might not work for all revisions of the zalman, my one is quite new)

I am using
- mythbuntu 8.10
- the event driver plpevt from http://www.plop.at/de/touchscreen.html for the touchscreen xorg event access
- the attached lirc_imontouch-0.1b.tar.gz modified with the patch attached for low level access to the touchscreen and IR remote.

The touchscreen entry in xorg.con looks like:

Code: Select all

Section "InputDevice"
    Identifier     "touchscreen"
    Driver         "plpevtch"
    Option         "Device" "/dev/input/event7"
    Option         "DeviceName" "touchscreen"
    Option         "MinX" "62"
    Option         "MinY" "279"
    Option         "MaxX" "3930"
    Option         "MaxY" "3958"
EndSection
you can calibrate it as described within the plpevtch docs
Unfortunately the touchscreen does not show up as event7 all the time I boot and I was not able to set up a udev rule to just rename it.
So I created a udev rule which creates a symblink - unfortunately the symblink does not work with Xorg - so I run a small script on boot that checks the symblink and "sed's" the xorg conf with the correct device:

Code: Select all

/etc/udev/rules.d/20-touchscreen.rules:KERNEL=="event*", SUBSYSTEM=="input", ATTRS{phys}=="usb-0000:00:1a.1-1/input0", SYMLINK+="input/eventT"

Code: Select all

#!/bin/bash
TEVDEV=`/bin/ls -l /dev/input/|grep eventT|awk '{print $10}'`
sed -e "s/\/dev\/input\/event[0-9]/\/dev\/input\/$TEVDEV/" /etc/X11/xorg.conf >/etc/X11/xorg.conf
(any other succestions are welcome, unfortunately xorg does not like to get pointed to the /dev/input/eventT symblink and I was unable to create a udev rule that just renames the device)

additional hints:

Code: Select all

/etc/udev/rules.d/etc/modprobe.d/options:#ignore touchscreen, will captured by lirc_imontouch
/etc/udev/rules.d/etc/modprobe.d/options:#options lirc_imontouch touch_passthrough=1
/etc/lirc/hardware.conf:REMOTE_MODULES="lirc_dev lirc_imontouch"
/etc/modprobe.d/options:#ignore touchscreen, will captured by lirc_imontouch
/etc/modprobe.d/options:options usbhid quirks=0x15c2:0x0034:0x0004
/usr/lib/xorg/modules/drivers/plpevtch_drv.so
Attachments
HD160XTPLUS.patch.gz
patch that was required for my Zalman HD160XT Plus
(1.31 KiB) Downloaded 902 times

rehar
Posts: 3
Joined: Sun Sep 07, 2008 12:12 pm

Re: iMON Touch LCD

Post by rehar » Fri Feb 13, 2009 3:42 pm

Hi rk111


You could try the following udev rule to force a different device node name

Code: Select all

KERNEL=="event*", SUBSYSTEM=="input", ATTRS{phys}=="usb-0000:00:1a.1-1/input0", NAME+="input/eventT"

rehar

rk111
Posts: 3
Joined: Sat Nov 01, 2008 4:53 am

Re: iMON Touch LCD

Post by rk111 » Sun Mar 08, 2009 6:39 am

thats what I tried but it does not seem to even think about creating my eventT file under /dev/input while the symblink rule works quite well

Post Reply