Commit 455980ab authored by Linus Torvalds's avatar Linus Torvalds

Merge USB update

parents 54f1543d b305ae0f
...@@ -690,7 +690,6 @@ static void irda_usb_net_timeout(struct net_device *netdev) ...@@ -690,7 +690,6 @@ static void irda_usb_net_timeout(struct net_device *netdev)
* The way I see it is that if we submit more than one Rx URB at a * The way I see it is that if we submit more than one Rx URB at a
* time, the Rx URB can be automatically re-submitted after the * time, the Rx URB can be automatically re-submitted after the
* completion handler is called. * completion handler is called.
* We make sure to disable this feature by setting urb->next to NULL
* *
* My take is that it's a questionable feature, and quite difficult * My take is that it's a questionable feature, and quite difficult
* to control and to make work effectively. * to control and to make work effectively.
...@@ -754,7 +753,6 @@ static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struc ...@@ -754,7 +753,6 @@ static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struc
/* Note : unlink *must* be synchronous because of the code in /* Note : unlink *must* be synchronous because of the code in
* irda_usb_net_close() -> free the skb - Jean II */ * irda_usb_net_close() -> free the skb - Jean II */
urb->status = 0; urb->status = 0;
urb->next = NULL; /* Don't auto resubmit URBs */
/* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */ /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */
ret = usb_submit_urb(urb, GFP_ATOMIC); ret = usb_submit_urb(urb, GFP_ATOMIC);
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
mod-subdirs := serial mod-subdirs := serial
obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_OHCI) += host/ obj-$(CONFIG_USB_OHCI) += host/
obj-$(CONFIG_USB_SL811HS) += host/
obj-$(CONFIG_USB_UHCI_ALT) += host/ obj-$(CONFIG_USB_UHCI_ALT) += host/
obj-$(CONFIG_USB_UHCI_HCD_ALT) += host/ obj-$(CONFIG_USB_UHCI_HCD_ALT) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/
......
...@@ -745,12 +745,18 @@ EXPORT_SYMBOL (usb_register_root_hub); ...@@ -745,12 +745,18 @@ EXPORT_SYMBOL (usb_register_root_hub);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /**
* usb_calc_bus_time: * usb_calc_bus_time: approximate periodic transaction time in nanoseconds
* @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
* @is_input: true iff the transaction sends data to the host
* @is_isoc: true for isochronous transactions, false for interrupt ones
* @bytecount: how many bytes in the transaction.
*
* Returns approximate bus time in nanoseconds for a periodic transaction. * Returns approximate bus time in nanoseconds for a periodic transaction.
* See USB 2.0 spec section 5.11.3 * See USB 2.0 spec section 5.11.3; only periodic transfers need to be
* scheduled in software, this function is only used for such scheduling.
*/ */
static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
{ {
unsigned long tmp; unsigned long tmp;
...@@ -772,14 +778,18 @@ static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) ...@@ -772,14 +778,18 @@ static long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
return (9107L + BW_HOST_DELAY + tmp); return (9107L + BW_HOST_DELAY + tmp);
} }
case USB_SPEED_HIGH: /* ISOC or INTR */ case USB_SPEED_HIGH: /* ISOC or INTR */
// FIXME merge from EHCI code; caller will need to handle // FIXME adjust for input vs output
// each part of a split separately. if (isoc)
return 0; tmp = HS_USECS (bytecount);
else
tmp = HS_USECS_ISO (bytecount);
return tmp;
default: default:
dbg ("bogus device speed!"); dbg ("bogus device speed!");
return -1; return -1;
} }
} }
EXPORT_SYMBOL (usb_calc_bus_time);
/* /*
* usb_check_bandwidth(): * usb_check_bandwidth():
......
...@@ -263,6 +263,22 @@ extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, ...@@ -263,6 +263,22 @@ extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
/*
* Ceiling microseconds (typical) for that many bytes at high speed
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
* to preallocate bandwidth)
*/
#define USB2_HOST_DELAY 5 /* nsec, guess */
#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ USB2_HOST_DELAY)
extern long usb_calc_bus_time (int speed, int is_input,
int isoc, int bytecount);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
extern struct usb_bus *usb_alloc_bus (struct usb_operations *); extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
......
...@@ -2032,8 +2032,8 @@ int usb_set_address(struct usb_device *dev) ...@@ -2032,8 +2032,8 @@ int usb_set_address(struct usb_device *dev)
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Returns zero on success, or else the status code returned by the * Returns the number of bytes received on success, or else the status code
* underlying usb_control_msg() call. * returned by the underlying usb_control_msg() call.
*/ */
int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
{ {
...@@ -2073,8 +2073,8 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char ...@@ -2073,8 +2073,8 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Returns zero on success, or else the status code returned by the * Returns the number of bytes received on success, or else the status code
* underlying usb_control_msg() call. * returned by the underlying usb_control_msg() call.
*/ */
int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
{ {
...@@ -2100,8 +2100,8 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char ...@@ -2100,8 +2100,8 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Returns zero on success, or else the status code returned by the * Returns the number of bytes received on success, or else the status code
* underlying usb_control_msg() call. * returned by the underlying usb_control_msg() call.
*/ */
int usb_get_device_descriptor(struct usb_device *dev) int usb_get_device_descriptor(struct usb_device *dev)
{ {
...@@ -2135,8 +2135,8 @@ int usb_get_device_descriptor(struct usb_device *dev) ...@@ -2135,8 +2135,8 @@ int usb_get_device_descriptor(struct usb_device *dev)
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Returns zero on success, or else the status code returned by the * Returns the number of bytes received on success, or else the status code
* underlying usb_control_msg() call. * returned by the underlying usb_control_msg() call.
*/ */
int usb_get_status(struct usb_device *dev, int type, int target, void *data) int usb_get_status(struct usb_device *dev, int type, int target, void *data)
{ {
......
...@@ -89,3 +89,13 @@ CONFIG_USB_OHCI ...@@ -89,3 +89,13 @@ CONFIG_USB_OHCI
inserted in and removed from the running kernel whenever you want). inserted in and removed from the running kernel whenever you want).
The module will be called usb-ohci.o. If you want to compile it The module will be called usb-ohci.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>. as a module, say M here and read <file:Documentation/modules.txt>.
CONFIG_USB_SL811HS
Say Y here if you have a SL811HS USB host controller in your system.
If you do not know what this is, please say N.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called hc_sl811.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
...@@ -19,3 +19,6 @@ fi ...@@ -19,3 +19,6 @@ fi
# define_bool CONFIG_USB_UHCI_ALT n # define_bool CONFIG_USB_UHCI_ALT n
#fi #fi
#dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB #dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
if [ "$CONFIG_ARM" = "y" ]; then
dep_tristate ' SL811HS support' CONFIG_USB_SL811HS $CONFIG_USB
fi
...@@ -11,5 +11,6 @@ obj-$(CONFIG_USB_UHCI_HCD_ALT) += uhci-hcd.o ...@@ -11,5 +11,6 @@ obj-$(CONFIG_USB_UHCI_HCD_ALT) += uhci-hcd.o
obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_UHCI) += usb-uhci.o
obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_UHCI_ALT) += uhci.o
obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o
obj-$(CONFIG_USB_SL811HS) += hc_sl811.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
...@@ -102,8 +102,8 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} ...@@ -102,8 +102,8 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
#ifdef DEBUG #ifdef DEBUG
#if 0 static void __attribute__((__unused__))
static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{ {
dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label, dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
qh, qh->hw_info1, qh->hw_info2, qh, qh->hw_info1, qh->hw_info2,
...@@ -117,15 +117,13 @@ static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -117,15 +117,13 @@ static void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->hw_buf [4]); qh->hw_buf [4]);
} }
} }
#endif
static const char *const fls_strings [] = static const char *const fls_strings [] =
{ "1024", "512", "256", "??" }; { "1024", "512", "256", "??" };
#else #else
#if 0 static inline void __attribute__((__unused__))
static inline void dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {}
#endif
#endif /* DEBUG */ #endif /* DEBUG */
/* functions have the "wrong" filename when they're output... */ /* functions have the "wrong" filename when they're output... */
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
* *
* HISTORY: * HISTORY:
* *
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
* 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other * 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other
* missing pieces: enabling 64bit dma, handoff from BIOS/SMM. * missing pieces: enabling 64bit dma, handoff from BIOS/SMM.
* 2002-05-07 Some error path cleanups to report better errors; wmb(); * 2002-05-07 Some error path cleanups to report better errors; wmb();
...@@ -82,7 +83,7 @@ ...@@ -82,7 +83,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2002-May-11" #define DRIVER_VERSION "2002-May-24"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
...@@ -622,7 +623,10 @@ dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", ...@@ -622,7 +623,10 @@ dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d",
return 0; return 0;
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
intr_deschedule (ehci, urb->start_frame, qh, urb->interval); intr_deschedule (ehci, urb->start_frame, qh,
(urb->dev->speed == USB_SPEED_HIGH)
? urb->interval
: (urb->interval << 3));
if (ehci->hcd.state == USB_STATE_HALT) if (ehci->hcd.state == USB_STATE_HALT)
urb->status = -ESHUTDOWN; urb->status = -ESHUTDOWN;
qh_completions (ehci, qh, 1); qh_completions (ehci, qh, 1);
......
...@@ -33,19 +33,6 @@ ...@@ -33,19 +33,6 @@
* or with "USB On The Go" additions to USB 2.0 ...) * or with "USB On The Go" additions to USB 2.0 ...)
*/ */
/*
* Ceiling microseconds (typical) for that many bytes at high speed
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
* to preallocate bandwidth)
*/
#define EHCI_HOST_DELAY 5 /* nsec, guess */
#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ EHCI_HOST_DELAY)
#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+ ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ EHCI_HOST_DELAY)
static int ehci_get_frame (struct usb_hcd *hcd); static int ehci_get_frame (struct usb_hcd *hcd);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -124,6 +111,9 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) ...@@ -124,6 +111,9 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
/* is it in the S-mask? */ /* is it in the S-mask? */
if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
usecs += q->qh->usecs; usecs += q->qh->usecs;
/* ... or C-mask? */
if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
usecs += q->qh->c_usecs;
q = &q->qh->qh_next; q = &q->qh->qh_next;
break; break;
case Q_TYPE_FSTN: case Q_TYPE_FSTN:
...@@ -273,6 +263,12 @@ static int check_period ( ...@@ -273,6 +263,12 @@ static int check_period (
unsigned period, unsigned period,
unsigned usecs unsigned usecs
) { ) {
/* complete split running into next frame?
* given FSTN support, we could sometimes check...
*/
if (uframe >= 8)
return 0;
/* /*
* 80% periodic == 100 usec/uframe available * 80% periodic == 100 usec/uframe available
* convert "usecs we need" to "max already claimed" * convert "usecs we need" to "max already claimed"
...@@ -284,6 +280,8 @@ static int check_period ( ...@@ -284,6 +280,8 @@ static int check_period (
// FIXME delete when intr_submit handles non-empty queues // FIXME delete when intr_submit handles non-empty queues
// this gives us a one intr/frame limit (vs N/uframe) // this gives us a one intr/frame limit (vs N/uframe)
// ... and also lets us avoid tracking split transactions
// that might collide at a given TT/hub.
if (ehci->pshadow [frame].ptr) if (ehci->pshadow [frame].ptr)
return 0; return 0;
...@@ -305,20 +303,54 @@ static int intr_submit ( ...@@ -305,20 +303,54 @@ static int intr_submit (
int mem_flags int mem_flags
) { ) {
unsigned epnum, period; unsigned epnum, period;
unsigned short usecs; unsigned short usecs, c_usecs, gap_uf;
unsigned long flags; unsigned long flags;
struct ehci_qh *qh; struct ehci_qh *qh;
struct hcd_dev *dev; struct hcd_dev *dev;
int is_input;
int status = 0; int status = 0;
/* get endpoint and transfer data */ /* get endpoint and transfer/schedule data */
epnum = usb_pipeendpoint (urb->pipe); epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe)) is_input = usb_pipein (urb->pipe);
if (is_input)
epnum |= 0x10; epnum |= 0x10;
if (urb->dev->speed != USB_SPEED_HIGH) {
dbg ("no intr/tt scheduling yet"); /*
status = -ENOSYS; * HS interrupt transfers are simple -- only one microframe. FS/LS
goto done; * interrupt transfers involve a SPLIT in one microframe and CSPLIT
* sometime later. We need to know how much time each will be
* needed in each microframe and, for FS/LS, how many microframes
* separate the two in the best case.
*/
usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
urb->transfer_buffer_length);
if (urb->dev->speed == USB_SPEED_HIGH) {
gap_uf = 0;
c_usecs = 0;
/* FIXME handle HS periods of less than 1 frame. */
period = urb->interval >> 3;
if (period < 1) {
dbg ("intr period %d uframes, NYET!", urb->interval);
status = -EINVAL;
goto done;
}
} else {
/* gap is a function of full/low speed transfer times */
gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0,
urb->transfer_buffer_length) / (125 * 1000);
/* FIXME this just approximates SPLIT/CSPLIT times */
if (is_input) { // SPLIT, gap, CSPLIT+DATA
c_usecs = usecs + HS_USECS (0);
usecs = HS_USECS (1);
} else { // SPLIT+DATA, gap, CSPLIT
usecs = usecs + HS_USECS (1);
c_usecs = HS_USECS (0);
}
period = urb->interval;
} }
/* /*
...@@ -339,16 +371,6 @@ static int intr_submit ( ...@@ -339,16 +371,6 @@ static int intr_submit (
goto done; goto done;
} }
usecs = HS_USECS (urb->transfer_buffer_length);
/* FIXME handle HS periods of less than 1 frame. */
period = urb->interval >> 3;
if (period < 1) {
dbg ("intr period %d uframes, NYET!", urb->interval);
status = -EINVAL;
goto done;
}
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
/* get the qh (must be empty and idle) */ /* get the qh (must be empty and idle) */
...@@ -392,6 +414,7 @@ static int intr_submit ( ...@@ -392,6 +414,7 @@ static int intr_submit (
qh->hw_next = EHCI_LIST_END; qh->hw_next = EHCI_LIST_END;
qh->usecs = usecs; qh->usecs = usecs;
qh->c_usecs = c_usecs;
urb->hcpriv = qh_get (qh); urb->hcpriv = qh_get (qh);
status = -ENOSPC; status = -ENOSPC;
...@@ -399,18 +422,47 @@ static int intr_submit ( ...@@ -399,18 +422,47 @@ static int intr_submit (
/* pick a set of schedule slots, link the QH into them */ /* pick a set of schedule slots, link the QH into them */
do { do {
int uframe; int uframe;
u32 c_mask = 0;
/* pick a set of slots such that all uframes have /* pick a set of slots such that all uframes have
* enough periodic bandwidth available. * enough periodic bandwidth available.
*
* FIXME for TT splits, need uframes for start and end.
* FSTNs can put end into next frame (uframes 0 or 1).
*/ */
frame--; frame--;
for (uframe = 0; uframe < 8; uframe++) { for (uframe = 0; uframe < 8; uframe++) {
if (check_period (ehci, frame, uframe, if (check_period (ehci, frame, uframe,
period, usecs) != 0) period, usecs) == 0)
break; continue;
/* If this is a split transaction, check the
* bandwidth available for the completion
* too. check both best and worst case gaps:
* worst case is SPLIT near uframe end, and
* CSPLIT near start ... best is vice versa.
* Difference can be almost two uframe times.
*
* FIXME don't even bother unless we know
* this TT is idle in that uframe ... right
* now we know only one interrupt transfer
* will be scheduled per frame, so we don't
* need to update/check TT state when we
* schedule a split (QH, SITD, or FSTN).
*
* FIXME ehci 0.96 and above can use FSTNs
*/
if (!c_usecs)
break;
if (check_period (ehci, frame,
uframe + gap_uf,
period, c_usecs) == 0)
continue;
if (check_period (ehci, frame,
uframe + gap_uf + 1,
period, c_usecs) == 0)
continue;
c_mask = 0x03 << (8 + uframe + gap_uf);
c_mask = cpu_to_le32 (c_mask);
break;
} }
if (uframe == 8) if (uframe == 8)
continue; continue;
...@@ -419,13 +471,14 @@ static int intr_submit ( ...@@ -419,13 +471,14 @@ static int intr_submit (
urb->start_frame = frame; urb->start_frame = frame;
status = 0; status = 0;
/* set S-frame mask */ /* reset S-frame and (maybe) C-frame masks */
qh->hw_info2 |= cpu_to_le32 (1 << uframe); qh->hw_info2 &= ~0xffff;
qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
// dbg_qh ("Schedule INTR qh", ehci, qh); // dbg_qh ("Schedule INTR qh", ehci, qh);
/* stuff into the periodic schedule */ /* stuff into the periodic schedule */
qh->qh_state = QH_STATE_LINKED; qh->qh_state = QH_STATE_LINKED;
vdbg ("qh %p usecs %d period %d starting %d.%d", vdbg ("qh %p usecs %d period %d.0 starting %d.%d",
qh, qh->usecs, period, frame, uframe); qh, qh->usecs, period, frame, uframe);
do { do {
if (unlikely (ehci->pshadow [frame].ptr != 0)) { if (unlikely (ehci->pshadow [frame].ptr != 0)) {
...@@ -443,7 +496,8 @@ static int intr_submit ( ...@@ -443,7 +496,8 @@ static int intr_submit (
} while (frame < ehci->periodic_size); } while (frame < ehci->periodic_size);
/* update bandwidth utilization records (for usbfs) */ /* update bandwidth utilization records (for usbfs) */
usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); usb_claim_bandwidth (urb->dev, urb,
(usecs + c_usecs) / period, 0);
/* maybe enable periodic schedule processing */ /* maybe enable periodic schedule processing */
if (!ehci->periodic_urbs++) if (!ehci->periodic_urbs++)
...@@ -557,6 +611,7 @@ itd_fill ( ...@@ -557,6 +611,7 @@ itd_fill (
u32 buf1; u32 buf1;
unsigned i, epnum, maxp, multi; unsigned i, epnum, maxp, multi;
unsigned length; unsigned length;
int is_input;
itd->hw_next = EHCI_LIST_END; itd->hw_next = EHCI_LIST_END;
itd->urb = urb; itd->urb = urb;
...@@ -578,7 +633,8 @@ itd_fill ( ...@@ -578,7 +633,8 @@ itd_fill (
* as encoded in the ep descriptor's maxpacket field * as encoded in the ep descriptor's maxpacket field
*/ */
epnum = usb_pipeendpoint (urb->pipe); epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe)) { is_input = usb_pipein (urb->pipe);
if (is_input) {
maxp = urb->dev->epmaxpacketin [epnum]; maxp = urb->dev->epmaxpacketin [epnum];
buf1 = (1 << 11); buf1 = (1 << 11);
} else { } else {
...@@ -598,7 +654,7 @@ itd_fill ( ...@@ -598,7 +654,7 @@ itd_fill (
urb->iso_frame_desc [index].length); urb->iso_frame_desc [index].length);
return -ENOSPC; return -ENOSPC;
} }
itd->usecs = HS_USECS_ISO (length); itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
/* "plus" info in low order bits of buffer pointers */ /* "plus" info in low order bits of buffer pointers */
itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum); itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
...@@ -919,17 +975,9 @@ itd_complete ( ...@@ -919,17 +975,9 @@ itd_complete (
return flags; return flags;
/* /*
* For now, always give the urb back to the driver ... expect it * Always give the urb back to the driver ... expect it to submit
* to submit a new urb (or resubmit this), and to have another * a new urb (or resubmit this), and to have another already queued
* already queued when un-interrupted transfers are needed. * when un-interrupted transfers are needed.
* No, that's not what OHCI or UHCI are now doing.
*
* FIXME Revisit the ISO URB model. It's cleaner not to have all
* the special case magic, but it'd be faster to reuse existing
* ITD/DMA setup and schedule state. Easy to dma_sync/complete(),
* then either reschedule or, if unlinking, free and giveback().
* But we can't overcommit like the full and low speed HCs do, and
* there's no clean way to report an error when rescheduling...
* *
* NOTE that for now we don't accelerate ISO unlinks; they just * NOTE that for now we don't accelerate ISO unlinks; they just
* happen according to the current schedule. Means a delay of * happen according to the current schedule. Means a delay of
......
...@@ -288,14 +288,11 @@ struct ehci_qh { ...@@ -288,14 +288,11 @@ struct ehci_qh {
atomic_t refcount; atomic_t refcount;
unsigned short usecs; /* intr bandwidth */ unsigned short usecs; /* intr bandwidth */
unsigned short c_usecs; /* ... split completion bw */
short qh_state; short qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */
#ifdef EHCI_SOFT_RETRIES
int retries;
#endif
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -360,6 +357,9 @@ struct ehci_sitd { ...@@ -360,6 +357,9 @@ struct ehci_sitd {
union ehci_shadow sitd_next; /* ptr to periodic q entry */ union ehci_shadow sitd_next; /* ptr to periodic q entry */
struct urb *urb; struct urb *urb;
dma_addr_t buf_dma; /* buffer address */ dma_addr_t buf_dma; /* buffer address */
unsigned short usecs; /* start bandwidth */
unsigned short c_usecs; /* completion bandwidth */
} __attribute__ ((aligned (32))); } __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
This diff is collapsed.
/*-------------------------------------------------------------------------*/
/* list of all controllers using this driver
* */
static LIST_HEAD (hci_hcd_list);
/* URB states (urb_state) */
/* isoc, interrupt single state */
/* bulk transfer main state and 0-length packet */
#define US_BULK 0
#define US_BULK0 1
/* three setup states */
#define US_CTRL_SETUP 2
#define US_CTRL_DATA 1
#define US_CTRL_ACK 0
/*-------------------------------------------------------------------------*/
/* HC private part of a device descriptor
* */
#define NUM_EDS 32
typedef struct epd {
struct urb *pipe_head;
struct list_head urb_queue;
// int urb_state;
struct timer_list timeout;
int last_iso; /* timestamp of last queued ISOC transfer */
} epd_t;
struct hci_device {
epd_t ed[NUM_EDS];
};
/*-------------------------------------------------------------------------*/
/* Virtual Root HUB
*/
#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv)
struct virt_root_hub {
int devnum; /* Address of Root Hub endpoint */
void *urb; /* interrupt URB of root hub */
int send; /* active flag */
int interval; /* intervall of roothub interrupt transfers */
struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */
};
#if 1
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#endif
/*-------------------------------------------------------------------------*/
/* struct for each HC
*/
#define MAX_TRANS 32
typedef struct td {
struct urb *urb;
__u16 len;
__u16 iso_index;
} td_t;
typedef struct td_array {
int len;
td_t td[MAX_TRANS];
} td_array_t;
typedef struct hci {
struct virt_root_hub rh; /* roothub */
wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */
int active; /* HC is operating */
struct list_head ctrl_list; /* set of ctrl endpoints */
struct list_head bulk_list; /* set of bulk endpoints */
struct list_head iso_list; /* set of isoc endpoints */
struct list_head intr_list; /* ordered (tree) set of int endpoints */
struct list_head del_list; /* set of entpoints to be deleted */
td_array_t *td_array;
td_array_t a_td_array;
td_array_t i_td_array[2];
struct list_head hci_hcd_list; /* list of all hci_hcd */
struct usb_bus *bus; /* our bus */
// int trans; /* number of transactions pending */
int active_urbs;
int active_trans;
int frame_number; /* frame number */
hcipriv_t hp; /* individual part of hc type */
int nakCnt;
int last_packet_nak;
} hci_t;
/*-------------------------------------------------------------------------*/
/* condition (error) CC codes and mapping OHCI like
*/
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
#define TD_NOTACCESSED 0x0F
/* urb interface functions */
static int hci_get_current_frame_number (struct usb_device *usb_dev);
static int hci_unlink_urb (struct urb * urb);
static int qu_queue_urb (hci_t * hci, struct urb * urb);
/* root hub */
static int rh_init_int_timer (struct urb * urb);
static int rh_submit_urb (struct urb * urb);
static int rh_unlink_urb (struct urb * urb);
/* schedule functions */
static int sh_add_packet (hci_t * hci, struct urb * urb);
/* hc specific functions */
static inline void hc_flush_data_cache (hci_t * hci, void *data, int len);
static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data,
int *cc, int *toggle, int length, int pid,
int urb_state);
static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle,
int maxps, int slow, int endpoint, int address,
int pid, int format, int urb_state);
static void hc_start_int (hci_t * hci);
static void hc_stop_int (hci_t * hci);
static void SL811Write (hci_t * hci, char offset, char data);
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header */
static void urb_print (struct urb * urb, char *str, int small)
{
unsigned int pipe = urb->pipe;
int i, len;
if (!urb->dev || !urb->dev->bus) {
dbg ("%s URB: no dev", str);
return;
}
printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n",
str, hci_get_current_frame_number (urb->dev),
usb_pipedevice (pipe), usb_pipeendpoint (pipe),
usb_pipeout (pipe) ? 'O' : 'I',
usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC")
: (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags,
urb->actual_length, urb->transfer_buffer_length, urb->status,
urb->status);
if (!small) {
if (usb_pipecontrol (pipe)) {
printk (__FILE__ ": cmd(8):");
for (i = 0; i < 8; i++)
printk (" %02x", ((__u8 *) urb->setup_packet)[i]);
printk ("\n");
}
if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
printk (__FILE__ ": data(%d/%d):", urb->actual_length,
urb->transfer_buffer_length);
len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length;
for (i = 0; i < 2096 && i < len; i++)
printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]);
printk ("%s stat:%d\n", i < len ? "..." : "",
urb->status);
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
Georg Acher + Deti Fliegl + Thomas Sailer Georg Acher + Deti Fliegl + Thomas Sailer
georg@acher.org deti@fliegl.de sailer@ife.ee.ethz.ch georg@acher.org deti@fliegl.de sailer@ife.ee.ethz.ch
$Id: usb-uhci-dbg.c,v 1.1 2002/05/14 20:36:57 acher Exp $ $Id: usb-uhci-dbg.c,v 1.2 2002/05/21 21:40:16 acher Exp $
*/ */
#ifdef DEBUG #ifdef DEBUG
...@@ -105,7 +105,7 @@ static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short s ...@@ -105,7 +105,7 @@ static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short s
void uhci_show_status (struct uhci_hcd *uhci) void uhci_show_status (struct uhci_hcd *uhci)
{ {
unsigned int io_addr = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
unsigned short usbcmd, usbstat, usbint, usbfrnum; unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd; unsigned int flbaseadd;
unsigned char sof; unsigned char sof;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
HW-initalization based on material of HW-initalization based on material of
Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds
$Id: usb-uhci-hcd.c,v 1.1 2002/05/14 20:36:57 acher Exp $ $Id: usb-uhci-hcd.c,v 1.3 2002/05/25 16:42:41 acher Exp $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/usb.h> #include <linux/usb.h>
#define CONFIG_USB_DEBUG
#ifdef CONFIG_USB_DEBUG #ifdef CONFIG_USB_DEBUG
#define DEBUG #define DEBUG
#else #else
...@@ -47,34 +46,34 @@ ...@@ -47,34 +46,34 @@
#include "../core/hcd.h" #include "../core/hcd.h"
#include "usb-uhci-hcd.h" #include "usb-uhci-hcd.h"
#define DRIVER_VERSION "$Revision: 1.1 $" #define DRIVER_VERSION "$Revision: 1.3 $"
#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer" #define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer"
#define DRIVER_DESC "USB 1.1 Universal Host Controller Interface driver (HCD)" #define DRIVER_DESC "USB 1.1 Universal Host Controller Interface driver (HCD)"
/*--------------------------------------------------------------------------*/
// Values you may tweak
/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth /*--------------------------------------------------------------------------*/
* Reclamation: feature that puts loop on descriptor loop when /* Values you may tweak with module parameters
*
* high_bw: 1=on (default), 0=off
* Turns on Full Speed Bandwidth Reclamation:
* Feature that puts a loop on the descriptor chain when
* there's some transfer going on. With FSBR, USB performance * there's some transfer going on. With FSBR, USB performance
* is optimal, but PCI can be slowed down up-to 5 times, slowing down * is optimal, but PCI can be slowed down up-to 5 times, slowing down
* system performance (eg. framebuffer devices). * system performance (eg. framebuffer devices).
*/ *
#define CONFIG_USB_UHCI_HIGH_BANDWIDTH * bulk_depth/ctrl_depth: 0=off (default), 1:on
* Puts descriptors for bulk/control transfers in depth-first mode.
/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has * This has somehow similar effect to FSBR (higher speed), but does not
* somehow similar effect to FSBR (higher speed), but does not
* slow PCI down. OTOH USB performace is slightly slower than * slow PCI down. OTOH USB performace is slightly slower than
* in FSBR case and single device could hog whole USB, starving * in FSBR case and single device could hog whole USB, starving
* other devices. * other devices. Some devices (e.g. STV680-based cameras) NEED this depth
*/ * first search to work properly.
#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first *
#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first * Turning off both high_bw and bulk_depth/ctrl_depth
/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
* will lead to <64KB/sec performance over USB for bulk transfers targeting * will lead to <64KB/sec performance over USB for bulk transfers targeting
* one device's endpoint. You probably do not want to do that. * one device's endpoint. You probably do not want to do that.
*/ */
// Other constants, there's usually no need to change them.
// stop bandwidth reclamation after (roughly) 50ms // stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT (HZ/20) #define IDLE_TIMEOUT (HZ/20)
...@@ -100,6 +99,14 @@ ...@@ -100,6 +99,14 @@
// NO serviceable parts below! // NO serviceable parts below!
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/* Can be set by module parameters */
static int high_bw = 1;
static int ctrl_depth = 0; /* 0: Breadth first, 1: Depth first */
static int bulk_depth = 0; /* 0: Breadth first, 1: Depth first */
// How much URBs with ->next are walked
#define MAX_NEXT_COUNT 2048
static struct uhci *devs = NULL; static struct uhci *devs = NULL;
/* used by userspace UHCI data structure dumper */ /* used by userspace UHCI data structure dumper */
...@@ -155,7 +162,6 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags ...@@ -155,7 +162,6 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags
spin_lock_irqsave (&uhci->urb_list_lock, flags); spin_lock_irqsave (&uhci->urb_list_lock, flags);
queued_urb = search_dev_ep (uhci, urb); // returns already queued urb for that pipe queued_urb = search_dev_ep (uhci, urb); // returns already queued urb for that pipe
if (queued_urb) { if (queued_urb) {
...@@ -165,7 +171,7 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags ...@@ -165,7 +171,7 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags
((type == PIPE_BULK) && ((type == PIPE_BULK) &&
(!(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 (&uhci->urb_list_lock, flags); spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
err("ENXIO (%s) %08x, flags %x, urb %p, burb %p, propably device driver bug...", err("ENXIO (%s) %08x, flags %x, urb %p, burb %p, probably device driver bug...",
PIPESTRING(type), PIPESTRING(type),
urb->pipe,urb->transfer_flags,urb,queued_urb); urb->pipe,urb->transfer_flags,urb,queued_urb);
return -ENXIO; // urb already queued return -ENXIO; // urb already queued
...@@ -196,7 +202,7 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags ...@@ -196,7 +202,7 @@ static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags
PCI_DMA_TODEVICE); PCI_DMA_TODEVICE);
// for bulk queuing it is essential that interrupts are disabled until submission // for bulk queuing it is essential that interrupts are disabled until submission
// all other type enable interrupts again // all other types enable interrupts again
switch (type) { switch (type) {
case PIPE_BULK: case PIPE_BULK:
if (queued_urb) { if (queued_urb) {
...@@ -293,7 +299,7 @@ static int uhci_get_frame (struct usb_hcd *hcd) ...@@ -293,7 +299,7 @@ static int uhci_get_frame (struct usb_hcd *hcd)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int hc_reset (struct uhci_hcd *uhci) static int hc_reset (struct uhci_hcd *uhci)
{ {
unsigned int io_addr = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
uhci->apm_state = 0; uhci->apm_state = 0;
uhci->running = 0; uhci->running = 0;
...@@ -306,7 +312,7 @@ static int hc_reset (struct uhci_hcd *uhci) ...@@ -306,7 +312,7 @@ static int hc_reset (struct uhci_hcd *uhci)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int hc_irq_run(struct uhci_hcd *uhci) static int hc_irq_run(struct uhci_hcd *uhci)
{ {
unsigned int io_addr = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
/* Turn on all interrupts */ /* Turn on all interrupts */
outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
...@@ -324,7 +330,7 @@ static int hc_irq_run(struct uhci_hcd *uhci) ...@@ -324,7 +330,7 @@ static int hc_irq_run(struct uhci_hcd *uhci)
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int hc_start (struct uhci_hcd *uhci) static int hc_start (struct uhci_hcd *uhci)
{ {
unsigned int io_addr = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
int timeout = 10; int timeout = 10;
struct usb_device *udev; struct usb_device *udev;
init_dbg("hc_start uhci %p",uhci); init_dbg("hc_start uhci %p",uhci);
...@@ -351,7 +357,6 @@ static int hc_start (struct uhci_hcd *uhci) ...@@ -351,7 +357,6 @@ static int hc_start (struct uhci_hcd *uhci)
uhci->hcd.state = USB_STATE_READY; uhci->hcd.state = USB_STATE_READY;
if (!udev) { if (!udev) {
uhci->running = 0; uhci->running = 0;
// FIXME cleanup
return -ENOMEM; return -ENOMEM;
} }
...@@ -360,7 +365,6 @@ static int hc_start (struct uhci_hcd *uhci) ...@@ -360,7 +365,6 @@ static int hc_start (struct uhci_hcd *uhci)
if (usb_register_root_hub (udev, &uhci->hcd.pdev->dev) != 0) { if (usb_register_root_hub (udev, &uhci->hcd.pdev->dev) != 0) {
usb_free_dev (udev); usb_free_dev (udev);
uhci->running = 0; uhci->running = 0;
// FIXME cleanup
return -ENODEV; return -ENODEV;
} }
...@@ -374,7 +378,7 @@ static int __devinit uhci_start (struct usb_hcd *hcd) ...@@ -374,7 +378,7 @@ static int __devinit uhci_start (struct usb_hcd *hcd)
{ {
struct uhci_hcd *uhci = hcd_to_uhci (hcd); struct uhci_hcd *uhci = hcd_to_uhci (hcd);
int ret; int ret;
int io_addr=(int)hcd->regs, io_size=0x20; // FIXME unsigned long io_addr=(unsigned long)hcd->regs, io_size=0x20;
init_dbg("uhci_start hcd %p uhci %p, pdev %p",hcd,uhci,hcd->pdev); init_dbg("uhci_start hcd %p uhci %p, pdev %p",hcd,uhci,hcd->pdev);
/* disable legacy emulation, Linux takes over... */ /* disable legacy emulation, Linux takes over... */
...@@ -448,7 +452,7 @@ static void uhci_stop (struct usb_hcd *hcd) ...@@ -448,7 +452,7 @@ static void uhci_stop (struct usb_hcd *hcd)
static void uhci_irq (struct usb_hcd *hcd) static void uhci_irq (struct usb_hcd *hcd)
{ {
struct uhci_hcd *uhci = hcd_to_uhci (hcd); struct uhci_hcd *uhci = hcd_to_uhci (hcd);
unsigned int io_addr = (int)hcd->regs; unsigned long io_addr = (unsigned long)hcd->regs;
unsigned short status; unsigned short status;
struct list_head *p, *p2; struct list_head *p, *p2;
int restarts, work_done; int restarts, work_done;
...@@ -589,6 +593,12 @@ static const struct hc_driver uhci_driver = { ...@@ -589,6 +593,12 @@ static const struct hc_driver uhci_driver = {
MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_AUTHOR (DRIVER_AUTHOR);
MODULE_DESCRIPTION (DRIVER_INFO); MODULE_DESCRIPTION (DRIVER_INFO);
MODULE_LICENSE ("GPL"); MODULE_LICENSE ("GPL");
MODULE_PARM (high_bw, "i");
MODULE_PARM_DESC (high_bw, "high_hw: Enable high bandwidth mode, 1=on (default), 0=off");
MODULE_PARM (bulk_depth, "i");
MODULE_PARM_DESC (bulk_depth, "bulk_depth: Depth first processing for bulk transfers, 0=off (default), 1=on");
MODULE_PARM (ctrl_depth, "i");
MODULE_PARM_DESC (ctrl_depth, "ctrl_depth: Depth first processing for control transfers, 0=off (default), 1=on");
static const struct pci_device_id __devinitdata pci_ids [] = { { static const struct pci_device_id __devinitdata pci_ids [] = { {
...@@ -627,6 +637,10 @@ static int __init uhci_hcd_init (void) ...@@ -627,6 +637,10 @@ static int __init uhci_hcd_init (void)
init_dbg (DRIVER_INFO); init_dbg (DRIVER_INFO);
init_dbg ("block sizes: hq %d td %d", init_dbg ("block sizes: hq %d td %d",
sizeof (struct qh), sizeof (struct td)); sizeof (struct qh), sizeof (struct td));
info("High bandwidth mode %s.%s%s",
high_bw?"enabled":"disabled",
ctrl_depth?"CTRL depth first enabled":"",
bulk_depth?"BULK depth first enabled":"");
return pci_module_init (&uhci_pci_driver); return pci_module_init (&uhci_pci_driver);
} }
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
HW-initalization based on material of HW-initalization based on material of
Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds
$Id: usb-uhci-hub.c,v 1.1 2002/05/14 20:36:57 acher Exp $ $Id: usb-uhci-hub.c,v 1.2 2002/05/21 21:40:16 acher Exp $
*/ */
#define CLR_RH_PORTSTAT(x) \ #define CLR_RH_PORTSTAT(x) \
...@@ -34,7 +34,7 @@ static int oldval=-1; ...@@ -34,7 +34,7 @@ static int oldval=-1;
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 = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
int i, len=0, data = 0, portstate; int i, len=0, data = 0, portstate;
int changed=0; int changed=0;
...@@ -99,7 +99,7 @@ static int uhci_hub_control ( ...@@ -99,7 +99,7 @@ static int uhci_hub_control (
int status = 0; int status = 0;
int stat = 0; int stat = 0;
int cstatus; int cstatus;
unsigned int io_addr = (int)uhci->hcd.regs; unsigned long io_addr = (unsigned long)uhci->hcd.regs;
int ports = uhci->maxports; int ports = uhci->maxports;
switch (typeReq) { switch (typeReq) {
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
HW-initalization based on material of HW-initalization based on material of
Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds
$Id: usb-uhci-mem.c,v 1.1 2002/05/14 20:36:57 acher Exp $ $Id: usb-uhci-mem.c,v 1.3 2002/05/25 16:42:41 acher Exp $
*/ */
/*###########################################################################*/ /*###########################################################################*/
...@@ -387,10 +387,9 @@ static void uhci_switch_timer_int(struct uhci_hcd *uhci) ...@@ -387,10 +387,9 @@ static void uhci_switch_timer_int(struct uhci_hcd *uhci)
wmb(); wmb();
} }
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
static void enable_desc_loop(struct uhci_hcd *uhci, struct urb *urb) static void enable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
{ {
int flags; unsigned long flags;
if (urb->transfer_flags & USB_NO_FSBR) if (urb->transfer_flags & USB_NO_FSBR)
return; return;
...@@ -405,7 +404,7 @@ static void enable_desc_loop(struct uhci_hcd *uhci, struct urb *urb) ...@@ -405,7 +404,7 @@ static void enable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
static void disable_desc_loop(struct uhci_hcd *uhci, struct urb *urb) static void disable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
{ {
int flags; unsigned long flags;
if (urb->transfer_flags & USB_NO_FSBR) if (urb->transfer_flags & USB_NO_FSBR)
return; return;
...@@ -422,18 +421,16 @@ static void disable_desc_loop(struct uhci_hcd *uhci, struct urb *urb) ...@@ -422,18 +421,16 @@ static void disable_desc_loop(struct uhci_hcd *uhci, struct urb *urb)
} }
spin_unlock_irqrestore (&uhci->qh_lock, flags); spin_unlock_irqrestore (&uhci->qh_lock, flags);
} }
#endif
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
static void queue_urb_unlocked (struct uhci_hcd *uhci, struct urb *urb) static void queue_urb_unlocked (struct uhci_hcd *uhci, struct urb *urb)
{ {
urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type; int type;
type=usb_pipetype (urb->pipe); type=usb_pipetype (urb->pipe);
if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) if (high_bw && ((type == PIPE_BULK) || (type == PIPE_CONTROL)))
enable_desc_loop(uhci, urb); enable_desc_loop(uhci, urb);
#endif
urb->status = -EINPROGRESS; urb->status = -EINPROGRESS;
priv->started=jiffies; priv->started=jiffies;
...@@ -455,14 +452,12 @@ static void queue_urb (struct uhci_hcd *uhci, struct urb *urb) ...@@ -455,14 +452,12 @@ static void queue_urb (struct uhci_hcd *uhci, struct urb *urb)
static void dequeue_urb (struct uhci_hcd *uhci, struct urb *urb) static void dequeue_urb (struct uhci_hcd *uhci, struct urb *urb)
{ {
urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
int type; int type;
dbg("dequeue URB %p",urb); dbg("dequeue URB %p",urb);
type=usb_pipetype (urb->pipe); type=usb_pipetype (urb->pipe);
if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) if (high_bw && ((type == PIPE_BULK) || (type == PIPE_CONTROL)))
disable_desc_loop(uhci, urb); disable_desc_loop(uhci, urb);
#endif
list_del (&priv->urb_list); list_del (&priv->urb_list);
if (urb->timeout && uhci->timeout_urbs) if (urb->timeout && uhci->timeout_urbs)
...@@ -624,10 +619,10 @@ static int init_skel (struct uhci_hcd *uhci) ...@@ -624,10 +619,10 @@ static int init_skel (struct uhci_hcd *uhci)
insert_qh (uhci, uhci->bulk_chain, qh, 0); insert_qh (uhci, uhci->bulk_chain, qh, 0);
uhci->control_chain = qh; uhci->control_chain = qh;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
// disabled reclamation loop // disabled reclamation loop
set_qh_head(uhci->chain_end, uhci->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM); if (high_bw)
#endif set_qh_head(uhci->chain_end, uhci->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
init_dbg("allocating qh: ls_control_chain"); init_dbg("allocating qh: ls_control_chain");
if (alloc_qh (uhci, &qh)) if (alloc_qh (uhci, &qh))
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
HW-initalization based on material of HW-initalization based on material of
Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds Randy Dunlap + Johannes Erdfelt + Gregory P. Smith + Linus Torvalds
$Id: usb-uhci-q.c,v 1.1 2002/05/14 20:36:57 acher Exp $ $Id: usb-uhci-q.c,v 1.3 2002/05/25 16:42:41 acher Exp $
*/ */
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
...@@ -59,7 +59,7 @@ static int uhci_submit_control_urb (struct uhci_hcd *uhci, struct urb *urb) ...@@ -59,7 +59,7 @@ static int uhci_submit_control_urb (struct uhci_hcd *uhci, struct urb *urb)
urb_priv_t *urb_priv = urb->hcpriv; urb_priv_t *urb_priv = urb->hcpriv;
unsigned long destination, status; unsigned long destination, status;
int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method int depth_first = ctrl_depth; // UHCI descriptor chasing method
unsigned long len; unsigned long len;
char *data; char *data;
...@@ -175,7 +175,7 @@ static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct ...@@ -175,7 +175,7 @@ static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct
unsigned int pipe = urb->pipe; unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len, last; int info, len, last;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method int depth_first = bulk_depth; // UHCI descriptor chasing method
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;
...@@ -305,7 +305,7 @@ static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct ...@@ -305,7 +305,7 @@ static int uhci_submit_bulk_urb (struct uhci_hcd *uhci, struct urb *urb, struct
static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb) static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb)
{ {
urb_priv_t *urb_priv = urb->hcpriv; urb_priv_t *urb_priv = urb->hcpriv;
int nint, n; int nint;
uhci_desc_t *td; uhci_desc_t *td;
int status, destination; int status, destination;
int info; int info;
...@@ -313,16 +313,14 @@ static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb) ...@@ -313,16 +313,14 @@ static int uhci_submit_int_urb (struct uhci_hcd *uhci, struct urb *urb)
if (urb->interval == 0) if (urb->interval == 0)
nint = 0; nint = 0;
else { // round interval down to 2^n else {
for (nint = 0, n = 1; nint <= 8; nint++, n += n) // log2-function (urb->interval already 2^n)
if (urb->interval < n) { nint = ffs(urb->interval);
urb->interval = n / 2; if (nint>7)
break; nint=7;
}
nint--;
} }
dbg("Rounded interval to %i, chain %i", urb->interval, nint); dbg("INT-interval %i, chain %i", urb->interval, nint);
// remember start frame, just in case... // remember start frame, just in case...
urb->start_frame = UHCI_GET_CURRENT_FRAME (uhci) & 1023; urb->start_frame = UHCI_GET_CURRENT_FRAME (uhci) & 1023;
...@@ -381,7 +379,7 @@ static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int ...@@ -381,7 +379,7 @@ static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int
} }
if (last_urb) { if (last_urb) {
*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; *end = (last_urb->start_frame + last_urb->number_of_packets*last_urb->interval) & 1023;
ret=0; ret=0;
} }
...@@ -395,12 +393,14 @@ static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int ...@@ -395,12 +393,14 @@ static int find_iso_limits (struct uhci_hcd *uhci, struct urb *urb, unsigned int
static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb) static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb)
{ {
unsigned int now; unsigned int now;
unsigned int start_limit = 0, stop_limit = 0, queued_size; unsigned int start_limit = 0, stop_limit = 0, queued_size, number_of_frames;
int limits; int limits;
now = UHCI_GET_CURRENT_FRAME (uhci) & 1023; now = UHCI_GET_CURRENT_FRAME (uhci) & 1023;
if ((unsigned) urb->number_of_packets > 900) number_of_frames = (unsigned) (urb->number_of_packets*urb->interval);
if ( number_of_frames > 900)
return -EFBIG; return -EFBIG;
limits = find_iso_limits (uhci, urb, &start_limit, &stop_limit); limits = find_iso_limits (uhci, urb, &start_limit, &stop_limit);
...@@ -415,17 +415,17 @@ static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb) ...@@ -415,17 +415,17 @@ static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb)
else { else {
urb->start_frame = stop_limit; // seamless linkage urb->start_frame = stop_limit; // seamless linkage
if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) { if (((now - urb->start_frame) & 1023) <= (unsigned) number_of_frames) {
info("iso_find_start: gap in seamless isochronous scheduling"); info("iso_find_start: gap in seamless isochronous scheduling");
dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", dbg("iso_find_start: now %u start_frame %u number_of_packets %u interval %u pipe 0x%08x",
now, urb->start_frame, urb->number_of_packets, urb->pipe); now, urb->start_frame, urb->number_of_packets, urb->interval, urb->pipe);
urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough
} }
} }
} }
else { else {
urb->start_frame &= 1023; urb->start_frame &= 1023;
if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) { if (((now - urb->start_frame) & 1023) < number_of_frames) {
dbg("iso_find_start: now between start_frame and end"); dbg("iso_find_start: now between start_frame and end");
return -EAGAIN; return -EAGAIN;
} }
...@@ -436,7 +436,7 @@ static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb) ...@@ -436,7 +436,7 @@ static int iso_find_start (struct uhci_hcd *uhci, struct urb *urb)
return 0; return 0;
if (((urb->start_frame - start_limit) & 1023) < queued_size || if (((urb->start_frame - start_limit) & 1023) < queued_size ||
((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { ((urb->start_frame + number_of_frames - 1 - start_limit) & 1023) < queued_size) {
dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u", dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
urb->start_frame, urb->number_of_packets, start_limit, stop_limit); urb->start_frame, urb->number_of_packets, start_limit, stop_limit);
return -EAGAIN; return -EAGAIN;
...@@ -507,7 +507,7 @@ static int uhci_submit_iso_urb (struct uhci_hcd *uhci, struct urb *urb, int mem_ ...@@ -507,7 +507,7 @@ static int uhci_submit_iso_urb (struct uhci_hcd *uhci, struct urb *urb, int mem_
urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset); urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
list_add_tail (&td->desc_list, &urb_priv->desc_list); list_add_tail (&td->desc_list, &urb_priv->desc_list);
insert_td_horizontal (uhci, uhci->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds insert_td_horizontal (uhci, uhci->iso_td[(urb->start_frame + n*urb->interval) & 1023], td); // store in iso-tds
} }
kfree (tdm); kfree (tdm);
...@@ -742,6 +742,7 @@ static int uhci_unlink_urb_async (struct uhci_hcd *uhci, struct urb *urb, int mo ...@@ -742,6 +742,7 @@ static int uhci_unlink_urb_async (struct uhci_hcd *uhci, struct urb *urb, int mo
switch (usb_pipetype (urb->pipe)) { switch (usb_pipetype (urb->pipe)) {
case PIPE_INTERRUPT: case PIPE_INTERRUPT:
urb_priv->flags = 0; // mark as deleted (if called from completion)
uhci_do_toggle (urb); uhci_do_toggle (urb);
case PIPE_ISOCHRONOUS: case PIPE_ISOCHRONOUS:
...@@ -853,12 +854,9 @@ static void uhci_check_timeouts(struct uhci_hcd *uhci) ...@@ -853,12 +854,9 @@ static void uhci_check_timeouts(struct uhci_hcd *uhci)
async_dbg("uhci_check_timeout: timeout for %p",urb); async_dbg("uhci_check_timeout: timeout for %p",urb);
uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB); uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);
} }
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH else if (high_bw && ((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
(hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT)) (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
disable_desc_loop(uhci, urb); disable_desc_loop(uhci, urb);
#endif
} }
uhci->timeout_check=jiffies; uhci->timeout_check=jiffies;
} }
...@@ -1040,9 +1038,8 @@ static int process_transfer (struct uhci_hcd *uhci, struct urb *urb, int mode) ...@@ -1040,9 +1038,8 @@ static int process_transfer (struct uhci_hcd *uhci, struct urb *urb, int mode)
uhci_clean_transfer(uhci, urb, qh, mode); uhci_clean_transfer(uhci, urb, qh, mode);
urb->status = status; urb->status = status;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH if (high_bw)
disable_desc_loop(uhci,urb); disable_desc_loop(uhci,urb);
#endif
dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
...@@ -1088,21 +1085,19 @@ static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode) ...@@ -1088,21 +1085,19 @@ static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode)
urb->actual_length = actual_length; urb->actual_length = actual_length;
recycle: recycle:
((urb_priv_t*)urb->hcpriv)->flags=1; // set to detect unlink during completion
uhci_urb_dma_sync(uhci, urb, urb->hcpriv); uhci_urb_dma_sync(uhci, urb, urb->hcpriv);
if (urb->complete) { if (urb->complete) {
//dbg("process_interrupt: calling completion, status %i",status); //dbg("process_interrupt: calling completion, status %i",status);
urb->status = status; urb->status = status;
((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion
spin_unlock(&uhci->urb_list_lock); spin_unlock(&uhci->urb_list_lock);
urb->complete ((struct urb *) urb); urb->complete ((struct urb *) urb);
spin_lock(&uhci->urb_list_lock); spin_lock(&uhci->urb_list_lock);
((urb_priv_t*)urb->hcpriv)->flags=0; // FIXME: unlink in completion not handled...
} }
if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
(urb->status != -ENOENT)) { (urb->status != -ENOENT) && ((urb_priv_t*)urb->hcpriv)->flags) {
urb->status = -EINPROGRESS; urb->status = -EINPROGRESS;
...@@ -1125,7 +1120,7 @@ static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode) ...@@ -1125,7 +1120,7 @@ static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode)
mb(); mb();
} }
else { else {
uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB); uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);
uhci_do_toggle (urb); // correct toggle after unlink uhci_do_toggle (urb); // correct toggle after unlink
clr_td_ioc(desc); // inactivate TD clr_td_ioc(desc); // inactivate TD
} }
...@@ -1199,7 +1194,6 @@ static int process_iso (struct uhci_hcd *uhci, struct urb *urb, int mode) ...@@ -1199,7 +1194,6 @@ static int process_iso (struct uhci_hcd *uhci, struct urb *urb, int mode)
p_tmp = p; p_tmp = p;
p = p->next; p = p->next;
list_del (p_tmp); list_del (p_tmp);
// delete_desc (uhci, desc);
// add to cool down pool // add to cool down pool
INIT_LIST_HEAD(&desc->horizontal); INIT_LIST_HEAD(&desc->horizontal);
...@@ -1243,19 +1237,18 @@ static int process_urb (struct uhci_hcd *uhci, struct list_head *p) ...@@ -1243,19 +1237,18 @@ static int process_urb (struct uhci_hcd *uhci, struct list_head *p)
break; break;
} }
if (urb->status != -EINPROGRESS) { if (urb->status != -EINPROGRESS && type != PIPE_INTERRUPT) {
dequeue_urb (uhci, urb); dequeue_urb (uhci, urb);
uhci_free_priv(uhci, urb, urb->hcpriv);
uhci_free_priv(uhci, urb, urb->hcpriv); spin_unlock(&uhci->urb_list_lock);
dbg("giveback urb %p, status %i, length %i\n",
urb, urb->status, urb->transfer_buffer_length);
usb_hcd_giveback_urb(&uhci->hcd, urb);
spin_lock(&uhci->urb_list_lock);
if (type != PIPE_INTERRUPT) { // process_interrupt does completion on its own
spin_unlock(&uhci->urb_list_lock);
dbg("giveback urb %p, status %i, length %i\n",
urb, urb->status, urb->transfer_buffer_length);
usb_hcd_giveback_urb(&uhci->hcd, urb);
spin_lock(&uhci->urb_list_lock);
}
} }
return ret; return ret;
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $ * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $
* *
* Current development and maintenance by: * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
* *
* Initial work by: * Initial work by:
* (c) 1999 Michael Gee (michael@linuxspecific.com) * (c) 1999 Michael Gee (michael@linuxspecific.com)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $ * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $
* *
* Current development and maintenance by: * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
* *
* Developed with the assistance of: * Developed with the assistance of:
* (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
...@@ -56,9 +56,6 @@ ...@@ -56,9 +56,6 @@
*/ */
#define US_ACT_COMMAND 1 #define US_ACT_COMMAND 1
#define US_ACT_DEVICE_RESET 2
#define US_ACT_BUS_RESET 3
#define US_ACT_HOST_RESET 4
#define US_ACT_EXIT 5 #define US_ACT_EXIT 5
/*********************************************************************** /***********************************************************************
......
...@@ -351,6 +351,29 @@ unsigned int usb_stor_transfer_length(Scsi_Cmnd *srb) ...@@ -351,6 +351,29 @@ unsigned int usb_stor_transfer_length(Scsi_Cmnd *srb)
* Data transfer routines * Data transfer routines
***********************************************************************/ ***********************************************************************/
/*
* This is subtle, so pay attention:
* ---------------------------------
* We're very concered about races with a command abort. Hanging this code is
* a sure fire way to hang the kernel.
*
* The abort function first sets the machine state, then tries to acquire the
* lock on the current_urb to abort it.
*
* Once a function grabs the current_urb_sem, then it -MUST- test the state to
* see if we just got aborted before the lock was grabbed. If so, it's
* essential to release the lock and return.
*
* The idea is that, once the current_urb_sem is held, the state can't really
* change anymore without also engaging the usb_unlink_urb() call _after_ the
* URB is actually submitted.
*
* So, we've guaranteed that (after the sm_state test), if we do submit the
* URB it will get aborted when we release the current_urb_sem. And we've
* also guaranteed that if the abort code was called before we held the
* current_urb_sem, we can safely get out before the URB is submitted.
*/
/* This is the completion handler which will wake us up when an URB /* This is the completion handler which will wake us up when an URB
* completes. * completes.
*/ */
...@@ -363,6 +386,9 @@ static void usb_stor_blocking_completion(struct urb *urb) ...@@ -363,6 +386,9 @@ static void usb_stor_blocking_completion(struct urb *urb)
/* This is the common part of the URB message submission code /* This is the common part of the URB message submission code
* This function expects the current_urb_sem to be held upon entry. * This function expects the current_urb_sem to be held upon entry.
*
* All URBs from the usb-storage driver _must_ pass through this function
* (or something like it) for the abort mechanisms to work properly.
*/ */
static int usb_stor_msg_common(struct us_data *us) static int usb_stor_msg_common(struct us_data *us)
{ {
...@@ -385,16 +411,6 @@ static int usb_stor_msg_common(struct us_data *us) ...@@ -385,16 +411,6 @@ static int usb_stor_msg_common(struct us_data *us)
return status; return status;
} }
/* has the current command been aborted? */
if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
/* avoid a race with usb_stor_abort_transport():
* if the abort took place before we submitted
* the URB, we must cancel it ourselves */
if (us->current_urb->status == -EINPROGRESS)
usb_unlink_urb(us->current_urb);
}
/* wait for the completion of the URB */ /* wait for the completion of the URB */
up(&(us->current_urb_sem)); up(&(us->current_urb_sem));
wait_for_completion(&urb_done); wait_for_completion(&urb_done);
...@@ -428,9 +444,15 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, ...@@ -428,9 +444,15 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
/* lock the URB */ /* lock the URB */
down(&(us->current_urb_sem)); down(&(us->current_urb_sem));
/* has the current command been aborted? */
if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
up(&(us->current_urb_sem));
return 0;
}
/* fill the URB */ /* fill the URB */
FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) &dr, data, size, (unsigned char*) dr, data, size,
usb_stor_blocking_completion, NULL); usb_stor_blocking_completion, NULL);
/* submit the URB */ /* submit the URB */
...@@ -456,6 +478,12 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, ...@@ -456,6 +478,12 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
/* lock the URB */ /* lock the URB */
down(&(us->current_urb_sem)); down(&(us->current_urb_sem));
/* has the current command been aborted? */
if (atomic_read(&us->sm_state) == US_STATE_ABORTING) {
up(&(us->current_urb_sem));
return 0;
}
/* fill the URB */ /* fill the URB */
FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len, FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
usb_stor_blocking_completion, NULL); usb_stor_blocking_completion, NULL);
...@@ -473,7 +501,15 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, ...@@ -473,7 +501,15 @@ int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
/* This is a version of usb_clear_halt() that doesn't read the status from /* This is a version of usb_clear_halt() that doesn't read the status from
* the device -- this is because some devices crash their internal firmware * the device -- this is because some devices crash their internal firmware
* when the status is requested after a halt * when the status is requested after a halt.
*
* A definitive list of these 'bad' devices is too difficult to maintain or
* make complete enough to be useful. This problem was first observed on the
* Hagiwara FlashGate DUAL unit. However, bus traces reveal that neither
* MacOS nor Windows checks the status after clearing a halt.
*
* Since many vendors in this space limit their testing to interoperability
* with these two OSes, specification violations like this one are common.
*/ */
int usb_stor_clear_halt(struct us_data *us, int pipe) int usb_stor_clear_halt(struct us_data *us, int pipe)
{ {
...@@ -823,24 +859,31 @@ void usb_stor_abort_transport(struct us_data *us) ...@@ -823,24 +859,31 @@ void usb_stor_abort_transport(struct us_data *us)
/* If the current state is wrong or if there's /* If the current state is wrong or if there's
* no srb, then there's nothing to do */ * no srb, then there's nothing to do */
if ( !(state == US_STATE_RUNNING || state == US_STATE_RESETTING) BUG_ON((state != US_STATE_RUNNING && state != US_STATE_RESETTING));
|| !us->srb) { BUG_ON(!(us->srb));
US_DEBUGP("-- invalid current state\n");
return; /* set state to abort */
}
atomic_set(&us->sm_state, US_STATE_ABORTING); atomic_set(&us->sm_state, US_STATE_ABORTING);
/* If the state machine is blocked waiting for an URB or an IRQ, /* If the state machine is blocked waiting for an URB or an IRQ,
* let's wake it up */ * let's wake it up */
/* if we have an URB pending, cancel it */ /* If we have an URB pending, cancel it. Note that we guarantee with
* the current_urb_sem that either (a) an URB has just been submitted,
* or (b) the URB will never get submitted. But, because of the use
* of us->sm_state and current_urb_sem, we can't get an URB sumbitted
* _after_ we set the state to US_STATE_ABORTING and this section of
* code runs. Thus we avoid deadlocks.
*/
down(&(us->current_urb_sem));
if (us->current_urb->status == -EINPROGRESS) { if (us->current_urb->status == -EINPROGRESS) {
US_DEBUGP("-- cancelling URB\n"); US_DEBUGP("-- cancelling URB\n");
usb_unlink_urb(us->current_urb); usb_unlink_urb(us->current_urb);
} }
up(&(us->current_urb_sem));
/* if we are waiting for an IRQ, simulate it */ /* If we are waiting for an IRQ, simulate it */
else if (test_bit(IP_WANTED, &us->bitflags)) { if (test_bit(IP_WANTED, &us->bitflags)) {
US_DEBUGP("-- simulating missing IRQ\n"); US_DEBUGP("-- simulating missing IRQ\n");
usb_stor_CBI_irq(us->irq_urb); usb_stor_CBI_irq(us->irq_urb);
} }
......
This diff is collapsed.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $ * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $
* *
* Current development and maintenance by: * Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
* *
* Initial work by: * Initial work by:
* (c) 1999 Michael Gee (michael@linuxspecific.com) * (c) 1999 Michael Gee (michael@linuxspecific.com)
...@@ -103,11 +103,15 @@ struct us_unusual_dev { ...@@ -103,11 +103,15 @@ struct us_unusual_dev {
#define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */ #define US_FL_SCM_MULT_TARG 0x00000020 /* supports multiple targets */
#define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs fixing */ #define US_FL_FIX_INQUIRY 0x00000040 /* INQUIRY response needs fixing */
#define US_STATE_DETACHED 1 /* State machine states */ /* device attached/detached states */
#define US_STATE_IDLE 2 #define US_STATE_DETACHED 1
#define US_STATE_RUNNING 3 #define US_STATE_ATTACHED 2
#define US_STATE_RESETTING 4
#define US_STATE_ABORTING 5 /* processing state machine states */
#define US_STATE_IDLE 1
#define US_STATE_RUNNING 2
#define US_STATE_RESETTING 3
#define US_STATE_ABORTING 4
#define USB_STOR_STRING_LEN 32 #define USB_STOR_STRING_LEN 32
...@@ -120,8 +124,13 @@ typedef void (*extra_data_destructor)(void *); /* extra data destructor */ ...@@ -120,8 +124,13 @@ typedef void (*extra_data_destructor)(void *); /* extra data destructor */
struct us_data { struct us_data {
struct us_data *next; /* next device */ struct us_data *next; /* next device */
/* the device we're working with */ /* The device we're working with
* It's important to note:
* (o) you must hold dev_semaphore to change pusb_dev
* (o) device_state should change whenever pusb_dev does
*/
struct semaphore dev_semaphore; /* protect pusb_dev */ struct semaphore dev_semaphore; /* protect pusb_dev */
atomic_t device_state; /* attached or detached */
struct usb_device *pusb_dev; /* this usb_device */ struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */ unsigned int flags; /* from filter initially */
......
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