/* Copyright 2008 William Poetra Yoga Hadisoeseno Frank Gevaerts This file is licensed under GPL v2. */ #include #include #include #include #include #include #include #include #include #include void usage() { fprintf(stderr, "usage: meizu_dfu m3 \n"); fprintf(stderr, " meizu_dfu m6 \n"); fprintf(stderr, " meizu_dfu m6sl \n"); exit(1); } uint32_t crc32(char *data, int len, uint32_t poly, uint32_t init) { uint32_t crc_table[256]; uint32_t crc, t; int i, j; // generate the table for (i = 0; i < 256; ++i) { t = i; for (j = 0; j < 8; ++j) if (t & 1) t = (t >> 1) ^ poly; else t >>= 1; crc_table[i] = t; } // calculate the crc crc = init; for (i = 0; i < len; ++i) crc = (crc >> 8) ^ crc_table[(crc^data[i]) & 0xff]; return crc; } typedef struct { char *name; char *data; int len; } image_data_t; typedef struct { int delay; int pre_off; uint32_t pre_sig; uint16_t suf_dev; uint16_t suf_prod; uint16_t suf_ven; uint16_t suf_dfu; char suf_sig[3]; uint8_t suf_len; } image_attr_t; #define BLOCK_SIZE 2048 #define DFU_TIMEOUT 0xa000 #define DFU_CRC_POLY 0xedb88320 #define DFU_INIT_CRC 0xffffffff #define USB_VID_SAMSUNG 0x0419 #define USB_PID_M6SL 0x0145 #define USB_PID_M3_M6 0x0141 void init_img(image_data_t *img, const char *filename, image_attr_t *attr) { int fd, len, i, readlen; struct stat statbuf; char buf[BLOCK_SIZE]; uint32_t dfu_crc; printf("Reading %s...", filename); stat(filename, &statbuf); len = statbuf.st_size; img->name = basename(strdup(filename)); img->data = malloc(len + 16); img->len = len + 16; fd = open(filename, O_RDONLY); for (i = 0; i < len; i += BLOCK_SIZE) { readlen = ((len - i) < BLOCK_SIZE) ? (len - i) : BLOCK_SIZE; read(fd, buf, readlen); memcpy(img->data + i, buf, readlen); } close(fd); // patch the data size in after the signature memcpy(img->data + attr->pre_off + 4, &img->len, 4); // append the suffix (excluding the checksum) memcpy(img->data + len, &attr->suf_dev, 2); memcpy(img->data + len + 2, &attr->suf_prod, 2); memcpy(img->data + len + 4, &attr->suf_ven, 2); memcpy(img->data + len + 6, &attr->suf_dfu, 2); memcpy(img->data + len + 8, &attr->suf_sig, 3); memcpy(img->data + len + 11, &attr->suf_len, 1); dfu_crc = crc32(img->data, len + 12, DFU_CRC_POLY, DFU_INIT_CRC); memcpy(img->data + len + 12, &dfu_crc, 4); #if 0 FILE *f = fopen(img->name, "w"); fwrite(img->data, len + 16, 1, f); fclose(f); #endif printf("OK\n"); } usb_dev_handle *usb_dev_open(uint16_t dfu_vid, uint16_t dfu_pid) { struct usb_bus *bus; struct usb_device *dev; usb_dev_handle *device; printf("USB initialization..."); usb_init(); usb_find_busses(); usb_find_devices(); for (bus = usb_get_busses(); bus != NULL; bus = bus->next) for (dev = bus->devices; dev != NULL; dev = dev->next) if (dev->descriptor.idVendor == dfu_vid && dev->descriptor.idProduct == dfu_pid) goto found; printf("\nNo device found, exiting.\n"); exit(1); found: printf(" Device found.\n"); device = usb_open(dev); usb_claim_interface(device, 0); return device; } void usb_mimic_windows(usb_dev_handle *device) { char data[1024]; usb_control_msg(device, 0x80, 0x06, 0x0100, 0x0000, data, 0x0012, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0200, 0x0000, data, 0x0009, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0200, 0x0000, data, 0x001b, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0100, 0x0000, data, 0x0040, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0100, 0x0000, data, 0x0012, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0200, 0x0000, data, 0x0009, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0300, 0x0000, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0303, 0x0409, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0200, 0x0000, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0300, 0x0000, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0302, 0x0409, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0300, 0x0000, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0302, 0x0409, data, 0x00ff, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0100, 0x0000, data, 0x0012, DFU_TIMEOUT); usb_control_msg(device, 0x80, 0x06, 0x0200, 0x0000, data, 0x0209, DFU_TIMEOUT); } void usb_dev_close(usb_dev_handle *device) { printf("Releasing interface..."); usb_release_interface(device, 0); printf(" OK\n"); } enum DFU_REQUEST { DFU_DETACH = 0, DFU_DOWNLOAD, DFU_UPLOAD, DFU_GETSTATUS, DFU_CLRSTATUS, DFU_GETSTATE, DFU_ABORT }; void get_cpu(usb_dev_handle *device) { char data[64]; int req_out_if = USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; int len; printf("GET CPU"); // check for "S5L8700 Rev.1" len = usb_control_msg(device, req_out_if, 0xff, 0x0002, 0, data, 0x003f, DFU_TIMEOUT); if (len < 0) { printf("\nError trying to get CPU model, exiting.\n"); exit(1); } memset(data + len, 0, 64 - len); printf(", got: %s\n", data); } void send_file(usb_dev_handle *device, image_data_t *img) { char dfu_ret[6]; char *data; int req_out_if = USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; int req_in_if = USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; int len, idx, writelen, i; printf("Sending %s... ", img->name); len = img->len; data = img->data; // loop for the file for (i = 0, idx = 0; i < len; i += BLOCK_SIZE, ++idx) { writelen = ((len - i) < BLOCK_SIZE) ? (len - i) : BLOCK_SIZE; usb_control_msg(device, req_out_if, DFU_DOWNLOAD, idx, 0, data + i, writelen, DFU_TIMEOUT); dfu_ret[4] = 0x00; while (dfu_ret[4] != 0x05) usb_control_msg(device, req_in_if, DFU_GETSTATUS, 0, 0, dfu_ret, 6, DFU_TIMEOUT); printf("#"); } usb_control_msg(device, req_out_if, DFU_DOWNLOAD, idx, 0, NULL, 0, DFU_TIMEOUT); dfu_ret[4] = 0x00; while (dfu_ret[4] != 0x07) usb_control_msg(device, req_in_if, DFU_GETSTATUS, 0, 0, dfu_ret, 6, DFU_TIMEOUT); printf(" OK\n"); } void clear_status(usb_dev_handle *device) { char dfu_ret[6]; int usb_in_if = USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; int usb_out_if = USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; printf("Clearing status..."); dfu_ret[4] = 0x00; while (dfu_ret[4] != 0x08) usb_control_msg(device, usb_in_if, DFU_GETSTATUS, 0, 0, dfu_ret, 6, DFU_TIMEOUT); usb_control_msg(device, usb_out_if, DFU_CLRSTATUS, 0, 0, NULL, 0, DFU_TIMEOUT); printf(" OK\n"); } void dfu_detach(usb_dev_handle *device) { char usb_ret[4]; int usb_in_oth = USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; int usb_out_oth = USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; printf("Detaching..."); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); usb_control_msg(device, usb_out_oth, DFU_DOWNLOAD, 0x0010, 3, NULL, 0, DFU_TIMEOUT); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); usb_control_msg(device, usb_in_oth, DFU_DETACH, 0x0000, 3, usb_ret, 4, DFU_TIMEOUT); printf(" OK\n"); } void dfu_m3_m6(char *file1, char *file2) { image_data_t img1, img2; image_attr_t attr1, attr2; usb_dev_handle *device; attr1.delay = 1000; attr1.pre_off = 0x20; attr1.pre_sig = 0x44465543; attr1.suf_dev = 0x0100; attr1.suf_prod = 0x0140; attr1.suf_ven = 0x0419; attr1.suf_dfu = 0x0100; memcpy(attr1.suf_sig, "RON", 3); attr1.suf_len = 0x10; attr2.delay = 1000; attr2.pre_off = 0x20; attr2.pre_sig = 0x44465543; attr2.suf_dev = 0x0100; attr2.suf_prod = 0x0140; attr2.suf_ven = 0x0419; attr2.suf_dfu = 0x0100; memcpy(attr2.suf_sig, "UFD", 3); attr2.suf_len = 0x10; init_img(&img1, file1, &attr1); init_img(&img2, file2, &attr2); device = usb_dev_open(USB_VID_SAMSUNG, USB_PID_M3_M6); // usb_mimic_windows(); get_cpu(device); get_cpu(device); send_file(device, &img1); printf("Wait a sec (literally)..."); sleep(1); printf(" OK\n"); clear_status(device); get_cpu(device); send_file(device, &img2); dfu_detach(device); usb_dev_close(device); } void dfu_m6sl(char *file1, char *file2) { image_data_t img1, img2; image_attr_t attr1, attr2; usb_dev_handle *device; attr1.delay = 1000; attr1.pre_off = 0x20; attr1.pre_sig = 0x44465543; attr1.suf_dev = 0x0100; attr1.suf_prod = 0x0140; attr1.suf_ven = 0x0419; attr1.suf_dfu = 0x0100; memcpy(attr1.suf_sig, "UFD", 3); attr1.suf_len = 0x10; attr2.delay = 1000; attr2.pre_off = 0x20; attr2.pre_sig = 0x44465543; attr2.suf_dev = 0x0100; attr2.suf_prod = 0x0140; attr2.suf_ven = 0x0419; attr2.suf_dfu = 0x0100; memcpy(attr2.suf_sig, "UFD", 3); attr2.suf_len = 0x10; init_img(&img1, file1, &attr1); init_img(&img2, file2, &attr2); device = usb_dev_open(USB_VID_SAMSUNG, USB_PID_M6SL); get_cpu(device); get_cpu(device); send_file(device, &img1); printf("Wait a sec (literally)..."); sleep(1); printf(" OK\n"); usb_dev_close(device); device = usb_dev_open(USB_VID_SAMSUNG, USB_PID_M6SL); get_cpu(device); get_cpu(device); send_file(device, &img2); dfu_detach(device); usb_dev_close(device); } int main(int argc, char **argv) { if (argc != 4) usage(); setvbuf(stdout, NULL, _IONBF, 0); if (!strcmp(argv[1], "m3")) dfu_m3_m6(argv[2], argv[3]); else if (!strcmp(argv[1], "m6")) dfu_m3_m6(argv[2], argv[3]); else if (!strcmp(argv[1], "m6sl")) dfu_m6sl(argv[2], argv[3]); else usage(); return 0; }