89ab3c2761
Reported by cppcheck Change-Id: I5ac7b73f2e3eaee3d5427d118944613e572f684d
427 lines
11 KiB
C
427 lines
11 KiB
C
/* on ubuntu compile with gcc -W rkusbtool.c -o rkusbtool -lusb-1.0 -I/usr/include/libusb-1.0/ */
|
|
#include <libusb.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define VERSION "v0.1"
|
|
|
|
#define RETRY_MAX 5
|
|
#define USB_TIMEOUT 512
|
|
#define VENDORID 0x071b
|
|
#define PRODUCTID 0x3203
|
|
|
|
#define OUT_EP 0x01
|
|
#define IN_EP 0x82
|
|
|
|
#define CBW_SIGNATURE 0x43425355
|
|
#define CSW_SIGNATURE 0x53425355
|
|
#define SCSICMD_READ_12 0xa8
|
|
|
|
/* rockchip specific commands */
|
|
#define RK_CMD 0xe0
|
|
#define RK_GET_VERSION 0xffffffff
|
|
#define RK_SWITCH_ROCKUSB 0xfeffffff
|
|
#define RK_CHECK_USB 0xfdffffff
|
|
#define RK_OPEN_SYSDISK 0xfcffffff
|
|
|
|
enum {
|
|
NONE = 0,
|
|
INFO = 1,
|
|
RKUSB = 2,
|
|
SYSDISK = 4,
|
|
CHECKUSB = 8
|
|
};
|
|
|
|
enum {
|
|
COMMAND_PASSED = 0,
|
|
COMMAND_FAILED = 1,
|
|
PHASE_ERROR = 2
|
|
};
|
|
|
|
struct CBWCB_t
|
|
{
|
|
uint8_t cbCode;
|
|
uint8_t cbLun;
|
|
uint32_t LBA;
|
|
uint32_t cbLen;
|
|
uint8_t reseved;
|
|
uint8_t control;
|
|
} __attribute__((__packed__));
|
|
|
|
struct CBW_t
|
|
{
|
|
uint32_t dCBWSignature;
|
|
uint32_t dCBWTag;
|
|
uint32_t dCBWDataTransferLength;
|
|
uint8_t bmCBWFlags;
|
|
uint8_t bCBWLUN;
|
|
uint8_t bCBWCBLength;
|
|
uint8_t CBWCB[16];
|
|
} __attribute__((__packed__));
|
|
|
|
struct CSW_t
|
|
{
|
|
uint32_t dCSWSignature;
|
|
uint32_t dCSWTag;
|
|
uint32_t dCSWDataResidue;
|
|
uint8_t bCSWStatus;
|
|
} __attribute__((__packed__));
|
|
|
|
static int send_msc_cmd(libusb_device_handle *hdev, struct CBWCB_t *cbwcb, uint32_t data_len, uint32_t *reftag)
|
|
{
|
|
struct CBW_t cbw;
|
|
int ret, repeat, transferred;
|
|
static uint32_t tag = 0xdaefbc01;
|
|
|
|
repeat = 0;
|
|
memset(&cbw, 0, sizeof(cbw));
|
|
cbw.dCBWSignature = CBW_SIGNATURE;
|
|
cbw.dCBWTag = tag++;
|
|
cbw.dCBWDataTransferLength = data_len;
|
|
cbw.bmCBWFlags = 0x80; /* device to host */
|
|
cbw.bCBWLUN = 0;
|
|
cbw.bCBWCBLength = sizeof(struct CBWCB_t);
|
|
memcpy(cbw.CBWCB, cbwcb, sizeof(struct CBWCB_t));
|
|
|
|
*reftag = cbw.dCBWTag;
|
|
do
|
|
{
|
|
/* transfer command to the device */
|
|
ret = libusb_bulk_transfer(hdev, OUT_EP, (unsigned char*)&cbw, 31, &transferred, USB_TIMEOUT);
|
|
if (ret == LIBUSB_ERROR_PIPE)
|
|
{
|
|
libusb_clear_halt(hdev, OUT_EP);
|
|
}
|
|
repeat++;
|
|
} while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX));
|
|
|
|
if (ret != LIBUSB_SUCCESS)
|
|
{
|
|
printf("error: command transfer error\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_msc_csw(libusb_device_handle *hdev, uint32_t reftag)
|
|
{
|
|
struct CSW_t csw;
|
|
int ret, repeat, transferred;
|
|
|
|
/* get CSW response from device */
|
|
repeat = 0;
|
|
do
|
|
{
|
|
ret = libusb_bulk_transfer(hdev, IN_EP, (unsigned char *)&csw, 13, &transferred, USB_TIMEOUT);
|
|
if (ret == LIBUSB_ERROR_PIPE)
|
|
{
|
|
libusb_clear_halt(hdev, IN_EP);
|
|
}
|
|
repeat++;
|
|
} while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX));
|
|
|
|
if (ret != LIBUSB_SUCCESS)
|
|
{
|
|
printf("error reading CSW\n");
|
|
return -3;
|
|
}
|
|
|
|
if (transferred != 13)
|
|
{
|
|
printf("error wrong size of CSW packet\n");
|
|
return -4;
|
|
}
|
|
|
|
if (csw.dCSWSignature != CSW_SIGNATURE)
|
|
{
|
|
printf("error: wrong CSW signature.\n");
|
|
return -5;
|
|
}
|
|
|
|
if (csw.dCSWTag != reftag)
|
|
{
|
|
printf("error: CSW dCSWTag mismatch\n");
|
|
return -6;
|
|
}
|
|
|
|
if (csw.bCSWStatus)
|
|
{
|
|
/* In case of CSW indicating error dump the content of the packet */
|
|
printf ("dCSWSignature: 0x%0x\n", csw.dCSWSignature);
|
|
printf ("dCSWTag: 0x%0x\n", csw.dCSWTag);
|
|
printf ("dCSWDataResidue: 0x%0x\n", csw.dCSWDataResidue);
|
|
printf ("bCSWStatus: 0x%0x\n", csw.bCSWStatus);
|
|
}
|
|
|
|
return csw.bCSWStatus;
|
|
}
|
|
|
|
static int rk_cmd(libusb_device_handle *hdev, uint32_t command, uint8_t *buf, uint8_t len)
|
|
{
|
|
struct CBWCB_t cbwcb;
|
|
int ret, transferred;
|
|
uint32_t reftag;
|
|
|
|
/* enter command */
|
|
memset(&cbwcb, 0, sizeof(cbwcb));
|
|
cbwcb.cbCode = SCSICMD_READ_12;
|
|
cbwcb.cbLun = RK_CMD;
|
|
cbwcb.LBA = command; /* RK_GET_VERSION, RK_OPEN_SYSDISK, RK_SWITCH_ROCKUSB */
|
|
cbwcb.cbLen = len; /* size of transfer in response to this command */
|
|
|
|
ret = send_msc_cmd(hdev, &cbwcb, len, &reftag);
|
|
|
|
/* get the response */
|
|
if (len > 0)
|
|
{
|
|
ret = libusb_bulk_transfer(hdev, IN_EP, buf, len, &transferred, USB_TIMEOUT);
|
|
if (ret != LIBUSB_SUCCESS || transferred != len)
|
|
{
|
|
printf("error: reading response data failed\n");
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
return get_msc_csw(hdev, reftag);
|
|
}
|
|
|
|
static int get_sense(libusb_device_handle *hdev)
|
|
{
|
|
struct CBWCB_t cbwcb;
|
|
unsigned char sense[0x12];
|
|
int size;
|
|
uint32_t reftag;
|
|
|
|
memset(&cbwcb, 0, sizeof(cbwcb));
|
|
cbwcb.cbCode = 0x03;
|
|
cbwcb.cbLun = 0;
|
|
cbwcb.LBA = 0;
|
|
cbwcb.cbLen = 0x12;
|
|
|
|
send_msc_cmd(hdev, &cbwcb, 0x12, &reftag);
|
|
libusb_bulk_transfer(hdev, IN_EP, (unsigned char*)&sense, 0x12, &size, USB_TIMEOUT);
|
|
|
|
return get_msc_csw(hdev, reftag);
|
|
}
|
|
|
|
static void usage(void)
|
|
{
|
|
printf("Usage: rkusbtool [options]\n");
|
|
printf("-h|--help This help message\n");
|
|
printf("-i|--info Get version string from the device\n");
|
|
printf("-d|--dfu Put device into DFU mode\n");
|
|
printf("-s|--sysdisk Open system disk\n");
|
|
printf("-c|--checkusb Check if dev is in System or Loader USB mode\n");
|
|
printf("-u|--usb <p>:<v> Override device PID and PIVD\n");
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
libusb_device_handle *hdev;
|
|
int ret;
|
|
int i = 1, action = NONE;
|
|
uint32_t ver[3];
|
|
uint16_t pid = PRODUCTID;
|
|
uint16_t vid = VENDORID;
|
|
|
|
if (argc < 2)
|
|
{
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
/* print banner */
|
|
fprintf(stderr,"rkusbtool " VERSION "\n");
|
|
fprintf(stderr,"(C) Marcin Bukat 2011\n");
|
|
fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n");
|
|
fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
|
|
|
|
/* arguments handling */
|
|
while (i < argc)
|
|
{
|
|
if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0))
|
|
{
|
|
action |= INFO;
|
|
}
|
|
else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--dfu")==0))
|
|
{
|
|
action |= RKUSB;
|
|
}
|
|
else if ((strcmp(argv[i],"-s")==0) || (strcmp(argv[i],"--sysdisk")==0))
|
|
{
|
|
action |= SYSDISK;
|
|
}
|
|
else if ((strcmp(argv[i],"-c")==0) || (strcmp(argv[i],"--checkusb")==0))
|
|
{
|
|
action |= CHECKUSB;
|
|
}
|
|
else if ((strcmp(argv[i],"-u")==0) || (strcmp(argv[i],"--usb")==0))
|
|
{
|
|
if (i + 1 == argc)
|
|
{
|
|
fprintf(stderr,"Missing argument for USB IDs\n");
|
|
return -1;
|
|
}
|
|
char *svid = argv[i + 1];
|
|
char *spid = strchr(svid, ':');
|
|
if(svid == NULL)
|
|
{
|
|
fprintf(stderr,"Invalid argument for USB IDs (missing ':')\n");
|
|
return -2;
|
|
}
|
|
char *end;
|
|
vid = strtoul(svid, &end, 0);
|
|
if(*end != ':')
|
|
{
|
|
fprintf(stderr,"Invalid argument for USB VID\n");
|
|
return -3;
|
|
}
|
|
pid = strtoul(spid + 1, &end, 0);
|
|
if(*end)
|
|
{
|
|
fprintf(stderr,"Invalid argument for USB PID\n");
|
|
return -4;
|
|
}
|
|
i++;
|
|
}
|
|
else if((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0))
|
|
{
|
|
usage();
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,"Unknown argument '%s'\n", argv[i]);
|
|
return -5;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/* initialize libusb */
|
|
libusb_init(NULL);
|
|
/* usb_set_debug(2); */
|
|
|
|
hdev = libusb_open_device_with_vid_pid(NULL, vid, pid);
|
|
if (hdev == NULL)
|
|
{
|
|
printf("error: can't open device\n");
|
|
return -10;
|
|
}
|
|
|
|
ret = libusb_kernel_driver_active(hdev, 0);
|
|
|
|
if (ret < 0)
|
|
{
|
|
printf ("error checking kernel driver active\n");
|
|
libusb_close(hdev);
|
|
return -3;
|
|
}
|
|
else
|
|
{
|
|
if (ret)
|
|
libusb_detach_kernel_driver(hdev, 0);
|
|
}
|
|
|
|
ret = libusb_set_configuration(hdev, 1);
|
|
if (ret < 0)
|
|
{
|
|
printf("error: could not select configuration (1)\n");
|
|
libusb_close(hdev);
|
|
return -3;
|
|
}
|
|
|
|
ret = libusb_claim_interface(hdev, 0);
|
|
if (ret < 0)
|
|
{
|
|
printf("error: could not claim interface #0\n");
|
|
libusb_close(hdev);
|
|
return -11;
|
|
}
|
|
|
|
ret = libusb_set_interface_alt_setting(hdev, 0, 0);
|
|
if ( ret != LIBUSB_SUCCESS)
|
|
{
|
|
printf("error: could not set alt setting for interface #0\n");
|
|
libusb_close(hdev);
|
|
return -11;
|
|
}
|
|
|
|
/* BulkOnly reset */
|
|
//ret = libusb_control_transfer(hdev, 0x21, 0xff, 0, 0, NULL, 0, USB_TIMEOUT);
|
|
|
|
/* BulkOnly get max lun */
|
|
//ret = libusb_control_transfer(hdev, 0xa1, 0xfe, 0, 0, &maxlun, 1, USB_TIMEOUT);
|
|
|
|
/* Devices that do not support multiple LUNs may STALL this command. */
|
|
//if (ret == 0)
|
|
// maxlun = -1;
|
|
|
|
//printf("MAXLUN: %d\n", maxlun);
|
|
|
|
get_sense(hdev);
|
|
|
|
if (action & INFO)
|
|
{
|
|
ret = rk_cmd(hdev, RK_GET_VERSION, (uint8_t *)ver, 12);
|
|
|
|
if (ret)
|
|
{
|
|
printf("error sending RK_GET_VERSION command. Err 0x%0x\n", ret);
|
|
libusb_close(hdev);
|
|
return ret;
|
|
}
|
|
|
|
printf("Rockchip device info:\n");
|
|
printf("loader ver: %x.%x\n", (ver[0]>>16)&0xff, ver[0]&0xff);
|
|
printf("kernel ver: %x.%x\n", (ver[1]>>16)&0xff, ver[1]&0xff);
|
|
printf("sdk ver: %x.%x\n", (ver[2]>>16)&0xff, ver[2]&0xff);
|
|
}
|
|
|
|
if (action & CHECKUSB)
|
|
{
|
|
printf("Checking USB mode...\n");
|
|
ret = rk_cmd(hdev, RK_CHECK_USB, (uint8_t *)ver, 1);
|
|
|
|
//if (ret)
|
|
//{
|
|
// libusb_close(hdev);
|
|
// return ret;
|
|
//}
|
|
|
|
if (*(char *)ver)
|
|
printf("The device is in Loader USB mode\n");
|
|
else
|
|
printf("The device is in System USB mode\n");
|
|
}
|
|
|
|
if (action & SYSDISK)
|
|
{
|
|
printf("Opening system disk...\n");
|
|
ret = rk_cmd(hdev, RK_OPEN_SYSDISK, NULL, 0);
|
|
|
|
if (ret)
|
|
{
|
|
libusb_close(hdev);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (action & RKUSB)
|
|
{
|
|
printf("Switching into rk DFU mode...\n");
|
|
ret = rk_cmd(hdev, RK_SWITCH_ROCKUSB, NULL, 0);
|
|
|
|
if (ret)
|
|
{
|
|
libusb_close(hdev);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
libusb_close(hdev);
|
|
libusb_exit(NULL);
|
|
return 0;
|
|
}
|