📄 sl811.c
字号:
head = &hc->intr_list; break; case PIPE_CONTROL: head = &hc->ctrl_list; break; case PIPE_BULK: head = &hc->bulk_list; break; } if (!sl811_alloc_urb_priv(urb)) { ret = -ENOMEM; goto out_unlock; } switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: if (urb->number_of_packets <= 0) { ret = -EINVAL; break; } bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; break; } if (!(ret = sl811_submit_isochronous(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; case PIPE_INTERRUPT: bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else if (!(ret = sl811_submit_interrupt(urb))) usb_claim_bandwidth(urb->dev, urb, bustime, 0); break; case PIPE_CONTROL: ret = sl811_submit_control(urb); break; case PIPE_BULK: ret = sl811_submit_bulk(urb); break; } if (ret == 0) { ((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies; list_add(&urb->urb_list, head); PDEBUG(4, "add to type list"); urb->status = -EINPROGRESS; if (++hc->active_urbs == 1) sl811_enable_interrupt(hc); goto out_unlock; } else { PDEBUG(2, "submit urb fail! error = %d", ret); sl811_free_urb_priv(urb); } out_unlock: spin_unlock(&urb->lock); return ret;}/* * Reset the urb */static void sl811_reset_urb(struct urb *urb){ struct sl811_urb_priv *urbp = urb->hcpriv; switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: sl811_reset_isochronous(urb); break; case PIPE_INTERRUPT: sl811_reset_interrupt(urb); break; case PIPE_CONTROL: return; case PIPE_BULK: sl811_reset_bulk(urb); break; } urbp->inserttime = jiffies;}/* * Return the result of a transfer */static void sl811_result_urb(struct urb *urb){ struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_hc *hc = urb->dev->bus->hcpriv; struct list_head *head = NULL; struct urb *u = NULL; int reset = 0; int ring = 0; if (urb->status != -EINPROGRESS) { PDEBUG(1, "urb status is not EINPROGRESS!"); return ; } spin_lock(&urb->lock); switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: head = &hc->iso_list; sl811_result_isochronous(urb); // if the urb is not unlink and is in a urb "ring", we reset it if (!urbp->unlink && urb->next) ring = 1; break; case PIPE_INTERRUPT: head = &hc->intr_list; sl811_result_interrupt(urb); // if the urb is not unlink and not "once" query, we reset. if (!urbp->unlink && urb->interval) reset = 1; break; case PIPE_CONTROL: head = &hc->ctrl_list; sl811_result_control(urb); break; case PIPE_BULK: head = &hc->bulk_list; sl811_result_bulk(urb); // if the urb is not unlink and is in a urb "ring", we reset it if (!urbp->unlink && urb->next) ring = 1; break; } PDEBUG(4, "result urb status = %d", urb->status); if (ring && urb->next == urb) reset = 1; if (!reset) { switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: usb_release_bandwidth(urb->dev, urb, 1); break; case PIPE_INTERRUPT: usb_release_bandwidth(urb->dev, urb, 0); break; } sl811_free_urb_priv(urb); } spin_unlock(&urb->lock); if (urb->complete) urb->complete(urb); if (reset) { spin_lock(&urb->lock); sl811_reset_urb(urb); if (usb_pipeint(urb->pipe)) list_add(&urb->urb_list, &hc->idle_intr_list); else list_add(&urb->urb_list, head); spin_unlock(&urb->lock); } else { if (--hc->active_urbs <= 0) { hc->active_urbs = 0; sl811_disable_interrupt(hc); } if (ring) u = urb->next; else u = sl811_find_same_devep(&hc->wait_list, urb); if (u) { if (!list_empty(&u->urb_list)) list_del(&u->urb_list); if (sl811_submit_urb_with_lock(u)) list_add(&u->urb_list, &hc->wait_list); } }}#ifdef SL811_TIMEOUT/* * Unlink the urb from the urb list */static int sl811_unlink_urb(struct urb *urb){ unsigned long flags; struct sl811_hc *hc; struct sl811_urb_priv *urbp; int call = 0; int schedule = 0; int count = 0; if (!urb) { PDEBUG(1, "urb is null"); return -EINVAL; } if (!urb->dev || !urb->dev->bus) { PDEBUG(1, "dev or bus is null"); return -ENODEV; } hc = urb->dev->bus->hcpriv; urbp = urb->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) return sl811_rh_unlink_urb(urb); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); // in wait list if (sl811_find_same_urb(&hc->wait_list, urb)) { PDEBUG(4, "unlink urb in wait list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; call = 1; goto out; } // in intr idle list. if (sl811_find_same_urb(&hc->idle_intr_list, urb)) { PDEBUG(4, "unlink urb in idle intr list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; sl811_free_urb_priv(urb); usb_release_bandwidth(urb->dev, urb, 0); if (--hc->active_urbs <= 0) { hc->active_urbs = 0; sl811_disable_interrupt(hc); } call = 1; goto out; } if (urb->status == -EINPROGRESS) { PDEBUG(3, "urb is still in progress"); urbp->unlink = 1;re_unlink: // Is it in progress? urbp = urb->hcpriv; if (urbp && hc->cur_td == urbp->cur_td) { ++count; if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) { PDEBUG(3, "unlink: cur td is still in progress! count = %d", count);re_schedule: schedule = 1; spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); schedule_timeout(HZ/50); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); } else { PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count); spin_unlock(&urb->lock); sl811_transfer_done(hc, 0); spin_lock(&urb->lock); } goto re_unlink; } if (list_empty(&urb->urb_list)) { PDEBUG(3, "unlink: list empty!"); goto out; } if (urb->transfer_flags & USB_TIMEOUT_KILLED) { PDEBUG(3, "unlink: time out killed"); // it is timeout killed by us goto result; } else if (urb->transfer_flags & USB_ASYNC_UNLINK) { // we do nothing, just let it be processing later PDEBUG(3, "unlink async, do nothing"); goto out; } else { // synchron without callback PDEBUG(3, "unlink synchron, we wait the urb complete or timeout"); if (schedule == 0) { PDEBUG(3, "goto re_schedule"); goto re_schedule; } else { PDEBUG(3, "already scheduled"); goto result; } } } else if (!list_empty(&urb->urb_list)) { PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); //list_del_init(&urb->urb_list); //call = 1; }out: spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); if (call && urb->complete) urb->complete(urb); return 0; result: spin_unlock(&urb->lock); list_del_init(&urb->urb_list); sl811_result_urb(urb); spin_unlock_irqrestore(&hc->hc_lock, flags); return 0;}#else/* * Unlink the urb from the urb list */static int sl811_unlink_urb(struct urb *urb){ unsigned long flags; struct sl811_hc *hc; struct sl811_urb_priv *urbp; int call = 0; if (!urb) { PDEBUG(1, "urb is null"); return -EINVAL; } if (!urb->dev || !urb->dev->bus) { PDEBUG(1, "dev or bus is null"); return -ENODEV; } hc = urb->dev->bus->hcpriv; urbp = urb->hcpriv; /* a request to the virtual root hub */ if (usb_pipedevice(urb->pipe) == hc->rh.devnum) return sl811_rh_unlink_urb(urb); spin_lock_irqsave(&hc->hc_lock, flags); spin_lock(&urb->lock); // in wait list if (sl811_find_same_urb(&hc->wait_list, urb)) { PDEBUG(2, "unlink urb in wait list"); list_del_init(&urb->urb_list); urb->status = -ENOENT; call = 1; goto out; } if (urb->status == -EINPROGRESS) { PDEBUG(2, "urb is still in progress"); urbp->unlink = 1; // Is it in progress? urbp = urb->hcpriv; if (urbp && hc->cur_td == urbp->cur_td) { // simple, let it out PDEBUG(2, "unlink: cur td is still in progress!"); hc->cur_td = NULL; } goto result; } else if (!list_empty(&urb->urb_list)) { PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status); list_del_init(&urb->urb_list); if (urbp) goto result; else call = 1; }out: spin_unlock(&urb->lock); spin_unlock_irqrestore(&hc->hc_lock, flags); if (call && urb->complete) urb->complete(urb); return 0; result: spin_unlock(&urb->lock); list_del_init(&urb->urb_list); sl811_result_urb(urb); spin_unlock_irqrestore(&hc->hc_lock, flags); return 0;}#endifstatic int sl811_get_current_frame_number(struct usb_device *usb_dev){ return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number;}static struct usb_operations sl811_device_operations = { sl811_alloc_dev_priv, sl811_free_dev_priv, sl811_get_current_frame_number, sl811_submit_urb, sl811_unlink_urb};/* * This functions transmit a td. */static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td){ sl811_print_td(4, td); sl811_write_buf(hc, SL811_ADDR_A, &td->addr, 4); if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT)) sl811_write_buf(hc, td->addr, td->buf, td->len); sl811_write(hc, SL811_CTRL_A, td->ctrl);} /* * This function checks the status of the transmitted or received packet * and copy the data from the SL811HS register into a buffer. */static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td){ struct urb *urb = td->urb;#ifdef SL811_DEBUG int dev = usb_pipedevice(td->urb->pipe); int ep = usb_pipeendpoint(td->urb->pipe);#endif sl811_read_buf(hc, SL811_STS_A, &td->status, 2); if (td->status & SL811_USB_STS_ACK) { td->done = 1; /* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) { PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep); td->td_status = -EILSEQ; }*/ if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0) sl811_read_buf(hc, td->addr, td->buf, td->len - td->left); if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) { PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td); td->td_status = -EREMOTEIO; } else td->td_status = 0; } else if (td->status & SL811_USB_STS_STALL) { PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td); td->td_status = -EPIPE; if (urb->dev) usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe)); td->done = 1; } else if (td->status & SL811_USB_STS_OVERFLOW) { PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT); td->td_status = -EOVERFLOW; td->done = 1; } else if (td->status & SL811_USB_STS_TIMEOUT ) { PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td); td->td_status = -ETIMEDOUT; if (--td->errcnt == 0) td->done = 1; } else if (td->status & SL811_USB_STS_ERROR) { PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td); td->td_status = -EILSEQ; if (--td->errcnt == 0) td->done = 1; } else if (td->status & SL811_USB_STS_NAK) { ++td->nakcnt; PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt); td->td_status = -EINPROGRESS; if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) { PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt); td->td_status = -ETIMEDOUT; td->done = 1; } } sl811_print_td(4, td);}/* * This function checks the status of current urb. */static int sl811_parse_cur_urb(struct urb *urb){ struct sl811_urb_priv *urbp = urb->hcpriv; struct sl811_td *td = urbp->cur_td; struct list_head *tmp; sl811_print_td(5, td); // this td not done yet. if (!td->done) return 0; // the last ld, so the urb is done. if (td == urbp->last_td) { PDEBUG(4, "urb = %p is done success", td->urb); if (usb_pipeisoc(td->urb->pipe)) PDEBUG(4, "ISO URB DONE, td = %p", td); return 1; } // iso transfer, we always advance to next td if (usb_pipeisoc(td->urb->pipe)) { tmp = &td->td_list; tmp = tmp->next; urbp->cur_td = list_entry(tmp, struct sl811_td, td_list); PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td); return 0; } // some error occur, so the urb is done. if (td->td_status) { PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status); return 1; } // short packet. if (td->left) { if (usb_pipecontrol(td->urb->pipe)) { // control packet, we advance to the last td PDEBUG(3, "ctrl short packet, advance to last td"); urbp->cur_td = urbp->last_td; return 0; } else { // interrut and bulk packet, urb is over. PDEBUG(3, "bulk or intr short packet, urb is over"); return 1; } } // we advance to next td. tmp = &td->td_list; tmp = tmp->next; urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);#ifdef SL811_DEBUG PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td); sl811_print_td(5, urbp->cur_td); if (td == urbp->cur_td)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -