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