Commit a96a4b53 authored by Georg Acher's avatar Georg Acher Committed by Greg Kroah-Hartman

[PATCH] USB-UHCI-HCD

After maturing for more than a month, I decided that it's enough...
So here's the patch for the HCD-version of usb-uhci, I called it
usb-uhci-hcd. For me, the usual devices work (TM).

Since the internals haven't changed much, I don't expect much difference to
usb-uhci in functionality and performance.

There is one major improvement: I've added a (preliminary) watchdog, that
regularly checks whether UHCI interrupts are coming through. If they are
"missing", ie. the HC halted without saying so, the HC is "re-animated" by a
real and clean restart, which is (internally) equivalent to a module-reload.
This is especially for VIA-chips which get comatose quite easily by babble
or other incorrect transfers.

The side effect is, of course, disconnects for all connected devices, but
they re-appear after a few seconds and were dead before anyway. So keyboard
and mouse work again, which is IMO a _very_ useful thing :-)

Another point: The watchdog (currently with moderate 8 interrupts/s) detects
IRQ-problems, so the misleading "device doesn't accept new device"-message
is replaced by an immediate error.

More comments and the separation into multiple files (I've taken ohci-hcd as
an example) should make the code more concise...

A few things are still on the todo-list and will come later:
- A few watchdog modifications (fewer interrupts, better diagnostics)
- Support for module "tune"-parameters for breadth/depth search, debug etc.
- interval support for ISO
- More SMP-tests
- maybe changes for reference counting when it is clear what survives :-)
parent 82b3d954
/*
UHCI HCD (Host Controller Driver) for USB, debugging calls
(c) 1999-2002
Georg Acher + Deti Fliegl + Thomas Sailer
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 $
*/
#ifdef DEBUG
static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh)
{
if (qh->type != QH_TYPE) {
dbg("qh has not QH_TYPE");
return;
}
dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr);
if (qh->hw.qh.head & UHCI_PTR_TERM)
dbg(" Head Terminate");
else
dbg(" Head: %s @ %08X",
(qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
qh->hw.qh.head & ~UHCI_PTR_BITS);
if (qh->hw.qh.element & UHCI_PTR_TERM)
dbg(" Element Terminate");
else
dbg(" Element: %s @ %08X",
(qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
qh->hw.qh.element & ~UHCI_PTR_BITS);
}
#endif
#if 0
static void uhci_show_td (puhci_desc_t td)
{
char *spid;
switch (td->hw.td.info & 0xff) {
case USB_PID_SETUP:
spid = "SETUP";
break;
case USB_PID_OUT:
spid = " OUT ";
break;
case USB_PID_IN:
spid = " IN ";
break;
default:
spid = " ? ";
break;
}
warn(" TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
td, td->dma_addr,
td->hw.td.info >> 21,
((td->hw.td.info >> 19) & 1),
(td->hw.td.info >> 15) & 15,
(td->hw.td.info >> 8) & 127,
spid,
td->hw.td.buffer);
warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
td->hw.td.status & 0x7ff,
((td->hw.td.status >> 27) & 3),
(td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
(td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
(td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
(td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
(td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
(td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
(td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
(td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
(td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
(td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
);
if (td->hw.td.link & UHCI_PTR_TERM)
warn(" TD Link Terminate");
else
warn(" Link points to %s @ %08x, %s",
(td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
td->hw.td.link & ~UHCI_PTR_BITS,
(td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
}
#endif
#ifdef DEBUG
static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status)
{
dbg(" stat%d = %04x %s%s%s%s%s%s%s%s",
port,
status,
(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
(status & USBPORTSC_PR) ? "PortReset " : "",
(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
(status & USBPORTSC_RD) ? "ResumeDetect " : "",
(status & USBPORTSC_PEC) ? "EnableChange " : "",
(status & USBPORTSC_PE) ? "PortEnabled " : "",
(status & USBPORTSC_CSC) ? "ConnectChange " : "",
(status & USBPORTSC_CCS) ? "PortConnected " : "");
}
void uhci_show_status (struct uhci_hcd *uhci)
{
unsigned int io_addr = (int)uhci->hcd.regs;
unsigned short usbcmd, usbstat, usbint, usbfrnum;
unsigned int flbaseadd;
unsigned char sof;
unsigned short portsc1, portsc2;
usbcmd = inw (io_addr + 0);
usbstat = inw (io_addr + 2);
usbint = inw (io_addr + 4);
usbfrnum = inw (io_addr + 6);
flbaseadd = inl (io_addr + 8);
sof = inb (io_addr + 12);
portsc1 = inw (io_addr + 16);
portsc2 = inw (io_addr + 18);
dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s",
usbcmd,
(usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
(usbcmd & USBCMD_CF) ? "CF " : "",
(usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
(usbcmd & USBCMD_FGR) ? "FGR " : "",
(usbcmd & USBCMD_EGSM) ? "EGSM " : "",
(usbcmd & USBCMD_GRESET) ? "GRESET " : "",
(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
(usbcmd & USBCMD_RS) ? "RS " : "");
dbg(" usbstat = %04x %s%s%s%s%s%s",
usbstat,
(usbstat & USBSTS_HCH) ? "HCHalted " : "",
(usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
(usbstat & USBSTS_HSE) ? "HostSystemError " : "",
(usbstat & USBSTS_RD) ? "ResumeDetect " : "",
(usbstat & USBSTS_ERROR) ? "USBError " : "",
(usbstat & USBSTS_USBINT) ? "USBINT " : "");
dbg(" usbint = %04x", usbint);
dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1,
0xfff & (4 * (unsigned int) usbfrnum));
dbg(" flbaseadd = %08x", flbaseadd);
dbg(" sof = %02x", sof);
uhci_show_sc (1, portsc1);
uhci_show_sc (2, portsc2);
}
#endif
This diff is collapsed.
#ifndef __LINUX_USB_UHCI_H
#define __LINUX_USB_UHCI_H
/* $Id: usb-uhci-hcd.h,v 1.1 2002/05/14 20:36:57 acher Exp $ */
#ifndef CONFIG_PCI
#error "UHCI needs the CONFIG_PCI option!"
#endif
#define MODNAME "usb-uhci-hcd"
#define UHCI_LATENCY_TIMER 0
static __inline__ void uhci_wait_ms(unsigned int ms)
{
if(!in_interrupt())
{
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(1 + ms * HZ / 1000);
}
else
mdelay(ms);
}
/* Command register */
#define USBCMD 0
#define USBCMD_RS 0x0001 /* Run/Stop */
#define USBCMD_HCRESET 0x0002 /* Host reset */
#define USBCMD_GRESET 0x0004 /* Global reset */
#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
#define USBCMD_FGR 0x0010 /* Force Global Resume */
#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
/* Status register */
#define USBSTS 2
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
/* Interrupt enable register */
#define USBINTR 4
#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
#define USBFRNUM 6
#define USBFLBASEADD 8
#define USBSOF 12
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
#define USBPORTSC_LS 0x0030 /* Line Status */
#define USBPORTSC_RD 0x0040 /* Resume Detect */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */
#define USBPORTSC_SUSP 0x1000 /* Suspend */
/* Legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */
#define UHCI_PID 0xff /* PID MASK */
#define UHCI_PTR_BITS 0x000F
#define UHCI_PTR_TERM 0x0001
#define UHCI_PTR_QH 0x0002
#define UHCI_PTR_DEPTH 0x0004
#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */
#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
/* for TD <status> */
#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
#define TD_CTRL_NAK (1 << 19) /* NAK Received */
#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
#define uhci_actual_length(desc) ((le32_to_cpu(desc->hw.td.status) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
/* for TD <flags>: */
#define UHCI_TD_REMOVE 0x0001 /* Remove when done */
/* for TD <info>: (a.k.a. Token) */
#define TD_TOKEN_TOGGLE 19
#define uhci_maxlen(token) ((token) >> 21)
#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
#define uhci_endpoint(token) (((token) >> 15) & 0xf)
#define uhci_devaddr(token) (((token) >> 8) & 0x7f)
#define uhci_devep(token) (((token) >> 8) & 0x7ff)
#define uhci_packetid(token) ((token) & 0xff)
#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
#define uhci_do_toggle(urb) usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))
#define uhci_get_toggle(urb) usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))
/* ------------------------------------------------------------------------------------
TD/QH-structures
------------------------------------------------------------------------------------ */
typedef enum {
TD_TYPE, QH_TYPE
} uhci_desc_type_t;
typedef struct {
__u32 link;
__u32 status;
__u32 info;
__u32 buffer;
} uhci_td_t, *puhci_td_t;
typedef struct {
__u32 head;
__u32 element; /* Queue element pointer */
} uhci_qh_t, *puhci_qh_t;
typedef struct {
union {
uhci_td_t td;
uhci_qh_t qh;
} hw;
uhci_desc_type_t type;
dma_addr_t dma_addr;
struct list_head horizontal;
struct list_head vertical;
struct list_head desc_list;
int last_used;
} uhci_desc_t, *puhci_desc_t;
typedef struct {
struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request
struct list_head urb_list;
struct urb *urb; // urb to which this data belongs
dma_addr_t setup_packet_dma;
dma_addr_t transfer_buffer_dma;
unsigned long started;
struct urb *next_queued_urb; // next queued urb for this EP
struct urb *prev_queued_urb;
uhci_desc_t *bottom_qh;
uhci_desc_t *next_qh; // next helper QH
char use_loop;
char flags;
} urb_priv_t, *purb_priv_t;
struct uhci_hcd {
unsigned int maxports;
int running;
int apm_state;
struct uhci_hcd *next; // chain of uhci device contexts
spinlock_t urb_list_lock; // lock to keep consistency
spinlock_t qh_lock;
spinlock_t td_lock;
atomic_t avoid_bulk;
__u32 *framelist;
dma_addr_t framelist_dma;
uhci_desc_t **iso_td;
uhci_desc_t *int_chain[8];
uhci_desc_t *ls_control_chain;
uhci_desc_t *control_chain;
uhci_desc_t *bulk_chain;
uhci_desc_t *chain_end;
uhci_desc_t *td1ms;
uhci_desc_t *td32ms;
uhci_desc_t *td128ms;
struct list_head urb_list; // list of all pending urbs
struct list_head free_desc_qh; // Cool down pool for QH
struct list_head free_desc_td; // Cool down pool for ISO/INT-TDs
struct list_head urb_unlinked; // list of all unlinked urbs
struct pci_dev *uhci_pci;
struct pci_pool *desc_pool;
int unlink_urb_done;
int loop_usage; // URBs using bandwidth reclamation
long timeout_check;
int timeout_urbs;
long last_error_time; // last error output in uhci_interrupt()
long last_hcd_irq;
int reanimations;
int need_init;
// Framework state
struct usb_hcd hcd;
};
#define hcd_to_uhci(hcd_ptr) list_entry(hcd_ptr, struct uhci_hcd, hcd)
#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH)
#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH)
#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((int)(uhci->hcd.regs) + USBFRNUM))
#define CLEAN_TRANSFER_NO_DELETION 0
#define CLEAN_TRANSFER_REGULAR 1
#define CLEAN_TRANSFER_DELETION_MARK 2
#define CLEAN_NOT_FORCED 0
#define CLEAN_FORCED 1
#define PROCESS_ISO_REGULAR 0
#define PROCESS_ISO_FORCE 1
#define PROCESS_INT_REGULAR 0
#define PROCESS_INT_REMOVE 1
#define UNLINK_ASYNC_STORE_URB 0
#define UNLINK_ASYNC_DONT_STORE 1
#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE))
#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val)
#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val)
#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val)
#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC)
#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC)
#endif
/*
UHCI HCD (Host Controller Driver) for USB, UHCI Root Hub
(c) 1999-2002
Georg Acher + Deti Fliegl + Thomas Sailer
georg@acher.org deti@fliegl.de sailer@ife.ee.ethz.ch
with the help of
David Brownell, david-b@pacbell.net
Adam Richter, adam@yggdrasil.com
Roman Weissgaerber, weissg@vienna.at
HW-initalization based on material of
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 $
*/
#define CLR_RH_PORTSTAT(x) \
status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
status = (status & 0xfff5) & ~(x); \
outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
#define SET_RH_PORTSTAT(x) \
status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
status = (status & 0xfff5) | (x); \
outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
static int oldval=-1;
/* build "status change" packet (one or two bytes) from HC registers
Since uhci_hub_status_data is called by a SW timer, it is also used
for monitoring HC health */
static int uhci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
unsigned int io_addr = (int)uhci->hcd.regs;
int i, len=0, data = 0, portstate;
int changed=0;
for (i = 0; i < uhci->maxports; i++) {
portstate=inw (io_addr + USBPORTSC1 + i * 2);
#if 0
if (i==0 && (portstate&0xf) != (oldval&0xf))
err("Port %i: %x", i+1, portstate);
#endif
if (i==0)
oldval=portstate;
if ((portstate & 0xa) > 0) {
changed=1;
}
data |= ((portstate & 0xa) > 0 ? (1 << (i + 1)) : 0);
len = (i + 1) / 8 + 1;
}
*(__u16 *)buf = cpu_to_le16 (data);
// Watchdog
if (uhci->running && time_after(jiffies, uhci->last_hcd_irq + WATCHDOG_TIMEOUT)) {
if (uhci->reanimations > MAX_REANIMATIONS) {
err("He's dead, Jim. Giving up reanimating the UHCI host controller.\n"
"Maybe a real module reload helps...");
uhci->running = 0;
}
else {
uhci->running = 0;
uhci->need_init=1; // init done in the next submit_urb
}
}
return changed?len:0;
}
/*-------------------------------------------------------------------------*/
static void uhci_hub_descriptor (struct uhci_hcd *uhci, struct usb_hub_descriptor *desc)
{
int ports = uhci->maxports;
u16 temp;
desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = 1;
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
temp = 1 + (ports / 8);
desc->bDescLength = 7 + 2 * temp;
desc->wHubCharacteristics = 0;
desc-> bitmap[0] = 0;
desc-> bitmap[1] = 0xff;
}
/*-------------------------------------------------------------------------*/
static int uhci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
int status = 0;
int stat = 0;
int cstatus;
unsigned int io_addr = (int)uhci->hcd.regs;
int ports = uhci->maxports;
switch (typeReq) {
case ClearHubFeature:
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT (USBPORTSC_PE);
break;
case USB_PORT_FEAT_C_ENABLE:
SET_RH_PORTSTAT (USBPORTSC_PEC);
break;
case USB_PORT_FEAT_SUSPEND:
CLR_RH_PORTSTAT (USBPORTSC_SUSP);
break;
case USB_PORT_FEAT_C_SUSPEND:
/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
break;
case USB_PORT_FEAT_POWER:
break;/* port power ** */
case USB_PORT_FEAT_C_CONNECTION:
SET_RH_PORTSTAT (USBPORTSC_CSC);
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
break; /* port power over current ** */
case USB_PORT_FEAT_C_RESET:
break;
default:
goto error;
}
break;
case GetHubDescriptor:
uhci_hub_descriptor (uhci, (struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
*(u32 *) buf = cpu_to_le32 (0);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
((status & USBPORTSC_PEC) >> (3 - 1));
status = (status & USBPORTSC_CCS) |
((status & USBPORTSC_PE) >> (2 - 1)) |
((status & USBPORTSC_SUSP) >> (12 - 2)) |
((status & USBPORTSC_PR) >> (9 - 4)) |
(1 << 8) | /* power on ** */
((status & USBPORTSC_LSDA) << (-8 + 9));
*(__u16 *) buf = cpu_to_le16 (status);
*(__u16 *) (buf + 2) = cpu_to_le16 (cstatus);
break;
case SetHubFeature:
// FIXME
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
SET_RH_PORTSTAT (USBPORTSC_SUSP);
break;
case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT (USBPORTSC_PR);
uhci_wait_ms (10);
CLR_RH_PORTSTAT (USBPORTSC_PR);
udelay (10);
SET_RH_PORTSTAT (USBPORTSC_PE);
uhci_wait_ms (10);
SET_RH_PORTSTAT (0xa);
break;
case USB_PORT_FEAT_POWER:
break; /* port power ** */
case USB_PORT_FEAT_ENABLE:
SET_RH_PORTSTAT (USBPORTSC_PE);
break;
default:
goto error;
}
break;
default:
error:
stat = -EPIPE;
}
dbg("Root-Hub stat port1: %x port2: %x",
inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
return stat;
}
This diff is collapsed.
This diff is collapsed.
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