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

📄 uhci.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
	}

	/* 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;
	}

	/* enter global suspend if nothing connected */
	if (!uhci->is_suspended && !ports_active(uhci))
		suspend_hc(uhci);

	rh_init_int_timer(urb);
}

/* Root Hub INTs are polled by this timer */
static int rh_init_int_timer(struct urb *urb)
{
	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;

	uhci->rh.interval = urb->interval;
	init_timer(&uhci->rh.rh_int_timer);
	uhci->rh.rh_int_timer.function = rh_int_timer_do;
	uhci->rh.rh_int_timer.data = (unsigned long)urb;
	uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
	add_timer(&uhci->rh.rh_int_timer);

	return 0;
}

#define OK(x)			len = (x); break

#define CLR_RH_PORTSTAT(x) \
	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
	status = (status & 0xfff5) & ~(x); \
	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))

#define SET_RH_PORTSTAT(x) \
	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
	status = (status & 0xfff5) | (x); \
	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))


/* Root Hub Control Pipe */
static int rh_submit_urb(struct urb *urb)
{
	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
	unsigned int pipe = urb->pipe;
	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
	void *data = urb->transfer_buffer;
	int leni = urb->transfer_buffer_length;
	int len = 0;
	int status = 0;
	int stat = 0;
	int i;
	unsigned int io_addr = uhci->io_addr;
	__u16 cstatus;
	__u16 bmRType_bReq;
	__u16 wValue;
	__u16 wIndex;
	__u16 wLength;

	if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
		uhci->rh.urb = urb;
		uhci->rh.send = 1;
		uhci->rh.interval = urb->interval;
		rh_init_int_timer(urb);

		return -EINPROGRESS;
	}

	bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8;
	wValue = le16_to_cpu(cmd->wValue);
	wIndex = le16_to_cpu(cmd->wIndex);
	wLength = le16_to_cpu(cmd->wLength);

	for (i = 0; i < 8; i++)
		uhci->rh.c_p_r[i] = 0;

	switch (bmRType_bReq) {
		/* Request Destination:
		   without flags: Device,
		   RH_INTERFACE: interface,
		   RH_ENDPOINT: endpoint,
		   RH_CLASS means HUB here,
		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
		*/

	case RH_GET_STATUS:
		*(__u16 *)data = cpu_to_le16(1);
		OK(2);
	case RH_GET_STATUS | RH_INTERFACE:
		*(__u16 *)data = cpu_to_le16(0);
		OK(2);
	case RH_GET_STATUS | RH_ENDPOINT:
		*(__u16 *)data = cpu_to_le16(0);
		OK(2);
	case RH_GET_STATUS | RH_CLASS:
		*(__u32 *)data = cpu_to_le32(0);
		OK(4);		/* hub power */
	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
		status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
			((status & USBPORTSC_PEC) >> (3 - 1)) |
			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
			status = (status & USBPORTSC_CCS) |
			((status & USBPORTSC_PE) >> (2 - 1)) |
			((status & USBPORTSC_SUSP) >> (12 - 2)) |
			((status & USBPORTSC_PR) >> (9 - 4)) |
			(1 << 8) |      /* power on */
			((status & USBPORTSC_LSDA) << (-8 + 9));

		*(__u16 *)data = cpu_to_le16(status);
		*(__u16 *)(data + 2) = cpu_to_le16(cstatus);
		OK(4);
	case RH_CLEAR_FEATURE | RH_ENDPOINT:
		switch (wValue) {
		case RH_ENDPOINT_STALL:
			OK(0);
		}
		break;
	case RH_CLEAR_FEATURE | RH_CLASS:
		switch (wValue) {
		case RH_C_HUB_OVER_CURRENT:
			OK(0);	/* hub power over current */
		}
		break;
	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
		switch (wValue) {
		case RH_PORT_ENABLE:
			CLR_RH_PORTSTAT(USBPORTSC_PE);
			OK(0);
		case RH_PORT_SUSPEND:
			CLR_RH_PORTSTAT(USBPORTSC_SUSP);
			OK(0);
		case RH_PORT_POWER:
			OK(0);	/* port power */
		case RH_C_PORT_CONNECTION:
			SET_RH_PORTSTAT(USBPORTSC_CSC);
			OK(0);
		case RH_C_PORT_ENABLE:
			SET_RH_PORTSTAT(USBPORTSC_PEC);
			OK(0);
		case RH_C_PORT_SUSPEND:
			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
			OK(0);
		case RH_C_PORT_OVER_CURRENT:
			OK(0);	/* port power over current */
		case RH_C_PORT_RESET:
			uhci->rh.c_p_r[wIndex - 1] = 0;
			OK(0);
		}
		break;
	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
		switch (wValue) {
		case RH_PORT_SUSPEND:
			SET_RH_PORTSTAT(USBPORTSC_SUSP);
			OK(0);
		case RH_PORT_RESET:
			SET_RH_PORTSTAT(USBPORTSC_PR);
			mdelay(50);	/* USB v1.1 7.1.7.3 */
			uhci->rh.c_p_r[wIndex - 1] = 1;
			CLR_RH_PORTSTAT(USBPORTSC_PR);
			udelay(10);
			SET_RH_PORTSTAT(USBPORTSC_PE);
			mdelay(10);
			SET_RH_PORTSTAT(0xa);
			OK(0);
		case RH_PORT_POWER:
			OK(0); /* port power ** */
		case RH_PORT_ENABLE:
			SET_RH_PORTSTAT(USBPORTSC_PE);
			OK(0);
		}
		break;
	case RH_SET_ADDRESS:
		uhci->rh.devnum = wValue;
		OK(0);
	case RH_GET_DESCRIPTOR:
		switch ((wValue & 0xff00) >> 8) {
		case 0x01:	/* device descriptor */
			len = min_t(unsigned int, leni,
				  min_t(unsigned int,
				      sizeof(root_hub_dev_des), wLength));
			memcpy(data, root_hub_dev_des, len);
			OK(len);
		case 0x02:	/* configuration descriptor */
			len = min_t(unsigned int, leni,
				  min_t(unsigned int,
				      sizeof(root_hub_config_des), wLength));
			memcpy (data, root_hub_config_des, len);
			OK(len);
		case 0x03:	/* string descriptors */
			len = usb_root_hub_string (wValue & 0xff,
				uhci->io_addr, "UHCI-alt",
				data, wLength);
			if (len > 0) {
				OK(min_t(int, leni, len));
			} else 
				stat = -EPIPE;
		}
		break;
	case RH_GET_DESCRIPTOR | RH_CLASS:
		root_hub_hub_des[2] = uhci->rh.numports;
		len = min_t(unsigned int, leni,
			  min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
		memcpy(data, root_hub_hub_des, len);
		OK(len);
	case RH_GET_CONFIGURATION:
		*(__u8 *)data = 0x01;
		OK(1);
	case RH_SET_CONFIGURATION:
		OK(0);
	case RH_GET_INTERFACE | RH_INTERFACE:
		*(__u8 *)data = 0x00;
		OK(1);
	case RH_SET_INTERFACE | RH_INTERFACE:
		OK(0);
	default:
		stat = -EPIPE;
	}

	urb->actual_length = len;

	return stat;
}

