patch to make the imon PAD generate mouse events

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

Moderator: Venky

Post Reply
DataPath
Posts: 34
Joined: Thu Mar 24, 2005 11:16 am
Location: ::1
Contact:

patch to make the imon PAD generate mouse events

Post by DataPath » Tue Jun 21, 2005 7:31 pm

apply the following patch to lirc_imon.c to cause it to generate mouse events on /dev/input/mice

First, credit where credit is due:
* Venky, for putting his time and energy into developing and maintaining the driver for all of us
* Vojtech Pavlik for his eminently readable usbmouse.c HID mouse driver, from which this is derived

I have tested this patch, and know the following to be true:
* This patch fixes the ugly module unload error relating to usb_unlink_urb
* The PAD and the L and R mouse click do not generate lirc events
* The PAD and the L and R mouse click DO generate output on /dev/input/mice
* The PAD and the L and R mouse click DO work under Xorg set to read from /dev/input/mice on my system

Caveats:
* Mouse only works while a lirc client is connected

Some things on my TODO list:
* make the input system hijack lirc events dependent on a module option ( or the mouse/keyboard button)
* allow volume/channel +/- buttons to be used as scroll wheel or additional mouse buttons
* make it possible to have certain other buttons emit keyboard codes (escape, tab, shift+tab, backspace, numbers, etc)

lirc_imon_plus_mouse.patch

Code: Select all

--- lirc_imon-0.7.1.c   2005-06-21 22:25:08.000000000 -0500
+++ lirc_imon-exp.c     2005-06-21 22:27:14.000000000 -0500
@@ -53,6 +53,7 @@

 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/input.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -161,6 +162,8 @@
                atomic_t busy;               /* write in progress         */
                int status;                  /* status of tx completion   */
        } tx;
+
+       struct input_dev mouse;
 };

 #define LOCK_CONTEXT   down (&context ->sem)
@@ -616,7 +619,7 @@

        LOCK_CONTEXT;

-       usb_unlink_urb (context ->rx_urb);
+       usb_kill_urb (context ->rx_urb);
        context ->ir_isopen = FALSE;
        MOD_DEC_USE_COUNT;
        info ("IR port closed");
@@ -700,11 +703,28 @@
 #endif

        if (context ->ir_onboard_decode) {
-
-               /* The signals have been decoded onboard the iMON controller */
-
-               lirc_buffer_write_1 (context ->plugin ->rbuf, buf);
-               wake_up (&context ->plugin ->rbuf ->wait_poll);
+               if(buf[0] & 0x40) {
+                       struct input_dev *mouse = &context->mouse;
+                       char rel_x = 0, rel_y = 0;
+                       rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+                       rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+                       if(buf[0] & 0x02) rel_x |= ~0x10+1;
+                       if(buf[0] & 0x01) rel_y |= ~0x10+1;
+
+                       input_report_key(mouse, BTN_LEFT,   buf[1] & 0x01);
+                       input_report_key(mouse, BTN_RIGHT,  buf[1] >> 2 & 0x01);
+                       /*input_report_key(mouse, BTN_MIDDLE, buf[0] & 0x04);*/
+                       /*input_report_key(mouse, BTN_SIDE,   buf[0] & 0x08);*/
+                       /*input_report_key(mouse, BTN_EXTRA,  buf[0] & 0x10);*/
+                       input_report_rel(mouse, REL_X,     rel_x);
+                       input_report_rel(mouse, REL_Y,     rel_y);
+                       /*input_report_rel(mouse, REL_WHEEL, buf[3]);*/
+                       input_sync(mouse);
+               } else {
+                       /* The signals have been decoded onboard the iMON controller */
+                       lirc_buffer_write_1 (context ->plugin ->rbuf, buf);
+                       wake_up (&context ->plugin ->rbuf ->wait_poll);
+               }
                return;
        }

@@ -1082,6 +1102,22 @@
 #endif
        }

+       /* register with kernel input system for PAD mouse */
+       context->mouse.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       context->mouse.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+       context->mouse.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+       context->mouse.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+       context->mouse.relbit[0] |= BIT(REL_WHEEL);
+       context->mouse.private = context;
+
+       context->mouse.id.bustype = BUS_USB;
+       context->mouse.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+       context->mouse.id.product = le16_to_cpu(dev->descriptor.idProduct);
+       context->mouse.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+       context->mouse.dev = &dev->dev;
+
+       input_register_device(&context->mouse);
+
        info ("%s: iMON device on usb<%d:%d> initialized",
                        __FUNCTION__, dev ->bus ->busnum, dev ->devnum);

