diff --git a/Makefile b/Makefile index 728b14e..f13ace6 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,11 @@ -CC = gcc -m32 +CC = gcc VERSION = 1.0 -CFLAGS = -g -O0 -Wall -Werror -DVERSION='"$(VERSION)"' +CFLAGS = -g -O -Wall -Werror -DVERSION='"$(VERSION)"' LDFLAGS = -g -OBJS = main.o util.o radio.o uv380.o md380.o -SRCS = main.c util.c radio.c uv380.c md380.c -LIBS = +OBJS = main.o util.o radio.o dfu.o uv380.o md380.o +LIBS = -lusb-1.0 # Mac OS X #CFLAGS += -I/usr/local/opt/gettext/include @@ -28,6 +27,7 @@ dmrconfig.linux: dmrconfig strip $@ ### +dfu.o: dfu.c util.h main.o: main.c radio.h util.h md380.o: md380.c radio.h util.h radio.o: radio.c radio.h util.h diff --git a/dfu.c b/dfu.c new file mode 100644 index 0000000..718b991 --- /dev/null +++ b/dfu.c @@ -0,0 +1,384 @@ +/* + * DFU routines. + * + * Copyright (C) 2018 Serge Vakulenko, KK6ABQ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include "util.h" + +// +// All DFU based Commands +// +#define REQUEST_TYPE_TO_HOST 0xA1 +#define REQUEST_TYPE_TO_DEVICE 0x21 + +enum { + REQUEST_DETACH = 0, + REQUEST_DNLOAD = 1, + REQUEST_UPLOAD = 2, + REQUEST_GETSTATUS = 3, + REQUEST_CLRSTATUS = 4, + REQUEST_GETSTATE = 5, + REQUEST_ABORT = 6, +}; + +enum { + statusOK = 0, + errTARGET = 0x01, + errFILE = 0x02, + errWRITE = 0x03, + errERASE = 0x04, + errCHECK_ERASED = 0x05, + erPROG = 0x06, + errVERIFY = 0x07, + errADDRESS = 0x08, + errNOTDONE = 0x09, + errFIRMWARE = 0x0A, + errVENDOR = 0x0B, + errUSBR = 0x0C, + errPOR = 0x0D, + errUNKNOWN = 0x0E, + errSTALLEDPK = 0x0F, +}; + +enum { + appIDLE = 0, + appDETACH = 1, + dfuIDLE = 2, + dfuDNLOAD_SYNC = 3, + dfuDNBUSY = 4, + dfuDNLOAD_IDLE = 5, + dfuMANIFEST_SYNC = 6, + dfuMANIFEST = 7, + dfuMANIFEST_WAIT_RESET = 8, + dfuUPLOAD_IDLE = 9, + dfuERROR = 10, +}; + +enum { + USB_OK = 0, + USB_IO = -1, + USB_INVALID_PARAM = -2, + USB_ACCESS = -3, + USB_NO_DEVICE = -4, + USB_NOT_FOUND = -5, + USB_BUSY = -6, + USB_TIMEOUT = -7, + USB_OVERFLOW = -8, + USB_PIPE = -9, + USB_INTERRUPTED = -10, + USB_NO_MEM = -11, + USB_NOT_SUPPORTED = -12, + USB_OTHER = -99 +}; + +typedef struct { + unsigned status : 8; + unsigned poll_timeout : 24; + unsigned state : 8; + unsigned string_index : 8; +} status_t; + +static libusb_context *ctx = NULL; +static libusb_device_handle *dev; +static status_t status; + +static int detach(int timeout) +{ + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_DETACH, timeout, 0, NULL, 0, 0); + return error; +} + +static int get_status() +{ + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_HOST, + REQUEST_GETSTATUS, 0, 0, (unsigned char*)&status, 6, 0); + return error; +} + +static int clear_status() +{ + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_CLRSTATUS, 0, 0, NULL, 0, 0); + return error; +} + +static int get_state(int *state) +{ + unsigned char state_buf; + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_HOST, + REQUEST_GETSTATE, 0, 0, &state_buf, 1, 0); + *state = state_buf; + return error; +} + +static int dfu_abort() +{ + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_ABORT, 0, 0, NULL, 0, 0); + return error; +} + +static void wait_dfu_idle() +{ + int state, error; + + for (;;) { + error = get_state(&state); + if (error < 0) { + fprintf(stderr, "%s: cannot get state: %d: %s\n", + __func__, error, libusb_strerror(error)); + exit(-1); + } + + switch (state) { + case dfuIDLE: + return; + + case appIDLE: + error = detach(1000); + break; + + case dfuERROR: + error = clear_status(); + break; + + case appDETACH: + case dfuDNBUSY: + case dfuMANIFEST_WAIT_RESET: + usleep(100000); + continue; + + default: + error = dfu_abort(); + break; + } + + if (error < 0) { + fprintf(stderr, "%s: unexpected usb error in state=%d: %d: %s\n", + __func__, state, error, libusb_strerror(error)); + exit(-1); + } + } +} + +static void md380_command(uint8_t a, uint8_t b) +{ + unsigned char cmd[2] = { a, b }; + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_DNLOAD, 0, 0, cmd, 2, 0); + if (error < 0) { + fprintf(stderr, "%s: cannot send command: %d: %s\n", + __func__, error, libusb_strerror(error)); + exit(-1); + } + get_status(); + usleep(100000); + wait_dfu_idle(); +} + +static void set_address(uint32_t address) +{ + unsigned char cmd[5] = { 0x21, + (uint8_t)address, + (uint8_t)(address >> 8), + (uint8_t)(address >> 16), + (uint8_t)(address >> 24), + }; + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_DNLOAD, 0, 0, cmd, 5, 0); + if (error < 0) { + fprintf(stderr, "%s: cannot send command: %d: %s\n", + __func__, error, libusb_strerror(error)); + exit(-1); + } + get_status(); + wait_dfu_idle(); +} + +static void erase_block(uint32_t address) +{ + unsigned char cmd[5] = { 0x41, + (uint8_t)address, + (uint8_t)(address >> 8), + (uint8_t)(address >> 16), + (uint8_t)(address >> 24), + }; + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_DNLOAD, 0, 0, cmd, 5, 0); + if (error < 0) { + fprintf(stderr, "%s: cannot send command: %d: %s\n", + __func__, error, libusb_strerror(error)); + exit(-1); + } + get_status(); + wait_dfu_idle(); +} + +void dfu_init(unsigned vid, unsigned pid, int write_nbytes) +{ + int error = libusb_init(&ctx); + if (error < 0) { + fprintf(stderr, "libusb init failed: %d: %s\n", + error, libusb_strerror(error)); + exit(-1); + } + + dev = libusb_open_device_with_vid_pid(ctx, vid, pid); + if (!dev) { + fprintf(stderr, "Cannot find USB device %04x:%04x\n", + vid, pid); + libusb_exit(ctx); + ctx = 0; + exit(-1); + } + if (libusb_kernel_driver_active(dev, 0)) { + libusb_detach_kernel_driver(dev, 0); + } + + error = libusb_claim_interface(dev, 0); + if (error < 0) { + fprintf(stderr, "Failed to claim USB interface: %d: %s\n", + error, libusb_strerror(error)); + libusb_close(dev); + libusb_exit(ctx); + ctx = 0; + exit(-1); + } + + wait_dfu_idle(); + + // Enter Programming Mode. + md380_command(0x91, 0x01); + + if (write_nbytes > 0) { + erase_block(0x00000000); + erase_block(0x00010000); + erase_block(0x00020000); + erase_block(0x00030000); + + if (write_nbytes > 256*1024) { + erase_block(0x00110000); + erase_block(0x00120000); + erase_block(0x00130000); + erase_block(0x00140000); + erase_block(0x00150000); + erase_block(0x00160000); + erase_block(0x00170000); + erase_block(0x00180000); + erase_block(0x00190000); + erase_block(0x001a0000); + erase_block(0x001b0000); + erase_block(0x001c0000); + erase_block(0x001d0000); + } + } + + // Zero address. + set_address(0x00000000); +} + +void dfu_close() +{ + if (ctx) { + libusb_release_interface(dev, 0); + libusb_close(dev); + libusb_exit(ctx); + ctx = 0; + } +} + +void dfu_read_block(int bno, unsigned char *data, int nbytes) +{ + if (bno >= 256) + bno += 832; + + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_HOST, + REQUEST_UPLOAD, bno+2, 0, data, nbytes, 0); + if (error < 0) { + fprintf(stderr, "%s: cannot read block %d, nbytes = %d: %d: %s\n", + __func__, bno, nbytes, error, libusb_strerror(error)); + exit(-1); + } + + get_status(); +} + +void dfu_write_block(int bno, unsigned char *data, int nbytes) +{ + if (bno >= 256) + bno += 832; + + int error = libusb_control_transfer(dev, REQUEST_TYPE_TO_DEVICE, + REQUEST_DNLOAD, bno+2, 0, data, nbytes, 0); + if (error < 0) { + fprintf(stderr, "%s: cannot write block %d, nbytes = %d: %d: %s\n", + __func__, bno, nbytes, error, libusb_strerror(error)); + exit(-1); + } + + get_status(); +} + +#if 0 +void device_to_file(const char *filename) +{ + FILE *out = fopen(filename, "w"); + if (! out) { + perror(filename); + exit(-1); + } + + int bno; + for (bno=0; bno<832; bno++) { + uint8_t data[1024]; + dfu_read_block(bno, data, 1024); + + if (fwrite(data, 1, 1024, out) != 1024) { + printf("\n"); + fprintf(stderr, "%s: cannot write output image!\n", __func__); + exit(-1); + } + printf("."); + fflush(stdout); + } + printf("\n"); + fclose(out); +} + +int main() +{ + dfu_init(0x0483, 0xdf11, 0); + + device_to_file("output.img"); + + dfu_close(); +} +#endif diff --git a/util.h b/util.h index c8fd4c8..08bd8a1 100644 --- a/util.h +++ b/util.h @@ -77,6 +77,14 @@ int serial_read(int fd, unsigned char *data, int len); // void serial_write(int fd, const void *data, int len); +// +// DFU functions. +// +void dfu_init(unsigned vid, unsigned pid, int write_nbytes); +void dfu_close(void); +void dfu_read_block(int bno, unsigned char *data, int nbytes); +void dfu_write_block(int bno, unsigned char *data, int nbytes); + // // Delay in milliseconds. //