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

📄 uhci-hcd.c

📁 ReactOs中的USB驱动
💻 C
📖 第 1 页 / 共 5 页
字号:

	if (!uhci->hcd.pdev ||
	     uhci->hcd.pdev->vendor != PCI_VENDOR_ID_INTEL ||
	     uhci->hcd.pdev->device != PCI_DEVICE_ID_INTEL_82371AB_2)
		return 1;

	/* This is a 82371AB/EB/MB USB controller which has a bug that
	 * causes false resume indications if any port has an
	 * over current condition.  To prevent problems, we will not
	 * allow a global suspend if any ports are OC.
	 *
	 * Some motherboards using the 82371AB/EB/MB (but not the USB portion)
	 * appear to hardwire the over current inputs active to disable
	 * the USB ports.
	 */

	/* check for over current condition on any port */
	for (i = 0; i < uhci->rh_numports; i++) {
		if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
			return 0;
	}

	return 1;
}

static void hc_state_transitions(struct uhci_hcd *uhci)
{
	switch (uhci->state) {
		case UHCI_RUNNING:

			/* global suspend if nothing connected for 1 second */
			if (!ports_active(uhci) && suspend_allowed(uhci)) {
				uhci->state = UHCI_SUSPENDING_GRACE;
				uhci->state_end = jiffies + HZ;
			}
			break;

		case UHCI_SUSPENDING_GRACE:
			if (ports_active(uhci))
				uhci->state = UHCI_RUNNING;
			else if (time_after_eq(jiffies, uhci->state_end))
				suspend_hc(uhci);
			break;

		case UHCI_SUSPENDED:

			/* wakeup if requested by a device */
			if (uhci->resume_detect)
				wakeup_hc(uhci);
			break;

		case UHCI_RESUMING_1:
		case UHCI_RESUMING_2:
		case UHCI_RUNNING_GRACE:
			if (time_after_eq(jiffies, uhci->state_end))
				wakeup_hc(uhci);
			break;

		default:
			break;
	}
}

static void start_hc(struct uhci_hcd *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 a 64-byte max packet */
	uhci->state = UHCI_RUNNING_GRACE;
	uhci->state_end = jiffies + HZ;
	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);

        uhci->hcd.state = USB_STATE_READY;
}

/*
 * De-allocate all resources..
 */