@@ -1125,12 +1161,12 @@
        context ->dev_present = FALSE;

        /* Stop reception */
-       usb_unlink_urb (context ->rx_urb);
+       usb_kill_urb (context ->rx_urb);

        /* Abort ongoing write */
        if (atomic_read (&context ->tx.busy)) {

-               usb_unlink_urb (context ->tx_urb);
+               usb_kill_urb (context ->tx_urb);
                wait_for_completion (&context ->tx.finished);
        }

@@ -1147,6 +1183,8 @@
 #endif
        }

+       input_unregister_device (&context ->mouse);
+
        UNLOCK_CONTEXT;

        if (!context ->ir_isopen && !context ->vfd_isopen)
Venky: Do you know how to make this so it's not dependent on a lirc client being connected?
Last edited by DataPath on Wed Jun 22, 2005 4:10 pm, edited 2 times in total.

DataPath
Posts: 34
Joined: Thu Mar 24, 2005 11:16 am
Location: ::1
Contact:

Post by DataPath » Tue Jun 21, 2005 7:40 pm

As it turns out, I had this patch working a couple weeks ago, and didn't realize it because I didn't have a lirc client connected while I was trying to test the mouse functionality.

notz
Posts: 2
Joined: Fri Jun 24, 2005 3:21 am
Location: Graz, Austria

Post by notz » Fri Jun 24, 2005 3:32 am

great work datapath :-)

i have made patch, based on your patch, that only pass one hex code to lirc for each direction. the patch works, but sometimes the repeat counter in lirc doesn't count or startes again on zero while i press in one direction. i don't know why (pherhaps there is a time gap to configure).

i think that these to patches should be merged and the mode should be switched by the mouse/keyboard button.

my patch (not nice, but works):

Code: Select all

--- drivers/lirc_imon/lirc_imon_orig.c  2005-04-16 20:10:02.000000000 +0200
+++ drivers/lirc_imon/lirc_imon.c       2005-06-24 02:08:04.000000000 +0200
@@ -703,6 +703,44 @@

                /* The signals have been decoded onboard the iMON controller */

+               if(buf[0] & 0x40) {
+                 int rel_x = 0, rel_y = 0;
+                 rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+                 rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+                 if(buf[0] & 0x02) rel_x |= ~0x10+1;
+                 if(buf[0] & 0x01) rel_y |= ~0x10+1;
+
+                  if(buf[1] & 0x01 || buf[1] >> 2 & 0x01) {
+                   /* mouse pressed */
+                 } else {
+                   if(rel_y>3 && rel_y-abs(rel_x)>3) { /* mouse s */
+                     buf[0] = 0x68;
+                     buf[1] = 0x82;
+                     buf[2] = 0x91;
+                   } else {
+                     if(-rel_y>3 && -rel_y-abs(rel_x)>3) { /* mouse n */
+                        buf[0] = 0x69;
+                        buf[1] = 0x02;
+                        buf[2] = 0x81;
+                     } else {
+                       if(rel_x>3 && rel_x-abs(rel_y)>3) { /* mouse e */
+                         buf[0] = 0x68;
+                         buf[1] = 0x8A;
+                         buf[2] = 0x81;
+                       } else {
+                         if(-rel_x>3 && -rel_x-abs(rel_y)>3) { /* mouse w */
+                           buf[0] = 0x6A;
+                           buf[1] = 0x82;
+                           buf[2] = 0x81;
+                         } else {
+                           return; /* discard those key codes */
+                         }
+                       }
+                     }
+                   }
+                 }
+               }
+
                lirc_buffer_write_1 (context ->plugin ->rbuf, buf);
                wake_up (&context ->plugin ->rbuf ->wait_poll);
                return;

Guest

Post by Guest » Thu Jun 30, 2005 5:24 am

I want to be able to use the "PAD" (mouse thing) as normal inputs for lirc..so i can use UP/DOWN/LEFT/RIGHT etc..is that possible ? i want to use it to navigate menus in freevo. freevo doesnt have mouse support, just keyboard and lirc.

