drivers/char/misc.c

Joachim Weber (jweb@CimTextil.DE)
Wed, 26 Nov 1997 15:32:52 +0100 (MET)


Hi Linus, hi Hackers,

last week a friend of mine and I succeeded in writing an ethercard-driver
for MYLEX LNE390A EISA boards. I myself own (among some other machines) a
HP Netserver 4/66 EISA/PCI and got hold of two of the boards mentioned above.

>From our point of view this driver works fine. We even tested the two boards
simultaneously ... no more problems seem(!) to be left

To be compiled into the kernel it needs the following code added into "Space.c":
..
extern int mx390a_probe(struct device *);
..
.. and later on in the same file:
..
#ifdef CONFIG_MX390A /* MYLEX EISA LNE390A. */
&& mx390a_probe(dev)
#endif
..
together with the appropriate entry in "Config.in".

To be loaded as module it needs a preloaded "8390.o".

We hope, this driver will find its way into the official source-tree.

10q

Joe

snip -----------------------------

/* mx390a.c driver for the MYLEX LNE390A EISA ethernet adaptor. */
/*
Derived by Joe Weber from Donald Beckers ac3200.c with
special thanks to Achim Neuman for 1st aid in debugging.
Copyright by the Autor 1997.
This software may only be used and distributed according to
the terms of the GNU Public License.

The author may be reached as jweb@cimtextil.de.

This is a driver for the MYLEX LNE390A EISA Ethernet LAN
Adapter. The programming information is from Russel Nelsons
"crynwr"-package "mylex.asm"
*/

static const char *version =
"mx390a.c:v0.89 97/11/20 Joe Weber (jweb@cimtextil.de)\n";

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

#include <asm/system.h>
#include <asm/io.h>

#include "8390.h"

/* Offsets from the base address. */
#define MX_NIC_BASE 0x00
#define MX_SA_PROM 0x16 /* The station address P
ROM. */
#define MX_ADDR0 0x00 /* Prefix station addres
s values. */
#define MX_ADDR1 0x80 /* !!!!These are just gu
esses!!!! */
#define MX_ADDR2 0xe5
#define MX_ID_PORT 0xC80
#define MX_EISA_ID 0x010009835
#define MX_RESET_PORT 0xC84
#define MX_RESET 0x00
#define MX_ENABLE 0x01
#define MX_CONFIG 0xC90 /* The configuration port. */

#define MX_IO_EXTENT 0x10 /* IS THIS REALLY TRUE ??? */
/* Actually accessed is:
* MX_NIC_BASE (
0-15)
* MX_SA_PROM (0
-5)
* MX_ID_PORT (0
-3)
* MX_RESET_PORT
* MX_CONFIG
*/

/* Decoding of the configuration register. */
static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3};
static int addrmap[8] =
{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};

#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
#define config2mem(configval) addrmap[(configval) & 7]
#define config2name(configval) port_name[((configval) >> 6) & 3]

/* First and last 8390 pages. */
#define MX_START_PG 0x00 /* First page of 8390 TX buffer */
#define MX_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */

int mx390a_probe(struct device *dev);
static int mx_probe1(int ioaddr, struct device *dev);

static int mx_open(struct device *dev);
static void mx_reset_8390(struct device *dev);
static void mx_block_input(struct device *dev, int count,
struct sk_buff *skb, int ring_offset);
static void mx_block_output(struct device *dev, const int count,
const unsigned char *buf
, const int start_page);
static void mx_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);

static int mx_close_card(struct device *dev);
_

/* Probe for the MX390A

The MX390A can be identified by either the EISA configuration registers,
or the unique value in the station address PROM.
*/

int mx390a_probe(struct device *dev)
{
unsigned short ioaddr = dev->base_addr;

if (ioaddr > 0x1ff) /* Check a single specified location. */
return mx_probe1(ioaddr, dev);
else if (ioaddr > 0) /* Don't probe at all. */
return ENXIO;

/* If you have a pre 0.99pl15 machine you should delete this line. */
if ( ! EISA_bus)
return ENXIO;

for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
if (check_region(ioaddr, MX_IO_EXTENT))
continue;
if (mx_probe1(ioaddr, dev) == 0)
return 0;
}

return ENODEV;
}

