Updated LAN driver

- changed directory layout
- Multicast support (automatic RX filter adjustment)
- MC68000 target in Makefile
- clarified licensing (GPLv3)
- README files
This commit is contained in:
Henryk Richter
2018-06-14 08:20:30 +02:00
parent 95bc477444
commit b13808f240
46 changed files with 919 additions and 485 deletions
+733
View File
@@ -0,0 +1,733 @@
/*
Microchip ENC28J60 Ethernet Interface Driver
Author: Henryk Richter
Copyright: GPL V2
Notes: when I ran into timing issues in early development, I moved over to quickly
testable ASM code for SPI communication. I didn't bother porting that stuff
back into C code. So this file now references enc28j60l.asm for lowlevel direct
hardware access.
As a consequence, the only functions needed from spi.c are the spi_init() and
spi_disable_eth().
TODO: - support for power down when unused, i.e. offline
- replace busy waiting loop after reset
Based on work by
Christian Vogelgsaang
Guido Socher
Pascal Stang (see enc28j60.c in AVRlib library for the original file)
*/
#undef _DO_DUMP
/* used for Delay() */
#include <dos/dos.h>
#include <proto/dos.h>
#ifdef _DO_DUMP
#include <stdio.h>
#endif
#include "compiler.h"
#include "enc28j60.h"
#include "enc28j60l.h" /* import lowlevel asm functions */
/* #include "spi.h" */ /* no direct use anymore, just for init purposes) */
#include "device.h"
/*
The RXSTART_INIT must be zero. See Rev. B4 Silicon Errata point 5.
Buffer boundaries applied to internal 8K ram
the entire available packet buffer space is allocated
rx packet layout
6 byte heaader
1518
sum: 1524
*/
#define RXSTART_INIT 0x0000 /* start of RX buffer, room for 4 packets */
#define RXSTOP_INIT 0x19FF /* end of RX buffer */
#define TXSTART_INIT 0x1A00 /* start of TX buffer, room for 1 packet */
#define TXSTOP_INIT 0x1FFF /* end of TX buffer */
/* max frame length which the conroller will accept: */
/* (note: maximum ethernet frame length would be 1518 when VLAN tagging is unused) */
#define MAX_FRAMELEN 1518
static u16 gNextPacketPtr;
static u08 is_full_duplex;
static u08 rev;
struct enc28j60_initvars {
u16 have_recv;
u08 last_mac[6];
u08 macon1;
u08 macon2;
u08 macon3;
u08 macon4;
};
static struct enc28j60_initvars initvars;
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart );
static int enc28j60_check_config( struct enc28j60_initvars *iv );
static uint8_t readOp (uint8_t op, uint8_t address)
{
return enc28j60l_ReadOp( op, address );
}
static void writeOp (uint8_t op, uint8_t address, uint8_t data)
{
enc28j60l_WriteOp( op, address,data );
}
static void readBuf(uint16_t len, uint8_t* data)
{
enc28j60l_ReadBuffer( data, (unsigned int)len );
}
void SetBank (uint8_t address)
{
enc28j60l_SetBank(address);
}
static uint8_t readRegByte (uint8_t address)
{
return enc28j60l_ReadRegByte( address );
}
#if 0
static uint16_t readReg(uint8_t address) {
return readRegByte(address) + (readRegByte(address+1) << 8);
}
#endif
static void writeRegByte (uint8_t address, uint8_t data)
{
enc28j60l_WriteRegByte( address, data );
}
static void writeReg(uint8_t address, uint16_t data)
{
enc28j60l_WriteReg( address, data );
}
/*
the PHY registers require special care
*/
static uint16_t readPhyByte (uint8_t address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (uint8_t address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
/* just a pass-through to the lowlevel routines */
void enc28j60_SetSPISpeed( unsigned long clock_divider )
{
enc28j60l_SetSPISpeed( clock_divider );
}
/* ---------- init ---------- */
/* Functions to enable/disable broadcast filter bits */
/* With the bit set, broadcast packets are filtered. */
void enc28j60_broadcast_multicast_filter( u08 flags )
{
u08 val = ERXFCON_UCEN|ERXFCON_CRCEN; /* default: crc check and unicast */
if( flags & PIO_INIT_PROMISC )
{
val = 0;
}
else
{
if( flags & PIO_INIT_BROAD_CAST )
val |= ERXFCON_BCEN;
if( flags & PIO_INIT_MULTI_CAST )
val |= ERXFCON_MCEN;
}
writeRegByte(ERXFCON, val );
}
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ESTAT, ESTAT_TXABRT);
gNextPacketPtr = RXSTART_INIT;
writeReg(ERXST, RXSTART_INIT);
writeReg(ERXRDPT, RXSTOP_INIT );
writeReg(ERXND, RXSTOP_INIT);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_RXERIF );
writeReg(MAMXFL, MAX_FRAMELEN);
writeRegByte(MACON4, iv->macon4 ); /* DEFER bit */
writeRegByte(MACON3, iv->macon3 );
writeRegByte(MACON2, iv->macon2 );
writeRegByte(MACON1, iv->macon1 );
enc28j60_setMAC( iv->last_mac );
writePhy(PHIE, PHIE_PGEIE|PHIE_LNKIE );
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
/* prepare flow control */
writeReg(EPAUS, 20 * 100); /* 100ms */
if( restart )
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
iv->have_recv = 0;
}
static int enc28j60_check_config( struct enc28j60_initvars *iv )
{
int ret = 1; /* def: be optimistic */
/* check RX registers */
if( !(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_RXEN ) )
ret = 0;
if( (readRegByte(MACON1)&iv->macon1) != iv->macon1 )
ret = 0;
if( (readRegByte(MACON2)&iv->macon2) != iv->macon2 )
ret = 0;
if( (readRegByte(MACON3)&iv->macon3) != iv->macon3 )
ret = 0;
/* check TX problems */
if( readRegByte(ESTAT) & ESTAT_TXABRT )
ret = 0;
if( readRegByte(EIR) & EIR_TXERIF )
ret = 0;
/* Verify MAC */
if( readRegByte(MAADR5) != iv->last_mac[0] )
ret = 0;
if( readRegByte(MAADR4) != iv->last_mac[1] )
ret = 0;
if( readRegByte(MAADR3) != iv->last_mac[2] )
ret = 0;
if( readRegByte(MAADR2) != iv->last_mac[3] )
ret = 0;
if( readRegByte(MAADR1) != iv->last_mac[4] )
ret = 0;
if( readRegByte(MAADR0) != iv->last_mac[5] )
ret = 0;
return ret;
}
u08 enc28j60_init(const u08 macaddr[6], u08 flags)
{
unsigned int count;
u08 mac3val;
struct enc28j60_initvars *iv = &initvars;
enc28j60l_Init(); /* spi_init(); spi_disable_eth(); */
{
int i;
for(i=0 ; i < 6 ; i++ )
iv->last_mac[i] = macaddr[i];
}
is_full_duplex = (flags & PIO_INIT_FULL_DUPLEX) == PIO_INIT_FULL_DUPLEX;
/* soft reset cpu */
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
#if 1
enc28j60l_UMinDelay( 2000 ); /* wait 2 ms */
#else
/* Delay(1);*/ /* _delay_ms(2); // errata B7/2 */
/* hard-coded delay right now (TODO: import timer functions) */
for( count = 0 ; count < 20000 ; count++ )
{
mac3val |= (u08)*((volatile unsigned short*)(0x00DE0004));
}
#endif
/* wait or error */
count = 0;
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) {
count ++;
if(count == 0xfff) {
return PIO_NOT_FOUND;
}
}
writeOp(ENC28J60_WRITE_CTRL_REG, ECON1, 0);
/* return rev */
rev = readRegByte(EREVID);
/* microchip forgot to step the number on the silcon when they */
/* released the revision B7. 6 is now rev B7. We still have */
/* to see what they do when they release B8. At the moment */
/* there is no B8 out yet */
if ( (rev < 1) || (rev > 10) ) /* I don't expect a rev > 10 (+1), so I use it to check for module presence */
return PIO_NOT_FOUND; /* important: check this before any writePhy -> no module gets us 0xff back */
if (rev > 5) ++rev;
writeReg(ETXST, TXSTART_INIT);
writeReg(ETXND, TXSTOP_INIT);
/* set packet filter */
enc28j60_broadcast_multicast_filter( flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
/* BIST pattern generator? */
writeReg(EPMM0, 0x303f);
writeReg(EPMCS, 0xf7f9);
if(is_full_duplex) {
writeRegByte(MABBIPG, 0x15);
writeReg(MAIPG, 0x0012);
} else {
writeRegByte(MABBIPG, 0x12);
writeReg(MAIPG, 0x0C12);
}
/* MAC init (with flow control) */
mac3val = MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN;
if(is_full_duplex) {
mac3val |= MACON3_FULDPX;
iv->macon4=0;
}
else
{
iv->macon4 = 1<<6; /* DEFER bit */
}
iv->macon3 = mac3val;
iv->macon2 = 0x00;
iv->macon1 = MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS;
/* set packet pointers */
enc28j60_recv_reset( iv, 0 );
/* PHY init */
if(is_full_duplex) {
writePhy(PHCON1, PHCON1_PDPXMD);
writePhy(PHCON2, 0);
} else {
writePhy(PHCON1, 0);
writePhy(PHCON2, PHCON2_HDLDIS);
}
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_DMAIF|EIR_LINKIF|EIR_TXIF|EIR_TXERIF|EIR_RXERIF|EIR_PKTIF);
writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_LINKIE|EIE_TXIE|EIE_TXERIE|EIE_RXERIE);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
/* wait a while for "LINK UP" -> break after 200 ms */
{
unsigned char status;
int runs = 100;
while( runs-- )
{
enc28j60_status( PIO_STATUS_LINK_UP, &status );
if( status )
break;
enc28j60l_UMinDelay( 2000 ); /* wait 2+ ms */
}
}
return PIO_OK;
}
/* ---------- exit ---------- */
void enc28j60_exit(void)
{
enc28j60_setOffline();
enc28j60l_Shutdown();
}
u08 enc28j60_setOnline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
return 1;
}
u08 enc28j60_setOffline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
return 1;
}
void enc28j60_setMAC( const u08 macaddr[6] )
{
/* set mac */
writeRegByte(MAADR5, macaddr[0]);
writeRegByte(MAADR4, macaddr[1]);
writeRegByte(MAADR3, macaddr[2]);
writeRegByte(MAADR2, macaddr[3]);
writeRegByte(MAADR1, macaddr[4]);
writeRegByte(MAADR0, macaddr[5]);
SetBank(EIR);
}
void enc28j60_getMAC( u08 macaddr[6] ) /* mainly for debug */
{
/* set mac */
macaddr[0] = readRegByte(MAADR5);
macaddr[1] = readRegByte(MAADR4);
macaddr[2] = readRegByte(MAADR3);
macaddr[3] = readRegByte(MAADR2);
macaddr[4] = readRegByte(MAADR1);
macaddr[5] = readRegByte(MAADR0);
}
/* ---------- control ---------- */
u08 enc28j60_control(u08 control_id, u08 value)
{
switch(control_id) {
case PIO_CONTROL_FLOW:
{
u08 flag;
if(is_full_duplex) {
flag = value ? 2 : 3;
} else {
flag = value ? 1 : 0;
}
writeRegByte(EFLOCON, flag);
return PIO_OK;
}
default:
return PIO_NOT_FOUND;
}
}
/* ---------- status ---------- */
u08 enc28j60_status(u08 status_id, u08 *value)
{
switch(status_id) {
case PIO_STATUS_VERSION:
*value = rev;
return PIO_OK;
case PIO_STATUS_LINK_UP:
*value = (readPhyByte(PHSTAT2) >> 2) & 1;
return PIO_OK;
default:
*value = 0;
return PIO_NOT_FOUND;
}
}
/* ---------- send ---------- */
u08 enc28j60_send(const u08 *data, u16 size)
{
/* wait for tx ready */
/* We can't do it here like on ATMEL MCUs, simply because
the Vampire SPI is way faster than 10 MBit/s so that
we would overwrite a frame still being sent out
-> wait first, then fill buffer
*/
while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
{
/* errata sheet issue 12 -> TXRTS never becomes 0 */
/* ignores errata issue 13 -> frame loss due to late collision */
/* eworks around errata sheet issue 15 -> ESTAT.LATECOL not set */
if (readRegByte(EIR) & EIR_TXERIF) {
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
}
/* write buffer */
enc28j60l_WriteBuffer( (u08*)data, size, TXSTART_INIT );
/* initiate send */
writeReg(ETXND, TXSTART_INIT+size);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
return PIO_OK;
}
/* ---------- recv ---------- */
/*
unrecoverable error in receiving, bad pointer or other causes
- reset the complete receive logic in order to start receiving anew
input: restart - 0 = don't re-enable RX automatically
1 = re-enable RX after resetting
*/
INLINE void next_pkt(void)
{
if (gNextPacketPtr - 1 > RXSTOP_INIT)
{
#if 1
enc28j60_recv_reset( &initvars, 1 );
#else
writeReg(ERXRDPT, RXSTOP_INIT);
gNextPacketPtr = 0;
#endif
}
else
writeReg(ERXRDPT, gNextPacketPtr - 1);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
/* endian agnostic version of header read */
static u08 read_hdr(u16 *got_size)
{
struct {
uint8_t nextPacketL;
uint8_t nextPacketH;
uint8_t byteCountL;
uint8_t byteCountH;
uint8_t statusL;
uint8_t statusH;
} header;
readBuf(sizeof header, (uint8_t*) &header);
gNextPacketPtr = (u16)header.nextPacketL + (((u16)header.nextPacketH)<<8);
*got_size = (u16)header.byteCountL + (((u16)header.byteCountH)<<8); /* - 4;*//* -4: remove the CRC count */
return header.statusL;
}
u08 enc28j60_recv(u08 *data, u16 max_size, u16 *got_size)
{
u08 status,result;
u16 len;
/* EIR->PKTIF would be faster but errata sheet says "unreliable" */
u08 pcnt = 1+readRegByte(EPKTCNT); /* wraps to 0 for 0xff = unconnected module */
if( pcnt < 2 ) /* 0xff+1=0x00, 0x00+1=0x01, 0x01+1=0x02 */
return PIO_NOT_FOUND;
writeReg(ERDPT, gNextPacketPtr);
/* read chip's packet header */
status = read_hdr(got_size);
/* was a receive error? */
if ((status & 0x80)==0)
{
/* TODO: recv reset ? */
next_pkt();
return PIO_IO_ERR;
}
/* check size */
len = *got_size;
result = PIO_OK;
if(len > max_size) {
len = max_size;
result = PIO_TOO_LARGE;
}
/* read packet */
readBuf(len, data);
next_pkt();
return result;
}
/* ---------- has_recv ---------- */
u08 enc28j60_has_recv(void)
{
u08 ret = readRegByte(EPKTCNT);
if( !ret )
{
initvars.have_recv++;
if( initvars.have_recv > 10 )
{
/* check for bad config vars and reset hw if that is the case */
if( !enc28j60_check_config( &initvars ) )
{
enc28j60_recv_reset( &initvars, 1 );
}
}
}
else
initvars.have_recv = 0;
return ret;
}
void enc28j60_dump_regs(void)
{
/* this will work only with DOSBase known globally, not when it's redirected to Plipbase */
#ifdef _DO_DUMP
u08 val,eir,eie,econ1,econ2,estat;
u16 erxrdpt,erxwrpt,erxst,erxnd,mamxfll;
u16 macon1,macon3,macon4;
eir = readRegByte(EIR);
eie = readRegByte(EIE);
econ1 = readRegByte(ECON1);
econ2 = readRegByte(ECON2);
estat = readRegByte(ESTAT);
printf("EIR EIE ECON1 ECON2 ESTAT\n");
printf("%02x %02x %02x %02x %02x\n",
eir,eie,econ1,econ2,estat);
erxrdpt = readRegByte(ERXRDPT) + (((u16)readRegByte(ERXRDPT+1))<<8);
erxwrpt = readRegByte(ERXWRPT) + (((u16)readRegByte(ERXWRPT+1))<<8);
erxst = readRegByte(ERXST) + (((u16)readRegByte(ERXST+1))<<8);
erxnd = readRegByte(ERXND) + (((u16)readRegByte(ERXND+1))<<8);
mamxfll = readRegByte(MAMXFL) + (((u16)readRegByte(MAMXFL+1))<<8);
printf("ERXRDPT ERXWRPT ERXST ERXND MAMXFL\n");
printf("%04x %04x %04x %04x %04x\n",
erxrdpt,erxwrpt,erxst,erxnd,mamxfll);
macon1 = readRegByte(MACON1);/* + (((u16)readRegByte(MACON1+1))<<8); */
macon3 = readRegByte(MACON3);/* + (((u16)readRegByte(MACON1+3))<<8); */
macon4 = readRegByte(MACON4);/* + (((u16)readRegByte(MACON1+4))<<8); */
printf("MACON1 MACON3 MACON4\n");
printf("%02x %02x %02x\n",
macon1,macon3,macon4);
printf("MAADR\n");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
(int)readRegByte(MAADR5),
(int)readRegByte(MAADR4),
(int)readRegByte(MAADR3),
(int)readRegByte(MAADR2),
(int)readRegByte(MAADR1),
(int)readRegByte(MAADR0) );
val = readRegByte(EIR);
#endif
}
#if 0
/* Contributed by Alex M. Based on code from: http://blog.derouineau.fr */
/* /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/ */
void enc28j60_power_down( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
while(readRegByte(ESTAT) & ESTAT_RXBUSY);
while(readRegByte(ECON1) & ECON1_TXRTS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}
void enc28j60_power_up( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
while(!readRegByte(ESTAT) & ESTAT_CLKRDY);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
uint8_t enc28j60_do_BIST ( void )
{
#define RANDOM_FILL 0b0000
#define ADDRESS_FILL 0b0100
#define PATTERN_SHIFT 0b1000
#define RANDOM_RACE 0b1100
/* init */
spi_disable_eth();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
_delay_ms(2); /* errata B7/2 */
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ;
/* now we can start the memory test */
uint16_t macResult;
uint16_t bitsResult;
/* clear some of the registers registers */
writeRegByte(ECON1, 0);
writeReg(EDMAST, 0);
/* Set up necessary pointers for the DMA to calculate over the entire memory */
writeReg(EDMAND, 0x1FFFu);
writeReg(ERXND, 0x1FFFu);
/* Enable Test Mode and do an Address Fill */
SetBank(EBSTCON);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* Compare the results */
/* 0xF807 should always be generated in Address fill mode */
if ((macResult != bitsResult) || (bitsResult != 0xF807)) {
return 0;
}
/* reset test flag */
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* Now start the BIST with random data test, and also keep on swapping the */
/* DMA/BIST memory ports. */
writeRegByte(EBSTSD, 0b10101010 /* | millis()*/);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* The checksum should be equal */
return macResult == bitsResult;
}
#endif
+71
View File
@@ -0,0 +1,71 @@
/*
* enc28j60.h - Microchip ENC28J60 interface
*
* Written by
* Henryk Richter <henryk.richter@gmx.net>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
#ifndef _INC_ENC28J60_H
#define _INC_ENC28J60_H
/* result values */
#define PIO_OK 0
#define PIO_NOT_FOUND 1
#define PIO_TOO_LARGE 2
#define PIO_IO_ERR 3
/* init flags */
#define PIO_INIT_FULL_DUPLEX 1
#define PIO_INIT_LOOP_BACK 2
#define PIO_INIT_BROAD_CAST 4
#define PIO_INIT_FLOW_CONTROL 8
#define PIO_INIT_MULTI_CAST 16
#define PIO_INIT_PROMISC 32
/* status flags */
#define PIO_STATUS_VERSION 0
#define PIO_STATUS_LINK_UP 1
/* control ids */
#define PIO_CONTROL_FLOW 0
/* sigh, old gcc does not have stdint.h */
typedef unsigned char u08;
typedef unsigned short u16;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
/* API functions */
u08 enc28j60_init(const u08 macaddr[6], u08 flags);
void enc28j60_exit(void);
u08 enc28j60_setOnline( void );
u08 enc28j60_setOffline( void );
void enc28j60_SetSPISpeed( unsigned long clock_divider );
u08 enc28j60_send(const u08 *data, u16 size);
u08 enc28j60_recv(u08 *data, u16 max_size, u16 *got_size);
u08 enc28j60_status( u08 status_id, u08 *value);
u08 enc28j60_has_recv(void);
void enc28j60_setMAC( const u08 macaddr[6] );
void enc28j60_getMAC( u08 macaddr[6] ); /* mainly for debug */
void enc28j60_dump_regs(void);
void enc28j60_broadcast_multicast_filter( u08 flags );
#endif /* _INC_ENC28J60_H */
File diff suppressed because it is too large Load Diff
+362
View File
@@ -0,0 +1,362 @@
/*
enc28j60l.h
author: Henryk Richter <henryk.richter@gmx.net>
purpose: header for low-level register and SPI access
to ENC28J60 networking device
*/
#ifndef _INC_ENC28J60L_H
#define _INC_ENC28J60L_H
#include "compiler.h"
/*
Purpose: prepare SPI for use
input: -
output: success/failure
>0 - all fine, ready to work
<=0 - init problem (codes undefined yet)
*/
ASM SAVEDS int enc28j60l_Init( void );
/*
Purpose: shutdown SPI
input: -
output: -
*/
ASM SAVEDS void enc28j60l_Shutdown( void );
/*
Purpose: delay at least the given amount of microseconds
input: delay time in microseconds
output: -
*/
ASM SAVEDS void enc28j60l_UMinDelay(
ASMR(d0) unsigned long udelay ASMREG(d0) );
/*
Purpose: set SPI clock divider
input: clock divider 1...n
output: -
*/
ASM SAVEDS void enc28j60l_SetSPISpeed(
ASMR(d0) unsigned long divider ASMREG(d0) );
/*
purpose: read one byte from a custom register
input: op = ENC28J60_READ_CTRL_REG
address = register address, including bits denoting the bank
data = data byte
*/
ASM SAVEDS unsigned char enc28j60l_ReadOp(
ASMR(d3) unsigned char op ASMREG(d3),
ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: write one byte to a custom register
input: op = ENC28J60_WRITE_CTRL_REG,
ENC28J60_BIT_FIELD_SET,
ENC28J60_BIT_FIELD_CLR
address = register address, including bits denoting the bank
data = data byte
*/
ASM void enc28j60l_WriteOp( ASMR(d3) unsigned char op ASMREG(d3),
ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned char data ASMREG(d1));
/*
purpose: select register bank based on given address
input:
address = register address, including bits denoting the bank
*/
ASM SAVEDS void enc28j60l_SetBank( ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: read one short from a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM SAVEDS unsigned short enc28j60l_ReadReg( ASMR(d0) unsigned char address ASMREG(d0) );
/*
purpose: write one short to a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM void enc28j60l_WriteReg( ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned short data ASMREG(d1));
/*
purpose: read one byte from a custom register
input: address = register address, including bits denoting the bank
*/
ASM SAVEDS unsigned char enc28j60l_ReadRegByte(
ASMR(d0) unsigned char address ASMREG(d0));
/*
purpose: write one byte to a custom register
input: address = register address, including bits denoting the bank
data = data word (unsigned short)
*/
ASM void enc28j60l_WriteRegByte( ASMR(d0) unsigned char address ASMREG(d0),
ASMR(d1) unsigned char data ASMREG(d1));
/*
purpose: Read buffer from device to computer
input: buffer = buffer to read into
size = length of buffer
*/
ASM void enc28j60l_ReadBuffer( ASMR(a1) unsigned char *buffer ASMREG(a1),
ASMR(d0) unsigned int size ASMREG(d0));
/*
purpose: Write buffer from computer to device
input: buffer = buffer to write
size = length of buffer
destptr = destination pointer in device
*/
ASM void enc28j60l_WriteBuffer( ASMR(a1) unsigned char *buffer ASMREG(a1),
ASMR(d0) unsigned int size ASMREG(d0),
ASMR(d1) unsigned short destptr ASMREG(d1));
/*
Definitions based on the enc28j60.c file from the AVRlib library by Pascal Stang.
ENC28J60 Control Registers
Control register definitions are a combination of address,
bank number, and Ethernet/MAC/PHY indicator bits.
- Register address (bits 0-4)
- Bank number (bits 5-6)
- MAC/PHY indicator (bit 7)
*/
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
/* All-bank registers */
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
/* Bank 0 registers */
#define ERDPT (0x00|0x00)
#define EWRPT (0x02|0x00)
#define ETXST (0x04|0x00)
#define ETXND (0x06|0x00)
#define ERXST (0x08|0x00)
#define ERXND (0x0A|0x00)
#define ERXRDPT (0x0C|0x00)
#define ERXWRPT (0x0E|0x00)
#define EDMAST (0x10|0x00)
#define EDMAND (0x12|0x00)
/* #define EDMADST (0x14|0x00) */
#define EDMACS (0x16|0x00)
/* Bank 1 registers */
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCS (0x10|0x20)
/* #define EPMO (0x14|0x20) */
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
/* Bank 2 registers */
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPG (0x06|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFL (0x0A|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWR (0x16|0x40|0x80)
#define MIRD (0x18|0x40|0x80)
/* Bank 3 registers */
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCS (0x08|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUS (0x18|0x60)
/* ENC28J60 ERXFCON Register Bit Definitions */
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
/* ENC28J60 EIE Register Bit Definitions */
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
/* ENC28J60 EIR Register Bit Definitions */
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
/* ENC28J60 ESTAT Register Bit Definitions */
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
/* ENC28J60 ECON2 Register Bit Definitions */
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
/* ENC28J60 ECON1 Register Bit Definitions */
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
/* ENC28J60 MACON1 Register Bit Definitions */
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
/* ENC28J60 MACON2 Register Bit Definitions */
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
/* ENC28J60 MACON3 Register Bit Definitions */
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
/* ENC28J60 MICMD Register Bit Definitions */
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
/* ENC28J60 MISTAT Register Bit Definitions */
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
/* ENC28J60 EBSTCON Register Bit Definitions */
#define EBSTCON_PSV2 0x80
#define EBSTCON_PSV1 0x40
#define EBSTCON_PSV0 0x20
#define EBSTCON_PSEL 0x10
#define EBSTCON_TMSEL1 0x08
#define EBSTCON_TMSEL0 0x04
#define EBSTCON_TME 0x02
#define EBSTCON_BISTST 0x01
/* PHY registers */
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
/* ENC28J60 PHY PHCON1 Register Bit Definitions */
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
/* ENC28J60 PHY PHSTAT1 Register Bit Definitions */
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
/* ENC28J60 PHY PHCON2 Register Bit Definitions */
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
/* PHIE Register */
#define PHIE_PGEIE (1<<1)
#define PHIE_LNKIE (1<<4)
/* ENC28J60 Packet Control Byte Bit Definitions */
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
/* SPI operation codes */
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
#endif /* _INC_ENC28J60L_H */
+343
View File
@@ -0,0 +1,343 @@
/*
hw.c
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
SDNet hardware interface to device
*/
#include <exec/libraries.h>
#include <proto/exec.h>
#include "device.h"
#include "hw.h"
#include <proto/vampire.h>
#include <vampire/vampire.h>
#include "vampuuid.h"
#include "enc28j60.h"
#ifndef PROTO_V2EXPNET
#define PORTID V_SDPORT
#else
#define PORTID V_WIFIPORT
#endif
extern const BYTE DeviceName[];
/* use generic board fields in current unit */
#define hw_allocated du_hwl0
#define HW_INTERVALDEF 10000
#define HW_DEFSPI 2
/* these calls might not run within the context of a process and
will be called from varying tasks and without prior init:
hw_Find_Boards()
hw_AllocBoard()
hw_ReleaseBoard()
hw_GetMacAddress()
these calls are in the context of the server task:
hw_Setup() - general init (board allocated already)
hw_Shutdown() - general shutdown (board still allocated)
hw_Attach() - make hardware ready for action (online)
hw_Detach() - put hardware into sleep mode (offline)
hw_recv_sigmask() - get signals for hardware (if any)
hw_recv_pending() - check for pending receive frames
hw_recv_frame() - receive one frame
hw_send_frame() - send one frame
hw_check_link_change() - check if HW link parameters need update
and apply them (called only when no RX/TX
in progress)
hw_config_init() - set defaults for HW
hw_config_update() - update config of HW
*/
const BYTE vres_name[] = V_VAMPIRENAME;
/* return number of boards present in system */
ASM SAVEDS LONG hw_Find_Boards( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) )
{
/* TODO: check for expansion port, i.e. distinguish V500/V600 */
/* TODO: actually verify that we have a module here */
ret = 1;
}
return ret;
}
ASM SAVEDS LONG hw_AllocBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) ) /* OpenResource is probably redundant */
{
if( unit == 0 )
{
ret = 1; /* def: looks good */
if( db->db_Units[unit].hw_allocated == 0 )
{
if( V_AllocExpansionPort( PORTID, (BYTE*)DeviceName ) )
ret = 0;
}
if( ret )
db->db_Units[unit].hw_allocated++;
}
}
return ret;
}
ASM SAVEDS LONG hw_ReleaseBoard( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
LONG ret = 0;
struct Library *VampireBase;
if( (VampireBase = OpenResource( (BYTE*)vres_name )) ) /* OpenResource is probably redundant */
{
if( unit == 0) /* only one device supported */
{
ret = 1; /* def: looks good */
if( db->db_Units[unit].hw_allocated > 0 )
{
db->db_Units[unit].hw_allocated--;
if( !db->db_Units[unit].hw_allocated )
{
V_FreeExpansionPort( PORTID );
}
}
}
}
return ret;
}
/* store MAC address at "mac", 6 Bytes */
ASM SAVEDS LONG hw_GetMacAddress( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *mac ASMREG(a1)
)
{
static UBYTE addr[6] = { 0x02,0x80,0x10,0x0b,0x0a,0xff};
UBYTE vsn[8];
LONG i;
if( vampire_UUID( vsn ) > 0 )
{
for(i=0 ; i<3 ; i++ )
{
addr[3+i] = vsn[i] ^ vsn[i+5];
}
}
for( i=0 ; i<6 ; i++ )
mac[i] = addr[i];
return 0;
}
/* prepare all boards such that a hw_attach() will succeed */
ASM SAVEDS LONG hw_Setup( ASMR(a0) DEVBASEP ASMREG(a0) )
{
LONG ret = 0;
struct HWData *hwd = &db->db_hwdat;
hwd->IntSig = -1;
if( InitIntervalTimer( &hwd->ivtimer ) > 0 )
{
hwd->SigMask = hwd->ivtimer.IVT_SigMask;
ret = 1;
}
return ret;
}
/* free resources, server is quitting */
ASM SAVEDS void hw_Shutdown( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
if( hwd->IntSig >= 0 )
FreeSignal( hwd->IntSig );
ExitIntervalTimer( &hwd->ivtimer );
}
/* make hardware ready for action (online) */
ASM SAVEDS LONG hw_Attach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
struct HWData *hwd = &db->db_hwdat;
LONG ret = 1;
u08 flags;
if( unit > 0 )
return 0;
flags = PIO_INIT_BROAD_CAST;
if( hwd->multicast )
flags |= PIO_INIT_MULTI_CAST;
if( hwd->fullduplex )
flags |= PIO_INIT_FULL_DUPLEX;
if( db->db_Units[0].du_Flags & DUF_PROMISC )
flags |= PIO_INIT_PROMISC;
enc28j60_SetSPISpeed( hwd->spispeed );
if( enc28j60_init( db->db_Units[0].du_CFGAddr, flags ) != PIO_OK )
ret = 0;
else
{
enc28j60_SetSPISpeed( hwd->spispeed );
StartIntervalTimer( &hwd->ivtimer, hwd->timervalue );
}
return ret;
}
/* put hardware into sleep mode (offline) */
ASM SAVEDS void hw_Detach( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0))
{
if( !unit )
{
struct HWData *hwd = &db->db_hwdat;
StopIntervalTimer( &hwd->ivtimer );
enc28j60_exit();
}
}
ASM SAVEDS void hw_ConfigInit( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
hwd->timervalue = HW_INTERVALDEF;
hwd->fullduplex = 0;
hwd->spispeed = HW_DEFSPI; /* default (see above) */
hwd->multicast = 0;
#if 0
USHORT i;
for( i=0 ; i < (USHORT)db->db_NBoards ; i++ )
{ /* no need, these are defaults */
db->db_Units[i].du_MTU = 1500;
db->db_Units[i].du_BitPerSec = 10000000;
}
#endif
}
ASM SAVEDS void hw_ConfigUpdate( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a1) void*argsv ASMREG(a1))
{
struct HWConfig *args = (struct HWConfig *)argsv; /* see hwprivate.h */
struct HWData *hwd = &db->db_hwdat;
if( args->timervalue )
hwd->timervalue = *args->timervalue;
if( args->fullduplex )
hwd->fullduplex = 1;
if( args->spispeed )
hwd->spispeed = *args->spispeed;
if( args->multicast )
hwd->multicast = 1;
}
ASM SAVEDS LONG hw_send_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1),
ASMR(d1) ULONG framesize ASMREG(d1) )
{
enc28j60_send( frame, framesize );
return 1;
}
ASM SAVEDS LONG hw_recv_pending( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
return (LONG)enc28j60_has_recv();
}
ASM SAVEDS LONG hw_recv_frame( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0),
ASMR(a1) UBYTE *frame ASMREG(a1))
{
volatile SHORT sz;
LONG ret;
/* important: don't call this without enc28j60_has_recv() > 0 check */
if( PIO_OK != enc28j60_recv( frame, 1518, (short*)&sz ) )
ret = 0;
else
ret = (LONG)sz;
return (LONG)ret;
}
ASM SAVEDS ULONG hw_recv_sigmask( ASMR(a0) DEVBASEP ASMREG(a0) )
{
struct HWData *hwd = &db->db_hwdat;
return hwd->SigMask;
}
/* check if HW link parameters need update */
ASM SAVEDS LONG hw_check_link_change( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(d0) ULONG unit ASMREG(d0) )
{
return 0;
}
/* process the list of multicast addresses for an appropriate
filtering list on the hardware
the server preprocesses active multicast listeners such that
this call can decide whether to enable multicast altogether,
filter with hashes etc.
*/
ASM SAVEDS LONG hw_change_multicast( ASMR(a0) DEVBASEP ASMREG(a0),
ASMR(a0) ULONG unit ASMREG(d0),
ASMR(a1) struct List *mcastlist ASMREG(a1) )
{
u08 flags;
flags = PIO_INIT_BROAD_CAST;
if( db->db_Units[0].du_Flags & DUF_PROMISC )
flags |= PIO_INIT_PROMISC;
/* list non-empty ? (sufficient for ENC28J60 -> it doesn`t have hashing) */
if( mcastlist->lh_Head->ln_Succ )
flags |= PIO_INIT_MULTI_CAST;
enc28j60_broadcast_multicast_filter( flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
return 1;
}
+52
View File
@@ -0,0 +1,52 @@
/*
hwprivate.h
(C) 2018 Henryk Richter <henryk.richter@gmx.net>
private structures for hardware instance
*/
#ifndef _INC_HWPRIVATE_H
#define _INC_HWPRIVATE_H
#include "compiler.h"
/* sdnet/v2expnet specific: polling timer */
#include "intervaltimer.h"
/* this struct is available in devicebase and should carry global information */
struct HWData {
ULONG SigMask; /* signal mask to wait for in server main instance */
struct IVT_TimerStruct ivtimer; /* Timer interval handling (via Interrupt) */
LONG IntSig;
/* config options */
ULONG timervalue; /* polling interval in ms */
ULONG fullduplex; /* force full duplex */
ULONG spispeed; /* SPI clock divider (>=1) */
ULONG multicast; /* multicast recv enable */
};
/* referenced from server.c */
#if (defined PROTO_V2EXPNET)
#define HW_CONFIGFILE "ENV:SANA2/v2expeth.config"
#else
#define HW_CONFIGFILE "ENV:SANA2/sdnet.config"
#endif
/* appended to COMMON_TEMPLATE */
#define HW_CONFIGTEMPLATE "TIMER/K/N,FULLDUPLEX/S,SPISPEED/K/N,MULTICAST/S"
/* referenced by server.c, hw.c */
struct HWConfig
{
struct CommonConfig common; /* import from hw.h, expected in server.c */
ULONG *timervalue;
ULONG fullduplex;
ULONG *spispeed;
ULONG multicast;
};
#endif /* _INC_HWPRIVATE_H */
@@ -0,0 +1,341 @@
******************************************************************************
* Name: intervaltimer.s *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
;MACHINE MC68040
include exec/types.i
include lvo/exec_lib.i
include lvo/dos_lib.i
include lvo/timer_lib.i
include exec/exec.i
include dos/dos.i
include dos/dostags.i
include exec/io.i
include dos/dosextens.i
include exec/lists.i
DO_TEST EQU 0
ifne 1
include "intervaltimer.i"
else
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc
xdef _InterruptIntervalTimer
xdef _InitIntervalTimer
xdef _ExitIntervalTimer
xdef _StartIntervalTimer
xdef _StopIntervalTimer
CALLEXEC macro
move.l 4.w,a6
jsr _LVO\1(a6)
endm
section code,text
ifne DO_TEST
TestTimer:
lea dos_name,a1 ;string base
moveq #0,d0
move.l 4.w,a6
jsr _LVOOpenLibrary(a6)
move.l d0,dosbase
beq .nodos
move.l dosbase,a6
jsr _LVOOutput(a6) ;get stdout
move.l d0,stdout
lea timerbase,a1
bsr _InitIntervalTimer
tst.l d0
bge .ok
move.l stdout,d0
move.l #failtimer,d1
move.l dosbase,16
jsr _LVOPutStr(a6)
bra .error
.ok
lea timerbase,a1
move.l #10000,d1
bsr _StartIntervalTimer
moveq #0,d3
.loop
cmp.l #10,d3
bgt.s .exitloop
move.l dosbase,A6
moveq #10,d1
jsr _LVODelay(a6)
addq.l #1,d3
lea timerbase,a1
cmp.l #100,IVT_Counter(a1)
blt.s .loop
.exitloop:
move.l #succname,d1
cmp.l #10,d3
ble.s .succ
move.l #failname,d1
.succ:
jsr _LVOVPrintf(a6)
lea timerbase,a1
bsr _StopIntervalTimer
.error
lea timerbase,a1
bsr _ExitIntervalTimer
move.l dosbase,d0
beq.s .nodos
move.l d0,a1
move.l 4.w,a6
jsr _LVOCloseLibrary(a6)
.nodos:
rts
ENDC ;DO_TEST
; A1 = IVT_TimerStruct
_InterruptIntervalTimer:
movem.l d1-a6,-(sp)
;<- basic Timer.device ops ->
move.l a1,a2
move.l IVT_Port(a2),a0
CALLEXEC GetMsg
tst.l d0
beq.s .error
move.l IVT_SigMask(a2),d0 ;move.l IVT_Signal(a2),d0
move.l IVT_SigTask(a2),a1
CALLEXEC Signal
tst.b IVT_Stopflag(a2)
bne.s .error ;not exactly an error but same consequence
move.l a2,a1
bsr _RestartIntervalTimer
;<- end basic Timer.device ops ->
;<- useful stuff ->
addq.l #1,IVT_Counter(a2)
;<- useful stuff ->
bra.s .endintroutine
.error:
clr.b IVT_Runflag(a2)
.endintroutine:
movem.l (sp)+,d1-a6
rts
_InitIntervalTimer: ;prepare timer.device for buffer swap interrupt
movem.l d1-a6,-(sp)
move.l a1,a2
move #IVT_Size-1,d0
.clr
clr.b (a1)+ ;
dbf d0,.clr
sf IVT_Init(a2)
sf IVT_Runflag(a2)
;signal to wait for from timer output if output queue full
moveq #-1,d0
CALLEXEC AllocSignal
move.l d0,IVT_Signal(a2) ; <0 ? error, valid are 0..31
blt .error ; error: have to go (shouldn't happen)
moveq #0,d1
bset d0,d1
move.l d1,IVT_SigMask(a2)
suba.l a1,a1
CALLEXEC FindTask
move.l d0,IVT_SigTask(a2)
CALLEXEC CreateMsgPort ;Create MsgPort for timing...
move.l d0,IVT_Port(a2)
beq .error ;0=error
move.l IVT_Port(a2),a0
move.l #IOTV_SIZE,d0
CALLEXEC CreateIORequest
move.l d0,IVT_IORequest(a2)
beq .error ;0=error
lea.l IVT_Int(a2),a0
lea _InterruptIntervalTimer(pc),a1
move.l a1,IS_CODE(a0)
move.l a2,IS_DATA(a0)
move.b #NT_INTERRUPT,LN_TYPE(a0)
move.b #0,LN_PRI(a0) ;-32,-16,0,16,32
move.l IVT_Port(a2),a1
;move.b #NT_MSGPORT,LN_TYPE(a1)
move.b #PA_SOFTINT,MP_FLAGS(a1)
move.l a0,MP_SIGTASK(a1) ;Softint eintragen
lea VBtimer_name,a0
moveq #UNIT_MICROHZ,d0
move.l IVT_IORequest(a2),a1
moveq #0,d1
CALLEXEC OpenDevice
tst.l d0
beq.s .ok
moveq #0,d0
bra.s .error
.ok ;OK, timer device is ready
st IVT_Init(a2)
moveq #1,d0 ;OK
.error
movem.l (sp)+,d1-a6
;ret 0 <= err, >0 = ok
rts
_ExitIntervalTimer:
;stop timer.device
movem.l d0-a6,-(sp)
move.l a1,a2
sf IVT_Init(a2)
move.l IVT_IORequest(a2),d5
beq.s .noIO
tst.b IVT_Runflag(a2)
beq.s .noAbort
st IVT_Stopflag(a2)
ifne 0
move.l d5,a1
CALLEXEC WaitIO
else
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
endc
.noAbort:
move.l d5,a1
CALLEXEC CloseDevice
move.l d5,a0
CALLEXEC DeleteIORequest
clr.l IVT_IORequest(a2)
.noIO:
move.l IVT_Port(a2),d0
beq.s .noPort
move.l d0,a0
CALLEXEC DeleteMsgPort
clr.l IVT_Port(a2)
.noPort:
move.l IVT_Signal(a2),d0
not.l d0 ;if( d0 == -1 ) d0 = 0
beq.s .nosig
not.l d0 ; swap back
CALLEXEC FreeSignal
moveq #-1,d0
move.l d0,IVT_Signal(a2)
.nosig:
movem.l (sp)+,d0-a6
rts
; input:
; A1 = IVT Structure (after init)
; D1 = Number of Microseconds
_StartIntervalTimer:
movem.l d0/a1/a2/a6,-(Sp)
move.l a1,a2
move.l d1,IVT_Interval(A2)
sf IVT_Stopflag(a2)
tst.b IVT_Runflag(a2) ;running already ? -> keep it running
bne.s .notimer
move.l a2,a1
bsr _RestartIntervalTimer
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
; Non-Public
; A1 - IVT Structure
_RestartIntervalTimer:
movem.l d0/a1/a2/a6,-(sp)
move.l a1,a2
move.l IVT_IORequest(a2),d0
beq.s .notimer
move.l d0,a1
move.w #TR_ADDREQUEST,IO_COMMAND(a1)
clr.b IO_FLAGS(a1)
clr.l IOTV_TIME+EV_HI(a1)
move.l IVT_Interval(A2),IOTV_TIME+EV_LO(a1)
move.l IO_DEVICE(a1),a6
jsr DEV_BEGINIO(A6)
st IVT_Runflag(a2)
.notimer
movem.l (sp)+,d0/a1/a2/a6
rts
_StopIntervalTimer:
movem.l d0-a6,-(Sp)
move.l a1,a2
st IVT_Stopflag(a2)
tst.b IVT_Runflag(a2)
beq.s .noIO
move.l IVT_IORequest(a2),d5
beq.s .noIO
move.l d5,a1
move.l IO_DEVICE(a1),a6
jsr DEV_ABORTIO(A6) ;clear pending requests
sf IVT_Runflag(a2)
.noIO:
movem.l (sp)+,d0-a6
rts
section data,data
dosbase: dc.l 0
stdout: dc.l 0
timerbase: ds.b IVT_Size
VBtimer_name: dc.b "timer.device",0
ifne DO_TEST
dos_name: dc.b "dos.library",0
succname: dc.b "success: timer worked",10,0
failname: dc.b "failure: timer didn't work",10,0
failtimer: dc.b "timer open failure",10,0
ENDC ;DO_TEST
END
@@ -0,0 +1,57 @@
/*
******************************************************************************
* Name: intervaltimer.h *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
*/
#ifndef _INC_INTERVALTIMER_H
#define _INC_INTERVALTIMER_H
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <devices/timer.h>
struct IVT_TimerStruct {
unsigned long IVT_Interval;
long IVT_Signal;
unsigned long IVT_SigMask;
struct Task *IVT_SigTask;
struct MsgPort *IVT_Port;
struct IORequest *IVT_IORequest;
struct Device *IVT_Device;
struct Interrupt IVT_Int;
unsigned char IVT_Init;
unsigned char IVT_Runflag;
unsigned char IVT_Stopflag;
unsigned char IVT_Unused;
unsigned long IVT_Counter;
};
#if 1
#include "compiler.h"
#else
/* self-contained in here as well */
#ifdef __SASC
#define ASM __asm
#define ASMR(x) register __ ## x
#define ASMREG(x)
#define SAVEDS __saveds
#else
#define ASM
#define ASMR(x) register
#define ASMREG(x) __asm("" #x "")
#define SAVEDS __saveds
#endif
#endif
ASM SAVEDS int InitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int StartIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1),
ASMR(d1) unsigned long Microsecs ASMREG(d1) );
ASM SAVEDS int StopIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
ASM SAVEDS int ExitIntervalTimer( ASMR(a1) struct IVT_TimerStruct* IntervalTimer ASMREG(a1) );
#endif /* _INC_INTERVALTIMER_H */
@@ -0,0 +1,27 @@
******************************************************************************
* Name: intervaltimer.i *
* Date: $Date: 2016-12-18 22:20:04 +0100 (So, 18. Dez 2016) $ *
* Authors: Henryk Richter *
******************************************************************************
ifnd _INC_INTERVALTIMER_I
_INC_INTERVALTIMER_I EQU 1
include exec/types.i
STRUCTURE IVT_TimerStruct,0
ULONG IVT_Interval ;<1e6 in microseconds
LONG IVT_Signal ;Signal that is sent back to task from interrupt
ULONG IVT_SigMask ;signal mask
APTR IVT_SigTask ;Task that is signaled
APTR IVT_Port
APTR IVT_IORequest
APTR IVT_Device
STRUCT IVT_Int,IS_SIZE ;
BYTE IVT_Init ; !=0 - initialized
BYTE IVT_Runflag ; current status ( !=0 = running )
BYTE IVT_Stopflag ; stop request if != 0
BYTE IVT_Unused ;
ULONG IVT_Counter ; Counter (statistics)
LABEL IVT_Size
endc ;_INC_TIMERTEST_I
+126
View File
@@ -0,0 +1,126 @@
; ------------------------------------------------------------------------------
; | Flash ROM Read Serial Number |
; | Copyright (c) 2016 APOLLO TEAM |
; | Christoph Höhne, modifications by Henryk Richter |
; ------------------------------------------------------------------------------
FLASH_ID EQU $14 ;cmd=0xAB
FLASH_ADR_W EQU $00DFF1F8
FLASH_ADR_R EQU $00DFF1FA
FLASH_MAGIC EQU $42420000
FLASH_CS EQU $1
FLASH_CSn EQU $0
FLASH_CLK EQU $2
FLASH_CLKn EQU $0
FLASH_MOSI EQU $4
FLASH_MOSIn EQU $0
FLASH_MISO EQU $1
FLASH_MISOn EQU $0
;VAMPIRE_MAGIC EQU ascii "Vamp"
VAMPIRE_MAGIC EQU $56616D70
xdef _vampire_UUID
;-------- Get 64 Bit unique ID --------------------
; Input
; A1 - pointer to 8 Bytes storage
; Output
; D0 = 0 - failure
; >0 - success
_vampire_UUID:
movem.l d1-a6,-(sp) ;lazy register store
bsr flashreadid
cmpi.b #FLASH_ID,d0
bne.s error$
bsr flashuniqueid
move.l d0,(a1)
move.l d1,4(a1)
moveq #1,d0
bra.s return$
error$:
moveq #0,d0
return$
movem.l (sp)+,d1-a6
rts
; uint8_t flashreadid(void)
; D0
flashreadid:
move.b #$AB,d1
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashbyte
bsr flashdeselect
rts
; uint64_t flashuniqueid(void)
; D0/D1
flashuniqueid:
move.b #$5A,d1
bsr flashbyte
clr d1
bsr flashbyte
clr d1
bsr flashbyte
move.b #$F8,d1
bsr flashbyte
bsr flashbyte
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
move.l d1,d3
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
lsl.l #8,d1
bsr flashbyte
move.l d3,d0
bsr flashdeselect
rts
; void flashselect(void)
;flashselect:
; move.l #FLASH_MAGIC+FLASH_CSn+FLASH_CLKn,d0
; move.l d0,FLASH_ADR_W
; rts
; void flashdeselect(void)
flashdeselect:
move.l #FLASH_MAGIC+FLASH_CS+FLASH_CLK,d3
move.l d3,FLASH_ADR_W
rts
; uint8_t flashbyte(uint8_t)
; D0 D1
flashbyte:
moveq #7,d2
;bsr flashselect
move.l #FLASH_MAGIC+FLASH_CSn+FLASH_CLKn,d0
move.l d0,FLASH_ADR_W
flashbyteloop:
lsl.b #1,d1
scs.b d0
andi.w #FLASH_MOSI,d0
move.l d0,FLASH_ADR_W
ori.w #FLASH_CLK,d0
move.l d0,FLASH_ADR_W
or.w FLASH_ADR_R,d1
dbra d2,flashbyteloop
move.b d1,d0
rts
+24
View File
@@ -0,0 +1,24 @@
/*
vampuuid.h
author: Henryk Richter <henryk.richter@gmx.net>
purpose: Vampire UUID reading prototypes
*/
#ifndef _INC_vampuuid_H
#define _INC_vampuuid_H
#include "compiler.h"
/*
Purpose: Read UUID of Vampire cards
input: buffer - storage for the returned UUID (8 Bytes)
output: success/failure
>0 - all fine, ready to work
<=0 - init problem (codes undefined yet)
*/
ASM SAVEDS int vampire_UUID( ASMR(a1) unsigned char *buffer ASMREG(a1) );
#endif /* __INC_vampuuid_H */