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