Commit a2dad09d authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linuxusb.bkbits.net/linus-2.5

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents d46f8df6 f7df36d6
...@@ -21,7 +21,6 @@ CONFIG_USB_EHCI_HCD ...@@ -21,7 +21,6 @@ CONFIG_USB_EHCI_HCD
The module will be called ehci-hcd.o. If you want to compile it as a The module will be called ehci-hcd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>. module, say M here and read <file:Documentation/modules.txt>.
OHCI (most USB hosts except VIA, Intel PIIX) support
CONFIG_USB_OHCI_HCD CONFIG_USB_OHCI_HCD
The Open Host Controller Interface (OHCI) is a standard for accessing The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's USB 1.1 host controller hardware. It does more in hardware than Intel's
......
...@@ -644,6 +644,49 @@ void usb_hub_port_disable(struct usb_device *hub, int port) ...@@ -644,6 +644,49 @@ void usb_hub_port_disable(struct usb_device *hub, int port)
port + 1, hub->devpath, ret); port + 1, hub->devpath, ret);
} }
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
*
* Apparently there are some bluetooth and irda-dongles and a number
* of low-speed devices which require longer delays of about 200-400ms.
* Not covered by the spec - but easy to deal with.
*
* This implementation uses 400ms minimum debounce timeout and checks
* every 10ms for transient disconnects to restart the delay.
*/
#define HUB_DEBOUNCE_TIMEOUT 400
#define HUB_DEBOUNCE_STEP 10
/* return: -1 on error, 0 on success, 1 on disconnect. */
static int usb_hub_port_debounce(struct usb_device *hub, int port)
{
int ret;
unsigned delay_time;
u16 portchange, portstatus;
for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; /* empty */ ) {
/* wait debounce step increment */
wait_ms(HUB_DEBOUNCE_STEP);
ret = usb_hub_port_status(hub, port, &portstatus, &portchange);
if (ret < 0)
return -1;
if ((portchange & USB_PORT_STAT_C_CONNECTION)) {
usb_clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION);
delay_time = 0;
}
else
delay_time += HUB_DEBOUNCE_STEP;
}
return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;
}
static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port, static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port,
u16 portstatus, u16 portchange) u16 portstatus, u16 portchange)
{ {
...@@ -671,12 +714,16 @@ static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port, ...@@ -671,12 +714,16 @@ static void usb_hub_port_connect_change(struct usb_hub *hubstate, int port,
return; return;
} }
if (usb_hub_port_debounce(hub, port)) {
err("connect-debounce failed, port %d disabled", port+1);
usb_hub_port_disable(hub, port);
return;
}
/* Some low speed devices have problems with the quick delay, so */ /* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */ /* be a bit pessimistic with those devices. RHbug #23670 */
if (portstatus & USB_PORT_STAT_LOW_SPEED) { if (portstatus & USB_PORT_STAT_LOW_SPEED)
wait_ms(400);
delay = HUB_LONG_RESET_TIME; delay = HUB_LONG_RESET_TIME;
}
down(&usb_address0_sem); down(&usb_address0_sem);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -9,11 +9,11 @@ ...@@ -9,11 +9,11 @@
#define OV511_DEBUG /* Turn on debug messages */ #define OV511_DEBUG /* Turn on debug messages */
#ifdef OV511_DEBUG #ifdef OV511_DEBUG
# define PDEBUG(level, fmt, args...) \ #define PDEBUG(level, fmt, args...) \
if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt,\
## args) __LINE__ , ## args)
#else #else
# define PDEBUG(level, fmt, args...) do {} while(0) #define PDEBUG(level, fmt, args...) do {} while(0)
#endif #endif
/* This macro restricts an int variable to an inclusive range */ /* This macro restricts an int variable to an inclusive range */
...@@ -36,98 +36,105 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \ ...@@ -36,98 +36,105 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
#define VEND_MATTEL 0x0813 #define VEND_MATTEL 0x0813
#define PROD_ME2CAM 0x0002 #define PROD_ME2CAM 0x0002
/* --------------------------------- */
/* OV51x REGISTER MNEMONICS */
/* --------------------------------- */
/* Camera interface register numbers */ /* Camera interface register numbers */
#define OV511_REG_CAMERA_DELAY_MODE 0x10 #define R511_CAM_DELAY 0x10
#define OV511_REG_CAMERA_EDGE_MODE 0x11 #define R511_CAM_EDGE 0x11
#define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM 0x12 #define R511_CAM_PXCNT 0x12
#define OV511_REG_CAMERA_CLAMPED_LINE_NUM 0x13 #define R511_CAM_LNCNT 0x13
#define OV511_REG_CAMERA_PIXEL_DIVISOR 0x14 #define R511_CAM_PXDIV 0x14
#define OV511_REG_CAMERA_LINE_DIVISOR 0x15 #define R511_CAM_LNDIV 0x15
#define OV511_REG_CAMERA_DATA_INPUT_SELECT 0x16 #define R511_CAM_UV_EN 0x16
#define OV511_REG_CAMERA_RESERVED_LINE_MODE 0x17 #define R511_CAM_LINE_MODE 0x17
#define OV511_REG_CAMERA_BITMASK 0x18 #define R511_CAM_OPTS 0x18
/* Snapshot mode camera interface register numbers */ /* Snapshot mode camera interface register numbers */
#define OV511_REG_SNAP_CAPTURED_FRAME 0x19 #define R511_SNAP_FRAME 0x19
#define OV511_REG_SNAP_CLAMPED_PIXEL_NUM 0x1A #define R511_SNAP_PXCNT 0x1A
#define OV511_REG_SNAP_CLAMPED_LINE_NUM 0x1B #define R511_SNAP_LNCNT 0x1B
#define OV511_REG_SNAP_PIXEL_DIVISOR 0x1C #define R511_SNAP_PXDIV 0x1C
#define OV511_REG_SNAP_LINE_DIVISOR 0x1D #define R511_SNAP_LNDIV 0x1D
#define OV511_REG_SNAP_DATA_INPUT_SELECT 0x1E #define R511_SNAP_UV_EN 0x1E
#define OV511_REG_SNAP_BITMASK 0x1F #define R511_SNAP_OPTS 0x1F
/* DRAM register numbers */ /* DRAM register numbers */
#define OV511_REG_DRAM_ENABLE_FLOW_CONTROL 0x20 #define R511_DRAM_FLOW_CTL 0x20
#define OV511_REG_DRAM_READ_CYCLE_PREDICT 0x21 #define R511_DRAM_ARCP 0x21
#define OV511_REG_DRAM_MANUAL_READ_CYCLE 0x22 #define R511_DRAM_MRC 0x22
#define OV511_REG_DRAM_REFRESH_COUNTER 0x23 #define R511_DRAM_RFC 0x23
/* ISO FIFO register numbers */ /* ISO FIFO register numbers */
#define OV511_REG_FIFO_PACKET_SIZE 0x30 #define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
#define OV511_REG_FIFO_BITMASK 0x31 #define R511_FIFO_OPTS 0x31
/* PIO register numbers */ /* Parallel IO register numbers */
#define OV511_REG_PIO_BITMASK 0x38 #define R511_PIO_OPTS 0x38
#define OV511_REG_PIO_DATA_PORT 0x39 #define R511_PIO_DATA 0x39
#define OV511_REG_PIO_BIST 0x3E #define R511_PIO_BIST 0x3E
#define R518_GPIO_IN 0x55 /* OV518(+) only */
/* I2C register numbers */ #define R518_GPIO_OUT 0x56 /* OV518(+) only */
#define OV511_REG_I2C_CONTROL 0x40 #define R518_GPIO_CTL 0x57 /* OV518(+) only */
#define OV518_REG_I2C_CONTROL 0x47 /* OV518(+) only */ #define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */
#define OV511_REG_I2C_SLAVE_ID_WRITE 0x41 #define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */
#define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42 #define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */
#define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43 #define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */
#define OV511_REG_I2C_SLAVE_ID_READ 0x44 #define R518_GPIO_RESET 0x5c /* OV518(+) only */
#define OV511_REG_I2C_DATA_PORT 0x45
#define OV511_REG_I2C_CLOCK_PRESCALER 0x46 /* I2C registers */
#define OV511_REG_I2C_TIME_OUT_COUNTER 0x47 #define R511_I2C_CTL 0x40
#define R518_I2C_CTL 0x47 /* OV518(+) only */
/* I2C snapshot register numbers */ #define R51x_I2C_W_SID 0x41
#define OV511_REG_I2C_SNAP_SUB_ADDRESS 0x48 #define R51x_I2C_SADDR_3 0x42
#define OV511_REG_I2C_SNAP_DATA_PORT 0x49 #define R51x_I2C_SADDR_2 0x43
#define R51x_I2C_R_SID 0x44
/* System control register numbers */ #define R51x_I2C_DATA 0x45
#define OV511_REG_SYSTEM_RESET 0x50 #define R51x_I2C_CLOCK 0x46
#define OV511_RESET_UDC 0x01 #define R51x_I2C_TIMEOUT 0x47
#define OV511_RESET_I2C 0x02
#define OV511_RESET_FIFO 0x04 /* I2C snapshot registers */
#define OV511_RESET_OMNICE 0x08 #define R511_SI2C_SADDR_3 0x48
#define OV511_RESET_DRAM_INTF 0x10 #define R511_SI2C_DATA 0x49
#define OV511_RESET_CAMERA_INTF 0x20
#define OV511_RESET_OV511 0x40 /* System control registers */
#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ #define R51x_SYS_RESET 0x50
#define OV511_RESET_ALL 0x7F /* Reset type definitions */
#define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51 #define OV511_RESET_UDC 0x01
#define OV511_REG_SYSTEM_SNAPSHOT 0x52 #define OV511_RESET_I2C 0x02
#define OV511_REG_SYSTEM_INIT 0x53 #define OV511_RESET_FIFO 0x04
#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+/OV518(+) only */ #define OV511_RESET_OMNICE 0x08
#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ #define OV511_RESET_DRAM 0x10
#define OV518_REG_GPIO_IN 0x55 /* OV518(+) only */ #define OV511_RESET_CAM_INT 0x20
#define OV518_REG_GPIO_OUT 0x56 /* OV518(+) only */ #define OV511_RESET_OV511 0x40
#define OV518_REG_GPIO_CTL 0x57 /* OV518(+) only */ #define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */
#define OV518_REG_GPIO_PULSE_IN 0x58 /* OV518(+) only */ #define OV511_RESET_ALL 0x7F
#define OV518_REG_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */
#define OV518_REG_GPIO_PULSE_POLARITY 0x5a /* OV518(+) only */ #define R511_SYS_CLOCK_DIV 0x51
#define OV518_REG_GPIO_PULSE_EN 0x5b /* OV518(+) only */ #define R51x_SYS_SNAP 0x52
#define OV518_REG_GPIO_RESET 0x5c /* OV518(+) only */ #define R51x_SYS_INIT 0x53
#define OV511_REG_SYSTEM_USER_DEFINED 0x5E #define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */
#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F #define R511_SYS_LED_CTL 0x55 /* OV511+ only */
#define R511_SYS_USER 0x5E
/* OmniCE register numbers */ #define R511_SYS_CUST_ID 0x5F
#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70
#define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71 /* OmniCE (compression) registers */
#define OV511_OMNICE_PREDICTION_VERT_Y 0x72 #define R511_COMP_PHY 0x70
#define OV511_OMNICE_PREDICTION_VERT_UV 0x73 #define R511_COMP_PHUV 0x71
#define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74 #define R511_COMP_PVY 0x72
#define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75 #define R511_COMP_PVUV 0x73
#define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76 #define R511_COMP_QHY 0x74
#define OV511_OMNICE_QUANTIZATION_VERT_UV 0x77 #define R511_COMP_QHUV 0x75
#define OV511_OMNICE_ENABLE 0x78 #define R511_COMP_QVY 0x76
#define OV511_OMNICE_LUT_ENABLE 0x79 #define R511_COMP_QVUV 0x77
#define OV511_OMNICE_Y_LUT_BEGIN 0x80 #define R511_COMP_EN 0x78
#define OV511_OMNICE_Y_LUT_END 0x9F #define R511_COMP_LUT_EN 0x79
#define OV511_OMNICE_UV_LUT_BEGIN 0xA0 #define R511_COMP_LUT_BEGIN 0x80
#define OV511_OMNICE_UV_LUT_END 0xBF
/* --------------------------------- */
/* ALTERNATE NUMBERS */
/* --------------------------------- */
/* Alternate numbers for various max packet sizes (OV511 only) */ /* Alternate numbers for various max packet sizes (OV511 only) */
#define OV511_ALT_SIZE_992 0 #define OV511_ALT_SIZE_992 0
...@@ -159,6 +166,10 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \ ...@@ -159,6 +166,10 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
#define OV518_ALT_SIZE_768 6 #define OV518_ALT_SIZE_768 6
#define OV518_ALT_SIZE_896 7 #define OV518_ALT_SIZE_896 7
/* --------------------------------- */
/* OV7610 REGISTER MNEMONICS */
/* --------------------------------- */
/* OV7610 registers */ /* OV7610 registers */
#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ #define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
#define OV7610_REG_BLUE 0x01 /* blue channel balance */ #define OV7610_REG_BLUE 0x01 /* blue channel balance */
...@@ -210,26 +221,27 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \ ...@@ -210,26 +221,27 @@ if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
/* 36-37 reserved */ /* 36-37 reserved */
#define OV7610_REG_COM_K 0x38 /* misc registers */ #define OV7610_REG_COM_K 0x38 /* misc registers */
/* --------------------------------- */
/* I2C ADDRESSES */
/* --------------------------------- */
#define OV7xx0_SID 0x42
#define OV6xx0_SID 0xC0
#define OV8xx0_SID 0xA0
#define KS0127_SID 0xD8
#define SAA7111A_SID 0x48
/* --------------------------------- */
/* MISCELLANEOUS DEFINES */
/* --------------------------------- */
#define I2C_CLOCK_PRESCALER 0x03
#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ #define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
#define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */
#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ #define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
#define PIXELS_PER_SEG 256 /* Pixels per segment */ #define PIXELS_PER_SEG 256 /* Pixels per segment */
#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */
/* I2C addresses */
#define OV7xx0_I2C_WRITE_ID 0x42
#define OV7xx0_I2C_READ_ID 0x43
#define OV6xx0_I2C_WRITE_ID 0xC0
#define OV6xx0_I2C_READ_ID 0xC1
#define OV8xx0_I2C_WRITE_ID 0xA0
#define OV8xx0_I2C_READ_ID 0xA1
#define KS0127_I2C_WRITE_ID 0xD8
#define KS0127_I2C_READ_ID 0xD9
#define SAA7111A_I2C_WRITE_ID 0x48
#define SAA7111A_I2C_READ_ID 0x49
#define OV511_I2C_CLOCK_PRESCALER 0x03
/* Bridge types */ /* Bridge types */
enum { enum {
...@@ -429,11 +441,14 @@ struct ov51x_decomp_ops { ...@@ -429,11 +441,14 @@ struct ov51x_decomp_ops {
#define OV511_NUMFRAMES 2 #define OV511_NUMFRAMES 2
#if OV511_NUMFRAMES > VIDEO_MAX_FRAME #if OV511_NUMFRAMES > VIDEO_MAX_FRAME
#error "OV511_NUMFRAMES is too high" #error "OV511_NUMFRAMES is too high"
#endif #endif
#define OV511_NUMSBUF 2 #define OV511_NUMSBUF 2
/* Control transfers use up to 4 bytes */
#define OV511_CBUF_SIZE 4
struct usb_ov511 { struct usb_ov511 {
struct video_device vdev; struct video_device vdev;
...@@ -535,6 +550,10 @@ struct usb_ov511 { ...@@ -535,6 +550,10 @@ struct usb_ov511 {
/* I2C interface to kernel */ /* I2C interface to kernel */
struct semaphore i2c_lock; /* Protect I2C controller regs */ struct semaphore i2c_lock; /* Protect I2C controller regs */
unsigned char primary_i2c_slave; /* I2C write id of sensor */ unsigned char primary_i2c_slave; /* I2C write id of sensor */
/* Control transaction stuff */
unsigned char *cbuf; /* Buffer for payload */
struct semaphore cbuf_lock;
}; };
struct cam_list { struct cam_list {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Maintainer: Johannes Erdfelt <johannes@erdfelt.com> * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
* *
* (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999-2001 Johannes Erdfelt, johannes@erdfelt.com * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
* (C) Copyright 1999 Randy Dunlap * (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Georg Acher, acher@in.tum.de * (C) Copyright 1999 Georg Acher, acher@in.tum.de
* (C) Copyright 1999 Deti Fliegl, deti@fliegl.de * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
...@@ -240,10 +240,10 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) ...@@ -240,10 +240,10 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
unsigned long flags; unsigned long flags;
/* If it's not inserted, don't remove it */ /* If it's not inserted, don't remove it */
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (td->frame == -1 && list_empty(&td->fl_list)) if (td->frame == -1 && list_empty(&td->fl_list))
return; goto out;
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
if (list_empty(&td->fl_list)) { if (list_empty(&td->fl_list)) {
uhci->fl->frame[td->frame] = td->link; uhci->fl->frame[td->frame] = td->link;
...@@ -268,6 +268,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) ...@@ -268,6 +268,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
list_del_init(&td->fl_list); list_del_init(&td->fl_list);
td->frame = -1; td->frame = -1;
out:
spin_unlock_irqrestore(&uhci->frame_list_lock, flags); spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
} }
...@@ -358,6 +359,9 @@ static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh) ...@@ -358,6 +359,9 @@ static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh)
pci_pool_free(uhci->qh_pool, qh, qh->dma_handle); pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
} }
/*
* MUST be called with uhci->frame_list_lock acquired
*/
static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb) static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb)
{ {
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
...@@ -417,11 +421,10 @@ static void uhci_remove_qh(struct uhci *uhci, struct urb *urb) ...@@ -417,11 +421,10 @@ static void uhci_remove_qh(struct uhci *uhci, struct urb *urb)
return; return;
/* Only go through the hoops if it's actually linked in */ /* Only go through the hoops if it's actually linked in */
spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (!list_empty(&qh->list)) { if (!list_empty(&qh->list)) {
qh->urbp = NULL; qh->urbp = NULL;
spin_lock_irqsave(&uhci->frame_list_lock, flags);
pqh = list_entry(qh->list.prev, struct uhci_qh, list); pqh = list_entry(qh->list.prev, struct uhci_qh, list);
if (pqh->urbp) { if (pqh->urbp) {
...@@ -444,9 +447,8 @@ static void uhci_remove_qh(struct uhci *uhci, struct urb *urb) ...@@ -444,9 +447,8 @@ static void uhci_remove_qh(struct uhci *uhci, struct urb *urb)
qh->element = qh->link = UHCI_PTR_TERM; qh->element = qh->link = UHCI_PTR_TERM;
list_del_init(&qh->list); list_del_init(&qh->list);
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
} }
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
...@@ -658,6 +660,9 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb) ...@@ -658,6 +660,9 @@ static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb)
return urbp; return urbp;
} }
/*
* MUST be called with urb->lock acquired
*/
static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
{ {
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
...@@ -667,6 +672,9 @@ static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) ...@@ -667,6 +672,9 @@ static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
list_add_tail(&td->list, &urbp->td_list); list_add_tail(&td->list, &urbp->td_list);
} }
/*
* MUST be called with urb->lock acquired
*/
static void uhci_remove_td_from_urb(struct uhci_td *td) static void uhci_remove_td_from_urb(struct uhci_td *td)
{ {
if (list_empty(&td->list)) if (list_empty(&td->list))
...@@ -677,22 +685,22 @@ static void uhci_remove_td_from_urb(struct uhci_td *td) ...@@ -677,22 +685,22 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
td->urb = NULL; td->urb = NULL;
} }
/*
* MUST be called with urb->lock acquired
*/
static void uhci_destroy_urb_priv(struct urb *urb) static void uhci_destroy_urb_priv(struct urb *urb)
{ {
struct list_head *head, *tmp; struct list_head *head, *tmp;
struct urb_priv *urbp; struct urb_priv *urbp;
struct uhci *uhci; struct uhci *uhci;
unsigned long flags;
spin_lock_irqsave(&urb->lock, flags);
urbp = (struct urb_priv *)urb->hcpriv; urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp) if (!urbp)
goto out; return;
if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) { if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb); warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb);
goto out; return;
} }
if (!list_empty(&urb->urb_list)) if (!list_empty(&urb->urb_list))
...@@ -715,20 +723,21 @@ static void uhci_destroy_urb_priv(struct urb *urb) ...@@ -715,20 +723,21 @@ static void uhci_destroy_urb_priv(struct urb *urb)
uhci_free_td(uhci, td); uhci_free_td(uhci, td);
} }
if (urbp->setup_packet_dma_handle) if (urbp->setup_packet_dma_handle) {
pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
urbp->setup_packet_dma_handle = 0;
}
if (urbp->transfer_buffer_dma_handle) if (urbp->transfer_buffer_dma_handle) {
pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
urb->transfer_buffer_length, usb_pipein(urb->pipe) ? urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
urbp->transfer_buffer_dma_handle = 0;
}
urb->hcpriv = NULL; urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp); kmem_cache_free(uhci_up_cachep, urbp);
out:
spin_unlock_irqrestore(&urb->lock, flags);
} }
static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb) static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
...@@ -872,7 +881,7 @@ static int uhci_submit_control(struct urb *urb) ...@@ -872,7 +881,7 @@ static int uhci_submit_control(struct urb *urb)
* It's IN if the pipe is an output pipe or we're not expecting * It's IN if the pipe is an output pipe or we're not expecting
* data back. * data back.
*/ */
destination &= ~TD_PID; destination &= ~TD_TOKEN_PID_MASK;
if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
destination |= USB_PID_IN; destination |= USB_PID_IN;
else else
...@@ -1312,9 +1321,7 @@ static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigne ...@@ -1312,9 +1321,7 @@ static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigne
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct list_head *tmp, *head; struct list_head *tmp, *head;
int ret = 0; int ret = 0;
unsigned long flags;
spin_lock_irqsave(&uhci->urb_list_lock, flags);
head = &uhci->urb_list; head = &uhci->urb_list;
tmp = head->next; tmp = head->next;
while (tmp != head) { while (tmp != head) {
...@@ -1337,8 +1344,6 @@ static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigne ...@@ -1337,8 +1344,6 @@ static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigne
} else } else
ret = -1; /* no previous urb found */ ret = -1; /* no previous urb found */
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return ret; return ret;
} }
...@@ -1446,34 +1451,30 @@ static int uhci_result_isochronous(struct urb *urb) ...@@ -1446,34 +1451,30 @@ static int uhci_result_isochronous(struct urb *urb)
return ret; return ret;
} }
/*
* MUST be called with uhci->urb_list_lock acquired
*/
static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb) static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
{ {
struct list_head *tmp, *head; struct list_head *tmp, *head;
unsigned long flags;
struct urb *u = NULL;
/* We don't match Isoc transfers since they are special */ /* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe)) if (usb_pipeisoc(urb->pipe))
return NULL; return NULL;
spin_lock_irqsave(&uhci->urb_list_lock, flags);
head = &uhci->urb_list; head = &uhci->urb_list;
tmp = head->next; tmp = head->next;
while (tmp != head) { while (tmp != head) {
u = list_entry(tmp, struct urb, urb_list); struct urb *u = list_entry(tmp, struct urb, urb_list);
tmp = tmp->next; tmp = tmp->next;
if (u->dev == urb->dev && u->pipe == urb->pipe && if (u->dev == urb->dev && u->pipe == urb->pipe &&
u->status == -EINPROGRESS) u->status == -EINPROGRESS)
goto out; return u;
} }
u = NULL;
out:
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return u; return NULL;
} }
static int uhci_submit_urb(struct urb *urb, int mem_flags) static int uhci_submit_urb(struct urb *urb, int mem_flags)
...@@ -1492,19 +1493,25 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags) ...@@ -1492,19 +1493,25 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags)
return -ENODEV; return -ENODEV;
} }
/* increment the reference count of the urb, as we now also control it */
urb = usb_get_urb(urb);
uhci = (struct uhci *)urb->dev->bus->hcpriv; uhci = (struct uhci *)urb->dev->bus->hcpriv;
INIT_LIST_HEAD(&urb->urb_list); INIT_LIST_HEAD(&urb->urb_list);
usb_inc_dev_use(urb->dev); usb_inc_dev_use(urb->dev);
spin_lock_irqsave(&urb->lock, flags); spin_lock_irqsave(&uhci->urb_list_lock, flags);
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET || if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
urb->status == -ECONNABORTED) { urb->status == -ECONNABORTED) {
dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status); dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status);
/* Since we can have problems on the out path */ /* Since we can have problems on the out path */
spin_unlock_irqrestore(&urb->lock, flags); spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
usb_dec_dev_use(urb->dev); usb_dec_dev_use(urb->dev);
usb_put_urb(urb);
return ret; return ret;
} }
...@@ -1572,18 +1579,21 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags) ...@@ -1572,18 +1579,21 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags)
out: out:
urb->status = ret; urb->status = ret;
spin_unlock_irqrestore(&urb->lock, flags);
if (ret == -EINPROGRESS) { if (ret == -EINPROGRESS) {
spin_lock_irqsave(&uhci->urb_list_lock, flags);
/* We use _tail to make find_urb_ep more efficient */ /* We use _tail to make find_urb_ep more efficient */
list_add_tail(&urb->urb_list, &uhci->urb_list); list_add_tail(&urb->urb_list, &uhci->urb_list);
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return 0; return 0;
} }
uhci_unlink_generic(uhci, urb); uhci_unlink_generic(uhci, urb);
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_call_completion(urb); uhci_call_completion(urb);
return ret; return ret;
...@@ -1592,7 +1602,7 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags) ...@@ -1592,7 +1602,7 @@ static int uhci_submit_urb(struct urb *urb, int mem_flags)
/* /*
* Return the result of a transfer * Return the result of a transfer
* *
* Must be called with urb_list_lock acquired * MUST be called with urb_list_lock acquired
*/ */
static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
{ {
...@@ -1631,10 +1641,10 @@ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) ...@@ -1631,10 +1641,10 @@ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
urbp->status = ret; urbp->status = ret;
spin_unlock_irqrestore(&urb->lock, flags); if (ret == -EINPROGRESS) {
spin_unlock_irqrestore(&urb->lock, flags);
if (ret == -EINPROGRESS)
return; return;
}
switch (usb_pipetype(urb->pipe)) { switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL: case PIPE_CONTROL:
...@@ -1664,15 +1674,22 @@ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb) ...@@ -1664,15 +1674,22 @@ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
usb_pipetype(urb->pipe), urb); usb_pipetype(urb->pipe), urb);
} }
/* Remove it from uhci->urb_list */
list_del_init(&urb->urb_list); list_del_init(&urb->urb_list);
uhci_add_complete(urb); uhci_add_complete(urb);
spin_unlock_irqrestore(&urb->lock, flags);
} }
/*
* MUST be called with urb->lock acquired
*/
static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
{ {
struct list_head *head, *tmp; struct list_head *head, *tmp;
struct urb_priv *urbp = urb->hcpriv; struct urb_priv *urbp = urb->hcpriv;
int prevactive = 1;
/* We can get called when urbp allocation fails, so check */ /* We can get called when urbp allocation fails, so check */
if (!urbp) if (!urbp)
...@@ -1680,6 +1697,19 @@ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) ...@@ -1680,6 +1697,19 @@ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
/*
* Now we need to find out what the last successful toggle was
* so we can update the local data toggle for the next transfer
*
* There's 3 way's the last successful completed TD is found:
*
* 1) The TD is NOT active and the actual length < expected length
* 2) The TD is NOT active and it's the last TD in the chain
* 3) The TD is active and the previous TD is NOT active
*
* Control and Isochronous ignore the toggle, so this is safe
* for all types
*/
head = &urbp->td_list; head = &urbp->td_list;
tmp = head->next; tmp = head->next;
while (tmp != head) { while (tmp != head) {
...@@ -1687,15 +1717,18 @@ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb) ...@@ -1687,15 +1717,18 @@ static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
tmp = tmp->next; tmp = tmp->next;
/* Control and Isochronous ignore the toggle, so this */ if (!(td->status & TD_CTRL_ACTIVE) &&
/* is safe for all types */ (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
if ((!(td->status & TD_CTRL_ACTIVE) && tmp == head))
(uhci_actual_length(td->status) < uhci_expected_length(td->info)) ||
tmp == head)) {
usb_settoggle(urb->dev, uhci_endpoint(td->info), usb_settoggle(urb->dev, uhci_endpoint(td->info),
uhci_packetout(td->info), uhci_packetout(td->info),
uhci_toggle(td->info) ^ 1); uhci_toggle(td->info) ^ 1);
} else if ((td->status & TD_CTRL_ACTIVE) && !prevactive)
usb_settoggle(urb->dev, uhci_endpoint(td->info),
uhci_packetout(td->info),
uhci_toggle(td->info));
prevactive = td->status & TD_CTRL_ACTIVE;
} }
uhci_delete_queued_urb(uhci, urb); uhci_delete_queued_urb(uhci, urb);
...@@ -1718,6 +1751,9 @@ static int uhci_unlink_urb(struct urb *urb) ...@@ -1718,6 +1751,9 @@ static int uhci_unlink_urb(struct urb *urb)
uhci = (struct uhci *)urb->dev->bus->hcpriv; uhci = (struct uhci *)urb->dev->bus->hcpriv;
spin_lock_irqsave(&uhci->urb_list_lock, flags);
spin_lock(&urb->lock);
/* Release bandwidth for Interrupt or Isoc. transfers */ /* Release bandwidth for Interrupt or Isoc. transfers */
/* Spinlock needed ? */ /* Spinlock needed ? */
if (urb->bandwidth) { if (urb->bandwidth) {
...@@ -1733,35 +1769,41 @@ static int uhci_unlink_urb(struct urb *urb) ...@@ -1733,35 +1769,41 @@ static int uhci_unlink_urb(struct urb *urb)
} }
} }
if (urb->status != -EINPROGRESS) if (urb->status != -EINPROGRESS) {
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
return 0; return 0;
}
spin_lock_irqsave(&uhci->urb_list_lock, flags);
list_del_init(&urb->urb_list); list_del_init(&urb->urb_list);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_unlink_generic(uhci, urb); uhci_unlink_generic(uhci, urb);
/* Short circuit the virtual root hub */ /* Short circuit the virtual root hub */
if (urb->dev == uhci->rh.dev) { if (urb->dev == uhci->rh.dev) {
rh_unlink_urb(urb); rh_unlink_urb(urb);
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_call_completion(urb); uhci_call_completion(urb);
} else { } else {
if (urb->transfer_flags & USB_ASYNC_UNLINK) { if (urb->transfer_flags & USB_ASYNC_UNLINK) {
/* urb_list is available now since we called */
/* uhci_unlink_generic already */
urbp->status = urb->status = -ECONNABORTED; urbp->status = urb->status = -ECONNABORTED;
spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); spin_lock(&uhci->urb_remove_list_lock);
/* Check to see if the remove list is empty */ /* If we're the first, set the next interrupt bit */
if (list_empty(&uhci->urb_remove_list)) if (list_empty(&uhci->urb_remove_list))
uhci_set_next_interrupt(uhci); uhci_set_next_interrupt(uhci);
list_add(&urb->urb_list, &uhci->urb_remove_list); list_add(&urb->urb_list, &uhci->urb_remove_list);
spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); spin_unlock(&uhci->urb_remove_list_lock);
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
} else { } else {
urb->status = -ENOENT; urb->status = -ENOENT;
...@@ -1774,6 +1816,9 @@ static int uhci_unlink_urb(struct urb *urb) ...@@ -1774,6 +1816,9 @@ static int uhci_unlink_urb(struct urb *urb)
} else } else
schedule_timeout(1+1*HZ/1000); schedule_timeout(1+1*HZ/1000);
spin_unlock(&urb->lock);
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
uhci_call_completion(urb); uhci_call_completion(urb);
} }
} }
...@@ -1907,12 +1952,14 @@ static __u8 root_hub_hub_des[] = ...@@ -1907,12 +1952,14 @@ static __u8 root_hub_hub_des[] =
/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
static int rh_send_irq(struct urb *urb) static int rh_send_irq(struct urb *urb)
{ {
int i, len = 1;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
unsigned int io_addr = uhci->io_addr; unsigned int io_addr = uhci->io_addr;
unsigned long flags;
int i, len = 1;
__u16 data = 0; __u16 data = 0;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
spin_lock_irqsave(&urb->lock, flags);
for (i = 0; i < uhci->rh.numports; i++) { for (i = 0; i < uhci->rh.numports; i++) {
data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
len = (i + 1) / 8 + 1; len = (i + 1) / 8 + 1;
...@@ -1922,6 +1969,8 @@ static int rh_send_irq(struct urb *urb) ...@@ -1922,6 +1969,8 @@ static int rh_send_irq(struct urb *urb)
urb->actual_length = len; urb->actual_length = len;
urbp->status = 0; urbp->status = 0;
spin_unlock_irqrestore(&urb->lock, flags);
if ((data > 0) && (uhci->rh.send != 0)) { if ((data > 0) && (uhci->rh.send != 0)) {
dbg("root-hub INT complete: port1: %x port2: %x data: %x", dbg("root-hub INT complete: port1: %x port2: %x data: %x",
inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
...@@ -1948,7 +1997,6 @@ static void rh_int_timer_do(unsigned long ptr) ...@@ -1948,7 +1997,6 @@ static void rh_int_timer_do(unsigned long ptr)
spin_lock_irqsave(&uhci->urb_list_lock, flags); spin_lock_irqsave(&uhci->urb_list_lock, flags);
head = &uhci->urb_list; head = &uhci->urb_list;
tmp = head->next; tmp = head->next;
while (tmp != head) { while (tmp != head) {
struct urb *u = list_entry(tmp, struct urb, urb_list); struct urb *u = list_entry(tmp, struct urb, urb_list);
...@@ -1956,6 +2004,8 @@ static void rh_int_timer_do(unsigned long ptr) ...@@ -1956,6 +2004,8 @@ static void rh_int_timer_do(unsigned long ptr)
tmp = tmp->next; tmp = tmp->next;
spin_lock(&urb->lock);
/* Check if the FSBR timed out */ /* Check if the FSBR timed out */
if (urbp->fsbr && !urbp->fsbr_timeout && time_after_eq(jiffies, urbp->fsbrtime + IDLE_TIMEOUT)) if (urbp->fsbr && !urbp->fsbr_timeout && time_after_eq(jiffies, urbp->fsbrtime + IDLE_TIMEOUT))
uhci_fsbr_timeout(uhci, u); uhci_fsbr_timeout(uhci, u);
...@@ -1965,6 +2015,8 @@ static void rh_int_timer_do(unsigned long ptr) ...@@ -1965,6 +2015,8 @@ static void rh_int_timer_do(unsigned long ptr)
list_del(&u->urb_list); list_del(&u->urb_list);
list_add_tail(&u->urb_list, &list); list_add_tail(&u->urb_list, &list);
} }
spin_unlock(&urb->lock);
} }
spin_unlock_irqrestore(&uhci->urb_list_lock, flags); spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
...@@ -2198,6 +2250,9 @@ static int rh_submit_urb(struct urb *urb) ...@@ -2198,6 +2250,9 @@ static int rh_submit_urb(struct urb *urb)
return stat; return stat;
} }
/*
* MUST be called with urb->lock acquired
*/
static int rh_unlink_urb(struct urb *urb) static int rh_unlink_urb(struct urb *urb)
{ {
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
...@@ -2233,16 +2288,25 @@ static void uhci_free_pending_qhs(struct uhci *uhci) ...@@ -2233,16 +2288,25 @@ static void uhci_free_pending_qhs(struct uhci *uhci)
static void uhci_call_completion(struct urb *urb) static void uhci_call_completion(struct urb *urb)
{ {
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct urb_priv *urbp;
struct usb_device *dev = urb->dev; struct usb_device *dev = urb->dev;
struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
int is_ring = 0, killed, resubmit_interrupt, status; int is_ring = 0, killed, resubmit_interrupt, status;
struct urb *nurb; struct urb *nurb;
unsigned long flags;
spin_lock_irqsave(&urb->lock, flags);
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp || !urb->dev) {
spin_unlock_irqrestore(&urb->lock, flags);
return;
}
killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
urb->status == -ECONNRESET); urb->status == -ECONNRESET);
resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
urb->interval && !killed); urb->interval);
nurb = urb->next; nurb = urb->next;
if (nurb && !killed) { if (nurb && !killed) {
...@@ -2267,14 +2331,6 @@ static void uhci_call_completion(struct urb *urb) ...@@ -2267,14 +2331,6 @@ static void uhci_call_completion(struct urb *urb)
is_ring = (nurb == urb); is_ring = (nurb == urb);
} }
status = urbp->status;
if (!resubmit_interrupt)
/* We don't need urb_priv anymore */
uhci_destroy_urb_priv(urb);
if (!killed)
urb->status = status;
if (urbp->transfer_buffer_dma_handle) if (urbp->transfer_buffer_dma_handle)
pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle,
urb->transfer_buffer_length, usb_pipein(urb->pipe) ? urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
...@@ -2284,11 +2340,28 @@ static void uhci_call_completion(struct urb *urb) ...@@ -2284,11 +2340,28 @@ static void uhci_call_completion(struct urb *urb)
pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle,
sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
status = urbp->status;
if (!resubmit_interrupt || killed)
/* We don't need urb_priv anymore */
uhci_destroy_urb_priv(urb);
if (!killed)
urb->status = status;
urb->dev = NULL; urb->dev = NULL;
if (urb->complete) spin_unlock_irqrestore(&urb->lock, flags);
if (urb->complete) {
urb->complete(urb); urb->complete(urb);
if (resubmit_interrupt) { /* Recheck the status. The completion handler may have */
/* unlinked the resubmitting interrupt URB */
killed = (urb->status == -ENOENT ||
urb->status == -ECONNABORTED ||
urb->status == -ECONNRESET);
}
if (resubmit_interrupt && !killed) {
urb->dev = dev; urb->dev = dev;
uhci_reset_interrupt(urb); uhci_reset_interrupt(urb);
} else { } else {
...@@ -2299,6 +2372,7 @@ static void uhci_call_completion(struct urb *urb) ...@@ -2299,6 +2372,7 @@ static void uhci_call_completion(struct urb *urb)
/* We decrement the usage count after we're done */ /* We decrement the usage count after we're done */
/* with everything */ /* with everything */
usb_dec_dev_use(dev); usb_dec_dev_use(dev);
usb_put_urb(urb);
} }
} }
} }
...@@ -2315,11 +2389,14 @@ static void uhci_finish_completion(struct uhci *uhci) ...@@ -2315,11 +2389,14 @@ static void uhci_finish_completion(struct uhci *uhci)
struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list); struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
struct urb *urb = urbp->urb; struct urb *urb = urbp->urb;
tmp = tmp->next;
list_del_init(&urbp->complete_list); list_del_init(&urbp->complete_list);
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
uhci_call_completion(urb); uhci_call_completion(urb);
spin_lock_irqsave(&uhci->complete_list_lock, flags);
head = &uhci->complete_list;
tmp = head->next;
} }
spin_unlock_irqrestore(&uhci->complete_list_lock, flags); spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
} }
...@@ -2341,7 +2418,8 @@ static void uhci_remove_pending_qhs(struct uhci *uhci) ...@@ -2341,7 +2418,8 @@ static void uhci_remove_pending_qhs(struct uhci *uhci)
list_del_init(&urb->urb_list); list_del_init(&urb->urb_list);
urbp->status = urb->status = -ECONNRESET; urbp->status = urb->status = -ECONNRESET;
uhci_call_completion(urb);
uhci_add_complete(urb);
} }
spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
} }
...@@ -3074,3 +3152,4 @@ module_exit(uhci_hcd_cleanup); ...@@ -3074,3 +3152,4 @@ module_exit(uhci_hcd_cleanup);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -121,15 +121,16 @@ struct uhci_qh { ...@@ -121,15 +121,16 @@ struct uhci_qh {
* for TD <info>: (a.k.a. Token) * for TD <info>: (a.k.a. Token)
*/ */
#define TD_TOKEN_TOGGLE 19 #define TD_TOKEN_TOGGLE 19
#define TD_PID 0xFF #define TD_TOKEN_PID_MASK 0xFF
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */
#define uhci_maxlen(token) ((token) >> 21) #define uhci_maxlen(token) ((token) >> 21)
#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ #define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> 8) & 0x7f) #define uhci_devaddr(token) (((token) >> 8) & 0x7f)
#define uhci_devep(token) (((token) >> 8) & 0x7ff) #define uhci_devep(token) (((token) >> 8) & 0x7ff)
#define uhci_packetid(token) ((token) & 0xff) #define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) #define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) #define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
...@@ -163,7 +164,7 @@ struct uhci_td { ...@@ -163,7 +164,7 @@ struct uhci_td {
struct list_head list; /* P: urb->lock */ struct list_head list; /* P: urb->lock */
int frame; int frame;
struct list_head fl_list; /* P: frame_list_lock */ struct list_head fl_list; /* P: uhci->frame_list_lock */
} __attribute__((aligned(16))); } __attribute__((aligned(16)));
/* /*
...@@ -306,21 +307,25 @@ struct uhci { ...@@ -306,21 +307,25 @@ struct uhci {
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
spinlock_t frame_list_lock; spinlock_t frame_list_lock;
struct uhci_frame_list *fl; /* Frame list */ struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */
int fsbr; /* Full speed bandwidth reclamation */ int fsbr; /* Full speed bandwidth reclamation */
int is_suspended; int is_suspended;
/* Main list of URB's currently controlled by this HC */
spinlock_t urb_list_lock;
struct list_head urb_list; /* P: uhci->urb_list_lock */
/* List of QH's that are done, but waiting to be unlinked (race) */
spinlock_t qh_remove_list_lock; spinlock_t qh_remove_list_lock;
struct list_head qh_remove_list; struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */
/* List of asynchronously unlinked URB's */
spinlock_t urb_remove_list_lock; spinlock_t urb_remove_list_lock;
struct list_head urb_remove_list; struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */
spinlock_t urb_list_lock;
struct list_head urb_list;
/* List of URB's awaiting completion callback */
spinlock_t complete_list_lock; spinlock_t complete_list_lock;
struct list_head complete_list; struct list_head complete_list; /* P: uhci->complete_list_lock */
struct virt_root_hub rh; /* private data of the virtual root hub */ struct virt_root_hub rh; /* private data of the virtual root hub */
}; };
...@@ -333,7 +338,7 @@ struct urb_priv { ...@@ -333,7 +338,7 @@ struct urb_priv {
dma_addr_t transfer_buffer_dma_handle; dma_addr_t transfer_buffer_dma_handle;
struct uhci_qh *qh; /* QH for this URB */ struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list; struct list_head td_list; /* P: urb->lock */
int fsbr : 1; /* URB turned on FSBR */ int fsbr : 1; /* URB turned on FSBR */
int fsbr_timeout : 1; /* URB timed out on FSBR */ int fsbr_timeout : 1; /* URB timed out on FSBR */
...@@ -345,12 +350,37 @@ struct urb_priv { ...@@ -345,12 +350,37 @@ struct urb_priv {
int status; /* Final status */ int status; /* Final status */
unsigned long inserttime; /* In jiffies */ unsigned long inserttime; /* In jiffies */
unsigned long fsbrtime; /* In jiffies */ unsigned long fsbrtime; /* In jiffies */
struct list_head queue_list; struct list_head queue_list; /* P: uhci->frame_list_lock */
struct list_head complete_list; struct list_head complete_list; /* P: uhci->complete_list_lock */
}; };
/*
* Locking in uhci.c
*
* spinlocks are used extensively to protect the many lists and data
* structures we have. It's not that pretty, but it's necessary. We
* need to be done with all of the locks (except complete_list_lock) when
* we call urb->complete. I've tried to make it simple enough so I don't
* have to spend hours racking my brain trying to figure out if the
* locking is safe.
*
* Here's the safe locking order to prevent deadlocks:
*
* #1 uhci->urb_list_lock
* #2 urb->lock
* #3 uhci->urb_remove_list_lock, uhci->frame_list_lock,
* uhci->qh_remove_list_lock
* #4 uhci->complete_list_lock
*
* If you're going to grab 2 or more locks at once, ALWAYS grab the lock
* at the lowest level FIRST and NEVER grab locks at the same level at the
* same time.
*
* So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
*/
/* ------------------------------------------------------------------------- /* -------------------------------------------------------------------------
Virtual Root HUB Virtual Root HUB
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
......
...@@ -205,6 +205,7 @@ static void urb_rm_priv_locked (struct urb * urb) ...@@ -205,6 +205,7 @@ static void urb_rm_priv_locked (struct urb * urb)
urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
urb->dev = NULL; urb->dev = NULL;
usb_put_urb (urb);
} }
} }
...@@ -553,6 +554,9 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -553,6 +554,9 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) // if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
// return -EPIPE; // return -EPIPE;
/* increment the reference count of the urb, as we now also control it */
urb = usb_get_urb (urb);
usb_inc_dev_use (urb->dev); usb_inc_dev_use (urb->dev);
ohci = (ohci_t *) urb->dev->bus->hcpriv; ohci = (ohci_t *) urb->dev->bus->hcpriv;
...@@ -568,12 +572,14 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -568,12 +572,14 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
* such as powering down ports */ * such as powering down ports */
if (ohci->disabled) { if (ohci->disabled) {
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -ESHUTDOWN; return -ESHUTDOWN;
} }
/* every endpoint has a ed, locate and fill it */ /* every endpoint has a ed, locate and fill it */
if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -ENOMEM; return -ENOMEM;
} }
...@@ -595,6 +601,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -595,6 +601,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
size = urb->number_of_packets; size = urb->number_of_packets;
if (size <= 0) { if (size <= 0) {
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -EINVAL; return -EINVAL;
} }
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
...@@ -615,6 +622,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -615,6 +622,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), mem_flags); urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), mem_flags);
if (!urb_priv) { if (!urb_priv) {
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -ENOMEM; return -ENOMEM;
} }
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
...@@ -632,6 +640,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -632,6 +640,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
urb_free_priv (ohci, urb_priv); urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&usb_ed_lock, flags); spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -ENOMEM; return -ENOMEM;
} }
} }
...@@ -640,6 +649,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -640,6 +649,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
urb_free_priv (ohci, urb_priv); urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&usb_ed_lock, flags); spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return -EINVAL; return -EINVAL;
} }
...@@ -662,6 +672,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags) ...@@ -662,6 +672,7 @@ static int sohci_submit_urb (struct urb * urb, int mem_flags)
urb_free_priv (ohci, urb_priv); urb_free_priv (ohci, urb_priv);
spin_unlock_irqrestore (&usb_ed_lock, flags); spin_unlock_irqrestore (&usb_ed_lock, flags);
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
return bustime; return bustime;
} }
usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe)); usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
...@@ -2100,6 +2111,7 @@ static int rh_submit_urb (struct urb * urb) ...@@ -2100,6 +2111,7 @@ static int rh_submit_urb (struct urb * urb)
urb->dev = NULL; urb->dev = NULL;
if (urb->complete) if (urb->complete)
urb->complete (urb); urb->complete (urb);
usb_put_urb (urb);
return 0; return 0;
} }
...@@ -2123,6 +2135,7 @@ static int rh_unlink_urb (struct urb * urb) ...@@ -2123,6 +2135,7 @@ static int rh_unlink_urb (struct urb * urb)
urb->complete (urb); urb->complete (urb);
} else } else
urb->status = -ENOENT; urb->status = -ENOENT;
usb_put_urb (urb);
} }
return 0; return 0;
} }
......
...@@ -1217,6 +1217,7 @@ _static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb) ...@@ -1217,6 +1217,7 @@ _static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb)
urb->complete ((struct urb *) urb); urb->complete ((struct urb *) urb);
} }
usb_dec_dev_use (usb_dev); usb_dec_dev_use (usb_dev);
usb_put_urb (urb);
} }
else else
spin_unlock_irqrestore (&s->urb_list_lock, flags); spin_unlock_irqrestore (&s->urb_list_lock, flags);
...@@ -1305,7 +1306,7 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) ...@@ -1305,7 +1306,7 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force)
#else #else
kfree (urb_priv); kfree (urb_priv);
#endif #endif
usb_put_urb (urb);
} }
} }
} }
...@@ -1650,6 +1651,9 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags) ...@@ -1650,6 +1651,9 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags)
return -EINVAL; return -EINVAL;
} }
/* increment the reference count of the urb, as we now also control it */
urb = usb_get_urb (urb);
usb_inc_dev_use (urb->dev); usb_inc_dev_use (urb->dev);
spin_lock_irqsave (&s->urb_list_lock, flags); spin_lock_irqsave (&s->urb_list_lock, flags);
...@@ -1665,6 +1669,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags) ...@@ -1665,6 +1669,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags)
(!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) { (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
spin_unlock_irqrestore (&s->urb_list_lock, flags); spin_unlock_irqrestore (&s->urb_list_lock, flags);
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
usb_put_urb (urb);
err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb); err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb);
return -ENXIO; // urb already queued return -ENXIO; // urb already queued
} }
...@@ -1678,6 +1683,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags) ...@@ -1678,6 +1683,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags)
if (!urb_priv) { if (!urb_priv) {
usb_dec_dev_use (urb->dev); usb_dec_dev_use (urb->dev);
spin_unlock_irqrestore (&s->urb_list_lock, flags); spin_unlock_irqrestore (&s->urb_list_lock, flags);
usb_put_urb (urb);
return -ENOMEM; return -ENOMEM;
} }
...@@ -1766,6 +1772,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags) ...@@ -1766,6 +1772,7 @@ _static int uhci_submit_urb (struct urb *urb, int mem_flags)
#else #else
kfree (urb_priv); kfree (urb_priv);
#endif #endif
usb_put_urb (urb);
return ret; return ret;
} }
...@@ -2730,6 +2737,7 @@ _static int process_urb (uhci_t *s, struct list_head *p) ...@@ -2730,6 +2737,7 @@ _static int process_urb (uhci_t *s, struct list_head *p)
} }
usb_dec_dev_use (usb_dev); usb_dec_dev_use (usb_dev);
usb_put_urb (urb);
} }
} }
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/module.h> #include <linux/module.h>
......
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