|
From: Ryan V. <sim...@ya...> - 2003-08-07 17:34:25
|
On Thu, 2003-08-07 at 10:02, Gerd Knorr wrote:
> Hi,
>=20
> This is a prof-of-concept port of a lirc infrared remote driver to the
> 2.6 kernel input layer. I picked lirc_i2c.c, simply because I have
> hardware to play with for that one. For now it works with Hauppauge
> WinTV cards only.
this is great that they have a proof of concept.
>=20
> The driver just puts the keys from the infrared remote into the normal
> keyboard queue, the lircd event dispatcher daemon is bypassed. Usage is
> very simple: Compile the driver, load the module, press keys on the
> remote and see them arrive in your shell.
>=20
this would be a good way to do IR keyboards but for an actual remote
this might be a very bad thing, i've got some remotes with keys that
just couldn't be mapped to any keyboard keys that would make sense, and
like its been said this would require keyboard focus.
> The number keys of the remote send KEY_KP<n> events, so your shell will
> interpret them either as digits or as cursor keys depending on numlock
> state. The X-Server seems not to see the other keys, try "showkeys" for
> them. Seems some more work is needed to make that working fully, i.e.
> "VOL+" arrives as "XF86AudioRaiseVolume"[1] key in X11 apps.
>=20
> Not sure whenever it is a good idea to put the IR keys into the normal
> keyboard queue. That means that the application with the keyboard focus
> will also receive the IR input. Depending on how you are using lirc you
> might call this a bug or a feature. IMHO it is less confusing for the
> users if keyboard focus and IR focus are identical. But there might be
> situations where it is useful that they are independant ...
>=20
the best idea would be to make it so that you can choose which way it
does this, the keyboard queue, or do it the way LIRC does right now,
because in alot of cases the way LIRC does this makes more sense to me
(though the config files are a bit odd sometimes)
> Comments? Flames?
>=20
> Gerd
>=20
> PS: I'm not in the lirc list, so please Cc: me on replies.
>=20
> [1] no joke, multimedia keyboards have those ...
>=20
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D[ ir-kbd-i2c.c ]=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
> /*
> * keyboard input driver for i2c IR remote controls
> *
> * Copyright (c) 2000-2003 Gerd Knorr <kr...@by...>
> * modified for PixelView (BT878P+W/FM) by
> * Michal Kochanowicz <mko...@pl...>
> * Christoph Bartelmus <li...@ba...>
> * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
> * Ulrich Mueller <ulr...@we...>
> *
> * 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
> *
> */
>=20
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/kernel.h>
> #include <linux/sched.h>
> #include <linux/string.h>
> #include <linux/timer.h>
> #include <linux/delay.h>
> #include <linux/errno.h>
> #include <linux/slab.h>
> #include <linux/i2c.h>
> #include <linux/workqueue.h>
> #include <linux/input.h>
>=20
> #include <asm/semaphore.h>
>=20
> struct IR;
>=20
> struct IR {
> struct i2c_client c;
> struct input_dev dev;
> struct work_struct work;
> struct timer_list timer;
>=20
> /* config */
> int (*get_key)(struct IR*, unsigned char*);
> int *keycodes;
>=20
> /* internal state */
> int keypressed;
> unsigned char key;
> };
>=20
> /* ----------------------------------------------------------------------=
- */
> /* insmod parameters =
*/
>=20
> static int debug =3D 1; /* debug output */
> MODULE_PARM(debug,"i");
>=20
> /*
> * If this key changes, a new key was pressed.
> */
> #define REPEAT_TOGGLE_0 192
> #define REPEAT_TOGGLE_1 224
>=20
> #define DEVNAME "ir-kbd-i2c"
> #define dprintk(level, fmt, arg...) if (debug >=3D level) \
> printk(KERN_DEBUG DEVNAME ": " fmt, ## arg)
>=20
> /* ----------------------------------------------------------------------=
- */
>=20
> static int keycodes_haup[256] =3D {
> [ 0x00 ] =3D KEY_KP0,
> [ 0x01 ] =3D KEY_KP1,
> [ 0x02 ] =3D KEY_KP2,
> [ 0x03 ] =3D KEY_KP3,
> [ 0x04 ] =3D KEY_KP4,
> [ 0x05 ] =3D KEY_KP5,
> [ 0x06 ] =3D KEY_KP6,
> [ 0x07 ] =3D KEY_KP7,
> [ 0x08 ] =3D KEY_KP8,
> [ 0x09 ] =3D KEY_KP9,
>=20
> [ 0x0c ] =3D KEY_RADIO, // radio
> [ 0x0d ] =3D KEY_MUTE, // mute
> [ 0x0f ] =3D KEY_TV, // tv
> [ 0x10 ] =3D KEY_VOLUMEUP, // VOL+
> [ 0x11 ] =3D KEY_VOLUMEDOWN, // VOL-
> [ 0x20 ] =3D KEY_CHANNELUP, // CH+
> [ 0x21 ] =3D KEY_CHANNELDOWN, // CH-
> [ 0x22 ] =3D KEY_CHANNEL, // source
>=20
> #if 0
> [ 0x1e ] =3D KEY_FIXME, // reserved
> [ 0x26 ] =3D KEY_FIXME, // minimize
> [ 0x2e ] =3D KEY_FIXME, // fullscreen
> #endif
> };
>=20
> /* ----------------------------------------------------------------------=
- */
>=20
> static inline int reverse(int data, int bits)
> {
> int i,c;
> =09
> for (c=3D0,i=3D0; i<bits; i++) {
> c |=3D (((data & (1<<i)) ? 1:0)) << (bits-1-i);
> }
> return c;
> }
>=20
> static int get_key_haup(struct IR *ir, unsigned char* key)
> {
> unsigned char buf[3];
>=20
> /* poll IR chip */
> if (3 !=3D i2c_master_recv(&ir->c,buf,3))
> return -EIO;
> dprintk(2,"ir data 0x%02x 0x%02x 0x%02x\n",
> buf[0], buf[1], buf[2]);
>=20
> /* key pressed ? */
> if (buf[0] !=3D REPEAT_TOGGLE_0 && buf[0] !=3D REPEAT_TOGGLE_1)
> return 0;
> =09
> /* return it */
> key[0] =3D buf[1] >> 2;
> return 1;
> }
>=20
> static int get_key_pixelview(struct IR *ir, unsigned char* key)
> {
> unsigned char b;
> =09
> /* poll IR chip */
> if (1 !=3D i2c_master_recv(&ir->c,&b,1)) {
> dprintk(1,"read error\n");
> return -EIO;
> }
> dprintk(2,"key %02x\n", b);
> key[0] =3D b;
> return 1;
> }
>=20
> static int get_key_pv951(struct IR *ir, unsigned char* key)
> {
> unsigned char b;
> =09
> /* poll IR chip */
> if (1 !=3D i2c_master_recv(&ir->c,&b,1)) {
> dprintk(1,"read error\n");
> return -EIO;
> }
> /* ignore 0xaa */
> if (b=3D=3D0xaa)
> return 0;
> dprintk(2,"key %02x\n", b);
> =09
> key[0] =3D b;
> return 1;
> }
>=20
> static int get_key_knc1(struct IR *ir, unsigned char *key)
> {
> unsigned char b;
> =09
> /* poll IR chip */
> if (1 !=3D i2c_master_recv(&ir->c,&b,1)) {
> dprintk(1,"read error\n");
> return -EIO;
> }
> =09
> /* it seems that 0xFE indicates that a button is still hold
> down, while 0xFF indicates that no button is hold
> down. 0xFE sequences are sometimes interrupted by 0xFF */
> =09
> dprintk(2,"key %02x\n", b);
> =09
> if(b =3D=3D 0xFF)
> return 0;
> =09
> if (b =3D=3D 0xFE)
> b =3D ir->key;
> =09
> key[0] =3D b;
> return 1;
> }
>=20
> /* ----------------------------------------------------------------------=
- */
>=20
> static void ir_key_event(struct IR *ir)
> {
> int keycode =3D 0;
>=20
> if (ir->keycodes && ir->keycodes[ir->key])
> keycode =3D ir->keycodes[ir->key];
> if (0 =3D=3D keycode) {
> dprintk(1,"unknown key: 0x%02x [down=3D%d]\n",
> ir->key,ir->keypressed);
> return;
> }
> dprintk(1,"key event code=3D%d down=3D%d\n",
> keycode,ir->keypressed);
> input_report_key(&ir->dev,keycode,ir->keypressed);
> input_sync(&ir->dev);
> }
>=20
> static void ir_key_poll(struct IR *ir)
> {
> unsigned char key;
> int rc;
>=20
> dprintk(2,"ir_poll_key\n");
> rc =3D ir->get_key(ir,&key);
> if (rc < 0) {
> dprintk(2,"error\n");
> return;
> }
>=20
> if (0 =3D=3D rc && ir->keypressed) {
> ir->keypressed =3D 0;
> ir_key_event(ir);
> }
>=20
> if (rc > 0) {
> if (ir->keypressed && ir->key !=3D key) {
> ir->keypressed =3D 0;
> ir_key_event(ir);
> }
> if (!ir->keypressed) {
> ir->key =3D key;
> ir->keypressed =3D 1;
> ir_key_event(ir);
> }
> }
> }
>=20
> static void ir_timer(unsigned long data)
> {
> struct IR *ir =3D (struct IR*)data;
> schedule_work(&ir->work);
> }
>=20
> static void ir_work(void *data)
> {
> struct IR *ir =3D data;
> ir_key_poll(ir);
> mod_timer(&ir->timer, jiffies+HZ/10);
> }
>=20
> /* ----------------------------------------------------------------------=
- */
>=20
> static int ir_attach(struct i2c_adapter *adap, int addr,
> unsigned short flags, int kind);
> static int ir_detach(struct i2c_client *client);
> static int ir_probe(struct i2c_adapter *adap);
>=20
> static struct i2c_driver driver =3D {
> .name =3D "ir remote kbd driver",
> .id =3D I2C_DRIVERID_EXP3, /* FIXME */
> .flags =3D I2C_DF_NOTIFY,
> .attach_adapter =3D ir_probe,
> .detach_client =3D ir_detach,
> };
>=20
> static struct i2c_client client_template =3D=20
> {
> I2C_DEVNAME("unset"),
> .driver =3D &driver
> };
>=20
> static int ir_attach(struct i2c_adapter *adap, int addr,
> unsigned short flags, int kind)
> {
> struct IR *ir;
> int i;
> =09
> if (NULL =3D=3D (ir =3D kmalloc(sizeof(struct IR),GFP_KERNEL)))
> return -ENOMEM;
> memset(ir,0,sizeof(*ir));
> ir->c =3D client_template;
>=20
> i2c_set_clientdata(&ir->c, ir);
> ir->c.adapter =3D adap;
> ir->c.addr =3D addr;
>=20
> switch(addr) {
> case 0x64:
> strlcpy(ir->c.dev.name,"Pixelview IR",sizeof(ir->c.dev.name));
> ir->get_key=3Dget_key_pixelview;
> //ir->keycodes =3D keycodes_FIXME;
> break;
> case 0x4b:
> strlcpy(ir->c.dev.name,"PV951 IR",sizeof(ir->c.dev.name));
> ir->get_key=3Dget_key_pv951;
> //ir->keycodes =3D keycodes_FIXME;
> break;
> case 0x18:
> case 0x1a:
> strlcpy(ir->c.dev.name,"Hauppauge IR",sizeof(ir->c.dev.name));
> ir->get_key=3Dget_key_haup;
> ir->keycodes =3D keycodes_haup;
> break;
> case 0x30:
> strlcpy(ir->c.dev.name,"KNC ONE IR",sizeof(ir->c.dev.name));
> ir->get_key=3Dget_key_knc1;
> //ir->keycodes =3D keycodes_FIXME;
> break;
> default:
> /* shouldn't happen */
> printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr);
> kfree(ir);
> return -1;
> }
> printk(DEVNAME ": chip found @ 0x%02x (%s)\n",addr,ir->c.dev.name);
>=20
> /* register i2c device */
> i2c_attach_client(&ir->c);
>=20
> /* register input device */
> init_input_dev(&ir->dev);
> ir->dev.name =3D ir->c.dev.name;
> ir->dev.evbit[0] =3D BIT(EV_KEY) | BIT(EV_REP);
> if (ir->keycodes) {
> for (i =3D 0; i < 255; i++)
> set_bit(ir->keycodes[i], ir->dev.keybit);
> clear_bit(0, ir->dev.keybit);
> }
> input_register_device(&ir->dev);
>=20
> /* start polling via eventd */
> INIT_WORK(&ir->work, ir_work, ir);
> init_timer(&ir->timer);
> ir->timer.function =3D ir_timer;
> ir->timer.data =3D (unsigned long)ir;
> schedule_work(&ir->work);
> =09
> return 0;
> }
>=20
> static int ir_detach(struct i2c_client *client)
> {
> struct IR *ir =3D i2c_get_clientdata(client);
>=20
> /* kill outstanding polls */
> del_timer(&ir->timer);
> flush_scheduled_work();
>=20
> /* unregister devices */
> input_unregister_device(&ir->dev);
> i2c_detach_client(&ir->c);
>=20
> /* free memory */
> kfree(ir);
> return 0;
> }
>=20
> static int ir_probe(struct i2c_adapter *adap)
> {
> =09
> /* The external IR receiver is at i2c address 0x34 (0x35 for
> reads). Future Hauppauge cards will have an internal
> receiver at 0x30 (0x31 for reads). In theory, both can be
> fitted, and Hauppauge suggest an external overrides an
> internal.=20
> =20
> That's why we probe 0x1a (~0x34) first. CB=20
> */
> =09
> static const int probe[] =3D { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
> struct i2c_client c; char buf; int i,rc;
>=20
> if (adap->id =3D=3D (I2C_ALGO_BIT | I2C_HW_B_BT848)) {
> memset(&c,0,sizeof(c));
> c.adapter =3D adap;
> for (i =3D 0; -1 !=3D probe[i]; i++) {
> c.addr =3D probe[i];
> rc =3D i2c_master_recv(&c,&buf,1);
> dprintk(1,"probe 0x%02x @ %s: %s\n",
> probe[i], adap->dev.name,=20
> (1 =3D=3D rc) ? "yes" : "no");
> if (1 =3D=3D rc) {
> ir_attach(adap,probe[i],0,0);
> break;
> }
> }
> }
> return 0;
> }
>=20
> /* ----------------------------------------------------------------------=
- */
>=20
> MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulric=
h Mueller");
> MODULE_DESCRIPTION("keyboard input driver for i2c IR remote controls");
> MODULE_LICENSE("GPL");
>=20
> static int ir_init(void)
> {
> i2c_add_driver(&driver);
> return 0;
> }
>=20
> static void ir_fini(void)
> {
> i2c_del_driver(&driver);
> }
>=20
> module_init(ir_init);
> module_exit(ir_fini);
>=20
> /*
> * Overrides for Emacs so that we follow Linus's tabbing style.
> * ----------------------------------------------------------------------=
-----
> * Local variables:
> * c-basic-offset: 8
> * End:
> */
>=20
>=20
> -------------------------------------------------------
> This SF.Net email sponsored by: Free pre-built ASP.NET sites including
> Data Reports, E-commerce, Portals, and Forums are available now.
> Download today and enter to win an XBOX or Visual Studio .NET.
> http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/=
01
--=20
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCM/CS/E/M/S d->-- s:+>+:-
!a--->-->->>+>++>+++$ C+++$>++++$ UL++++>++++$
P+++>++++$ L++++>++++$ !E-? W++>++$>+++$
N+++>* !o? !K? w--(---) O-- M-(--) V-- PS?
PE Y+(++) PGP+++(++) t++>+++$ 5--(-) X++>+++
R* tv+++>++ b+>++ DI++++>+++++ D+++@ G++>++++
e>+$>++$>+++$>++++$>+++++$ h>+>++$ r* y-->->++
-----END GEEK CODE BLOCK-----
=
=20
-----BEGIN SIMPSONS GEEK CODE BLOCK-----
S1.3 HOM+++ MRG++ BAR+++ LIS++ MAG+++
SLH++ ABE+++ P&S++ BUR+++ SMI+ SKI++
OTT+++ MIL* MTN* BNY++ NED+++ MAU MOL+
APU+++ KRU+++ CWG++ RWG++ NEL++ SNA+
BOB++ MEL++ I&S++++ TRO+++ HTZ+++
FRI*** TEE CBG* STU- GIL? f++/>+++
n+>+++ Iso $+++ M1985
-----END SIMPSONS GEEK CODE BLOCK-----
|