int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musb *musb = hcd_to_musb(hcd); u32 temp; int retval = 0; unsigned long flags; spin_lock_irqsave(&musb->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, false); break; case USB_PORT_FEAT_POWER: if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) musb_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } DBG(5, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( 0x0001 /* per-port power switching */ | 0x0010 /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->DeviceRemovable[0] = 0x02; /* port 1 */ desc->DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: if (wIndex != 1) goto error; /* finish RESET signaling? */ if ((musb->port1_status & USB_PORT_STAT_RESET) && time_after_eq(jiffies, musb->rh_timer)) musb_port_reset(musb, false); /* finish RESUME signaling? */ if ((musb->port1_status & MUSB_PORT_STAT_RESUME) && time_after_eq(jiffies, musb->rh_timer)) { u8 power; power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; DBG(4, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); /* ISSUE: DaVinci (RTL 1.300) disconnects after * resume of high speed peripherals (but not full * speed ones). */ musb->is_active = 1; musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; usb_hcd_poll_rh_status(musb_to_hcd(musb)); /* NOTE: it might really be A_WAIT_BCON ... */ musb->xceiv->state = OTG_STATE_A_HOST; } put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n", musb->port1_status); break; case SetPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ if (!(is_otg_enabled(musb) && hcd->self.is_b_host)) musb_start(musb); break; case USB_PORT_FEAT_RESET: musb_port_reset(musb, true); break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, true); break; case USB_PORT_FEAT_TEST: wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSB_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSB_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_load_testpacket(musb); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; musb_writeb(musb->mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSB_TEST_FIFO_ACCESS; break; default: goto error; } musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; default: goto error; } DBG(5, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musb->lock, flags); return retval; }
int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musb *musb = hcd_to_musb(hcd); u32 temp; int retval = 0; unsigned long flags; spin_lock_irqsave(&musb->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, false); break; case USB_PORT_FEAT_POWER: if (!hcd->self.is_b_host) musb_platform_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } dev_dbg(musb->controller, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( 0x0001 /* per-port power switching */ | 0x0010 /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */ desc->u.hs.DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: if (wIndex != 1) goto error; put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ dev_dbg(musb->controller, "port status %08x\n", musb->port1_status); break; case SetPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ if (!hcd->self.is_b_host && musb_has_gadget(musb)) musb_start(musb); break; case USB_PORT_FEAT_RESET: musb_port_reset(musb, true); break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, true); break; case USB_PORT_FEAT_TEST: if (unlikely(is_host_active(musb))) goto error; wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSB_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSB_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_load_testpacket(musb); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; musb_writeb(musb->mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSB_TEST_FIFO_ACCESS; break; default: goto error; } musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; default: goto error; } dev_dbg(musb->controller, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musb->lock, flags); return retval; }
/* * root hub control */ static int musb_submit_rh_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *cmd) { int leni = transfer_len; int len = 0; int stat = 0; u32 datab[4]; const u8 *data_buf = (u8 *) datab; u16 bmRType_bReq; u16 wValue; u16 wIndex; u16 wLength; u16 int_usb; if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { debug("Root-Hub submit IRQ: NOT implemented\n"); return 0; } bmRType_bReq = cmd->requesttype | (cmd->request << 8); wValue = swap_16(cmd->value); wIndex = swap_16(cmd->index); wLength = swap_16(cmd->length); debug("--- HUB ----------------------------------------\n"); debug("submit rh urb, req=%x val=%#x index=%#x len=%d\n", bmRType_bReq, wValue, wIndex, wLength); debug("------------------------------------------------\n"); switch (bmRType_bReq) { case RH_GET_STATUS: debug("RH_GET_STATUS\n"); *(__u16 *) data_buf = swap_16(1); len = 2; break; case RH_GET_STATUS | RH_INTERFACE: debug("RH_GET_STATUS | RH_INTERFACE\n"); *(__u16 *) data_buf = swap_16(0); len = 2; break; case RH_GET_STATUS | RH_ENDPOINT: debug("RH_GET_STATUS | RH_ENDPOINT\n"); *(__u16 *) data_buf = swap_16(0); len = 2; break; case RH_GET_STATUS | RH_CLASS: debug("RH_GET_STATUS | RH_CLASS\n"); *(__u32 *) data_buf = swap_32(0); len = 4; break; case RH_GET_STATUS | RH_OTHER | RH_CLASS: debug("RH_GET_STATUS | RH_OTHER | RH_CLASS\n"); int_usb = readw(&musbr->intrusb); if (int_usb & MUSB_INTR_CONNECT) { port_status |= USB_PORT_STAT_CONNECTION | (USB_PORT_STAT_C_CONNECTION << 16); port_status |= USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_ENABLE; } if (port_status & USB_PORT_STAT_RESET) musb_port_reset(0); *(__u32 *) data_buf = swap_32(port_status); len = 4; break; case RH_CLEAR_FEATURE | RH_ENDPOINT: debug("RH_CLEAR_FEATURE | RH_ENDPOINT\n"); switch (wValue) { case RH_ENDPOINT_STALL: debug("C_HUB_ENDPOINT_STALL\n"); len = 0; break; } port_status &= ~(1 << wValue); break; case RH_CLEAR_FEATURE | RH_CLASS: debug("RH_CLEAR_FEATURE | RH_CLASS\n"); switch (wValue) { case RH_C_HUB_LOCAL_POWER: debug("C_HUB_LOCAL_POWER\n"); len = 0; break; case RH_C_HUB_OVER_CURRENT: debug("C_HUB_OVER_CURRENT\n"); len = 0; break; } port_status &= ~(1 << wValue); break; case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: debug("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS\n"); switch (wValue) { case RH_PORT_ENABLE: len = 0; break; case RH_PORT_SUSPEND: len = 0; break; case RH_PORT_POWER: len = 0; break; case RH_C_PORT_CONNECTION: len = 0; break; case RH_C_PORT_ENABLE: len = 0; break; case RH_C_PORT_SUSPEND: len = 0; break; case RH_C_PORT_OVER_CURRENT: len = 0; break; case RH_C_PORT_RESET: len = 0; break; default: debug("invalid wValue\n"); stat = USB_ST_STALLED; } port_status &= ~(1 << wValue); break; case RH_SET_FEATURE | RH_OTHER | RH_CLASS: debug("RH_SET_FEATURE | RH_OTHER | RH_CLASS\n"); switch (wValue) { case RH_PORT_SUSPEND: len = 0; break; case RH_PORT_RESET: musb_port_reset(1); len = 0; break; case RH_PORT_POWER: len = 0; break; case RH_PORT_ENABLE: len = 0; break; default: debug("invalid wValue\n"); stat = USB_ST_STALLED; } port_status |= 1 << wValue; break; case RH_SET_ADDRESS: debug("RH_SET_ADDRESS\n"); rh_devnum = wValue; len = 0; break; case RH_GET_DESCRIPTOR: debug("RH_GET_DESCRIPTOR: %x, %d\n", wValue, wLength); switch (wValue) { case (USB_DT_DEVICE << 8): /* device descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_dev_des), wLength)); data_buf = root_hub_dev_des; break; case (USB_DT_CONFIG << 8): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_config_des), wLength)); data_buf = root_hub_config_des; break; case ((USB_DT_STRING << 8) | 0x00): /* string 0 descriptors */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_str_index0), wLength)); data_buf = root_hub_str_index0; break; case ((USB_DT_STRING << 8) | 0x01): /* string 1 descriptors */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_str_index1), wLength)); data_buf = root_hub_str_index1; break; default: debug("invalid wValue\n"); stat = USB_ST_STALLED; } break; case RH_GET_DESCRIPTOR | RH_CLASS: { u8 *_data_buf = (u8 *) datab; debug("RH_GET_DESCRIPTOR | RH_CLASS\n"); _data_buf[0] = 0x09; /* min length; */ _data_buf[1] = 0x29; _data_buf[2] = 0x1; /* 1 port */ _data_buf[3] = 0x01; /* per-port power switching */ _data_buf[3] |= 0x10; /* no overcurrent reporting */ /* Corresponds to data_buf[4-7] */ _data_buf[4] = 0; _data_buf[5] = 5; _data_buf[6] = 0; _data_buf[7] = 0x02; _data_buf[8] = 0xff; len = min_t(unsigned int, leni, min_t(unsigned int, data_buf[0], wLength)); break; } case RH_GET_CONFIGURATION: debug("RH_GET_CONFIGURATION\n"); *(__u8 *) data_buf = 0x01; len = 1; break; case RH_SET_CONFIGURATION: debug("RH_SET_CONFIGURATION\n"); len = 0; break; default: debug("*** *** *** unsupported root hub command *** *** ***\n"); stat = USB_ST_STALLED; } len = min_t(int, len, leni); if (buffer != data_buf) memcpy(buffer, data_buf, len); dev->act_len = len; dev->status = stat; debug("dev act_len %d, status %d\n", dev->act_len, dev->status); return stat; }