1 |
1626 |
jcastillo |
/* 3c507.c: An EtherLink16 device driver for Linux. */
|
2 |
|
|
/*
|
3 |
|
|
Written 1993,1994 by Donald Becker.
|
4 |
|
|
|
5 |
|
|
Copyright 1993 United States Government as represented by the
|
6 |
|
|
Director, National Security Agency.
|
7 |
|
|
|
8 |
|
|
This software may be used and distributed according to the terms
|
9 |
|
|
of the GNU Public License, incorporated herein by reference.
|
10 |
|
|
|
11 |
|
|
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
|
12 |
|
|
Center of Excellence in Space Data and Information Sciences
|
13 |
|
|
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
|
14 |
|
|
|
15 |
|
|
Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
|
16 |
|
|
and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
|
17 |
|
|
Mark Salazar <leslie@access.digex.net> made the changes for cards with
|
18 |
|
|
only 16K packet buffers.
|
19 |
|
|
|
20 |
|
|
Things remaining to do:
|
21 |
|
|
Verify that the tx and rx buffers don't have fencepost errors.
|
22 |
|
|
Move the theory of operation and memory map documentation.
|
23 |
|
|
The statistics need to be updated correctly.
|
24 |
|
|
*/
|
25 |
|
|
|
26 |
|
|
static const char *version =
|
27 |
|
|
"3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
|
28 |
|
|
|
29 |
|
|
|
30 |
|
|
#include <linux/module.h>
|
31 |
|
|
|
32 |
|
|
/*
|
33 |
|
|
Sources:
|
34 |
|
|
This driver wouldn't have been written with the availability of the
|
35 |
|
|
Crynwr driver source code. It provided a known-working implementation
|
36 |
|
|
that filled in the gaping holes of the Intel documentation. Three cheers
|
37 |
|
|
for Russ Nelson.
|
38 |
|
|
|
39 |
|
|
Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
|
40 |
|
|
info that the casual reader might think that it documents the i82586 :-<.
|
41 |
|
|
*/
|
42 |
|
|
|
43 |
|
|
#include <linux/kernel.h>
|
44 |
|
|
#include <linux/sched.h>
|
45 |
|
|
#include <linux/types.h>
|
46 |
|
|
#include <linux/fcntl.h>
|
47 |
|
|
#include <linux/interrupt.h>
|
48 |
|
|
#include <linux/ptrace.h>
|
49 |
|
|
#include <linux/ioport.h>
|
50 |
|
|
#include <linux/in.h>
|
51 |
|
|
#include <linux/string.h>
|
52 |
|
|
#include <asm/system.h>
|
53 |
|
|
#include <asm/bitops.h>
|
54 |
|
|
#include <asm/io.h>
|
55 |
|
|
#include <asm/dma.h>
|
56 |
|
|
#include <linux/errno.h>
|
57 |
|
|
|
58 |
|
|
#include <linux/netdevice.h>
|
59 |
|
|
#include <linux/etherdevice.h>
|
60 |
|
|
#include <linux/skbuff.h>
|
61 |
|
|
#include <linux/malloc.h>
|
62 |
|
|
|
63 |
|
|
|
64 |
|
|
/* use 0 for production, 1 for verification, 2..7 for debug */
|
65 |
|
|
#ifndef NET_DEBUG
|
66 |
|
|
#define NET_DEBUG 1
|
67 |
|
|
#endif
|
68 |
|
|
static unsigned int net_debug = NET_DEBUG;
|
69 |
|
|
|
70 |
|
|
/* A zero-terminated list of common I/O addresses to be probed. */
|
71 |
|
|
static unsigned int netcard_portlist[] =
|
72 |
|
|
{ 0x300, 0x320, 0x340, 0x280, 0};
|
73 |
|
|
|
74 |
|
|
/*
|
75 |
|
|
Details of the i82586.
|
76 |
|
|
|
77 |
|
|
You'll really need the databook to understand the details of this part,
|
78 |
|
|
but the outline is that the i82586 has two separate processing units.
|
79 |
|
|
Both are started from a list of three configuration tables, of which only
|
80 |
|
|
the last, the System Control Block (SCB), is used after reset-time. The SCB
|
81 |
|
|
has the following fields:
|
82 |
|
|
Status word
|
83 |
|
|
Command word
|
84 |
|
|
Tx/Command block addr.
|
85 |
|
|
Rx block addr.
|
86 |
|
|
The command word accepts the following controls for the Tx and Rx units:
|
87 |
|
|
*/
|
88 |
|
|
|
89 |
|
|
#define CUC_START 0x0100
|
90 |
|
|
#define CUC_RESUME 0x0200
|
91 |
|
|
#define CUC_SUSPEND 0x0300
|
92 |
|
|
#define RX_START 0x0010
|
93 |
|
|
#define RX_RESUME 0x0020
|
94 |
|
|
#define RX_SUSPEND 0x0030
|
95 |
|
|
|
96 |
|
|
/* The Rx unit uses a list of frame descriptors and a list of data buffer
|
97 |
|
|
descriptors. We use full-sized (1518 byte) data buffers, so there is
|
98 |
|
|
a one-to-one pairing of frame descriptors to buffer descriptors.
|
99 |
|
|
|
100 |
|
|
The Tx ("command") unit executes a list of commands that look like:
|
101 |
|
|
Status word Written by the 82586 when the command is done.
|
102 |
|
|
Command word Command in lower 3 bits, post-command action in upper 3
|
103 |
|
|
Link word The address of the next command.
|
104 |
|
|
Parameters (as needed).
|
105 |
|
|
|
106 |
|
|
Some definitions related to the Command Word are:
|
107 |
|
|
*/
|
108 |
|
|
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
|
109 |
|
|
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
|
110 |
|
|
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
|
111 |
|
|
|
112 |
|
|
enum commands {
|
113 |
|
|
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
|
114 |
|
|
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
|
115 |
|
|
|
116 |
|
|
/* Information that need to be kept for each board. */
|
117 |
|
|
struct net_local {
|
118 |
|
|
struct enet_statistics stats;
|
119 |
|
|
int last_restart;
|
120 |
|
|
ushort rx_head;
|
121 |
|
|
ushort rx_tail;
|
122 |
|
|
ushort tx_head;
|
123 |
|
|
ushort tx_cmd_link;
|
124 |
|
|
ushort tx_reap;
|
125 |
|
|
};
|
126 |
|
|
|
127 |
|
|
/*
|
128 |
|
|
Details of the EtherLink16 Implementation
|
129 |
|
|
The 3c507 is a generic shared-memory i82586 implementation.
|
130 |
|
|
The host can map 16K, 32K, 48K, or 64K of the 64K memory into
|
131 |
|
|
0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
|
132 |
|
|
*/
|
133 |
|
|
|
134 |
|
|
/* Offsets from the base I/O address. */
|
135 |
|
|
#define SA_DATA 0 /* Station address data, or 3Com signature. */
|
136 |
|
|
#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
|
137 |
|
|
#define RESET_IRQ 10 /* Reset the latched IRQ line. */
|
138 |
|
|
#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
|
139 |
|
|
#define ROM_CONFIG 13
|
140 |
|
|
#define MEM_CONFIG 14
|
141 |
|
|
#define IRQ_CONFIG 15
|
142 |
|
|
#define EL16_IO_EXTENT 16
|
143 |
|
|
|
144 |
|
|
/* The ID port is used at boot-time to locate the ethercard. */
|
145 |
|
|
#define ID_PORT 0x100
|
146 |
|
|
|
147 |
|
|
/* Offsets to registers in the mailbox (SCB). */
|
148 |
|
|
#define iSCB_STATUS 0x8
|
149 |
|
|
#define iSCB_CMD 0xA
|
150 |
|
|
#define iSCB_CBL 0xC /* Command BLock offset. */
|
151 |
|
|
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
|
152 |
|
|
|
153 |
|
|
/* Since the 3c507 maps the shared memory window so that the last byte is
|
154 |
|
|
at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
|
155 |
|
|
48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
|
156 |
|
|
We can account for this be setting the 'SBC Base' entry in the ISCP table
|
157 |
|
|
below for all the 16 bit offset addresses, and also adding the 'SCB Base'
|
158 |
|
|
value to all 24 bit physical addresses (in the SCP table and the TX and RX
|
159 |
|
|
Buffer Descriptors).
|
160 |
|
|
-Mark
|
161 |
|
|
*/
|
162 |
|
|
#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start))
|
163 |
|
|
|
164 |
|
|
/*
|
165 |
|
|
What follows in 'init_words[]' is the "program" that is downloaded to the
|
166 |
|
|
82586 memory. It's mostly tables and command blocks, and starts at the
|
167 |
|
|
reset address 0xfffff6. This is designed to be similar to the EtherExpress,
|
168 |
|
|
thus the unusual location of the SCB at 0x0008.
|
169 |
|
|
|
170 |
|
|
Even with the additional "don't care" values, doing it this way takes less
|
171 |
|
|
program space than initializing the individual tables, and I feel it's much
|
172 |
|
|
cleaner.
|
173 |
|
|
|
174 |
|
|
The databook is particularly useless for the first two structures, I had
|
175 |
|
|
to use the Crynwr driver as an example.
|
176 |
|
|
|
177 |
|
|
The memory setup is as follows:
|
178 |
|
|
*/
|
179 |
|
|
|
180 |
|
|
#define CONFIG_CMD 0x0018
|
181 |
|
|
#define SET_SA_CMD 0x0024
|
182 |
|
|
#define SA_OFFSET 0x002A
|
183 |
|
|
#define IDLELOOP 0x30
|
184 |
|
|
#define TDR_CMD 0x38
|
185 |
|
|
#define TDR_TIME 0x3C
|
186 |
|
|
#define DUMP_CMD 0x40
|
187 |
|
|
#define DIAG_CMD 0x48
|
188 |
|
|
#define SET_MC_CMD 0x4E
|
189 |
|
|
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
|
190 |
|
|
|
191 |
|
|
#define TX_BUF_START 0x0100
|
192 |
|
|
#define NUM_TX_BUFS 4
|
193 |
|
|
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
|
194 |
|
|
|
195 |
|
|
#define RX_BUF_START 0x2000
|
196 |
|
|
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
|
197 |
|
|
#define RX_BUF_END (dev->mem_end - dev->mem_start)
|
198 |
|
|
|
199 |
|
|
/*
|
200 |
|
|
That's it: only 86 bytes to set up the beast, including every extra
|
201 |
|
|
command available. The 170 byte buffer at DUMP_DATA is shared between the
|
202 |
|
|
Dump command (called only by the diagnostic program) and the SetMulticastList
|
203 |
|
|
command.
|
204 |
|
|
|
205 |
|
|
To complete the memory setup you only have to write the station address at
|
206 |
|
|
SA_OFFSET and create the Tx & Rx buffer lists.
|
207 |
|
|
|
208 |
|
|
The Tx command chain and buffer list is setup as follows:
|
209 |
|
|
A Tx command table, with the data buffer pointing to...
|
210 |
|
|
A Tx data buffer descriptor. The packet is in a single buffer, rather than
|
211 |
|
|
chaining together several smaller buffers.
|
212 |
|
|
A NoOp command, which initially points to itself,
|
213 |
|
|
And the packet data.
|
214 |
|
|
|
215 |
|
|
A transmit is done by filling in the Tx command table and data buffer,
|
216 |
|
|
re-writing the NoOp command, and finally changing the offset of the last
|
217 |
|
|
command to point to the current Tx command. When the Tx command is finished,
|
218 |
|
|
it jumps to the NoOp, when it loops until the next Tx command changes the
|
219 |
|
|
"link offset" in the NoOp. This way the 82586 never has to go through the
|
220 |
|
|
slow restart sequence.
|
221 |
|
|
|
222 |
|
|
The Rx buffer list is set up in the obvious ring structure. We have enough
|
223 |
|
|
memory (and low enough interrupt latency) that we can avoid the complicated
|
224 |
|
|
Rx buffer linked lists by alway associating a full-size Rx data buffer with
|
225 |
|
|
each Rx data frame.
|
226 |
|
|
|
227 |
|
|
I current use four transmit buffers starting at TX_BUF_START (0x0100), and
|
228 |
|
|
use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
|
229 |
|
|
|
230 |
|
|
*/
|
231 |
|
|
|
232 |
|
|
unsigned short init_words[] = {
|
233 |
|
|
/* System Configuration Pointer (SCP). */
|
234 |
|
|
0x0000, /* Set bus size to 16 bits. */
|
235 |
|
|
0,0, /* pad words. */
|
236 |
|
|
0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
|
237 |
|
|
|
238 |
|
|
/* Intermediate System Configuration Pointer (ISCP). */
|
239 |
|
|
0x0001, /* Status word that's cleared when init is done. */
|
240 |
|
|
0x0008,0,0, /* SCB offset, (skip, skip) */
|
241 |
|
|
|
242 |
|
|
/* System Control Block (SCB). */
|
243 |
|
|
0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
|
244 |
|
|
CONFIG_CMD, /* Command list pointer, points to Configure. */
|
245 |
|
|
RX_BUF_START, /* Rx block list. */
|
246 |
|
|
0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
|
247 |
|
|
|
248 |
|
|
/* 0x0018: Configure command. Change to put MAC data with packet. */
|
249 |
|
|
0, CmdConfigure, /* Status, command. */
|
250 |
|
|
SET_SA_CMD, /* Next command is Set Station Addr. */
|
251 |
|
|
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
|
252 |
|
|
0x2e40, /* Magic values, including MAC data location. */
|
253 |
|
|
0, /* Unused pad word. */
|
254 |
|
|
|
255 |
|
|
/* 0x0024: Setup station address command. */
|
256 |
|
|
0, CmdSASetup,
|
257 |
|
|
SET_MC_CMD, /* Next command. */
|
258 |
|
|
0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
|
259 |
|
|
|
260 |
|
|
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
|
261 |
|
|
0, CmdNOp, IDLELOOP, 0 /* pad */,
|
262 |
|
|
|
263 |
|
|
/* 0x0038: A unused Time-Domain Reflectometer command. */
|
264 |
|
|
0, CmdTDR, IDLELOOP, 0,
|
265 |
|
|
|
266 |
|
|
/* 0x0040: An unused Dump State command. */
|
267 |
|
|
0, CmdDump, IDLELOOP, DUMP_DATA,
|
268 |
|
|
|
269 |
|
|
/* 0x0048: An unused Diagnose command. */
|
270 |
|
|
0, CmdDiagnose, IDLELOOP,
|
271 |
|
|
|
272 |
|
|
/* 0x004E: An empty set-multicast-list command. */
|
273 |
|
|
0, CmdMulticastList, IDLELOOP, 0,
|
274 |
|
|
};
|
275 |
|
|
|
276 |
|
|
/* Index to functions, as function prototypes. */
|
277 |
|
|
|
278 |
|
|
extern int el16_probe(struct device *dev); /* Called from Space.c */
|
279 |
|
|
|
280 |
|
|
static int el16_probe1(struct device *dev, int ioaddr);
|
281 |
|
|
static int el16_open(struct device *dev);
|
282 |
|
|
static int el16_send_packet(struct sk_buff *skb, struct device *dev);
|
283 |
|
|
static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs);
|
284 |
|
|
static void el16_rx(struct device *dev);
|
285 |
|
|
static int el16_close(struct device *dev);
|
286 |
|
|
static struct enet_statistics *el16_get_stats(struct device *dev);
|
287 |
|
|
|
288 |
|
|
static void hardware_send_packet(struct device *dev, void *buf, short length);
|
289 |
|
|
void init_82586_mem(struct device *dev);
|
290 |
|
|
|
291 |
|
|
|
292 |
|
|
#ifdef HAVE_DEVLIST
|
293 |
|
|
struct netdev_entry netcard_drv =
|
294 |
|
|
{"3c507", el16_probe1, EL16_IO_EXTENT, netcard_portlist};
|
295 |
|
|
#endif
|
296 |
|
|
|
297 |
|
|
/* Check for a network adaptor of this type, and return '0' iff one exists.
|
298 |
|
|
If dev->base_addr == 0, probe all likely locations.
|
299 |
|
|
If dev->base_addr == 1, always return failure.
|
300 |
|
|
If dev->base_addr == 2, (detachable devices only) allocate space for the
|
301 |
|
|
device and return success.
|
302 |
|
|
*/
|
303 |
|
|
int
|
304 |
|
|
el16_probe(struct device *dev)
|
305 |
|
|
{
|
306 |
|
|
int base_addr = dev ? dev->base_addr : 0;
|
307 |
|
|
int i;
|
308 |
|
|
|
309 |
|
|
if (base_addr > 0x1ff) /* Check a single specified location. */
|
310 |
|
|
return el16_probe1(dev, base_addr);
|
311 |
|
|
else if (base_addr != 0)
|
312 |
|
|
return ENXIO; /* Don't probe at all. */
|
313 |
|
|
|
314 |
|
|
for (i = 0; netcard_portlist[i]; i++) {
|
315 |
|
|
int ioaddr = netcard_portlist[i];
|
316 |
|
|
if (check_region(ioaddr, EL16_IO_EXTENT))
|
317 |
|
|
continue;
|
318 |
|
|
if (el16_probe1(dev, ioaddr) == 0)
|
319 |
|
|
return 0;
|
320 |
|
|
}
|
321 |
|
|
|
322 |
|
|
return ENODEV;
|
323 |
|
|
}
|
324 |
|
|
|
325 |
|
|
int el16_probe1(struct device *dev, int ioaddr)
|
326 |
|
|
{
|
327 |
|
|
static unsigned char init_ID_done = 0, version_printed = 0;
|
328 |
|
|
int i, irq, irqval;
|
329 |
|
|
|
330 |
|
|
if (init_ID_done == 0) {
|
331 |
|
|
ushort lrs_state = 0xff;
|
332 |
|
|
/* Send the ID sequence to the ID_PORT to enable the board(s). */
|
333 |
|
|
outb(0x00, ID_PORT);
|
334 |
|
|
for(i = 0; i < 255; i++) {
|
335 |
|
|
outb(lrs_state, ID_PORT);
|
336 |
|
|
lrs_state <<= 1;
|
337 |
|
|
if (lrs_state & 0x100)
|
338 |
|
|
lrs_state ^= 0xe7;
|
339 |
|
|
}
|
340 |
|
|
outb(0x00, ID_PORT);
|
341 |
|
|
init_ID_done = 1;
|
342 |
|
|
}
|
343 |
|
|
|
344 |
|
|
if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3'
|
345 |
|
|
&& inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O')
|
346 |
|
|
;
|
347 |
|
|
else
|
348 |
|
|
return ENODEV;
|
349 |
|
|
|
350 |
|
|
/* Allocate a new 'dev' if needed. */
|
351 |
|
|
if (dev == NULL)
|
352 |
|
|
dev = init_etherdev(0, sizeof(struct net_local));
|
353 |
|
|
|
354 |
|
|
if (net_debug && version_printed++ == 0)
|
355 |
|
|
printk(version);
|
356 |
|
|
|
357 |
|
|
printk("%s: 3c507 at %#x,", dev->name, ioaddr);
|
358 |
|
|
|
359 |
|
|
/* We should make a few more checks here, like the first three octets of
|
360 |
|
|
the S.A. for the manufacturer's code. */
|
361 |
|
|
|
362 |
|
|
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
|
363 |
|
|
|
364 |
|
|
irqval = request_irq(irq, &el16_interrupt, 0, "3c507", NULL);
|
365 |
|
|
if (irqval) {
|
366 |
|
|
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
|
367 |
|
|
return EAGAIN;
|
368 |
|
|
}
|
369 |
|
|
|
370 |
|
|
/* We've committed to using the board, and can start filling in *dev. */
|
371 |
|
|
request_region(ioaddr, EL16_IO_EXTENT, "3c507");
|
372 |
|
|
dev->base_addr = ioaddr;
|
373 |
|
|
|
374 |
|
|
outb(0x01, ioaddr + MISC_CTRL);
|
375 |
|
|
for (i = 0; i < 6; i++) {
|
376 |
|
|
dev->dev_addr[i] = inb(ioaddr + i);
|
377 |
|
|
printk(" %02x", dev->dev_addr[i]);
|
378 |
|
|
}
|
379 |
|
|
|
380 |
|
|
if ((dev->mem_start & 0xf) > 0)
|
381 |
|
|
net_debug = dev->mem_start & 7;
|
382 |
|
|
|
383 |
|
|
#ifdef MEM_BASE
|
384 |
|
|
dev->mem_start = MEM_BASE;
|
385 |
|
|
dev->mem_end = dev->mem_start + 0x10000;
|
386 |
|
|
#else
|
387 |
|
|
{
|
388 |
|
|
int base;
|
389 |
|
|
int size;
|
390 |
|
|
char mem_config = inb(ioaddr + MEM_CONFIG);
|
391 |
|
|
if (mem_config & 0x20) {
|
392 |
|
|
size = 64*1024;
|
393 |
|
|
base = 0xf00000 + (mem_config & 0x08 ? 0x080000
|
394 |
|
|
: ((mem_config & 3) << 17));
|
395 |
|
|
} else {
|
396 |
|
|
size = ((mem_config & 3) + 1) << 14;
|
397 |
|
|
base = 0x0c0000 + ( (mem_config & 0x18) << 12);
|
398 |
|
|
}
|
399 |
|
|
dev->mem_start = base;
|
400 |
|
|
dev->mem_end = base + size;
|
401 |
|
|
}
|
402 |
|
|
#endif
|
403 |
|
|
|
404 |
|
|
dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
|
405 |
|
|
dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
|
406 |
|
|
|
407 |
|
|
printk(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq,
|
408 |
|
|
dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
|
409 |
|
|
|
410 |
|
|
if (net_debug)
|
411 |
|
|
printk(version);
|
412 |
|
|
|
413 |
|
|
/* Initialize the device structure. */
|
414 |
|
|
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
|
415 |
|
|
if (dev->priv == NULL)
|
416 |
|
|
return -ENOMEM;
|
417 |
|
|
memset(dev->priv, 0, sizeof(struct net_local));
|
418 |
|
|
|
419 |
|
|
dev->open = el16_open;
|
420 |
|
|
dev->stop = el16_close;
|
421 |
|
|
dev->hard_start_xmit = el16_send_packet;
|
422 |
|
|
dev->get_stats = el16_get_stats;
|
423 |
|
|
|
424 |
|
|
ether_setup(dev); /* Generic ethernet behaviour */
|
425 |
|
|
|
426 |
|
|
dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */
|
427 |
|
|
|
428 |
|
|
return 0;
|
429 |
|
|
}
|
430 |
|
|
|
431 |
|
|
|
432 |
|
|
|
433 |
|
|
static int
|
434 |
|
|
el16_open(struct device *dev)
|
435 |
|
|
{
|
436 |
|
|
irq2dev_map[dev->irq] = dev;
|
437 |
|
|
|
438 |
|
|
/* Initialize the 82586 memory and start it. */
|
439 |
|
|
init_82586_mem(dev);
|
440 |
|
|
|
441 |
|
|
dev->tbusy = 0;
|
442 |
|
|
dev->interrupt = 0;
|
443 |
|
|
dev->start = 1;
|
444 |
|
|
|
445 |
|
|
MOD_INC_USE_COUNT;
|
446 |
|
|
|
447 |
|
|
return 0;
|
448 |
|
|
}
|
449 |
|
|
|
450 |
|
|
static int
|
451 |
|
|
el16_send_packet(struct sk_buff *skb, struct device *dev)
|
452 |
|
|
{
|
453 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
454 |
|
|
int ioaddr = dev->base_addr;
|
455 |
|
|
short *shmem = (short*)dev->mem_start;
|
456 |
|
|
|
457 |
|
|
if (dev->tbusy) {
|
458 |
|
|
/* If we get here, some higher level has decided we are broken.
|
459 |
|
|
There should really be a "kick me" function call instead. */
|
460 |
|
|
int tickssofar = jiffies - dev->trans_start;
|
461 |
|
|
if (tickssofar < 5)
|
462 |
|
|
return 1;
|
463 |
|
|
if (net_debug > 1)
|
464 |
|
|
printk("%s: transmit timed out, %s? ", dev->name,
|
465 |
|
|
shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
|
466 |
|
|
"network cable problem");
|
467 |
|
|
/* Try to restart the adaptor. */
|
468 |
|
|
if (lp->last_restart == lp->stats.tx_packets) {
|
469 |
|
|
if (net_debug > 1) printk("Resetting board.\n");
|
470 |
|
|
/* Completely reset the adaptor. */
|
471 |
|
|
init_82586_mem(dev);
|
472 |
|
|
} else {
|
473 |
|
|
/* Issue the channel attention signal and hope it "gets better". */
|
474 |
|
|
if (net_debug > 1) printk("Kicking board.\n");
|
475 |
|
|
shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
|
476 |
|
|
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
|
477 |
|
|
lp->last_restart = lp->stats.tx_packets;
|
478 |
|
|
}
|
479 |
|
|
dev->tbusy=0;
|
480 |
|
|
dev->trans_start = jiffies;
|
481 |
|
|
}
|
482 |
|
|
|
483 |
|
|
/* If some higher layer thinks we've missed an tx-done interrupt
|
484 |
|
|
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
|
485 |
|
|
itself. */
|
486 |
|
|
if (skb == NULL) {
|
487 |
|
|
dev_tint(dev);
|
488 |
|
|
return 0;
|
489 |
|
|
}
|
490 |
|
|
|
491 |
|
|
/* Block a timer-based transmit from overlapping. */
|
492 |
|
|
if (set_bit(0, (void*)&dev->tbusy) != 0)
|
493 |
|
|
printk("%s: Transmitter access conflict.\n", dev->name);
|
494 |
|
|
else {
|
495 |
|
|
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
|
496 |
|
|
unsigned char *buf = skb->data;
|
497 |
|
|
|
498 |
|
|
/* Disable the 82586's input to the interrupt line. */
|
499 |
|
|
outb(0x80, ioaddr + MISC_CTRL);
|
500 |
|
|
hardware_send_packet(dev, buf, length);
|
501 |
|
|
dev->trans_start = jiffies;
|
502 |
|
|
/* Enable the 82586 interrupt input. */
|
503 |
|
|
outb(0x84, ioaddr + MISC_CTRL);
|
504 |
|
|
}
|
505 |
|
|
|
506 |
|
|
dev_kfree_skb (skb, FREE_WRITE);
|
507 |
|
|
|
508 |
|
|
/* You might need to clean up and record Tx statistics here. */
|
509 |
|
|
|
510 |
|
|
return 0;
|
511 |
|
|
}
|
512 |
|
|
|
513 |
|
|
/* The typical workload of the driver:
|
514 |
|
|
Handle the network interface interrupts. */
|
515 |
|
|
static void
|
516 |
|
|
el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
517 |
|
|
{
|
518 |
|
|
struct device *dev = (struct device *)(irq2dev_map[irq]);
|
519 |
|
|
struct net_local *lp;
|
520 |
|
|
int ioaddr, status, boguscount = 0;
|
521 |
|
|
ushort ack_cmd = 0;
|
522 |
|
|
ushort *shmem;
|
523 |
|
|
|
524 |
|
|
if (dev == NULL) {
|
525 |
|
|
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
|
526 |
|
|
return;
|
527 |
|
|
}
|
528 |
|
|
dev->interrupt = 1;
|
529 |
|
|
|
530 |
|
|
ioaddr = dev->base_addr;
|
531 |
|
|
lp = (struct net_local *)dev->priv;
|
532 |
|
|
shmem = ((ushort*)dev->mem_start);
|
533 |
|
|
|
534 |
|
|
status = shmem[iSCB_STATUS>>1];
|
535 |
|
|
|
536 |
|
|
if (net_debug > 4) {
|
537 |
|
|
printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
|
538 |
|
|
}
|
539 |
|
|
|
540 |
|
|
/* Disable the 82586's input to the interrupt line. */
|
541 |
|
|
outb(0x80, ioaddr + MISC_CTRL);
|
542 |
|
|
|
543 |
|
|
/* Reap the Tx packet buffers. */
|
544 |
|
|
while (lp->tx_reap != lp->tx_head) {
|
545 |
|
|
unsigned short tx_status = shmem[lp->tx_reap>>1];
|
546 |
|
|
|
547 |
|
|
if (tx_status == 0) {
|
548 |
|
|
if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
|
549 |
|
|
break;
|
550 |
|
|
}
|
551 |
|
|
if (tx_status & 0x2000) {
|
552 |
|
|
lp->stats.tx_packets++;
|
553 |
|
|
lp->stats.collisions += tx_status & 0xf;
|
554 |
|
|
dev->tbusy = 0;
|
555 |
|
|
mark_bh(NET_BH); /* Inform upper layers. */
|
556 |
|
|
} else {
|
557 |
|
|
lp->stats.tx_errors++;
|
558 |
|
|
if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
|
559 |
|
|
if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
|
560 |
|
|
if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
|
561 |
|
|
if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
|
562 |
|
|
}
|
563 |
|
|
if (net_debug > 5)
|
564 |
|
|
printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
|
565 |
|
|
lp->tx_reap += TX_BUF_SIZE;
|
566 |
|
|
if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
|
567 |
|
|
lp->tx_reap = TX_BUF_START;
|
568 |
|
|
if (++boguscount > 4)
|
569 |
|
|
break;
|
570 |
|
|
}
|
571 |
|
|
|
572 |
|
|
if (status & 0x4000) { /* Packet received. */
|
573 |
|
|
if (net_debug > 5)
|
574 |
|
|
printk("Received packet, rx_head %04x.\n", lp->rx_head);
|
575 |
|
|
el16_rx(dev);
|
576 |
|
|
}
|
577 |
|
|
|
578 |
|
|
/* Acknowledge the interrupt sources. */
|
579 |
|
|
ack_cmd = status & 0xf000;
|
580 |
|
|
|
581 |
|
|
if ((status & 0x0700) != 0x0200 && dev->start) {
|
582 |
|
|
if (net_debug)
|
583 |
|
|
printk("%s: Command unit stopped, status %04x, restarting.\n",
|
584 |
|
|
dev->name, status);
|
585 |
|
|
/* If this ever occurs we should really re-write the idle loop, reset
|
586 |
|
|
the Tx list, and do a complete restart of the command unit.
|
587 |
|
|
For now we rely on the Tx timeout if the resume doesn't work. */
|
588 |
|
|
ack_cmd |= CUC_RESUME;
|
589 |
|
|
}
|
590 |
|
|
|
591 |
|
|
if ((status & 0x0070) != 0x0040 && dev->start) {
|
592 |
|
|
static void init_rx_bufs(struct device *);
|
593 |
|
|
/* The Rx unit is not ready, it must be hung. Restart the receiver by
|
594 |
|
|
initializing the rx buffers, and issuing an Rx start command. */
|
595 |
|
|
if (net_debug)
|
596 |
|
|
printk("%s: Rx unit stopped, status %04x, restarting.\n",
|
597 |
|
|
dev->name, status);
|
598 |
|
|
init_rx_bufs(dev);
|
599 |
|
|
shmem[iSCB_RFA >> 1] = RX_BUF_START;
|
600 |
|
|
ack_cmd |= RX_START;
|
601 |
|
|
}
|
602 |
|
|
|
603 |
|
|
shmem[iSCB_CMD>>1] = ack_cmd;
|
604 |
|
|
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
|
605 |
|
|
|
606 |
|
|
/* Clear the latched interrupt. */
|
607 |
|
|
outb(0, ioaddr + RESET_IRQ);
|
608 |
|
|
|
609 |
|
|
/* Enable the 82586's interrupt input. */
|
610 |
|
|
outb(0x84, ioaddr + MISC_CTRL);
|
611 |
|
|
|
612 |
|
|
return;
|
613 |
|
|
}
|
614 |
|
|
|
615 |
|
|
static int
|
616 |
|
|
el16_close(struct device *dev)
|
617 |
|
|
{
|
618 |
|
|
int ioaddr = dev->base_addr;
|
619 |
|
|
ushort *shmem = (short*)dev->mem_start;
|
620 |
|
|
|
621 |
|
|
dev->tbusy = 1;
|
622 |
|
|
dev->start = 0;
|
623 |
|
|
|
624 |
|
|
/* Flush the Tx and disable Rx. */
|
625 |
|
|
shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
|
626 |
|
|
outb(0, ioaddr + SIGNAL_CA);
|
627 |
|
|
|
628 |
|
|
/* Disable the 82586's input to the interrupt line. */
|
629 |
|
|
outb(0x80, ioaddr + MISC_CTRL);
|
630 |
|
|
|
631 |
|
|
/* We always physically use the IRQ line, so we don't do free_irq().
|
632 |
|
|
We do remove ourselves from the map. */
|
633 |
|
|
|
634 |
|
|
irq2dev_map[dev->irq] = 0;
|
635 |
|
|
|
636 |
|
|
/* Update the statistics here. */
|
637 |
|
|
|
638 |
|
|
MOD_DEC_USE_COUNT;
|
639 |
|
|
|
640 |
|
|
return 0;
|
641 |
|
|
}
|
642 |
|
|
|
643 |
|
|
/* Get the current statistics. This may be called with the card open or
|
644 |
|
|
closed. */
|
645 |
|
|
static struct enet_statistics *
|
646 |
|
|
el16_get_stats(struct device *dev)
|
647 |
|
|
{
|
648 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
649 |
|
|
|
650 |
|
|
/* ToDo: decide if there are any useful statistics from the SCB. */
|
651 |
|
|
|
652 |
|
|
return &lp->stats;
|
653 |
|
|
}
|
654 |
|
|
|
655 |
|
|
/* Initialize the Rx-block list. */
|
656 |
|
|
static void
|
657 |
|
|
init_rx_bufs(struct device *dev)
|
658 |
|
|
{
|
659 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
660 |
|
|
unsigned short *write_ptr;
|
661 |
|
|
unsigned short SCB_base = SCB_BASE;
|
662 |
|
|
|
663 |
|
|
int cur_rxbuf = lp->rx_head = RX_BUF_START;
|
664 |
|
|
|
665 |
|
|
/* Initialize each Rx frame + data buffer. */
|
666 |
|
|
do { /* While there is room for one more. */
|
667 |
|
|
|
668 |
|
|
write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
|
669 |
|
|
|
670 |
|
|
*write_ptr++ = 0x0000; /* Status */
|
671 |
|
|
*write_ptr++ = 0x0000; /* Command */
|
672 |
|
|
*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
|
673 |
|
|
*write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
|
674 |
|
|
*write_ptr++ = 0x0000; /* Pad for dest addr. */
|
675 |
|
|
*write_ptr++ = 0x0000;
|
676 |
|
|
*write_ptr++ = 0x0000;
|
677 |
|
|
*write_ptr++ = 0x0000; /* Pad for source addr. */
|
678 |
|
|
*write_ptr++ = 0x0000;
|
679 |
|
|
*write_ptr++ = 0x0000;
|
680 |
|
|
*write_ptr++ = 0x0000; /* Pad for protocol. */
|
681 |
|
|
|
682 |
|
|
*write_ptr++ = 0x0000; /* Buffer: Actual count */
|
683 |
|
|
*write_ptr++ = -1; /* Buffer: Next (none). */
|
684 |
|
|
*write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
|
685 |
|
|
*write_ptr++ = 0x0000;
|
686 |
|
|
/* Finally, the number of bytes in the buffer. */
|
687 |
|
|
*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
|
688 |
|
|
|
689 |
|
|
lp->rx_tail = cur_rxbuf;
|
690 |
|
|
cur_rxbuf += RX_BUF_SIZE;
|
691 |
|
|
} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
|
692 |
|
|
|
693 |
|
|
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
|
694 |
|
|
the list a ring. */
|
695 |
|
|
write_ptr = (unsigned short *)
|
696 |
|
|
(dev->mem_start + lp->rx_tail + 2);
|
697 |
|
|
*write_ptr++ = 0xC000; /* Command, mark as last. */
|
698 |
|
|
*write_ptr++ = lp->rx_head; /* Link */
|
699 |
|
|
|
700 |
|
|
}
|
701 |
|
|
|
702 |
|
|
void
|
703 |
|
|
init_82586_mem(struct device *dev)
|
704 |
|
|
{
|
705 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
706 |
|
|
short ioaddr = dev->base_addr;
|
707 |
|
|
ushort *shmem = (short*)dev->mem_start;
|
708 |
|
|
|
709 |
|
|
/* Enable loopback to protect the wire while starting up,
|
710 |
|
|
and hold the 586 in reset during the memory initialization. */
|
711 |
|
|
outb(0x20, ioaddr + MISC_CTRL);
|
712 |
|
|
|
713 |
|
|
/* Fix the ISCP address and base. */
|
714 |
|
|
init_words[3] = SCB_BASE;
|
715 |
|
|
init_words[7] = SCB_BASE;
|
716 |
|
|
|
717 |
|
|
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
|
718 |
|
|
memcpy((void*)dev->mem_end-10, init_words, 10);
|
719 |
|
|
|
720 |
|
|
/* Write the words at 0x0000. */
|
721 |
|
|
memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
|
722 |
|
|
|
723 |
|
|
/* Fill in the station address. */
|
724 |
|
|
memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
|
725 |
|
|
sizeof(dev->dev_addr));
|
726 |
|
|
|
727 |
|
|
/* The Tx-block list is written as needed. We just set up the values. */
|
728 |
|
|
lp->tx_cmd_link = IDLELOOP + 4;
|
729 |
|
|
lp->tx_head = lp->tx_reap = TX_BUF_START;
|
730 |
|
|
|
731 |
|
|
init_rx_bufs(dev);
|
732 |
|
|
|
733 |
|
|
/* Start the 586 by releasing the reset line, but leave loopback. */
|
734 |
|
|
outb(0xA0, ioaddr + MISC_CTRL);
|
735 |
|
|
|
736 |
|
|
/* This was time consuming to track down: you need to give two channel
|
737 |
|
|
attention signals to reliably start up the i82586. */
|
738 |
|
|
outb(0, ioaddr + SIGNAL_CA);
|
739 |
|
|
|
740 |
|
|
{
|
741 |
|
|
int boguscnt = 50;
|
742 |
|
|
while (shmem[iSCB_STATUS>>1] == 0)
|
743 |
|
|
if (--boguscnt == 0) {
|
744 |
|
|
printk("%s: i82586 initialization timed out with status %04x,"
|
745 |
|
|
"cmd %04x.\n", dev->name,
|
746 |
|
|
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
747 |
|
|
break;
|
748 |
|
|
}
|
749 |
|
|
/* Issue channel-attn -- the 82586 won't start. */
|
750 |
|
|
outb(0, ioaddr + SIGNAL_CA);
|
751 |
|
|
}
|
752 |
|
|
|
753 |
|
|
/* Disable loopback and enable interrupts. */
|
754 |
|
|
outb(0x84, ioaddr + MISC_CTRL);
|
755 |
|
|
if (net_debug > 4)
|
756 |
|
|
printk("%s: Initialized 82586, status %04x.\n", dev->name,
|
757 |
|
|
shmem[iSCB_STATUS>>1]);
|
758 |
|
|
return;
|
759 |
|
|
}
|
760 |
|
|
|
761 |
|
|
static void
|
762 |
|
|
hardware_send_packet(struct device *dev, void *buf, short length)
|
763 |
|
|
{
|
764 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
765 |
|
|
short ioaddr = dev->base_addr;
|
766 |
|
|
ushort tx_block = lp->tx_head;
|
767 |
|
|
ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
|
768 |
|
|
|
769 |
|
|
/* Set the write pointer to the Tx block, and put out the header. */
|
770 |
|
|
*write_ptr++ = 0x0000; /* Tx status */
|
771 |
|
|
*write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
|
772 |
|
|
*write_ptr++ = tx_block+16; /* Next command is a NoOp. */
|
773 |
|
|
*write_ptr++ = tx_block+8; /* Data Buffer offset. */
|
774 |
|
|
|
775 |
|
|
/* Output the data buffer descriptor. */
|
776 |
|
|
*write_ptr++ = length | 0x8000; /* Byte count parameter. */
|
777 |
|
|
*write_ptr++ = -1; /* No next data buffer. */
|
778 |
|
|
*write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
|
779 |
|
|
*write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
|
780 |
|
|
|
781 |
|
|
/* Output the Loop-back NoOp command. */
|
782 |
|
|
*write_ptr++ = 0x0000; /* Tx status */
|
783 |
|
|
*write_ptr++ = CmdNOp; /* Tx command */
|
784 |
|
|
*write_ptr++ = tx_block+16; /* Next is myself. */
|
785 |
|
|
|
786 |
|
|
/* Output the packet at the write pointer. */
|
787 |
|
|
memcpy(write_ptr, buf, length);
|
788 |
|
|
|
789 |
|
|
/* Set the old command link pointing to this send packet. */
|
790 |
|
|
*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
|
791 |
|
|
lp->tx_cmd_link = tx_block + 20;
|
792 |
|
|
|
793 |
|
|
/* Set the next free tx region. */
|
794 |
|
|
lp->tx_head = tx_block + TX_BUF_SIZE;
|
795 |
|
|
if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
|
796 |
|
|
lp->tx_head = TX_BUF_START;
|
797 |
|
|
|
798 |
|
|
if (net_debug > 4) {
|
799 |
|
|
printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
|
800 |
|
|
dev->name, ioaddr, length, tx_block, lp->tx_head);
|
801 |
|
|
}
|
802 |
|
|
|
803 |
|
|
if (lp->tx_head != lp->tx_reap)
|
804 |
|
|
dev->tbusy = 0;
|
805 |
|
|
}
|
806 |
|
|
|
807 |
|
|
static void
|
808 |
|
|
el16_rx(struct device *dev)
|
809 |
|
|
{
|
810 |
|
|
struct net_local *lp = (struct net_local *)dev->priv;
|
811 |
|
|
short *shmem = (short*)dev->mem_start;
|
812 |
|
|
ushort rx_head = lp->rx_head;
|
813 |
|
|
ushort rx_tail = lp->rx_tail;
|
814 |
|
|
ushort boguscount = 10;
|
815 |
|
|
short frame_status;
|
816 |
|
|
|
817 |
|
|
while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
|
818 |
|
|
ushort *read_frame = (short *)(dev->mem_start + rx_head);
|
819 |
|
|
ushort rfd_cmd = read_frame[1];
|
820 |
|
|
ushort next_rx_frame = read_frame[2];
|
821 |
|
|
ushort data_buffer_addr = read_frame[3];
|
822 |
|
|
ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
|
823 |
|
|
ushort pkt_len = data_frame[0];
|
824 |
|
|
|
825 |
|
|
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|
826 |
|
|
|| (pkt_len & 0xC000) != 0xC000) {
|
827 |
|
|
printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
|
828 |
|
|
"next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
|
829 |
|
|
frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
|
830 |
|
|
pkt_len);
|
831 |
|
|
} else if ((frame_status & 0x2000) == 0) {
|
832 |
|
|
/* Frame Rxed, but with error. */
|
833 |
|
|
lp->stats.rx_errors++;
|
834 |
|
|
if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
|
835 |
|
|
if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
|
836 |
|
|
if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
|
837 |
|
|
if (frame_status & 0x0100) lp->stats.rx_over_errors++;
|
838 |
|
|
if (frame_status & 0x0080) lp->stats.rx_length_errors++;
|
839 |
|
|
} else {
|
840 |
|
|
/* Malloc up new buffer. */
|
841 |
|
|
struct sk_buff *skb;
|
842 |
|
|
|
843 |
|
|
pkt_len &= 0x3fff;
|
844 |
|
|
skb = dev_alloc_skb(pkt_len+2);
|
845 |
|
|
if (skb == NULL) {
|
846 |
|
|
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
|
847 |
|
|
lp->stats.rx_dropped++;
|
848 |
|
|
break;
|
849 |
|
|
}
|
850 |
|
|
|
851 |
|
|
skb_reserve(skb,2);
|
852 |
|
|
skb->dev = dev;
|
853 |
|
|
|
854 |
|
|
/* 'skb->data' points to the start of sk_buff data area. */
|
855 |
|
|
memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
|
856 |
|
|
|
857 |
|
|
skb->protocol=eth_type_trans(skb,dev);
|
858 |
|
|
netif_rx(skb);
|
859 |
|
|
lp->stats.rx_packets++;
|
860 |
|
|
}
|
861 |
|
|
|
862 |
|
|
/* Clear the status word and set End-of-List on the rx frame. */
|
863 |
|
|
read_frame[0] = 0;
|
864 |
|
|
read_frame[1] = 0xC000;
|
865 |
|
|
/* Clear the end-of-list on the prev. RFD. */
|
866 |
|
|
*(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
|
867 |
|
|
|
868 |
|
|
rx_tail = rx_head;
|
869 |
|
|
rx_head = next_rx_frame;
|
870 |
|
|
if (--boguscount == 0)
|
871 |
|
|
break;
|
872 |
|
|
}
|
873 |
|
|
|
874 |
|
|
lp->rx_head = rx_head;
|
875 |
|
|
lp->rx_tail = rx_tail;
|
876 |
|
|
}
|
877 |
|
|
#ifdef MODULE
|
878 |
|
|
static char devicename[9] = { 0, };
|
879 |
|
|
static struct device dev_3c507 = {
|
880 |
|
|
devicename, /* device name is inserted by linux/drivers/net/net_init.c */
|
881 |
|
|
0, 0, 0, 0,
|
882 |
|
|
0, 0,
|
883 |
|
|
0, 0, 0, NULL, el16_probe
|
884 |
|
|
};
|
885 |
|
|
|
886 |
|
|
static int io = 0x300;
|
887 |
|
|
static int irq = 0;
|
888 |
|
|
|
889 |
|
|
int init_module(void)
|
890 |
|
|
{
|
891 |
|
|
if (io == 0)
|
892 |
|
|
printk("3c507: You should not use auto-probing with insmod!\n");
|
893 |
|
|
dev_3c507.base_addr = io;
|
894 |
|
|
dev_3c507.irq = irq;
|
895 |
|
|
if (register_netdev(&dev_3c507) != 0) {
|
896 |
|
|
printk("3c507: register_netdev() returned non-zero.\n");
|
897 |
|
|
return -EIO;
|
898 |
|
|
}
|
899 |
|
|
return 0;
|
900 |
|
|
}
|
901 |
|
|
|
902 |
|
|
void
|
903 |
|
|
cleanup_module(void)
|
904 |
|
|
{
|
905 |
|
|
unregister_netdev(&dev_3c507);
|
906 |
|
|
kfree(dev_3c507.priv);
|
907 |
|
|
dev_3c507.priv = NULL;
|
908 |
|
|
|
909 |
|
|
/* If we don't do this, we can't re-insmod it later. */
|
910 |
|
|
free_irq(dev_3c507.irq, NULL);
|
911 |
|
|
release_region(dev_3c507.base_addr, EL16_IO_EXTENT);
|
912 |
|
|
}
|
913 |
|
|
#endif /* MODULE */
|
914 |
|
|
|
915 |
|
|
/*
|
916 |
|
|
* Local variables:
|
917 |
|
|
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
|
918 |
|
|
* version-control: t
|
919 |
|
|
* kept-new-versions: 5
|
920 |
|
|
* tab-width: 4
|
921 |
|
|
* c-indent-level: 4
|
922 |
|
|
* End:
|
923 |
|
|
*/
|