Commit | Line | Data |
---|---|---|
3a9c3d12 NC |
1 | /* |
2 | * Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved. | |
3 | * | |
4 | * This software may be freely used, copied, modified, and distributed | |
5 | * provided that the above copyright notice is preserved in all copies of the | |
6 | * software. | |
7 | */ | |
8 | ||
9 | /* -*-C-*- | |
10 | * | |
11 | * $Revision$ | |
12 | * $Date$ | |
13 | * | |
14 | * | |
15 | * serdrv.c - Synchronous Serial Driver for Angel. | |
16 | * This is nice and simple just to get something going. | |
17 | */ | |
18 | ||
19 | #ifdef __hpux | |
20 | # define _POSIX_SOURCE 1 | |
21 | #endif | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | ||
27 | #include "crc.h" | |
28 | #include "devices.h" | |
29 | #include "buffers.h" | |
30 | #include "rxtx.h" | |
31 | #include "hostchan.h" | |
32 | #include "params.h" | |
33 | #include "logging.h" | |
34 | ||
35 | #ifdef COMPILING_ON_WINDOWS | |
36 | # undef ERROR | |
37 | # undef IGNORE | |
38 | # include <windows.h> | |
39 | # include "angeldll.h" | |
40 | # include "comb_api.h" | |
41 | #else | |
42 | # ifdef __hpux | |
43 | # define _TERMIOS_INCLUDED | |
44 | # include <sys/termio.h> | |
45 | # undef _TERMIOS_INCLUDED | |
46 | # else | |
47 | # include <termios.h> | |
48 | # endif | |
49 | # include "unixcomm.h" | |
50 | #endif | |
51 | ||
52 | #ifndef UNUSED | |
53 | # define UNUSED(x) (x = x) /* Silence compiler warnings */ | |
54 | #endif | |
55 | ||
56 | #define MAXREADSIZE 512 | |
57 | #define MAXWRITESIZE 512 | |
58 | ||
59 | #define SERIAL_FC_SET ((1<<serial_XON)|(1<<serial_XOFF)) | |
60 | #define SERIAL_CTL_SET ((1<<serial_STX)|(1<<serial_ETX)|(1<<serial_ESC)) | |
61 | #define SERIAL_ESC_SET (SERIAL_FC_SET|SERIAL_CTL_SET) | |
62 | ||
63 | static const struct re_config config = { | |
64 | serial_STX, serial_ETX, serial_ESC, /* self-explanatory? */ | |
65 | SERIAL_FC_SET, /* set of flow-control characters */ | |
66 | SERIAL_ESC_SET, /* set of characters to be escaped */ | |
67 | NULL /* serial_flow_control */, NULL , /* what to do with FC chars */ | |
68 | angel_DD_RxEng_BufferAlloc, NULL /* how to get a buffer */ | |
69 | }; | |
70 | ||
71 | static struct re_state rxstate; | |
72 | ||
73 | typedef struct writestate { | |
74 | unsigned int wbindex; | |
75 | /* static te_status testatus;*/ | |
76 | unsigned char writebuf[MAXWRITESIZE]; | |
77 | struct te_state txstate; | |
78 | } writestate; | |
79 | ||
80 | static struct writestate wstate; | |
81 | ||
82 | /* | |
83 | * The set of parameter options supported by the device | |
84 | */ | |
85 | static unsigned int baud_options[] = { | |
86 | #ifdef __hpux | |
87 | 115200, 57600, | |
88 | #endif | |
89 | 38400, 19200, 9600 | |
90 | }; | |
91 | ||
92 | static ParameterList param_list[] = { | |
93 | { AP_BAUD_RATE, | |
94 | sizeof(baud_options)/sizeof(unsigned int), | |
95 | baud_options } | |
96 | }; | |
97 | ||
98 | static const ParameterOptions serial_options = { | |
99 | sizeof(param_list)/sizeof(ParameterList), param_list }; | |
100 | ||
101 | /* | |
102 | * The default parameter config for the device | |
103 | */ | |
104 | static Parameter param_default[] = { | |
105 | { AP_BAUD_RATE, 9600 } | |
106 | }; | |
107 | ||
108 | static ParameterConfig serial_defaults = { | |
109 | sizeof(param_default)/sizeof(Parameter), param_default }; | |
110 | ||
111 | /* | |
112 | * The user-modified options for the device | |
113 | */ | |
114 | static unsigned int user_baud_options[sizeof(baud_options)/sizeof(unsigned)]; | |
115 | ||
116 | static ParameterList param_user_list[] = { | |
117 | { AP_BAUD_RATE, | |
118 | sizeof(user_baud_options)/sizeof(unsigned), | |
119 | user_baud_options } | |
120 | }; | |
121 | ||
122 | static ParameterOptions user_options = { | |
123 | sizeof(param_user_list)/sizeof(ParameterList), param_user_list }; | |
124 | ||
125 | static bool user_options_set; | |
126 | ||
127 | /* forward declarations */ | |
128 | static int serial_reset( void ); | |
129 | static int serial_set_params( const ParameterConfig *config ); | |
130 | static int SerialMatch(const char *name, const char *arg); | |
131 | ||
132 | static void process_baud_rate( unsigned int target_baud_rate ) | |
133 | { | |
134 | const ParameterList *full_list; | |
135 | ParameterList *user_list; | |
136 | ||
137 | /* create subset of full options */ | |
138 | full_list = Angel_FindParamList( &serial_options, AP_BAUD_RATE ); | |
139 | user_list = Angel_FindParamList( &user_options, AP_BAUD_RATE ); | |
140 | ||
141 | if ( full_list != NULL && user_list != NULL ) | |
142 | { | |
143 | unsigned int i, j; | |
144 | unsigned int def_baud = 0; | |
145 | ||
146 | /* find lower or equal to */ | |
147 | for ( i = 0; i < full_list->num_options; ++i ) | |
148 | if ( target_baud_rate >= full_list->option[i] ) | |
149 | { | |
150 | /* copy remaining */ | |
151 | for ( j = 0; j < (full_list->num_options - i); ++j ) | |
152 | user_list->option[j] = full_list->option[i+j]; | |
153 | user_list->num_options = j; | |
154 | ||
155 | /* check this is not the default */ | |
156 | Angel_FindParam( AP_BAUD_RATE, &serial_defaults, &def_baud ); | |
157 | if ( (j == 1) && (user_list->option[0] == def_baud) ) | |
158 | { | |
159 | #ifdef DEBUG | |
160 | printf( "user selected default\n" ); | |
161 | #endif | |
162 | } | |
163 | else | |
164 | { | |
165 | user_options_set = TRUE; | |
166 | #ifdef DEBUG | |
167 | printf( "user options are: " ); | |
168 | for ( j = 0; j < user_list->num_options; ++j ) | |
169 | printf( "%u ", user_list->option[j] ); | |
170 | printf( "\n" ); | |
171 | #endif | |
172 | } | |
173 | ||
174 | break; /* out of i loop */ | |
175 | } | |
176 | ||
177 | #ifdef DEBUG | |
178 | if ( i >= full_list->num_options ) | |
179 | printf( "couldn't match baud rate %u\n", target_baud_rate ); | |
180 | #endif | |
181 | } | |
182 | #ifdef DEBUG | |
183 | else | |
184 | printf( "failed to find lists\n" ); | |
185 | #endif | |
186 | } | |
187 | ||
188 | static int SerialOpen(const char *name, const char *arg) | |
189 | { | |
190 | const char *port_name = name; | |
191 | ||
192 | #ifdef DEBUG | |
193 | printf("SerialOpen: name %s arg %s\n", name, arg ? arg : "<NULL>"); | |
194 | #endif | |
195 | ||
196 | #ifdef COMPILING_ON_WINDOWS | |
197 | if (IsOpenSerial()) return -1; | |
198 | #else | |
199 | if (Unix_IsSerialInUse()) return -1; | |
200 | #endif | |
201 | ||
202 | #ifdef COMPILING_ON_WINDOWS | |
203 | if (SerialMatch(name, arg) != adp_ok) | |
204 | return adp_failed; | |
205 | #else | |
206 | port_name = Unix_MatchValidSerialDevice(port_name); | |
207 | # ifdef DEBUG | |
208 | printf("translated port to %s\n", port_name == 0 ? "NULL" : port_name); | |
209 | # endif | |
210 | if (port_name == 0) return adp_failed; | |
211 | #endif | |
212 | ||
213 | user_options_set = FALSE; | |
214 | ||
215 | /* interpret and store the arguments */ | |
216 | if ( arg != NULL ) | |
217 | { | |
218 | unsigned int target_baud_rate; | |
219 | target_baud_rate = (unsigned int)strtoul(arg, NULL, 10); | |
220 | if (target_baud_rate > 0) | |
221 | { | |
222 | #ifdef DEBUG | |
223 | printf( "user selected baud rate %u\n", target_baud_rate ); | |
224 | #endif | |
225 | process_baud_rate( target_baud_rate ); | |
226 | } | |
227 | #ifdef DEBUG | |
228 | else | |
229 | printf( "could not understand baud rate %s\n", arg ); | |
230 | #endif | |
231 | } | |
232 | ||
233 | #ifdef COMPILING_ON_WINDOWS | |
234 | { | |
235 | int port = IsValidDevice(name); | |
236 | if (OpenSerial(port, FALSE) != COM_OK) | |
237 | return -1; | |
238 | } | |
239 | #else | |
240 | if (Unix_OpenSerial(port_name) < 0) | |
241 | return -1; | |
242 | #endif | |
243 | ||
244 | serial_reset(); | |
245 | ||
246 | #if defined(__unix) || defined(__CYGWIN32__) | |
247 | Unix_ioctlNonBlocking(); | |
248 | #endif | |
249 | ||
250 | Angel_RxEngineInit(&config, &rxstate); | |
251 | /* | |
252 | * DANGER!: passing in NULL as the packet is ok for now as it is just | |
253 | * IGNOREd but this may well change | |
254 | */ | |
255 | Angel_TxEngineInit(&config, NULL, &wstate.txstate); | |
256 | return 0; | |
257 | } | |
258 | ||
259 | static int SerialMatch(const char *name, const char *arg) | |
260 | { | |
261 | UNUSED(arg); | |
262 | #ifdef COMPILING_ON_WINDOWS | |
263 | if (IsValidDevice(name) == COM_DEVICENOTVALID) | |
264 | return -1; | |
265 | else | |
266 | return 0; | |
267 | #else | |
268 | return Unix_MatchValidSerialDevice(name) == 0 ? -1 : 0; | |
269 | #endif | |
270 | } | |
271 | ||
272 | static void SerialClose(void) | |
273 | { | |
274 | #ifdef DO_TRACE | |
275 | printf("SerialClose()\n"); | |
276 | #endif | |
277 | ||
278 | #ifdef COMPILING_ON_WINDOWS | |
279 | CloseSerial(); | |
280 | #else | |
281 | Unix_CloseSerial(); | |
282 | #endif | |
283 | } | |
284 | ||
285 | static int SerialRead(DriverCall *dc, bool block) { | |
286 | static unsigned char readbuf[MAXREADSIZE]; | |
287 | static int rbindex=0; | |
288 | ||
289 | int nread; | |
290 | int read_errno; | |
291 | int c=0; | |
292 | re_status restatus; | |
293 | int ret_code = -1; /* assume bad packet or error */ | |
294 | ||
295 | /* must not overflow buffer and must start after the existing data */ | |
296 | #ifdef COMPILING_ON_WINDOWS | |
297 | { | |
298 | BOOL dummy = FALSE; | |
299 | nread = BytesInRXBufferSerial(); | |
300 | ||
301 | if (nread > MAXREADSIZE - rbindex) | |
302 | nread = MAXREADSIZE - rbindex; | |
303 | ||
304 | if ((read_errno = ReadSerial(readbuf+rbindex, nread, &dummy)) == COM_READFAIL) | |
305 | { | |
306 | MessageBox(GetFocus(), "Read error\n", "Angel", MB_OK | MB_ICONSTOP); | |
307 | return -1; /* SJ - This really needs to return a value, which is picked up in */ | |
308 | /* DevSW_Read as meaning stop debugger but don't kill. */ | |
309 | } | |
310 | else if (pfnProgressCallback != NULL && read_errno == COM_OK) | |
311 | { | |
312 | progressInfo.nRead += nread; | |
313 | (*pfnProgressCallback)(&progressInfo); | |
314 | } | |
315 | } | |
316 | #else | |
317 | nread = Unix_ReadSerial(readbuf+rbindex, MAXREADSIZE-rbindex, block); | |
318 | read_errno = errno; | |
319 | #endif | |
320 | ||
321 | if ((nread > 0) || (rbindex > 0)) { | |
322 | ||
323 | #ifdef DO_TRACE | |
324 | printf("[%d@%d] ", nread, rbindex); | |
325 | #endif | |
326 | ||
327 | if (nread>0) | |
328 | rbindex = rbindex+nread; | |
329 | ||
330 | do { | |
331 | restatus = Angel_RxEngine(readbuf[c], &(dc->dc_packet), &rxstate); | |
332 | #ifdef DO_TRACE | |
333 | printf("<%02X ",readbuf[c]); | |
334 | if (!(++c % 16)) | |
335 | printf("\n"); | |
336 | #else | |
337 | c++; | |
338 | #endif | |
339 | } while (c<rbindex && | |
340 | ((restatus == RS_IN_PKT) || (restatus == RS_WAIT_PKT))); | |
341 | ||
342 | #ifdef DO_TRACE | |
343 | if (c % 16) | |
344 | printf("\n"); | |
345 | #endif | |
346 | ||
347 | switch(restatus) { | |
348 | ||
349 | case RS_GOOD_PKT: | |
350 | ret_code = 1; | |
351 | /* fall through to: */ | |
352 | ||
353 | case RS_BAD_PKT: | |
354 | /* | |
355 | * We now need to shuffle any left over data down to the | |
356 | * beginning of our private buffer ready to be used | |
357 | *for the next packet | |
358 | */ | |
359 | #ifdef DO_TRACE | |
360 | printf("SerialRead() processed %d, moving down %d\n", c, rbindex-c); | |
361 | #endif | |
362 | if (c != rbindex) memmove((char *) readbuf, (char *) (readbuf+c), | |
363 | rbindex-c); | |
364 | rbindex -= c; | |
365 | break; | |
366 | ||
367 | case RS_IN_PKT: | |
368 | case RS_WAIT_PKT: | |
369 | rbindex = 0; /* will have processed all we had */ | |
370 | ret_code = 0; | |
371 | break; | |
372 | ||
373 | default: | |
374 | #ifdef DEBUG | |
375 | printf("Bad re_status in serialRead()\n"); | |
376 | #endif | |
377 | break; | |
378 | } | |
379 | } else if (nread == 0) | |
380 | ret_code = 0; /* nothing to read */ | |
381 | else if (read_errno == ERRNO_FOR_BLOCKED_IO) /* nread < 0 */ | |
382 | ret_code = 0; | |
383 | ||
384 | #ifdef DEBUG | |
385 | if ((nread<0) && (read_errno!=ERRNO_FOR_BLOCKED_IO)) | |
386 | perror("read() error in serialRead()"); | |
387 | #endif | |
388 | ||
389 | return ret_code; | |
390 | } | |
391 | ||
392 | ||
393 | static int SerialWrite(DriverCall *dc) { | |
394 | int nwritten = 0; | |
395 | te_status testatus = TS_IN_PKT; | |
396 | ||
397 | if (dc->dc_context == NULL) { | |
398 | Angel_TxEngineInit(&config, &(dc->dc_packet), &(wstate.txstate)); | |
399 | wstate.wbindex = 0; | |
400 | dc->dc_context = &wstate; | |
401 | } | |
402 | ||
403 | while ((testatus == TS_IN_PKT) && (wstate.wbindex < MAXWRITESIZE)) | |
404 | { | |
405 | /* send the raw data through the tx engine to escape and encapsulate */ | |
406 | testatus = Angel_TxEngine(&(dc->dc_packet), &(wstate.txstate), | |
407 | &(wstate.writebuf)[wstate.wbindex]); | |
408 | if (testatus != TS_IDLE) wstate.wbindex++; | |
409 | } | |
410 | ||
411 | if (testatus == TS_IDLE) { | |
412 | #ifdef DEBUG | |
413 | printf("SerialWrite: testatus is TS_IDLE during preprocessing\n"); | |
414 | #endif | |
415 | } | |
416 | ||
417 | #ifdef DO_TRACE | |
418 | { | |
419 | int i = 0; | |
420 | ||
421 | while (i<wstate.wbindex) | |
422 | { | |
423 | printf(">%02X ",wstate.writebuf[i]); | |
424 | ||
425 | if (!(++i % 16)) | |
426 | printf("\n"); | |
427 | } | |
428 | if (i % 16) | |
429 | printf("\n"); | |
430 | } | |
431 | #endif | |
432 | ||
433 | #ifdef COMPILING_ON_WINDOWS | |
434 | if (WriteSerial(wstate.writebuf, wstate.wbindex) == COM_OK) | |
435 | { | |
436 | nwritten = wstate.wbindex; | |
437 | if (pfnProgressCallback != NULL) | |
438 | { | |
439 | progressInfo.nWritten += nwritten; | |
440 | (*pfnProgressCallback)(&progressInfo); | |
441 | } | |
442 | } | |
443 | else | |
444 | { | |
445 | MessageBox(GetFocus(), "Write error\n", "Angel", MB_OK | MB_ICONSTOP); | |
446 | return -1; /* SJ - This really needs to return a value, which is picked up in */ | |
447 | /* DevSW_Read as meaning stop debugger but don't kill. */ | |
448 | } | |
449 | #else | |
450 | nwritten = Unix_WriteSerial(wstate.writebuf, wstate.wbindex); | |
451 | ||
452 | if (nwritten < 0) { | |
453 | nwritten=0; | |
454 | } | |
455 | #endif | |
456 | ||
457 | #ifdef DEBUG | |
458 | if (nwritten > 0) | |
459 | printf("Wrote %#04x bytes\n", nwritten); | |
460 | #endif | |
461 | ||
462 | if ((unsigned) nwritten == wstate.wbindex && | |
463 | (testatus == TS_DONE_PKT || testatus == TS_IDLE)) { | |
464 | ||
465 | /* finished sending the packet */ | |
466 | ||
467 | #ifdef DEBUG | |
468 | printf("SerialWrite: calling Angel_TxEngineInit after sending packet (len=%i)\n",wstate.wbindex); | |
469 | #endif | |
470 | testatus = TS_IN_PKT; | |
471 | wstate.wbindex = 0; | |
472 | return 1; | |
473 | } | |
474 | else { | |
475 | #ifdef DEBUG | |
476 | printf("SerialWrite: Wrote part of packet wbindex=%i, nwritten=%i\n", | |
477 | wstate.wbindex, nwritten); | |
478 | #endif | |
479 | ||
480 | /* | |
481 | * still some data left to send shuffle whats left down and reset | |
482 | * the ptr | |
483 | */ | |
484 | memmove((char *) wstate.writebuf, (char *) (wstate.writebuf+nwritten), | |
485 | wstate.wbindex-nwritten); | |
486 | wstate.wbindex -= nwritten; | |
487 | return 0; | |
488 | } | |
489 | return -1; | |
490 | } | |
491 | ||
492 | ||
493 | static int serial_reset( void ) | |
494 | { | |
495 | #ifdef DEBUG | |
496 | printf( "serial_reset\n" ); | |
497 | #endif | |
498 | ||
499 | #ifdef COMPILING_ON_WINDOWS | |
500 | FlushSerial(); | |
501 | #else | |
502 | Unix_ResetSerial(); | |
503 | #endif | |
504 | ||
505 | return serial_set_params( &serial_defaults ); | |
506 | } | |
507 | ||
508 | ||
509 | static int find_baud_rate( unsigned int *speed ) | |
510 | { | |
511 | static struct { | |
512 | unsigned int baud; | |
513 | int termiosValue; | |
514 | } possibleBaudRates[] = { | |
515 | #if defined(__hpux) | |
516 | {115200,_B115200}, {57600,_B57600}, | |
517 | #endif | |
518 | #ifdef COMPILING_ON_WINDOWS | |
519 | {38400,CBR_38400}, {19200,CBR_19200}, {9600, CBR_9600}, {0,0} | |
520 | #else | |
521 | {38400,B38400}, {19200,B19200}, {9600, B9600}, {0,0} | |
522 | #endif | |
523 | }; | |
524 | unsigned int i; | |
525 | ||
526 | /* look for lower or matching -- will always terminate at 0 end marker */ | |
527 | for ( i = 0; possibleBaudRates[i].baud > *speed; ++i ) | |
528 | /* do nothing */ ; | |
529 | ||
530 | if ( possibleBaudRates[i].baud > 0 ) | |
531 | *speed = possibleBaudRates[i].baud; | |
532 | ||
533 | return possibleBaudRates[i].termiosValue; | |
534 | } | |
535 | ||
536 | ||
537 | static int serial_set_params( const ParameterConfig *config ) | |
538 | { | |
539 | unsigned int speed; | |
540 | int termios_value; | |
541 | ||
542 | #ifdef DEBUG | |
543 | printf( "serial_set_params\n" ); | |
544 | #endif | |
545 | ||
546 | if ( ! Angel_FindParam( AP_BAUD_RATE, config, &speed ) ) | |
547 | { | |
548 | #ifdef DEBUG | |
549 | printf( "speed not found in config\n" ); | |
550 | #endif | |
551 | return DE_OKAY; | |
552 | } | |
553 | ||
554 | termios_value = find_baud_rate( &speed ); | |
555 | if ( termios_value == 0 ) | |
556 | { | |
557 | #ifdef DEBUG | |
558 | printf( "speed not valid: %u\n", speed ); | |
559 | #endif | |
560 | return DE_OKAY; | |
561 | } | |
562 | ||
563 | #ifdef DEBUG | |
564 | printf( "setting speed to %u\n", speed ); | |
565 | #endif | |
566 | ||
567 | #ifdef COMPILING_ON_WINDOWS | |
568 | SetBaudRate((WORD)termios_value); | |
569 | #else | |
570 | Unix_SetSerialBaudRate(termios_value); | |
571 | #endif | |
572 | ||
573 | return DE_OKAY; | |
574 | } | |
575 | ||
576 | ||
577 | static int serial_get_user_params( ParameterOptions **p_options ) | |
578 | { | |
579 | #ifdef DEBUG | |
580 | printf( "serial_get_user_params\n" ); | |
581 | #endif | |
582 | ||
583 | if ( user_options_set ) | |
584 | { | |
585 | *p_options = &user_options; | |
586 | } | |
587 | else | |
588 | { | |
589 | *p_options = NULL; | |
590 | } | |
591 | ||
592 | return DE_OKAY; | |
593 | } | |
594 | ||
595 | ||
596 | static int serial_get_default_params( ParameterConfig **p_config ) | |
597 | { | |
598 | #ifdef DEBUG | |
599 | printf( "serial_get_default_params\n" ); | |
600 | #endif | |
601 | ||
602 | *p_config = (ParameterConfig *) &serial_defaults; | |
603 | return DE_OKAY; | |
604 | } | |
605 | ||
606 | ||
607 | static int SerialIoctl(const int opcode, void *args) { | |
608 | ||
609 | int ret_code; | |
610 | ||
611 | #ifdef DEBUG | |
612 | printf( "SerialIoctl: op %d arg %p\n", opcode, args ? args : "<NULL>"); | |
613 | #endif | |
614 | ||
615 | switch (opcode) | |
616 | { | |
617 | case DC_RESET: | |
618 | ret_code = serial_reset(); | |
619 | break; | |
620 | ||
621 | case DC_SET_PARAMS: | |
622 | ret_code = serial_set_params((const ParameterConfig *)args); | |
623 | break; | |
624 | ||
625 | case DC_GET_USER_PARAMS: | |
626 | ret_code = serial_get_user_params((ParameterOptions **)args); | |
627 | break; | |
628 | ||
629 | case DC_GET_DEFAULT_PARAMS: | |
630 | ret_code = serial_get_default_params((ParameterConfig **)args); | |
631 | break; | |
632 | ||
633 | default: | |
634 | ret_code = DE_BAD_OP; | |
635 | break; | |
636 | } | |
637 | ||
638 | return ret_code; | |
639 | } | |
640 | ||
641 | DeviceDescr angel_SerialDevice = { | |
642 | "SERIAL", | |
643 | SerialOpen, | |
644 | SerialMatch, | |
645 | SerialClose, | |
646 | SerialRead, | |
647 | SerialWrite, | |
648 | SerialIoctl | |
649 | }; |