summaryrefslogtreecommitdiff
path: root/gdb/rdi-share/rx.c
blob: caf833ad24b1cbc27852c865400f247bd67edeea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/* 
 * Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
 * 
 * This software may be freely used, copied, modified, and distributed
 * provided that the above copyright notice is preserved in all copies of the
 * software.
 */

/*-*-C-*-
 *
 * $Revision$
 *     $Date$
 *
 *
 *   Project: ANGEL
 *
 *     Title:  Character reception engine
 */

#include <stdarg.h>    /* ANSI varargs support */
#include "angel.h"     /* Angel system definitions */
#include "angel_endian.h"    /* Endian independant memory access macros */
#include "crc.h"       /* crc generation definitions and headers */
#include "rxtx.h"
#include "channels.h"
#include "buffers.h"
#ifdef TARGET
#  include "devdriv.h"
#endif
#include "logging.h"

static re_status unexp_stx(struct re_state *rxstate);
static re_status unexp_etx(struct re_state *rxstate);

/* bitfield for the rx_engine state */
typedef enum rx_state_flag{
  RST_STX,
  RST_TYP,
  RST_LEN,
  RST_DAT,
  RST_CRC,
  RST_ETX,
  RST_ESC = (0x1 << 0x3)
} rx_state_flag;

void Angel_RxEngineInit(const struct re_config *rxconfig,
                        struct re_state *rxstate)
{
  rxstate->rx_state = RST_STX;
  rxstate->field_c = 0;
  rxstate->index = 0;
  rxstate->crc = 0;
  rxstate->error = RE_OKAY;
  rxstate->config = rxconfig;
}

re_status Angel_RxEngine(unsigned char new_ch, struct data_packet *packet,
                         struct re_state *rxstate)
{
  /*
   * TODO: add the flow control bits in
   * Note: We test for the data field in a seperate case so we can
   * completely avoid entering the switch for most chars
   */

  /* see if we're expecting a escaped char */
  if ((rxstate->rx_state & RST_ESC) == RST_ESC)
  {
    /* unescape the char and unset the flag*/
    new_ch &= ~serial_ESCAPE;
#ifdef DO_TRACE
    __rt_trace("rxe-echar-%2x ", new_ch);
#endif
    rxstate->rx_state &= ~RST_ESC;
  }
  else if ( (1 << new_ch) & rxstate->config->esc_set )
  {
    /* see if the incoming char is a special one */
    if (new_ch == rxstate->config->esc)
    {
#ifdef DO_TRACE
      __rt_trace("rxe-esc ");
#endif
      rxstate->rx_state |= RST_ESC;
      return RS_IN_PKT;
    }
    else
    {
      /*
       * must be a normal packet so do some unexpected etx/stx checking
       * we haven't been told to escape or received an escape so unless
       * we are expecting an stx or etx then we can take the unexpected 
       * stx/etx trap
       */
      if ((new_ch == (rxstate->config->stx)) && (rxstate->rx_state != RST_STX))
        return unexp_stx(rxstate);
      if ((new_ch == (rxstate->config->etx)) && (rxstate->rx_state != RST_ETX))
        return unexp_etx(rxstate);
    }
  }

  if (rxstate->rx_state == RST_DAT)
  {
    /*
     * do this to speed up the common case, no real penalty for
     * other cases 
     */
#ifdef DO_TRACE
    __rt_trace("rxe-dat ");
#endif

    rxstate->crc = crc32(&new_ch, 1, rxstate->crc);
    (packet->data)[rxstate->index++] = (unsigned int)new_ch & 0xff;

    if (rxstate->index == packet->len)
      rxstate->rx_state = RST_CRC;

    return RS_IN_PKT;
  }

