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

📄 uhci.c

📁 基于S3CEB2410平台LINUX操作系统下 USB驱动源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
			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;}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 urb_priv *)urb->hcpriv;	struct usb_device *dev = urb->dev;	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;	int is_ring = 0, killed, resubmit_interrupt, status;	struct urb *nurb;	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||			urb->status == -ECONNRESET);	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&			urb->interval && !killed);	nurb = urb->next;	if (nurb && !killed) {		int count = 0;		while (nurb && nurb != urb && count < MAX_URB_LOOP) {			if (nurb->status == -ENOENT ||			    nurb->status == -ECONNABORTED ||			    nurb->status == -ECONNRESET) {				killed = 1;				break;			}			nurb = nurb->next;			count++;		}		if (count == MAX_URB_LOOP)			err("uhci_call_completion: too many linked URB's, loop? (first loop)");		/* Check to see if chain is a ring */		is_ring = (nurb == urb);	}	status = urbp->status;	if (!resubmit_interrupt)		/* We don't need urb_priv anymore */		uhci_destroy_urb_priv(urb);	if (!killed)		urb->status = status;	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(devrequest), PCI_DMA_TODEVICE);	urb->dev = NULL;	if (urb->complete)		urb->complete(urb);	if (resubmit_interrupt) {		urb->dev = dev;		uhci_reset_interrupt(urb);	} else {		if (is_ring && !killed) {			urb->dev = dev;			uhci_submit_urb(urb);		} else {			/* We decrement the usage count after we're done */			/*  with everything */			usb_dec_dev_use(dev);		}	}}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;		tmp = tmp->next;		list_del_init(&urbp->complete_list);		uhci_call_completion(urb);	}	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_call_completion(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 a 64-byte max packet */	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);}#ifdef CONFIG_PROC_FSstatic int uhci_num = 0;#endifstatic void free_uhci(struct uhci *uhci){	kfree(uhci);}/* * De-allocate all resources.. */static void release_uhci(struct uhci *uhci){	int i;#ifdef CONFIG_PROC_FS	char buf[8];#endif	if (uhci->irq >= 0) {		free_irq(uhci->irq, uhci);		uhci->irq = -1;	}	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;	}	if (uhci->bus) {		usb_free_bus(uhci->bus);		uhci->bus = 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	free_uhci(uhci);}/* * 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 alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size){	struct uhci *uhci;	int retval;	char buf[8], *bufp = buf;	int i, port;	struct usb_bus *bus;	dma_addr_t dma_handle;#ifdef CONFIG_PROC_FS	struct proc_dir_entry *ent;#endif	retval = -ENODEV;	if (pci_enable_device(dev) < 0) {		err("couldn't enable PCI device");		goto err_enable_device;	}	if (!dev->irq) {		err("found UHCI device with no IRQ assigned. check BIOS settings!");		goto err_invalid_irq;	}	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {		err("PCI subsystem doesn't support 32 bit addressing?");		goto err_pci_dma_supported;	}	retval = -EBUSY;	if (!request_region(io_addr, io_size, "usb-uhci")) {		err("couldn't allocate I/O range %x - %x", io_addr,			io_addr + io_size - 1);		goto err_request_region;	}	pci_set_master(dev);#ifndef __sparc__	sprintf(buf, "%d", dev->irq);#else	bufp = __irq_itoa(dev->irq);#endif	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",		io_addr, bufp);	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {		err("couldn't set PCI dma mask");		retval = -ENODEV;		goto err_pci_set_dma_mask;	}	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);	if (!uhci) {		err("couldn't allocate uhci structure");		retval = -ENOMEM;		goto err_alloc_uhci;	}	uhci->dev = dev;	uhci->io_addr = io_addr;	uhci->io_size = io_size;	pci_set_drvdata(dev, uhci);#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);	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);	/* We need exactly one page (per UHCI specs), how convenient */	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */#if PAGE_SIZE < (4 * 1024)#error PAGE_SIZE is not atleast 4k#endif	uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);	if (!uhci->fl) {		err("unable to allocate consistent 

⌨️ 快捷键说明

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