First Commit of my working state
[simh.git] / PDP11 / pdp11_xu.c
CommitLineData
196ba1fc
PH
1/* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator\r
2 ------------------------------------------------------------------------------\r
3\r
4 Copyright (c) 2003-2007, David T. Hittner\r
5\r
6 Permission is hereby granted, free of charge, to any person obtaining a\r
7 copy of this software and associated documentation files (the "Software"),\r
8 to deal in the Software without restriction, including without limitation\r
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
10 and/or sell copies of the Software, and to permit persons to whom the\r
11 Software is furnished to do so, subject to the following conditions:\r
12\r
13 The above copyright notice and this permission notice shall be included in\r
14 all copies or substantial portions of the Software.\r
15\r
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
19 THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22\r
23 Except as contained in this notice, the name of the author shall not be\r
24 used in advertising or otherwise to promote the sale, use or other dealings\r
25 in this Software without prior written authorization from the author.\r
26\r
27 ------------------------------------------------------------------------------\r
28\r
29 This DEUNA/DELUA simulation is based on:\r
30 Digital DELUA Users Guide, Part# EK-DELUA-UG-002\r
31 Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001\r
32 These manuals can be found online at:\r
33 http://www.spies.com/~aek/pdf/dec/unibus\r
34\r
35 Testing performed:\r
36 1) Receives/Transmits single packet under custom RSX driver\r
37 2) Passes RSTS 10.1 controller probe diagnostics during boot\r
38 3) VMS 7.2 on VAX780 summary:\r
39 (May/2007: WinXP x64 host; MS VC++ 2005; SIMH v3.7-0 base; WinPcap 4.0)\r
40 LAT - SET HOST/LAT in/out\r
41 DECNET - SET HOST in/out, COPY in/out\r
42 TCP/IP - PING in/out; SET HOST/TELNET in/out, COPY/FTP in/out\r
43 Clustering - Successfully clustered with AlphaVMS 8.2\r
44 4) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail\r
45\r
46 Known issues:\r
47 1) Most auxiliary commands are not implemented yet.\r
48 2) System_ID broadcast is not implemented.\r
49 3) There are residual Map_ReadB and Map_WriteB from the FvK version that\r
50 probably need to be converted to Map_ReadW and Map_WriteW calls.\r
51 4) Some jerkiness seen during interactive I/O with remote systems;\r
52 this is probably attributable to changed polling times from when\r
53 the poll duration was standardized for idling support.\r
54\r
55 ------------------------------------------------------------------------------\r
56\r
57 Modification history:\r
58\r
59 18-Jun-07 RMS Added UNIT_IDLE flag\r
60 03-May-07 DTH Added missing FC_RMAL command; cleared multicast on write\r
61 29-Oct-06 RMS Synced poll and clock\r
62 08-Dec-05 DTH Implemented ancilliary functions 022/023/024/025\r
63 18-Nov-05 DTH Corrected time between system ID packets\r
64 07-Sep-05 DTH Corrected runt packet processing (found by Tim Chapman),\r
65 Removed unused variable\r
66 16-Aug-05 RMS Fixed C++ declaration and cast problems\r
67 10-Mar-05 RMS Fixed equality test in RCSTAT (from Mark Hittinger)\r
68 16-Jan-04 DTH Added more info to SHOW MOD commands\r
69 09-Jan-04 DTH Made XU floating address so that XUB will float correctly\r
70 08-Jan-04 DTH Added system_id message\r
71 06-Jan-04 DTH Added protection against changing mac and type if attached\r
72 05-Jan-04 DTH Moved most of xu_setmac to sim_ether\r
73 Implemented auxiliary function 12/13\r
74 Added SET/SHOW XU STATS\r
75 31-Dec-03 DTH RSTS 10.1 accepts controller during boot tests\r
76 Implemented chained buffers in transmit/receive processing\r
77 29-Dec-03 DTH Primitive RSX packet sending succeeds\r
78 23-Dec-03 DTH Implemented write function\r
79 17-Dec-03 DTH Implemented read function\r
80 05-May-03 DTH Started XU simulation -\r
81 core logic pirated from unreleased FvK PDP10 variant\r
82\r
83 ------------------------------------------------------------------------------\r
84*/\r
85\r
86#include "pdp11_xu.h"\r
87\r
88extern int32 tmxr_poll, tmr_poll, clk_tps, cpu_astop;\r
89extern FILE *sim_log;\r
90\r
91t_stat xu_rd(int32* data, int32 PA, int32 access);\r
92t_stat xu_wr(int32 data, int32 PA, int32 access);\r
93t_stat xu_svc(UNIT * uptr);\r
94t_stat xu_reset (DEVICE * dptr);\r
95t_stat xu_attach (UNIT * uptr, char * cptr);\r
96t_stat xu_detach (UNIT * uptr);\r
97t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc);\r
98t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc);\r
99t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc);\r
100t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc);\r
101t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc);\r
102t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc);\r
103int32 xu_int (void);\r
104t_stat xu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r
105t_stat xu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r
106void xua_read_callback(int status);\r
107void xub_read_callback(int status);\r
108void xua_write_callback(int status);\r
109void xub_write_callback(int status);\r
110void xu_setint (CTLR* xu);\r
111void xu_clrint (CTLR* xu);\r
112void xu_process_receive(CTLR* xu);\r
113void xu_dump_rxring(CTLR* xu);\r
114void xu_dump_txring(CTLR* xu);\r
115\r
116DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr,\r
1171, IVCL (XU), VEC_XU, {&xu_int} };\r
118\r
119UNIT xua_unit[] = {\r
120 { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */\r
121};\r
122\r
123struct xu_device xua = {\r
124 xua_read_callback, /* read callback routine */\r
125 xua_write_callback, /* write callback routine */\r
126 {0x08, 0x00, 0x2B, 0xCC, 0xDD, 0xEE}, /* mac */\r
127 XU_T_DELUA /* type */\r
128 };\r
129\r
130MTAB xu_mod[] = {\r
131#if defined (VM_PDP11)\r
132 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",\r
133 &set_addr, &show_addr, NULL },\r
134#else\r
135 { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL,\r
136 NULL, &show_addr, NULL },\r
137#endif\r
138 { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL,\r
139 NULL, &show_vec, NULL },\r
140 { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx",\r
141 &xu_setmac, &xu_showmac, NULL },\r
142 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH",\r
143 NULL, &eth_show, NULL },\r
144 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS",\r
145 &xu_set_stats, &xu_show_stats, NULL },\r
146 { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}",\r
147 &xu_set_type, &xu_show_type, NULL },\r
148 { 0 },\r
149};\r
150\r
151REG xua_reg[] = {\r
152 { NULL } };\r
153\r
154DEBTAB xu_debug[] = {\r
155 {"TRACE", DBG_TRC},\r
156 {"WARN", DBG_WRN},\r
157 {"REG", DBG_REG},\r
158 {"PACKET", DBG_PCK},\r
159 {"ETH", DBG_ETH},\r
160 {0}\r
161};\r
162\r
163\r
164DEVICE xu_dev = {\r
165 "XU", xua_unit, xua_reg, xu_mod,\r
166 1, XU_RDX, 8, 1, XU_RDX, 8,\r
167 &xu_ex, &xu_dep, &xu_reset,\r
168 NULL, &xu_attach, &xu_detach,\r
169 &xua_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,\r
170 0, xu_debug\r
171 };\r
172\r
173\r
174/* XUB does not exist in the PDP10 simulation */\r
175#if defined(IOBA_XUB)\r
176\r
177DIB xub_dib = { IOBA_XUB, IOLN_XUB, &xu_rd, &xu_wr,\r
178 1, IVCL (XU), 0, { &xu_int } };\r
179\r
180UNIT xub_unit[] = {\r
181 { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */\r
182};\r
183\r
184struct xu_device xub = {\r
185 xub_read_callback, /* read callback routine */\r
186 xub_write_callback, /* write callback routine */\r
187 {0x08, 0x00, 0x2B, 0xDD, 0xEE, 0xFF}, /* mac */\r
188 XU_T_DELUA /* type */\r
189 };\r
190\r
191REG xub_reg[] = {\r
192 { NULL } };\r
193\r
194DEVICE xub_dev = {\r
195 "XUB", xub_unit, xub_reg, xu_mod,\r
196 1, XU_RDX, 8, 1, XU_RDX, 8,\r
197 &xu_ex, &xu_dep, &xu_reset,\r
198 NULL, &xu_attach, &xu_detach,\r
199 &xub_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,\r
200 0, xu_debug\r
201};\r
202\r
203#define XU_MAX_CONTROLLERS 2\r
204CTLR xu_ctrl[] = {\r
205 {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */\r
206 ,{&xub_dev, xub_unit, &xub_dib, &xub} /* XUB controller */\r
207};\r
208#else /* IOBA_XUB */\r
209#define XU_MAX_CONTROLLERS 1\r
210CTLR xu_ctrl[] = {\r
211 {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */\r
212}; \r
213#endif /* IOBA_XUB */\r
214\r
215/*============================================================================*/\r
216\r
217/* Multicontroller support */\r
218\r
219CTLR* xu_unit2ctlr(UNIT* uptr)\r
220{\r
221 int i;\r
222 unsigned int j;\r
223 for (i=0; i<XU_MAX_CONTROLLERS; i++)\r
224 for (j=0; j<xu_ctrl[i].dev->numunits; j++)\r
225 if (&xu_ctrl[i].unit[j] == uptr)\r
226 return &xu_ctrl[i];\r
227 /* not found */\r
228 return 0;\r
229}\r
230\r
231CTLR* xu_dev2ctlr(DEVICE* dptr)\r
232{\r
233 int i;\r
234 for (i=0; i<XU_MAX_CONTROLLERS; i++)\r
235 if (xu_ctrl[i].dev == dptr)\r
236 return &xu_ctrl[i];\r
237 /* not found */\r
238 return 0;\r
239}\r
240\r
241CTLR* xu_pa2ctlr(uint32 PA)\r
242{\r
243 int i;\r
244 for (i=0; i<XU_MAX_CONTROLLERS; i++)\r
245 if ((PA >= xu_ctrl[i].dib->ba) && (PA < (xu_ctrl[i].dib->ba + xu_ctrl[i].dib->lnt)))\r
246 return &xu_ctrl[i];\r
247 /* not found */\r
248 return 0;\r
249}\r
250\r
251/*============================================================================*/\r
252\r
253/* stop simh from reading non-existant unit data stream */\r
254t_stat xu_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw)\r
255{\r
256 return SCPE_NOFNC;\r
257}\r
258\r
259/* stop simh from writing non-existant unit data stream */\r
260t_stat xu_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw)\r
261{\r
262 return SCPE_NOFNC;\r
263}\r
264\r
265t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc)\r
266{\r
267 CTLR* xu = xu_unit2ctlr(uptr);\r
268 char buffer[20];\r
269\r
270 eth_mac_fmt((ETH_MAC*)xu->var->mac, buffer);\r
271 fprintf(st, "MAC=%s", buffer);\r
272 return SCPE_OK;\r
273}\r
274\r
275t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc)\r
276{\r
277 t_stat status;\r
278 CTLR* xu = xu_unit2ctlr(uptr);\r
279\r
280 if (!cptr) return SCPE_IERR;\r
281 if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
282 status = eth_mac_scan(&xu->var->mac, cptr);\r
283 return status;\r
284}\r
285\r
286t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc)\r
287{\r
288 CTLR* xu = xu_unit2ctlr(uptr);\r
289\r
290 /* set stats to zero, regardless of passed parameter */\r
291 memset(&xu->var->stats, 0, sizeof(struct xu_stats));\r
292 return SCPE_OK;\r
293}\r
294\r
295t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc)\r
296{\r
297 char* fmt = " %-24s%d\n";\r
298 CTLR* xu = xu_unit2ctlr(uptr);\r
299 struct xu_stats* stats = &xu->var->stats;\r
300\r
301 fprintf(st, "Ethernet statistics:\n");\r
302 fprintf(st, fmt, "Seconds since cleared:", stats->secs); \r
303 fprintf(st, fmt, "Recv frames:", stats->frecv);\r
304 fprintf(st, fmt, "Recv dbytes:", stats->rbytes);\r
305 fprintf(st, fmt, "Xmit frames:", stats->ftrans);\r
306 fprintf(st, fmt, "Xmit dbytes:", stats->tbytes);\r
307 fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv);\r
308 fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes);\r
309 fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans);\r
310 fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes);\r
311 return SCPE_OK;\r
312}\r
313\r
314t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc)\r
315{\r
316 CTLR* xu = xu_unit2ctlr(uptr);\r
317 fprintf(st, "type=");\r
318 switch (xu->var->type) {\r
319 case XU_T_DEUNA: fprintf(st, "DEUNA"); break;\r
320 case XU_T_DELUA: fprintf(st, "DELUA"); break;\r
321 }\r
322 return SCPE_OK;\r
323}\r
324\r
325t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc)\r
326{\r
327 CTLR* xu = xu_unit2ctlr(uptr);\r
328 if (!cptr) return SCPE_IERR;\r
329 if (uptr->flags & UNIT_ATT) return SCPE_ALATT;\r
330\r
331 /* this assumes that the parameter has already been upcased */\r
332 if (!strcmp(cptr, "DEUNA")) xu->var->type = XU_T_DEUNA;\r
333 else if (!strcmp(cptr, "DELUA")) xu->var->type = XU_T_DELUA;\r
334 else return SCPE_ARG;\r
335\r
336 return SCPE_OK;\r
337}\r
338\r
339/*============================================================================*/\r
340\r
341void upd_stat16(uint16* stat, uint16 add)\r
342{\r
343 *stat += add;\r
344 /* did stat roll over? latches at maximum */\r
345 if (*stat < add)\r
346 *stat = 0xFFFF;\r
347}\r
348\r
349void upd_stat32(uint32* stat, uint32 add)\r
350{\r
351 *stat += add;\r
352 /* did stat roll over? latches at maximum */\r
353 if (*stat < add)\r
354 *stat = 0xFFFFFFFF;\r
355}\r
356\r
357void bit_stat16(uint16* stat, uint16 bits)\r
358{\r
359 *stat |= bits;\r
360}\r
361\r
362t_stat xu_process_local (CTLR* xu, ETH_PACK* pack)\r
363{\r
364 return SCPE_NOFNC; /* not implemented yet */\r
365}\r
366\r
367void xu_read_callback(CTLR* xu, int status)\r
368{\r
369 /* process any packets locally that can be */\r
370 status = xu_process_local (xu, &xu->var->read_buffer);\r
371\r
372 /* add packet to read queue */\r
373 if (status != SCPE_OK)\r
374 ethq_insert(&xu->var->ReadQ, 2, &xu->var->read_buffer, 0);\r
375}\r
376\r
377void xua_read_callback(int status)\r
378{\r
379 xu_read_callback(&xu_ctrl[0], status);\r
380}\r
381\r
382void xub_read_callback(int status)\r
383{\r
384 xu_read_callback(&xu_ctrl[1], status);\r
385}\r
386\r
387t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id)\r
388{\r
389 static uint16 receipt = 0;\r
390 ETH_PACK system_id;\r
391 uint8* const msg = &system_id.msg[0];\r
392 t_stat status;\r
393\r
394 sim_debug(DBG_TRC, xu->dev, "xu_system_id()\n");\r
395 memset (&system_id, 0, sizeof(system_id));\r
396 memcpy (&msg[0], dest, sizeof(ETH_MAC));\r
397 memcpy (&msg[6], xu->var->setup.macs[0], sizeof(ETH_MAC));\r
398 msg[12] = 0x60; /* type */\r
399 msg[13] = 0x02; /* type */\r
400 msg[14] = 0x1C; /* character count */\r
401 msg[15] = 0x00; /* character count */\r
402 msg[16] = 0x07; /* code */\r
403 msg[17] = 0x00; /* zero pad */\r
404 if (receipt_id) {\r
405 msg[18] = receipt_id & 0xFF; /* receipt number */\r
406 msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */\r
407 } else {\r
408 msg[18] = receipt & 0xFF; /* receipt number */\r
409 msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */\r
410 }\r
411\r
412 /* MOP VERSION */\r
413 msg[20] = 0x01; /* type */\r
414 msg[21] = 0x00; /* type */\r
415 msg[22] = 0x03; /* length */\r
416 msg[23] = 0x03; /* version */\r
417 msg[24] = 0x00; /* eco */\r
418 msg[25] = 0x00; /* user eco */\r
419\r
420 /* FUNCTION */\r
421 msg[26] = 0x02; /* type */\r
422 msg[27] = 0x00; /* type */\r
423 msg[28] = 0x02; /* length */\r
424 msg[29] = 0x05; /* value 1 */\r
425 msg[30] = 0x00; /* value 2 */\r
426\r
427 /* HARDWARE ADDRESS */\r
428 msg[31] = 0x07; /* type */\r
429 msg[32] = 0x00; /* type */\r
430 msg[33] = 0x06; /* length */\r
431 memcpy (&msg[34], xu->var->mac, sizeof(ETH_MAC)); /* ROM address */\r
432\r
433 /* DEVICE TYPE */\r
434 msg[40] = 0x64; /* type */\r
435 msg[41] = 0x00; /* type */\r
436 msg[42] = 0x01; /* length */\r
437 if (xu->var->type == XU_T_DEUNA)\r
438 msg[43] = 1; /* value (1=DEUNA) */\r
439 else\r
440 msg[43] = 11; /* value (11=DELUA) */\r
441\r
442 /* write system id */\r
443 system_id.len = 60;\r
444 status = eth_write(xu->var->etherface, &system_id, NULL);\r
445\r
446 return status;\r
447}\r
448\r
449t_stat xu_svc(UNIT* uptr)\r
450{\r
451 int queue_size;\r
452 t_stat status;\r
453 CTLR* xu = xu_unit2ctlr(uptr);\r
454 const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00};\r
455 const int one_second = clk_tps * tmr_poll;\r
456\r
457 /* First pump any queued packets into the system */\r
458 if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING))\r
459 xu_process_receive(xu);\r
460\r
461 /* Now read and queue packets that have arrived */\r
462 /* This is repeated as long as they are available and we have room */\r
463 do\r
464 {\r
465 queue_size = xu->var->ReadQ.count;\r
466 /* read a packet from the ethernet - processing is via the callback */\r
467 status = eth_read (xu->var->etherface, &xu->var->read_buffer, xu->var->rcallback);\r
468 } while (queue_size != xu->var->ReadQ.count);\r
469\r
470 /* Now pump any still queued packets into the system */\r
471 if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING))\r
472 xu_process_receive(xu);\r
473\r
474 /* send identity packet when timer expires */\r
475 if (--xu->var->idtmr <= 0) {\r
476 if ((xu->var->mode & MODE_DMNT) == 0) /* if maint msg is not disabled */\r
477 status = xu_system_id(xu, mop_multicast, 0); /* then send ID packet */\r
478 xu->var->idtmr = XU_ID_TIMER_VAL * one_second; /* reset timer */\r
479 }\r
480\r
481 /* has one second timer expired? if so, update stats and reset timer */\r
482 if (++xu->var->sectmr >= XU_SERVICE_INTERVAL) {\r
483 upd_stat16 (&xu->var->stats.secs, 1);\r
484 xu->var->sectmr = 0;\r
485 }\r
486\r
487 /* resubmit service timer if controller not halted */\r
488 switch (xu->var->pcsr1 & PCSR1_STATE) {\r
489 case STATE_READY:\r
490 case STATE_RUNNING:\r
491 sim_activate(&xu->unit[0], tmxr_poll);\r
492 break;\r
493 };\r
494\r
495 return SCPE_OK;\r
496}\r
497\r
498void xu_write_callback (CTLR* xu, int status)\r
499{\r
500 xu->var->write_buffer.status = status;\r
501}\r
502\r
503void xua_write_callback (int status)\r
504{\r
505 xu_write_callback(&xu_ctrl[0], status);\r
506}\r
507\r
508void xub_write_callback (int status)\r
509{\r
510 xu_write_callback(&xu_ctrl[1], status);\r
511}\r
512\r
513void xu_setclrint(CTLR* xu, int32 bits)\r
514{\r
515 if (xu->var->pcsr0 & 0xFF00) { /* if any interrupt bits on, */\r
516 xu->var->pcsr0 |= PCSR0_INTR; /* turn master bit on */\r
517 xu_setint(xu); /* and trigger interrupt */\r
518 } else {\r
519 xu->var->pcsr0 &= ~PCSR0_INTR; /* ... or off */\r
520 xu_clrint(xu); /* and clear interrupt if needed*/\r
521 }\r
522}\r
523\r
524t_stat xu_sw_reset (CTLR* xu)\r
525{\r
526 t_stat status;\r
527\r
528 sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n");\r
529\r
530 /* Clear the registers. */\r
531 xu->var->pcsr0 = PCSR0_DNI | PCSR0_INTR;\r
532 xu->var->pcsr1 = STATE_READY;\r
533 switch (xu->var->type) {\r
534 case XU_T_DELUA:\r
535 xu->var->pcsr1 |= TYPE_DELUA;\r
536 break;\r
537 case XU_T_DEUNA:\r
538 xu->var->pcsr1 |= TYPE_DEUNA;\r
539 if (!xu->var->etherface) /* if not attached, set transceiver powerfail */\r
540 xu->var->pcsr1 |= PCSR1_XPWR;\r
541 break;\r
542 }\r
543 xu->var->pcsr2 = 0;\r
544 xu->var->pcsr3 = 0;\r
545\r
546 /* Clear the parameters. */\r
547 xu->var->mode = 0;\r
548 xu->var->pcbb = 0;\r
549 xu->var->stat = 0;\r
550\r
551 /* clear read queue */\r
552 ethq_clear(&xu->var->ReadQ);\r
553\r
554 /* clear setup info */\r
555 memset(&xu->var->setup, 0, sizeof(struct xu_setup));\r
556\r
557 /* clear network statistics */\r
558 memset(&xu->var->stats, 0, sizeof(struct xu_stats));\r
559\r
560 /* reset ethernet interface */\r
561 memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC));\r
562 xu->var->setup.mac_count = 1;\r
563 if (xu->var->etherface)\r
564 status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,\r
565 &xu->var->mac, xu->var->setup.multicast,\r
566 xu->var->setup.promiscuous);\r
567\r
568 /* activate device if not disabled */\r
569 if ((xu->dev->flags & DEV_DIS) == 0) {\r
570 sim_activate_abs(&xu->unit[0], clk_cosched (tmxr_poll));\r
571 }\r
572\r
573 /* clear load_server address */\r
574 memset(xu->var->load_server, 0, sizeof(ETH_MAC));\r
575\r
576 return SCPE_OK;\r
577}\r
578\r
579/* Reset device. */\r
580t_stat xu_reset(DEVICE* dptr)\r
581{\r
582 t_stat status;\r
583 CTLR* xu = xu_dev2ctlr(dptr);\r
584\r
585 sim_debug(DBG_TRC, xu->dev, "xu_reset()\n");\r
586 /* init read queue (first time only) */\r
587 status = ethq_init (&xu->var->ReadQ, XU_QUE_MAX);\r
588 if (status != SCPE_OK)\r
589 return status;\r
590\r
591 /* software reset controller */\r
592 xu_sw_reset(xu);\r
593\r
594 return SCPE_OK;\r
595}\r
596\r
597\r
598/* Perform one of the defined ancillary functions. */\r
599int32 xu_command(CTLR* xu)\r
600{\r
601 uint32 udbb;\r
602 int fnc, mtlen, i, j;\r
603 uint16 value, pltlen;\r
604 t_stat status, rstatus, wstatus, wstatus2, wstatus3;\r
605 struct xu_stats* stats = &xu->var->stats;\r
606 uint16* udb = xu->var->udb;\r
607 uint16* mac_w = (uint16*) xu->var->mac;\r
608 static const ETH_MAC zeros = {0,0,0,0,0,0};\r
609 static const ETH_MAC mcast_load_server = {0xAB, 0x00, 0x00, 0x01, 0x00, 0x00};\r
610 static char* command[] = {\r
611 "NO-OP",\r
612 "Start Microaddress",\r
613 "Read Default Physical Address",\r
614 "NO-OP",\r
615 "Read Physical Address",\r
616 "Write Physical Address",\r
617 "Read Multicast Address List",\r
618 "Write Multicast Address List",\r
619 "Read Descriptor Ring Format",\r
620 "Write Descriptor Ring Format",\r
621 "Read Counters",\r
622 "Read/Clear Counters",\r
623 "Read Mode Register",\r
624 "Write Mode Register",\r
625 "Read Status",\r
626 "Read/Clear Status",\r
627 "Dump Internal Memory",\r
628 "Load Internal Memory",\r
629 "Read System ID",\r
630 "Write System ID",\r
631 "Read Load Server Address",\r
632 "Write Load Server Address"\r
633 };\r
634\r
635 /* Grab the PCB from the host. */\r
636 rstatus = Map_ReadW(xu->var->pcbb, 8, xu->var->pcb);\r
637 if (rstatus != 0)\r
638 return PCSR0_PCEI + 1;\r
639\r
640 /* High 8 bits are defined as MBZ. */\r
641 if (xu->var->pcb[0] & 0177400)\r
642 return PCSR0_PCEI;\r
643\r
644 /* Decode the function to be performed. */\r
645 fnc = xu->var->pcb[0] & 0377;\r
646 sim_debug(DBG_TRC, xu->dev, "xu_command(), Command: %s [0%o]\n", command[fnc], fnc);\r
647\r
648 switch (fnc) {\r
649 case FC_NOOP:\r
650 break;\r
651\r
652 case FC_RDPA: /* read default physical address */\r
653 wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->mac);\r
654 if (wstatus)\r
655 return PCSR0_PCEI + 1;\r
656 break;\r
657\r
658 case FC_RPA: /* read current physical address */\r
659 wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]);\r
660 if (wstatus)\r
661 return PCSR0_PCEI + 1;\r
662 break;\r
663\r
664 case FC_WPA: /* write current physical address */\r
665 rstatus = Map_ReadB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]);\r
666 if (xu->var->pcb[1] & 1)\r
667 return PCSR0_PCEI;\r
668 break;\r
669\r
670 case FC_RMAL: /* read multicast address list */\r
671 mtlen = (xu->var->pcb[2] & 0xFF00) >> 8;\r
672 udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16);\r
673 wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[1]);\r
674 break;\r
675\r
676 case FC_WMAL: /* write multicast address list */\r
677 mtlen = (xu->var->pcb[2] & 0xFF00) >> 8;\r
678sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen);\r
679 if (mtlen > 10)\r
680 return PCSR0_PCEI;\r
681 udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16);\r
682 /* clear existing multicast list */\r
683 for (i=1; i<XU_FILTER_MAX; i++) {\r
684 for (j=0; j<6; j++)\r
685 xu->var->setup.macs[i][j] = 0;\r
686 }\r
687 /* get multicast list from host */\r
688 rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[1]);\r
689 if (rstatus == 0) {\r
690 xu->var->setup.mac_count = mtlen + 1;\r
691 status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,\r
692 xu->var->setup.macs, xu->var->setup.multicast,\r
693 xu->var->setup.promiscuous);\r
694 } else {\r
695 xu->var->pcsr0 |= PCSR0_PCEI;\r
696 }\r
697 break;\r
698\r
699 case FC_RRF: /* read ring format */\r
700 if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) \r
701 return PCSR0_PCEI;\r
702 xu->var->udb[0] = xu->var->tdrb & 0177776;\r
703 xu->var->udb[1] = (xu->var->telen << 8) + ((xu->var->tdrb >> 16) & 3);\r
704 xu->var->udb[2] = xu->var->trlen;\r
705 xu->var->udb[3] = xu->var->rdrb & 0177776;\r
706 xu->var->udb[4] = (xu->var->relen << 8) + ((xu->var->rdrb >> 16) & 3);\r
707 xu->var->udb[5] = xu->var->rrlen;\r
708\r
709 /* Write UDB to host memory. */\r
710 udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);\r
711 wstatus = Map_WriteW(udbb, 12, xu->var->pcb);\r
712 if (wstatus != 0)\r
713 return PCSR0_PCEI+1;\r
714 break;\r
715\r
716 case FC_WRF: /* write ring format */\r
717 if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) \r
718 return PCSR0_PCEI;\r
719 if ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)\r
720 return PCSR0_PCEI;\r
721\r
722 /* Read UDB into local memory. */\r
723 udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);\r
724 rstatus = Map_ReadW(udbb, 12, xu->var->udb);\r
725 if (rstatus)\r
726 return PCSR0_PCEI+1;\r
727\r
728 if ((xu->var->udb[0] & 1) || (xu->var->udb[1] & 0374) ||\r
729 (xu->var->udb[3] & 1) || (xu->var->udb[4] & 0374) ||\r
730 (xu->var->udb[5] < 2)) {\r
731 return PCSR0_PCEI;\r
732 }\r
733\r
734 xu->var->tdrb = ((xu->var->udb[1] & 3) << 16) + (xu->var->udb[0] & 0177776);\r
735 xu->var->telen = (xu->var->udb[1] >> 8) & 0377;\r
736 xu->var->trlen = xu->var->udb[2];\r
737 xu->var->rdrb = ((xu->var->udb[4] & 3) << 16) + (xu->var->udb[3] & 0177776);\r
738 xu->var->relen = (xu->var->udb[4] >> 8) & 0377;\r
739 xu->var->rrlen = xu->var->udb[5];\r
740 xu->var->rxnext = 0;\r
741 xu->var->txnext = 0;\r
742// xu_dump_rxring(xu);\r
743// xu_dump_txring(xu);\r
744\r
745 break;\r
746\r
747 case FC_RDCTR: /* read counters */\r
748 case FC_RDCLCTR: /* read and clear counters */\r
749 /* prepare udb for stats transfer */\r
750 memset(xu->var->udb, 0, sizeof(xu->var->udb));\r
751\r
752 /* place stats in udb */\r
753 udb[0] = 68; /* udb length */\r
754 udb[1] = stats->secs; /* seconds since zeroed */\r
755 udb[2] = stats->frecv & 0xFFFF; /* frames received <15:00> */\r
756 udb[3] = stats->frecv >> 16; /* frames received <31:16> */\r
757 udb[4] = stats->mfrecv & 0xFFFF; /* multicast frames received <15:00> */\r
758 udb[5] = stats->mfrecv >> 16; /* multicast frames received <31:16> */\r
759 udb[6] = stats->rxerf; /* receive error status bits */\r
760 udb[7] = stats->frecve; /* frames received with error */\r
761 udb[8] = stats->rbytes & 0xFFFF; /* data bytes received <15:00> */\r
762 udb[9] = stats->rbytes >> 16; /* data bytes received <31:16> */\r
763 udb[10] = stats->mrbytes & 0xFFFF; /* multicast data bytes received <15:00> */\r
764 udb[11] = stats->mrbytes >> 16; /* multicast data bytes received <31:16> */\r
765 udb[12] = stats->rlossi; /* received frames lost - internal buffer */\r
766 udb[13] = stats->rlossl; /* received frames lost - local buffer */\r
767 udb[14] = stats->ftrans & 0xFFFF; /* frames transmitted <15:00> */\r
768 udb[15] = stats->ftrans >> 16; /* frames transmitted <31:16> */\r
769 udb[16] = stats->mftrans & 0xFFFF; /* multicast frames transmitted <15:00> */\r
770 udb[17] = stats->mftrans >> 16; /* multicast frames transmitted <31:16> */\r
771 udb[18] = stats->ftrans3 & 0xFFFF; /* frames transmitted 3+ tries <15:00> */\r
772 udb[19] = stats->ftrans3 >> 16; /* frames transmitted 3+ tries <31:16> */\r
773 udb[20] = stats->ftrans2 & 0xFFFF; /* frames transmitted 2 tries <15:00> */\r
774 udb[21] = stats->ftrans2 >> 16; /* frames transmitted 2 tries <31:16> */\r
775 udb[22] = stats->ftransd & 0xFFFF; /* frames transmitted deferred <15:00> */\r
776 udb[23] = stats->ftransd >> 16; /* frames transmitted deferred <31:16> */\r
777 udb[24] = stats->tbytes & 0xFFFF; /* data bytes transmitted <15:00> */\r
778 udb[25] = stats->tbytes >> 16; /* data bytes transmitted <31:16> */\r
779 udb[26] = stats->mtbytes & 0xFFFF; /* multicast data bytes transmitted <15:00> */\r
780 udb[27] = stats->mtbytes >> 16; /* multicast data bytes transmitted <31:16> */\r
781 udb[28] = stats->txerf; /* transmit frame error status bits */\r
782 udb[29] = stats->ftransa; /* transmit frames aborted */\r
783 udb[30] = stats->txccf; /* transmit collision check failure */\r
784 udb[31] = 0; /* MBZ */\r
785 udb[32] = stats->porterr; /* port driver error */\r
786 udb[33] = stats->bablcnt; /* babble counter */\r
787\r
788 /* transfer udb to host */\r
789 udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);\r
790 wstatus = Map_WriteW(udbb, 68, xu->var->udb);\r
791 if (wstatus) {\r
792 xu->var->pcsr0 |= PCSR0_PCEI;\r
793 }\r
794\r
795 /* if clear function, clear network stats */\r
796 if (fnc == FC_RDCLCTR)\r
797 memset(stats, 0, sizeof(struct xu_stats));\r
798 break;\r
799\r
800 case FC_RMODE: /* read mode register */\r
801 value = xu->var->mode;\r
802 wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value);\r
803 if (wstatus)\r
804 return PCSR0_PCEI + 1;\r
805 break;\r
806\r
807 case FC_WMODE: /* write mode register */\r
808 value = xu->var->mode;\r
809 xu->var->mode = xu->var->pcb[1];\r
810sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode);\r
811\r
812 /* set promiscuous and multicast flags */\r
813 xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0;\r
814 xu->var->setup.multicast = (xu->var->mode & MODE_ENAL) ? 1 : 0;\r
815\r
816 /* if promiscuous or multicast flags changed, change filter */\r
817 if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL))\r
818 status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,\r
819 &xu->var->mac, xu->var->setup.multicast,\r
820 xu->var->setup.promiscuous);\r
821 break;\r
822\r
823 case FC_RSTAT: /* read extended status */\r
824 case FC_RCSTAT: /* read and clear extended status */\r
825 value = xu->var->stat;\r
826 wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value);\r
827 value = 10;\r
828 wstatus2 = Map_WriteW(xu->var->pcbb+4, 2, &value);\r
829 value = 32;\r
830 wstatus3 = Map_WriteW(xu->var->pcbb+6, 2, &value);\r
831 if (wstatus + wstatus2 + wstatus3)\r
832 return PCSR0_PCEI + 1;\r
833\r
834 if (fnc == FC_RCSTAT)\r
835 xu->var->stat &= 0377; /* clear high byte */\r
836 break;\r
837\r
838 case FC_RSID: /* read system id parameters */\r
839 /* prepare udb for transfer */\r
840 memset(xu->var->udb, 0, sizeof(xu->var->udb));\r
841\r
842 udb[11] = 0x260; /* type */\r
843 udb[12] = 28/* + parameter size */; /* ccount */\r
844 udb[13] = 7; /* code */\r
845 udb[14] = 0; /* recnum */\r
846 /* mop information */\r
847 udb[15] = 1; /* mvtype */\r
848 udb[16] = 0x0303; /* mvver + mvlen */\r
849 udb[17] = 0; /* mvueco + mveco */\r
850 /* function information */\r
851 udb[18] = 2; /* ftype */\r
852 udb[19] = 0x0502; /* fval1 + flen */\r
853 udb[20] = 0x0700; /* hatype<07:00> + fval2 */\r
854 udb[21] = 0x0600; /* halen + hatype<15:08> */\r
855 /* built-in MAC address */\r
856 udb[21] = mac_w[0]; /* HA<15:00> */\r
857 udb[22] = mac_w[1]; /* HA<31:16> */\r
858 udb[23] = mac_w[2]; /* HA<47:32> */\r
859 udb[24] = 0x64; /* dtype */\r
860 udb[25] = (11 << 8) + 1; /* dvalue + dlen */\r
861 \r
862 /* transfer udb to host */\r
863 udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);\r
864 wstatus = Map_WriteW(udbb, 52, xu->var->udb);\r
865 if (wstatus)\r
866 xu->var->pcsr0 |= PCSR0_PCEI;\r
867 break;\r
868\r
869 case FC_WSID: /* write system id parameters */\r
870 /* get udb base */\r
871 udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);\r
872 /* get udb length */\r
873 pltlen = xu->var->pcb[3];\r
874\r
875 /* transfer udb from host */\r
876 rstatus = Map_ReadW(udbb, pltlen * 2, xu->var->udb);\r
877 if (rstatus)\r
878 return PCSR0_PCEI + 1;\r
879\r
880 /* decode and store system ID fields , if we ever need to.\r
881 for right now, just return "success" */\r
882\r
883 break;\r
884\r
885 case FC_RLSA: /* read load server address */\r
886 if (memcmp(xu->var->load_server, zeros, sizeof(ETH_MAC))) {\r
887 /* not set, use default multicast load address */\r
888 wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*) mcast_load_server);\r
889 } else {\r
890 /* is set, use load_server */\r
891 wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->load_server);\r
892 }\r
893 if (wstatus)\r
894 return PCSR0_PCEI + 1;\r
895 break;\r
896\r
897\r
898 case FC_WLSA: /* write load server address */\r
899 rstatus = Map_ReadB(xu->var->pcbb + 2, 6, xu->var->load_server);\r
900 if (rstatus)\r
901 return PCSR0_PCEI + 1;\r
902 break;\r
903\r
904 default: /* Unknown (unimplemented) command. */\r
905 printf("%s: unknown ancilliary command 0%o requested !\n", xu->dev->name, fnc);\r
906 return PCSR0_PCEI;\r
907 break;\r
908\r
909 } /* switch */\r
910\r
911 return PCSR0_DNI;\r
912}\r
913\r
914/* Transfer received packets into receive ring. */\r
915void xu_process_receive(CTLR* xu)\r
916{\r
917 uint32 segb, ba;\r
918 int slen, wlen, off;\r
919 t_stat rstatus, wstatus;\r
920 ETH_ITEM* item = 0;\r
921 int state = xu->var->pcsr1 & PCSR1_STATE;\r
922 int no_buffers = xu->var->pcsr0 & PCSR0_RCBI;\r
923\r
924 sim_debug(DBG_TRC, xu->dev, "xu_process_receive(), buffers: %d\n", xu->var->rrlen);\r
925\r
926/* xu_dump_rxring(xu); /* debug receive ring */\r
927\r
928 /* process only when in the running state, and host buffers are available */\r
929 if ((state != STATE_RUNNING) || no_buffers)\r
930 return;\r
931\r
932 /* check read queue for buffer loss */\r
933 if (xu->var->ReadQ.loss) {\r
934 upd_stat16(&xu->var->stats.rlossl, (uint16) xu->var->ReadQ.loss);\r
935 xu->var->ReadQ.loss = 0;\r
936 }\r
937\r
938 /* while there are still packets left to process in the queue */\r
939 while (xu->var->ReadQ.count > 0) {\r
940\r
941 /* get next receive buffer */\r
942 ba = xu->var->rdrb + (xu->var->relen * 2) * xu->var->rxnext;\r
943 rstatus = Map_ReadW (ba, 8, xu->var->rxhdr);\r
944 if (rstatus) {\r
945 /* tell host bus read failed */\r
946 xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;\r
947 xu->var->pcsr0 |= PCSR0_SERI;\r
948 break;\r
949 }\r
950\r
951 /* if buffer not owned by controller, exit [at end of ring] */\r
952 if (!(xu->var->rxhdr[2] & RXR_OWN)) {\r
953 /* tell the host there are no more buffers */\r
954 /* xu->var->pcsr0 |= PCSR0_RCBI; */ /* I don't think this is correct 08-dec-2005 dth */\r
955 break;\r
956 }\r
957\r
958 /* set buffer length and address */\r
959 slen = xu->var->rxhdr[0];\r
960 segb = xu->var->rxhdr[1] + ((xu->var->rxhdr[2] & 3) << 16);\r
961\r
962 /* get first packet from receive queue */\r
963 if (!item) {\r
964 item = &xu->var->ReadQ.item[xu->var->ReadQ.head];\r
965 /*\r
966 * 2.11BSD does not seem to like small packets.\r
967 * For example.. an incoming ARP packet is:\r
968 * ETH dstaddr [6]\r
969 * ETH srcaddr [6]\r
970 * ETH type [2]\r
971 * ARP arphdr [8]\r
972 * ARP dstha [6]\r
973 * ARP dstpa [4]\r
974 * ARP srcha [6]\r
975 * ARP srcpa [4]\r
976 *\r
977 * for a total of 42 bytes. According to the 2.11BSD\r
978 * driver for DEUNA (if_de.c), this is not a legal size,\r
979 * and the packet is dropped. Therefore, we pad the\r
980 * thing to minimum size here. Stupid runts...\r
981 */\r
982 if (item->packet.len < ETH_MIN_PACKET) {\r
983 int len = item->packet.len;\r
984 memset (&item->packet.msg[len], 0, ETH_MIN_PACKET - len);\r
985 item->packet.len = ETH_MIN_PACKET;\r
986 }\r
987 }\r
988\r
989 /* is this the start of frame? */\r
990 if (item->packet.used == 0) {\r
991 xu->var->rxhdr[2] |= RXR_STF;\r
992 off = 0;\r
993 }\r
994\r
995 /* figure out chained packet size */\r
996 wlen = item->packet.crc_len - item->packet.used;\r
997 if (wlen > slen)\r
998 wlen = slen;\r
999\r
1000 /* transfer chained packet to host buffer */\r
1001 wstatus = Map_WriteB (segb, wlen, &item->packet.msg[off]);\r
1002 if (wstatus) {\r
1003 /* error during write */\r
1004 xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;\r
1005 xu->var->pcsr0 |= PCSR0_SERI;\r
1006 break;\r
1007 }\r
1008\r
1009 /* update chained counts */\r
1010 item->packet.used += wlen;\r
1011 off += wlen;\r
1012\r
1013 /* Is this the end-of-frame? */\r
1014 if (item->packet.used == item->packet.crc_len) {\r
1015 /* mark end-of-frame */\r
1016 xu->var->rxhdr[2] |= RXR_ENF;\r
1017\r
1018 /*\r
1019 * Fill in the Received Message Length field.\r
1020 * The documenation notes that the DEUNA actually performs\r
1021 * a full CRC check on the data buffer, and adds this CRC\r
1022 * value to the data, in the last 4 bytes. The question\r
1023 * is: does MLEN include these 4 bytes, or not??? --FvK\r
1024 *\r
1025 * A quick look at the RSX Process Software driver shows\r
1026 * that the CRC byte count(4) is added to MLEN, but does\r
1027 * not show if the DEUNA/DELUA actually transfers the\r
1028 * CRC bytes to the host buffers, since the driver never\r
1029 * tries to use them. However, since the host max buffer\r
1030 * size is only 1514, not 1518, I doubt the CRC is actually\r
1031 * transferred in normal mode. Maybe CRC is transferred\r
1032 * and used in Loopback mode.. -- DTH\r
1033 *\r
1034 * The VMS XEDRIVER indicates that CRC is transferred as\r
1035 * part of the packet, and is included in the MLEN count. -- DTH\r
1036 */\r
1037 xu->var->rxhdr[3] &= ~RXR_MLEN;\r
1038 xu->var->rxhdr[3] |= (item->packet.crc_len);\r
1039 if (xu->var->mode & MODE_DRDC) /* data chaining disabled */\r
1040 xu->var->rxhdr[3] |= RXR_NCHN;\r
1041\r
1042 /* update stats */\r
1043 upd_stat32(&xu->var->stats.frecv, 1);\r
1044 upd_stat32(&xu->var->stats.rbytes, item->packet.len - 14);\r
1045 if (item->packet.msg[0] & 1) { /* multicast? */\r
1046 upd_stat32(&xu->var->stats.mfrecv, 1);\r
1047 upd_stat32(&xu->var->stats.mrbytes, item->packet.len - 14);\r
1048 }\r
1049\r
1050 /* remove processed packet from the receive queue */\r
1051 ethq_remove (&xu->var->ReadQ);\r
1052 item = 0;\r
1053\r
1054 /* tell host we received a packet */\r
1055 xu->var->pcsr0 |= PCSR0_RXI;\r
1056 } /* if end-of-frame */\r
1057\r
1058 /* give buffer back to host */\r
1059 xu->var->rxhdr[2] &= ~RXR_OWN; /* clear ownership flag */\r
1060\r
1061 /* update the ring entry in host memory. */\r
1062 wstatus = Map_WriteW (ba, 8, xu->var->rxhdr);\r
1063 if (wstatus) {\r
1064 /* tell host bus write failed */\r
1065 xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;\r
1066 xu->var->pcsr0 |= PCSR0_SERI;\r
1067 /* if this was end-of-frame, log frame loss */\r
1068 if (xu->var->rxhdr[2] & RXR_ENF)\r
1069 upd_stat16(&xu->var->stats.rlossi, 1);\r
1070 }\r
1071\r
1072 /* set to next receive ring buffer */\r
1073 xu->var->rxnext += 1;\r
1074 if (xu->var->rxnext == xu->var->rrlen)\r
1075 xu->var->rxnext = 0;\r
1076\r
1077 } /* while */\r
1078\r
1079 /* if we failed to finish receiving the frame, flush the packet */\r
1080 if (item) {\r
1081 ethq_remove(&xu->var->ReadQ);\r
1082 upd_stat16(&xu->var->stats.rlossl, 1);\r
1083 }\r
1084\r
1085 /* set or clear interrupt, depending on what happened */\r
1086 xu_setclrint(xu, 0);\r
1087// xu_dump_rxring(xu); /* debug receive ring */\r
1088\r
1089}\r
1090\r
1091void xu_process_transmit(CTLR* xu)\r
1092{\r
1093 uint32 segb, ba;\r
1094 int slen, wlen, i, off, giant, runt;\r
1095 t_stat rstatus, wstatus;\r
1096\r
1097 sim_debug(DBG_TRC, xu->dev, "xu_process_transmit()\n");\r
1098/* xu_dump_txring(xu); /* debug receive ring */\r
1099\r
1100 for (;;) {\r
1101\r
1102 /* get next transmit buffer */\r
1103 ba = xu->var->tdrb + (xu->var->telen * 2) * xu->var->txnext;\r
1104 rstatus = Map_ReadW (ba, 8, xu->var->txhdr);\r
1105 if (rstatus) {\r
1106 /* tell host bus read failed */\r
1107 xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG;\r
1108 xu->var->pcsr0 |= PCSR0_SERI;\r
1109 break;\r
1110 }\r
1111\r
1112 /* if buffer not owned by controller, exit [at end of ring] */\r
1113 if (!(xu->var->txhdr[2] & TXR_OWN))\r
1114 break;\r
1115\r
1116 /* set buffer length and address */\r
1117 slen = xu->var->txhdr[0];\r
1118 segb = xu->var->txhdr[1] + ((xu->var->txhdr[2] & 3) << 16);\r
1119 wlen = slen;\r
1120\r
1121 /* prepare to accumulate transmit information if start of frame */\r
1122 if (xu->var->txhdr[2] & TXR_STF) {\r
1123 memset(&xu->var->write_buffer, 0, sizeof(ETH_PACK));\r
1124 off = giant = runt = 0;\r
1125 }\r
1126\r
1127 /* get packet data from host */\r
1128 if (xu->var->write_buffer.len + slen > ETH_MAX_PACKET) {\r
1129 wlen = ETH_MAX_PACKET - xu->var->write_buffer.len;\r
1130 giant = 1;\r
1131 }\r
1132 if (wlen > 0) {\r
1133 rstatus = Map_ReadB(segb, wlen, &xu->var->write_buffer.msg[off]);\r
1134 if (rstatus) {\r
1135 /* tell host bus read failed */\r
1136 xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG;\r
1137 xu->var->pcsr0 |= PCSR0_SERI;\r
1138 break;\r
1139 }\r
1140 }\r
1141 off += wlen;\r
1142 xu->var->write_buffer.len += wlen;\r
1143\r
1144 /* transmit packet when end-of-frame is reached */\r
1145 if (xu->var->txhdr[2] & TXR_ENF) {\r
1146\r
1147 /* make sure packet is minimum length */\r
1148 if (xu->var->write_buffer.len < ETH_MIN_PACKET) {\r
1149 xu->var->write_buffer.len = ETH_MIN_PACKET; /* pad packet to minimum length */\r
1150 if ((xu->var->mode & MODE_TPAD) == 0) /* if pad mode is NOT on, set runt error flag */\r
1151 runt = 1;\r
1152 }\r
1153\r
1154 /* are we in internal loopback mode ? */\r
1155 if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) {\r
1156 /* just put packet in receive buffer */\r
1157 ethq_insert (&xu->var->ReadQ, 1, &xu->var->write_buffer, 0);\r
1158 } else {\r
1159 /* transmit packet synchronously - write callback sets status */\r
1160 wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback);\r
1161 if (wstatus)\r
1162 xu->var->pcsr0 |= PCSR0_PCEI;\r
1163 }\r
1164\r
1165 /* update transmit status in transmit buffer */\r
1166 if (xu->var->write_buffer.status != 0) {\r
1167 /* failure */\r
1168 const uint16 tdr = 100 + wlen * 8; /* arbitrary value */\r
1169 xu->var->txhdr[3] |= TXR_RTRY;\r
1170 xu->var->txhdr[3] |= tdr & TXR_TDR;\r
1171 xu->var->txhdr[2] |= TXR_ERRS;\r
1172 }\r
1173\r
1174 /* was packet too big or too small? */\r
1175 if (giant || runt) {\r
1176 xu->var->txhdr[3] |= TXR_BUFL;\r
1177 xu->var->txhdr[2] |= TXR_ERRS;\r
1178 }\r
1179\r
1180 /* was packet self-addressed? */\r
1181 for (i=0; i<XU_FILTER_MAX; i++)\r
1182 if (memcmp(xu->var->write_buffer.msg, xu->var->setup.macs[i], sizeof(ETH_MAC)) == 0)\r
1183 xu->var->txhdr[2] |= TXR_MTCH;\r
1184\r
1185 /* tell host we transmitted a packet */\r
1186 xu->var->pcsr0 |= PCSR0_TXI;\r
1187\r
1188 /* update stats */\r
1189 upd_stat32(&xu->var->stats.ftrans, 1);\r
1190 upd_stat32(&xu->var->stats.tbytes, xu->var->write_buffer.len - 14);\r
1191 if (xu->var->write_buffer.msg[0] & 1) { /* multicast? */\r
1192 upd_stat32(&xu->var->stats.mftrans, 1);\r
1193 upd_stat32(&xu->var->stats.mtbytes, xu->var->write_buffer.len - 14);\r
1194 }\r
1195 if (giant)\r
1196 bit_stat16(&xu->var->stats.txerf, 0x10);\r
1197\r
1198 } /* if end-of-frame */\r
1199\r
1200 \r
1201 /* give buffer ownership back to host */\r
1202 xu->var->txhdr[2] &= ~TXR_OWN;\r
1203\r
1204 /* update transmit buffer */\r
1205 wstatus = Map_WriteW (ba, 8, xu->var->txhdr);\r
1206 if (wstatus) {\r
1207 /* tell host bus write failed */\r
1208 xu->var->pcsr0 |= PCSR0_PCEI;\r
1209 /* update stats */\r
1210 upd_stat16(&xu->var->stats.ftransa, 1);\r
1211 break;\r
1212 }\r
1213\r
1214 /* set to next transmit ring buffer */\r
1215 xu->var->txnext += 1;\r
1216 if (xu->var->txnext == xu->var->trlen)\r
1217 xu->var->txnext = 0;\r
1218\r
1219 } /* while */\r
1220}\r
1221\r
1222void xu_port_command (CTLR* xu)\r
1223{\r
1224 char* msg;\r
1225 int command = xu->var->pcsr0 & PCSR0_PCMD;\r
1226 int state = xu->var->pcsr1 & PCSR1_STATE;\r
1227 int bits;\r
1228 static char* commands[] = {\r
1229 "NO-OP",\r
1230 "GET PCBB",\r
1231 "GET CMD",\r
1232 "SELFTEST",\r
1233 "START",\r
1234 "BOOT",\r
1235 "Reserved NO-OP",\r
1236 "Reserved NO-OP",\r
1237 "PDMD",\r
1238 "Reserved NO-OP",\r
1239 "Reserved NO-OP",\r
1240 "Reserved NO-OP",\r
1241 "Reserved NO-OP",\r
1242 "Reserved NO-OP",\r
1243 "HALT",\r
1244 "STOP"\r
1245 };\r
1246\r
1247 sim_debug(DBG_TRC, xu->dev, "xu_port_command(), Command = %s [0%o]\n", commands[command], command);\r
1248 switch (command) { /* cases in order of most used to least used */\r
1249 case CMD_PDMD: /* POLLING DEMAND */\r
1250 /* process transmit buffers, receive buffers are done in the service timer */\r
1251 xu_process_transmit(xu);\r
1252 xu->var->pcsr0 |= PCSR0_DNI;\r
1253 break;\r
1254\r
1255 case CMD_GETCMD: /* GET COMMAND */\r
1256 bits = xu_command(xu);\r
1257 xu->var->pcsr0 |= PCSR0_DNI;\r
1258 break;\r
1259\r
1260 case CMD_GETPCBB: /* GET PCB-BASE */\r
1261 xu->var->pcbb = (xu->var->pcsr3 << 16) | xu->var->pcsr2;\r
1262 xu->var->pcsr0 |= PCSR0_DNI;\r
1263 break;\r
1264\r
1265 case CMD_SELFTEST: /* SELFTEST */\r
1266 xu_sw_reset(xu);\r
1267 xu->var->pcsr0 |= PCSR0_DNI;\r
1268 break;\r
1269\r
1270 case CMD_START: /* START */\r
1271 if (state == STATE_READY) {\r
1272 xu->var->pcsr1 &= ~PCSR1_STATE;\r
1273 xu->var->pcsr1 |= STATE_RUNNING; \r
1274 xu->var->pcsr0 |= PCSR0_DNI;\r
1275\r
1276 /* reset ring pointers */\r
1277 xu->var->rxnext = 0;\r
1278 xu->var->txnext = 0;\r
1279\r
1280 } else\r
1281 xu->var->pcsr0 |= PCSR0_PCEI;\r
1282 break;\r
1283\r
1284 case CMD_HALT: /* HALT */\r
1285 if ((state == STATE_READY) || (state == STATE_RUNNING)) {\r
1286 sim_cancel (&xu->unit[0]); /* cancel service timer */\r
1287 xu->var->pcsr1 &= ~PCSR1_STATE;\r
1288 xu->var->pcsr1 |= STATE_HALT;\r
1289 xu->var->pcsr0 |= PCSR0_DNI;\r
1290 } else\r
1291 xu->var->pcsr0 |= PCSR0_PCEI;\r
1292 break;\r
1293\r
1294 case CMD_STOP: /* STOP */\r
1295 if (state == STATE_RUNNING) {\r
1296 xu->var->pcsr1 &= ~PCSR1_STATE;\r
1297 xu->var->pcsr1 |= STATE_READY;\r
1298 xu->var->pcsr0 |= PCSR0_DNI;\r
1299 } else\r
1300 xu->var->pcsr0 |= PCSR0_PCEI;\r
1301 break;\r
1302\r
1303 case CMD_BOOT: /* BOOT */\r
1304 /* not implemented */\r
1305 msg = "%s: BOOT command not implemented!\n";\r
1306 printf (msg, xu->dev->name);\r
1307 if (sim_log) fprintf(sim_log, msg, xu->dev->name);\r
1308\r
1309 xu->var->pcsr0 |= PCSR0_PCEI;\r
1310 break;\r
1311\r
1312 case CMD_NOOP: /* NO-OP */\r
1313 /* NOOP does NOT set DNI */\r
1314 break;\r
1315\r
1316 case CMD_RSV06: /* RESERVED */\r
1317 case CMD_RSV07: /* RESERVED */\r
1318 case CMD_RSV11: /* RESERVED */\r
1319 case CMD_RSV12: /* RESERVED */\r
1320 case CMD_RSV13: /* RESERVED */\r
1321 case CMD_RSV14: /* RESERVED */\r
1322 case CMD_RSV15: /* RESERVED */\r
1323 /* all reserved commands act as a no-op but set DNI */\r
1324 xu->var->pcsr0 |= PCSR0_DNI;\r
1325 break;\r
1326 } /* switch */\r
1327\r
1328 /* set interrupt if needed */\r
1329 xu_setclrint(xu, 0);\r
1330}\r
1331\r
1332t_stat xu_rd(int32 *data, int32 PA, int32 access)\r
1333{\r
1334 CTLR* xu = xu_pa2ctlr(PA);\r
1335 int reg = (PA >> 1) & 03;\r
1336\r
1337 switch (reg) {\r
1338 case 00:\r
1339 *data = xu->var->pcsr0;\r
1340 break;\r
1341 case 01:\r
1342 *data = xu->var->pcsr1;\r
1343 break;\r
1344 case 02:\r
1345 *data = xu->var->pcsr2;\r
1346 break;\r
1347 case 03:\r
1348 *data = xu->var->pcsr3;\r
1349 break;\r
1350 }\r
1351 sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data);\r
1352 if (PA & 1)\r
1353 sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg);\r
1354 return SCPE_OK;\r
1355}\r
1356\r
1357t_stat xu_wr(int32 data, int32 PA, int32 access)\r
1358{\r
1359 CTLR* xu = xu_pa2ctlr(PA);\r
1360 int reg = (PA >> 1) & 03;\r
1361 char desc[10];\r
1362\r
1363 switch (access) {\r
1364 case WRITE :\r
1365 strcpy(desc, "Word");\r
1366 break;\r
1367 case WRITEB:\r
1368 if (PA & 1) {\r
1369 strcpy(desc, "ByteHi");\r
1370 } else {\r
1371 strcpy(desc, "ByteLo");\r
1372 }\r
1373 break;\r
1374 default :\r
1375 strcpy(desc, "Unknown");\r
1376 break;\r
1377 }\r
1378 sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc);\r
1379 switch (reg) {\r
1380 case 00:\r
1381 /* Clear write-one-to-clear interrupt bits */\r
1382 if (access == WRITEB) {\r
1383 data &= 0377;\r
1384 if (PA & 1) {\r
1385 /* Handle WriteOneToClear trick. */\r
1386 xu->var->pcsr0 &= ~((data << 8) & 0177400);\r
1387\r
1388 /* set/reset interrupt */\r
1389 xu_setclrint(xu, 0);\r
1390\r
1391 /* Bail out early to avoid PCMD crap. */\r
1392 return SCPE_OK;\r
1393 }\r
1394 } else { /* access == WRITE [Word] */\r
1395 uint16 mask = data & 0xFF00; /* only interested in high byte */\r
1396 xu->var->pcsr0 &= ~mask; /* clear write-one-to-clear bits */\r
1397 }\r
1398 /* RESET function requested? */\r
1399 if (data & PCSR0_RSET) {\r
1400 xu_sw_reset(xu);\r
1401 xu_setclrint(xu, 0);\r
1402 return SCPE_OK; /* nothing else to do on reset */\r
1403 }\r
1404 /* Handle the INTE interlock; if INTE changes state, no commands can occur */\r
1405 if ((xu->var->pcsr0 ^ data) & PCSR0_INTE) {\r
1406 xu->var->pcsr0 ^= PCSR0_INTE;\r
1407 xu->var->pcsr0 |= PCSR0_DNI;\r
1408 if (xu->var->pcsr0 & PCSR0_INTE) {\r
1409 sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Enabled\n");\r
1410 } else {\r
1411 sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Disabled\n");\r
1412 }\r
1413 } else {\r
1414 /* Normal write, no interlock. */\r
1415 xu->var->pcsr0 &= ~PCSR0_PCMD;\r
1416 xu->var->pcsr0 |= (data & PCSR0_PCMD);\r
1417 xu_port_command(xu);\r
1418 }\r
1419 /* We might have changed the interrupt sys. */\r
1420 xu_setclrint(xu, 0);\r
1421 break;\r
1422\r
1423 case 01:\r
1424 sim_debug(DBG_WRN, xu->dev, "xu_wr(), invalid write access on PCSR1!\n");\r
1425 break;\r
1426\r
1427 case 02:\r
1428 xu->var->pcsr2 = data & 0177776; /* store word, but not MBZ LSB */\r
1429 break;\r
1430\r
1431 case 03:\r
1432 xu->var->pcsr3 = data & 0000003; /* store significant bits */\r
1433 break;\r
1434 }\r
1435 return SCPE_OK;\r
1436}\r
1437\r
1438\r
1439/* attach device: */\r
1440t_stat xu_attach(UNIT* uptr, char* cptr)\r
1441{\r
1442 t_stat status;\r
1443 char* tptr;\r
1444 CTLR* xu = xu_unit2ctlr(uptr);\r
1445\r
1446 sim_debug(DBG_TRC, xu->dev, "xu_attach(cptr=%s)\n", cptr);\r
1447 tptr = (char *) malloc(strlen(cptr) + 1);\r
1448 if (tptr == NULL) return SCPE_MEM;\r
1449 strcpy(tptr, cptr);\r
1450\r
1451 xu->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV));\r
1452 if (!xu->var->etherface) return SCPE_MEM;\r
1453\r
1454 status = eth_open(xu->var->etherface, cptr, xu->dev, DBG_ETH);\r
1455 if (status != SCPE_OK) {\r
1456 free(tptr);\r
1457 free(xu->var->etherface);\r
1458 xu->var->etherface = 0;\r
1459 return status;\r
1460 }\r
1461 uptr->filename = tptr;\r
1462 uptr->flags |= UNIT_ATT;\r
1463 eth_setcrc(xu->var->etherface, 1); /* enable CRC */\r
1464\r
1465 /* reset the device with the new attach info */\r
1466 xu_reset(xu->dev);\r
1467\r
1468 return SCPE_OK;\r
1469}\r
1470\r
1471/* detach device: */\r
1472\r
1473t_stat xu_detach(UNIT* uptr)\r
1474{\r
1475 t_stat status;\r
1476 CTLR* xu = xu_unit2ctlr(uptr);\r
1477 sim_debug(DBG_TRC, xu->dev, "xu_detach()\n");\r
1478\r
1479 if (uptr->flags & UNIT_ATT) {\r
1480 status = eth_close (xu->var->etherface);\r
1481 free(xu->var->etherface);\r
1482 xu->var->etherface = 0;\r
1483 free(uptr->filename);\r
1484 uptr->filename = NULL;\r
1485 uptr->flags &= ~UNIT_ATT;\r
1486 }\r
1487 return SCPE_OK;\r
1488}\r
1489\r
1490void xu_setint(CTLR* xu)\r
1491{\r
1492 if (xu->var->pcsr0 & PCSR0_INTE) {\r
1493 xu->var->irq = 1;\r
1494 SET_INT(XU);\r
1495 }\r
1496 return;\r
1497}\r
1498\r
1499void xu_clrint(CTLR* xu)\r
1500{\r
1501 int i;\r
1502 xu->var->irq = 0; /* set controller irq off */\r
1503 /* clear master interrupt? */\r
1504 for (i=0; i<XU_MAX_CONTROLLERS; i++) /* check all controllers.. */\r
1505 if (xu_ctrl[i].var->irq) { /* if any irqs enabled */\r
1506 SET_INT(XU); /* set master interrupt on */\r
1507 return;\r
1508 }\r
1509 CLR_INT(XU); /* clear master interrupt */\r
1510 return;\r
1511}\r
1512\r
1513int32 xu_int (void)\r
1514{\r
1515 int i;\r
1516 for (i=0; i<XU_MAX_CONTROLLERS; i++) {\r
1517 CTLR* xu = &xu_ctrl[i];\r
1518 if (xu->var->irq) { /* if interrupt pending */\r
1519 xu_clrint(xu); /* clear interrupt */\r
1520 return xu->dib->vec; /* return vector */\r
1521 }\r
1522 }\r
1523 return 0; /* no interrupt request active */\r
1524}\r
1525\r
1526/*==============================================================================\r
1527/ debugging routines\r
1528/=============================================================================*/\r
1529\r
1530void xu_dump_rxring (CTLR* xu)\r
1531{\r
1532 int i;\r
1533 int rrlen = xu->var->rrlen;\r
1534 printf ("receive ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->rdrb, xu->var->rrlen, xu->var->relen, xu->var->rxnext);\r
1535 for (i=0; i<rrlen; i++) {\r
1536 uint16 rxhdr[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};\r
1537 uint32 ba = xu->var->rdrb + (xu->var->relen * 2) * i;\r
1538 t_stat rstatus = Map_ReadW (ba, 8, rxhdr); /* get rxring entry[i] */\r
1539 int own = (rxhdr[2] & RXR_OWN) >> 15;\r
1540 int len = rxhdr[0];\r
1541 uint32 addr = rxhdr[1] + ((rxhdr[2] & 3) << 16);\r
1542 printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, rxhdr[0], rxhdr[1], rxhdr[2], rxhdr[3]);\r
1543 }\r
1544}\r
1545\r
1546void xu_dump_txring (CTLR* xu)\r
1547{\r
1548 int i;\r
1549 int trlen = xu->var->trlen;\r
1550 printf ("transmit ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->tdrb, xu->var->trlen, xu->var->telen, xu->var->txnext);\r
1551 for (i=0; i<trlen; i++) {\r
1552 uint16 txhdr[4];\r
1553 uint32 ba = xu->var->tdrb + (xu->var->telen * 2) * i;\r
1554 t_stat tstatus = Map_ReadW (ba, 8, txhdr); /* get rxring entry[i] */\r
1555 int own = (txhdr[2] & RXR_OWN) >> 15;\r
1556 int len = txhdr[0];\r
1557 uint32 addr = txhdr[1] + ((txhdr[2] & 3) << 16);\r
1558 printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, txhdr[0], txhdr[1], txhdr[2], txhdr[3]);\r
1559 }\r
1560}\r