Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@kroah.com>2002-11-26 19:24:48 -0800
committerGreg Kroah-Hartman <greg@kroah.com>2002-11-26 19:24:48 -0800
commit6406f3a956e26a2e22738256a342c55444378ca4 (patch)
treee89d43c4cd4998644cf790b898f5c54abaaee336
parent828bb5f408cbae3c7dae1545f3ceb0bba55cdf47 (diff)
parentb6f5eb6a38edd381a1dc0c5824c348cfec526409 (diff)
Merge kroah.com:/home/linux/linux/BK/bleeding-2.5
into kroah.com:/home/linux/linux/BK/gregkh-2.5
-rw-r--r--Documentation/usb/ov511.txt9
-rw-r--r--Documentation/usb/usb-serial.txt45
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/hotplug.c6
-rw-r--r--drivers/usb/core/Kconfig12
-rw-r--r--drivers/usb/core/devio.c67
-rw-r--r--drivers/usb/host/ohci-mem.c130
-rw-r--r--drivers/usb/host/ohci.h21
-rw-r--r--drivers/usb/media/Kconfig29
-rw-r--r--drivers/usb/media/ov511.c227
-rw-r--r--drivers/usb/media/ov511.h4
-rw-r--r--drivers/usb/media/pwc-ctrl.c28
-rw-r--r--drivers/usb/media/pwc-if.c307
-rw-r--r--drivers/usb/media/pwc-misc.c2
-rw-r--r--drivers/usb/media/pwc-uncompress.c26
-rw-r--r--drivers/usb/media/pwc-uncompress.h2
-rw-r--r--drivers/usb/media/pwc.h9
-rw-r--r--drivers/usb/media/vicam.c301
-rw-r--r--drivers/usb/misc/tiglusb.c11
-rw-r--r--drivers/usb/serial/Kconfig5
-rw-r--r--drivers/usb/serial/Makefile5
-rw-r--r--drivers/usb/serial/ezusb.c67
-rw-r--r--drivers/usb/serial/generic.c304
-rw-r--r--drivers/usb/serial/ipaq.c23
-rw-r--r--drivers/usb/serial/keyspan.c2
-rw-r--r--drivers/usb/serial/usb-serial.c357
-rw-r--r--drivers/usb/serial/usb-serial.h28
-rw-r--r--drivers/usb/serial/visor.c2
-rw-r--r--drivers/usb/serial/visor.h1
-rw-r--r--drivers/usb/serial/whiteheat.c469
-rw-r--r--include/linux/usb.h11
31 files changed, 1481 insertions, 1031 deletions
diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt
index 8c36ebe503fb..e1974ec8217e 100644
--- a/Documentation/usb/ov511.txt
+++ b/Documentation/usb/ov511.txt
@@ -255,6 +255,15 @@ MODULE PARAMETERS:
might be necessary if your camera has a custom lens assembly. This has
no effect with video capture devices.
+ NAME: ov518_color
+ TYPE: integer (Boolean)
+ DEFAULT: 0 (off)
+ DESC: Enable OV518 color support. This is off by default since it doesn't
+ work most of the time. If you want to try it, you must also load
+ ov518_decomp with the "nouv=0" parameter. If you get improper colors or
+ diagonal lines through the image, restart your video app and try again.
+ Repeat as necessary.
+
WORKING FEATURES:
o Color streaming/capture at most widths and heights that are multiples of 8.
o Monochrome (use force_palette=1 to enable)
diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt
index ac32a1465338..7e6150c50088 100644
--- a/Documentation/usb/usb-serial.txt
+++ b/Documentation/usb/usb-serial.txt
@@ -94,10 +94,11 @@ HandSpring Visor, Palm USB, and Clié USB driver
Compaq iPAQ, HP Jornada and Casio EM500 driver
This driver can be used to connect to Compaq iPAQ, HP Jornada and Casio EM500
- PDAs running Windows CE 3.0 or PocketPC 2002 using a USB cable/cradle. It
- has been tested only on the Compaq H3135, but is rumoured to work on
- with the H3600 and later models as well as the Jornada 548 and 568.
- With minor modifications, it may work for other CE based handhelds too.
+ PDAs running Windows CE 3.0 or PocketPC 2002 using a USB cable/cradle.
+ It's very likely that every device supported by ActiveSync USB works with this
+ driver. The driver supports the Compaq iPAQ, Jornada 548/568 and the Casio
+ EM500 out of the box. For others, please use module parameters to specify
+ the product and vendor id. e.g. modprobe ipaq vendor=0x3f0 product=0x1125
The driver presents a serial interface (usually on /dev/ttyUSB0) over
which one may run ppp and establish a TCP/IP link to the iPAQ. Once this
@@ -105,36 +106,12 @@ Compaq iPAQ, HP Jornada and Casio EM500 driver
significant advantage of using USB is speed - you can get 73 to 113
kbytes/sec for download/upload to the iPAQ.
- The driver works intermittently with the usb-uhci driver but quite
- reliably with the uhci driver. However, performance is much better
- with usb-uhci. It does not seem to work with ohci at all.
-
- You must setup hotplug to invoke pppd as soon as the iPAQ is connected.
- A ppp script like the one below should be kept in the file
- /etc/hotplug/usb/ipaq Remember to chmod +x. Make sure there are no
- options in /etc/ppp/options or ~/.ppprc which conflict with the ones
- given below.
-
- #!/bin/bash
-
- MYIP=linux.box.ip
- REMOTEIP=ipaq.ip
- MYDNS=my.dns.server
- killall -9 pppd
- /usr/sbin/pppd /dev/ttyUSB0 \
- connect "/usr/sbin/chat -v TIMEOUT 60 CLIENT 'CLIENTSERVER\c'" \
- nocrtscts local debug passive $MYIP:$REMOTEIP ms-dns $MYDNS noauth \
- proxyarp
-
- You must also download and install asyncd from http://synce.sourceforge.net
- This is required to emulate keep-alive packets which are exchanged by
- ActiveSync and the iPAQ.
-
- On connecting the cable, you should see the usual "Device Connected",
- "User Authenticated" messages flash by on your iPAQ. Once connected,
- you can use Win CE programs like ftpView, Pocket Outlook from the iPAQ
- and xcerdisp, synce utilities from the Linux side. Remember to enable IP
- forwarding.
+ This driver is only one of a set of components required to utilize
+ the USB connection. Please visit http://synce.sourceforge.net which
+ contains the necessary packages and a simple step-by-step howto.
+
+ Once connected, you can use Win CE programs like ftpView, Pocket Outlook
+ from the iPAQ and xcerdisp, synce utilities from the Linux side.
To use Pocket IE, follow the instructions given at
http://www.tekguru.co.uk/EM500/usbtonet.htm to achieve the same thing
diff --git a/drivers/base/base.h b/drivers/base/base.h
index c9eeed6b07e9..6194d88242ec 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -34,7 +34,7 @@ static inline int dev_hotplug(struct device *dev, const char *action)
{
return 0;
}
-static int class_hotplug(struct device *dev, const char *action)
+static inline int class_hotplug(struct device *dev, const char *action)
{
return 0;
}
diff --git a/drivers/base/hotplug.c b/drivers/base/hotplug.c
index 3499840e34b5..95937fbe3676 100644
--- a/drivers/base/hotplug.c
+++ b/drivers/base/hotplug.c
@@ -35,6 +35,8 @@
#define BUFFER_SIZE 1024 /* should be enough memory for the env */
#define NUM_ENVP 32 /* number of env pointers */
+static char prefix [] = "devices"; /* /sys/devices/... */
+
static int do_hotplug (struct device *dev, char *argv1, const char *action,
int (* hotplug) (struct device *, char **, int, char *, int))
{
@@ -72,7 +74,7 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action,
}
dev_length = get_devpath_length (dev);
- dev_length += strlen("root");
+ dev_length += strlen(prefix);
dev_path = kmalloc (dev_length, GFP_KERNEL);
if (!dev_path) {
kfree (buffer);
@@ -80,7 +82,7 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action,
return -ENOMEM;
}
memset (dev_path, 0x00, dev_length);
- strcpy (dev_path, "root");
+ strcpy (dev_path, prefix);
fill_devpath (dev, dev_path, dev_length);
argv [0] = hotplug_path;
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 721c15f295f5..6004a5231186 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -38,18 +38,6 @@ config USB_DEVICEFS
Most users want to say Y here.
-config USB_LONG_TIMEOUT
- bool "Long timeout for slow-responding devices (some MGE Ellipse UPSes)"
- depends on USB
- help
- This option makes the standard time out a bit longer. Basically,
- some devices are just slow to respond, so this makes usb more
- patient. There should be no harm in selecting this, but it is
- needed for some MGE Ellipse UPSes.
-
- If you have an MGE Ellipse UPS, or you see timeouts in HID
- transactions, say Y; otherwise say N.
-
config USB_BANDWIDTH
bool "Enforce USB bandwidth allocation (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 8b0bebe8d62e..4199b9332979 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -53,6 +53,7 @@ struct async {
struct dev_state *ps;
struct task_struct *task;
unsigned int signr;
+ unsigned int intf;
void *userbuffer;
void *userurb;
struct urb *urb;
@@ -273,23 +274,47 @@ static void async_completed(struct urb *urb)
}
}
-static void destroy_all_async(struct dev_state *ps)
+static void destroy_async (struct dev_state *ps, struct list_head *list)
{
- struct async *as;
- unsigned long flags;
+ struct async *as;
+ unsigned long flags;
- spin_lock_irqsave(&ps->lock, flags);
- while (!list_empty(&ps->async_pending)) {
- as = list_entry(ps->async_pending.next, struct async, asynclist);
- list_del_init(&as->asynclist);
- spin_unlock_irqrestore(&ps->lock, flags);
+ spin_lock_irqsave(&ps->lock, flags);
+ while (!list_empty(list)) {
+ as = list_entry(list->next, struct async, asynclist);
+ list_del_init(&as->asynclist);
+ spin_unlock_irqrestore(&ps->lock, flags);
/* usb_unlink_urb calls the completion handler with status == -ENOENT */
- usb_unlink_urb(as->urb);
- spin_lock_irqsave(&ps->lock, flags);
- }
- spin_unlock_irqrestore(&ps->lock, flags);
- while ((as = async_getcompleted(ps)))
- free_async(as);
+ usb_unlink_urb(as->urb);
+ spin_lock_irqsave(&ps->lock, flags);
+ }
+ spin_unlock_irqrestore(&ps->lock, flags);
+ while ((as = async_getcompleted(ps)))
+ free_async(as);
+}
+
+static void destroy_async_on_interface (struct dev_state *ps, unsigned int intf)
+{
+ struct async *as;
+ struct list_head *p, hitlist;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&hitlist);
+ spin_lock_irqsave(&ps->lock, flags);
+ for (p = ps->async_pending.next; p != &ps->async_pending; ) {
+ as = list_entry(p, struct async, asynclist);
+ p = p->next;
+
+ if (as->intf == intf)
+ list_move_tail(&as->asynclist, &hitlist);
+ }
+ spin_unlock_irqrestore(&ps->lock, flags);
+ destroy_async(ps, &hitlist);
+}
+
+extern __inline__ void destroy_all_async(struct dev_state *ps)
+{
+ destroy_async(ps, &ps->async_pending);
}
/*
@@ -751,7 +776,7 @@ static int proc_submiturb(struct dev_state *ps, void *arg)
struct async *as;
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
- int ret, interval = 0;
+ int ret, interval = 0, intf = -1;
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
@@ -763,9 +788,9 @@ static int proc_submiturb(struct dev_state *ps, void *arg)
if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX))
return -EINVAL;
if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
- if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0)
- return ret;
- if ((ret = checkintf(ps, ret)))
+ if ((intf = findintfep(ps->dev, uurb.endpoint)) < 0)
+ return intf;
+ if ((ret = checkintf(ps, intf)))
return ret;
}
switch(uurb.type) {
@@ -889,6 +914,7 @@ static int proc_submiturb(struct dev_state *ps, void *arg)
else
as->userbuffer = NULL;
as->signr = uurb.signr;
+ as->intf = intf;
as->task = current;
if (!(uurb.endpoint & USB_DIR_IN)) {
if (copy_from_user(as->urb->transfer_buffer, uurb.buffer, as->urb->transfer_buffer_length)) {
@@ -1035,7 +1061,10 @@ static int proc_releaseinterface(struct dev_state *ps, void *arg)
return -EFAULT;
if ((ret = findintfif(ps->dev, intf)) < 0)
return ret;
- return releaseintf(ps, intf);
+ if ((ret = releaseintf(ps, intf)) < 0)
+ return ret;
+ destroy_async_on_interface (ps, intf);
+ return 0;
}
static int proc_ioctl (struct dev_state *ps, void *arg)
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index 11d4bac4a19c..f4bc1a75248f 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -14,7 +14,8 @@
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use pci_pool or pci_alloc_consistent
- * - driver buffers, read/written by HC ... single shot DMA mapped
+ * - driver buffers, read/written by HC ... the hcd glue or the
+ * device driver provides us with dma addresses
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pagable.
@@ -41,95 +42,6 @@ static void ohci_hcd_free (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
-/* Recover a TD/ED using its collision chain */
-static inline void *
-dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
-{
- struct hash_t * scan = entry->head;
- while (scan && scan->dma != dma)
- scan = scan->next;
- return scan ? scan->virt : 0;
-}
-
-static inline struct td *
-dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
-{
- td_dma &= TD_MASK;
- return (struct td *) dma_to_ed_td(&(hc->td_hash [TD_HASH_FUNC(td_dma)]),
- td_dma);
-}
-
-// FIXME: when updating the hashtables this way, mem_flags is unusable...
-
-/* Add a hash entry for a TD/ED; return true on success */
-static inline int
-hash_add_ed_td (
- struct hash_list_t *entry,
- void *virt,
- dma_addr_t dma,
- int mem_flags
-)
-{
- struct hash_t * scan;
-
- scan = (struct hash_t *) kmalloc (sizeof *scan, mem_flags);
- if (!scan)
- return 0;
-
- if (!entry->tail) {
- entry->head = entry->tail = scan;
- } else {
- entry->tail->next = scan;
- entry->tail = scan;
- }
-
- scan->virt = virt;
- scan->dma = dma;
- scan->next = NULL;
- return 1;
-}
-
-static inline int
-hash_add_td (struct ohci_hcd *hc, struct td *td, int mem_flags)
-{
- return hash_add_ed_td (&(hc->td_hash [TD_HASH_FUNC (td->td_dma)]),
- td, td->td_dma, mem_flags);
-}
-
-
-static inline void
-hash_free_ed_td (struct hash_list_t *entry, void *virt)
-{
- struct hash_t *scan, *prev;
- scan = prev = entry->head;
-
- // Find and unlink hash entry
- while (scan && scan->virt != virt) {
- prev = scan;
- scan = scan->next;
- }
- if (scan) {
- if (scan == entry->head) {
- if (entry->head == entry->tail)
- entry->head = entry->tail = NULL;
- else
- entry->head = scan->next;
- } else if (scan == entry->tail) {
- entry->tail = prev;
- prev->next = NULL;
- } else
- prev->next = scan->next;
- kfree(scan);
- }
-}
-
-static inline void
-hash_free_td (struct ohci_hcd *hc, struct td * td)
-{
- hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
-}
-
-
static int ohci_mem_init (struct ohci_hcd *ohci)
{
ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
@@ -161,6 +73,21 @@ static void ohci_mem_cleanup (struct ohci_hcd *ohci)
}
}
+/*-------------------------------------------------------------------------*/
+
+/* ohci "done list" processing needs this mapping */
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+ struct td *td;
+
+ td_dma &= TD_MASK;
+ td = hc->td_hash [TD_HASH_FUNC(td_dma)];
+ while (td && td->td_dma != td_dma)
+ td = td->td_hash;
+ return td;
+}
+
/* TDs ... */
static struct td *
td_alloc (struct ohci_hcd *hc, int mem_flags)
@@ -170,12 +97,17 @@ td_alloc (struct ohci_hcd *hc, int mem_flags)
td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
+ int hash;
+
+ /* in case hc fetches it, make it look dead */
+ memset (td, 0, sizeof *td);
+ td->hwNextTD = cpu_to_le32 (dma);
td->td_dma = dma;
+
/* hash it for later reverse mapping */
- if (!hash_add_td (hc, td, mem_flags)) {
- pci_pool_free (hc->td_cache, td, dma);
- return NULL;
- }
+ hash = TD_HASH_FUNC (dma);
+ td->td_hash = hc->td_hash [hash];
+ hc->td_hash [hash] = td;
}
return td;
}
@@ -183,10 +115,18 @@ td_alloc (struct ohci_hcd *hc, int mem_flags)
static void
td_free (struct ohci_hcd *hc, struct td *td)
{
- hash_free_td (hc, td);
+ struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
+
+ while (*prev && *prev != td)
+ prev = &(*prev)->td_hash;
+ if (*prev)
+ *prev = td->td_hash;
+ else
+ dev_dbg (*hc->hcd.controller, "bad hash for td %p\n", td);
pci_pool_free (hc->td_cache, td, td->td_dma);
}
+/*-------------------------------------------------------------------------*/
/* EDs ... */
static struct ed *
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index ed25b8c0588d..5c2e08a4027e 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -111,6 +111,7 @@ struct td {
/* rest are purely for the driver's use */
__u8 index;
struct ed *ed;
+ struct td *td_hash; /* dma-->td hashtable */
struct td *next_dl_td;
struct urb *urb;
@@ -320,23 +321,9 @@ typedef struct urb_priv {
#define URB_DEL 1
-
-/* Hash struct used for TD/ED hashing */
-struct hash_t {
- void *virt;
- dma_addr_t dma;
- struct hash_t *next; // chaining for collision cases
-};
-
-/* List of TD/ED hash entries */
-struct hash_list_t {
- struct hash_t *head;
- struct hash_t *tail;
-};
-
#define TD_HASH_SIZE 64 /* power'o'two */
-
-#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
+// sizeof (struct td) ~= 64 == 2^6 ...
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
/*
@@ -373,7 +360,7 @@ struct ohci_hcd {
*/
struct pci_pool *td_cache;
struct pci_pool *ed_cache;
- struct hash_list_t td_hash [TD_HASH_SIZE];
+ struct td *td_hash [TD_HASH_SIZE];
/*
* driver state
diff --git a/drivers/usb/media/Kconfig b/drivers/usb/media/Kconfig
index b002ead8c4be..a179a8649c63 100644
--- a/drivers/usb/media/Kconfig
+++ b/drivers/usb/media/Kconfig
@@ -120,15 +120,26 @@ config USB_PWC
tristate "USB Philips Cameras"
depends on USB && VIDEO_DEV
---help---
- Say Y or M here if you want to use one of these Philips USB webcams:
- PCA645, PCA646, PCVC675, PCVC680, PCVC690, PCVC730, PCVC740, or
- the Askey VC010. The PCA635, PCVC665 and PCVC720 are not supported
- by this driver and never will be.
-
- This driver has an optional plugin, which is distributed as a binary
- module only. It contains code that allow you to use higher
- resolutions and framerates but may not be distributed as source.
- But even without this plugin you can these cams for most
+ Say Y or M here if you want to use one of these Philips & OEM
+ webcams:
+ * Philips PCA645, PCA646
+ * Philips PCVC675, PCVC680, PCVC690
+ * Philips PCVC730, PCVC740, PCVC750
+ * Askey VC010
+ * Logitech QuickCam Pro 3000, 4000, 'Zoom' and 'Notebook'
+ * Samsung MPC-C10, MPC-C30
+ * Creative Webcam 5
+ * SOTECT Afina Eye
+ * Visionite VCS-UC300, VCS-UM100
+
+ The PCA635, PCVC665 and PCVC720 are not supported by this driver
+ and never will be, but the 665 and 720 are supported by other
+ drivers.
+
+ This driver has an optional plugin (called PWCX), which is
+ distributed as a binary module only. It contains code that allow you
+ to use higher resolutions and framerates but may not be distributed
+ as source. But even without this plugin you can these cams for most
applications.
See <file:Documentation/usb/philips.txt> for more information and
diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c
index 0f8f81143f61..6eb6b2f3f828 100644
--- a/drivers/usb/media/ov511.c
+++ b/drivers/usb/media/ov511.c
@@ -60,7 +60,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.62 for Linux 2.5"
+#define DRIVER_VERSION "v1.63 for Linux 2.5"
#define EMAIL "mark@alpha.dyndns.org"
#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \
& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
@@ -121,6 +121,7 @@ static int backlight;
static int unit_video[OV511_MAX_UNIT_VIDEO];
static int remove_zeros;
static int mirror;
+static int ov518_color;
MODULE_PARM(autobright, "i");
MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
@@ -193,6 +194,8 @@ MODULE_PARM_DESC(remove_zeros,
"Remove zero-padding from uncompressed incoming data");
MODULE_PARM(mirror, "i");
MODULE_PARM_DESC(mirror, "Reverse image horizontally");
+MODULE_PARM(ov518_color, "i");
+MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -259,6 +262,7 @@ static struct symbolic_list camlist[] = {
{ 100, "Lifeview RoboCam" },
{ 102, "AverMedia InterCam Elite" },
{ 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
+ { 134, "Ezonics EZCam II" },
{ 192, "Webeye 2000B" },
{ 253, "Alpha Vision Tech. AlphaCam SE" },
{ -1, NULL }
@@ -293,6 +297,7 @@ static struct symbolic_list brglist[] = {
{ -1, NULL }
};
+#if defined(CONFIG_VIDEO_PROC_FS)
static struct symbolic_list senlist[] = {
{ SEN_OV76BE, "OV76BE" },
{ SEN_OV7610, "OV7610" },
@@ -308,6 +313,7 @@ static struct symbolic_list senlist[] = {
{ SEN_SAA7111A, "SAA7111A" },
{ -1, NULL }
};
+#endif
/* URB error codes: */
static struct symbolic_list urb_errlist[] = {
@@ -321,20 +327,6 @@ static struct symbolic_list urb_errlist[] = {
};
/**********************************************************************
- * Prototypes
- **********************************************************************/
-
-static void ov51x_clear_snapshot(struct usb_ov511 *);
-static inline int sensor_get_picture(struct usb_ov511 *,
- struct video_picture *);
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
-static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
-static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int,
- unsigned long);
-static int ov51x_check_snapshot(struct usb_ov511 *);
-#endif
-
-/**********************************************************************
* Memory management
**********************************************************************/
@@ -396,11 +388,19 @@ rvfree(void *mem, unsigned long size)
* Based on the CPiA driver version 0.7.4 -claudio
**********************************************************************/
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+#if defined(CONFIG_VIDEO_PROC_FS)
static struct proc_dir_entry *ov511_proc_entry = NULL;
extern struct proc_dir_entry *video_proc_entry;
+/* Prototypes */
+static void ov51x_clear_snapshot(struct usb_ov511 *);
+static int sensor_get_picture(struct usb_ov511 *, struct video_picture *);
+static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
+static int ov51x_check_snapshot(struct usb_ov511 *);
+static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int,
+ unsigned long);
+
static struct file_operations ov511_control_fops = {
.ioctl = ov51x_control_ioctl,
};
@@ -460,6 +460,8 @@ ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
symbolic(senlist, ov->sensor));
out += sprintf(out, "packet_size : %d\n", ov->packet_size);
out += sprintf(out, "framebuffer : 0x%p\n", ov->fbuf);
+ out += sprintf(out, "packet_numbering: %d\n", ov->packet_numbering);
+ out += sprintf(out, "topology : %s\n", ov->usb_path);
len = out - page;
len -= off;
@@ -637,7 +639,12 @@ proc_ov511_destroy(void)
remove_proc_entry("ov511", video_proc_entry);
}
-#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
+#else
+static inline void create_proc_ov511_cam(struct usb_ov511 *ov) { }
+static inline void destroy_proc_ov511_cam(struct usb_ov511 *ov) { }
+static inline void proc_ov511_create(void) { }
+static inline void proc_ov511_destroy(void) { }
+#endif /* #ifdef CONFIG_VIDEO_PROC_FS */
/**********************************************************************
*
@@ -1116,7 +1123,7 @@ i2c_w_mask(struct usb_ov511 *ov,
* when calling this. This should not be called from outside the i2c I/O
* functions.
*/
-static inline int
+static int
i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave)
{
int rc;
@@ -1357,7 +1364,7 @@ ov51x_clear_snapshot(struct usb_ov511 *ov)
}
}
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+#if defined(CONFIG_VIDEO_PROC_FS)
/* Checks the status of the snapshot button. Returns 1 if it was pressed since
* it was last cleared, and zero in all other cases (including errors) */
static int
@@ -1970,7 +1977,7 @@ sensor_get_hue(struct usb_ov511 *ov, unsigned short *val)
/* -------------------------------------------------------------------------- */
-static inline int
+static int
sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p)
{
int rc;
@@ -2001,7 +2008,7 @@ sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p)
return 0;
}
-static inline int
+static int
sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p)
{
int rc;
@@ -2032,7 +2039,7 @@ sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p)
return 0;
}
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+#if defined(CONFIG_VIDEO_PROC_FS)
// FIXME: Exposure range is only 0x00-0x7f in interlace mode
/* Sets current exposure for sensor. This only has an effect if auto-exposure
* is off */
@@ -2117,7 +2124,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val)
#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
-static inline void
+static void
ov51x_led_control(struct usb_ov511 *ov, int enable)
{
PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
@@ -2198,7 +2205,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq)
* Unsupported: KS0127, KS0127B, SAA7111A
* Returns: 0 for success
*/
-static inline int
+static int
sensor_set_banding_filter(struct usb_ov511 *ov, int enable)
{
int rc;
@@ -2226,7 +2233,7 @@ sensor_set_banding_filter(struct usb_ov511 *ov, int enable)
* Unsupported: KS0127, KS0127B, SAA7111A
* Returns: 0 for success
*/
-static inline int
+static int
sensor_set_auto_brightness(struct usb_ov511 *ov, int enable)
{
int rc;
@@ -2254,7 +2261,7 @@ sensor_set_auto_brightness(struct usb_ov511 *ov, int enable)
* Unsupported: KS0127, KS0127B, SAA7111A
* Returns: 0 for success
*/
-static inline int
+static int
sensor_set_auto_exposure(struct usb_ov511 *ov, int enable)
{
PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
@@ -2333,7 +2340,7 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable)
return 0;
}
-static inline int
+static int
sensor_set_mirror(struct usb_ov511 *ov, int enable)
{
PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
@@ -2434,7 +2441,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
i2c_w(ov, 0x14, qvga?0x24:0x04);
break;
case SEN_OV6630:
- i2c_w(ov, 0x14, qvga?0xa4:0x84);
+ i2c_w(ov, 0x14, qvga?0xa0:0x80);
break;
default:
err("Invalid sensor");
@@ -2448,13 +2455,33 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
/* these aren't valid on the OV6620/OV7620/6630? */
i2c_w_mask(ov, 0x0e, 0x40, 0x40);
}
- i2c_w_mask(ov, 0x13, 0x20, 0x20);
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x00, 0x10);
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ }
} else {
if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
/* not valid on the OV6620/OV7620/6630? */
i2c_w_mask(ov, 0x0e, 0x00, 0x40);
}
- i2c_w_mask(ov, 0x13, 0x00, 0x20);
+
+ /* The OV518 needs special treatment. Although both the OV518
+ * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+ * bus is actually used. The UV bus is tied to ground.
+ * Therefore, the OV6630 needs to be in 8-bit multiplexed
+ * output mode */
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x10, 0x10);
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ }
}
/******** Clock programming ********/
@@ -2781,8 +2808,29 @@ ov518_mode_init_regs(struct usb_ov511 *ov,
reg_w(ov, 0x3d, 0);
reg_w(ov, 0x3e, 0);
- reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
- reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ if (ov->bridge == BRG_OV518 && ov518_color) {
+ /* OV518 needs U and V swapped */
+ i2c_w_mask(ov, 0x15, 0x00, 0x01);
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ /* Set 16-bit input format (UV data are ignored) */
+ reg_w_mask(ov, 0x20, 0x00, 0x08);
+
+ /* Set 8-bit (4:0:0) output format */
+ reg_w_mask(ov, 0x28, 0x00, 0xf0);
+ reg_w_mask(ov, 0x38, 0x00, 0xf0);
+ } else {
+ /* Set 8-bit (YVYU) input format */
+ reg_w_mask(ov, 0x20, 0x08, 0x08);
+
+ /* Set 12-bit (4:2:0) output format */
+ reg_w_mask(ov, 0x28, 0x80, 0xf0);
+ reg_w_mask(ov, 0x38, 0x80, 0xf0);
+ }
+ } else {
+ reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ }
hsegs = width / 16;
vsegs = height / 4;
@@ -3074,7 +3122,7 @@ make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
}
/*
- * For RAW BW (YUV400) images, data shows up in 256 byte segments.
+ * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments.
* The segments represent 4 squares of 8x8 pixels as follows:
*
* 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
@@ -3105,7 +3153,7 @@ yuv400raw_to_yuv400p(struct ov511_frame *frame,
}
/*
- * For YUV4:2:0 images, the data shows up in 384 byte segments.
+ * For YUV 4:2:0 images, the data show up in 384 byte segments.
* The first 64 bytes of each segment are U, the next 64 are V. The U and
* V are arranged as follows:
*
@@ -3124,8 +3172,8 @@ yuv400raw_to_yuv400p(struct ov511_frame *frame,
* ... ... ...
* 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. If the width is not an
+ * Note that the U and V data in one segment represent a 16 x 16 pixel
+ * area, but the Y data represent a 32 x 8 pixel area. If the width is not an
* even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
* next horizontal stripe.
*
@@ -3133,7 +3181,7 @@ yuv400raw_to_yuv400p(struct ov511_frame *frame,
* verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
* this puts the data on the standard output and can be analyzed with the
* parseppm.c utility I wrote. That's a much faster way for figuring out how
- * this data is scrambled.
+ * these data are scrambled.
*/
/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
@@ -4285,7 +4333,6 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
}
file->private_data = NULL;
-
return 0;
}
@@ -4752,7 +4799,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
return rc;
}
-static inline int
+static int
ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos)
{
struct video_device *vdev = file->private_data;
@@ -4882,7 +4929,7 @@ restart:
PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
count, frame->bytes_read);
- /* If all data has been read... */
+ /* If all data have been read... */
if (frame->bytes_read
>= get_frame_length(frame)) {
frame->bytes_read = 0;
@@ -4966,7 +5013,7 @@ static struct video_device vdev_template = {
.fops = &ov511_fops,
};
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+#if defined(CONFIG_VIDEO_PROC_FS)
static int
ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long ularg)
@@ -5453,14 +5500,12 @@ ov6xx0_configure(struct usb_ov511 *ov)
{ OV511_I2C_BUS, 0x4f, 0x04 },
// Do 50-53 have any effect?
// Toggle 0x12[2] off and on here?
- { OV511_DONE_BUS, 0x0, 0x00 },
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
};
- /* This chip is undocumented so many of these are guesses. OK=verified,
- * A=Added since 6620, U=unknown function (not a 6620 reg) */
static struct ov511_regvals aRegvalsNorm6x30[] = {
/*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
- /*00?*/ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x11, 0x00 },
/*OK*/ { OV511_I2C_BUS, 0x03, 0x60 },
/*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
{ OV511_I2C_BUS, 0x07, 0xa8 },
@@ -5468,16 +5513,8 @@ ov6xx0_configure(struct usb_ov511 *ov)
/*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 },
/*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 },
/*A*/ { OV511_I2C_BUS, 0x0e, 0x20 },
-
-// /*24?*/ { OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
-// { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
-
-// /*A*/ { OV511_I2C_BUS, 0x13, 0x21 },
-// /*A*/ { OV511_I2C_BUS, 0x13, 0x25 }, /* Tristate Y and UV busses */
-
// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 },
- /* 0x16: 0x06 helps frame stability with moving objects */
- /*03?*/ { OV511_I2C_BUS, 0x16, 0x06 },
+ { OV511_I2C_BUS, 0x16, 0x03 },
// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
// 21 & 22? The suggested values look wrong. Go with default
/*A*/ { OV511_I2C_BUS, 0x23, 0xc0 },
@@ -5490,49 +5527,34 @@ ov6xx0_configure(struct usb_ov511 *ov)
/*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
-// /*U*/ { OV511_I2C_BUS, 0x2c, 0xa0 },
{ OV511_I2C_BUS, 0x2d, 0x99 },
// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
-// /*U*/ { OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary
-// /*U*/ { OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary
// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 },
// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
- /*OK*/ { OV511_I2C_BUS, 0x3d, 0x80 },
+ { OV511_I2C_BUS, 0x3d, 0x80 },
// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e },
-// /*U*/ { OV511_I2C_BUS, 0x40, 0x00 },
-// /*U*/ { OV511_I2C_BUS, 0x41, 0x00 },
-// /*U*/ { OV511_I2C_BUS, 0x42, 0x80 },
-// /*U*/ { OV511_I2C_BUS, 0x43, 0x3f },
-// /*U*/ { OV511_I2C_BUS, 0x44, 0x80 },
-// /*U*/ { OV511_I2C_BUS, 0x45, 0x20 },
-// /*U*/ { OV511_I2C_BUS, 0x46, 0x20 },
-// /*U*/ { OV511_I2C_BUS, 0x47, 0x80 },
-// /*U*/ { OV511_I2C_BUS, 0x48, 0x7f },
-// /*U*/ { OV511_I2C_BUS, 0x49, 0x00 },
/* These next two registers (0x4a, 0x4b) are undocumented. They
* control the color balance */
// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 },
-// /*U*/ { OV511_I2C_BUS, 0x4c, 0xd0 },
- /*d2?*/ { OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */
+ { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
/*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 },
- /*04?*/ { OV511_I2C_BUS, 0x4f, 0x07 },
-// /*U*/ { OV511_I2C_BUS, 0x50, 0xff },
- /*U*/ { OV511_I2C_BUS, 0x54, 0x23 },
-// /*U*/ { OV511_I2C_BUS, 0x55, 0xff },
-// /*U*/ { OV511_I2C_BUS, 0x56, 0x12 },
- /*U*/ { OV511_I2C_BUS, 0x57, 0x81 },
-// /*U*/ { OV511_I2C_BUS, 0x58, 0x75 },
- /*U*/ { OV511_I2C_BUS, 0x59, 0x01 },
- /*U*/ { OV511_I2C_BUS, 0x5a, 0x2c },
- /*U*/ { OV511_I2C_BUS, 0x5b, 0x0f },
-// /*U*/ { OV511_I2C_BUS, 0x5c, 0x10 },
- { OV511_DONE_BUS, 0x0, 0x00 },
+
+ /* UV average mode, color killer: strongest */
+ { OV511_I2C_BUS, 0x4f, 0x07 },
+
+ { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */
+ { OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */
+ { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */
+ { OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */
+ { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */
+// { OV511_I2C_BUS, 0x5c, 0x10 },
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
};
PDEBUG(4, "starting sensor configuration");
@@ -5553,16 +5575,19 @@ ov6xx0_configure(struct usb_ov511 *ov)
return -1;
}
- if ((rc & 3) == 0)
+ if ((rc & 3) == 0) {
ov->sensor = SEN_OV6630;
- else if ((rc & 3) == 1)
+ info("Sensor is an OV6630");
+ } else if ((rc & 3) == 1) {
ov->sensor = SEN_OV6620;
- else if ((rc & 3) == 2)
+ info("Sensor is an OV6620");
+ } else if ((rc & 3) == 2) {
ov->sensor = SEN_OV6630;
- else if ((rc & 3) == 3)
+ info("Sensor is an OV6630AE");
+ } else if ((rc & 3) == 3) {
ov->sensor = SEN_OV6630;
-
- info("Sensor is an %s", symbolic(senlist, ov->sensor));
+ info("Sensor is an OV6630AF");
+ }
/* Set sensor-specific vars */
ov->maxwidth = 352;
@@ -5922,7 +5947,7 @@ ov518_configure(struct usb_ov511 *ov)
{ OV511_REG_BUS, 0x5d, 0x03 },
{ OV511_REG_BUS, 0x24, 0x9f },
{ OV511_REG_BUS, 0x25, 0x90 },
- { OV511_REG_BUS, 0x20, 0x00 }, /* Was 0x08 */
+ { OV511_REG_BUS, 0x20, 0x00 },
{ OV511_REG_BUS, 0x51, 0x04 },
{ OV511_REG_BUS, 0x71, 0x19 },
{ OV511_DONE_BUS, 0x0, 0x00 },
@@ -5935,7 +5960,7 @@ ov518_configure(struct usb_ov511 *ov)
{ OV511_REG_BUS, 0x5d, 0x03 },
{ OV511_REG_BUS, 0x24, 0x9f },
{ OV511_REG_BUS, 0x25, 0x90 },
- { OV511_REG_BUS, 0x20, 0x60 }, /* Was 0x08 */
+ { OV511_REG_BUS, 0x20, 0x60 },
{ OV511_REG_BUS, 0x51, 0x02 },
{ OV511_REG_BUS, 0x71, 0x19 },
{ OV511_REG_BUS, 0x40, 0xff },
@@ -6151,6 +6176,11 @@ ov51x_probe(struct usb_interface *intf,
ov->buf_state = BUF_NOT_ALLOCATED;
+ if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) {
+ err("usb_make_path error");
+ goto error_dealloc;
+ }
+
/* Allocate control transfer buffer. */
/* Must be kmalloc()'ed, for DMA compatibility */
ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL);
@@ -6212,20 +6242,16 @@ ov51x_probe(struct usb_interface *intf,
goto error;
}
- info("Device registered on minor %d", ov->vdev.minor);
+ info("Device at %s registered to minor %d", ov->usb_path,
+ ov->vdev.minor);
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
create_proc_ov511_cam(ov);
-#endif
dev_set_drvdata (&intf->dev, ov);
- return 0;
+ return 0;
error:
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
- /* Safe to call even if entry doesn't exist */
destroy_proc_ov511_cam(ov);
-#endif
if (ov->cbuf) {
down(&ov->cbuf_lock);
@@ -6274,12 +6300,9 @@ ov51x_disconnect(struct usb_interface *intf)
wake_up_interruptible(&ov->wq);
ov->streaming = 0;
-
ov51x_unlink_isoc(ov);
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
destroy_proc_ov511_cam(ov);
-#endif
ov->dev = NULL;
@@ -6396,9 +6419,7 @@ ov511_deregister_decomp_module(int ov518, int mmx)
static int __init
usb_ov511_init(void)
{
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
proc_ov511_create();
-#endif
if (usb_register(&ov511_driver) < 0)
return -1;
@@ -6414,9 +6435,7 @@ usb_ov511_exit(void)
usb_deregister(&ov511_driver);
info("driver deregistered");
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
proc_ov511_destroy();
-#endif
}
module_init(usb_ov511_init);
diff --git a/drivers/usb/media/ov511.h b/drivers/usb/media/ov511.h
index 9fa16de5bb31..347015b72210 100644
--- a/drivers/usb/media/ov511.h
+++ b/drivers/usb/media/ov511.h
@@ -253,6 +253,9 @@
/* Control transfers use up to 4 bytes */
#define OV511_CBUF_SIZE 4
+/* Size of usb_make_path() buffer */
+#define OV511_USB_PATH_LEN 64
+
/* Bridge types */
enum {
BRG_UNKNOWN,
@@ -450,6 +453,7 @@ struct usb_ov511 {
int customid;
char *desc;
unsigned char iface;
+ char usb_path[OV511_USB_PATH_LEN];
/* Determined by sensor type */
int maxwidth;
diff --git a/drivers/usb/media/pwc-ctrl.c b/drivers/usb/media/pwc-ctrl.c
index 71337853115d..6244117a470f 100644
--- a/drivers/usb/media/pwc-ctrl.c
+++ b/drivers/usb/media/pwc-ctrl.c
@@ -256,8 +256,10 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra
memcpy(buf, pEntry->mode, 3);
ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
- if (ret < 0)
+ if (ret < 0) {
+ Debug("Failed to send video command... %d\n", ret);
return ret;
+ }
if (pEntry->compressed && pdev->decompressor != NULL)
pdev->decompressor->init(pdev->release, buf, pdev->decompress_data);
@@ -1103,12 +1105,7 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
buf[0] = on_value;
buf[1] = off_value;
- return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
- SET_STATUS_CTL,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- LED_FORMATTER,
- pdev->vcinterface,
- &buf, 2, HZ / 2);
+ return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2);
}
int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
@@ -1122,13 +1119,7 @@ int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
return 0;
}
- ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
- GET_STATUS_CTL,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- LED_FORMATTER,
- pdev->vcinterface,
- &buf, 2, HZ / 2);
-
+ ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2);
if (ret < 0)
return ret;
*on_value = buf[0] * 100;
@@ -1279,7 +1270,6 @@ static inline int pwc_get_dynamic_noise(struct pwc_device *pdev)
ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
if (ret < 0)
return ret;
-//Debug("pwc_get_dynamic_noise = %d\n", buf);
return buf;
}
@@ -1363,12 +1353,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCPROBE:
{
- struct pwc_probe probe;
+ struct pwc_probe *probe = arg;
- strcpy(probe.name, pdev->vdev->name);
- probe.type = pdev->type;
- if (copy_to_user(arg, &probe, sizeof(probe)))
- ret = -EFAULT;
+ strcpy(probe->name, pdev->vdev->name);
+ probe->type = pdev->type;
break;
}
diff --git a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c
index f630ff649fd4..2ceefe446df3 100644
--- a/drivers/usb/media/pwc-if.c
+++ b/drivers/usb/media/pwc-if.c
@@ -1,6 +1,6 @@
/* Linux driver for Philips webcam
USB and Video4Linux interface part.
- (C) 1999-2001 Nemosoft Unv.
+ (C) 1999-2002 Nemosoft Unv.
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
@@ -42,32 +42,31 @@
- Alistar Moire: QuickCam 3000 Pro device/product ID
- Tony Hoyle: Creative Labs Webcam 5 device/product ID
- Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
- - Jk Fang: SOTEC device/product ID
+ - Jk Fang: SOTEC Afina Eye ID
+ - Xavier Roche: QuickCam Pro 4000 ID
+ - Jens Knudsen: QuickCam Zoom ID
+ - J. Debert: QuickCam for Notebooks ID
*/
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/wrapper.h>
-#include <linux/mm.h>
#include <asm/io.h>
#include "pwc.h"
#include "pwc-ioctl.h"
#include "pwc-uncompress.h"
-#if !defined(MAP_NR)
-#define MAP_NR(a) virt_to_page(a)
-#endif
-
/* Function prototypes and driver templates */
/* hotplug device table support */
static struct usb_device_id pwc_device_table [] = {
- { USB_DEVICE(0x0471, 0x0302) },
+ { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
{ USB_DEVICE(0x0471, 0x0303) },
{ USB_DEVICE(0x0471, 0x0304) },
{ USB_DEVICE(0x0471, 0x0307) },
@@ -76,12 +75,17 @@ static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0310) },
{ USB_DEVICE(0x0471, 0x0311) },
{ USB_DEVICE(0x0471, 0x0312) },
- { USB_DEVICE(0x069A, 0x0001) },
- { USB_DEVICE(0x046D, 0x08b0) },
- { USB_DEVICE(0x055D, 0x9000) },
+ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+ { USB_DEVICE(0x046D, 0x08b0) }, /* Logitech QuickCam Pro 3000 */
+ { USB_DEVICE(0x046D, 0x08b1) }, /* Logitech QuickCam for Notebooks */
+ { USB_DEVICE(0x046D, 0x08b2) }, /* Logitech QuickCam Pro 4000 */
+ { USB_DEVICE(0x046D, 0x08b3) }, /* Logitech QuickCam Zoom */
+ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
{ USB_DEVICE(0x055D, 0x9001) },
- { USB_DEVICE(0x041E, 0x400C) },
- { USB_DEVICE(0x04CC, 0x8116) },
+ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+ { USB_DEVICE(0x0d81, 0x1900) },
{ }
};
MODULE_DEVICE_TABLE(usb, pwc_device_table);
@@ -106,7 +110,7 @@ static int default_fbufs = 3; /* Default number of frame buffers */
static int default_mbufs = 2; /* Default number of mmap() buffers */
int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
static int power_save = 0;
-static int led_on = 1, led_off = 0; /* defaults to LED that is on while in use */
+static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
static struct {
int type;
@@ -167,7 +171,7 @@ static struct video_device pwc_template = {
succeeded. The pwc_device struct links back to both structures.
When a device is unplugged while in use it will be removed from the
- list of known USB devices; I also de-register as a V4L device, but
+ list of known USB devices; I also de-register it as a V4L device, but
unfortunately I can't free the memory since the struct is still in use
by the file descriptor. This free-ing is then deferend until the first
opportunity. Crude, but it works.
@@ -240,7 +244,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev)
int i;
void *kbuf;
- Trace(TRACE_MEMORY, "Entering allocate_buffers(%p).\n", pdev);
+ Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
if (pdev == NULL)
return -ENXIO;
@@ -315,7 +319,9 @@ static int pwc_allocate_buffers(struct pwc_device *pdev)
for (; i < MAX_IMAGES; i++)
pdev->image_ptr[i] = NULL;
- Trace(TRACE_MEMORY, "Leaving pwc_allocate_buffers().\n");
+ kbuf = NULL;
+
+ Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
return 0;
}
@@ -369,6 +375,7 @@ static void pwc_free_buffers(struct pwc_device *pdev)
rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
}
pdev->image_data = NULL;
+
Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
}
@@ -570,12 +577,10 @@ static inline void pwc_next_image(struct pwc_device *pdev)
pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
}
-/* 2001-10-14: The YUV420 is still there, but you can only set it from within
- a program (YUV420P being the default) */
+/* 2002-10-11: YUV420P is the only palette remaining. */
static int pwc_set_palette(struct pwc_device *pdev, int pal)
{
- if ( pal == VIDEO_PALETTE_YUV420
- || pal == VIDEO_PALETTE_YUV420P
+ if ( pal == VIDEO_PALETTE_YUV420P
#if PWC_DEBUG
|| pal == VIDEO_PALETTE_RAW
#endif
@@ -613,7 +618,7 @@ static void pwc_isoc_handler(struct urb *urb)
}
#endif
if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
- Trace(TRACE_OPEN, "pwc_isoc_handler(): URB unlinked.\n");
+ Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return;
}
if (urb->status != -EINPROGRESS && urb->status != 0) {
@@ -686,9 +691,22 @@ static void pwc_isoc_handler(struct urb *urb)
#if PWC_DEBUG
Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
#endif
- pdev->drop_frames = 2;
+ pdev->drop_frames += 2;
pdev->vframes_error++;
}
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ if (ptr[0] & 0x01)
+ Info("Snapshot button pressed.\n");
+ else
+ Info("Snapshot button released.\n");
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+ if (ptr[0] & 0x02)
+ Info("Image is mirrored.\n");
+ else
+ Info("Image is normal.\n");
+ }
+ pdev->vmirror = ptr[0] & 0x03;
/* Sometimes the trailer of the 730 is still sent as a 4 byte packet
after a short frame; this condition is filtered out specifically. A 4 byte
frame doesn't make sense anyway.
@@ -705,7 +723,7 @@ static void pwc_isoc_handler(struct urb *urb)
/* In case we were instructed to drop the frame, do so silently.
The buffer pointers are not updated either (but the counters are reset below).
*/
- if (pdev->drop_frames)
+ if (pdev->drop_frames > 0)
pdev->drop_frames--;
else {
/* Check for underflow first */
@@ -741,17 +759,23 @@ static void pwc_isoc_handler(struct urb *urb)
} /* .. flen < last_packet_size */
pdev->vlast_packet_size = flen;
} /* ..status == 0 */
-#ifdef PWC_DEBUG
+#if PWC_DEBUG
/* This is normally not interesting to the user, unless you are really debugging something */
- else
- Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+ else {
+ static int iso_error = 0;
+ iso_error++;
+ if (iso_error < 20)
+ Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+ }
#endif
}
if (awake)
wake_up_interruptible(&pdev->frameq);
urb->dev = pdev->udev;
- usb_submit_urb(urb, GFP_ATOMIC);
+ i = usb_submit_urb(urb, GFP_ATOMIC);
+ if (i != 0)
+ Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
@@ -762,7 +786,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
int i, j, ret;
struct usb_host_interface *idesc;
- int cur_alt;
if (pdev == NULL)
return -EFAULT;
@@ -770,12 +793,11 @@ static int pwc_isoc_init(struct pwc_device *pdev)
return 0;
pdev->vsync = 0;
udev = pdev->udev;
-
+
/* Get the current alternate interface, adjust packet size */
if (!udev->actconfig)
return -EFAULT;
- cur_alt = udev->actconfig->interface[0].act_altsetting;
- idesc = &udev->actconfig->interface[0].altsetting[cur_alt];
+ idesc = &udev->actconfig->interface[0].altsetting[pdev->valternate];
if (!idesc)
return -EFAULT;
@@ -792,7 +814,13 @@ static int pwc_isoc_init(struct pwc_device *pdev)
return -ENFILE; /* Odd error, that should be noticable */
}
+ /* Set alternate interface */
ret = 0;
+ Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
+ ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+ if (ret < 0)
+ return ret;
+
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
@@ -801,6 +829,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
break;
}
pdev->sbuf[i].urb = urb;
+ Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
}
if (ret) {
/* De-allocate in reverse order */
@@ -812,8 +841,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
}
return ret;
}
-
-
+
/* init URB structure */
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = pdev->sbuf[i].urb;
@@ -829,7 +857,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
urb->start_frame = 0;
urb->number_of_packets = ISO_FRAMES_PER_DESC;
for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
- urb->iso_frame_desc[j].offset = j * pdev->vmax_packet_size;
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
}
}
@@ -840,11 +868,12 @@ static int pwc_isoc_init(struct pwc_device *pdev)
if (ret)
Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
else
- Trace(TRACE_OPEN, "pwc_isoc_init(): URB submitted.\n");
+ Trace(TRACE_OPEN, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
}
- /* data should stream in now */
+ /* All is done... */
pdev->iso_init = 1;
+ Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
return 0;
}
@@ -852,21 +881,34 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
int i;
+ Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
if (pdev == NULL)
return;
- if (!pdev->iso_init)
- return;
+
+ /* Unlinking ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ struct urb *urb;
+
+ urb = pdev->sbuf[i].urb;
+ if (urb != 0) {
+ if (pdev->iso_init) {
+ Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
+ usb_unlink_urb(urb);
+ }
+ Trace(TRACE_MEMORY, "Freeing URB\n");
+ usb_free_urb(urb);
+ pdev->sbuf[i].urb = NULL;
+ }
+ }
+
/* Stop camera, but only if we are sure the camera is still there */
- if (!pdev->unplugged)
+ if (!pdev->unplugged) {
+ Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
usb_set_interface(pdev->udev, 0, 0);
- /* Unlinking ISOC buffers one by one */
- for (i = MAX_ISO_BUFS - 1; i >= 0; i--) {
- //pdev->sbuf[i].urb->next = NULL;
- usb_unlink_urb(pdev->sbuf[i].urb);
- usb_free_urb(pdev->sbuf[i].urb);
- pdev->sbuf[i].urb = NULL;
}
+
pdev->iso_init = 0;
+ Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
}
int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
@@ -881,11 +923,10 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
if (ret) /* That failed... restore old mode (we know that worked) */
ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
- else /* Set (new) alternate interface */
- ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
if (!ret)
- ret = pwc_isoc_init(pdev);
- pdev->drop_frames = 1; /* try to avoid garbage during switch */
+ if (pwc_isoc_init(pdev) < 0)
+ Info("Failed to restart ISOC transfer in pwc_try_video_mode.\n");
+ pdev->drop_frames++; /* try to avoid garbage during switch */
return ret;
}
@@ -921,7 +962,7 @@ static int pwc_video_open(struct inode *inode, struct file *file)
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev;
- Trace(TRACE_OPEN, "video_open called(0x%p).\n", vdev);
+ Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev == NULL)
@@ -932,10 +973,30 @@ static int pwc_video_open(struct inode *inode, struct file *file)
down(&pdev->modlock);
if (!pdev->usb_init) {
Trace(TRACE_OPEN, "Doing first time initialization.\n");
- /* Reset camera */
- if (usb_set_interface(pdev->udev, 0, 0))
- Info("Failed to set alternate interface to 0.\n");
pdev->usb_init = 1;
+
+ if (pwc_trace & TRACE_OPEN) {
+ /* Query CMOS sensor type */
+ const char *sensor_type = NULL;
+
+ i = pwc_get_cmos_sensor(pdev);
+ switch(i) {
+ case -1: /* Unknown, show nothing */; break;
+ case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
+ case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
+ case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break;
+ case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break;
+ case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break;
+ case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
+ case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
+ case 0x40: sensor_type = "UPA 1021 sensor"; break;
+ case 0x100: sensor_type = "VGA sensor"; break;
+ case 0x101: sensor_type = "PAL MR sensor"; break;
+ default: sensor_type = "unknown type of sensor"; break;
+ }
+ if (sensor_type != NULL)
+ Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+ }
}
/* Turn on camera */
@@ -1000,12 +1061,6 @@ static int pwc_video_open(struct inode *inode, struct file *file)
return i;
}
- i = usb_set_interface(pdev->udev, 0, pdev->valternate);
- if (i) {
- Trace(TRACE_OPEN, "Failed to set alternate interface = %d.\n", i);
- up(&pdev->modlock);
- return -EINVAL;
- }
i = pwc_isoc_init(pdev);
if (i) {
Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
@@ -1023,7 +1078,7 @@ static int pwc_video_open(struct inode *inode, struct file *file)
if (pdev->decompressor != NULL)
pdev->decompressor->lock();
up(&pdev->modlock);
- Trace(TRACE_OPEN, "video_open() returning 0.\n");
+ Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
return 0;
}
@@ -1034,15 +1089,12 @@ static int pwc_video_close(struct inode *inode, struct file *file)
struct pwc_device *pdev;
int i;
- Trace(TRACE_OPEN, "video_close called(0x%p).\n", vdev);
+ Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev->vopen == 0)
Info("video_close() called on closed device?\n");
- /* Free isoc URBs */
- pwc_isoc_cleanup(pdev);
-
/* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot
programs)
@@ -1050,15 +1102,14 @@ static int pwc_video_close(struct inode *inode, struct file *file)
if (pdev->vframe_count > 20)
Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
- if (!pdev->unplugged) {
- /* Normal close: stop isochronuous and interrupt endpoint */
- Trace(TRACE_OPEN, "Normal close(): setting interface to 0.\n");
- usb_set_interface(pdev->udev, 0, 0);
+ /* Free isoc URBs, stop camera */
+ pwc_isoc_cleanup(pdev);
+ if (!pdev->unplugged) {
/* Turn LEDs off */
if (pwc_set_leds(pdev, 0, 0) < 0)
- Info("Failed to set LED on/off time..\n");
- /* Power down camere to save energy */
+ Info("Failed to set LED on/off time.\n");
+ /* Power down camera to save energy */
if (power_save) {
i = pwc_camera_power(pdev, 0);
if (i < 0)
@@ -1077,6 +1128,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
if (pdev->unplugged)
wake_up(&pdev->remove_ok);
file->private_data = NULL;
+ Trace(TRACE_OPEN, "<< video_close()\n");
return 0;
}
@@ -1514,7 +1566,6 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
pdev = vdev->priv;
- /* FIXME - audit mmap during a read */
pos = (unsigned long)pdev->image_data;
while (size > 0) {
page = kvirt_to_pa(pos);
@@ -1547,14 +1598,14 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
int vendor_id, product_id, type_id;
int i, hint;
int video_nr = -1; /* default: use next available device */
- char serial_number[30];
+ char serial_number[30], *name;
free_mem_leak();
/* Check if we can handle this device */
- Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
- udev->descriptor.idVendor, udev->descriptor.idProduct,
- intf->altsetting->desc.bInterfaceNumber);
+ Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
+ udev->descriptor.idVendor, udev->descriptor.idProduct,
+ intf->altsetting->desc.bInterfaceNumber);
/* the interfaces are probed one by one. We are only interested in the
video interface (0) now.
@@ -1570,38 +1621,47 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
switch (product_id) {
case 0x0302:
Info("Philips PCA645VC USB webcam detected.\n");
+ name = "Philips 645 webcam";
type_id = 645;
break;
case 0x0303:
Info("Philips PCA646VC USB webcam detected.\n");
+ name = "Philips 646 webcam";
type_id = 646;
break;
case 0x0304:
Info("Askey VC010 type 2 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
type_id = 646;
break;
case 0x0307:
Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+ name = "Philips 675 webcam";
type_id = 675;
break;
case 0x0308:
Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+ name = "Philips 680 webcam";
type_id = 680;
break;
case 0x030C:
Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+ name = "Philips 690 webcam";
type_id = 690;
break;
case 0x0310:
Info("Philips PCVC730K (ToUCam Fun) USB webcam detected.\n");
+ name = "Philips 730 webcam";
type_id = 730;
break;
case 0x0311:
Info("Philips PCVC740K (ToUCam Pro) USB webcam detected.\n");
+ name = "Philips 740 webcam";
type_id = 740;
break;
case 0x0312:
Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+ name = "Philips 750 webcam";
type_id = 750;
break;
default:
@@ -1613,6 +1673,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
switch(product_id) {
case 0x0001:
Info("Askey VC010 type 1 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
type_id = 645;
break;
default:
@@ -1623,9 +1684,25 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x046d) {
switch(product_id) {
case 0x08b0:
- Info("Logitech QuickCam 3000 Pro detected.\n");
+ Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 3000";
type_id = 730;
- break;
+ break;
+ case 0x08b1:
+ Info("Logitech QuickCam for Noteboos USB webcam detected.\n");
+ name = "Logitech QuickCam Notebook";
+ type_id = 740; /* ?? unknown sensor */
+ break;
+ case 0x08b2:
+ Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 4000";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b3:
+ Info("Logitech QuickCam Zoom USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* ?? unknown sensor */
+ break;
default:
return -ENODEV;
break;
@@ -1639,10 +1716,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
switch(product_id) {
case 0x9000:
Info("Samsung MPC-C10 USB webcam detected.\n");
+ name = "Samsung MPC-C10";
type_id = 675;
break;
case 0x9001:
Info("Samsung MPC-C30 USB webcam detected.\n");
+ name = "Samsung MPC-C30";
type_id = 675;
break;
default:
@@ -1654,6 +1733,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
switch(product_id) {
case 0x400c:
Info("Creative Labs Webcam 5 detected.\n");
+ name = "Creative Labs Webcam 5";
type_id = 730;
break;
default:
@@ -1664,7 +1744,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x04cc) {
switch(product_id) {
case 0x8116:
- Info("SOTEC CMS-001 USB webcam detected.\n");
+ Info("Sotec Afina Eye USB webcam detected.\n");
+ name = "Sotec Afina Eye";
type_id = 730;
break;
default:
@@ -1672,7 +1753,25 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
break;
}
}
- else return -ENODEV; /* Not Philips, Askey, Logitech, Samsung, Creative or SOTEC, for sure. */
+ else if (vendor_id == 0x0d81) {
+ switch(product_id) {
+ case 0x1900:
+ Info("Visionite VCS-UC300 USB webcam detected.\n");
+ name = "Visionite VCS-UC300";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x1910:
+ Info("Visionite VCS-UM100 USB webcam detected.\n");
+ name = "Visionite VCS-UM100";
+ type_id = 730; /* CMOS sensor */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else
+ return -ENODEV; /* Not any of the know types; but the list keeps growing. */
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
@@ -1706,7 +1805,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
return -ENOMEM;
}
memcpy(vdev, &pwc_template, sizeof(pwc_template));
- sprintf(vdev->name, "Philips %d webcam", pdev->type);
+ strcpy(vdev->name, name);
SET_MODULE_OWNER(vdev);
pdev->vdev = vdev;
vdev->priv = pdev;
@@ -1714,7 +1813,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->release = udev->descriptor.bcdDevice;
Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
-
/* Now search device_hint[] table for a match, so we can hint a node number. */
for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
@@ -1761,40 +1859,41 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
dev_set_drvdata (&intf->dev, NULL);
if (pdev == NULL) {
Err("pwc_disconnect() Called without private pointer.\n");
- goto out_err;
+ goto disconnect_out;
}
if (pdev->udev == NULL) {
Err("pwc_disconnect() already called for %p\n", pdev);
- goto out_err;
+ goto disconnect_out;
}
if (pdev->udev != interface_to_usbdev(intf)) {
Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
- goto out_err;
+ goto disconnect_out;
}
#ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) {
Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
- goto out_err;
+ goto disconnect_out;
}
-#endif
-
+#endif
+
pdev->unplugged = 1;
if (pdev->vdev != NULL) {
- video_unregister_device(pdev->vdev);
+ Trace(TRACE_PROBE, "Unregistering video device.\n");
+ video_unregister_device(pdev->vdev);
if (pdev->vopen) {
Info("Disconnected while device/video is open!\n");
-
+
/* Wake up any processes that might be waiting for
a frame, let them return an error condition
*/
wake_up(&pdev->frameq);
-
+
/* Wait until we get a 'go' from _close(). This used
to have a gigantic race condition, since we kfree()
- stuff here, but we have to wait until close()
- is finished.
+ stuff here, but we have to wait until close()
+ is finished.
*/
-
+
Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
add_wait_queue(&pdev->remove_ok, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1808,26 +1907,25 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
}
else {
/* Normal disconnect; remove from available devices */
- Trace(TRACE_PROBE, "Unregistering video device normally.\n");
kfree(pdev->vdev);
pdev->vdev = NULL;
}
}
+disconnect_out:
/* search device_hint[] table if we occupy a slot, by any chance */
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
if (device_hint[hint].pdev == pdev)
device_hint[hint].pdev = NULL;
pdev->udev = NULL;
-out_err:
unlock_kernel();
kfree(pdev);
}
/* *grunt* We have to do atoi ourselves :-( */
-static int pwc_atoi(char *s)
+static int pwc_atoi(const char *s)
{
int k = 0;
@@ -1872,7 +1970,7 @@ MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
MODULE_PARM(dev_hint, "0-10s");
MODULE_PARM_DESC(dev_hint, "Device node hints");
-MODULE_DESCRIPTION("Philips USB webcam driver");
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
MODULE_AUTHOR("Nemosoft Unv. <nemosoft@smcc.demon.nl>");
MODULE_LICENSE("GPL");
@@ -1882,11 +1980,12 @@ static int __init usb_pwc_init(void)
char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n");
- Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro, Samsung MPC-C10 and MPC-C30, the Creative WebCam 5 and the SOTEC CMS-001.\n");
+ Info("Also supports the Askey VC010, various Logitech QuickCams, Samsung MPC-C10 and MPC-C30,\n");
+ Info("the Creative WebCam 5, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
if (fps) {
- if (fps < 5 || fps > 30) {
- Err("Framerate out of bounds (5-30).\n");
+ if (fps < 4 || fps > 30) {
+ Err("Framerate out of bounds (4-30).\n");
return -EINVAL;
}
default_fps = fps;
@@ -1938,9 +2037,9 @@ static int __init usb_pwc_init(void)
if (power_save)
Info("Enabling power save on open/close.\n");
if (leds[0] >= 0)
- led_on = leds[0] / 100;
+ led_on = leds[0];
if (leds[1] >= 0)
- led_off = leds[1] / 100;
+ led_off = leds[1];
/* Big device node whoopla. Basicly, it allows you to assign a
device node (/dev/videoX) to a camera, based on its type
@@ -1999,7 +2098,7 @@ static int __init usb_pwc_init(void)
device_hint[i].serial_number[k] = '\0';
}
}
-#ifdef PWC_DEBUG
+#if PWC_DEBUG
Debug("device_hint[%d]:\n", i);
Debug(" type : %d\n", device_hint[i].type);
Debug(" serial# : %s\n", device_hint[i].serial_number);
diff --git a/drivers/usb/media/pwc-misc.c b/drivers/usb/media/pwc-misc.c
index 254da4186dd5..ecb57de7f586 100644
--- a/drivers/usb/media/pwc-misc.c
+++ b/drivers/usb/media/pwc-misc.c
@@ -1,6 +1,6 @@
/* Linux driver for Philips webcam
Various miscellaneous functions and tables.
- (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+ (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl)
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
diff --git a/drivers/usb/media/pwc-uncompress.c b/drivers/usb/media/pwc-uncompress.c
index 69f3688359ca..b34074e5d40d 100644
--- a/drivers/usb/media/pwc-uncompress.c
+++ b/drivers/usb/media/pwc-uncompress.c
@@ -1,6 +1,6 @@
/* Linux driver for Philips webcam
Decompression frontend.
- (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+ (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl)
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
@@ -77,7 +77,7 @@ int pwc_decompress(struct pwc_device *pdev)
{
struct pwc_frame_buf *fbuf;
int n, line, col, stride;
- void *yuv, *image, *dst;
+ void *yuv, *image;
u16 *src;
u16 *dsty, *dstu, *dstv;
@@ -114,19 +114,6 @@ int pwc_decompress(struct pwc_device *pdev)
to get the desired output format/size.
*/
switch (pdev->vpalette) {
- case VIDEO_PALETTE_YUV420:
- /* Calculate byte offsets per line in image & view */
- n = (pdev->image.x * 3) / 2;
- col = (pdev->view.x * 3) / 2;
- /* Offset into image */
- dst = image + (pdev->view.x * pdev->offset.y + pdev->offset.x) * 3 / 2;
- for (line = 0; line < pdev->image.y; line++) {
- memcpy(dst, yuv, n);
- yuv += n;
- dst += col;
- }
- break;
-
case VIDEO_PALETTE_YUV420P:
/*
* We do some byte shuffling here to go from the
@@ -163,17 +150,20 @@ int pwc_decompress(struct pwc_device *pdev)
dstu += (stride >> 1);
}
break;
+ default:
+ Err("Unsupported palette!");
+ break;
}
}
else {
/* Compressed; the decompressor routines will write the data
- in interlaced or planar format immediately.
+ in planar format immediately.
*/
if (pdev->decompressor)
pdev->decompressor->decompress(
&pdev->image, &pdev->view, &pdev->offset,
- yuv, image,
- pdev->vpalette == VIDEO_PALETTE_YUV420P ? 1 : 0,
+ yuv, image,
+ 1,
pdev->decompress_data, pdev->vbandlength);
else
return -ENXIO; /* No such device or address: missing decompressor */
diff --git a/drivers/usb/media/pwc-uncompress.h b/drivers/usb/media/pwc-uncompress.h
index d6220cf82863..58c32aab0792 100644
--- a/drivers/usb/media/pwc-uncompress.h
+++ b/drivers/usb/media/pwc-uncompress.h
@@ -1,4 +1,4 @@
-/* (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+/* (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl)
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
diff --git a/drivers/usb/media/pwc.h b/drivers/usb/media/pwc.h
index 63856cb7b8a0..913f2d4aa193 100644
--- a/drivers/usb/media/pwc.h
+++ b/drivers/usb/media/pwc.h
@@ -60,8 +60,8 @@
/* Version block */
#define PWC_MAJOR 8
-#define PWC_MINOR 7
-#define PWC_VERSION "8.7"
+#define PWC_MINOR 9
+#define PWC_VERSION "8.9"
#define PWC_NAME "pwc"
/* Turn certain features on/off */
@@ -130,7 +130,7 @@ struct pwc_device
int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */
int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
- int vpalette; /* YUV, RGB24, RGB32, etc */
+ int vpalette; /* YUV */
int vframe_count; /* received frames */
int vframes_dumped; /* counter for dumped frames */
int vframes_error; /* frames received in error */
@@ -140,7 +140,8 @@ struct pwc_device
int vbandlength; /* compressed band length; 0 is uncompressed */
char vsnapshot; /* snapshot mode */
char vsync; /* used by isoc handler */
-
+ char vmirror; /* for ToUCaM series */
+
/* The image acquisition requires 3 to 4 steps:
1. data is gathered in short packets from the USB controller
2. data is synchronized and packed into a frame buffer
diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c
index 1f7138f93133..d7ba9d6440bf 100644
--- a/drivers/usb/media/vicam.c
+++ b/drivers/usb/media/vicam.c
@@ -1,7 +1,7 @@
/*
* USB ViCam WebCam driver
* Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
- * John Tyner (fill in email address)
+ * John Tyner (jtyner@cs.ucr.edu)
*
* Supports 3COM HomeConnect PC Digital WebCam
*
@@ -29,7 +29,7 @@
* Andy Armstrong who reverse engineered the color encoding and
* Pavel Machek and Chris Cheney who worked on reverse engineering the
* camera controls and wrote the first generation driver.
- * */
+ */
#include <linux/kernel.h>
#include <linux/wrapper.h>
@@ -51,19 +51,25 @@
#define DBG(fmn,args...) do {} while(0)
#endif
-/* Version Information */
-#define DRIVER_VERSION "v1.0"
-#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org"
-#define DRIVER_DESC "ViCam WebCam Driver"
+#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org"
+#define DRIVER_DESC "ViCam WebCam Driver"
/* Define these values to match your device */
#define USB_VICAM_VENDOR_ID 0x04c1
#define USB_VICAM_PRODUCT_ID 0x009d
-#define VICAM_BYTES_PER_PIXEL 3
-#define VICAM_MAX_READ_SIZE (512*242+128)
-#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240)
-#define VICAM_FRAMES 2
+#define VICAM_BYTES_PER_PIXEL 3
+#define VICAM_MAX_READ_SIZE (512*242+128)
+#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240)
+#define VICAM_FRAMES 2
+
+#define VICAM_HEADER_SIZE 64
+
+#define clamp( x, l, h ) max_t( __typeof__( x ), \
+ ( l ), \
+ min_t( __typeof__( x ), \
+ ( h ), \
+ ( x ) ) )
/* Not sure what all the bytes in these char
* arrays do, but they're necessary to make
@@ -408,7 +414,8 @@ struct vicam_camera {
struct video_device vdev; // v4l video device
struct usb_device *udev; // usb device
- struct semaphore busy_lock; // guard against SMP multithreading
+ /* guard against simultaneous accesses to the camera */
+ struct semaphore cam_lock;
int is_initialized;
u8 open_count;
@@ -424,17 +431,21 @@ struct vicam_camera {
static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id);
static void vicam_disconnect(struct usb_interface *intf);
static void read_frame(struct vicam_camera *cam, int framenum);
-
-static int
-send_control_msg(struct usb_device *udev, u8 request, u16 value, u16 index,
- unsigned char *cp, u16 size)
+static void vicam_decode_color(const u8 *, u8 *);
+
+static int __send_control_msg(struct vicam_camera *cam,
+ u8 request,
+ u16 value,
+ u16 index,
+ unsigned char *cp,
+ u16 size)
{
int status;
/* cp must be memory that has been allocated by kmalloc */
- status = usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
+ status = usb_control_msg(cam->udev,
+ usb_sndctrlpipe(cam->udev, 0),
request,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, value, index,
@@ -450,6 +461,22 @@ send_control_msg(struct usb_device *udev, u8 request, u16 value, u16 index,
return status;
}
+static int send_control_msg(struct vicam_camera *cam,
+ u8 request,
+ u16 value,
+ u16 index,
+ unsigned char *cp,
+ u16 size)
+{
+ int status = -ENODEV;
+ down(&cam->cam_lock);
+ if (cam->udev) {
+ status = __send_control_msg(cam, request, value,
+ index, cp, size);
+ }
+ up(&cam->cam_lock);
+ return status;
+}
static int
initialize_camera(struct vicam_camera *cam)
{
@@ -465,14 +492,13 @@ initialize_camera(struct vicam_camera *cam)
{ .data = setup3, .size = sizeof(setup3) },
{ .data = NULL, .size = 0 }
};
-
- struct usb_device *udev = cam->udev;
+
int err, i;
for (i = 0, err = 0; firmware[i].data && !err; i++) {
memcpy(cam->cntrlbuf, firmware[i].data, firmware[i].size);
- err = send_control_msg(udev, 0xff, 0, 0,
+ err = send_control_msg(cam, 0xff, 0, 0,
cam->cntrlbuf, firmware[i].size);
}
@@ -484,11 +510,11 @@ set_camera_power(struct vicam_camera *cam, int state)
{
int status;
- if ((status = send_control_msg(cam->udev, 0x50, state, 0, NULL, 0)) < 0)
+ if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0)
return status;
if (state) {
- send_control_msg(cam->udev, 0x55, 1, 0, NULL, 0);
+ send_control_msg(cam, 0x55, 1, 0, NULL, 0);
}
return 0;
@@ -504,10 +530,6 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign
if (!cam)
return -ENODEV;
- /* make this _really_ smp-safe */
- if (down_interruptible(&cam->busy_lock))
- return -EINTR;
-
switch (ioctlnr) {
/* query capabilites */
case VIDIOCGCAP:
@@ -694,6 +716,9 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign
DBG("VIDIOCSYNC: %d\n", frame);
read_frame(cam, frame);
+ vicam_decode_color(cam->raw_image,
+ cam->framebuf +
+ frame * VICAM_MAX_FRAME_SIZE );
break;
}
@@ -724,7 +749,6 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign
break;
}
- up(&cam->busy_lock);
return retval;
}
@@ -741,26 +765,25 @@ vicam_open(struct inode *inode, struct file *file)
"vicam video_device improperly initialized");
}
- if ( down_interruptible(&cam->busy_lock) )
- return -EINTR;
+ /* the videodev_lock held above us protects us from
+ * simultaneous opens...for now. we probably shouldn't
+ * rely on this fact forever.
+ */
if (cam->open_count > 0) {
printk(KERN_INFO
"vicam_open called on already opened camera");
- up(&cam->busy_lock);
return -EBUSY;
}
cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL);
if (!cam->raw_image) {
- up(&cam->busy_lock);
return -ENOMEM;
}
cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
if (!cam->framebuf) {
kfree(cam->raw_image);
- up(&cam->busy_lock);
return -ENOMEM;
}
@@ -768,7 +791,6 @@ vicam_open(struct inode *inode, struct file *file)
if (!cam->cntrlbuf) {
kfree(cam->raw_image);
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
- up(&cam->busy_lock);
return -ENOMEM;
}
@@ -785,8 +807,6 @@ vicam_open(struct inode *inode, struct file *file)
cam->needsDummyRead = 1;
cam->open_count++;
- up(&cam->busy_lock);
-
file->private_data = cam;
return 0;
@@ -796,118 +816,105 @@ static int
vicam_close(struct inode *inode, struct file *file)
{
struct vicam_camera *cam = file->private_data;
+ int open_count;
+ struct usb_device *udev;
+
DBG("close\n");
+
+ /* it's not the end of the world if
+ * we fail to turn the camera off.
+ */
+
set_camera_power(cam, 0);
kfree(cam->raw_image);
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
kfree(cam->cntrlbuf);
+ down(&cam->cam_lock);
+
cam->open_count--;
+ open_count = cam->open_count;
+ udev = cam->udev;
+
+ up(&cam->cam_lock);
+
+ if (!open_count && !udev) {
+ kfree(cam);
+ }
return 0;
}
-inline int pin(int x)
+static void vicam_decode_color(const u8 *data, u8 *rgb)
{
- return((x > 255) ? 255 : ((x < 0) ? 0 : x));
-}
+ /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB
+ * Copyright (C) 2002 Monroe Williams (monroe@pobox.com)
+ */
-inline void writepixel(char *rgb, int Y, int Cr, int Cb)
-{
- Y = 1160 * (Y - 16);
-
- rgb[2] = pin( ( ( Y + ( 1594 * Cr ) ) + 500 ) / 1300 );
- rgb[1] = pin( ( ( Y - ( 392 * Cb ) - ( 813 * Cr ) ) + 500 ) / 1000 );
- rgb[0] = pin( ( ( Y + ( 2017 * Cb ) ) + 500 ) / 900 );
-}
+ int i, prevY, nextY;
-#define DATA_HEADER_SIZE 64
+ prevY = 512;
+ nextY = 512;
-// --------------------------------------------------------------------------------
-// vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB
-//
-// Copyright (C) 2002 Monroe Williams (monroe@pobox.com)
-// --------------------------------------------------------------------------------
+ data += VICAM_HEADER_SIZE;
-static void vicam_decode_color( char *data, char *rgb)
-{
- int x,y;
- int Cr, Cb;
- int sign;
- int prevX, nextX, prevY, nextY;
- int skip;
- unsigned char *src;
- unsigned char *dst;
+ for( i = 0; i < 240; i++, data += 512 ) {
+ const int y = ( i * 242 ) / 240;
- prevY = 512;
- nextY = 512;
+ int j, prevX, nextX;
+ int Y, Cr, Cb;
- src = data + DATA_HEADER_SIZE;
- dst = rgb;
+ if ( y == 242 - 1 ) {
+ nextY = -512;
+ }
- for(y = 1; y < 241; y += 2)
- {
- // even line
- sign = 1;
prevX = 1;
nextX = 1;
- skip = 0;
+ for ( j = 0; j < 320; j++, rgb += 3 ) {
+ const int x = ( j * 512 ) / 320;
+ const u8 * const src = &data[x];
- dst = rgb + (y-1)*320*3;
-
- for(x = 0; x < 512; x++)
- {
- if(x == 512-1)
+ if ( x == 512 - 1 ) {
nextX = -1;
+ }
- Cr = sign * ((src[prevX] - src[0]) + (src[nextX] - src[0])) >> 1;
- Cb = sign * ((src[prevY] - src[prevX + prevY]) + (src[prevY] - src[nextX + prevY]) + (src[nextY] - src[prevX + nextY]) + (src[nextY] - src[nextX + nextY])) >> 2;
-
- writepixel(
- dst + ((x*5)>>3)*3,
- src[0] + (sign * (Cr >> 1)),
- Cr,
- Cb);
-
- src++;
- sign *= -1;
- prevX = -1;
- }
-
- prevY = -512;
+ Cr = ( src[prevX] - src[0] ) +
+ ( src[nextX] - src[0] );
+ Cr /= 2;
- if(y == (242 - 2))
- nextY = -512;
+ Cb = ( src[prevY] - src[prevX + prevY] ) +
+ ( src[prevY] - src[nextX + prevY] ) +
+ ( src[nextY] - src[prevX + nextY] ) +
+ ( src[nextY] - src[nextX + nextY] );
+ Cb /= 4;
- // odd line
- sign = 1;
- prevX = 1;
- nextX = 1;
+ Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 );
- skip = 0;
+ if ( i & 1 ) {
+ int Ct = Cr;
+ Cr = Cb;
+ Cb = Ct;
+ }
- dst = rgb + (y)*320*3;
-
- for(x = 0; x < 512; x++)
- {
- if(x == 512-1)
- nextX = -1;
-
- Cr = sign * ((src[prevX + prevY] - src[prevY]) + (src[nextX + prevY] - src[prevY]) + (src[prevX + nextY] - src[nextY]) + (src[nextX + nextY] - src[nextY])) >> 2;
- Cb = sign * ((src[0] - src[prevX]) + (src[0] - src[nextX])) >> 1;
+ if ( ( x ^ i ) & 1 ) {
+ Cr = -Cr;
+ Cb = -Cb;
+ }
- writepixel(
- dst + ((x * 5)>>3)*3,
- src[0] - (sign * (Cb >> 1)),
- Cr,
- Cb);
+ rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) +
+ 500 ) / 900, 0, 255 );
+ rgb[1] = clamp( ( ( Y - ( 392 * Cb ) -
+ ( 813 * Cr ) ) +
+ 500 ) / 1000, 0, 255 );
+ rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) +
+ 500 ) / 1300, 0, 255 );
- src++;
- sign *= -1;
prevX = -1;
}
+
+ prevY = -512;
}
}
@@ -953,12 +960,18 @@ read_frame(struct vicam_camera *cam, int framenum)
request[8] = 0;
// bytes 9-15 do not seem to affect exposure or image quality
- n = send_control_msg(cam->udev, 0x51, 0x80, 0, request, 16);
+ down(&cam->cam_lock);
+
+ if (!cam->udev) {
+ goto done;
+ }
+
+ n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16);
if (n < 0) {
printk(KERN_ERR
" Problem sending frame capture control message");
- return;
+ goto done;
}
n = usb_bulk_msg(cam->udev,
@@ -971,9 +984,8 @@ read_frame(struct vicam_camera *cam, int framenum)
n);
}
- vicam_decode_color(cam->raw_image,
- cam->framebuf +
- framenum * VICAM_MAX_FRAME_SIZE );
+ done:
+ up(&cam->cam_lock);
}
static int
@@ -983,17 +995,16 @@ vicam_read( struct file *file, char *buf, size_t count, loff_t *ppos )
DBG("read %d bytes.\n", (int) count);
- if ( down_interruptible(&cam->busy_lock) )
- return -EINTR;
-
if (*ppos >= VICAM_MAX_FRAME_SIZE) {
*ppos = 0;
- up(&cam->busy_lock);
return 0;
}
if (*ppos == 0) {
read_frame(cam, 0);
+ vicam_decode_color(cam->raw_image,
+ cam->framebuf +
+ 0 * VICAM_MAX_FRAME_SIZE);
}
count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos);
@@ -1008,8 +1019,6 @@ vicam_read( struct file *file, char *buf, size_t count, loff_t *ppos )
*ppos = 0;
}
- up(&cam->busy_lock);
-
return count;
}
@@ -1034,10 +1043,6 @@ vicam_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
*/
- /* make this _really_ smp-safe */
- if (down_interruptible(&cam->busy_lock))
- return -EINTR;
-
pos = (unsigned long)cam->framebuf;
while (size > 0) {
page = kvirt_to_pa(pos);
@@ -1052,8 +1057,6 @@ vicam_mmap(struct file *file, struct vm_area_struct *vma)
size = 0;
}
- up(&cam->busy_lock);
-
return 0;
}
@@ -1285,7 +1288,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
cam->shutter_speed = 15;
- init_MUTEX(&cam->busy_lock);
+ init_MUTEX(&cam->cam_lock);
memcpy(&cam->vdev, &vicam_template,
sizeof (vicam_template));
@@ -1312,17 +1315,43 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
static void
vicam_disconnect(struct usb_interface *intf)
{
+ int open_count;
struct vicam_camera *cam = dev_get_drvdata(&intf->dev);
-
dev_set_drvdata ( &intf->dev, NULL );
-
- cam->udev = NULL;
-
+
+ /* we must unregister the device before taking its
+ * cam_lock. This is because the video open call
+ * holds the same lock as video unregister. if we
+ * unregister inside of the cam_lock and open also
+ * uses the cam_lock, we get deadlock.
+ */
+
video_unregister_device(&cam->vdev);
+ /* stop the camera from being used */
+
+ down(&cam->cam_lock);
+
+ /* mark the camera as gone */
+
+ cam->udev = NULL;
+
vicam_destroy_proc_entry(cam);
- kfree(cam);
+ /* the only thing left to do is synchronize with
+ * our close/release function on who should release
+ * the camera memory. if there are any users using the
+ * camera, it's their job. if there are no users,
+ * it's ours.
+ */
+
+ open_count = cam->open_count;
+
+ up(&cam->cam_lock);
+
+ if (!open_count) {
+ kfree(cam);
+ }
printk(KERN_DEBUG "ViCam-based WebCam disconnected\n");
}
diff --git a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c
index c535d28da923..b2ed1ed76bfd 100644
--- a/drivers/usb/misc/tiglusb.c
+++ b/drivers/usb/misc/tiglusb.c
@@ -185,7 +185,7 @@ tiglusb_read (struct file *filp, char *buf, size_t count, loff_t * f_pos)
pipe = usb_rcvbulkpipe (s->dev, 1);
result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_read,
- &bytes_read, HZ / (timeout / 10));
+ &bytes_read, HZ * 10 / timeout);
if (result == -ETIMEDOUT) { /* NAK */
ret = result;
if (!bytes_read) {
@@ -242,7 +242,7 @@ tiglusb_write (struct file *filp, const char *buf, size_t count, loff_t * f_pos)
pipe = usb_sndbulkpipe (s->dev, 2);
result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_write,
- &bytes_written, HZ / (timeout / 10));
+ &bytes_written, HZ * 10 / timeout);
if (result == -ETIMEDOUT) { /* NAK */
warn ("tiglusb_write, NAK received.");
@@ -453,6 +453,8 @@ tiglusb_setup (char *str)
if (ints[0] > 0) {
timeout = ints[1];
}
+ if (!timeout)
+ timeout = TIMAXTIME;
return 1;
}
@@ -494,6 +496,9 @@ tiglusb_init (void)
info (DRIVER_DESC ", " DRIVER_VERSION);
+ if (!timeout)
+ timeout = TIMAXTIME;
+
return 0;
}
@@ -516,6 +521,6 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE (DRIVER_LICENSE);
MODULE_PARM (timeout, "i");
-MODULE_PARM_DESC (timeout, "Timeout (default=1.5 seconds)");
+MODULE_PARM_DESC (timeout, "Timeout in tenths of seconds (default=1.5 seconds)");
/* --------------------------------------------------------------------- */
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 12cbae5b5704..305058779a37 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -400,5 +400,10 @@ config USB_SERIAL_OMNINET
The module will be called omninet.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+config USB_EZUSB
+ bool
+ depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT
+ default y
+
endmenu
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 3e3f9d02924a..30b1e6b0fd40 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_USB_SERIAL) += usbserial.o
usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE) += console.o
+usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o
obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
@@ -29,9 +30,9 @@ obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
# Objects that export symbols.
-export-objs := usb-serial.o
+export-objs := usb-serial.o ezusb.o
-usbserial-objs := usb-serial.o $(usbserial-obj-y)
+usbserial-objs := usb-serial.o generic.o $(usbserial-obj-y)
include $(TOPDIR)/Rules.make
diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c
new file mode 100644
index 000000000000..35616b5e23a0
--- /dev/null
+++ b/drivers/usb/serial/ezusb.c
@@ -0,0 +1,67 @@
+/*
+ * EZ-USB specific functions used by some of the USB to Serial drivers.
+ *
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ static int debug = 1;
+#else
+ static int debug;
+#endif
+
+#include "usb-serial.h"
+
+/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
+#define CPUCS_REG 0x7F92
+
+int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
+{
+ int result;
+ unsigned char *transfer_buffer;
+
+ /* dbg("ezusb_writememory %x, %d", address, length); */
+ if (!serial->dev) {
+ dbg("%s - no physical device present, failing.", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ transfer_buffer = kmalloc (length, GFP_KERNEL);
+ if (!transfer_buffer) {
+ err("%s - kmalloc(%d) failed.", __FUNCTION__, length);
+ return -ENOMEM;
+ }
+ memcpy (transfer_buffer, data, length);
+ result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 3*HZ);
+ kfree (transfer_buffer);
+ return result;
+}
+
+int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
+{
+ int response;
+ dbg("%s - %d", __FUNCTION__, reset_bit);
+ response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
+ if (response < 0) {
+ err("%s- %d failed", __FUNCTION__, reset_bit);
+ }
+ return response;
+}
+
+
+EXPORT_SYMBOL(ezusb_writememory);
+EXPORT_SYMBOL(ezusb_set_reset);
+
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
new file mode 100644
index 000000000000..29dc63fa01d7
--- /dev/null
+++ b/drivers/usb/serial/generic.c
@@ -0,0 +1,304 @@
+/*
+ * USB Serial Converter Generic functions
+ *
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ static int debug = 1;
+#else
+ static int debug;
+#endif
+
+#include "usb-serial.h"
+
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+static __u16 vendor = 0x05f9;
+static __u16 product = 0xffff;
+
+MODULE_PARM(vendor, "h");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "h");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
+
+/* All of the device info needed for the Generic Serial Converter */
+struct usb_serial_device_type usb_serial_generic_device = {
+ .owner = THIS_MODULE,
+ .name = "Generic",
+ .id_table = generic_device_ids,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
+ .num_ports = 1,
+ .shutdown = usb_serial_generic_shutdown,
+};
+#endif
+
+int usb_serial_generic_register (int _debug)
+{
+ int retval = 0;
+
+ debug = _debug;
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ generic_device_ids[0].idVendor = vendor;
+ generic_device_ids[0].idProduct = product;
+ generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
+
+ /* register our generic driver with ourselves */
+ retval = usb_serial_register (&usb_serial_generic_device);
+#endif
+ return retval;
+}
+
+void usb_serial_generic_deregister (void)
+{
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ /* remove our generic driver */
+ usb_serial_deregister (&usb_serial_generic_device);
+#endif
+}
+
+int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ int result = 0;
+
+ if (port_paranoia_check (port, __FUNCTION__))
+ return -ENODEV;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* force low_latency on so that our tty_push actually forces the data through,
+ otherwise it is scheduled, and with high data rates (like with OHCI) data
+ can get lost. */
+ if (port->tty)
+ port->tty->low_latency = 1;
+
+ /* if we have a bulk interrupt, start reading from it */
+ if (serial->num_bulk_in) {
+ /* Start reading from the device */
+ usb_fill_bulk_urb (port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ ((serial->type->read_bulk_callback) ?
+ serial->type->read_bulk_callback :
+ usb_serial_generic_read_bulk_callback),
+ port);
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (result)
+ err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+ }
+
+ return result;
+}
+
+static void generic_cleanup (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (serial->dev) {
+ /* shutdown any bulk reads that might be going on */
+ if (serial->num_bulk_out)
+ usb_unlink_urb (port->write_urb);
+ if (serial->num_bulk_in)
+ usb_unlink_urb (port->read_urb);
+ }
+}
+
+void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp)
+{
+ dbg("%s - port %d", __FUNCTION__, port->number);
+ generic_cleanup (port);
+}
+
+int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = port->serial;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (count == 0) {
+ dbg("%s - write request of 0 bytes", __FUNCTION__);
+ return (0);
+ }
+
+ /* only do something if we have a bulk out endpoint */
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status == -EINPROGRESS) {
+ dbg("%s - already writing", __FUNCTION__);
+ return (0);
+ }
+
+ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+ if (from_user) {
+ if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
+ return -EFAULT;
+ }
+ else {
+ memcpy (port->write_urb->transfer_buffer, buf, count);
+ }
+
+ usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+
+ /* set up our urb */
+ usb_fill_bulk_urb (port->write_urb, serial->dev,
+ usb_sndbulkpipe (serial->dev,
+ port->bulk_out_endpointAddress),
+ port->write_urb->transfer_buffer, count,
+ ((serial->type->write_bulk_callback) ?
+ serial->type->write_bulk_callback :
+ usb_serial_generic_write_bulk_callback), port);
+
+ /* send the data out the bulk port */
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ if (result)
+ err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
+ else
+ result = count;
+
+ return result;
+ }
+
+ /* no bulk out, so return 0 bytes written */
+ return (0);
+}
+
+int usb_serial_generic_write_room (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ int room = 0;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status != -EINPROGRESS)
+ room = port->bulk_out_size;
+ }
+
+ dbg("%s - returns %d", __FUNCTION__, room);
+ return (room);
+}
+
+int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ int chars = 0;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status == -EINPROGRESS)
+ chars = port->write_urb->transfer_buffer_length;
+ }
+
+ dbg("%s - returns %d", __FUNCTION__, chars);
+ return (chars);
+}
+
+void usb_serial_generic_read_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ int i;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (!serial) {
+ dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+ return;
+ }
+
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+ return;
+ }
+
+ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+ if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty_flip_buffer_push(tty);
+ }
+ /* this doesn't actually push the data through unless tty->low_latency is set */
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue trying to always read */
+ usb_fill_bulk_urb (port->read_urb, serial->dev,
+ usb_rcvbulkpipe (serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ ((serial->type->read_bulk_callback) ?
+ serial->type->read_bulk_callback :
+ usb_serial_generic_read_bulk_callback), port);
+ result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (result)
+ err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+}
+
+void usb_serial_generic_write_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (!serial) {
+ dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+ return;
+ }
+
+ if (urb->status) {
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+ return;
+ }
+
+ usb_serial_port_softint((void *)port);
+
+ schedule_work(&port->work);
+}
+
+void usb_serial_generic_shutdown (struct usb_serial *serial)
+{
+ int i;
+
+ dbg("%s", __FUNCTION__);
+
+ /* stop reads and writes on all ports */
+ for (i=0; i < serial->num_ports; ++i) {
+ generic_cleanup (&serial->port[i]);
+ }
+}
+
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index c4a145cb5aa2..8d0a4d5b57a7 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -9,6 +9,10 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
+ * (26/11/2002) ganesh
+ * Added insmod options to specify product and vendor id.
+ * Use modprobe ipaq vendor=0xfoo product=0xbar
+ *
* (26/7/2002) ganesh
* Fixed up broken error handling in ipaq_open. Retry the "kickstart"
* packet much harder - this drastically reduces connection failures.
@@ -63,10 +67,13 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.2"
+
+#define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
#define DRIVER_DESC "USB Compaq iPAQ, HP Jornada, Casio EM500 driver"
+static int product, vendor;
+
/* Function prototypes for an ipaq */
static int ipaq_open (struct usb_serial_port *port, struct file *filp);
static void ipaq_close (struct usb_serial_port *port, struct file *filp);
@@ -85,6 +92,8 @@ static void ipaq_destroy_lists(struct usb_serial_port *port);
static struct usb_device_id ipaq_id_table [] = {
+ /* The first entry is a placeholder for the insmod-specified device */
+ { USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_548_ID) },
{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_568_ID) },
@@ -521,9 +530,14 @@ static void ipaq_shutdown(struct usb_serial *serial)
static int __init ipaq_init(void)
{
+ spin_lock_init(&write_list_lock);
usb_serial_register(&ipaq_device);
- usb_register(&ipaq_driver);
info(DRIVER_DESC " " DRIVER_VERSION);
+ if (vendor) {
+ ipaq_id_table[0].idVendor = vendor;
+ ipaq_id_table[0].idProduct = product;
+ }
+ usb_register(&ipaq_driver);
return 0;
}
@@ -546,3 +560,8 @@ MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
+MODULE_PARM(vendor, "h");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "h");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 5afe854714ec..2315dbf6299a 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -346,7 +346,7 @@ static int keyspan_write(struct usb_serial_port *port, int from_user,
if (this_urb->status == -EINPROGRESS) {
if (this_urb->transfer_flags & URB_ASYNC_UNLINK)
break;
- if (jiffies - p_priv->tx_start_time[flip] < 10 * HZ)
+ if (time_before(jiffies, p_priv->tx_start_time[flip] + 10 * HZ))
break;
this_urb->transfer_flags |= URB_ASYNC_UNLINK;
usb_unlink_urb(this_urb);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 1ee4eace260b..cfcf341f4cbb 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -345,41 +345,12 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.7"
+#define DRIVER_VERSION "v1.8"
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
#define DRIVER_DESC "USB Serial Driver core"
-/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
-/* need to always compile these in, as some of the other devices use these functions as their own. */
-/* if a driver does not provide a function pointer, the generic function will be called. */
-int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
-int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
-static void generic_close (struct usb_serial_port *port, struct file *filp);
-static int generic_write_room (struct usb_serial_port *port);
-static int generic_chars_in_buffer (struct usb_serial_port *port);
-static void generic_read_bulk_callback (struct urb *urb);
-static void generic_write_bulk_callback (struct urb *urb);
-static void generic_shutdown (struct usb_serial *serial);
-
#ifdef CONFIG_USB_SERIAL_GENERIC
-static __u16 vendor = 0x05f9;
-static __u16 product = 0xffff;
-
-static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
-
-/* All of the device info needed for the Generic Serial Converter */
-static struct usb_serial_device_type generic_device = {
- .owner = THIS_MODULE,
- .name = "Generic",
- .id_table = generic_device_ids,
- .num_interrupt_in = NUM_DONT_CARE,
- .num_bulk_in = NUM_DONT_CARE,
- .num_bulk_out = NUM_DONT_CARE,
- .num_ports = 1,
- .shutdown = generic_shutdown,
-};
-
/* we want to look at all devices, as the vendor/product id can change
* depending on the command line argument */
static struct usb_device_id generic_serial_ids[] = {
@@ -387,27 +358,6 @@ static struct usb_device_id generic_serial_ids[] = {
{}
};
-static int generic_register (void)
-{
- generic_device_ids[0].idVendor = vendor;
- generic_device_ids[0].idProduct = product;
- generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
-
- /* register our generic driver with ourselves */
- return usb_serial_register (&generic_device);
-}
-
-static void generic_deregister (void)
-{
- /* remove our generic driver */
- usb_serial_deregister (&generic_device);
-}
-
-#else
-
-static inline int generic_register (void) { return 0; }
-static inline void generic_deregister (void) { }
-
#endif /* CONFIG_USB_SERIAL_GENERIC */
/* Driver structure we register with the USB core */
@@ -488,45 +438,6 @@ static void return_serial (struct usb_serial *serial)
return;
}
-#ifdef USES_EZUSB_FUNCTIONS
-/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
-#define CPUCS_REG 0x7F92
-
-int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
-{
- int result;
- unsigned char *transfer_buffer;
-
- /* dbg("ezusb_writememory %x, %d", address, length); */
- if (!serial->dev) {
- dbg("%s - no physical device present, failing.", __FUNCTION__);
- return -ENODEV;
- }
-
- transfer_buffer = kmalloc (length, GFP_KERNEL);
- if (!transfer_buffer) {
- err("%s - kmalloc(%d) failed.", __FUNCTION__, length);
- return -ENOMEM;
- }
- memcpy (transfer_buffer, data, length);
- result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 3*HZ);
- kfree (transfer_buffer);
- return result;
-}
-
-int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
-{
- int response;
- dbg("%s - %d", __FUNCTION__, reset_bit);
- response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
- if (response < 0) {
- err("%s- %d failed", __FUNCTION__, reset_bit);
- }
- return response;
-}
-
-#endif /* USES_EZUSB_FUNCTIONS */
-
/*****************************************************************************
* Driver tty interface functions
*****************************************************************************/
@@ -593,7 +504,7 @@ static void __serial_close(struct usb_serial_port *port, struct file *filp)
if (port->serial->type->close)
port->serial->type->close(port, filp);
else
- generic_close(port, filp);
+ usb_serial_generic_close(port, filp);
port->open_count = 0;
}
@@ -672,7 +583,7 @@ static int serial_write_room (struct tty_struct *tty)
if (serial->type->write_room)
retval = serial->type->write_room(port);
else
- retval = generic_write_room(port);
+ retval = usb_serial_generic_write_room(port);
exit:
up (&port->sem);
@@ -701,7 +612,7 @@ static int serial_chars_in_buffer (struct tty_struct *tty)
if (serial->type->chars_in_buffer)
retval = serial->type->chars_in_buffer(port);
else
- retval = generic_chars_in_buffer(port);
+ retval = usb_serial_generic_chars_in_buffer(port);
exit:
up (&port->sem);
@@ -844,7 +755,7 @@ static void serial_shutdown (struct usb_serial *serial)
if (serial->type->shutdown)
serial->type->shutdown(serial);
else
- generic_shutdown(serial);
+ usb_serial_generic_shutdown(serial);
}
static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
@@ -889,235 +800,6 @@ done:
return ((count < begin+length-off) ? count : begin+length-off);
}
-/*****************************************************************************
- * generic devices specific driver functions
- *****************************************************************************/
-int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
- int result = 0;
-
- if (port_paranoia_check (port, __FUNCTION__))
- return -ENODEV;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- /* force low_latency on so that our tty_push actually forces the data through,
- otherwise it is scheduled, and with high data rates (like with OHCI) data
- can get lost. */
- if (port->tty)
- port->tty->low_latency = 1;
-
- /* if we have a bulk interrupt, start reading from it */
- if (serial->num_bulk_in) {
- /* Start reading from the device */
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- ((serial->type->read_bulk_callback) ?
- serial->type->read_bulk_callback :
- generic_read_bulk_callback),
- port);
- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (result)
- err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
- }
-
- return result;
-}
-
-static void generic_cleanup (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (serial->dev) {
- /* shutdown any bulk reads that might be going on */
- if (serial->num_bulk_out)
- usb_unlink_urb (port->write_urb);
- if (serial->num_bulk_in)
- usb_unlink_urb (port->read_urb);
- }
-}
-
-static void generic_close (struct usb_serial_port *port, struct file * filp)
-{
- dbg("%s - port %d", __FUNCTION__, port->number);
- generic_cleanup (port);
-}
-
-int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
-{
- struct usb_serial *serial = port->serial;
- int result;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (count == 0) {
- dbg("%s - write request of 0 bytes", __FUNCTION__);
- return (0);
- }
-
- /* only do something if we have a bulk out endpoint */
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS) {
- dbg("%s - already writing", __FUNCTION__);
- return (0);
- }
-
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
- if (from_user) {
- if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
- return -EFAULT;
- }
- else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
- }
-
- usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
-
- /* set up our urb */
- usb_fill_bulk_urb (port->write_urb, serial->dev,
- usb_sndbulkpipe (serial->dev,
- port->bulk_out_endpointAddress),
- port->write_urb->transfer_buffer, count,
- ((serial->type->write_bulk_callback) ?
- serial->type->write_bulk_callback :
- generic_write_bulk_callback), port);
-
- /* send the data out the bulk port */
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
- else
- result = count;
-
- return result;
- }
-
- /* no bulk out, so return 0 bytes written */
- return (0);
-}
-
-static int generic_write_room (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
- int room = 0;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (serial->num_bulk_out) {
- if (port->write_urb->status != -EINPROGRESS)
- room = port->bulk_out_size;
- }
-
- dbg("%s - returns %d", __FUNCTION__, room);
- return (room);
-}
-
-static int generic_chars_in_buffer (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
- int chars = 0;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS)
- chars = port->write_urb->transfer_buffer_length;
- }
-
- dbg("%s - returns %d", __FUNCTION__, chars);
- return (chars);
-}
-
-static void generic_read_bulk_callback (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
- int i;
- int result;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (!serial) {
- dbg("%s - bad serial pointer, exiting", __FUNCTION__);
- return;
- }
-
- if (urb->status) {
- dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
- return;
- }
-
- usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
-
- tty = port->tty;
- if (tty && urb->actual_length) {
- for (i = 0; i < urb->actual_length ; ++i) {
- /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
- if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
- tty_flip_buffer_push(tty);
- }
- /* this doesn't actually push the data through unless tty->low_latency is set */
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
- }
-
- /* Continue trying to always read */
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe (serial->dev,
- port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- ((serial->type->read_bulk_callback) ?
- serial->type->read_bulk_callback :
- generic_read_bulk_callback), port);
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
-}
-
-static void generic_write_bulk_callback (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- if (!serial) {
- dbg("%s - bad serial pointer, exiting", __FUNCTION__);
- return;
- }
-
- if (urb->status) {
- dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
- return;
- }
-
- usb_serial_port_softint((void *)port);
-
- schedule_work(&port->work);
-}
-
-static void generic_shutdown (struct usb_serial *serial)
-{
- int i;
-
- dbg("%s", __FUNCTION__);
-
- /* stop reads and writes on all ports */
- for (i=0; i < serial->num_ports; ++i) {
- generic_cleanup (&serial->port[i]);
- }
-}
-
void usb_serial_port_softint(void *private)
{
struct usb_serial_port *port = (struct usb_serial_port *)private;
@@ -1268,7 +950,6 @@ int usb_serial_probe(struct usb_interface *interface,
(dev->descriptor.idProduct == PL2303_PRODUCT_ID)) ||
((dev->descriptor.idVendor == ATEN_VENDOR_ID) &&
(dev->descriptor.idProduct == ATEN_PRODUCT_ID))) {
- //if (ifnum == 1) {
if (interface != &dev->actconfig->interface[0]) {
/* check out the endpoints of the other interface*/
//interface = &dev->actconfig->interface[ifnum ^ 1];
@@ -1303,7 +984,7 @@ int usb_serial_probe(struct usb_interface *interface,
info("%s converter detected", type->name);
#ifdef CONFIG_USB_SERIAL_GENERIC
- if (type == &generic_device) {
+ if (type == &usb_serial_generic_device) {
num_ports = num_bulk_out;
if (num_ports == 0) {
err("Generic device with no bulk out, not allowed.");
@@ -1359,7 +1040,7 @@ int usb_serial_probe(struct usb_interface *interface,
port->bulk_in_buffer, buffer_size,
((serial->type->read_bulk_callback) ?
serial->type->read_bulk_callback :
- generic_read_bulk_callback),
+ usb_serial_generic_read_bulk_callback),
port);
}
@@ -1385,7 +1066,7 @@ int usb_serial_probe(struct usb_interface *interface,
port->bulk_out_buffer, buffer_size,
((serial->type->write_bulk_callback) ?
serial->type->write_bulk_callback :
- generic_write_bulk_callback),
+ usb_serial_generic_write_bulk_callback),
port);
}
@@ -1504,10 +1185,10 @@ void usb_serial_disconnect(struct usb_interface *interface)
port = &serial->port[i];
down (&port->sem);
if (port->tty != NULL) {
+ port->tty->driver_data = NULL;
while (port->open_count > 0) {
__serial_close(port, NULL);
}
- port->tty->driver_data = NULL;
}
up (&port->sem);
}
@@ -1607,7 +1288,7 @@ static int __init usb_serial_init(void)
}
/* register the generic driver, if we should */
- result = generic_register();
+ result = usb_serial_generic_register(debug);
if (result < 0) {
err("%s - registering generic driver failed", __FUNCTION__);
goto exit;
@@ -1637,7 +1318,7 @@ exit_tty:
tty_unregister_driver(&serial_tty_driver);
exit_generic:
- generic_deregister();
+ usb_serial_generic_deregister();
exit:
err ("%s - returning with error %d", __FUNCTION__, result);
@@ -1649,7 +1330,7 @@ static void __exit usb_serial_exit(void)
{
usb_serial_console_exit();
- generic_deregister();
+ usb_serial_generic_deregister();
usb_deregister(&usb_serial_driver);
tty_unregister_driver(&serial_tty_driver);
@@ -1699,12 +1380,6 @@ EXPORT_SYMBOL(usb_serial_deregister);
EXPORT_SYMBOL(usb_serial_probe);
EXPORT_SYMBOL(usb_serial_disconnect);
EXPORT_SYMBOL(usb_serial_port_softint);
-#ifdef USES_EZUSB_FUNCTIONS
- EXPORT_SYMBOL(ezusb_writememory);
- EXPORT_SYMBOL(ezusb_set_reset);
-#endif
-
-
/* Module information */
@@ -1714,11 +1389,3 @@ MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-MODULE_PARM(vendor, "h");
-MODULE_PARM_DESC(vendor, "User specified USB idVendor");
-
-MODULE_PARM(product, "h");
-MODULE_PARM_DESC(product, "User specified USB idProduct");
-#endif
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index 73c83d1a1415..82c75d867f39 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -237,24 +237,8 @@ extern void usb_serial_port_softint(void *private);
extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id);
extern void usb_serial_disconnect(struct usb_interface *iface);
-/* determine if we should include the EzUSB loader functions */
-#undef USES_EZUSB_FUNCTIONS
-#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE)
- #define USES_EZUSB_FUNCTIONS
-#endif
-#if defined(CONFIG_USB_SERIAL_XIRCOM) || defined(CONFIG_USB_SERIAL_XIRCOM_MODULE)
- #define USES_EZUSB_FUNCTIONS
-#endif
-#if defined(CONFIG_USB_SERIAL_KEYSPAN) || defined(CONFIG_USB_SERIAL_KEYSPAN_MODULE)
- #define USES_EZUSB_FUNCTIONS
-#endif
-#if defined(CONFIG_USB_SERIAL_WHITEHEAT) || defined(CONFIG_USB_SERIAL_WHITEHEAT_MODULE)
- #define USES_EZUSB_FUNCTIONS
-#endif
-#ifdef USES_EZUSB_FUNCTIONS
extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
-#endif
/* USB Serial console functions */
#ifdef CONFIG_USB_SERIAL_CONSOLE
@@ -265,10 +249,20 @@ static inline void usb_serial_console_init (int debug, int minor) { }
static inline void usb_serial_console_exit (void) { }
#endif
-/* Functions needed by the usb serial console code */
+/* Functions needed by other parts of the usbserial core */
extern struct usb_serial *usb_serial_get_by_minor (unsigned int minor);
extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
extern int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp);
+extern int usb_serial_generic_write_room (struct usb_serial_port *port);
+extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port);
+extern void usb_serial_generic_read_bulk_callback (struct urb *urb);
+extern void usb_serial_generic_write_bulk_callback (struct urb *urb);
+extern void usb_serial_generic_shutdown (struct usb_serial *serial);
+extern int usb_serial_generic_register (int debug);
+extern void usb_serial_generic_deregister (void);
+
+extern struct usb_serial_device_type usb_serial_generic_device;
/* Inline functions to check the sanity of a pointer that is passed to us */
static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index 9c4ba970eb35..2d05857bb1c5 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -186,6 +186,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
+ { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
@@ -209,6 +210,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
+ { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h
index a06f1e8070b2..3e5c23cb2e23 100644
--- a/drivers/usb/serial/visor.h
+++ b/drivers/usb/serial/visor.h
@@ -28,6 +28,7 @@
#define PALM_M125_ID 0x0040
#define PALM_M130_ID 0x0050
#define PALM_TUNGSTEN_T_ID 0x0060
+#define PALM_TUNGSTEN_Z_ID 0x0031
#define PALM_ZIRE_ID 0x0070
#define SONY_VENDOR_ID 0x054C
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 74eeef54f871..027354fd948c 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -202,10 +202,23 @@ struct whiteheat_command_private {
#define THROTTLED 0x01
#define ACTUALLY_THROTTLED 0x02
+static int urb_pool_size = 8;
+
+struct whiteheat_urb_wrap {
+ struct list_head list;
+ struct urb *urb;
+};
+
struct whiteheat_private {
spinlock_t lock;
__u8 flags;
__u8 mcr;
+ struct list_head rx_urbs_free;
+ struct list_head rx_urbs_submitted;
+ struct list_head rx_urb_q;
+ struct work_struct rx_work;
+ struct list_head tx_urbs_free;
+ struct list_head tx_urbs_submitted;
};
@@ -215,6 +228,11 @@ static void stop_command_port(struct usb_serial *serial);
static void command_port_write_callback(struct urb *urb);
static void command_port_read_callback(struct urb *urb);
+static int start_port_read(struct usb_serial_port *port);
+static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, struct list_head *head);
+static struct list_head *list_first(struct list_head *head);
+static void rx_data_softint(void *private);
+
static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize);
static int firm_open(struct usb_serial_port *port);
static int firm_close(struct usb_serial_port *port);
@@ -330,6 +348,11 @@ static int whiteheat_attach (struct usb_serial *serial)
__u8 command[2] = { WHITEHEAT_GET_HW_INFO, 0 };
__u8 result[sizeof(*hw_info) + 1];
int i;
+ int j;
+ struct urb *urb;
+ int buf_size;
+ struct whiteheat_urb_wrap *wrap;
+ struct list_head *tmp;
command_port = &serial->port[COMMAND_PORT];
@@ -373,18 +396,80 @@ static int whiteheat_attach (struct usb_serial *serial)
port = &serial->port[i];
info = (struct whiteheat_private *)kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
- if (info == NULL)
- goto no_memory;
+ if (info == NULL) {
+ err("%s: Out of memory for port structures\n", serial->type->name);
+ goto no_private;
+ }
spin_lock_init(&info->lock);
info->flags = 0;
info->mcr = 0;
+ INIT_WORK(&info->rx_work, rx_data_softint, port);
+
+ INIT_LIST_HEAD(&info->rx_urbs_free);
+ INIT_LIST_HEAD(&info->rx_urbs_submitted);
+ INIT_LIST_HEAD(&info->rx_urb_q);
+ INIT_LIST_HEAD(&info->tx_urbs_free);
+ INIT_LIST_HEAD(&info->tx_urbs_submitted);
+
+ for (j = 0; j < urb_pool_size; j++) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ err("No free urbs available");
+ goto no_rx_urb;
+ }
+ buf_size = port->read_urb->transfer_buffer_length;
+ urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
+ if (!urb->transfer_buffer) {
+ err("Couldn't allocate urb buffer");
+ goto no_rx_buf;
+ }
+ wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
+ if (!wrap) {
+ err("Couldn't allocate urb wrapper");
+ goto no_rx_wrap;
+ }
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ urb->transfer_buffer, buf_size,
+ whiteheat_read_callback, port);
+ wrap->urb = urb;
+ list_add(&wrap->list, &info->rx_urbs_free);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ err("No free urbs available");
+ goto no_tx_urb;
+ }
+ buf_size = port->write_urb->transfer_buffer_length;
+ urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
+ if (!urb->transfer_buffer) {
+ err("Couldn't allocate urb buffer");
+ goto no_tx_buf;
+ }
+ wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
+ if (!wrap) {
+ err("Couldn't allocate urb wrapper");
+ goto no_tx_wrap;
+ }
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ urb->transfer_buffer, buf_size,
+ whiteheat_write_callback, port);
+ wrap->urb = urb;
+ list_add(&wrap->list, &info->tx_urbs_free);
+ }
+
port->private = info;
}
command_info = (struct whiteheat_command_private *)kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL);
- if (command_info == NULL)
- goto no_memory;
+ if (command_info == NULL) {
+ err("%s: Out of memory for port structures\n", serial->type->name);
+ goto no_command_private;
+ }
spin_lock_init(&command_info->lock);
command_info->port_running = 0;
@@ -402,12 +487,35 @@ no_firmware:
err("%s: please contact support@connecttech.com\n", serial->type->name);
return -ENODEV;
-no_memory:
- for (i--; i >= 0; i--) {
+no_command_private:
+ for (i = serial->num_ports - 1; i >= 0; i--) {
port = &serial->port[i];
- kfree(port->private);
+ info = port->private;
+ for (j = urb_pool_size - 1; j >= 0; j--) {
+ tmp = list_first(&info->tx_urbs_free);
+ list_del(tmp);
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ kfree(wrap);
+no_tx_wrap:
+ kfree(urb->transfer_buffer);
+no_tx_buf:
+ usb_free_urb(urb);
+no_tx_urb:
+ tmp = list_first(&info->rx_urbs_free);
+ list_del(tmp);
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ kfree(wrap);
+no_rx_wrap:
+ kfree(urb->transfer_buffer);
+no_rx_buf:
+ usb_free_urb(urb);
+no_rx_urb:
+ }
+ kfree(info);
+no_private:
}
- err("%s: Out of memory for port structures\n", serial->type->name);
return -ENOMEM;
}
@@ -416,6 +524,11 @@ static void whiteheat_shutdown (struct usb_serial *serial)
{
struct usb_serial_port *command_port;
struct usb_serial_port *port;
+ struct whiteheat_private *info;
+ struct whiteheat_urb_wrap *wrap;
+ struct urb *urb;
+ struct list_head *tmp;
+ struct list_head *tmp2;
int i;
dbg("%s", __FUNCTION__);
@@ -423,12 +536,27 @@ static void whiteheat_shutdown (struct usb_serial *serial)
/* free up our private data for our command port */
command_port = &serial->port[COMMAND_PORT];
kfree (command_port->private);
- command_port->private = NULL;
for (i = 0; i < serial->num_ports; i++) {
port = &serial->port[i];
- kfree(port->private);
- port->private = NULL;
+ info = port->private;
+ list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
+ list_del(tmp);
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ kfree(wrap);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) {
+ list_del(tmp);
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ kfree(wrap);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ kfree(info);
}
return;
@@ -446,6 +574,9 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
if (retval)
goto exit;
+ if (port->tty)
+ port->tty->low_latency = 1;
+
/* send an open port command */
retval = firm_open(port);
if (retval) {
@@ -469,8 +600,7 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
/* Start reading from the device */
- port->read_urb->dev = port->serial->dev;
- retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ retval = start_port_read(port);
if (retval) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
firm_close(port);
@@ -486,6 +616,13 @@ exit:
static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
{
+ struct whiteheat_private *info = port->private;
+ struct whiteheat_urb_wrap *wrap;
+ struct urb *urb;
+ struct list_head *tmp;
+ struct list_head *tmp2;
+ unsigned long flags;
+
dbg("%s - port %d", __FUNCTION__, port->number);
/* filp is NULL when called from usb_serial_disconnect */
@@ -515,8 +652,26 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
firm_close(port);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
+ spin_lock_irqsave(&info->lock, flags);
+ list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ usb_unlink_urb(urb);
+ list_del(tmp);
+ list_add(tmp, &info->rx_urbs_free);
+ }
+ list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
+ list_del(tmp);
+ list_add(tmp, &info->rx_urbs_free);
+ }
+ list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ usb_unlink_urb(urb);
+ list_del(tmp);
+ list_add(tmp, &info->tx_urbs_free);
+ }
+ spin_unlock_irqrestore(&info->lock, flags);
stop_command_port(port->serial);
@@ -527,7 +682,14 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
static int whiteheat_write(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
struct usb_serial *serial = port->serial;
+ struct whiteheat_private *info = port->private;
+ struct whiteheat_urb_wrap *wrap;
+ struct urb *urb;
int result;
+ int bytes;
+ int sent = 0;
+ unsigned long flags;
+ struct list_head *tmp;
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -536,43 +698,65 @@ static int whiteheat_write(struct usb_serial_port *port, int from_user, const un
return (0);
}
- if (port->write_urb->status == -EINPROGRESS) {
- dbg ("%s - already writing", __FUNCTION__);
- return (0);
- }
+ while (count) {
+ spin_lock_irqsave(&info->lock, flags);
+ if (list_empty(&info->tx_urbs_free)) {
+ spin_unlock_irqrestore(&info->lock, flags);
+ break;
+ }
+ tmp = list_first(&info->tx_urbs_free);
+ list_del(tmp);
+ spin_unlock_irqrestore(&info->lock, flags);
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ bytes = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+ if (from_user) {
+ if (copy_from_user(urb->transfer_buffer, buf + sent, bytes))
+ return -EFAULT;
+ } else {
+ memcpy (urb->transfer_buffer, buf + sent, bytes);
+ }
- if (from_user) {
- if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
- return -EFAULT;
- }
- else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
+ usb_serial_debug_data (__FILE__, __FUNCTION__, bytes, urb->transfer_buffer);
+
+ urb->dev = serial->dev;
+ urb->transfer_buffer_length = bytes;
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result) {
+ err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
+ sent = result;
+ spin_lock_irqsave(&info->lock, flags);
+ list_add(tmp, &info->tx_urbs_free);
+ spin_unlock_irqrestore(&info->lock, flags);
+ break;
+ } else {
+ sent += bytes;
+ count -= bytes;
+ spin_lock_irqsave(&info->lock, flags);
+ list_add(tmp, &info->tx_urbs_submitted);
+ spin_unlock_irqrestore(&info->lock, flags);
+ }
}
- usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
-
- port->write_urb->dev = serial->dev;
- port->write_urb->transfer_buffer_length = count;
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
- else
- result = count;
-
- return result;
+ return sent;
}
static int whiteheat_write_room(struct usb_serial_port *port)
{
+ struct whiteheat_private *info = port->private;
+ struct list_head *tmp;
int room = 0;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
- if (port->write_urb->status != -EINPROGRESS)
- room = port->bulk_out_size;
+ spin_lock_irqsave(&info->lock, flags);
+ list_for_each(tmp, &info->tx_urbs_free)
+ room++;
+ spin_unlock_irqrestore(&info->lock, flags);
+ room *= port->bulk_out_size;
dbg("%s - returns %d", __FUNCTION__, room);
return (room);
@@ -715,12 +899,20 @@ static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) {
static int whiteheat_chars_in_buffer(struct usb_serial_port *port)
{
+ struct whiteheat_private *info = port->private;
+ struct list_head *tmp;
+ struct whiteheat_urb_wrap *wrap;
int chars = 0;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
- if (port->write_urb->status == -EINPROGRESS)
- chars = port->write_urb->transfer_buffer_length;
+ spin_lock_irqsave(&info->lock, flags);
+ list_for_each(tmp, &info->tx_urbs_submitted) {
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ chars += wrap->urb->transfer_buffer_length;
+ }
+ spin_unlock_irqrestore(&info->lock, flags);
dbg ("%s - returns %d", __FUNCTION__, chars);
return (chars);
@@ -745,25 +937,19 @@ static void whiteheat_throttle (struct usb_serial_port *port)
static void whiteheat_unthrottle (struct usb_serial_port *port)
{
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
- int result;
+ int actually_throttled;
unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
spin_lock_irqsave(&info->lock, flags);
-
- if (info->flags & ACTUALLY_THROTTLED) {
- /* Continue trying to always read */
- port->read_urb->dev = port->serial->dev;
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
- }
-
+ actually_throttled = info->flags & ACTUALLY_THROTTLED;
info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
-
spin_unlock_irqrestore(&info->lock, flags);
+ if (actually_throttled)
+ rx_data_softint(port);
+
return;
}
@@ -846,53 +1032,50 @@ static void whiteheat_read_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
- struct tty_struct *tty;
+ struct whiteheat_urb_wrap *wrap;
unsigned char *data = urb->transfer_buffer;
- int i;
- int result;
struct whiteheat_private *info = (struct whiteheat_private *)port->private;
- unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock(&info->lock);
+ wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);
+ if (!wrap) {
+ spin_unlock(&info->lock);
+ err("%s - Not my urb!", __FUNCTION__);
+ return;
+ }
+ list_del(&wrap->list);
+ spin_unlock(&info->lock);
+
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+ spin_lock(&info->lock);
+ list_add(&wrap->list, &info->rx_urbs_free);
+ spin_unlock(&info->lock);
return;
}
if (urb->status) {
dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+ spin_lock(&info->lock);
+ list_add(&wrap->list, &info->rx_urbs_free);
+ spin_unlock(&info->lock);
return;
}
usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
- tty = port->tty;
- if (tty && urb->actual_length) {
- for (i = 0; i < urb->actual_length ; ++i) {
- /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
- if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
- tty_flip_buffer_push(tty);
- }
- /* this doesn't actually push the data through unless tty->low_latency is set */
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
- }
-
- spin_lock_irqsave(&info->lock, flags);
+ spin_lock(&info->lock);
+ list_add_tail(&wrap->list, &info->rx_urb_q);
if (info->flags & THROTTLED) {
info->flags |= ACTUALLY_THROTTLED;
- spin_unlock_irqrestore(&info->lock, flags);
+ spin_unlock(&info->lock);
return;
}
- spin_unlock_irqrestore(&info->lock, flags);
+ spin_unlock(&info->lock);
- /* Continue trying to always read */
- port->read_urb->dev = serial->dev;
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+ schedule_work(&info->rx_work);
}
@@ -900,9 +1083,22 @@ static void whiteheat_write_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+ struct whiteheat_private *info = port->private;
+ struct whiteheat_urb_wrap *wrap;
dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock(&info->lock);
+ wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
+ if (!wrap) {
+ spin_unlock(&info->lock);
+ err("%s - Not my urb!", __FUNCTION__);
+ return;
+ }
+ list_del(&wrap->list);
+ list_add(&wrap->list, &info->tx_urbs_free);
+ spin_unlock(&info->lock);
+
if (!serial) {
dbg("%s - bad serial pointer, exiting", __FUNCTION__);
return;
@@ -1176,6 +1372,122 @@ static void stop_command_port(struct usb_serial *serial)
}
+static int start_port_read(struct usb_serial_port *port) {
+ struct whiteheat_private *info = port->private;
+ struct whiteheat_urb_wrap *wrap;
+ struct urb *urb;
+ int retval = 0;
+ unsigned long flags;
+ struct list_head *tmp;
+ struct list_head *tmp2;
+
+ spin_lock_irqsave(&info->lock, flags);
+
+ list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
+ list_del(tmp);
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ urb->dev = port->serial->dev;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ list_add(tmp, &info->rx_urbs_free);
+ list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+ usb_unlink_urb(urb);
+ list_del(tmp);
+ list_add(tmp, &info->rx_urbs_free);
+ }
+ break;
+ }
+ list_add(tmp, &info->rx_urbs_submitted);
+ }
+
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ return retval;
+}
+
+
+static struct whiteheat_urb_wrap *urb_to_wrap(struct urb* urb, struct list_head *head) {
+ struct whiteheat_urb_wrap *wrap;
+ struct list_head *tmp;
+
+ list_for_each(tmp, head) {
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ if (wrap->urb == urb)
+ return wrap;
+ }
+
+ return NULL;
+}
+
+
+static struct list_head *list_first(struct list_head *head) {
+ return head->next;
+}
+
+
+static void rx_data_softint(void *private) {
+ struct usb_serial_port *port = (struct usb_serial_port *)private;
+ struct whiteheat_private *info = port->private;
+ struct tty_struct *tty = port->tty;
+ struct whiteheat_urb_wrap *wrap;
+ struct urb *urb;
+ unsigned long flags;
+ struct list_head *tmp;
+ struct list_head *tmp2;
+ int result;
+ int sent = 0;
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (info->flags & THROTTLED) {
+ spin_unlock_irqrestore(&info->lock, flags);
+ return;
+ }
+
+ list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
+ list_del(tmp);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
+ urb = wrap->urb;
+
+ if (tty && urb->actual_length) {
+ if (urb->actual_length > TTY_FLIPBUF_SIZE - tty->flip.count) {
+ spin_lock_irqsave(&info->lock, flags);
+ list_add(tmp, &info->rx_urb_q);
+ spin_unlock_irqrestore(&info->lock, flags);
+ tty_flip_buffer_push(tty);
+ schedule_work(&info->rx_work);
+ return;
+ }
+
+ memcpy(tty->flip.char_buf_ptr, urb->transfer_buffer, urb->actual_length);
+ tty->flip.char_buf_ptr += urb->actual_length;
+ tty->flip.count += urb->actual_length;
+ sent += urb->actual_length;
+ }
+
+ urb->dev = port->serial->dev;
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result) {
+ err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+ spin_lock_irqsave(&info->lock, flags);
+ list_add(tmp, &info->rx_urbs_free);
+ continue;
+ }
+
+ spin_lock_irqsave(&info->lock, flags);
+ list_add(tmp, &info->rx_urbs_submitted);
+ }
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (sent)
+ tty_flip_buffer_push(tty);
+}
+
+
/*****************************************************************************
* Connect Tech's White Heat module functions
*****************************************************************************/
@@ -1204,5 +1516,8 @@ MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
+MODULE_PARM(urb_pool_size, "i");
+MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");
+
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 0f486d95bdb7..774d35815121 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -901,14 +901,11 @@ extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
/*
* timeouts, in seconds, used for sending/receiving control messages
* they typically complete within a few frames (msec) after they're issued
+ * USB identifies 5 second timeouts, maybe more in a few cases, and a few
+ * slow devices (like some MGE Ellipse UPSes) actually push that limit.
*/
-#ifdef CONFIG_USB_LONG_TIMEOUT
-#define USB_CTRL_GET_TIMEOUT 4
-#else
-#define USB_CTRL_GET_TIMEOUT 3
-#endif
-
-#define USB_CTRL_SET_TIMEOUT 3
+#define USB_CTRL_GET_TIMEOUT 5
+#define USB_CTRL_SET_TIMEOUT 5
/**