Commit 523cc485 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/gregkh/linux/usb-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents e4569a84 097032ac
This file contains some additional information for the Philips webcams. This file contains some additional information for the Philips and OEM webcams.
E-mail: webcam@smcc.demon.nl Last updated: 2001-09-24 E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19
Site: http://www.smcc.demon.nl/webcam/
The main webpage for the Philips driver is http://www.smcc.demon.nl/webcam/.
It contains a lot of extra information, a FAQ, and the binary plugin As of this moment, the following cameras are supported:
'PWCX'. This plugin contains decompression routines that allow you to * Philips PCA645
use higher image sizes and framerates; in addition the webcam uses less * Philips PCA646
bandwidth on the USB bus (handy if you want to run more than 1 camera * Philips PCVC675
simultaneously). These routines fall under an NDA, and may therefor not be * Philips PCVC680
distributed as source; however, its use is completely optional. * Philips PCVC690
* Philips PCVC720/40
* Philips PCVC730
* Philips PCVC740
* Philips PCVC750
* Askey VC010
* Creative Labs Webcam 5
* Creative Labs Webcam Pro Ex
* Logitech QuickCam 3000 Pro
* Logitech QuickCam 4000 Pro
* Logitech QuickCam Notebook Pro
* Logitech QuickCam Zoom
* Logitech QuickCam Orbit
* Logitech QuickCam Sphere
* Samsung MPC-C10
* Samsung MPC-C30
* Sotec Afina Eye
* AME CU-001
* Visionite VCS-UM100
* Visionite VCS-UC300
The main webpage for the Philips driver is at the address above. It contains
a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
contains decompression routines that allow you to use higher image sizes and
framerates; in addition the webcam uses less bandwidth on the USB bus (handy
if you want to run more than 1 camera simultaneously). These routines fall
under a NDA, and may therefor not be distributed as source; however, its use
is completely optional.
You can build this code either into your kernel, or as a module. I recommend You can build this code either into your kernel, or as a module. I recommend
the latter, since it makes troubleshooting a lot easier. The built-in the latter, since it makes troubleshooting a lot easier. The built-in
...@@ -27,14 +54,14 @@ fps ...@@ -27,14 +54,14 @@ fps
Specifies the desired framerate. Is an integer in the range of 4-30. Specifies the desired framerate. Is an integer in the range of 4-30.
fbufs fbufs
This parameter specifies the number of internal buffers to use for storing This paramter specifies the number of internal buffers to use for storing
frames from the cam. This will help if the process that reads images from frames from the cam. This will help if the process that reads images from
the cam is a bit slow or momentarily busy. However, on slow machines it the cam is a bit slow or momentarely busy. However, on slow machines it
only introduces lag, so choose carefully. The default is 3, which is only introduces lag, so choose carefully. The default is 3, which is
reasonable. You can set it between 2 and 5. reasonable. You can set it between 2 and 5.
mbufs mbufs
This is an integer between 1 and 4. It will tell the module the number of This is an integer between 1 and 10. It will tell the module the number of
buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends.
The default is 2, which is adequate for most applications (double The default is 2, which is adequate for most applications (double
buffering). buffering).
...@@ -45,9 +72,9 @@ mbufs ...@@ -45,9 +72,9 @@ mbufs
slack when your program is behind. But you need a multi-threaded or slack when your program is behind. But you need a multi-threaded or
forked program to really take advantage of these buffers. forked program to really take advantage of these buffers.
The absolute maximum is 4, but don't set it too high! Every buffer takes The absolute maximum is 10, but don't set it too high! Every buffer takes
up 1.22 MB of RAM, so unless you have a lot of memory setting this to up 460 KB of RAM, so unless you have a lot of memory setting this to
something more than 2 is an absolute waste. This memory is only something more than 4 is an absolute waste. This memory is only
allocated during open(), so nothing is wasted when the camera is not in allocated during open(), so nothing is wasted when the camera is not in
use. use.
...@@ -75,8 +102,9 @@ compression (only useful with the plugin) ...@@ -75,8 +102,9 @@ compression (only useful with the plugin)
See the FAQ on the website for an overview of which modes require See the FAQ on the website for an overview of which modes require
compression. compression.
The compression parameter only applies to the Vesta & ToUCam cameras. The compression parameter does not apply to the 645 and 646 cameras
The 645 and 646 have fixed compression parameters. and OEM models derived from those (only a few). Most cams honour this
parameter.
leds leds
This settings takes 2 integers, that define the on/off time for the LED This settings takes 2 integers, that define the on/off time for the LED
...@@ -89,14 +117,17 @@ leds ...@@ -89,14 +117,17 @@ leds
leds=0,0 leds=0,0
the LED never goes on, making it suitable for silent survaillance. the LED never goes on, making it suitable for silent surveillance.
By default the camera's LED is on solid while in use, and turned off By default the camera's LED is on solid while in use, and turned off
when the camera is not used anymore. when the camera is not used anymore.
This parameter works only with the ToUCam range of cameras (730, 740, This parameter works only with the ToUCam range of cameras (720, 730, 740,
750). For other cameras this command is silently ignored, and the LED 750) and OEMs. For other cameras this command is silently ignored, and
cannot be controlled. the LED cannot be controlled.
Finally: this parameters does not take effect UNTIL the first time you
open the camera device. Until then, the LED remains on.
dev_hint dev_hint
A long standing problem with USB devices is their dynamic nature: you A long standing problem with USB devices is their dynamic nature: you
...@@ -126,7 +157,7 @@ dev_hint ...@@ -126,7 +157,7 @@ dev_hint
other cameras will get the first free other cameras will get the first free
available slot (see below). available slot (see below).
dev_hint=645:1,680=2 The PCA645 camera will get /dev/video1, dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1,
and a PCVC680 /dev/video2. and a PCVC680 /dev/video2.
dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber
...@@ -176,13 +207,16 @@ trace ...@@ -176,13 +207,16 @@ trace
64 0x40 Show viewport and image sizes Off 64 0x40 Show viewport and image sizes Off
128 0x80 PWCX debugging Off
For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, For example, to trace the open() & read() fuctions, sum 8 + 4 = 12,
so you would supply trace=12 during insmod or modprobe. If so you would supply trace=12 during insmod or modprobe. If
you want to turn the initialization and probing tracing off, set trace=0. you want to turn the initialization and probing tracing off, set trace=0.
The default value for trace is 35 (0x23). The default value for trace is 35 (0x23).
Example:
Example:
# modprobe pwc size=cif fps=15 power_save=1 # modprobe pwc size=cif fps=15 power_save=1
...@@ -192,7 +226,7 @@ cameras. Each camera has its own set of buffers. ...@@ -192,7 +226,7 @@ cameras. Each camera has its own set of buffers.
size and fps only specify defaults when you open() the device; this is to size and fps only specify defaults when you open() the device; this is to
accommodate some tools that don't set the size. You can change these accommodate some tools that don't set the size. You can change these
settings after open() with the Video4Linux ioctl() calls. The default of settings after open() with the Video4Linux ioctl() calls. The default of
defaults is QCIF size at 10 fps, BGR order. defaults is QCIF size at 10 fps.
The compression parameter is semiglobal; it sets the initial compression The compression parameter is semiglobal; it sets the initial compression
preference for all camera's, but this parameter can be set per camera with preference for all camera's, but this parameter can be set per camera with
...@@ -200,4 +234,3 @@ the VIDIOCPWCSCQUAL ioctl() call. ...@@ -200,4 +234,3 @@ the VIDIOCPWCSCQUAL ioctl() call.
All parameters are optional. All parameters are optional.
This diff is collapsed.
...@@ -86,17 +86,23 @@ struct acm { ...@@ -86,17 +86,23 @@ struct acm {
struct usb_interface *data; /* data interface */ struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */ struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */
struct acm_line line; /* line coding (bits, stop, parity) */ struct acm_line line; /* line coding (bits, stop, parity) */
struct work_struct work; /* work queue entry for line discipline waking up */ struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */ struct tasklet_struct bh; /* rx processing */
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int used; /* someone has this acm's device open */ unsigned int used; /* someone has this acm's device open */
unsigned int minor; /* acm minor number */ unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */ unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */ unsigned char clocal; /* termios CLOCAL */
unsigned char ready_for_write; /* write urb can be used */ unsigned char ready_for_write; /* write urb can be used */
unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */
unsigned int ctrl_caps; /* control capabilities from the class specific header */
}; };
/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ /* "Union Functional Descriptor" from CDC spec 5.2.3.X */
...@@ -110,6 +116,12 @@ struct union_desc { ...@@ -110,6 +116,12 @@ struct union_desc {
/* ... and there could be other slave interfaces */ /* ... and there could be other slave interfaces */
} __attribute__ ((packed)); } __attribute__ ((packed));
/* class specific descriptor types */
#define CDC_CALL_MANAGEMENT_TYPE 0x01
#define CDC_AC_MANAGEMENT_TYPE 0x02
#define CDC_UNION_TYPE 0x06 #define CDC_UNION_TYPE 0x06
#define CDC_COUNTRY_TYPE 0x07
#define CDC_DATA_INTERFACE_TYPE 0x0a #define CDC_DATA_INTERFACE_TYPE 0x0a
...@@ -1034,7 +1034,7 @@ static inline void show_string(struct usb_device *udev, char *id, int index) ...@@ -1034,7 +1034,7 @@ static inline void show_string(struct usb_device *udev, char *id, int index)
{} {}
#endif #endif
/* /**
* usb_new_device - perform initial device setup (usbcore-internal) * usb_new_device - perform initial device setup (usbcore-internal)
* @udev: newly addressed device (in ADDRESS state) * @udev: newly addressed device (in ADDRESS state)
* *
...@@ -1333,13 +1333,14 @@ static int hub_set_address(struct usb_device *udev) ...@@ -1333,13 +1333,14 @@ static int hub_set_address(struct usb_device *udev)
return retval; return retval;
} }
/* reset device, (re)assign address, get device descriptor. /* Reset device, (re)assign address, get device descriptor.
* device connection is stable, no more debouncing needed. * Device connection must be stable, no more debouncing needed.
* returns device in USB_STATE_ADDRESS, except on error. * Returns device in USB_STATE_ADDRESS, except on error.
* on error return, device is no longer usable (ref dropped).
* *
* caller owns dev->serialize for the device, guarding against * If this is called for an already-existing device (as part of
* config changes and disconnect processing. * usb_reset_device), the caller must own the device lock. For a
* newly detected device that is not accessible through any global
* pointers, it's not necessary to lock the device.
*/ */
static int static int
hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port) hub_port_init (struct usb_device *hdev, struct usb_device *udev, int port)
...@@ -1571,6 +1572,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1571,6 +1572,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
/* Disconnect any existing devices under this port */ /* Disconnect any existing devices under this port */
if (hdev->children[port]) if (hdev->children[port])
usb_disconnect(&hdev->children[port]); usb_disconnect(&hdev->children[port]);
clear_bit(port, hub->change_bits);
if (portchange & USB_PORT_STAT_C_CONNECTION) { if (portchange & USB_PORT_STAT_C_CONNECTION) {
status = hub_port_debounce(hdev, port); status = hub_port_debounce(hdev, port);
...@@ -1785,12 +1787,15 @@ static void hub_events(void) ...@@ -1785,12 +1787,15 @@ static void hub_events(void)
/* deal with port status changes */ /* deal with port status changes */
for (i = 0; i < hub->descriptor->bNbrPorts; i++) { for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
if (!test_and_clear_bit(i+1, hub->event_bits)) connect_change = test_bit(i, hub->change_bits);
if (!test_and_clear_bit(i+1, hub->event_bits) &&
!connect_change)
continue; continue;
ret = hub_port_status(hdev, i, &portstatus, &portchange);
ret = hub_port_status(hdev, i,
&portstatus, &portchange);
if (ret < 0) if (ret < 0)
continue; continue;
connect_change = 0;
if (portchange & USB_PORT_STAT_C_CONNECTION) { if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, clear_port_feature(hdev,
...@@ -1819,7 +1824,7 @@ static void hub_events(void) ...@@ -1819,7 +1824,7 @@ static void hub_events(void)
dev_err (hub_dev, dev_err (hub_dev,
"port %i " "port %i "
"disabled by hub (EMI?), " "disabled by hub (EMI?), "
"re-enabling...", "re-enabling...\n",
i + 1); i + 1);
connect_change = 1; connect_change = 1;
} }
...@@ -1996,23 +2001,34 @@ static int config_descriptors_changed(struct usb_device *udev) ...@@ -1996,23 +2001,34 @@ static int config_descriptors_changed(struct usb_device *udev)
!= 0) { != 0) {
dev_dbg(&udev->dev, "config index %d changed (#%d)\n", dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
index, buf->bConfigurationValue); index, buf->bConfigurationValue);
/* FIXME enable this when we can re-enumerate after reset; break;
* until then DFU-ish drivers need this and other workarounds
*/
// break;
} }
} }
kfree(buf); kfree(buf);
return index != udev->descriptor.bNumConfigurations; return index != udev->descriptor.bNumConfigurations;
} }
/* /**
* usb_reset_devce - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
*
* WARNING - don't reset any device unless drivers for all of its * WARNING - don't reset any device unless drivers for all of its
* interfaces are expecting that reset! Maybe some driver->reset() * interfaces are expecting that reset! Maybe some driver->reset()
* method should eventually help ensure sufficient cooperation. * method should eventually help ensure sufficient cooperation.
* *
* This is the same as usb_reset_device() except that the caller * Do a port reset, reassign the device's address, and establish its
* already holds dev->serialize. For example, it's safe to use * former operating configuration. If the reset fails, or the device's
* descriptors change from their values before the reset, or the original
* configuration and altsettings cannot be restored, a flag will be set
* telling khubd to pretend the device has been disconnected and then
* re-connected. All drivers will be unbound, and the device will be
* re-enumerated and probed all over again.
*
* Returns 0 if the reset succeeded, -ENODEV if the device has been
* flagged for logical disconnection, or some other negative error code
* if the reset wasn't even attempted.
*
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware. * this from a driver probe() routine after downloading new firmware.
*/ */
int __usb_reset_device(struct usb_device *udev) int __usb_reset_device(struct usb_device *udev)
...@@ -2020,13 +2036,23 @@ int __usb_reset_device(struct usb_device *udev) ...@@ -2020,13 +2036,23 @@ int __usb_reset_device(struct usb_device *udev)
struct usb_device *parent = udev->parent; struct usb_device *parent = udev->parent;
struct usb_device_descriptor descriptor = udev->descriptor; struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret, port = -1; int i, ret, port = -1;
struct usb_hub *hub;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
udev->state);
return -EINVAL;
}
/* FIXME: This should be legal for regular hubs. Root hubs may
* have special requirements. */
if (udev->maxchild) { if (udev->maxchild) {
/* this requires hub- or hcd-specific logic; /* this requires hub- or hcd-specific logic;
* see hub_reset() and OHCI hc_restart() * see hub_reset() and OHCI hc_restart()
*/ */
dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__); dev_dbg(&udev->dev, "%s for hub!\n", __FUNCTION__);
return -EINVAL; return -EISDIR;
} }
for (i = 0; i < parent->maxchild; i++) for (i = 0; i < parent->maxchild; i++)
...@@ -2035,8 +2061,11 @@ int __usb_reset_device(struct usb_device *udev) ...@@ -2035,8 +2061,11 @@ int __usb_reset_device(struct usb_device *udev)
break; break;
} }
if (port < 0) if (port < 0) {
/* If this ever happens, it's very bad */
dev_err(&udev->dev, "Can't locate device's port!\n");
return -ENOENT; return -ENOENT;
}
ret = hub_port_init(parent, udev, port); ret = hub_port_init(parent, udev, port);
if (ret < 0) if (ret < 0)
...@@ -2088,8 +2117,18 @@ int __usb_reset_device(struct usb_device *udev) ...@@ -2088,8 +2117,18 @@ int __usb_reset_device(struct usb_device *udev)
return 0; return 0;
re_enumerate: re_enumerate:
/* FIXME make some task re-enumerate; don't just mark unusable */
hub_port_disable(parent, port); hub_port_disable(parent, port);
hub = usb_get_intfdata(parent->actconfig->interface[0]);
set_bit(port, hub->change_bits);
spin_lock_irq(&hub_event_lock);
if (list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irq(&hub_event_lock);
return -ENODEV; return -ENODEV;
} }
EXPORT_SYMBOL(__usb_reset_device); EXPORT_SYMBOL(__usb_reset_device);
......
...@@ -204,6 +204,8 @@ struct usb_hub { ...@@ -204,6 +204,8 @@ struct usb_hub {
struct list_head event_list; /* hubs w/data or errs ready */ struct list_head event_list; /* hubs w/data or errs ready */
unsigned long event_bits[1]; /* status change bitmask */ unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short! #error event_bits[] is too short!
#endif #endif
......
...@@ -146,8 +146,6 @@ struct dummy { ...@@ -146,8 +146,6 @@ struct dummy {
u8 fifo_buf [FIFO_SIZE]; u8 fifo_buf [FIFO_SIZE];
u16 devstatus; u16 devstatus;
struct hcd_dev *hdev;
/* /*
* MASTER/HOST side support * MASTER/HOST side support
*/ */
...@@ -159,6 +157,8 @@ struct dummy { ...@@ -159,6 +157,8 @@ struct dummy {
struct completion released; struct completion released;
unsigned resuming:1; unsigned resuming:1;
unsigned long re_timeout; unsigned long re_timeout;
struct usb_device *udev;
}; };
static struct dummy *the_controller; static struct dummy *the_controller;
...@@ -739,11 +739,9 @@ stop_activity (struct dummy *dum, struct usb_gadget_driver *driver) ...@@ -739,11 +739,9 @@ stop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
struct dummy_ep *ep; struct dummy_ep *ep;
/* prevent any more requests */ /* prevent any more requests */
dum->hdev = 0;
dum->address = 0; dum->address = 0;
/* this might not succeed ... */ /* The timer is left running so that outstanding URBs can fail */
del_timer (&dum->timer);
/* nuke any pending requests first, so driver i/o is quiesced */ /* nuke any pending requests first, so driver i/o is quiesced */
list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
...@@ -784,7 +782,6 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) ...@@ -784,7 +782,6 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
driver_unregister (&driver->driver); driver_unregister (&driver->driver);
del_timer_sync (&dum->timer);
return 0; return 0;
} }
EXPORT_SYMBOL (usb_gadget_unregister_driver); EXPORT_SYMBOL (usb_gadget_unregister_driver);
...@@ -825,7 +822,12 @@ static int dummy_urb_enqueue ( ...@@ -825,7 +822,12 @@ static int dummy_urb_enqueue (
dum = container_of (hcd, struct dummy, hcd); dum = container_of (hcd, struct dummy, hcd);
spin_lock_irqsave (&dum->lock, flags); spin_lock_irqsave (&dum->lock, flags);
dum->hdev = urb->dev->hcpriv; if (!dum->udev) {
dum->udev = urb->dev;
usb_get_dev (dum->udev);
} else if (unlikely (dum->udev != urb->dev))
dev_err (hardware, "usb_device address has changed!\n");
urb->hcpriv = dum; urb->hcpriv = dum;
if (usb_pipetype (urb->pipe) == PIPE_CONTROL) if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */ urb->error_count = 1; /* mark as a new urb */
...@@ -1032,17 +1034,12 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) ...@@ -1032,17 +1034,12 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
static void dummy_timer (unsigned long _dum) static void dummy_timer (unsigned long _dum)
{ {
struct dummy *dum = (struct dummy *) _dum; struct dummy *dum = (struct dummy *) _dum;
struct hcd_dev *hdev = dum->hdev; struct hcd_dev *hdev;
struct list_head *entry, *tmp; struct list_head *entry, *tmp;
unsigned long flags; unsigned long flags;
int limit, total; int limit, total;
int i; int i;
if (!hdev) {
dev_err (hardware, "timer fired with device gone?\n");
return;
}
/* simplistic model for one frame's bandwidth */ /* simplistic model for one frame's bandwidth */
switch (dum->gadget.speed) { switch (dum->gadget.speed) {
case USB_SPEED_LOW: case USB_SPEED_LOW:
...@@ -1063,6 +1060,14 @@ static void dummy_timer (unsigned long _dum) ...@@ -1063,6 +1060,14 @@ static void dummy_timer (unsigned long _dum)
/* look at each urb queued by the host side driver */ /* look at each urb queued by the host side driver */
spin_lock_irqsave (&dum->lock, flags); spin_lock_irqsave (&dum->lock, flags);
if (!dum->udev) {
dev_err (hardware, "timer fired with no URBs pending?\n");
spin_unlock_irqrestore (&dum->lock, flags);
return;
}
hdev = dum->udev->hcpriv;
for (i = 0; i < DUMMY_ENDPOINTS; i++) { for (i = 0; i < DUMMY_ENDPOINTS; i++) {
if (!ep_name [i]) if (!ep_name [i])
break; break;
...@@ -1334,7 +1339,11 @@ static void dummy_timer (unsigned long _dum) ...@@ -1334,7 +1339,11 @@ static void dummy_timer (unsigned long _dum)
/* want a 1 msec delay here */ /* want a 1 msec delay here */
if (!list_empty (&hdev->urb_list)) if (!list_empty (&hdev->urb_list))
mod_timer (&dum->timer, jiffies + 1); mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
else {
usb_put_dev (dum->udev);
dum->udev = NULL;
}
spin_unlock_irqrestore (&dum->lock, flags); spin_unlock_irqrestore (&dum->lock, flags);
} }
...@@ -1571,10 +1580,12 @@ show_urbs (struct device *dev, char *buf) ...@@ -1571,10 +1580,12 @@ show_urbs (struct device *dev, char *buf)
struct urb *urb; struct urb *urb;
size_t size = 0; size_t size = 0;
unsigned long flags; unsigned long flags;
struct hcd_dev *hdev;
spin_lock_irqsave (&dum->lock, flags); spin_lock_irqsave (&dum->lock, flags);
if (dum->hdev) { if (dum->udev) {
list_for_each_entry (urb, &dum->hdev->urb_list, urb_list) { hdev = dum->udev->hcpriv;
list_for_each_entry (urb, &hdev->urb_list, urb_list) {
size_t temp; size_t temp;
temp = show_urb (buf, PAGE_SIZE - size, urb); temp = show_urb (buf, PAGE_SIZE - size, urb);
......
...@@ -234,7 +234,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); ...@@ -234,7 +234,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
/* For CDC-incapable hardware, choose the simple cdc subset. /* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this. * Anything that talks bulk (without notable bugs) can do this.
*/ */
#ifdef CONFIG_USB_GADGET_PXA #ifdef CONFIG_USB_GADGET_PXA2XX
#define DEV_CONFIG_SUBSET #define DEV_CONFIG_SUBSET
#endif #endif
...@@ -1597,6 +1597,8 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1597,6 +1597,8 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* respond with data transfer before status phase? */ /* respond with data transfer before status phase? */
if (value >= 0) { if (value >= 0) {
req->length = value; req->length = value;
req->zero = value < ctrl->wLength
&& (value % gadget->ep0->maxpacket) == 0;
value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
if (value < 0) { if (value < 0) {
DEBUG (dev, "ep_queue --> %d\n", value); DEBUG (dev, "ep_queue --> %d\n", value);
...@@ -2373,6 +2375,7 @@ eth_bind (struct usb_gadget *gadget) ...@@ -2373,6 +2375,7 @@ eth_bind (struct usb_gadget *gadget)
EP_OUT_NAME = ep->name; EP_OUT_NAME = ep->name;
ep->driver_data = ep; /* claim */ ep->driver_data = ep; /* claim */
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* CDC Ethernet control interface doesn't require a status endpoint. /* CDC Ethernet control interface doesn't require a status endpoint.
* Since some hosts expect one, try to allocate one anyway. * Since some hosts expect one, try to allocate one anyway.
*/ */
...@@ -2386,13 +2389,12 @@ eth_bind (struct usb_gadget *gadget) ...@@ -2386,13 +2389,12 @@ eth_bind (struct usb_gadget *gadget)
"can't run RNDIS on %s\n", "can't run RNDIS on %s\n",
gadget->name); gadget->name);
return -ENODEV; return -ENODEV;
#ifdef DEV_CONFIG_CDC
} else if (cdc) { } else if (cdc) {
control_intf.bNumEndpoints = 0; control_intf.bNumEndpoints = 0;
/* FIXME remove endpoint from descriptor list */ /* FIXME remove endpoint from descriptor list */
#endif
} }
} }
#endif
/* one config: cdc, else minimal subset */ /* one config: cdc, else minimal subset */
if (!cdc) { if (!cdc) {
...@@ -2446,7 +2448,7 @@ eth_bind (struct usb_gadget *gadget) ...@@ -2446,7 +2448,7 @@ eth_bind (struct usb_gadget *gadget)
/* Module params for these addresses should come from ID proms. /* Module params for these addresses should come from ID proms.
* The host side address is used with CDC and RNDIS, and commonly * The host side address is used with CDC and RNDIS, and commonly
* end ups in a persistent config database. * ends up in a persistent config database.
*/ */
get_ether_addr(dev_addr, net->dev_addr); get_ether_addr(dev_addr, net->dev_addr);
if (cdc || rndis) { if (cdc || rndis) {
......
...@@ -1465,6 +1465,8 @@ static int fsg_setup(struct usb_gadget *gadget, ...@@ -1465,6 +1465,8 @@ static int fsg_setup(struct usb_gadget *gadget,
/* Respond with data/status or defer until later? */ /* Respond with data/status or defer until later? */
if (rc >= 0 && rc != DELAYED_STATUS) { if (rc >= 0 && rc != DELAYED_STATUS) {
fsg->ep0req->length = rc; fsg->ep0req->length = rc;
fsg->ep0req->zero = rc < ctrl->wLength
&& (rc % gadget->ep0->maxpacket) == 0;
fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
"ep0-in" : "ep0-out"); "ep0-in" : "ep0-out");
rc = ep0_queue(fsg); rc = ep0_queue(fsg);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
*/ */
#define DEBUG 1 /* data to help fault diagnosis */ // #define DEBUG /* data to help fault diagnosis */
// #define VERBOSE /* extra debug messages (success too) */ // #define VERBOSE /* extra debug messages (success too) */
#include <linux/init.h> #include <linux/init.h>
...@@ -44,7 +44,8 @@ ...@@ -44,7 +44,8 @@
* The gadgetfs API maps each endpoint to a file descriptor so that you * The gadgetfs API maps each endpoint to a file descriptor so that you
* can use standard synchronous read/write calls for I/O. There's some * can use standard synchronous read/write calls for I/O. There's some
* O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode * O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode
* drivers show how this works in practice. * drivers show how this works in practice. You can also use AIO to
* eliminate I/O gaps between requests, to help when streaming data.
* *
* Key parts that must be USB-specific are protocols defining how the * Key parts that must be USB-specific are protocols defining how the
* read/write operations relate to the hardware state machines. There * read/write operations relate to the hardware state machines. There
...@@ -70,7 +71,7 @@ ...@@ -70,7 +71,7 @@
*/ */
#define DRIVER_DESC "USB Gadget filesystem" #define DRIVER_DESC "USB Gadget filesystem"
#define DRIVER_VERSION "20 Aug 2003" #define DRIVER_VERSION "18 Nov 2003"
static const char driver_desc [] = DRIVER_DESC; static const char driver_desc [] = DRIVER_DESC;
static const char shortname [] = "gadgetfs"; static const char shortname [] = "gadgetfs";
...@@ -539,6 +540,177 @@ static int ep_ioctl (struct inode *inode, struct file *fd, ...@@ -539,6 +540,177 @@ static int ep_ioctl (struct inode *inode, struct file *fd,
return status; return status;
} }
/*----------------------------------------------------------------------*/
/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
void *buf;
char __user *ubuf;
unsigned actual;
};
static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
{
struct kiocb_priv *priv = (void *) &iocb->private;
struct ep_data *epdata;
int value;
local_irq_disable();
epdata = priv->epdata;
// spin_lock(&epdata->dev->lock);
kiocbSetCancelled(iocb);
if (likely(epdata && epdata->ep && priv->req))
value = usb_ep_dequeue (epdata->ep, priv->req);
else
value = -EINVAL;
// spin_unlock(&epdata->dev->lock);
local_irq_enable();
aio_put_req(iocb);
return value;
}
static long ep_aio_read_retry(struct kiocb *iocb)
{
struct kiocb_priv *priv = (void *) &iocb->private;
int status = priv->actual;
/* we "retry" to get the right mm context for this: */
status = copy_to_user(priv->ubuf, priv->buf, priv->actual);
if (unlikely(0 != status))
status = -EFAULT;
else
status = priv->actual;
kfree(priv->buf);
aio_put_req(iocb);
return status;
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct kiocb *iocb = req->context;
struct kiocb_priv *priv = (void *) &iocb->private;
struct ep_data *epdata = priv->epdata;
/* lock against disconnect (and ideally, cancel) */
spin_lock(&epdata->dev->lock);
priv->req = 0;
priv->epdata = 0;
if (NULL == iocb->ki_retry
|| unlikely(0 == req->actual)
|| unlikely(kiocbIsCancelled(iocb))) {
kfree(req->buf);
/* aio_complete() reports bytes-transferred _and_ faults */
if (unlikely(kiocbIsCancelled(iocb)))
aio_put_req(iocb);
else
aio_complete(iocb,
req->actual ? req->actual : req->status,
req->status);
} else {
/* retry() won't report both; so we hide some faults */
if (unlikely(0 != req->status))
DBG(epdata->dev, "%s fault %d len %d\n",
ep->name, req->status, req->actual);
priv->buf = req->buf;
priv->actual = req->actual;
kick_iocb(iocb);
}
spin_unlock(&epdata->dev->lock);
usb_ep_free_request(ep, req);
put_ep(epdata);
}
static ssize_t
ep_aio_rwtail(struct kiocb *iocb, char *buf, size_t len, struct ep_data *epdata)
{
struct kiocb_priv *priv = (void *) &iocb->private;
struct usb_request *req;
ssize_t value;
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
kfree(buf);
return value;
}
iocb->ki_cancel = ep_aio_cancel;
get_ep(epdata);
priv->epdata = epdata;
priv->actual = 0;
/* each kiocb is coupled to one usb_request, but we can't
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
if (likely(epdata->ep)) {
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
if (likely(req)) {
priv->req = req;
req->buf = buf;
req->length = len;
req->complete = ep_aio_complete;
req->context = iocb;
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
if (unlikely(0 != value))
usb_ep_free_request(epdata->ep, req);
} else
value = -EAGAIN;
} else
value = -ENODEV;
spin_unlock_irq(&epdata->dev->lock);
up(&epdata->lock);
if (unlikely(value))
put_ep(epdata);
else
value = -EIOCBQUEUED;
return value;
}
static ssize_t
ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o)
{
struct kiocb_priv *priv = (void *) &iocb->private;
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
iocb->ki_retry = ep_aio_read_retry;
priv->ubuf = ubuf;
return ep_aio_rwtail(iocb, buf, len, epdata);
}
static ssize_t
ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
return -EINVAL;
buf = kmalloc(len, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
if (unlikely(copy_from_user(buf, ubuf, len) != 0)) {
kfree(buf);
return -EFAULT;
}
return ep_aio_rwtail(iocb, buf, len, epdata);
}
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */ /* used after endpoint configuration */
static struct file_operations ep_io_operations = { static struct file_operations ep_io_operations = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -547,8 +719,8 @@ static struct file_operations ep_io_operations = { ...@@ -547,8 +719,8 @@ static struct file_operations ep_io_operations = {
.ioctl = ep_ioctl, .ioctl = ep_ioctl,
.release = ep_release, .release = ep_release,
// .aio_read = ep_aio_read, .aio_read = ep_aio_read,
// .aio_write = ep_aio_write, .aio_write = ep_aio_write,
}; };
/* ENDPOINT INITIALIZATION /* ENDPOINT INITIALIZATION
...@@ -1320,6 +1492,8 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1320,6 +1492,8 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* proceed with data transfer and status phases? */ /* proceed with data transfer and status phases? */
if (value >= 0 && dev->state != STATE_SETUP) { if (value >= 0 && dev->state != STATE_SETUP) {
req->length = value; req->length = value;
req->zero = value < ctrl->wLength
&& (value % gadget->ep0->maxpacket) == 0;
value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
if (value < 0) { if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value); DBG (dev, "ep_queue --> %d\n", value);
......
...@@ -1573,6 +1573,8 @@ static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctr ...@@ -1573,6 +1573,8 @@ static int gs_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctr
/* respond with data transfer before status phase? */ /* respond with data transfer before status phase? */
if (ret >= 0) { if (ret >= 0) {
req->length = ret; req->length = ret;
req->zero = ret < ctrl->wLength
&& (ret % gadget->ep0->maxpacket) == 0;
ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR printk(KERN_ERR
......
...@@ -1037,6 +1037,8 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1037,6 +1037,8 @@ zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* respond with data transfer before status phase? */ /* respond with data transfer before status phase? */
if (value >= 0) { if (value >= 0) {
req->length = value; req->length = value;
req->zero = value < ctrl->wLength
&& (value % gadget->ep0->maxpacket) == 0;
value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
if (value < 0) { if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value); DBG (dev, "ep_queue --> %d\n", value);
......
...@@ -425,7 +425,6 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -425,7 +425,6 @@ static int ehci_start (struct usb_hcd *hcd)
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
return retval; return retval;
} }
writel (INTR_MASK, &ehci->regs->intr_enable);
writel (ehci->periodic_dma, &ehci->regs->frame_list); writel (ehci->periodic_dma, &ehci->regs->frame_list);
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
...@@ -531,7 +530,8 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -531,7 +530,8 @@ static int ehci_start (struct usb_hcd *hcd)
/* /*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is * are explicitly handed to companion controller(s), so no TT is
* involved with the root hub. * involved with the root hub. (Except where one is integrated,
* and there's no companion controller unless maybe for USB OTG.)
*/ */
ehci->reboot_notifier.notifier_call = ehci_reboot; ehci->reboot_notifier.notifier_call = ehci_reboot;
register_reboot_notifier (&ehci->reboot_notifier); register_reboot_notifier (&ehci->reboot_notifier);
...@@ -563,6 +563,8 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -563,6 +563,8 @@ static int ehci_start (struct usb_hcd *hcd)
goto done2; goto done2;
} }
writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
create_debug_files (ehci); create_debug_files (ehci);
return 0; return 0;
...@@ -573,6 +575,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -573,6 +575,7 @@ static int ehci_start (struct usb_hcd *hcd)
static void ehci_stop (struct usb_hcd *hcd) static void ehci_stop (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u8 rh_ports, port;
ehci_dbg (ehci, "stop\n"); ehci_dbg (ehci, "stop\n");
...@@ -584,7 +587,16 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -584,7 +587,16 @@ static void ehci_stop (struct usb_hcd *hcd)
return; return;
} }
del_timer_sync (&ehci->watchdog); del_timer_sync (&ehci->watchdog);
/* Turn off port power on all root hub ports. */
rh_ports = HCS_N_PORTS (ehci->hcs_params);
for (port = 1; port <= rh_ports; port++) {
ehci_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER,
port, NULL, 0);
}
ehci_reset (ehci); ehci_reset (ehci);
writel (0, &ehci->regs->intr_enable);
/* let companion controllers work when we aren't */ /* let companion controllers work when we aren't */
writel (0, &ehci->regs->configured_flag); writel (0, &ehci->regs->configured_flag);
...@@ -704,12 +716,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -704,12 +716,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
status = readl (&ehci->regs->status); status = readl (&ehci->regs->status);
/* shared irq */
if (status == 0) {
spin_unlock (&ehci->lock);
return IRQ_NONE;
}
/* e.g. cardbus physical eject */ /* e.g. cardbus physical eject */
if (status == ~(u32) 0) { if (status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n"); ehci_dbg (ehci, "device removed\n");
...@@ -717,8 +723,10 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -717,8 +723,10 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
} }
status &= INTR_MASK; status &= INTR_MASK;
if (!status) /* irq sharing? */ if (!status) { /* irq sharing? */
goto done; spin_unlock(&ehci->lock);
return IRQ_NONE;
}
/* clear (just) interrupts */ /* clear (just) interrupts */
writel (status, &ehci->regs->status); writel (status, &ehci->regs->status);
...@@ -789,7 +797,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -789,7 +797,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
if (bh) if (bh)
ehci_work (ehci, regs); ehci_work (ehci, regs);
done:
spin_unlock (&ehci->lock); spin_unlock (&ehci->lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
...@@ -248,7 +248,7 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len) ...@@ -248,7 +248,7 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
{ {
char *out = buf; char *out = buf;
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
unsigned short usbcmd, usbstat, usbint, usbfrnum; unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd; unsigned int flbaseadd;
unsigned char sof; unsigned char sof;
......
...@@ -1762,7 +1762,7 @@ static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) ...@@ -1762,7 +1762,7 @@ static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
unsigned short status; unsigned short status;
struct list_head *tmp, *head; struct list_head *tmp, *head;
unsigned int age; unsigned int age;
...@@ -1835,7 +1835,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -1835,7 +1835,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
static void reset_hc(struct uhci_hcd *uhci) static void reset_hc(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
/* Turn off PIRQ, SMI, and all interrupts. This also turns off /* Turn off PIRQ, SMI, and all interrupts. This also turns off
* the BIOS's USB Legacy Support. * the BIOS's USB Legacy Support.
...@@ -1856,7 +1856,7 @@ static void reset_hc(struct uhci_hcd *uhci) ...@@ -1856,7 +1856,7 @@ static void reset_hc(struct uhci_hcd *uhci)
static void suspend_hc(struct uhci_hcd *uhci) static void suspend_hc(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
uhci->state = UHCI_SUSPENDED; uhci->state = UHCI_SUSPENDED;
...@@ -1866,7 +1866,7 @@ static void suspend_hc(struct uhci_hcd *uhci) ...@@ -1866,7 +1866,7 @@ static void suspend_hc(struct uhci_hcd *uhci)
static void wakeup_hc(struct uhci_hcd *uhci) static void wakeup_hc(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
switch (uhci->state) { switch (uhci->state) {
case UHCI_SUSPENDED: /* Start the resume */ case UHCI_SUSPENDED: /* Start the resume */
...@@ -1906,7 +1906,7 @@ static void wakeup_hc(struct uhci_hcd *uhci) ...@@ -1906,7 +1906,7 @@ static void wakeup_hc(struct uhci_hcd *uhci)
static int ports_active(struct uhci_hcd *uhci) static int ports_active(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
int connection = 0; int connection = 0;
int i; int i;
...@@ -1918,7 +1918,7 @@ static int ports_active(struct uhci_hcd *uhci) ...@@ -1918,7 +1918,7 @@ static int ports_active(struct uhci_hcd *uhci)
static int suspend_allowed(struct uhci_hcd *uhci) static int suspend_allowed(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
int i; int i;
if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL) if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
...@@ -1983,7 +1983,7 @@ static void hc_state_transitions(struct uhci_hcd *uhci) ...@@ -1983,7 +1983,7 @@ static void hc_state_transitions(struct uhci_hcd *uhci)
static void start_hc(struct uhci_hcd *uhci) static void start_hc(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
int timeout = 1000; int timeout = 1000;
/* /*
...@@ -2261,6 +2261,11 @@ static int uhci_start(struct usb_hcd *hcd) ...@@ -2261,6 +2261,11 @@ static int uhci_start(struct usb_hcd *hcd)
uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle); uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle);
} }
/*
* Some architectures require a full mb() to enforce completion of
* the memory writes above before the I/O transfers in start_hc().
*/
mb();
start_hc(uhci); start_hc(uhci);
init_stall_timer(hcd); init_stall_timer(hcd);
......
...@@ -36,7 +36,7 @@ static __u8 root_hub_hub_des[] = ...@@ -36,7 +36,7 @@ static __u8 root_hub_hub_des[] =
static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned int io_addr = uhci->io_addr; unsigned long io_addr = uhci->io_addr;
int i; int i;
*buf = 0; *buf = 0;
...@@ -69,7 +69,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -69,7 +69,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int status, retval = 0, len = 0; int status, retval = 0, len = 0;
unsigned int port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1); unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
__u16 wPortChange, wPortStatus; __u16 wPortChange, wPortStatus;
switch (typeReq) { switch (typeReq) {
......
...@@ -110,7 +110,7 @@ int hid_tmff_init(struct hid_device *hid) ...@@ -110,7 +110,7 @@ int hid_tmff_init(struct hid_device *hid)
{ {
struct tmff_device *private; struct tmff_device *private;
struct list_head *pos; struct list_head *pos;
struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL); private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL);
if (!private) if (!private)
......
...@@ -108,7 +108,7 @@ config USB_OV511 ...@@ -108,7 +108,7 @@ config USB_OV511
config USB_PWC config USB_PWC
tristate "USB Philips Cameras" tristate "USB Philips Cameras"
depends on USB && VIDEO_DEV && CONFIG_BROKEN depends on USB && VIDEO_DEV
---help--- ---help---
Say Y or M here if you want to use one of these Philips & OEM Say Y or M here if you want to use one of these Philips & OEM
webcams: webcams:
......
...@@ -324,7 +324,7 @@ static void resubmit_urb(struct uvd *uvd, struct urb *urb) ...@@ -324,7 +324,7 @@ static void resubmit_urb(struct uvd *uvd, struct urb *urb)
} }
urb->dev = uvd->dev; urb->dev = uvd->dev;
urb->status = 0; urb->status = 0;
ret = usb_submit_urb(urb, GFP_KERNEL); ret = usb_submit_urb(urb, GFP_ATOMIC);
DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length); DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length);
if(ret) if(ret)
err("usb_submit_urb error (%d)", ret); err("usb_submit_urb error (%d)", ret);
......
This diff is collapsed.
This diff is collapsed.
#ifndef PWC_IOCTL_H #ifndef PWC_IOCTL_H
#define PWC_IOCTL_H #define PWC_IOCTL_H
/* (C) 2001-2003 Nemosoft Unv. webcam@smcc.demon.nl /* (C) 2001-2004 Nemosoft Unv. webcam@smcc.demon.nl
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -18,13 +18,18 @@ ...@@ -18,13 +18,18 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
/* This is pwc-ioctl.h belonging to PWC 8.10 */ /* This is pwc-ioctl.h belonging to PWC 8.12.1
It contains structures and defines to communicate from user space
directly to the driver.
*/
/* /*
Changes Changes
2001/08/03 Alvarado Added ioctl constants to access methods for 2001/08/03 Alvarado Added ioctl constants to access methods for
changing white balance and red/blue gains changing white balance and red/blue gains
2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
2003/12/13 Nemosft Unv. Some modifications to make interfacing to
PWCX easier
*/ */
/* These are private ioctl() commands, specific for the Philips webcams. /* These are private ioctl() commands, specific for the Philips webcams.
...@@ -40,6 +45,14 @@ ...@@ -40,6 +45,14 @@
*/ */
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
#define PSZ_QSIF 0x01
#define PSZ_QCIF 0x02
#define PSZ_SIF 0x03
#define PSZ_CIF 0x04
#define PSZ_VGA 0x05
#define PSZ_MAX 6
/* The frame rate is encoded in the video_window.flags parameter using /* The frame rate is encoded in the video_window.flags parameter using
...@@ -55,13 +68,25 @@ ...@@ -55,13 +68,25 @@
#define PWC_FPS_SNAPSHOT 0x00400000 #define PWC_FPS_SNAPSHOT 0x00400000
/* structure for transfering x & y coordinates */
struct pwc_coord
{
int x, y; /* guess what */
int size; /* size, or offset */
};
/* Used with VIDIOCPWCPROBE */
struct pwc_probe struct pwc_probe
{ {
char name[32]; char name[32];
int type; int type;
}; };
struct pwc_serial
{
char serial[30]; /* String with serial number. Contains terminating 0 */
};
/* pwc_whitebalance.mode values */ /* pwc_whitebalance.mode values */
#define PWC_WB_INDOOR 0 #define PWC_WB_INDOOR 0
...@@ -78,7 +103,6 @@ struct pwc_probe ...@@ -78,7 +103,6 @@ struct pwc_probe
otherwise undefined. otherwise undefined.
'read_red' and 'read_blue' are read-only. 'read_red' and 'read_blue' are read-only.
*/ */
struct pwc_whitebalance struct pwc_whitebalance
{ {
int mode; int mode;
...@@ -117,7 +141,7 @@ struct pwc_imagesize ...@@ -117,7 +141,7 @@ struct pwc_imagesize
#define PWC_MPT_TILT 0x02 #define PWC_MPT_TILT 0x02
#define PWC_MPT_TIMEOUT 0x04 /* for status */ #define PWC_MPT_TIMEOUT 0x04 /* for status */
/* Set angles; when absolute = 1, the angle is absolute and the /* Set angles; when absolute != 0, the angle is absolute and the
driver calculates the relative offset for you. This can only driver calculates the relative offset for you. This can only
be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
absolute angles. absolute angles.
...@@ -127,18 +151,14 @@ struct pwc_mpt_angles ...@@ -127,18 +151,14 @@ struct pwc_mpt_angles
int absolute; /* write-only */ int absolute; /* write-only */
int pan; /* degrees * 100 */ int pan; /* degrees * 100 */
int tilt; /* degress * 100 */ int tilt; /* degress * 100 */
int zoom; /* N/A, set to -1 */
}; };
/* Range of angles of the camera, both horizontally and vertically. /* Range of angles of the camera, both horizontally and vertically.
The zoom is not used, maybe in the future...
*/ */
struct pwc_mpt_range struct pwc_mpt_range
{ {
int pan_min, pan_max; /* degrees * 100 */ int pan_min, pan_max; /* degrees * 100 */
int tilt_min, tilt_max; int tilt_min, tilt_max;
int zoom_min, zoom_max; /* -1, -1 */
}; };
struct pwc_mpt_status struct pwc_mpt_status
...@@ -149,6 +169,30 @@ struct pwc_mpt_status ...@@ -149,6 +169,30 @@ struct pwc_mpt_status
}; };
/* This is used for out-of-kernel decompression. With it, you can get
all the necessary information to initialize and use the decompressor
routines in standalone applications.
*/
struct pwc_video_command
{
int type; /* camera type (645, 675, 730, etc.) */
int release; /* release number */
int size; /* one of PSZ_* */
int alternate;
int command_len; /* length of USB video command */
unsigned char command_buf[13]; /* Actual USB video command */
int bandlength; /* >0 = compressed */
int frame_size; /* Size of one (un)compressed frame */
};
/* Flags for PWCX subroutines. Not all modules honour all flags. */
#define PWCX_FLAG_PLANAR 0x0001
#define PWCX_FLAG_BAYER 0x0008
/* IOCTL definitions */
/* Restore user settings */ /* Restore user settings */
#define VIDIOCPWCRUSER _IO('v', 192) #define VIDIOCPWCRUSER _IO('v', 192)
/* Save user settings */ /* Save user settings */
...@@ -169,6 +213,9 @@ struct pwc_mpt_status ...@@ -169,6 +213,9 @@ struct pwc_mpt_status
#define VIDIOCPWCGCQUAL _IOR('v', 195, int) #define VIDIOCPWCGCQUAL _IOR('v', 195, int)
/* Retrieve serial number of camera */
#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial)
/* This is a probe function; since so many devices are supported, it /* This is a probe function; since so many devices are supported, it
becomes difficult to include all the names in programs that want to becomes difficult to include all the names in programs that want to
check for the enhanced Philips stuff. So in stead, try this PROBE; check for the enhanced Philips stuff. So in stead, try this PROBE;
...@@ -226,4 +273,7 @@ struct pwc_mpt_status ...@@ -226,4 +273,7 @@ struct pwc_mpt_status
#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) #define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) #define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
/* Get the USB set-video command; needed for initializing libpwcx */
#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
#endif #endif
...@@ -36,9 +36,28 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) ...@@ -36,9 +36,28 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height)
{ {
int i, find; int i, find;
/* Make sure we don't go beyond our max size */ /* Make sure we don't go beyond our max size.
NB: we have different limits for RAW and normal modes. In case
you don't have the decompressor loaded or use RAW mode,
the maximum viewable size is smaller.
*/
if (pdev->vpalette == VIDEO_PALETTE_RAW)
{
if (width > pdev->abs_max.x || height > pdev->abs_max.y)
{
Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n");
return -1;
}
}
else
{
if (width > pdev->view_max.x || height > pdev->view_max.y) if (width > pdev->view_max.x || height > pdev->view_max.y)
{
Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n");
return -1; return -1;
}
}
/* Find the largest size supported by the camera that fits into the /* Find the largest size supported by the camera that fits into the
requested size. requested size.
*/ */
...@@ -62,6 +81,8 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -62,6 +81,8 @@ void pwc_construct(struct pwc_device *pdev)
pdev->view_min.y = 96; pdev->view_min.y = 96;
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->abs_max.x = 352;
pdev->abs_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
pdev->vcinterface = 2; pdev->vcinterface = 2;
pdev->vendpoint = 4; pdev->vendpoint = 4;
...@@ -77,13 +98,14 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -77,13 +98,14 @@ void pwc_construct(struct pwc_device *pdev)
if (pdev->decompressor != NULL) { if (pdev->decompressor != NULL) {
pdev->view_max.x = 640; pdev->view_max.x = 640;
pdev->view_max.y = 480; pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
} }
else { else {
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF;
} }
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
pdev->abs_max.x = 640;
pdev->abs_max.y = 480;
pdev->vcinterface = 3; pdev->vcinterface = 3;
pdev->vendpoint = 4; pdev->vendpoint = 4;
pdev->frame_header_size = 0; pdev->frame_header_size = 0;
...@@ -99,24 +121,26 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -99,24 +121,26 @@ void pwc_construct(struct pwc_device *pdev)
if (pdev->decompressor != NULL) { if (pdev->decompressor != NULL) {
pdev->view_max.x = 640; pdev->view_max.x = 640;
pdev->view_max.y = 480; pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
} }
else { else {
/* Tell CIF, even though SIF really is the maximum, but some tools really need CIF */ /* We use CIF, not SIF since some tools really need CIF. So we cheat a bit. */
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF;
} }
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
pdev->abs_max.x = 640;
pdev->abs_max.y = 480;
pdev->vcinterface = 3; pdev->vcinterface = 3;
pdev->vendpoint = 5; pdev->vendpoint = 5;
pdev->frame_header_size = TOUCAM_HEADER_SIZE; pdev->frame_header_size = TOUCAM_HEADER_SIZE;
pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
break; break;
} }
pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */
pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
/* length of image, in YUV format */ /* length of image, in YUV format; always allocate enough memory. */
pdev->len_per_image = (pdev->view_max.size * 3) / 2; pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2;
} }
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
themselves. It also has a decompressor wrapper function. themselves. It also has a decompressor wrapper function.
*/ */
#include <asm/current.h>
#include <asm/types.h> #include <asm/types.h>
// #include <linux/sched.h>
#include "pwc.h" #include "pwc.h"
#include "pwc-uncompress.h" #include "pwc-uncompress.h"
...@@ -81,7 +83,6 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -81,7 +83,6 @@ int pwc_decompress(struct pwc_device *pdev)
u16 *src; u16 *src;
u16 *dsty, *dstu, *dstv; u16 *dsty, *dstu, *dstv;
if (pdev == NULL) if (pdev == NULL)
return -EFAULT; return -EFAULT;
#if defined(__KERNEL__) && defined(PWC_MAGIC) #if defined(__KERNEL__) && defined(PWC_MAGIC)
...@@ -99,6 +100,14 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -99,6 +100,14 @@ int pwc_decompress(struct pwc_device *pdev)
return -EFAULT; return -EFAULT;
yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
/* Raw format; that's easy... */
if (pdev->vpalette == VIDEO_PALETTE_RAW)
{
memcpy(image, yuv, pdev->frame_size);
return 0;
}
if (pdev->vbandlength == 0) { if (pdev->vbandlength == 0) {
/* Uncompressed mode. We copy the data into the output buffer, /* Uncompressed mode. We copy the data into the output buffer,
using the viewport size (which may be larger than the image using the viewport size (which may be larger than the image
...@@ -144,11 +153,17 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -144,11 +153,17 @@ int pwc_decompress(struct pwc_device *pdev)
/* Compressed; the decompressor routines will write the data /* Compressed; the decompressor routines will write the data
in planar format immediately. in planar format immediately.
*/ */
int flags;
flags = PWCX_FLAG_PLANAR;
if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot)
flags |= PWCX_FLAG_BAYER;
if (pdev->decompressor) if (pdev->decompressor)
pdev->decompressor->decompress( pdev->decompressor->decompress(
&pdev->image, &pdev->view, &pdev->offset, &pdev->image, &pdev->view, &pdev->offset,
yuv, image, yuv, image,
1, flags,
pdev->decompress_data, pdev->vbandlength); pdev->decompress_data, pdev->vbandlength);
else else
return -ENXIO; /* No such device or address: missing decompressor */ return -ENXIO; /* No such device or address: missing decompressor */
......
...@@ -24,9 +24,15 @@ ...@@ -24,9 +24,15 @@
#define PWC_UNCOMPRESS_H #define PWC_UNCOMPRESS_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/linkage.h>
#include <linux/list.h> #include <linux/list.h>
#include "pwc.h" #include "pwc-ioctl.h"
/* from pwc-dec.h */
#define PWCX_FLAG_PLANAR 0x0001
/* */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -42,10 +48,11 @@ struct pwc_decompressor ...@@ -42,10 +48,11 @@ struct pwc_decompressor
int type; /* type of camera (645, 680, etc) */ int type; /* type of camera (645, 680, etc) */
int table_size; /* memory needed */ int table_size; /* memory needed */
void (* init)(int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */ void (* init)(int type, int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */
void (* exit)(void); /* Cleanup routine */ void (* exit)(void); /* Cleanup routine */
void (* decompress)(struct pwc_coord *image, struct pwc_coord *view, struct pwc_coord *offset, void (* decompress)(struct pwc_coord *image, struct pwc_coord *view,
void *src, void *dst, int planar, struct pwc_coord *offset,
void *src, void *dst, int flags,
void *table, int bandlength); void *table, int bandlength);
void (* lock)(void); /* make sure module cannot be unloaded */ void (* lock)(void); /* make sure module cannot be unloaded */
void (* unlock)(void); /* release lock on module */ void (* unlock)(void); /* release lock on module */
......
...@@ -22,15 +22,15 @@ ...@@ -22,15 +22,15 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/spinlock.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/smp_lock.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/errno.h> #include <asm/errno.h>
#include "pwc-uncompress.h"
#include "pwc-ioctl.h" #include "pwc-ioctl.h"
/* Defines and structures for the Philips webcam */ /* Defines and structures for the Philips webcam */
...@@ -65,9 +65,9 @@ ...@@ -65,9 +65,9 @@
#define FEATURE_MOTOR_PANTILT 0x0001 #define FEATURE_MOTOR_PANTILT 0x0001
/* Version block */ /* Version block */
#define PWC_MAJOR 8 #define PWC_MAJOR 9
#define PWC_MINOR 12 #define PWC_MINOR 0
#define PWC_VERSION "8.12" #define PWC_VERSION "9.0.1"
#define PWC_NAME "pwc" #define PWC_NAME "pwc"
/* Turn certain features on/off */ /* Turn certain features on/off */
...@@ -90,12 +90,6 @@ ...@@ -90,12 +90,6 @@
/* Absolute maximum number of buffers available for mmap() */ /* Absolute maximum number of buffers available for mmap() */
#define MAX_IMAGES 10 #define MAX_IMAGES 10
struct pwc_coord
{
int x, y; /* guess what */
int size; /* size, or offset */
};
/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
struct pwc_iso_buf struct pwc_iso_buf
{ {
...@@ -118,7 +112,7 @@ struct pwc_frame_buf ...@@ -118,7 +112,7 @@ struct pwc_frame_buf
struct pwc_device struct pwc_device
{ {
struct video_device vdev; struct video_device *vdev;
#ifdef PWC_MAGIC #ifdef PWC_MAGIC
int magic; int magic;
#endif #endif
...@@ -128,6 +122,7 @@ struct pwc_device ...@@ -128,6 +122,7 @@ struct pwc_device
int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
int release; /* release number */ int release; /* release number */
int features; /* feature bits */ int features; /* feature bits */
char serial[30]; /* serial number (string) */
int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */
int usb_init; /* set when the cam has been initialized over USB */ int usb_init; /* set when the cam has been initialized over USB */
...@@ -137,6 +132,7 @@ struct pwc_device ...@@ -137,6 +132,7 @@ struct pwc_device
int vcinterface; /* video control interface */ int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */ int valternate; /* alternate interface needed */
int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
int vpalette; /* palette: 420P, RAW or RGBBAYER */
int vframe_count; /* received frames */ int vframe_count; /* received frames */
int vframes_dumped; /* counter for dumped frames */ int vframes_dumped; /* counter for dumped frames */
int vframes_error; /* frames received in error */ int vframes_error; /* frames received in error */
...@@ -149,6 +145,9 @@ struct pwc_device ...@@ -149,6 +145,9 @@ struct pwc_device
char vsync; /* used by isoc handler */ char vsync; /* used by isoc handler */
char vmirror; /* for ToUCaM series */ char vmirror; /* for ToUCaM series */
int cmd_len;
unsigned char cmd_buf[13];
/* The image acquisition requires 3 to 4 steps: /* The image acquisition requires 3 to 4 steps:
1. data is gathered in short packets from the USB controller 1. data is gathered in short packets from the USB controller
2. data is synchronized and packed into a frame buffer 2. data is synchronized and packed into a frame buffer
...@@ -169,8 +168,9 @@ struct pwc_device ...@@ -169,8 +168,9 @@ struct pwc_device
struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */
struct pwc_frame_buf *fill_frame; /* frame currently being filled */ struct pwc_frame_buf *fill_frame; /* frame currently being filled */
struct pwc_frame_buf *read_frame; /* frame currently read by user process */ struct pwc_frame_buf *read_frame; /* frame currently read by user process */
int frame_size;
int frame_header_size, frame_trailer_size; int frame_header_size, frame_trailer_size;
int frame_size;
int frame_total_size; /* including header & trailer */
int drop_frames; int drop_frames;
#if PWC_DEBUG #if PWC_DEBUG
int sequence; /* Debugging aid */ int sequence; /* Debugging aid */
...@@ -187,7 +187,8 @@ struct pwc_device ...@@ -187,7 +187,8 @@ struct pwc_device
a gray or black border. view_min <= image <= view <= view_max; a gray or black border. view_min <= image <= view <= view_max;
*/ */
int image_mask; /* bitmask of supported sizes */ int image_mask; /* bitmask of supported sizes */
struct pwc_coord view_min, view_max; /* minimum and maximum sizes */ struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */
struct pwc_coord abs_max; /* maximum supported size with compression */
struct pwc_coord image, view; /* image and viewport size */ struct pwc_coord image, view; /* image and viewport size */
struct pwc_coord offset; /* offset within the viewport */ struct pwc_coord offset; /* offset within the viewport */
...@@ -213,16 +214,6 @@ struct pwc_device ...@@ -213,16 +214,6 @@ struct pwc_device
#endif #endif
}; };
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
#define PSZ_QSIF 0x01
#define PSZ_QCIF 0x02
#define PSZ_SIF 0x03
#define PSZ_CIF 0x04
#define PSZ_VGA 0x05
#define PSZ_MAX 6
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -259,7 +250,7 @@ extern int pwc_get_saturation(struct pwc_device *pdev); ...@@ -259,7 +250,7 @@ extern int pwc_get_saturation(struct pwc_device *pdev);
extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_saturation(struct pwc_device *pdev, int value);
extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value);
extern int pwc_get_cmos_sensor(struct pwc_device *pdev); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
/* Power down or up the camera; not supported by all models */ /* Power down or up the camera; not supported by all models */
extern int pwc_camera_power(struct pwc_device *pdev, int power); extern int pwc_camera_power(struct pwc_device *pdev, int power);
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#define DEBUG #undef DEBUG
#ifdef DEBUG #ifdef DEBUG
#define kaweth_dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" ,##arg) #define kaweth_dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" ,##arg)
...@@ -592,7 +592,7 @@ static void kaweth_usb_receive(struct urb *urb, struct pt_regs *regs) ...@@ -592,7 +592,7 @@ static void kaweth_usb_receive(struct urb *urb, struct pt_regs *regs)
struct sk_buff *skb; struct sk_buff *skb;
if(unlikely(urb->status == -ECONNRESET || urb->status == -ECONNABORTED)) if(unlikely(urb->status == -ECONNRESET || urb->status == -ECONNABORTED || urb->status == -ESHUTDOWN))
/* we are killed - set a flag and wake the disconnect handler */ /* we are killed - set a flag and wake the disconnect handler */
{ {
kaweth->end = 1; kaweth->end = 1;
......
...@@ -1137,8 +1137,6 @@ static void pegasus_set_multicast(struct net_device *net) ...@@ -1137,8 +1137,6 @@ static void pegasus_set_multicast(struct net_device *net)
{ {
pegasus_t *pegasus = net->priv; pegasus_t *pegasus = net->priv;
netif_stop_queue(net);
if (net->flags & IFF_PROMISC) { if (net->flags & IFF_PROMISC) {
pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
info("%s: Promiscuous mode enabled", net->name); info("%s: Promiscuous mode enabled", net->name);
...@@ -1154,8 +1152,6 @@ static void pegasus_set_multicast(struct net_device *net) ...@@ -1154,8 +1152,6 @@ static void pegasus_set_multicast(struct net_device *net)
pegasus->flags |= ETH_REGS_CHANGE; pegasus->flags |= ETH_REGS_CHANGE;
ctrl_callback(pegasus->ctrl_urb, NULL); ctrl_callback(pegasus->ctrl_urb, NULL);
netif_wake_queue(net);
} }
static __u8 mii_phy_probe(pegasus_t * pegasus) static __u8 mii_phy_probe(pegasus_t * pegasus)
......
...@@ -53,6 +53,32 @@ struct usb_serial_device_type usb_serial_generic_device = { ...@@ -53,6 +53,32 @@ struct usb_serial_device_type usb_serial_generic_device = {
.num_ports = 1, .num_ports = 1,
.shutdown = usb_serial_generic_shutdown, .shutdown = usb_serial_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[] = {
{.driver_info = 42},
{}
};
static int generic_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
const struct usb_device_id *id_pattern;
id_pattern = usb_match_id(interface, generic_device_ids);
if (id_pattern != NULL)
return usb_serial_probe(interface, id);
return -ENODEV;
}
static struct usb_driver generic_driver = {
.owner = THIS_MODULE,
.name = "usbserial_generic",
.probe = generic_probe,
.disconnect = usb_serial_disconnect,
.id_table = generic_serial_ids,
};
#endif #endif
int usb_serial_generic_register (int _debug) int usb_serial_generic_register (int _debug)
...@@ -67,6 +93,12 @@ int usb_serial_generic_register (int _debug) ...@@ -67,6 +93,12 @@ int usb_serial_generic_register (int _debug)
/* register our generic driver with ourselves */ /* register our generic driver with ourselves */
retval = usb_serial_register (&usb_serial_generic_device); retval = usb_serial_register (&usb_serial_generic_device);
if (retval)
goto exit;
retval = usb_register(&generic_driver);
if (retval)
usb_serial_deregister(&usb_serial_generic_device);
exit:
#endif #endif
return retval; return retval;
} }
...@@ -75,6 +107,7 @@ void usb_serial_generic_deregister (void) ...@@ -75,6 +107,7 @@ void usb_serial_generic_deregister (void)
{ {
#ifdef CONFIG_USB_SERIAL_GENERIC #ifdef CONFIG_USB_SERIAL_GENERIC
/* remove our generic driver */ /* remove our generic driver */
usb_deregister(&generic_driver);
usb_serial_deregister (&usb_serial_generic_device); usb_serial_deregister (&usb_serial_generic_device);
#endif #endif
} }
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.10" #define DRIVER_VERSION "v0.11"
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
...@@ -82,6 +82,7 @@ static struct usb_device_id id_table [] = { ...@@ -82,6 +82,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
...@@ -172,20 +173,38 @@ static struct usb_serial_device_type pl2303_device = { ...@@ -172,20 +173,38 @@ static struct usb_serial_device_type pl2303_device = {
.shutdown = pl2303_shutdown, .shutdown = pl2303_shutdown,
}; };
enum pl2303_type {
type_0, /* don't know the difference between type 0 and */
type_1, /* type 1, until someone from prolific tells us... */
HX, /* HX version of the pl2303 chip */
};
struct pl2303_private { struct pl2303_private {
spinlock_t lock; spinlock_t lock;
wait_queue_head_t delta_msr_wait; wait_queue_head_t delta_msr_wait;
u8 line_control; u8 line_control;
u8 line_status; u8 line_status;
u8 termios_initialized; u8 termios_initialized;
enum pl2303_type type;
}; };
static int pl2303_startup (struct usb_serial *serial) static int pl2303_startup (struct usb_serial *serial)
{ {
struct pl2303_private *priv; struct pl2303_private *priv;
enum pl2303_type type = type_0;
int i; int i;
if (serial->dev->descriptor.bDeviceClass == 0x02)
type = type_0;
else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
type = HX;
else if (serial->dev->descriptor.bDeviceClass == 0x00)
type = type_1;
else if (serial->dev->descriptor.bDeviceClass == 0xFF)
type = type_1;
dbg("device type: %d", type);
for (i = 0; i < serial->num_ports; ++i) { for (i = 0; i < serial->num_ports; ++i) {
priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL); priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -193,6 +212,7 @@ static int pl2303_startup (struct usb_serial *serial) ...@@ -193,6 +212,7 @@ static int pl2303_startup (struct usb_serial *serial)
memset (priv, 0x00, sizeof (struct pl2303_private)); memset (priv, 0x00, sizeof (struct pl2303_private));
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
init_waitqueue_head(&priv->delta_msr_wait); init_waitqueue_head(&priv->delta_msr_wait);
priv->type = type;
usb_set_serial_port_data(serial->port[i], priv); usb_set_serial_port_data(serial->port[i], priv);
} }
return 0; return 0;
...@@ -395,20 +415,27 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol ...@@ -395,20 +415,27 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if (cflag & CRTSCTS) { if (cflag & CRTSCTS) {
i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), __u16 index;
VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE, if (priv->type == HX)
0x0, 0x41, NULL, 0, 100); index = 0x61;
dbg ("0x40:0x1:0x0:0x41 %d", i); else
index = 0x41;
i = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
VENDOR_WRITE_REQUEST,
VENDOR_WRITE_REQUEST_TYPE,
0x0, index, NULL, 0, 100);
dbg ("0x40:0x1:0x0:0x%x %d", index, i);
} }
kfree (buf); kfree (buf);
} }
static int pl2303_open (struct usb_serial_port *port, struct file *filp) static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{ {
struct termios tmp_termios; struct termios tmp_termios;
struct usb_serial *serial = port->serial; struct usb_serial *serial = port->serial;
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned char *buf; unsigned char *buf;
int result; int result;
...@@ -439,6 +466,18 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) ...@@ -439,6 +466,18 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0);
if (priv->type == HX) {
/* HX chip */
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x44);
/* reset upstream data pipes */
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 8, 0);
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 9, 0);
} else {
SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 0x24);
}
kfree(buf); kfree(buf);
......
...@@ -42,5 +42,11 @@ ...@@ -42,5 +42,11 @@
#define SITECOM_VENDOR_ID 0x6189 #define SITECOM_VENDOR_ID 0x6189
#define SITECOM_PRODUCT_ID 0x2068 #define SITECOM_PRODUCT_ID 0x2068
/* Alcatel OT535/735 USB cable */
#define ALCATEL_VENDOR_ID 0x11f7 #define ALCATEL_VENDOR_ID 0x11f7
#define ALCATEL_PRODUCT_ID 0x02df #define ALCATEL_PRODUCT_ID 0x02df
/* Samsung I330 phone cradle */
#define SAMSUNG_VENDOR_ID 0x04e8
#define SAMSUNG_PRODUCT_ID 0x8001
...@@ -355,25 +355,12 @@ ...@@ -355,25 +355,12 @@
#define DRIVER_DESC "USB Serial Driver core" #define DRIVER_DESC "USB Serial Driver core"
#ifdef CONFIG_USB_SERIAL_GENERIC
/* 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[] = {
{.driver_info = 42},
{}
};
#endif /* CONFIG_USB_SERIAL_GENERIC */
/* Driver structure we register with the USB core */ /* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = { static struct usb_driver usb_serial_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "usbserial", .name = "usbserial",
.probe = usb_serial_probe, .probe = usb_serial_probe,
.disconnect = usb_serial_disconnect, .disconnect = usb_serial_disconnect,
#ifdef CONFIG_USB_SERIAL_GENERIC
.id_table = generic_serial_ids,
#endif
}; };
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead /* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead
...@@ -1383,22 +1370,9 @@ int usb_serial_register(struct usb_serial_device_type *new_device) ...@@ -1383,22 +1370,9 @@ int usb_serial_register(struct usb_serial_device_type *new_device)
void usb_serial_deregister(struct usb_serial_device_type *device) void usb_serial_deregister(struct usb_serial_device_type *device)
{ {
struct usb_serial *serial;
int i;
info("USB Serial deregistering driver %s", device->name); info("USB Serial deregistering driver %s", device->name);
/* clear out the serial_table if the device is attached to a port */
for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial = serial_table[i];
if ((serial != NULL) && (serial->type == device)) {
usb_driver_release_interface (&usb_serial_driver, serial->interface);
usb_serial_disconnect (serial->interface);
}
}
list_del(&device->driver_list); list_del(&device->driver_list);
usb_serial_bus_deregister (device); usb_serial_bus_deregister(device);
} }
......
...@@ -708,6 +708,12 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -708,6 +708,12 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
srb->sense_buffer[0] = 0x0; srb->sense_buffer[0] = 0x0;
} }
} }
/* Did we transfer less than the minimum amount required? */
if (srb->result == SAM_STAT_GOOD &&
srb->request_bufflen - srb->resid < srb->underflow)
srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24);
return; return;
/* abort processing: the bulk-only transport requires a reset /* abort processing: the bulk-only transport requires a reset
......
...@@ -73,6 +73,16 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, ...@@ -73,6 +73,16 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,
US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0),
#endif #endif
/* <torsten.scherer@uni-bielefeld.de>: I don't know the name of the bridge
* manufacturer, but I've got an external USB drive by the Revoltec company
* that needs this. otherwise the drive is recognized as /dev/sda, but any
* access to it blocks indefinitely.
*/
UNUSUAL_DEV( 0x0402, 0x5621, 0x0103, 0x0103,
"Revoltec",
"USB/IDE Bridge (ATA/ATAPI)",
US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
/* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au> /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
* Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
* always fails and confuses drive. * always fails and confuses drive.
...@@ -686,7 +696,7 @@ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, ...@@ -686,7 +696,7 @@ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff,
UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001, UNUSUAL_DEV( 0x097a, 0x0001, 0x0000, 0x0001,
"Minds@Work", "Minds@Work",
"Digital Wallet", "Digital Wallet",
US_SC_SCSI, US_PR_CB, NULL, US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_MODE_XLATE ), US_FL_MODE_XLATE ),
UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100,
......
...@@ -624,6 +624,7 @@ void fastcall kick_iocb(struct kiocb *iocb) ...@@ -624,6 +624,7 @@ void fastcall kick_iocb(struct kiocb *iocb)
queue_work(aio_wq, &ctx->wq); queue_work(aio_wq, &ctx->wq);
} }
} }
EXPORT_SYMBOL(kick_iocb);
/* aio_complete /* aio_complete
* Called when the io request on the given iocb is complete. * Called when the io request on the given iocb is complete.
......
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