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

📄 uhci-q.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
	return ret;}static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb){	int limits;	unsigned int start = 0, end = 0;	if (urb->number_of_packets > 900)	/* 900? Why? */		return -EFBIG;	limits = isochronous_find_limits(uhci, urb, &start, &end);	if (urb->transfer_flags & URB_ISO_ASAP) {		if (limits) {			uhci_get_current_frame_number(uhci);			urb->start_frame = (uhci->frame_number + 10)					& (UHCI_NUMFRAMES - 1);		} else			urb->start_frame = end;	} else {		urb->start_frame &= (UHCI_NUMFRAMES - 1);		/* FIXME: Sanity check */	}	return 0;}/* * Isochronous transfers */static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb){	struct uhci_td *td;	int i, ret, frame;	int status, destination;	struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);	ret = isochronous_find_start(uhci, urb);	if (ret)		return ret;	for (i = 0; i < urb->number_of_packets; i++) {		td = uhci_alloc_td(uhci);		if (!td)			return -ENOMEM;		uhci_add_td_to_urb(urb, td);		uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),			urb->transfer_dma + urb->iso_frame_desc[i].offset);		if (i + 1 >= urb->number_of_packets)			td->status |= cpu_to_le32(TD_CTRL_IOC);	}	frame = urb->start_frame;	list_for_each_entry(td, &urbp->td_list, list) {		uhci_insert_td_frame_list(uhci, td, frame);		frame += urb->interval;	}	return -EINPROGRESS;}static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb){	struct uhci_td *td;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	int status;	int i, ret = 0;	urb->actual_length = urb->error_count = 0;	i = 0;	list_for_each_entry(td, &urbp->td_list, list) {		int actlength;		unsigned int ctrlstat = td_status(td);		if (ctrlstat & TD_CTRL_ACTIVE)			return -EINPROGRESS;		actlength = uhci_actual_length(ctrlstat);		urb->iso_frame_desc[i].actual_length = actlength;		urb->actual_length += actlength;		status = uhci_map_status(uhci_status_bits(ctrlstat),				usb_pipeout(urb->pipe));		urb->iso_frame_desc[i].status = status;		if (status) {			urb->error_count++;			ret = status;		}		i++;	}	unlink_isochronous_tds(uhci, urb);	return ret;}static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb){	struct urb_priv *up;	/* We don't match Isoc transfers since they are special */	if (usb_pipeisoc(urb->pipe))		return NULL;	list_for_each_entry(up, &uhci->urb_list, urb_list) {		struct urb *u = up->urb;		if (u->dev == urb->dev && u->status == -EINPROGRESS) {			/* For control, ignore the direction */			if (usb_pipecontrol(urb->pipe) &&			    (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))				return u;			else if (u->pipe == urb->pipe)				return u;		}	}	return NULL;}static int uhci_urb_enqueue(struct usb_hcd *hcd,		struct usb_host_endpoint *ep,		struct urb *urb, gfp_t mem_flags){	int ret;	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	unsigned long flags;	struct urb *eurb;	int bustime;	spin_lock_irqsave(&uhci->lock, flags);	ret = urb->status;	if (ret != -EINPROGRESS)		/* URB already unlinked! */		goto out;	eurb = uhci_find_urb_ep(uhci, urb);	if (!uhci_alloc_urb_priv(uhci, urb)) {		ret = -ENOMEM;		goto out;	}	switch (usb_pipetype(urb->pipe)) {	case PIPE_CONTROL:		ret = uhci_submit_control(uhci, urb, eurb);		break;	case PIPE_INTERRUPT:		if (!eurb) {			bustime = usb_check_bandwidth(urb->dev, urb);			if (bustime < 0)				ret = bustime;			else {				ret = uhci_submit_interrupt(uhci, urb, eurb);				if (ret == -EINPROGRESS)					usb_claim_bandwidth(urb->dev, urb, bustime, 0);			}		} else {	/* inherit from parent */			urb->bandwidth = eurb->bandwidth;			ret = uhci_submit_interrupt(uhci, urb, eurb);		}		break;	case PIPE_BULK:		ret = uhci_submit_bulk(uhci, urb, eurb);		break;	case PIPE_ISOCHRONOUS:		bustime = usb_check_bandwidth(urb->dev, urb);		if (bustime < 0) {			ret = bustime;			break;		}		ret = uhci_submit_isochronous(uhci, urb);		if (ret == -EINPROGRESS)			usb_claim_bandwidth(urb->dev, urb, bustime, 1);		break;	}	if (ret != -EINPROGRESS) {		/* Submit failed, so delete it from the urb_list */		struct urb_priv *urbp = urb->hcpriv;		list_del_init(&urbp->urb_list);		uhci_destroy_urb_priv(uhci, urb);	} else		ret = 0;out:	spin_unlock_irqrestore(&uhci->lock, flags);	return ret;}/* * Return the result of a transfer */static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb){	int ret = -EINPROGRESS;	struct urb_priv *urbp;	spin_lock(&urb->lock);	urbp = (struct urb_priv *)urb->hcpriv;	if (urb->status != -EINPROGRESS)	/* URB already dequeued */		goto out;	switch (usb_pipetype(urb->pipe)) {	case PIPE_CONTROL:		ret = uhci_result_control(uhci, urb);		break;	case PIPE_BULK:	case PIPE_INTERRUPT:		ret = uhci_result_common(uhci, urb);		break;	case PIPE_ISOCHRONOUS:		ret = uhci_result_isochronous(uhci, urb);		break;	}	if (ret == -EINPROGRESS)		goto out;	urb->status = ret;	switch (usb_pipetype(urb->pipe)) {	case PIPE_CONTROL:	case PIPE_BULK:	case PIPE_ISOCHRONOUS:		/* Release bandwidth for Interrupt or Isoc. transfers */		if (urb->bandwidth)			usb_release_bandwidth(urb->dev, urb, 1);		uhci_unlink_generic(uhci, urb);		break;	case PIPE_INTERRUPT:		/* Release bandwidth for Interrupt or Isoc. transfers */		/* Make sure we don't release if we have a queued URB */		if (list_empty(&urbp->queue_list) && urb->bandwidth)			usb_release_bandwidth(urb->dev, urb, 0);		else			/* bandwidth was passed on to queued URB, */			/* so don't let usb_unlink_urb() release it */			urb->bandwidth = 0;		uhci_unlink_generic(uhci, urb);		break;	default:		dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "				"for urb %p\n",				__FUNCTION__, usb_pipetype(urb->pipe), urb);	}	/* Move it from uhci->urb_list to uhci->complete_list */	uhci_moveto_complete(uhci, urbp);out:	spin_unlock(&urb->lock);}static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb){	struct list_head *head;	struct uhci_td *td;	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	int prevactive = 0;	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */	/*	 * Now we need to find out what the last successful toggle was	 * so we can update the local data toggle for the next transfer	 *	 * There are 2 ways the last successful completed TD is found:	 *	 * 1) The TD is NOT active and the actual length < expected length	 * 2) The TD is NOT active and it's the last TD in the chain	 *	 * and a third way the first uncompleted TD is found:	 *	 * 3) The TD is active and the previous TD is NOT active	 *	 * Control and Isochronous ignore the toggle, so this is safe	 * for all types	 *	 * FIXME: The toggle fixups won't be 100% reliable until we	 * change over to using a single queue for each endpoint and	 * stop the queue before unlinking.	 */	head = &urbp->td_list;	list_for_each_entry(td, head, list) {		unsigned int ctrlstat = td_status(td);		if (!(ctrlstat & TD_CTRL_ACTIVE) &&				(uhci_actual_length(ctrlstat) <				 uhci_expected_length(td_token(td)) ||				td->list.next == head))			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),				uhci_packetout(td_token(td)),				uhci_toggle(td_token(td)) ^ 1);		else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)			usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),				uhci_packetout(td_token(td)),				uhci_toggle(td_token(td)));		prevactive = ctrlstat & TD_CTRL_ACTIVE;	}	uhci_delete_queued_urb(uhci, urb);	/* The interrupt loop will reclaim the QH's */	uhci_remove_qh(uhci, urbp->qh);	urbp->qh = NULL;}static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	unsigned long flags;	struct urb_priv *urbp;	spin_lock_irqsave(&uhci->lock, flags);	urbp = urb->hcpriv;	if (!urbp)			/* URB was never linked! */		goto done;	list_del_init(&urbp->urb_list);	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)		unlink_isochronous_tds(uhci, urb);	uhci_unlink_generic(uhci, urb);	uhci_get_current_frame_number(uhci);	if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) {		uhci_remove_pending_urbps(uhci);		uhci->urb_remove_age = uhci->frame_number;	}	/* If we're the first, set the next interrupt bit */	if (list_empty(&uhci->urb_remove_list))		uhci_set_next_interrupt(uhci);	list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);done:	spin_unlock_irqrestore(&uhci->lock, flags);	return 0;}static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb){	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;	struct list_head *head;	struct uhci_td *td;	int count = 0;	uhci_dec_fsbr(uhci, urb);	urbp->fsbr_timeout = 1;	/*	 * Ideally we would want to fix qh->element as well, but it's	 * read/write by the HC, so that can introduce a race. It's not	 * really worth the hassle	 */	head = &urbp->td_list;	list_for_each_entry(td, head, list) {		/*		 * Make sure we don't do the last one (since it'll have the		 * TERM bit set) as well as we skip every so many TD's to		 * make sure it doesn't hog the bandwidth		 */		if (td->list.next != head && (count % DEPTH_INTERVAL) ==				(DEPTH_INTERVAL - 1))			td->link |= UHCI_PTR_DEPTH;		count++;	}	return 0;}static void uhci_free_pending_qhs(struct uhci_hcd *uhci){	struct uhci_qh *qh, *tmp;	list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {		list_del_init(&qh->remove_list);		uhci_free_qh(uhci, qh);	}}static void uhci_free_pending_tds(struct uhci_hcd *uhci){	struct uhci_td *td, *tmp;	list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {		list_del_init(&td->remove_list);		uhci_free_td(uhci, td);	}}static voiduhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)__releases(uhci->lock)__acquires(uhci->lock){	struct uhci_hcd *uhci = hcd_to_uhci(hcd);	uhci_destroy_urb_priv(uhci, urb);	spin_unlock(&uhci->lock);	usb_hcd_giveback_urb(hcd, urb, regs);	spin_lock(&uhci->lock);}static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs){	struct urb_priv *urbp, *tmp;	list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {		struct urb *urb = urbp->urb;		list_del_init(&urbp->urb_list);		uhci_finish_urb(uhci_to_hcd(uhci), urb, regs);	}}static void uhci_remove_pending_urbps(struct uhci_hcd *uhci){	/* Splice the urb_remove_list onto the end of the complete_list */	list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);}/* Process events in the schedule, but only in one thread at a time */static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs){	struct urb_priv *urbp, *tmp;	/* Don't allow re-entrant calls */	if (uhci->scan_in_progress) {		uhci->need_rescan = 1;		return;	}	uhci->scan_in_progress = 1; rescan:	uhci->need_rescan = 0;	uhci_clear_next_interrupt(uhci);	uhci_get_current_frame_number(uhci);	if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)		uhci_free_pending_qhs(uhci);	if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)		uhci_free_pending_tds(uhci);	if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age)		uhci_remove_pending_urbps(uhci);	/* Walk the list of pending URBs to see which ones completed	 * (must be _safe because uhci_transfer_result() dequeues URBs) */	list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {		struct urb *urb = urbp->urb;		/* Checks the status and does all of the magic necessary */		uhci_transfer_result(uhci, urb);	}	uhci_finish_completion(uhci, regs);	/* If the controller is stopped, we can finish these off right now */	if (uhci->is_stopped) {		uhci_free_pending_qhs(uhci);		uhci_free_pending_tds(uhci);		uhci_remove_pending_urbps(uhci);	}	if (uhci->need_rescan)		goto rescan;	uhci->scan_in_progress = 0;	if (list_empty(&uhci->urb_remove_list) &&	    list_empty(&uhci->td_remove_list) &&	    list_empty(&uhci->qh_remove_list))		uhci_clear_next_interrupt(uhci);	else		uhci_set_next_interrupt(uhci);	/* Wake up anyone waiting for an URB to complete */	wake_up_all(&uhci->waitqh);}static void check_fsbr(struct uhci_hcd *uhci){	struct urb_priv *up;	list_for_each_entry(up, &uhci->urb_list, urb_list) {		struct urb *u = up->urb;		spin_lock(&u->lock);		/* Check if the FSBR timed out */		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))			uhci_fsbr_timeout(uhci, u);		spin_unlock(&u->lock);	}	/* Really disable FSBR */	if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {		uhci->fsbrtimeout = 0;		uhci->skel_term_qh->link = UHCI_PTR_TERM;	}}

⌨️ 快捷键说明

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