static int mx_probe1(int ioaddr, struct device *dev)
{
int i;

#ifndef final_version
printk("MX390A ethercard probe at %#3x:", ioaddr);

for(i = 0; i < 6; i++)
printk(" %02x", inb(ioaddr + MX_SA_PROM + i));
#endif

/* !!!!The values of MX_ADDRn (see above) should be corrected when we
find out the correct station address prefix!!!! */
if (inb(ioaddr + MX_SA_PROM + 0) != MX_ADDR0
|| inb(ioaddr + MX_SA_PROM + 1) != MX_ADDR1
|| inb(ioaddr + MX_SA_PROM + 2) != MX_ADDR2 ) {
#ifndef final_version
printk(" not found (invalid prefix).\n");
#endif
return ENODEV;
}

/* The correct probe method is to check the EISA ID. */
for (i = 0; i < 4; i++)
if (inl(ioaddr + MX_ID_PORT) != MX_EISA_ID) {
printk("EISA ID mismatch, %8x vs %8x.\n",
inl(ioaddr + MX_ID_PORT), MX_EISA_ID);
return ENODEV;
}

/* We should have a "dev" from Space.c or the static module table. */
if (dev == NULL) {
printk("mx390a.c: Passed a NULL device.\n");
dev = init_etherdev(0, 0);
}

for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = inb(ioaddr + MX_SA_PROM + i);

#ifndef final_version
printk("\nMX390A ethercard configuration register is %#02x,"
" EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + MX_CONFIG),
inb(ioaddr + MX_ID_PORT + 0), inb(ioaddr + MX_ID_PORT + 1),
inb(ioaddr + MX_ID_PORT + 2), inb(ioaddr + MX_ID_PORT + 3));
#endif

/* Assign and allocate the interrupt now. */
if (dev->irq == 0)
dev->irq = config2irq(inb(ioaddr + MX_CONFIG));
else if (dev->irq == 2)
dev->irq = 9;

if (request_irq(dev->irq, ei_interrupt, 0, "mx390a", NULL)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return EAGAIN;
}

/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to allocate memory for dev->priv.\n");
free_irq(dev->irq, NULL);
return -ENOMEM;
}

request_region(ioaddr, MX_IO_EXTENT, "mx390a");

dev->base_addr = ioaddr;

#ifdef notyet
if (dev->mem_start) { /* Override the value from the b
oard. */
for (i = 0; i < 7; i++)
if (addrmap[i] == dev->mem_start)
break;
if (i >= 7)
i = 0;
outb((inb(ioaddr + MX_CONFIG) & ~7) | i, ioaddr + MX_CONFIG);
}
#endif

dev->if_port = inb(ioaddr + MX_CONFIG) >> 6;
dev->mem_start = config2mem(inb(ioaddr + MX_CONFIG));
dev->rmem_start = dev->mem_start + TX_PAGES*256;
dev->mem_end = dev->rmem_end = dev->mem_start
+ (MX_STOP_PG - MX_START_PG)*256;

ei_status.name = "MX390A";
ei_status.tx_start_page = MX_START_PG;
ei_status.rx_start_page = MX_START_PG + TX_PAGES;
ei_status.stop_page = MX_STOP_PG;
ei_status.word16 = 1;

printk("\n%s: MX390A at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n
",
dev->name, ioaddr, dev->irq, port_name[dev->if_port],
dev->mem_start, dev->mem_end-1);

if (ei_debug > 0)
printk(version);

ei_status.reset_8390 = &mx_reset_8390;
ei_status.block_input = &mx_block_input;
ei_status.block_output = &mx_block_output;
ei_status.get_8390_hdr = &mx_get_8390_hdr;

dev->open = &mx_open;
dev->stop = &mx_close_card;
NS8390_init(dev, 0);
return 0;
}

static int mx_open(struct device *dev)
{
#ifdef notyet
/* Someday we may enable the IRQ and shared memory here. */
int ioaddr = dev->base_addr;

if (request_irq(dev->irq, ei_interrupt, 0, "mx390a", NULL))
return -EAGAIN;
#endif

ei_open(dev);

MOD_INC_USE_COUNT;

return 0;
}

static void mx_reset_8390(struct device *dev)
{
ushort ioaddr = dev->base_addr;

outb(MX_RESET, ioaddr + MX_RESET_PORT);
if (ei_debug > 1) printk("resetting MX390A, t=%ld...", jiffies);

ei_status.txing = 0;
outb(MX_ENABLE, ioaddr + MX_RESET_PORT);
if (ei_debug > 1) printk("reset done\n");

return;
}

/* Grab the 8390 specific header. Similar to the block_input routine, but
we don't need to be concerned with ring wrap as the header will be at
the start of a page, so we optimize accordingly. */

static void
mx_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
{
unsigned long hdr_start = dev->mem_start + ((ring_page - MX_START_PG)<<8
);
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
}

