/* * Copyright (c) LOTUS. All rights reserved. */ #include "usb3.h" #include "usb3_hw.h" #include "usb3_drv.h" #include "usb3_prot.h" #include "usb3_pcd.h" #include #include #include #define BUFFER_SIZE 512 extern void usb3_bulk_out_transfer(void *dev); const struct usb_interface_descriptor interface = { sizeof(struct usb_interface_descriptor), /* b_length */ UDESC_INTERFACE, /* b_descriptor_type */ 0, /* b_interface_number */ 0, /* b_alternate_setting */ 2, /* b_num_endpoints */ USB_CLASS_VENDOR_SPEC, /* b_interface_class */ USB_SC_VENDOR_SPEC, /* b_interface_subclass */ USB_PR_VENDOR_SPEC, /* b_interface_protocol */ 0, /* i_interface */ }; /* Two endpoint descriptors: bulk-in, bulk-out. */ const struct usb_ss_ep_comp_descriptor ep_comp = { sizeof(struct usb_ss_ep_comp_descriptor), /* b_length */ UDESC_SS_USB_COMPANION, /* b_descriptor_type */ 0, /* b_max_burst */ 0, /* bm_attributes */ 0, /* w_bytes_per_interval */ }; const struct usb_endpoint_descriptor hs_bulk_in = { sizeof(struct usb_endpoint_descriptor), /* b_length */ UDESC_ENDPOINT, /* b_descriptor_type */ UE_DIR_IN | USB3_BULK_IN_EP, /* b_endpoint_address */ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ 0x200, /* w_max_packet_size: 512 of high-speed */ 0, /* b_interval */ }; const struct usb_endpoint_descriptor hs_bulk_out = { sizeof(struct usb_endpoint_descriptor), /* b_length */ UDESC_ENDPOINT, /* b_descriptor_type */ UE_DIR_OUT | USB3_BULK_OUT_EP, /* b_endpoint_address */ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ 0x200, /* w_max_packet_size: 512 of high-speed */ 1, /* b_interval */ }; const struct usb_endpoint_descriptor ss_bulk_in = { sizeof(struct usb_endpoint_descriptor), /* b_length */ UDESC_ENDPOINT, /* b_descriptor_type */ UE_DIR_IN | USB3_BULK_IN_EP, /* b_endpoint_address */ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ 0x400, /* w_max_packet_size: 1024 of super-speed */ 0, /* b_interval */ }; const struct usb_endpoint_descriptor ss_bulk_out = { sizeof(struct usb_endpoint_descriptor), /* b_length */ UDESC_ENDPOINT, /* b_descriptor_type */ UE_DIR_OUT | USB3_BULK_OUT_EP, /* b_endpoint_address */ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ 0x400, /* w_max_packet_size: 1024 of super-speed */ 0, /* b_interval */ }; /* * The BOS Descriptor */ const struct usb_dev_cap_20_ext_desc capability1 = { sizeof(struct usb_dev_cap_20_ext_desc), /* b_length */ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ USB_DEVICE_CAPABILITY_20_EXTENSION, /* b_devcapability_type */ 0x2, /* bm_attributes */ }; const struct usb_dev_cap_ss_usb capability2 = { sizeof(struct usb_dev_cap_ss_usb), /* b_length */ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ USB_DEVICE_CAPABILITY_SS_USB, /* b_devcapability_type */ 0x0, /* bm_attributes */ (USB_DC_SS_USB_SPEED_SUPPORT_SS | USB_DC_SS_USB_SPEED_SUPPORT_HIGH), /* w_speeds_supported */ 0x2, /* b_functionality_support */ /* @todo set these to correct value */ 0xa, /* b_u1dev_exit_lat */ 0x100, /* w_u2dev_exit_lat */ }; const struct usb_dev_cap_container_id capability3 = { sizeof(struct usb_dev_cap_container_id), /* b_length */ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ USB_DEVICE_CAPABILITY_CONTAINER_ID, /* b_devcapability_type */ 0, /* b_reserved */ /* @todo Create UUID */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* container_id */ }; const struct wusb_bos_desc usb_bos = { sizeof(struct wusb_bos_desc), /* b_length */ UDESC_BOS, /* b_descriptor_type */ (sizeof(struct wusb_bos_desc) /* w_total_length */ + sizeof(capability1) + sizeof(capability2) + sizeof(capability3)), 3, /* b_num_device_caps */ }; usb3_pcd_ep_t *usb3_get_out_ep(usb3_pcd_t *pcd, unsigned int ep_num) { if (ep_num == 0) { return &pcd->ep0; } else { return &pcd->out_ep; } } usb3_pcd_ep_t *usb3_get_in_ep(usb3_pcd_t *pcd, unsigned int ep_num) { if (ep_num == 0) { return &pcd->ep0; } else { return &pcd->in_ep; } } usb3_pcd_ep_t *usb3_get_ep_by_addr(usb3_pcd_t *pcd, unsigned short index) { unsigned int ep_num = ue_get_addr(index); if (ue_get_dir(index) == UE_DIR_IN) { return usb3_get_in_ep(pcd, ep_num); } else { return usb3_get_out_ep(pcd, ep_num); } } void usb3_ep_clear_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) { usb3_dev_ep_regs_t *ep_reg; if (ep->is_in) { ep_reg = ep->in_ep_reg; } else { ep_reg = ep->out_ep_reg; } usb3_dep_cstall(pcd, ep_reg); } unsigned int usb3_get_device_speed(usb3_pcd_t *pcd) { unsigned int dsts; unsigned int speed = USB_SPEED_UNKNOWN; dsts = usb3_rd32(&pcd->dev_global_regs->dsts); switch ((dsts >> USB3_DSTS_CONNSPD_SHIFT) & (USB3_DSTS_CONNSPD_BITS >> USB3_DSTS_CONNSPD_SHIFT)) { case USB3_SPEED_HS_PHY_30MHZ_OR_60MHZ: speed = USB_SPEED_HIGH; break; case USB3_SPEED_FS_PHY_30MHZ_OR_60MHZ: case USB3_SPEED_FS_PHY_48MHZ: speed = USB_SPEED_FULL; break; case USB3_SPEED_LS_PHY_6MHZ: speed = USB_SPEED_LOW; break; case USB3_SPEED_SS_PHY_125MHZ_OR_250MHZ: speed = USB_SPEED_SUPER; break; default: break; } return speed; } void usb3_pcd_set_speed(usb3_pcd_t *pcd, int speed) { /* Set the MPS of EP0 based on the connection speed */ switch (speed) { case USB_SPEED_SUPER: pcd->ep0.maxpacket = 512; /* 512: 512bytes */ pcd->in_ep.maxpacket = USB3_MAX_PACKET_SIZE; pcd->out_ep.maxpacket = USB3_MAX_PACKET_SIZE; break; case USB_SPEED_HIGH: pcd->ep0.maxpacket = 64; /* 64: 64bytes */ pcd->in_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE; pcd->out_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE; break; case USB_SPEED_FULL: pcd->ep0.maxpacket = 64; /* 64: 64bytes */ pcd->in_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE; pcd->out_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE; break; case USB_SPEED_LOW: pcd->ep0.maxpacket = 8; /* 8: 8bytes */ pcd->in_ep.maxpacket = 0; pcd->out_ep.maxpacket = 0; break; default: break; } } void usb3_ep_set_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) { usb3_dev_ep_regs_t *ep_reg; if (ep->is_in) { ep_reg = ep->in_ep_reg; } else { ep_reg = ep->out_ep_reg; } usb3_dep_sstall(pcd, ep_reg); } static void ep0_do_stall(usb3_pcd_t *pcd) { usb3_pcd_ep_t *ep0 = &pcd->ep0; /* Stall EP0 IN & OUT simultanelusly */ ep0->is_in = 1; usb3_ep_set_stall(pcd, ep0); ep0->is_in = 0; usb3_ep_set_stall(pcd, ep0); /* Prepare for the next setup transfer */ ep0->stopped = 1; pcd->ep0state = EP0_IDLE; usb3_ep0_out_start(pcd); } static void do_clear_halt(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) { usb3_ep_clear_stall(pcd, ep); if (ep->stopped) ep->stopped = 0; } void usb3_pcd_ep_enable(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) { /* * Activate the EP */ ep->stopped = 0; ep->xfer_started = 0; /* Set initial data PID. */ if (ep->type == USB3_EP_TYPE_BULK) ep->data_pid_start = 0; usb3_ep_activate(pcd, ep); } void usb3_do_status_ut_device(usb3_pcd_t *pcd) { if (pcd == NULL) return; unsigned char *status = pcd->ep0_status_buf; if (pcd->speed != USB_SPEED_SUPER) { *(status + 1) = 0; return; } if (pcd->state == USB3_STATE_CONFIGURED) { if (usb3_u1_enabled(pcd)) *status |= 1 << 2; /* left shift 2 bit */ if (usb3_u2_enabled(pcd)) *status |= 1 << 3; /* left shift 3 bit */ *status |= pcd->ltm_enable << 4; /* left shift 4 bit */ } *(status + 1) = 0; } static void usb3_do_get_status(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; unsigned char *status = pcd->ep0_status_buf; usb3_pcd_ep_t *ep; if (ctrl.w_length != 0x2) { ep0_do_stall(pcd); return; } switch (ut_get_recipient(ctrl.bm_request_type)) { case UT_DEVICE: usb3_do_status_ut_device(pcd); break; case UT_INTERFACE: *status = 0; *(status + 1) = 0; break; case UT_ENDPOINT: ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); /* @todo check for EP stall */ *status = ep->stopped; *(status + 1) = 0; break; default: ep0_do_stall(pcd); return; } pcd->ep0_req.bufdma = status; pcd->ep0_req.length = 0x2; pcd->ep0_req.actual = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); } static void usb3_do_clear_feature(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; usb3_pcd_ep_t *ep; switch (ut_get_recipient(ctrl.bm_request_type)) { case UT_DEVICE: switch (ctrl.w_value) { case UF_DEVICE_REMOTE_WAKEUP: break; case UF_TEST_MODE: /* @todo Add CLEAR_FEATURE for TEST modes. */ break; case UF_U1_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ep0_do_stall(pcd); return; } usb3_disable_u1(pcd); break; case UF_U2_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ep0_do_stall(pcd); return; } usb3_disable_u2(pcd); break; case UF_LTM_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED || ctrl.w_index != 0) { ep0_do_stall(pcd); return; } pcd->ltm_enable = 0; break; default: ep0_do_stall(pcd); return; } break; case UT_INTERFACE: /* if FUNCTION_SUSPEND ... */ if (ctrl.w_value) { ep0_do_stall(pcd); return; } break; case UT_ENDPOINT: ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); if (ctrl.w_value != UF_ENDPOINT_HALT) { ep0_do_stall(pcd); return; } do_clear_halt(pcd, ep); break; default: break; } pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; } static void usb3_do_set_feature(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; usb3_pcd_ep_t *ep; switch (ut_get_recipient(ctrl.bm_request_type)) { case UT_DEVICE: switch (ctrl.w_value) { case UF_DEVICE_REMOTE_WAKEUP: break; case UF_TEST_MODE: break; case UF_DEVICE_B_HNP_ENABLE: break; case UOTG_NTF_HOST_REL: break; case UOTG_B3_RSP_ENABLE: break; case UF_DEVICE_A_HNP_SUPPORT: /* RH port supports HNP */ break; case UF_DEVICE_A_ALT_HNP_SUPPORT: /* other RH port does */ break; case UF_U1_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ep0_do_stall(pcd); return; } usb3_enable_u1(pcd); break; case UF_U2_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ep0_do_stall(pcd); return; } usb3_enable_u2(pcd); break; case UF_LTM_ENABLE: if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED || ctrl.w_index != 0) { ep0_do_stall(pcd); return; } pcd->ltm_enable = 1; break; default: ep0_do_stall(pcd); return; } break; case UT_INTERFACE: /* if FUNCTION_SUSPEND ... */ if (ctrl.w_value) { ep0_do_stall(pcd); return; } break; case UT_ENDPOINT: ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); if (ctrl.w_value != UF_ENDPOINT_HALT) { ep0_do_stall(pcd); return; } ep->stopped = 1; usb3_ep_set_stall(pcd, ep); break; default: break; } pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; } static void usb3_do_set_address(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; if (ctrl.bm_request_type == UT_DEVICE) { usb3_set_address(pcd, ctrl.w_value); pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; if (ctrl.w_value) { pcd->state = USB3_STATE_ADDRESSED; } else { pcd->state = USB3_STATE_DEFAULT; } } } static void usb3_do_set_config(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; unsigned short wvalue = ctrl.w_value; usb3_pcd_ep_t *ep; if (ctrl.bm_request_type != (UT_WRITE | UT_STANDARD | UT_DEVICE)) { ep0_do_stall(pcd); return; } if (!wvalue || (wvalue == CONFIG_VALUE)) { pcd->new_config = wvalue; /* Set new configuration */ if (wvalue) { /* Activate bulk in endpoint */ ep = &pcd->in_ep; usb3_pcd_ep_enable(pcd, ep); /* Activate bulk out endpoint */ ep = &pcd->out_ep; usb3_pcd_ep_enable(pcd, ep); /* Prepare for next bulk transfer */ usb3_bulk_out_transfer((void *)pcd); usb3_bulk_in_transfer((void *)pcd, "start download process."); pcd->state = USB3_STATE_CONFIGURED; } else { pcd->state = USB3_STATE_ADDRESSED; } pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; } else { ep0_do_stall(pcd); } } static void usb3_do_get_config(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; unsigned char *status = pcd->ep0_status_buf; if (ctrl.bm_request_type != (UT_READ | UT_STANDARD | UT_DEVICE)) { ep0_do_stall(pcd); return; } /* Notify host the current config value */ *status = pcd->new_config; pcd->ep0_req.bufdma = status; pcd->ep0_req.length = 1; pcd->ep0_req.actual = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); } static void usb3_do_get_descriptor(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; usb3_device_t *usb3_dev = pcd->usb3_dev; unsigned char dt = ctrl.w_value >> 8; /* 8bit */ unsigned char index = (unsigned char)ctrl.w_value; unsigned short len = ctrl.w_length; unsigned char *buf = pcd->ep0_status_buf; unsigned short value = 0; if (ctrl.bm_request_type != (UT_READ | UT_STANDARD | UT_DEVICE)) { ep0_do_stall(pcd); return; } switch (dt) { case UDESC_DEVICE: { struct usb_device_descriptor *dev = (struct usb_device_descriptor *)usb3_dev->dev_desc; dev->b_length = sizeof(struct usb_device_descriptor); dev->b_descriptor_type = UDESC_DEVICE; dev->b_device_class = 0; dev->b_device_sub_class = 0; dev->b_device_protocol = 0; if (pcd->speed == USB_SPEED_SUPER) { dev->bcd_usb = 0x300; dev->b_max_packet_size0 = 9; // NOTE! 2 ^ 9 = 512 for USB3 } else if (pcd->speed == USB_SPEED_HIGH) { dev->bcd_usb = 0x0200; dev->b_max_packet_size0 = pcd->ep0.maxpacket; } else { dev->bcd_usb = 0x0110; dev->b_max_packet_size0 = pcd->ep0.maxpacket; } dev->id_vendor = 0x1D6B; dev->id_product = 0xA001; dev->bcd_device = 0x0100; dev->i_manufacturer = STRING_MANUFACTURER; dev->i_product = STRING_PRODUCT; dev->i_serial_number = 0; dev->b_num_configurations = 1; value = sizeof(struct usb_device_descriptor); memcpy((void *)buf, (void *)dev, value); } break; case UDESC_DEVICE_QUALIFIER: { struct usb_qualifier_descriptor *qual = (struct usb_qualifier_descriptor *)buf; struct usb_device_descriptor *dev = (struct usb_device_descriptor *)usb3_dev->dev_desc; qual->b_length = sizeof(*qual); qual->b_descriptor_type = UDESC_DEVICE_QUALIFIER; qual->bcd_usb = dev->bcd_usb; qual->b_device_class = dev->b_device_class; qual->b_device_sub_class = dev->b_device_sub_class; qual->b_device_protocol = dev->b_device_protocol; qual->b_max_packet_size0 = dev->b_max_packet_size0; qual->b_num_configurations = 1; qual->b_reserved = 0; value = sizeof(usb_qualifier_descriptor_t); } break; case UDESC_CONFIG: { struct usb_config_descriptor *config = (struct usb_config_descriptor *)buf; config->b_length = sizeof(*config); config->b_descriptor_type = UDESC_CONFIG; config->b_num_interfaces = 1; config->b_configuration_value = CONFIG_VALUE; config->i_configuration = 0; config->bm_attributes = USB_CONFIG_ATT_ONE; if (pcd->speed == USB_SPEED_SUPER) { config->b_max_power = USB_CONFIG_VBUS_DRAW / 8; /* divided 8 */ } else { config->b_max_power = USB_CONFIG_VBUS_DRAW / 2; /* divided 2 */ } buf += sizeof(*config); memcpy((void *)buf, (void *)&interface, sizeof(interface)); buf += sizeof(interface); switch (pcd->speed) { case USB_SPEED_SUPER: memcpy((void *)buf, (void *)&ss_bulk_in, sizeof(ss_bulk_in)); buf += sizeof(ss_bulk_in); memcpy((void *)buf, (void *)&ep_comp, sizeof(ep_comp)); buf += sizeof(ep_comp); memcpy((void *)buf, (void *)&ss_bulk_out, sizeof(ss_bulk_out)); buf += sizeof(ss_bulk_out); memcpy((void *)buf, (void *)&ep_comp, sizeof(ep_comp)); config->w_total_length = sizeof(*config) + sizeof(interface) + sizeof(ss_bulk_in) + sizeof(ep_comp) + sizeof(ss_bulk_out) + sizeof(ep_comp); break; default: /* HS/FS */ { struct usb_endpoint_descriptor *endp = (struct usb_endpoint_descriptor *)buf; memcpy((void *)buf, (void *)&hs_bulk_in, sizeof(hs_bulk_in)); (endp++)->w_max_packet_size = pcd->in_ep.maxpacket; buf += sizeof(hs_bulk_in); memcpy((void *)buf, (void *)&hs_bulk_out, sizeof(hs_bulk_out)); (endp++)->w_max_packet_size = pcd->out_ep.maxpacket; } config->w_total_length = sizeof(*config) + sizeof(interface) + sizeof(hs_bulk_in) + sizeof(hs_bulk_out); break; } value = config->w_total_length; } break; case UDESC_STRING: { switch (index) { case STRING_LANGUAGE: buf[0] = 0x04; buf[1] = UDESC_STRING; buf[2] = 0x09; /* buf number 2 data */ buf[3] = 0x04; /* buf number 3 data */ value = 0x04; break; case STRING_MANUFACTURER: { int ix; unsigned char *ptr = &buf[1]; *ptr++ = UDESC_STRING; memcpy((void *)ptr, (void *)usb3_dev->string_manu, usb3_dev->string_manu_len); ptr += usb3_dev->string_manu_len; for (ix = 0; ix < sizeof(usb3_dev->manu_suffix); ix++) { char chr = usb3_dev->manu_suffix[ix]; if (chr == '\0') break; if (!isprint(chr)) chr = '?'; *ptr++ = chr; *ptr++ = '\0'; } buf[0] = (unsigned char)(ptr - buf); value = buf[0]; break; } case STRING_PRODUCT: buf[0] = usb3_dev->string_prod_len + 0x2; buf[1] = UDESC_STRING; memcpy((void *)(buf + 0x2), (void *)usb3_dev->string_prod, usb3_dev->string_prod_len); value = usb3_dev->string_prod_len + 0x2; break; default: ep0_do_stall(pcd); return; } } break; case UDESC_BOS: if (pcd->speed != USB_SPEED_SUPER) { /* * The USB compliance test (USB 2.0 Command Verifier) * issues this request. We should not run into the * default path here. But return for now until * the superspeed support is added. */ } value = usb_bos.w_total_length; memcpy((void *)buf, (void *)&usb_bos, sizeof(usb_bos)); buf += sizeof(usb_bos); memcpy((void *)buf, (void *)&capability1, sizeof(capability1)); buf += sizeof(capability1); memcpy((void *)buf, (void *)&capability2, sizeof(capability2)); buf += sizeof(capability2); memcpy((void *)buf, (void *)&capability3, sizeof(capability3)); break; default: ep0_do_stall(pcd); return; } pcd->ep0_req.bufdma = pcd->ep0_status_buf; pcd->ep0_req.length = value < len ? value : len; pcd->ep0_req.actual = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); } void usb3_do_setup(usb3_pcd_t *pcd) { usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; usb3_pcd_ep_t *ep0 = &pcd->ep0; unsigned short wlength; wlength = ctrl.w_length; ep0->stopped = 0; ep0->three_stage = 1; if (ctrl.bm_request_type & UE_DIR_IN) { ep0->is_in = 1; pcd->ep0state = EP0_IN_DATA_PHASE; } else { ep0->is_in = 0; pcd->ep0state = EP0_OUT_DATA_PHASE; } if (wlength == 0) { ep0->is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; ep0->three_stage = 0; } if ((ut_get_type(ctrl.bm_request_type)) != UT_STANDARD) { ep0_do_stall(pcd); return; } switch (ctrl.b_request) { case UR_GET_STATUS: usb3_do_get_status(pcd); break; case UR_CLEAR_FEATURE: usb3_do_clear_feature(pcd); break; case UR_SET_FEATURE: usb3_do_set_feature(pcd); break; case UR_SET_ADDRESS: usb3_do_set_address(pcd); break; case UR_SET_CONFIG: usb3_do_set_config(pcd); /* Must wait until SetConfig before accepting U1/U2 link * control, otherwise we have problems with VIA hubs */ usb3_accept_u1(pcd); usb3_accept_u2(pcd); usb_info("usb enum done\n"); pcd->ltm_enable = 0; break; case UR_GET_CONFIG: usb3_do_get_config(pcd); break; case UR_GET_DESCRIPTOR: usb3_do_get_descriptor(pcd); break; case UR_SET_SEL: /* For now this is a no-op */ pcd->ep0_req.bufdma = pcd->ep0_status_buf; pcd->ep0_req.length = USB3_STATUS_BUF_SIZE; pcd->ep0_req.actual = 0; ep0->send_zlp = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); break; case UR_SET_ISOC_DELAY: /* For now this is a no-op */ pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; break; default: ep0_do_stall(pcd); break; } } void usb3_os_get_trb(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, usb3_pcd_req_t *req) { /* If EP0, fill request with EP0 IN/OUT data TRB */ if (ep == &pcd->ep0) { if (ep->is_in) { req->trb = pcd->ep0_in_desc; req->trbdma = (phys_addr_t)pcd->ep0_in_desc; } else { req->trb = pcd->ep0_out_desc; req->trbdma = (phys_addr_t)pcd->ep0_out_desc; } /* Else fill request with TRB from the non-EP0 allocation */ } else { req->trb = ep->ep_desc; req->trbdma = (phys_addr_t)ep->ep_desc; } } void usb3_ep0_start_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req) { usb3_pcd_ep_t *ep0 = &pcd->ep0; usb3_dev_ep_regs_t *ep_reg; usb3_dma_desc_t *desc; phys_addr_t desc_dma; unsigned int desc_type, len; unsigned char tri; /* Get the DMA Descriptor (TRB) for this request */ usb3_os_get_trb(pcd, ep0, req); desc = req->trb; desc_dma = req->trbdma; if (ep0->is_in) { /* * Start DMA on EP0-IN */ ep_reg = ep0->in_ep_reg; /* DMA Descriptor (TRB) setup */ len = req->length; if (pcd->ep0state == EP0_IN_STATUS_PHASE) { if (ep0->three_stage) desc_type = USB3_DSCCTL_TRBCTL_STATUS_3; else desc_type = USB3_DSCCTL_TRBCTL_STATUS_2; } else { desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST; } usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, desc_type, USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); /* Issue "DEPSTRTXFER" command to EP0-IN */ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ep0->tri_in = tri; } else { /* * Start DMA on EP0-OUT */ ep_reg = ep0->out_ep_reg; /* DMA Descriptor (TRB) setup */ len = (req->length + ep0->maxpacket - 1) & ~(ep0->maxpacket - 1); if (pcd->ep0state == EP0_OUT_STATUS_PHASE) { if (ep0->three_stage) desc_type = USB3_DSCCTL_TRBCTL_STATUS_3; else desc_type = USB3_DSCCTL_TRBCTL_STATUS_2; } else { desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST; } usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, desc_type, USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); /* Issue "DEPSTRTXFER" command to EP0-OUT */ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ep0->tri_out = tri; } } static void ep0_continue_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req) { usb3_pcd_ep_t *ep0 = &pcd->ep0; usb3_dev_ep_regs_t *ep_reg; usb3_dma_desc_t *desc; phys_addr_t desc_dma; unsigned char tri; /* It can be called to send a 0-length packet after the end of a transfer, so the code here * only supports that case. */ if (ep0->is_in) { desc = pcd->ep0_in_desc; desc_dma = (phys_addr_t)(pcd->ep0_in_desc); ep_reg = ep0->in_ep_reg; /* DMA Descriptor Setup */ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, 0, 0, USB3_DSCCTL_TRBCTL_NORMAL, USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ep0->tri_in = tri; } } void usb3_ep_start_transfer(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) { usb3_pcd_req_t *req = &ep->req; usb3_dev_ep_regs_t *ep_reg; usb3_dma_desc_t *desc; phys_addr_t desc_dma; unsigned int len; unsigned char tri; /* Get the TRB for this request */ usb3_os_get_trb(pcd, ep, req); ep->send_zlp = 0; desc = req->trb; desc_dma = req->trbdma; if (ep->is_in) { /* For IN, TRB length is just xfer length */ len = req->length; } else { /* For OUT, TRB length must be multiple of maxpacket */ /* Must be power of 2, use cheap AND */ len = (req->length + ep->maxpacket - 1) & ~(ep->maxpacket - 1); req->length = len; } /* DMA Descriptor Setup */ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, USB3_DSCCTL_TRBCTL_NORMAL, USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_LST_BIT, 1); if (ep->is_in) { /* * Start DMA on EPn-IN */ ep_reg = ep->in_ep_reg; /* Issue "DEPSTRTXFER" command to EP */ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ep->tri_in = tri; } else { /* * Start DMA on EPn-OUT */ ep_reg = ep->out_ep_reg; #if 1 if (ep->xfer_started) { /* Issue "DEPUPDTXFER" command to EP */ usb3_dep_updatexfer(pcd, ep_reg, ep->tri_out); } else { #endif /* Issue "DEPSTRTXFER" command to EP */ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ep->tri_out = tri; ep->xfer_started = 1; } } } void usb3_bulk_out_transfer(void *dev) { usb3_pcd_t *pcd = (usb3_pcd_t *)dev; usb3_pcd_ep_t *ep = &pcd->out_ep; usb3_pcd_req_t *req = &ep->req; req->length = ep->maxpacket; req->bufdma = pcd->ss_bulk_buf; req->complete = usb3_handle_protocol; usb3_ep_start_transfer(pcd, ep); return; } void usb3_bulk_out_continue_transfer(void *dev) { usb3_pcd_t *pcd = (usb3_pcd_t *)dev; usb3_pcd_ep_t *ep = &pcd->out_ep; usb3_ep_start_transfer(pcd, ep); } void usb_tx_status_complete(void *dev) {} void usb3_bulk_in_transfer(void *dev, const char *status) { usb3_pcd_t *pcd = (usb3_pcd_t *)dev; usb3_pcd_ep_t *ep = &pcd->in_ep; usb3_pcd_req_t *req = &ep->req; int len; len = strlen(status) + 1; memset(req->bufdma, 0, BUFFER_SIZE); memcpy(req->bufdma, status, len); req->length = len; req->complete = usb_tx_status_complete; usb3_ep_start_transfer(pcd, ep); } static void ep0_complete_request(usb3_pcd_t *pcd, usb3_pcd_req_t *req, usb3_dma_desc_t *desc, int status) { usb3_pcd_ep_t *ep = &pcd->ep0; if (!req) return; if (pcd->ep0state == EP0_OUT_DATA_PHASE || pcd->ep0state == EP0_IN_DATA_PHASE) { if (ep->is_in) { if (usb3_get_xfercnt(desc) == 0) { pcd->ep0.is_in = 0; pcd->ep0state = EP0_OUT_WAIT_NRDY; } } else { pcd->ep0.is_in = 1; pcd->ep0state = EP0_IN_WAIT_NRDY; } } } static void setup_in_status_phase(usb3_pcd_t *pcd, void *buf) { usb3_pcd_ep_t *ep0 = &pcd->ep0; if (pcd->ep0state == EP0_STALL) return; ep0->is_in = 1; pcd->ep0state = EP0_IN_STATUS_PHASE; pcd->ep0_req.bufdma = buf; pcd->ep0_req.length = 0; pcd->ep0_req.actual = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); } static void setup_out_status_phase(usb3_pcd_t *pcd, void *buf) { usb3_pcd_ep_t *ep0 = &pcd->ep0; if (pcd->ep0state == EP0_STALL) return; ep0->is_in = 0; pcd->ep0state = EP0_OUT_STATUS_PHASE; pcd->ep0_req.bufdma = buf; pcd->ep0_req.length = 0; pcd->ep0_req.actual = 0; usb3_ep0_start_transfer(pcd, &pcd->ep0_req); } void usb3_handle_ep0(usb3_pcd_t *pcd, usb3_pcd_req_t *req, unsigned int event) { usb3_pcd_ep_t *ep0 = &pcd->ep0; usb3_dma_desc_t *desc; unsigned int byte_count; unsigned int len = 0; unsigned int status; switch (pcd->ep0state) { case EP0_IN_DATA_PHASE: if (!req) req = &pcd->ep0_req; desc = pcd->ep0_in_desc; if (dwc_usb3_is_hwo(desc)) goto out; status = usb3_get_xfersts(desc); if (status & USB3_TRBRSP_SETUP_PEND) /* Start of a new Control transfer */ desc->status = 0; byte_count = req->length - usb3_get_xfercnt(desc); req->actual += byte_count; req->bufdma += byte_count; if (req->actual < req->length) { /* IN CONTINUE, Stall EP0 */ ep0_do_stall(pcd); } else if (ep0->send_zlp) { /* CONTINUE TRANSFER IN ZLP */ ep0_continue_transfer(pcd, req); ep0->send_zlp = 0; } else { /* COMPLETE IN TRANSFER */ ep0_complete_request(pcd, req, desc, 0); } break; case EP0_OUT_DATA_PHASE: if (!req) req = &pcd->ep0_req; desc = pcd->ep0_out_desc; if (dwc_usb3_is_hwo(desc)) goto out; status = usb3_get_xfersts(desc); if (status & USB3_TRBRSP_SETUP_PEND) /* Start of a new Control transfer */ len = (req->length + ep0->maxpacket - 1) & ~(ep0->maxpacket - 1); byte_count = len - usb3_get_xfercnt(desc); req->actual += byte_count; req->bufdma += byte_count; if (ep0->send_zlp) { /* CONTINUE TRANSFER OUT ZLP */ ep0_continue_transfer(pcd, req); ep0->send_zlp = 0; } else { /* COMPLETE OUT TRANSFER */ ep0_complete_request(pcd, req, desc, 0); } break; case EP0_IN_WAIT_NRDY: case EP0_OUT_WAIT_NRDY: if (ep0->is_in) setup_in_status_phase(pcd, pcd->ep0_setup_pkt); else setup_out_status_phase(pcd, pcd->ep0_setup_pkt); break; case EP0_IN_STATUS_PHASE: case EP0_OUT_STATUS_PHASE: if (ep0->is_in) desc = pcd->ep0_in_desc; else desc = pcd->ep0_out_desc; ep0_complete_request(pcd, req, desc, 0); pcd->ep0state = EP0_IDLE; ep0->stopped = 1; ep0->is_in = 0; /* OUT for next SETUP */ /* Prepare for more SETUP Packets */ usb3_ep0_out_start(pcd); break; case EP0_STALL: break; case EP0_IDLE: break; default: break; } out: return; } void usb3_os_handle_ep0(usb3_pcd_t *pcd, unsigned int event) { usb3_pcd_req_t *req = NULL; if (pcd->ep0state == EP0_IDLE) { usb3_do_setup(pcd); } else { usb3_handle_ep0(pcd, req, event); } } void usb3_request_done(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, usb3_pcd_req_t *req, int status) { if (ep != &pcd->ep0) req->trb = NULL; if (req->complete) req->complete(pcd); req->actual = 0; return; } int usb3_ep_complete_request(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, unsigned int event) { usb3_pcd_req_t *req = &ep->req; usb3_dma_desc_t *desc = req->trb; unsigned int byte_count; int error = USB_NO_ERR; ep->send_zlp = 0; if (!desc) return USB_PROCESS_ERR; if (dwc_usb3_is_hwo(desc)) return USB_PROCESS_ERR; if (ep->is_in) { /* IN endpoint */ if (usb3_get_xfercnt(desc) == 0) req->actual += req->length; /* Reset IN tri */ ep->tri_in = 0; /* Complete the IN request */ usb3_request_done(pcd, ep, req, 0); } else { /* OUT endpoint */ byte_count = req->length - usb3_get_xfercnt(desc); req->actual += byte_count; req->bufdma += byte_count; pcd->file_received += byte_count; /* Reset OUT tri */ ep->tri_out = 0; /* OUT transfer complete or not */ if ((byte_count < ep->maxpacket) || (pcd->file_capacity <= pcd->file_received - FRAME_HEAD_LEN)) { /* Complete the OUT request */ usb3_request_done(pcd, ep, req, 0); } else { usb3_bulk_out_continue_transfer((void *)pcd); } } return error; }