2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-01-24 20:45:30 +03:00
/*
* Linux driver for TerraTec DMX 6F ire USB
*
* Firmware loader
*
* Author : Torsten Schenk < torsten . schenk @ zoho . com >
* Created : Jan 01 , 2011
* Copyright : ( C ) Torsten Schenk
*/
# include <linux/firmware.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2011-04-23 22:56:43 +04:00
# include <linux/bitrev.h>
2011-09-23 15:32:11 +04:00
# include <linux/kernel.h>
2011-01-24 20:45:30 +03:00
# include "firmware.h"
# include "chip.h"
MODULE_FIRMWARE ( " 6fire/dmx6firel2.ihx " ) ;
MODULE_FIRMWARE ( " 6fire/dmx6fireap.ihx " ) ;
MODULE_FIRMWARE ( " 6fire/dmx6firecf.bin " ) ;
enum {
FPGA_BUFSIZE = 512 , FPGA_EP = 2
} ;
/*
* wMaxPacketSize of pcm endpoints .
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm . c
* fpp : frames per isopacket
*
* CAUTION : keep sizeof < = buffer [ ] in usb6fire_fw_init
*/
static const u8 ep_w_max_packet_size [ ] = {
0xe4 , 0x00 , 0xe4 , 0x00 , /* alt 1: 228 EP2 and EP6 (7 fpp) */
0xa4 , 0x01 , 0xa4 , 0x01 , /* alt 2: 420 EP2 and EP6 (13 fpp)*/
0x94 , 0x01 , 0x5c , 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
} ;
2013-05-23 15:38:29 +04:00
static const u8 known_fw_versions [ ] [ 2 ] = {
{ 0x03 , 0x01 }
2011-04-04 13:49:57 +04:00
} ;
2011-01-24 20:45:30 +03:00
struct ihex_record {
u16 address ;
u8 len ;
u8 data [ 256 ] ;
2011-03-31 05:57:33 +04:00
char error ; /* true if an error occurred parsing this record */
2011-01-24 20:45:30 +03:00
u8 max_len ; /* maximum record length in whole ihex */
/* private */
const char * txt_data ;
unsigned int txt_length ;
unsigned int txt_offset ; /* current position in txt_data */
} ;
static u8 usb6fire_fw_ihex_hex ( const u8 * data , u8 * crc )
{
2011-09-23 15:32:11 +04:00
u8 val = 0 ;
int hval ;
hval = hex_to_bin ( data [ 0 ] ) ;
if ( hval > = 0 )
val | = ( hval < < 4 ) ;
hval = hex_to_bin ( data [ 1 ] ) ;
if ( hval > = 0 )
val | = hval ;
2011-01-24 20:45:30 +03:00
* crc + = val ;
return val ;
}
/*
* returns true if record is available , false otherwise .
2011-03-31 05:57:33 +04:00
* iff an error occurred , false will be returned and record - > error will be true .
2011-01-24 20:45:30 +03:00
*/
static bool usb6fire_fw_ihex_next_record ( struct ihex_record * record )
{
u8 crc = 0 ;
u8 type ;
int i ;
record - > error = false ;
/* find begin of record (marked by a colon) */
while ( record - > txt_offset < record - > txt_length
& & record - > txt_data [ record - > txt_offset ] ! = ' : ' )
record - > txt_offset + + ;
if ( record - > txt_offset = = record - > txt_length )
return false ;
/* number of characters needed for len, addr and type entries */
record - > txt_offset + + ;
if ( record - > txt_offset + 8 > record - > txt_length ) {
record - > error = true ;
return false ;
}
record - > len = usb6fire_fw_ihex_hex ( record - > txt_data +
record - > txt_offset , & crc ) ;
record - > txt_offset + = 2 ;
record - > address = usb6fire_fw_ihex_hex ( record - > txt_data +
record - > txt_offset , & crc ) < < 8 ;
record - > txt_offset + = 2 ;
record - > address | = usb6fire_fw_ihex_hex ( record - > txt_data +
record - > txt_offset , & crc ) ;
record - > txt_offset + = 2 ;
type = usb6fire_fw_ihex_hex ( record - > txt_data +
record - > txt_offset , & crc ) ;
record - > txt_offset + = 2 ;
/* number of characters needed for data and crc entries */
if ( record - > txt_offset + 2 * ( record - > len + 1 ) > record - > txt_length ) {
record - > error = true ;
return false ;
}
for ( i = 0 ; i < record - > len ; i + + ) {
record - > data [ i ] = usb6fire_fw_ihex_hex ( record - > txt_data
+ record - > txt_offset , & crc ) ;
record - > txt_offset + = 2 ;
}
usb6fire_fw_ihex_hex ( record - > txt_data + record - > txt_offset , & crc ) ;
if ( crc ) {
record - > error = true ;
return false ;
}
if ( type = = 1 | | ! record - > len ) /* eof */
return false ;
else if ( type = = 0 )
return true ;
else {
record - > error = true ;
return false ;
}
}
static int usb6fire_fw_ihex_init ( const struct firmware * fw ,
struct ihex_record * record )
{
record - > txt_data = fw - > data ;
record - > txt_length = fw - > size ;
record - > txt_offset = 0 ;
record - > max_len = 0 ;
/* read all records, if loop ends, record->error indicates,
* whether ihex is valid . */
while ( usb6fire_fw_ihex_next_record ( record ) )
record - > max_len = max ( record - > len , record - > max_len ) ;
if ( record - > error )
return - EINVAL ;
record - > txt_offset = 0 ;
return 0 ;
}
static int usb6fire_fw_ezusb_write ( struct usb_device * device ,
int type , int value , char * data , int len )
{
int ret ;
ret = usb_control_msg ( device , usb_sndctrlpipe ( device , 0 ) , type ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
value , 0 , data , len , HZ ) ;
if ( ret < 0 )
return ret ;
else if ( ret ! = len )
return - EIO ;
return 0 ;
}
static int usb6fire_fw_ezusb_read ( struct usb_device * device ,
int type , int value , char * data , int len )
{
int ret = usb_control_msg ( device , usb_rcvctrlpipe ( device , 0 ) , type ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE , value ,
0 , data , len , HZ ) ;
if ( ret < 0 )
return ret ;
else if ( ret ! = len )
return - EIO ;
return 0 ;
}
static int usb6fire_fw_fpga_write ( struct usb_device * device ,
char * data , int len )
{
int actual_len ;
int ret ;
ret = usb_bulk_msg ( device , usb_sndbulkpipe ( device , FPGA_EP ) , data , len ,
& actual_len , HZ ) ;
if ( ret < 0 )
return ret ;
else if ( actual_len ! = len )
return - EIO ;
return 0 ;
}
static int usb6fire_fw_ezusb_upload (
struct usb_interface * intf , const char * fwname ,
unsigned int postaddr , u8 * postdata , unsigned int postlen )
{
int ret ;
u8 data ;
struct usb_device * device = interface_to_usbdev ( intf ) ;
2012-06-16 18:58:36 +04:00
const struct firmware * fw = NULL ;
2011-01-24 20:45:30 +03:00
struct ihex_record * rec = kmalloc ( sizeof ( struct ihex_record ) ,
GFP_KERNEL ) ;
if ( ! rec )
return - ENOMEM ;
ret = request_firmware ( & fw , fwname , & device - > dev ) ;
if ( ret < 0 ) {
kfree ( rec ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" error requesting ezusb firmware %s. \n " , fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
ret = usb6fire_fw_ihex_init ( fw , rec ) ;
if ( ret < 0 ) {
kfree ( rec ) ;
2011-05-30 14:49:01 +04:00
release_firmware ( fw ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" error validating ezusb firmware %s. \n " , fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
/* upload firmware image */
data = 0x01 ; /* stop ezusb cpu */
ret = usb6fire_fw_ezusb_write ( device , 0xa0 , 0xe600 , & data , 1 ) ;
if ( ret < 0 ) {
kfree ( rec ) ;
release_firmware ( fw ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload ezusb firmware %s: begin message. \n " ,
fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
while ( usb6fire_fw_ihex_next_record ( rec ) ) { /* write firmware */
ret = usb6fire_fw_ezusb_write ( device , 0xa0 , rec - > address ,
rec - > data , rec - > len ) ;
if ( ret < 0 ) {
kfree ( rec ) ;
release_firmware ( fw ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload ezusb firmware %s: data urb. \n " ,
fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
}
release_firmware ( fw ) ;
kfree ( rec ) ;
if ( postdata ) { /* write data after firmware has been uploaded */
ret = usb6fire_fw_ezusb_write ( device , 0xa0 , postaddr ,
postdata , postlen ) ;
if ( ret < 0 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload ezusb firmware %s: post urb. \n " ,
fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
}
data = 0x00 ; /* resume ezusb cpu */
ret = usb6fire_fw_ezusb_write ( device , 0xa0 , 0xe600 , & data , 1 ) ;
if ( ret < 0 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload ezusb firmware %s: end message. \n " ,
fwname ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
return 0 ;
}
static int usb6fire_fw_fpga_upload (
struct usb_interface * intf , const char * fwname )
{
int ret ;
int i ;
struct usb_device * device = interface_to_usbdev ( intf ) ;
u8 * buffer = kmalloc ( FPGA_BUFSIZE , GFP_KERNEL ) ;
const char * c ;
const char * end ;
const struct firmware * fw ;
if ( ! buffer )
return - ENOMEM ;
ret = request_firmware ( & fw , fwname , & device - > dev ) ;
if ( ret < 0 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev , " unable to get fpga firmware %s. \n " ,
2011-01-24 20:45:30 +03:00
fwname ) ;
kfree ( buffer ) ;
return - EIO ;
}
c = fw - > data ;
end = fw - > data + fw - > size ;
ret = usb6fire_fw_ezusb_write ( device , 8 , 0 , NULL , 0 ) ;
if ( ret < 0 ) {
kfree ( buffer ) ;
release_firmware ( fw ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload fpga firmware: begin urb. \n " ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
while ( c ! = end ) {
for ( i = 0 ; c ! = end & & i < FPGA_BUFSIZE ; i + + , c + + )
2014-10-29 00:22:49 +03:00
buffer [ i ] = bitrev8 ( ( u8 ) * c ) ;
2011-01-24 20:45:30 +03:00
ret = usb6fire_fw_fpga_write ( device , buffer , i ) ;
if ( ret < 0 ) {
release_firmware ( fw ) ;
kfree ( buffer ) ;
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload fpga firmware: fw urb. \n " ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
}
release_firmware ( fw ) ;
kfree ( buffer ) ;
ret = usb6fire_fw_ezusb_write ( device , 9 , 0 , NULL , 0 ) ;
if ( ret < 0 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to upload fpga firmware: end urb. \n " ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
return 0 ;
}
2011-04-04 13:49:57 +04:00
/* check, if the firmware version the devices has currently loaded
* is known by this driver . ' version ' needs to have 4 bytes version
* info data . */
2014-02-26 18:51:04 +04:00
static int usb6fire_fw_check ( struct usb_interface * intf , const u8 * version )
2011-04-04 13:49:57 +04:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( known_fw_versions ) ; i + + )
2013-05-23 15:38:29 +04:00
if ( ! memcmp ( version , known_fw_versions + i , 2 ) )
2011-04-04 13:49:57 +04:00
return 0 ;
2015-10-16 15:14:29 +03:00
dev_err ( & intf - > dev , " invalid firmware version in device: %4ph. "
2011-04-04 13:49:57 +04:00
" please reconnect to power. if this failure "
" still happens, check your firmware installation. " ,
2013-08-07 18:55:14 +04:00
version ) ;
2011-04-04 13:49:57 +04:00
return - EINVAL ;
}
2011-01-24 20:45:30 +03:00
int usb6fire_fw_init ( struct usb_interface * intf )
{
int i ;
int ret ;
struct usb_device * device = interface_to_usbdev ( intf ) ;
/* buffer: 8 receiving bytes from device and
* sizeof ( EP_W_MAX_PACKET_SIZE ) bytes for non - const copy */
u8 buffer [ 12 ] ;
ret = usb6fire_fw_ezusb_read ( device , 1 , 0 , buffer , 8 ) ;
if ( ret < 0 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unable to receive device firmware state. \n " ) ;
2011-01-24 20:45:30 +03:00
return ret ;
}
2011-04-04 13:49:57 +04:00
if ( buffer [ 0 ] ! = 0xeb | | buffer [ 1 ] ! = 0xaa | | buffer [ 2 ] ! = 0x55 ) {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unknown device firmware state received from device: " ) ;
2011-01-24 20:45:30 +03:00
for ( i = 0 ; i < 8 ; i + + )
2014-02-26 18:51:04 +04:00
printk ( KERN_CONT " %02x " , buffer [ i ] ) ;
printk ( KERN_CONT " \n " ) ;
2011-01-24 20:45:30 +03:00
return - EIO ;
}
/* do we need fpga loader ezusb firmware? */
2011-04-04 13:49:57 +04:00
if ( buffer [ 3 ] = = 0x01 ) {
2011-01-24 20:45:30 +03:00
ret = usb6fire_fw_ezusb_upload ( intf ,
" 6fire/dmx6firel2.ihx " , 0 , NULL , 0 ) ;
if ( ret < 0 )
return ret ;
return FW_NOT_READY ;
}
/* do we need fpga firmware and application ezusb firmware? */
2011-04-04 13:49:57 +04:00
else if ( buffer [ 3 ] = = 0x02 ) {
2014-02-26 18:51:04 +04:00
ret = usb6fire_fw_check ( intf , buffer + 4 ) ;
2011-04-04 13:49:57 +04:00
if ( ret < 0 )
return ret ;
2011-01-24 20:45:30 +03:00
ret = usb6fire_fw_fpga_upload ( intf , " 6fire/dmx6firecf.bin " ) ;
if ( ret < 0 )
return ret ;
memcpy ( buffer , ep_w_max_packet_size ,
sizeof ( ep_w_max_packet_size ) ) ;
ret = usb6fire_fw_ezusb_upload ( intf , " 6fire/dmx6fireap.ihx " ,
0x0003 , buffer , sizeof ( ep_w_max_packet_size ) ) ;
if ( ret < 0 )
return ret ;
return FW_NOT_READY ;
}
/* all fw loaded? */
2011-04-04 13:49:57 +04:00
else if ( buffer [ 3 ] = = 0x03 )
2014-02-26 18:51:04 +04:00
return usb6fire_fw_check ( intf , buffer + 4 ) ;
2011-01-24 20:45:30 +03:00
/* unknown data? */
else {
2014-02-26 18:51:04 +04:00
dev_err ( & intf - > dev ,
" unknown device firmware state received from device: " ) ;
2011-01-24 20:45:30 +03:00
for ( i = 0 ; i < 8 ; i + + )
2014-02-26 18:51:04 +04:00
printk ( KERN_CONT " %02x " , buffer [ i ] ) ;
printk ( KERN_CONT " \n " ) ;
2011-01-24 20:45:30 +03:00
return - EIO ;
}
return 0 ;
}