|
From: Vojtech P. <vo...@us...> - 2002-02-22 11:41:52
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/input/other
In directory usw-pr-cvs1:/tmp/cvs-serv17943/other
Added Files:
ati_remote.c
Log Message:
Add ati remote driver.
--- NEW FILE: ati_remote.c ---
/*
*
* Copyright (c) 2002 Vladimir Dergachev
*
* USB ATI Remote support
*
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to vo...@mi...
*
* This driver was derived from usbati_remote and usbkbd drivers by Vojtech Pavlik
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Vladimir Dergachev <vo...@mi...>"
#define DRIVER_DESC "USB ATI Remote driver"
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
/* Get hi and low bytes of a 16-bits int */
#define HI(a) ((unsigned char)((a) >> 8))
#define LO(a) ((unsigned char)((a) & 0xff))
struct ati_remote {
unsigned char data[8];
char name[128];
unsigned char old[2];
unsigned long old_jiffies;
struct usb_device *usbdev;
struct input_dev dev;
struct urb irq, out;
wait_queue_head_t wait;
devrequest dr;
int open;
};
static char init1[]={
0x01, 0x00, 0x20, 0x14 };
static char init2[]={
0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
#define KIND_END 0
#define KIND_LITERAL 1
#define KIND_FILTERED 2
#define KIND_LU 3
#define KIND_RU 4
#define KIND_LD 5
#define KIND_RD 6
#define KIND_ACCEL 7
static struct {
short kind;
unsigned char data1, data2;
int type;
unsigned int code;
int value;
} ati_remote_translation_table[]={
{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left ati_remote button */
{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0},
{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1}, /* right ati_remote button */
{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},
/* ati_remote */
{KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
{KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
{KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
{KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
{KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
{KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
{KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
{KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
/* keyboard.. */
{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* key left */
{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* key right */
{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* key down */
{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* key left */
{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_ENTER, 1}, /* key enter */
{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* key "checkbox" */
{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* key menu */
{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* key power */
{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_PROG1, 1}, /* key TV */
{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_PROG2, 1}, /* key DVD */
{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* key Web */
{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* key "open book" */
{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* key "hand" */
{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* key "timer" */
{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1},
{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1},
{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1},
{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_PAGEDOWN, 1}, /* prev channel*/
{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_PAGEUP, 1}, /* next channel */
{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1},
{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAYCD, 1}, /* ke pay */
{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1},
{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1},
{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1},
{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1},
{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* maximize */
{KIND_END, 0x00, 0x00, EV_MAX+1, 0, 0} /* END */
};
/*
* Send a packet of bytes to the device
*/
static void send_packet(struct ati_remote *ati_remote, u16 cmd, unsigned char* data)
{
DECLARE_WAITQUEUE(wait, current);
int timeout = HZ; /* 1 second */
memcpy(ati_remote->out.transfer_buffer + 1, data, LO(cmd));
((char*)ati_remote->out.transfer_buffer)[0] = HI(cmd);
ati_remote->out.transfer_buffer_length = LO(cmd) + 1;
ati_remote->out.dev = ati_remote->usbdev;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&ati_remote->wait, &wait);
if (usb_submit_urb(&ati_remote->out)) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&ati_remote->wait, &wait);
return;
}
while (timeout && ati_remote->out.status == -EINPROGRESS)
timeout = schedule_timeout(timeout);
set_current_state(TASK_RUNNING);
remove_wait_queue(&ati_remote->wait, &wait);
if (!timeout)
usb_unlink_urb(&ati_remote->out);
}
static void ati_remote_irq(struct urb *urb)
{
struct ati_remote *ati_remote = urb->context;
unsigned char *data = ati_remote->data;
struct input_dev *dev = &ati_remote->dev;
int i;
int accel;
if (urb->status) return;
if(urb->actual_length==4){
if((data[0]!=0x14)||(data[3]!=0xf0))
printk("** weird key=%02x%02x%02x%02x\n", data[0], data[1], data[2], data[3]);
} else
if(urb->actual_length==1){
if((data[0]!=(unsigned char)0xff)&&(data[0]!=0x00))
printk("** weird byte=0x%02x\n", data[0]);
} else {
printk("length=%d %02x %02x %02x %02x %02x %02x %02x %02x\n",
urb->actual_length, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
}
accel=1;
if((ati_remote->old[0]==data[1])&&(ati_remote->old[1]==data[2])){
if(ati_remote->old_jiffies+4*HZ<jiffies)accel=8;
else
if(ati_remote->old_jiffies+3*HZ<jiffies)accel=6;
else
if(ati_remote->old_jiffies+2*HZ<jiffies)accel=4;
else
if(ati_remote->old_jiffies+HZ<jiffies)accel=3;
else
if(ati_remote->old_jiffies+(HZ>>1)<jiffies)accel=2;
}
if((urb->actual_length==4) && (data[0]==0x14) && (data[3]==0xf0)){
for(i=0;ati_remote_translation_table[i].kind!=KIND_END;i++){
if((ati_remote_translation_table[i].data1==data[1]) && (ati_remote_translation_table[i].data2==data[2])){
switch(ati_remote_translation_table[i].kind){
case KIND_LITERAL:
input_event(dev, ati_remote_translation_table[i].type,
ati_remote_translation_table[i].code,
ati_remote_translation_table[i].value);
break;
case KIND_ACCEL:
input_event(dev, ati_remote_translation_table[i].type,
ati_remote_translation_table[i].code,
ati_remote_translation_table[i].value*accel);
break;
case KIND_LU:
input_report_rel(dev, REL_X, -accel);
input_report_rel(dev, REL_Y, -accel);
break;
case KIND_RU:
input_report_rel(dev, REL_X, accel);
input_report_rel(dev, REL_Y, -accel);
break;
case KIND_LD:
input_report_rel(dev, REL_X, -accel);
input_report_rel(dev, REL_Y, accel);
break;
case KIND_RD:
input_report_rel(dev, REL_X, accel);
input_report_rel(dev, REL_Y, accel);
break;
case KIND_FILTERED:
if((ati_remote->old[0]==data[1])&&(ati_remote->old[1]==data[2])&&((ati_remote->old_jiffies+(HZ>>2))>jiffies)){
return;
}
input_event(dev, ati_remote_translation_table[i].type,
ati_remote_translation_table[i].code,
1);
input_event(dev, ati_remote_translation_table[i].type,
ati_remote_translation_table[i].code,
0);
break;
default:
printk("kind=%d\n", ati_remote_translation_table[i].kind);
}
break;
}
}
if(ati_remote_translation_table[i].kind==KIND_END){
printk("** unknown key=%02x%02x\n", data[1], data[2]);
}
if((ati_remote->old[0]!=data[1])||(ati_remote->old[1]!=data[2]))
ati_remote->old_jiffies=jiffies;
ati_remote->old[0]=data[1];
ati_remote->old[1]=data[2];
}
}
static int ati_remote_open(struct input_dev *dev)
{
struct ati_remote *ati_remote = dev->private;
printk("ati_remote_open %d\n", ati_remote->open);
if (ati_remote->open++)
return 0;
ati_remote->irq.dev = ati_remote->usbdev;
if (usb_submit_urb(&ati_remote->irq))
return -EIO;
printk("done: ati_remote_open now open=%d\n", ati_remote->open);
return 0;
}
static void ati_remote_close(struct input_dev *dev)
{
struct ati_remote *ati_remote = dev->private;
if (!--ati_remote->open)
usb_unlink_urb(&ati_remote->irq);
}
static void ati_remote_usb_out(struct urb *urb)
{
struct ati_remote *ati_remote = urb->context;
if (urb->status) return;
if (waitqueue_active(&ati_remote->wait))
wake_up(&ati_remote->wait);
}
static void *ati_remote_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
struct usb_interface *iface;
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint, *epout;
struct ati_remote *ati_remote;
int pipe, maxp;
char *buf;
int i;
iface = &dev->actconfig->interface[ifnum];
interface = &iface->altsetting[iface->act_altsetting];
if (interface->bNumEndpoints != 2) return NULL;
/* use the first endpoint only for now */
endpoint = interface->endpoint + 0;
if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
if ((endpoint->bmAttributes & 3) != 3) return NULL;
epout = interface->endpoint + 1;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
printk("maxp=%d endpoint=0x%02x\n", maxp, endpoint->bEndpointAddress);
usb_set_idle(dev, interface->bInterfaceNumber, 0, 0);
if (!(ati_remote = kmalloc(sizeof(struct ati_remote)+32, GFP_KERNEL))) return NULL;
memset(ati_remote, 0, sizeof(struct ati_remote)+32);
ati_remote->usbdev = dev;
for(i=0;ati_remote_translation_table[i].kind!=KIND_END;i++)
if(ati_remote_translation_table[i].type==EV_KEY)
set_bit(ati_remote_translation_table[i].code, ati_remote->dev.keybit);
clear_bit(BTN_LEFT, ati_remote->dev.keybit);
clear_bit(BTN_RIGHT, ati_remote->dev.keybit);
ati_remote->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
ati_remote->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
ati_remote->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
ati_remote->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
ati_remote->dev.relbit[0] |= BIT(REL_WHEEL);
ati_remote->dev.private = ati_remote;
ati_remote->dev.open = ati_remote_open;
ati_remote->dev.close = ati_remote_close;
ati_remote->dev.name = ati_remote->name;
ati_remote->dev.idbus = BUS_USB;
ati_remote->dev.idvendor = dev->descriptor.idVendor;
ati_remote->dev.idproduct = dev->descriptor.idProduct;
ati_remote->dev.idversion = dev->descriptor.bcdDevice;
init_waitqueue_head(&ati_remote->wait);
ati_remote->dr.requesttype = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
ati_remote->dr.index = 0;
ati_remote->dr.length = 16;
ati_remote->old[0]=0;
ati_remote->old[1]=0;
ati_remote->old_jiffies=jiffies;
if (!(buf = kmalloc(63, GFP_KERNEL))) {
kfree(ati_remote);
return NULL;
}
if (dev->descriptor.iManufacturer &&
usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0)
strcat(ati_remote->name, buf);
if (dev->descriptor.iProduct &&
usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0)
sprintf(ati_remote->name, "%s %s", ati_remote->name, buf);
if (!strlen(ati_remote->name))
sprintf(ati_remote->name, "USB HIDBP Mouse %04x:%04x",
ati_remote->dev.idvendor, ati_remote->dev.idproduct);
kfree(buf);
FILL_INT_URB(&ati_remote->irq, dev, pipe, ati_remote->data, maxp > 8 ? 8 : maxp,
ati_remote_irq, ati_remote, endpoint->bInterval);
FILL_INT_URB(&ati_remote->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
ati_remote + 1, 32, ati_remote_usb_out, ati_remote, epout->bInterval);
input_register_device(&ati_remote->dev);
printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n",
ati_remote->dev.number, ati_remote->name, dev->bus->busnum, dev->devnum, ifnum);
send_packet(ati_remote, 0x8004, init1);
send_packet(ati_remote, 0x8007, init2);
usb_unlink_urb(&(ati_remote->out));
return ati_remote;
}
static void ati_remote_disconnect(struct usb_device *dev, void *ptr)
{
struct ati_remote *ati_remote = ptr;
usb_unlink_urb(&ati_remote->irq);
input_unregister_device(&ati_remote->dev);
kfree(ati_remote);
}
static struct usb_device_id ati_remote_id_table [] = {
{ USB_DEVICE(0x0bc7, 0x004) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, ati_remote_id_table);
static struct usb_driver ati_remote_driver = {
name: "ati_remote",
probe: ati_remote_probe,
disconnect: ati_remote_disconnect,
id_table: ati_remote_id_table,
};
static int __init ati_remote_init(void)
{
usb_register(&ati_remote_driver);
info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
}
static void __exit ati_remote_exit(void)
{
usb_deregister(&ati_remote_driver);
}
module_init(ati_remote_init);
module_exit(ati_remote_exit);
|