277 lines
7.2 KiB
C
277 lines
7.2 KiB
C
|
/*
|
||
|
* Written by Oron Peled <oron@actcom.co.il>
|
||
|
* Copyright (C) 2008, Xorcom
|
||
|
*
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
#include <regex.h>
|
||
|
#include "hexfile.h"
|
||
|
#include "pic_loader.h"
|
||
|
#include "debug.h"
|
||
|
|
||
|
#define DBG_MASK 0x03
|
||
|
#define MAX_HEX_LINES 10000
|
||
|
#define TIMEOUT 500
|
||
|
|
||
|
enum xpp_packet_types {
|
||
|
PIC_REQ_XOP = 0x09,
|
||
|
PIC_REP_XOP = 0x0A
|
||
|
};
|
||
|
|
||
|
struct xpp_packet_header {
|
||
|
struct {
|
||
|
uint16_t len;
|
||
|
uint8_t op;
|
||
|
uint8_t unit;
|
||
|
} PACKED header;
|
||
|
union {
|
||
|
struct {
|
||
|
struct {
|
||
|
uint8_t flags;
|
||
|
uint8_t card_type;
|
||
|
uint16_t offs;
|
||
|
} pic_header;
|
||
|
uint8_t data[3];
|
||
|
} PACKED pic_packet;
|
||
|
} d;
|
||
|
} PACKED;
|
||
|
|
||
|
int send_picline(struct astribank_device *astribank, uint8_t card_type, enum pic_command pcmd, int offs, uint8_t *data, int data_len)
|
||
|
{
|
||
|
int recv_answer = 0;
|
||
|
char buf[PACKET_SIZE];
|
||
|
struct xpp_packet_header *phead = (struct xpp_packet_header *)buf;
|
||
|
int pack_len;
|
||
|
int ret;
|
||
|
|
||
|
assert(astribank != NULL);
|
||
|
pack_len = data_len + sizeof(phead->header) + sizeof(phead->d.pic_packet.pic_header);
|
||
|
phead->header.len = pack_len;
|
||
|
phead->header.op = PIC_REQ_XOP;
|
||
|
phead->header.unit = 0x00;
|
||
|
phead->d.pic_packet.pic_header.flags = pcmd;
|
||
|
phead->d.pic_packet.pic_header.card_type = card_type;
|
||
|
phead->d.pic_packet.pic_header.offs = offs;
|
||
|
if(data)
|
||
|
memcpy(phead->d.pic_packet.data, data, data_len);
|
||
|
switch (pcmd) {
|
||
|
case PIC_START_FLAG:
|
||
|
break;
|
||
|
case PIC_DATA_FLAG:
|
||
|
break;
|
||
|
case PIC_END_FLAG:
|
||
|
recv_answer = 1;
|
||
|
break;
|
||
|
case PIC_ENDS_FLAG:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
DBG("PICLINE: pack_len=%d pcmd=%d\n", pack_len, pcmd);
|
||
|
dump_packet(LOG_DEBUG, "dump:picline[W]", (char *)phead, pack_len);
|
||
|
|
||
|
ret = send_usb(astribank, buf, pack_len, TIMEOUT);
|
||
|
if(ret < 0) {
|
||
|
ERR("send_usb failed: %d\n", ret);
|
||
|
return ret;
|
||
|
}
|
||
|
DBG("send_usb: Written %d bytes\n", ret);
|
||
|
if (recv_answer) {
|
||
|
ret = recv_usb(astribank, buf, sizeof(buf), TIMEOUT);
|
||
|
if(ret <= 0) {
|
||
|
ERR("No USB packs to read\n");
|
||
|
return ret;
|
||
|
} else {
|
||
|
phead = (struct xpp_packet_header *)buf;
|
||
|
if(phead->header.op != PIC_REP_XOP) {
|
||
|
ERR("Got unexpected reply OP=0x%02X\n", phead->header.op);
|
||
|
dump_packet(LOG_ERR, "hexline[ERR]", buf, ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
DBG("received OP=0x%02X, checksum=%02X\n", phead->header.op, phead->d.pic_packet.data[0]);
|
||
|
if(phead->d.pic_packet.data[0] != 0) {
|
||
|
ERR("PIC burning, bad checksum\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const char *pic_basename(const char *fname, uint8_t *card_type)
|
||
|
{
|
||
|
const char *basename;
|
||
|
regex_t regex;
|
||
|
char ebuf[BUFSIZ];
|
||
|
const char re[] = "PIC_TYPE_([0-9]+)\\.hex";
|
||
|
regmatch_t pmatch[2]; /* One for the whole match, one for the number */
|
||
|
int nmatch = (sizeof(pmatch)/sizeof(pmatch[0]));
|
||
|
int len;
|
||
|
int ret;
|
||
|
|
||
|
basename = strrchr(fname, '/');
|
||
|
if(!basename)
|
||
|
basename = fname;
|
||
|
if((ret = regcomp(®ex, re, REG_ICASE | REG_EXTENDED)) != 0) {
|
||
|
regerror(ret, ®ex, ebuf, sizeof(ebuf));
|
||
|
ERR("regcomp: %s\n", ebuf);
|
||
|
return NULL;
|
||
|
}
|
||
|
if((ret = regexec(®ex, basename, nmatch, pmatch, 0)) != 0) {
|
||
|
regerror(ret, ®ex, ebuf, sizeof(ebuf));
|
||
|
ERR("regexec: %s\n", ebuf);
|
||
|
regfree(®ex);
|
||
|
return NULL;
|
||
|
}
|
||
|
/*
|
||
|
* Should have both complete match and a parentheses match
|
||
|
*/
|
||
|
if(pmatch[0].rm_so == -1 || pmatch[1].rm_so == -1) {
|
||
|
ERR("pic_basename: Bad match: pmatch[0].rm_so=%d pmatch[1].rm_so=%d\n",
|
||
|
pmatch[0].rm_so, pmatch[1].rm_so == -1);
|
||
|
regfree(®ex);
|
||
|
return NULL;
|
||
|
}
|
||
|
len = pmatch[1].rm_eo - pmatch[1].rm_so;
|
||
|
if(len >= sizeof(ebuf) - 1)
|
||
|
len = sizeof(ebuf) - 1;
|
||
|
memcpy(ebuf, basename + pmatch[1].rm_so, len);
|
||
|
ebuf[len] = '\0';
|
||
|
DBG("match: %s\n", ebuf);
|
||
|
ret = atoi(ebuf);
|
||
|
if(ret <= 0 || ret > 9) {
|
||
|
ERR("pic_basename: Bad type number %d\n", ret);
|
||
|
regfree(®ex);
|
||
|
return NULL;
|
||
|
}
|
||
|
*card_type = ret;
|
||
|
regfree(®ex);
|
||
|
return basename;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Returns: true on success, false on failure
|
||
|
*/
|
||
|
static int pic_burn(struct astribank_device *astribank, const struct hexdata *hexdata)
|
||
|
{
|
||
|
const char *v = hexdata->version_info;
|
||
|
const char *basename;
|
||
|
uint8_t *data;
|
||
|
unsigned char check_sum = 0;
|
||
|
uint8_t card_type;
|
||
|
int ret;
|
||
|
unsigned int i;
|
||
|
|
||
|
v = (v[0]) ? v : "Unknown";
|
||
|
assert(astribank != NULL);
|
||
|
assert(hexdata != NULL);
|
||
|
if(!astribank->is_usb2) {
|
||
|
ERR("Skip PIC burning (not USB2)\n");
|
||
|
return 0;
|
||
|
}
|
||
|
INFO("Load PIC: %s (version %s)\n", hexdata->fname, hexdata->version_info);
|
||
|
basename = pic_basename(hexdata->fname, &card_type);
|
||
|
if(!basename) {
|
||
|
ERR("Bad PIC filename '%s'. Abort.\n", hexdata->fname);
|
||
|
return 0;
|
||
|
}
|
||
|
DBG("basename=%s card_type=%d maxlines=%d\n",
|
||
|
basename, card_type, hexdata->maxlines);
|
||
|
/*
|
||
|
* Try to read extra left-overs from USB controller
|
||
|
*/
|
||
|
for(i = 2; i; i--) {
|
||
|
char buf[PACKET_SIZE];
|
||
|
|
||
|
if(usb_bulk_read(astribank->handle, astribank->my_ep_in, buf, sizeof(buf), 1) <= 0)
|
||
|
break;
|
||
|
}
|
||
|
if((ret = send_picline(astribank, card_type, PIC_START_FLAG, 0, NULL, 0)) != 0) {
|
||
|
perror("Failed sending start hexline");
|
||
|
return 0;
|
||
|
}
|
||
|
for(i = 0; i < hexdata->maxlines; i++) {
|
||
|
struct hexline *hexline;
|
||
|
unsigned int len;
|
||
|
|
||
|
hexline = hexdata->lines[i];
|
||
|
if(!hexline) {
|
||
|
ERR("hexdata finished early (line %d)", i);
|
||
|
return 0;
|
||
|
}
|
||
|
if(hexline->d.content.header.tt == TT_DATA) {
|
||
|
len = hexline->d.content.header.ll; /* don't send checksum */
|
||
|
if(len != 3) {
|
||
|
ERR("Bad line len %d\n", len);
|
||
|
return 0;
|
||
|
}
|
||
|
data = hexline->d.content.tt_data.data;
|
||
|
check_sum ^= data[0] ^ data[1] ^ data[2];
|
||
|
ret = send_picline(astribank, card_type, PIC_DATA_FLAG,
|
||
|
hexline->d.content.header.offset, data, len);
|
||
|
if(ret) {
|
||
|
perror("Failed sending data hexline");
|
||
|
return 0;
|
||
|
}
|
||
|
} else if(hexline->d.content.header.tt == TT_EOF) {
|
||
|
break;
|
||
|
} else {
|
||
|
ERR("Unexpected TT = %d in line %d\n",
|
||
|
hexline->d.content.header.tt, i);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
if((ret = send_picline(astribank, card_type, PIC_END_FLAG, 0, &check_sum, 1)) != 0) {
|
||
|
perror("Failed sending end hexline");
|
||
|
return 0;
|
||
|
}
|
||
|
DBG("Finished...\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int load_pic(struct astribank_device *astribank, int numfiles, char *filelist[])
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
DBG("Loading %d PIC files...\n", numfiles);
|
||
|
for(i = 0; i < numfiles; i++) {
|
||
|
struct hexdata *picdata;
|
||
|
const char *curr = filelist[i];
|
||
|
|
||
|
DBG("%s\n", curr);
|
||
|
if((picdata = parse_hexfile(curr, MAX_HEX_LINES)) == NULL) {
|
||
|
perror(curr);
|
||
|
return -errno;
|
||
|
}
|
||
|
if(!pic_burn(astribank, picdata)) {
|
||
|
ERR("PIC %s burning failed\n", curr);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
free_hexdata(picdata);
|
||
|
}
|
||
|
if((i = send_picline(astribank, 0, PIC_ENDS_FLAG, 0, NULL, 0)) != 0) {
|
||
|
ERR("PIC end burning failed\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|