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

📄 dummy_hcd.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
		if (ep)
			ep->already_seen = ep->setup_stage = 0;

		spin_unlock (&dum->lock);
		usb_hcd_giveback_urb (dummy_to_hcd(dum), urb, NULL);
		spin_lock (&dum->lock);

		goto restart;
	}

	if (list_empty (&dum->urbp_list)) {
		usb_put_dev (dum->udev);
		dum->udev = NULL;
	} else if (dum->rh_state == DUMMY_RH_RUNNING) {
		/* want a 1 msec delay here */
		mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
	}

	spin_unlock_irqrestore (&dum->lock, flags);
}

/*-------------------------------------------------------------------------*/

#define PORT_C_MASK \
	((USB_PORT_STAT_C_CONNECTION \
	| USB_PORT_STAT_C_ENABLE \
	| USB_PORT_STAT_C_SUSPEND \
	| USB_PORT_STAT_C_OVERCURRENT \
	| USB_PORT_STAT_C_RESET) << 16)

static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
{
	struct dummy		*dum;
	unsigned long		flags;
	int			retval = 0;

	dum = hcd_to_dummy (hcd);

	spin_lock_irqsave (&dum->lock, flags);
	if (hcd->state != HC_STATE_RUNNING)
		goto done;

	if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
		dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
		dum->port_status &= ~USB_PORT_STAT_SUSPEND;
		set_link_state (dum);
	}

	if ((dum->port_status & PORT_C_MASK) != 0) {
		*buf = (1 << 1);
		dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
				dum->port_status);
		retval = 1;
		if (dum->rh_state == DUMMY_RH_SUSPENDED)
			usb_hcd_resume_root_hub (hcd);
	}
done:
	spin_unlock_irqrestore (&dum->lock, flags);
	return retval;
}

static inline void
hub_descriptor (struct usb_hub_descriptor *desc)
{
	memset (desc, 0, sizeof *desc);
	desc->bDescriptorType = 0x29;
	desc->bDescLength = 9;
	desc->wHubCharacteristics = (__force __u16)
			(__constant_cpu_to_le16 (0x0001));
	desc->bNbrPorts = 1;
	desc->bitmap [0] = 0xff;
	desc->bitmap [1] = 0xff;
}

static int dummy_hub_control (
	struct usb_hcd	*hcd,
	u16		typeReq,
	u16		wValue,
	u16		wIndex,
	char		*buf,
	u16		wLength
) {
	struct dummy	*dum;
	int		retval = 0;
	unsigned long	flags;

	if (hcd->state != HC_STATE_RUNNING)
		return -ETIMEDOUT;

	dum = hcd_to_dummy (hcd);
	spin_lock_irqsave (&dum->lock, flags);
	switch (typeReq) {
	case ClearHubFeature:
		break;
	case ClearPortFeature:
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			if (dum->port_status & USB_PORT_STAT_SUSPEND) {
				/* 20msec resume signaling */
				dum->resuming = 1;
				dum->re_timeout = jiffies +
						msecs_to_jiffies(20);
			}
			break;
		case USB_PORT_FEAT_POWER:
			if (dum->port_status & USB_PORT_STAT_POWER)
				dev_dbg (dummy_dev(dum), "power-off\n");
			/* FALLS THROUGH */
		default:
			dum->port_status &= ~(1 << wValue);
			set_link_state (dum);
		}
		break;
	case GetHubDescriptor:
		hub_descriptor ((struct usb_hub_descriptor *) buf);
		break;
	case GetHubStatus:
		*(__le32 *) buf = __constant_cpu_to_le32 (0);
		break;
	case GetPortStatus:
		if (wIndex != 1)
			retval = -EPIPE;

		/* whoever resets or resumes must GetPortStatus to
		 * complete it!!
		 */
		if (dum->resuming &&
				time_after_eq (jiffies, dum->re_timeout)) {
			dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
			dum->port_status &= ~USB_PORT_STAT_SUSPEND;
		}
		if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
				time_after_eq (jiffies, dum->re_timeout)) {
			dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
			dum->port_status &= ~USB_PORT_STAT_RESET;
			if (dum->pullup) {
				dum->port_status |= USB_PORT_STAT_ENABLE;
				/* give it the best speed we agree on */
				dum->gadget.speed = dum->driver->speed;
				dum->gadget.ep0->maxpacket = 64;
				switch (dum->gadget.speed) {
				case USB_SPEED_HIGH:
					dum->port_status |=
						USB_PORT_STAT_HIGH_SPEED;
					break;
				case USB_SPEED_LOW:
					dum->gadget.ep0->maxpacket = 8;
					dum->port_status |=
						USB_PORT_STAT_LOW_SPEED;
					break;
				default:
					dum->gadget.speed = USB_SPEED_FULL;
					break;
				}
			}
		}
		set_link_state (dum);
		((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
		((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
		break;
	case SetHubFeature:
		retval = -EPIPE;
		break;
	case SetPortFeature:
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			if (dum->active) {
				dum->port_status |= USB_PORT_STAT_SUSPEND;

				/* HNP would happen here; for now we
				 * assume b_bus_req is always true.
				 */
				set_link_state (dum);
				if (((1 << USB_DEVICE_B_HNP_ENABLE)
						& dum->devstatus) != 0)
					dev_dbg (dummy_dev(dum),
							"no HNP yet!\n");
			}
			break;
		case USB_PORT_FEAT_POWER:
			dum->port_status |= USB_PORT_STAT_POWER;
			set_link_state (dum);
			break;
		case USB_PORT_FEAT_RESET:
			/* if it's already enabled, disable */
			dum->port_status &= ~(USB_PORT_STAT_ENABLE
					| USB_PORT_STAT_LOW_SPEED
					| USB_PORT_STAT_HIGH_SPEED);
			dum->devstatus = 0;
			/* 50msec reset signaling */
			dum->re_timeout = jiffies + msecs_to_jiffies(50);
			/* FALLS THROUGH */
		default:
			if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
				dum->port_status |= (1 << wValue);
				set_link_state (dum);
			}
		}
		break;

	default:
		dev_dbg (dummy_dev(dum),
			"hub control req%04x v%04x i%04x l%d\n",
			typeReq, wValue, wIndex, wLength);

		/* "protocol stall" on error */
		retval = -EPIPE;
	}
	spin_unlock_irqrestore (&dum->lock, flags);

	if ((dum->port_status & PORT_C_MASK) != 0)
		usb_hcd_poll_rh_status (hcd);
	return retval;
}

