406 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Linux driver for TerraTec DMX 6Fire USB4 *5 * Firmware loader6 *7 * Author: Torsten Schenk <torsten.schenk@zoho.com>8 * Created: Jan 01, 20119 * Copyright: (C) Torsten Schenk10 */11 12#include <linux/firmware.h>13#include <linux/module.h>14#include <linux/bitrev.h>15#include <linux/kernel.h>16 17#include "firmware.h"18#include "chip.h"19 20MODULE_FIRMWARE("6fire/dmx6firel2.ihx");21MODULE_FIRMWARE("6fire/dmx6fireap.ihx");22MODULE_FIRMWARE("6fire/dmx6firecf.bin");23 24enum {25 FPGA_BUFSIZE = 512, FPGA_EP = 226};27 28/*29 * wMaxPacketSize of pcm endpoints.30 * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c31 * fpp: frames per isopacket32 *33 * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init34 */35static const u8 ep_w_max_packet_size[] = {36 0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */37 0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/38 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */39};40 41static const u8 known_fw_versions[][2] = {42 { 0x03, 0x01 }43};44 45struct ihex_record {46 u16 address;47 u8 len;48 u8 data[256];49 char error; /* true if an error occurred parsing this record */50 51 u8 max_len; /* maximum record length in whole ihex */52 53 /* private */54 const char *txt_data;55 unsigned int txt_length;56 unsigned int txt_offset; /* current position in txt_data */57};58 59static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc)60{61 u8 val = 0;62 int hval;63 64 hval = hex_to_bin(data[0]);65 if (hval >= 0)66 val |= (hval << 4);67 68 hval = hex_to_bin(data[1]);69 if (hval >= 0)70 val |= hval;71 72 *crc += val;73 return val;74}75 76/*77 * returns true if record is available, false otherwise.78 * iff an error occurred, false will be returned and record->error will be true.79 */80static bool usb6fire_fw_ihex_next_record(struct ihex_record *record)81{82 u8 crc = 0;83 u8 type;84 int i;85 86 record->error = false;87 88 /* find begin of record (marked by a colon) */89 while (record->txt_offset < record->txt_length90 && record->txt_data[record->txt_offset] != ':')91 record->txt_offset++;92 if (record->txt_offset == record->txt_length)93 return false;94 95 /* number of characters needed for len, addr and type entries */96 record->txt_offset++;97 if (record->txt_offset + 8 > record->txt_length) {98 record->error = true;99 return false;100 }101 102 record->len = usb6fire_fw_ihex_hex(record->txt_data +103 record->txt_offset, &crc);104 record->txt_offset += 2;105 record->address = usb6fire_fw_ihex_hex(record->txt_data +106 record->txt_offset, &crc) << 8;107 record->txt_offset += 2;108 record->address |= usb6fire_fw_ihex_hex(record->txt_data +109 record->txt_offset, &crc);110 record->txt_offset += 2;111 type = usb6fire_fw_ihex_hex(record->txt_data +112 record->txt_offset, &crc);113 record->txt_offset += 2;114 115 /* number of characters needed for data and crc entries */116 if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) {117 record->error = true;118 return false;119 }120 for (i = 0; i < record->len; i++) {121 record->data[i] = usb6fire_fw_ihex_hex(record->txt_data122 + record->txt_offset, &crc);123 record->txt_offset += 2;124 }125 usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc);126 if (crc) {127 record->error = true;128 return false;129 }130 131 if (type == 1 || !record->len) /* eof */132 return false;133 else if (type == 0)134 return true;135 else {136 record->error = true;137 return false;138 }139}140 141static int usb6fire_fw_ihex_init(const struct firmware *fw,142 struct ihex_record *record)143{144 record->txt_data = fw->data;145 record->txt_length = fw->size;146 record->txt_offset = 0;147 record->max_len = 0;148 /* read all records, if loop ends, record->error indicates,149 * whether ihex is valid. */150 while (usb6fire_fw_ihex_next_record(record))151 record->max_len = max(record->len, record->max_len);152 if (record->error)153 return -EINVAL;154 record->txt_offset = 0;155 return 0;156}157 158static int usb6fire_fw_ezusb_write(struct usb_device *device,159 int type, int value, char *data, int len)160{161 return usb_control_msg_send(device, 0, type,162 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,163 value, 0, data, len, 1000, GFP_KERNEL);164}165 166static int usb6fire_fw_ezusb_read(struct usb_device *device,167 int type, int value, char *data, int len)168{169 return usb_control_msg_recv(device, 0, type,170 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,171 value, 0, data, len, 1000, GFP_KERNEL);172}173 174static int usb6fire_fw_fpga_write(struct usb_device *device,175 char *data, int len)176{177 int actual_len;178 int ret;179 180 ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,181 &actual_len, 1000);182 if (ret < 0)183 return ret;184 else if (actual_len != len)185 return -EIO;186 return 0;187}188 189static int usb6fire_fw_ezusb_upload(190 struct usb_interface *intf, const char *fwname,191 unsigned int postaddr, u8 *postdata, unsigned int postlen)192{193 int ret;194 u8 data;195 struct usb_device *device = interface_to_usbdev(intf);196 const struct firmware *fw = NULL;197 struct ihex_record *rec = kmalloc(sizeof(struct ihex_record),198 GFP_KERNEL);199 200 if (!rec)201 return -ENOMEM;202 203 ret = request_firmware(&fw, fwname, &device->dev);204 if (ret < 0) {205 kfree(rec);206 dev_err(&intf->dev,207 "error requesting ezusb firmware %s.\n", fwname);208 return ret;209 }210 ret = usb6fire_fw_ihex_init(fw, rec);211 if (ret < 0) {212 kfree(rec);213 release_firmware(fw);214 dev_err(&intf->dev,215 "error validating ezusb firmware %s.\n", fwname);216 return ret;217 }218 /* upload firmware image */219 data = 0x01; /* stop ezusb cpu */220 ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);221 if (ret) {222 kfree(rec);223 release_firmware(fw);224 dev_err(&intf->dev,225 "unable to upload ezusb firmware %s: begin message.\n",226 fwname);227 return ret;228 }229 230 while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */231 ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address,232 rec->data, rec->len);233 if (ret) {234 kfree(rec);235 release_firmware(fw);236 dev_err(&intf->dev,237 "unable to upload ezusb firmware %s: data urb.\n",238 fwname);239 return ret;240 }241 }242 243 release_firmware(fw);244 kfree(rec);245 if (postdata) { /* write data after firmware has been uploaded */246 ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr,247 postdata, postlen);248 if (ret) {249 dev_err(&intf->dev,250 "unable to upload ezusb firmware %s: post urb.\n",251 fwname);252 return ret;253 }254 }255 256 data = 0x00; /* resume ezusb cpu */257 ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);258 if (ret) {259 dev_err(&intf->dev,260 "unable to upload ezusb firmware %s: end message.\n",261 fwname);262 return ret;263 }264 return 0;265}266 267static int usb6fire_fw_fpga_upload(268 struct usb_interface *intf, const char *fwname)269{270 int ret;271 int i;272 struct usb_device *device = interface_to_usbdev(intf);273 u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL);274 const char *c;275 const char *end;276 const struct firmware *fw;277 278 if (!buffer)279 return -ENOMEM;280 281 ret = request_firmware(&fw, fwname, &device->dev);282 if (ret < 0) {283 dev_err(&intf->dev, "unable to get fpga firmware %s.\n",284 fwname);285 kfree(buffer);286 return -EIO;287 }288 289 c = fw->data;290 end = fw->data + fw->size;291 292 ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0);293 if (ret) {294 kfree(buffer);295 release_firmware(fw);296 dev_err(&intf->dev,297 "unable to upload fpga firmware: begin urb.\n");298 return ret;299 }300 301 while (c != end) {302 for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)303 buffer[i] = bitrev8((u8)*c);304 305 ret = usb6fire_fw_fpga_write(device, buffer, i);306 if (ret < 0) {307 release_firmware(fw);308 kfree(buffer);309 dev_err(&intf->dev,310 "unable to upload fpga firmware: fw urb.\n");311 return ret;312 }313 }314 release_firmware(fw);315 kfree(buffer);316 317 ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0);318 if (ret) {319 dev_err(&intf->dev,320 "unable to upload fpga firmware: end urb.\n");321 return ret;322 }323 return 0;324}325 326/* check, if the firmware version the devices has currently loaded327 * is known by this driver. 'version' needs to have 4 bytes version328 * info data. */329static int usb6fire_fw_check(struct usb_interface *intf, const u8 *version)330{331 int i;332 333 for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)334 if (!memcmp(version, known_fw_versions + i, 2))335 return 0;336 337 dev_err(&intf->dev, "invalid firmware version in device: %4ph. "338 "please reconnect to power. if this failure "339 "still happens, check your firmware installation.",340 version);341 return -EINVAL;342}343 344int usb6fire_fw_init(struct usb_interface *intf)345{346 int i;347 int ret;348 struct usb_device *device = interface_to_usbdev(intf);349 /* buffer: 8 receiving bytes from device and350 * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */351 u8 buffer[12];352 353 ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8);354 if (ret) {355 dev_err(&intf->dev,356 "unable to receive device firmware state.\n");357 return ret;358 }359 if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {360 dev_err(&intf->dev,361 "unknown device firmware state received from device:");362 for (i = 0; i < 8; i++)363 printk(KERN_CONT "%02x ", buffer[i]);364 printk(KERN_CONT "\n");365 return -EIO;366 }367 /* do we need fpga loader ezusb firmware? */368 if (buffer[3] == 0x01) {369 ret = usb6fire_fw_ezusb_upload(intf,370 "6fire/dmx6firel2.ihx", 0, NULL, 0);371 if (ret < 0)372 return ret;373 return FW_NOT_READY;374 }375 /* do we need fpga firmware and application ezusb firmware? */376 else if (buffer[3] == 0x02) {377 ret = usb6fire_fw_check(intf, buffer + 4);378 if (ret < 0)379 return ret;380 ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");381 if (ret < 0)382 return ret;383 memcpy(buffer, ep_w_max_packet_size,384 sizeof(ep_w_max_packet_size));385 ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx",386 0x0003, buffer, sizeof(ep_w_max_packet_size));387 if (ret < 0)388 return ret;389 return FW_NOT_READY;390 }391 /* all fw loaded? */392 else if (buffer[3] == 0x03)393 return usb6fire_fw_check(intf, buffer + 4);394 /* unknown data? */395 else {396 dev_err(&intf->dev,397 "unknown device firmware state received from device: ");398 for (i = 0; i < 8; i++)399 printk(KERN_CONT "%02x ", buffer[i]);400 printk(KERN_CONT "\n");401 return -EIO;402 }403 return 0;404}405 406