notz
Posts: 2
Joined: Fri Jun 24, 2005 3:21 am
Location: Graz, Austria

Post by notz » Thu Jun 30, 2005 8:39 am

Anonymous wrote:I want to be able to use the "PAD" (mouse thing) as normal inputs for lirc..so i can use UP/DOWN/LEFT/RIGHT etc..is that possible ?
the above patch do this. but there is a problem with the repeat counter. so it's not 100 percent working.

Guest

Post by Guest » Thu Jun 30, 2005 3:07 pm

so , that is being worked on too ? excellent. i feared that people where only working on getting it working as a 'mouse'.

This is from the manafacturers site:

"Instead of the stick controller of iMON RSC, iMON PAD remote controller installs the PAD controller which generates more precise and pressure dependent mouse cursor movement. The PAD controller’s response rate is also about 30ms and is equivalent to that of Mouse. The PAD controller is designed to be able to be also used as four (up, down, left, right) arrow buttons on Keyboard."

So it seems that it must be 'switchable' between those modes i assume ?

I hope this gets worked out! Thanks in advance!

Guest

Post by Guest » Thu Jun 30, 2005 3:42 pm

well i suppose thats what the mouse/keyboard button on the remote is for ?
i can only assume that this tells the windows software how to interpret the codes it recieves ?

Reverse engineering that software is not an option?

Guest

Post by Guest » Thu Jun 30, 2005 5:37 pm

About merging the two above patches, that sounds like a great idea. But which mode will the remote be in by default ? or do you think we should be able to configure that somewhere ? personally, id like it in 'navigation mode' by default, and be able to switch to 'mouse mode' if i want.

Your thoughts ?

Guest

Post by Guest » Thu Jun 30, 2005 11:55 pm

Ok, i have downloaded your patch, applied it, and setup lirc with it. i have got what i think are good codes, as up/down/left/right work all the time now. there was ALOT of repeat events though, so i set a lircrc block as follows.

begin
prog = freevo
button = Up
config = UP
repeat = 0
end

that stops any repeats from happening at all, when pressing Up within freevo.
it tells freevo/lirc to ignore any repeats, until it receives a KeyRelease i assume..

Anyway, works perfectly. If you want my lircd.conf, im about to post it at http://stuff.wishie.net/

DataPath
Posts: 34
Joined: Thu Mar 24, 2005 11:16 am
Location: ::1
Contact:

Post by DataPath » Fri Jul 01, 2005 5:21 am

I'm working on an update to the patch so that you would only get the real mouse-like behavior if you load the driver with a special option.

No option, or option=0, and the driver does nothing special - outputs all the raw values

option = 1, and it will output to lirc just up/down y, left/right x
option = 2, and it will operate as a mouse with left and right click, toggled back to option 0 (or option 1) by the keyboard/mouse button
option = 3, and it will operate as a mouse, and many other buttons act as keyboard buttons, toggled back to option 0 (or option 1) by the keyboard/mouse button

wishie
Posts: 4
Joined: Fri Jul 01, 2005 5:52 am
Location: Australia
Contact:

Patch and lircd.conf uploaded

Post by wishie » Fri Jul 01, 2005 5:59 am

I have now uploaded the patch to lirc (by datapath, then edited by notz) to http://stuff.wishie.net/lirc-imon-pad.patch . I have also uploaded my config file (that i have tested, and am happy with the results) to http://stuff.wishie.net/lircd.conf .

Now, this did produce quite a few repeat events for the Up/Down/Left/Right, so in my lircrc file (where you specify what each button does) i set repeat to 0, as mentioned above. I have been testing this setup on my Accent HT-400G for the last 5-6 hours, with no complaints as yet.

Thank you DataPath and notz for your great work, keep it up!
Im looking forward to your new patch version DataPath, although i personally dont see a need for mouse type movement.
The day Microsoft makes something that doesnt suck, is the day they make a vacuum cleaner.

DataPath
Posts: 34
Joined: Thu Mar 24, 2005 11:16 am
Location: ::1
Contact:

Post by DataPath » Fri Jul 01, 2005 7:28 pm

Well, I know I use it. I use it as a mouse for my Linux box, to run apps. I start ZSNES, load a game, and then use my gamepad to play.