/*
 * MUST be called with urb->lock acquired
 */
static int rh_unlink_urb(struct urb *urb)
{
	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;

	if (uhci->rh.urb == urb) {
		urb->status = -ENOENT;
		uhci->rh.send = 0;
		uhci->rh.urb = NULL;
		del_timer(&uhci->rh.rh_int_timer);
	}
	return 0;
}

static void uhci_free_pending_qhs(struct uhci *uhci)
{
	struct list_head *tmp, *head;
	unsigned long flags;

	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
	head = &uhci->qh_remove_list;
	tmp = head->next;
	while (tmp != head) {
		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);

		tmp = tmp->next;

		list_del_init(&qh->remove_list);

		uhci_free_qh(uhci, qh);
	}
	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
}

static void uhci_call_completion(struct urb *urb)
{
	struct urb_priv *urbp;
	struct usb_device *dev = urb->dev;
	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
	int killed, resubmit_interrupt, status;
	unsigned long flags;

	spin_lock_irqsave(&urb->lock, flags);

	urbp = (struct urb_priv *)urb->hcpriv;
	if (!urbp || !urb->dev) {
		spin_unlock_irqrestore(&urb->lock, flags);
		return;
	}

	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
			urb->status == -ECONNRESET);
	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
			urb->interval);

	if (urbp->transfer_buffer_dma_handle)
		pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle,
			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);

	if (urbp->setup_packet_dma_handle)
		pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle,
			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);

	status = urbp->status;
	if (!resubmit_interrupt || killed)
		/* We don't need urb_priv anymore */
		uhci_destroy_urb_priv(urb);

	if (!killed)
		urb->status = status;

	urb->dev = NULL;
	spin_unlock_irqrestore(&urb->lock, flags);

	if (urb->complete)
		urb->complete(urb);

	if (resubmit_interrupt)
		/* Recheck the status. The completion handler may have */
		/*  unlinked the resubmitting interrupt URB */
		killed = (urb->status == -ENOENT ||
			  urb->status == -ECONNABORTED ||
			  urb->status == -ECONNRESET);

	if (resubmit_interrupt && !killed) {
		urb->dev = dev;
		uhci_reset_interrupt(urb);
	} else {
		/* We decrement the usage count after we're done */
		/*  with everything */
		usb_put_dev(dev);
		usb_put_urb(urb);
	}
}

