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

📄 uhci.c

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

	/* Short circuit the virtual root hub */
	if (urb->dev == uhci->rh.dev) {
		ret = rh_submit_urb(urb);

		goto out;
	}

	switch (usb_pipetype(urb->pipe)) {
	case PIPE_CONTROL:
		ret = uhci_submit_control(urb);
		break;
	case PIPE_INTERRUPT:
		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
			bustime = usb_check_bandwidth(urb->dev, urb);
			if (bustime < 0)
				ret = bustime;
			else {
				ret = uhci_submit_interrupt(urb);
				if (ret == -EINPROGRESS)
					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
			}
		} else		/* bandwidth is already set */
			ret = uhci_submit_interrupt(urb);
		break;
	case PIPE_BULK:
		ret = uhci_submit_bulk(urb, eurb);
		break;
	case PIPE_ISOCHRONOUS:
		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
			if (urb->number_of_packets <= 0) {
				ret = -EINVAL;
				break;
			}
			bustime = usb_check_bandwidth(urb->dev, urb);
			if (bustime < 0) {
				ret = bustime;
				break;
			}

			ret = uhci_submit_isochronous(urb);
			if (ret == -EINPROGRESS)
				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
		} else		/* bandwidth is already set */
			ret = uhci_submit_isochronous(urb);
		break;
	}

out:
	urb->status = ret;

	if (ret == -EINPROGRESS) {
		/* We use _tail to make find_urb_ep more efficient */
		list_add_tail(&urb->urb_list, &uhci->urb_list);

		spin_unlock(&urb->lock);
		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

		return 0;
	}

	uhci_unlink_generic(uhci, urb);

	spin_unlock(&urb->lock);
	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

	/* Only call completion if it was successful */
	if (!ret)
		uhci_call_completion(urb);

	return ret;
}

/*
 * Return the result of a transfer
 *
 * MUST be called with urb_list_lock acquired
 */
static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
{
	int ret = -EINVAL;
	unsigned long flags;
	struct urb_priv *urbp;

	/* The root hub is special */
	if (urb->dev == uhci->rh.dev)
		return;

	spin_lock_irqsave(&urb->lock, flags);

	urbp = (struct urb_priv *)urb->hcpriv;

	if (urb->status != -EINPROGRESS) {
		info("uhci_transfer_result: called for URB %p not in flight?", urb);
		goto out;
	}

	switch (usb_pipetype(urb->pipe)) {
	case PIPE_CONTROL:
		ret = uhci_result_control(urb);
		break;
	case PIPE_INTERRUPT:
		ret = uhci_result_interrupt(urb);
		break;
	case PIPE_BULK:
		ret = uhci_result_bulk(urb);
		break;
	case PIPE_ISOCHRONOUS:
		ret = uhci_result_isochronous(urb);
		break;
	}

	urbp->status = ret;

	if (ret == -EINPROGRESS)
		goto out;

	switch (usb_pipetype(urb->pipe)) {
	case PIPE_CONTROL:
	case PIPE_BULK:
	case PIPE_ISOCHRONOUS:
		/* Release bandwidth for Interrupt or Isoc. transfers */
		/* Spinlock needed ? */
		if (urb->bandwidth)
			usb_release_bandwidth(urb->dev, urb, 1);
		uhci_unlink_generic(uhci, urb);
		break;
	case PIPE_INTERRUPT:
		/* Interrupts are an exception */
		if (urb->interval)
			goto out_complete;

		/* Release bandwidth for Interrupt or Isoc. transfers */
		/* Spinlock needed ? */
		if (urb->bandwidth)
			usb_release_bandwidth(urb->dev, urb, 0);
		uhci_unlink_generic(uhci, urb);
		break;
	default:
		info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
			usb_pipetype(urb->pipe), urb);
	}

	/* Remove it from uhci->urb_list */
	list_del_init(&urb->urb_list);

out_complete:
	uhci_add_complete(urb);

out:
	spin_unlock_irqrestore(&urb->lock, flags);
}

/*
 * MUST be called with urb->lock acquired
 */