wishie
Posts: 4
Joined: Fri Jul 01, 2005 5:52 am
Location: Australia
Contact:

Post by wishie » Sat Jul 02, 2005 1:44 am

Yeah, fair enough.

I was thinking more from a home theatre point of view.
Been working on this setup for a few days now (home theatre) so the tunnel vision must have kicked in..
The day Microsoft makes something that doesnt suck, is the day they make a vacuum cleaner.

Guest

Post by Guest » Fri Jul 15, 2005 8:54 am

I am trying to convert this to work uner kernel 2.4. I have most of it down, but I was wondering what this line need to be in a 2.4 context:
context->mouse.dev = &dev->dev;

Also, is there a 2.4 equivilent for:
input_sync(mouse)

Thanks!
W

Warren

lirc_imon patch for lircmd mouse and keyboard arrow controls

Post by Warren » Tue Jul 19, 2005 10:10 am

Here is a patch that I have made. I couldn't figure out the usb mouse tie-in for 2.4 kernels in the time that I had, so what I made was a driver that changes from 4 arrows and 2 click buttons in keyboard mode (where it starts) to 8 directions and 2 clicks in mouse mode which can be put through lircmd.

Here is the patch:

Code: Select all

*** lirc_imon.c-before-i-messed-it-up   Thu Jul 14 13:44:32 2005
--- lirc_imon.c Tue Jul 19 12:22:49 2005
***************
*** 4,7 ****
--- 4,13 ----
   *   $Id: lirc_imon.c,v 1.6 2005/04/16 18:10:02 venkyr Exp $
   *
+  *   Version 0.3.1
+  *            Patched by Warren Melnick, C.A.C. Media (warren@cacmedia.tv)
+  *            Now has the ability to support both arrows and a kbd click
+  *            mode or an 8-directional mouse mode with clicks for use with
+  *            lircmd.  Switch between modes with Mouse/Keyboard.
+  *
   *   Version 0.3
   *            Supports newer iMON models that send decoded IR signals.
***************
*** 162,165 ****
--- 168,173 ----
                int status;                  /* status of tx completion   */
        } tx;
+
+       int mousing;
  };

***************
*** 569,572 ****
--- 577,583 ----
        }

+       /* Start as arrows, not mouse */
+       context ->mousing = 0;
+
        /* initial IR protocol decode variables */
        context ->rx.count = 0;
***************
*** 679,682 ****
--- 690,694 ----
        unsigned char mask;
        int chunk_num;
+       int key;


***************
*** 700,745 ****
  #endif