static void uhci_finish_completion(struct uhci *uhci)
{
	struct list_head *tmp, *head;
	unsigned long flags;

	spin_lock_irqsave(&uhci->complete_list_lock, flags);
	head = &uhci->complete_list;
	tmp = head->next;
	while (tmp != head) {
		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
		struct urb *urb = urbp->urb;

		list_del_init(&urbp->complete_list);
		spin_unlock_irqrestore(&uhci->complete_list_lock, flags);

		uhci_call_completion(urb);

		spin_lock_irqsave(&uhci->complete_list_lock, flags);
		head = &uhci->complete_list;
		tmp = head->next;
	}
	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
}

static void uhci_remove_pending_qhs(struct uhci *uhci)
{
	struct list_head *tmp, *head;
	unsigned long flags;

	spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
	head = &uhci->urb_remove_list;
	tmp = head->next;
	while (tmp != head) {
		struct urb *urb = list_entry(tmp, struct urb, urb_list);
		struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;

		tmp = tmp->next;

		list_del_init(&urb->urb_list);

		urbp->status = urb->status = -ECONNRESET;

		uhci_add_complete(urb);
	}
	spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
}

static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
{
	struct uhci *uhci = __uhci;
	unsigned int io_addr = uhci->io_addr;
	unsigned short status;
	struct list_head *tmp, *head;

	/*
	 * Read the interrupt status, and write it back to clear the
	 * interrupt cause
	 */
	status = inw(io_addr + USBSTS);
	if (!status)	/* shared interrupt, not mine */
		return;
	outw(status, io_addr + USBSTS);		/* Clear it */

	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
		if (status & USBSTS_HSE)
			err("%x: host system error, PCI problems?", io_addr);
		if (status & USBSTS_HCPE)
			err("%x: host controller process error. something bad happened", io_addr);
		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
			err("%x: host controller halted. very bad", io_addr);
			/* FIXME: Reset the controller, fix the offending TD */
		}
	}

	if (status & USBSTS_RD)
		wakeup_hc(uhci);

	uhci_free_pending_qhs(uhci);

	uhci_remove_pending_qhs(uhci);

	uhci_clear_next_interrupt(uhci);

	/* Walk the list of pending URB's to see which ones completed */
	spin_lock(&uhci->urb_list_lock);
	head = &uhci->urb_list;
	tmp = head->next;
	while (tmp != head) {
		struct urb *urb = list_entry(tmp, struct urb, urb_list);

		tmp = tmp->next;

		/* Checks the status and does all of the magic necessary */
		uhci_transfer_result(uhci, urb);
	}
	spin_unlock(&uhci->urb_list_lock);

	uhci_finish_completion(uhci);
}

static void reset_hc(struct uhci *uhci)
{
	unsigned int io_addr = uhci->io_addr;

	/* Global reset for 50ms */
	outw(USBCMD_GRESET, io_addr + USBCMD);
	wait_ms(50);
	outw(0, io_addr + USBCMD);
	wait_ms(10);
}

static void suspend_hc(struct uhci *uhci)
{
	unsigned int io_addr = uhci->io_addr;

	dbg("%x: suspend_hc", io_addr);

	outw(USBCMD_EGSM, io_addr + USBCMD);

	uhci->is_suspended = 1;
}

static void wakeup_hc(struct uhci *uhci)
{
	unsigned int io_addr = uhci->io_addr;
	unsigned int status;

	dbg("%x: wakeup_hc", io_addr);

	outw(0, io_addr + USBCMD);
	
	/* wait for EOP to be sent */
	status = inw(io_addr + USBCMD);
	while (status & USBCMD_FGR)
		status = inw(io_addr + USBCMD);

	uhci->is_suspended = 0;

	/* Run and mark it configured with a 64-byte max packet */
	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
}

static int ports_active(struct uhci *uhci)
{
	unsigned int io_addr = uhci->io_addr;
	int connection = 0;
	int i;

	for (i = 0; i < uhci->rh.numports; i++)
		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);

	return connection;
}

static void start_hc(struct uhci *uhci)
{
	unsigned int io_addr = uhci->io_addr;
	int timeout = 1000;

	/*
	 * Reset the HC - this will force us to get a
	 * new notification of any already connected
	 * ports due to the virtual disconnect that it
	 * implies.
	 */
	outw(USBCMD_HCRESET, io_addr + USBCMD);
	while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
		if (!--timeout) {
			printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
			break;
		}
	}

	/* Turn on all interrupts */
	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
		io_addr + USBINTR);

	/* Start at frame 0 */
	outw(0, io_addr + USBFRNUM);
	outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);

	/* Run and mark it configured with 

⌨️ 快捷键说明

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