static void release_uhci(struct uhci_hcd *uhci)
{
	int i;

	for (i = 0; i < UHCI_NUM_SKELQH; i++)
		if (uhci->skelqh[i]) {
			uhci_free_qh(uhci, uhci->skelqh[i]);
			uhci->skelqh[i] = NULL;
		}

	if (uhci->term_td) {
		uhci_free_td(uhci, uhci->term_td);
		uhci->term_td = NULL;
	}

	if (uhci->qh_pool) {
		pci_pool_destroy(uhci->qh_pool);
		uhci->qh_pool = NULL;
	}

	if (uhci->td_pool) {
		pci_pool_destroy(uhci->td_pool);
		uhci->td_pool = NULL;
	}

	if (uhci->fl) {
		pci_free_consistent(uhci->hcd.pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
		uhci->fl = NULL;
	}

#ifdef CONFIG_PROC_FS
	if (uhci->proc_entry) {
		remove_proc_entry(uhci->hcd.self.bus_name, uhci_proc_root);
		uhci->proc_entry = NULL;
	}
#endif
}

/*
 * Allocate a frame list, and then setup the skeleton
 *
 * The hardware doesn't really know any difference
 * in the queues, but the order does matter for the
 * protocols higher up. The order is:
 *
 *  - any isochronous events handled before any
 *    of the queues. We don't do that here, because
 *    we'll create the actual TD entries on demand.
 *  - The first queue is the interrupt queue.
 *  - The second queue is the control queue, split into low and high speed
 *  - The third queue is bulk queue.
 *  - The fourth queue is the bandwidth reclamation queue, which loops back
 *    to the high speed control queue.
 */
static int __devinit uhci_start(struct usb_hcd *hcd)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
	int retval = -EBUSY;
	int i, port;
	unsigned io_size;
	dma_addr_t dma_handle;
	struct usb_device *udev;
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry *ent;
#endif

	uhci->io_addr = (unsigned long) hcd->regs;
	io_size = pci_resource_len(hcd->pdev, hcd->region);

#ifdef CONFIG_PROC_FS
	ent = create_proc_entry(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
	if (!ent) {
		err("couldn't create uhci proc entry");
		retval = -ENOMEM;
		goto err_create_proc_entry;
	}

	ent->data = uhci;
	ent->proc_fops = &uhci_proc_operations;
	ent->size = 0;
	uhci->proc_entry = ent;
#endif

	/* Reset here so we don't get any interrupts from an old setup */
	/*  or broken setup */
	reset_hc(uhci);

	uhci->fsbr = 0;
	uhci->fsbrtimeout = 0;

	spin_lock_init(&uhci->qh_remove_list_lock);
	INIT_LIST_HEAD(&uhci->qh_remove_list);

	spin_lock_init(&uhci->urb_remove_list_lock);
	INIT_LIST_HEAD(&uhci->urb_remove_list);

	spin_lock_init(&uhci->urb_list_lock);
	INIT_LIST_HEAD(&uhci->urb_list);

	spin_lock_init(&uhci->complete_list_lock);
	INIT_LIST_HEAD(&uhci->complete_list);

	spin_lock_init(&uhci->frame_list_lock);

	uhci->fl = pci_alloc_consistent(hcd->pdev, sizeof(*uhci->fl), &dma_handle);
	if (!uhci->fl) {
		err("unable to allocate consistent memory for frame list");
		goto err_alloc_fl;
	}

	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));

	uhci->fl->dma_handle = dma_handle;

	uhci->td_pool = pci_pool_create("uhci_td", hcd->pdev,
		sizeof(struct uhci_td), 16, 0);
	if (!uhci->td_pool) {
		err("unable to create td pci_pool");
		goto err_create_td_pool;
	}

	uhci->qh_pool = pci_pool_create("uhci_qh", hcd->pdev,
		sizeof(struct uhci_qh), 16, 0);
	if (!uhci->qh_pool) {
		err("unable to create qh pci_pool");
		goto err_create_qh_pool;
	}

	/* Initialize the root hub */

	/* UHCI specs says devices must have 2 ports, but goes on to say */
	/*  they may have more but give no way to determine how many they */
	/*  have. However, according to the UHCI spec, Bit 7 is always set */
	/*  to 1. So we try to use this to our advantage */
	for (port = 0; port < (io_size - 0x10) / 2; port++) {
		unsigned int portstatus;

		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
		if (!(portstatus & 0x0080))
			break;
	}
	if (debug)
		info("detected %d ports", port);

	/* This is experimental so anything less than 2 or greater than 8 is */
	/*  something weird and we'll ignore it */
	if (port < 2 || port > 8) {
		info("port count misdetected? forcing to 2 ports");
		port = 2;
	}

	uhci->rh_numports = port;

	hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self);
	if (!udev) {
		err("unable to allocate root hub");
		goto err_alloc_root_hub;
	}
	hcd->pdev->bus = (struct pci_bus  *)udev; /* Fix bus pointer for initial device */

	uhci->term_td = uhci_alloc_td(uhci, udev);
	if (!uhci->term_td) {
		err("unable to allocate terminating TD");
		goto err_alloc_term_td;
	}

	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
		uhci->skelqh[i] = uhci_alloc_qh(uhci, udev);
		if (!uhci->skelqh[i]) {
			err("unable to allocate QH %d", i);
			goto err_alloc_skelqh;
		}
	}

	/*
	 * 8 Interrupt queues; link int2 to int1, int4 to int2, etc
	 * then link int1 to control and control to bulk
	 */
	uhci->skel_int128_qh->link = cpu_to_le32(uhci->skel_int64_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int64_qh->link = cpu_to_le32(uhci->skel_int32_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int32_qh->link = cpu_to_le32(uhci->skel_int16_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int16_qh->link = cpu_to_le32(uhci->skel_int8_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int8_qh->link = cpu_to_le32(uhci->skel_int4_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int4_qh->link = cpu_to_le32(uhci->skel_int2_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int2_qh->link = cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;

	uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;

	/* This dummy TD is to work around a bug in Intel PIIX controllers */
	uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
		(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
	uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle);

	uhci->skel_term_qh->link = UHCI_PTR_TERM;
	uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle);

	/*
	 * Fill the frame list: make all entries point to
	 * the proper interrupt queue.
	 *
	 * This is probably silly, but it's a simple way to
	 * scatter the interrupt queues in a way that gives
	 * us a reasonable dynamic range for irq latencies.
	 */
	for (i = 0; i < UHCI_NUMFRAMES; i++) {
		int irq = 0;

		if (i & 1) {
			irq++;
			if (i & 2) {
				irq++;
				if (i & 4) {
					irq++;
					if (i & 8) {
						irq++;
						if (i & 16) {
							irq++;
							if (i & 32) {
								irq++;
								if (i & 64)
									irq++;
							}
						}
					}
				}
			}
		}

		/* Only place we don't use the frame list routines */
		uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[7 - irq]->dma_handle);
	}

	start_hc(uhci);

	init_stall_timer(hcd);

	/* disable legacy emulation */
	pci_write_config_word(hcd->pdev, USBLEGSUP, USBLEGSUP_DEFAULT);

	usb_connect(udev);
	udev->speed = USB_SPEED_FULL;

	if (usb_register_root_hub(udev, &hcd->pdev->dev) != 0) {
		err("unable to start root hub");
		retval = -ENOMEM;
		goto err_start_root_hub;
	}

	return 0;

/*
 * error exits:
 */
err_start_root_hub:
	reset_hc(uhci);

	del_timer_sync(&uhci->stall_timer);

err_alloc_skelqh:
	for (i = 0; i < UHCI_NUM_SKELQH; i++)
		if (uhci->skelqh[i]) {
			uhci_free_qh(uhci, uhci->skelqh[i]);
			uhci->skelqh[i] = NULL;
		}

	uhci_free_td(uhci, uhci->term_td);
	uhci->term_td = NULL;

err_alloc_term_td:
	usb_put_dev(udev);
	hcd->self.root_hub = NULL;

err_alloc_root_hub:
	pci_pool_destroy(uhci->qh_pool);
	uhci->qh_pool = NULL;

err_create_qh_pool:
	pci_pool_destroy(uhci->td_pool);
	uhci->td_pool = NULL;

err_create_td_pool:
	pci_free_consistent(hcd->pdev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
	uhci->fl = NULL;

err_alloc_fl:
#ifdef CONFIG_PROC_FS
	remove_proc_entry(hcd->self.bus_name, uhci_proc_root);
	uhci->proc_entry = NULL;

err_create_proc_entry:
#endif

	return retval;
}

static void uhci_stop(struct usb_hcd *hcd)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);

	del_timer_sync(&uhci->stall_timer);

	/*
	 * At this point, we're guaranteed that no new connects can be made
	 * to this bus since there are no more parents
	 */
	uhci_free_pending_qhs(uhci);
	uhci_remove_pending_qhs(uhci);

	reset_hc(uhci);

	uhci_free_pending_qhs(uhci);

	release_uhci(uhci);
}

#ifdef CONFIG_PM
static int uhci_suspend(struct usb_hcd *hcd, u32 state)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);

	/* Don't try to suspend broken motherboards, reset instead */
	if (suspend_allowed(uhci))
		suspend_hc(uhci);
	else
		reset_hc(uhci);
	return 0;
}

