mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
2305 lines
68 KiB
C
2305 lines
68 KiB
C
/* $Id: i825x6.c,v 1.8 2010/06/05 14:43:27 fredette Exp $ */
|
|
|
|
/* ic/i825x6.c - implementation of the Intel 825x6 emulation: */
|
|
|
|
/*
|
|
* Copyright (c) 2004 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: i825x6.c,v 1.8 2010/06/05 14:43:27 fredette Exp $");
|
|
|
|
/* XXX FIXME - TLB usage here is not thread-safe: */
|
|
|
|
/* includes: */
|
|
#include <tme/generic/bus-device.h>
|
|
#include <tme/generic/ethernet.h>
|
|
#undef TME_I825X6_VERSION
|
|
#define TME_I825X6_VERSION TME_X_VERSION(0, 0)
|
|
#include <tme/ic/i825x6.h>
|
|
#include "i825x6reg.h"
|
|
|
|
/* macros: */
|
|
|
|
/* these get and put values of different sizes. the values *must* be aligned: */
|
|
#define TME_I825X6_GET16(bytes) tme_letoh_u16(*((const tme_uint16_t *) (bytes)))
|
|
#define TME_I825X6_PUT16(bytes, val) (*((tme_uint16_t *) (bytes)) = tme_htole_u16(val))
|
|
#define TME_I82586_GET24(bytes) (tme_letoh_u32(*((const tme_uint32_t *) (bytes))) & 0xffffff)
|
|
|
|
/* these read and write regions using DMA: */
|
|
#define TME_I825X6_READ(address, v) \
|
|
do { \
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device, \
|
|
(address), \
|
|
sizeof(v), \
|
|
(tme_uint8_t *) &(v), \
|
|
TME_I825X6_LOCKS_DEFAULT); \
|
|
assert (rc == TME_OK); \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define TME_I825X6_WRITE(address, v) \
|
|
do { \
|
|
rc = tme_bus_device_dma_write_16(&i825x6->tme_i825x6_device, \
|
|
(address), \
|
|
sizeof(v), \
|
|
(const tme_uint8_t *) &(v), \
|
|
TME_I825X6_LOCKS_DEFAULT); \
|
|
assert (rc == TME_OK); \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
/* these read and write values of different sizes using DMA: */
|
|
#define TME_I825X6_READ16(address, v) \
|
|
do { \
|
|
TME_I825X6_READ(address, value16); \
|
|
v = TME_I825X6_GET16(&value16); \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define TME_I82586_READ24(address, v) \
|
|
do { \
|
|
TME_I825X6_READ(address, value32); \
|
|
v = TME_I82586_GET24(&value32); \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define TME_I825X6_WRITE16(address, v) \
|
|
do { \
|
|
TME_I825X6_PUT16(&value16, v); \
|
|
TME_I825X6_WRITE(address, value16); \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
/* the "reset" stat_cus_rus_t value: */
|
|
#define TME_I825X6_CA_RESET (0xffff)
|
|
|
|
/* the address of the idle "Command Block": */
|
|
#define TME_I825X6_CB_ADDRESS_IDLE (0xffffffff)
|
|
|
|
/* an undefined Receive Unit address: */
|
|
#define TME_I825X6_RU_ADDRESS_UNDEF (0xffffffff)
|
|
|
|
/* an undefined Receive Unit offset: */
|
|
#define TME_I825X6_RU_OFFSET_UNDEF (0xffff)
|
|
|
|
/* the default locks: */
|
|
#define TME_I825X6_LOCKS_DEFAULT (0)
|
|
|
|
/* the size of the TLB entry hash: */
|
|
#define TME_I825X6_TLB_HASH_SIZE (512)
|
|
|
|
/* the callout flags: */
|
|
#define TME_I825X6_CALLOUTS_CHECK (0)
|
|
#define TME_I825X6_CALLOUTS_RUNNING TME_BIT(0)
|
|
#define TME_I825X6_CALLOUTS_MASK (-2)
|
|
#define TME_I825X6_CALLOUT_CTRL TME_BIT(1)
|
|
#define TME_I825X6_CALLOUT_CONFIG TME_BIT(2)
|
|
#define TME_I825X6_CALLOUT_READ TME_BIT(3)
|
|
#define TME_I825X6_CALLOUT_INT TME_BIT(4)
|
|
#define TME_I825X6_CALLOUT_CA TME_BIT(5)
|
|
#define TME_I825X6_CALLOUT_CU TME_BIT(6)
|
|
|
|
#if 1
|
|
extern int printf(const char *format, ...);
|
|
#endif
|
|
|
|
/* structures: */
|
|
|
|
/* an rx buffer: */
|
|
struct tme_i825x6_rx_buffer {
|
|
|
|
/* the generic ethernet frame chunk. this must be first, since we
|
|
abuse its tme_ethernet_frame_chunk_next for our own next pointer: */
|
|
union {
|
|
struct tme_i825x6_rx_buffer *_tme_i825x6_rx_buffer_u_next;
|
|
struct tme_ethernet_frame_chunk _tme_i825x6_rx_buffer_u_frame_chunk;
|
|
} _tme_i825x6_rx_buffer_u;
|
|
#define TME_I825X6_RX_BUFFER_NEXT(rx_buffer) \
|
|
((rx_buffer)->_tme_i825x6_rx_buffer_u._tme_i825x6_rx_buffer_u_next)
|
|
#define tme_i825x6_rx_buffer_frame_chunk _tme_i825x6_rx_buffer_u._tme_i825x6_rx_buffer_u_frame_chunk
|
|
|
|
/* when this is TME_I825X6_RU_ADDRESS_UNDEF, this rx buffer was made
|
|
from a fast-write TLB entry, and the generic ethernet frame chunk
|
|
points directly into the fast-write memory. otherwise, the
|
|
generic ethernet frame chunk points to a private intermediate
|
|
buffer, and this is the bus address to DMA the buffer back to: */
|
|
tme_uint32_t tme_i825x6_rx_buffer_rb_address;
|
|
|
|
/* when this is not TME_I825X6_RU_ADDRESS_UNDEF, this rx buffer
|
|
finishes filling the buffer attached to the Receive Buffer
|
|
Descriptor at this address, and signals the receiver to update
|
|
that descriptor's Size field: */
|
|
tme_uint32_t tme_i825x6_rx_buffer_rbd_address;
|
|
};
|
|
|
|
/* the chip: */
|
|
struct tme_i825x6 {
|
|
|
|
/* our simple bus device header: */
|
|
struct tme_bus_device tme_i825x6_device;
|
|
#define tme_i825x6_element tme_i825x6_device.tme_bus_device_element
|
|
|
|
/* the Ethernet connection: */
|
|
struct tme_ethernet_connection *tme_i825x6_eth_connection;
|
|
|
|
/* the mutex protecting the chip: */
|
|
tme_mutex_t tme_i825x6_mutex;
|
|
|
|
/* the callout flags: */
|
|
int tme_i825x6_callout_flags;
|
|
|
|
/* our DMA TLB hash: */
|
|
struct tme_bus_tlb tme_i825x6_tlb_hash[TME_I825X6_TLB_HASH_SIZE];
|
|
int tme_i825x6_tlb_hash_added;
|
|
|
|
/* the i825x6 bus signals: */
|
|
struct tme_bus_signals tme_i825x6_bus_signals;
|
|
|
|
/* the rx buffer free list: */
|
|
struct tme_i825x6_rx_buffer *tme_i825x6_rx_buffer_free_list;
|
|
|
|
/* this is nonzero if the next CA follows RESET: */
|
|
int tme_i825x6_ca_follows_reset;
|
|
|
|
/* the Ethernet addresses. there are always at least two addresses
|
|
in this array - the broadcast address and the Individual Address,
|
|
in that order: */
|
|
unsigned int tme_i825x6_address_count;
|
|
tme_uint8_t *tme_i825x6_addresses;
|
|
|
|
/* the i82586 AL-LOC value: */
|
|
int tme_i825x6_al_loc;
|
|
|
|
/* the i82586 PRM value: */
|
|
int tme_i825x6_prm;
|
|
|
|
/* the i82586 and 32-bit segmented i82596 SCB base: */
|
|
tme_uint32_t tme_i825x6_scb_base;
|
|
|
|
/* the SCB address: */
|
|
tme_uint32_t tme_i825x6_scb_address;
|
|
|
|
/* the SCB status word: */
|
|
tme_uint16_t tme_i825x6_stat_cus_rus_t;
|
|
|
|
/* the SCB Command Unit Command: */
|
|
tme_uint16_t tme_i825x6_cuc;
|
|
|
|
/* the CB address: */
|
|
tme_uint32_t tme_i825x6_cb_address;
|
|
|
|
/* the CB status word: */
|
|
tme_uint16_t tme_i825x6_c_b_ok_a;
|
|
|
|
/* the CB command word: */
|
|
tme_uint16_t tme_i825x6_el_s_i_cmd;
|
|
|
|
/* the next CB address: */
|
|
tme_uint32_t tme_i825x6_cb_address_next;
|
|
|
|
/* the RFD address: */
|
|
tme_uint32_t tme_i825x6_rfd_address;
|
|
|
|
/* the Free Buffer List: */
|
|
struct tme_i825x6_rx_buffer *tme_i825x6_fbl;
|
|
|
|
/* the size of all buffers on the Free Buffer List: */
|
|
tme_uint32_t tme_i825x6_fbl_size;
|
|
|
|
/* the address of the offset of the next free RBD: */
|
|
tme_uint32_t tme_i825x6_rbd_offset_address;
|
|
};
|
|
|
|
/* prototypes: */
|
|
static void _tme_i825x6_abort_ru _TME_P((struct tme_i825x6 *));
|
|
|
|
/* globals: */
|
|
static const struct tme_bus_signals _tme_i825x6_bus_signals = TME_BUS_SIGNALS_I825X6;
|
|
|
|
/* this resets the i825x6: */
|
|
static void
|
|
_tme_i825x6_reset(struct tme_i825x6 *i825x6)
|
|
{
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"reset"));
|
|
|
|
/* clear all pending callouts: */
|
|
i825x6->tme_i825x6_callout_flags &= TME_I825X6_CALLOUTS_MASK;
|
|
|
|
/* abort the Receive Unit: */
|
|
_tme_i825x6_abort_ru(i825x6);
|
|
|
|
/* the Receive Unit is now Idle: */
|
|
i825x6->tme_i825x6_stat_cus_rus_t
|
|
= ((i825x6->tme_i825x6_stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_IDLE);
|
|
|
|
/* we have no packet to transmit: */
|
|
i825x6->tme_i825x6_el_s_i_cmd = TME_I825X6_CB_CMD_NOP;
|
|
|
|
/* if the interrupt line is currently asserted, negate it: */
|
|
if (i825x6->tme_i825x6_stat_cus_rus_t
|
|
& TME_I825X6_SCB_STAT_MASK) {
|
|
i825x6->tme_i825x6_stat_cus_rus_t &= ~TME_I825X6_SCB_STAT_MASK;
|
|
#if 0
|
|
printf("_tme_i825x6_reset() CALLOUT_INT\n");
|
|
#endif
|
|
|
|
i825x6->tme_i825x6_callout_flags |= TME_I825X6_CALLOUT_INT;
|
|
}
|
|
|
|
/* initialize the address list to match two addresses - the
|
|
broadcast address, and the Individual Address (which is
|
|
initially the same as the broadcast address): */
|
|
i825x6->tme_i825x6_address_count = 2;
|
|
memcpy (i825x6->tme_i825x6_addresses,
|
|
&tme_ethernet_addr_broadcast[0],
|
|
TME_ETHERNET_ADDR_SIZE);
|
|
memcpy (i825x6->tme_i825x6_addresses + TME_ETHERNET_ADDR_SIZE,
|
|
&tme_ethernet_addr_broadcast[0],
|
|
TME_ETHERNET_ADDR_SIZE);
|
|
|
|
/* the next CA follows RESET: */
|
|
i825x6->tme_i825x6_ca_follows_reset = TRUE;
|
|
}
|
|
|
|
/* this hashes an address into a TLB entry: */
|
|
static struct tme_bus_tlb *
|
|
_tme_i825x6_tlb_hash(void *_i825x6,
|
|
tme_bus_addr_t linear_address,
|
|
unsigned int cycles)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = (struct tme_i825x6 *) _i825x6;
|
|
|
|
/* return the TLB entry: */
|
|
return (i825x6->tme_i825x6_tlb_hash
|
|
+ ((((tme_bus_addr32_t) linear_address) >> 10) & (TME_I825X6_TLB_HASH_SIZE - 1)));
|
|
}
|
|
|
|
/* this locks the mutex: */
|
|
static void
|
|
_tme_i825x6_lock(void *_i825x6,
|
|
unsigned int locks)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = (struct tme_i825x6 *) _i825x6;
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
}
|
|
|
|
/* this unlocks the mutex: */
|
|
static void
|
|
_tme_i825x6_unlock(void *_i825x6,
|
|
unsigned int locks)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = (struct tme_i825x6 *) _i825x6;
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
}
|
|
|
|
/* this frees an rx buffer: */
|
|
static struct tme_i825x6_rx_buffer *
|
|
_tme_i825x6_rx_buffer_free(struct tme_i825x6 *i825x6,
|
|
struct tme_i825x6_rx_buffer *rx_buffer)
|
|
{
|
|
struct tme_i825x6_rx_buffer *rx_buffer_next;
|
|
|
|
/* get the next rx buffer: */
|
|
rx_buffer_next = TME_I825X6_RX_BUFFER_NEXT(rx_buffer);
|
|
|
|
/* put this rx buffer on the free list: */
|
|
TME_I825X6_RX_BUFFER_NEXT(rx_buffer) = i825x6->tme_i825x6_rx_buffer_free_list;
|
|
i825x6->tme_i825x6_rx_buffer_free_list = rx_buffer;
|
|
|
|
/* return the next rx buffer: */
|
|
return (rx_buffer_next);
|
|
}
|
|
|
|
/* this allocates a rx buffer: */
|
|
static struct tme_i825x6_rx_buffer *
|
|
_tme_i825x6_rx_buffer_new(struct tme_i825x6 *i825x6,
|
|
struct tme_i825x6_rx_buffer ***__prev)
|
|
{
|
|
struct tme_i825x6_rx_buffer *rx_buffer, **_prev;
|
|
|
|
/* if the free list is not empty: */
|
|
rx_buffer = i825x6->tme_i825x6_rx_buffer_free_list;
|
|
if (rx_buffer != NULL) {
|
|
|
|
/* remove this rx buffer from the free list: */
|
|
i825x6->tme_i825x6_rx_buffer_free_list = TME_I825X6_RX_BUFFER_NEXT(rx_buffer);
|
|
}
|
|
|
|
/* otherwise, the free list is empty: */
|
|
else {
|
|
|
|
/* allocate a new rx buffer: */
|
|
rx_buffer = tme_new(struct tme_i825x6_rx_buffer, 1);
|
|
|
|
/* treat this as an old fast-write TLB entry: */
|
|
rx_buffer->tme_i825x6_rx_buffer_rb_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
}
|
|
|
|
/* add this new rx buffer to the list: */
|
|
_prev = *__prev;
|
|
*_prev = rx_buffer;
|
|
_prev = &TME_I825X6_RX_BUFFER_NEXT(rx_buffer);
|
|
*__prev = _prev;
|
|
|
|
/* return the new buffer: */
|
|
return (rx_buffer);
|
|
}
|
|
|
|
/* given a bus address and a size, this adds rx buffers to a list: */
|
|
static struct tme_i825x6_rx_buffer *
|
|
_tme_i825x6_rx_buffers_add(struct tme_i825x6 *i825x6,
|
|
tme_uint32_t address_init,
|
|
tme_uint32_t size,
|
|
struct tme_i825x6_rx_buffer ***__prev)
|
|
{
|
|
struct tme_i825x6_rx_buffer *rx_buffer, **_prev;
|
|
struct tme_bus_tlb *tlb, tlb_local;
|
|
struct tme_bus_connection *conn_bus;
|
|
tme_bus_addr32_t count_minus_one, count;
|
|
tme_bus_addr32_t tlb_addr_last;
|
|
int err;
|
|
|
|
/* recover the rx buffers list: */
|
|
_prev = *__prev;
|
|
|
|
/* there isn't a last rx buffer yet: */
|
|
rx_buffer = NULL;
|
|
|
|
/* loop while we have more addresses to cover: */
|
|
for (; size > 0; ) {
|
|
|
|
/* hash this address into a TLB entry: */
|
|
tlb = _tme_i825x6_tlb_hash(i825x6,
|
|
address_init,
|
|
TME_BUS_CYCLE_WRITE);
|
|
|
|
/* busy this TLB entry: */
|
|
tme_bus_tlb_busy(tlb);
|
|
|
|
/* if this TLB entry is invalid, or doesn't cover this address, or
|
|
if it doesn't allow writing, reload it: */
|
|
tlb_addr_last = tlb->tme_bus_tlb_addr_last;
|
|
if (tme_bus_tlb_is_invalid(tlb)
|
|
|| address_init < (tme_bus_addr32_t) tlb->tme_bus_tlb_addr_first
|
|
|| address_init > tlb_addr_last
|
|
|| (tlb->tme_bus_tlb_emulator_off_write == TME_EMULATOR_OFF_UNDEF
|
|
&& !(tlb->tme_bus_tlb_cycles_ok & TME_BUS_CYCLE_WRITE))) {
|
|
|
|
/* unbusy this TLB entry for filling: */
|
|
tme_bus_tlb_unbusy_fill(tlb);
|
|
|
|
/* pass this TLB's token: */
|
|
tlb_local.tme_bus_tlb_token = tlb->tme_bus_tlb_token;
|
|
|
|
/* get our bus connection: */
|
|
conn_bus = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
|
|
i825x6->tme_i825x6_device.tme_bus_device_connection,
|
|
&i825x6->tme_i825x6_device.tme_bus_device_connection_rwlock);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* reload the TLB entry: */
|
|
err = (*conn_bus->tme_bus_tlb_fill)
|
|
(conn_bus,
|
|
&tlb_local,
|
|
address_init,
|
|
TME_BUS_CYCLE_WRITE);
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* XXX we could create a poison frame chunk instead of
|
|
aborting: */
|
|
if (err != TME_OK) {
|
|
abort();
|
|
}
|
|
|
|
/* store the TLB entry: */
|
|
*tlb = tlb_local;
|
|
|
|
/* loop to check the newly filled TLB entry: */
|
|
continue;
|
|
}
|
|
|
|
/* see how many addresses we can cover with this TLB entry,
|
|
starting at this address: */
|
|
count_minus_one = (tlb_addr_last - address_init);
|
|
count_minus_one = TME_MIN(count_minus_one,
|
|
(size - 1));
|
|
count = count_minus_one + 1;
|
|
assert (count != 0);
|
|
|
|
/* allocate another rx buffer: */
|
|
rx_buffer = _tme_i825x6_rx_buffer_new(i825x6, &_prev);
|
|
|
|
/* if this TLB entry allows fast writing: */
|
|
if (tlb->tme_bus_tlb_emulator_off_write != TME_EMULATOR_OFF_UNDEF) {
|
|
|
|
/* if this rx buffer was previously a slow rx buffer, free its
|
|
chunk buffer: */
|
|
if (rx_buffer->tme_i825x6_rx_buffer_rb_address != TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
tme_free(rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes);
|
|
}
|
|
|
|
/* this is a fast rx buffer: */
|
|
rx_buffer->tme_i825x6_rx_buffer_rb_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes
|
|
/* XXX FIXME - this breaks volatile: */
|
|
= (tme_uint8_t *) tlb->tme_bus_tlb_emulator_off_write + address_init;
|
|
|
|
/* unbusy the TLB: */
|
|
/* XXX FIXME - this is not thread-safe: */
|
|
tme_bus_tlb_unbusy(tlb);
|
|
}
|
|
|
|
/* otherwise, this TLB entry does not allow fast writing: */
|
|
else {
|
|
|
|
/* if this rx buffer was previously a fast rx buffer,
|
|
allocate a new chunk buffer: */
|
|
if (rx_buffer->tme_i825x6_rx_buffer_rb_address == TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes
|
|
= tme_new(tme_uint8_t,
|
|
count);
|
|
}
|
|
|
|
/* otherwise, if this rx buffer was previously a slow receive
|
|
buffer, with a chunk buffer smaller than what we need,
|
|
reallocate the chunk buffer: */
|
|
else if (rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes_count
|
|
< count) {
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes
|
|
= tme_renew(tme_uint8_t,
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes,
|
|
count);
|
|
}
|
|
|
|
/* this is a slow frame chunk: */
|
|
rx_buffer->tme_i825x6_rx_buffer_rb_address = address_init;
|
|
}
|
|
|
|
/* finish this rx buffer: */
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes_count = count;
|
|
rx_buffer->tme_i825x6_rx_buffer_rbd_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
|
|
/* update the address and size: */
|
|
address_init += count;
|
|
size -= count;
|
|
}
|
|
|
|
/* update the rx buffers list end: */
|
|
*__prev = _prev;
|
|
|
|
/* return the last rx buffer: */
|
|
return (rx_buffer);
|
|
}
|
|
|
|
/* this refills the Free Buffer List: */
|
|
static tme_uint16_t
|
|
_tme_i825x6_fbl_refill(struct tme_i825x6 *i825x6, int from_rfd)
|
|
{
|
|
struct tme_i825x6_rx_buffer *rx_buffer, **_prev;
|
|
tme_uint32_t fbl_size;
|
|
tme_uint32_t rbd_offset_address, rbd_address, rb_address;
|
|
tme_uint16_t rbd_offset, rbd_offset_first;
|
|
tme_uint16_t el_p_size;
|
|
tme_uint16_t size;
|
|
tme_uint32_t value32;
|
|
tme_uint16_t value16;
|
|
int rc;
|
|
|
|
/* assume that there are no free Receive Buffer Descriptors: */
|
|
rbd_offset_first = TME_I825X6_RU_OFFSET_UNDEF;
|
|
|
|
/* find the end of the Free Buffer List: */
|
|
for (_prev = &i825x6->tme_i825x6_fbl;
|
|
(rx_buffer = *_prev) != NULL;
|
|
_prev = &TME_I825X6_RX_BUFFER_NEXT(rx_buffer)) {
|
|
|
|
/* if we don't have the first free Receive Buffer Descriptor yet,
|
|
and this is the last rx buffer for a Receive Buffer, its
|
|
Receive Buffer Descriptor is the first free one: */
|
|
rbd_address = rx_buffer->tme_i825x6_rx_buffer_rbd_address;
|
|
if (rbd_offset_first == TME_I825X6_RU_OFFSET_UNDEF
|
|
&& rbd_address != TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
rbd_offset_first = rbd_address - i825x6->tme_i825x6_scb_base;
|
|
}
|
|
}
|
|
|
|
/* get the address of the next RBD offset: */
|
|
rbd_offset_address = i825x6->tme_i825x6_rbd_offset_address;
|
|
|
|
/* stop now if the address of the next RBD offset is undefined: */
|
|
if (rbd_offset_address == TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
return (rbd_offset_first);
|
|
}
|
|
|
|
/* get the current size of the Free Buffer List: */
|
|
fbl_size = i825x6->tme_i825x6_fbl_size;
|
|
|
|
/* turn Receive Buffers into rx buffers on the Free Buffer List,
|
|
until the Free Buffer List has a maximum-sized Ethernet frame's
|
|
worth of buffers: */
|
|
for (; fbl_size < TME_ETHERNET_FRAME_MAX; ) {
|
|
|
|
/* read in the Receive Buffer Descriptor offset: */
|
|
TME_I825X6_READ16(rbd_offset_address,
|
|
rbd_offset);
|
|
|
|
/* if this Receive Buffer Descriptor offset is in an RFD, stop if
|
|
the offset is all-bits-one, indicating no RBDs: */
|
|
if (from_rfd
|
|
&& rbd_offset == TME_I825X6_RU_OFFSET_UNDEF) {
|
|
break;
|
|
}
|
|
from_rfd = FALSE;
|
|
|
|
/* if we don't have the first free Receive Buffer Descriptor yet,
|
|
this is it: */
|
|
if (rbd_offset_first == TME_I825X6_RU_OFFSET_UNDEF) {
|
|
rbd_offset_first = rbd_offset;
|
|
}
|
|
|
|
/* make the Receive Buffer Descriptor address: */
|
|
rbd_address = i825x6->tme_i825x6_scb_base + rbd_offset;
|
|
|
|
/* read in the Receive Buffer address: */
|
|
TME_I82586_READ24((rbd_address
|
|
+ TME_I82586_RBD_RB_ADDRESS),
|
|
rb_address);
|
|
|
|
/* read in the EL_P_SIZE field: */
|
|
TME_I825X6_READ16((rbd_address
|
|
+ TME_I82586_RBD_EL_P_SIZE),
|
|
el_p_size);
|
|
|
|
/* get the size of this Receive Buffer: */
|
|
size = el_p_size & TME_I825X6_RBD_SIZE_MASK;
|
|
|
|
/* if this Receive Buffer has zero size, stop now. this can
|
|
happen with NetBSD 1.6.x, which zeroes and reinitializes the
|
|
memory for the i825x6 without stopping the Receive Unit: */
|
|
if (size == 0) {
|
|
|
|
/* log a complaint: */
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
0, EBADF,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
_("caught an empty Receive Buffer")));
|
|
|
|
break;
|
|
}
|
|
|
|
/* add this Receive Buffer to the rx buffers: */
|
|
rx_buffer = _tme_i825x6_rx_buffers_add(i825x6,
|
|
rb_address,
|
|
size,
|
|
&_prev);
|
|
|
|
/* on the last rx buffer for a Receive Buffer, set the address of
|
|
the Receive Buffer Descriptor, so we can update its size field: */
|
|
rx_buffer->tme_i825x6_rx_buffer_rbd_address = rbd_address;
|
|
|
|
/* update the amount of space on the Free Buffer list: */
|
|
fbl_size += size;
|
|
|
|
/* if this is the last Receive Buffer Descriptor: */
|
|
if (el_p_size & TME_I825X6_RBD_EL) {
|
|
|
|
/* the address of the next RBD offset is undefined: */
|
|
rbd_offset_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
|
|
/* stop now: */
|
|
break;
|
|
}
|
|
|
|
/* get the address of the next RBD offset: */
|
|
rbd_offset_address
|
|
= (rbd_address
|
|
+ TME_I82586_RBD_RBD_OFFSET);
|
|
}
|
|
|
|
/* terminate the Free Buffer List: */
|
|
*_prev = NULL;
|
|
|
|
/* save the current size of the Free Buffer List: */
|
|
i825x6->tme_i825x6_fbl_size = fbl_size;
|
|
|
|
/* save the address of the next RBD offset: */
|
|
i825x6->tme_i825x6_rbd_offset_address = rbd_offset_address;
|
|
|
|
/* return the address of the first free Receive Buffer Descriptor: */
|
|
return (rbd_offset_first);
|
|
}
|
|
|
|
/* this aborts the Receive Unit: */
|
|
static void
|
|
_tme_i825x6_abort_ru(struct tme_i825x6 *i825x6)
|
|
{
|
|
struct tme_i825x6_rx_buffer *rx_buffer;
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"RU abort"));
|
|
|
|
/* free the Free Buffer List: */
|
|
for (rx_buffer = i825x6->tme_i825x6_fbl;
|
|
rx_buffer != NULL;
|
|
rx_buffer = _tme_i825x6_rx_buffer_free(i825x6, rx_buffer));
|
|
|
|
/* the Receive Unit now has no resources: */
|
|
i825x6->tme_i825x6_rfd_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
i825x6->tme_i825x6_rbd_offset_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
i825x6->tme_i825x6_fbl = NULL;
|
|
i825x6->tme_i825x6_fbl_size = 0;
|
|
}
|
|
|
|
/* this does a DMA directly into transmit frame chunks: */
|
|
static int
|
|
_tme_i825x6_chunks_dma_tx(struct tme_i825x6 *i825x6,
|
|
struct tme_ethernet_frame_chunk *frame_chunks,
|
|
tme_uint32_t address,
|
|
tme_uint32_t size)
|
|
{
|
|
tme_uint32_t count;
|
|
int rc;
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_chunks_dma_tx; xmit %d\n", size);
|
|
#endif
|
|
|
|
/* while we have bytes left to DMA: */
|
|
for (; size > 0; ) {
|
|
|
|
/* get the count of bytes to copy in this iteration: */
|
|
count = frame_chunks->tme_ethernet_frame_chunk_bytes_count;
|
|
if (count == 0) {
|
|
break;
|
|
}
|
|
count = TME_MIN(count, size);
|
|
|
|
/* do the copy: */
|
|
/* XXX FIXME this assumes an i82586: */
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device,
|
|
address,
|
|
count,
|
|
frame_chunks->tme_ethernet_frame_chunk_bytes,
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
if (rc != TME_OK) {
|
|
return (rc);
|
|
}
|
|
|
|
/* update: */
|
|
size -= count;
|
|
frame_chunks->tme_ethernet_frame_chunk_bytes += count;
|
|
if ((frame_chunks->tme_ethernet_frame_chunk_bytes_count -= count) == 0
|
|
&& frame_chunks->tme_ethernet_frame_chunk_next != NULL) {
|
|
*frame_chunks = *frame_chunks->tme_ethernet_frame_chunk_next;
|
|
}
|
|
}
|
|
|
|
/* success: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this does a memcpy directly into transmit frame chunks: */
|
|
static void
|
|
_tme_i825x6_chunks_mem_tx(struct tme_ethernet_frame_chunk *frame_chunks,
|
|
const tme_uint8_t *data,
|
|
unsigned int size)
|
|
{
|
|
unsigned int count;
|
|
|
|
/* while we have bytes left to copy: */
|
|
for (; size > 0; ) {
|
|
|
|
/* get the count of bytes to copy in this iteration: */
|
|
count = frame_chunks->tme_ethernet_frame_chunk_bytes_count;
|
|
if (count == 0) {
|
|
break;
|
|
}
|
|
count = TME_MIN(count, size);
|
|
|
|
/* do the copy: */
|
|
memcpy(frame_chunks->tme_ethernet_frame_chunk_bytes,
|
|
data,
|
|
count);
|
|
|
|
/* update: */
|
|
size -= count;
|
|
frame_chunks->tme_ethernet_frame_chunk_bytes += count;
|
|
if ((frame_chunks->tme_ethernet_frame_chunk_bytes_count -= count) == 0
|
|
&& frame_chunks->tme_ethernet_frame_chunk_next != NULL) {
|
|
*frame_chunks = *frame_chunks->tme_ethernet_frame_chunk_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* this is called to handle a Channel Attention (CA) callout: */
|
|
static tme_uint16_t
|
|
_tme_i825x6_callout_ca(struct tme_i825x6 *i825x6, tme_uint16_t stat_cus_rus_t)
|
|
{
|
|
tme_uint8_t scp[TME_I825X6_SCP_SIZE];
|
|
tme_uint32_t iscp_address, rfd_offset;
|
|
tme_uint8_t iscp[TME_I825X6_ISCP_SIZE];
|
|
tme_uint16_t ack_cuc_r_ruc;
|
|
tme_uint16_t cuc;
|
|
tme_uint16_t value16;
|
|
tme_uint16_t rbd_offset_first;
|
|
int rc;
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_callout_ca()\n");
|
|
#endif
|
|
/* if this CA follows RESET: */
|
|
if (i825x6->tme_i825x6_ca_follows_reset) {
|
|
|
|
/* the next CA will not follow RESET: */
|
|
i825x6->tme_i825x6_ca_follows_reset = FALSE;
|
|
|
|
/* read in the SCP: */
|
|
TME_I825X6_READ(TME_I825X6_SCP_ADDRESS, scp);
|
|
|
|
/* check the SYSBUS byte: */
|
|
assert ((scp[TME_I825X6_SCP_SYSBUS]
|
|
& TME_I825X6_SCP_SYSBUS_MODE_MASK)
|
|
== TME_I825X6_SCP_SYSBUS_MODE_82586);
|
|
|
|
/* get the ISCP address: */
|
|
iscp_address = TME_I82586_GET24(&scp[TME_I825X6_SCP_ISCP_ADDRESS]);
|
|
|
|
/* read in the ISCP: */
|
|
TME_I825X6_READ(iscp_address, iscp);
|
|
|
|
/* get the SCB base and SCB address: */
|
|
i825x6->tme_i825x6_scb_base = TME_I82586_GET24(&iscp[TME_I82586_ISCP_SCB_BASE]);
|
|
i825x6->tme_i825x6_scb_address
|
|
= (i825x6->tme_i825x6_scb_base
|
|
+ TME_I825X6_GET16(&iscp[TME_I82586_ISCP_SCB_OFFSET]));
|
|
|
|
/* "The 82596 clears BUSY": */
|
|
iscp[TME_I825X6_ISCP_BUSY] = 0;
|
|
TME_I825X6_WRITE((iscp_address
|
|
+ TME_I825X6_ISCP_BUSY),
|
|
iscp[TME_I825X6_ISCP_BUSY]);
|
|
|
|
/* "The 82596 ... sets CX and CNR to equal 1 in the SCB": */
|
|
stat_cus_rus_t
|
|
= (TME_I825X6_SCB_STAT_CX
|
|
| TME_I825X6_SCB_STAT_CNA
|
|
| TME_I825X6_SCB_CUS_IDLE
|
|
| TME_I825X6_SCB_RUS_IDLE);
|
|
|
|
/* "The 82596 ... sends an interrupt to the CPU": */
|
|
#if 0
|
|
printf("_tme_i825x6_callout_ca() CALLOUT_INT\n");
|
|
#endif
|
|
i825x6->tme_i825x6_callout_flags = TME_I825X6_CALLOUTS_RUNNING | TME_I825X6_CALLOUT_INT;
|
|
}
|
|
|
|
/* otherwise, this CA does not follow RESET: */
|
|
else {
|
|
|
|
/* read in the SCB command word: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I825X6_SCB_ACK_CUC_R_RUC),
|
|
ack_cuc_r_ruc);
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"SCB command 0x%04x",
|
|
ack_cuc_r_ruc));
|
|
|
|
/* handle a Reset: */
|
|
if (ack_cuc_r_ruc & TME_I825X6_SCB_RESET) {
|
|
_tme_i825x6_reset(i825x6);
|
|
return (TME_I825X6_CA_RESET);
|
|
}
|
|
|
|
/* clear any acknowledged Status bits: */
|
|
stat_cus_rus_t
|
|
&= ~(ack_cuc_r_ruc
|
|
& TME_I825X6_SCB_STAT_MASK);
|
|
|
|
/* if the Command Unit Command isn't a NOP: */
|
|
cuc = (ack_cuc_r_ruc
|
|
& TME_I825X6_SCB_CUC_MASK);
|
|
if (cuc != TME_I825X6_SCB_CUC_NOP) {
|
|
|
|
/* set this Command Unit Command: */
|
|
i825x6->tme_i825x6_cuc = cuc;
|
|
|
|
/* if the Command Unit Command is an Abort, or if the Command
|
|
Unit isn't already Active, run the Command Unit: */
|
|
if ((cuc == TME_I825X6_SCB_CUC_ABORT)
|
|
|| ((stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
!= TME_I825X6_SCB_CUS_ACTIVE)) {
|
|
i825x6->tme_i825x6_callout_flags |= TME_I825X6_CALLOUT_CU;
|
|
}
|
|
}
|
|
|
|
/* dispatch on the Receive Unit command: */
|
|
switch (ack_cuc_r_ruc & TME_I825X6_SCB_RUC_MASK) {
|
|
|
|
case TME_I825X6_SCB_RUC_NOP:
|
|
break;
|
|
|
|
case TME_I825X6_SCB_RUC_START:
|
|
|
|
/* if the Receive Unit is not Idle: */
|
|
switch (stat_cus_rus_t & TME_I82586_SCB_RUS_MASK) {
|
|
case TME_I825X6_SCB_RUS_READY:
|
|
case TME_I825X6_SCB_RUS_SUSPENDED:
|
|
case TME_I825X6_SCB_RUS_ERESOURCE:
|
|
|
|
/* abort the Receive Unit: */
|
|
_tme_i825x6_abort_ru(i825x6);
|
|
break;
|
|
|
|
case TME_I825X6_SCB_RUS_IDLE:
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* the Receive Unit must have no resources: */
|
|
assert (i825x6->tme_i825x6_rfd_address == TME_I825X6_RU_ADDRESS_UNDEF);
|
|
assert (i825x6->tme_i825x6_rbd_offset_address == TME_I825X6_RU_ADDRESS_UNDEF);
|
|
assert (i825x6->tme_i825x6_fbl == NULL && i825x6->tme_i825x6_fbl_size == 0);
|
|
|
|
/* get the RFD offset from the SCB: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I82586_SCB_RFA_OFFSET),
|
|
rfd_offset);
|
|
|
|
/* if the RFD offset is defined: */
|
|
if (rfd_offset != TME_I825X6_RU_OFFSET_UNDEF) {
|
|
|
|
/* set the RFD address: */
|
|
i825x6->tme_i825x6_rfd_address
|
|
= (i825x6->tme_i825x6_scb_base
|
|
+ rfd_offset);
|
|
|
|
/* set the RBD offset address: */
|
|
i825x6->tme_i825x6_rbd_offset_address
|
|
= (i825x6->tme_i825x6_rfd_address
|
|
+ TME_I82586_RFD_RBD_OFFSET);
|
|
|
|
/* refill the Free Buffer List: */
|
|
rbd_offset_first = _tme_i825x6_fbl_refill(i825x6, TRUE);
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"RU start RFD 0x%06x RBD 0x%04x",
|
|
i825x6->tme_i825x6_rfd_address,
|
|
rbd_offset_first));
|
|
}
|
|
|
|
/* FALLTHROUGH: */
|
|
case TME_I825X6_SCB_RUC_RESUME:
|
|
|
|
/* the Receive Unit is now Ready: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_READY);
|
|
break;
|
|
|
|
case TME_I825X6_SCB_RUC_SUSPEND:
|
|
|
|
/* the Receive Unit is now Suspended: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_SUSPENDED);
|
|
break;
|
|
|
|
case TME_I825X6_SCB_RUC_ABORT:
|
|
|
|
/* abort the Receive Unit: */
|
|
_tme_i825x6_abort_ru(i825x6);
|
|
|
|
/* the Receive Unit is now Idle: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_IDLE);
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/* always clear the SCB command word after a CA: */
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I825X6_SCB_ACK_CUC_R_RUC),
|
|
0);
|
|
|
|
/* return the current status word: */
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* this is called to handle a Command Unit (CU) callout: */
|
|
static tme_uint16_t
|
|
_tme_i825x6_callout_cu(struct tme_i825x6 *i825x6, tme_uint16_t stat_cus_rus_t)
|
|
{
|
|
tme_uint16_t cuc;
|
|
tme_uint16_t el_s_i_cmd;
|
|
tme_uint16_t mc_count;
|
|
tme_uint8_t config_bytes[12];
|
|
unsigned int config_byte_count;
|
|
unsigned int callouts;
|
|
tme_uint16_t value16;
|
|
int rc;
|
|
|
|
/* get the SCB Command Unit Command: */
|
|
cuc = i825x6->tme_i825x6_cuc;
|
|
|
|
/* if the Command Unit was active, complete the command that it
|
|
was working on: */
|
|
if ((stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
== TME_I825X6_SCB_CUS_ACTIVE) {
|
|
|
|
/* finish the CB status word. clear B, and set C. if the
|
|
command was aborted, clear OK and set A, else set OK and
|
|
clear A: */
|
|
i825x6->tme_i825x6_c_b_ok_a
|
|
= ((i825x6->tme_i825x6_c_b_ok_a
|
|
& ~TME_I825X6_FLAG_B)
|
|
| TME_I825X6_FLAG_C
|
|
| TME_I825X6_FLAG_OK
|
|
| TME_I825X6_CB_A);
|
|
i825x6->tme_i825x6_c_b_ok_a
|
|
^= ((cuc
|
|
== TME_I825X6_SCB_CUC_ABORT)
|
|
? TME_I825X6_FLAG_OK
|
|
: TME_I825X6_CB_A);
|
|
|
|
/* write the CB status word: */
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I825X6_CB_C_B_OK_A),
|
|
i825x6->tme_i825x6_c_b_ok_a);
|
|
|
|
/* if this command had the I bit set, set CX: */
|
|
if (i825x6->tme_i825x6_el_s_i_cmd & TME_I825X6_CB_I) {
|
|
stat_cus_rus_t |= TME_I825X6_SCB_STAT_CX;
|
|
}
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"CU finish 0x%06x status 0x%04x",
|
|
i825x6->tme_i825x6_cb_address,
|
|
i825x6->tme_i825x6_c_b_ok_a));
|
|
}
|
|
|
|
/* dispatch on the Command Unit command: */
|
|
switch (cuc) {
|
|
|
|
case TME_I825X6_SCB_CUC_NOP:
|
|
break;
|
|
|
|
case TME_I825X6_SCB_CUC_START:
|
|
|
|
/* get the CBL address: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I82586_SCB_CBL_OFFSET),
|
|
i825x6->tme_i825x6_cb_address_next);
|
|
i825x6->tme_i825x6_cb_address_next += i825x6->tme_i825x6_scb_base;
|
|
|
|
/* FALLTHROUGH */
|
|
case TME_I825X6_SCB_CUC_RESUME:
|
|
|
|
/* the Command Unit is now active: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I825X6_SCB_CUS_MASK)
|
|
| TME_I825X6_SCB_CUS_ACTIVE);
|
|
break;
|
|
|
|
case TME_I825X6_SCB_CUC_ABORT:
|
|
|
|
/* the Command Unit is now Idle: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I825X6_SCB_CUS_MASK)
|
|
| TME_I825X6_SCB_CUS_IDLE);
|
|
break;
|
|
|
|
case TME_I825X6_SCB_CUC_SUSPEND:
|
|
|
|
/* the Command Unit is now Suspended: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I825X6_SCB_CUS_MASK)
|
|
| TME_I825X6_SCB_CUS_SUSPENDED);
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* if the Command Unit is not active, return now: */
|
|
if ((stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
!= TME_I825X6_SCB_CUS_ACTIVE) {
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* advance to the next command: */
|
|
i825x6->tme_i825x6_cb_address = i825x6->tme_i825x6_cb_address_next;
|
|
|
|
/* if there is no next command, the Command Unit is now Idle: */
|
|
if (i825x6->tme_i825x6_cb_address == TME_I825X6_CB_ADDRESS_IDLE) {
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"CU idle"));
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I825X6_SCB_CUS_MASK)
|
|
| TME_I825X6_SCB_CUS_IDLE);
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* start the CB status word: */
|
|
i825x6->tme_i825x6_c_b_ok_a = TME_I825X6_FLAG_B;
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I825X6_CB_C_B_OK_A),
|
|
i825x6->tme_i825x6_c_b_ok_a);
|
|
|
|
/* get the CB command word: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I825X6_CB_EL_S_I_CMD),
|
|
i825x6->tme_i825x6_el_s_i_cmd);
|
|
el_s_i_cmd = i825x6->tme_i825x6_el_s_i_cmd;
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"CU start 0x%06x el_s_i_cmd 0x%04x",
|
|
i825x6->tme_i825x6_cb_address,
|
|
el_s_i_cmd));
|
|
|
|
/* if EL is set, there is no next Command Block, and when
|
|
the Command Unit tries to execute the next command it
|
|
will go Idle: */
|
|
if (el_s_i_cmd & TME_I825X6_FLAG_EL) {
|
|
i825x6->tme_i825x6_cb_address_next = TME_I825X6_CB_ADDRESS_IDLE;
|
|
}
|
|
|
|
/* otherwise, get the address of the next Command Block: */
|
|
else {
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_LINK_OFFSET),
|
|
i825x6->tme_i825x6_cb_address_next);
|
|
i825x6->tme_i825x6_cb_address_next += i825x6->tme_i825x6_scb_base;
|
|
}
|
|
|
|
/* if S is set, after the Command Unit executes this command
|
|
it will Suspend, else it will stay Active: */
|
|
i825x6->tme_i825x6_cuc
|
|
= ((el_s_i_cmd & TME_I825X6_FLAG_S)
|
|
? TME_I825X6_SCB_CUC_SUSPEND
|
|
: TME_I825X6_SCB_CUC_NOP);
|
|
|
|
/* assume that this command will complete now: */
|
|
callouts = TME_I825X6_CALLOUT_CU;
|
|
|
|
/* dispatch on this command: */
|
|
switch (el_s_i_cmd & TME_I825X6_CB_CMD_MASK) {
|
|
|
|
case TME_I825X6_CB_CMD_NOP:
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_SETUP_IA:
|
|
|
|
/* read in the new Individual Address, into the second element of
|
|
the addresses array: */
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device,
|
|
(i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X),
|
|
TME_ETHERNET_ADDR_SIZE,
|
|
(i825x6->tme_i825x6_addresses
|
|
+ TME_ETHERNET_ADDR_SIZE),
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
assert (rc == TME_OK);
|
|
|
|
/* call out an Ethernet configuration change: */
|
|
callouts |= TME_I825X6_CALLOUT_CONFIG;
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_CONFIGURE:
|
|
|
|
/* read in bytes 0 and 1 of the configuration: */
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device,
|
|
(i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X),
|
|
sizeof(tme_uint16_t),
|
|
config_bytes,
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
assert (rc == TME_OK);
|
|
|
|
/* "In the 82586 mode the maximum number of configuration bytes
|
|
is 12. Any number larger than 12 will be reduced to 12 and
|
|
any number less than 4 will be increased to 4." */
|
|
config_byte_count = (config_bytes[0] & 0x0f);
|
|
if (config_byte_count < 4) {
|
|
config_byte_count = 4;
|
|
}
|
|
else if (config_byte_count > 12) {
|
|
config_byte_count = 12;
|
|
}
|
|
|
|
/* read in the remaining bytes of the configuration: */
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device,
|
|
(i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X
|
|
+ sizeof(tme_uint16_t)),
|
|
(config_byte_count
|
|
- sizeof(tme_uint16_t)),
|
|
(config_bytes
|
|
+ sizeof(tme_uint16_t)),
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
assert (rc == TME_OK);
|
|
|
|
/* byte 3: */
|
|
if (config_byte_count > 3) {
|
|
|
|
/* AL-LOC: */
|
|
i825x6->tme_i825x6_al_loc = config_bytes[3] & 0x08;
|
|
|
|
/* Address Length: */
|
|
if ((config_bytes[3] & 0x07) != TME_ETHERNET_ADDR_SIZE) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/* byte 8: */
|
|
if (config_byte_count > 8) {
|
|
|
|
/* Promiscuous Mode: */
|
|
i825x6->tme_i825x6_prm = config_bytes[8] & 0x01;
|
|
|
|
/* call out an Ethernet configuration change: */
|
|
callouts |= TME_I825X6_CALLOUT_CONFIG;
|
|
}
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_SETUP_MC:
|
|
|
|
/* read in the MC COUNT byte count: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X),
|
|
mc_count);
|
|
mc_count -= (mc_count % TME_ETHERNET_ADDR_SIZE);
|
|
|
|
/* reallocate the addresses list, which is always at least two
|
|
elements long - the broadcast address and the individual
|
|
address: */
|
|
i825x6->tme_i825x6_address_count = (2 + (mc_count / TME_ETHERNET_ADDR_SIZE));
|
|
i825x6->tme_i825x6_addresses
|
|
= tme_renew(tme_uint8_t,
|
|
i825x6->tme_i825x6_addresses,
|
|
(i825x6->tme_i825x6_address_count
|
|
* TME_ETHERNET_ADDR_SIZE));
|
|
|
|
/* read in the new Multicast addresses: */
|
|
rc = tme_bus_device_dma_read_16(&i825x6->tme_i825x6_device,
|
|
(i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X
|
|
+ sizeof(mc_count)),
|
|
mc_count,
|
|
(i825x6->tme_i825x6_addresses
|
|
+ (2
|
|
* TME_ETHERNET_ADDR_SIZE)),
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
assert (rc == TME_OK);
|
|
|
|
/* call out an Ethernet configuration change: */
|
|
callouts |= TME_I825X6_CALLOUT_CONFIG;
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_TRANSMIT:
|
|
|
|
/* call out only a control change; this command is not
|
|
completing now: */
|
|
callouts = TME_I825X6_CALLOUT_CTRL;
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_TDR:
|
|
|
|
/* write a successful TDR status: */
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_CB_X),
|
|
TME_I82586_TDR_STATUS_OK);
|
|
break;
|
|
|
|
case TME_I825X6_CB_CMD_DUMP:
|
|
abort();
|
|
|
|
case TME_I825X6_CB_CMD_DIAGNOSE:
|
|
break;
|
|
}
|
|
|
|
/* add to the callouts and return the current status: */
|
|
i825x6->tme_i825x6_callout_flags |= callouts;
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* this is called to handle a Receive Unit (RU) callout: */
|
|
static tme_uint16_t
|
|
_tme_i825x6_callout_ru(struct tme_i825x6 *i825x6, tme_uint16_t stat_cus_rus_t)
|
|
{
|
|
struct tme_ethernet_connection *conn_eth;
|
|
tme_ethernet_fid_t frame_id;
|
|
tme_uint16_t el_s_sf;
|
|
tme_uint16_t eof_f_act_count;
|
|
tme_uint16_t c_b_ok_status;
|
|
struct tme_i825x6_rx_buffer *rx_buffers, *rx_buffer, **_prev;
|
|
unsigned int rbd_size, rx_buffer_size;
|
|
tme_uint32_t rfd_address;
|
|
tme_uint16_t rbd_offset_next;
|
|
tme_uint16_t discards;
|
|
int resid;
|
|
int rc;
|
|
tme_uint16_t value16;
|
|
|
|
/* start a list of rx buffers: */
|
|
rx_buffers = NULL;
|
|
_prev = &rx_buffers;
|
|
|
|
/* start counting the number of bytes in the first Receive Buffer: */
|
|
rbd_size = 0;
|
|
|
|
/* assume that we have no Receive Frame Descriptor: */
|
|
rfd_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
el_s_sf = 0;
|
|
|
|
/* if the Receive Unit is Active and we have a Receive Frame Descriptor: */
|
|
if (((stat_cus_rus_t & TME_I82586_SCB_RUS_MASK)
|
|
== TME_I825X6_SCB_RUS_READY)
|
|
&& ((rfd_address = i825x6->tme_i825x6_rfd_address)
|
|
!= TME_I825X6_RU_ADDRESS_UNDEF)) {
|
|
|
|
/* get the flags word: */
|
|
TME_I825X6_READ16((rfd_address
|
|
+ TME_I825X6_RFD_EL_S_SF),
|
|
el_s_sf);
|
|
|
|
/* if AL-LOC is set to zero, the Ethernet/802.3 MAC header will be
|
|
received into the Receive Frame Descriptor: */
|
|
if (i825x6->tme_i825x6_al_loc == 0) {
|
|
|
|
/* make a set of rx buffers out of the Ethernet header part of
|
|
the Receive Frame Descriptor: */
|
|
_tme_i825x6_rx_buffers_add(i825x6,
|
|
(rfd_address
|
|
+ TME_I82586_RFD_RBD_ETH_HEADER),
|
|
TME_ETHERNET_HEADER_SIZE,
|
|
&_prev);
|
|
|
|
/* account for the Receive Frame Descriptor header bytes in the
|
|
Free Buffer List space and Receive Buffer size calculations: */
|
|
i825x6->tme_i825x6_fbl_size += TME_ETHERNET_HEADER_SIZE;
|
|
rbd_size = 0 - TME_ETHERNET_HEADER_SIZE;
|
|
}
|
|
|
|
/* take the Free Buffer List: */
|
|
*_prev = i825x6->tme_i825x6_fbl;
|
|
}
|
|
|
|
/* get the Ethernet connection: */
|
|
conn_eth = i825x6->tme_i825x6_eth_connection;
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* do the callout: */
|
|
resid = (conn_eth == NULL
|
|
? 0
|
|
: ((*conn_eth->tme_ethernet_connection_read)
|
|
(conn_eth,
|
|
&frame_id,
|
|
&rx_buffers->tme_i825x6_rx_buffer_frame_chunk,
|
|
TME_ETHERNET_READ_NEXT)));
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* if the read failed: */
|
|
if (resid <= 0) {
|
|
|
|
/* convention dictates that we forget that the connection was
|
|
readable, which we already have done by clearing the
|
|
CALLOUT_READ flag: */
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* mark that we need to loop to callout to read more frames: */
|
|
i825x6->tme_i825x6_callout_flags |= TME_I825X6_CALLOUT_READ;
|
|
|
|
/* return now if the Receive Unit is Idle: */
|
|
if ((stat_cus_rus_t & TME_I82586_SCB_RUS_MASK)
|
|
== TME_I825X6_SCB_RUS_IDLE) {
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"RU RFD 0x%06x el_s_sf 0x%04x",
|
|
rfd_address,
|
|
el_s_sf));
|
|
|
|
/* we must have received more than just headers: */
|
|
assert (resid > TME_ETHERNET_HEADER_SIZE);
|
|
|
|
/* if we have a Receive Frame Descriptor: */
|
|
if (rfd_address != TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
|
|
/* walk the rx buffers, stopping early only if we have exhausted
|
|
the Ethernet frame *and* we have updated the last Receive
|
|
Buffer used to receive it: */
|
|
for (rx_buffer = rx_buffers;
|
|
(rx_buffer != NULL
|
|
&& (resid > 0
|
|
|| rbd_size > 0)); ) {
|
|
|
|
/* calculate the number of bytes used in this rx buffer. since
|
|
a single Receive Buffer can be split into many rx buffers,
|
|
the Ethernet frame may not fill all of them, so this can be
|
|
zero: */
|
|
rx_buffer_size
|
|
= TME_MIN((unsigned int) resid,
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes_count);
|
|
|
|
/* this many more bytes have been received into this Receive Buffer: */
|
|
rbd_size += rx_buffer_size;
|
|
|
|
/* this many more bytes have been accounted for in the Ethernet frame: */
|
|
resid -= rx_buffer_size;
|
|
|
|
/* if this was a slow frame chunk: */
|
|
if (rx_buffer->tme_i825x6_rx_buffer_rb_address
|
|
!= TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
|
|
/* DMA the contents out: */
|
|
rc = tme_bus_device_dma_write_16(&i825x6->tme_i825x6_device,
|
|
rx_buffer->tme_i825x6_rx_buffer_rb_address,
|
|
rx_buffer_size,
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes,
|
|
TME_I825X6_LOCKS_DEFAULT);
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* if this was the last rx buffer for a Receive Buffer: */
|
|
if (rx_buffer->tme_i825x6_rx_buffer_rbd_address
|
|
!= TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
|
|
/* make the EOF_F_ACT_COUNT field: */
|
|
assert (rbd_size > 0 && rbd_size <= TME_I825X6_RBD_ACT_COUNT_MASK);
|
|
eof_f_act_count = ((resid == 0
|
|
? TME_I825X6_RBD_EOF
|
|
: 0)
|
|
| TME_I825X6_RBD_F
|
|
| rbd_size);
|
|
|
|
/* write the EOF_F_ACT_COUNT field out to the Receive Buffer
|
|
Descriptor: */
|
|
TME_I825X6_WRITE16((rx_buffer->tme_i825x6_rx_buffer_rbd_address
|
|
+ TME_I825X6_RBD_EOF_F_ACT_COUNT),
|
|
eof_f_act_count);
|
|
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"RU RBD 0x%06x last-size 0x%04x eof_f_act_count 0x%04x",
|
|
rx_buffer->tme_i825x6_rx_buffer_rbd_address,
|
|
rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes_count,
|
|
eof_f_act_count));
|
|
|
|
/* we're starting on the next Receive Buffer: */
|
|
rbd_size = 0;
|
|
}
|
|
|
|
/* free this rx buffer and move to the next rx buffer: */
|
|
i825x6->tme_i825x6_fbl_size -= rx_buffer->tme_i825x6_rx_buffer_frame_chunk.tme_ethernet_frame_chunk_bytes_count;
|
|
rx_buffer = _tme_i825x6_rx_buffer_free(i825x6, rx_buffer);
|
|
}
|
|
|
|
/* update the Free Buffer List: */
|
|
assert ((rx_buffer != NULL) == (i825x6->tme_i825x6_fbl_size != 0));
|
|
i825x6->tme_i825x6_fbl = rx_buffer;
|
|
|
|
/* make the C_B_OK_STATUS field: */
|
|
c_b_ok_status = (TME_I825X6_FLAG_C
|
|
| (resid == 0
|
|
? TME_I825X6_FLAG_OK
|
|
: TME_I825X6_RFD_STATUS_RNR));
|
|
|
|
/* write the C_B_OK_STATUS field in the RFD: */
|
|
TME_I825X6_WRITE16((rfd_address
|
|
+ TME_I825X6_RFD_C_B_OK_STATUS),
|
|
c_b_ok_status);
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_callout_ru() signal int\n");
|
|
#endif
|
|
/* signal an interrupt: */
|
|
stat_cus_rus_t |= TME_I825X6_SCB_STAT_FR;
|
|
|
|
/* if EL is set, there is no next Receive Frame Descriptor,
|
|
and when the Receive Unit tries to receive another frame
|
|
it will go No Resources: */
|
|
if (el_s_sf & TME_I825X6_FLAG_EL) {
|
|
rfd_address = TME_I825X6_RU_ADDRESS_UNDEF;
|
|
}
|
|
|
|
/* otherwise, get the address of the next Receive Frame Descriptor: */
|
|
else {
|
|
TME_I825X6_READ16((rfd_address
|
|
+ TME_I82586_RFD_LINK_OFFSET),
|
|
rfd_address);
|
|
rfd_address += i825x6->tme_i825x6_scb_base;
|
|
}
|
|
|
|
/* set the next RFD address: */
|
|
i825x6->tme_i825x6_rfd_address = rfd_address;
|
|
|
|
/* if there is a next RFD address: */
|
|
if (rfd_address != TME_I825X6_RU_ADDRESS_UNDEF) {
|
|
|
|
/* refill the Free Buffer List: */
|
|
rbd_offset_next = _tme_i825x6_fbl_refill(i825x6, FALSE);
|
|
|
|
/* write the offset of the first free Receive Buffer Descriptor
|
|
in the RBD Offset field in the RFD: */
|
|
TME_I825X6_WRITE16((rfd_address
|
|
+ TME_I82586_RFD_RBD_OFFSET),
|
|
rbd_offset_next);
|
|
}
|
|
|
|
/* if S is set, the Receive Unit becomes Suspended: */
|
|
if (el_s_sf & TME_I825X6_FLAG_S) {
|
|
|
|
/* the Receive Unit is now Suspended: */
|
|
stat_cus_rus_t
|
|
= ((stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_SUSPENDED);
|
|
}
|
|
}
|
|
|
|
/* otherwise, we had no Receive Frame Descriptor, so we had
|
|
to discard this packet entirely: */
|
|
else {
|
|
|
|
/* account for the discarded packet: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I82586_SCB_ERRORS_RESOURCE),
|
|
discards);
|
|
discards++;
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I82586_SCB_ERRORS_RESOURCE),
|
|
discards);
|
|
}
|
|
|
|
/* if we ran out of resources: */
|
|
if (resid > 0) {
|
|
|
|
/* the receiver is now out of resources: */
|
|
stat_cus_rus_t = ((stat_cus_rus_t
|
|
& ~TME_I82586_SCB_RUS_MASK)
|
|
| TME_I825X6_SCB_RUS_ERESOURCE);
|
|
}
|
|
|
|
/* done: */
|
|
return (stat_cus_rus_t);
|
|
}
|
|
|
|
/* the i825x6 callout function. it must be called with the mutex locked: */
|
|
static void
|
|
_tme_i825x6_callout(struct tme_i825x6 *i825x6, int new_callouts)
|
|
{
|
|
struct tme_ethernet_connection *conn_eth;
|
|
struct tme_bus_connection *conn_bus;
|
|
int callouts, later_callouts;
|
|
unsigned int ctrl;
|
|
struct tme_ethernet_config config;
|
|
int rc;
|
|
tme_uint16_t stat_cus_rus_t;
|
|
tme_uint16_t value16;
|
|
int int_asserted;
|
|
unsigned int address_i;
|
|
|
|
/* add in any new callouts: */
|
|
i825x6->tme_i825x6_callout_flags |= new_callouts;
|
|
|
|
/* if this function is already running in another thread, simply
|
|
return now. the other thread will do our work: */
|
|
if (i825x6->tme_i825x6_callout_flags & TME_I825X6_CALLOUTS_RUNNING) {
|
|
return;
|
|
}
|
|
|
|
/* callouts are now running: */
|
|
i825x6->tme_i825x6_callout_flags |= TME_I825X6_CALLOUTS_RUNNING;
|
|
|
|
/* assume that we won't need any later callouts: */
|
|
later_callouts = 0;
|
|
|
|
/* loop while callouts are needed: */
|
|
for (; (callouts = i825x6->tme_i825x6_callout_flags) & TME_I825X6_CALLOUTS_MASK; ) {
|
|
|
|
/* clear the needed callouts: */
|
|
i825x6->tme_i825x6_callout_flags = callouts & ~TME_I825X6_CALLOUTS_MASK;
|
|
callouts &= TME_I825X6_CALLOUTS_MASK;
|
|
|
|
/* get this card's connection: */
|
|
conn_eth = i825x6->tme_i825x6_eth_connection;
|
|
|
|
/* get the current SCB status word. this word is referenced and
|
|
updated throughout the body of this for loop; any changes made
|
|
are written out at the bottom of the loop: */
|
|
stat_cus_rus_t = i825x6->tme_i825x6_stat_cus_rus_t;
|
|
|
|
/* if we need to handle a Channel Attention: */
|
|
if (callouts & TME_I825X6_CALLOUT_CA) {
|
|
stat_cus_rus_t = _tme_i825x6_callout_ca(i825x6, stat_cus_rus_t);
|
|
if (stat_cus_rus_t == TME_I825X6_CA_RESET) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* if we need to run the Command Unit: */
|
|
if (callouts & TME_I825X6_CALLOUT_CU) {
|
|
stat_cus_rus_t = _tme_i825x6_callout_cu(i825x6, stat_cus_rus_t);
|
|
}
|
|
|
|
/* if we need to call out new control information: */
|
|
if (callouts & TME_I825X6_CALLOUT_CTRL) {
|
|
|
|
/* form the new ctrl: */
|
|
ctrl = 0;
|
|
if (((stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
== TME_I825X6_SCB_CUS_ACTIVE)
|
|
&& ((i825x6->tme_i825x6_el_s_i_cmd
|
|
& TME_I825X6_CB_CMD_MASK)
|
|
== TME_I825X6_CB_CMD_TRANSMIT)) {
|
|
ctrl |= TME_ETHERNET_CTRL_OK_READ;
|
|
}
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_callout; \n");
|
|
#endif
|
|
/* do the callout: */
|
|
rc = (conn_eth != NULL
|
|
? ((*conn_eth->tme_ethernet_connection_ctrl)
|
|
(conn_eth,
|
|
ctrl))
|
|
: TME_OK);
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* if the callout was unsuccessful, remember that at some later
|
|
time this callout should be attempted again: */
|
|
if (rc != TME_OK) {
|
|
later_callouts |= TME_I825X6_CALLOUT_CTRL;
|
|
}
|
|
}
|
|
|
|
/* if we need to call out new config information: */
|
|
if (callouts & TME_I825X6_CALLOUT_CONFIG) {
|
|
|
|
/* form the new config: */
|
|
memset(&config, 0, sizeof(config));
|
|
|
|
/* our Ethernet addresses: */
|
|
config.tme_ethernet_config_addr_count = i825x6->tme_i825x6_address_count;
|
|
config.tme_ethernet_config_addrs
|
|
= tme_new(const tme_uint8_t *,
|
|
i825x6->tme_i825x6_address_count);
|
|
for (address_i = 0;
|
|
address_i < i825x6->tme_i825x6_address_count;
|
|
address_i++) {
|
|
config.tme_ethernet_config_addrs[address_i]
|
|
= &i825x6->tme_i825x6_addresses[(address_i
|
|
* TME_ETHERNET_ADDR_SIZE)];
|
|
}
|
|
|
|
/* our config flags: */
|
|
config.tme_ethernet_config_flags
|
|
= (TME_ETHERNET_CONFIG_NORMAL
|
|
| (i825x6->tme_i825x6_prm
|
|
? TME_ETHERNET_CONFIG_PROMISC
|
|
: 0));
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* do the callout: */
|
|
rc = (conn_eth == NULL
|
|
? TME_OK
|
|
: ((*conn_eth->tme_ethernet_connection_config)
|
|
(conn_eth,
|
|
&config)));
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* free the Ethernet address pointer array: */
|
|
tme_free(config.tme_ethernet_config_addrs);
|
|
|
|
/* if the callout was unsuccessful, remember that at some later
|
|
time this callout should be attempted again: */
|
|
if (rc != TME_OK) {
|
|
later_callouts |= TME_I825X6_CALLOUT_CONFIG;
|
|
}
|
|
}
|
|
|
|
/* if the Ethernet is readable: */
|
|
if (callouts & TME_I825X6_CALLOUT_READ) {
|
|
|
|
/* if the Receive Unit is Suspended, make this callout later: */
|
|
if ((stat_cus_rus_t
|
|
& TME_I82586_SCB_RUS_MASK)
|
|
== TME_I825X6_SCB_RUS_SUSPENDED) {
|
|
later_callouts |= TME_I825X6_CALLOUT_READ;
|
|
}
|
|
|
|
/* otherwise, the Receive Unit is not Suspended, so read this
|
|
frame. the frame will not be stored if the Receive Unit is
|
|
in the Idle or No Resources state: */
|
|
else {
|
|
stat_cus_rus_t = _tme_i825x6_callout_ru(i825x6, stat_cus_rus_t);
|
|
}
|
|
}
|
|
|
|
/* note if the Command Unit left the Active state: */
|
|
if (((i825x6->tme_i825x6_stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
== TME_I825X6_SCB_CUS_ACTIVE)
|
|
&& ((stat_cus_rus_t
|
|
& TME_I825X6_SCB_CUS_MASK)
|
|
!= TME_I825X6_SCB_CUS_ACTIVE)) {
|
|
stat_cus_rus_t |= TME_I825X6_SCB_STAT_CNA;
|
|
}
|
|
|
|
/* note if the Receive Unit left the Ready state: */
|
|
if (((i825x6->tme_i825x6_stat_cus_rus_t
|
|
& TME_I82586_SCB_RUS_MASK)
|
|
== TME_I825X6_SCB_RUS_READY)
|
|
&& ((stat_cus_rus_t
|
|
& TME_I82586_SCB_RUS_MASK)
|
|
!= TME_I825X6_SCB_RUS_READY)) {
|
|
stat_cus_rus_t |= TME_I825X6_SCB_STAT_RNR;
|
|
}
|
|
|
|
/* if our Status bits have changed such that our interrupt signal
|
|
changes, we need to call out an interrupt: */
|
|
if (!(i825x6->tme_i825x6_stat_cus_rus_t
|
|
& TME_I825X6_SCB_STAT_MASK)
|
|
!= !(stat_cus_rus_t
|
|
& TME_I825X6_SCB_STAT_MASK)) {
|
|
#if 0
|
|
printf("_tme_i825x6_callout() CALLOUT_INT\n");
|
|
#endif
|
|
i825x6->tme_i825x6_callout_flags |= TME_I825X6_CALLOUT_INT;
|
|
}
|
|
|
|
/* if our SCB status word has changed, write it out: */
|
|
if (stat_cus_rus_t != i825x6->tme_i825x6_stat_cus_rus_t) {
|
|
|
|
/* log: */
|
|
tme_log(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
100, TME_OK,
|
|
(&i825x6->tme_i825x6_element->tme_element_log_handle,
|
|
"SCB status 0x%04x -> 0x%04x",
|
|
i825x6->tme_i825x6_stat_cus_rus_t,
|
|
stat_cus_rus_t));
|
|
|
|
TME_I825X6_WRITE16((i825x6->tme_i825x6_scb_address
|
|
+ TME_I825X6_SCB_STAT_CUS_RUS_T),
|
|
stat_cus_rus_t);
|
|
i825x6->tme_i825x6_stat_cus_rus_t = stat_cus_rus_t;
|
|
}
|
|
|
|
/* if we need to call out a change to our interrupt signal: */
|
|
if (callouts & TME_I825X6_CALLOUT_INT) {
|
|
|
|
/* see if the interrupt signal should be asserted or negated: */
|
|
int_asserted = (stat_cus_rus_t & TME_I825X6_SCB_STAT_MASK);
|
|
|
|
/* unlock our mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* get our bus connection: */
|
|
conn_bus = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
|
|
i825x6->tme_i825x6_device.tme_bus_device_connection,
|
|
&i825x6->tme_i825x6_device.tme_bus_device_connection_rwlock);
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_callout; signal int - asserted %x\n", (int)int_asserted);
|
|
#endif
|
|
/* call out the bus interrupt signal edge: */
|
|
rc = (*conn_bus->tme_bus_signal)
|
|
(conn_bus,
|
|
TME_BUS_SIGNAL_INT_UNSPEC
|
|
| (int_asserted
|
|
? TME_BUS_SIGNAL_LEVEL_ASSERTED
|
|
: TME_BUS_SIGNAL_LEVEL_NEGATED));
|
|
|
|
/* lock our mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* if this callout failed, remember that at some later time this
|
|
callout should be attempted again: */
|
|
if (rc != TME_OK) {
|
|
#if 0
|
|
printf("_tme_i825x6_callout() later CALLOUT_INT\n");
|
|
#endif
|
|
later_callouts |= TME_I825X6_CALLOUT_INT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* put in any later callouts, and clear that callouts are running: */
|
|
i825x6->tme_i825x6_callout_flags = later_callouts;
|
|
}
|
|
|
|
/* the i825x6 bus signal handler: */
|
|
static int
|
|
_tme_i825x6_signal(void *_i825x6,
|
|
unsigned int signal)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
int new_callouts;
|
|
unsigned int level;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = (struct tme_i825x6 *) _i825x6;
|
|
|
|
/* assume we won't need any new callouts: */
|
|
new_callouts = 0;
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* take out the signal level: */
|
|
level = signal & TME_BUS_SIGNAL_LEVEL_MASK;
|
|
signal = TME_BUS_SIGNAL_WHICH(signal);
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_signal() level %x, signal %x; switch index %x\n",
|
|
(int)level, (int)signal,
|
|
(int)(signal - i825x6->tme_i825x6_bus_signals.tme_bus_signals_first));
|
|
#endif
|
|
/* dispatch on the generic bus signals: */
|
|
switch (signal) {
|
|
case TME_BUS_SIGNAL_RESET:
|
|
if (level == TME_BUS_SIGNAL_LEVEL_ASSERTED) {
|
|
_tme_i825x6_reset(i825x6);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* dispatch on the i825x6 bus signals: */
|
|
if (signal >= i825x6->tme_i825x6_bus_signals.tme_bus_signals_first) {
|
|
switch (signal - i825x6->tme_i825x6_bus_signals.tme_bus_signals_first) {
|
|
|
|
case TME_I825X6_SIGNAL_CA:
|
|
if (level == TME_BUS_SIGNAL_LEVEL_ASSERTED) {
|
|
new_callouts |= TME_I825X6_CALLOUT_CA;
|
|
} else {
|
|
#if 0
|
|
printf("TME_I825X6_SIGNAL_CA; deasserted!!!!\n");
|
|
#endif
|
|
#if 1
|
|
i825x6->tme_i825x6_stat_cus_rus_t &= ~TME_I825X6_SCB_STAT_MASK;
|
|
new_callouts |= TME_I825X6_CALLOUT_INT;
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case TME_I825X6_SIGNAL_LOOP:
|
|
#if 0
|
|
if (level == TME_BUS_SIGNAL_LEVEL_ASSERTED) {
|
|
abort();
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* make any new callouts: */
|
|
_tme_i825x6_callout(i825x6, new_callouts);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* no faults: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this is called when a device changes its configuration: */
|
|
static int
|
|
_tme_i825x6_config(struct tme_ethernet_connection *conn_eth,
|
|
struct tme_ethernet_config *config)
|
|
{
|
|
/* we don't care when other devices on the Ethernet
|
|
reconfigure themselves: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this is called when control lines change: */
|
|
static int
|
|
_tme_i825x6_ctrl(struct tme_ethernet_connection *conn_eth,
|
|
unsigned int ctrl)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
int new_callouts;
|
|
|
|
/* recover our data structures: */
|
|
i825x6 = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* assume that we won't need any new callouts: */
|
|
new_callouts = 0;
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* if this connection is readable, call out a read: */
|
|
if (ctrl & TME_ETHERNET_CTRL_OK_READ) {
|
|
new_callouts |= TME_I825X6_CALLOUT_READ;
|
|
}
|
|
|
|
/* make any new callouts: */
|
|
_tme_i825x6_callout(i825x6, new_callouts);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this is called to read frames (from the i825x6 perspective, to transmit them): */
|
|
static int
|
|
_tme_i825x6_read(struct tme_ethernet_connection *conn_eth,
|
|
tme_ethernet_fid_t *_frame_id,
|
|
struct tme_ethernet_frame_chunk *frame_chunks,
|
|
unsigned int flags)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
int new_callouts;
|
|
struct tme_ethernet_frame_chunk frame_chunk_buffer;
|
|
tme_uint32_t tbd_address, tb_address;
|
|
tme_uint16_t eof_size;
|
|
tme_uint16_t c_b_ok_a;
|
|
tme_uint16_t value16;
|
|
tme_uint32_t value32;
|
|
int rc, err;
|
|
int length;
|
|
|
|
/* recover our data structures: */
|
|
i825x6 = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* assume that we won't need any new callouts: */
|
|
new_callouts = 0;
|
|
|
|
/* start our local copy of the caller's frame chunks: */
|
|
if (frame_chunks != NULL) {
|
|
frame_chunk_buffer = *frame_chunks;
|
|
}
|
|
else {
|
|
frame_chunk_buffer.tme_ethernet_frame_chunk_bytes_count = 0;
|
|
}
|
|
frame_chunks = &frame_chunk_buffer;
|
|
|
|
/* lock our mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* assume that we will have no packet to transmit: */
|
|
length = 0;
|
|
|
|
/* if we have a packet to transmit: */
|
|
if ((i825x6->tme_i825x6_el_s_i_cmd
|
|
& TME_I825X6_CB_CMD_MASK)
|
|
== TME_I825X6_CB_CMD_TRANSMIT) {
|
|
|
|
#if 0
|
|
printf("_tme_i825x6_read; xmit\n");
|
|
#endif
|
|
|
|
/* assume that we will succeed: */
|
|
err = TME_OK;
|
|
do {
|
|
|
|
/* a helper macro for DMAing and copying data into chunks: */
|
|
#define CHUNKS_DMA_TX(addr, size) \
|
|
err = _tme_i825x6_chunks_dma_tx(i825x6, frame_chunks, (addr), (size)); \
|
|
if (err != TME_OK) break; \
|
|
length += size
|
|
#define CHUNKS_MEM_TX(data, size) \
|
|
_tme_i825x6_chunks_mem_tx(frame_chunks, (data), (size)); \
|
|
length += size
|
|
|
|
/* if AL-LOC is set to zero, add the Ethernet/802.3 MAC header: */
|
|
if (i825x6->tme_i825x6_al_loc == 0) {
|
|
|
|
/* the destination address: */
|
|
CHUNKS_DMA_TX(i825x6->tme_i825x6_cb_address + TME_I82586_TCB_ADDR_DEST,
|
|
TME_ETHERNET_ADDR_SIZE);
|
|
|
|
/* our source address (our Individual Address): */
|
|
CHUNKS_MEM_TX(&i825x6->tme_i825x6_addresses[TME_ETHERNET_ADDR_SIZE],
|
|
TME_ETHERNET_ADDR_SIZE);
|
|
|
|
/* the length field: */
|
|
CHUNKS_DMA_TX(i825x6->tme_i825x6_cb_address + TME_I82586_TCB_LENGTH,
|
|
TME_ETHERNET_LENGTH_SIZE);
|
|
}
|
|
|
|
/* the transmit buffers: */
|
|
TME_I825X6_READ16((i825x6->tme_i825x6_cb_address
|
|
+ TME_I82586_TCB_TBD_OFFSET),
|
|
tbd_address);
|
|
for (; tbd_address != 0xffff; ) {
|
|
tbd_address += i825x6->tme_i825x6_scb_base;
|
|
|
|
TME_I825X6_READ16((tbd_address
|
|
+ TME_I82586_TBD_EOF_SIZE),
|
|
eof_size);
|
|
TME_I82586_READ24((tbd_address
|
|
+ TME_I82586_TBD_TB_ADDRESS),
|
|
tb_address);
|
|
|
|
/* the transmit buffer contents: */
|
|
CHUNKS_DMA_TX(tb_address,
|
|
(eof_size & TME_I82586_TBD_SIZE_MASK));
|
|
|
|
/* the next transmit buffer: */
|
|
if (eof_size & TME_I82586_TBD_EOF) {
|
|
break;
|
|
}
|
|
TME_I825X6_READ16((tbd_address
|
|
+ TME_I82586_TBD_TBD_OFFSET),
|
|
tbd_address);
|
|
}
|
|
|
|
#undef CHUNKS_DMA_TX
|
|
#undef CHUNKS_MEM_TX
|
|
|
|
} while (/* CONSTCOND */ 0);
|
|
|
|
/* get the CB status word and clear all of the transmit status bits: */
|
|
c_b_ok_a
|
|
= (i825x6->tme_i825x6_c_b_ok_a
|
|
& ~TME_I825X6_TCB_STATUS_MASK);
|
|
|
|
/* if we got a bus error: */
|
|
if (err != TME_OK) {
|
|
|
|
/* set the DMA underrun transmit status bit: */
|
|
c_b_ok_a |= TME_I825X6_TCB_STATUS_UNDERRUN;
|
|
|
|
/* return an error to our caller: */
|
|
length = -ENOENT;
|
|
}
|
|
|
|
/* update the CB status word: */
|
|
i825x6->tme_i825x6_c_b_ok_a = c_b_ok_a;
|
|
|
|
/* run the Command Unit: */
|
|
new_callouts |= TME_I825X6_CALLOUT_CU;
|
|
|
|
/* we no longer have a packet to transmit: */
|
|
i825x6->tme_i825x6_el_s_i_cmd
|
|
= ((i825x6->tme_i825x6_el_s_i_cmd
|
|
& ~TME_I825X6_CB_CMD_MASK)
|
|
| TME_I825X6_CB_CMD_NOP);
|
|
}
|
|
|
|
/* make any new callouts: */
|
|
_tme_i825x6_callout(i825x6, new_callouts);
|
|
|
|
/* unlock our mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* done: */
|
|
return (length);
|
|
}
|
|
|
|
/* this makes a new Ethernet connection: */
|
|
static int
|
|
_tme_i825x6_connection_make_eth(struct tme_connection *conn, unsigned int state)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
struct tme_ethernet_connection *conn_eth;
|
|
struct tme_ethernet_connection *conn_eth_other;
|
|
|
|
/* recover our data structures: */
|
|
i825x6 = conn->tme_connection_element->tme_element_private;
|
|
conn_eth = (struct tme_ethernet_connection *) conn;
|
|
conn_eth_other = (struct tme_ethernet_connection *) conn->tme_connection_other;
|
|
|
|
/* both sides must be Ethernet connections: */
|
|
assert(conn->tme_connection_type == TME_CONNECTION_ETHERNET);
|
|
assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_ETHERNET);
|
|
|
|
/* we're always set up to answer calls across the connection, so we
|
|
only have to do work when the connection has gone full, namely
|
|
taking the other side of the connection: */
|
|
if (state == TME_CONNECTION_FULL) {
|
|
|
|
/* lock our mutex: */
|
|
tme_mutex_lock(&i825x6->tme_i825x6_mutex);
|
|
|
|
/* save our connection: */
|
|
i825x6->tme_i825x6_eth_connection = conn_eth_other;
|
|
|
|
/* unlock our mutex: */
|
|
tme_mutex_unlock(&i825x6->tme_i825x6_mutex);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this makes a new bus connection: */
|
|
static int
|
|
_tme_i825x6_connection_make_bus(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
struct tme_bus_connection *conn_bus;
|
|
int rc;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = conn->tme_connection_element->tme_element_private;
|
|
|
|
/* call the bus device connection maker: */
|
|
rc = tme_bus_device_connection_make(conn, state);
|
|
|
|
/* if the full connection was successful, and we don't have a TLB
|
|
hash yet, allocate it and add our bus signals: */
|
|
if (rc == TME_OK
|
|
&& state == TME_CONNECTION_FULL
|
|
&& !i825x6->tme_i825x6_tlb_hash_added) {
|
|
|
|
/* get our bus connection: */
|
|
conn_bus
|
|
= tme_memory_atomic_pointer_read(struct tme_bus_connection *,
|
|
i825x6->tme_i825x6_device.tme_bus_device_connection,
|
|
&i825x6->tme_i825x6_device.tme_bus_device_connection_rwlock);
|
|
|
|
/* add the TLB set: */
|
|
rc = tme_bus_device_tlb_set_add(&i825x6->tme_i825x6_device,
|
|
TME_I825X6_TLB_HASH_SIZE,
|
|
i825x6->tme_i825x6_tlb_hash);
|
|
assert (rc == TME_OK);
|
|
i825x6->tme_i825x6_tlb_hash_added = TRUE;
|
|
|
|
/* add our bus signals: */
|
|
i825x6->tme_i825x6_bus_signals = _tme_i825x6_bus_signals;
|
|
rc = ((*conn_bus->tme_bus_signals_add)
|
|
(conn_bus,
|
|
&i825x6->tme_i825x6_bus_signals));
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/* this breaks a connection: */
|
|
static int
|
|
_tme_i825x6_connection_break(struct tme_connection *conn, unsigned int state)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this makes a new connection side for a i825x6: */
|
|
static int
|
|
_tme_i825x6_connections_new(struct tme_element *element,
|
|
const char * const *args,
|
|
struct tme_connection **_conns,
|
|
char **_output)
|
|
{
|
|
struct tme_i825x6 *i825x6;
|
|
struct tme_ethernet_connection *conn_eth;
|
|
struct tme_connection *conn;
|
|
int rc;
|
|
|
|
/* recover our data structure: */
|
|
i825x6 = (struct tme_i825x6 *) element->tme_element_private;
|
|
|
|
/* make the generic bus device connection side: */
|
|
rc = tme_bus_device_connections_new(element, args, _conns, _output);
|
|
if (rc != TME_OK) {
|
|
return (rc);
|
|
}
|
|
|
|
/* since we need to allocate our TLB hash and add our signals sets
|
|
when we make our bus connection, make sure any generic bus device
|
|
connection sides use our connection maker: */
|
|
for (conn = *_conns;
|
|
conn != NULL;
|
|
conn = conn->tme_connection_next) {
|
|
if ((conn->tme_connection_type
|
|
== TME_CONNECTION_BUS_GENERIC)
|
|
&& (conn->tme_connection_make
|
|
== tme_bus_device_connection_make)) {
|
|
conn->tme_connection_make
|
|
= _tme_i825x6_connection_make_bus;
|
|
}
|
|
}
|
|
|
|
/* if we don't have an Ethernet connection, make one: */
|
|
if (i825x6->tme_i825x6_eth_connection == NULL) {
|
|
|
|
/* allocate the new Ethernet connection: */
|
|
conn_eth = tme_new0(struct tme_ethernet_connection, 1);
|
|
conn = &conn_eth->tme_ethernet_connection;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_next = *_conns;
|
|
conn->tme_connection_type = TME_CONNECTION_ETHERNET;
|
|
conn->tme_connection_score = tme_ethernet_connection_score;
|
|
conn->tme_connection_make = _tme_i825x6_connection_make_eth;
|
|
conn->tme_connection_break = _tme_i825x6_connection_break;
|
|
|
|
/* fill in the Ethernet connection: */
|
|
conn_eth->tme_ethernet_connection_config = _tme_i825x6_config;
|
|
conn_eth->tme_ethernet_connection_ctrl = _tme_i825x6_ctrl;
|
|
conn_eth->tme_ethernet_connection_read = _tme_i825x6_read;
|
|
|
|
/* return the connection side possibility: */
|
|
*_conns = conn;
|
|
}
|
|
|
|
/* done: */
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* the new i82586 function: */
|
|
TME_ELEMENT_X_NEW_DECL(tme_ic_,i825x6,i82586) {
|
|
struct tme_i825x6 *i825x6;
|
|
int arg_i;
|
|
int usage;
|
|
|
|
/* check our arguments: */
|
|
usage = 0;
|
|
arg_i = 1;
|
|
for (;;) {
|
|
|
|
if (0) {
|
|
}
|
|
|
|
/* if we ran out of arguments: */
|
|
else if (args[arg_i] == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
/* otherwise this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
_("usage:"),
|
|
args[0]);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* start the i825x6 structure: */
|
|
i825x6 = tme_new0(struct tme_i825x6, 1);
|
|
i825x6->tme_i825x6_element = element;
|
|
tme_mutex_init(&i825x6->tme_i825x6_mutex);
|
|
i825x6->tme_i825x6_address_count = 2;
|
|
i825x6->tme_i825x6_addresses
|
|
= tme_new(tme_uint8_t,
|
|
i825x6->tme_i825x6_address_count
|
|
* TME_ETHERNET_ADDR_SIZE);
|
|
|
|
/* initialize our simple bus device descriptor: */
|
|
i825x6->tme_i825x6_device.tme_bus_device_element = element;
|
|
i825x6->tme_i825x6_device.tme_bus_device_signal = _tme_i825x6_signal;
|
|
i825x6->tme_i825x6_device.tme_bus_device_lock = _tme_i825x6_lock;
|
|
i825x6->tme_i825x6_device.tme_bus_device_unlock = _tme_i825x6_unlock;
|
|
i825x6->tme_i825x6_device.tme_bus_device_tlb_hash = _tme_i825x6_tlb_hash;
|
|
i825x6->tme_i825x6_device.tme_bus_device_router = tme_bus_device_router_16el;
|
|
|
|
/* fill the element: */
|
|
element->tme_element_private = i825x6;
|
|
element->tme_element_connections_new = _tme_i825x6_connections_new;
|
|
|
|
/* reset the i825x6: */
|
|
_tme_i825x6_reset(i825x6);
|
|
|
|
return (TME_OK);
|
|
}
|