⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb-uhci.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 
 * Universal Host Controller Interface driver for USB (take II).
 *
 * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
 *               Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
 *               Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
 *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
 * (c) 2000      Yggdrasil Computing, Inc. (port of new PCI interface support
 *               from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
 * (C) 2000      David Brownell, david-b@pacbell.net (usb-ohci.c)
 *          
 * HW-initalization based on material of
 *
 * (C) Copyright 1999 Linus Torvalds
 * (C) Copyright 1999 Johannes Erdfelt
 * (C) Copyright 1999 Randy Dunlap
 * (C) Copyright 1999 Gregory P. Smith
 *
 * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 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/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>	/* for in_interrupt() */
#include <linux/init.h>
#include <linux/version.h>
#include <linux/pm.h>
#include <linux/timer.h>

#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/byteorder.h>

/* This enables more detailed sanity checks in submit_iso */
//#define ISO_SANITY_CHECK

/* This enables debug printks */
#define DEBUG

/* This enables all symbols to be exported, to ease debugging oopses */
//#define DEBUG_SYMBOLS

/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB

#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__

#include <linux/usb.h>
#include "../core/hcd.h"
#include "usb-uhci.h"
#include "usb-uhci-debug.h"

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.275"
#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
#define DRIVER_DESC "USB Universal Host Controller Interface driver"

#undef DEBUG
#undef dbg
#define dbg(format, arg...) do {} while (0)
#define DEBUG_SYMBOLS
#ifdef DEBUG_SYMBOLS
	#define _static
	#ifndef EXPORT_SYMTAB
		#define EXPORT_SYMTAB
	#endif
#else
	#define _static static
#endif

#define queue_dbg dbg //err
#define async_dbg dbg //err

#ifdef DEBUG_SLAB
	static kmem_cache_t *urb_priv_kmem;
#endif

#define SLAB_FLAG     (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)

/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth
 * Reclamation: feature that puts loop on descriptor loop 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).
 */
#define CONFIG_USB_UHCI_HIGH_BANDWIDTH 

/* *_DEPTH_FIRST puts descriptor 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.
 */
#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 CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
 * will lead to <64KB/sec performance over USB for bulk transfers targeting
 * one device's endpoint. You probably do not want to do that.
 */

// stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT  (HZ/20)

// Suppress HC interrupt error messages for 5s
#define ERROR_SUPPRESSION_TIME (HZ*5)