!       if (context ->ir_onboard_decode) {

                /* The signals have been decoded onboard the iMON controller */

!                if(buf[0] & 0x40) {
!                  int rel_x = 0, rel_y = 0;
!                  rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
!                  rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
!                  if(buf[0] & 0x02) rel_x |= ~0x10+1;
!                  if(buf[0] & 0x01) rel_y |= ~0x10+1;
!
!                   if(buf[1] & 0x01 || buf[1] >> 2 & 0x01) {
!                    /* mouse pressed */
!                  } else {
!                    if(rel_y>3 && rel_y-abs(rel_x)>3) { /* mouse s */
!                      buf[0] = 0x68;
!                      buf[1] = 0x82;
!                      buf[2] = 0x91;
!                    } else {
!                      if(-rel_y>3 && -rel_y-abs(rel_x)>3) { /* mouse n */
!                         buf[0] = 0x69;
!                         buf[1] = 0x02;
!                         buf[2] = 0x81;
!                      } else {
!                        if(rel_x>3 && rel_x-abs(rel_y)>3) { /* mouse e */
!                          buf[0] = 0x68;
!                          buf[1] = 0x8A;
!                          buf[2] = 0x81;
!                        } else {
!                          if(-rel_x>3 && -rel_x-abs(rel_y)>3) { /* mouse w */
!                            buf[0] = 0x6A;
!                            buf[1] = 0x82;
!                            buf[2] = 0x81;
!                          } else {
!                            return; /* discard those key codes */
!                          }
!                        }
!                      }
!                    }
!                  }
!                }
!
                lirc_buffer_write_1 (context ->plugin ->rbuf, buf);
                wake_up (&context ->plugin ->rbuf ->wait_poll);
--- 712,776 ----
  #endif

!       key = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
!         if (key == 0x299115b7) {
!               context -> mousing = (! context ->mousing);
! #ifdef DEBUG
!               printk("Mousing flag is now %d.\n", context ->mousing);
! #endif
!       }

+       if (context ->ir_onboard_decode) {
+       int rel_x = 0, rel_y = 0;
+               if(buf[0] & 0x40) {
                /* The signals have been decoded onboard the iMON controller */
+                       rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 | (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+                       rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 | (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+                       if(buf[0] & 0x02) rel_x |= ~0x10+1;
+                       if(buf[0] & 0x01) rel_y |= ~0x10+1;
+
+                       if (buf[1] & 0x01 || buf[1] >> 2 & 0x01) {
+                                               /* mouse pressed */
+                               if (! context->mousing) buf[0] &= ~0x40;
+                       } else if (((buf[0] & 0x3f) == 0x28) && (buf[1] == 0x82) && (buf[2] == 0x81)) {
+                               /* mouse released */
+                       } else if (rel_y > 7) { // Down
+                               if (rel_x < -7) { // sw
+                                       buf[0] = (context->mousing) ? 0x6a : 0x2a;
+                                       buf[1] = 0xe2;
+                                       buf[2] = 0xb9;
+                               } else if (rel_x > 7) { // se
+                                       buf[0] = (context->mousing) ? 0x68 : 0x28;
+                                       buf[1] = 0xda;
+                                       buf[2] = 0xb9;
+                               } else { // south
+                                       buf[0] = (context->mousing) ? 0x68 : 0x28;
+                                       buf[1] = 0x82;
+                                       buf[2] = 0x91;
+                               }
+                       } else if (rel_y < -7) { // Up
+                               if (rel_x < -7) { // nw
+                                       buf[0] = (context->mousing) ? 0x6b : 0x2b;
+                                       buf[1] = 0x22;
+                                       buf[2] = 0xa1;
+                               } else if (rel_x > 7) { // ne
+                                       buf[0] = (context->mousing) ? 0x69 : 0x29;
+                                       buf[1] = 0x3a;
+                                       buf[2] = 0xa1;
+                               } else { // north
+                                       buf[0] = (context->mousing) ? 0x69 : 0x29;
+                                       buf[1] = 0x02;
+                                       buf[2] = 0x81;
+                               }
+                       } else if (rel_x < 0) { // west
+                               buf[0] = (context->mousing) ? 0x6A : 0x2A;
+                               buf[1] = 0x82;
+                               buf[2] = 0x81;
+                       } else { //east
+                               buf[0] = (context->mousing) ? 0x68 : 0x28;
+                               buf[1] = 0x8A;
+                               buf[2] = 0x81;
+                       }

!               }
                lirc_buffer_write_1 (context ->plugin ->rbuf, buf);
                wake_up (&context ->plugin ->rbuf ->wait_poll);
Here are the keys in lircd.conf:

Code: Select all

          Up                       0x290281B7
          Down                     0x288291B7
          Left                     0x2A8281B7
          Right                    0x288A81B7
          KbdLeftClick             0x288301B7
          KbdRightClick            0x288481B7
          Mouse_N                  0x690281B7
          Mouse_S                  0x688291B7
          Mouse_W                  0x6A8281B7
          Mouse_E                  0x688A81B7
          Mouse_NE                 0x693aa1b7
          Mouse_NW                 0x6b22a1b7
          Mouse_SE                 0x68dab9b7
          Mouse_SW                 0x6ae2b9b7
          MouseLeftClick           0x688301B7
          MouseRightClick          0x688481B7
and here is my lircmd.conf:

Code: Select all

PROTOCOL IntelliMouse

MOVE_N  * Mouse_N
MOVE_NE * Mouse_NE
MOVE_E  * Mouse_E
MOVE_SE * Mouse_SE
MOVE_S  * Mouse_S
MOVE_SW * Mouse_SW
MOVE_W  * Mouse_W
MOVE_NW * Mouse_NW

BUTTON1_CLICK * MouseLeftClick
BUTTON3_CLICK * MouseRightClick
I have intentions of giving the mouse more resolution in the future, but I do not know when I will have time for that,.

Regards,
Warren Melnick
C.A.C. Media Inc.

Post Reply