453 lines
12 KiB
C
453 lines
12 KiB
C
/*
|
|
* uIP lan91c96 (smc9194) driver
|
|
* Based on cs8900a driver, copyrighted (c) 2001, by Adam Dunkels
|
|
* Copyright (c) 2003, Josef Soucek
|
|
* All rights reserved.
|
|
*
|
|
* Ethernet card for Commodore 64, based on lan91c96 chip
|
|
* is a device created by IDE64 Project team.
|
|
* More information: http://ide64.come.to
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $Id: lan91c96.c,v 1.1 2006/06/17 22:41:26 adamdunkels Exp $
|
|
*
|
|
*/
|
|
|
|
#include "lan91c96.h"
|
|
#include "contiki-net.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
// #define DEBUG
|
|
|
|
#define ETHBASE 0xde10
|
|
|
|
#define ETHBSR ETHBASE+0x0e /* Bank select register R/W (2B) */
|
|
|
|
/* Register bank 0 */
|
|
|
|
#define ETHTCR ETHBASE /* Transmition control register R/W (2B) */
|
|
#define ETHEPHSR ETHBASE+2 /* EPH status register R/O (2B) */
|
|
#define ETHRCR ETHBASE+4 /* Receive control register R/W (2B) */
|
|
#define ETHECR ETHBASE+6 /* Counter register R/O (2B) */
|
|
#define ETHMIR ETHBASE+8 /* Memory information register R/O (2B) */
|
|
#define ETHMCR ETHBASE+0x0a /* Memory Config. reg. +0 R/W +1 R/O (2B) */
|
|
|
|
/* Register bank 1 */
|
|
|
|
#define ETHCR ETHBASE /* Configuration register R/W (2B) */
|
|
#define ETHBAR ETHBASE+2 /* Base address register R/W (2B) */
|
|
#define ETHIAR ETHBASE+4 /* Individual address register R/W (6B) */
|
|
#define ETHGPR ETHBASE+0x0a /* General address register R/W (2B) */
|
|
#define ETHCTR ETHBASE+0x0c /* Control register R/W (2B) */
|
|
|
|
/* Register bank 2 */
|
|
|
|
#define ETHMMUCR ETHBASE /* MMU command register W/O (1B) */
|
|
#define ETHAUTOTX ETHBASE+1 /* AUTO TX start register R/W (1B) */
|
|
#define ETHPNR ETHBASE+2 /* Packet number register R/W (1B) */
|
|
#define ETHARR ETHBASE+3 /* Allocation result register R/O (1B) */
|
|
#define ETHFIFO ETHBASE+4 /* FIFO ports register R/O (2B) */
|
|
#define ETHPTR ETHBASE+6 /* Pointer register R/W (2B) */
|
|
#define ETHDATA ETHBASE+8 /* Data register R/W (4B) */
|
|
#define ETHIST ETHBASE+0x0c /* Interrupt status register R/O (1B) */
|
|
#define ETHACK ETHBASE+0x0c /* Interrupt acknowledge register W/O (1B) */
|
|
#define ETHMSK ETHBASE+0x0d /* Interrupt mask register R/W (1B) */
|
|
|
|
/* Register bank 3 */
|
|
|
|
#define ETHMT ETHBASE /* Multicast table R/W (8B) */
|
|
#define ETHMGMT ETHBASE+8 /* Management interface R/W (2B) */
|
|
#define ETHREV ETHBASE+0x0a /* Revision register R/W (2B) */
|
|
#define ETHERCV ETHBASE+0x0c /* Early RCV register R/W (2B) */
|
|
|
|
#define BANK(num) asm("lda #%b", num); asm("sta %w", ETHBSR);
|
|
|
|
#ifdef DEBUG
|
|
static void print_packet(u8_t *, u16_t);
|
|
#endif
|
|
|
|
static u8_t packet_status;
|
|
static u16_t packet_length;
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
#pragma optimize(push, off)
|
|
void lan91c96_init(void)
|
|
{
|
|
/* Check if high byte is 0x33 */
|
|
asm("lda %w", ETHBSR+1);
|
|
asm("cmp #$33");
|
|
asm("beq %g", L1);
|
|
|
|
asm("inc $d021"); /* Error */
|
|
|
|
L1:
|
|
/* Reset ETH card */
|
|
BANK(0);
|
|
asm("lda #%%10000000"); /* Software reset */
|
|
asm("sta %w", ETHRCR+1);
|
|
|
|
asm("lda #0");
|
|
asm("sta %w", ETHRCR);
|
|
asm("sta %w", ETHRCR+1);
|
|
|
|
/* delay */
|
|
asm("ldy #0");
|
|
L2:
|
|
asm("cmp ($ff,x)"); /* 6 cycles */
|
|
asm("cmp ($ff,x)"); /* 6 cycles */
|
|
asm("dey"); /* 2 cycles */
|
|
asm("bne %g", L2); /* 3 cycles */
|
|
/* 17*256=4352 => 4,4 ms */
|
|
|
|
/* Enable transmit and receive */
|
|
asm("lda #%%10000001"); /* Enable transmit TXENA, PAD_EN */
|
|
asm("sta %w", ETHTCR);
|
|
asm("lda #%%00000011"); /* Enable receive, strip CRC ??? */
|
|
asm("sta %w", ETHRCR+1);
|
|
|
|
BANK(1);
|
|
asm("lda %w", ETHCR+1);
|
|
asm("ora #%%00010000"); /* No wait (IOCHRDY) */
|
|
asm("sta %w", ETHCR+1);
|
|
|
|
asm("lda #%%00001001"); /* Auto release */
|
|
asm("sta %w", ETHCTR+1);
|
|
|
|
/* Set MAC address */
|
|
asm("lda %v", uip_ethaddr);
|
|
asm("sta %w", ETHIAR);
|
|
asm("lda %v+1", uip_ethaddr);
|
|
asm("sta %w", ETHIAR+1);
|
|
asm("lda %v+2", uip_ethaddr);
|
|
asm("sta %w", ETHIAR+2);
|
|
asm("lda %v+3", uip_ethaddr);
|
|
asm("sta %w", ETHIAR+3);
|
|
asm("lda %v+4", uip_ethaddr);
|
|
asm("sta %w", ETHIAR+4);
|
|
asm("lda %v+5", uip_ethaddr);
|
|
asm("sta %w", ETHIAR+5);
|
|
|
|
BANK(2);
|
|
asm("lda #%%00001111"); /* RCV INT, ALLOC INT, TX INT, TX EMPTY */
|
|
asm("sta %w", ETHMSK);
|
|
}
|
|
#pragma optimize(pop)
|
|
/*-----------------------------------------------------------------------------------*/
|
|
#pragma optimize(push, off)
|
|
u16_t lan91c96_poll(void)
|
|
{
|
|
#ifdef DEBUG
|
|
BANK(0);
|
|
printf("RAM: %d ", ((*(unsigned int *)(ETHMIR)) & 0xff00));
|
|
BANK(2);
|
|
#endif
|
|
|
|
asm("lda %w", ETHIST);
|
|
asm("and #%%00000001"); /* RCV INT */
|
|
asm("bne %g", L1);
|
|
|
|
/* No packet available */
|
|
return 0;
|
|
|
|
L1:
|
|
|
|
#ifdef DEBUG
|
|
printf("RCV: IRQ\n");
|
|
#endif
|
|
|
|
asm("lda #0");
|
|
asm("sta %w", ETHPTR);
|
|
asm("lda #%%11100000"); /* RCV,AUTO INCR.,READ */
|
|
asm("sta %w", ETHPTR+1);
|
|
|
|
asm("lda %w", ETHDATA); /* Status word */
|
|
asm("lda %w", ETHDATA);
|
|
asm("sta %v", packet_status); /* High byte only */
|
|
|
|
asm("lda %w", ETHDATA); /* Total number of bytes */
|
|
asm("sta %v", packet_length);
|
|
asm("lda %w", ETHDATA);
|
|
asm("sta %v+1", packet_length);
|
|
|
|
/* Last word contain 'last data byte' and 0x60 */
|
|
/* or 'fill byte' and 0x40 */
|
|
|
|
packet_length -= 6; /* The packet contains 3 extra words */
|
|
|
|
asm("lda %v", packet_status);
|
|
asm("and #$10");
|
|
asm("beq %g", L2);
|
|
|
|
packet_length++;
|
|
|
|
#ifdef DEBUG
|
|
printf("RCV: odd number of bytes\n");
|
|
#endif
|
|
|
|
L2:
|
|
|
|
#ifdef DEBUG
|
|
printf("RCV: L:%d ST-HIGH:0x%02x ", packet_length, packet_status);
|
|
#endif
|
|
|
|
if(packet_length > UIP_BUFSIZE) {
|
|
|
|
/* Remove and release RX packet from FIFO */
|
|
asm("lda #%%10000000");
|
|
asm("sta %w", ETHMMUCR);
|
|
|
|
#ifdef DEBUG
|
|
printf("RCV: UIP_BUFSIZE exceeded - packet dropped!\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
asm("lda #<%v", uip_buf);
|
|
asm("sta ptr1");
|
|
asm("lda #>%v", uip_buf);
|
|
asm("sta ptr1+1");
|
|
|
|
asm("lda %v+1", packet_length);
|
|
asm("sta tmp1");
|
|
|
|
asm("ldy #0");
|
|
L3:
|
|
asm("lda %w", ETHDATA);
|
|
asm("sta (ptr1),y");
|
|
asm("iny");
|
|
asm("bne %g", L4);
|
|
asm("inc ptr1+1");
|
|
L4:
|
|
asm("cpy %v", packet_length);
|
|
asm("bne %g", L3);
|
|
asm("dec tmp1");
|
|
asm("bpl %g", L3);
|
|
|
|
/* Remove and release RX packet from FIFO */
|
|
asm("lda #%%10000000");
|
|
asm("sta %w", ETHMMUCR);
|
|
|
|
#ifdef DEBUG
|
|
print_packet(uip_buf, packet_length);
|
|
#endif
|
|
|
|
return packet_length;
|
|
}
|
|
#pragma optimize(pop)
|
|
/*-----------------------------------------------------------------------------------*/
|
|
#pragma optimize(push, off)
|
|
void lan91c96_send(void)
|
|
{
|
|
/* First 14+40 (IP and TCP header) is send from uip_buf */
|
|
/* than data from uip_appdata */
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: send packet\n");
|
|
#endif
|
|
|
|
asm("lda %v+1", uip_len);
|
|
asm("ora #%%00100000"); /* Allocate memory for TX */
|
|
asm("sta %w", ETHMMUCR);
|
|
|
|
asm("ldy #8"); /* Wait... */
|
|
L1: /* Wait for allocation ready */
|
|
asm("lda %w", ETHIST);
|
|
asm("and #%%00001000"); /* ALLOC INT */
|
|
asm("bne %g", L2);
|
|
asm("dey");
|
|
asm("bne %g", L1);
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: ERR: memory alloc timeout\n");
|
|
#endif
|
|
|
|
return;
|
|
|
|
L2:
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: packet memory allocated\n");
|
|
#endif
|
|
|
|
asm("lda #%%00001000"); /* Acknowledge int, is it necessary ??? */
|
|
asm("sta %w", ETHACK);
|
|
|
|
asm("lda %w", ETHARR);
|
|
asm("sta %w", ETHPNR); /* Set packet address */
|
|
|
|
asm("lda #0");
|
|
asm("sta %w", ETHPTR);
|
|
asm("lda #%%01000000"); /* AUTO INCR. */
|
|
asm("sta %w", ETHPTR+1);
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: L:%d ", uip_len);
|
|
#endif
|
|
|
|
asm("lda #0"); /* Status written by CSMA */
|
|
asm("sta %w", ETHDATA);
|
|
asm("sta %w", ETHDATA);
|
|
|
|
asm("lda %v", uip_len);
|
|
asm("and #$01");
|
|
asm("beq %g", L3);
|
|
|
|
packet_length = uip_len + 5;
|
|
asm("jmp %g", L4);
|
|
|
|
L3:
|
|
packet_length = uip_len + 6; /* +6 for status word, length and ctl byte */
|
|
|
|
L4:
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: L:%d ", packet_length);
|
|
#endif
|
|
|
|
asm("lda %v", packet_length);
|
|
asm("sta %w", ETHDATA);
|
|
asm("lda %v+1", packet_length);
|
|
asm("sta %w", ETHDATA);
|
|
|
|
#ifdef DEBUG
|
|
print_packet(uip_buf, uip_len);
|
|
#endif
|
|
|
|
/* Send 14+40=54 bytes of header */
|
|
|
|
if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
|
|
|
|
#ifdef DEBUG
|
|
printf("SND: short packet sent.\n");
|
|
#endif
|
|
|
|
asm("ldy #0");
|
|
L5:
|
|
asm("lda %v,y", uip_buf);
|
|
asm("sta %w", ETHDATA);
|
|
asm("iny");
|
|
asm("cpy %v", uip_len);
|
|
asm("bne %g", L5);
|
|
|
|
} else {
|
|
|
|
asm("ldy #0");
|
|
L6:
|
|
asm("lda %v,y", uip_buf);
|
|
asm("sta %w", ETHDATA);
|
|
asm("iny");
|
|
asm("cpy #%b", UIP_LLH_LEN + UIP_TCPIP_HLEN);
|
|
asm("bne %g", L6);
|
|
|
|
packet_length = uip_len - (UIP_LLH_LEN + UIP_TCPIP_HLEN);
|
|
|
|
asm("lda %v", uip_appdata); /* uip_appdata is pointer */
|
|
asm("sta ptr1");
|
|
asm("lda %v+1", uip_appdata);
|
|
asm("sta ptr1+1");
|
|
|
|
asm("ldy #0");
|
|
L7:
|
|
asm("lda (ptr1),y");
|
|
asm("sta %w", ETHDATA);
|
|
asm("iny");
|
|
asm("bne %g", L8);
|
|
asm("inc ptr1+1");
|
|
L8:
|
|
asm("cpy %v", packet_length);
|
|
asm("bne %g", L7);
|
|
asm("dec %v+1", packet_length);
|
|
asm("bpl %g", L7);
|
|
}
|
|
|
|
asm("lda %v", packet_length);
|
|
asm("and #$01");
|
|
asm("beq %g", L9);
|
|
|
|
asm("lda #%%00100000");
|
|
asm("sta %w", ETHDATA); /* Control byte */
|
|
|
|
asm("lda #%%11000000"); /* ENQUEUE PACKET - transmit packet */
|
|
asm("sta %w", ETHMMUCR);
|
|
|
|
#ifdef DEBUG
|
|
printf("\n## %02x", *(unsigned char *)(ETHIST));
|
|
#endif
|
|
|
|
return;
|
|
|
|
L9:
|
|
asm("lda #0");
|
|
asm("sta %w", ETHDATA); /* Fill byte */
|
|
asm("sta %w", ETHDATA); /* Control byte */
|
|
|
|
asm("lda #%%11000000"); /* ENQUEUE PACKET - transmit packet */
|
|
asm("sta %w", ETHMMUCR);
|
|
|
|
#ifdef DEBUG
|
|
printf("\n## %02x\n", *(unsigned char *)(ETHIST));
|
|
#endif
|
|
|
|
}
|
|
#pragma optimize(pop)
|
|
/*-----------------------------------------------------------------------------------*/
|
|
#ifdef DEBUG
|
|
static void print_packet(u8_t *buf, u16_t length)
|
|
{
|
|
int i;
|
|
int remainder;
|
|
int lines;
|
|
u8_t a;
|
|
int cur;
|
|
int address=0;
|
|
|
|
printf("\nPacket of length %d \n", length);
|
|
|
|
lines = length / 8;
|
|
remainder = length % 8;
|
|
|
|
for(i = 0; i < lines; i++) {
|
|
printf(":%04x ", address=i*8);
|
|
|
|
for(cur = 0; cur < 8; cur++) {
|
|
a = *(buf++);
|
|
printf("%02x ", a);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf(":%04x ", address+8);
|
|
|
|
for (i = 0; i < remainder; i++) {
|
|
a = *(buf++);
|
|
printf("%02x ", a);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif /* DEBUG */
|