summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-19 22:45:37 +0000
commit6d403070f28cd44860fdb3a53be5da0275c65cf4 (patch)
tree0d0e7fe7b5fb7568d19e11d7d862b77a866ce081 /drivers/usb
parentecf1bf5f6c2e668d03b0a9fb026db7aa41e292e1 (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')
-rw-r--r--drivers/usb/Config.in6
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/acm.c1
-rw-r--r--drivers/usb/audio.c32
-rw-r--r--drivers/usb/dabusb.c8
-rw-r--r--drivers/usb/dabusb.h2
-rw-r--r--drivers/usb/dc2xx.c12
-rw-r--r--drivers/usb/devio.c214
-rw-r--r--drivers/usb/dsbr100.c5
-rw-r--r--drivers/usb/evdev.c55
-rw-r--r--drivers/usb/graphire.c195
-rw-r--r--drivers/usb/hid-debug.h2
-rw-r--r--drivers/usb/hid.c83
-rw-r--r--drivers/usb/hid.h4
-rw-r--r--drivers/usb/hub.c262
-rw-r--r--drivers/usb/input.c39
-rw-r--r--drivers/usb/joydev.c25
-rw-r--r--drivers/usb/keybdev.c8
-rw-r--r--drivers/usb/mdc800.c8
-rw-r--r--drivers/usb/microtek.c1051
-rw-r--r--drivers/usb/microtek.h73
-rw-r--r--drivers/usb/mousedev.c22
-rw-r--r--drivers/usb/ov511.c286
-rw-r--r--drivers/usb/ov511.h15
-rw-r--r--drivers/usb/pegasus.c29
-rw-r--r--drivers/usb/printer.c109
-rw-r--r--drivers/usb/scanner.c8
-rw-r--r--drivers/usb/scanner.h4
-rw-r--r--drivers/usb/serial/digi_acceleport.c821
-rw-r--r--drivers/usb/usb-core.c2
-rw-r--r--drivers/usb/usb-ohci.c16
-rw-r--r--drivers/usb/usb-uhci.c6
-rw-r--r--drivers/usb/usb.c266
-rw-r--r--drivers/usb/usbkbd.c75
-rw-r--r--drivers/usb/usbmouse.c91
-rw-r--r--drivers/usb/uss720.c6
-rw-r--r--drivers/usb/wacom.c87
-rw-r--r--drivers/usb/wmforce.c71
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;
}