_static int rh_submit_urb (struct urb *urb);
_static int rh_unlink_urb (struct urb *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
_static int process_transfer (uhci_t *s, struct urb *urb, int mode);
_static int process_interrupt (uhci_t *s, struct urb *urb);
_static int process_iso (uhci_t *s, struct urb *urb, int force);

static uhci_t *devs = NULL;

/* used by userspace UHCI data structure dumper */
uhci_t **uhci_devices = &devs;

/*-------------------------------------------------------------------*/
// Cleans up collected QHs, but not more than 100 in one go
void clean_descs(uhci_t *s, int force)
{
	struct list_head *q;
	uhci_desc_t *qh;
	int now=UHCI_GET_CURRENT_FRAME(s), n=0;

	q=s->free_desc.prev;

	while (q != &s->free_desc && (force || n<100)) {
		qh = list_entry (q, uhci_desc_t, horizontal);		
		q=qh->horizontal.prev;

		if ((qh->last_used!=now) || force)
			delete_qh(s,qh);
		n++;
	}
}
/*-------------------------------------------------------------------*/
_static void uhci_switch_timer_int(uhci_t *s)
{

	if (!list_empty(&s->urb_unlinked))
		set_td_ioc(s->td1ms);
	else
		clr_td_ioc(s->td1ms);

	if (s->timeout_urbs)
		set_td_ioc(s->td32ms);
	else
		clr_td_ioc(s->td32ms);
	wmb();
}
/*-------------------------------------------------------------------*/
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
_static void enable_desc_loop(uhci_t *s, struct urb *urb)
{
	unsigned long flags;

	if (urb->transfer_flags & USB_NO_FSBR)
		return;

	spin_lock_irqsave (&s->qh_lock, flags);
	s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM);
	mb();
	s->loop_usage++;
	((urb_priv_t*)urb->hcpriv)->use_loop=1;
	spin_unlock_irqrestore (&s->qh_lock, flags);
}
/*-------------------------------------------------------------------*/
_static void disable_desc_loop(uhci_t *s, struct urb *urb)
{
	unsigned long flags;

	if (urb->transfer_flags & USB_NO_FSBR)
		return;

	spin_lock_irqsave (&s->qh_lock, flags);
	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
		s->loop_usage--;

		if (!s->loop_usage) {
			s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM);
			mb();
		}
		((urb_priv_t*)urb->hcpriv)->use_loop=0;
	}
	spin_unlock_irqrestore (&s->qh_lock, flags);
}
#endif
/*-------------------------------------------------------------------*/
_static void queue_urb_unlocked (uhci_t *s, struct urb *urb)
{
	struct list_head *p=&urb->urb_list;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
	{
		int type;
		type=usb_pipetype (urb->pipe);

		if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
			enable_desc_loop(s, urb);
	}
#endif
	urb->status = -EINPROGRESS;
	((urb_priv_t*)urb->hcpriv)->started=jiffies;
	list_add (p, &s->urb_list);
	if (urb->timeout)
		s->timeout_urbs++;
	uhci_switch_timer_int(s);
}
/*-------------------------------------------------------------------*/
_static void queue_urb (uhci_t *s, struct urb *urb)
{
	unsigned long flags=0;

	spin_lock_irqsave (&s->urb_list_lock, flags);
	queue_urb_unlocked(s,urb);
	spin_unlock_irqrestore (&s->urb_list_lock, flags);
}
/*-------------------------------------------------------------------*/
_static void dequeue_urb (uhci_t *s, struct urb *urb)
{
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
	int type;

	type=usb_pipetype (urb->pipe);

	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
		disable_desc_loop(s, urb);
#endif

	list_del (&urb->urb_list);
	if (urb->timeout && s->timeout_urbs)
		s->timeout_urbs--;

}
/*-------------------------------------------------------------------*/
_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags)
{
	dma_addr_t dma_handle;

	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
	if (!*new)
		return -ENOMEM;
	memset (*new, 0, sizeof (uhci_desc_t));
	(*new)->dma_addr = dma_handle;
	set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS));	// last by default
	(*new)->type = TD_TYPE;
	mb();
	INIT_LIST_HEAD (&(*new)->vertical);
	INIT_LIST_HEAD (&(*new)->horizontal);
	
	return 0;
}
/*-------------------------------------------------------------------*/
// append a qh to td.link physically, the SW linkage is not affected
_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
{
	unsigned long xxx;
	
	spin_lock_irqsave (&s->td_lock, xxx);

	set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
       
	mb();
	spin_unlock_irqrestore (&s->td_lock, xxx);
}
/*-------------------------------------------------------------------*/
/* insert td at last position in td-list of qh (vertical) */
_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
{
	uhci_desc_t *prev;
	unsigned long xxx;
	
	spin_lock_irqsave (&s->td_lock, xxx);

	list_add_tail (&new->vertical, &qh->vertical);

	prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);

	if (qh == prev ) {
		// virgin qh without any tds
		set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM);
	}
	else {
		// already tds inserted, implicitely remove TERM bit of prev
		set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH));
	}
	mb();
	spin_unlock_irqrestore (&s->td_lock, xxx);
	
	return 0;
}
/*-------------------------------------------------------------------*/
/* insert new_td after td (horizontal) */
_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new)
{
	uhci_desc_t *next;
	unsigned long flags;
	
	spin_lock_irqsave (&s->td_lock, flags);

	next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
	list_add (&new->horizontal, &td->horizontal);
	new->hw.td.link = td->hw.td.link;
	set_td_link(td, new->dma_addr);
	mb();
	spin_unlock_irqrestore (&s->td_lock, flags);	
	
	return 0;
}
/*-------------------------------------------------------------------*/
_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
{
	uhci_desc_t *next, *prev;
	int dir = 0;
	unsigned long flags;
	
	spin_lock_irqsave (&s->td_lock, flags);
	
	next = list_entry (element->vertical.next, uhci_desc_t, vertical);
	
	if (next == element) {
		dir = 1;
		prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
	}
	else 
		prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
	
	if (phys_unlink) {
		// really remove HW linking
		if (prev->type == TD_TYPE)
			prev->hw.td.link = element->hw.td.link;
		else
			prev->hw.qh.element = element->hw.td.link;
	}

	mb ();

	if (dir == 0)
		list_del (&element->vertical);
	else
		list_del (&element->horizontal);
	
	spin_unlock_irqrestore (&s->td_lock, flags);	
	
	return 0;
}