static int uhci_resume(struct usb_hcd *hcd)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);

	pci_set_master(uhci->hcd.pdev);

	if (uhci->state == UHCI_SUSPENDED)
		uhci->resume_detect = 1;
	else {
		reset_hc(uhci);
		start_hc(uhci);
	}
	uhci->hcd.state = USB_STATE_READY;
	return 0;
}
#endif

static struct usb_hcd *uhci_hcd_alloc(void)
{
	struct uhci_hcd *uhci;

	uhci = (struct uhci_hcd *)kmalloc(sizeof(*uhci), GFP_KERNEL);
	if (!uhci)
		return NULL;

	memset(uhci, 0, sizeof(*uhci));
	return &uhci->hcd;
}

static void uhci_hcd_free(struct usb_hcd *hcd)
{
	kfree(hcd_to_uhci(hcd));
}

static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
	return uhci_get_current_frame_number(hcd_to_uhci(hcd));
}

static const char hcd_name[] = "uhci-hcd";

static struct hc_driver uhci_driver = {
	.description =		hcd_name,

	/* Generic hardware linkage */
	.irq =			uhci_irq,
	.flags =		HCD_USB11,

	/* Basic lifecycle operations */
	.start =		uhci_start,
#ifdef CONFIG_PM
	.suspend =		uhci_suspend,
	.resume =		uhci_resume,
#endif
	.stop =			uhci_stop,

	.hcd_alloc =		uhci_hcd_alloc,
	.hcd_free =		uhci_hcd_free,

	.urb_enqueue =		uhci_urb_enqueue,
	.urb_dequeue =		uhci_urb_dequeue,

	.get_frame_number =	uhci_hcd_get_frame_number,

	.hub_status_data =	uhci_hub_status_data,
	.hub_control =		uhci_hub_control,
};

const struct pci_device_id __devinitdata uhci_pci_ids[] = { {

	/* handle any USB UHCI controller */
	.class = 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
	.cl

⌨️ 快捷键说明

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