📄 ohci-hcd-1.c
字号:
/* * URB OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * * [ Initialisation is based on Linus' ] * [ uhci code and gregs ohci fragments ] * [ (C) Copyright 1999 Linus Torvalds ] * [ (C) Copyright 1999 Gregory P. Smith] * * * History: * * v5.2 1999/12/07 URB 3rd preview, * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume * i386: HUB, Keyboard, Mouse, Printer * * v4.3 1999/10/27 multiple HCs, bulk_request * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. * v4.0 1999/08/18 * v3.0 1999/06/25 * v2.1 1999/05/09 code clean up * v2.0 1999/05/04 * v1.0 1999/04/27 initial release * */ #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/malloc.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/timer.h>// #include <linux/spinlock.h>#include <linux/list.h>#include <linux/interrupt.h> /* for in_interrupt() */#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#define DEBUG#include "usb.h"#include "ohci-hcd.h"#ifdef DEBUG #define dbg printk#else #define dbg __xxxstatic void __xxx (const char *format, ...) {}#endif#ifdef CONFIG_APM#include <linux/apm_bios.h>static int handle_apm_event (apm_event_t event);#endif#ifdef CONFIG_PMAC_PBOOK#include <linux/adb.h>#include <linux/pmu.h>#endifstatic DECLARE_WAIT_QUEUE_HEAD (op_wakeup); static LIST_HEAD (ohci_hcd_list);spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;/*-------------------------------------------------------------------------* * URB support functions *-------------------------------------------------------------------------*/ /* free the private part of an URB */ static void urb_rm_priv (urb_t * urb) { urb_priv_t * urb_priv = urb->hcpriv; int i; void * wait; if (!urb_priv) return; wait = urb_priv->wait; for (i = 0; i < urb_priv->length; i++) { if (urb_priv->td [i]) { OHCI_FREE (urb_priv->td [i]); } } kfree (urb->hcpriv); urb->hcpriv = NULL; if (wait) { add_wait_queue (&op_wakeup, wait); wake_up (&op_wakeup); }}/*-------------------------------------------------------------------------*/ #ifdef DEBUGstatic int sohci_get_current_frame_number (struct usb_device * dev);/* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ static void urb_print (urb_t * urb, char * str, int small){ unsigned int pipe= urb->pipe; int i, len; if (!urb->dev || !urb->dev->bus) { printk(KERN_DEBUG " %s URB: no dev\n", str); return; } printk (KERN_DEBUG "%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s," "flags:%4x,len:%d/%d,stat:%d(%x)\n", str, sohci_get_current_frame_number (urb->dev), usb_pipedevice (pipe), usb_pipeendpoint (pipe), usb_pipeout (pipe)? 'O': 'I', usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): (usb_pipecontrol (pipe)? "CTRL": "BULK"), urb->transfer_flags, urb->actual_length, urb->transfer_buffer_length, urb->status, urb->status); if (!small) { if (usb_pipecontrol (pipe)) { printk (KERN_DEBUG " cmd(8):"); for (i = 0; i < 8 ; i++) printk (" %02x", ((__u8 *) urb->setup_packet) [i]); printk ("\n"); } if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { printk (KERN_DEBUG " data(%d/%d):", urb->actual_length, urb->transfer_buffer_length); len = usb_pipeout (pipe)? urb->transfer_buffer_length: urb->actual_length; for (i = 0; i < 16 && i < len; i++) printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); printk ("%s stat:%d\n", i < len? "...": "", urb->status); } } }/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/void ep_print_int_eds (ohci_t * ohci, char * str) { int i, j; __u32 * ed_p; for (i= 0; i < 32; i++) { j = 5; printk (KERN_DEBUG " %s branch int %2d(%2x): ", str, i, i); ed_p = &(ohci->hcca.int_table [i]); while (*ed_p != 0 && j--) { printk ("ed: %4x; ", (((ed_t *) bus_to_virt (*ed_p))->hwINFO)); ed_p = &(((ed_t *) bus_to_virt (*ed_p))->hwNextED); } printk ("\n"); }} #endif/*-------------------------------------------------------------------------* * Interface functions (URB) *-------------------------------------------------------------------------*//* return a request to the completion handler */ static int sohci_return_urb (urb_t * urb){ urb_priv_t * urb_priv = urb->hcpriv; urb_t * urbt; unsigned int flags; int i; /* just to be sure */ if (!urb->complete) { urb_rm_priv (urb); usb_dec_dev_use (urb->dev); return -1; } if (!urb_priv) return -1; /* urb already unlinked */ #ifdef DEBUG urb_print (urb, "RET", usb_pipeout (urb->pipe));#endif switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: urb->complete (urb); /* call complete and requeue URB */ urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; if (urb_priv->state != URB_DEL) td_submit_urb (urb); break; case PIPE_ISOCHRONOUS: for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); if (urbt) { /* send the reply and requeue URB */ urb->complete (urb); spin_lock_irqsave (&usb_ed_lock, flags); urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; urb->start_frame = urb_priv->ed->last_iso + 1; if (urb_priv->state != URB_DEL) { for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; } td_submit_urb (urb); } spin_unlock_irqrestore (&usb_ed_lock, flags); } else { /* unlink URB, call complete */ urb_rm_priv (urb); usb_dec_dev_use (urb->dev); urb->complete (urb); } break; case PIPE_BULK: case PIPE_CONTROL: /* unlink URB, call complete */ urb_rm_priv (urb); usb_dec_dev_use (urb->dev); urb->complete (urb); break; } return 0;}/*-------------------------------------------------------------------------*//* get a transfer request */ static int sohci_submit_urb (urb_t * urb){ ohci_t * ohci; ed_t * ed; urb_priv_t * urb_priv; unsigned int pipe = urb->pipe; int i, size = 0; unsigned int flags; if (!urb->dev || !urb->dev->bus) return -EINVAL; if (urb->hcpriv) return -EINVAL; /* urb already in use */ usb_inc_dev_use (urb->dev); ohci = (ohci_t *) urb->dev->bus->hcpriv; #ifdef DEBUG urb_print (urb, "SUB", usb_pipein (pipe));#endif if (usb_pipedevice (pipe) == ohci->rh.devnum) return rh_submit_urb (urb); /* a request to the virtual root hub */ /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) { usb_dec_dev_use (urb->dev); return -ENOMEM; } /* for the private part of the URB we need the number of TDs (size) */ switch (usb_pipetype (pipe)) { case PIPE_BULK: /* one TD for every 4096 Byte */ size = (urb->transfer_buffer_length - 1) / 4096 + 1; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; } break; case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ size = (urb->transfer_buffer_length == 0)? 2: (urb->transfer_buffer_length - 1) / 4096 + 3; break; case PIPE_INTERRUPT: /* one TD */ size = 1; break; } /* allocate the private part or the URB */ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (!urb_priv) { usb_dec_dev_use (urb->dev); return -ENOMEM; } memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); /* fill the private part of the URB */ urb->hcpriv = urb_priv; urb_priv->length = size; urb_priv->td_cnt = 0; urb_priv->state = 0; urb_priv->ed = ed; urb_priv->wait = NULL; /* allocate the TDs */ for (i = 0; i < size; i++) { OHCI_ALLOC (urb_priv->td[i], sizeof (td_t)); if (!urb_priv->td[i]) { usb_dec_dev_use (urb->dev); urb_rm_priv (urb); return -ENOMEM; } } spin_lock_irqsave (&usb_ed_lock, flags); if (ed->state == ED_NEW || (ed->state & ED_DEL)) { urb_rm_priv(urb); usb_dec_dev_use (urb->dev); return -EINVAL; } /* for ISOC transfers calculate start frame index */ if (urb->transfer_flags & USB_ISO_ASAP) { urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): (ohci->hcca.frame_no + 10)) & 0xffff; } td_submit_urb (urb); /* fill the TDs and link it to the ed */ if (ed->state != ED_OPER) /* link the ed into a chain if is not already */ ep_link (ohci, ed); spin_unlock_irqrestore (&usb_ed_lock, flags); urb->status = USB_ST_URB_PENDING; // queue_urb(s, &urb->urb_list); return 0; }/*-------------------------------------------------------------------------*//* deactivate all TDs and remove the private part of the URB */ static int sohci_unlink_urb (urb_t * urb){ unsigned int flags; ohci_t * ohci; DECLARE_WAITQUEUE (wait, current); if (!urb) /* just to be sure */ return -EINVAL; #ifdef DEBUG urb_print (urb, "UNLINK", 1);#endif ohci = (ohci_t *) urb->dev->bus->hcpriv; if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); /* a request to the virtual root hub */ if (urb->hcpriv) { if (urb->status == USB_ST_URB_PENDING) { /* URB active? */ urb_priv_t * urb_priv = urb->hcpriv; urb_priv->state = URB_DEL; /* we want to delete the TDs of an URB from an ed * request the deletion, it will be handled at the next USB-frame */ urb_priv->wait = &wait; spin_lock_irqsave (&usb_ed_lock, flags); ep_rm_ed (urb->dev, urb_priv->ed); urb_priv->ed->state |= ED_URB_DEL; spin_unlock_irqrestore (&usb_ed_lock, flags); current->state = TASK_UNINTERRUPTIBLE; schedule_timeout (HZ / 10); /* wait until all TDs are deleted */ remove_wait_queue (&op_wakeup, &wait); } else urb_rm_priv (urb); usb_dec_dev_use (urb->dev); } return 0;}/*-------------------------------------------------------------------------*//* allocate private data space for a usb device */static int sohci_alloc_dev (struct usb_device *usb_dev){ struct ohci_device * dev; dev = kmalloc (sizeof (*dev), GFP_KERNEL); if (!dev) return -ENOMEM; memset (dev, 0, sizeof (*dev)); usb_dev->hcpriv = dev; return 0;}/*-------------------------------------------------------------------------*//* free private data space of usb device */ static int sohci_free_dev (struct usb_device * usb_dev){ unsigned int flags; int i, cnt = 0; ed_t * ed; DECLARE_WAITQUEUE (wait, current); struct ohci_device * dev = usb_to_ohci (usb_dev); ohci_t * ohci = usb_dev->bus->hcpriv; if (!dev) return 0; if (usb_dev->devnum >= 0) { /* delete all TDs of all EDs */ spin_lock_irqsave (&usb_ed_lock, flags); for(i = 0; i < NUM_EDS; i++) { ed = &(dev->ed[i]); if (ed->state != ED_NEW) { if (ed->state == ED_OPER) ep_unlink (ohci, ed); ep_rm_ed (usb_dev, ed); ed->state = ED_DEL; cnt++; } } spin_unlock_irqrestore (&usb_ed_lock, flags); if (cnt > 0) { dev->wait = &wait; current->state = TASK_UNINTERRUPTIBLE; schedule_timeout (HZ / 10); remove_wait_queue (&op_wakeup, &wait); } } kfree (dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -