15661f08aSStefan Agner /* 25661f08aSStefan Agner * f_sdp.c -- USB HID Serial Download Protocol 35661f08aSStefan Agner * 45661f08aSStefan Agner * Copyright (C) 2017 Toradex 55661f08aSStefan Agner * Author: Stefan Agner <stefan.agner@toradex.com> 65661f08aSStefan Agner * 75661f08aSStefan Agner * This file implements the Serial Download Protocol (SDP) as specified in 85661f08aSStefan Agner * the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and 95661f08aSStefan Agner * allows to download images directly to memory. The implementation 105661f08aSStefan Agner * works with the imx_loader (imx_usb) USB client software on host side. 115661f08aSStefan Agner * 125661f08aSStefan Agner * Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and 135661f08aSStefan Agner * SKIP_DCD_HEADER are only stubs. 145661f08aSStefan Agner * 155661f08aSStefan Agner * Parts of the implementation are based on f_dfu and f_thor. 165661f08aSStefan Agner * 175661f08aSStefan Agner * SPDX-License-Identifier: GPL-2.0+ 185661f08aSStefan Agner */ 195661f08aSStefan Agner 205661f08aSStefan Agner #include <errno.h> 215661f08aSStefan Agner #include <common.h> 225661f08aSStefan Agner #include <console.h> 235661f08aSStefan Agner #include <malloc.h> 245661f08aSStefan Agner 255661f08aSStefan Agner #include <linux/usb/ch9.h> 265661f08aSStefan Agner #include <linux/usb/gadget.h> 275661f08aSStefan Agner #include <linux/usb/composite.h> 285661f08aSStefan Agner 295661f08aSStefan Agner #include <asm/io.h> 305661f08aSStefan Agner #include <g_dnl.h> 315661f08aSStefan Agner #include <sdp.h> 32*ccd7a4d2SStefan Agner #include <spl.h> 33*ccd7a4d2SStefan Agner #include <image.h> 345661f08aSStefan Agner #include <imximage.h> 355661f08aSStefan Agner 365661f08aSStefan Agner #define HID_REPORT_ID_MASK 0x000000ff 375661f08aSStefan Agner 385661f08aSStefan Agner /* 395661f08aSStefan Agner * HID class requests 405661f08aSStefan Agner */ 415661f08aSStefan Agner #define HID_REQ_GET_REPORT 0x01 425661f08aSStefan Agner #define HID_REQ_GET_IDLE 0x02 435661f08aSStefan Agner #define HID_REQ_GET_PROTOCOL 0x03 445661f08aSStefan Agner #define HID_REQ_SET_REPORT 0x09 455661f08aSStefan Agner #define HID_REQ_SET_IDLE 0x0A 465661f08aSStefan Agner #define HID_REQ_SET_PROTOCOL 0x0B 475661f08aSStefan Agner 485661f08aSStefan Agner #define HID_USAGE_PAGE_LEN 76 495661f08aSStefan Agner 505661f08aSStefan Agner struct hid_report { 515661f08aSStefan Agner u8 usage_page[HID_USAGE_PAGE_LEN]; 525661f08aSStefan Agner } __packed; 535661f08aSStefan Agner 545661f08aSStefan Agner #define SDP_READ_REGISTER 0x0101 555661f08aSStefan Agner #define SDP_WRITE_REGISTER 0x0202 565661f08aSStefan Agner #define SDP_WRITE_FILE 0x0404 575661f08aSStefan Agner #define SDP_ERROR_STATUS 0x0505 585661f08aSStefan Agner #define SDP_DCD_WRITE 0x0a0a 595661f08aSStefan Agner #define SDP_JUMP_ADDRESS 0x0b0b 605661f08aSStefan Agner #define SDP_SKIP_DCD_HEADER 0x0c0c 615661f08aSStefan Agner 625661f08aSStefan Agner #define SDP_SECURITY_CLOSED 0x12343412 635661f08aSStefan Agner #define SDP_SECURITY_OPEN 0x56787856 645661f08aSStefan Agner 655661f08aSStefan Agner #define SDP_WRITE_FILE_COMPLETE 0x88888888 665661f08aSStefan Agner #define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12 675661f08aSStefan Agner #define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009 685661f08aSStefan Agner #define SDP_ERROR_IMXHEADER 0x000a0533 695661f08aSStefan Agner 705661f08aSStefan Agner #define SDP_COMMAND_LEN 16 715661f08aSStefan Agner 725661f08aSStefan Agner struct sdp_command { 735661f08aSStefan Agner u16 cmd; 745661f08aSStefan Agner u32 addr; 755661f08aSStefan Agner u8 format; 765661f08aSStefan Agner u32 cnt; 775661f08aSStefan Agner u32 data; 785661f08aSStefan Agner u8 rsvd; 795661f08aSStefan Agner } __packed; 805661f08aSStefan Agner 815661f08aSStefan Agner enum sdp_state { 825661f08aSStefan Agner SDP_STATE_IDLE, 835661f08aSStefan Agner SDP_STATE_RX_DCD_DATA, 845661f08aSStefan Agner SDP_STATE_RX_FILE_DATA, 855661f08aSStefan Agner SDP_STATE_TX_SEC_CONF, 865661f08aSStefan Agner SDP_STATE_TX_SEC_CONF_BUSY, 875661f08aSStefan Agner SDP_STATE_TX_REGISTER, 885661f08aSStefan Agner SDP_STATE_TX_REGISTER_BUSY, 895661f08aSStefan Agner SDP_STATE_TX_STATUS, 905661f08aSStefan Agner SDP_STATE_TX_STATUS_BUSY, 915661f08aSStefan Agner SDP_STATE_JUMP, 925661f08aSStefan Agner }; 935661f08aSStefan Agner 945661f08aSStefan Agner struct f_sdp { 955661f08aSStefan Agner struct usb_function usb_function; 965661f08aSStefan Agner 975661f08aSStefan Agner struct usb_descriptor_header **function; 985661f08aSStefan Agner 995661f08aSStefan Agner u8 altsetting; 1005661f08aSStefan Agner enum sdp_state state; 1015661f08aSStefan Agner enum sdp_state next_state; 1025661f08aSStefan Agner u32 dnl_address; 1035661f08aSStefan Agner u32 dnl_bytes_remaining; 1045661f08aSStefan Agner u32 jmp_address; 1055661f08aSStefan Agner bool always_send_status; 1065661f08aSStefan Agner u32 error_status; 1075661f08aSStefan Agner 1085661f08aSStefan Agner /* EP0 request */ 1095661f08aSStefan Agner struct usb_request *req; 1105661f08aSStefan Agner 1115661f08aSStefan Agner /* EP1 IN */ 1125661f08aSStefan Agner struct usb_ep *in_ep; 1135661f08aSStefan Agner struct usb_request *in_req; 1145661f08aSStefan Agner 1155661f08aSStefan Agner bool configuration_done; 1165661f08aSStefan Agner }; 1175661f08aSStefan Agner 1185661f08aSStefan Agner static struct f_sdp *sdp_func; 1195661f08aSStefan Agner 1205661f08aSStefan Agner static inline struct f_sdp *func_to_sdp(struct usb_function *f) 1215661f08aSStefan Agner { 1225661f08aSStefan Agner return container_of(f, struct f_sdp, usb_function); 1235661f08aSStefan Agner } 1245661f08aSStefan Agner 1255661f08aSStefan Agner static struct usb_interface_descriptor sdp_intf_runtime = { 1265661f08aSStefan Agner .bLength = sizeof(sdp_intf_runtime), 1275661f08aSStefan Agner .bDescriptorType = USB_DT_INTERFACE, 1285661f08aSStefan Agner .bAlternateSetting = 0, 1295661f08aSStefan Agner .bNumEndpoints = 1, 1305661f08aSStefan Agner .bInterfaceClass = USB_CLASS_HID, 1315661f08aSStefan Agner .bInterfaceSubClass = 0, 1325661f08aSStefan Agner .bInterfaceProtocol = 0, 1335661f08aSStefan Agner /* .iInterface = DYNAMIC */ 1345661f08aSStefan Agner }; 1355661f08aSStefan Agner 1365661f08aSStefan Agner /* HID configuration */ 1375661f08aSStefan Agner static struct usb_class_hid_descriptor sdp_hid_desc = { 1385661f08aSStefan Agner .bLength = sizeof(sdp_hid_desc), 1395661f08aSStefan Agner .bDescriptorType = USB_DT_CS_DEVICE, 1405661f08aSStefan Agner 1415661f08aSStefan Agner .bcdCDC = __constant_cpu_to_le16(0x0110), 1425661f08aSStefan Agner .bCountryCode = 0, 1435661f08aSStefan Agner .bNumDescriptors = 1, 1445661f08aSStefan Agner 1455661f08aSStefan Agner .bDescriptorType0 = USB_DT_HID_REPORT, 1465661f08aSStefan Agner .wDescriptorLength0 = HID_USAGE_PAGE_LEN, 1475661f08aSStefan Agner }; 1485661f08aSStefan Agner 1495661f08aSStefan Agner static struct usb_endpoint_descriptor in_desc = { 1505661f08aSStefan Agner .bLength = USB_DT_ENDPOINT_SIZE, 1515661f08aSStefan Agner .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/ 1525661f08aSStefan Agner 1535661f08aSStefan Agner .bEndpointAddress = 1 | USB_DIR_IN, 1545661f08aSStefan Agner .bmAttributes = USB_ENDPOINT_XFER_INT, 1555661f08aSStefan Agner .wMaxPacketSize = 64, 1565661f08aSStefan Agner .bInterval = 1, 1575661f08aSStefan Agner }; 1585661f08aSStefan Agner 1595661f08aSStefan Agner static struct usb_descriptor_header *sdp_runtime_descs[] = { 1605661f08aSStefan Agner (struct usb_descriptor_header *)&sdp_intf_runtime, 1615661f08aSStefan Agner (struct usb_descriptor_header *)&sdp_hid_desc, 1625661f08aSStefan Agner (struct usb_descriptor_header *)&in_desc, 1635661f08aSStefan Agner NULL, 1645661f08aSStefan Agner }; 1655661f08aSStefan Agner 1665661f08aSStefan Agner /* This is synchronized with what the SoC implementation reports */ 1675661f08aSStefan Agner static struct hid_report sdp_hid_report = { 1685661f08aSStefan Agner .usage_page = { 1695661f08aSStefan Agner 0x06, 0x00, 0xff, /* Usage Page */ 1705661f08aSStefan Agner 0x09, 0x01, /* Usage (Pointer?) */ 1715661f08aSStefan Agner 0xa1, 0x01, /* Collection */ 1725661f08aSStefan Agner 1735661f08aSStefan Agner 0x85, 0x01, /* Report ID */ 1745661f08aSStefan Agner 0x19, 0x01, /* Usage Minimum */ 1755661f08aSStefan Agner 0x29, 0x01, /* Usage Maximum */ 1765661f08aSStefan Agner 0x15, 0x00, /* Local Minimum */ 1775661f08aSStefan Agner 0x26, 0xFF, 0x00, /* Local Maximum? */ 1785661f08aSStefan Agner 0x75, 0x08, /* Report Size */ 1795661f08aSStefan Agner 0x95, 0x10, /* Report Count */ 1805661f08aSStefan Agner 0x91, 0x02, /* Output Data */ 1815661f08aSStefan Agner 1825661f08aSStefan Agner 0x85, 0x02, /* Report ID */ 1835661f08aSStefan Agner 0x19, 0x01, /* Usage Minimum */ 1845661f08aSStefan Agner 0x29, 0x01, /* Usage Maximum */ 1855661f08aSStefan Agner 0x15, 0x00, /* Local Minimum */ 1865661f08aSStefan Agner 0x26, 0xFF, 0x00, /* Local Maximum? */ 1875661f08aSStefan Agner 0x75, 0x80, /* Report Size 128 */ 1885661f08aSStefan Agner 0x95, 0x40, /* Report Count */ 1895661f08aSStefan Agner 0x91, 0x02, /* Output Data */ 1905661f08aSStefan Agner 1915661f08aSStefan Agner 0x85, 0x03, /* Report ID */ 1925661f08aSStefan Agner 0x19, 0x01, /* Usage Minimum */ 1935661f08aSStefan Agner 0x29, 0x01, /* Usage Maximum */ 1945661f08aSStefan Agner 0x15, 0x00, /* Local Minimum */ 1955661f08aSStefan Agner 0x26, 0xFF, 0x00, /* Local Maximum? */ 1965661f08aSStefan Agner 0x75, 0x08, /* Report Size 8 */ 1975661f08aSStefan Agner 0x95, 0x04, /* Report Count */ 1985661f08aSStefan Agner 0x81, 0x02, /* Input Data */ 1995661f08aSStefan Agner 2005661f08aSStefan Agner 0x85, 0x04, /* Report ID */ 2015661f08aSStefan Agner 0x19, 0x01, /* Usage Minimum */ 2025661f08aSStefan Agner 0x29, 0x01, /* Usage Maximum */ 2035661f08aSStefan Agner 0x15, 0x00, /* Local Minimum */ 2045661f08aSStefan Agner 0x26, 0xFF, 0x00, /* Local Maximum? */ 2055661f08aSStefan Agner 0x75, 0x08, /* Report Size 8 */ 2065661f08aSStefan Agner 0x95, 0x40, /* Report Count */ 2075661f08aSStefan Agner 0x81, 0x02, /* Input Data */ 2085661f08aSStefan Agner 0xc0 2095661f08aSStefan Agner }, 2105661f08aSStefan Agner }; 2115661f08aSStefan Agner 2125661f08aSStefan Agner static const char sdp_name[] = "Serial Downloader Protocol"; 2135661f08aSStefan Agner 2145661f08aSStefan Agner /* 2155661f08aSStefan Agner * static strings, in UTF-8 2165661f08aSStefan Agner */ 2175661f08aSStefan Agner static struct usb_string strings_sdp_generic[] = { 2185661f08aSStefan Agner [0].s = sdp_name, 2195661f08aSStefan Agner { } /* end of list */ 2205661f08aSStefan Agner }; 2215661f08aSStefan Agner 2225661f08aSStefan Agner static struct usb_gadget_strings stringtab_sdp_generic = { 2235661f08aSStefan Agner .language = 0x0409, /* en-us */ 2245661f08aSStefan Agner .strings = strings_sdp_generic, 2255661f08aSStefan Agner }; 2265661f08aSStefan Agner 2275661f08aSStefan Agner static struct usb_gadget_strings *sdp_generic_strings[] = { 2285661f08aSStefan Agner &stringtab_sdp_generic, 2295661f08aSStefan Agner NULL, 2305661f08aSStefan Agner }; 2315661f08aSStefan Agner 2325661f08aSStefan Agner static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req) 2335661f08aSStefan Agner { 2345661f08aSStefan Agner struct f_sdp *sdp = req->context; 2355661f08aSStefan Agner int status = req->status; 2365661f08aSStefan Agner u8 *data = req->buf; 2375661f08aSStefan Agner u8 report = data[0]; 2385661f08aSStefan Agner 2395661f08aSStefan Agner if (status != 0) { 2405661f08aSStefan Agner error("Status: %d", status); 2415661f08aSStefan Agner return; 2425661f08aSStefan Agner } 2435661f08aSStefan Agner 2445661f08aSStefan Agner if (report != 1) { 2455661f08aSStefan Agner error("Unexpected report %d", report); 2465661f08aSStefan Agner return; 2475661f08aSStefan Agner } 2485661f08aSStefan Agner 2495661f08aSStefan Agner struct sdp_command *cmd = req->buf + 1; 2505661f08aSStefan Agner 2515661f08aSStefan Agner debug("%s: command: %04x, addr: %08x, cnt: %u\n", 2525661f08aSStefan Agner __func__, be16_to_cpu(cmd->cmd), 2535661f08aSStefan Agner be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt)); 2545661f08aSStefan Agner 2555661f08aSStefan Agner switch (be16_to_cpu(cmd->cmd)) { 2565661f08aSStefan Agner case SDP_READ_REGISTER: 2575661f08aSStefan Agner sdp->always_send_status = false; 2585661f08aSStefan Agner sdp->error_status = 0x0; 2595661f08aSStefan Agner 2605661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 2615661f08aSStefan Agner sdp->dnl_address = be32_to_cpu(cmd->addr); 2625661f08aSStefan Agner sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); 2635661f08aSStefan Agner sdp->next_state = SDP_STATE_TX_REGISTER; 2645661f08aSStefan Agner printf("Reading %d registers at 0x%08x... ", 2655661f08aSStefan Agner sdp->dnl_bytes_remaining, sdp->dnl_address); 2665661f08aSStefan Agner break; 2675661f08aSStefan Agner case SDP_WRITE_FILE: 2685661f08aSStefan Agner sdp->always_send_status = true; 2695661f08aSStefan Agner sdp->error_status = SDP_WRITE_FILE_COMPLETE; 2705661f08aSStefan Agner 2715661f08aSStefan Agner sdp->state = SDP_STATE_RX_FILE_DATA; 2725661f08aSStefan Agner sdp->dnl_address = be32_to_cpu(cmd->addr); 2735661f08aSStefan Agner sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); 2745661f08aSStefan Agner sdp->next_state = SDP_STATE_IDLE; 2755661f08aSStefan Agner 2765661f08aSStefan Agner printf("Downloading file of size %d to 0x%08x... ", 2775661f08aSStefan Agner sdp->dnl_bytes_remaining, sdp->dnl_address); 2785661f08aSStefan Agner 2795661f08aSStefan Agner break; 2805661f08aSStefan Agner case SDP_ERROR_STATUS: 2815661f08aSStefan Agner sdp->always_send_status = true; 2825661f08aSStefan Agner sdp->error_status = 0; 2835661f08aSStefan Agner 2845661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 2855661f08aSStefan Agner sdp->next_state = SDP_STATE_IDLE; 2865661f08aSStefan Agner break; 2875661f08aSStefan Agner case SDP_DCD_WRITE: 2885661f08aSStefan Agner sdp->always_send_status = true; 2895661f08aSStefan Agner sdp->error_status = SDP_WRITE_REGISTER_COMPLETE; 2905661f08aSStefan Agner 2915661f08aSStefan Agner sdp->state = SDP_STATE_RX_DCD_DATA; 2925661f08aSStefan Agner sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); 2935661f08aSStefan Agner sdp->next_state = SDP_STATE_IDLE; 2945661f08aSStefan Agner break; 2955661f08aSStefan Agner case SDP_JUMP_ADDRESS: 2965661f08aSStefan Agner sdp->always_send_status = false; 2975661f08aSStefan Agner sdp->error_status = 0; 2985661f08aSStefan Agner 2995661f08aSStefan Agner sdp->jmp_address = be32_to_cpu(cmd->addr); 3005661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 3015661f08aSStefan Agner sdp->next_state = SDP_STATE_JUMP; 3025661f08aSStefan Agner break; 3035661f08aSStefan Agner case SDP_SKIP_DCD_HEADER: 3045661f08aSStefan Agner sdp->always_send_status = true; 3055661f08aSStefan Agner sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE; 3065661f08aSStefan Agner 3075661f08aSStefan Agner /* Ignore command, DCD not supported anyway */ 3085661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 3095661f08aSStefan Agner sdp->next_state = SDP_STATE_IDLE; 3105661f08aSStefan Agner break; 3115661f08aSStefan Agner default: 3125661f08aSStefan Agner error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd)); 3135661f08aSStefan Agner } 3145661f08aSStefan Agner } 3155661f08aSStefan Agner 3165661f08aSStefan Agner static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) 3175661f08aSStefan Agner { 3185661f08aSStefan Agner struct f_sdp *sdp = req->context; 3195661f08aSStefan Agner int status = req->status; 3205661f08aSStefan Agner u8 *data = req->buf; 3215661f08aSStefan Agner u8 report = data[0]; 3225661f08aSStefan Agner int datalen = req->length - 1; 3235661f08aSStefan Agner 3245661f08aSStefan Agner if (status != 0) { 3255661f08aSStefan Agner error("Status: %d", status); 3265661f08aSStefan Agner return; 3275661f08aSStefan Agner } 3285661f08aSStefan Agner 3295661f08aSStefan Agner if (report != 2) { 3305661f08aSStefan Agner error("Unexpected report %d", report); 3315661f08aSStefan Agner return; 3325661f08aSStefan Agner } 3335661f08aSStefan Agner 3345661f08aSStefan Agner if (sdp->dnl_bytes_remaining < datalen) { 3355661f08aSStefan Agner /* 3365661f08aSStefan Agner * Some USB stacks require to send a complete buffer as 3375661f08aSStefan Agner * specified in the HID descriptor. This leads to longer 3385661f08aSStefan Agner * transfers than the file length, no problem for us. 3395661f08aSStefan Agner */ 3405661f08aSStefan Agner sdp->dnl_bytes_remaining = 0; 3415661f08aSStefan Agner } else { 3425661f08aSStefan Agner sdp->dnl_bytes_remaining -= datalen; 3435661f08aSStefan Agner } 3445661f08aSStefan Agner 3455661f08aSStefan Agner if (sdp->state == SDP_STATE_RX_FILE_DATA) { 3465661f08aSStefan Agner memcpy((void *)sdp->dnl_address, req->buf + 1, datalen); 3475661f08aSStefan Agner sdp->dnl_address += datalen; 3485661f08aSStefan Agner } 3495661f08aSStefan Agner 3505661f08aSStefan Agner if (sdp->dnl_bytes_remaining) 3515661f08aSStefan Agner return; 3525661f08aSStefan Agner 3535661f08aSStefan Agner printf("done\n"); 3545661f08aSStefan Agner 3555661f08aSStefan Agner switch (sdp->state) { 3565661f08aSStefan Agner case SDP_STATE_RX_FILE_DATA: 3575661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 3585661f08aSStefan Agner break; 3595661f08aSStefan Agner case SDP_STATE_RX_DCD_DATA: 3605661f08aSStefan Agner sdp->state = SDP_STATE_TX_SEC_CONF; 3615661f08aSStefan Agner break; 3625661f08aSStefan Agner default: 3635661f08aSStefan Agner error("Invalid state: %d", sdp->state); 3645661f08aSStefan Agner } 3655661f08aSStefan Agner } 3665661f08aSStefan Agner 3675661f08aSStefan Agner static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req) 3685661f08aSStefan Agner { 3695661f08aSStefan Agner struct f_sdp *sdp = req->context; 3705661f08aSStefan Agner int status = req->status; 3715661f08aSStefan Agner 3725661f08aSStefan Agner if (status != 0) { 3735661f08aSStefan Agner error("Status: %d", status); 3745661f08aSStefan Agner return; 3755661f08aSStefan Agner } 3765661f08aSStefan Agner 3775661f08aSStefan Agner switch (sdp->state) { 3785661f08aSStefan Agner case SDP_STATE_TX_SEC_CONF_BUSY: 3795661f08aSStefan Agner /* Not all commands require status report */ 3805661f08aSStefan Agner if (sdp->always_send_status || sdp->error_status) 3815661f08aSStefan Agner sdp->state = SDP_STATE_TX_STATUS; 3825661f08aSStefan Agner else 3835661f08aSStefan Agner sdp->state = sdp->next_state; 3845661f08aSStefan Agner 3855661f08aSStefan Agner break; 3865661f08aSStefan Agner case SDP_STATE_TX_STATUS_BUSY: 3875661f08aSStefan Agner sdp->state = sdp->next_state; 3885661f08aSStefan Agner break; 3895661f08aSStefan Agner case SDP_STATE_TX_REGISTER_BUSY: 3905661f08aSStefan Agner if (sdp->dnl_bytes_remaining) 3915661f08aSStefan Agner sdp->state = SDP_STATE_TX_REGISTER; 3925661f08aSStefan Agner else 3935661f08aSStefan Agner sdp->state = SDP_STATE_IDLE; 3945661f08aSStefan Agner break; 3955661f08aSStefan Agner default: 3965661f08aSStefan Agner error("Wrong State: %d", sdp->state); 3975661f08aSStefan Agner sdp->state = SDP_STATE_IDLE; 3985661f08aSStefan Agner break; 3995661f08aSStefan Agner } 4005661f08aSStefan Agner debug("%s complete --> %d, %d/%d\n", ep->name, 4015661f08aSStefan Agner status, req->actual, req->length); 4025661f08aSStefan Agner } 4035661f08aSStefan Agner 4045661f08aSStefan Agner static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 4055661f08aSStefan Agner { 4065661f08aSStefan Agner struct usb_gadget *gadget = f->config->cdev->gadget; 4075661f08aSStefan Agner struct usb_request *req = f->config->cdev->req; 4085661f08aSStefan Agner struct f_sdp *sdp = f->config->cdev->req->context; 4095661f08aSStefan Agner u16 len = le16_to_cpu(ctrl->wLength); 4105661f08aSStefan Agner u16 w_value = le16_to_cpu(ctrl->wValue); 4115661f08aSStefan Agner int value = 0; 4125661f08aSStefan Agner u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; 4135661f08aSStefan Agner 4145661f08aSStefan Agner debug("w_value: 0x%04x len: 0x%04x\n", w_value, len); 4155661f08aSStefan Agner debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n", 4165661f08aSStefan Agner req_type, ctrl->bRequest, sdp->state); 4175661f08aSStefan Agner 4185661f08aSStefan Agner if (req_type == USB_TYPE_STANDARD) { 4195661f08aSStefan Agner if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { 4205661f08aSStefan Agner /* Send HID report descriptor */ 4215661f08aSStefan Agner value = min(len, (u16) sizeof(sdp_hid_report)); 4225661f08aSStefan Agner memcpy(req->buf, &sdp_hid_report, value); 4235661f08aSStefan Agner sdp->configuration_done = true; 4245661f08aSStefan Agner } 4255661f08aSStefan Agner } 4265661f08aSStefan Agner 4275661f08aSStefan Agner if (req_type == USB_TYPE_CLASS) { 4285661f08aSStefan Agner int report = w_value & HID_REPORT_ID_MASK; 4295661f08aSStefan Agner 4305661f08aSStefan Agner /* HID (SDP) request */ 4315661f08aSStefan Agner switch (ctrl->bRequest) { 4325661f08aSStefan Agner case HID_REQ_SET_REPORT: 4335661f08aSStefan Agner switch (report) { 4345661f08aSStefan Agner case 1: 4355661f08aSStefan Agner value = SDP_COMMAND_LEN + 1; 4365661f08aSStefan Agner req->complete = sdp_rx_command_complete; 4375661f08aSStefan Agner break; 4385661f08aSStefan Agner case 2: 4395661f08aSStefan Agner value = len; 4405661f08aSStefan Agner req->complete = sdp_rx_data_complete; 4415661f08aSStefan Agner break; 4425661f08aSStefan Agner } 4435661f08aSStefan Agner } 4445661f08aSStefan Agner } 4455661f08aSStefan Agner 4465661f08aSStefan Agner if (value >= 0) { 4475661f08aSStefan Agner req->length = value; 4485661f08aSStefan Agner req->zero = value < len; 4495661f08aSStefan Agner value = usb_ep_queue(gadget->ep0, req, 0); 4505661f08aSStefan Agner if (value < 0) { 4515661f08aSStefan Agner debug("ep_queue --> %d\n", value); 4525661f08aSStefan Agner req->status = 0; 4535661f08aSStefan Agner } 4545661f08aSStefan Agner } 4555661f08aSStefan Agner 4565661f08aSStefan Agner return value; 4575661f08aSStefan Agner } 4585661f08aSStefan Agner 4595661f08aSStefan Agner static int sdp_bind(struct usb_configuration *c, struct usb_function *f) 4605661f08aSStefan Agner { 4615661f08aSStefan Agner struct usb_gadget *gadget = c->cdev->gadget; 4625661f08aSStefan Agner struct usb_composite_dev *cdev = c->cdev; 4635661f08aSStefan Agner struct f_sdp *sdp = func_to_sdp(f); 4645661f08aSStefan Agner int rv = 0, id; 4655661f08aSStefan Agner 4665661f08aSStefan Agner id = usb_interface_id(c, f); 4675661f08aSStefan Agner if (id < 0) 4685661f08aSStefan Agner return id; 4695661f08aSStefan Agner sdp_intf_runtime.bInterfaceNumber = id; 4705661f08aSStefan Agner 4715661f08aSStefan Agner struct usb_ep *ep; 4725661f08aSStefan Agner 4735661f08aSStefan Agner /* allocate instance-specific endpoints */ 4745661f08aSStefan Agner ep = usb_ep_autoconfig(gadget, &in_desc); 4755661f08aSStefan Agner if (!ep) { 4765661f08aSStefan Agner rv = -ENODEV; 4775661f08aSStefan Agner goto error; 4785661f08aSStefan Agner } 4795661f08aSStefan Agner 4805661f08aSStefan Agner sdp->in_ep = ep; /* Store IN EP for enabling @ setup */ 4815661f08aSStefan Agner 4825661f08aSStefan Agner cdev->req->context = sdp; 4835661f08aSStefan Agner 4845661f08aSStefan Agner error: 4855661f08aSStefan Agner return rv; 4865661f08aSStefan Agner } 4875661f08aSStefan Agner 4885661f08aSStefan Agner static void sdp_unbind(struct usb_configuration *c, struct usb_function *f) 4895661f08aSStefan Agner { 4905661f08aSStefan Agner free(sdp_func); 4915661f08aSStefan Agner sdp_func = NULL; 4925661f08aSStefan Agner } 4935661f08aSStefan Agner 4945661f08aSStefan Agner static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) 4955661f08aSStefan Agner { 4965661f08aSStefan Agner struct usb_request *req; 4975661f08aSStefan Agner 4985661f08aSStefan Agner req = usb_ep_alloc_request(ep, 0); 4995661f08aSStefan Agner if (!req) 5005661f08aSStefan Agner return req; 5015661f08aSStefan Agner 5025661f08aSStefan Agner req->length = length; 5035661f08aSStefan Agner req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); 5045661f08aSStefan Agner if (!req->buf) { 5055661f08aSStefan Agner usb_ep_free_request(ep, req); 5065661f08aSStefan Agner req = NULL; 5075661f08aSStefan Agner } 5085661f08aSStefan Agner 5095661f08aSStefan Agner return req; 5105661f08aSStefan Agner } 5115661f08aSStefan Agner 5125661f08aSStefan Agner 5135661f08aSStefan Agner static struct usb_request *sdp_start_ep(struct usb_ep *ep) 5145661f08aSStefan Agner { 5155661f08aSStefan Agner struct usb_request *req; 5165661f08aSStefan Agner 5175661f08aSStefan Agner req = alloc_ep_req(ep, 64); 5185661f08aSStefan Agner debug("%s: ep:%p req:%p\n", __func__, ep, req); 5195661f08aSStefan Agner 5205661f08aSStefan Agner if (!req) 5215661f08aSStefan Agner return NULL; 5225661f08aSStefan Agner 5235661f08aSStefan Agner memset(req->buf, 0, req->length); 5245661f08aSStefan Agner req->complete = sdp_tx_complete; 5255661f08aSStefan Agner 5265661f08aSStefan Agner return req; 5275661f08aSStefan Agner } 5285661f08aSStefan Agner static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 5295661f08aSStefan Agner { 5305661f08aSStefan Agner struct f_sdp *sdp = func_to_sdp(f); 5315661f08aSStefan Agner struct usb_composite_dev *cdev = f->config->cdev; 5325661f08aSStefan Agner int result; 5335661f08aSStefan Agner 5345661f08aSStefan Agner debug("%s: intf: %d alt: %d\n", __func__, intf, alt); 5355661f08aSStefan Agner 5365661f08aSStefan Agner result = usb_ep_enable(sdp->in_ep, &in_desc); 5375661f08aSStefan Agner if (result) 5385661f08aSStefan Agner return result; 5395661f08aSStefan Agner sdp->in_req = sdp_start_ep(sdp->in_ep); 5405661f08aSStefan Agner sdp->in_req->context = sdp; 5415661f08aSStefan Agner 5425661f08aSStefan Agner sdp->in_ep->driver_data = cdev; /* claim */ 5435661f08aSStefan Agner 5445661f08aSStefan Agner sdp->altsetting = alt; 5455661f08aSStefan Agner sdp->state = SDP_STATE_IDLE; 5465661f08aSStefan Agner 5475661f08aSStefan Agner return 0; 5485661f08aSStefan Agner } 5495661f08aSStefan Agner 5505661f08aSStefan Agner static int sdp_get_alt(struct usb_function *f, unsigned intf) 5515661f08aSStefan Agner { 5525661f08aSStefan Agner struct f_sdp *sdp = func_to_sdp(f); 5535661f08aSStefan Agner 5545661f08aSStefan Agner return sdp->altsetting; 5555661f08aSStefan Agner } 5565661f08aSStefan Agner 5575661f08aSStefan Agner static void sdp_disable(struct usb_function *f) 5585661f08aSStefan Agner { 5595661f08aSStefan Agner struct f_sdp *sdp = func_to_sdp(f); 5605661f08aSStefan Agner 5615661f08aSStefan Agner usb_ep_disable(sdp->in_ep); 5625661f08aSStefan Agner 5635661f08aSStefan Agner if (sdp->in_req) { 5645661f08aSStefan Agner free(sdp->in_req); 5655661f08aSStefan Agner sdp->in_req = NULL; 5665661f08aSStefan Agner } 5675661f08aSStefan Agner } 5685661f08aSStefan Agner 5695661f08aSStefan Agner static int sdp_bind_config(struct usb_configuration *c) 5705661f08aSStefan Agner { 5715661f08aSStefan Agner int status; 5725661f08aSStefan Agner 5735661f08aSStefan Agner if (!sdp_func) { 5745661f08aSStefan Agner sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func)); 5755661f08aSStefan Agner if (!sdp_func) 5765661f08aSStefan Agner return -ENOMEM; 5775661f08aSStefan Agner } 5785661f08aSStefan Agner 5795661f08aSStefan Agner memset(sdp_func, 0, sizeof(*sdp_func)); 5805661f08aSStefan Agner 5815661f08aSStefan Agner sdp_func->usb_function.name = "sdp"; 5825661f08aSStefan Agner sdp_func->usb_function.hs_descriptors = sdp_runtime_descs; 5835661f08aSStefan Agner sdp_func->usb_function.descriptors = sdp_runtime_descs; 5845661f08aSStefan Agner sdp_func->usb_function.bind = sdp_bind; 5855661f08aSStefan Agner sdp_func->usb_function.unbind = sdp_unbind; 5865661f08aSStefan Agner sdp_func->usb_function.set_alt = sdp_set_alt; 5875661f08aSStefan Agner sdp_func->usb_function.get_alt = sdp_get_alt; 5885661f08aSStefan Agner sdp_func->usb_function.disable = sdp_disable; 5895661f08aSStefan Agner sdp_func->usb_function.strings = sdp_generic_strings; 5905661f08aSStefan Agner sdp_func->usb_function.setup = sdp_setup; 5915661f08aSStefan Agner 5925661f08aSStefan Agner status = usb_add_function(c, &sdp_func->usb_function); 5935661f08aSStefan Agner 5945661f08aSStefan Agner return status; 5955661f08aSStefan Agner } 5965661f08aSStefan Agner 5975661f08aSStefan Agner int sdp_init(int controller_index) 5985661f08aSStefan Agner { 5995661f08aSStefan Agner printf("SDP: initialize...\n"); 6005661f08aSStefan Agner while (!sdp_func->configuration_done) { 6015661f08aSStefan Agner if (ctrlc()) { 6025661f08aSStefan Agner puts("\rCTRL+C - Operation aborted.\n"); 6035661f08aSStefan Agner return 1; 6045661f08aSStefan Agner } 6055661f08aSStefan Agner usb_gadget_handle_interrupts(controller_index); 6065661f08aSStefan Agner } 6075661f08aSStefan Agner 6085661f08aSStefan Agner return 0; 6095661f08aSStefan Agner } 6105661f08aSStefan Agner 6115661f08aSStefan Agner static u32 sdp_jump_imxheader(void *address) 6125661f08aSStefan Agner { 6135661f08aSStefan Agner flash_header_v2_t *headerv2 = address; 6145661f08aSStefan Agner ulong (*entry)(void); 6155661f08aSStefan Agner 6165661f08aSStefan Agner if (headerv2->header.tag != IVT_HEADER_TAG) { 6175661f08aSStefan Agner printf("Header Tag is not an IMX image\n"); 6185661f08aSStefan Agner return SDP_ERROR_IMXHEADER; 6195661f08aSStefan Agner } 6205661f08aSStefan Agner 6215661f08aSStefan Agner printf("Jumping to 0x%08x\n", headerv2->entry); 6225661f08aSStefan Agner entry = (void *)headerv2->entry; 6235661f08aSStefan Agner entry(); 6245661f08aSStefan Agner 6255661f08aSStefan Agner /* The image probably never returns hence we won't reach that point */ 6265661f08aSStefan Agner return 0; 6275661f08aSStefan Agner } 6285661f08aSStefan Agner 6295661f08aSStefan Agner static void sdp_handle_in_ep(void) 6305661f08aSStefan Agner { 6315661f08aSStefan Agner u8 *data = sdp_func->in_req->buf; 6325661f08aSStefan Agner u32 status; 6335661f08aSStefan Agner int datalen; 6345661f08aSStefan Agner 6355661f08aSStefan Agner switch (sdp_func->state) { 6365661f08aSStefan Agner case SDP_STATE_TX_SEC_CONF: 6375661f08aSStefan Agner debug("Report 3: HAB security\n"); 6385661f08aSStefan Agner data[0] = 3; 6395661f08aSStefan Agner 6405661f08aSStefan Agner status = SDP_SECURITY_OPEN; 6415661f08aSStefan Agner memcpy(&data[1], &status, 4); 6425661f08aSStefan Agner sdp_func->in_req->length = 5; 6435661f08aSStefan Agner usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); 6445661f08aSStefan Agner sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY; 6455661f08aSStefan Agner break; 6465661f08aSStefan Agner 6475661f08aSStefan Agner case SDP_STATE_TX_STATUS: 6485661f08aSStefan Agner debug("Report 4: Status\n"); 6495661f08aSStefan Agner data[0] = 4; 6505661f08aSStefan Agner 6515661f08aSStefan Agner memcpy(&data[1], &sdp_func->error_status, 4); 6525661f08aSStefan Agner sdp_func->in_req->length = 65; 6535661f08aSStefan Agner usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); 6545661f08aSStefan Agner sdp_func->state = SDP_STATE_TX_STATUS_BUSY; 6555661f08aSStefan Agner break; 6565661f08aSStefan Agner case SDP_STATE_TX_REGISTER: 6575661f08aSStefan Agner debug("Report 4: Register Values\n"); 6585661f08aSStefan Agner data[0] = 4; 6595661f08aSStefan Agner 6605661f08aSStefan Agner datalen = sdp_func->dnl_bytes_remaining; 6615661f08aSStefan Agner 6625661f08aSStefan Agner if (datalen > 64) 6635661f08aSStefan Agner datalen = 64; 6645661f08aSStefan Agner 6655661f08aSStefan Agner memcpy(&data[1], (void *)sdp_func->dnl_address, datalen); 6665661f08aSStefan Agner sdp_func->in_req->length = 65; 6675661f08aSStefan Agner 6685661f08aSStefan Agner sdp_func->dnl_bytes_remaining -= datalen; 6695661f08aSStefan Agner sdp_func->dnl_address += datalen; 6705661f08aSStefan Agner 6715661f08aSStefan Agner usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); 6725661f08aSStefan Agner sdp_func->state = SDP_STATE_TX_REGISTER_BUSY; 6735661f08aSStefan Agner break; 6745661f08aSStefan Agner case SDP_STATE_JUMP: 675*ccd7a4d2SStefan Agner printf("Jumping to header at 0x%08x\n", sdp_func->jmp_address); 676*ccd7a4d2SStefan Agner status = sdp_jump_imxheader((void *)sdp_func->jmp_address); 677*ccd7a4d2SStefan Agner 678*ccd7a4d2SStefan Agner /* If imx header fails, try some U-Boot specific headers */ 679*ccd7a4d2SStefan Agner if (status) { 680*ccd7a4d2SStefan Agner #ifdef CONFIG_SPL_BUILD 681*ccd7a4d2SStefan Agner /* In SPL, allow jumps to U-Boot images */ 682*ccd7a4d2SStefan Agner struct spl_image_info spl_image = {}; 683*ccd7a4d2SStefan Agner spl_parse_image_header(&spl_image, 684*ccd7a4d2SStefan Agner (struct image_header *)sdp_func->jmp_address); 685*ccd7a4d2SStefan Agner jump_to_image_no_args(&spl_image); 686*ccd7a4d2SStefan Agner #else 687*ccd7a4d2SStefan Agner /* In U-Boot, allow jumps to scripts */ 688*ccd7a4d2SStefan Agner source(sdp_func->jmp_address, "script@1"); 689*ccd7a4d2SStefan Agner #endif 690*ccd7a4d2SStefan Agner } 6915661f08aSStefan Agner 6925661f08aSStefan Agner sdp_func->next_state = SDP_STATE_IDLE; 6935661f08aSStefan Agner sdp_func->error_status = status; 6945661f08aSStefan Agner 6955661f08aSStefan Agner /* Only send Report 4 if there was an error */ 6965661f08aSStefan Agner if (status) 6975661f08aSStefan Agner sdp_func->state = SDP_STATE_TX_STATUS; 6985661f08aSStefan Agner else 6995661f08aSStefan Agner sdp_func->state = SDP_STATE_IDLE; 7005661f08aSStefan Agner break; 7015661f08aSStefan Agner default: 7025661f08aSStefan Agner break; 7035661f08aSStefan Agner }; 7045661f08aSStefan Agner } 7055661f08aSStefan Agner 7065661f08aSStefan Agner void sdp_handle(int controller_index) 7075661f08aSStefan Agner { 7085661f08aSStefan Agner printf("SDP: handle requests...\n"); 7095661f08aSStefan Agner while (1) { 7105661f08aSStefan Agner if (ctrlc()) { 7115661f08aSStefan Agner puts("\rCTRL+C - Operation aborted.\n"); 7125661f08aSStefan Agner return; 7135661f08aSStefan Agner } 7145661f08aSStefan Agner 7155661f08aSStefan Agner usb_gadget_handle_interrupts(controller_index); 7165661f08aSStefan Agner 7175661f08aSStefan Agner sdp_handle_in_ep(); 7185661f08aSStefan Agner } 7195661f08aSStefan Agner } 7205661f08aSStefan Agner 7215661f08aSStefan Agner int sdp_add(struct usb_configuration *c) 7225661f08aSStefan Agner { 7235661f08aSStefan Agner int id; 7245661f08aSStefan Agner 7255661f08aSStefan Agner id = usb_string_id(c->cdev); 7265661f08aSStefan Agner if (id < 0) 7275661f08aSStefan Agner return id; 7285661f08aSStefan Agner strings_sdp_generic[0].id = id; 7295661f08aSStefan Agner sdp_intf_runtime.iInterface = id; 7305661f08aSStefan Agner 7315661f08aSStefan Agner debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__, 7325661f08aSStefan Agner c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); 7335661f08aSStefan Agner 7345661f08aSStefan Agner return sdp_bind_config(c); 7355661f08aSStefan Agner } 7365661f08aSStefan Agner 7375661f08aSStefan Agner DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add); 738