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.4 $
12 *     $Date: 1999/01/28 03:50:16 $
13 *
14 *
15 * serpardv.c - Serial/Parallel Driver for Angel.
16 */
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "crc.h"
22#include "devices.h"
23#include "buffers.h"
24#include "rxtx.h"
25#include "hostchan.h"
26#include "params.h"
27#include "logging.h"
28#include "hsys.h"
29
30#ifdef COMPILING_ON_WINDOWS
31#  undef   ERROR
32#  undef   IGNORE
33#  include <windows.h>
34#  include "angeldll.h"
35#  include "comb_api.h"
36#else
37#  ifdef __hpux
38#    define _TERMIOS_INCLUDED
39#    include <sys/termio.h>
40#    undef _TERMIOS_INCLUDED
41#  else
42#    include <termios.h>
43#  endif
44#  include "unixcomm.h"
45#endif
46
47#ifndef UNUSED
48#  define UNUSED(x) (x = x)     /* Silence compiler warnings */
49#endif
50
51#define MAXREADSIZE     512
52#define MAXWRITESIZE    512
53
54#define SERPAR_FC_SET  ((1 << serial_XON) | (1 << serial_XOFF))
55#define SERPAR_CTL_SET ((1 << serial_STX) | (1 << serial_ETX) | \
56                        (1 << serial_ESC))
57#define SERPAR_ESC_SET (SERPAR_FC_SET | SERPAR_CTL_SET)
58
59static const struct re_config config = {
60    serial_STX, serial_ETX, serial_ESC, /* self-explanatory?               */
61    SERPAR_FC_SET,                      /* set of flow-control characters  */
62    SERPAR_ESC_SET,                     /* set of characters to be escaped */
63    NULL,                               /* serial_flow_control */
64    NULL,                               /* what to do with FC chars */
65    angel_DD_RxEng_BufferAlloc, NULL    /* how to get a buffer */
66};
67
68static struct re_state rxstate;
69
70/*
71 * structure used for manipulating transmit data
72 */
73typedef struct TxState
74{
75    struct te_state state;
76    unsigned int    index;
77    unsigned char   writebuf[MAXWRITESIZE];
78} TxState;
79
80/*
81 * The set of parameter options supported by the device
82 */
83static unsigned int baud_options[] =
84{
85#ifdef __hpux
86    115200, 57600,
87#endif
88    38400, 19200, 9600
89};
90
91static ParameterList param_list[] =
92{
93    {
94        AP_BAUD_RATE,
95        sizeof(baud_options) / sizeof(unsigned int),
96        baud_options
97    }
98};
99
100static const ParameterOptions serpar_options =
101{
102    sizeof(param_list) / sizeof(ParameterList),
103    param_list
104};
105
106/*
107 * The default parameter config for the device
108 */
109static Parameter param_default[] =
110{
111    { AP_BAUD_RATE, 9600 }
112};
113
114static const ParameterConfig serpar_defaults =
115{
116    sizeof(param_default)/sizeof(Parameter),
117    param_default
118};
119
120/*
121 * The user-modified options for the device
122 */
123static unsigned int user_baud_options[sizeof(baud_options) /
124                                     sizeof(unsigned int)];
125
126static ParameterList param_user_list[] =
127{
128    {
129        AP_BAUD_RATE,
130        sizeof(user_baud_options) / sizeof(unsigned),
131        user_baud_options
132    }
133};
134
135static ParameterOptions user_options =
136{
137    sizeof(param_user_list) / sizeof(ParameterList),
138    param_user_list
139};
140
141static bool user_options_set;
142
143/* forward declarations */
144static int serpar_reset(void);
145static int serpar_set_params(const ParameterConfig *config);
146static int SerparMatch(const char *name, const char *arg);
147
148static void process_baud_rate(unsigned int target_baud_rate)
149{
150    const ParameterList *full_list;
151    ParameterList       *user_list;
152
153    /* create subset of full options */
154    full_list = Angel_FindParamList(&serpar_options, AP_BAUD_RATE);
155    user_list = Angel_FindParamList(&user_options,   AP_BAUD_RATE);
156
157    if (full_list != NULL && user_list != NULL)
158    {
159        unsigned int i, j;
160        unsigned int def_baud = 0;
161
162        /* find lower or equal to */
163        for (i = 0; i < full_list->num_options; ++i)
164           if (target_baud_rate >= full_list->option[i])
165           {
166               /* copy remaining */
167               for (j = 0; j < (full_list->num_options - i); ++j)
168                  user_list->option[j] = full_list->option[i+j];
169               user_list->num_options = j;
170
171               /* check this is not the default */
172               Angel_FindParam(AP_BAUD_RATE, &serpar_defaults, &def_baud);
173               if ((j == 1) && (user_list->option[0] == def_baud))
174               {
175#ifdef DEBUG
176                   printf("user selected default\n");
177#endif
178               }
179               else
180               {
181                   user_options_set = TRUE;
182#ifdef DEBUG
183                   printf("user options are: ");
184                   for (j = 0; j < user_list->num_options; ++j)
185                      printf("%u ", user_list->option[j]);
186                   printf("\n");
187#endif
188               }
189
190               break;   /* out of i loop */
191           }
192
193#ifdef DEBUG
194        if (i >= full_list->num_options)
195           printf("couldn't match baud rate %u\n", target_baud_rate);
196#endif
197    }
198#ifdef DEBUG
199    else
200       printf("failed to find lists\n");
201#endif
202}
203
204static int SerparOpen(const char *name, const char *arg)
205{
206    char *sername = NULL;
207    char *parname = NULL;
208
209#ifdef DEBUG
210    printf("SerparOpen: name %s arg %s\n", name, arg ? arg : "<NULL>");
211#endif
212
213#ifdef COMPILING_ON_WINDOWS
214    if (IsOpenSerial() || IsOpenParallel()) return -1;
215#else
216    if (Unix_IsSerialInUse() || Unix_IsParallelInUse()) return -1;
217#endif
218
219#ifdef COMPILING_ON_WINDOWS
220    if (SerparMatch(name, arg) == -1)
221        return -1;
222#else
223    Unix_IsValidParallelDevice(name,&sername,&parname);
224# ifdef DEBUG
225    printf("translated %s to serial %s and parallel %s\n",
226           name==0 ? "NULL" : name,
227           sername==0 ? "NULL" : sername,
228           parname==0 ? "NULL" : parname);
229# endif
230    if (sername==NULL || parname==NULL) return -1;
231#endif
232
233    user_options_set = FALSE;
234
235    /* interpret and store the arguments */
236    if (arg != NULL)
237    {
238        unsigned int target_baud_rate;
239
240        target_baud_rate = (unsigned int)strtoul(arg, NULL, 10);
241
242        if (target_baud_rate > 0)
243        {
244#ifdef DEBUG
245            printf("user selected baud rate %u\n", target_baud_rate);
246#endif
247            process_baud_rate(target_baud_rate);
248        }
249#ifdef DEBUG
250        else
251            printf("could not understand baud rate %s\n", arg);
252#endif
253    }
254
255#ifdef COMPILING_ON_WINDOWS
256    {
257        /*
258         * The serial port number is in name[0] followed by
259         * the parallel port number in name[1]
260         */
261
262        int sport = name[0] - '0';
263        int pport = name[1] - '0';
264
265        if (OpenParallel(pport) != COM_OK)
266            return -1;
267
268        if (OpenSerial(sport, FALSE) != COM_OK)
269        {
270            CloseParallel();
271            return -1;
272        }
273    }
274#else
275    Unix_OpenParallel(parname);
276    Unix_OpenSerial(sername);
277#endif
278
279    serpar_reset();
280
281#if defined(__unix) || defined(__CYGWIN__)
282    Unix_ioctlNonBlocking();
283#endif
284
285    Angel_RxEngineInit(&config, &rxstate);
286
287    return 0;
288}
289
290#ifdef COMPILING_ON_WINDOWS
291static int SerparMatch(const char *name, const char *arg)
292{
293    char sername[2];
294    char parname[2];
295
296    UNUSED(arg);
297
298    sername[0] = name[0];
299    parname[0] = name[1];
300    sername[1] = parname[1] = 0;
301
302    if (IsValidDevice(sername) == COM_DEVICENOTVALID ||
303        IsValidDevice(parname) == COM_DEVICENOTVALID)
304        return -1;
305    else
306        return 0;
307}
308#else
309static int SerparMatch(const char *portstring, const char *arg)
310{
311    char *sername=NULL, *parname=NULL;
312    UNUSED(arg);
313
314    Unix_IsValidParallelDevice(portstring,&sername,&parname);
315
316      /* Match failed if either sername or parname are still NULL */
317    if (sername==NULL || parname==NULL) return -1;
318    return 0;
319}
320#endif
321
322static void SerparClose(void)
323{
324#ifdef COMPILING_ON_WINDOWS
325    CloseParallel();
326    CloseSerial();
327#else
328    Unix_CloseParallel();
329    Unix_CloseSerial();
330#endif
331}
332
333static int SerparRead(DriverCall *dc, bool block)
334{
335    static unsigned char readbuf[MAXREADSIZE];
336    static int rbindex = 0;
337
338    int nread;
339    int read_errno;
340    int c = 0;
341    re_status restatus;
342    int ret_code = -1;            /* assume bad packet or error */
343
344    /*
345     * we must not overflow buffer, and must start after
346     * the existing data
347     */
348#ifdef COMPILING_ON_WINDOWS
349    {
350        BOOL dummy = FALSE;
351        nread = BytesInRXBufferSerial();
352
353        if (nread > MAXREADSIZE - rbindex)
354            nread = MAXREADSIZE - rbindex;
355        read_errno = ReadSerial(readbuf+rbindex, nread, &dummy);
356        if (pfnProgressCallback != NULL && read_errno == COM_OK)
357        {
358            progressInfo.nRead += nread;
359            (*pfnProgressCallback)(&progressInfo);
360        }
361    }
362#else
363    nread = Unix_ReadSerial(readbuf+rbindex, MAXREADSIZE-rbindex, block);
364    read_errno = errno;
365#endif
366
367    if ((nread > 0) || (rbindex > 0))
368    {
369#ifdef DO_TRACE
370        printf("[%d@%d] ", nread, rbindex);
371#endif
372
373        if (nread > 0)
374            rbindex = rbindex + nread;
375
376        do
377        {
378            restatus = Angel_RxEngine(readbuf[c], &(dc->dc_packet), &rxstate);
379
380#ifdef DO_TRACE
381            printf("<%02X ",readbuf[c]);
382#endif
383            c++;
384        } while (c < rbindex &&
385                 ((restatus == RS_IN_PKT) || (restatus == RS_WAIT_PKT)));
386
387#ifdef DO_TRACE
388        printf("\n");
389#endif
390
391        switch(restatus)
392        {
393          case RS_GOOD_PKT:
394              ret_code = 1;
395              /* fall through to: */
396
397          case RS_BAD_PKT:
398              /*
399               * We now need to shuffle any left over data down to the
400               * beginning of our private buffer ready to be used
401               *for the next packet
402               */
403#ifdef DO_TRACE
404              printf("SerparRead() processed %d, moving down %d\n",
405                     c, rbindex - c);
406#endif
407
408              if (c != rbindex)
409                  memmove((char *) readbuf, (char *) (readbuf + c), rbindex - c);
410
411              rbindex -= c;
412
413              break;
414
415          case RS_IN_PKT:
416          case RS_WAIT_PKT:
417              rbindex = 0;            /* will have processed all we had */
418              ret_code = 0;
419              break;
420
421          default:
422#ifdef DEBUG
423              printf("Bad re_status in SerparRead()\n");
424#endif
425              break;
426        }
427    }
428    else if (nread == 0)
429        /* nothing to read */
430        ret_code = 0;
431    else if (read_errno == ERRNO_FOR_BLOCKED_IO) /* nread < 0 */
432        ret_code = 0;
433
434#ifdef DEBUG
435    if ((nread < 0) && (read_errno != ERRNO_FOR_BLOCKED_IO))
436        perror("read() error in SerparRead()");
437#endif
438
439    return ret_code;
440}
441
442/*
443 *  Function: send_packet
444 *   Purpose: Send a stream of bytes to Angel through the parallel port
445 *
446 * Algorithm: We need to present the data in a form that all boards can
447 *            swallow.  With the PID board, this is a problem: for reasons
448 *            described in the driver (angel/pid/st16c552.c), data are
449 *            sent a nybble at a time on D0-D2 and D4; D3 is wired to ACK,
450 *            which generates an interrupt when it goes low.  This routine
451 *            fills in an array of nybbles, with ACK clear in all but the
452 *            last one.  If, for whatever reason, the write fails, then
453 *            ACK is forced high (thereby enabling the next write a chance
454 *            to be noticed when the falling edge of ACK generates an
455 *            interrupt (hopefully).
456 *
457 *    Params:
458 *       Input: txstate Contains the packet to be sent
459 *
460 *   Returns: Number of *complete* bytes written
461 */
462
463static int SerparWrite(DriverCall *dc)
464{
465    te_status status;
466    int nwritten = 0;
467    static TxState txstate;
468
469    /*
470     * is this a new packet?
471     */
472    if (dc->dc_context == NULL)
473    {
474        /*
475         * yes - initialise TxEngine
476         */
477        Angel_TxEngineInit(&config, &dc->dc_packet, &txstate.state);
478
479        txstate.index = 0;
480        dc->dc_context = &txstate;
481    }
482
483    /*
484     * fill the buffer using the Tx Engine
485     */
486    do
487    {
488        status = Angel_TxEngine(&dc->dc_packet, &txstate.state,
489                                &txstate.writebuf[txstate.index]);
490        if (status != TS_IDLE) txstate.index++;
491
492    } while (status == TS_IN_PKT && txstate.index < MAXWRITESIZE);
493
494#ifdef DO_TRACE
495    {
496        unsigned int i = 0;
497
498        while (i < txstate.index)
499        {
500            printf(">%02X ", txstate.writebuf[i]);
501
502            if (!(++i % 16))
503                putc('\n', stdout);
504        }
505
506        if (i % 16)
507            putc('\n', stdout);
508    }
509#endif
510
511    /*
512     * the data are ready, all we need now is to send them out
513     * in a form that Angel can swallow.
514     */
515#ifdef COMPILING_ON_WINDOWS
516  if (WriteParallel(txstate.writebuf, txstate.index) == COM_OK)
517  {
518    nwritten = txstate.index;
519    if (pfnProgressCallback != NULL)
520    {
521      progressInfo.nWritten += nwritten;
522      (*pfnProgressCallback)(&progressInfo);
523    }
524  }
525  else
526  {
527      MessageBox(GetFocus(), "Write error\n", "Angel", MB_OK | MB_ICONSTOP);
528      return -1;   /* SJ - This really needs to return a value, which is picked up in */
529                   /*      DevSW_Read as meaning stop debugger but don't kill. */
530  }
531#else
532    nwritten = Unix_WriteParallel(txstate.writebuf, txstate.index);
533#endif
534
535    if (nwritten < 0) nwritten = 0;
536
537#ifdef DO_TRACE
538    printf("SerparWrite: wrote %d out of %d bytes\n",
539           nwritten, txstate.index);
540#endif
541
542    /*
543     * has the whole packet gone?
544     */
545    if (nwritten == (int)txstate.index &&
546        (status == TS_DONE_PKT || status == TS_IDLE))
547        /*
548         * yes it has
549         */
550        return 1;
551    else
552    {
553        /*
554         * if some data are left, shuffle them
555         * to the start of the buffer
556         */
557        if (nwritten != (int)txstate.index && nwritten != 0)
558        {
559            txstate.index -= nwritten;
560            (void)memmove((char *) txstate.writebuf,
561                          (char *) (txstate.writebuf + nwritten),
562                          txstate.index);
563        }
564        else if (nwritten == (int)txstate.index)
565            txstate.index = 0;
566
567        return 0;
568    }
569}
570
571static int serpar_reset(void)
572{
573#ifdef COMPILING_ON_WINDOWS
574    FlushParallel();
575    FlushSerial();
576#else
577    Unix_ResetParallel();
578    Unix_ResetSerial();
579#endif
580
581    return serpar_set_params(&serpar_defaults);
582}
583
584static int find_baud_rate(unsigned int *speed)
585{
586    static struct
587    {
588        unsigned int baud;
589        int termiosValue;
590    } possibleBaudRates[] =
591      {
592#if defined(__hpux)
593          {115200, _B115200}, {57600, _B57600},
594#endif
595#ifdef COMPILING_ON_WINDOWS
596        {38400, CBR_38400}, {19200, CBR_19200}, {9600, CBR_9600}, {0, 0}
597#else
598        {38400, B38400}, {19200, B19200}, {9600, B9600}, {0, 0}
599#endif
600    };
601    unsigned int i;
602
603    /* look for lower or matching -- will always terminate at 0 end marker */
604    for (i = 0; possibleBaudRates[i].baud > *speed; ++i)
605        /* do nothing */
606        ;
607
608    if (possibleBaudRates[i].baud > 0)
609       *speed = possibleBaudRates[i].baud;
610
611    return possibleBaudRates[i].termiosValue;
612}
613
614static int serpar_set_params(const ParameterConfig *config)
615{
616    unsigned int speed;
617    int termios_value;
618
619#ifdef DEBUG
620    printf("serpar_set_params\n");
621#endif
622
623    if (!Angel_FindParam(AP_BAUD_RATE, config, &speed))
624    {
625#ifdef DEBUG
626        printf("speed not found in config\n");
627#endif
628        return DE_OKAY;
629    }
630
631    termios_value = find_baud_rate(&speed);
632    if (termios_value == 0)
633    {
634#ifdef DEBUG
635        printf("speed not valid: %u\n", speed);
636#endif
637        return DE_OKAY;
638    }
639
640#ifdef DEBUG
641    printf("setting speed to %u\n", speed);
642#endif
643
644#ifdef COMPILING_ON_WINDOWS
645    SetBaudRate((WORD)termios_value);
646#else
647    Unix_SetSerialBaudRate(termios_value);
648#endif
649
650    return DE_OKAY;
651}
652
653
654static int serpar_get_user_params(ParameterOptions **p_options)
655{
656#ifdef DEBUG
657    printf("serpar_get_user_params\n");
658#endif
659
660    if (user_options_set)
661    {
662        *p_options = &user_options;
663    }
664    else
665    {
666        *p_options = NULL;
667    }
668
669    return DE_OKAY;
670}
671
672
673static int serial_get_default_params( const ParameterConfig **p_config )
674{
675#ifdef DEBUG
676    printf( "serial_get_default_params\n" );
677#endif
678
679    *p_config = &serpar_defaults;
680    return DE_OKAY;
681}
682
683
684static int SerparIoctl(const int opcode, void *args)
685{
686    int ret_code;
687
688#ifdef DEBUG
689    printf("SerparIoctl: op %d arg %p\n", opcode, args ? args : "<NULL>");
690#endif
691
692    switch (opcode)
693    {
694       case DC_RESET:
695           ret_code = serpar_reset();
696           break;
697
698       case DC_SET_PARAMS:
699           ret_code = serpar_set_params((const ParameterConfig *)args);
700           break;
701
702       case DC_GET_USER_PARAMS:
703           ret_code = serpar_get_user_params((ParameterOptions **)args);
704           break;
705
706       case DC_GET_DEFAULT_PARAMS:
707           ret_code =
708               serial_get_default_params((const ParameterConfig **)args);
709           break;
710
711       default:
712           ret_code = DE_BAD_OP;
713           break;
714    }
715
716  return ret_code;
717}
718
719DeviceDescr angel_SerparDevice =
720{
721    "SERPAR",
722    SerparOpen,
723    SerparMatch,
724    SerparClose,
725    SerparRead,
726    SerparWrite,
727    SerparIoctl
728};
729
730/* EOF serpardr.c */
731