forked from len0rd/rockbox
imxtools/sbloader: rewrite hid code
Rewrite code with proper documentation: it uses a UMS like CBW/CSW to wrap commands and status. Change-Id: I10476c87aaea96d4b9e54f8c1c266835c8e89721
This commit is contained in:
parent
1c63993e05
commit
468aa959c7
1 changed files with 121 additions and 37 deletions
|
|
@ -36,14 +36,6 @@ bool g_debug = false;
|
||||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void put32le(uint8_t *buf, uint32_t i)
|
|
||||||
{
|
|
||||||
*buf++ = i & 0xff;
|
|
||||||
*buf++ = (i >> 8) & 0xff;
|
|
||||||
*buf++ = (i >> 16) & 0xff;
|
|
||||||
*buf++ = (i >> 24) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void put32be(uint8_t *buf, uint32_t i)
|
static void put32be(uint8_t *buf, uint32_t i)
|
||||||
{
|
{
|
||||||
*buf++ = (i >> 24) & 0xff;
|
*buf++ = (i >> 24) & 0xff;
|
||||||
|
|
@ -75,44 +67,101 @@ struct dev_info_t g_dev_info[] =
|
||||||
{0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */
|
{0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Command Block Descriptor (CDB) */
|
||||||
|
struct hid_cdb_t
|
||||||
|
{
|
||||||
|
uint8_t command;
|
||||||
|
uint32_t length; // big-endian!
|
||||||
|
uint8_t reserved[11];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// command
|
||||||
|
#define BLTC_DOWNLOAD_FW 2
|
||||||
|
|
||||||
|
/* Command Block Wrapper (CBW) */
|
||||||
|
struct hid_cbw_t
|
||||||
|
{
|
||||||
|
uint32_t signature; // BLTC or PITC
|
||||||
|
uint32_t tag; // returned in CSW
|
||||||
|
uint32_t length; // number of bytes to transfer
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t reserved[2];
|
||||||
|
struct hid_cdb_t cdb;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* HID Command Report */
|
||||||
|
struct hid_cmd_report_t
|
||||||
|
{
|
||||||
|
uint8_t report_id;
|
||||||
|
struct hid_cbw_t cbw;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// report id
|
||||||
|
#define HID_BLTC_DATA_REPORT 2
|
||||||
|
#define HID_BLTC_CMD_REPORT 1
|
||||||
|
|
||||||
|
// signature
|
||||||
|
#define CBW_BLTC 0x43544C42 /* "BLTC" */
|
||||||
|
#define CBW_PITC 0x43544950 /* "PITC" */
|
||||||
|
// flags
|
||||||
|
#define CBW_DIR_IN 0x80
|
||||||
|
#define CBW_DIR_OUT 0x00
|
||||||
|
|
||||||
|
/* Command Status Wrapper (CSW) */
|
||||||
|
struct hid_csw_t
|
||||||
|
{
|
||||||
|
uint32_t signature; // BLTS or PITS
|
||||||
|
uint32_t tag; // given in CBW
|
||||||
|
uint32_t residue; // number of bytes not transferred
|
||||||
|
uint8_t status;
|
||||||
|
} __attribute__((packed));
|
||||||
|
// signature
|
||||||
|
#define CSW_BLTS 0x53544C42 /* "BLTS" */
|
||||||
|
#define CSW_PITS 0x53544950 /* "PITS" */
|
||||||
|
// status
|
||||||
|
#define CSW_PASSED 0x00
|
||||||
|
#define CSW_FAILED 0x01
|
||||||
|
#define CSW_PHASE_ERROR 0x02
|
||||||
|
|
||||||
|
/* HID Status Report */
|
||||||
|
struct hid_status_report_t
|
||||||
|
{
|
||||||
|
uint8_t report_id;
|
||||||
|
struct hid_csw_t csw;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define HID_BLTC_STATUS_REPORT 4
|
||||||
|
|
||||||
static int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers)
|
static int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers)
|
||||||
{
|
{
|
||||||
libusb_detach_kernel_driver(dev, 0);
|
libusb_detach_kernel_driver(dev, 0);
|
||||||
libusb_claim_interface(dev, 0);
|
libusb_claim_interface(dev, 0);
|
||||||
|
|
||||||
|
int recv_size;
|
||||||
|
uint32_t my_tag = 0xcafebabe;
|
||||||
uint8_t *xfer_buf = malloc(1 + xfer_size);
|
uint8_t *xfer_buf = malloc(1 + xfer_size);
|
||||||
uint8_t *p = xfer_buf;
|
struct hid_cmd_report_t cmd;
|
||||||
|
memset(&cmd, 0, sizeof(cmd));
|
||||||
*p++ = 0x01; /* Report id */
|
cmd.report_id = HID_BLTC_CMD_REPORT;
|
||||||
|
cmd.cbw.signature = CBW_BLTC;
|
||||||
/* Command block wrapper */
|
cmd.cbw.tag = my_tag;
|
||||||
*p++ = 'B'; /* Signature */
|
cmd.cbw.length = size;
|
||||||
*p++ = 'L';
|
cmd.cbw.flags = CBW_DIR_OUT;
|
||||||
*p++ = 'T';
|
cmd.cbw.cdb.command = BLTC_DOWNLOAD_FW;
|
||||||
*p++ = 'C';
|
put32be((void *)&cmd.cbw.cdb.length, size);
|
||||||
put32le(p, 0x1); /* Tag */
|
|
||||||
p += 4;
|
|
||||||
put32le(p, size); /* Payload size */
|
|
||||||
p += 4;
|
|
||||||
*p++ = 0; /* Flags (host to device) */
|
|
||||||
p += 2; /* Reserved */
|
|
||||||
|
|
||||||
/* Command descriptor block */
|
|
||||||
*p++ = 0x02; /* Firmware download */
|
|
||||||
put32be(p, size); /* Download size */
|
|
||||||
|
|
||||||
int ret = libusb_control_transfer(dev,
|
int ret = libusb_control_transfer(dev,
|
||||||
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0x9, 0x201, 0,
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0x9, 0x201, 0,
|
||||||
xfer_buf, xfer_size + 1, 1000);
|
(void *)&cmd, sizeof(cmd), 1000);
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf("transfer error at init step\n");
|
printf("transfer error at init step\n");
|
||||||
return 1;
|
goto Lstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < nr_xfers; i++)
|
for(int i = 0; i < nr_xfers; i++)
|
||||||
{
|
{
|
||||||
xfer_buf[0] = 0x2;
|
xfer_buf[0] = HID_BLTC_DATA_REPORT;
|
||||||
memcpy(&xfer_buf[1], &data[i * xfer_size], xfer_size);
|
memcpy(&xfer_buf[1], &data[i * xfer_size], xfer_size);
|
||||||
|
|
||||||
ret = libusb_control_transfer(dev,
|
ret = libusb_control_transfer(dev,
|
||||||
|
|
@ -121,18 +170,53 @@ static int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int
|
||||||
if(ret < 0)
|
if(ret < 0)
|
||||||
{
|
{
|
||||||
printf("transfer error at send step %d\n", i);
|
printf("transfer error at send step %d\n", i);
|
||||||
return 1;
|
goto Lstatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int recv_size;
|
Lstatus:
|
||||||
ret = libusb_interrupt_transfer(dev, 0x81, xfer_buf, xfer_size, &recv_size,
|
ret = libusb_interrupt_transfer(dev, 0x81, xfer_buf, xfer_size,
|
||||||
1000);
|
&recv_size, 1000);
|
||||||
if(ret < 0)
|
if(ret == 0 && recv_size == sizeof(struct hid_status_report_t))
|
||||||
{
|
{
|
||||||
printf("transfer error at final stage\n");
|
struct hid_status_report_t *report = (void *)xfer_buf;
|
||||||
return 1;
|
if(report->report_id != HID_BLTC_STATUS_REPORT)
|
||||||
|
{
|
||||||
|
printf("Error: got non-status report\n");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
if(report->csw.signature != CSW_BLTS)
|
||||||
|
{
|
||||||
|
printf("Error: status report signature mismatch\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if(report->csw.tag != my_tag)
|
||||||
|
{
|
||||||
|
printf("Error: status report tag mismtahc\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if(report->csw.residue != 0)
|
||||||
|
printf("Warning: %d byte were not transferred\n", report->csw.residue);
|
||||||
|
switch(report->csw.status)
|
||||||
|
{
|
||||||
|
case CSW_PASSED:
|
||||||
|
printf("Status: Passed\n");
|
||||||
|
return 0;
|
||||||
|
case CSW_FAILED:
|
||||||
|
printf("Status: Failed\n");
|
||||||
|
return -1;
|
||||||
|
case CSW_PHASE_ERROR:
|
||||||
|
printf("Status: Phase Error\n");
|
||||||
|
return -2;
|
||||||
|
default:
|
||||||
|
printf("Status: Unknown Error\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ret < 0)
|
||||||
|
printf("Error: cannot get status report\n");
|
||||||
|
else
|
||||||
|
printf("Error: status report has wrong size\n");
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue