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

📄 uhci-hcd.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
	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_hcd *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_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 */
	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
}

#ifdef CONFIG_PROC_FS
static int uhci_num = 0;
#endif

/*
 * De-allocate all resources..
 */
static void release_uhci(struct uhci_hcd *uhci)
{
	int i;
#ifdef CONFIG_PROC_FS
	char buf[8];
#endif

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

	for (i = 0; i < UHCI_NUM_SKELTD; i++)
		if (uhci->skeltd[i]) {
			uhci_free_td(uhci, uhci->skeltd[i]);
			uhci->skeltd[i] = 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->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
		uhci->fl = NULL;
	}

#ifdef CONFIG_PROC_FS
	if (uhci->proc_entry) {
		sprintf(buf, "hc%d", uhci->num);

		remove_proc_entry(buf, 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);
	struct pci_dev *dev = hcd->pdev;
	int retval = -EBUSY;
	int i, port;
	dma_addr_t dma_handle;
#ifdef CONFIG_PROC_FS
	char buf[8];
	struct proc_dir_entry *ent;
#endif

	uhci->dev = dev;

	/* Should probably move to core/hcd.c */
	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
		err("couldn't set PCI dma mask");
		retval = -ENODEV;
		goto err_pci_set_dma_mask;
	}

	uhci->io_addr = pci_resource_start(dev, hcd->region);
	uhci->io_size = pci_resource_len(dev, hcd->region);

#ifdef CONFIG_PROC_FS
	uhci->num = uhci_num++;

	sprintf(buf, "hc%d", uhci->num);

	ent = create_proc_entry(buf, 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;

	uhci->is_suspended = 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(dev, 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", dev,
		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
	if (!uhci->td_pool) {
		err("unable to create td pci_pool");
		goto err_create_td_pool;
	}

	uhci->qh_pool = pci_pool_create("uhci_qh", dev,
		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
	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 < (uhci->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 = uhci->rh_dev = usb_alloc_dev(NULL, &hcd->self);
	if (!uhci->rh_dev) {
		err("unable to allocate root hub");
		goto err_alloc_root_hub;
	}

	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh_dev);
	if (!uhci->skeltd[0]) {
		err("unable to allocate TD 0");
		goto err_alloc_skeltd;
	}

	/*
	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
	 * then link int1 to control and control to bulk
	 */
	for (i = 1; i < 9; i++) {
		struct uhci_td *td;

		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh_dev);
		if (!td) {
			err("unable to allocate TD %d", i);
			goto err_alloc_skeltd;
		}

		uhci_fill_td(td, 0, uhci_explen(UHCI_NULL_DATA_SIZE) |
			(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
		td->link = cpu_to_le32(uhci->skeltd[i - 1]->dma_handle);
	}

	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh_dev);
	if (!uhci->skel_term_td) {
		err("unable to allocate skel TD term");
		goto err_alloc_skeltd;
	}

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

	uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) |
		(0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
	uhci->skel_int1_td->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_ls_control_qh->element = UHCI_PTR_TERM;

	uhci->skel_hs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;

	uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_bulk_qh->element = UHCI_PTR_TERM;

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

	uhci->skel_term_qh->link = UHCI_PTR_TERM;
	uhci->skel_term_qh->element = cpu_to_le32(uhci->skel_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->skeltd[irq]->dma_handle);
	}

	start_hc(uhci);

	init_stall_timer(hcd);

	/* disable legacy emulation */
	pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);

        hcd->state = USB_STATE_READY;

	usb_connect(uhci->rh_dev);
        uhci->rh_dev->speed = USB_SPEED_FULL;

	if (usb_register_root_hub(uhci->rh_dev, &dev->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(&uhci->stall_timer);

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

err_alloc_skelqh:
	for (i = 0; i < UHCI_NUM_SKELTD; i++)
		if (uhci->skeltd[i]) {
			uhci_free_td(uhci, uhci->skeltd[i]);
			uhci->skeltd[i] = NULL;
		}

err_alloc_skeltd:
	usb_free_dev(uhci->rh_dev);
	uhci->rh_dev = 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(dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
	uhci->fl = NULL;

err_alloc_fl:
#ifdef CONFIG_PROC_FS
	remove_proc_entry(buf, uhci_proc_root);
	uhci->proc_entry = NULL;

err_create_proc_entry:
#endif

err_pci_set_dma_mask:

	return retval;
}

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

	if (uhci->rh_dev)
		usb_disconnect(&uhci->rh_dev);

	del_timer(&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);

	suspend_hc(uhci);
	return 0;
}

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

	pci_set_master(uhci->dev);

	reset_hc(uhci);
	start_hc(uhci);
	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 const 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,
	free_config:		NULL,

	get_frame_number:	uhci_hcd_get_frame_number,

	hub_status_data:	uhci_hub_status_data,
	hub_control:		uhci_hub_control,
};

static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {

⌨️ 快捷键说明

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