Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* Linux Ethernet device driver for the 3Com Etherlink Plus (3C505)
* By Craig Southeren, Juha Laiho and Philip Blundell
*
* 3c505.c This module implements an interface to the 3Com
* Etherlink Plus (3c505) Ethernet card. Linux device
* driver interface reverse engineered from the Linux 3C509
* device drivers. Some 3C505 information gleaned from
* the Crynwr packet driver. Still this driver would not
* be here without 3C505 technical reference provided by
* 3Com.
*
* $Id: 3c505.c,v 1.10 1996/04/16 13:06:27 phil Exp $
*
* Authors: Linux 3c505 device driver by
* Craig Southeren, <craigs@ineluki.apana.org.au>
* Final debugging by
* Andrew Tridgell, <tridge@nimbus.anu.edu.au>
* Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
* Juha Laiho, <jlaiho@ichaos.nullnet.fi>
* Linux 3C509 driver by
* Donald Becker, <becker@super.org>
* (Now at <becker@scyld.com>)
* Crynwr packet driver by
* Krishnan Gopalan and Gregg Stefancik,
* Clemson University Engineering Computer Operations.
* Portions of the code have been adapted from the 3c505
* driver for NCSA Telnet by Bruce Orchard and later
* modified by Warren Van Houten and krus@diku.dk.
* 3C505 technical information provided by
* Terry Murphy, of 3Com Network Adapter Division
* Linux 1.3.0 changes by
* Alan Cox <Alan.Cox@linux.org>
* More debugging, DMA support, currently maintained by
* Philip Blundell <philb@gnu.org>
* Multicard/soft configurable dma channel/rev 2 hardware support
* by Christopher Collins <ccollins@pcug.org.au>
* Ethtool support (jgarzik), 11/17/2001
*/
#define DRV_NAME "3c505"
#define DRV_VERSION "1.10a"
/* Theory of operation:
*
* The 3c505 is quite an intelligent board. All communication with it is done
* by means of Primary Command Blocks (PCBs); these are transferred using PIO
* through the command register. The card has 256k of on-board RAM, which is
* used to buffer received packets. It might seem at first that more buffers
* are better, but in fact this isn't true. From my tests, it seems that
* more than about 10 buffers are unnecessary, and there is a noticeable
* performance hit in having more active on the card. So the majority of the
* card's memory isn't, in fact, used. Sadly, the card only has one transmit
* buffer and, short of loading our own firmware into it (which is what some
* drivers resort to) there's nothing we can do about this.
*
* We keep up to 4 "receive packet" commands active on the board at a time.
* When a packet comes in, so long as there is a receive command active, the
* board will send us a "packet received" PCB and then add the data for that
* packet to the DMA queue. If a DMA transfer is not already in progress, we
* set one up to start uploading the data. We have to maintain a list of
* backlogged receive packets, because the card may decide to tell us about
* a newly-arrived packet at any time, and we may not be able to start a DMA
* transfer immediately (ie one may already be going on). We can't NAK the
* PCB, because then it would throw the packet away.
*
* Trying to send a PCB to the card at the wrong moment seems to have bad
* effects. If we send it a transmit PCB while a receive DMA is happening,
* it will just NAK the PCB and so we will have wasted our time. Worse, it
* sometimes seems to interrupt the transfer. The majority of the low-level
* code is protected by one huge semaphore -- "busy" -- which is set whenever
* it probably isn't safe to do anything to the card. The receive routine
* must gain a lock on "busy" before it can start a DMA transfer, and the
* transmit routine must gain a lock before it sends the first PCB to the card.
* The send_pcb() routine also has an internal semaphore to protect it against
* being re-entered (which would be disastrous) -- this is needed because
* several things can happen asynchronously (re-priming the receiver and
* asking the card for statistics, for example). send_pcb() will also refuse
* to talk to the card at all if a DMA upload is happening. The higher-level
* networking code will reschedule a later retry if some part of the driver
* is blocked. In practice, this doesn't seem to happen very often.
*/
/* This driver may now work with revision 2.x hardware, since all the read
* operations on the HCR have been removed (we now keep our own softcopy).
* But I don't have an old card to test it on.
*
* This has had the bad effect that the autoprobe routine is now a bit
* less friendly to other devices. However, it was never very good.
* before, so I doubt it will hurt anybody.
*/
/* The driver is a mess. I took Craig's and Juha's code, and hacked it firstly
* to make it more reliable, and secondly to add DMA mode. Many things could
* probably be done better; the concurrency protection is particularly awful.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include "3c505.h"
/*********************************************************
*
* define debug messages here as common strings to reduce space
*
*********************************************************/
static const char filename[] = __FILE__;
static const char timeout_msg[] = "*** timeout at %s:%s (line %d) ***\n";
#define TIMEOUT_MSG(lineno) \
printk(timeout_msg, filename,__func__,(lineno))
static const char invalid_pcb_msg[] =
"*** invalid pcb length %d at %s:%s (line %d) ***\n";
#define INVALID_PCB_MSG(len) \
printk(invalid_pcb_msg, (len),filename,__func__,__LINE__)
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
static char search_msg[] __initdata = KERN_INFO "%s: Looking for 3c505 adapter at address %#x...";
static char stilllooking_msg[] __initdata = "still looking...";
static char found_msg[] __initdata = "found.\n";
static char notfound_msg[] __initdata = "not found (reason = %d)\n";
static char couldnot_msg[] __initdata = KERN_INFO "%s: 3c505 not found\n";
/*********************************************************
*
* various other debug stuff
*
*********************************************************/
#ifdef ELP_DEBUG
static int elp_debug = ELP_DEBUG;
#else
static int elp_debug;
#endif
#define debug elp_debug
/*
* 0 = no messages (well, some)
* 1 = messages when high level commands performed
* 2 = messages when low level commands performed
* 3 = messages when interrupts received
*/
/*****************************************************************
*
* List of I/O-addresses we try to auto-sense
* Last element MUST BE 0!
*****************************************************************/
static int addr_list[] __initdata = {0x300, 0x280, 0x310, 0};
/* Dma Memory related stuff */
static unsigned long dma_mem_alloc(int size)
{
int order = get_order(size);
return __get_dma_pages(GFP_KERNEL, order);
}
/*****************************************************************
*
* Functions for I/O (note the inline !)
*
*****************************************************************/
static inline unsigned char inb_status(unsigned int base_addr)
{
return inb(base_addr + PORT_STATUS);
}
static inline int inb_command(unsigned int base_addr)
{
return inb(base_addr + PORT_COMMAND);
Loading
Loading full blame...