Commit 6406f3a9 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/linux/linux/BK/bleeding-2.5

into kroah.com:/home/linux/linux/BK/gregkh-2.5
parents 828bb5f4 b6f5eb6a
......@@ -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)
......
......@@ -94,10 +94,11 @@ HandSpring Visor, Palm USB, and Cli
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
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
......@@ -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 *
......
......@@ -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
......
......@@ -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
......
......@@ -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[] = {
......@@ -320,20 +326,6 @@ static struct symbolic_list urb_errlist[] = {
{ -1, NULL }
};
/**********************************************************************
* 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 @@ ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos)
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);
......
......@@ -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;
......
......@@ -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;
}
......
/* 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);
......
/* 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
......
/* 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 */
......
/* (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
......
......@@ -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
......
/*
* 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");
}
......
......@@ -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)");
/* --------------------------------------------------------------------- */
......@@ -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
......@@ -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
/*
* 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);
/*
* 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]);
}
}
......@@ -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");
......@@ -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);
......
......@@ -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 @@ static int serial_read_proc (char *page, char **start, off_t off, int count, int
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 @@ static int __init usb_serial_init(void)
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
......@@ -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)
......
......@@ -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) },
......
......@@ -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
......
......@@ -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 @@ static int whiteheat_attach (struct usb_serial *serial)
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 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
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");
......@@ -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
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment