summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/pegasus.c579
-rw-r--r--drivers/usb/rio500.c455
-rw-r--r--drivers/usb/rio500_usb.h37
-rw-r--r--drivers/usb/wacom.c286
4 files changed, 1357 insertions, 0 deletions
diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c
new file mode 100644
index 000000000..1e752dc75
--- /dev/null
+++ b/drivers/usb/pegasus.c
@@ -0,0 +1,579 @@
+/*
+**
+** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
+**
+** Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
+**
+** Distribute under GPL version 2 or later.
+*/
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "usb.h"
+
+#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
+#error You can not compile this driver on this kernel with this C options!
+#endif
+
+
+#define ADMTEK_VENDOR_ID 0x07a6
+#define ADMTEK_HPNA_PEGASUS 0x0986
+
+#define HPNA_MTU 1500
+#define MAX_MTU 1536
+
+#define TX_TIMEOUT (HZ*5)
+#define SOMETHING (jiffies + TX_TIMEOUT)
+
+
+static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+
+
+typedef struct usb_hpna
+{
+ struct usb_device *usb_dev;
+ struct net_device *net_dev;
+ int present;
+ int active;
+ void *irq_handler;
+ struct list_head list;
+ struct net_device_stats stats;
+ spinlock_t hpna_lock;
+ struct timer_list timer;
+
+ unsigned int rx_pipe;
+ unsigned char * rx_buff;
+ urb_t rx_urb;
+
+ unsigned int tx_pipe;
+ unsigned char * tx_buff;
+ urb_t tx_urb;
+ struct sk_buff * tx_skbuff;
+
+ __u8 intr_ival;
+ unsigned int intr_pipe;
+ unsigned char intr_buff[8];
+ urb_t intr_urb;
+} usb_hpna_t;
+
+
+usb_hpna_t usb_dev_hpna;
+static int loopback = 0;
+int multicast_filter_limit = 32;
+static LIST_HEAD(hpna_list);
+
+
+MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
+MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_PARM(loopback, "i");
+
+
+
+/*** vendor specific commands ***/
+static __inline__ int hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
+ indx, data, size, HZ);
+}
+
+
+static __inline__ int hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
+{
+ __u8 data = value;
+ return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
+ data, indx, &data, 1, HZ);
+}
+
+
+static __inline__ int hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
+ indx, data, size, HZ);
+}
+
+
+static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
+{
+ int i;
+ __u8 data[4];
+
+ data[0] = 1;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0x40 + index;
+ hpna_set_registers( dev, 0x25, 4, data );
+ for ( i=0; i<100; i++ ) {
+ hpna_get_registers( dev, 0x25, 4, data );
+ if ( data[3] & 0x80 ) {
+ *regdata = *(__u16 *)(data+1);
+ return 0;
+ }
+ udelay(100);
+ }
+ warn("read_phy_word() failed");
+ return 1;
+}
+
+
+static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+{
+ int i;
+ __u8 data[4];
+
+ data[0] = 1;
+ data[1] = regdata;
+ data[2] = regdata >> 8;
+ data[3] = 0x20 + index;
+ hpna_set_registers( dev, 0x25, 4, data );
+ for ( i=0; i<100; i++ ) {
+ hpna_get_registers( dev, 0x28, 1, data );
+ if ( data[0] & 0x80 ) {
+ return 0;
+ }
+ udelay(100);
+ }
+ warn("write_phy_word() failed");
+ return 1;
+}
+
+
+int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+{
+ int i;
+ __u8 data[4];
+
+ data[0] = index;
+ data[1] = data[2] = 0;
+ data[3] = 0x02;
+ hpna_set_registers(dev, 0x20, 4, data);
+ for ( i=0; i<100; i++ ) {
+ hpna_get_registers(dev, 0x23, 1, data);
+ if ( data[0] & 4 ) {
+ hpna_get_registers(dev, 0x21, 2, data);
+ *retdata = *(__u16 *)data;
+ return 0;
+ }
+ }
+ warn("read_srom_word() failed");
+ return 1;
+}
+/*** end ***/
+
+
+
+
+int get_node_id( struct usb_device *dev, __u8 *id )
+{
+ int i;
+
+ for ( i=0; i<3; i++ ) {
+ if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
+ return 1;
+ }
+ return 0;
+}
+
+
+static int reset_mac( struct usb_device *dev )
+{
+ __u8 data = 0x8;
+ int i;
+
+ hpna_set_register( dev, 1, 0x08 );
+ for ( i=0; i<100; i++ ) {
+ hpna_get_registers( dev, 1, 1, &data);
+ if ( !(data & 0x08) ) {
+ if ( loopback & 1 )
+ return 0;
+ else if ( loopback & 2 ) {
+ write_phy_word( dev, 0, 0x4000 );
+ /*return 0;*/
+ }
+ hpna_set_register( dev, 0x7e, 0x24 );
+ hpna_set_register( dev, 0x7e, 0x27 );
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+int start_net( struct net_device *dev, struct usb_device *usb_dev )
+{
+ __u16 partmedia, temp;
+ __u8 node_id[6];
+ __u8 data[4];
+
+ if ( get_node_id(usb_dev, node_id) )
+ return 1;
+ hpna_set_registers(usb_dev, 0x10, 6, node_id);
+ memcpy(dev->dev_addr, node_id, 6);
+ if ( read_phy_word(usb_dev, 1, &temp) )
+ return 2;
+ if ( !(temp & 4) ) {
+ if ( loopback )
+ goto ok;
+ err("link NOT established - %x", temp);
+ return 3;
+ }
+ok:
+ if ( read_phy_word(usb_dev, 5, &partmedia) )
+ return 4;
+ temp = partmedia;
+ partmedia &= 0x1f;
+ if ( partmedia != 1 ) {
+ err("party FAIL %x", temp);
+ return 5;
+ }
+ partmedia = temp;
+ if ( partmedia & 0x100 )
+ data[1] = 0x30;
+ else {
+ if ( partmedia & 0x80 )
+ data[1] = 0x10;
+ else
+ data[1] = 0;
+ }
+
+ data[0] = 0xc9;
+ data[2] = (loopback & 1) ? 0x08 : 0x00;
+
+ hpna_set_registers(usb_dev, 0, 3, data);
+
+ return 0;
+}
+
+
+static void hpna_read_irq( purb_t urb )
+{
+ struct net_device *net_dev = urb->context;
+ usb_hpna_t *hpna = net_dev->priv;
+ int count = urb->actual_length, res;
+ int rx_status = *(int *)(hpna->rx_buff + count - 4);
+
+
+ if ( urb->status ) {
+ info( "%s: RX status %d\n", net_dev->name, urb->status );
+ goto goon;
+ }
+
+ if ( !count )
+ goto goon;
+/* if ( rx_status & 0x00010000 )
+ goto goon;
+*/
+ if ( rx_status & 0x000e0000 ) {
+ dbg("%s: error receiving packet %x",
+ net_dev->name, rx_status & 0xe0000);
+ hpna->stats.rx_errors++;
+ if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
+ if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
+ if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
+ } else {
+ struct sk_buff *skb;
+ __u16 pkt_len = (rx_status & 0xfff) - 8;
+
+
+ if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
+ skb->dev = net_dev;
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
+ skb_put(skb, pkt_len);
+ } else
+ goto goon;
+ skb->protocol = eth_type_trans(skb, net_dev);
+ netif_rx(skb);
+ hpna->stats.rx_packets++;
+ hpna->stats.rx_bytes += pkt_len;
+ }
+goon:
+ if ( (res = usb_submit_urb( &hpna->rx_urb )) )
+ warn("failed rx_urb %d", res);
+}
+
+
+static void hpna_irq( urb_t *urb)
+{
+ if( urb->status ) {
+ __u8 *d = urb->transfer_buffer;
+ printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
+ d[0], d[1], d[2], d[3], d[4], d[5] );
+ }
+}
+
+
+static void hpna_write_irq( purb_t urb )
+{
+ struct net_device *net_dev = urb->context;
+ usb_hpna_t *hpna = net_dev->priv;
+
+
+ spin_lock( &hpna->hpna_lock );
+
+ if ( urb->status )
+ info("%s: TX status %d\n", net_dev->name, urb->status);
+ netif_wake_queue( net_dev );
+
+ spin_unlock( &hpna->hpna_lock );
+}
+
+
+static void tx_timeout( struct net_device *dev )
+{
+ usb_hpna_t *hpna = dev->priv;
+
+ warn( "%s: Tx timed out. Reseting...", dev->name );
+ hpna->stats.tx_errors++;
+ dev->trans_start = jiffies;
+ netif_wake_queue( dev );
+}
+
+
+static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+{
+ usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
+ int count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
+ int res;
+
+ spin_lock( &hpna->hpna_lock );
+
+ netif_stop_queue( net_dev );
+ ((__u16 *)hpna->tx_buff)[0] = skb->len;
+ memcpy(hpna->tx_buff+2, skb->data, skb->len);
+ (&hpna->tx_urb)->transfer_buffer_length = count;
+ if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+ warn("failed tx_urb %d", res);
+ hpna->stats.tx_errors++;
+ netif_start_queue( net_dev );
+ } else {
+ hpna->stats.tx_packets++;
+ hpna->stats.tx_bytes += skb->len;
+ net_dev->trans_start = jiffies;
+ }
+ dev_kfree_skb( skb );
+ spin_unlock( &hpna->hpna_lock );
+ return 0;
+}
+
+
+static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
+{
+ return &((usb_hpna_t *)dev->priv)->stats;
+}
+
+static int hpna_open( struct net_device *net_dev )
+{
+ usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
+ int res;
+
+ if ( hpna->active )
+ return -EBUSY;
+ else
+ hpna->active = 1;
+
+ if ( start_net(net_dev, hpna->usb_dev) ) {
+ err("can't start_net()");
+ return -EIO;
+ }
+
+ if ( (res = usb_submit_urb( &hpna->rx_urb )) )
+ warn("failed rx_urb %d", res);
+
+/* usb_submit_urb( &hpna->intr_urb );*/
+ netif_start_queue( net_dev );
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int hpna_close( struct net_device *net_dev )
+{
+ usb_hpna_t *hpna = net_dev->priv;
+
+
+ netif_stop_queue( net_dev );
+
+ usb_unlink_urb( &hpna->rx_urb );
+ usb_unlink_urb( &hpna->tx_urb );
+/* usb_unlink_urb( hpna->intr_urb );*/
+
+ hpna->active = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+{
+ __u16 *data = (__u16 *)&rq->ifr_data;
+ usb_hpna_t *hpna = dev->priv;
+
+ switch( cmd ) {
+ case SIOCDEVPRIVATE:
+ data[0] = 1;
+ case SIOCDEVPRIVATE+1:
+ read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
+ return 0;
+ case SIOCDEVPRIVATE+2:
+ if ( !capable(CAP_NET_ADMIN) )
+ return -EPERM;
+ write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+
+static void set_rx_mode( struct net_device *net_dev )
+{
+ usb_hpna_t *hpna=net_dev->priv;
+
+ netif_stop_queue( net_dev );
+
+ if ( net_dev->flags & IFF_PROMISC ) {
+ info("%s: Promiscuous mode enabled", net_dev->name);
+ hpna_set_register( hpna->usb_dev, 2, 0x04 );
+ } else if ((net_dev->mc_count > multicast_filter_limit) ||
+ (net_dev->flags & IFF_ALLMULTI)) {
+ hpna_set_register(hpna->usb_dev, 0, 0xfa);
+ hpna_set_register(hpna->usb_dev, 2, 0);
+ } else {
+ dbg("%s: set Rx mode", net_dev->name);
+ }
+
+ netif_wake_queue( net_dev );
+}
+
+
+static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+{
+ struct net_device *net_dev;
+ usb_hpna_t *hpna = &usb_dev_hpna;
+
+
+
+ if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+ dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
+ return NULL;
+ }
+
+ printk("USB HPNA Pegasus found\n");
+
+ if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ err("usb_set_configuration() failed");
+ return NULL;
+ }
+
+ hpna->usb_dev = dev;
+
+ hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
+ hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
+ hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
+
+ if ( reset_mac(dev) ) {
+ err("can't reset MAC");
+ }
+
+ hpna->present = 1;
+
+ if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
+ err("not enough mem for out buff");
+ return NULL;
+ }
+ if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
+ kfree_s(hpna->rx_buff, MAX_MTU);
+ err("not enough mem for out buff");
+ return NULL;
+ }
+
+ net_dev = init_etherdev( 0, 0 );
+ hpna->net_dev = net_dev;
+ net_dev->priv = hpna;
+ net_dev->open = hpna_open;
+ net_dev->stop = hpna_close;
+ net_dev->watchdog_timeo = TX_TIMEOUT;
+ net_dev->tx_timeout = tx_timeout;
+ net_dev->do_ioctl = hpna_ioctl;
+ net_dev->hard_start_xmit = hpna_start_xmit;
+ net_dev->set_multicast_list = set_rx_mode;
+ net_dev->get_stats = hpna_netdev_stats;
+ net_dev->mtu = HPNA_MTU;
+ hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
+
+ FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe,
+ hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
+ FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe,
+ hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
+ FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
+ hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
+
+/* list_add( &hpna->list, &hpna_list );*/
+
+ return net_dev;
+}
+
+
+static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+{
+ struct net_device *net_dev = ptr;
+ struct usb_hpna *hpna = net_dev->priv;
+
+
+ if ( net_dev->flags & IFF_UP )
+ dev_close(net_dev);
+
+ unregister_netdev( net_dev );
+
+ if ( !hpna ) /* should never happen */
+ return;
+
+ usb_unlink_urb( &hpna->rx_urb );
+ usb_unlink_urb( &hpna->tx_urb );
+/* usb_unlink_urb( &hpna->intr_urb );*/
+ kfree_s(hpna->rx_buff, MAX_MTU);
+ kfree_s(hpna->tx_buff, MAX_MTU);
+
+ hpna->usb_dev = NULL;
+ hpna->present = 0;
+
+ printk("USB HPNA disconnected\n");
+}
+
+
+static struct usb_driver usb_hpna_driver = {
+ "ADMtek \"Pegasus\" USB Ethernet",
+ usb_hpna_probe,
+ usb_hpna_disconnect,
+ {NULL, NULL}
+};
+
+
+
+static int __init start_hpna( void )
+{
+ printk( version );
+ return usb_register( &usb_hpna_driver );
+}
+
+
+static void __exit stop_hpna( void )
+{
+ usb_deregister( &usb_hpna_driver );
+}
+
+
+module_init( start_hpna );
+module_exit( stop_hpna );
diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c
new file mode 100644
index 000000000..e25ba03d3
--- /dev/null
+++ b/drivers/usb/rio500.c
@@ -0,0 +1,455 @@
+/* -*- linux-c -*- */
+
+/*
+ * Driver for USB Rio 500
+ *
+ * Cesar Miquel (miquel@df.uba.ar)
+ *
+ * based on hp_scanner.c by David E. Nelson (dnelson@jump.net)
+ *
+ * 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.
+ *
+ * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
+
+#include "usb.h"
+
+#include "rio500_usb.h"
+
+#define RIO_MINOR 64
+
+/* stall/wait timeout for rio */
+#define NAK_TIMEOUT (HZ)
+
+#define IBUF_SIZE 128
+
+/* Size of the rio buffer */
+#define OBUF_SIZE 0x10000
+
+struct rio_usb_data {
+ struct usb_device *rio_dev; /* init: probe_rio */
+ unsigned int ifnum; /* Interface number of the USB device */
+ int isopen; /* nz if open */
+ int present; /* Device is present on the bus */
+ char *obuf, *ibuf; /* transfer buffers */
+ char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */
+ wait_queue_head_t wait_q; /* for timeouts */
+};
+
+static struct rio_usb_data rio_instance;
+
+static int open_rio(struct inode *inode, struct file *file)
+{
+ struct rio_usb_data *rio = &rio_instance;
+
+ if (rio->isopen || !rio->present) {
+ return -EBUSY;
+ }
+ rio->isopen = 1;
+
+ init_waitqueue_head(&rio->wait_q);
+
+ MOD_INC_USE_COUNT;
+
+ info("Rio opened.");
+
+ return 0;
+}
+
+static int close_rio(struct inode *inode, struct file *file)
+{
+ struct rio_usb_data *rio = &rio_instance;
+
+ rio->isopen = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ info("Rio closed.");
+ return 0;
+}
+
+static int
+ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct RioCommand rio_cmd;
+ struct rio_usb_data *rio = &rio_instance;
+ void *data;
+ unsigned char *buffer;
+ int result, requesttype;
+ int retries;
+
+ /* Sanity check to make sure rio is connected, powered, etc */
+ if ( rio == NULL ||
+ rio->present == 0 ||
+ rio->rio_dev == NULL )
+ return -1;
+
+ switch (cmd) {
+ case RIO_RECV_COMMAND:
+ data = (void *) arg;
+ if (data == NULL)
+ break;
+ copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand),
+ -EFAULT);
+ if (rio_cmd.length > PAGE_SIZE)
+ return -EINVAL;
+ buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+ if (buffer == NULL)
+ return -ENOMEM;
+ copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length,
+ -EFAULT);
+
+ requesttype = rio_cmd.requesttype | USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ dbg
+ ("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
+ requesttype, rio_cmd.request, rio_cmd.value,
+ rio_cmd.index, rio_cmd.length);
+ /* Send rio control message */
+ retries = 3;
+ while (retries) {
+ result = usb_control_msg(rio->rio_dev,
+ usb_rcvctrlpipe(rio-> rio_dev, 0),
+ rio_cmd.request,
+ requesttype,
+ rio_cmd.value,
+ rio_cmd.index, buffer,
+ rio_cmd.length,
+ rio_cmd.timeout);
+ if (result == -ETIMEDOUT)
+ retries--;
+ else if (result < 0) {
+ err("Error executing ioctrl. code = %d",
+ le32_to_cpu(result));
+ retries = 0;
+ } else {
+ dbg("Executed ioctl. Result = %d (data=%04x)",
+ le32_to_cpu(result),
+ le32_to_cpu(*((long *) buffer)));
+ copy_to_user_ret(rio_cmd.buffer, buffer,
+ rio_cmd.length, -EFAULT);
+ retries = 0;
+ }
+
+ /* rio_cmd.buffer contains a raw stream of single byte
+ data which has been returned from rio. Data is
+ interpreted at application level. For data that
+ will be cast to data types longer than 1 byte, data
+ will be little_endian and will potentially need to
+ be swapped at the app level */
+
+ }
+ free_page((unsigned long) buffer);
+ break;
+
+ case RIO_SEND_COMMAND:
+ data = (void *) arg;
+ if (data == NULL)
+ break;
+ copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand),
+ -EFAULT);
+ if (rio_cmd.length > PAGE_SIZE)
+ return -EINVAL;
+ buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+ if (buffer == NULL)
+ return -ENOMEM;
+ copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length,
+ -EFAULT);
+
+ requesttype = rio_cmd.requesttype | USB_DIR_OUT |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ dbg("sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x",
+ requesttype, rio_cmd.request, rio_cmd.value,
+ rio_cmd.index, rio_cmd.length);
+ /* Send rio control message */
+ retries = 3;
+ while (retries) {
+ result = usb_control_msg(rio->rio_dev,
+ usb_sndctrlpipe(rio-> rio_dev, 0),
+ rio_cmd.request,
+ requesttype,
+ rio_cmd.value,
+ rio_cmd.index, buffer,
+ rio_cmd.length,
+ rio_cmd.timeout);
+ if (result == -ETIMEDOUT)
+ retries--;
+ else if (result < 0) {
+ err("Error executing ioctrl. code = %d",
+ le32_to_cpu(result));
+ retries = 0;
+ } else {
+ dbg("Executed ioctl. Result = %d",
+ le32_to_cpu(result));
+ retries = 0;
+
+ }
+
+ }
+ free_page((unsigned long) buffer);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ break;
+ }
+
+ return 0;
+}
+
+static ssize_t
+write_rio(struct file *file, const char *buffer,
+ size_t count, loff_t * ppos)
+{
+ struct rio_usb_data *rio = &rio_instance;
+
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned int partial;
+
+ int result = 0;
+ int maxretry;
+
+ /* Sanity check to make sure rio is connected, powered, etc */
+ if ( rio == NULL ||
+ rio->present == 0 ||
+ rio->rio_dev == NULL )
+ return -1;
+
+ do {
+ unsigned long thistime;
+ char *obuf = rio->obuf;
+
+ thistime = copy_size =
+ (count >= OBUF_SIZE) ? OBUF_SIZE : count;
+ if (copy_from_user(rio->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = 5;
+ while (thistime) {
+ if (!rio->rio_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+
+ result = usb_bulk_msg(rio->rio_dev,
+ usb_sndbulkpipe(rio->rio_dev, 2),
+ obuf, thistime, &partial, 5 * HZ);
+
+ dbg("write stats: result:%d thistime:%lu partial:%u",
+ result, thistime, partial);
+
+ if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
+ if (!maxretry--) {
+ return -ETIME;
+ }
+ interruptible_sleep_on_timeout(&rio-> wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (!result & partial) {
+ obuf += partial;
+ thistime -= partial;
+ } else
+ break;
+ };
+ if (result) {
+ err("Write Whoops - %x", result);
+ return -EIO;
+ }
+ bytes_written += copy_size;
+ count -= copy_size;
+ buffer += copy_size;
+ } while (count > 0);
+
+ return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t
+read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos)
+{
+ struct rio_usb_data *rio = &rio_instance;
+ ssize_t read_count;
+ unsigned int partial;
+ int this_read;
+ int result;
+ int maxretry = 10;
+ char *ibuf = rio->ibuf;
+
+ /* Sanity check to make sure rio is connected, powered, etc */
+ if ( rio == NULL ||
+ rio->present == 0 ||
+ rio->rio_dev == NULL )
+ return -1;
+
+ read_count = 0;
+
+ while (count > 0) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!rio->rio_dev)
+ return -ENODEV;
+ this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+
+ result = usb_bulk_msg(rio->rio_dev,
+ usb_rcvbulkpipe(rio->rio_dev, 1),
+ ibuf, this_read, &partial,
+ (int) (HZ * .1));
+
+ dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u",
+ result, this_read, partial);
+
+ if (partial) {
+ count = this_read = partial;
+ } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */
+ if (!maxretry--) {
+ err("read_rio: maxretry timeout");
+ return -ETIME;
+ }
+ interruptible_sleep_on_timeout(&rio->wait_q,
+ NAK_TIMEOUT);
+ continue;
+ } else if (result != USB_ST_DATAUNDERRUN) {
+ err("Read Whoops - result:%u partial:%u this_read:%u",
+ result, partial, this_read);
+ return -EIO;
+ } else {
+ return (0);
+ }
+
+ if (this_read) {
+ if (copy_to_user(buffer, ibuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static void *probe_rio(struct usb_device *dev, unsigned int ifnum)
+{
+ struct rio_usb_data *rio = &rio_instance;
+
+ if (dev->descriptor.idVendor != 0x841) {
+ return NULL;
+ }
+
+ if (dev->descriptor.idProduct != 0x1 /* RIO 500 */ ) {
+ warn(KERN_INFO "Rio player model not supported/tested.");
+ return NULL;
+ }
+
+ info("USB Rio found at address %d", dev->devnum);
+
+ rio->present = 1;
+ rio->rio_dev = dev;
+
+ if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+ err("probe_rio: Not enough memory for the output buffer");
+ return NULL;
+ }
+ dbg("probe_rio: obuf address:%p", rio->obuf);
+
+ if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+ err("probe_rio: Not enough memory for the input buffer");
+ kfree(rio->obuf);
+ return NULL;
+ }
+ dbg("probe_rio: ibuf address:%p", rio->ibuf);
+
+ return rio;
+}
+
+static void disconnect_rio(struct usb_device *dev, void *ptr)
+{
+ struct rio_usb_data *rio = (struct rio_usb_data *) ptr;
+
+ if (rio->isopen) {
+ rio->isopen = 0;
+ /* better let it finish - the release will do whats needed */
+ rio->rio_dev = NULL;
+ return;
+ }
+ kfree(rio->ibuf);
+ kfree(rio->obuf);
+
+ info("USB Rio disconnected.");
+
+ rio->present = 0;
+}
+
+static struct
+file_operations usb_rio_fops = {
+ NULL, /* seek */
+ read_rio,
+ write_rio,
+ NULL, /* readdir */
+ NULL, /* poll */
+ ioctl_rio, /* ioctl */
+ NULL, /* mmap */
+ open_rio,
+ NULL, /* flush */
+ close_rio,
+ NULL,
+ NULL, /* fasync */
+};
+
+static struct
+usb_driver rio_driver = {
+ "rio500",
+ probe_rio,
+ disconnect_rio,
+ {NULL, NULL},
+ &usb_rio_fops,
+ RIO_MINOR
+};
+
+int usb_rio_init(void)
+{
+ if (usb_register(&rio_driver) < 0)
+ return -1;
+
+ info("USB Rio support registered.");
+ return 0;
+}
+
+
+void usb_rio_cleanup(void)
+{
+ struct rio_usb_data *rio = &rio_instance;
+
+ rio->present = 0;
+ usb_deregister(&rio_driver);
+
+
+}
+
+module_init(usb_rio_init);
+module_exit(usb_rio_cleanup);
+
diff --git a/drivers/usb/rio500_usb.h b/drivers/usb/rio500_usb.h
new file mode 100644
index 000000000..9fc712f3d
--- /dev/null
+++ b/drivers/usb/rio500_usb.h
@@ -0,0 +1,37 @@
+/* ----------------------------------------------------------------------
+
+ Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar)
+
+ 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.
+
+ ---------------------------------------------------------------------- */
+
+
+
+#define RIO_SEND_COMMAND 0x1
+#define RIO_RECV_COMMAND 0x2
+
+#define RIO_DIR_OUT 0x0
+#define RIO_DIR_IN 0x1
+
+struct RioCommand {
+ short length;
+ int request;
+ int requesttype;
+ int value;
+ int index;
+ void *buffer;
+ int timeout;
+};
diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c
new file mode 100644
index 000000000..c67d30a60
--- /dev/null
+++ b/drivers/usb/wacom.c
@@ -0,0 +1,286 @@
+/*
+ * wacom.c Version 0.3
+ *
+ * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
+ * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ * Sponsored by SuSE
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
+ * v0.3 (vp) - Support for Intuos added
+ */
+
+/*
+ * 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 <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include "usb.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+
+/*
+ * Wacom Graphire packet:
+ *
+ * The input report:
+ *
+ * byte 0: report ID (2)
+ * byte 1: bit7 mouse/pen/rubber near
+ * bit5-6 0 - pen, 1 - rubber, 2 - mouse
+ * bit4 1 ?
+ * bit3 0 ?
+ * bit2 mouse middle button / pen button2
+ * bit1 mouse right button / pen button1
+ * bit0 mouse left button / pen tip / rubber
+ * byte 2: X low bits
+ * byte 3: X high bits
+ * byte 4: Y low bits
+ * byte 5: Y high bits
+ * byte 6: pen pressure low bits / mouse wheel
+ * byte 7: pen presure high bits / mouse distance
+ *
+ * There are also two single-byte feature reports (2 and 3).
+ *
+ * Resolution:
+ * X: 0 - 10206
+ * Y: 0 - 7422
+ *
+ * (0,0) is upper left corner
+ *
+ * Wacom Intuos packet:
+ *
+ * byte 0: report ID (2)
+ * byte 1: bit7 1 ?
+ * bit6 tilt (pressure?) data valid
+ * bit5 near
+ * bit4 0 ?
+ * bit3 0 ?
+ * bit2 pen button
+ * bit1 first packet (contains other infos)
+ * bit0 0 ?
+ * byte 2: X high bits
+ * byte 3: X low bits
+ * byte 4: Y high bits
+ * byte 5: Y low bits
+ * byte 6: bits 0-7: pressure (bits 2-9)
+ * byte 7: bits 6-7: pressure (bits 0-1)
+ * byte 7: bits 0-5: X tilt (bits 1-6)
+ * byte 8: bit 7: X tilt (bit 0)
+ * byte 8: bits 0-6: Y tilt (bits 0-6)
+ * byte 9: ?
+ */
+
+#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
+#define USB_DEVICE_ID_WACOM_INTUOS 0x0021
+
+struct wacom {
+ signed char data[12];
+ struct input_dev dev;
+ struct urb irq;
+};
+
+static void wacom_graphire_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+
+ if (urb->status) return;
+
+ if (data[0] != 2)
+ dbg("received unknown report #%d", data[0]);
+
+ if ( data[1] & 0x80 ) {
+ input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
+ input_report_abs(dev, ABS_Y, 7422 - (data[4] | ((__u32)data[5] << 8)));
+ }
+
+ switch ((data[1] >> 5) & 3) {
+
+ case 0: /* Pen */
+ input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
+ input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
+ input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
+ input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+ break;
+
+ case 1: /* Rubber */
+ input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+ input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
+ input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
+ input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+ break;
+
+ case 2: /* Mouse */
+ input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
+ input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
+ input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
+ input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
+ input_report_abs(dev, ABS_DISTANCE, data[7]);
+ input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+ break;
+ }
+}
+
+static void wacom_intuos_irq(struct urb *urb)
+{
+ struct wacom *wacom = urb->context;
+ unsigned char *data = wacom->data;
+ struct input_dev *dev = &wacom->dev;
+ unsigned int t;
+
+ if (urb->status) return;
+
+ if (data[0] != 2)
+ dbg("received unknown report #%d", data[0]);
+
+ if (data[1] & 0x02) /* First record, weird data */
+ return;
+
+ if (data[1] & 0x20) { /* Near */
+ input_report_abs(dev, ABS_X, (((unsigned int) data[2]) << 8) | data[3]);
+ input_report_abs(dev, ABS_Y, 16240 - ((((unsigned int) data[4]) << 8) | data[5]));
+ }
+
+ input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x20);
+ input_report_btn(dev, BTN_STYLUS, data[1] & 0x04);
+
+ t = (((unsigned int) data[6]) << 2) | ((data[7] & 0xC0) >> 6);
+
+ input_report_btn(dev, BTN_TOUCH, t > 10);
+ input_report_abs(dev, ABS_PRESSURE, t);
+
+ if (data[1] & 0x40) { /* Tilt data */
+ input_report_abs(dev, ABS_TILT_X, ((((unsigned int) data[7]) & 0x3f) << 1) | ((data[8] & 0x80) >> 7));
+ input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+ }
+}
+
+static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ struct usb_endpoint_descriptor *endpoint;
+ struct wacom *wacom;
+ char *name;
+
+ if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM ||
+ (dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE &&
+ dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS))
+ return NULL;
+
+ endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+
+ if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
+ memset(wacom, 0, sizeof(struct wacom));
+
+ switch (dev->descriptor.idProduct) {
+
+ case USB_DEVICE_ID_WACOM_GRAPHIRE:
+
+ wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+ wacom->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE);
+ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
+ wacom->dev.relbit[0] |= BIT(REL_WHEEL);
+ wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
+
+ wacom->dev.absmax[ABS_X] = 10206;
+ wacom->dev.absmax[ABS_Y] = 7422;
+ wacom->dev.absmax[ABS_PRESSURE] = 511;
+ wacom->dev.absmax[ABS_DISTANCE] = 32;
+
+ FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom->data, 8, wacom_graphire_irq, wacom, endpoint->bInterval);
+
+ name = "Graphire";
+ break;
+
+ case USB_DEVICE_ID_WACOM_INTUOS:
+
+ wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+ wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ wacom->dev.absbit[0] |= BIT(ABS_TILT_X) | BIT(ABS_TILT_Y);
+
+ wacom->dev.absmax[ABS_X] = 20320;
+ wacom->dev.absmax[ABS_Y] = 16240;
+ wacom->dev.absmax[ABS_PRESSURE] = 1024;
+ wacom->dev.absmax[ABS_TILT_X] = 127;
+ wacom->dev.absmax[ABS_TILT_Y] = 127;
+
+ FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom->data, 8, wacom_intuos_irq, wacom, endpoint->bInterval);
+
+ name = "Intuos";
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (usb_submit_urb(&wacom->irq)) {
+ kfree(wacom);
+ return NULL;
+ }
+
+ input_register_device(&wacom->dev);
+
+ printk(KERN_INFO "input%d: Wacom %s\n", wacom->dev.number, name);
+
+ return wacom;
+}
+
+static void wacom_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct wacom *wacom = ptr;
+ usb_unlink_urb(&wacom->irq);
+ input_unregister_device(&wacom->dev);
+ kfree(wacom);
+}
+
+static struct usb_driver wacom_driver = {
+ name: "wacom",
+ probe: wacom_probe,
+ disconnect: wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+ usb_register(&wacom_driver);
+ return 0;
+}
+
+static void __exit wacom_exit(void)
+{
+ usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);