1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <termios.h>
5 #include <sysexits.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <sys/time.h>
10 #include "roboctl.h"
11
12 /**
13 *
14 */
15
pic_send_command(int fd,const char * raw_cmd,int raw_len,const char * data,int dlen,char * response,int eot)16 rct_status_t pic_send_command(int fd,const char *raw_cmd,int raw_len,
17 const char *data,int dlen,char *response,
18 int eot)
19
20 {
21 char packet[PIC_CMD_MAX+1],
22 payload[PIC_CMD_MAX+1],
23 checksum = 0;
24 int payload_len,
25 packet_len,
26 c,
27 sent;
28
29 debug_printf("pic_send_command(): raw_cmd: ");
30 debug_hex_dump(raw_cmd,raw_len);
31
32 /* Build payload */
33 for (payload_len=0; payload_len < raw_len; ++payload_len)
34 {
35 checksum -= raw_cmd[payload_len];
36 payload[payload_len] = raw_cmd[payload_len];
37 }
38
39 /* If write command, add data */
40 if ( raw_cmd[0] == PIC_WRITE_PROGRAM_MEM )
41 {
42 if ( dlen > 31 )
43 {
44 fprintf(stderr,"pic_send_data(): Cannot send more than 31 blocks.\n");
45 return -1;
46 }
47
48 dlen *= PIC_WRITE_BLOCK_SIZE; /* 8 bytes in a write block */
49 c = 0;
50 while ( dlen-- > 0 )
51 {
52 checksum -= data[c];
53 payload[payload_len++] = data[c++];
54 }
55 }
56
57 /* Add checksum */
58 payload[payload_len++] = checksum;
59
60 debug_printf("Raw payload: ");
61 debug_hex_dump(payload,payload_len);
62
63 /* PIC protocol begins with 2 SI chars */
64 packet[0] = packet[1] = CHAR_SI;
65
66 /* Add payload (with special chars escaped) to packet */
67 packet_len = 2 + memcpy_esc(packet+2,payload,payload_len);
68
69 /* Add EOT to finish packet */
70 packet[packet_len++] = CHAR_EOT;
71
72 debug_printf("Command packet: ");
73 debug_hex_dump(packet,packet_len);
74
75 /* Transmit to controller */
76 if ( (sent = write(fd,packet,packet_len)) != packet_len )
77 {
78 fprintf(stderr,"Error sending command. %d bytes to write, sent %d\n",
79 packet_len,sent);
80 return -1;
81 }
82
83 pic_read_response(fd,response,eot);
84
85 return RCT_OK;
86 }
87
88
pic_read_response(int fd,char * response,int eot)89 rct_status_t pic_read_response(int fd,char *response,int eot)
90
91 {
92 int esc;
93 char *dest;
94
95 /* Read response */
96 dest = response;
97 esc = 0;
98 do
99 {
100 read(fd,dest,1);
101 if ( *dest == CHAR_ESC )
102 {
103 esc = 1;
104 read(fd,dest,1);
105 }
106 else
107 {
108 esc = 0;
109 }
110 //debug_printf("%c ", *dest);
111 } while ( (dest-response < PIC_RESPONSE_MAX) &&
112 ((*dest++ != eot) || esc) );
113
114 debug_printf("Response packet: ");
115 debug_hex_dump(response,dest-response);
116 return RCT_OK;
117 }
118
119
120 /**
121 * Dump a character string of length 'len' in HEX format.
122 */
123
debug_hex_dump(const char * str,int len)124 void debug_hex_dump(const char *str,int len)
125
126 {
127 const char *p;
128 extern int Debug;
129
130 if ( Debug )
131 {
132 for (p=str; p<str+len; ++p)
133 printf("0x%02X ",(unsigned char)*p);
134 putchar('\n');
135 fflush(stdout);
136 }
137 }
138
139
memcpy_esc(char * dest,const char * src,int slen)140 int memcpy_esc(char *dest,const char *src,int slen)
141
142 {
143 char *p = dest;
144
145 while ( slen-- > 0 )
146 {
147 /* SI, EOT, and ESC in payload must be escaped */
148 switch(*src)
149 {
150 case CHAR_SI:
151 case CHAR_EOT:
152 case CHAR_ESC:
153 *p++ = CHAR_ESC;
154 break;
155 default:
156 break;
157 }
158 *p++ = *src++;
159 }
160 return p - dest;
161 }
162
163
pic_erase_program_mem(rct_pic_t * pic,unsigned long start_address,unsigned long end_address)164 rct_status_t pic_erase_program_mem(rct_pic_t *pic,
165 unsigned long start_address,
166 unsigned long end_address)
167
168 {
169 unsigned long erase_blocks, addr, blocks;
170 char cmd[7];
171
172 erase_blocks = (end_address - start_address) / PIC_ERASE_BLOCK_SIZE + 1;
173 debug_printf("Program length: %lu Erase blocks: %lu\n",
174 end_address - start_address,erase_blocks);
175 debug_printf("Erasing %lu blocks at %06lX.\n",erase_blocks,start_address);
176
177 /*
178 * If low_len is 0 (e.g. len = 256) the controller just goes to the IFI> prompt.
179 * Work around this by erasing blocks less than 256 bytes, so that low_len
180 * is always > 0 and high_len is always 0.
181 */
182 for (addr = start_address; erase_blocks > 0;
183 addr += PIC_MAX_ERASE_BLOCKS * PIC_ERASE_BLOCK_SIZE)
184 {
185 blocks = MIN(PIC_MAX_ERASE_BLOCKS, erase_blocks);
186 erase_blocks -= blocks;
187 debug_printf("\nErasing %u blocks at 0x%3x\n", blocks, addr);
188 snprintf(cmd,7,"%c%c%c%c%c%c",
189 PIC_ERASE_PROGRAM_MEM,
190 (int)blocks, /* low_len */
191 (int)(addr & 0xff),
192 (int)((addr >> 8) & 0xff),
193 (int)((addr >> 16) & 0xff),
194 (int)0); /* high_len */
195 debug_hex_dump(cmd,6);
196 pic_send_command(pic->fd,cmd,6,NULL,0,pic->response,CHAR_EOT);
197 }
198 debug_printf("\n*** pic_erase_program_mem() ***\n");
199 //getchar();
200 return RCT_OK;
201 }
202
203
pic_write_program_mem(rct_pic_t * pic,unsigned long address,unsigned int blocks,char * code)204 rct_status_t pic_write_program_mem(rct_pic_t *pic,unsigned long address,
205 unsigned int blocks,char *code)
206
207 {
208 char cmd[6];
209
210 if ( blocks > 255 )
211 {
212 fprintf(stderr,"pic_write_program_mem(): Tried to write %d blocks. Maximum is 255.\n",blocks);
213 return -1;
214 }
215 snprintf(cmd,5,"%c%c%c%c%c",
216 PIC_WRITE_PROGRAM_MEM,
217 blocks,
218 (int)(address & 0xff),
219 (int)((address >> 8) & 0xff),
220 (int)((address >> 16) & 0xff));
221 debug_printf("\n*** pic_write_program_mem() ***\n");
222 return pic_send_command(pic->fd,cmd,5,code,blocks,pic->response,CHAR_EOT);
223 }
224
225
pic_get_bootloader_version(rct_pic_t * pic)226 rct_status_t pic_get_bootloader_version(rct_pic_t *pic)
227
228 {
229 char cmd[2];
230 rct_status_t status;
231
232 cmd[0] = PIC_GET_BOOTLOADER_VERSION;
233 cmd[1] = 2;
234 debug_printf("\n*** pic_get_bootloader_version() ***\n");
235 status = pic_send_command(pic->fd,cmd,2,NULL,0,pic->response,CHAR_EOT);
236 if ( status == RCT_OK )
237 {
238 pic->bootloader_major = pic->response[4];
239 pic->bootloader_minor = pic->response[5];
240 }
241 return status;
242 }
243
244
pic_print_bootloader_version(rct_pic_t * pic)245 rct_status_t pic_print_bootloader_version(rct_pic_t *pic)
246
247 {
248 rct_status_t status;
249
250 if ( (status=pic_get_bootloader_version(pic)) == RCT_OK )
251 {
252 printf("Bootloader: %d.%d\n",pic->bootloader_major,
253 pic->bootloader_minor);
254 }
255 return RCT_OK;
256 }
257
258
pic_read_program_mem(rct_pic_t * pic,unsigned long address,unsigned int bytes)259 rct_status_t pic_read_program_mem(rct_pic_t *pic,unsigned long address,
260 unsigned int bytes)
261
262 {
263 char cmd[5];
264
265 snprintf(cmd,5,"%c%c%c%c%c",
266 PIC_READ_PROGRAM_MEM,
267 bytes,
268 (int)(address & 0xff),
269 (int)((address >> 8) & 0xff),
270 (int)((address >> 16) & 0xff));
271 debug_printf("\n*** pic_read_program_mem() ***\n");
272 return pic_send_command(pic->fd,cmd,5,NULL,0,pic->response,CHAR_EOT);
273 }
274
275
pic_return_to_user_code(rct_pic_t * pic)276 rct_status_t pic_return_to_user_code(rct_pic_t *pic)
277
278 {
279 char cmd[2];
280 rct_status_t status;
281
282 cmd[0] = PIC_RETURN_TO_USER_CODE;
283 cmd[1] = 0x40;
284 debug_printf("\n*** pic_return_to_user_code() ***\n");
285 status = pic_send_command(pic->fd,cmd,2,NULL,0,pic->response,'\x40');
286
287 /* Read extra trailing response bytes? */
288 /*
289 for (c=0; 1; ++c)
290 {
291 read(pic->fd, cmd, 1);
292 fprintf(stderr, "Extra byte %d: %c\n", c, cmd[0]);
293 }
294 */
295 return status;
296 }
297
298
hex_val(char * start,int digits)299 unsigned long hex_val(char *start,int digits)
300
301 {
302 unsigned long val;
303 char temp,
304 *end;
305
306 temp = start[digits];
307 start[digits] = '\0';
308 val = strtol(start,&end,16);
309 if ( end != start + digits )
310 {
311 fprintf(stderr,"Error converting hex value: %s\n",start);
312 val = -1;
313 }
314 start[digits] = temp;
315 return val;
316 }
317
318
pic_reset(int fd,char * response)319 rct_status_t pic_reset(int fd,char *response)
320
321 {
322 debug_printf("\n*** pic_reset() ***\n");
323 return pic_send_command(fd,"\x00\x00",2,NULL,0,response,CHAR_EOT);
324 }
325
326
327 #if defined(__CYGWIN__) || defined(sun)
cfmakeraw(struct termios * t)328 void cfmakeraw(struct termios *t)
329 {
330 t->c_iflag |= IGNBRK; /* Ignore break condition */
331 t->c_iflag &= ~(INLCR|ICRNL); /* Disable CR/NL mappings */
332 t->c_oflag &= ~OPOST; /* Disable all output processing */
333 t->c_cflag |= CREAD;
334 t->c_cflag &= ~CRTSCTS; /* Disable flow control */
335 }
336 #endif
337
338
pic_open_controller(rct_pic_t * pic,char * device)339 rct_status_t pic_open_controller(rct_pic_t *pic, char *device)
340
341 {
342 pic->fd = open(device, O_RDWR|O_NOCTTY);
343 if ( pic->fd == -1 )
344 {
345 fprintf(stderr, "Cannot open %s for read/write.\n", device);
346 return EX_UNAVAILABLE;
347 }
348
349 /* Get tty attributes */
350 if ( tcgetattr(pic->fd, &pic->original_port_settings) != 0 )
351 {
352 fprintf(stderr, "%s: tcgetattr() failed: %s\n",
353 __func__, strerror(errno));
354 exit(EX_OSERR);
355 }
356
357 /* Put tty into raw mode (no buffering or char translations
358 intended for terminals) */
359 pic->current_port_settings = pic->original_port_settings;
360 cfmakeraw(&pic->current_port_settings);
361
362 /*
363 * Vex uses 115200 bits/sec, no parity, 8 data bits, 1 stop bit
364 * Assume same for other PIC devices for now. Maybe add a tty
365 * settings argument to this function later if necessary.
366 */
367 cfsetispeed(&pic->current_port_settings, PIC_BAUD_RATE);
368 cfsetospeed(&pic->current_port_settings, PIC_BAUD_RATE);
369 pic->current_port_settings.c_cflag |= CS8|CLOCAL|CREAD;
370 pic->current_port_settings.c_cflag &= ~CRTSCTS;
371 //pic->current_port_settings.c_cflag &= ~(CCTS_OFLOW|CRTSCTS|CRTS_IFLOW|HUPCL);
372 pic->current_port_settings.c_iflag |= IGNPAR;
373 if ( tcsetattr(pic->fd, TCSANOW, &pic->current_port_settings) != 0 )
374 {
375 fprintf(stderr, "%s: tcsetattr() failed: %s\n",
376 __func__, strerror(errno));
377 exit(EX_OSERR);
378 }
379
380 return RCT_OK;
381 }
382
383
pic_close_controller(rct_pic_t * pic)384 rct_status_t pic_close_controller(rct_pic_t *pic)
385
386 {
387 /* Restore original port settings. */
388 /* TCSAFLUSH makes the change after output buffers are flushed and
389 discards unread input. */
390 if ( tcsetattr(pic->fd,TCSAFLUSH,&pic->original_port_settings) != 0 )
391 {
392 fprintf(stderr, "%s: tcsetattr() failed: %s\n",
393 __func__, strerror(errno));
394 exit(EX_OSERR);
395 }
396
397 close(pic->fd);
398 return RCT_OK;
399 }
400
401
pic_init_struct(rct_pic_t * pic)402 void pic_init_struct(rct_pic_t *pic)
403
404 {
405 pic->fd = -1;
406 pic->device = NULL;
407 }
408
409
pic_valid_program_range(unsigned long start_address,unsigned long end_address,unsigned long valid_start,unsigned long valid_end,const char * caller)410 int pic_valid_program_range(unsigned long start_address,
411 unsigned long end_address,
412 unsigned long valid_start,
413 unsigned long valid_end,
414 const char *caller)
415
416 {
417 if ( end_address < start_address )
418 {
419 fprintf(stderr,"%s(): end_address %06lX"
420 "is less than start_address: %06lX.\n",
421 caller,end_address,start_address);
422 return -1;
423 }
424 if ( (start_address >= valid_start) && (end_address <= valid_end) )
425 {
426 return 1;
427 }
428 else
429 {
430 fprintf(stderr,"%s(): Valid program address is %04lx to %04lx.\n",
431 caller,valid_start,valid_end);
432 fprintf(stderr,"Start and end addresses received are %04lx to %04lx.\n",
433 start_address,end_address);
434 return 0;
435 }
436 }
437
438