diff --git a/flash-tool/Makefile b/flash-tool/Makefile index d10c841..12853d7 100644 --- a/flash-tool/Makefile +++ b/flash-tool/Makefile @@ -34,8 +34,8 @@ CFLAGS += -pedantic -Wall -W -O1 -g3 -std=gnu99 LDFLAGS += -lusb BINARY_NAME = inflash -SRC_C= main.c -SRC_H= main.h +SRC_C= main.c usb.c +SRC_H= main.h usb.h SRC_O= $(SRC_C:.c=.o) $(BINARY_NAME): $(SRC_O) $(SRC_H) Makefile diff --git a/flash-tool/main.c b/flash-tool/main.c index f785ed2..376d945 100644 --- a/flash-tool/main.c +++ b/flash-tool/main.c @@ -22,144 +22,96 @@ #include "main.h" +#include "usb.h" -#include #include #include +#include +#include #include +#include +#include +#include -static int get_ingenic_device(struct ingenic_dev *ingenic_dev) +int load_file(struct ingenic_dev *ingenic_dev, const char *file_path) { - struct usb_bus *usb_busses, *usb_bus; - struct usb_device *usb_dev; - int count = 0; + struct stat fstat; + int fd, status, res = -1; - usb_busses = usb_get_busses(); + if (ingenic_dev->file_buff) + free(ingenic_dev->file_buff); - for (usb_bus = usb_busses; usb_bus != NULL; usb_bus = usb_bus->next) { - for (usb_dev = usb_bus->devices; usb_dev != NULL; usb_dev = usb_dev->next) { + ingenic_dev->file_buff = NULL; - if ((usb_dev->descriptor.idVendor == VENDOR_ID) && - (usb_dev->descriptor.idProduct == PRODUCT_ID)) { - ingenic_dev->usb_dev = usb_dev; - count++; - } + status = stat(file_path, &fstat); - } + if (status < 0) { + fprintf(stderr, "Error - can't get file size from '%s': %s\n", file_path, strerror(errno)); + goto out; } - return count; -} + ingenic_dev->file_len = fstat.st_size; + ingenic_dev->file_buff = malloc(ingenic_dev->file_len); -static int get_ingenic_interface(struct ingenic_dev *ingenic_dev) -{ - struct usb_config_descriptor *usb_config_desc; - struct usb_interface_descriptor *usb_if_desc; - struct usb_interface *usb_if; - int config_index, if_index, alt_index; - - for (config_index = 0; config_index < ingenic_dev->usb_dev->descriptor.bNumConfigurations; config_index++) { - usb_config_desc = &ingenic_dev->usb_dev->config[config_index]; - - if (!usb_config_desc) - return 0; - - for (if_index = 0; if_index < usb_config_desc->bNumInterfaces; if_index++) { - usb_if = &usb_config_desc->interface[if_index]; - - if (!usb_if) - return 0; - - for (alt_index = 0; alt_index < usb_if->num_altsetting; alt_index++) { - usb_if_desc = &usb_if->altsetting[alt_index]; - - if (!usb_if_desc) - return 0; - - if ((usb_if_desc->bInterfaceClass == 0xff) && - (usb_if_desc->bInterfaceSubClass == 0)) { - ingenic_dev->interface = usb_if_desc->bInterfaceNumber; - return 1; - } - } - } + if (!ingenic_dev->file_buff) { + fprintf(stderr, "Error - can't allocate memory to read file '%s': %s\n", file_path, strerror(errno)); + return -1; } - return 0; + fd = open(file_path, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "Error - can't open file '%s': %s\n", file_path, strerror(errno)); + goto out; + } + + status = read(fd, ingenic_dev->file_buff, ingenic_dev->file_len); + + if (status < ingenic_dev->file_len) { + fprintf(stderr, "Error - can't read file '%s': %s\n", file_path, strerror(errno)); + goto close; + } + + res = 1; + +close: + close(fd); +out: + return res; } int main(int argc, char **argv) { - int num_ingenic, status; - int res = EXIT_FAILURE; - char cpu_buff[8]; struct ingenic_dev ingenic_dev; + int res = EXIT_FAILURE; if ((getuid()) || (getgid())) { fprintf(stderr, "Error - you must be root to run '%s'\n", argv[0]); - exit(EXIT_FAILURE); + goto out; } memset(&ingenic_dev, 0, sizeof(struct ingenic_dev)); - memset(cpu_buff, 0, sizeof(cpu_buff)); - usb_init(); -// usb_set_debug(255); - usb_find_busses(); - usb_find_devices(); + if (usb_ingenic_init(&ingenic_dev) < 1) + goto out; - num_ingenic = get_ingenic_device(&ingenic_dev); + if (usb_get_ingenic_cpu(&ingenic_dev) < 1) + goto out; - if (num_ingenic == 0) { - fprintf(stderr, "Error - no Ingenic device found\n"); - goto exit; - } + if (load_file(&ingenic_dev, STAGE1_FILE_PATH) < 1) + goto out; - if (num_ingenic > 1) { - fprintf(stderr, "Error - too many Ingenic devices found: %i\n", num_ingenic); - goto exit; - } + if (usb_ingenic_upload(&ingenic_dev, 1) < 1) + goto cleanup; - ingenic_dev.usb_handle = usb_open(ingenic_dev.usb_dev); - if (!ingenic_dev.usb_handle) { - fprintf(stderr, "Error - can't open Ingenic device: %s\n", usb_strerror()); - goto exit; - } + res = EXIT_SUCCESS; - if (get_ingenic_interface(&ingenic_dev) < 1) { - fprintf(stderr, "Error - can't find Ingenic interface\n"); - goto close_handle; - } - - if (usb_claim_interface(ingenic_dev.usb_handle, ingenic_dev.interface) < 0) { - fprintf(stderr, "Error - can't claim Ingenic interface: %s\n", usb_strerror()); - goto close_handle; - } - - status = usb_control_msg(ingenic_dev.usb_handle, - /* bmRequestType */ INGENIC_REQUEST_TYPE, - /* bRequest */ VR_GET_CPU_INFO, - /* wValue */ 0, - /* wIndex */ 0, - /* Data */ cpu_buff, - /* wLength */ 8, - USB_TIMEOUT); - - if (status != sizeof(cpu_buff)) { - fprintf(stderr, "Error - can't retrieve Ingenic CPU type: %i\n", status); - goto release_if; - } - - printf("Found cpu: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\n", - cpu_buff[0], cpu_buff[1], cpu_buff[2], cpu_buff[3], - cpu_buff[4], cpu_buff[5], cpu_buff[6], cpu_buff[7]); - -release_if: - usb_release_interface(ingenic_dev.usb_handle, ingenic_dev.interface); -close_handle: - usb_close(ingenic_dev.usb_handle); -exit: - exit(res); +cleanup: + if (ingenic_dev.file_buff) + free(ingenic_dev.file_buff); +out: + usb_ingenic_cleanup(&ingenic_dev); + return res; } diff --git a/flash-tool/main.h b/flash-tool/main.h index 4f6489d..39f6ebf 100644 --- a/flash-tool/main.h +++ b/flash-tool/main.h @@ -27,19 +27,14 @@ #define VENDOR_ID 0x601a #define PRODUCT_ID 0x4740 -#define INGENIC_REQUEST_TYPE 0x40 -#define VR_GET_CPU_INFO 0x00 -#define VR_SET_DATA_ADDRESS 0x01 -#define VR_SET_DATA_LENGTH 0x02 -#define VR_FLUSH_CACHES 0x03 -#define VR_PROGRAM_START1 0x04 -#define VR_PROGRAM_START2 0x05 - -#define USB_TIMEOUT 5000 +#define STAGE1_FILE_PATH "fw.bin" struct ingenic_dev { struct usb_device *usb_dev; struct usb_dev_handle *usb_handle; uint8_t interface; + char cpu_info_buff[8]; + char *file_buff; + int file_len; }; diff --git a/flash-tool/usb.c b/flash-tool/usb.c new file mode 100644 index 0000000..84996d1 --- /dev/null +++ b/flash-tool/usb.c @@ -0,0 +1,247 @@ +/* + * "Ingenic flash tool" - flash the Ingenic CPU via USB + * + * (C) Copyright 2009 + * Author: Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 3 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + + + +#include "main.h" +#include "usb.h" +#include +#include + + +static int get_ingenic_device(struct ingenic_dev *ingenic_dev) +{ + struct usb_bus *usb_busses, *usb_bus; + struct usb_device *usb_dev; + int count = 0; + + usb_busses = usb_get_busses(); + + for (usb_bus = usb_busses; usb_bus != NULL; usb_bus = usb_bus->next) { + for (usb_dev = usb_bus->devices; usb_dev != NULL; usb_dev = usb_dev->next) { + + if ((usb_dev->descriptor.idVendor == VENDOR_ID) && + (usb_dev->descriptor.idProduct == PRODUCT_ID)) { + ingenic_dev->usb_dev = usb_dev; + count++; + } + + } + } + + return count; +} + +static int get_ingenic_interface(struct ingenic_dev *ingenic_dev) +{ + struct usb_config_descriptor *usb_config_desc; + struct usb_interface_descriptor *usb_if_desc; + struct usb_interface *usb_if; + int config_index, if_index, alt_index; + + for (config_index = 0; config_index < ingenic_dev->usb_dev->descriptor.bNumConfigurations; config_index++) { + usb_config_desc = &ingenic_dev->usb_dev->config[config_index]; + + if (!usb_config_desc) + return 0; + + for (if_index = 0; if_index < usb_config_desc->bNumInterfaces; if_index++) { + usb_if = &usb_config_desc->interface[if_index]; + + if (!usb_if) + return 0; + + for (alt_index = 0; alt_index < usb_if->num_altsetting; alt_index++) { + usb_if_desc = &usb_if->altsetting[alt_index]; + + if (!usb_if_desc) + return 0; + + if ((usb_if_desc->bInterfaceClass == 0xff) && + (usb_if_desc->bInterfaceSubClass == 0)) { + ingenic_dev->interface = usb_if_desc->bInterfaceNumber; + return 1; + } + } + } + } + + return 0; +} + +int usb_ingenic_init(struct ingenic_dev *ingenic_dev) +{ + int num_ingenic, status = -1; + + usb_init(); +// usb_set_debug(255); + usb_find_busses(); + usb_find_devices(); + + num_ingenic = get_ingenic_device(ingenic_dev); + + if (num_ingenic == 0) { + fprintf(stderr, "Error - no Ingenic device found\n"); + goto out; + } + + if (num_ingenic > 1) { + fprintf(stderr, "Error - too many Ingenic devices found: %i\n", num_ingenic); + goto out; + } + + ingenic_dev->usb_handle = usb_open(ingenic_dev->usb_dev); + if (!ingenic_dev->usb_handle) { + fprintf(stderr, "Error - can't open Ingenic device: %s\n", usb_strerror()); + goto out; + } + + if (get_ingenic_interface(ingenic_dev) < 1) { + fprintf(stderr, "Error - can't find Ingenic interface\n"); + goto out; + } + + if (usb_claim_interface(ingenic_dev->usb_handle, ingenic_dev->interface) < 0) { + fprintf(stderr, "Error - can't claim Ingenic interface: %s\n", usb_strerror()); + goto out; + } + + status = 1; + +out: + return status; +} + +int usb_get_ingenic_cpu(struct ingenic_dev *ingenic_dev) +{ + int status; + + status = usb_control_msg(ingenic_dev->usb_handle, + /* bmRequestType */ USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + /* bRequest */ VR_GET_CPU_INFO, + /* wValue */ 0, + /* wIndex */ 0, + /* Data */ ingenic_dev->cpu_info_buff, + /* wLength */ 8, + USB_TIMEOUT); + + if (status != sizeof(ingenic_dev->cpu_info_buff)) { + fprintf(stderr, "Error - can't retrieve Ingenic CPU information: %i\n", status); + goto out; + } + + printf("CPU data: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\n", + ingenic_dev->cpu_info_buff[0], ingenic_dev->cpu_info_buff[1], + ingenic_dev->cpu_info_buff[2], ingenic_dev->cpu_info_buff[3], + ingenic_dev->cpu_info_buff[4], ingenic_dev->cpu_info_buff[5], + ingenic_dev->cpu_info_buff[6], ingenic_dev->cpu_info_buff[7]); + + status = 1; + +out: + return status; +} + +int usb_send_bulk_to_ingenic(struct ingenic_dev *ingenic_dev) +{ + int status; + + status = usb_bulk_write(ingenic_dev->usb_handle, + /* endpoint */ INGENIC_ENDPOINT, + /* bulk data */ ingenic_dev->file_buff, // + i, + /* bulk data length */ ingenic_dev->file_len, + USB_TIMEOUT); + + if (status < ingenic_dev->file_len) { + fprintf(stderr, "Error - can't send bulk data to Ingenic CPU: %i\n", status); + return -1; + } + + return 1; +} + +int usb_ingenic_upload(struct ingenic_dev *ingenic_dev, int stage) +{ + int status; + + /* tell the device the RAM address to store the file */ + status = usb_control_msg(ingenic_dev->usb_handle, + /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + /* bRequest */ VR_SET_DATA_ADDRESS, + /* wValue */ (stage == 1 ? STAGE1_ADDR_MSB : STAGE2_ADDR_MSB), + /* wIndex */ (stage == 1 ? STAGE1_ADDR_LSB : STAGE2_ADDR_LSB), + /* Data */ 0, + /* wLength */ 0, + USB_TIMEOUT); + + if (status != 0) { + fprintf(stderr, "Error - can't set the address on Ingenic device: %i\n", status); + goto out; + } + +// not needed ? the windows driver does not send the length +// /* tell the device the length of the file to be uploaded */ +// status = usb_control_msg(ingenic_dev->usb_handle, +// /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +// /* bRequest */ VR_SET_DATA_LENGTH, +// /* wValue */ htons(ingenic_dev->file_len), +// /* wIndex */ ingenic_dev->file_len, +// /* Data */ 0, +// /* wLength */ 0, +// USB_TIMEOUT); +// +// if (status != 0) +// fprintf(stderr, "Error - can't set the address on Ingenic device: %i\n", status); + + status = usb_send_bulk_to_ingenic(ingenic_dev); + + if (status < 1) + return status; + + /* tell the device to start the uploaded device */ + status = usb_control_msg(ingenic_dev->usb_handle, + /* bmRequestType */ USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + /* bRequest */ (stage == 1 ? VR_PROGRAM_START1 : VR_PROGRAM_START2), + /* wValue */ (stage == 1 ? STAGE1_ADDR_MSB : STAGE2_ADDR_MSB), + /* wIndex */ (stage == 1 ? STAGE1_ADDR_LSB : STAGE2_ADDR_LSB), + /* Data */ 0, + /* wLength */ 0, + USB_TIMEOUT); + + if (status != 0) { + fprintf(stderr, "Error - can't start the uploaded binary on the Ingenic device: %i\n", status); + goto out; + } + + status = 1; + +out: + return status; +} + +void usb_ingenic_cleanup(struct ingenic_dev *ingenic_dev) +{ + if ((ingenic_dev->usb_handle) && (ingenic_dev->interface)) + usb_release_interface(ingenic_dev->usb_handle, ingenic_dev->interface); + + if (ingenic_dev->usb_handle) + usb_close(ingenic_dev->usb_handle); +} diff --git a/flash-tool/usb.h b/flash-tool/usb.h new file mode 100644 index 0000000..e25de29 --- /dev/null +++ b/flash-tool/usb.h @@ -0,0 +1,45 @@ +/* + * "Ingenic flash tool" - flash the Ingenic CPU via USB + * + * (C) Copyright 2009 + * Author: Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 3 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + + + +#define INGENIC_ENDPOINT 0x01 + +#define VR_GET_CPU_INFO 0x00 +#define VR_SET_DATA_ADDRESS 0x01 +#define VR_SET_DATA_LENGTH 0x02 +#define VR_FLUSH_CACHES 0x03 +#define VR_PROGRAM_START1 0x04 +#define VR_PROGRAM_START2 0x05 + +#define STAGE1_ADDR_MSB 0x8000 +#define STAGE1_ADDR_LSB 0x0000 +#define STAGE2_ADDR_MSB 0x8000 +#define STAGE2_ADDR_LSB 0x0000 + +#define USB_PACKET_SIZE 512 +#define USB_TIMEOUT 5000 + + +int usb_ingenic_init(struct ingenic_dev *ingenic_dev); +int usb_get_ingenic_cpu(struct ingenic_dev *ingenic_dev); +int usb_ingenic_upload(struct ingenic_dev *ingenic_dev, int stage); +void usb_ingenic_cleanup(struct ingenic_dev *ingenic_dev);