diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-19 22:45:37 +0000 |
commit | 6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch) | |
tree | 0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/usb | |
parent | ecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (diff) |
Merge with 2.4.0-test1-ac21 + pile of MIPS cleanups to make merging
possible. Chainsawed RM200 kernel to compile again. Jazz machine
status unknown.
Diffstat (limited to 'drivers/usb')
38 files changed, 3060 insertions, 940 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 74253ed99..042f742da 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -10,6 +10,11 @@ if [ ! "$CONFIG_USB" = "n" ]; then comment 'Miscellaneous USB options' bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH + else + define_bool CONFIG_USB_BANDWIDTH n + fi comment 'USB Controllers' if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then @@ -55,6 +60,7 @@ comment 'USB Devices' dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI fi comment 'USB HID' diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index f0ca0476e..ec8687ae6 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_USB_MICROTEK) += microtek.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 9dbd51973..b60733b4a 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -329,6 +329,7 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c if (!ACM_READY(acm)) return -EINVAL; if (acm->writeurb.status == -EINPROGRESS) return 0; + if (!count) return 0; count = (count > acm->writesize) ? acm->writesize : count; diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index ad94fef55..67f1635a6 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -1788,7 +1788,7 @@ static int get_rec_src(struct usb_mixerdev *ms) dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); continue; } - for (j = i; j < ms->numch; i++) { + for (j = i; j < ms->numch; j++) { if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) continue; mask |= 1 << j; @@ -1821,7 +1821,7 @@ static int set_rec_src(struct usb_mixerdev *ms, int srcmask) } /* first generate smask */ smask = bmask = 0; - for (j = i; j < ms->numch; i++) { + for (j = i; j < ms->numch; j++) { if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) continue; smask |= 1 << ms->ch[j].osschannel; @@ -1835,7 +1835,7 @@ static int set_rec_src(struct usb_mixerdev *ms, int srcmask) continue; if (j > 1) srcmask &= ~bmask; - for (j = i; j < ms->numch; i++) { + for (j = i; j < ms->numch; j++) { if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) continue; if (!(srcmask & (1 << ms->ch[j].osschannel))) @@ -1937,7 +1937,6 @@ static int usb_audio_open_mixdev(struct inode *inode, struct file *file) file->private_data = ms; s->count++; - MOD_INC_USE_COUNT; up(&open_sem); return 0; } @@ -1949,7 +1948,6 @@ static int usb_audio_release_mixdev(struct inode *inode, struct file *file) down(&open_sem); release(s); - MOD_DEC_USE_COUNT; return 0; } @@ -2045,6 +2043,7 @@ static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsign } static /*const*/ struct file_operations usb_mixer_fops = { + owner: THIS_MODULE, llseek: usb_audio_llseek, ioctl: usb_audio_ioctl_mixdev, open: usb_audio_open_mixdev, @@ -2609,7 +2608,6 @@ static int usb_audio_open(struct inode *inode, struct file *file) file->private_data = as; as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); s->count++; - MOD_INC_USE_COUNT; up(&open_sem); return 0; } @@ -2645,11 +2643,11 @@ static int usb_audio_release(struct inode *inode, struct file *file) as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); release(s); wake_up(&open_wait); - MOD_DEC_USE_COUNT; return 0; } static /*const*/ struct file_operations usb_audio_fops = { + owner: THIS_MODULE, llseek: usb_audio_llseek, read: usb_audio_read, write: usb_audio_write, @@ -3193,6 +3191,16 @@ static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer state->termtype = 0; } +static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid) +{ + unsigned int i; + + for (i = 0; i < state->nrmixch; i++) + if (state->mixch[i].unitid == unitid) + return &state->mixch[i]; + return NULL; +} + static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) { unsigned int chnum, i, mixch; @@ -3206,7 +3214,9 @@ static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *se usb_audio_recurseunit(state, selector[5]); if (state->nrmixch != mixch) { mch = &state->mixch[state->nrmixch-1]; - mch->slctunitid = selector[5] | (1 << 8); + mch->slctunitid = selector[3] | (1 << 8); + } else if ((mch = slctsrc_findunit(state, selector[5]))) { + mch->slctunitid = selector[3] | (1 << 8); } else { printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]); } @@ -3223,7 +3233,9 @@ static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *se } if (state->nrmixch != mixch) { mch = &state->mixch[state->nrmixch-1]; - mch->slctunitid = selector[5] | ((i + 1) << 8); + mch->slctunitid = selector[3] | ((i + 1) << 8); + } else if ((mch = slctsrc_findunit(state, selector[5+i]))) { + mch->slctunitid = selector[3] | ((i + 1) << 8); } else { printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1); } @@ -3604,8 +3616,10 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) #endif if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) { +#if 0 printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n", dev->descriptor.idVendor, dev->descriptor.idProduct); +#endif return NULL; } /* diff --git a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c index c13c4a02e..8b1df4edf 100644 --- a/drivers/usb/dabusb.c +++ b/drivers/usb/dabusb.c @@ -573,10 +573,9 @@ static int dabusb_open (struct inode *inode, struct file *file) int devnum = MINOR (inode->i_rdev); pdabusb_t s; - if (devnum < DABUSB_MINOR || devnum > (DABUSB_MINOR + NRDABUSB)) + if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) return -EIO; - MOD_INC_USE_COUNT; s = &dabusb[devnum - DABUSB_MINOR]; dbg("dabusb_open"); @@ -586,20 +585,17 @@ static int dabusb_open (struct inode *inode, struct file *file) up (&s->mutex); if (file->f_flags & O_NONBLOCK) { - MOD_DEC_USE_COUNT; return -EBUSY; } schedule_timeout (HZ / 2); if (signal_pending (current)) { - MOD_DEC_USE_COUNT; return -EAGAIN; } down (&s->mutex); } if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { err("set_interface failed"); - MOD_DEC_USE_COUNT; return -EINVAL; } s->opened = 1; @@ -629,7 +625,6 @@ static int dabusb_release (struct inode *inode, struct file *file) else wake_up (&s->remove_ok); - MOD_DEC_USE_COUNT; s->opened = 0; return 0; } @@ -692,6 +687,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm static struct file_operations dabusb_fops = { + owner: THIS_MODULE, llseek: dabusb_llseek, read: dabusb_read, ioctl: dabusb_ioctl, diff --git a/drivers/usb/dabusb.h b/drivers/usb/dabusb.h index ddb3685a4..82877003c 100644 --- a/drivers/usb/dabusb.h +++ b/drivers/usb/dabusb.h @@ -49,7 +49,7 @@ typedef struct #define _DABUSB_IF 2 -#define _DABUSB_ISOPIPE 0x89 +#define _DABUSB_ISOPIPE 0x09 #define _ISOPIPESIZE 16384 #define _BULK_DATA_LEN 64 diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c index b440a82af..4fcfa640c 100644 --- a/drivers/usb/dc2xx.c +++ b/drivers/usb/dc2xx.c @@ -82,8 +82,8 @@ /* table of cameras that work through this driver */ static const struct camera { - short idVendor; - short idProduct; + unsigned short idVendor; + unsigned short idProduct; /* plus hooks for camera-specific info if needed */ } cameras [] = { /* These have the same application level protocol */ @@ -98,7 +98,7 @@ static const struct camera { { 0x040a, 0x0110 }, // Kodak DC-260 { 0x040a, 0x0111 }, // Kodak DC-265 { 0x040a, 0x0112 }, // Kodak DC-290 -// { 0x03f0, 0xffff }, // HP PhotoSmart C500 + { 0xf003, 0x6002 }, // HP PhotoSmart C500 /* Other USB devices may well work here too, so long as they * just stick to half duplex bulk packet exchanges. That @@ -284,9 +284,6 @@ static int camera_open (struct inode *inode, struct file *file) } dbg ("open"); - - /* Keep driver from being unloaded while it's in use */ - MOD_INC_USE_COUNT; camera->isActive = 0; file->private_data = camera; @@ -306,8 +303,6 @@ static int camera_release (struct inode *inode, struct file *file) kfree (camera); } - MOD_DEC_USE_COUNT; - dbg ("close"); return 0; @@ -319,6 +314,7 @@ static int camera_release (struct inode *inode, struct file *file) */ static /* const */ struct file_operations usb_camera_fops = { /* Uses GCC initializer extension; simpler to maintain */ + owner: THIS_MODULE, read: camera_read, write: camera_write, open: camera_open, diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index c5d24ec4f..235385515 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -187,26 +187,60 @@ static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t ssize_t ret = 0; unsigned len; loff_t pos; + int i; pos = *ppos; down_read(&ps->devsem); - if (!ps->dev) + if (!ps->dev) { ret = -ENODEV; - else if (pos < 0) + goto err; + } else if (pos < 0) { ret = -EINVAL; - else if (pos < sizeof(struct usb_device_descriptor)) { + goto err; + } + + if (pos < sizeof(struct usb_device_descriptor)) { len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; - if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) + if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) { ret = -EFAULT; - else { + goto err; + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos = sizeof(struct usb_device_descriptor); + for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) { + struct usb_config_descriptor *config = + (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; + unsigned int length = le16_to_cpu(config->wTotalLength); + + if (*ppos < pos + length) { + len = length - (*ppos - pos); + if (len > nbytes) + len = nbytes; + + if (copy_to_user(buf, + ps->dev->rawdescriptors[i] + (*ppos - pos), len)) { + ret = -EFAULT; + goto err; + } + *ppos += len; buf += len; nbytes -= len; ret += len; } + + pos += length; } + +err: up_read(&ps->devsem); return ret; } @@ -376,12 +410,9 @@ static void driver_disconnect(struct usb_device *dev, void *context) } struct usb_driver usbdevfs_driver = { - "usbdevfs", - driver_probe, - driver_disconnect, - LIST_HEAD_INIT(usbdevfs_driver.driver_list), - NULL, - 0 + name: "usbdevfs", + probe: driver_probe, + disconnect: driver_disconnect, }; static int claimintf(struct dev_state *ps, unsigned int intf) @@ -481,6 +512,28 @@ static int findintfif(struct usb_device *dev, unsigned int ifn) return -ENOENT; } +extern struct list_head usb_driver_list; + +static int finddriver(struct usb_driver **driver, char *name) +{ + struct list_head *tmp; + + tmp = usb_driver_list.next; + while (tmp != &usb_driver_list) { + struct usb_driver *d = list_entry(tmp, struct usb_driver, + driver_list); + + if (!strcmp(d->name, name)) { + *driver = d; + return 0; + } + + tmp = tmp->next; + } + + return -EINVAL; +} + /* * file operations */ @@ -662,16 +715,77 @@ static int proc_resetep(struct dev_state *ps, void *arg) return 0; } +static int proc_getdriver(struct dev_state *ps, void *arg) +{ + struct usbdevfs_getdriver gd; + struct usb_interface *interface; + int ret; + + copy_from_user_ret(&gd, arg, sizeof(gd), -EFAULT); + if ((ret = findintfif(ps->dev, gd.interface)) < 0) + return ret; + interface = usb_ifnum_to_if(ps->dev, gd.interface); + if (!interface) + return -EINVAL; + if (!interface->driver) + return -ENODATA; + strcpy(gd.driver, interface->driver->name); + copy_to_user_ret(arg, &gd, sizeof(gd), -EFAULT); + return 0; +} + +static int proc_connectinfo(struct dev_state *ps, void *arg) +{ + struct usbdevfs_connectinfo ci; + + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->slow; + copy_to_user_ret(arg, &ci, sizeof(ci), -EFAULT); + return 0; +} + +static int proc_resetdevice(struct dev_state *ps) +{ + int i, ret; + + ret = usb_reset_device(ps->dev); + if (ret < 0) + return ret; + + for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = &ps->dev->actconfig->interface[i]; + + /* Don't simulate interfaces we've claimed */ + if (test_bit(i, &ps->ifclaimed)) + continue; + + if (intf->driver) { + down(&intf->driver->serialize); + intf->driver->disconnect(ps->dev, intf->private_data); + intf->driver->probe(ps->dev, i); + up(&intf->driver->serialize); + } + } + + return 0; +} + static int proc_setintf(struct dev_state *ps, void *arg) { struct usbdevfs_setinterface setintf; + struct usb_interface *interface; int ret; copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT); if ((ret = findintfif(ps->dev, setintf.interface)) < 0) return ret; - if ((ret = checkintf(ps, ret))) - return ret; + interface = usb_ifnum_to_if(ps->dev, setintf.interface); + if (!interface) + return -EINVAL; + if (interface->driver) { + if ((ret = checkintf(ps, ret))) + return ret; + } if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting)) return -EINVAL; return 0; @@ -913,6 +1027,65 @@ static int proc_releaseinterface(struct dev_state *ps, void *arg) return releaseintf(ps, intf); } +static int proc_ioctl (struct dev_state *ps, void *arg) +{ + struct usbdevfs_ioctl ctrl; + int size; + void *buf = 0; + int retval = 0; + + /* get input parameters and alloc buffer */ + copy_from_user_ret (&ctrl, (void *) arg, sizeof (ctrl), -EFAULT); + if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { + if ((buf = kmalloc (size, GFP_KERNEL)) == 0) + return -ENOMEM; + if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0 + && copy_from_user (buf, ctrl.data, size) != 0) { + kfree (buf); + return -EFAULT; + } else + memset (arg, size, 0); + } + + /* ioctl to device */ + if (ctrl.ifno < 0) { + switch (ctrl.ioctl_code) { + /* access/release token for issuing control messages + * ask a particular driver to bind/unbind, ... etc + */ + } + retval = -ENOSYS; + + /* ioctl to the driver which has claimed a given interface */ + } else { + struct usb_interface *ifp = 0; + if (!ps->dev) + retval = -ENODEV; + else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces) + retval = -EINVAL; + else { + if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + retval = -EINVAL; + else if (ifp->driver == 0 || ifp->driver->ioctl == 0) + retval = -ENOSYS; + } + if (retval == 0) + /* ifno might usefully be passed ... */ + retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf); + /* size = min (size, retval)? */ + } + + /* cleanup and return */ + if (retval >= 0 + && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 + && size > 0 + && copy_to_user (ctrl.data, buf, size) != 0) + retval = -EFAULT; + if (buf != 0) + kfree (buf); + return retval; +} + static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dev_state *ps = (struct dev_state *)file->private_data; @@ -944,6 +1117,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd inode->i_mtime = CURRENT_TIME; break; + case USBDEVFS_RESET: + ret = proc_resetdevice(ps); + break; + + case USBDEVFS_GETDRIVER: + ret = proc_getdriver(ps, (void *)arg); + break; + + case USBDEVFS_CONNECTINFO: + ret = proc_connectinfo(ps, (void *)arg); + break; + case USBDEVFS_SETINTERFACE: ret = proc_setintf(ps, (void *)arg); break; @@ -982,6 +1167,9 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ret = proc_releaseinterface(ps, (void *)arg); break; + case USBDEVFS_IOCTL: + ret = proc_ioctl(ps, (void *) arg); + break; } up_read(&ps->devsem); if (ret >= 0) diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c index 50bbafb31..e2056a2e3 100644 --- a/drivers/usb/dsbr100.c +++ b/drivers/usb/dsbr100.c @@ -33,6 +33,9 @@ History: + Version 0.23: + Markus: Sign extension bug fixed by declaring transfer_buffer unsigned + Version 0.22: Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, thanks to Mike Cox for pointing the problem out. @@ -74,7 +77,7 @@ static void usb_dsbr100_close(struct video_device *dev); typedef struct { struct urb readurb,writeurb; struct usb_device *dev; - char transfer_buffer[TB_LEN]; + unsigned char transfer_buffer[TB_LEN]; int curfreq; int stereo; int ifnum; diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c index ad3a8ffac..3e21f6cb5 100644 --- a/drivers/usb/evdev.c +++ b/drivers/usb/evdev.c @@ -1,7 +1,7 @@ /* - * evdev.c Version 0.1 + * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * Event char devices, giving access to raw input device events. * @@ -72,8 +72,7 @@ static void evdev_event(struct input_handle *handle, unsigned int type, unsigned list->buffer[list->head].value = value; list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + kill_fasync(&list->fasync, SIGIO, POLL_IN); list = list->next; } @@ -111,7 +110,6 @@ static int evdev_release(struct inode * inode, struct file * file) kfree(list); - MOD_DEC_USE_COUNT; return 0; } @@ -123,10 +121,7 @@ static int evdev_open(struct inode * inode, struct file * file) if (i > EVDEV_MINORS || !evdev_table[i]) return -ENODEV; - MOD_INC_USE_COUNT; - if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) { - MOD_DEC_USE_COUNT; return -ENOMEM; } memset(list, 0, sizeof(struct evdev_list)); @@ -207,14 +202,20 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct evdev_list *list = file->private_data; struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; + int retval; switch (cmd) { case EVIOCGVERSION: - return put_user(EV_VERSION, (__u32 *) arg); + return put_user(EV_VERSION, (int *) arg); + case EVIOCGID: - return copy_to_user(&dev->id, (void *) arg, - sizeof(struct input_id)) ? -EFAULT : 0; + if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval; + if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval; + if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval; + if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval; + return 0; + default: if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) @@ -222,8 +223,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { - long *bits = NULL; - int len = 0; + long *bits; + int len; switch (_IOC_NR(cmd) & EV_MAX) { case 0: bits = dev->evbit; len = EV_MAX; break; @@ -235,20 +236,40 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -EINVAL; } len = NBITS(len) * sizeof(long); - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - return copy_to_user((void *) arg, bits, len) ? -EFAULT : len; + if (len > _IOC_SIZE(cmd)) { + printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n", + len, _IOC_SIZE(cmd)); + len = _IOC_SIZE(cmd); + } + return copy_to_user((char *) arg, bits, len) ? -EFAULT : len; } if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { - int len = strlen(dev->name) + 1; + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len; } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval; + if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval; + if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval; + if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval; + if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval; + + return 0; + } } return -EINVAL; } static struct file_operations evdev_fops = { + owner: THIS_MODULE, read: evdev_read, write: evdev_write, poll: evdev_poll, @@ -286,7 +307,7 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); - printk("event%d: Event device for input%d\n", minor, dev->number); + printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); return &evdev->handle; } diff --git a/drivers/usb/graphire.c b/drivers/usb/graphire.c deleted file mode 100644 index 3537c8607..000000000 --- a/drivers/usb/graphire.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * graphire.c Version 0.2 - * - * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> - * - * USB Wacom Graphire tablet support - * - * Sponsored by SuSE - * - * ChangeLog: - * v0.1 (vp) - Initial release - * v0.2 (aba) - Support for all buttons / combinations - */ - -/* - * 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>"); - -/* - * Thanks for the following information to: Andreas Bach Aaen <abach@mail1.stofanet.dk> - * - * 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 - */ - -#define USB_VENDOR_ID_WACOM 0x056a -#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 - -struct graphire { - signed char data[8]; - struct input_dev dev; - struct urb irq; -}; - -static void graphire_irq(struct urb *urb) -{ - struct graphire *graphire = urb->context; - unsigned char *data = graphire->data; - struct input_dev *dev = &graphire->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_key(dev, BTN_TOOL_PEN, !!(data[1] & 0x80)); - input_report_key(dev, BTN_TOUCH, !!(data[1] & 0x01)); - input_report_key(dev, BTN_STYLUS, !!(data[1] & 0x02)); - input_report_key(dev, BTN_STYLUS2, !!(data[1] & 0x04)); - input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); - break; - - case 1: /* Rubber */ - input_report_key(dev, BTN_TOOL_RUBBER, !!(data[1] & 0x80)); - input_report_key(dev, BTN_TOUCH, !!(data[1] & 0x01)); - input_report_key(dev, BTN_STYLUS, !!(data[1] & 0x02)); - input_report_key(dev, BTN_STYLUS2, !!(data[1] & 0x04)); - input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); - break; - - case 2: /* Mouse */ - input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24); - input_report_key(dev, BTN_LEFT, !!(data[1] & 0x01)); - input_report_key(dev, BTN_RIGHT, !!(data[1] & 0x02)); - input_report_key(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 *graphire_probe(struct usb_device *dev, unsigned int ifnum) -{ - struct usb_endpoint_descriptor *endpoint; - struct graphire *graphire; - - if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM || - dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE) - return NULL; - - endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - - if (!(graphire = kmalloc(sizeof(struct graphire), GFP_KERNEL))) return NULL; - memset(graphire, 0, sizeof(struct graphire)); - - graphire->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); - graphire->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); - graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE); - graphire->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2); - graphire->dev.relbit[0] |= BIT(REL_WHEEL); - graphire->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE); - - graphire->dev.absmax[ABS_X] = 10206; - graphire->dev.absmax[ABS_Y] = 7422; - graphire->dev.absmax[ABS_PRESSURE] = 511; - graphire->dev.absmax[ABS_DISTANCE] = 32; - - FILL_INT_URB(&graphire->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), - graphire->data, 8, graphire_irq, graphire, endpoint->bInterval); - - if (usb_submit_urb(&graphire->irq)) { - kfree(graphire); - return NULL; - } - - input_register_device(&graphire->dev); - - printk(KERN_INFO "input%d: Wacom Graphire\n", graphire->dev.number); - - return graphire; -} - -static void graphire_disconnect(struct usb_device *dev, void *ptr) -{ - struct graphire *graphire = ptr; - usb_unlink_urb(&graphire->irq); - input_unregister_device(&graphire->dev); - kfree(graphire); -} - -static struct usb_driver graphire_driver = { - name: "graphire", - probe: graphire_probe, - disconnect: graphire_disconnect, -}; - -static int __init graphire_init(void) -{ - usb_register(&graphire_driver); - return 0; -} - -static void __exit graphire_exit(void) -{ - usb_deregister(&graphire_driver); -} - -module_init(graphire_init); -module_exit(graphire_exit); diff --git a/drivers/usb/hid-debug.h b/drivers/usb/hid-debug.h index b72565fb9..26f5e9e1b 100644 --- a/drivers/usb/hid-debug.h +++ b/drivers/usb/hid-debug.h @@ -1,5 +1,5 @@ /* - * driver/usb/hid-debug.h + * $Id: hid-debug.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $ * * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> * (c) 2000 Vojtech Pavlik <vojtech@suse.cz> diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c index 6c1f7f1d7..ae529b746 100644 --- a/drivers/usb/hid.c +++ b/drivers/usb/hid.c @@ -1,5 +1,5 @@ /* - * hid.c Version 0.8 + * $Id: hid.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000 Vojtech Pavlik @@ -80,6 +80,9 @@ static struct { __s32 y; } hid_hat_to_axis[] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0, 0}}; +static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", + "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; + /* * Register a new report for a device. */ @@ -1259,6 +1262,27 @@ static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code return 0; } +static int hid_open(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (hid->open++) + return 0; + + if (usb_submit_urb(&hid->urb)) + return -EIO; + + return 0; +} + +static void hid_close(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (!--hid->open) + usb_unlink_urb(&hid->urb); +} + /* * Configure the input layer interface * Read all reports and initalize the absoulte field values. @@ -1272,6 +1296,8 @@ static void hid_init_input(struct hid_device *hid) hid->input.private = hid; hid->input.event = hid_event; + hid->input.open = hid_open; + hid->input.close = hid_close; for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { @@ -1313,7 +1339,7 @@ struct hid_blacklist { { 0, 0 } }; -static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) +static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, char *name) { struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0; struct hid_descriptor *hdesc; @@ -1380,11 +1406,6 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval); - if (usb_submit_urb(&hid->urb)) { - dbg("submitting interrupt URB failed"); - continue; - } - break; } @@ -1405,6 +1426,20 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) hid->dr.index = hid->ifnum; hid->dr.length = 1; + hid->input.name = hid->name; + hid->input.idbus = BUS_USB; + hid->input.idvendor = dev->descriptor.idVendor; + hid->input.idproduct = dev->descriptor.idProduct; + hid->input.idversion = dev->descriptor.bcdDevice; + + if (strlen(name)) + strcpy(hid->name, name); + else + sprintf(hid->name, "USB HID %s %04x:%04x", + ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? + hid_types[hid->application & 0xffff] : "Device", + hid->input.idvendor, hid->input.idproduct); + FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), (void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid); @@ -1416,13 +1451,27 @@ static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum) static void* hid_probe(struct usb_device *dev, unsigned int ifnum) { - char *hid_name[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", - "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; struct hid_device *hid; + char name[128]; + char *buf; dbg("HID probe called for ifnum %d", ifnum); - if (!(hid = usb_hid_configure(dev, ifnum))) + name[0] = 0; + + if (!(buf = kmalloc(63, GFP_KERNEL))) + return NULL; + + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(name, "%s %s", name, buf); + + kfree(buf); + + if (!(hid = usb_hid_configure(dev, ifnum, name))) return NULL; hid_dump_device(hid); @@ -1430,10 +1479,18 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum) hid_init_input(hid); input_register_device(&hid->input); - printk(KERN_INFO "input%d: USB HID v%x.%02x %s\n", - hid->input.number, hid->version >> 8, hid->version & 0xff, + printk(KERN_INFO "input%d: USB HID v%x.%02x %s", + hid->input.number, + hid->version >> 8, hid->version & 0xff, ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? - hid_name[hid->application & 0xffff] : "device"); + hid_types[hid->application & 0xffff] : "Device"); + + if (strlen(name)) + printk(" [%s]", name); + else + printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct); + + printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum); return hid; } diff --git a/drivers/usb/hid.h b/drivers/usb/hid.h index 18e7481c2..88ab5eaca 100644 --- a/drivers/usb/hid.h +++ b/drivers/usb/hid.h @@ -2,7 +2,7 @@ #define __HID_H /* - * drivers/usb/hid.h Version 0.8 + * $Id: hid.h,v 1.4 2000/05/29 09:01:52 vojtech Exp $ * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000 Vojtech Pavlik @@ -292,7 +292,9 @@ struct hid_device { /* device report descriptor */ struct urb urb; /* USB URB structure */ struct urb urbout; /* Output URB */ struct input_dev input; /* input device structure */ + int open; /* is the device open by input? */ int quirks; /* Various nasty tricks the device can pull on us */ + char name[128]; /* Device name */ }; #define HID_GLOBAL_STACK_SIZE 4 diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 0b7cf6b09..9aa246ea0 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -18,7 +18,9 @@ #undef DEBUG #endif #include <linux/usb.h> +#include <linux/usbdevice_fs.h> +#include <asm/semaphore.h> #include <asm/uaccess.h> #include <asm/byteorder.h> @@ -26,6 +28,7 @@ /* Wakes up khubd */ static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(usb_address0_sem); static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */ @@ -120,18 +123,25 @@ static int usb_hub_configure(struct usb_hub *hub) struct usb_hub_descriptor *descriptor; struct usb_descriptor_header *header; struct usb_hub_status *hubsts; - int i; + int i, ret; /* Get the length first */ - if (usb_get_hub_descriptor(dev, buffer, 4) < 0) + ret = usb_get_hub_descriptor(dev, buffer, sizeof(*header)); + if (ret < 0) { + err("Unable to get partial hub descriptor (err = %d)", ret); return -1; + } header = (struct usb_descriptor_header *)buffer; bitmap = kmalloc(header->bLength, GFP_KERNEL); - if (!bitmap) + if (!bitmap) { + err("Unable to kmalloc %d bytes for bitmap", header->bLength); return -1; + } - if (usb_get_hub_descriptor(dev, bitmap, header->bLength) < 0) { + ret = usb_get_hub_descriptor(dev, bitmap, header->bLength); + if (ret < 0) { + err("Unable to get hub descriptor (err = %d)", ret); kfree(bitmap); return -1; } @@ -182,8 +192,11 @@ static int usb_hub_configure(struct usb_hub *hub) kfree(bitmap); - if (usb_get_hub_status(dev, buffer) < 0) + ret = usb_get_hub_status(dev, buffer); + if (ret < 0) { + err("Unable to get hub status (err = %d)", ret); return -1; + } hubsts = (struct usb_hub_status *)buffer; dbg("local power source is %s", @@ -225,12 +238,16 @@ static void *hub_probe(struct usb_device *dev, unsigned int i) endpoint = &interface->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ - if (!(endpoint->bEndpointAddress & USB_DIR_IN)) + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { + err("Device is hub class, but has output endpoint?"); return NULL; + } /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) + if ((endpoint->bmAttributes & 3) != 3) { + err("Device is hub class, but has endpoint other than interrupt?"); return NULL; + } /* We found a hub */ info("USB hub found"); @@ -321,17 +338,49 @@ static void hub_disconnect(struct usb_device *dev, void *ptr) kfree(hub); } +static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data) +{ + /* assert ifno == 0 (part of hub spec) */ + switch (code) { + case USBDEVFS_HUB_PORTINFO: { + struct usbdevfs_hub_portinfo *info = user_data; + unsigned long flags; + int i; + + spin_lock_irqsave (&hub_event_lock, flags); + if (hub->devnum <= 0) + info->nports = 0; + else { + info->nports = hub->maxchild; + for (i = 0; i < info->nports; i++) { + if (hub->children [i] == NULL) + info->port [i] = 0; + else + info->port [i] = hub->children [i]->devnum; + } + } + spin_unlock_irqrestore (&hub_event_lock, flags); + + return info->nports + 1; + } + + default: + return -ENOSYS; + } +} + static void usb_hub_port_connect_change(struct usb_device *hub, int port) { struct usb_device *usb; struct usb_port_status portsts; unsigned short portstatus, portchange; - int tries; + int ret, tries; wait_ms(100); - /* Check status */ - if (usb_get_port_status(hub, port + 1, &portsts)<0) { - err("get_port_status failed"); + + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); return; } @@ -351,21 +400,22 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) if (!(portstatus & USB_PORT_STAT_CONNECTION)) return; } - wait_ms(400); + wait_ms(400); - /* Reset the port */ + down(&usb_address0_sem); #define MAX_TRIES 5 - - for(tries=0;tries<MAX_TRIES;tries++) { - + /* Reset the port */ + for (tries = 0; tries < MAX_TRIES ; tries++) { usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); - wait_ms(200); - - if (usb_get_port_status(hub, port + 1, &portsts)<0) { - err("get_port_status failed"); - return; + wait_ms(200); + + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + goto out; } + portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("portstatus %x, change %x, %s", portstatus ,portchange, @@ -373,7 +423,7 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) if ((portchange & USB_PORT_STAT_C_CONNECTION) || !(portstatus & USB_PORT_STAT_CONNECTION)) - return; + goto out; if (portstatus & USB_PORT_STAT_ENABLE) break; @@ -381,20 +431,19 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) wait_ms(200); } - if (tries==MAX_TRIES) { + if (tries >= MAX_TRIES) { err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES); err("Maybe the USB cable is bad?"); - return; + goto out; } usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); /* Allocate a new device struct for it */ - usb = usb_alloc_dev(hub, hub->bus); if (!usb) { err("couldn't allocate usb_device"); - return; + goto out; } usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; @@ -405,12 +454,25 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) usb_connect(usb); /* Run it through the hoops (find a driver, etc) */ - if (usb_new_device(usb)) { - usb_disconnect(&hub->children[port]); - /* Woops, disable the port */ - dbg("hub: disabling port %d", port + 1); - usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE); + ret = usb_new_device(usb); + if (ret) { + /* Try resetting the device. Windows does this and it */ + /* gets some devices working correctly */ + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + + ret = usb_new_device(usb); + if (ret) { + usb_disconnect(&hub->children[port]); + + /* Woops, disable the port */ + dbg("hub: disabling port %d", port + 1); + usb_clear_port_feature(hub, port + 1, + USB_PORT_FEAT_ENABLE); + } } + +out: + up(&usb_address0_sem); } static void usb_hub_events(void) @@ -521,10 +583,6 @@ he_unlock: static int usb_hub_thread(void *__hub) { -/* - MOD_INC_USE_COUNT; -*/ - khubd_running = 1; lock_kernel(); @@ -545,10 +603,6 @@ static int usb_hub_thread(void *__hub) interruptible_sleep_on(&khubd_wait); } while (!signal_pending(current)); -/* - MOD_DEC_USE_COUNT; -*/ - dbg("usb_hub_thread exiting"); khubd_running = 0; @@ -556,10 +610,10 @@ static int usb_hub_thread(void *__hub) } static struct usb_driver hub_driver = { - "hub", - hub_probe, - hub_disconnect, - { NULL, NULL } + name: "hub", + probe: hub_probe, + ioctl: hub_ioctl, + disconnect: hub_disconnect }; /* @@ -569,8 +623,10 @@ int usb_hub_init(void) { int pid; - if (usb_register(&hub_driver) < 0) + if (usb_register(&hub_driver) < 0) { + err("Unable to register USB hub driver"); return -1; + } pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); @@ -615,28 +671,130 @@ void usb_hub_cleanup(void) usb_deregister(&hub_driver); } /* usb_hub_cleanup() */ +/* + * WARNING - If a driver calls usb_reset_device, you should simulate a + * disconnect() and probe() for other interfaces you doesn't claim. This + * is left up to the driver writer right now. This insures other drivers + * have a chance to re-setup their interface. + * + * Take a look at proc_resetdevice in devio.c for some sample code to + * do this. + */ int usb_reset_device(struct usb_device *dev) { struct usb_device *parent = dev->parent; - int i; + struct usb_device_descriptor descriptor; + int i, ret, port = -1; if (!parent) { err("attempting to reset root hub!"); return -EINVAL; } - for (i = 0; i < parent->maxchild; i++) { + for (i = 0; i < parent->maxchild; i++) if (parent->children[i] == dev) { - usb_set_port_feature(parent, i + 1, - USB_PORT_FEAT_RESET); + port = i; + break; + } + + if (port < 0) + return -ENOENT; - usb_disconnect(&dev); - usb_hub_port_connect_change(parent, i); + down(&usb_address0_sem); - return 0; + /* Send a reset to the device */ + usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET); + + wait_ms(200); + + usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET); + + /* Reprogram the Address */ + ret = usb_set_address(dev); + if (ret < 0) { + err("USB device not accepting new address (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + up(&usb_address0_sem); + return ret; + } + + wait_ms(10); /* Let the SET_ADDRESS settle */ + + up(&usb_address0_sem); + + /* + * Now we fetch the configuration descriptors for the device and + * see if anything has changed. If it has, we dump the current + * parsed descriptors and reparse from scratch. Then we leave + * the device alone for the caller to finish setting up. + * + * If nothing changed, we reprogram the configuration and then + * the alternate settings. + */ + ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor, + sizeof(descriptor)); + if (ret < 0) + return ret; + + le16_to_cpus(&descriptor.bcdUSB); + le16_to_cpus(&descriptor.idVendor); + le16_to_cpus(&descriptor.idProduct); + le16_to_cpus(&descriptor.bcdDevice); + + if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) { + usb_destroy_configuration(dev); + + ret = usb_get_device_descriptor(dev); + if (ret < sizeof(dev->descriptor)) { + if (ret < 0) + err("unable to get device descriptor (error=%d)", ret); + else + err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret); + + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return -EIO; + } + + ret = usb_get_configuration(dev); + if (ret < 0) { + err("unable to get configuration (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; } + + dev->actconfig = dev->config; + usb_set_maxpacket(dev); + + return 1; + } else { + ret = usb_set_configuration(dev, + dev->actconfig->bConfigurationValue); + if (ret < 0) { + err("failed to set active configuration (error=%d)", + ret); + return ret; + } + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = + &dev->actconfig->interface[i]; + struct usb_interface_descriptor *as = + &intf->altsetting[intf->act_altsetting]; + + ret = usb_set_interface(dev, as->bInterfaceNumber, + as->bAlternateSetting); + if (ret < 0) { + err("failed to set active alternate setting for interface %d (error=%d)", i, ret); + return ret; + } + } + + return 0; } - return -ENOENT; + return 0; } diff --git a/drivers/usb/input.c b/drivers/usb/input.c index 35f268fac..66a3d2911 100644 --- a/drivers/usb/input.c +++ b/drivers/usb/input.c @@ -1,7 +1,7 @@ /* - * input.c Version 0.1 + * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * The input layer module itself * @@ -91,11 +91,27 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in case EV_ABS: - if (code > ABS_MAX || !test_bit(code, dev->absbit) || (value == dev->abs[code])) + if (code > ABS_MAX || !test_bit(code, dev->absbit)) return; - dev->abs[code] = value; + if (dev->absfuzz[code]) { + if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) + return; + + if ((value > dev->abs[code] - dev->absfuzz[code]) && + (value < dev->abs[code] + dev->absfuzz[code])) + value = (dev->abs[code] * 3 + value) >> 2; + if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] << 1))) + value = (dev->abs[code] + value) >> 1; + } + + if (dev->abs[code] == value) + return; + + dev->abs[code] = value; break; case EV_REL: @@ -344,16 +360,25 @@ void input_unregister_handler(struct input_handler *handler) static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5]; + struct file_operations *old_fops; + int err; if (!handler || !handler->fops || !handler->fops->open) return -ENODEV; - file->f_op = handler->fops; - - return handler->fops->open(inode, file); + old_fops = file->f_op; + file->f_op = fops_get(handler->fops); + err = handler->fops->open(inode, file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; } static struct file_operations input_fops = { + owner: THIS_MODULE, open: input_open_file, }; diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c index 73ebc4e41..0f0bb2773 100644 --- a/drivers/usb/joydev.c +++ b/drivers/usb/joydev.c @@ -1,7 +1,7 @@ /* - * joydev.c Version 0.1 + * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke * * Joystick device driver for the input driver suite. @@ -53,7 +53,6 @@ struct joydev { int used; int open; int minor; - char name[32]; struct input_handle handle; wait_queue_head_t wait; devfs_handle_t devfs; @@ -139,8 +138,7 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) list->startup = 0; - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + kill_fasync(&list->fasync, SIGIO, POLL_IN); list = list->next; } @@ -178,7 +176,6 @@ static int joydev_release(struct inode * inode, struct file * file) kfree(list); - MOD_DEC_USE_COUNT; return 0; } @@ -190,10 +187,7 @@ static int joydev_open(struct inode *inode, struct file *file) if (i > JOYDEV_MINORS || !joydev_table[i]) return -ENODEV; - MOD_INC_USE_COUNT; - if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) { - MOD_DEC_USE_COUNT; return -ENOMEM; } memset(list, 0, sizeof(struct joydev_list)); @@ -322,6 +316,8 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd { struct joydev_list *list = file->private_data; struct joydev *joydev = list->joydev; + struct input_dev *dev = joydev->handle.dev; + switch (cmd) { @@ -360,9 +356,11 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; default: if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - int len = strlen(joydev->name) + 1; + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - if (copy_to_user((char *) arg, joydev->name, len)) return -EFAULT; + if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT; return len; } } @@ -370,6 +368,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd } static struct file_operations joydev_fops = { + owner: THIS_MODULE, read: joydev_read, write: joydev_write, poll: joydev_poll, @@ -401,8 +400,6 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct init_waitqueue_head(&joydev->wait); - sprintf(joydev->name, "joydev%d", joydev->minor); - joydev->minor = minor; joydev_table[minor] = joydev; @@ -449,7 +446,7 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); - printk("js%d: Joystick device for input%d\n", minor, dev->number); + printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number); return &joydev->handle; } diff --git a/drivers/usb/keybdev.c b/drivers/usb/keybdev.c index 5ba9869e9..76496349b 100644 --- a/drivers/usb/keybdev.c +++ b/drivers/usb/keybdev.c @@ -1,7 +1,7 @@ /* - * keybdev.c Version 0.1 + * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * Input driver to keyboard driver binding. * @@ -162,14 +162,14 @@ static struct input_handle *keybdev_connect(struct input_handler *handler, struc input_open_device(handle); - printk("keybdev.c: Adding keyboard: input%d\n", dev->number); + printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); return handle; } static void keybdev_disconnect(struct input_handle *handle) { - printk("keybdev.c: Removing keyboard: input%d\n", handle->dev->number); + printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); input_close_device(handle); kfree(handle); } diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c index 96fa3a151..dc578df33 100644 --- a/drivers/usb/mdc800.c +++ b/drivers/usb/mdc800.c @@ -539,17 +539,13 @@ static int mdc800_device_open (struct inode* inode, struct file *file) { int retval=0; - MOD_INC_USE_COUNT; - if (mdc800->state == NOT_CONNECTED) { - MOD_DEC_USE_COUNT; return -EBUSY; } if (mdc800->open) { - MOD_DEC_USE_COUNT; return -EBUSY; } @@ -568,7 +564,6 @@ static int mdc800_device_open (struct inode* inode, struct file *file) if (usb_submit_urb (mdc800->irq_urb)) { err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status); - MOD_DEC_USE_COUNT; return -EIO; } @@ -599,8 +594,6 @@ static int mdc800_device_release (struct inode* inode, struct file *file) retval=-EIO; } - MOD_DEC_USE_COUNT; - return retval; } @@ -833,6 +826,7 @@ static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t l /* File Operations of this drivers */ static struct file_operations mdc800_device_ops = { + owner: THIS_MODULE, read: mdc800_device_read, write: mdc800_device_write, open: mdc800_device_open, diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c new file mode 100644 index 000000000..6586b8c40 --- /dev/null +++ b/drivers/usb/microtek.c @@ -0,0 +1,1051 @@ +/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others. + * + * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com> + * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de> + * + * Parts shamelessly stolen from usb-storage and copyright by their + * authors. Thanks to Matt Dharm for giving us permission! + * + * This driver implements a SCSI host controller driver and a USB + * device driver. To avoid confusion, all the USB related stuff is + * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_. + * + * Microtek (www.microtek.com) did not release the specifications for + * their USB protocol to us, so we had to reverse engineer them. We + * don't know for which models they are valid. + * + * The X6 USB has three bulk endpoints, one output (0x1) down which + * commands and outgoing data are sent, and two input: 0x82 from which + * normal data is read from the scanner (in packets of maximum 32 + * bytes) and from which the status byte is read, and 0x83 from which + * the results of a scan (or preview) are read in up to 64 * 1024 byte + * chunks by the Windows driver. We don't know how much it is possible + * to read at a time from 0x83. + * + * It seems possible to read (with URB transfers) everything from 0x82 + * in one go, without bothering to read in 32 byte chunks. + * + * There seems to be an optimisation of a further READ implicit if + * you simply read from 0x83. + * + * Guessed protocol: + * + * Send raw SCSI command to EP 0x1 + * + * If there is data to receive: + * If the command was READ datatype=image: + * Read a lot of data from EP 0x83 + * Else: + * Read data from EP 0x82 + * Else: + * If there is data to transmit: + * Write it to EP 0x1 + * + * Read status byte from EP 0x82 + * + * References: + * + * The SCSI command set for the scanner is available from + * ftp://ftp.microtek.com/microtek/devpack/ + * + * Microtek NV sent us a more up to date version of the document. If + * you want it, just send mail. + * + * Status: + * + * This driver does not work properly yet. + * Untested with multiple scanners. + * Untested on SMP. + * Untested on UHCI. + * + * History: + * + * 20000417 starting history + * 20000417 fixed load oops + * 20000417 fixed unload oops + * 20000419 fixed READ IMAGE detection + * 20000424 started conversion to use URBs + * 20000502 handled short transfers as errors + * 20000513 rename and organisation of functions (john) + * 20000513 added IDs for all products supported by Windows driver (john) + * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john) + * 20000514 Version 0.0.8j + * 20000514 Fix reporting of non-existant devices to SCSI layer (john) + * 20000514 Added MTS_DEBUG_INT (john) + * 20000514 Changed "usb-microtek" to "microtek" for consistency (john) + * 20000514 Stupid bug fixes (john) + * 20000514 Version 0.0.9j + * 20000515 Put transfer context and URB in mts_desc (john) + * 20000515 Added prelim turn off debugging support (john) + * 20000515 Version 0.0.10j + * 20000515 Fixed up URB allocation (clear URB on alloc) (john) + * 20000515 Version 0.0.11j + * 20000516 Removed unnecessary spinlock in mts_transfer_context (john) + * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john) + * 20000516 Implemented (badly) scsi_abort (john) + * 20000516 Version 0.0.12j + * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john) + * 20000517 Added mts_debug_dump to print ll USB info (john) + * 20000518 Tweaks and documentation updates (john) + * 20000518 Version 0.0.13j + * 20000518 Cleaned up abort handling (john) + * 20000523 Removed scsi_command and various scsi_..._resets (john) + * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john) + * 20000523 Fixed last tiresome compile warning (john) + * 20000523 Version 0.0.14j (though version 0.1 has come out?) + * 20000602 Added primitive reset + * 20000602 Version 0.2.0 + * 20000603 various cosmetic changes + * 20000603 Version 0.2.1 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/spinlock.h> +#include <linux/smp_lock.h> +#include <linux/usb.h> +#include <linux/proc_fs.h> + +#include <asm/atomic.h> +#include <linux/blk.h> +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "microtek.h" + +/* Constants */ + +#define MTS_ABORT_TIMEOUT HZ /*jiffies*/ + + +/* Should we do debugging? */ + +#define MTS_DO_DEBUG + + +/* USB layer driver interface */ + +static void *mts_usb_probe(struct usb_device *dev, unsigned int interface); +static void mts_usb_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver mts_usb_driver = { + "microtek", + mts_usb_probe, + mts_usb_disconnect, + { NULL, NULL } /* we need no fops. let gcc fill it */ +}; + + +/* Internal driver stuff */ + +#define MTS_VERSION "0.2.1" +#define MTS_NAME "microtek usb (rev " MTS_VERSION "): " + +#define MTS_WARNING(x...) \ + printk( KERN_WARNING MTS_NAME ## x ) +#define MTS_ERROR(x...) \ + printk( KERN_ERR MTS_NAME ## x ) +#define MTS_INT_ERROR(x...) \ + MTS_ERROR( ## x ) +#define MTS_MESSAGE(x...) \ + printk( KERN_INFO MTS_NAME ## x ) + +#if defined MTS_DO_DEBUG + +#define MTS_DEBUG(x...) \ + printk( KERN_DEBUG MTS_NAME ## x ) + +#define MTS_DEBUG_GOT_HERE() \ + MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ ) +#define MTS_DEBUG_INT() \ + do { MTS_DEBUG_GOT_HERE(); \ + MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \ + MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ + mts_debug_dump(context->instance);\ + } while(0) +#else + +#define MTS_NUL_STATEMENT do { } while(0) + +#define MTS_DEBUG(x...) MTS_NUL_STATEMENT +#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT +#define MTS_DEBUG_INT() MTS_NUL_STATEMENT + +#endif + + + +#define MTS_INT_INIT()\ + do {\ + context = (struct mts_transfer_context*)transfer->context; \ + if (atomic_read(&context->do_abort)) {\ + mts_transfer_cleanup(transfer);\ + return;\ + }\ + MTS_DEBUG_INT();\ + } while (0) + +static inline void mts_debug_dump(struct mts_desc* desc) { + MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n", + (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0], + (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0] + ); + MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n", + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_response), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_image) + ); +} + + +static inline void mts_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: + MTS_DEBUG("can't decode command\n"); + goto out; + break; + } + MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len); + + out: + MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); +} + +static inline int mts_is_aborting(struct mts_desc* desc) { + return (atomic_read(&desc->context.do_abort)); +} + +static inline void mts_request_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + atomic_set(&desc->context.do_abort,1); +} + +static inline void mts_urb_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + if ( desc->urb.status == USB_ST_URB_PENDING ) { + usb_unlink_urb( &desc->urb ); + } +} + +static inline void mts_wait_abort(struct mts_desc* desc) +{ + mts_request_abort(desc); + + while( !atomic_read(&desc->lock.count) ) { +/* Is there a function to check if the semaphore is locked? */ + schedule_timeout( MTS_ABORT_TIMEOUT ); + MTS_DEBUG_GOT_HERE(); + mts_urb_abort(desc); + } + +} + + +static struct mts_desc * mts_list; /* list of active scanners */ +struct semaphore mts_list_semaphore; + +/* Internal list operations */ + +static +void mts_remove_nolock( struct mts_desc* to_remove ) +{ + MTS_DEBUG( "removing 0x%x from list\n", + (int)to_remove ); + + mts_wait_abort(to_remove); + + down( &to_remove->lock ); + + MTS_DEBUG_GOT_HERE(); + + if ( to_remove != mts_list ) { + MTS_DEBUG_GOT_HERE(); + if (to_remove->prev && to_remove->next) + to_remove->prev->next = to_remove->next; + } else { + MTS_DEBUG_GOT_HERE(); + mts_list = to_remove->next; + if (mts_list) { + MTS_DEBUG_GOT_HERE(); + mts_list->prev = 0; + } + } + + if ( to_remove->next ) { + MTS_DEBUG_GOT_HERE(); + to_remove->next->prev = to_remove->prev; + } + + MTS_DEBUG_GOT_HERE(); + scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl)); + + kfree( to_remove ); +} + +static +void mts_add_nolock( struct mts_desc* to_add ) +{ + MTS_DEBUG( "adding 0x%x to list\n", (int)to_add ); + + to_add->prev = 0; + to_add->next = mts_list; + if ( mts_list ) { + mts_list->prev = to_add; + } + + mts_list = to_add; +} + + + + +/* SCSI driver interface */ + +/* scsi related functions - dummies for now mostly */ + +static int mts_scsi_release(struct Scsi_Host *psh) +{ + MTS_DEBUG_GOT_HERE(); + + return 0; +} + +static int mts_scsi_abort (Scsi_Cmnd *srb) +/* interrupt context (!) */ +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + + mts_request_abort(desc); + mts_urb_abort(desc); + + return SCSI_ABORT_PENDING; +} + +static int mts_scsi_host_reset (Scsi_Cmnd *srb) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + + usb_reset_device(desc->usb_dev); + return 0; /* RANT why here 0 and not SUCCESS */ +} + +/* the core of the scsi part */ + +/* faking a detection - which can't fail :-) */ + +static int mts_scsi_detect (struct SHT * sht) +{ + /* Whole function stolen from usb-storage */ + + struct mts_desc * desc = (struct mts_desc *)sht->proc_dir; + /* What a hideous hack! */ + + char local_name[48]; + + MTS_DEBUG_GOT_HERE(); + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "microtek-%d", desc->host_number); + sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + /* FIXME: where is this freed ? */ + + if (!sht->proc_name) { + MTS_ERROR( "unable to allocate memory for proc interface!!\n" ); + return 0; + } + + strcpy(sht->proc_name, local_name); + + sht->proc_dir = NULL; + + /* In host->hostdata we store a pointer to desc */ + desc->host = scsi_register(sht, sizeof(desc)); + desc->host->hostdata[0] = (unsigned long)desc; +/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ + + return 1; +} + + + +/* Main entrypoint: SCSI commands are dispatched to here */ + + + +static +int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ); + +static void mts_transfer_cleanup( struct urb *transfer ); + + +inline static +void mts_int_submit_urb (struct urb* transfer, + int pipe, + void* data, + unsigned length, + mts_usb_urb_callback callback ) +/* Interrupt context! */ + +/* Holding transfer->context->lock! */ +{ + int res; + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + FILL_BULK_URB(transfer, + context->instance->usb_dev, + pipe, + data, + length, + callback, + context + ); + +/* transfer->transfer_flags = USB_DISABLE_SPD;*/ + transfer->transfer_flags = USB_ASYNC_UNLINK; + transfer->status = 0; + transfer->timeout = 100; + + res = usb_submit_urb( transfer ); + if ( res ) { + MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + context->srb->result = DID_ERROR << 16; + context->state = mts_con_error; + mts_transfer_cleanup(transfer); + } + return; +} + + +static void mts_transfer_cleanup( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; + + up( &context->instance->lock ); + if ( context->final_callback ) + context->final_callback(context->srb); + +} + +static void mts_transfer_done( struct urb *transfer ) +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->srb->result &= MTS_MAX_CHUNK_MASK; + context->srb->result |= (unsigned)context->status<<1; + + mts_transfer_cleanup(transfer); + + return; +} + + +static void mts_get_status( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->state = mts_con_status; + + mts_int_submit_urb(transfer, + usb_rcvbulkpipe(context->instance->usb_dev, + context->instance->ep_response), + &context->status, + 1, + mts_transfer_done ); + + + return; +} + +static void mts_data_done( struct urb* transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( context->data_length != transfer->actual_length ) { + context->srb->resid = context->data_length - transfer->actual_length; + } else if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + } + + mts_get_status(transfer); + + return; +} + + +static void mts_command_done( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + mts_transfer_cleanup(transfer); + + return; + } + + if ( context->data ) { + context->state = mts_con_data; + mts_int_submit_urb(transfer, + context->data_pipe, + context->data, + context->data_length, + mts_data_done); + } else mts_get_status(transfer); + + return; +} + + + + static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 }; + static const u8 mts_read_image_sig_len = 4; + static const unsigned char mts_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + +#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1) + +static void +mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) +{ + + int pipe; + + + MTS_DEBUG_GOT_HERE(); + + desc->context.instance = desc; + desc->context.srb = srb; + desc->context.state = mts_con_command; + atomic_set(&desc->context.do_abort,0); + + if ( !srb->bufflen ){ + desc->context.data = 0; + desc->context.data_length = 0; + return; + } else { + desc->context.data = srb->buffer; + desc->context.data_length = srb->bufflen; + } + + /* can't rely on srb->sc_data_direction */ + + /* Brutally ripped from usb-storage */ + + if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len ) +) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image); + MTS_DEBUG( "transfering from desc->ep_image == %d\n", + (int)desc->ep_image ); + } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) { + pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response); + MTS_DEBUG( "transfering from desc->ep_response == %d\n", + (int)desc->ep_response); + } else { + MTS_DEBUG("transfering to desc->ep_out == %d\n", + (int)desc->ep_out); + pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out); + } + desc->context.data_pipe = pipe; +} + + +static +int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + int err = 0; + int res; + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + mts_debug_dump(desc); + + if ( srb->device->lun || srb->device->id || srb->device->channel ) { + + MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel ); + + MTS_DEBUG("this device doesn't exist\n"); + + srb->result = DID_BAD_TARGET << 16; + + if(callback) + callback(srb); + + goto out; + } + + down(&desc->lock); + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + + + FILL_BULK_URB(&desc->urb, + desc->usb_dev, + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + srb->cmnd, + srb->cmd_len, + mts_command_done, + &desc->context + ); + + + mts_build_transfer_context( srb, desc ); + desc->context.final_callback = callback; + desc->urb.timeout = 100; + desc->urb.transfer_flags = USB_ASYNC_UNLINK; + +/* desc->urb.transfer_flags = USB_DISABLE_SPD;*/ + + res=usb_submit_urb(&desc->urb); + + if(res){ + MTS_ERROR("error %d submitting URB\n",(int)res); + srb->result = DID_ERROR << 16; + + if(callback) + callback(srb); + + goto out; + } + + MTS_DEBUG_GOT_HERE(); + + out: + return err; +} +/* + * this defines our 'host' + */ + +/* NOTE: This is taken from usb-storage, should be right. */ + + +static Scsi_Host_Template mts_scsi_host_template = { + name: "microtek", + detect: mts_scsi_detect, + release: mts_scsi_release, + command: 0, + queuecommand: mts_scsi_queuecommand, + + eh_abort_handler: mts_scsi_abort, + eh_device_reset_handler:0, + eh_bus_reset_handler: 0, + eh_host_reset_handler: mts_scsi_host_reset, + + can_queue: 1, + this_id: -1, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: FALSE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + + +/* USB layer driver interface implementation */ + +static void mts_usb_disconnect (struct usb_device *dev, void *ptr) +{ + struct mts_desc* to_remove = (struct mts_desc*)ptr; + + MTS_DEBUG_GOT_HERE(); + + /* leave the list - lock it */ + down(&mts_list_semaphore); + + mts_remove_nolock(to_remove); + + up(&mts_list_semaphore); +} + +struct vendor_product +{ + u16 idVendor; + u16 idProduct; + char* name; + enum + { + mts_sup_unknown=0, + mts_sup_alpha, + mts_sup_full + } + support_status; +} ; + + +/* These are taken from the msmUSB.inf file on the Windows driver CD */ +const static struct vendor_product mts_supported_products[] = +{ + { + 0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x94,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x99,"Scanmaker X6",mts_sup_alpha + }, + { + 0x5da, 0x9a,"Phantom C6",mts_sup_unknown + }, + { + 0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown + } +} +; +const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ]; + /* Must never be derefed, points to one after last entry */ + + +static void * mts_usb_probe (struct usb_device *dev, unsigned int interface) +{ + int i; + int result; + int ep_out = -1; + int ep_in_set[3]; /* this will break if we have more than three endpoints + which is why we check */ + int *ep_in_current = ep_in_set; + + struct mts_desc * new_desc; + struct vendor_product const* p; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting; + + MTS_DEBUG_GOT_HERE(); + MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); + + MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", + (int)dev->descriptor.idProduct, + (int)dev->descriptor.idVendor ); + + MTS_DEBUG_GOT_HERE(); + + /* checking IDs */ + for( p = mts_supported_products; p != mts_last_product; p++ ) + if ( dev->descriptor.idVendor == p->idVendor && + dev->descriptor.idProduct == p->idProduct ) + goto is_supported; + else + MTS_DEBUG( "doesn't appear to be model %s\n", p->name ); + + MTS_DEBUG( "returning NULL: unsupported\n" ); + + return NULL; + + is_supported: + + MTS_DEBUG_GOT_HERE(); + + MTS_DEBUG( "found model %s\n", p->name ); + if ( p->support_status != mts_sup_full ) + MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", + p->name ); + + /* the altsettting 0 on the interface we're probing */ + altsetting = + &(dev->actconfig->interface[interface].altsetting[0]); + + + /* Check if the config is sane */ + + if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) { + MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints ); + return NULL; + } + + for( i = 0; i < altsetting->bNumEndpoints; i++ ) { + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + + MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + (int)altsetting->endpoint[i].bEndpointAddress ); + } else { + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + *ep_in_current++ + = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else { + if ( ep_out != -1 ) { + MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); + return NULL; + } + + ep_out = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + } + + } + + + if ( ep_out == -1 ) { + MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + return NULL; + } + + + /* I don't understand the following fully (it's from usb-storage) -- John */ + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + + MTS_DEBUG("usb_set_interface returned %d.\n",result); + switch( result ) + { + case 0: /* no error */ + break; + + case -EPIPE: + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + MTS_DEBUG( "clearing clearing stall on control interface\n" ); + break; + + default: + MTS_DEBUG( "unknown error %d from usb_set_interface\n", + (int)result ); + return NULL; + } + + + /* allocating a new descriptor */ + new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL); + if (new_desc == NULL) + { + MTS_ERROR("couldn't allocate scanner desc, bailing out!\n"); + return NULL; + } + + /* As done by usb_alloc_urb */ + memset( new_desc, 0, sizeof(*new_desc) ); + spin_lock_init(&new_desc->urb.lock); + + + /* initialising that descriptor */ + new_desc->usb_dev = dev; + new_desc->interface = interface; + + init_MUTEX(&new_desc->lock); + + if(mts_list){ + new_desc->host_number = mts_list->host_number+1; + } else { + new_desc->host_number = 0; + } + + /* endpoints */ + + new_desc->ep_out = ep_out; + new_desc->ep_response = ep_in_set[0]; + new_desc->ep_image = ep_in_set[1]; + + + if ( new_desc->ep_out != MTS_EP_OUT ) + MTS_WARNING( "will this work? Command EP is not usually %d\n", + (int)new_desc->ep_out ); + + if ( new_desc->ep_response != MTS_EP_RESPONSE ) + MTS_WARNING( "will this work? Response EP is not usually %d\n", + (int)new_desc->ep_response ); + + if ( new_desc->ep_image != MTS_EP_IMAGE ) + MTS_WARNING( "will this work? Image data EP is not usually %d\n", + (int)new_desc->ep_image ); + + + /* Initialize the host template based on the default one */ + memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template)); + /* HACK from usb-storage - this is needed for scsi detection */ + (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */ + + MTS_DEBUG("registering SCSI module\n"); + + new_desc->ctempl.module = THIS_MODULE; + result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl)); + /* Will get hit back in microtek_detect by this func */ + if ( result ) + { + MTS_ERROR( "error %d from scsi_register_module! Help!\n", + (int)result ); + + /* FIXME: need more cleanup? */ + kfree( new_desc ); + return NULL; + } + MTS_DEBUG_GOT_HERE(); + + /* FIXME: the bomb is armed, must the host be registered under lock ? */ + /* join the list - lock it */ + down(&mts_list_semaphore); + + mts_add_nolock( new_desc ); + + up(&mts_list_semaphore); + + + MTS_DEBUG("completed probe and exiting happily\n"); + + return (void *)new_desc; +} + + + +/* get us noticed by the rest of the kernel */ + +int __init microtek_drv_init(void) +{ + int result; + + MTS_DEBUG_GOT_HERE(); + init_MUTEX(&mts_list_semaphore); + + if ((result = usb_register(&mts_usb_driver)) < 0) { + MTS_DEBUG("usb_register returned %d\n", result ); + return -1; + } else { + MTS_DEBUG("driver registered.\n"); + } + + return 0; +} + +void __exit microtek_drv_exit(void) +{ + struct mts_desc* next; + + MTS_DEBUG_GOT_HERE(); + + usb_deregister(&mts_usb_driver); + + down(&mts_list_semaphore); + + while (mts_list) { + /* keep track of where the next one is */ + next = mts_list->next; + + mts_remove_nolock( mts_list ); + + /* advance the list pointer */ + mts_list = next; + } + + up(&mts_list_semaphore); +} + +module_init(microtek_drv_init); +module_exit(microtek_drv_exit); diff --git a/drivers/usb/microtek.h b/drivers/usb/microtek.h new file mode 100644 index 000000000..939234560 --- /dev/null +++ b/drivers/usb/microtek.h @@ -0,0 +1,73 @@ + /* + * Driver for Microtek Scanmaker X6 USB scanner and possibly others. + * + * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com> + * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de> + * + * See microtek.c for history + * + */ + +typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *); +typedef void (*mts_usb_urb_callback) (struct urb *); + + +struct mts_transfer_context +{ + struct mts_desc* instance; + mts_scsi_cmnd_callback final_callback; + Scsi_Cmnd *srb; + + void* data; + unsigned data_length; + int data_pipe; + + enum { + mts_con_none, + mts_con_command, + mts_con_data, + mts_con_status, + mts_con_error, + mts_con_done + } + state; + + atomic_t do_abort; /* when != 0 URB completion routines will + return straightaway */ + + u8 status; /* status returned from ep_response after command completion */ +}; + + +struct mts_desc { + struct mts_desc *next; + struct mts_desc *prev; + + struct usb_device *usb_dev; + + int interface; + + /* Endpoint addresses */ + u8 ep_out; + u8 ep_response; + u8 ep_image; + + struct Scsi_Host * host; + Scsi_Host_Template ctempl; + int host_number; + + struct semaphore lock; + + struct urb urb; + struct mts_transfer_context context; +}; + + +#define MTS_EP_OUT 0x1 +#define MTS_EP_RESPONSE 0x2 +#define MTS_EP_IMAGE 0x3 +#define MTS_EP_TOTAL 0x3 + +#define MTS_MAX_CHUNK_MASK ~0x3fu +/*maximum amount the scanner will transmit at once */ + diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c index d80385bad..399d2a0c0 100644 --- a/drivers/usb/mousedev.c +++ b/drivers/usb/mousedev.c @@ -1,7 +1,7 @@ /* - * mousedev.c Version 0.1 + * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * Input driver to PS/2 or ImPS/2 device driver module. * @@ -138,8 +138,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig list->ready = 1; - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + kill_fasync(&list->fasync, SIGIO, POLL_IN); list = list->next; } @@ -191,7 +190,6 @@ static int mousedev_release(struct inode * inode, struct file * file) kfree(list); - MOD_DEC_USE_COUNT; return 0; } @@ -203,12 +201,8 @@ static int mousedev_open(struct inode * inode, struct file * file) if (i > MOUSEDEV_MINORS || !mousedev_table[i]) return -ENODEV; - MOD_INC_USE_COUNT; - - if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) { - MOD_DEC_USE_COUNT; + if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) return -ENOMEM; - } memset(list, 0, sizeof(struct mousedev_list)); list->mousedev = mousedev_table[i]; @@ -311,8 +305,7 @@ static ssize_t mousedev_write(struct file * file, const char * buffer, size_t co list->buffer = list->bufsiz; } - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + kill_fasync(&list->fasync, SIGIO, POLL_IN); wake_up_interruptible(&list->mousedev->wait); @@ -376,6 +369,7 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait) } struct file_operations mousedev_fops = { + owner: THIS_MODULE, read: mousedev_read, write: mousedev_write, poll: mousedev_poll, @@ -421,7 +415,7 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru if (mousedev_mix.open) input_open_device(&mousedev->handle); - printk("mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); + printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); return &mousedev->handle; } @@ -459,7 +453,7 @@ static int __init mousedev_init(void) mousedev_mix.minor = MOUSEDEV_MIX; mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); - printk("mice: PS/2 mouse device common for all mice\n"); + printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); return 0; } diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 3ffb6f8eb..98c1c830f 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -2,8 +2,8 @@ * OmniVision OV511 Camera-to-USB Bridge Driver * * Copyright (c) 1999-2000 Mark W. McClelland - * Many improvements by Bret Wallach - * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000 + * Many improvements by Bret Wallach <bwallac1@san.rr.com> + * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) * Snapshot code by Kevin Moore * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> * Changes by Claudio Matsuoka <claudio@conectiva.com> @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.15"; +static const char version[] = "1.16"; #define __NO_VERSION__ @@ -55,6 +55,8 @@ static const char version[] = "1.15"; #include "ov511.h" +#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ + #define OV511_I2C_RETRIES 3 /* Video Size 640 x 480 x 3 bytes for RGB */ @@ -64,8 +66,8 @@ static const char version[] = "1.15"; #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 -#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_RGB24 ? 384 : 256) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_RGB24 ? 24 : 8) +#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -280,6 +282,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ + out += sprintf (out, "driver_version : %s\n", version); out += sprintf (out, "custom_id : %d\n", ov511->customid); out += sprintf (out, "model : %s\n", ov511->desc ? clist[ov511->desc].description : "unknown"); @@ -300,10 +303,6 @@ static int ov511_read_proc(char *page, char **start, off_t off, ov511->frame[i].depth); out += sprintf (out, " size : %d %d\n", ov511->frame[i].width, ov511->frame[i].height); -#if 0 - out += sprintf (out, " hdr_size : %d %d\n", - ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight); -#endif out += sprintf (out, " format : "); for (j = 0; plist[j].num >= 0; j++) { if (plist[j].num == ov511->frame[i].format) { @@ -317,10 +316,6 @@ static int ov511_read_proc(char *page, char **start, off_t off, ov511->frame[i].segsize); out += sprintf (out, " data_buffer : 0x%p\n", ov511->frame[i].data); -#if 0 - out += sprintf (out, " bytesread : %ld\n", - ov511->frame[i].bytes_read); -#endif } out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled)); out += sprintf (out, "bridge : %s\n", @@ -846,28 +841,20 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } -/* FIXME: add 400x300, 176x144, 160x140 */ +/* FIXME: 176x144, 160x140 */ +/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */ static struct mode_list mlist[] = { - { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00, - 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e }, - { 640, 480, VIDEO_PALETTE_RGB24,0x4f, 0x3d, 0x00, 0x00, - 0x4f, 0x3d, 0x00, 0x00, 0x06, 0x03, 0x24, 0x04, 0x9e }, - { 320, 240, VIDEO_PALETTE_GREY, 0x27, 0x1f, 0x00, 0x00, - 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, - { 320, 240, VIDEO_PALETTE_RGB24,0x27, 0x1f, 0x00, 0x00, - 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, - { 352, 288, VIDEO_PALETTE_GREY, 0x2b, 0x25, 0x00, 0x00, - 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, - { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00, - 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, VIDEO_PALETTE_GREY, 0x2f, 0x25, 0x00, 0x00, - 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, VIDEO_PALETTE_RGB24,0x2f, 0x25, 0x00, 0x00, - 0x2f, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, VIDEO_PALETTE_GREY, 0x37, 0x29, 0x00, 0x00, - 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, VIDEO_PALETTE_RGB24,0x37, 0x29, 0x00, 0x00, - 0x37, 0x29, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */ + { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, + { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, + { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, + { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, + { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, { 0, 0 } }; @@ -875,11 +862,9 @@ static int ov511_mode_init_regs(struct usb_ov511 *ov511, int width, int height, int mode, int sub_flag) { - int rc = 0; - struct usb_device *dev = ov511->dev; - int hwsbase = 0; - int hwebase = 0; int i; + struct usb_device *dev = ov511->dev; + int hwsbase = 0, hwebase = 0; PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); @@ -930,87 +915,55 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, break; } -#if 0 - /* FIXME: subwindow support is currently broken! - */ - if (width == 640 && height == 480) { - if (sub_flag) { - /* horizontal window start */ - ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2)); - /* horizontal window end */ - ov511_i2c_write(dev, 0x18, - hwebase+((ov511->subx+ov511->subw)>>2)); - /* vertical window start */ - ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1)); - /* vertical window end */ - ov511_i2c_write(dev, 0x1a, - 0x5+((ov511->suby+ov511->subh)>>1)); - ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1); - ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1); - /* clock rate control */ - ov511_i2c_write(dev, 0x11, 0x01); - - /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, (ov511->subw>>3)-1); - ov511_reg_write(dev, 0x1b, (ov511->subh>>3)-1); - ov511_reg_write(dev, 0x1c, 0x00); - ov511_reg_write(dev, 0x1d, 0x00); - } else { - ov511_i2c_write(dev, 0x17, hwsbase); - ov511_i2c_write(dev, 0x18, hwebase + (640>>2)); - ov511_i2c_write(dev, 0x19, 0x5); - ov511_i2c_write(dev, 0x1a, 5 + (480>>1)); - ov511_reg_write(dev, 0x12, 0x4f); - ov511_reg_write(dev, 0x13, 0x3d); - - /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, 0x4f); - ov511_reg_write(dev, 0x1b, 0x3d); - ov511_reg_write(dev, 0x1c, 0x00); - ov511_reg_write(dev, 0x1d, 0x00); - - if (mode == VIDEO_PALETTE_GREY) { - ov511_i2c_write(dev, 0x11, 4); /* check */ - } else { - ov511_i2c_write(dev, 0x11, 6); /* check */ - } - } - - ov511_reg_write(dev, 0x14, 0x00); /* Pixel divisor */ - ov511_reg_write(dev, 0x15, 0x00); /* Line divisor */ - - /* FIXME?? Shouldn't below be true only for YUV420? */ - ov511_reg_write(dev, 0x18, 0x03); /* YUV420/422, YFIR */ + if (sub_flag) { + ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2)); + ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2)); + ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1)); + ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1)); + } else { + ov511_i2c_write(dev, 0x17, hwsbase); + ov511_i2c_write(dev, 0x18, hwebase + (640>>2)); + ov511_i2c_write(dev, 0x19, 0x5); + ov511_i2c_write(dev, 0x1a, 5 + (480>>1)); + } - ov511_i2c_write(dev, 0x12, 0x24); /* Common A */ - ov511_i2c_write(dev, 0x14, 0x04); /* Common C */ + for (i = 0; mlist[i].width; i++) { + int lncnt, pxcnt, clock; - /* 7620 doesn't have register 0x35, so play it safe */ - if (ov511->sensor != SEN_OV7620) - ov511_i2c_write(dev, 0x35, 0x9e); -#endif + if (width != mlist[i].width || height != mlist[i].height) + continue; - for (i = 0; mlist[i].width; i++) { - if (width != mlist[i].width || - height != mlist[i].height || - mode != mlist[i].mode) + if (!mlist[i].color && mode != VIDEO_PALETTE_GREY) continue; - ov511_reg_write(dev, 0x12, mlist[i].pxcnt); - ov511_reg_write(dev, 0x13, mlist[i].lncnt); + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt; + lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt; + + ov511_reg_write(dev, 0x12, pxcnt); + ov511_reg_write(dev, 0x13, lncnt); ov511_reg_write(dev, 0x14, mlist[i].pxdv); ov511_reg_write(dev, 0x15, mlist[i].lndv); ov511_reg_write(dev, 0x18, mlist[i].m420); /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, mlist[i].s_pxcnt); - ov511_reg_write(dev, 0x1b, mlist[i].s_lncnt); - ov511_reg_write(dev, 0x1c, mlist[i].s_pxdv); - ov511_reg_write(dev, 0x1d, mlist[i].s_lndv); - - ov511_i2c_write(dev, 0x11, mlist[i].clock); /* check */ - + ov511_reg_write(dev, 0x1a, pxcnt); + ov511_reg_write(dev, 0x1b, lncnt); + ov511_reg_write(dev, 0x1c, mlist[i].pxdv); + ov511_reg_write(dev, 0x1d, mlist[i].lndv); + + /* Calculate and set the clock divisor */ + clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) + * (mlist[i].color ? 3 : 2) / 2) / 66000; + ov511_i2c_write(dev, 0x11, clock); + +#ifdef OV511_GBR422 + ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); +#else ov511_i2c_write(dev, 0x12, mlist[i].common_A); +#endif ov511_i2c_write(dev, 0x14, mlist[i].common_C); /* 7620 doesn't have register 0x35, so play it safe */ @@ -1020,20 +973,20 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, break; } + if (ov511_restart(ov511->dev) < 0) + return -EIO; + if (mlist[i].width == 0) { err("Unknown mode (%d, %d): %d", width, height, mode); - rc = -EINVAL; + return -EINVAL; } - if (ov511_restart(ov511->dev) < 0) - return -EIO; - #ifdef OV511_DEBUG if (debug >= 5) ov511_dump_i2c_regs(dev); #endif - return rc; + return 0; } /********************************************************************** @@ -1115,7 +1068,7 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 * 8 9 ... 15 72 73 ... 79 200 201 ... 207 * ... ... ... - * 56 57 ... 63 120 121 127 248 249 ... 255 + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 * * Note that the U and V data in one segment represents a 16 x 16 pixel * area, but the Y data represents a 32 x 8 pixel area. @@ -1132,6 +1085,57 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #undef OV511_DUMPPIX +#ifdef OV511_GBR422 +static void +ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + pIn = pIn0; + pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0); + + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) = + *(pOut1 + iWidth*3 + 3) = *pIn++; + pOut1 += 6; + } + pOut += iWidth*3*2; + } + + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2); + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) = + *(pOut1 + iWidth*3 + 3) = *pIn++; + pOut1 += 6; + } + pOut += iWidth*3*2; + } + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY + 1; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = *pIn++; + pOut1 += 3; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } +} + +#else + static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) @@ -1225,6 +1229,7 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, } #endif } +#endif /* * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. @@ -1252,7 +1257,7 @@ ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, for (m = 0; m < 8; m++) { *pOut1++ = *pIn++; } - pOut1 += iWidth - WDIV; + pOut1 += iWidth - 8; } pOut += 8; } @@ -1350,12 +1355,10 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) /* Frame end */ if (cdata[8] & 0x80) { -#if 0 struct timeval *ts; ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); - do_gettimeofday (ts); -#endif + do_gettimeofday (ts); PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d", ov511->curframe, (int)(cdata[ov511->packet_size - 1]), @@ -1456,9 +1459,9 @@ check_middle: } /* - * iY counts segment lines - * jY counts segment columns - * iOutY is the offset (in bytes) of the segment upper left corner + * i counts segment lines + * j counts segment columns + * iOut is the offset (in bytes) of the upper left corner */ iY = iSegY / (frame->width / WDIV); jY = iSegY - iY * (frame->width / WDIV); @@ -1467,11 +1470,15 @@ check_middle: jUV = iSegUV - iUV * (frame->width / WDIV * 2); iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3); - if (frame->format == VIDEO_PALETTE_GREY) + switch (frame->format) { + case VIDEO_PALETTE_GREY: ov511_parse_data_grey (pData, pOut, iOutY, frame->width); - else if (frame->format == VIDEO_PALETTE_RGB24) + break; + case VIDEO_PALETTE_RGB24: ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, iY & 1, frame->width); + break; + } pData = &cdata[iPix]; } @@ -1480,8 +1487,7 @@ check_middle: if (frame->segment < frame->width * frame->height / 256) { ov511->scratchlen = (ov511->packet_size - 1) - iPix; if (ov511->scratchlen < frame->segsize) - memmove(ov511->scratch, pData, - ov511->scratchlen); + memmove(ov511->scratch, pData, ov511->scratchlen); else ov511->scratchlen = 0; } @@ -1887,18 +1893,11 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) return -EINVAL; if (vc.decimation) return -EINVAL; -#if 0 - vc.x /= 4; - vc.x *= 4; - vc.y /= 2; - vc.y *= 2; - vc.width /= 32; - vc.width *= 32; -#else + vc.x &= ~3L; vc.y &= ~1L; vc.y &= ~31L; -#endif + if (vc.width == 0) vc.width = 32; @@ -2290,6 +2289,17 @@ static int ov76xx_configure(struct usb_ov511 *ov511) int i, success; int rc; + /* Lawrence Glaister <lg@jfm.bc.ca> reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ static struct ov511_regvals aRegvalsNorm7610[] = { { OV511_I2C_BUS, 0x10, 0xff }, { OV511_I2C_BUS, 0x16, 0x06 }, @@ -2299,16 +2309,16 @@ static int ov76xx_configure(struct usb_ov511 *ov511) { OV511_I2C_BUS, 0x06, 0x00 }, { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x38, 0x81 }, - { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ { OV511_I2C_BUS, 0x05, 0x00 }, - { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x20, 0x1c }, { OV511_I2C_BUS, 0x23, 0x2a }, { OV511_I2C_BUS, 0x24, 0x10 }, { OV511_I2C_BUS, 0x25, 0x8a }, { OV511_I2C_BUS, 0x27, 0xc2 }, - { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ + { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ { OV511_I2C_BUS, 0x2a, 0x04 }, { OV511_I2C_BUS, 0x2c, 0xfe }, { OV511_I2C_BUS, 0x30, 0x71 }, @@ -2331,7 +2341,7 @@ static int ov76xx_configure(struct usb_ov511 *ov511) { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x28, 0x24 }, { OV511_I2C_BUS, 0x05, 0x00 }, - { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x23, 0x00 }, { OV511_I2C_BUS, 0x24, 0x10 }, diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h index d3a9a78ba..92827afb3 100644 --- a/drivers/usb/ov511.h +++ b/drivers/usb/ov511.h @@ -339,16 +339,11 @@ struct palette_list { struct mode_list { int width; int height; - int mode; - u8 pxcnt; - u8 lncnt; - u8 pxdv; - u8 lndv; - u8 s_pxcnt; - u8 s_lncnt; - u8 s_pxdv; - u8 s_lndv; - u8 clock; + int color; /* 0=grayscale, 1=color */ + u8 pxcnt; /* pixel counter */ + u8 lncnt; /* line counter */ + u8 pxdv; /* pixel divisor */ + u8 lndv; /* line divisor */ u8 m420; u8 common_A; u8 common_C; diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index b40a947ed..80b82bba2 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -16,7 +16,7 @@ #include <linux/usb.h> -static const char *version = __FILE__ ": v0.3.12 2000/05/22 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; #define PEGASUS_MTU 1500 @@ -64,8 +64,11 @@ static struct usb_eth_dev usb_dev_id[] = { {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, + {"D-Link DU-10", 0x07b8, 0xabc1, NULL}, + {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, {"Linksys USB100TX", 0x066b, 0x2203, NULL}, {"Linksys USB100TX", 0x066b, 0x2204, NULL}, + {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL}, {"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL}, {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL}, {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL}, @@ -80,8 +83,8 @@ static struct usb_eth_dev usb_dev_id[] = { #define pegasus_set_registers(dev, indx, size, data)\ usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); #define pegasus_set_register(dev, indx, value) \ - { __u8 data = value; \ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);} + { __u8 __data = value; \ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);} static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) @@ -223,7 +226,7 @@ static void pegasus_read_bulk(struct urb *urb) __u16 pkt_len; if (urb->status) { - info("%s: RX status %d", net->name, urb->status); + dbg("%s: RX status %d", net->name, urb->status); goto goon; } @@ -369,9 +372,12 @@ static int pegasus_close(struct net_device *net) netif_stop_queue(net); - usb_unlink_urb(&pegasus->rx_urb); - usb_unlink_urb(&pegasus->tx_urb); - usb_unlink_urb(&pegasus->intr_urb); + if ( pegasus->rx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->rx_urb); + if ( pegasus->tx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->tx_urb); + if ( pegasus->intr_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->intr_urb); MOD_DEC_USE_COUNT; @@ -510,9 +516,12 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) unregister_netdev(pegasus->net); - usb_unlink_urb(&pegasus->rx_urb); - usb_unlink_urb(&pegasus->tx_urb); - usb_unlink_urb(&pegasus->intr_urb); + if ( pegasus->rx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->rx_urb); + if ( pegasus->tx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->tx_urb); + if ( pegasus->intr_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->intr_urb); kfree(pegasus); } diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index c013a8154..9aafce2c1 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -1,9 +1,10 @@ /* - * printer.c Version 0.4 + * printer.c Version 0.5 * - * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> - * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> - * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> + * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> + * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2000 Randy Dunlap <randy.dunlap@intel.com> * * USB Printer Device Class driver for USB printers and printer cables * @@ -14,6 +15,7 @@ * v0.2 - some more cleanups * v0.3 - cleaner again, waitqueue fixes * v0.4 - fixes in unidirectional mode + * v0.5 - add DEVICE_ID string support */ /* @@ -44,6 +46,18 @@ #include <linux/usb.h> #define USBLP_BUF_SIZE 8192 +#define DEVICE_ID_SIZE 1024 + +#define IOCNR_GET_DEVICE_ID 1 +#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* get device_id string */ + +/* + * A DEVICE_ID string may include the printer's serial number. + * It should end with a semi-colon (';'). + * An example from an HP 970C DeskJet printer is (this is one long string, + * with the serial number changed): +MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ: ; + */ /* * USB Printer Requests @@ -67,6 +81,8 @@ struct usblp { int minor; /* minor number of device */ unsigned char used; /* True if open */ unsigned char bidir; /* interface is bidirectional */ + unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ + /* first 2 bytes are (big-endian) length */ }; static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ }; @@ -75,21 +91,22 @@ static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ }; * Functions for usblp control messages. */ -static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, void *buf, int len) +static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, int value, void *buf, int len) { int retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), - request, USB_TYPE_CLASS | dir | recip, 0, usblp->ifnum, buf, len, HZ * 5); - dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d len: %#x result: %d", request, !!dir, recip, len, retval); + request, USB_TYPE_CLASS | dir | recip, value, usblp->ifnum, buf, len, HZ * 5); + dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d", + request, !!dir, recip, value, len, retval); return retval < 0 ? retval : 0; } #define usblp_read_status(usblp, status)\ - usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, status, 1) -#define usblp_get_id(usblp, id, maxlen)\ - usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, id, maxlen) + usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1) +#define usblp_get_id(usblp, config, id, maxlen)\ + usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen) #define usblp_reset(usblp)\ - usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, NULL, 0) + usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0) /* * URB callback. @@ -122,7 +139,6 @@ static int usblp_check_status(struct usblp *usblp) } if (status & LP_PERRORP) { - if (status & LP_POUTPA) { info("usblp%d: out of paper", usblp->minor); return -ENOSPC; @@ -161,10 +177,7 @@ static int usblp_open(struct inode *inode, struct file *file) if (usblp->used) return -EBUSY; - MOD_INC_USE_COUNT; - if ((retval = usblp_check_status(usblp))) { - MOD_DEC_USE_COUNT; return retval; } @@ -192,13 +205,12 @@ static int usblp_release(struct inode *inode, struct file *file) if (usblp->bidir) usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); - MOD_DEC_USE_COUNT; return 0; } usblp_table[usblp->minor] = NULL; + kfree(usblp->device_id_string); kfree(usblp); - MOD_DEC_USE_COUNT; return 0; } @@ -212,6 +224,34 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); } +static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int length; + struct usblp *usblp = file->private_data; + + if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ)) + return -EINVAL; + + switch (_IOC_NR(cmd)) { + case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ +#if 0 + dbg ("usblp_ioctl GET_DEVICE_ID: actlen=%d, user size=%d, string='%s'", + length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); +#endif + if (length > _IOC_SIZE(cmd)) + length = _IOC_SIZE(cmd); /* truncate */ + if (copy_to_user ((unsigned char *)arg, usblp->device_id_string, (unsigned long) length)) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct usblp *usblp = file->private_data; @@ -318,6 +358,7 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) struct usb_endpoint_descriptor *epread, *epwrite; struct usblp *usblp; int minor, i, alts = -1, bidir = 0; + int length, err; char *buf; for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { @@ -386,6 +427,13 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) return NULL; } + if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) { + err("out of memory"); + kfree(usblp); + kfree(buf); + return NULL; + } + FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf, 0, usblp_bulk, usblp); @@ -393,6 +441,27 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); + /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */ + err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1); + if (err >= 0) { + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ + if (length < DEVICE_ID_SIZE) + usblp->device_id_string[length] = '\0'; + else + usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0'; + dbg ("usblp%d Device ID string [%d]=%s", + minor, length, &usblp->device_id_string[2]); + } + else { + err ("usblp%d: error = %d reading IEEE-1284 Device ID string", + minor, err); + usblp->device_id_string[0] = usblp->device_id_string[1] = '\0'; + } + +#ifdef DEBUG + usblp_check_status(usblp); +#endif + info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); @@ -418,16 +487,20 @@ static void usblp_disconnect(struct usb_device *dev, void *ptr) if (usblp->used) return; + kfree(usblp->device_id_string); + usblp_table[usblp->minor] = NULL; kfree(usblp); } static struct file_operations usblp_fops = { + owner: THIS_MODULE, read: usblp_read, write: usblp_write, + poll: usblp_poll, + ioctl: usblp_ioctl, open: usblp_open, release: usblp_release, - poll: usblp_poll, }; static struct usb_driver usblp_driver = { diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index 38021887e..d326e5892 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -1,7 +1,7 @@ /* -*- linux-c -*- */ /* - * Driver for USB Scanners (linux-2.3.99-pre6-3) + * Driver for USB Scanners (linux-2.4.0test1-ac7) * * Copyright (C) 1999, 2000 David E. Nelson * @@ -194,6 +194,12 @@ * - Added wait queues to read_scanner(). * * + * 0.4.3.1 + * + * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud + * Linders <rlinders@xs4all.nl>. + * + * * TODO * * - Performance diff --git a/drivers/usb/scanner.h b/drivers/usb/scanner.h index f8a4d2163..7662f125e 100644 --- a/drivers/usb/scanner.h +++ b/drivers/usb/scanner.h @@ -1,5 +1,5 @@ /* - * Driver for USB Scanners (linux-2.3.99-pre6-3) + * Driver for USB Scanners (linux-2.4.0test1-ac7) * * Copyright (C) 1999, 2000 David E. Nelson * @@ -118,7 +118,7 @@ static const struct scanner_device { { 0x03f0, 0x0205 }, /* 3300C */ { 0x03f0, 0x0101 }, /* 4100C */ { 0x03f0, 0x0105 }, /* 4200C */ - { 0x03f0, 0x0202 }, /* PhotoSmart S20 */ + { 0x03f0, 0x0102 }, /* PhotoSmart S20 */ { 0x03f0, 0x0401 }, /* 5200C */ { 0x03f0, 0x0201 }, /* 6200C */ { 0x03f0, 0x0601 }, /* 6300C */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index fe2d2013b..c3cce34c3 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,24 +14,75 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (6/4/2000) pberger and borchers +* -- Replaced separate calls to spin_unlock_irqrestore and +* interruptible_sleep_on_interruptible with a new function +* cond_wait_interruptible_timeout_irqrestore. This eliminates +* the race condition where the wake up could happen after +* the unlock and before the sleep. +* -- Close now waits for output to drain. +* -- Open waits until any close in progress is finished. +* -- All out of band responses are now processed, not just the +* first in a USB packet. +* -- Fixed a bug that prevented the driver from working when the +* first Digi port was not the first USB serial port--the driver +* was mistakenly using the external USB serial port number to +* try to index into its internal ports. +* -- Fixed an SMP bug -- write_bulk_callback is called directly from +* an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are +* needed for locks outside write_bulk_callback that are also +* acquired by write_bulk_callback to prevent deadlocks. +* -- Fixed support for select() by making digi_chars_in_buffer() +* return 256 when -EINPROGRESS is set, as the line discipline +* code in n_tty.c expects. +* -- Fixed an include file ordering problem that prevented debugging +* messages from working. +* -- Fixed an intermittent timeout problem that caused writes to +* sometimes get stuck on some machines on some kernels. It turns +* out in these circumstances write_chan() (in n_tty.c) was +* asleep waiting for our wakeup call. Even though we call +* wake_up_interruptible() in digi_write_bulk_callback(), there is +* a race condition that could cause the wakeup to fail: if our +* wake_up_interruptible() call occurs between the time that our +* driver write routine finishes and write_chan() sets current->state +* to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state +* to TASK_RUNNING will be lost and write_chan's subsequent call to +* schedule() will never return (unless it catches a signal). +* This race condition occurs because write_bulk_callback() (and thus +* the wakeup) are called asynchonously from an interrupt, rather than +* from the scheduler. We can avoid the race by calling the wakeup +* from the scheduler queue and that's our fix: Now, at the end of +* write_bulk_callback() we queue up a wakeup call on the scheduler +* task queue. We still also invoke the wakeup directly since that +* squeezes a bit more performance out of the driver, and any lost +* race conditions will get cleaned up at the next scheduler run. +* +* NOTE: The problem also goes away if you comment out +* the two code lines in write_chan() where current->state +* is set to TASK_RUNNING just before calling driver.write() and to +* TASK_INTERRUPTIBLE immediately afterwards. This is why the +* problem did not show up with the 2.2 kernels -- they do not +* include that code. +* * (5/16/2000) pberger and borchers -* -- added timeouts to sleeps -* -- handle transition to/from B0 in digi_set_termios +* -- Added timeouts to sleeps, to defend against lost wake ups. +* -- Handle transition to/from B0 baud rate in digi_set_termios. * * (5/13/2000) pberger and borchers -* -- all commands now sent on out of band port, using digi_write_oob -* -- get modem control signals whenever they change, support TIOCMGET/ -* SET/BIS/BIC ioctls +* -- All commands now sent on out of band port, using +* digi_write_oob_command. +* -- Get modem control signals whenever they change, support TIOCMGET/ +* SET/BIS/BIC ioctls. * -- digi_set_termios now supports parity, word size, stop bits, and -* receive enable -* -- cleaned up open and close, use digi_set_termios and digi_write_oob -* to set port parameters -* -- added digi_startup_device to start read chains on all ports -* -- write buffer is only used when count==1, to be sure put_char can -* write a char (unless the buffer is full) +* receive enable. +* -- Cleaned up open and close, use digi_set_termios and +* digi_write_oob_command to set port parameters. +* -- Added digi_startup_device to start read chains on all ports. +* -- Write buffer is only used when count==1, to be sure put_char can +* write a char (unless the buffer is full). * * (5/10/2000) pberger and borchers -* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls +* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close. * -- Fixed problem where the first incoming character is lost on * port opens after the first close on that port. Now we keep * the read_urb chain open until shutdown. @@ -43,7 +94,33 @@ * (5/3/2000) pberger and borchers * -- First alpha version of the driver--many known limitations and bugs. * -* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $ +* +* Locking and SMP +* +* - Each port, including the out-of-band port, has a lock used to +* serialize all access to the port's private structure. +* - The port lock is also used to serialize all writes and access to +* the port's URB. +* - The port lock is also used for the port write_wait condition +* variable. Holding the port lock will prevent a wake up on the +* port's write_wait; this can be used with cond_wait_... to be sure +* the wake up is not lost in a race when dropping the lock and +* sleeping waiting for the wakeup. +* - digi_write() does not sleep, since it is sometimes called on +* interrupt time. +* - digi_write_bulk_callback() and digi_read_bulk_callback() are +* called directly from interrupts. Hence spin_lock_irqsave() +* and spin_lock_irqrestore() are used in the rest of the code +* for any locks they acquire. +* - digi_write_bulk_callback() gets the port lock before waking up +* processes sleeping on the port write_wait. It also schedules +* wake ups so they happen from the scheduler, because the tty +* system can miss wake ups from interrupts. +* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to +* recheck the condition they are sleeping on. This is defensive, +* in case a wake up is lost. +* +* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $ */ #include <linux/config.h> @@ -60,8 +137,7 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/spinlock.h> -#include <linux/usb.h> -#include "usb-serial.h" +#include <linux/tqueue.h> #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG @@ -69,16 +145,25 @@ #undef DEBUG #endif +#include <linux/usb.h> +#include "usb-serial.h" + /* Defines */ /* port buffer length -- must be <= transfer buffer length - 2 */ /* so we can be sure to send the full buffer in one urb */ -#define DIGI_PORT_BUF_LEN 16 +#define DIGI_PORT_BUF_LEN 8 -/* retry timeout while waiting for urb->status to go to 0 */ +/* retry timeout while sleeping */ #define DIGI_RETRY_TIMEOUT (HZ/10) +/* timeout while waiting for tty output to drain in close */ +/* this delay is used twice in close, so the total delay could */ +/* be twice this value */ +#define DIGI_CLOSE_TIMEOUT (5*HZ) + + /* AccelePort USB Defines */ /* ids */ @@ -173,6 +258,9 @@ #define DIGI_FLUSH_RX 2 #define DIGI_RESUME_TX 4 /* clears xoff condition */ +#define DIGI_TRANSMIT_NOT_IDLE 0 +#define DIGI_TRANSMIT_IDLE 1 + #define DIGI_DISABLE 0 #define DIGI_ENABLE 1 @@ -211,18 +299,29 @@ /* Structures */ typedef struct digi_private { + int dp_port_num; spinlock_t dp_port_lock; int dp_buf_len; unsigned char dp_buf[DIGI_PORT_BUF_LEN]; unsigned int dp_modem_signals; + int dp_transmit_idle; + int dp_in_close; + wait_queue_head_t dp_close_wait; /* wait queue for close */ + struct tq_struct dp_tasks; } digi_private_t; /* Local Function Declarations */ -static int digi_write_oob( unsigned char *buf, int count ); +static void digi_wakeup_write( struct usb_serial_port *port ); +static void digi_wakeup_write_lock( struct usb_serial_port *port ); +static int digi_write_oob_command( unsigned char *buf, int count ); +static int digi_write_inb_command( struct usb_serial_port *port, + unsigned char *buf, int count ) __attribute__((unused)); static int digi_set_modem_signals( struct usb_serial_port *port, unsigned int modem_signals ); +static int digi_transmit_idle( struct usb_serial_port *port, + unsigned long timeout ); static void digi_rx_throttle (struct usb_serial_port *port); static void digi_rx_unthrottle (struct usb_serial_port *port); static void digi_set_termios( struct usb_serial_port *port, @@ -241,22 +340,24 @@ static int digi_startup_device( struct usb_serial *serial ); static int digi_startup( struct usb_serial *serial ); static void digi_shutdown( struct usb_serial *serial ); static void digi_read_bulk_callback( struct urb *urb ); -static void digi_read_oob( struct urb *urb ); +static void digi_read_oob_callback( struct urb *urb ); /* Statics */ /* device info needed for the Digi serial converter */ -static __u16 digi_vendor_id = DIGI_VENDOR_ID; -static __u16 digi_product_id = DIGI_ID; +static u16 digi_vendor_id = DIGI_VENDOR_ID; +static u16 digi_product_id = DIGI_ID; /* out of band port */ static int oob_port_num; /* index of out-of-band port */ static struct usb_serial_port *oob_port; /* out-of-band port */ static int device_startup = 0; -/* startup lock -- used to by digi_startup_device */ -spinlock_t startup_lock; +spinlock_t startup_lock; /* used by startup_device */ + +static wait_queue_head_t modem_change_wait; +static wait_queue_head_t transmit_idle_wait; /* Globals */ @@ -292,7 +393,84 @@ struct usb_serial_device_type digi_acceleport_device = { /* Functions */ /* -* Digi Write OOB +* Cond Wait Interruptible Timeout Irqrestore +* +* Do spin_unlock_irqrestore and interruptible_sleep_on_timeout +* so that wake ups are not lost if they occur between the unlock +* and the sleep. In other words, spin_lock_irqrestore and +* interruptible_sleep_on_timeout are "atomic" with respect to +* wake ups. This is used to implement condition variables. +*/ + +static long cond_wait_interruptible_timeout_irqrestore( + wait_queue_head_t *q, long timeout, + spinlock_t *lock, unsigned long flags ) +{ + + wait_queue_t wait; + + + init_waitqueue_entry( &wait, current ); + + set_current_state( TASK_INTERRUPTIBLE ); + + add_wait_queue( q, &wait ); + + spin_unlock_irqrestore( lock, flags ); + + timeout = schedule_timeout(timeout); + + set_current_state( TASK_RUNNING ); + + remove_wait_queue( q, &wait ); + + return( timeout ); + +} + + +/* +* Digi Wakeup Write +* +* Wake up port, line discipline, and tty processes sleeping +* on writes. +*/ + +static void digi_wakeup_write_lock( struct usb_serial_port *port ) +{ + + unsigned long flags; + digi_private_t *priv = (digi_private_t *)(port->private); + + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + digi_wakeup_write( port ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + +} + +static void digi_wakeup_write( struct usb_serial_port *port ) +{ + + struct tty_struct *tty = port->tty; + + + /* wake up port processes */ + wake_up_interruptible( &port->write_wait ); + + /* wake up line discipline */ + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) + (tty->ldisc.write_wakeup)(tty); + + /* wake up other tty processes */ + wake_up_interruptible( &tty->write_wait ); + +} + + +/* +* Digi Write OOB Command * * Write commands on the out of band port. Commands are 4 * bytes each, multiple commands can be sent at once, and @@ -301,28 +479,29 @@ struct usb_serial_device_type digi_acceleport_device = { * a negative error returned by usb_submit_urb. */ -static int digi_write_oob( unsigned char *buf, int count ) +static int digi_write_oob_command( unsigned char *buf, int count ) { int ret = 0; int len; digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); + unsigned long flags = 0; -dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count ); +dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); - spin_lock( &oob_priv->dp_port_lock ); + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); while( count > 0 ) { while( oob_port->write_urb->status == -EINPROGRESS ) { - spin_unlock( &oob_priv->dp_port_lock ); - interruptible_sleep_on_timeout( &oob_port->write_wait, - DIGI_RETRY_TIMEOUT ); + cond_wait_interruptible_timeout_irqrestore( + &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->dp_port_lock, flags ); if( signal_pending(current) ) { return( -EINTR ); } - spin_lock( &oob_priv->dp_port_lock ); + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); } /* len must be a multiple of 4, so commands are not split */ @@ -337,14 +516,106 @@ dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count ); count -= len; buf += len; } else { - dbg( "digi_write_oob: usb_submit_urb failed, ret=%d", - ret ); + dbg( + "digi_write_oob_command: usb_submit_urb failed, ret=%d", + ret ); break; } } - spin_unlock( &oob_priv->dp_port_lock ); + spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); + + return( ret ); + +} + + +/* +* Digi Write In Band Command +* +* Write commands on the given port. Commands are 4 +* bytes each, multiple commands can be sent at once, and +* no command will be split across USB packets. Returns 0 +* if successful, or a negative error returned by digi_write. +*/ + +static int digi_write_inb_command( struct usb_serial_port *port, + unsigned char *buf, int count ) +{ + + int ret = 0; + int len; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long flags = 0; + + +dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num, +count ); + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + while( count > 0 ) { + + while( port->write_urb->status == -EINPROGRESS ) { + cond_wait_interruptible_timeout_irqrestore( + &port->write_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + /* len must be a multiple of 4 and small enough to */ + /* guarantee the write will send all data (or none), */ + /* so commands are not split */ + len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); + if( len > 4 ) + len &= ~3; + + /* write any buffered data first */ + if( priv->dp_buf_len > 0 ) { + data[0] = DIGI_CMD_SEND_DATA; + data[1] = priv->dp_buf_len; + memcpy( data+2, priv->dp_buf, priv->dp_buf_len ); + memcpy( data+2+priv->dp_buf_len, buf, len ); + port->write_urb->transfer_buffer_length + = priv->dp_buf_len+2+len; + } else { + memcpy( data, buf, len ); + port->write_urb->transfer_buffer_length = len; + } + +#ifdef DEBUG_DATA + { + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + priv->dp_port_num, port->write_urb->transfer_buffer_length ); + for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); + } +#endif + + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_buf_len = 0; + count -= len; + buf += len; + } else { + dbg( + "digi_write_inb_command: usb_submit_urb failed, ret=%d", + ret ); + break; + } + + } + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( ret ); @@ -369,35 +640,35 @@ static int digi_set_modem_signals( struct usb_serial_port *port, unsigned char *data = oob_port->write_urb->transfer_buffer; digi_private_t *port_priv = (digi_private_t *)(port->private); digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); + unsigned long flags = 0; dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", -port->number, modem_signals ); +port_priv->dp_port_num, modem_signals ); - spin_lock( &oob_priv->dp_port_lock ); - spin_lock( &port_priv->dp_port_lock ); + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + spin_lock_irqsave( &port_priv->dp_port_lock, flags ); while( oob_port->write_urb->status == -EINPROGRESS ) { - spin_unlock( &port_priv->dp_port_lock ); - spin_unlock( &oob_priv->dp_port_lock ); - interruptible_sleep_on_timeout( &oob_port->write_wait, - DIGI_RETRY_TIMEOUT ); + spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + cond_wait_interruptible_timeout_irqrestore( + &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->dp_port_lock, flags ); if( signal_pending(current) ) { return( -EINTR ); } - spin_lock( &oob_priv->dp_port_lock ); - spin_lock( &port_priv->dp_port_lock ); + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + spin_lock_irqsave( &port_priv->dp_port_lock, flags ); } - /* command is 4 bytes: command, line, argument, pad */ data[0] = DIGI_CMD_SET_DTR_SIGNAL; - data[1] = port->number; + data[1] = port_priv->dp_port_num; data[2] = (modem_signals&TIOCM_DTR) ? DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; data[3] = 0; data[4] = DIGI_CMD_SET_RTS_SIGNAL; - data[5] = port->number; + data[5] = port_priv->dp_port_num; data[6] = (modem_signals&TIOCM_RTS) ? DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; data[7] = 0; @@ -413,18 +684,77 @@ port->number, modem_signals ); ret ); } - spin_unlock( &port_priv->dp_port_lock ); - spin_unlock( &oob_priv->dp_port_lock ); + spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); return( ret ); } +/* +* Digi Transmit Idle +* +* Digi transmit idle waits, up to timeout ticks, for the transmitter +* to go idle. It returns 0 if successful or a negative error. +* +* There are race conditions here if more than one process is calling +* digi_transmit_idle on the same port at the same time. However, this +* is only called from close, and only one process can be in close on a +* port at a time, so its ok. +*/ + +static int digi_transmit_idle( struct usb_serial_port *port, + unsigned long timeout ) +{ + + int ret; + unsigned char buf[2]; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; + + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + buf[0] = DIGI_CMD_TRANSMIT_IDLE; + buf[1] = 0; + + if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 ) + return( ret ); + + timeout += jiffies; + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + while( jiffies < timeout && !priv->dp_transmit_idle ) { + cond_wait_interruptible_timeout_irqrestore( + &transmit_idle_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + return( 0 ); + +} + + static void digi_rx_throttle( struct usb_serial_port *port ) { -dbg( "digi_rx_throttle: TOP: port=%d", port->number ); +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); /* stop receiving characters. We just turn off the URB request, and let chars pile up in the device. If we're doing hardware @@ -441,7 +771,12 @@ dbg( "digi_rx_throttle: TOP: port=%d", port->number ); static void digi_rx_unthrottle( struct usb_serial_port *port ) { -dbg( "digi_rx_unthrottle: TOP: port=%d", port->number ); +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); /* just restart the receive interrupt URB */ //if (usb_submit_urb(port->interrupt_in_urb)) @@ -454,6 +789,7 @@ static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ) { + digi_private_t *priv = (digi_private_t *)(port->private); unsigned int iflag = port->tty->termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; @@ -463,7 +799,7 @@ static void digi_set_termios( struct usb_serial_port *port, int i = 0; -dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", port->number, iflag, old_iflag, cflag, old_cflag ); +dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag ); /* set baud rate */ if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { @@ -506,7 +842,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol if( arg != -1 ) { buf[i++] = DIGI_CMD_SET_BAUD_RATE; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; } @@ -526,7 +862,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol } buf[i++] = DIGI_CMD_SET_PARITY; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; @@ -550,7 +886,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol if( arg != -1 ) { buf[i++] = DIGI_CMD_SET_WORD_SIZE; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; } @@ -566,7 +902,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol arg = DIGI_STOP_BITS_1; buf[i++] = DIGI_CMD_SET_STOP_BITS; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; @@ -589,7 +925,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; @@ -612,7 +948,7 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; @@ -627,13 +963,13 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol arg = DIGI_DISABLE; buf[i++] = DIGI_CMD_RECEIVE_ENABLE; - buf[i++] = port->number; + buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; } - if( (ret=digi_write_oob( buf, i )) != 0 ) + if( (ret=digi_write_oob_command( buf, i )) != 0 ) dbg( "digi_set_termios: write oob failed, ret=%d", ret ); } @@ -641,7 +977,14 @@ dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, ol static void digi_break_ctl( struct usb_serial_port *port, int break_state ) { -dbg( "digi_break_ctl: TOP: port=%d", port->number ); + +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num ); + } @@ -651,16 +994,17 @@ static int digi_ioctl( struct usb_serial_port *port, struct file *file, digi_private_t *priv = (digi_private_t *)(port->private); unsigned int val; + unsigned long flags = 0; -dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd ); +dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd ); switch (cmd) { case TIOCMGET: - spin_lock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); val = priv->dp_modem_signals; - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) ) return( -EFAULT ); return( 0 ); @@ -670,12 +1014,12 @@ dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd ); case TIOCMBIC: if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) ) return( -EFAULT ); - spin_lock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); if( cmd == TIOCMBIS ) val = priv->dp_modem_signals | val; else if( cmd == TIOCMBIC ) val = priv->dp_modem_signals & ~val; - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( digi_set_modem_signals( port, val ) ); case TIOCMIWAIT: @@ -701,15 +1045,17 @@ static int digi_write( struct usb_serial_port *port, int from_user, int ret,data_len,new_len; digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long flags = 0; dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d", -port->number, count, from_user, in_interrupt() ); +priv->dp_port_num, count, from_user, in_interrupt() ); /* be sure only one write proceeds at a time */ /* there are races on the port private buffer */ /* and races to check write_urb->status */ - spin_lock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); /* wait for urb status clear to submit another urb */ if( port->write_urb->status == -EINPROGRESS ) { @@ -724,7 +1070,7 @@ port->number, count, from_user, in_interrupt() ); new_len = 0; } - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( new_len ); @@ -736,43 +1082,41 @@ port->number, count, from_user, in_interrupt() ); data_len = new_len + priv->dp_buf_len; if( data_len == 0 ) { - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( 0 ); } - *((unsigned char *)(port->write_urb->transfer_buffer)) - = (unsigned char)DIGI_CMD_SEND_DATA; - *((unsigned char *)(port->write_urb->transfer_buffer)+1) - = (unsigned char)data_len; - port->write_urb->transfer_buffer_length = data_len+2; + *data++ = DIGI_CMD_SEND_DATA; + *data++ = data_len; + /* copy in buffered data first */ - memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, - priv->dp_buf_len ); + memcpy( data, priv->dp_buf, priv->dp_buf_len ); + data += priv->dp_buf_len; /* copy in new data */ if( from_user ) { - copy_from_user( - port->write_urb->transfer_buffer+2+priv->dp_buf_len, - buf, new_len ); + if( copy_from_user( data, buf, new_len ) ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( -EFAULT ); + } } else { - memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len, - buf, new_len ); + memcpy( data, buf, new_len ); } #ifdef DEBUG_DATA -{ + { int i; printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", - port->number, port->write_urb->transfer_buffer_length ); + priv->dp_port_num, port->write_urb->transfer_buffer_length ); for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { printk( "%.2x ", ((unsigned char *)port->write_urb->transfer_buffer)[i] ); } printk( "\n" ); -} + } #endif if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { @@ -781,13 +1125,11 @@ port->number, count, from_user, in_interrupt() ); } else { dbg( "digi_write: usb_submit_urb failed, ret=%d", ret ); - /* no bytes written - should we return the error code or 0? */ - ret = 0; } /* return length of new data written, or error */ dbg( "digi_write: returning %d", ret ); - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); return( ret ); } @@ -798,17 +1140,18 @@ static void digi_write_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; - struct tty_struct *tty = port->tty; digi_private_t *priv = (digi_private_t *)(port->private); int ret; -dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); +dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num ); /* handle callback on out-of-band port */ - if( port->number == oob_port_num ) { + if( priv->dp_port_num == oob_port_num ) { dbg( "digi_write_bulk_callback: oob callback" ); + spin_lock( &priv->dp_port_lock ); wake_up_interruptible( &port->write_wait ); + spin_unlock( &priv->dp_port_lock ); return; } @@ -833,17 +1176,17 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); priv->dp_buf_len ); #ifdef DEBUG_DATA -{ + { int i; printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=", - port->number, port->write_urb->transfer_buffer_length ); + priv->dp_port_num, port->write_urb->transfer_buffer_length ); for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { printk( "%.2x ", ((unsigned char *)port->write_urb->transfer_buffer)[i] ); } printk( "\n" ); -} + } #endif if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { @@ -853,19 +1196,17 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); } } - spin_unlock( &priv->dp_port_lock ); - /* wake up port processes */ - wake_up_interruptible( &port->write_wait ); + /* wake up processes sleeping on writes immediately */ + digi_wakeup_write( port ); - /* wake up line discipline */ - tty = port->tty; - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup ) - (tty->ldisc.write_wakeup)(tty); + spin_unlock( &priv->dp_port_lock ); - /* wake up other tty processes */ - wake_up_interruptible( &tty->write_wait ); + /* also queue up a wakeup at scheduler time, in case we */ + /* lost the race in write_chan(). */ + priv->dp_tasks.routine = (void *)digi_wakeup_write_lock; + priv->dp_tasks.data = (void *)port; + queue_task( &(priv->dp_tasks), &tq_scheduler ); } @@ -875,20 +1216,21 @@ static int digi_write_room( struct usb_serial_port *port ) int room; digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; -dbg( "digi_write_room: TOP: port=%d", port->number ); +dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num ); - spin_lock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); if( port->write_urb->status == -EINPROGRESS ) room = 0; else room = port->bulk_out_size - 2 - priv->dp_buf_len; - spin_unlock( &priv->dp_port_lock ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); -dbg( "digi_write_room: port=%d, room=%d", port->number, room ); +dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room ); return( room ); } @@ -900,13 +1242,14 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) digi_private_t *priv = (digi_private_t *)(port->private); -dbg( "digi_chars_in_buffer: TOP: port=%d", port->number ); +dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num ); if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, port->bulk_out_size - 2 ); - return( port->bulk_out_size - 2 ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 ); + /* return( port->bulk_out_size - 2 ); */ + return( 256 ); } else { -dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len ); return( priv->dp_buf_len ); } @@ -916,43 +1259,66 @@ dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len ) static int digi_open( struct usb_serial_port *port, struct file *filp ) { - int i = 0; int ret; unsigned char buf[32]; digi_private_t *priv = (digi_private_t *)(port->private); struct termios not_termios; + unsigned long flags = 0; -dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active ); +dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active ); /* be sure the device is started up */ if( digi_startup_device( port->serial ) != 0 ) return( -ENXIO ); - MOD_INC_USE_COUNT; - /* if port is already open, just return */ /* be sure exactly one open proceeds */ - spin_lock( &priv->dp_port_lock ); - if( port->active++ ) { - spin_unlock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); + if( port->active >= 1 && !priv->dp_in_close ) { + ++port->active; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_INC_USE_COUNT; return( 0 ); } - spin_unlock( &priv->dp_port_lock ); + + /* don't wait on a close in progress for non-blocking opens */ + if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( -EAGAIN ); + } + + /* prevent other opens from proceeding, before giving up lock */ + ++port->active; + + /* wait for close to finish */ + while( priv->dp_in_close ) { + cond_wait_interruptible_timeout_irqrestore( + &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + --port->active; + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_INC_USE_COUNT; /* read modem signals automatically whenever they change */ - buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; - buf[i++] = port->number; - buf[i++] = DIGI_ENABLE; - buf[i++] = 0; + buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[1] = priv->dp_port_num; + buf[2] = DIGI_ENABLE; + buf[3] = 0; /* flush fifos */ - buf[i++] = DIGI_CMD_IFLUSH_FIFO; - buf[i++] = port->number; - buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; - buf[i++] = 0; + buf[4] = DIGI_CMD_IFLUSH_FIFO; + buf[5] = priv->dp_port_num; + buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[7] = 0; - if( (ret=digi_write_oob( buf, i )) != 0 ) + if( (ret=digi_write_oob_command( buf, 8 )) != 0 ) dbg( "digi_open: write oob failed, ret=%d", ret ); /* set termios settings */ @@ -971,58 +1337,83 @@ dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active ); static void digi_close( struct usb_serial_port *port, struct file *filp ) { - int i = 0; int ret; unsigned char buf[32]; + struct tty_struct *tty = port->tty; digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; -dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active ); +dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active ); /* do cleanup only after final close on this port */ - spin_lock( &priv->dp_port_lock ); - if( --port->active ) { - spin_unlock( &priv->dp_port_lock ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); + if( port->active > 1 ) { + --port->active; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); MOD_DEC_USE_COUNT; return; + } else if( port->active <= 0 ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return; } - spin_unlock( &priv->dp_port_lock ); - + priv->dp_in_close = 1; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + /* tell line discipline to process only XON/XOFF */ + tty->closing = 1; + + /* wait for output to drain */ + if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + tty_wait_until_sent( tty, DIGI_CLOSE_TIMEOUT ); + } + + /* flush driver and line discipline buffers */ + if( tty->driver.flush_buffer ) + tty->driver.flush_buffer( tty ); + if( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer( tty ); + + /* wait for transmit idle */ + if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + digi_transmit_idle( port, DIGI_CLOSE_TIMEOUT ); + } + /* drop DTR and RTS */ digi_set_modem_signals( port, 0 ); /* disable input flow control */ - buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; - buf[i++] = port->number; - buf[i++] = DIGI_DISABLE; - buf[i++] = 0; + buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[1] = priv->dp_port_num; + buf[2] = DIGI_DISABLE; + buf[3] = 0; /* disable output flow control */ - buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; - buf[i++] = port->number; - buf[i++] = DIGI_DISABLE; - buf[i++] = 0; + buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[5] = priv->dp_port_num; + buf[6] = DIGI_DISABLE; + buf[7] = 0; /* disable reading modem signals automatically */ - buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; - buf[i++] = port->number; - buf[i++] = DIGI_DISABLE; - buf[i++] = 0; + buf[8] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[9] = priv->dp_port_num; + buf[10] = DIGI_DISABLE; + buf[11] = 0; /* flush fifos */ - buf[i++] = DIGI_CMD_IFLUSH_FIFO; - buf[i++] = port->number; - buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; - buf[i++] = 0; + buf[12] = DIGI_CMD_IFLUSH_FIFO; + buf[13] = priv->dp_port_num; + buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[15] = 0; /* disable receive */ - buf[i++] = DIGI_CMD_RECEIVE_ENABLE; - buf[i++] = port->number; - buf[i++] = DIGI_DISABLE; - buf[i++] = 0; + buf[16] = DIGI_CMD_RECEIVE_ENABLE; + buf[17] = priv->dp_port_num; + buf[18] = DIGI_DISABLE; + buf[19] = 0; - if( (ret=digi_write_oob( buf, i )) != 0 ) + if( (ret=digi_write_oob_command( buf, 20 )) != 0 ) dbg( "digi_close: write oob failed, ret=%d", ret ); /* wait for final commands on oob port to complete */ @@ -1033,12 +1424,21 @@ dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active ); break; } } - + /* shutdown any outstanding bulk writes */ usb_unlink_urb (port->write_urb); + tty->closing = 0; + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + --port->active; + priv->dp_in_close = 0; + wake_up_interruptible( &priv->dp_close_wait ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_DEC_USE_COUNT; +dbg( "digi_close: done" ); } @@ -1093,6 +1493,8 @@ static int digi_startup( struct usb_serial *serial ) dbg( "digi_startup: TOP" ); spin_lock_init( &startup_lock ); + init_waitqueue_head( &modem_change_wait ); + init_waitqueue_head( &transmit_idle_wait ); /* allocate the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ @@ -1108,8 +1510,14 @@ dbg( "digi_startup: TOP" ); return( 1 ); /* error */ /* initialize private structure */ + priv->dp_port_num = i; priv->dp_buf_len = 0; priv->dp_modem_signals = 0; + priv->dp_transmit_idle = 0; + priv->dp_in_close = 0; + init_waitqueue_head( &priv->dp_close_wait ); + priv->dp_tasks.next = NULL; + priv->dp_tasks.data = NULL; spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ @@ -1157,6 +1565,7 @@ static void digi_read_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty = port->tty; + digi_private_t *priv = (digi_private_t *)(port->private); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; int status = ((unsigned char *)urb->transfer_buffer)[2]; @@ -1164,11 +1573,11 @@ static void digi_read_bulk_callback( struct urb *urb ) int ret,i; -dbg( "digi_read_bulk_callback: TOP: port=%d", port->number ); +dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num ); /* handle oob callback */ - if( port->number == oob_port_num ) { - digi_read_oob( urb ); + if( priv->dp_port_num == oob_port_num ) { + digi_read_oob_callback( urb ); return; } @@ -1181,13 +1590,15 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", port->number ); if( urb->status ) { dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", urb->status ); + if( urb->status == -ENOENT ) + return; goto resubmit; } #ifdef DEBUG_DATA if( urb->actual_length ) { printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=", - port->number, urb->actual_length ); + priv->dp_port_num, urb->actual_length ); for( i=0; i<urb->actual_length; ++i ) { printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); } @@ -1196,7 +1607,7 @@ if( urb->actual_length ) { #endif if( urb->actual_length != len + 2 ) - err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", port->number, opcode, len, urb->actual_length, status ); + err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status ); /* receive data */ if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) { @@ -1209,67 +1620,89 @@ if( urb->actual_length ) { /* continue read */ resubmit: - if( (ret=usb_submit_urb(urb)) != 0 ) + if( (ret=usb_submit_urb(urb)) != 0 ) { dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d", ret ); + } } -static void digi_read_oob( struct urb *urb ) +static void digi_read_oob_callback( struct urb *urb ) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; digi_private_t *priv; - int oob_opcode = ((unsigned char *)urb->transfer_buffer)[0]; - int oob_line = ((unsigned char *)urb->transfer_buffer)[1]; - int oob_status = ((unsigned char *)urb->transfer_buffer)[2]; - int oob_ret = ((unsigned char *)urb->transfer_buffer)[3]; - int ret; + int opcode, line, status, val; + int i,ret; -dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret ); +dbg( "digi_read_oob_callback: len=%d", urb->actual_length ); if( urb->status ) { - dbg( "digi_read_oob: nonzero read bulk status on oob: %d", + dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d", urb->status ); + if( urb->status == -ENOENT ) + return; goto resubmit; } - if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) { + for( i=0; i<urb->actual_length-3; ) { - priv = serial->port[oob_line].private; + opcode = ((unsigned char *)urb->transfer_buffer)[i++]; + line = ((unsigned char *)urb->transfer_buffer)[i++]; + status = ((unsigned char *)urb->transfer_buffer)[i++]; + val = ((unsigned char *)urb->transfer_buffer)[i++]; - spin_lock( &priv->dp_port_lock ); +dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val ); - /* convert from digi flags to termiox flags */ - if( oob_ret & DIGI_READ_INPUT_SIGNALS_CTS ) - priv->dp_modem_signals |= TIOCM_CTS; - else - priv->dp_modem_signals &= ~TIOCM_CTS; - if( oob_ret & DIGI_READ_INPUT_SIGNALS_DSR ) - priv->dp_modem_signals |= TIOCM_DSR; - else - priv->dp_modem_signals &= ~TIOCM_DSR; - if( oob_ret & DIGI_READ_INPUT_SIGNALS_RI ) - priv->dp_modem_signals |= TIOCM_RI; - else - priv->dp_modem_signals &= ~TIOCM_RI; - if( oob_ret & DIGI_READ_INPUT_SIGNALS_DCD ) - priv->dp_modem_signals |= TIOCM_CD; - else - priv->dp_modem_signals &= ~TIOCM_CD; + if( status != 0 ) + continue; - spin_unlock( &priv->dp_port_lock ); + priv = serial->port[line].private; + + if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) { + + spin_lock( &priv->dp_port_lock ); + + /* convert from digi flags to termiox flags */ + if( val & DIGI_READ_INPUT_SIGNALS_CTS ) + priv->dp_modem_signals |= TIOCM_CTS; + else + priv->dp_modem_signals &= ~TIOCM_CTS; + if( val & DIGI_READ_INPUT_SIGNALS_DSR ) + priv->dp_modem_signals |= TIOCM_DSR; + else + priv->dp_modem_signals &= ~TIOCM_DSR; + if( val & DIGI_READ_INPUT_SIGNALS_RI ) + priv->dp_modem_signals |= TIOCM_RI; + else + priv->dp_modem_signals &= ~TIOCM_RI; + if( val & DIGI_READ_INPUT_SIGNALS_DCD ) + priv->dp_modem_signals |= TIOCM_CD; + else + priv->dp_modem_signals &= ~TIOCM_CD; + + wake_up_interruptible( &modem_change_wait ); + spin_unlock( &priv->dp_port_lock ); + + } else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) { + + spin_lock( &priv->dp_port_lock ); + priv->dp_transmit_idle = 1; + wake_up_interruptible( &transmit_idle_wait ); + spin_unlock( &priv->dp_port_lock ); + + } } + resubmit: if( (ret=usb_submit_urb(urb)) != 0 ) { - dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d", + dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d", ret ); } } - diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index f2e11fc28..25d1d24cf 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -65,7 +65,7 @@ int usb_init(void) #endif { usb_major_init(); - usbdevfs_init(); + usbdevfs_init(); usb_hub_init(); #ifndef CONFIG_USB_MODULE diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index a37e805f0..b1d2d2a01 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -527,12 +527,12 @@ static int sohci_unlink_urb (urb_t * urb) #ifdef DEBUG urb_print (urb, "UNLINK", 1); #endif - - usb_dec_dev_use (urb->dev); - if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) { + usb_dec_dev_use(urb->dev); return rh_unlink_urb (urb); /* a request to the virtual root hub */ - + } + if (urb->hcpriv) { /* URB active? */ if (urb->status == USB_ST_URB_PENDING && !ohci->disabled) { @@ -548,15 +548,19 @@ static int sohci_unlink_urb (urb_t * urb) urb_priv->ed->state |= ED_URB_DEL; spin_unlock_irqrestore (&usb_ed_lock, flags); if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { + usb_dec_dev_use (urb->dev); add_wait_queue (&op_wakeup, &wait); current->state = TASK_UNINTERRUPTIBLE; if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ err("unlink URB timeout!"); remove_wait_queue (&op_wakeup, &wait); urb->status = -ENOENT; - } else + } else { + /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; + } } else { + usb_dec_dev_use (urb->dev); urb_rm_priv (urb); if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { urb->complete (urb); @@ -1974,7 +1978,7 @@ static int hc_start_ohci (struct pci_dev * dev) return -ENODEV; pci_set_master (dev); - mem_base = dev->resource[0].start; + mem_base = pci_resource_start(dev, 0); mem_base = (unsigned long) ioremap_nocache (mem_base, 4096); if (!mem_base) { diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index f2c51f205..ba9b44ac3 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -12,7 +12,7 @@ * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * - * $Id: usb-uhci.c,v 1.231 2000/05/13 15:34:17 acher Exp $ + * $Id: usb-uhci.c,v 1.232 2000/06/11 13:18:30 acher Exp $ */ #include <linux/config.h> @@ -48,7 +48,7 @@ /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB -#define VERSTR "$Revision: 1.231 $ time " __TIME__ " " __DATE__ +#define VERSTR "$Revision: 1.232 $ time " __TIME__ " " __DATE__ #include <linux/usb.h> #include "usb-uhci.h" @@ -2600,7 +2600,7 @@ _static void start_hc (uhci_t *s) s->running = 1; } -_static void __exit uhci_cleanup_dev(uhci_t *s) +_static void uhci_cleanup_dev(uhci_t *s) { struct usb_device *root_hub = s->bus->root_hub; diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 75e1c1b92..08ef79fe4 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -6,6 +6,7 @@ * (C) Copyright Andreas Gal 1999 * (C) Copyright Gregory P. Smith 1999 * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 * * NOTE! This is not actually a driver at all, rather this is * just a collection of helper routines that implement the @@ -31,6 +32,13 @@ #endif #include <linux/usb.h> +static const int usb_bandwidth_option = +#ifdef CONFIG_USB_BANDWIDTH + 1; +#else + 0; +#endif + /* * Prototypes for the device driver probing/loading functions */ @@ -61,6 +69,9 @@ int usb_register(struct usb_driver *new_driver) } info("registered new driver %s", new_driver->name); + + init_MUTEX(&new_driver->serialize); + /* Add it to the list of known drivers */ list_add(&new_driver->driver_list, &usb_driver_list); @@ -105,7 +116,9 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) struct usb_interface *interface = &dev->actconfig->interface[i]; if (interface->driver == driver) { + down(&driver->serialize); driver->disconnect(dev, interface->private_data); + up(&driver->serialize); usb_driver_release_interface(driver, interface); /* * This will go through the list looking for another @@ -142,13 +155,23 @@ void usb_deregister(struct usb_driver *driver) } } +struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) +{ + int i; + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) + if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum) + return &dev->actconfig->interface[i]; + + return NULL; +} /* - * calc_bus_time: + * usb_calc_bus_time: * * returns (approximate) USB bus time in nanoseconds for a USB transaction. */ -static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount) +static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount) { unsigned long tmp; @@ -178,16 +201,16 @@ static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); -} /* end calc_bus_time */ +} /* - * check_bandwidth_alloc(): + * usb_check_bandwidth(): * * old_alloc is from host_controller->bandwidth_allocated in microseconds; * bustime is from calc_bus_time(), but converted to microseconds. * - * returns 0 if successful, - * -1 if bandwidth request fails. + * returns <bustime in us> if successful, + * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails. * * FIXME: * This initial implementation does not use Endpoint.bInterval @@ -204,21 +227,67 @@ static long calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount * However, this first cut at USB bandwidth allocation does not * contain any frame allocation tracking. */ -static int check_bandwidth_alloc (unsigned int old_alloc, long bustime) +int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) { - unsigned int new_alloc; + int new_alloc; + int old_alloc = dev->bus->bandwidth_allocated; + unsigned int pipe = urb->pipe; + long bustime; + + bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), + usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe))); + if (usb_pipeisoc(pipe)) + bustime = NS_TO_US(bustime) / urb->number_of_packets; + else + bustime = NS_TO_US(bustime); - new_alloc = old_alloc + bustime; + new_alloc = old_alloc + (int)bustime; /* what new total allocated bus time would be */ - dbg("usb-bandwidth-alloc: was: %u, new: %u, " - "bustime = %ld us, Pipe allowed: %s", - old_alloc, new_alloc, bustime, - (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? - "yes" : "no"); + if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) + dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us", + usb_bandwidth_option ? "" : "would have ", + old_alloc, new_alloc, bustime); + + if (!usb_bandwidth_option) /* don't enforce it */ + return (bustime); + return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR; +} + +void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) +{ + dev->bus->bandwidth_allocated += bustime; + if (isoc) + dev->bus->bandwidth_isoc_reqs++; + else + dev->bus->bandwidth_int_reqs++; + urb->bandwidth = bustime; + + dbg("bw_alloc increased by %d to %d for %d requesters", + bustime, + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +} + +/* + * usb_release_bandwidth(): + * + * called to release a pipe's bandwidth (in microseconds) + */ +void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc) +{ + dev->bus->bandwidth_allocated -= urb->bandwidth; + if (isoc) + dev->bus->bandwidth_isoc_reqs--; + else + dev->bus->bandwidth_int_reqs--; - return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? 0 : -1; -} /* end check_bandwidth_alloc */ + dbg("bw_alloc reduced by %d to %d for %d requesters", + urb->bandwidth, + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); + urb->bandwidth = 0; +} /* * New functions for (de)registering a controller @@ -242,7 +311,7 @@ struct usb_bus *usb_alloc_bus(struct usb_operations *op) bus->bandwidth_isoc_reqs = 0; INIT_LIST_HEAD(&bus->bus_list); - INIT_LIST_HEAD(&bus->inodes); + INIT_LIST_HEAD(&bus->inodes); return bus; } @@ -393,7 +462,10 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) driver_list); tmp = tmp->next; - if (!(private = driver->probe(dev, ifnum))) + down(&driver->serialize); + private = driver->probe(dev, ifnum); + up(&driver->serialize); + if (!private) continue; usb_driver_claim_interface(driver, interface, private); @@ -452,8 +524,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) dev->bus = bus; dev->parent = parent; atomic_set(&dev->refcnt, 1); - INIT_LIST_HEAD(&dev->inodes); - INIT_LIST_HEAD(&dev->filelist); + INIT_LIST_HEAD(&dev->inodes); + INIT_LIST_HEAD(&dev->filelist); dev->bus->op->allocate(dev); @@ -659,21 +731,6 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, } /* - * usb_release_bandwidth(): - * - * called to release an interrupt pipe's bandwidth (in microseconds) - */ -void usb_release_bandwidth(struct usb_device *dev, int bw_alloc) -{ - dev->bus->bandwidth_allocated -= bw_alloc; - dev->bus->bandwidth_int_reqs--; - dbg("bw_alloc reduced to %d for %d requesters", - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + - dev->bus->bandwidth_isoc_reqs); -} - -/* * usb_get_current_frame_number() * * returns the current frame number for the parent USB bus/controller @@ -684,6 +741,7 @@ int usb_get_current_frame_number(struct usb_device *usb_dev) return usb_dev->bus->op->get_frame_number (usb_dev); } /*-------------------------------------------------------------------*/ + static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size) { struct usb_descriptor_header *header; @@ -826,7 +884,7 @@ static int usb_parse_interface(struct usb_device *dev, struct usb_interface *int begin = buffer; numskipped = 0; - /* Skip over at Interface class or vendor descriptors */ + /* Skip over any interface, class or vendor descriptors */ while (size >= sizeof(struct usb_descriptor_header)) { header = (struct usb_descriptor_header *)buffer; @@ -987,6 +1045,13 @@ void usb_destroy_configuration(struct usb_device *dev) if (!dev->config) return; + if (dev->rawdescriptors) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + kfree(dev->rawdescriptors[i]); + + kfree(dev->rawdescriptors); + } + for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { struct usb_config_descriptor *cf = &dev->config[c]; @@ -1129,7 +1194,9 @@ void usb_disconnect(struct usb_device **pdev) struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_driver *driver = interface->driver; if (driver) { + down(&driver->serialize); driver->disconnect(dev, interface->private_data); + up(&driver->serialize); usb_driver_release_interface(driver, interface); } } @@ -1143,14 +1210,13 @@ void usb_disconnect(struct usb_device **pdev) } /* remove /proc/bus/usb entry */ - usbdevfs_remove_device(dev); + usbdevfs_remove_device(dev); /* Free up the device itself, including its device number */ if (dev->devnum > 0) clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - + usb_free_dev(dev); - } /* @@ -1266,7 +1332,7 @@ int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) (duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT); } -static void usb_set_maxpacket(struct usb_device *dev) +void usb_set_maxpacket(struct usb_device *dev) { int i, b; @@ -1337,15 +1403,10 @@ int usb_clear_halt(struct usb_device *dev, int pipe) int usb_set_interface(struct usb_device *dev, int interface, int alternate) { - struct usb_interface *iface = NULL; - int ret, i; + struct usb_interface *iface; + int ret; - for (i=0; i<dev->actconfig->bNumInterfaces; i++) { - if (dev->actconfig->interface[i].altsetting->bInterfaceNumber == interface) { - iface = &dev->actconfig->interface[i]; - break; - } - } + iface = usb_ifnum_to_if(dev, interface); if (!iface) { warn("selecting invalid interface %d", interface); return -EINVAL; @@ -1407,11 +1468,10 @@ int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsign int usb_get_configuration(struct usb_device *dev) { - int result; - unsigned int cfgno; + int result; + unsigned int cfgno, length; unsigned char buffer[8]; unsigned char *bigbuffer; - unsigned int tmp; struct usb_config_descriptor *desc = (struct usb_config_descriptor *)buffer; @@ -1435,9 +1495,14 @@ int usb_get_configuration(struct usb_device *dev) memset(dev->config, 0, dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); - for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { - + dev->rawdescriptors = (char **)kmalloc(sizeof(char *) * + dev->descriptor.bNumConfigurations, GFP_KERNEL); + if (!dev->rawdescriptors) { + err("out of memory"); + return -1; + } + for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { /* We grab the first 8 bytes so we know how long the whole */ /* configuration is */ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); @@ -1445,41 +1510,41 @@ int usb_get_configuration(struct usb_device *dev) if (result < 0) err("unable to get descriptor"); else - err("config descriptor too short (expected %i, got %i)",8,result); + err("config descriptor too short (expected %i, got %i)", 8, result); goto err; } /* Get the full buffer */ - le16_to_cpus(&desc->wTotalLength); + length = le16_to_cpu(desc->wTotalLength); - bigbuffer = kmalloc(desc->wTotalLength, GFP_KERNEL); + bigbuffer = kmalloc(length, GFP_KERNEL); if (!bigbuffer) { err("unable to allocate memory for configuration descriptors"); - result=-ENOMEM; + result = -ENOMEM; goto err; } - tmp=desc->wTotalLength; + /* Now that we know the length, get the whole thing */ - result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, desc->wTotalLength); + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); if (result < 0) { err("couldn't get all of config descriptors"); kfree(bigbuffer); goto err; } - if (result < tmp) { - err("config descriptor too short (expected %i, got %i)",tmp,result); + if (result < length) { + err("config descriptor too short (expected %i, got %i)", length, result); kfree(bigbuffer); goto err; } - result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer); - kfree(bigbuffer); + dev->rawdescriptors[cfgno] = bigbuffer; + + result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer); if (result > 0) dbg("descriptor data left"); - else if (result < 0) - { - result=-1; + else if (result < 0) { + result = -1; goto err; } } @@ -1560,8 +1625,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) */ int usb_new_device(struct usb_device *dev) { - int addr, err; - int tmp; + int err; info("USB new device connect, assigned device number %d", dev->devnum); @@ -1572,10 +1636,15 @@ int usb_new_device(struct usb_device *dev) dev->epmaxpacketin [0] = 8; dev->epmaxpacketout[0] = 8; - /* Even though we have assigned an address for the device, we */ - /* haven't told it what it's address is yet */ - addr = dev->devnum; - dev->devnum = 0; + err = usb_set_address(dev); + if (err < 0) { + err("USB device not accepting new address (error=%d)", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; + } + + wait_ms(10); /* Let the SET_ADDRESS settle */ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); if (err < 8) { @@ -1583,34 +1652,19 @@ int usb_new_device(struct usb_device *dev) err("USB device not responding, giving up (error=%d)", err); else err("USB device descriptor short read (expected %i, got %i)",8,err); - clear_bit(addr, &dev->bus->devmap.devicemap); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; - dev->devnum = addr; - - err = usb_set_address(dev); - - if (err < 0) { - err("USB device not accepting new address (error=%d)", err); - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - dev->devnum = -1; - return 1; - } - - wait_ms(10); /* Let the SET_ADDRESS settle */ - - tmp = sizeof(dev->descriptor); - err = usb_get_device_descriptor(dev); - if (err < tmp) { + if (err < sizeof(dev->descriptor)) { if (err < 0) - err("unable to get device descriptor (error=%d)",err); + err("unable to get device descriptor (error=%d)", err); else - err("USB device descriptor short read (expected %i, got %i)",tmp,err); + err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; @@ -1646,7 +1700,7 @@ int usb_new_device(struct usb_device *dev) #endif /* now that the basic setup is over, add a /proc/bus/usb entry */ - usbdevfs_add_device(dev); + usbdevfs_add_device(dev); /* find drivers willing to handle this device */ usb_find_drivers(dev); @@ -1658,16 +1712,25 @@ static int usb_open(struct inode * inode, struct file * file) { int minor = MINOR(inode->i_rdev); struct usb_driver *c = usb_minors[minor/16]; - - file->f_op = NULL; - - if (c && (file->f_op = c->fops) && file->f_op->open) - return file->f_op->open(inode,file); - else - return -ENODEV; + int err = -ENODEV; + struct file_operations *old_fops; + + if (!c || !c->fops) + return err; + old_fops = file->f_op; + file->f_op = fops_get(c->fops); + if (file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; } static struct file_operations usb_fops = { + owner: THIS_MODULE, open: usb_open, }; @@ -1704,6 +1767,8 @@ struct list_head *usb_bus_get_list(void) * into the kernel, and other device drivers are built as modules, * then these symbols need to be exported for the modules to use. */ +EXPORT_SYMBOL(usb_ifnum_to_if); + EXPORT_SYMBOL(usb_register); EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_alloc_bus); @@ -1724,6 +1789,9 @@ EXPORT_SYMBOL(usb_new_device); EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_disconnect); + +EXPORT_SYMBOL(usb_check_bandwidth); +EXPORT_SYMBOL(usb_claim_bandwidth); EXPORT_SYMBOL(usb_release_bandwidth); EXPORT_SYMBOL(usb_set_address); diff --git a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c index 6eb49248c..42f82604e 100644 --- a/drivers/usb/usbkbd.c +++ b/drivers/usb/usbkbd.c @@ -1,7 +1,7 @@ /* - * usbkbd.c Version 0.1 + * $Id: usbkbd.c,v 1.11 2000/05/29 09:01:52 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * USB HIDBP Keyboard support * @@ -63,6 +63,8 @@ struct usb_kbd { struct urb irq, led; devrequest dr; unsigned char leds; + char name[128]; + int open; }; static void usb_kbd_irq(struct urb *urb) @@ -125,12 +127,34 @@ static void usb_kbd_led(struct urb *urb) warn("led urb status %d received", urb->status); } +static int usb_kbd_open(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + if (kbd->open++) + return 0; + + if (usb_submit_urb(&kbd->irq)) + return -EIO; + + return 0; +} + +static void usb_kbd_close(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + if (!--kbd->open) + usb_unlink_urb(&kbd->irq); +} + static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_kbd *kbd; - int i; + int i, pipe, maxp; + char *buf; if (dev->descriptor.bNumConfigurations != 1) return NULL; interface = dev->config[0].interface[ifnum].altsetting + 0; @@ -144,6 +168,9 @@ static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) if (!(endpoint->bEndpointAddress & 0x80)) return NULL; if ((endpoint->bmAttributes & 3) != 3) return NULL; + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + usb_set_protocol(dev, interface->bInterfaceNumber, 0); usb_set_idle(dev, interface->bInterfaceNumber, 0, 0); @@ -159,14 +186,11 @@ static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) kbd->dev.private = kbd; kbd->dev.event = usb_kbd_event; + kbd->dev.open = usb_kbd_open; + kbd->dev.close = usb_kbd_close; - { - int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - int maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp, - usb_kbd_irq, kbd, endpoint->bInterval); - } + FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp, + usb_kbd_irq, kbd, endpoint->bInterval); kbd->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; kbd->dr.request = USB_REQ_SET_REPORT; @@ -174,18 +198,37 @@ static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) kbd->dr.index = interface->bInterfaceNumber; kbd->dr.length = 1; - FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0), - (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd); - - if (usb_submit_urb(&kbd->irq)) { + kbd->dev.name = kbd->name; + kbd->dev.idbus = BUS_USB; + kbd->dev.idvendor = dev->descriptor.idVendor; + kbd->dev.idproduct = dev->descriptor.idProduct; + kbd->dev.idversion = dev->descriptor.bcdDevice; + + if (!(buf = kmalloc(63, GFP_KERNEL))) { kfree(kbd); return NULL; } - input_register_device(&kbd->dev); + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(kbd->name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(kbd->name, "%s %s", kbd->name, buf); + + if (!strlen(kbd->name)) + sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x", + kbd->dev.idvendor, kbd->dev.idproduct); - printk(KERN_INFO "input%d: USB HIDBP keyboard\n", kbd->dev.number); + kfree(buf); + + FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0), + (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd); + + input_register_device(&kbd->dev); + printk(KERN_INFO "input%d: %s on on usb%d:%d.%d\n", + kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum); return kbd; } diff --git a/drivers/usb/usbmouse.c b/drivers/usb/usbmouse.c index a6d0fe8cf..55bd14fdb 100644 --- a/drivers/usb/usbmouse.c +++ b/drivers/usb/usbmouse.c @@ -1,7 +1,7 @@ /* - * usbmouse.c Version 0.1 + * $Id: usbmouse.c,v 1.5 2000/05/29 09:01:52 vojtech Exp $ * - * Copyright (c) 1999 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * * USB HIDBP Mouse support * @@ -37,12 +37,12 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -#define USBMOUSE_EXTRA - struct usb_mouse { signed char data[8]; + char name[128]; struct input_dev dev; struct urb irq; + int open; }; static void usb_mouse_irq(struct urb *urb) @@ -53,16 +53,36 @@ static void usb_mouse_irq(struct urb *urb) if (urb->status) return; - input_report_key(dev, BTN_LEFT, !!(data[0] & 0x01)); - input_report_key(dev, BTN_RIGHT, !!(data[0] & 0x02)); - input_report_key(dev, BTN_MIDDLE, !!(data[0] & 0x04)); - input_report_rel(dev, REL_X, data[1]); - input_report_rel(dev, REL_Y, data[2]); -#ifdef USBMOUSE_EXTRA - input_report_key(dev, BTN_SIDE, !!(data[0] & 0x08)); - input_report_key(dev, BTN_EXTRA, !!(data[0] & 0x10)); + input_report_key(dev, BTN_LEFT, data[0] & 0x01); + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); + input_report_key(dev, BTN_SIDE, data[0] & 0x08); + input_report_key(dev, BTN_EXTRA, data[0] & 0x10); + + input_report_rel(dev, REL_X, data[1]); + input_report_rel(dev, REL_Y, data[2]); input_report_rel(dev, REL_WHEEL, data[3]); -#endif +} + +static int usb_mouse_open(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + if (mouse->open++) + return 0; + + if (usb_submit_urb(&mouse->irq)) + return -EIO; + + return 0; +} + +static void usb_mouse_close(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + if (!--mouse->open) + usb_unlink_urb(&mouse->irq); } static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) @@ -70,6 +90,8 @@ static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_mouse *mouse; + int pipe, maxp; + char *buf; if (dev->descriptor.bNumConfigurations != 1) return NULL; interface = dev->config[0].interface[ifnum].altsetting + 0; @@ -83,9 +105,9 @@ static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) if (!(endpoint->bEndpointAddress & 0x80)) return NULL; if ((endpoint->bmAttributes & 3) != 3) return NULL; -#ifndef USBMOUSE_EXTRA - usb_set_protocol(dev, interface->bInterfaceNumber, 0); -#endif + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + usb_set_idle(dev, interface->bInterfaceNumber, 0, 0); if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL; @@ -94,27 +116,44 @@ static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); -#ifdef USBMOUSE_EXTRA mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); mouse->dev.relbit[0] |= BIT(REL_WHEEL); -#endif - { - int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - int maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + mouse->dev.private = mouse; + mouse->dev.open = usb_mouse_open; + mouse->dev.close = usb_mouse_close; - FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp, - usb_mouse_irq, mouse, endpoint->bInterval); - } + mouse->dev.name = mouse->name; + mouse->dev.idbus = BUS_USB; + mouse->dev.idvendor = dev->descriptor.idVendor; + mouse->dev.idproduct = dev->descriptor.idProduct; + mouse->dev.idversion = dev->descriptor.bcdDevice; - if (usb_submit_urb(&mouse->irq)) { + if (!(buf = kmalloc(63, GFP_KERNEL))) { kfree(mouse); return NULL; } + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(mouse->name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(mouse->name, "%s %s", mouse->name, buf); + + if (!strlen(mouse->name)) + sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x", + mouse->dev.idvendor, mouse->dev.idproduct); + + kfree(buf); + + FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp, + usb_mouse_irq, mouse, endpoint->bInterval); + input_register_device(&mouse->dev); - printk(KERN_INFO "input%d: USB HIDBP mouse\n", mouse->dev.number); + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum); return mouse; } diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c index 4a0d3accd..12cb880e4 100644 --- a/drivers/usb/uss720.c +++ b/drivers/usb/uss720.c @@ -546,14 +546,14 @@ static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum) struct parport *pp; int i; - printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); - if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) && (usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) && (usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284)) return NULL; + printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); + /* our known interfaces have 3 alternate settings */ if (usbdev->actconfig->interface[ifnum].num_altsetting != 3) return NULL; diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c index 3fa4a2d35..de2821e47 100644 --- a/drivers/usb/wacom.c +++ b/drivers/usb/wacom.c @@ -1,5 +1,5 @@ /* - * wacom.c Version 0.5 + * $Id: wacom.c,v 1.9 2000/05/29 09:01:52 vojtech Exp $ * * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> @@ -18,6 +18,9 @@ * relative mode, proximity. * v0.5 (vp) - Big cleanup, nifty features removed, * they belong in userspace + * v1.8 (vp) - Submit URB only when operating, moved to CVS, + * use input_report_key instead of report_btn and + * other cleanups */ /* @@ -116,6 +119,7 @@ struct wacom { struct urb irq; struct wacom_features *features; int tool; + int open; }; static void wacom_graphire_irq(struct urb *urb) @@ -137,18 +141,18 @@ static void wacom_graphire_irq(struct urb *urb) switch ((data[1] >> 5) & 3) { case 0: /* Pen */ - input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80); + input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80); break; case 1: /* Rubber */ - input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80); + input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80); 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_key(dev, BTN_TOOL_MOUSE, data[7] > 24); + input_report_key(dev, BTN_LEFT, data[1] & 0x01); + input_report_key(dev, BTN_RIGHT, data[1] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); input_report_abs(dev, ABS_DISTANCE, data[7]); input_report_rel(dev, REL_WHEEL, (signed char) data[6]); return; @@ -156,9 +160,9 @@ static void wacom_graphire_irq(struct urb *urb) input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); - 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_key(dev, BTN_TOUCH, data[1] & 0x01); + input_report_key(dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); } static void wacom_intuos_irq(struct urb *urb) @@ -177,21 +181,23 @@ static void wacom_intuos_irq(struct urb *urb) switch (((__u32)data[2] << 4) | (data[3] >> 4)) { case 0x012: wacom->tool = BTN_TOOL_PENCIL; break; /* Inking pen */ + case 0x822: case 0x022: wacom->tool = BTN_TOOL_PEN; break; /* Pen */ case 0x032: wacom->tool = BTN_TOOL_BRUSH; break; /* Stroke pen */ case 0x094: wacom->tool = BTN_TOOL_MOUSE; break; /* Mouse 4D */ case 0x096: wacom->tool = BTN_TOOL_LENS; break; /* Lens cursor */ + case 0x82a: case 0x0fa: wacom->tool = BTN_TOOL_RUBBER; break; /* Eraser */ case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH; break; /* Airbrush */ default: wacom->tool = BTN_TOOL_PEN; break; /* Unknown tool */ } - input_report_btn(dev, wacom->tool, 1); + input_report_key(dev, wacom->tool, 1); return; } if ((data[1] | data[2] | data[3] | data[4] | data[5] | data[6] | data[7] | data[8] | data[9]) == 0x80) { /* Exit report */ - input_report_btn(dev, wacom->tool, 0); + input_report_key(dev, wacom->tool, 0); return; } @@ -202,29 +208,50 @@ static void wacom_intuos_irq(struct urb *urb) input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); - input_report_btn(dev, BTN_STYLUS, data[1] & 2); - input_report_btn(dev, BTN_STYLUS2, data[1] & 4); - input_report_btn(dev, BTN_TOUCH, t > 10); + input_report_key(dev, BTN_STYLUS, data[1] & 2); + input_report_key(dev, BTN_STYLUS2, data[1] & 4); + input_report_key(dev, BTN_TOUCH, t > 10); } #define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS)) struct wacom_features wacom_features[] = { - { "Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq, + { "Wacom Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq, BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 }, - { "Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq, + { "Wacom Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq, 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, - { "Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq, + { "Wacom Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq, 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, - { "Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq, + { "Wacom Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq, 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, - { "Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq, + { "Wacom Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq, 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, - { "Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq, + { "Wacom Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq, 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, { NULL , 0 } }; +static int wacom_open(struct input_dev *dev) +{ + struct wacom *wacom = dev->private; + + if (wacom->open++) + return 0; + + if (usb_submit_urb(&wacom->irq)) + return -EIO; + + return 0; +} + +static void wacom_close(struct input_dev *dev) +{ + struct wacom *wacom = dev->private; + + if (!--wacom->open) + usb_unlink_urb(&wacom->irq); +} + static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_endpoint_descriptor *endpoint; @@ -256,17 +283,23 @@ static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) wacom->dev.absmax[ABS_TILT_X] = 127; wacom->dev.absmax[ABS_TILT_Y] = 127; + wacom->dev.private = wacom; + wacom->dev.open = wacom_open; + wacom->dev.close = wacom_close; + + wacom->dev.name = wacom->features->name; + wacom->dev.idbus = BUS_USB; + wacom->dev.idvendor = dev->descriptor.idVendor; + wacom->dev.idproduct = dev->descriptor.idProduct; + wacom->dev.idversion = dev->descriptor.bcdDevice; + FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval); - if (usb_submit_urb(&wacom->irq)) { - kfree(wacom); - return NULL; - } - input_register_device(&wacom->dev); - printk(KERN_INFO "input%d: Wacom %s on usb%d\n", wacom->dev.number, wacom->features->name, dev->devnum); + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum); return wacom; } diff --git a/drivers/usb/wmforce.c b/drivers/usb/wmforce.c index 1d8402f97..dff963b74 100644 --- a/drivers/usb/wmforce.c +++ b/drivers/usb/wmforce.c @@ -1,5 +1,5 @@ /* - * wmforce.c Version 0.1 + * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ * * Copyright (c) 2000 Vojtech Pavlik * @@ -44,6 +44,7 @@ struct wmforce { signed char data[8]; struct input_dev dev; struct urb irq; + int open; }; static struct { @@ -51,6 +52,8 @@ static struct { __s32 y; } wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; +static char *wmforce_name = "Logitech WingMan Force"; + static void wmforce_irq(struct urb *urb) { struct wmforce *wmforce = urb->context; @@ -71,15 +74,36 @@ static void wmforce_irq(struct urb *urb) input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x); input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y); - input_report_key(dev, BTN_TRIGGER, !!(data[6] & 0x01)); - input_report_key(dev, BTN_TOP, !!(data[6] & 0x02)); - input_report_key(dev, BTN_THUMB, !!(data[6] & 0x04)); - input_report_key(dev, BTN_TOP2, !!(data[6] & 0x08)); - input_report_key(dev, BTN_BASE, !!(data[6] & 0x10)); - input_report_key(dev, BTN_BASE2, !!(data[6] & 0x20)); - input_report_key(dev, BTN_BASE3, !!(data[6] & 0x40)); - input_report_key(dev, BTN_BASE4, !!(data[6] & 0x80)); - input_report_key(dev, BTN_BASE5, !!(data[7] & 0x01)); + input_report_key(dev, BTN_TRIGGER, data[6] & 0x01); + input_report_key(dev, BTN_TOP, data[6] & 0x02); + input_report_key(dev, BTN_THUMB, data[6] & 0x04); + input_report_key(dev, BTN_TOP2, data[6] & 0x08); + input_report_key(dev, BTN_BASE, data[6] & 0x10); + input_report_key(dev, BTN_BASE2, data[6] & 0x20); + input_report_key(dev, BTN_BASE3, data[6] & 0x40); + input_report_key(dev, BTN_BASE4, data[6] & 0x80); + input_report_key(dev, BTN_BASE5, data[7] & 0x01); +} + +static int wmforce_open(struct input_dev *dev) +{ + struct wmforce *wmforce = dev->private; + + if (wmforce->open++) + return 0; + + if (usb_submit_urb(&wmforce->irq)) + return -EIO; + + return 0; +} + +static void wmforce_close(struct input_dev *dev) +{ + struct wmforce *wmforce = dev->private; + + if (!--wmforce->open) + usb_unlink_urb(&wmforce->irq); } static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum) @@ -105,33 +129,34 @@ static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum) for (i = ABS_X; i <= ABS_Y; i++) { wmforce->dev.absmax[i] = 1920; wmforce->dev.absmin[i] = -1920; - wmforce->dev.absfuzz[i] = 0; wmforce->dev.absflat[i] = 128; } - wmforce->dev.absmax[ABS_THROTTLE] = 0; - wmforce->dev.absmin[ABS_THROTTLE] = 255; - wmforce->dev.absfuzz[ABS_THROTTLE] = 0; - wmforce->dev.absflat[ABS_THROTTLE] = 0; + wmforce->dev.absmax[ABS_THROTTLE] = 255; + wmforce->dev.absmin[ABS_THROTTLE] = 0; for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { wmforce->dev.absmax[i] = 1; wmforce->dev.absmin[i] = -1; - wmforce->dev.absfuzz[i] = 0; - wmforce->dev.absflat[i] = 0; } + wmforce->dev.private = wmforce; + wmforce->dev.open = wmforce_open; + wmforce->dev.close = wmforce_close; + + wmforce->dev.name = wmforce_name; + wmforce->dev.idbus = BUS_USB; + wmforce->dev.idvendor = dev->descriptor.idVendor; + wmforce->dev.idproduct = dev->descriptor.idProduct; + wmforce->dev.idversion = dev->descriptor.bcdDevice; + FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval); - if (usb_submit_urb(&wmforce->irq)) { - kfree(wmforce); - return NULL; - } - input_register_device(&wmforce->dev); - printk(KERN_INFO "input%d: Logitech WingMan Force USB\n", wmforce->dev.number); + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum); return wmforce; } |