/*-------------------------------------------------------------------*/
_static int delete_desc (uhci_t *s, uhci_desc_t *element)
{
	pci_pool_free(s->desc_pool, element, element->dma_addr);
	return 0;
}
/*-------------------------------------------------------------------*/
// Allocates qh element
_static int alloc_qh (uhci_t *s, uhci_desc_t ** new)
{
	dma_addr_t dma_handle;

	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
	if (!*new)
		return -ENOMEM;
	memset (*new, 0, sizeof (uhci_desc_t));
	(*new)->dma_addr = dma_handle;
	set_qh_head(*new, UHCI_PTR_TERM);
	set_qh_element(*new, UHCI_PTR_TERM);
	(*new)->type = QH_TYPE;
	
	mb();
	INIT_LIST_HEAD (&(*new)->horizontal);
	INIT_LIST_HEAD (&(*new)->vertical);
	
	dbg("Allocated qh @ %p", *new);
	
	return 0;
}
/*-------------------------------------------------------------------*/
// inserts new qh before/after the qh at pos
// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
{
	uhci_desc_t *old;
	unsigned long flags;

	spin_lock_irqsave (&s->qh_lock, flags);

	if (!order) {
		// (OLD) (POS) -> (OLD) (NEW) (POS)
		old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
		list_add_tail (&new->horizontal, &pos->horizontal);
		set_qh_head(new, MAKE_QH_ADDR (pos)) ;
		if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM)))
			set_qh_head(old, MAKE_QH_ADDR (new)) ;
	}
	else {
		// (POS) (OLD) -> (POS) (NEW) (OLD)
		old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
		list_add (&new->horizontal, &pos->horizontal);
		set_qh_head(new, MAKE_QH_ADDR (old));
		set_qh_head(pos, MAKE_QH_ADDR (new)) ;
	}

	mb ();
	
	spin_unlock_irqrestore (&s->qh_lock, flags);

	return 0;
}

/*-------------------------------------------------------------------*/
_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
{
	uhci_desc_t  *prev;
	unsigned long flags;

	spin_lock_irqsave (&s->qh_lock, flags);
	
	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
	prev->hw.qh.head = element->hw.qh.head;

	dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, 
	    list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15);
	
	list_del(&element->horizontal);

	mb ();
	spin_unlock_irqrestore (&s->qh_lock, flags);
	
	return 0;
}
/*-------------------------------------------------------------------*/
_static int delete_qh (uhci_t *s, uhci_desc_t *qh)
{
	uhci_desc_t *td;
	struct list_head *p;
	
	list_del (&qh->horizontal);

	while ((p = qh->vertical.next) != &qh->vertical) {
		td = list_entry (p, uhci_desc_t, vertical);
		dbg("unlink td @ %p",td);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -