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

📄 usb-uhci.c

📁 ep9315平台下USB驱动的源码
💻 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>/* 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 "usb-uhci.h"#include "usb-uhci-debug.h"#include "../hcd.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 () || current->state != TASK_RUNNING ? SLAB_ATOMIC : SLAB_KERNEL)#define KMALLOC_FLAG  (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_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);// How much URBs with ->next are walked#define MAX_NEXT_COUNT 2048static 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 govoid 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);		unlink_td (s, td, 0); // no physical unlink		delete_desc (s, td);	}	delete_desc (s, qh);		return 0;

⌨️ 快捷键说明

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