1 /* $Id: 3c400.c,v 1.11 2010/06/05 13:49:56 fredette Exp $ */
2
3 /* bus/multibus/3c400.c - implementation of the Multibus 3c400 emulation: */
4
5 /*
6 * Copyright (c) 2003 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: 3c400.c,v 1.11 2010/06/05 13:49:56 fredette Exp $");
38
39 /* includes: */
40 #include <tme/generic/bus-device.h>
41 #include <tme/generic/ethernet.h>
42
43 /* macros: */
44
45 /* register offsets and sizes: */
46 #define TME_3C400_REG_CSR (0)
47 #define TME_3C400_SIZ_CSR (sizeof(tme_uint16_t))
48 #define TME_3C400_REG_BACKOFF (2)
49 #define TME_3C400_SIZ_BACKOFF (sizeof(tme_uint16_t))
50 #define TME_3C400_REG_AROM (1024)
51 #define TME_3C400_SIZ_AROM (TME_ETHERNET_ADDR_SIZE)
52 #define TME_3C400_REG_ARAM (1536)
53 #define TME_3C400_SIZ_ARAM (TME_ETHERNET_ADDR_SIZE)
54 #define TME_3C400_REG_TBUF (2048)
55 #define TME_3C400_SIZ_BUF (2048)
56 #define TME_3C400_REG_ABUF (TME_3C400_REG_TBUF + TME_3C400_SIZ_BUF)
57 #define TME_3C400_REG_BBUF (TME_3C400_REG_ABUF + TME_3C400_SIZ_BUF)
58 #define TME_3C400_SIZ_CARD (TME_3C400_REG_BBUF + TME_3C400_SIZ_BUF)
59
60 /* the bits in the Control/Status Register. software can set and
61 clear bits covered by TME_3C400_CSR_INTPA. software can set, but
62 not clear, bits not covered by TME_3C400_CSR_INTPA: */
63 #define TME_3C400_CSR_BBSW (0x8000) /* B buffer empty (belongs to card) */
64 #define TME_3C400_CSR_ABSW (0x4000) /* A buffer empty (belongs to card) */
65 #define TME_3C400_CSR_TBSW (0x2000) /* T buffer full (belongs to card) */
66 #define TME_3C400_CSR_JAM (0x1000) /* Ethernet jammed (collision) */
67 #define TME_3C400_CSR_AMSW (0x0800) /* address RAM belongs to ether */
68 #define TME_3C400_CSR_RBBA (0x0400) /* B buffer received before A */
69 #define TME_3C400_CSR_RESET (0x0100) /* reset the card */
70 #define TME_3C400_CSR_INTPA (0x00ff) /* mask for interrupt and PA fields */
71 #define TME_3C400_CSR_BINT (0x0080) /* B buffer interrupt enable */
72 #define TME_3C400_CSR_AINT (0x0040) /* A buffer interrupt enable */
73 #define TME_3C400_CSR_TINT (0x0020) /* T buffer interrupt enable */
74 #define TME_3C400_CSR_JINT (0x0010) /* jam interrupt enable */
75 #define TME_3C400_CSR_PAMASK (0x000f) /* PA field */
76 #define TME_3C400_CSR_PA (0x0007) /* receive mine+broadcast-errors */
77 #define TME_3C400_CSR_PROMISC (0x0001) /* receive all-errors */
78
79 /* the first 16 bits of all buffers are a status word: */
80 #define TME_3C400_SIZ_BUF_STATUS (sizeof(tme_uint16_t))
81
82 /* the bits of a receive buffer status word: */
83 /* Frame Check Sequence (CRC) error */
84 #define TME_3C400_RBUF_FCSERR (0x8000)
85 /* this packet was broadcast: */
86 #define TME_3C400_RBUF_BROADCAST (0x4000)
87 /* this packet had a "range error": */
88 #define TME_3C400_RBUF_RGERR (0x2000)
89 /* this packet matched our address: */
90 #define TME_3C400_RBUF_ADDRMATCH (0x1000)
91 /* this packet had a framing error: */
92 #define TME_3C400_RBUF_FRERR (0x0800)
93 /* the first byte after the frame in the buffer: */
94 #define TME_3C400_RBUF_DOFF_MASK (0x07ff)
95
96 /* these get and put the CSR: */
97 #define TME_3C400_CSR_GET(_3c400) \
98 ((((tme_uint16_t) (_3c400)->tme_3c400_card[TME_3C400_REG_CSR + 0]) << 8) \
99 + (_3c400)->tme_3c400_card[TME_3C400_REG_CSR + 1])
100 #define TME_3C400_CSR_PUT(_3c400, csr) \
101 do { \
102 (_3c400)->tme_3c400_card[TME_3C400_REG_CSR + 0] = (csr) >> 8; \
103 (_3c400)->tme_3c400_card[TME_3C400_REG_CSR + 1] = (tme_uint8_t) (csr); \
104 } while (/* CONSTCOND */ 0)
105
106 /* the callout flags: */
107 #define TME_3C400_CALLOUT_CHECK (0)
108 #define TME_3C400_CALLOUT_RUNNING TME_BIT(0)
109 #define TME_3C400_CALLOUTS_MASK (-2)
110 #define TME_3C400_CALLOUT_CTRL TME_BIT(1)
111 #define TME_3C400_CALLOUT_CONFIG TME_BIT(2)
112 #define TME_3C400_CALLOUT_READ TME_BIT(3)
113 #define TME_3C400_CALLOUT_INT TME_BIT(4)
114
115 /* structures: */
116
117 /* the card: */
118 struct tme_3c400 {
119
120 /* our simple bus device header: */
121 struct tme_bus_device tme_3c400_device;
122 #define tme_3c400_element tme_3c400_device.tme_bus_device_element
123
124 /* the mutex protecting the card: */
125 tme_mutex_t tme_3c400_mutex;
126
127 /* the rwlock protecting the card: */
128 tme_rwlock_t tme_3c400_rwlock;
129
130 /* the Ethernet connection: */
131 struct tme_ethernet_connection *tme_3c400_eth_connection;
132
133 /* the callout flags: */
134 int tme_3c400_callout_flags;
135
136 /* if our interrupt line is currently asserted: */
137 int tme_3c400_int_asserted;
138
139 /* it's easiest to just model the card as a chunk of memory: */
140 tme_uint8_t tme_3c400_card[TME_3C400_SIZ_CARD];
141
142 #ifndef TME_NO_LOG
143 tme_uint16_t tme_3c400_last_log_csr;
144 #endif /* !TME_NO_LOG */
145 };
146
147 /* this resets the card: */
148 static void
_tme_3c400_reset(struct tme_3c400 * _3c400)149 _tme_3c400_reset(struct tme_3c400 *_3c400)
150 {
151 tme_uint16_t csr;
152
153 /* the reset CSR value: */
154 csr = 0;
155
156 /* set the CSR: */
157 TME_3C400_CSR_PUT(_3c400, csr);
158
159 /* clear all pending callouts: */
160 _3c400->tme_3c400_callout_flags &= TME_3C400_CALLOUTS_MASK;
161
162 /* if the interrupt line is currently asserted, negate it: */
163 if (_3c400->tme_3c400_int_asserted) {
164 _3c400->tme_3c400_callout_flags |= TME_3C400_CALLOUT_INT;
165 }
166 }
167
168 /* the _3c400 callout function. it must be called with the mutex locked: */
169 static void
_tme_3c400_callout(struct tme_3c400 * _3c400,int new_callouts)170 _tme_3c400_callout(struct tme_3c400 *_3c400, int new_callouts)
171 {
172 struct tme_ethernet_connection *conn_eth;
173 struct tme_bus_connection *conn_bus;
174 tme_uint16_t csr, csr_rbba, recv_buffer;
175 int callouts, later_callouts;
176 unsigned int ctrl;
177 struct tme_ethernet_config config;
178 int rc;
179 const tme_uint8_t *addrs[2];
180 tme_ethernet_fid_t frame_id;
181 tme_uint8_t *rbuf;
182 tme_uint16_t status;
183 struct tme_ethernet_frame_chunk *frame_chunk, frame_chunk_buffer;
184 int int_asserted;
185
186 /* add in any new callouts: */
187 _3c400->tme_3c400_callout_flags |= new_callouts;
188
189 /* if this function is already running in another thread, simply
190 return now. the other thread will do our work: */
191 if (_3c400->tme_3c400_callout_flags & TME_3C400_CALLOUT_RUNNING) {
192 return;
193 }
194
195 /* callouts are now running: */
196 _3c400->tme_3c400_callout_flags |= TME_3C400_CALLOUT_RUNNING;
197
198 /* assume that we won't need any later callouts: */
199 later_callouts = 0;
200
201 /* loop while callouts are needed: */
202 for (; (callouts = _3c400->tme_3c400_callout_flags) & TME_3C400_CALLOUTS_MASK; ) {
203
204 /* clear the needed callouts: */
205 _3c400->tme_3c400_callout_flags = callouts & ~TME_3C400_CALLOUTS_MASK;
206 callouts &= TME_3C400_CALLOUTS_MASK;
207
208 /* get this card's connection: */
209 conn_eth = _3c400->tme_3c400_eth_connection;
210
211 /* if we need to call out new control information: */
212 if (callouts & TME_3C400_CALLOUT_CTRL) {
213
214 /* get the current CSR value: */
215 csr = TME_3C400_CSR_GET(_3c400);
216
217 /* form the new ctrl: */
218 ctrl = 0;
219 if (csr & TME_3C400_CSR_TBSW) {
220 ctrl |= TME_ETHERNET_CTRL_OK_READ;
221 }
222
223 /* unlock the mutex: */
224 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
225
226 /* do the callout: */
227 rc = (conn_eth != NULL
228 ? ((*conn_eth->tme_ethernet_connection_ctrl)
229 (conn_eth,
230 ctrl))
231 : TME_OK);
232
233 /* lock the mutex: */
234 tme_mutex_lock(&_3c400->tme_3c400_mutex);
235
236 /* if the callout was unsuccessful, remember that at some later
237 time this callout should be attempted again: */
238 if (rc != TME_OK) {
239 later_callouts |= TME_3C400_CALLOUT_CTRL;
240 }
241 }
242
243 /* if we need to call out new config information: */
244 if (callouts & TME_3C400_CALLOUT_CONFIG) {
245
246 /* get the current CSR value: */
247 csr = TME_3C400_CSR_GET(_3c400);
248
249 /* form the new config: */
250 memset(&config, 0, sizeof(config));
251
252 /* our Ethernet address: */
253 config.tme_ethernet_config_addr_count = 0;
254 addrs[config.tme_ethernet_config_addr_count++]
255 = tme_ethernet_addr_broadcast;
256 if (csr & TME_3C400_CSR_AMSW) {
257 addrs[config.tme_ethernet_config_addr_count++]
258 = &_3c400->tme_3c400_card[TME_3C400_REG_ARAM];
259 }
260 config.tme_ethernet_config_addrs = addrs;
261
262 /* our config flags: */
263 config.tme_ethernet_config_flags = TME_ETHERNET_CONFIG_NORMAL;
264 switch (csr & TME_3C400_CSR_PAMASK) {
265 case 0:
266 config.tme_ethernet_config_addr_count = 0;
267 break;
268 case TME_3C400_CSR_PA:
269 break;
270 case TME_3C400_CSR_PROMISC:
271 config.tme_ethernet_config_flags |= TME_ETHERNET_CONFIG_PROMISC;
272 break;
273 default: abort();
274 }
275
276 /* unlock the mutex: */
277 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
278
279 /* do the callout: */
280 rc = (conn_eth == NULL
281 ? TME_OK
282 : ((*conn_eth->tme_ethernet_connection_config)
283 (conn_eth,
284 &config)));
285
286 /* lock the mutex: */
287 tme_mutex_lock(&_3c400->tme_3c400_mutex);
288
289 /* if the callout was unsuccessful, remember that at some later
290 time this callout should be attempted again: */
291 if (rc != TME_OK) {
292 later_callouts |= TME_3C400_CALLOUT_CONFIG;
293 }
294 }
295
296 /* if the Ethernet is readable: */
297 if (callouts & TME_3C400_CALLOUT_READ) {
298
299 /* get the current CSR value: */
300 csr = TME_3C400_CSR_GET(_3c400);
301
302 /* try to find an empty buffer: */
303 switch (csr & (TME_3C400_CSR_BBSW | TME_3C400_CSR_ABSW)) {
304 default:
305 /* both buffers are full: */
306 recv_buffer = 0;
307 csr_rbba = (csr & TME_3C400_CSR_RBBA);
308 break;
309 case TME_3C400_CSR_BBSW:
310 /* the A buffer is full but the B buffer is empty: */
311 recv_buffer = TME_3C400_CSR_BBSW;
312 csr_rbba = 0;
313 break;
314 case TME_3C400_CSR_ABSW:
315 /* the B buffer is full but the A buffer is empty: */
316 recv_buffer = TME_3C400_CSR_ABSW;
317 csr_rbba = TME_3C400_CSR_RBBA;
318 break;
319 case TME_3C400_CSR_ABSW | TME_3C400_CSR_BBSW:
320 /* both buffers are empty: */
321 recv_buffer = TME_3C400_CSR_ABSW;
322 csr_rbba = 0;
323 break;
324 }
325
326 /* unlock the mutex: */
327 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
328
329 /* make a frame chunk to receive this frame. remember that the
330 first two bytes of our card's buffers are a status word: */
331 if (recv_buffer == 0) {
332 rbuf = NULL;
333 frame_chunk = NULL;
334 }
335 else {
336 rbuf =
337 &_3c400->tme_3c400_card[(recv_buffer == TME_3C400_CSR_ABSW
338 ? TME_3C400_REG_ABUF
339 : TME_3C400_REG_BBUF)];
340 frame_chunk_buffer.tme_ethernet_frame_chunk_next = NULL;
341 frame_chunk_buffer.tme_ethernet_frame_chunk_bytes
342 = rbuf + TME_3C400_SIZ_BUF_STATUS;
343 frame_chunk_buffer.tme_ethernet_frame_chunk_bytes_count
344 = TME_3C400_SIZ_BUF;
345 frame_chunk = &frame_chunk_buffer;
346 }
347
348 /* do the callout: */
349 rc = (conn_eth == NULL
350 ? TME_OK
351 : ((*conn_eth->tme_ethernet_connection_read)
352 (conn_eth,
353 &frame_id,
354 frame_chunk,
355 TME_ETHERNET_READ_NEXT)));
356
357 /* lock the mutex: */
358 tme_mutex_lock(&_3c400->tme_3c400_mutex);
359
360 /* get the current CSR value: */
361 csr = TME_3C400_CSR_GET(_3c400);
362
363 /* if the read was successful: */
364 if (rc > 0) {
365
366 /* if this frame was received into a buffer: */
367 if (recv_buffer != 0) {
368
369 /* form the status word for this buffer: */
370 status = 0;
371
372 /* the first thing in the frame is the destination address,
373 which we check against our address and the broadcast
374 address: */
375 if (memcmp(rbuf + TME_3C400_SIZ_BUF_STATUS,
376 &_3c400->tme_3c400_card[TME_3C400_REG_ARAM],
377 TME_ETHERNET_ADDR_SIZE) == 0) {
378 status |= TME_3C400_RBUF_ADDRMATCH;
379 }
380 else if (memcmp(rbuf + TME_3C400_SIZ_BUF_STATUS,
381 tme_ethernet_addr_broadcast,
382 TME_ETHERNET_ADDR_SIZE) == 0) {
383 status |= TME_3C400_RBUF_BROADCAST;
384 }
385
386 /* put in the offset of the first free byte in the status.
387 make sure we present a packet that is at least as big
388 as the smallest Ethernet frame: */
389 rc = TME_MAX(rc, TME_ETHERNET_FRAME_MIN - TME_ETHERNET_CRC_SIZE);
390 status |= TME_3C400_SIZ_BUF_STATUS + rc;
391
392 /* put in the status: */
393 *((tme_uint16_t *) rbuf) = tme_htobe_u16(status);
394
395 /* update the CSR to reflect the new packet: */
396 csr = (csr & ~(recv_buffer | TME_3C400_CSR_RBBA)) | csr_rbba;
397 TME_3C400_CSR_PUT(_3c400, csr);
398
399 /* if interrupts are enabled on this buffer, mark that we need
400 to callout an interrupt: */
401 if (csr & (recv_buffer >> 8)) {
402 _3c400->tme_3c400_callout_flags |= TME_3C400_CALLOUT_INT;
403 }
404 }
405
406 /* mark that we need to loop to callout to read more frames: */
407 _3c400->tme_3c400_callout_flags |= TME_3C400_CALLOUT_READ;
408 }
409
410 /* otherwise, the read failed. convention dictates that we
411 forget that the connection was readable, which we already
412 have done by clearing the CALLOUT_READ flag: */
413 }
414
415 /* if we need to call out a possible change to our interrupt
416 signal: */
417 if (callouts & TME_3C400_CALLOUT_INT) {
418
419 /* get the current CSR value: */
420 csr = TME_3C400_CSR_GET(_3c400);
421
422 /* see if the interrupt signal should be asserted or negated: */
423 int_asserted = ((~(csr & (TME_3C400_CSR_BBSW
424 | TME_3C400_CSR_ABSW
425 | TME_3C400_CSR_TBSW)))
426 & ((csr & (TME_3C400_CSR_BINT
427 | TME_3C400_CSR_AINT
428 | TME_3C400_CSR_TINT)) << 8));
429
430 /* if the interrupt signal doesn't already have the right state: */
431 if (!int_asserted != !_3c400->tme_3c400_int_asserted) {
432
433 /* unlock our mutex: */
434 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
435
436 /* get our bus connection: */
437 conn_bus = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
438 _3c400->tme_3c400_device.tme_bus_device_connection,
439 &_3c400->tme_3c400_device.tme_bus_device_connection_rwlock);
440
441 /* call out the bus interrupt signal edge: */
442 rc = (*conn_bus->tme_bus_signal)
443 (conn_bus,
444 TME_BUS_SIGNAL_INT_UNSPEC
445 | (int_asserted
446 ? TME_BUS_SIGNAL_LEVEL_ASSERTED
447 : TME_BUS_SIGNAL_LEVEL_NEGATED));
448
449 /* lock our mutex: */
450 tme_mutex_lock(&_3c400->tme_3c400_mutex);
451
452 /* if this callout was successful, note the new state of the
453 interrupt signal: */
454 if (rc == TME_OK) {
455 _3c400->tme_3c400_int_asserted = int_asserted;
456 }
457
458 /* otherwise, remember that at some later time this callout
459 should be attempted again: */
460 else {
461 later_callouts |= TME_3C400_CALLOUT_INT;
462 }
463 }
464 }
465 }
466
467 /* put in any later callouts, and clear that callouts are running: */
468 _3c400->tme_3c400_callout_flags = later_callouts;
469 }
470
471 /* the _3c400 bus cycle handler: */
472 static int
_tme_3c400_bus_cycle(void * __3c400,struct tme_bus_cycle * cycle_init)473 _tme_3c400_bus_cycle(void *__3c400, struct tme_bus_cycle *cycle_init)
474 {
475 struct tme_3c400 *_3c400;
476 tme_uint16_t csr_old, csr_new, csr_diff;
477 int new_callouts;
478
479 /* recover our data structure: */
480 _3c400 = (struct tme_3c400 *) __3c400;
481
482 /* assume we won't need any new callouts: */
483 new_callouts = 0;
484
485 /* lock the mutex: */
486 tme_mutex_lock(&_3c400->tme_3c400_mutex);
487
488 /* get the changed CSR value - there are bits that software can only
489 set, and not clear: */
490 csr_old = TME_3C400_CSR_GET(_3c400);
491
492 /* unless this address falls within the address ROM, run the cycle: */
493 if ((cycle_init->tme_bus_cycle_address
494 < TME_3C400_REG_AROM)
495 || (cycle_init->tme_bus_cycle_address
496 >= TME_3C400_REG_ARAM)) {
497 tme_bus_cycle_xfer_memory(cycle_init,
498 _3c400->tme_3c400_card,
499 _3c400->tme_3c400_device.tme_bus_device_address_last);
500 }
501
502 /* get the new CSR value, and put back any bits that software
503 cannot clear: */
504 csr_new = TME_3C400_CSR_GET(_3c400);
505 csr_new |= (csr_old & ~TME_3C400_CSR_INTPA);
506
507 /* get the set of bits that has changed: */
508 csr_diff = (csr_old ^ csr_new);
509
510 /* if this is a reset: */
511 if (csr_diff & TME_3C400_CSR_RESET) {
512 _tme_3c400_reset(_3c400);
513 }
514
515 /* otherwise: */
516 else {
517
518 /* if the transmit buffer now belongs to the card, call out
519 that we are now readable: */
520 if (csr_diff & TME_3C400_CSR_TBSW) {
521 new_callouts |= TME_3C400_CALLOUT_CTRL;
522 }
523
524 /* if the address RAM now belongs to the card, or if the address
525 filter configuration has changed, call out the config change: */
526 if ((csr_diff & TME_3C400_CSR_AMSW)
527 || (csr_diff & TME_3C400_CSR_PAMASK) != 0) {
528 new_callouts |= TME_3C400_CALLOUT_CONFIG;
529 }
530
531 /* if any interrupt enable status bits have changed,
532 call out the interrupt signal change: */
533 if (csr_diff & (TME_3C400_CSR_BINT
534 | TME_3C400_CSR_AINT
535 | TME_3C400_CSR_TINT)) {
536 new_callouts |= TME_3C400_CALLOUT_INT;
537 }
538
539 /* set the current CSR value: */
540 TME_3C400_CSR_PUT(_3c400, csr_new);
541
542 #ifndef TME_NO_LOG
543 if (csr_new != _3c400->tme_3c400_last_log_csr) {
544 _3c400->tme_3c400_last_log_csr = csr_new;
545 tme_log(&_3c400->tme_3c400_element->tme_element_log_handle,
546 1000, TME_OK,
547 (&_3c400->tme_3c400_element->tme_element_log_handle,
548 "csr now 0x%04x",
549 csr_new));
550 }
551 #endif /* !TME_NO_LOG */
552 }
553
554 /* make any new callouts: */
555 _tme_3c400_callout(_3c400, new_callouts);
556
557 /* unlock the mutex: */
558 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
559
560 /* no faults: */
561 return (TME_OK);
562 }
563
564 /* this is called when a device changes its configuration: */
565 static int
_tme_3c400_config(struct tme_ethernet_connection * conn_eth,struct tme_ethernet_config * config)566 _tme_3c400_config(struct tme_ethernet_connection *conn_eth,
567 struct tme_ethernet_config *config)
568 {
569 /* we don't care when other devices on the Ethernet
570 reconfigure themselves: */
571 return (TME_OK);
572 }
573
574 /* this is called when control lines change: */
575 static int
_tme_3c400_ctrl(struct tme_ethernet_connection * conn_eth,unsigned int ctrl)576 _tme_3c400_ctrl(struct tme_ethernet_connection *conn_eth,
577 unsigned int ctrl)
578 {
579 struct tme_3c400 *_3c400;
580 int new_callouts;
581
582 /* recover our data structures: */
583 _3c400 = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
584
585 /* assume that we won't need any new callouts: */
586 new_callouts = 0;
587
588 /* lock the mutex: */
589 tme_mutex_lock(&_3c400->tme_3c400_mutex);
590
591 /* if this connection is readable, call out a read: */
592 if (ctrl & TME_ETHERNET_CTRL_OK_READ) {
593 new_callouts |= TME_3C400_CALLOUT_READ;
594 }
595
596 /* make any new callouts: */
597 _tme_3c400_callout(_3c400, new_callouts);
598
599 /* unlock the mutex: */
600 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
601
602 return (TME_OK);
603 }
604
605 /* this is called to read frames (from the 3c400 perspective, to transmit them): */
606 static int
_tme_3c400_read(struct tme_ethernet_connection * conn_eth,tme_ethernet_fid_t * _frame_id,struct tme_ethernet_frame_chunk * frame_chunks,unsigned int flags)607 _tme_3c400_read(struct tme_ethernet_connection *conn_eth,
608 tme_ethernet_fid_t *_frame_id,
609 struct tme_ethernet_frame_chunk *frame_chunks,
610 unsigned int flags)
611 {
612 struct tme_3c400 *_3c400;
613 struct tme_ethernet_frame_chunk frame_chunk_buffer;
614 tme_uint16_t csr, count;
615 int new_callouts;
616 int rc;
617
618 /* recover our data structures: */
619 _3c400 = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
620
621 /* assume that we won't need any new callouts: */
622 new_callouts = 0;
623
624 /* lock our mutex: */
625 tme_mutex_lock(&_3c400->tme_3c400_mutex);
626
627 /* get the current CSR value: */
628 csr = TME_3C400_CSR_GET(_3c400);
629
630 /* if the transmit buffer is full: */
631 if (csr & TME_3C400_CSR_TBSW) {
632
633 /* get the count of bytes in the frame: */
634 count = (TME_3C400_SIZ_BUF
635 - (tme_betoh_u16(*((tme_uint16_t *)
636 &_3c400->tme_3c400_card[TME_3C400_REG_TBUF]))
637 & TME_3C400_RBUF_DOFF_MASK));
638
639 /* form the single frame chunk: */
640 frame_chunk_buffer.tme_ethernet_frame_chunk_next = NULL;
641 frame_chunk_buffer.tme_ethernet_frame_chunk_bytes
642 = (&_3c400->tme_3c400_card[TME_3C400_REG_TBUF]
643 + TME_3C400_SIZ_BUF
644 - count);
645 frame_chunk_buffer.tme_ethernet_frame_chunk_bytes_count
646 = count;
647
648 /* copy out the frame: */
649 count = tme_ethernet_chunks_copy(frame_chunks, &frame_chunk_buffer);
650
651 /* if this isn't a peek: */
652 if (!(flags & TME_ETHERNET_READ_PEEK)) {
653
654 /* mark the transmit buffer as empty: */
655 csr &= ~TME_3C400_CSR_TBSW;
656 TME_3C400_CSR_PUT(_3c400, csr);
657
658 /* if transmit buffer interrupts are enabled, call out
659 an interrupt: */
660 if (csr & TME_3C400_CSR_TINT) {
661 new_callouts |= TME_3C400_CALLOUT_INT;
662 }
663 }
664
665 /* success: */
666 rc = count;
667 }
668
669 /* if the transmit buffer is empty, return an error: */
670 else {
671 rc = -ENOENT;
672 }
673
674 /* make any new callouts: */
675 _tme_3c400_callout(_3c400, new_callouts);
676
677 /* unlock our mutex: */
678 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
679
680 /* done: */
681 return (rc);
682 }
683
684 /* the _3c400 TLB filler: */
685 static int
_tme_3c400_tlb_fill(void * __3c400,struct tme_bus_tlb * tlb,tme_bus_addr_t address_wider,unsigned int cycles)686 _tme_3c400_tlb_fill(void *__3c400, struct tme_bus_tlb *tlb,
687 tme_bus_addr_t address_wider, unsigned int cycles)
688 {
689 struct tme_3c400 *_3c400;
690 tme_bus_addr32_t address;
691
692 /* recover our data structure: */
693 _3c400 = (struct tme_3c400 *) __3c400;
694
695 /* get the normal-width address: */
696 address = address_wider;
697 assert (address == address_wider);
698
699 /* the address must be within range: */
700 assert(address < TME_3C400_SIZ_CARD);
701
702 /* initialize the TLB entry: */
703 tme_bus_tlb_initialize(tlb);
704
705 /* if the address falls from the CSR to the address ROM: */
706 if (TME_3C400_REG_CSR <= address
707 && address < TME_3C400_REG_AROM) {
708
709 /* this TLB entry covers this range: */
710 tlb->tme_bus_tlb_addr_first = TME_3C400_REG_CSR;
711 tlb->tme_bus_tlb_addr_last = TME_3C400_REG_AROM - 1;
712 }
713
714 /* if this address falls from the address ROM to the address RAM: */
715 else if (TME_3C400_REG_AROM <= address
716 && address < TME_3C400_REG_ARAM) {
717
718 /* this TLB entry covers this range: */
719 tlb->tme_bus_tlb_addr_first = TME_3C400_REG_AROM;
720 tlb->tme_bus_tlb_addr_last = TME_3C400_REG_ARAM - 1;
721 }
722
723 /* anything else covers the remainder of the device: */
724 else {
725
726 /* this TLB entry can cover from the address RAM to the end of the card: */
727 tlb->tme_bus_tlb_addr_first = TME_3C400_REG_ARAM;
728 tlb->tme_bus_tlb_addr_last = TME_3C400_SIZ_CARD - 1;
729
730 /* this TLB entry allows fast writing: */
731 tlb->tme_bus_tlb_emulator_off_write = &_3c400->tme_3c400_card[0];
732 }
733
734 /* all address ranges allow fast reading: */
735 tlb->tme_bus_tlb_emulator_off_read = &_3c400->tme_3c400_card[0];
736 tlb->tme_bus_tlb_rwlock = &_3c400->tme_3c400_rwlock;
737
738 /* allow reading and writing: */
739 tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
740
741 /* our bus cycle handler: */
742 tlb->tme_bus_tlb_cycle_private = _3c400;
743 tlb->tme_bus_tlb_cycle = _tme_3c400_bus_cycle;
744
745 return (TME_OK);
746 }
747
748 /* this makes a new Ethernet connection: */
749 static int
_tme_3c400_connection_make(struct tme_connection * conn,unsigned int state)750 _tme_3c400_connection_make(struct tme_connection *conn, unsigned int state)
751 {
752 struct tme_3c400 *_3c400;
753 struct tme_ethernet_connection *conn_eth;
754 struct tme_ethernet_connection *conn_eth_other;
755
756 /* recover our data structures: */
757 _3c400 = conn->tme_connection_element->tme_element_private;
758 conn_eth = (struct tme_ethernet_connection *) conn;
759 conn_eth_other = (struct tme_ethernet_connection *) conn->tme_connection_other;
760
761 /* both sides must be Ethernet connections: */
762 assert(conn->tme_connection_type == TME_CONNECTION_ETHERNET);
763 assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_ETHERNET);
764
765 /* we're always set up to answer calls across the connection, so we
766 only have to do work when the connection has gone full, namely
767 taking the other side of the connection: */
768 if (state == TME_CONNECTION_FULL) {
769
770 /* lock our mutex: */
771 tme_mutex_lock(&_3c400->tme_3c400_mutex);
772
773 /* save our connection: */
774 _3c400->tme_3c400_eth_connection = conn_eth_other;
775
776 /* unlock our mutex: */
777 tme_mutex_unlock(&_3c400->tme_3c400_mutex);
778 }
779
780 return (TME_OK);
781 }
782
783 /* this breaks a connection: */
784 static int
_tme_3c400_connection_break(struct tme_connection * conn,unsigned int state)785 _tme_3c400_connection_break(struct tme_connection *conn, unsigned int state)
786 {
787 abort();
788 }
789
790 /* this makes a new connection side for a 3c400: */
791 static int
_tme_3c400_connections_new(struct tme_element * element,const char * const * args,struct tme_connection ** _conns,char ** _output)792 _tme_3c400_connections_new(struct tme_element *element,
793 const char * const *args,
794 struct tme_connection **_conns,
795 char **_output)
796 {
797 struct tme_3c400 *_3c400;
798 struct tme_ethernet_connection *conn_eth;
799 struct tme_connection *conn;
800 int rc;
801
802 /* recover our data structure: */
803 _3c400 = (struct tme_3c400 *) element->tme_element_private;
804
805 /* make the generic bus device connection side: */
806 rc = tme_bus_device_connections_new(element, args, _conns, _output);
807 if (rc != TME_OK) {
808 return (rc);
809 }
810
811 /* if we don't have an Ethernet connection, make one: */
812 if (_3c400->tme_3c400_eth_connection == NULL) {
813
814 /* allocate the new Ethernet connection: */
815 conn_eth = tme_new0(struct tme_ethernet_connection, 1);
816 conn = &conn_eth->tme_ethernet_connection;
817
818 /* fill in the generic connection: */
819 conn->tme_connection_next = *_conns;
820 conn->tme_connection_type = TME_CONNECTION_ETHERNET;
821 conn->tme_connection_score = tme_ethernet_connection_score;
822 conn->tme_connection_make = _tme_3c400_connection_make;
823 conn->tme_connection_break = _tme_3c400_connection_break;
824
825 /* fill in the Ethernet connection: */
826 conn_eth->tme_ethernet_connection_config = _tme_3c400_config;
827 conn_eth->tme_ethernet_connection_ctrl = _tme_3c400_ctrl;
828 conn_eth->tme_ethernet_connection_read = _tme_3c400_read;
829
830 /* return the connection side possibility: */
831 *_conns = conn;
832 }
833
834 /* done: */
835 return (TME_OK);
836 }
837
838 /* the new _3c400 function: */
839 TME_ELEMENT_SUB_NEW_DECL(tme_bus_multibus,3c400) {
840 struct tme_3c400 *_3c400;
841 tme_uint8_t arom[TME_ETHERNET_ADDR_SIZE];
842 int arom_ok;
843 int arg_i;
844 int usage;
845
846 /* check our arguments: */
847 usage = 0;
848 arom_ok = FALSE;
849 arg_i = 1;
850 for (;;) {
851
852 /* our Ethernet address ROM: */
853 if (TME_ARG_IS(args[arg_i], "ether")
854 && tme_ethernet_addr_parse(args[arg_i + 1], arom) == TME_OK) {
855 arom_ok = TRUE;
856 arg_i += 2;
857 }
858
859 /* if we ran out of arguments: */
860 else if (args[arg_i] == NULL) {
861
862 /* we must have been given our Ethernet address ROM: */
863 if (!arom_ok) {
864 usage = TRUE;
865 }
866 break;
867 }
868
869 /* otherwise this is a bad argument: */
870 else {
871 tme_output_append_error(_output,
872 "%s %s, ",
873 args[arg_i],
874 _("unexpected"));
875 usage = TRUE;
876 break;
877 }
878 }
879
880 if (usage) {
881 tme_output_append_error(_output,
882 "%s %s ether %s",
883 _("usage:"),
884 args[0],
885 _("ETHERNET-ADDRESS"));
886 return (EINVAL);
887 }
888
889 /* start the _3c400 structure: */
890 _3c400 = tme_new0(struct tme_3c400, 1);
891 _3c400->tme_3c400_element = element;
892 tme_mutex_init(&_3c400->tme_3c400_mutex);
893 tme_rwlock_init(&_3c400->tme_3c400_rwlock);
894 memcpy(_3c400->tme_3c400_card + TME_3C400_REG_AROM,
895 arom,
896 sizeof(arom));
897
898 /* initialize our simple bus device descriptor: */
899 assert((TME_3C400_SIZ_CARD & (TME_3C400_SIZ_CARD - 1)) == 0);
900 _3c400->tme_3c400_device.tme_bus_device_element = element;
901 _3c400->tme_3c400_device.tme_bus_device_tlb_fill = _tme_3c400_tlb_fill;
902 _3c400->tme_3c400_device.tme_bus_device_address_last = TME_3C400_SIZ_CARD - 1;
903
904 /* fill the element: */
905 element->tme_element_private = _3c400;
906 element->tme_element_connections_new = _tme_3c400_connections_new;
907
908 return (TME_OK);
909 }
910