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

📄 usb-uhci-mem.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  
    UHCI HCD (Host Controller Driver) for USB
    UHCI memory allocation and basic descriptor handling
   
    (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-mem.c,v 1.3 2002/05/25 16:42:41 acher Exp $
 */

/*###########################################################################*/
//                        UHCI STRUCTURE
/*###########################################################################*/
static struct usb_hcd *uhci_hcd_alloc (void)
{
	struct uhci_hcd *uhci;
	int len;

	len=sizeof (struct uhci_hcd);
	uhci = (struct uhci_hcd *) kmalloc (len, GFP_KERNEL);
	if (uhci == 0) 
		return NULL;
	
	memset (uhci, 0, len);
	init_dbg("uhci @ %p, hcd @ %p",uhci, &(uhci->hcd));
	INIT_LIST_HEAD (&uhci->free_desc_qh);
	INIT_LIST_HEAD (&uhci->free_desc_td);
	INIT_LIST_HEAD (&uhci->urb_list);
	INIT_LIST_HEAD (&uhci->urb_unlinked);
	spin_lock_init (&uhci->urb_list_lock);
	spin_lock_init (&uhci->qh_lock);
	spin_lock_init (&uhci->td_lock);
	atomic_set(&uhci->avoid_bulk, 0);		
	return &(uhci->hcd);
}
/*-------------------------------------------------------------------*/
static void uhci_hcd_free (struct usb_hcd *hcd)
{
	kfree (hcd_to_uhci (hcd));
}
/*###########################################################################*/
//                   DMA/PCI CONSISTENCY
/*###########################################################################*/

static void uhci_urb_dma_sync(struct uhci_hcd *uhci, struct urb *urb, urb_priv_t *urb_priv)
{
	if (urb_priv->setup_packet_dma)
		pci_dma_sync_single(uhci->uhci_pci, urb_priv->setup_packet_dma,
				    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);

	if (urb_priv->transfer_buffer_dma)
		pci_dma_sync_single(uhci->uhci_pci, urb_priv->transfer_buffer_dma,
				    urb->transfer_buffer_length,
				    usb_pipein(urb->pipe) ?
				    PCI_DMA_FROMDEVICE :
				    PCI_DMA_TODEVICE);
}
/*-------------------------------------------------------------------*/
static void uhci_urb_dma_unmap(struct uhci_hcd *uhci, struct urb *urb, urb_priv_t *urb_priv)
{
	if (urb_priv->setup_packet_dma) {
		pci_unmap_single(uhci->uhci_pci, urb_priv->setup_packet_dma,
				 sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
		urb_priv->setup_packet_dma = 0;
	}
	if (urb_priv->transfer_buffer_dma) {
		pci_unmap_single(uhci->uhci_pci, urb_priv->transfer_buffer_dma,
				 urb->transfer_buffer_length,
				 usb_pipein(urb->pipe) ?
				 PCI_DMA_FROMDEVICE :
				 PCI_DMA_TODEVICE);
		urb_priv->transfer_buffer_dma = 0;
	}
}
/*###########################################################################*/
//                       TRANSFER DESCRIPTORS (TD)
/*###########################################################################*/

static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
{
	td->hw.td.status = cpu_to_le32(status);
	td->hw.td.info = cpu_to_le32(info);
	td->hw.td.buffer = cpu_to_le32(buffer);
}
/*-------------------------------------------------------------------*/
static int alloc_td (struct uhci_hcd *uhci, uhci_desc_t ** new, int flags)
{
	dma_addr_t dma_handle;

	*new = pci_pool_alloc(uhci->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;
}
/*-------------------------------------------------------------------*/
/* insert td at last position in td-list of qh (vertical) */
static int insert_td (struct uhci_hcd *uhci, uhci_desc_t *qh, uhci_desc_t* new, int flags)
{
	uhci_desc_t *prev;
	unsigned long cpuflags;
	
	spin_lock_irqsave (&uhci->td_lock, cpuflags);

	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 (&uhci->td_lock, cpuflags);
	
	return 0;
}
/*-------------------------------------------------------------------*/
/* insert new_td after td (horizontal) */
static int insert_td_horizontal (struct uhci_hcd *uhci, uhci_desc_t *td, uhci_desc_t* new)
{
	uhci_desc_t *next;
	unsigned long flags;
	
	spin_lock_irqsave (&uhci->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;
	mb();
	set_td_link(td, new->dma_addr);
	mb();
	spin_unlock_irqrestore (&uhci->td_lock, flags);	
	
	return 0;
}
/*-------------------------------------------------------------------*/
static int unlink_td (struct uhci_hcd *uhci, uhci_desc_t *element, int phys_unlink)
{
	uhci_desc_t *next, *prev;
	int dir = 0;
	unsigned long flags;
	
	spin_lock_irqsave (&uhci->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 (&uhci->td_lock, flags);	
	
	return 0;
}
/*###########################################################################*/
//               QUEUE HEADS (QH)
/*###########################################################################*/
// Allocates qh element
static int alloc_qh (struct uhci_hcd *uhci, uhci_desc_t ** new)
{
	dma_addr_t dma_handle;

	*new = pci_pool_alloc(uhci->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 (struct uhci_hcd *uhci, uhci_desc_t *pos, uhci_desc_t *new, int order)
{
	uhci_desc_t *old;
	unsigned long flags;

	spin_lock_irqsave (&uhci->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)) ;
		mb();
		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));
		mb();
		set_qh_head(pos, MAKE_QH_ADDR (new)) ;
	}
 	mb ();
	
	spin_unlock_irqrestore (&uhci->qh_lock, flags);

	return 0;
}
/*-------------------------------------------------------------------*/
// append a qh to td.link physically, the SW linkage is not affected
static void append_qh(struct uhci_hcd *uhci, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
{
	unsigned long cpuflags;
	
	spin_lock_irqsave (&uhci->td_lock, cpuflags);

	set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
       
	mb();
	spin_unlock_irqrestore (&uhci->td_lock, cpuflags);
}
/*-------------------------------------------------------------------*/
static int unlink_qh (struct uhci_hcd *uhci, uhci_desc_t *element)
{
	uhci_desc_t  *prev;
	unsigned long flags;
	__u32 old_head;
	spin_lock_irqsave (&uhci->qh_lock, flags);
	
	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);

	old_head = element->hw.qh.head;
	element->hw.qh.head = UHCI_PTR_TERM;
	mb();
	prev->hw.qh.head = old_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 (&uhci->qh_lock, flags);
	
	return 0;
}
/*-------------------------------------------------------------------*/
static int delete_desc (struct uhci_hcd *uhci, uhci_desc_t *element)
{
	pci_pool_free(uhci->desc_pool, element, element->dma_addr);
	return 0;
}
/*-------------------------------------------------------------------*/
static int delete_qh (struct uhci_hcd *uhci, uhci_desc_t *qh)
{
	uhci_desc_t *td;
	struct list_head *p;
	int n=0;

	list_del (&qh->horizontal);

	while ((p = qh->vertical.next) != &qh->vertical && n<10000) {
		td = list_entry (p, uhci_desc_t, vertical);
		dbg("unlink td @ %p",td);
		unlink_td (uhci, td, 0); // no physical unlink
		delete_desc (uhci, td);
		n++;
	}
	// never trust any software, not even your own...
	if (n>=10000)
		err("delete_qh: Garbage in QH list, giving up");

	delete_desc (uhci, qh);
	
	return 0;
}
/*###########################################################################*/
//             DESCRIPTOR CHAINING HELPERS
/*###########################################################################*/

static void clean_td_chain (struct uhci_hcd *uhci, uhci_desc_t *td)
{
	struct list_head *p;
	uhci_desc_t *td1;

	if (!td)
		return;
	
	while ((p = td->horizontal.next) != &td->horizontal) {
		td1 = list_entry (p, uhci_desc_t, horizontal);
		delete_desc (uhci, td1);
	}
	
	delete_desc (uhci, td);
}
/*-------------------------------------------------------------------*/
// Cleans up collected QHs/TDs, but not more than 100 in one go
void clean_descs(struct uhci_hcd *uhci, int force)
{
	struct list_head *q;
	uhci_desc_t *qh,*td;
	int now=UHCI_GET_CURRENT_FRAME(uhci), n=0;

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

		if ((qh->last_used!=now) || force) {
			delete_qh(uhci,qh);
		}
		n++;
	}

	q=uhci->free_desc_td.prev;

⌨️ 快捷键说明

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