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

📄 sl811-hcd.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 4 页
字号:
		while (*prev && ((temp = *prev) != ep))			prev = &temp->next;		if (*prev)			*prev = ep->next;		sl811->load[i] -= ep->load;	}	ep->branch = PERIODIC_SIZE;	sl811->periodic_count--;	sl811_to_hcd(sl811)->self.bandwidth_allocated		-= ep->load / ep->period;	if (ep == sl811->next_periodic)		sl811->next_periodic = ep->next;	/* we might turn SOFs back on again for the async schedule */	if (sl811->periodic_count == 0)		sofirq_off(sl811);}static voiddone(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank){	u8			status;	struct urb		*urb;	int			urbstat = -EINPROGRESS;	if (unlikely(!ep))		return;	status = sl811_read(sl811, bank + SL11H_PKTSTATREG);	urb = container_of(ep->hep->urb_list.next, struct urb, urb_list);	/* we can safely ignore NAKs */	if (status & SL11H_STATMASK_NAK) {		// PACKET("...NAK_%02x qh%p\n", bank, ep);		if (!ep->period)			ep->nak_count++;		ep->error_count = 0;	/* ACK advances transfer, toggle, and maybe queue */	} else if (status & SL11H_STATMASK_ACK) {		struct usb_device	*udev = urb->dev;		int			len;		unsigned char		*buf;		/* urb->iso_frame_desc is currently ignored here... */		ep->nak_count = ep->error_count = 0;		switch (ep->nextpid) {		case USB_PID_OUT:			// PACKET("...ACK/out_%02x qh%p\n", bank, ep);			urb->actual_length += ep->length;			usb_dotoggle(udev, ep->epnum, 1);			if (urb->actual_length					== urb->transfer_buffer_length) {				if (usb_pipecontrol(urb->pipe))					ep->nextpid = USB_PID_ACK;				/* some bulk protocols terminate OUT transfers				 * by a short packet, using ZLPs not padding.				 */				else if (ep->length < ep->maxpacket						|| !(urb->transfer_flags							& URB_ZERO_PACKET))					urbstat = 0;			}			break;		case USB_PID_IN:			// PACKET("...ACK/in_%02x qh%p\n", bank, ep);			buf = urb->transfer_buffer + urb->actual_length;			prefetchw(buf);			len = ep->maxpacket - sl811_read(sl811,						bank + SL11H_XFERCNTREG);			if (len > ep->length) {				len = ep->length;				urb->status = -EOVERFLOW;			}			urb->actual_length += len;			sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),					buf, len);			usb_dotoggle(udev, ep->epnum, 0);			if (urb->actual_length == urb->transfer_buffer_length)				urbstat = 0;			else if (len < ep->maxpacket) {				if (urb->transfer_flags & URB_SHORT_NOT_OK)					urbstat = -EREMOTEIO;				else					urbstat = 0;			}			if (usb_pipecontrol(urb->pipe)					&& (urbstat == -EREMOTEIO						|| urbstat == 0)) {				/* NOTE if the status stage STALLs (why?),				 * this reports the wrong urb status.				 */				spin_lock(&urb->lock);				if (urb->status == -EINPROGRESS)					urb->status = urbstat;				spin_unlock(&urb->lock);				urb = NULL;				ep->nextpid = USB_PID_ACK;			}			break;		case USB_PID_SETUP:			// PACKET("...ACK/setup_%02x qh%p\n", bank, ep);			if (urb->transfer_buffer_length == urb->actual_length)				ep->nextpid = USB_PID_ACK;			else if (usb_pipeout(urb->pipe)) {				usb_settoggle(udev, 0, 1, 1);				ep->nextpid = USB_PID_OUT;			} else {				usb_settoggle(udev, 0, 0, 1);				ep->nextpid = USB_PID_IN;			}			break;		case USB_PID_ACK:			// PACKET("...ACK/status_%02x qh%p\n", bank, ep);			urbstat = 0;			break;		}	/* STALL stops all transfers */	} else if (status & SL11H_STATMASK_STALL) {		PACKET("...STALL_%02x qh%p\n", bank, ep);		ep->nak_count = ep->error_count = 0;		urbstat = -EPIPE;	/* error? retry, until "3 strikes" */	} else if (++ep->error_count >= 3) {		if (status & SL11H_STATMASK_TMOUT)			urbstat = -ETIME;		else if (status & SL11H_STATMASK_OVF)			urbstat = -EOVERFLOW;		else			urbstat = -EPROTO;		ep->error_count = 0;		PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",				bank, status, ep, urbstat);	}	if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))		finish_request(sl811, ep, urb, urbstat);}static inline u8 checkdone(struct sl811 *sl811){	u8	ctl;	u8	irqstat = 0;	if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {		ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));		if (ctl & SL11H_HCTLMASK_ARM)			sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);		DBG("%s DONE_A: ctrl %02x sts %02x\n",			(ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",			ctl,			sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));		irqstat |= SL11H_INTMASK_DONE_A;	}#ifdef	USE_B	if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {		ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));		if (ctl & SL11H_HCTLMASK_ARM)			sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);		DBG("%s DONE_B: ctrl %02x sts %02x\n",			(ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",			ctl,			sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));		irqstat |= SL11H_INTMASK_DONE_A;	}#endif	return irqstat;}static irqreturn_t sl811h_irq(struct usb_hcd *hcd){	struct sl811	*sl811 = hcd_to_sl811(hcd);	u8		irqstat;	irqreturn_t	ret = IRQ_NONE;	unsigned	retries = 5;	spin_lock(&sl811->lock);retry:	irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;	if (irqstat) {		sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);		irqstat &= sl811->irq_enable;	}#ifdef	QUIRK2	/* this may no longer be necessary ... */	if (irqstat == 0) {		irqstat = checkdone(sl811);		if (irqstat)			sl811->stat_lost++;	}#endif	/* USB packets, not necessarily handled in the order they're	 * issued ... that's fine if they're different endpoints.	 */	if (irqstat & SL11H_INTMASK_DONE_A) {		done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF));		sl811->active_a = NULL;		sl811->stat_a++;	}#ifdef USE_B	if (irqstat & SL11H_INTMASK_DONE_B) {		done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF));		sl811->active_b = NULL;		sl811->stat_b++;	}#endif	if (irqstat & SL11H_INTMASK_SOFINTR) {		unsigned index;		index = sl811->frame++ % (PERIODIC_SIZE - 1);		sl811->stat_sof++;		/* be graceful about almost-inevitable periodic schedule		 * overruns:  continue the previous frame's transfers iff		 * this one has nothing scheduled.		 */		if (sl811->next_periodic) {			// ERR("overrun to slot %d\n", index);			sl811->stat_overrun++;		}		if (sl811->periodic[index])			sl811->next_periodic = sl811->periodic[index];	}	/* khubd manages debouncing and wakeup */	if (irqstat & SL11H_INTMASK_INSRMV) {		sl811->stat_insrmv++;		/* most stats are reset for each VBUS session */		sl811->stat_wake = 0;		sl811->stat_sof = 0;		sl811->stat_a = 0;		sl811->stat_b = 0;		sl811->stat_lost = 0;		sl811->ctrl1 = 0;		sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);		sl811->irq_enable = SL11H_INTMASK_INSRMV;		sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);		/* usbcore nukes other pending transactions on disconnect */		if (sl811->active_a) {			sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);			finish_request(sl811, sl811->active_a,				container_of(sl811->active_a						->hep->urb_list.next,					struct urb, urb_list),				-ESHUTDOWN);			sl811->active_a = NULL;		}#ifdef	USE_B		if (sl811->active_b) {			sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);			finish_request(sl811, sl811->active_b,				container_of(sl811->active_b						->hep->urb_list.next,					struct urb, urb_list),				NULL, -ESHUTDOWN);			sl811->active_b = NULL;		}#endif		/* port status seems weird until after reset, so		 * force the reset and make khubd clean up later.		 */		sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)				| (1 << USB_PORT_FEAT_CONNECTION);	} else if (irqstat & SL11H_INTMASK_RD) {		if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {			DBG("wakeup\n");			sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;			sl811->stat_wake++;		} else			irqstat &= ~SL11H_INTMASK_RD;	}	if (irqstat) {		if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))			start_transfer(sl811);		ret = IRQ_HANDLED;		if (retries--)			goto retry;	}	if (sl811->periodic_count == 0 && list_empty(&sl811->async))		sofirq_off(sl811);	sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);	spin_unlock(&sl811->lock);	return ret;}/*-------------------------------------------------------------------------*//* usb 1.1 says max 90% of a frame is available for periodic transfers. * this driver doesn't promise that much since it's got to handle an * IRQ per packet; irq handling latencies also use up that time. * * NOTE:  the periodic schedule is a sparse tree, with the load for * each branch minimized.  see fig 3.5 in the OHCI spec for example. */#define	MAX_PERIODIC_LOAD	500	/* out of 1000 usec */static int balance(struct sl811 *sl811, u16 period, u16 load){	int	i, branch = -ENOSPC;	/* search for the least loaded schedule branch of that period	 * which has enough bandwidth left unreserved.	 */	for (i = 0; i < period ; i++) {		if (branch < 0 || sl811->load[branch] > sl811->load[i]) {			int	j;			for (j = i; j < PERIODIC_SIZE; j += period) {				if ((sl811->load[j] + load)						> MAX_PERIODIC_LOAD)					break;			}			if (j < PERIODIC_SIZE)				continue;			branch = i;		}	}	return branch;}/*-------------------------------------------------------------------------*/static int sl811h_urb_enqueue(	struct usb_hcd		*hcd,	struct usb_host_endpoint *hep,	struct urb		*urb,	gfp_t			mem_flags) {	struct sl811		*sl811 = hcd_to_sl811(hcd);	struct usb_device	*udev = urb->dev;	unsigned int		pipe = urb->pipe;	int			is_out = !usb_pipein(pipe);	int			type = usb_pipetype(pipe);	int			epnum = usb_pipeendpoint(pipe);	struct sl811h_ep	*ep = NULL;	unsigned long		flags;	int			i;	int			retval = 0;#ifdef	DISABLE_ISO	if (type == PIPE_ISOCHRONOUS)		return -ENOSPC;#endif	/* avoid all allocations within spinlocks */	if (!hep->hcpriv)		ep = kzalloc(sizeof *ep, mem_flags);	spin_lock_irqsave(&sl811->lock, flags);	/* don't submit to a dead or disabled port */	if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))			|| !HC_IS_RUNNING(hcd->state)) {		retval = -ENODEV;		kfree(ep);		goto fail;	}	if (hep->hcpriv) {		kfree(ep);		ep = hep->hcpriv;	} else if (!ep) {		retval = -ENOMEM;		goto fail;	} else {		INIT_LIST_HEAD(&ep->schedule);		ep->udev = udev;		ep->epnum = epnum;		ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);		ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;		usb_settoggle(udev, epnum, is_out, 0);		if (type == PIPE_CONTROL)			ep->nextpid = USB_PID_SETUP;		else if (is_out)			ep->nextpid = USB_PID_OUT;		else			ep->nextpid = USB_PID_IN;		if (ep->maxpacket > H_MAXPACKET) {			/* iso packets up to 240 bytes could work... */			DBG("dev %d ep%d maxpacket %d\n",				udev->devnum, epnum, ep->maxpacket);			retval = -EINVAL;			goto fail;		}		if (udev->speed == USB_SPEED_LOW) {			/* send preamble for external hub? */			if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))				ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;		}		switch (type) {		case PIPE_ISOCHRONOUS:		case PIPE_INTERRUPT:			if (urb->interval > PERIODIC_SIZE)				urb->interval = PERIODIC_SIZE;			ep->period = urb->interval;			ep->branch = PERIODIC_SIZE;			if (type == PIPE_ISOCHRONOUS)				ep->defctrl |= SL11H_HCTLMASK_ISOCH;			ep->load = usb_calc_bus_time(udev->speed, !is_out,				(type == PIPE_ISOCHRONOUS),				usb_maxpacket(udev, pipe, is_out))					/ 1000;			break;		}		ep->hep = hep;		hep->hcpriv = ep;	}	/* maybe put endpoint into schedule */	switch (type) {	case PIPE_CONTROL:	case PIPE_BULK:		if (list_empty(&ep->schedule))			list_add_tail(&ep->schedule, &sl811->async);		break;	case PIPE_ISOCHRONOUS:	case PIPE_INTERRUPT:		urb->interval = ep->period;		if (ep->branch < PERIODIC_SIZE) {			/* NOTE:  the phase is correct here, but the value			 * needs offsetting by the transfer queue depth.			 * All current drivers ignore start_frame, so this			 * is unlikely to ever matter...			 */			urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))						+ ep->branch;			break;		}		retval = balance(sl811, ep->period, ep->load);		if (retval < 0)			goto fail;		ep->branch = retval;		retval = 0;		urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))					+ ep->branch;		/* sort each schedule branch by period (slow before fast)		 * to share the faster parts of the tree without needing

⌨️ 快捷键说明

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