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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
Executable
+1011
File diff suppressed because it is too large
Load Diff
Executable
+362
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Executable
+24
@@ -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 */
|
||||
Reference in New Issue
Block a user