diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/pegasus.c | 579 | ||||
-rw-r--r-- | drivers/usb/rio500.c | 455 | ||||
-rw-r--r-- | drivers/usb/rio500_usb.h | 37 | ||||
-rw-r--r-- | drivers/usb/wacom.c | 286 |
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); |