static int dummy_hub_suspend (struct usb_hcd *hcd)
{
	struct dummy *dum = hcd_to_dummy (hcd);

	spin_lock_irq (&dum->lock);
	dum->rh_state = DUMMY_RH_SUSPENDED;
	set_link_state (dum);
	spin_unlock_irq (&dum->lock);
	return 0;
}

static int dummy_hub_resume (struct usb_hcd *hcd)
{
	struct dummy *dum = hcd_to_dummy (hcd);

	spin_lock_irq (&dum->lock);
	dum->rh_state = DUMMY_RH_RUNNING;
	set_link_state (dum);
	if (!list_empty(&dum->urbp_list))
		mod_timer (&dum->timer, jiffies);
	spin_unlock_irq (&dum->lock);
	return 0;
}

/*-------------------------------------------------------------------------*/

static inline ssize_t
show_urb (char *buf, size_t size, struct urb *urb)
{
	int ep = usb_pipeendpoint (urb->pipe);

	return snprintf (buf, size,
		"urb/%p %s ep%d%s%s len %d/%d\n",
		urb,
		({ char *s;
		 switch (urb->dev->speed) {
		 case USB_SPEED_LOW:	s = "ls"; break;
		 case USB_SPEED_FULL:	s = "fs"; break;
		 case USB_SPEED_HIGH:	s = "hs"; break;
		 default:		s = "?"; break;
		 }; s; }),
		ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "",
		({ char *s; \
		 switch (usb_pipetype (urb->pipe)) { \
		 case PIPE_CONTROL:	s = ""; break; \
		 case PIPE_BULK:	s = "-bulk"; break; \
		 case PIPE_INTERRUPT:	s = "-int"; break; \
		 default: 		s = "-iso"; break; \
		}; s;}),
		urb->actual_length, urb->transfer_buffer_length);
}

static ssize_t
show_urbs (struct device *dev, struct device_attribute *attr, char *buf)
{
	struct usb_hcd		*hcd = dev_get_drvdata (dev);
	struct dummy		*dum = hcd_to_dummy (hcd);
	struct urbp		*urbp;
	size_t			size = 0;
	unsigned long		flags;

	spin_lock_irqsave (&dum->lock, flags);
	list_for_each_entry (urbp, &dum->urbp_list, urbp_list) {
		size_t		temp;

		temp = show_urb (buf, PAGE_SIZE - size, urbp->urb);
		buf += temp;
		size += temp;
	}
	spin_unlock_irqrestore (&dum->lock, flags);

	return size;
}
static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);

static int dummy_start (struct usb_hcd *hcd)
{
	struct dummy		*dum;

	dum = hcd_to_dummy (hcd);

	/*
	 * MASTER side init ... we emulate a root hub that'll only ever
	 * talk to one device (the slave side).  Also appears in sysfs,
	 * just like more familiar pci-based HCDs.
	 */
	spin_lock_init (&dum->lock);
	init_timer (&dum->timer);
	dum->timer.function = dummy_timer;
	dum->timer.data = (unsigned long) dum;
	dum->rh_state = DUMMY_RH_RUNNING;

	INIT_LIST_HEAD (&dum->urbp_list);

	/* only show a low-power port: just 8mA */
	hcd->power_budget = 8;
	hcd->state = HC_STATE_RUNNING;
	hcd->uses_new_polling = 1;

#ifdef CONFIG_USB_OTG
	hcd->self.otg_port = 1;
#endif

	/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
	device_create_file (dummy_dev(dum), &dev_attr_urbs);
	return 0;
}

static void dummy_stop (struct usb_hcd *hcd)
{
	struct dummy		*dum;

	dum = hcd_to_dummy (hcd);

	device_remove_file (dummy_dev(dum), &dev_attr_urbs);
	usb_gadget_unregister_driver (dum->driver);
	dev_info (dummy_dev(dum), "stopped\n");
}