/* Block input and output are easy on shared memory ethercards, the only
complication is when the ring buffer wraps. */

static void mx_block_input(struct device *dev, int count, struct sk_buff *skb,
int ring_offset)
{
unsigned long xfer_start = dev->mem_start + ring_offset - (MX_START_PG<<
8);

if (xfer_start + count > dev->rmem_end) {
/* We must wrap the input move. */
int semi_count = dev->rmem_end - xfer_start;
int i = semi_count / 4, j, ofs;

count -= semi_count;
for(ofs = 0; ofs < i; ofs++)
{
*(((unsigned long *)skb->data) + ofs) = *(((unsigned long *)xfer_start) + ofs
);
}
i += count / 4;
if(count % 4) i++;
for(j = 0; ofs < i; ofs++, j++)
{
*(((unsigned long *)skb->data) + ofs) = *(((unsigned long *)dev->rmem_start)
+ j);
}
} else {
/* Packet is in one chunk */
int i = count / 4, ofs;

if(count % 4) i++;
for(ofs = 0; ofs < i; ofs++)
{
*(((unsigned long *)skb->data) + ofs) = *(((unsigned long *)xfer_start) + ofs
);
}
}
}

static void mx_block_output(struct device *dev, int count,
const unsigned char *buf
, int start_page)
{
unsigned long shmem = dev->mem_start + ((start_page - MX_START_PG)<<8);

int i = count / 4, ofs;

if(count - 4 * (count / 4)) i++;
for(ofs = 0; ofs < i; ofs++)
{
*(((unsigned long *)shmem) + ofs) = *(((unsigned long *)buf) + o
fs);
}
}

static int mx_close_card(struct device *dev)
{
dev->start = 0;
dev->tbusy = 1;

if (ei_debug > 1)
printk("%s: Shutting down ethercard.\n", dev->name);

#ifdef notyet
/* We should someday disable shared memory and interrupts. */
outb(0x00, ioaddr + 6); /* Disable interrupts. */
free_irq(dev->irq, NULL);
irq2dev_map[dev->irq] = 0;
#endif

ei_close(dev);

MOD_DEC_USE_COUNT;

return 0;
}

#ifdef MODULE
#define MAX_MX39_CARDS 4 /* Max number of MX39 cards per module */
#define NAMELEN 8 /* # of chars for storing dev->name */
static char namelist[NAMELEN * MAX_MX39_CARDS] = { 0, };
static struct device dev_mx39[MAX_MX39_CARDS] = {
{
NULL, /* assign a chunk of namelist[] below */
0, 0, 0, 0,
0, 0,
0, 0, 0, NULL, NULL
},
};

static int io[MAX_MX39_CARDS] = { 0, };
static int irq[MAX_MX39_CARDS] = { 0, };
static int mem[MAX_MX39_CARDS] = { 0, };

int
init_module(void)
{
int this_dev, found = 0;

for (this_dev = 0; this_dev < MAX_MX39_CARDS; this_dev++) {
struct device *dev = &dev_mx39[this_dev];
dev->name = namelist+(NAMELEN*this_dev);
dev->irq = irq[this_dev];
dev->base_addr = io[this_dev];
dev->mem_start = mem[this_dev]; /* Currently ignored by
driver */
dev->init = mx390a_probe;
/* Default is to only install one card. */
if (io[this_dev] == 0 && this_dev != 0) break;
if (register_netdev(dev) != 0) {
printk(KERN_WARNING "mx390a.c: No mx390a card found (i/o = 0x%x).\n", io[this
_dev]);
if (found != 0) return 0; /* Got at least one. */
return -ENXIO;
}
found++;
}

return 0;
}

void
cleanup_module(void)
{
int this_dev;

for (this_dev = 0; this_dev < MAX_MX39_CARDS; this_dev++) {
struct device *dev = &dev_mx39[this_dev];
if (dev->priv != NULL) {
kfree(dev->priv);
dev->priv = NULL;
/* Someday free_irq + irq2dev may be in mx_close_card()
*/
free_irq(dev->irq, NULL);
irq2dev_map[dev->irq] = NULL;
release_region(dev->base_addr, MX_IO_EXTENT);
unregister_netdev(dev);
}
}
}
#endif /* MODULE */
_
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-p
rototypes -O6 -m486 -c mx390a.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/

snap -----------------------------

----------------------------------
E-Mail: Joachim Weber <jweb@cimtextil.de>
Date: 26-Nov-97
Time: 15:32:52

This message was sent by XFMail
----------------------------------