Commit | Line | Data |
---|---|---|
c906108c SS |
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 | * Project: ANGEL | |
16 | * | |
17 | * Title: Character reception engine | |
18 | */ | |
19 | ||
20 | #include <stdarg.h> /* ANSI varargs support */ | |
21 | #include "angel.h" /* Angel system definitions */ | |
5c44784c | 22 | #include "angel_endian.h" /* Endian independant memory access macros */ |
c906108c SS |
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 | ||
32 | static re_status unexp_stx(struct re_state *rxstate); | |
33 | static re_status unexp_etx(struct re_state *rxstate); | |
34 | ||
35 | /* bitfield for the rx_engine state */ | |
36 | typedef 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 | ||
46 | void 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 | ||
57 | re_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 | ||
293 | static 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 | ||
306 | static 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 | */ | |
329 | bool 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 */ |