📄 usb-uhci-hcd.c
字号:
/*
UHCI HCD (Host Controller Driver) for USB, main part for HCD frame
(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-hcd.c,v 1.3 2002/05/25 16:42:41 acher Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h> /* for in_interrupt () */
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/usb.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include "../core/hcd.h"
#include "usb-uhci-hcd.h"
#define DRIVER_VERSION "$Revision: 1.3 $"
#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer"
#define DRIVER_DESC "USB 1.1 Universal Host Controller Interface driver (HCD)"
/*--------------------------------------------------------------------------*/
/* 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
* is optimal, but PCI can be slowed down up-to 5 times, slowing down
* system performance (eg. framebuffer devices).
*
* bulk_depth/ctrl_depth: 0=off (default), 1:on
* Puts descriptors for bulk/control transfers in depth-first mode.
* This has somehow similar effect to FSBR (higher speed), but does not
* slow PCI down. OTOH USB performace is slightly slower than
* in FSBR case and single device could hog whole USB, starving
* other devices. Some devices (e.g. STV680-based cameras) NEED this depth
* first search to work properly.
*
* Turning off both high_bw and bulk_depth/ctrl_depth
* will lead to <64KB/sec performance over USB for bulk transfers targeting
* 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
#define IDLE_TIMEOUT (HZ/20)
// Suppress HC interrupt error messages for 5s
#define ERROR_SUPPRESSION_TIME (HZ*5)
// HC watchdog
#define WATCHDOG_TIMEOUT (4*HZ)
#define MAX_REANIMATIONS 5
#define DEBUG_SYMBOLS
#ifdef DEBUG_SYMBOLS
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
#endif
#define queue_dbg dbg
#define async_dbg dbg
#define init_dbg dbg
/*--------------------------------------------------------------------------*/
// 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;
/* used by userspace UHCI data structure dumper */
struct uhci **uhci_devices = &devs;
/* A few prototypes */
static int uhci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb);
static int hc_reset (struct uhci_hcd *uhci);
static void uhci_stop (struct usb_hcd *hcd);
static int process_transfer (struct uhci_hcd *uhci, struct urb *urb, int mode);
static int process_iso (struct uhci_hcd *uhci, struct urb *urb, int mode);
static int process_interrupt (struct uhci_hcd *uhci, struct urb *urb, int mode);
static int process_urb (struct uhci_hcd *uhci, struct list_head *p);
static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags);
static int hc_defibrillate(struct uhci_hcd * uhci);
static int hc_irq_run(struct uhci_hcd *uhci);
#include "usb-uhci-dbg.c"
#include "usb-uhci-mem.c"
#include "usb-uhci-hub.c"
#include "usb-uhci-q.c"
#define PIPESTRING(x) (x==PIPE_BULK?"Bulk":(x==PIPE_INTERRUPT?"Interrupt":(x==PIPE_CONTROL?"Control":"Iso")))
/*--------------------------------------------------------------------------*/
static int uhci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, int mem_flags)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
urb_priv_t *urb_priv;
int ret = 0, type;
unsigned long flags;
struct urb *queued_urb=NULL;
int bustime;
type = usb_pipetype (urb->pipe);
// err("submit_urb: scheduling %p (%s), tb %p, len %i", urb,
// PIPESTRING(type),urb->transfer_buffer,urb->transfer_buffer_length);
if (uhci->need_init) {
if (in_interrupt())
return -ESHUTDOWN;
spin_lock_irqsave (&uhci->urb_list_lock, flags);
ret = hc_defibrillate(uhci);
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
if (ret)
return ret;
}
if (!uhci->running)
return -ESHUTDOWN;
spin_lock_irqsave (&uhci->urb_list_lock, flags);
queued_urb = search_dev_ep (uhci, urb); // returns already queued urb for that pipe
if (queued_urb) {
queue_dbg("found bulk urb %p\n", queued_urb);
if (( type != PIPE_BULK) ||
((type == PIPE_BULK) &&
(!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
err("ENXIO (%s) %08x, flags %x, urb %p, burb %p, probably device driver bug...",
PIPESTRING(type),
urb->pipe,urb->transfer_flags,urb,queued_urb);
return -ENXIO; // urb already queued
}
}
urb_priv = uhci_alloc_priv(mem_flags);
if (!urb_priv) {
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
return -ENOMEM;
}
urb->hcpriv = urb_priv;
urb_priv->urb=urb;
INIT_LIST_HEAD (&urb_priv->desc_list);
if (type == PIPE_CONTROL)
urb_priv->setup_packet_dma = pci_map_single(uhci->uhci_pci, urb->setup_packet,
sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
if (urb->transfer_buffer_length)
urb_priv->transfer_buffer_dma = pci_map_single(uhci->uhci_pci,
urb->transfer_buffer,
urb->transfer_buffer_length,
usb_pipein(urb->pipe) ?
PCI_DMA_FROMDEVICE :
PCI_DMA_TODEVICE);
// for bulk queuing it is essential that interrupts are disabled until submission
// all other types enable interrupts again
switch (type) {
case PIPE_BULK:
if (queued_urb) {
while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb) // find last queued bulk
queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb;
((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb;
}
atomic_inc (&uhci->avoid_bulk);
ret = uhci_submit_bulk_urb (uhci, urb, queued_urb);
atomic_dec (&uhci->avoid_bulk);
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
break;
case PIPE_ISOCHRONOUS:
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
if (urb->bandwidth == 0) { /* not yet checked/allocated */
bustime = usb_check_bandwidth (urb->dev, urb);
if (bustime < 0)
ret = bustime;
else {
ret = uhci_submit_iso_urb(uhci, urb, mem_flags);
if (ret == 0)
usb_claim_bandwidth (urb->dev, urb, bustime, 1);
}
} else { /* bandwidth is already set */
ret = uhci_submit_iso_urb(uhci, urb, mem_flags);
}
break;
case PIPE_INTERRUPT:
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
if (urb->bandwidth == 0) { /* not yet checked/allocated */
bustime = usb_check_bandwidth (urb->dev, urb);
if (bustime < 0)
ret = bustime;
else {
ret = uhci_submit_int_urb(uhci, urb);
if (ret == 0)
usb_claim_bandwidth (urb->dev, urb, bustime, 0);
}
} else { /* bandwidth is already set */
ret = uhci_submit_int_urb(uhci, urb);
}
break;
case PIPE_CONTROL:
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
ret = uhci_submit_control_urb (uhci, urb);
break;
default:
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
ret = -EINVAL;
}
// err("submit_urb: scheduled with ret: %d", ret);
if (ret != 0)
uhci_free_priv(uhci, urb, urb_priv);
return ret;
}
/*--------------------------------------------------------------------------*/
static int uhci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
unsigned long flags=0;
struct uhci_hcd *uhci;
dbg("uhci_urb_dequeue called for %p",urb);
uhci = hcd_to_uhci (hcd);
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
int ret;
spin_lock_irqsave (&uhci->urb_list_lock, flags);
ret = uhci_unlink_urb_async(uhci, urb, UNLINK_ASYNC_STORE_URB);
spin_unlock_irqrestore (&uhci->urb_list_lock, flags);
return ret;
}
else
return uhci_unlink_urb_sync(uhci, urb);
}
/*--------------------------------------------------------------------------*/
static int uhci_get_frame (struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci (hcd);
return inw ((int)uhci->hcd.regs + USBFRNUM);
}
/*--------------------------------------------------------------------------*/
// Init and shutdown functions for HW
/*--------------------------------------------------------------------------*/
static int hc_reset (struct uhci_hcd *uhci)
{
unsigned long io_addr = (unsigned long)uhci->hcd.regs;
uhci->apm_state = 0;
uhci->running = 0;
outw (USBCMD_GRESET, io_addr + USBCMD);
uhci_wait_ms (50); /* Global reset for 50ms */
outw (0, io_addr + USBCMD);
uhci_wait_ms (10);
return 0;
}
/*--------------------------------------------------------------------------*/
static int hc_irq_run(struct uhci_hcd *uhci)
{
unsigned long io_addr = (unsigned long)uhci->hcd.regs;
/* Turn on all interrupts */
outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
/* Start at frame 0 */
outw (0, io_addr + USBFRNUM);
outl (uhci->framelist_dma, io_addr + USBFLBASEADD);
/* Run and mark it configured with a 64-byte max packet */
outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->apm_state = 1;
uhci->running = 1;
uhci->last_hcd_irq = jiffies+4*HZ;
return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -