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: 1999/11/01 13:31:40 $
13 *
14 *
15 *   Project: ANGEL
16 *
17 *     Title:  Character reception engine
18 */
19
20#include <stdarg.h>    /* ANSI varargs support */
21#include "angel.h"     /* Angel system definitions */
22#include "angel_endian.h"    /* Endian independant memory access macros */
23#include "crc.h"       /* crc generation definitions and headers */
24#include "rxtx.h"
25#include "channels.h"
26#include "buffers.h"
27#ifdef TARGET
28#  include "devdriv.h"
29#endif
30#include "logging.h"
31
32static re_status unexp_stx(struct re_state *rxstate);
33static re_status unexp_etx(struct re_state *rxstate);
34
35/* bitfield for the rx_engine state */
36typedef enum rx_state_flag{
37  RST_STX,
38  RST_TYP,
39  RST_LEN,
40  RST_DAT,
41  RST_CRC,
42  RST_ETX,
43  RST_ESC = (0x1 << 0x3)
44} rx_state_flag;
45
46void Angel_RxEngineInit(const struct re_config *rxconfig,
47                        struct re_state *rxstate)
48{
49  rxstate->rx_state = RST_STX;
50  rxstate->field_c = 0;
51  rxstate->index = 0;
52  rxstate->crc = 0;
53  rxstate->error = RE_OKAY;
54  rxstate->config = rxconfig;
55}
56
57re_status Angel_RxEngine(unsigned char new_ch, struct data_packet *packet,
58                         struct re_state *rxstate)
59{
60  /*
61   * TODO: add the flow control bits in
62   * Note: We test for the data field in a seperate case so we can
63   * completely avoid entering the switch for most chars
64   */
65
66  /* see if we're expecting a escaped char */
67  if ((rxstate->rx_state & RST_ESC) == RST_ESC)
68  {
69    /* unescape the char and unset the flag*/
70    new_ch &= ~serial_ESCAPE;
71#ifdef DO_TRACE
72    __rt_trace("rxe-echar-%2x ", new_ch);
73#endif
74    rxstate->rx_state &= ~RST_ESC;
75  }
76  else if ( (1 << new_ch) & rxstate->config->esc_set )
77  {
78    /* see if the incoming char is a special one */
79    if (new_ch == rxstate->config->esc)
80    {
81#ifdef DO_TRACE
82      __rt_trace("rxe-esc ");
83#endif
84      rxstate->rx_state |= RST_ESC;
85      return RS_IN_PKT;
86    }
87    else
88    {
89      /*
90       * must be a normal packet so do some unexpected etx/stx checking
91       * we haven't been told to escape or received an escape so unless
92       * we are expecting an stx or etx then we can take the unexpected
93       * stx/etx trap
94       */
95      if ((new_ch == (rxstate->config->stx)) && (rxstate->rx_state != RST_STX))
96        return unexp_stx(rxstate);
97      if ((new_ch == (rxstate->config->etx)) && (rxstate->rx_state != RST_ETX))
98        return unexp_etx(rxstate);
99    }
100  }
101
102  if (rxstate->rx_state == RST_DAT)
103  {
104    /*
105     * do this to speed up the common case, no real penalty for
106     * other cases
107     */
108#ifdef DO_TRACE
109    __rt_trace("rxe-dat ");
110#endif
111
112    rxstate->crc = crc32(&new_ch, 1, rxstate->crc);
113    (packet->data)[rxstate->index++] = (unsigned int)new_ch & 0xff;
114
115    if (rxstate->index == packet->len)
116      rxstate->rx_state = RST_CRC;
117
118    return RS_IN_PKT;
119  }
120
121  /*
122   * Now that the common case is out of the way we can test for everything
123   * else without worrying quite so much about the speed, changing the
124   * order to len,crc,stx,etx,typ might gain a tiny bit of speed but lets
125   * leave that for the moment
126   */
127  switch (rxstate->rx_state)
128  {
129    case RST_STX:
130      if (new_ch == rxstate->config->stx)
131      {
132        rxstate->rx_state = RST_TYP;
133        rxstate->error = RE_OKAY;
134        rxstate->crc = startCRC32;
135        rxstate->index = 0;
136        return RS_IN_PKT;
137      }
138      else
139      {
140        rxstate->error = RE_OKAY;
141        return RS_WAIT_PKT;
142      }
143
144    case RST_TYP:
145      packet->type = (DevChanID)new_ch;
146      rxstate->rx_state = RST_LEN;
147      rxstate->error = RE_OKAY;
148      rxstate->field_c = 0; /* set up here for the length that follows */
149#ifdef DO_TRACE
150      __rt_trace("rxe-type-%2x ", packet->type);
151#endif
152      rxstate->crc = crc32(&new_ch, 1, rxstate->crc);
153
154      return RS_IN_PKT;
155
156    case RST_LEN:
157      rxstate->crc = crc32(&new_ch, 1, rxstate->crc);
158
159      if (rxstate->field_c++ == 0)
160      {
161        /* first length byte */
162        packet->len = ((unsigned int)new_ch) << 8;
163        return RS_IN_PKT;
164      }
165      else
166      {
167        /* got the whole legth */
168        packet->len |= new_ch;
169#ifdef DO_TRACE
170        __rt_trace("rxe-len-%4x\n", packet->len);
171#endif
172
173        /* check that the length is ok */
174        if (packet->len == 0)
175        {
176          /* empty pkt */
177          rxstate->field_c = 0;
178          rxstate->rx_state = RST_CRC;
179          return RS_IN_PKT;
180        }
181        else
182        {
183          if (packet->data == NULL)
184          {
185            /* need to alloc the data buffer */
186            if (!rxstate->config->ba_callback(
187                packet, rxstate->config->ba_data)) {
188              rxstate->rx_state = RST_STX;
189              rxstate->error = RE_INTERNAL;
190              return RS_BAD_PKT;
191            }
192          }
193
194          if (packet->len > packet->buf_len)
195          {
196            /* pkt bigger than buffer */
197            rxstate->field_c = 0;
198            rxstate->rx_state = RST_STX;
199            rxstate->error = RE_LEN;
200            return RS_BAD_PKT;
201          }
202          else
203          {
204            /* packet ok */
205            rxstate->field_c = 0;
206            rxstate->rx_state = RST_DAT;
207            return RS_IN_PKT;
208          }
209        }
210      }
211
212    case RST_DAT:
213      /* dummy case (dealt with earlier) */
214#ifdef ASSERTIONS_ENABLED
215      __rt_warning("ERROR: hit RST_dat in switch\n");
216#endif
217      rxstate->rx_state = RST_STX;
218      rxstate->error = RE_INTERNAL;
219      return RS_BAD_PKT;
220
221    case RST_CRC:
222      if (rxstate->field_c == 0)
223        packet->crc = 0;
224
225      packet->crc |= (new_ch & 0xFF) << ((3 - rxstate->field_c) * 8);
226      rxstate->field_c++;
227
228      if (rxstate->field_c == 4)
229      {
230        /* last crc field */
231        rxstate->field_c = 0;
232        rxstate->rx_state = RST_ETX;
233#ifdef DO_TRACE
234        __rt_trace("rxe-rcrc-%8x ", packet->crc);
235#endif
236      }
237
238      return RS_IN_PKT;
239
240    case RST_ETX:
241      if (new_ch == rxstate->config->etx)
242      {
243#if defined(DEBUG) && !defined(NO_PKT_DATA)
244        {
245          int c;
246# ifdef DO_TRACE
247          __rt_trace("\n");
248# endif
249          __rt_info("RXE Data =");
250          for (c=0; c < packet->len; c++)
251            __rt_info("%02x", packet->data[c]);
252          __rt_info("\n");
253        }
254#endif
255
256        /* check crc */
257        if (rxstate->crc == packet->crc)
258        {
259          /* crc ok */
260          rxstate->rx_state = RST_STX;
261          rxstate->field_c = 0;
262          return RS_GOOD_PKT;
263        }
264        else
265        {
266#ifdef ASSERTIONS_ENABLED
267          __rt_warning("Bad crc, rx calculates it should be 0x%x\n", rxstate->crc);
268#endif
269          rxstate->rx_state = RST_STX;
270          rxstate->error = RE_CRC;
271          return RS_BAD_PKT;
272        }
273      }
274      else if (new_ch == rxstate->config->stx)
275        return unexp_stx(rxstate);
276      else
277      {
278        rxstate->rx_state = RST_STX;
279        rxstate->error = RE_NETX;
280        return RS_BAD_PKT;
281      }
282
283    default:
284#ifdef ASSERTIONS_ENABLED
285      __rt_warning("ERROR fell through rxengine\n");
286#endif
287      rxstate->rx_state = RST_STX;
288      rxstate->error = RE_INTERNAL;
289      return RS_BAD_PKT;
290  }
291}
292
293static re_status unexp_stx(struct re_state *rxstate)
294{
295#ifdef ASSERTIONS_ENABLED
296  __rt_warning("Unexpected stx\n");
297#endif
298  rxstate->crc = startCRC32;
299  rxstate->index = 0;
300  rxstate->rx_state = RST_TYP;
301  rxstate->error = RE_U_STX;
302  rxstate->field_c = 0;
303  return RS_BAD_PKT;
304}
305
306static re_status unexp_etx(struct re_state *rxstate)
307{
308#ifdef ASSERTIONS_ENABLED
309  __rt_warning("Unexpected etx, rxstate: index= 0x%2x, field_c=0x%2x, state=0x%2x\n", rxstate->index, rxstate->field_c, rxstate->rx_state);
310#endif
311  rxstate->crc = 0;
312  rxstate->index = 0;
313  rxstate->rx_state = RST_STX;
314  rxstate->error = RE_U_ETX;
315  rxstate->field_c = 0;
316  return RS_BAD_PKT;
317}
318
319/*
320 * This can be used as the buffer allocation callback for the rx engine,
321 * and makes use of angel_DD_GetBuffer() [in devdrv.h].
322 *
323 * Saves duplicating this callback function in every device driver that
324 * uses the rx engine.
325 *
326 * Note that this REQUIRES that the device id is installed as ba_data
327 * in the rx engine config structure for the driver.
328 */
329bool angel_DD_RxEng_BufferAlloc( struct data_packet *packet, void *cb_data )
330{
331#ifdef TARGET
332    DeviceID devid = (DeviceID)cb_data;
333#else
334    IGNORE(cb_data);
335#endif
336
337    if ( packet->type < DC_NUM_CHANNELS )
338    {
339        /* request a buffer down from the channels layer */
340#ifdef TARGET
341        packet->data = angel_DD_GetBuffer( devid, packet->type,
342                                           packet->len              );
343#else
344        packet->data = malloc(packet->len);
345#endif
346        if ( packet->data == NULL )
347           return FALSE;
348        else
349        {
350            packet->buf_len = packet->len;
351            return TRUE;
352        }
353    }
354    else
355    {
356        /* bad type field */
357        return FALSE;
358    }
359}
360
361/* EOF rx.c */
362