18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for TerraTec DMX 6Fire USB 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Firmware loader 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Torsten Schenk <torsten.schenk@zoho.com> 88c2ecf20Sopenharmony_ci * Created: Jan 01, 2011 98c2ecf20Sopenharmony_ci * Copyright: (C) Torsten Schenk 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/firmware.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/bitrev.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "firmware.h" 188c2ecf20Sopenharmony_ci#include "chip.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_FIRMWARE("6fire/dmx6firel2.ihx"); 218c2ecf20Sopenharmony_ciMODULE_FIRMWARE("6fire/dmx6fireap.ihx"); 228c2ecf20Sopenharmony_ciMODULE_FIRMWARE("6fire/dmx6firecf.bin"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum { 258c2ecf20Sopenharmony_ci FPGA_BUFSIZE = 512, FPGA_EP = 2 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * wMaxPacketSize of pcm endpoints. 308c2ecf20Sopenharmony_ci * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c 318c2ecf20Sopenharmony_ci * fpp: frames per isopacket 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic const u8 ep_w_max_packet_size[] = { 368c2ecf20Sopenharmony_ci 0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */ 378c2ecf20Sopenharmony_ci 0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/ 388c2ecf20Sopenharmony_ci 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */ 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const u8 known_fw_versions[][2] = { 428c2ecf20Sopenharmony_ci { 0x03, 0x01 } 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct ihex_record { 468c2ecf20Sopenharmony_ci u16 address; 478c2ecf20Sopenharmony_ci u8 len; 488c2ecf20Sopenharmony_ci u8 data[256]; 498c2ecf20Sopenharmony_ci char error; /* true if an error occurred parsing this record */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci u8 max_len; /* maximum record length in whole ihex */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* private */ 548c2ecf20Sopenharmony_ci const char *txt_data; 558c2ecf20Sopenharmony_ci unsigned int txt_length; 568c2ecf20Sopenharmony_ci unsigned int txt_offset; /* current position in txt_data */ 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u8 val = 0; 628c2ecf20Sopenharmony_ci int hval; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci hval = hex_to_bin(data[0]); 658c2ecf20Sopenharmony_ci if (hval >= 0) 668c2ecf20Sopenharmony_ci val |= (hval << 4); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci hval = hex_to_bin(data[1]); 698c2ecf20Sopenharmony_ci if (hval >= 0) 708c2ecf20Sopenharmony_ci val |= hval; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci *crc += val; 738c2ecf20Sopenharmony_ci return val; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * returns true if record is available, false otherwise. 788c2ecf20Sopenharmony_ci * iff an error occurred, false will be returned and record->error will be true. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic bool usb6fire_fw_ihex_next_record(struct ihex_record *record) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u8 crc = 0; 838c2ecf20Sopenharmony_ci u8 type; 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci record->error = false; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* find begin of record (marked by a colon) */ 898c2ecf20Sopenharmony_ci while (record->txt_offset < record->txt_length 908c2ecf20Sopenharmony_ci && record->txt_data[record->txt_offset] != ':') 918c2ecf20Sopenharmony_ci record->txt_offset++; 928c2ecf20Sopenharmony_ci if (record->txt_offset == record->txt_length) 938c2ecf20Sopenharmony_ci return false; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* number of characters needed for len, addr and type entries */ 968c2ecf20Sopenharmony_ci record->txt_offset++; 978c2ecf20Sopenharmony_ci if (record->txt_offset + 8 > record->txt_length) { 988c2ecf20Sopenharmony_ci record->error = true; 998c2ecf20Sopenharmony_ci return false; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci record->len = usb6fire_fw_ihex_hex(record->txt_data + 1038c2ecf20Sopenharmony_ci record->txt_offset, &crc); 1048c2ecf20Sopenharmony_ci record->txt_offset += 2; 1058c2ecf20Sopenharmony_ci record->address = usb6fire_fw_ihex_hex(record->txt_data + 1068c2ecf20Sopenharmony_ci record->txt_offset, &crc) << 8; 1078c2ecf20Sopenharmony_ci record->txt_offset += 2; 1088c2ecf20Sopenharmony_ci record->address |= usb6fire_fw_ihex_hex(record->txt_data + 1098c2ecf20Sopenharmony_ci record->txt_offset, &crc); 1108c2ecf20Sopenharmony_ci record->txt_offset += 2; 1118c2ecf20Sopenharmony_ci type = usb6fire_fw_ihex_hex(record->txt_data + 1128c2ecf20Sopenharmony_ci record->txt_offset, &crc); 1138c2ecf20Sopenharmony_ci record->txt_offset += 2; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* number of characters needed for data and crc entries */ 1168c2ecf20Sopenharmony_ci if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) { 1178c2ecf20Sopenharmony_ci record->error = true; 1188c2ecf20Sopenharmony_ci return false; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci for (i = 0; i < record->len; i++) { 1218c2ecf20Sopenharmony_ci record->data[i] = usb6fire_fw_ihex_hex(record->txt_data 1228c2ecf20Sopenharmony_ci + record->txt_offset, &crc); 1238c2ecf20Sopenharmony_ci record->txt_offset += 2; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc); 1268c2ecf20Sopenharmony_ci if (crc) { 1278c2ecf20Sopenharmony_ci record->error = true; 1288c2ecf20Sopenharmony_ci return false; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (type == 1 || !record->len) /* eof */ 1328c2ecf20Sopenharmony_ci return false; 1338c2ecf20Sopenharmony_ci else if (type == 0) 1348c2ecf20Sopenharmony_ci return true; 1358c2ecf20Sopenharmony_ci else { 1368c2ecf20Sopenharmony_ci record->error = true; 1378c2ecf20Sopenharmony_ci return false; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int usb6fire_fw_ihex_init(const struct firmware *fw, 1428c2ecf20Sopenharmony_ci struct ihex_record *record) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci record->txt_data = fw->data; 1458c2ecf20Sopenharmony_ci record->txt_length = fw->size; 1468c2ecf20Sopenharmony_ci record->txt_offset = 0; 1478c2ecf20Sopenharmony_ci record->max_len = 0; 1488c2ecf20Sopenharmony_ci /* read all records, if loop ends, record->error indicates, 1498c2ecf20Sopenharmony_ci * whether ihex is valid. */ 1508c2ecf20Sopenharmony_ci while (usb6fire_fw_ihex_next_record(record)) 1518c2ecf20Sopenharmony_ci record->max_len = max(record->len, record->max_len); 1528c2ecf20Sopenharmony_ci if (record->error) 1538c2ecf20Sopenharmony_ci return -EINVAL; 1548c2ecf20Sopenharmony_ci record->txt_offset = 0; 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int usb6fire_fw_ezusb_write(struct usb_device *device, 1598c2ecf20Sopenharmony_ci int type, int value, char *data, int len) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci return usb_control_msg_send(device, 0, type, 1628c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1638c2ecf20Sopenharmony_ci value, 0, data, len, 1000, GFP_KERNEL); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int usb6fire_fw_ezusb_read(struct usb_device *device, 1678c2ecf20Sopenharmony_ci int type, int value, char *data, int len) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci return usb_control_msg_recv(device, 0, type, 1708c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1718c2ecf20Sopenharmony_ci value, 0, data, len, 1000, GFP_KERNEL); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int usb6fire_fw_fpga_write(struct usb_device *device, 1758c2ecf20Sopenharmony_ci char *data, int len) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int actual_len; 1788c2ecf20Sopenharmony_ci int ret; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len, 1818c2ecf20Sopenharmony_ci &actual_len, 1000); 1828c2ecf20Sopenharmony_ci if (ret < 0) 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci else if (actual_len != len) 1858c2ecf20Sopenharmony_ci return -EIO; 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int usb6fire_fw_ezusb_upload( 1908c2ecf20Sopenharmony_ci struct usb_interface *intf, const char *fwname, 1918c2ecf20Sopenharmony_ci unsigned int postaddr, u8 *postdata, unsigned int postlen) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int ret; 1948c2ecf20Sopenharmony_ci u8 data; 1958c2ecf20Sopenharmony_ci struct usb_device *device = interface_to_usbdev(intf); 1968c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 1978c2ecf20Sopenharmony_ci struct ihex_record *rec = kmalloc(sizeof(struct ihex_record), 1988c2ecf20Sopenharmony_ci GFP_KERNEL); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!rec) 2018c2ecf20Sopenharmony_ci return -ENOMEM; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fwname, &device->dev); 2048c2ecf20Sopenharmony_ci if (ret < 0) { 2058c2ecf20Sopenharmony_ci kfree(rec); 2068c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2078c2ecf20Sopenharmony_ci "error requesting ezusb firmware %s.\n", fwname); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci ret = usb6fire_fw_ihex_init(fw, rec); 2118c2ecf20Sopenharmony_ci if (ret < 0) { 2128c2ecf20Sopenharmony_ci kfree(rec); 2138c2ecf20Sopenharmony_ci release_firmware(fw); 2148c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2158c2ecf20Sopenharmony_ci "error validating ezusb firmware %s.\n", fwname); 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci /* upload firmware image */ 2198c2ecf20Sopenharmony_ci data = 0x01; /* stop ezusb cpu */ 2208c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); 2218c2ecf20Sopenharmony_ci if (ret) { 2228c2ecf20Sopenharmony_ci kfree(rec); 2238c2ecf20Sopenharmony_ci release_firmware(fw); 2248c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2258c2ecf20Sopenharmony_ci "unable to upload ezusb firmware %s: begin message.\n", 2268c2ecf20Sopenharmony_ci fwname); 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */ 2318c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address, 2328c2ecf20Sopenharmony_ci rec->data, rec->len); 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci kfree(rec); 2358c2ecf20Sopenharmony_ci release_firmware(fw); 2368c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2378c2ecf20Sopenharmony_ci "unable to upload ezusb firmware %s: data urb.\n", 2388c2ecf20Sopenharmony_ci fwname); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci release_firmware(fw); 2448c2ecf20Sopenharmony_ci kfree(rec); 2458c2ecf20Sopenharmony_ci if (postdata) { /* write data after firmware has been uploaded */ 2468c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr, 2478c2ecf20Sopenharmony_ci postdata, postlen); 2488c2ecf20Sopenharmony_ci if (ret) { 2498c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2508c2ecf20Sopenharmony_ci "unable to upload ezusb firmware %s: post urb.\n", 2518c2ecf20Sopenharmony_ci fwname); 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci data = 0x00; /* resume ezusb cpu */ 2578c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); 2588c2ecf20Sopenharmony_ci if (ret) { 2598c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2608c2ecf20Sopenharmony_ci "unable to upload ezusb firmware %s: end message.\n", 2618c2ecf20Sopenharmony_ci fwname); 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int usb6fire_fw_fpga_upload( 2688c2ecf20Sopenharmony_ci struct usb_interface *intf, const char *fwname) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci int i; 2728c2ecf20Sopenharmony_ci struct usb_device *device = interface_to_usbdev(intf); 2738c2ecf20Sopenharmony_ci u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); 2748c2ecf20Sopenharmony_ci const char *c; 2758c2ecf20Sopenharmony_ci const char *end; 2768c2ecf20Sopenharmony_ci const struct firmware *fw; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!buffer) 2798c2ecf20Sopenharmony_ci return -ENOMEM; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fwname, &device->dev); 2828c2ecf20Sopenharmony_ci if (ret < 0) { 2838c2ecf20Sopenharmony_ci dev_err(&intf->dev, "unable to get fpga firmware %s.\n", 2848c2ecf20Sopenharmony_ci fwname); 2858c2ecf20Sopenharmony_ci kfree(buffer); 2868c2ecf20Sopenharmony_ci return -EIO; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci c = fw->data; 2908c2ecf20Sopenharmony_ci end = fw->data + fw->size; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0); 2938c2ecf20Sopenharmony_ci if (ret) { 2948c2ecf20Sopenharmony_ci kfree(buffer); 2958c2ecf20Sopenharmony_ci release_firmware(fw); 2968c2ecf20Sopenharmony_ci dev_err(&intf->dev, 2978c2ecf20Sopenharmony_ci "unable to upload fpga firmware: begin urb.\n"); 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci while (c != end) { 3028c2ecf20Sopenharmony_ci for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) 3038c2ecf20Sopenharmony_ci buffer[i] = bitrev8((u8)*c); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = usb6fire_fw_fpga_write(device, buffer, i); 3068c2ecf20Sopenharmony_ci if (ret < 0) { 3078c2ecf20Sopenharmony_ci release_firmware(fw); 3088c2ecf20Sopenharmony_ci kfree(buffer); 3098c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3108c2ecf20Sopenharmony_ci "unable to upload fpga firmware: fw urb.\n"); 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci release_firmware(fw); 3158c2ecf20Sopenharmony_ci kfree(buffer); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0); 3188c2ecf20Sopenharmony_ci if (ret) { 3198c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3208c2ecf20Sopenharmony_ci "unable to upload fpga firmware: end urb.\n"); 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* check, if the firmware version the devices has currently loaded 3278c2ecf20Sopenharmony_ci * is known by this driver. 'version' needs to have 4 bytes version 3288c2ecf20Sopenharmony_ci * info data. */ 3298c2ecf20Sopenharmony_cistatic int usb6fire_fw_check(struct usb_interface *intf, const u8 *version) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int i; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++) 3348c2ecf20Sopenharmony_ci if (!memcmp(version, known_fw_versions + i, 2)) 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci dev_err(&intf->dev, "invalid firmware version in device: %4ph. " 3388c2ecf20Sopenharmony_ci "please reconnect to power. if this failure " 3398c2ecf20Sopenharmony_ci "still happens, check your firmware installation.", 3408c2ecf20Sopenharmony_ci version); 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciint usb6fire_fw_init(struct usb_interface *intf) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int i; 3478c2ecf20Sopenharmony_ci int ret; 3488c2ecf20Sopenharmony_ci struct usb_device *device = interface_to_usbdev(intf); 3498c2ecf20Sopenharmony_ci /* buffer: 8 receiving bytes from device and 3508c2ecf20Sopenharmony_ci * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */ 3518c2ecf20Sopenharmony_ci u8 buffer[12]; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8); 3548c2ecf20Sopenharmony_ci if (ret) { 3558c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3568c2ecf20Sopenharmony_ci "unable to receive device firmware state.\n"); 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) { 3608c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3618c2ecf20Sopenharmony_ci "unknown device firmware state received from device:"); 3628c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 3638c2ecf20Sopenharmony_ci printk(KERN_CONT "%02x ", buffer[i]); 3648c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 3658c2ecf20Sopenharmony_ci return -EIO; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci /* do we need fpga loader ezusb firmware? */ 3688c2ecf20Sopenharmony_ci if (buffer[3] == 0x01) { 3698c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_upload(intf, 3708c2ecf20Sopenharmony_ci "6fire/dmx6firel2.ihx", 0, NULL, 0); 3718c2ecf20Sopenharmony_ci if (ret < 0) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci return FW_NOT_READY; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci /* do we need fpga firmware and application ezusb firmware? */ 3768c2ecf20Sopenharmony_ci else if (buffer[3] == 0x02) { 3778c2ecf20Sopenharmony_ci ret = usb6fire_fw_check(intf, buffer + 4); 3788c2ecf20Sopenharmony_ci if (ret < 0) 3798c2ecf20Sopenharmony_ci return ret; 3808c2ecf20Sopenharmony_ci ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin"); 3818c2ecf20Sopenharmony_ci if (ret < 0) 3828c2ecf20Sopenharmony_ci return ret; 3838c2ecf20Sopenharmony_ci memcpy(buffer, ep_w_max_packet_size, 3848c2ecf20Sopenharmony_ci sizeof(ep_w_max_packet_size)); 3858c2ecf20Sopenharmony_ci ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx", 3868c2ecf20Sopenharmony_ci 0x0003, buffer, sizeof(ep_w_max_packet_size)); 3878c2ecf20Sopenharmony_ci if (ret < 0) 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci return FW_NOT_READY; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci /* all fw loaded? */ 3928c2ecf20Sopenharmony_ci else if (buffer[3] == 0x03) 3938c2ecf20Sopenharmony_ci return usb6fire_fw_check(intf, buffer + 4); 3948c2ecf20Sopenharmony_ci /* unknown data? */ 3958c2ecf20Sopenharmony_ci else { 3968c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3978c2ecf20Sopenharmony_ci "unknown device firmware state received from device: "); 3988c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 3998c2ecf20Sopenharmony_ci printk(KERN_CONT "%02x ", buffer[i]); 4008c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 4018c2ecf20Sopenharmony_ci return -EIO; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 406