mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 14:00:19 +00:00 
			
		
		
		
	Avoid incorrect fall through: A USB_RT_HUB request should not be treated as USB_RT_PORT. Simplify the coding: Avoid duplicate debug() statements. This fixes all -Wimplicit-fallthrough warnings. Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com> Reviewed-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			323 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * (C) Copyright 2015 Google, Inc
 | 
						|
 * Written by Simon Glass <sjg@chromium.org>
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <log.h>
 | 
						|
#include <usb.h>
 | 
						|
#include <dm/device-internal.h>
 | 
						|
 | 
						|
/* We only support up to 8 */
 | 
						|
#define SANDBOX_NUM_PORTS	4
 | 
						|
 | 
						|
struct sandbox_hub_plat {
 | 
						|
	struct usb_dev_plat plat;
 | 
						|
	int port;	/* Port number (numbered from 0) */
 | 
						|
};
 | 
						|
 | 
						|
enum {
 | 
						|
	STRING_MANUFACTURER = 1,
 | 
						|
	STRING_PRODUCT,
 | 
						|
	STRING_SERIAL,
 | 
						|
 | 
						|
	STRING_count,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_string hub_strings[] = {
 | 
						|
	{STRING_MANUFACTURER,	"sandbox"},
 | 
						|
	{STRING_PRODUCT,	"hub"},
 | 
						|
	{STRING_SERIAL,		"2345"},
 | 
						|
	{},
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_device_descriptor hub_device_desc = {
 | 
						|
	.bLength =		sizeof(hub_device_desc),
 | 
						|
	.bDescriptorType =	USB_DT_DEVICE,
 | 
						|
 | 
						|
	.bcdUSB =		__constant_cpu_to_le16(0x0200),
 | 
						|
 | 
						|
	.bDeviceClass =		USB_CLASS_HUB,
 | 
						|
	.bDeviceSubClass =	0,
 | 
						|
	.bDeviceProtocol =	0,
 | 
						|
 | 
						|
	.idVendor =		__constant_cpu_to_le16(0x1234),
 | 
						|
	.idProduct =		__constant_cpu_to_le16(0x5678),
 | 
						|
	.iManufacturer =	STRING_MANUFACTURER,
 | 
						|
	.iProduct =		STRING_PRODUCT,
 | 
						|
	.iSerialNumber =	STRING_SERIAL,
 | 
						|
	.bNumConfigurations =	1,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_config_descriptor hub_config1 = {
 | 
						|
	.bLength		= sizeof(hub_config1),
 | 
						|
	.bDescriptorType	= USB_DT_CONFIG,
 | 
						|
 | 
						|
	/* wTotalLength is set up by usb-emul-uclass */
 | 
						|
	.bNumInterfaces		= 1,
 | 
						|
	.bConfigurationValue	= 0,
 | 
						|
	.iConfiguration		= 0,
 | 
						|
	.bmAttributes		= 1 << 7,
 | 
						|
	.bMaxPower		= 50,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_interface_descriptor hub_interface0 = {
 | 
						|
	.bLength		= sizeof(hub_interface0),
 | 
						|
	.bDescriptorType	= USB_DT_INTERFACE,
 | 
						|
 | 
						|
	.bInterfaceNumber	= 0,
 | 
						|
	.bAlternateSetting	= 0,
 | 
						|
	.bNumEndpoints		= 1,
 | 
						|
	.bInterfaceClass	= USB_CLASS_HUB,
 | 
						|
	.bInterfaceSubClass	= 0,
 | 
						|
	.bInterfaceProtocol	= US_PR_CB,
 | 
						|
	.iInterface		= 0,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_endpoint_descriptor hub_endpoint0_in = {
 | 
						|
	.bLength		= USB_DT_ENDPOINT_SIZE,
 | 
						|
	.bDescriptorType	= USB_DT_ENDPOINT,
 | 
						|
 | 
						|
	.bEndpointAddress	= 1 | USB_DIR_IN,
 | 
						|
	.bmAttributes		= USB_ENDPOINT_XFER_INT,
 | 
						|
	.wMaxPacketSize		= __constant_cpu_to_le16(1024),
 | 
						|
	.bInterval		= 0,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_hub_descriptor hub_desc = {
 | 
						|
	.bLength		= sizeof(hub_desc),
 | 
						|
	.bDescriptorType	= USB_DT_HUB,
 | 
						|
	.bNbrPorts		= SANDBOX_NUM_PORTS,
 | 
						|
	.wHubCharacteristics	= __constant_cpu_to_le16(1 << 0 | 1 << 3 |
 | 
						|
								1 << 7),
 | 
						|
	.bPwrOn2PwrGood		= 2,
 | 
						|
	.bHubContrCurrent	= 5,
 | 
						|
	{
 | 
						|
		{
 | 
						|
			/* all ports removeable */
 | 
						|
			.DeviceRemovable	= {0, 0xff}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#if SANDBOX_NUM_PORTS > 8
 | 
						|
#error "This code sets up an incorrect mask"
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
static void *hub_desc_list[] = {
 | 
						|
	&hub_device_desc,
 | 
						|
	&hub_config1,
 | 
						|
	&hub_interface0,
 | 
						|
	&hub_endpoint0_in,
 | 
						|
	&hub_desc,
 | 
						|
	NULL,
 | 
						|
};
 | 
						|
 | 
						|
struct sandbox_hub_priv {
 | 
						|
	int status[SANDBOX_NUM_PORTS];
 | 
						|
	int change[SANDBOX_NUM_PORTS];
 | 
						|
};
 | 
						|
 | 
						|
static struct udevice *hub_find_device(struct udevice *hub, int port,
 | 
						|
				       enum usb_device_speed *speed)
 | 
						|
{
 | 
						|
	struct udevice *dev;
 | 
						|
	struct usb_generic_descriptor **gen_desc;
 | 
						|
	struct usb_device_descriptor **dev_desc;
 | 
						|
 | 
						|
	for (device_find_first_child(hub, &dev);
 | 
						|
	     dev;
 | 
						|
	     device_find_next_child(&dev)) {
 | 
						|
		struct sandbox_hub_plat *plat;
 | 
						|
 | 
						|
		plat = dev_get_parent_plat(dev);
 | 
						|
		if (plat->port == port) {
 | 
						|
			gen_desc = plat->plat.desc_list;
 | 
						|
			gen_desc = usb_emul_find_descriptor(gen_desc,
 | 
						|
							    USB_DT_DEVICE, 0);
 | 
						|
			dev_desc = (struct usb_device_descriptor **)gen_desc;
 | 
						|
 | 
						|
			switch (le16_to_cpu((*dev_desc)->bcdUSB)) {
 | 
						|
			case 0x0100:
 | 
						|
				*speed = USB_SPEED_LOW;
 | 
						|
				break;
 | 
						|
			case 0x0101:
 | 
						|
				*speed = USB_SPEED_FULL;
 | 
						|
				break;
 | 
						|
			case 0x0200:
 | 
						|
			default:
 | 
						|
				*speed = USB_SPEED_HIGH;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			return dev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
 | 
						|
{
 | 
						|
	struct sandbox_hub_priv *priv = dev_get_priv(hub);
 | 
						|
	int *status = &priv->status[port];
 | 
						|
	int *change = &priv->change[port];
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if ((clear | set) & USB_PORT_STAT_POWER) {
 | 
						|
		enum usb_device_speed speed;
 | 
						|
		struct udevice *dev = hub_find_device(hub, port, &speed);
 | 
						|
 | 
						|
		if (dev) {
 | 
						|
			if (set & USB_PORT_STAT_POWER) {
 | 
						|
				ret = device_probe(dev);
 | 
						|
				debug("%s: %s: power on, probed, ret=%d\n",
 | 
						|
				      __func__, dev->name, ret);
 | 
						|
				if (!ret) {
 | 
						|
					set |= USB_PORT_STAT_CONNECTION |
 | 
						|
						USB_PORT_STAT_ENABLE;
 | 
						|
					if (speed == USB_SPEED_LOW)
 | 
						|
						set |= USB_PORT_STAT_LOW_SPEED;
 | 
						|
					else if (speed == USB_SPEED_HIGH)
 | 
						|
						set |= USB_PORT_STAT_HIGH_SPEED;
 | 
						|
				}
 | 
						|
 | 
						|
			} else if (clear & USB_PORT_STAT_POWER) {
 | 
						|
				debug("%s: %s: power off, removed, ret=%d\n",
 | 
						|
				      __func__, dev->name, ret);
 | 
						|
				ret = device_remove(dev, DM_REMOVE_NORMAL);
 | 
						|
				clear |= USB_PORT_STAT_CONNECTION;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*change |= *status & clear;
 | 
						|
	*change |= ~*status & set;
 | 
						|
	*change &= 0x1f;
 | 
						|
	*status = (*status & ~clear) | set;
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int sandbox_hub_submit_control_msg(struct udevice *bus,
 | 
						|
					  struct usb_device *udev,
 | 
						|
					  unsigned long pipe,
 | 
						|
					  void *buffer, int length,
 | 
						|
					  struct devrequest *setup)
 | 
						|
{
 | 
						|
	struct sandbox_hub_priv *priv = dev_get_priv(bus);
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (pipe == usb_rcvctrlpipe(udev, 0)) {
 | 
						|
		switch (setup->requesttype) {
 | 
						|
		case USB_RT_HUB | USB_DIR_IN:
 | 
						|
			switch (setup->request) {
 | 
						|
			case USB_REQ_GET_STATUS: {
 | 
						|
				struct usb_hub_status *hubsts = buffer;
 | 
						|
 | 
						|
				hubsts->wHubStatus = 0;
 | 
						|
				hubsts->wHubChange = 0;
 | 
						|
				udev->status = 0;
 | 
						|
				udev->act_len = sizeof(*hubsts);
 | 
						|
				return 0;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case USB_RT_PORT | USB_DIR_IN:
 | 
						|
			switch (setup->request) {
 | 
						|
			case USB_REQ_GET_STATUS: {
 | 
						|
				struct usb_port_status *portsts = buffer;
 | 
						|
				int port;
 | 
						|
 | 
						|
				port = (setup->index & USB_HUB_PORT_MASK) - 1;
 | 
						|
				portsts->wPortStatus = priv->status[port];
 | 
						|
				portsts->wPortChange = priv->change[port];
 | 
						|
				udev->status = 0;
 | 
						|
				udev->act_len = sizeof(*portsts);
 | 
						|
				return 0;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		debug("%s: rx ctl requesttype=%x, request=%x\n",
 | 
						|
		      __func__, setup->requesttype, setup->request);
 | 
						|
	} else if (pipe == usb_sndctrlpipe(udev, 0)) {
 | 
						|
		switch (setup->requesttype) {
 | 
						|
		case USB_RT_PORT:
 | 
						|
			switch (setup->request) {
 | 
						|
			case USB_REQ_SET_FEATURE: {
 | 
						|
				int port;
 | 
						|
 | 
						|
				port = (setup->index & USB_HUB_PORT_MASK) - 1;
 | 
						|
				debug("set feature port=%x, feature=%x\n",
 | 
						|
				      port, setup->value);
 | 
						|
				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
 | 
						|
					ret = clrset_post_state(bus, port, 0,
 | 
						|
							1 << setup->value);
 | 
						|
				} else {
 | 
						|
					debug("  ** Invalid feature\n");
 | 
						|
				}
 | 
						|
				return ret;
 | 
						|
			    }
 | 
						|
			case USB_REQ_CLEAR_FEATURE: {
 | 
						|
				int port;
 | 
						|
 | 
						|
				port = (setup->index & USB_HUB_PORT_MASK) - 1;
 | 
						|
				debug("clear feature port=%x, feature=%x\n",
 | 
						|
				      port, setup->value);
 | 
						|
				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
 | 
						|
					ret = clrset_post_state(bus, port,
 | 
						|
							1 << setup->value, 0);
 | 
						|
				} else {
 | 
						|
					priv->change[port] &= 1 <<
 | 
						|
						(setup->value - 16);
 | 
						|
				}
 | 
						|
				udev->status = 0;
 | 
						|
				return 0;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
		}
 | 
						|
		debug("%s: tx ctl requesttype=%x, request=%x\n",
 | 
						|
		      __func__, setup->requesttype, setup->request);
 | 
						|
	}
 | 
						|
	debug("pipe=%lx\n", pipe);
 | 
						|
 | 
						|
	return -EIO;
 | 
						|
}
 | 
						|
 | 
						|
static int sandbox_hub_bind(struct udevice *dev)
 | 
						|
{
 | 
						|
	return usb_emul_setup_device(dev, hub_strings, hub_desc_list);
 | 
						|
}
 | 
						|
 | 
						|
static int sandbox_child_post_bind(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct sandbox_hub_plat *plat = dev_get_parent_plat(dev);
 | 
						|
	struct usb_emul_plat *emul = dev_get_uclass_plat(dev);
 | 
						|
 | 
						|
	plat->port = dev_read_u32_default(dev, "reg", -1);
 | 
						|
	emul->port1 = plat->port + 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dm_usb_ops sandbox_usb_hub_ops = {
 | 
						|
	.control	= sandbox_hub_submit_control_msg,
 | 
						|
};
 | 
						|
 | 
						|
static const struct udevice_id sandbox_usb_hub_ids[] = {
 | 
						|
	{ .compatible = "sandbox,usb-hub" },
 | 
						|
	{ }
 | 
						|
};
 | 
						|
 | 
						|
U_BOOT_DRIVER(usb_sandbox_hub) = {
 | 
						|
	.name	= "usb_sandbox_hub",
 | 
						|
	.id	= UCLASS_USB_EMUL,
 | 
						|
	.of_match = sandbox_usb_hub_ids,
 | 
						|
	.bind	= sandbox_hub_bind,
 | 
						|
	.ops	= &sandbox_usb_hub_ops,
 | 
						|
	.priv_auto	= sizeof(struct sandbox_hub_priv),
 | 
						|
	.per_child_plat_auto	= sizeof(struct sandbox_hub_plat),
 | 
						|
	.child_post_bind = sandbox_child_post_bind,
 | 
						|
};
 |