Commit | Line | Data |
---|---|---|
c906108c SS |
1 | /* This file is part of GDB. |
2 | ||
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation; either version 2 of the License, or | |
6 | (at your option) any later version. | |
7 | ||
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | ||
13 | You should have received a copy of the GNU General Public License | |
14 | along with this program; if not, write to the Free Software | |
15 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
16 | ||
17 | /* This started out life as code shared between the nindy monitor and | |
18 | GDB. For various reasons, this is no longer true. Eventually, it | |
19 | probably should be merged into remote-nindy.c. */ | |
20 | ||
21 | /****************************************************************************** | |
22 | * | |
23 | * NINDY INTERFACE ROUTINES | |
24 | * | |
25 | * The caller of these routines should be aware that: | |
26 | * | |
27 | * (1) ninConnect() should be called to open communications with the | |
28 | * remote NINDY board before any of the other routines are invoked. | |
29 | * | |
30 | * (2) almost all interactions are driven by the host: nindy sends information | |
31 | * in response to host commands. | |
32 | * | |
33 | * (3) the lone exception to (2) is the single character DLE (^P, 0x10). | |
34 | * Receipt of a DLE from NINDY indicates that the application program | |
35 | * running under NINDY has stopped execution and that NINDY is now | |
36 | * available to talk to the host (all other communication received after | |
37 | * the application has been started should be presumed to come from the | |
38 | * application and should be passed on by the host to stdout). | |
39 | * | |
40 | * (4) the reason the application program stopped can be determined with the | |
41 | * ninStopWhy() function. There are three classes of stop reasons: | |
42 | * | |
43 | * (a) the application has terminated execution. | |
44 | * The host should take appropriate action. | |
45 | * | |
46 | * (b) the application had a fault or trace event. | |
47 | * The host should take appropriate action. | |
48 | * | |
49 | * (c) the application wishes to make a service request (srq) of the host; | |
50 | * e.g., to open/close a file, read/write a file, etc. The ninSrq() | |
51 | * function should be called to determine the nature of the request | |
52 | * and process it. | |
53 | */ | |
54 | ||
55 | #include <stdio.h> | |
56 | #include "defs.h" | |
57 | #include "serial.h" | |
58 | #ifdef ANSI_PROTOTYPES | |
59 | #include <stdarg.h> | |
60 | #else | |
61 | #include <varargs.h> | |
62 | #endif | |
63 | ||
64 | #if !defined (HAVE_TERMIOS) && !defined (HAVE_TERMIO) && !defined (HAVE_SGTTY) | |
65 | #define HAVE_SGTTY | |
66 | #endif | |
67 | ||
68 | #ifdef HAVE_SGTTY | |
69 | #include <sys/ioctl.h> | |
70 | #endif | |
71 | ||
72 | #include <sys/types.h> /* Needed by file.h on Sys V */ | |
73 | #include <sys/file.h> | |
74 | #include <signal.h> | |
75 | #include <sys/stat.h> | |
76 | ||
77 | #if 0 | |
78 | #include "ttycntl.h" | |
79 | #endif | |
80 | #include "block_io.h" | |
81 | #include "wait.h" | |
82 | #include "env.h" | |
83 | ||
84 | #define DLE 0x10 /* ^P */ | |
85 | #define XON 0x11 /* ^Q */ | |
86 | #define XOFF 0x13 /* ^S */ | |
87 | #define ESC 0x1b | |
88 | ||
89 | #define TIMEOUT -1 | |
90 | ||
91 | int quiet = 0; /* 1 => stifle unnecessary messages */ | |
92 | serial_t nindy_serial; | |
93 | ||
94 | static int old_nindy = 0; /* 1 => use old (hex) communication protocol */ | |
95 | static ninStrGet(); | |
96 | \f | |
97 | /**************************** | |
98 | * * | |
99 | * MISCELLANEOUS UTILTIES * | |
100 | * * | |
101 | ****************************/ | |
102 | ||
103 | /****************************************************************************** | |
104 | * say: | |
105 | * This is a printf that takes at most two arguments (in addition to the | |
106 | * format string) and that outputs nothing if verbose output has been | |
107 | * suppressed. | |
108 | *****************************************************************************/ | |
109 | ||
110 | /* VARARGS */ | |
111 | static void | |
112 | #ifdef ANSI_PROTOTYPES | |
113 | say (char *fmt, ...) | |
114 | #else | |
115 | say (va_alist) | |
116 | va_dcl | |
117 | #endif | |
118 | { | |
119 | va_list args; | |
120 | #ifdef ANSI_PROTOTYPES | |
121 | va_start(args, fmt); | |
122 | #else | |
123 | char *fmt; | |
124 | ||
125 | va_start (args); | |
126 | fmt = va_arg (args, char *); | |
127 | #endif | |
128 | ||
129 | if (!quiet) | |
130 | { | |
131 | vfprintf_unfiltered (gdb_stdout, fmt, args); | |
132 | gdb_flush (gdb_stdout); | |
133 | } | |
134 | va_end (args); | |
135 | } | |
136 | ||
137 | /****************************************************************************** | |
138 | * exists: | |
139 | * Creates a full pathname by concatenating up to three name components | |
140 | * onto a specified base name; optionally looks up the base name as a | |
141 | * runtime environment variable; and checks to see if the file or | |
142 | * directory specified by the pathname actually exists. | |
143 | * | |
144 | * Returns: the full pathname if it exists, NULL otherwise. | |
145 | * (returned pathname is in malloc'd memory and must be freed | |
146 | * by caller). | |
147 | *****************************************************************************/ | |
148 | static char * | |
149 | exists( base, c1, c2, c3, env ) | |
150 | char *base; /* Base directory of path */ | |
151 | char *c1, *c2, *c3; /* Components (subdirectories and/or file name) to be | |
152 | * appended onto the base directory name. One or | |
153 | * more may be omitted by passing NULL pointers. | |
154 | */ | |
155 | int env; /* If 1, '*base' is the name of an environment variable | |
156 | * to be examined for the base directory name; | |
157 | * otherwise, '*base' is the actual name of the | |
158 | * base directory. | |
159 | */ | |
160 | { | |
161 | struct stat buf;/* For call to 'stat' -- never examined */ | |
162 | char *path; /* Pointer to full pathname (malloc'd memory) */ | |
163 | int len; /* Length of full pathname (incl. terminator) */ | |
164 | extern char *getenv(); | |
165 | ||
166 | ||
167 | if ( env ){ | |
168 | base = getenv( base ); | |
169 | if ( base == NULL ){ | |
170 | return NULL; | |
171 | } | |
172 | } | |
173 | ||
174 | len = strlen(base) + 4; | |
175 | /* +4 for terminator and "/" before each component */ | |
176 | if ( c1 != NULL ){ | |
177 | len += strlen(c1); | |
178 | } | |
179 | if ( c2 != NULL ){ | |
180 | len += strlen(c2); | |
181 | } | |
182 | if ( c3 != NULL ){ | |
183 | len += strlen(c3); | |
184 | } | |
185 | ||
186 | path = xmalloc (len); | |
187 | ||
188 | strcpy( path, base ); | |
189 | if ( c1 != NULL ){ | |
190 | strcat( path, "/" ); | |
191 | strcat( path, c1 ); | |
192 | if ( c2 != NULL ){ | |
193 | strcat( path, "/" ); | |
194 | strcat( path, c2 ); | |
195 | if ( c3 != NULL ){ | |
196 | strcat( path, "/" ); | |
197 | strcat( path, c3 ); | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | if ( stat(path,&buf) != 0 ){ | |
203 | free( path ); | |
204 | path = NULL; | |
205 | } | |
206 | return path; | |
207 | } | |
208 | \f | |
209 | /***************************** | |
210 | * * | |
211 | * LOW-LEVEL COMMUNICATION * | |
212 | * * | |
213 | *****************************/ | |
214 | ||
215 | /* Read *exactly* N characters from the NINDY tty, and put them in | |
216 | *BUF. Translate escape sequences into single characters, counting | |
217 | each such sequence as 1 character. | |
218 | ||
219 | An escape sequence consists of ESC and a following character. The | |
220 | ESC is discarded and the other character gets bit 0x40 cleared -- | |
221 | thus ESC P == ^P, ESC S == ^S, ESC [ == ESC, etc. | |
222 | ||
223 | Return 1 if successful, 0 if more than TIMEOUT seconds pass without | |
224 | any input. */ | |
225 | ||
226 | static int | |
227 | rdnin (buf,n,timeout) | |
228 | unsigned char * buf; /* Where to place characters read */ | |
229 | int n; /* Number of characters to read */ | |
230 | int timeout; /* Timeout, in seconds */ | |
231 | { | |
232 | int escape_seen; /* 1 => last character of a read was an ESC */ | |
233 | int c; | |
234 | ||
235 | escape_seen = 0; | |
236 | while (n) | |
237 | { | |
238 | c = SERIAL_READCHAR (nindy_serial, timeout); | |
239 | switch (c) | |
240 | { | |
241 | case SERIAL_ERROR: | |
242 | case SERIAL_TIMEOUT: | |
243 | case SERIAL_EOF: | |
244 | return 0; | |
245 | ||
246 | case ESC: | |
247 | escape_seen = 1; | |
248 | break; | |
249 | ||
250 | default: | |
251 | if (escape_seen) | |
252 | { | |
253 | escape_seen = 0; | |
254 | c &= ~0x40; | |
255 | } | |
256 | *buf++ = c; | |
257 | --n; | |
258 | break; | |
259 | } | |
260 | } | |
261 | return 1; | |
262 | } | |
263 | ||
264 | ||
265 | /****************************************************************************** | |
266 | * getpkt: | |
267 | * Read a packet from a remote NINDY, with error checking, into the | |
268 | * indicated buffer. | |
269 | * | |
270 | * Return packet status byte on success, TIMEOUT on failure. | |
271 | ******************************************************************************/ | |
272 | static | |
273 | int | |
274 | getpkt(buf) | |
275 | unsigned char *buf; | |
276 | { | |
277 | int i; | |
278 | unsigned char hdr[3]; /* Packet header: | |
279 | * hdr[0] = low byte of message length | |
280 | * hdr[1] = high byte of message length | |
281 | * hdr[2] = message status | |
282 | */ | |
283 | int cnt; /* Message length (status byte + data) */ | |
284 | unsigned char cs_calc; /* Checksum calculated */ | |
285 | unsigned char cs_recv; /* Checksum received */ | |
286 | static char errfmt[] = | |
287 | "Bad checksum (recv=0x%02x; calc=0x%02x); retrying\r\n"; | |
288 | ||
289 | while (1){ | |
290 | if ( !rdnin(hdr,3,5) ){ | |
291 | return TIMEOUT; | |
292 | } | |
293 | cnt = (hdr[1]<<8) + hdr[0] - 1; | |
294 | /* -1 for status byte (already read) */ | |
295 | ||
296 | /* Caller's buffer may only be big enough for message body, | |
297 | * without status byte and checksum, so make sure to read | |
298 | * checksum into a separate buffer. | |
299 | */ | |
300 | if ( !rdnin(buf,cnt,5) || !rdnin(&cs_recv,1,5) ){ | |
301 | return TIMEOUT; | |
302 | } | |
303 | ||
304 | /* Calculate checksum | |
305 | */ | |
306 | cs_calc = hdr[0] + hdr[1] + hdr[2]; | |
307 | for ( i = 0; i < cnt; i++ ){ | |
308 | cs_calc += buf[i]; | |
309 | } | |
310 | if ( cs_calc == cs_recv ){ | |
311 | SERIAL_WRITE (nindy_serial, "+", 1); | |
312 | return hdr[2]; | |
313 | } | |
314 | ||
315 | /* Bad checksum: report, send NAK, and re-receive | |
316 | */ | |
317 | fprintf(stderr, errfmt, cs_recv, cs_calc ); | |
318 | SERIAL_WRITE (nindy_serial, "-", 1); | |
319 | } | |
320 | } | |
321 | ||
322 | ||
323 | /****************************************************************************** | |
324 | * putpkt: | |
325 | * Send a packet to NINDY, checksumming it and converting special | |
326 | * characters to escape sequences. | |
327 | ******************************************************************************/ | |
328 | ||
329 | /* This macro puts the character 'c' into the buffer pointed at by 'p', | |
330 | * and increments the pointer. If 'c' is one of the 4 special characters | |
331 | * in the transmission protocol, it is converted into a 2-character | |
332 | * escape sequence. | |
333 | */ | |
334 | #define PUTBUF(c,p) \ | |
335 | if ( c == DLE || c == ESC || c == XON || c == XOFF ){ \ | |
336 | *p++ = ESC; \ | |
337 | *p++ = c | 0x40; \ | |
338 | } else { \ | |
339 | *p++ = c; \ | |
340 | } | |
341 | ||
342 | static | |
343 | putpkt( msg, len ) | |
344 | unsigned char *msg; /* Command to be sent, without lead ^P (\020) or checksum */ | |
345 | int len; /* Number of bytes in message */ | |
346 | { | |
347 | static char *buf = NULL;/* Local buffer -- build packet here */ | |
348 | static int maxbuf = 0; /* Current length of buffer */ | |
349 | unsigned char ack; /* Response received from NINDY */ | |
350 | unsigned char checksum; /* Packet checksum */ | |
351 | char *p; /* Pointer into buffer */ | |
352 | int lenhi, lenlo; /* High and low bytes of message length */ | |
353 | int i; | |
354 | ||
355 | ||
356 | /* Make sure local buffer is big enough. Must include space for | |
357 | * packet length, message body, and checksum. And in the worst | |
358 | * case, each character would expand into a 2-character escape | |
359 | * sequence. | |
360 | */ | |
361 | if ( maxbuf < ((2*len)+10) ){ | |
362 | if ( buf ){ | |
363 | free( buf ); | |
364 | } | |
365 | buf = xmalloc( maxbuf=((2*len)+10) ); | |
366 | } | |
367 | ||
368 | /* Attention, NINDY! | |
369 | */ | |
370 | SERIAL_WRITE (nindy_serial, "\020", 1); | |
371 | ||
372 | ||
373 | lenlo = len & 0xff; | |
374 | lenhi = (len>>8) & 0xff; | |
375 | checksum = lenlo + lenhi; | |
376 | p = buf; | |
377 | ||
378 | PUTBUF( lenlo, p ); | |
379 | PUTBUF( lenhi, p ); | |
380 | ||
381 | for ( i=0; i<len; i++ ){ | |
382 | PUTBUF( msg[i], p ); | |
383 | checksum += msg[i]; | |
384 | } | |
385 | ||
386 | PUTBUF( checksum, p ); | |
387 | ||
388 | /* Send checksummed message over and over until we get a positive ack | |
389 | */ | |
390 | SERIAL_WRITE (nindy_serial, buf, p - buf); | |
391 | while (1){ | |
392 | if ( !rdnin(&ack,1,5) ){ | |
393 | /* timed out */ | |
394 | fprintf(stderr,"ACK timed out; resending\r\n"); | |
395 | /* Attention, NINDY! */ | |
396 | SERIAL_WRITE (nindy_serial, "\020", 1); | |
397 | SERIAL_WRITE (nindy_serial, buf, p - buf); | |
398 | } else if ( ack == '+' ){ | |
399 | return; | |
400 | } else if ( ack == '-' ){ | |
401 | fprintf( stderr, "Remote NAK; resending\r\n" ); | |
402 | SERIAL_WRITE (nindy_serial, buf, p - buf); | |
403 | } else { | |
404 | fprintf( stderr, "Bad ACK, ignored: <%c>\r\n", ack ); | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | ||
410 | ||
411 | /****************************************************************************** | |
412 | * send: | |
413 | * Send a message to a remote NINDY. Check message status byte | |
414 | * for error responses. If no error, return NINDY reponse (if any). | |
415 | ******************************************************************************/ | |
416 | static | |
417 | send( out, len, in ) | |
418 | unsigned char *out; /* Message to be sent to NINDY */ | |
419 | int len; /* Number of meaningful bytes in out buffer */ | |
420 | unsigned char *in; /* Where to put response received from NINDY */ | |
421 | { | |
422 | char *fmt; | |
423 | int status; | |
424 | static char *errmsg[] = { | |
425 | "", /* 0 */ | |
426 | "Buffer overflow", /* 1 */ | |
427 | "Unknown command", /* 2 */ | |
428 | "Wrong amount of data to load register(s)", /* 3 */ | |
429 | "Missing command argument(s)", /* 4 */ | |
430 | "Odd number of digits sent to load memory", /* 5 */ | |
431 | "Unknown register name", /* 6 */ | |
432 | "No such memory segment", /* 7 */ | |
433 | "No breakpoint available", /* 8 */ | |
434 | "Can't set requested baud rate", /* 9 */ | |
435 | }; | |
436 | # define NUMERRS ( sizeof(errmsg) / sizeof(errmsg[0]) ) | |
437 | ||
438 | static char err1[] = "Unknown error response from NINDY: #%d\r\n"; | |
439 | static char err2[] = "Error response #%d from NINDY: %s\r\n"; | |
440 | ||
441 | while (1){ | |
442 | putpkt(out,len); | |
443 | status = getpkt(in); | |
444 | if ( status == TIMEOUT ){ | |
445 | fprintf( stderr, "Response timed out; resending\r\n" ); | |
446 | } else { | |
447 | break; | |
448 | } | |
449 | } | |
450 | ||
451 | if ( status ){ | |
452 | fmt = status > NUMERRS ? err1 : err2; | |
453 | fprintf( stderr, fmt, status, errmsg[status] ); | |
454 | abort(); | |
455 | } | |
456 | } | |
457 | \f | |
458 | /************************ | |
459 | * * | |
460 | * BAUD RATE ROUTINES * | |
461 | * * | |
462 | ************************/ | |
463 | ||
464 | /* Table of baudrates known to be acceptable to NINDY. Each baud rate | |
465 | * appears both as character string and as a Unix baud rate constant. | |
466 | */ | |
467 | struct baudrate { | |
468 | char *string; | |
469 | int rate; | |
470 | }; | |
471 | ||
472 | static struct baudrate baudtab[] = { | |
473 | "1200", 1200, | |
474 | "2400", 2400, | |
475 | "4800", 4800, | |
476 | "9600", 9600, | |
477 | "19200", 19200, | |
478 | "38400", 38400, | |
479 | NULL, 0 /* End of table */ | |
480 | }; | |
481 | ||
482 | /****************************************************************************** | |
483 | * parse_baudrate: | |
484 | * Look up the passed baud rate in the baudrate table. If found, change | |
485 | * our internal record of the current baud rate, but don't do anything | |
486 | * about the tty just now. | |
487 | * | |
488 | * Return pointer to baudrate structure on success, NULL on failure. | |
489 | ******************************************************************************/ | |
490 | static | |
491 | struct baudrate * | |
492 | parse_baudrate(s) | |
493 | char *s; /* Desired baud rate, as an ASCII (decimal) string */ | |
494 | { | |
495 | int i; | |
496 | ||
497 | for ( i=0; baudtab[i].string != NULL; i++ ){ | |
498 | if ( !strcmp(baudtab[i].string,s) ){ | |
499 | return &baudtab[i]; | |
500 | } | |
501 | } | |
502 | return NULL; | |
503 | } | |
504 | ||
505 | /****************************************************************************** | |
506 | * try_baudrate: | |
507 | * Try speaking to NINDY via the specified file descriptor at the | |
508 | * specified baudrate. Assume success if we can send an empty command | |
509 | * with a bogus checksum and receive a NAK (response of '-') back within | |
510 | * one second. | |
511 | * | |
512 | * Return 1 on success, 0 on failure. | |
513 | ***************************************************************************/ | |
514 | ||
515 | static int | |
516 | try_baudrate (serial, brp) | |
517 | serial_t serial; | |
518 | struct baudrate *brp; | |
519 | { | |
520 | unsigned char c; | |
521 | ||
522 | /* Set specified baud rate and flush all pending input */ | |
523 | SERIAL_SETBAUDRATE (serial, brp->rate); | |
524 | tty_flush (serial); | |
525 | ||
526 | /* Send empty command with bad checksum, hope for NAK ('-') response */ | |
527 | SERIAL_WRITE (serial, "\020\0\0\001", 4); | |
528 | ||
529 | /* Anything but a quick '-', including error, eof, or timeout, means that | |
530 | this baudrate doesn't work. */ | |
531 | return SERIAL_READCHAR (serial, 1) == '-'; | |
532 | } | |
533 | ||
534 | /****************************************************************************** | |
535 | * autobaud: | |
536 | * Get NINDY talking over the specified file descriptor at the specified | |
537 | * baud rate. First see if NINDY's already talking at 'baudrate'. If | |
538 | * not, run through all the legal baudrates in 'baudtab' until one works, | |
539 | * and then tell NINDY to talk at 'baudrate' instead. | |
540 | ******************************************************************************/ | |
541 | static | |
542 | autobaud( serial, brp ) | |
543 | serial_t serial; | |
544 | struct baudrate *brp; | |
545 | { | |
546 | int i; | |
547 | int failures; | |
548 | ||
549 | say("NINDY at wrong baud rate? Trying to autobaud...\n"); | |
550 | failures = i = 0; | |
551 | while (1) | |
552 | { | |
553 | say( "\r%s... ", baudtab[i].string ); | |
554 | if (try_baudrate(serial, &baudtab[i])) | |
555 | { | |
556 | break; | |
557 | } | |
558 | if (baudtab[++i].string == NULL) | |
559 | { | |
560 | /* End of table -- wraparound */ | |
561 | i = 0; | |
562 | if ( failures++ ) | |
563 | { | |
564 | say("\nAutobaud failed again. Giving up.\n"); | |
565 | exit(1); | |
566 | } | |
567 | else | |
568 | { | |
569 | say("\nAutobaud failed. Trying again...\n"); | |
570 | } | |
571 | } | |
572 | } | |
573 | ||
574 | /* Found NINDY's current baud rate; now change it. */ | |
575 | say("Changing NINDY baudrate to %s\n", brp->string); | |
576 | ninBaud (brp->string); | |
577 | ||
578 | /* Change our baud rate back to rate to which we just set NINDY. */ | |
579 | SERIAL_SETBAUDRATE (serial, brp->rate); | |
580 | } | |
581 | \f | |
582 | /********************************** | |
583 | * * | |
584 | * NINDY INTERFACE ROUTINES * | |
585 | * * | |
586 | * ninConnect *MUST* be the first * | |
587 | * one of these routines called. * | |
588 | **********************************/ | |
589 | ||
590 | ||
591 | /****************************************************************************** | |
592 | * ninBaud: | |
593 | * Ask NINDY to change the baud rate on its serial port. | |
594 | * Assumes we know the baud rate at which NINDY's currently talking. | |
595 | ******************************************************************************/ | |
596 | ninBaud( baudrate ) | |
597 | char *baudrate; /* Desired baud rate, as a string of ASCII decimal | |
598 | * digits. | |
599 | */ | |
600 | { | |
601 | unsigned char msg[100]; | |
602 | ||
603 | tty_flush (nindy_serial); | |
604 | ||
605 | if (old_nindy) | |
606 | { | |
607 | char *p; /* Pointer into buffer */ | |
608 | unsigned char csum; /* Calculated checksum */ | |
609 | ||
610 | /* Can't use putpkt() because after the baudrate change NINDY's | |
611 | ack/nak will look like gibberish. */ | |
612 | ||
613 | for (p=baudrate, csum=020+'z'; *p; p++) | |
614 | { | |
615 | csum += *p; | |
616 | } | |
617 | sprintf (msg, "\020z%s#%02x", baudrate, csum); | |
618 | SERIAL_WRITE (nindy_serial, msg, strlen (msg)); | |
619 | } | |
620 | else | |
621 | { | |
622 | /* Can't use "send" because NINDY reply will be unreadable after | |
623 | baud rate change. */ | |
624 | sprintf( msg, "z%s", baudrate ); | |
625 | putpkt( msg, strlen(msg)+1 ); /* "+1" to send terminator too */ | |
626 | } | |
627 | } | |
628 | ||
629 | /****************************************************************************** | |
630 | * ninBptDel: | |
631 | * Ask NINDY to delete the specified type of *hardware* breakpoint at | |
632 | * the specified address. If the 'addr' is -1, all breakpoints of | |
633 | * the specified type are deleted. | |
634 | ***************************************************************************/ | |
635 | ninBptDel( addr, type ) | |
636 | long addr; /* Address in 960 memory */ | |
637 | char type; /* 'd' => data bkpt, 'i' => instruction breakpoint */ | |
638 | { | |
639 | unsigned char buf[10]; | |
640 | ||
641 | if ( old_nindy ){ | |
642 | OninBptDel( addr, type == 'd' ? 1 : 0 ); | |
643 | return; | |
644 | } | |
645 | ||
646 | buf[0] = 'b'; | |
647 | buf[1] = type; | |
648 | ||
649 | if ( addr == -1 ){ | |
650 | send( buf, 2, NULL ); | |
651 | } else { | |
652 | store_unsigned_integer (&buf[2], 4, addr); | |
653 | send( buf, 6, NULL ); | |
654 | } | |
655 | } | |
656 | ||
657 | ||
658 | /****************************************************************************** | |
659 | * ninBptSet: | |
660 | * Ask NINDY to set the specified type of *hardware* breakpoint at | |
661 | * the specified address. | |
662 | ******************************************************************************/ | |
663 | ninBptSet( addr, type ) | |
664 | long addr; /* Address in 960 memory */ | |
665 | char type; /* 'd' => data bkpt, 'i' => instruction breakpoint */ | |
666 | { | |
667 | unsigned char buf[10]; | |
668 | ||
669 | if ( old_nindy ){ | |
670 | OninBptSet( addr, type == 'd' ? 1 : 0 ); | |
671 | return; | |
672 | } | |
673 | ||
674 | ||
675 | buf[0] = 'B'; | |
676 | buf[1] = type; | |
677 | store_unsigned_integer (&buf[2], 4, addr); | |
678 | send( buf, 6, NULL ); | |
679 | } | |
680 | ||
681 | ||
682 | /****************************************************************************** | |
683 | * ninConnect: | |
684 | * Open the specified tty. Get communications working at the specified | |
685 | * baud rate. Flush any pending I/O on the tty. | |
686 | * | |
687 | * Return the file descriptor, or -1 on failure. | |
688 | ******************************************************************************/ | |
689 | int | |
690 | ninConnect( name, baudrate, brk, silent, old_protocol ) | |
691 | char *name; /* "/dev/ttyXX" to be opened */ | |
692 | char *baudrate;/* baud rate: a string of ascii decimal digits (eg,"9600")*/ | |
693 | int brk; /* 1 => send break to tty first thing after opening it*/ | |
694 | int silent; /* 1 => stifle unnecessary messages when talking to | |
695 | * this tty. | |
696 | */ | |
697 | int old_protocol; | |
698 | { | |
699 | int i; | |
700 | char *p; | |
701 | struct baudrate *brp; | |
702 | ||
703 | /* We will try each of the following paths when trying to open the tty | |
704 | */ | |
705 | static char *prefix[] = { "", "/dev/", "/dev/tty", NULL }; | |
706 | ||
707 | if ( old_protocol ){ | |
708 | old_nindy = 1; | |
709 | } | |
710 | ||
711 | quiet = silent; /* Make global to this file */ | |
712 | ||
713 | for ( i=0; prefix[i] != NULL; i++ ){ | |
714 | p = xmalloc(strlen(prefix[i]) + strlen(name) + 1 ); | |
715 | strcpy( p, prefix[i] ); | |
716 | strcat( p, name ); | |
717 | nindy_serial = SERIAL_OPEN (p); | |
718 | if (nindy_serial != NULL) { | |
719 | #ifdef TIOCEXCL | |
720 | /* Exclusive use mode (hp9000 does not support it) */ | |
721 | ioctl(nindy_serial->fd,TIOCEXCL,NULL); | |
722 | #endif | |
723 | SERIAL_RAW (nindy_serial); | |
724 | ||
725 | if (brk) | |
726 | { | |
727 | SERIAL_SEND_BREAK (nindy_serial); | |
728 | } | |
729 | ||
730 | brp = parse_baudrate( baudrate ); | |
731 | if ( brp == NULL ){ | |
732 | say("Illegal baudrate %s ignored; using 9600\n", | |
733 | baudrate); | |
734 | brp = parse_baudrate( "9600" ); | |
735 | } | |
736 | ||
737 | if ( !try_baudrate(nindy_serial, brp) ){ | |
738 | autobaud(nindy_serial, brp); | |
739 | } | |
740 | tty_flush (nindy_serial); | |
741 | say( "Connected to %s\n", p ); | |
742 | free(p); | |
743 | break; | |
744 | } | |
745 | free(p); | |
746 | } | |
747 | return 0; | |
748 | } | |
749 | ||
750 | #if 0 | |
751 | ||
752 | /* Currently unused; shouldn't we be doing this on target_kill and | |
753 | perhaps target_mourn? FIXME. */ | |
754 | ||
755 | /****************************************************************************** | |
756 | * ninGdbExit: | |
757 | * Ask NINDY to leave GDB mode and print a NINDY prompt. | |
758 | ****************************************************************************/ | |
759 | ninGdbExit() | |
760 | { | |
761 | if ( old_nindy ){ | |
762 | OninGdbExit(); | |
763 | return; | |
764 | } | |
765 | putpkt((unsigned char *) "E", 1 ); | |
766 | } | |
767 | #endif | |
768 | ||
769 | /****************************************************************************** | |
770 | * ninGo: | |
771 | * Ask NINDY to start or continue execution of an application program | |
772 | * in it's memory at the current ip. | |
773 | ******************************************************************************/ | |
774 | ninGo( step_flag ) | |
775 | int step_flag; /* 1 => run in single-step mode */ | |
776 | { | |
777 | if ( old_nindy ){ | |
778 | OninGo( step_flag ); | |
779 | return; | |
780 | } | |
781 | putpkt((unsigned char *) (step_flag ? "s" : "c"), 1 ); | |
782 | } | |
783 | ||
784 | ||
785 | /****************************************************************************** | |
786 | * ninMemGet: | |
787 | * Read a string of bytes from NINDY's address space (960 memory). | |
788 | ******************************************************************************/ | |
789 | int | |
790 | ninMemGet(ninaddr, hostaddr, len) | |
791 | long ninaddr; /* Source address, in the 960 memory space */ | |
792 | unsigned char *hostaddr; /* Destination address, in our memory space */ | |
793 | int len; /* Number of bytes to read */ | |
794 | { | |
795 | unsigned char buf[BUFSIZE+20]; | |
796 | int cnt; /* Number of bytes in next transfer */ | |
797 | int origlen = len; | |
798 | ||
799 | if ( old_nindy ){ | |
800 | OninMemGet(ninaddr, hostaddr, len); | |
801 | return; | |
802 | } | |
803 | ||
804 | for ( ; len > 0; len -= BUFSIZE ){ | |
805 | cnt = len > BUFSIZE ? BUFSIZE : len; | |
806 | ||
807 | buf[0] = 'm'; | |
808 | store_unsigned_integer (&buf[1], 4, ninaddr); | |
809 | buf[5] = cnt & 0xff; | |
810 | buf[6] = (cnt>>8) & 0xff; | |
811 | ||
812 | send( buf, 7, hostaddr ); | |
813 | ||
814 | ninaddr += cnt; | |
815 | hostaddr += cnt; | |
816 | } | |
817 | return origlen; | |
818 | } | |
819 | ||
820 | ||
821 | /****************************************************************************** | |
822 | * ninMemPut: | |
823 | * Write a string of bytes into NINDY's address space (960 memory). | |
824 | ******************************************************************************/ | |
825 | int | |
826 | ninMemPut( ninaddr, hostaddr, len ) | |
827 | long ninaddr; /* Destination address, in NINDY memory space */ | |
828 | unsigned char *hostaddr; /* Source address, in our memory space */ | |
829 | int len; /* Number of bytes to write */ | |
830 | { | |
831 | unsigned char buf[BUFSIZE+20]; | |
832 | int cnt; /* Number of bytes in next transfer */ | |
833 | int origlen = len; | |
834 | ||
835 | if ( old_nindy ){ | |
836 | OninMemPut( ninaddr, hostaddr, len ); | |
837 | return; | |
838 | } | |
839 | for ( ; len > 0; len -= BUFSIZE ){ | |
840 | cnt = len > BUFSIZE ? BUFSIZE : len; | |
841 | ||
842 | buf[0] = 'M'; | |
843 | store_unsigned_integer (&buf[1], 4, ninaddr); | |
844 | memcpy(buf + 5, hostaddr, cnt); | |
845 | send( buf, cnt+5, NULL ); | |
846 | ||
847 | ninaddr += cnt; | |
848 | hostaddr += cnt; | |
849 | } | |
850 | return origlen; | |
851 | } | |
852 | ||
853 | /****************************************************************************** | |
854 | * ninRegGet: | |
855 | * Retrieve the contents of a 960 register, and return them as a long | |
856 | * in host byte order. | |
857 | * | |
858 | * THIS ROUTINE CAN ONLY BE USED TO READ THE LOCAL, GLOBAL, AND | |
859 | * ip/ac/pc/tc REGISTERS. | |
860 | * | |
861 | ******************************************************************************/ | |
862 | long | |
863 | ninRegGet( regname ) | |
864 | char *regname; /* Register name recognized by NINDY, subject to the | |
865 | * above limitations. | |
866 | */ | |
867 | { | |
868 | unsigned char outbuf[10]; | |
869 | unsigned char inbuf[20]; | |
870 | ||
871 | if ( old_nindy ){ | |
872 | return OninRegGet( regname ); | |
873 | } | |
874 | ||
875 | sprintf( outbuf, "u%s:", regname ); | |
876 | send( outbuf, strlen(outbuf), inbuf ); | |
877 | return extract_unsigned_integer (inbuf, 4); | |
878 | } | |
879 | ||
880 | /****************************************************************************** | |
881 | * ninRegPut: | |
882 | * Set the contents of a 960 register. | |
883 | * | |
884 | * THIS ROUTINE CAN ONLY BE USED TO SET THE LOCAL, GLOBAL, AND | |
885 | * ip/ac/pc/tc REGISTERS. | |
886 | * | |
887 | ******************************************************************************/ | |
888 | ninRegPut( regname, val ) | |
889 | char *regname; /* Register name recognized by NINDY, subject to the | |
890 | * above limitations. | |
891 | */ | |
892 | long val; /* New contents of register, in host byte-order */ | |
893 | { | |
894 | unsigned char buf[20]; | |
895 | int len; | |
896 | ||
897 | if ( old_nindy ){ | |
898 | OninRegPut( regname, val ); | |
899 | return; | |
900 | } | |
901 | ||
902 | sprintf( buf, "U%s:", regname ); | |
903 | len = strlen(buf); | |
904 | store_unsigned_integer (&buf[len], 4, val); | |
905 | send( buf, len+4, NULL ); | |
906 | } | |
907 | ||
908 | /****************************************************************************** | |
909 | * ninRegsGet: | |
910 | * Get a dump of the contents of the entire 960 register set. The | |
911 | * individual registers appear in the dump in the following order: | |
912 | * | |
913 | * pfp sp rip r3 r4 r5 r6 r7 | |
914 | * r8 r9 r10 r11 r12 r13 r14 r15 | |
915 | * g0 g1 g2 g3 g4 g5 g6 g7 | |
916 | * g8 g9 g10 g11 g12 g13 g14 fp | |
917 | * pc ac ip tc fp0 fp1 fp2 fp3 | |
918 | * | |
919 | * Each individual register comprises exactly 4 bytes, except for | |
920 | * fp0-fp3, which are 8 bytes. All register values are in 960 | |
921 | * (little-endian) byte order. | |
922 | * | |
923 | ******************************************************************************/ | |
924 | ninRegsGet( regp ) | |
925 | unsigned char *regp; /* Where to place the register dump */ | |
926 | { | |
927 | if ( old_nindy ){ | |
928 | OninRegsGet( regp ); | |
929 | return; | |
930 | } | |
931 | send( (unsigned char *) "r", 1, regp ); | |
932 | } | |
933 | ||
934 | ||
935 | /****************************************************************************** | |
936 | * ninRegsPut: | |
937 | * Initialize the entire 960 register set to a specified set of values. | |
938 | * The format of the register value data should be the same as that | |
939 | * returned by ninRegsGet. | |
940 | * | |
941 | * WARNING: | |
942 | * All register values must be in 960 (little-endian) byte order. | |
943 | * | |
944 | ******************************************************************************/ | |
945 | ninRegsPut( regp ) | |
946 | char *regp; /* Pointer to desired values of registers */ | |
947 | { | |
948 | /* Number of bytes that we send to nindy. I believe this is defined by | |
949 | the protocol (it does not agree with REGISTER_BYTES). */ | |
950 | #define NINDY_REGISTER_BYTES ((36*4) + (4*8)) | |
951 | unsigned char buf[NINDY_REGISTER_BYTES+10]; | |
952 | ||
953 | if ( old_nindy ){ | |
954 | OninRegsPut( regp ); | |
955 | return; | |
956 | } | |
957 | ||
958 | buf[0] = 'R'; | |
959 | memcpy(buf+1, regp, NINDY_REGISTER_BYTES ); | |
960 | send( buf, NINDY_REGISTER_BYTES+1, NULL ); | |
961 | } | |
962 | ||
963 | ||
964 | /****************************************************************************** | |
965 | * ninReset: | |
966 | * Ask NINDY to perform a soft reset; wait for the reset to complete. | |
967 | * | |
968 | ******************************************************************************/ | |
969 | ninReset() | |
970 | { | |
971 | unsigned char ack; | |
972 | ||
973 | if ( old_nindy ){ | |
974 | OninReset(); | |
975 | return; | |
976 | } | |
977 | ||
978 | while (1){ | |
979 | putpkt((unsigned char *) "X", 1 ); | |
980 | while (1){ | |
981 | if ( !rdnin(&ack,1,5) ){ | |
982 | /* Timed out */ | |
983 | break; /* Resend */ | |
984 | } | |
985 | if ( ack == '+' ){ | |
986 | return; | |
987 | } | |
988 | } | |
989 | } | |
990 | } | |
991 | ||
992 | ||
993 | /****************************************************************************** | |
994 | * ninSrq: | |
995 | * Assume NINDY has stopped execution of the 960 application program in | |
996 | * order to process a host service request (srq). Ask NINDY for the | |
997 | * srq arguments, perform the requested service, and send an "srq | |
998 | * complete" message so NINDY will return control to the application. | |
999 | * | |
1000 | ******************************************************************************/ | |
1001 | ninSrq() | |
1002 | { | |
1003 | /* FIXME: Imposes arbitrary limits on lengths of pathnames and such. */ | |
1004 | unsigned char buf[BUFSIZE]; | |
1005 | int retcode; | |
1006 | unsigned char srqnum; | |
1007 | int i; | |
1008 | int offset; | |
1009 | int arg[MAX_SRQ_ARGS]; | |
1010 | ||
1011 | if ( old_nindy ){ | |
1012 | OninSrq(); | |
1013 | return; | |
1014 | } | |
1015 | ||
1016 | ||
1017 | /* Get srq number and arguments | |
1018 | */ | |
1019 | send((unsigned char *) "!", 1, buf ); | |
1020 | ||
1021 | srqnum = buf[0]; | |
1022 | for ( i=0, offset=1; i < MAX_SRQ_ARGS; i++, offset+=4 ){ | |
1023 | arg[i] = extract_unsigned_integer (&buf[offset], 4); | |
1024 | } | |
1025 | ||
1026 | /* Process Srq | |
1027 | */ | |
1028 | switch( srqnum ){ | |
1029 | case BS_CLOSE: | |
1030 | /* args: file descriptor */ | |
1031 | if ( arg[0] > 2 ){ | |
1032 | retcode = close( arg[0] ); | |
1033 | } else { | |
1034 | retcode = 0; | |
1035 | } | |
1036 | break; | |
1037 | case BS_CREAT: | |
1038 | /* args: filename, mode */ | |
1039 | ninStrGet( arg[0], buf ); | |
1040 | retcode = creat(buf,arg[1]); | |
1041 | break; | |
1042 | case BS_OPEN: | |
1043 | /* args: filename, flags, mode */ | |
1044 | ninStrGet( arg[0], buf ); | |
1045 | retcode = open(buf,arg[1],arg[2]); | |
1046 | break; | |
1047 | case BS_READ: | |
1048 | /* args: file descriptor, buffer, count */ | |
1049 | retcode = read(arg[0],buf,arg[2]); | |
1050 | if ( retcode > 0 ){ | |
1051 | ninMemPut( arg[1], buf, retcode ); | |
1052 | } | |
1053 | break; | |
1054 | case BS_SEEK: | |
1055 | /* args: file descriptor, offset, whence */ | |
1056 | retcode = lseek(arg[0],arg[1],arg[2]); | |
1057 | break; | |
1058 | case BS_WRITE: | |
1059 | /* args: file descriptor, buffer, count */ | |
1060 | ninMemGet( arg[1], buf, arg[2] ); | |
1061 | retcode = write(arg[0],buf,arg[2]); | |
1062 | break; | |
1063 | default: | |
1064 | retcode = -1; | |
1065 | break; | |
1066 | } | |
1067 | ||
1068 | /* Send request termination status to NINDY | |
1069 | */ | |
1070 | buf[0] = 'e'; | |
1071 | store_unsigned_integer (&buf[1], 4, retcode); | |
1072 | send( buf, 5, NULL ); | |
1073 | } | |
1074 | ||
1075 | ||
1076 | /****************************************************************************** | |
1077 | * ninStopWhy: | |
1078 | * Assume the application program has stopped (i.e., a DLE was received | |
1079 | * from NINDY). Ask NINDY for status information describing the | |
1080 | * reason for the halt. | |
1081 | * | |
1082 | * Returns a non-zero value if the user program has exited, 0 otherwise. | |
1083 | * Also returns the following information, through passed pointers: | |
1084 | * - why: an exit code if program the exited; otherwise the reason | |
1085 | * why the program halted (see stop.h for values). | |
1086 | * - contents of register ip (little-endian byte order) | |
1087 | * - contents of register sp (little-endian byte order) | |
1088 | * - contents of register fp (little-endian byte order) | |
1089 | ******************************************************************************/ | |
1090 | char | |
1091 | ninStopWhy( whyp, ipp, fpp, spp ) | |
1092 | unsigned char *whyp; /* Return the 'why' code through this pointer */ | |
1093 | long *ipp; /* Return contents of register ip through this pointer */ | |
1094 | long *fpp; /* Return contents of register fp through this pointer */ | |
1095 | long *spp; /* Return contents of register sp through this pointer */ | |
1096 | { | |
1097 | unsigned char buf[30]; | |
1098 | extern char OninStopWhy (); | |
1099 | ||
1100 | if ( old_nindy ){ | |
1101 | return OninStopWhy( whyp, ipp, fpp, spp ); | |
1102 | } | |
1103 | send((unsigned char *) "?", 1, buf ); | |
1104 | ||
1105 | *whyp = buf[1]; | |
1106 | memcpy ((char *)ipp, &buf[2], sizeof (*ipp)); | |
1107 | memcpy ((char *)fpp, &buf[6], sizeof (*ipp)); | |
1108 | memcpy ((char *)spp, &buf[10], sizeof (*ipp)); | |
1109 | return buf[0]; | |
1110 | } | |
1111 | ||
1112 | /****************************************************************************** | |
1113 | * ninStrGet: | |
1114 | * Read a '\0'-terminated string of data out of the 960 memory space. | |
1115 | * | |
1116 | ******************************************************************************/ | |
1117 | static | |
1118 | ninStrGet( ninaddr, hostaddr ) | |
1119 | unsigned long ninaddr; /* Address of string in NINDY memory space */ | |
1120 | unsigned char *hostaddr; /* Address of the buffer to which string should | |
1121 | * be copied. | |
1122 | */ | |
1123 | { | |
1124 | unsigned char cmd[5]; | |
1125 | ||
1126 | cmd[0] = '"'; | |
1127 | store_unsigned_integer (&cmd[1], 4, ninaddr); | |
1128 | send( cmd, 5, hostaddr ); | |
1129 | } | |
1130 | ||
1131 | #if 0 | |
1132 | /* Not used. */ | |
1133 | ||
1134 | /****************************************************************************** | |
1135 | * ninVersion: | |
1136 | * Ask NINDY for version information about itself. | |
1137 | * The information is sent as an ascii string in the form "x.xx,<arch>", | |
1138 | * where, | |
1139 | * x.xx is the version number | |
1140 | * <arch> is the processor architecture: "KA", "KB", "MC", "CA" * | |
1141 | * | |
1142 | ******************************************************************************/ | |
1143 | int | |
1144 | ninVersion( p ) | |
1145 | unsigned char *p; /* Where to place version string */ | |
1146 | { | |
1147 | ||
1148 | if ( old_nindy ){ | |
1149 | return OninVersion( p ); | |
1150 | } | |
1151 | send((unsigned char *) "v", 1, p ); | |
1152 | return strlen(p); | |
1153 | } | |
1154 | #endif /* 0 */ |