rockbox/utils/rknanoutils/rkload/rkloader.c
Amaury Pouly 6ea48cf92b rknanoutils: add dfu tool
This tool can upload a firmware to the device in DFU mode. The
protocol is the same as the rk27xx devices except that it can
load a bigger (unlimited ?) firmware.

Change-Id: Ic9d4c5087629a9156f9d5d5cdc80767e6359c431
2013-03-21 19:02:02 +01:00

219 lines
5.6 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2013 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb.h>
#include <stdint.h>
#include <stdbool.h>
#include <getopt.h>
bool g_debug = false;
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
/* shamelessly copied from rk27xx utils */
static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
{
uint8_t key[] =
{
0x7C, 0x4E, 0x03, 0x04,
0x55, 0x05, 0x09, 0x07,
0x2D, 0x2C, 0x7B, 0x38,
0x17, 0x0D, 0x17, 0x11
};
int i, i3, x, val, idx;
uint8_t key1[0x100];
uint8_t key2[0x100];
for (i=0; i < 0x100; i++)
{
key1[i] = i;
key2[i] = key[i & 0xf];
}
i3 = 0;
for (i=0; i < 0x100; i++)
{
x = key1[i];
i3 = key1[i] + i3;
i3 += key2[i];
i3 &= 0xff;
key1[i] = key1[i3];
key1[i3] = x;
}
idx = 0;
for (i=0; i < size; i++)
{
x = key1[(i + 1) & 0xff];
val = x;
idx = (x + idx) & 0xff;
key1[(i + 1) & 0xff] = key1[idx];
key1[idx] = (x & 0xff);
val = (key1[(i + 1)&0xff] + x) & 0xff;
val = key1[val];
outpg[i] = val ^ inpg[i];
}
}
static uint16_t compute_crc(uint8_t *buf, int size)
{
uint16_t result = 65535;
for(; size; buf++, size--)
{
for(int bit = 128; bit; bit >>= 1)
{
if(result & 0x8000)
result = (2 * result) ^ 0x1021;
else
result *= 2;
if(*buf & bit)
result ^= 0x1021;
}
}
return result;
}
int send_dfu(libusb_device_handle *dev, void *buffer, long size)
{
/* FIXME I never tried but if the image size is a multiple of 4096 we
* probably need to send a last zero length packet for the DFU to boot */
while(size >= 0)
{
int xfer = MIN(size, 4096);
if(g_debug)
printf("[rkloader] send %d bytes\n", xfer);
int ret = libusb_control_transfer(dev,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 12, 0, 1137,
buffer, xfer, 1000);
if(ret < 0)
{
fprintf(stderr, "transfer error: %d\n", ret);
return 1;
}
buffer += xfer;
size -= xfer;
if(xfer != 4096)
break;
}
return 0;
}
static void usage(void)
{
printf("usage: rkload [options] <file>\n");
printf("options:\n");
printf(" --help/-? Display this help\n");
printf(" --debug/-d Enable debug output\n");
printf(" --encode/-e Encode file before sending it\n");
}
int main(int argc, char **argv)
{
bool encode = false;
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, '?'},
{"debug", no_argument, 0, 'd'},
{"encode", no_argument, 0, 'e'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "?de", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case 'd':
g_debug = true;
break;
case '?':
usage();
break;
case 'e':
encode = true;
break;
default:
abort();
}
}
if(argc - optind != 1)
{
usage();
return 1;
}
libusb_init(NULL);
libusb_set_debug(NULL, 3);
if(g_debug)
printf("[rkloader] opening device...\n");
libusb_device_handle *dev = libusb_open_device_with_vid_pid(NULL, 0x071b, 0x3226);
if(dev == NULL)
return fprintf(stderr, "No device found\n");
if(g_debug)
printf("[rkloader] loading file...\n");
FILE *f = fopen(argv[optind], "rb");
if(f == NULL)
return fprintf(stderr, "Cannot open file for reading: %m\n");
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
/* allocate two more bytes for the crc */
void *buffer = malloc(size + 2);
fread(buffer, size, 1, f);
fclose(f);
/* encode buffer if needed */
if(encode)
{
if(g_debug)
printf("[rkloader] encoding buffer...\n");
encode_page(buffer, buffer, size);
}
/* compute crc */
if(g_debug)
printf("[rkloader] computing crc...\n");
uint16_t crc = compute_crc(buffer, size);
*(uint8_t *)(buffer + size) = crc >> 8;
*(uint8_t *)(buffer + size + 1) = crc & 0xff;
/* send buffer */
if(g_debug)
printf("[rkloader] sending buffer...\n");
int ret = send_dfu(dev, buffer, size + 2);
free(buffer);
libusb_close(dev);
return ret;
}