1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
/* Name: main.c
* Project: AVR bootloader HID
* Author: Christian Starkjohann
* Creation Date: 2007-03-19
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
* License: Proprietary, free under certain conditions. See Documentation.
* This Revision: $Id: main.c 787 2010-05-30 20:54:25Z cs $
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "usbcalls.h"
#define IDENT_VENDOR_NUM 0x16c0
#define IDENT_VENDOR_STRING "obdev.at"
#define IDENT_PRODUCT_NUM 1503
#define IDENT_PRODUCT_STRING "HIDBoot"
// extra delays before more USB requests for tiny85 compatibility while chip frozen
// this number could be lower than 16000 - minimum will be greater than 9000 (erase and write)
#define TINY85_POSTWRITE_DELAY 16000
/* ------------------------------------------------------------------------- */
static char dataBuffer[65536 + 256]; /* buffer for file data */
static int startAddress, endAddress;
static char leaveBootLoader = 0;
/* ------------------------------------------------------------------------- */
static int parseUntilColon(FILE *fp)
{
int c;
do{
c = getc(fp);
}while(c != ':' && c != EOF);
return c;
}
static int parseHex(FILE *fp, int numDigits)
{
int i;
char temp[9];
for(i = 0; i < numDigits; i++)
temp[i] = getc(fp);
temp[i] = 0;
return strtol(temp, NULL, 16);
}
/* ------------------------------------------------------------------------- */
static int parseIntelHex(char *hexfile, char buffer[65536 + 256], int *startAddr, int *endAddr)
{
int address, base, d, segment, i, lineLen, sum;
FILE *input;
input = fopen(hexfile, "r");
if(input == NULL){
fprintf(stderr, "error opening %s: %s\n", hexfile, strerror(errno));
return 1;
}
while(parseUntilColon(input) == ':'){
sum = 0;
sum += lineLen = parseHex(input, 2);
base = address = parseHex(input, 4);
sum += address >> 8;
sum += address;
sum += segment = parseHex(input, 2); /* segment value? */
if(segment != 0) /* ignore lines where this byte is not 0 */
continue;
for(i = 0; i < lineLen ; i++){
d = parseHex(input, 2);
buffer[address++] = d;
sum += d;
}
sum += parseHex(input, 2);
if((sum & 0xff) != 0){
fprintf(stderr, "Warning: Checksum error between address 0x%x and 0x%x\n", base, address);
}
if(*startAddr > base)
*startAddr = base;
if(*endAddr < address)
*endAddr = address;
}
fclose(input);
return 0;
}
/* ------------------------------------------------------------------------- */
char *usbErrorMessage(int errCode)
{
static char buffer[80];
switch(errCode){
case USB_ERROR_ACCESS: return "Access to device denied";
case USB_ERROR_NOTFOUND: return "The specified device was not found";
case USB_ERROR_BUSY: return "The device is used by another application";
case USB_ERROR_IO: return "Communication error with device";
default:
sprintf(buffer, "Unknown USB error %d", errCode);
return buffer;
}
return NULL; /* not reached */
}
static int getUsbInt(char *buffer, int numBytes)
{
int shift = 0, value = 0, i;
for(i = 0; i < numBytes; i++){
value |= ((int)*buffer & 0xff) << shift;
shift += 8;
buffer++;
}
return value;
}
static void setUsbInt(char *buffer, int value, int numBytes)
{
int i;
for(i = 0; i < numBytes; i++){
*buffer++ = value;
value >>= 8;
}
}
/* ------------------------------------------------------------------------- */
typedef struct deviceInfo{
char reportId;
char pageSize[2]; // TODO: change this to one byte?
char flashSize[4]; // TODO: change this to two bytes?
}deviceInfo_t;
typedef struct deviceData{
char reportId;
char address[3];
char data[64];
}deviceData_t;
static int uploadData(char *dataBuffer, int startAddr, int endAddr)
{
usbDevice_t *dev = NULL;
int err = 0, len, pageSize, deviceSize;
union{
char bytes[1];
deviceInfo_t info;
deviceData_t data;
} buffer;
if((err = usbOpenDevice(&dev, IDENT_VENDOR_NUM, IDENT_VENDOR_STRING, IDENT_PRODUCT_NUM, IDENT_PRODUCT_STRING, 1)) != 0){
fprintf(stderr, "Error opening HIDBoot device: %s\n", usbErrorMessage(err));
goto errorOccurred;
}
len = sizeof(buffer);
if(endAddr > startAddr){ // we need to upload data
if((err = usbGetReport(dev, USB_HID_REPORT_TYPE_FEATURE, 1, buffer.bytes, &len)) != 0){
fprintf(stderr, "Error reading page size: %s\n", usbErrorMessage(err));
goto errorOccurred;
}
if(len < sizeof(buffer.info)){
fprintf(stderr, "Not enough bytes in device info report (%d instead of %d)\n", len, (int)sizeof(buffer.info));
err = -1;
goto errorOccurred;
}
pageSize = getUsbInt(buffer.info.pageSize, 2);
deviceSize = getUsbInt(buffer.info.flashSize, 4);
printf("Page size = %d (0x%x)\n", pageSize, pageSize);
printf("Device size = %d (0x%x)\n", deviceSize, deviceSize);
if(endAddr > deviceSize){
fprintf(stderr, "Data (%d bytes) exceeds remaining flash size!\n", endAddr);
err = -1;
goto errorOccurred;
}
startAddr -= startAddr % pageSize; // round down to start of page
endAddr = (endAddr - (endAddr % pageSize)) + pageSize; // round up to next whole page
printf("Uploading %d (0x%x) bytes starting at %d (0x%x)\n", endAddr - startAddr, endAddr - startAddr, startAddr, startAddr);
while(startAddr < endAddr){
buffer.data.reportId = 2;
memcpy(buffer.data.data, dataBuffer + startAddr, pageSize);
setUsbInt(buffer.data.address, startAddr, 3);
printf("\r0x%05x ... 0x%05x", startAddr, startAddr + pageSize);
fflush(stdout);
if((err = usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, sizeof(buffer.data))) != 0){
fprintf(stderr, "Error uploading data block: %s\n", usbErrorMessage(err));
goto errorOccurred;
}
startAddr += pageSize;
// special tiny85 chillout session - chip freezes during write, so we
// need to make sure we don't send it any requests while it's busy
// erasing and writing
usleep(TINY85_POSTWRITE_DELAY); // regular page write duration
}
printf("\n");
}
if(leaveBootLoader){
/* and now leave boot loader: */
buffer.info.reportId = 1;
usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, sizeof(buffer.info));
/* Ignore errors here. If the device reboots before we poll the response,
* this request fails.
*/
}
errorOccurred:
if(dev != NULL)
usbCloseDevice(dev);
return err;
}
/* ------------------------------------------------------------------------- */
static void printUsage(char *pname)
{
fprintf(stderr, "usage: %s [-r] [<intel-hexfile>]\n", pname);
}
int main(int argc, char **argv)
{
char *file = NULL;
if(argc < 2){
printUsage(argv[0]);
return 1;
}
if(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0){
printUsage(argv[0]);
return 1;
}
if(strcmp(argv[1], "-r") == 0){
leaveBootLoader = 1;
if(argc >= 3){
file = argv[2];
}
}else{
file = argv[1];
}
startAddress = sizeof(dataBuffer);
endAddress = 0;
if(file != NULL){ // an upload file was given, load the data
memset(dataBuffer, -1, sizeof(dataBuffer));
if(parseIntelHex(file, dataBuffer, &startAddress, &endAddress))
return 1;
if(startAddress >= endAddress){
fprintf(stderr, "No data in input file, exiting.\n");
return 0;
}
}
// if no file was given, endAddress is less than startAddress and no data is uploaded
if(uploadData(dataBuffer, startAddress, endAddress))
return 1;
return 0;
}
/* ------------------------------------------------------------------------- */
|