static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
{
	struct list_head *head, *tmp;
	struct urb_priv *urbp = urb->hcpriv;
	int prevactive = 1;

	/* We can get called when urbp allocation fails, so check */
	if (!urbp)
		return;

	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */

	/*
	 * Now we need to find out what the last successful toggle was
	 * so we can update the local data toggle for the next transfer
	 *
	 * There's 3 way's the last successful completed TD is found:
	 *
	 * 1) The TD is NOT active and the actual length < expected length
	 * 2) The TD is NOT active and it's the last TD in the chain
	 * 3) The TD is active and the previous TD is NOT active
	 *
	 * Control and Isochronous ignore the toggle, so this is safe
	 * for all types
	 */
	head = &urbp->td_list;
	tmp = head->next;
	while (tmp != head) {
		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);

		tmp = tmp->next;

		if (!(td->status & TD_CTRL_ACTIVE) &&
		    (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
		    tmp == head))
			usb_settoggle(urb->dev, uhci_endpoint(td->info),
				uhci_packetout(td->info),
				uhci_toggle(td->info) ^ 1);
		else if ((td->status & TD_CTRL_ACTIVE) && !prevactive)
			usb_settoggle(urb->dev, uhci_endpoint(td->info),
				uhci_packetout(td->info),
				uhci_toggle(td->info));

		prevactive = td->status & TD_CTRL_ACTIVE;
	}

	uhci_delete_queued_urb(uhci, urb);

	/* The interrupt loop will reclaim the QH's */
	uhci_remove_qh(uhci, urbp->qh);
	urbp->qh = NULL;
}

static int uhci_unlink_urb(struct urb *urb)
{
	struct uhci *uhci;
	unsigned long flags;
	struct urb_priv *urbp = urb->hcpriv;

	if (!urb)
		return -EINVAL;

	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
		return -ENODEV;

	uhci = (struct uhci *)urb->dev->bus->hcpriv;

	spin_lock_irqsave(&uhci->urb_list_lock, flags);
	spin_lock(&urb->lock);

	/* Release bandwidth for Interrupt or Isoc. transfers */
	/* Spinlock needed ? */
	if (urb->bandwidth) {
		switch (usb_pipetype(urb->pipe)) {
		case PIPE_INTERRUPT:
			usb_release_bandwidth(urb->dev, urb, 0);
			break;
		case PIPE_ISOCHRONOUS:
			usb_release_bandwidth(urb->dev, urb, 1);
			break;
		default:
			break;
		}
	}

	if (urb->status != -EINPROGRESS) {
		spin_unlock(&urb->lock);
		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
		return 0;
	}

	list_del_init(&urb->urb_list);

	uhci_unlink_generic(uhci, urb);

	/* Short circuit the virtual root hub */
	if (urb->dev == uhci->rh.dev) {
		rh_unlink_urb(urb);

		spin_unlock(&urb->lock);
		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

		uhci_call_completion(urb);
	} else {
		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
			urbp->status = urb->status = -ECONNABORTED;

			spin_lock(&uhci->urb_remove_list_lock);

			/* If we're the first, set the next interrupt bit */
			if (list_empty(&uhci->urb_remove_list))
				uhci_set_next_interrupt(uhci);
			
			list_add(&urb->urb_list, &uhci->urb_remove_list);

			spin_unlock(&uhci->urb_remove_list_lock);

			spin_unlock(&urb->lock);
			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

		} else {
			urb->status = -ENOENT;

			spin_unlock(&urb->lock);
			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

			if (in_interrupt()) {	/* wait at least 1 frame */
				static int errorcount = 10;

				if (errorcount--)
					dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
				udelay(1000);
			} else
				schedule_timeout(1+1*HZ/1000); 

			uhci_call_completion(urb);
		}
	}

	return 0;
}

static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb)
{
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
	struct list_head *head, *tmp;
	int count = 0;

	uhci_dec_fsbr(uhci, urb);

	urbp->fsbr_timeout = 1;

	/*
	 * Ideally we would want to fix qh->element as well, but it's
	 * read/write by the HC, so that can introduce a race. It's not
	 * really worth the hassle
	 */

	head = &urbp->td_list;
	tmp = head->next;
	while (tmp != head) {
		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);

		tmp = tmp->next;

		/*
		 * Make sure we don't do the last one (since it'll have the
		 * TERM bit set) as well as we skip every so many TD's to
		 * make sure it doesn't hog the bandwidth
		 */
		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
			td->link |= UHCI_PTR_DEPTH;

		count++;
	}

	return 0;
}

/*
 * uhci_get_current_frame_number()
 *
 * returns the current frame number for a USB bus/controller.
 */
static int uhci_get_current_frame_number(struct usb_device *dev)
{
	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;

	return inw(uhci->io_addr + USBFRNUM);
}