  /*
   * Now that the common case is out of the way we can test for everything
   * else without worrying quite so much about the speed, changing the
   * order to len,crc,stx,etx,typ might gain a tiny bit of speed but lets
   * leave that for the moment
   */
  switch (rxstate->rx_state)
  {
    case RST_STX:
      if (new_ch == rxstate->config->stx)
      {
        rxstate->rx_state = RST_TYP;
        rxstate->error = RE_OKAY;
        rxstate->crc = startCRC32;
        rxstate->index = 0;
        return RS_IN_PKT;
      }
      else
      {
        rxstate->error = RE_OKAY;
        return RS_WAIT_PKT;
      }

    case RST_TYP:
      packet->type = (DevChanID)new_ch;
      rxstate->rx_state = RST_LEN;
      rxstate->error = RE_OKAY;
      rxstate->field_c = 0; /* set up here for the length that follows */
#ifdef DO_TRACE 
      __rt_trace("rxe-type-%2x ", packet->type);
#endif
      rxstate->crc = crc32(&new_ch, 1, rxstate->crc);

      return RS_IN_PKT;

    case RST_LEN:
      rxstate->crc = crc32(&new_ch, 1, rxstate->crc);

      if (rxstate->field_c++ == 0)
      {
        /* first length byte */
        packet->len = ((unsigned int)new_ch) << 8;
        return RS_IN_PKT;
      }
      else
      {
        /* got the whole legth */
        packet->len |= new_ch;
#ifdef DO_TRACE
        __rt_trace("rxe-len-%4x\n", packet->len);
#endif

        /* check that the length is ok */
        if (packet->len == 0)
        {
          /* empty pkt */
          rxstate->field_c = 0;
          rxstate->rx_state = RST_CRC;
          return RS_IN_PKT;
        }
        else
        {
          if (packet->data == NULL)
          {
            /* need to alloc the data buffer */
            if (!rxstate->config->ba_callback(
                packet, rxstate->config->ba_data)) {
              rxstate->rx_state = RST_STX;
              rxstate->error = RE_INTERNAL;
              return RS_BAD_PKT;
            }
          }

          if (packet->len > packet->buf_len)
          {
            /* pkt bigger than buffer */
            rxstate->field_c = 0;
            rxstate->rx_state = RST_STX;
            rxstate->error = RE_LEN;
            return RS_BAD_PKT;
          }
          else
          {
            /* packet ok */
            rxstate->field_c = 0;
            rxstate->rx_state = RST_DAT;
            return RS_IN_PKT;
          }
        }
      }

    case RST_DAT:
      /* dummy case (dealt with earlier) */
#ifdef ASSERTIONS_ENABLED
      __rt_warning("ERROR: hit RST_dat in switch\n");
#endif
      rxstate->rx_state = RST_STX;
      rxstate->error = RE_INTERNAL;
      return RS_BAD_PKT;

    case RST_CRC:
      if (rxstate->field_c == 0)
        packet->crc = 0;

      packet->crc |= (new_ch & 0xFF) << ((3 - rxstate->field_c) * 8);
      rxstate->field_c++;

      if (rxstate->field_c == 4)
      {
        /* last crc field */
        rxstate->field_c = 0;
        rxstate->rx_state = RST_ETX;
#ifdef DO_TRACE
        __rt_trace("rxe-rcrc-%8x ", packet->crc);
#endif
      }

      return RS_IN_PKT;

    case RST_ETX:
      if (new_ch == rxstate->config->etx)
      {
#if defined(DEBUG) && !defined(NO_PKT_DATA)
        {
          int c;
# ifdef DO_TRACE
          __rt_trace("\n");
# endif
          __rt_info("RXE Data =");
          for (c=0; c < packet->len; c++)
            __rt_info("%02x", packet->data[c]);
          __rt_info("\n"); 
        }
#endif

        /* check crc */
        if (rxstate->crc == packet->crc)
        {
          /* crc ok */
          rxstate->rx_state = RST_STX;
          rxstate->field_c = 0;
          return RS_GOOD_PKT;
        }
        else
        {
#ifdef ASSERTIONS_ENABLED
          __rt_warning("Bad crc, rx calculates it should be 0x%x\n", rxstate->crc);
#endif
          rxstate->rx_state = RST_STX;
          rxstate->error = RE_CRC;
          return RS_BAD_PKT;
        }
      }
      else if (new_ch == rxstate->config->stx)
        return unexp_stx(rxstate);
      else
      {
        rxstate->rx_state = RST_STX;
        rxstate->error = RE_NETX;
        return RS_BAD_PKT;
      }

    default:
#ifdef ASSERTIONS_ENABLED
      __rt_warning("ERROR fell through rxengine\n");
#endif
      rxstate->rx_state = RST_STX;
      rxstate->error = RE_INTERNAL;
      return RS_BAD_PKT;
  }
}

static re_status unexp_stx(struct re_state *rxstate)
{
#ifdef ASSERTIONS_ENABLED
  __rt_warning("Unexpected stx\n");
#endif
  rxstate->crc = startCRC32;
  rxstate->index = 0;
  rxstate->rx_state = RST_TYP;
  rxstate->error = RE_U_STX;
  rxstate->field_c = 0;
  return RS_BAD_PKT;
}

static re_status unexp_etx(struct re_state *rxstate)
{
#ifdef ASSERTIONS_ENABLED
  __rt_warning("Unexpected etx, rxstate: index= 0x%2x, field_c=0x%2x, state=0x%2x\n", rxstate->index, rxstate->field_c, rxstate->rx_state);
#endif
  rxstate->crc = 0;
  rxstate->index = 0;
  rxstate->rx_state = RST_STX;
  rxstate->error = RE_U_ETX;
  rxstate->field_c = 0;
  return RS_BAD_PKT;
}

/*
 * This can be used as the buffer allocation callback for the rx engine,
 * and makes use of angel_DD_GetBuffer() [in devdrv.h]. 
 *
 * Saves duplicating this callback function in every device driver that
 * uses the rx engine.
 *
 * Note that this REQUIRES that the device id is installed as ba_data
 * in the rx engine config structure for the driver.
 */
bool angel_DD_RxEng_BufferAlloc( struct data_packet *packet, void *cb_data )
{
#ifdef TARGET
    DeviceID devid = (DeviceID)cb_data;
#else
    IGNORE(cb_data);
#endif

    if ( packet->type < DC_NUM_CHANNELS )
    {
        /* request a buffer down from the channels layer */
#ifdef TARGET
        packet->data = angel_DD_GetBuffer( devid, packet->type,
                                           packet->len              );
#else
        packet->data = malloc(packet->len);
#endif
        if ( packet->data == NULL )
           return FALSE;
        else
        {
            packet->buf_len = packet->len;
            return TRUE;
        }
    }
    else
    {
        /* bad type field */
        return FALSE;
    }
}

/* EOF rx.c */