brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · c51abc5 Raw
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