static int hci_submit_urb (struct urb * urb) { hci_t * hci; unsigned int pipe = urb->pipe; unsigned long flags; int ret; if (!urb->dev || !urb->dev->bus || urb->hcpriv) return -EINVAL; if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; hci = (hci_t *) urb->dev->bus->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice (pipe) == hci->rh.devnum) { return rh_submit_urb (urb); } if (urb_debug) urb_print (urb, "SUB", usb_pipein (pipe)); /* queue the URB to its endpoint-queue */ spin_lock_irqsave (&usb_urb_lock, flags); ret = hcs_urb_queue (hci, urb); spin_unlock_irqrestore (&usb_urb_lock, flags); return ret; }
/*************************************************************************** * Function Name : rh_int_timer_do * * This function is called when the timer expires. It gets the the port * change data and pass along to the upper protocol. * * Note: The virtual root hub interrupt pipe are polled by the timer * every "interval" ms * * Input: ptr = ptr to the urb * * Return: none **************************************************************************/ static void rh_int_timer_do (unsigned long ptr) { int len; struct urb *urb = (struct urb *) ptr; hci_t *hci = urb->dev->bus->hcpriv; DBGFUNC ("enter rh_int_timer_do\n"); if (hci->rh.send) { len = rh_send_irq (hci, urb->transfer_buffer, urb->transfer_buffer_length); if (len > 0) { urb->actual_length = len; if (urb_debug == 2) urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe)); if (urb->complete) { urb->complete (urb, NULL); } } } /* re-activate the timer */ rh_init_int_timer (urb); }
//transfer buffer must be aligned on an 8 bytes boundary static int hci_submit_urb (urb_t * urb) { hci_t * hci; unsigned int pipe = urb->pipe; unsigned long flags; int ret; if (!urb->dev || !urb->dev->bus || urb->hcpriv) return -EINVAL; #if BURST_TRANSFER_SIZE >= 8 if ( ((int)urb->transfer_buffer) & 7) { if ( (urb->transfer_buffer_length >8) || ( ( ((int)urb->transfer_buffer) & 1 ) && (urb->transfer_buffer_length >1) ) ) { printk(KERN_ERR __FILE__ ": improper alignment, size %d bytes\n",urb->transfer_buffer_length); return -EINVAL; } } #endif if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; hci = (hci_t *) urb->dev->bus->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice (pipe) == hci->rh.devnum) { return rh_submit_urb (urb); } if (urb_debug) urb_print (urb, "SUB", usb_pipein (pipe)); /* queue the URB to its endpoint-queue */ spin_lock_irqsave (&hci->urb_list_lock, flags); ret = hcs_urb_queue (hci, urb); spin_unlock_irqrestore (&hci->urb_list_lock, flags); return ret; }
/* * decouple the URB from the HC queues (TDs, urb_priv); * reporting is always done * asynchronously, and we might be dealing with an urb that's * partially transferred, or an ED with other urbs being unlinked. */ static int admhc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { struct admhcd *ahcd = hcd_to_admhcd(hcd); unsigned long flags; int ret; spin_lock_irqsave(&ahcd->lock, flags); #ifdef ADMHC_VERBOSE_DEBUG urb_print(ahcd, urb, "DEQUEUE", 1, status); #endif ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret) { /* Do nothing */ ; } else if (HC_IS_RUNNING(hcd->state)) { struct urb_priv *urb_priv; /* Unless an IRQ completed the unlink while it was being * handed to us, flag it for unlink and giveback, and force * some upcoming INTR_SF to call finish_unlinks() */ urb_priv = urb->hcpriv; if (urb_priv) { if (urb_priv->ed->state == ED_OPER) start_ed_unlink(ahcd, urb_priv->ed); } } else { /* * with HC dead, we won't respect hc queue pointers * any more ... just clean up every urb's memory. */ if (urb->hcpriv) finish_urb(ahcd, urb, status); } spin_unlock_irqrestore(&ahcd->lock, flags); return ret; }
static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) { struct usb_device * dev = urb->dev; int resubmit = 0; if (urb_debug) urb_print (urb, "RET", usb_pipeout (urb->pipe)); resubmit = urb->interval && resub_ok; urb->dev = urb->hcpriv = NULL; if (urb->complete) urb->complete (urb); /* call complete */ if (resubmit) { /* requeue the URB */ urb->dev = dev; hcs_urb_queue (hci, urb); } return 0; }
static int hcs_return_urb (hci_t * hci, urb_t * urb, int resub_ok) { int resubmit = 0; struct dmaWork* dw=NULL; if (urb_debug) urb_print (urb, "RET", usb_pipeout (urb->pipe)); resubmit = urb->interval && resub_ok; if (!resubmit) { urb->dev = NULL; dw = urb->hcpriv; urb->hcpriv = NULL; } if (urb->complete) { if ((!resubmit)||!IntervalStatus(urb)) { urb->complete (urb); /* call complete */ } } if (resubmit) { /* requeue the URB */ if (!usb_pipeint (urb->pipe) || (urb->interval==0)) { urb->start_frame = hci->frame_no; hcs_urb_queue (hci, urb); } else { urb->start_frame = hci->frame_no + urb->interval; list_add (&urb->urb_list, &hci->waiting_intr_list); hc116x_enable_sofint(hci); } } else { if (dw) FreeDmaWork(hci,dw); //this also wakes anyone waiting on its destruction } return 0; }
static int hci_unlink_urb (struct urb * urb) { unsigned long flags; hci_t * hci; DECLARE_WAITQUEUE (wait, current); void * comp = NULL; if (!urb) /* just to be sure */ return -EINVAL; if (!urb->dev || !urb->dev->bus) return -ENODEV; hci = (hci_t *) urb->dev->bus->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { return rh_unlink_urb (urb); } if (urb_debug) urb_print (urb, "UNLINK", 1); spin_lock_irqsave (&usb_urb_lock, flags); /* HACK: It seems the HID driver sometimes tries to unlink * URBs which have never been submitted - and hence whose * urb_list field has never been initialized. This causes * crashes on unplug */ if ( (urb->urb_list.prev && urb->urb_list.next) && !list_empty (&urb->urb_list)) { /* URB active? */ if (urb->transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { /* asynchron with callback */ list_del (&urb->urb_list); /* relink the urb to the del list */ list_add (&urb->urb_list, &hci->del_list); comp = urb->complete; urb->complete = NULL; spin_unlock_irqrestore (&usb_urb_lock, flags); } else { /* synchron without callback */ add_wait_queue (&hci->waitq, &wait); set_current_state (TASK_UNINTERRUPTIBLE); list_del (&urb->urb_list); /* relink the urb to the del list */ list_add (&urb->urb_list, &hci->del_list); spin_unlock_irqrestore (&usb_urb_lock, flags); schedule_timeout(HZ/50); if (!list_empty (&urb->urb_list)) list_del (&urb->urb_list); urb->complete = comp; urb->hcpriv = NULL; remove_wait_queue (&hci->waitq, &wait); } } else { /* hcd does not own URB but we keep the driver happy anyway */ spin_unlock_irqrestore (&usb_urb_lock, flags); if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { urb->status = -ENOENT; urb->actual_length = 0; urb->complete (urb); urb->status = 0; } else { urb->status = -ENOENT; } } return 0; }
/***************************************************************** * * Function Name: hc_interrupt * * Interrupt service routine. * * 1) determine the causes of interrupt * 2) clears all interrupts * 3) calls appropriate function to service the interrupt * * Input: irq = interrupt line associated with the controller * hci = data structure for the host controller * r = holds the snapshot of the processor's context before * the processor entered interrupt code. (not used here) * * Return value : None. * *****************************************************************/ static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) { char ii; hci_t *hci = __hci; int isExcessNak = 0; int urb_state = 0; char tmpIrq = 0; /* Get value from interrupt status register */ ii = SL811Read (hci, SL11H_INTSTATREG); if (ii & SL11H_INTMASK_INSRMV) { /* Device insertion or removal detected for the USB port */ SL811Write (hci, SL11H_INTENBLREG, 0); SL811Write (hci, SL11H_CTLREG1, 0); mdelay (100); // wait for device stable handleInsRmvIntr (hci); return; } /* Clear all interrupts */ SL811Write (hci, SL11H_INTSTATREG, 0xff); if (ii & SL11H_INTMASK_XFERDONE) { /* USB Done interrupt occurred */ urb_state = sh_done_list (hci, &isExcessNak); #ifdef WARNING if (hci->td_array->len > 0) printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", hci->td_array->len); #endif if (hci->td_array->len == 0 && !isExcessNak && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { if (urb_state == 0) { /* All urb_state has not been finished yet! * continue with the current urb transaction */ if (hci->last_packet_nak == 0) { if (!usb_pipecontrol (hci->td_array->td[0].urb->pipe)) sh_add_packet (hci, hci->td_array-> td[0].urb); } } else { /* The last transaction has completed: * schedule the next transaction */ sh_schedule_trans (hci, 0); } } SL811Write (hci, SL11H_INTSTATREG, 0xff); return; } if (ii & SL11H_INTMASK_SOFINTR) { hci->frame_number = (hci->frame_number + 1) % 2048; if (hci->td_array->len == 0) sh_schedule_trans (hci, 1); else { if (sofWaitCnt++ > 100) { /* The last transaction has not completed. * Need to retire the current td, and let * it transmit again later on. * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER * GET TO THIS POINT) */ DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", hci->td_array->len); urb_print (hci->td_array->td[hci->td_array->len - 1].urb, "INTERRUPT", 0); sh_done_list (hci, &isExcessNak); SL811Write (hci, SL11H_INTSTATREG, 0xff); hci->td_array->len = 0; sofWaitCnt = 0; } } tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); if (tmpIrq) { DBG ("IRQ occurred while service SOF: irq = 0x%x\n", tmpIrq); /* If we receive a DONE IRQ after schedule, need to * handle DONE IRQ again */ if (tmpIrq & SL11H_INTMASK_XFERDONE) { DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n", tmpIrq); urb_state = sh_done_list (hci, &isExcessNak); } SL811Write (hci, SL11H_INTSTATREG, 0xff); } } else { DBG ("SL811 ISR: unknown, int = 0x%x \n", ii); } SL811Write (hci, SL11H_INTSTATREG, 0xff); return; }
/* * queue up an urb for anything except the root hub */ static int admhc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct admhcd *ahcd = hcd_to_admhcd(hcd); struct ed *ed; struct urb_priv *urb_priv; unsigned int pipe = urb->pipe; int td_cnt = 0; unsigned long flags; int ret = 0; #ifdef ADMHC_VERBOSE_DEBUG spin_lock_irqsave(&ahcd->lock, flags); urb_print(ahcd, urb, "ENQEUE", usb_pipein(pipe), -EINPROGRESS); spin_unlock_irqrestore(&ahcd->lock, flags); #endif /* every endpoint has an ed, locate and maybe (re)initialize it */ ed = ed_get(ahcd, urb->ep, urb->dev, pipe, urb->interval); if (!ed) return -ENOMEM; /* for the private part of the URB we need the number of TDs */ switch (ed->type) { case PIPE_CONTROL: if (urb->transfer_buffer_length > TD_DATALEN_MAX) /* td_submit_urb() doesn't yet handle these */ return -EMSGSIZE; /* 1 TD for setup, 1 for ACK, plus ... */ td_cnt = 2; /* FALLTHROUGH */ case PIPE_BULK: /* one TD for every 4096 Bytes (can be upto 8K) */ td_cnt += urb->transfer_buffer_length / TD_DATALEN_MAX; /* ... and for any remaining bytes ... */ if ((urb->transfer_buffer_length % TD_DATALEN_MAX) != 0) td_cnt++; /* ... and maybe a zero length packet to wrap it up */ if (td_cnt == 0) td_cnt++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 && (urb->transfer_buffer_length % usb_maxpacket(urb->dev, pipe, usb_pipeout (pipe))) == 0) td_cnt++; break; case PIPE_INTERRUPT: /* * for Interrupt IN/OUT transactions, each ED contains * only 1 TD. * TODO: check transfer_buffer_length? */ td_cnt = 1; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ td_cnt = urb->number_of_packets; break; } urb_priv = urb_priv_alloc(ahcd, td_cnt, mem_flags); if (!urb_priv) return -ENOMEM; urb_priv->ed = ed; spin_lock_irqsave(&ahcd->lock, flags); /* don't submit to a dead HC */ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { ret = -ENODEV; goto fail; } if (!HC_IS_RUNNING(hcd->state)) { ret = -ENODEV; goto fail; } ret = usb_hcd_link_urb_to_ep(hcd, urb); if (ret) goto fail; /* schedule the ed if needed */ if (ed->state == ED_IDLE) { ret = ed_schedule(ahcd, ed); if (ret < 0) { usb_hcd_unlink_urb_from_ep(hcd, urb); goto fail; } if (ed->type == PIPE_ISOCHRONOUS) { u16 frame = admhc_frame_no(ahcd); /* delay a few frames before the first TD */ frame += max_t (u16, 8, ed->interval); frame &= ~(ed->interval - 1); frame |= ed->branch; urb->start_frame = frame; /* yes, only URB_ISO_ASAP is supported, and * urb->start_frame is never used as input. */ } } else if (ed->type == PIPE_ISOCHRONOUS) urb->start_frame = ed->last_iso + ed->interval; /* fill the TDs and link them to the ed; and * enable that part of the schedule, if needed * and update count of queued periodic urbs */ urb->hcpriv = urb_priv; td_submit_urb(ahcd, urb); #ifdef ADMHC_VERBOSE_DEBUG admhc_dump_ed(ahcd, "admhc_urb_enqueue", urb_priv->ed, 1); #endif fail: if (ret) urb_priv_free(ahcd, urb_priv); spin_unlock_irqrestore(&ahcd->lock, flags); return ret; }