/* * Written by Oron Peled * 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 #include #include #include #include #include #include #include "hexfile.h" #include "pic_loader.h" #define DBG_MASK 0x20 #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 *ab, 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(ab != 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, DBG_MASK, "dump:picline[W]", (char *)phead, pack_len); ret = astribank_send(ab, 0, buf, pack_len, TIMEOUT); if(ret < 0) { ERR("astribank_send failed: %d\n", ret); return ret; } DBG("astribank_send: Written %d bytes\n", ret); if (recv_answer) { ret = astribank_recv(ab, 0, 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, DBG_MASK, "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 *ab, 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; const char *devstr; const struct xusb_device *xusb; v = (v[0]) ? v : "Unknown"; assert(ab != NULL); assert(hexdata != NULL); xusb = xusb_dev_of_astribank(ab); devstr = xusb_devpath(xusb); i = xusb_packet_size(xusb); if(i != 512) { ERR("%s: Skip PIC burning (not USB2)\n", devstr); return 0; } INFO("%s [%s]: Loading PIC Firmware: %s (version %s)\n", devstr, xusb_serial(xusb), hexdata->fname, hexdata->version_info); basename = pic_basename(hexdata->fname, &card_type); if(!basename) { ERR("%s: Bad PIC filename '%s'. Abort.\n", devstr, 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 (astribank_recv(ab, 0, buf, sizeof(buf), TIMEOUT) <= 0) break; } if((ret = send_picline(ab, 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("%s: hexdata finished early (line %d)", devstr, 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("%s: Bad line len %d\n", devstr, len); return 0; } data = hexline->d.content.tt_data.data; check_sum ^= data[0] ^ data[1] ^ data[2]; ret = send_picline(ab, 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("%s: Unexpected TT = %d in line %d\n", devstr, hexline->d.content.header.tt, i); return 0; } } if((ret = send_picline(ab, 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 *ab, int numfiles, char *filelist[]) { int i; const char *devstr; devstr = xusb_devpath(xusb_dev_of_astribank(ab)); DBG("%s: Loading %d PIC files...\n", devstr, 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(ab, picdata)) { ERR("%s: PIC %s burning failed\n", devstr, curr); return -ENODEV; } free_hexdata(picdata); } if((i = send_picline(ab, 0, PIC_ENDS_FLAG, 0, NULL, 0)) != 0) { ERR("%s: PIC end burning failed\n", devstr); return -ENODEV; } return 0; }