/*-------------------------------------------------------------------------*/

static int dummy_h_get_frame (struct usb_hcd *hcd)
{
	return dummy_g_get_frame (NULL);
}

static const struct hc_driver dummy_hcd = {
	.description =		(char *) driver_name,
	.product_desc =		"Dummy host controller",
	.hcd_priv_size =	sizeof(struct dummy),

	.flags =		HCD_USB2,

	.start =		dummy_start,
	.stop =			dummy_stop,

	.urb_enqueue = 		dummy_urb_enqueue,
	.urb_dequeue = 		dummy_urb_dequeue,

	.get_frame_number = 	dummy_h_get_frame,

	.hub_status_data = 	dummy_hub_status,
	.hub_control = 		dummy_hub_control,
	.hub_suspend =		dummy_hub_suspend,
	.hub_resume =		dummy_hub_resume,
};

static int dummy_hcd_probe (struct device *dev)
{
	struct usb_hcd		*hcd;
	int			retval;

	dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);

	hcd = usb_create_hcd (&dummy_hcd, dev, dev->bus_id);
	if (!hcd)
		return -ENOMEM;
	the_controller = hcd_to_dummy (hcd);

	retval = usb_add_hcd(hcd, 0, 0);
	if (retval != 0) {
		usb_put_hcd (hcd);
		the_controller = NULL;
	}
	return retval;
}

static int dummy_hcd_remove (struct device *dev)
{
	struct usb_hcd		*hcd;

	hcd = dev_get_drvdata (dev);
	usb_remove_hcd (hcd);
	usb_put_hcd (hcd);
	the_controller = NULL;
	return 0;
}

static int dummy_hcd_suspend (struct device *dev, pm_message_t state,
		u32 level)
{
	struct usb_hcd		*hcd;

	if (level != SUSPEND_DISABLE)
		return 0;

	dev_dbg (dev, "%s\n", __FUNCTION__);
	hcd = dev_get_drvdata (dev);

#ifndef CONFIG_USB_SUSPEND
	/* Otherwise this would never happen */
	usb_lock_device (hcd->self.root_hub);
	dummy_hub_suspend (hcd);
	usb_unlock_device (hcd->self.root_hub);
#endif

	hcd->state = HC_STATE_SUSPENDED;
	return 0;
}

static int dummy_hcd_resume (struct device *dev, u32 level)
{
	struct usb_hcd		*hcd;

	if (level != RESUME_ENABLE)
		return 0;

	dev_dbg (dev, "%s\n", __FUNCTION__);
	hcd = dev_get_drvdata (dev);
	hcd->state = HC_STATE_RUNNING;

#ifndef CONFIG_USB_SUSPEND
	/* Otherwise this would never happen */
	usb_lock_device (hcd->self.root_hub);
	dummy_hub_resume (hcd);
	usb_unlock_device (hcd->self.root_hub);
#endif

	usb_hcd_poll_rh_status (hcd);
	return 0;
}

static struct device_driver dummy_hcd_driver = {
	.name		= (char *) driver_name,
	.bus		= &platform_bus_type,
	.probe		= dummy_hcd_probe,
	.remove		= dummy_hcd_remove,
	.suspend	= dummy_hcd_suspend,
	.resume		= dummy_hcd_resume,
};

/*-------------------------------------------------------------------------*/

/* These don't need to do anything because the pdev structures are
 * statically allocated. */
static void
dummy_udc_release (struct device *dev) {}

static void
dummy_hcd_release (struct device *dev) {}

static struct platform_device		the_udc_pdev = {
	.name		= (char *) gadget_name,
	.id		= -1,
	.dev		= {
		.release	= dummy_udc_release,
	},
};

static struct platform_device		the_hcd_pdev = {
	.name		= (char *) driver_name,
	.id		= -1,
	.dev		= {
		.release	= dummy_hcd_release,
	},
};

static int __init init (void)
{
	int	retval;

	if (usb_disabled ())
		return -ENODEV;

	retval = driver_register (&dummy_hcd_driver);
	if (retval < 0)
		return retval;

	retval = driver_register (&dummy_udc_driver);
	if (retval < 0)
		goto err_register_udc_driver;

	retval = platform_device_register (&the_hcd_pdev);
	if (retval < 0)
		goto err_register_hcd;

	retval = platform_device_register (&the_udc_pdev);
	if (retval < 0)
		goto err_register_udc;
	return retval;

err_register_udc:
	platform_device_unregister (&the_hcd_pdev);
err_register_hcd:
	driver_unregister (&dummy_udc_driver);
err_register_udc_driver:
	driver_unregister (&dummy_hcd_driver);
	return retval;
}
module_init (init);

static void __exit cleanup (void)
{
	platform_device_unregister (&the_udc_pdev);
	platform_device_unregister (&the_hcd_pdev);
	driver_unregister (&dummy_udc_driver);
	driver_unregister (&dummy_hcd_driver);
}
module_exit (cleanup);

⌨️ 快捷键说明

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