struct usb_operations uhci_device_operations = {
	get_frame_number:	uhci_get_current_frame_number,
	submit_urb:		uhci_submit_urb,
	unlink_urb:		uhci_unlink_urb,
};

/* Virtual Root Hub */

static __u8 root_hub_dev_des[] =
{
 	0x12,			/*  __u8  bLength; */
	0x01,			/*  __u8  bDescriptorType; Device */
	0x00,			/*  __u16 bcdUSB; v1.0 */
	0x01,
	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
	0x00,			/*  __u8  bDeviceSubClass; */
	0x00,			/*  __u8  bDeviceProtocol; */
	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
	0x00,			/*  __u16 idVendor; */
	0x00,
	0x00,			/*  __u16 idProduct; */
	0x00,
	0x00,			/*  __u16 bcdDevice; */
	0x00,
	0x00,			/*  __u8  iManufacturer; */
	0x02,			/*  __u8  iProduct; */
	0x01,			/*  __u8  iSerialNumber; */
	0x01			/*  __u8  bNumConfigurations; */
};


/* Configuration descriptor */
static __u8 root_hub_config_des[] =
{
	0x09,			/*  __u8  bLength; */
	0x02,			/*  __u8  bDescriptorType; Configuration */
	0x19,			/*  __u16 wTotalLength; */
	0x00,
	0x01,			/*  __u8  bNumInterfaces; */
	0x01,			/*  __u8  bConfigurationValue; */
	0x00,			/*  __u8  iConfiguration; */
	0x40,			/*  __u8  bmAttributes;
					Bit 7: Bus-powered, 6: Self-powered,
					Bit 5 Remote-wakeup, 4..0: resvd */
	0x00,			/*  __u8  MaxPower; */

	/* interface */
	0x09,			/*  __u8  if_bLength; */
	0x04,			/*  __u8  if_bDescriptorType; Interface */
	0x00,			/*  __u8  if_bInterfaceNumber; */
	0x00,			/*  __u8  if_bAlternateSetting; */
	0x01,			/*  __u8  if_bNumEndpoints; */
	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
	0x00,			/*  __u8  if_bInterfaceSubClass; */
	0x00,			/*  __u8  if_bInterfaceProtocol; */
	0x00,			/*  __u8  if_iInterface; */

	/* endpoint */
	0x07,			/*  __u8  ep_bLength; */
	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
	0x08,			/*  __u16 ep_wMaxPacketSize; 8 Bytes */
	0x00,
	0xff			/*  __u8  ep_bInterval; 255 ms */
};

static __u8 root_hub_hub_des[] =
{
	0x09,			/*  __u8  bLength; */
	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
	0x02,			/*  __u8  bNbrPorts; */
	0x00,			/* __u16  wHubCharacteristics; */
	0x00,
	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
};

/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
static int rh_send_irq(struct urb *urb)
{
	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
	unsigned int io_addr = uhci->io_addr;
	unsigned long flags;
	int i, len = 1;
	__u16 data = 0;

	spin_lock_irqsave(&urb->lock, flags);
	for (i = 0; i < uhci->rh.numports; i++) {
		data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
		len = (i + 1) / 8 + 1;
	}

	*(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
	urb->actual_length = len;
	urbp->status = 0;

	spin_unlock_irqrestore(&urb->lock, flags);

	if ((data > 0) && (uhci->rh.send != 0)) {
		dbg("root-hub INT complete: port1: %x port2: %x data: %x",
			inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
		uhci_call_completion(urb);
	}

	return 0;
}

/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
static int rh_init_int_timer(struct urb *urb);

static void rh_int_timer_do(unsigned long ptr)
{
	struct urb *urb = (struct urb *)ptr;
	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
	struct list_head list, *tmp, *head;
	unsigned long flags;

	if (uhci->rh.send)
		rh_send_irq(urb);

	INIT_LIST_HEAD(&list);

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

		tmp = tmp->next;

		spin_lock(&u->lock);

		/* Check if the FSBR timed out */
		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
			uhci_fsbr_timeout(uhci, u);

		/* Check if the URB timed out */
		if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
			list_del(&u->urb_list);
			list_add_tail(&u->urb_list, &list);
		}

		spin_unlock(&u->lock);
	}
	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);

	head = &list;
	tmp = head->next;
	while (tmp != head) {
		struct urb *u = list_entry(tmp, struct urb, urb_list);

		tmp = tmp->next;

		u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
		uhci_unlink_urb(u);

⌨️ 快捷键说明

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