1 |
/* |
2 |
* PearPC |
3 |
* rtl8139.cc |
4 |
* |
5 |
* RealTek 8139 Emulation |
6 |
* References: |
7 |
* [1] pearpc 3c90x driver |
8 |
* [2] Linux Kernel 2.4.22 (drivers/net/rtl8139.c) |
9 |
* [3] realtek 8139 technical specification/programming guide |
10 |
* |
11 |
* Copyright (C) 2004 John Kelley (pearpc@kelley.ca) |
12 |
* Copyright (C) 2003 Stefan Weyergraf |
13 |
* Copyright (C) 2004 Eric Estabrooks (estabroo2battlefoundry.net) |
14 |
* |
15 |
* This program is free software; you can redistribute it and/or modify |
16 |
* it under the terms of the GNU General Public License version 2 as |
17 |
* published by the Free Software Foundation. |
18 |
* |
19 |
* This program is distributed in the hope that it will be useful, |
20 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 |
* GNU General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with this program; if not, write to the Free Software |
26 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
27 |
* |
28 |
* FIXME: Implement EthTunDriver write frame prefixing |
29 |
*/ |
30 |
|
31 |
#include <cerrno> |
32 |
#include <cstdlib> |
33 |
#include <cstring> |
34 |
#include <unistd.h> |
35 |
|
36 |
#include "system/sys.h" |
37 |
#include "system/systhread.h" |
38 |
#include "system/arch/sysendian.h" |
39 |
#include "cpu/mem.h" |
40 |
#include "cpu/debug.h" |
41 |
#include "system/sysethtun.h" |
42 |
#include "tools/crc32.h" |
43 |
#include "tools/data.h" |
44 |
#include "tools/debug.h" |
45 |
#include "tools/except.h" |
46 |
#include "tools/snprintf.h" |
47 |
#include "io/pic/pic.h" |
48 |
#include "io/pci/pci.h" |
49 |
#include "debug/tracers.h" |
50 |
#include "rtl8139.h" |
51 |
|
52 |
#define MAX_PACKET_SIZE 6000 |
53 |
#define MAX_PACKETS 128 |
54 |
|
55 |
|
56 |
enum RxHeaderBits { |
57 |
Rx_ROK = 1<<0, // receive okay |
58 |
Rx_FAE = 1<<1, // frame alignment error |
59 |
Rx_CRC = 1<<2, // crc error |
60 |
Rx_LONG = 1<<3, // packet > 4k |
61 |
Rx_RUNT = 1<<4, // packet < 64 bytes |
62 |
Rx_ISE = 1<<5, // invalid symbol error |
63 |
/* bits 6-12 reserved */ |
64 |
Rx_BAR = 1<<13, // broadcast |
65 |
Rx_PAM = 1<<14, // exact match |
66 |
Rx_MAR = 1<<15, // multicast |
67 |
}; |
68 |
|
69 |
enum RxConfigurationBits { |
70 |
Rx_RBLEN = 3<<11, |
71 |
}; |
72 |
|
73 |
/* registers */ |
74 |
struct Registers { |
75 |
uint8 id0; // 0x00 (mac address) |
76 |
uint8 id1; |
77 |
uint8 id2; |
78 |
uint8 id3; |
79 |
uint8 id4; |
80 |
uint8 id5; |
81 |
uint16 rsvd0; // 0x06-0x07 |
82 |
uint8 mar0; |
83 |
uint8 mar1; |
84 |
uint8 mar2; |
85 |
uint8 mar3; |
86 |
uint8 mar4; |
87 |
uint8 mar5; |
88 |
uint8 mar6; |
89 |
uint8 mar7; |
90 |
uint32 TxStatusD0; // 0x10 |
91 |
uint32 TxStatusD1; // 0x14 |
92 |
uint32 TxStatusD2; // 0x18 |
93 |
uint32 TxStatusD3; // 0x1c |
94 |
uint32 TxStartAddrD0; // 0x20 |
95 |
uint32 TxStartAddrD1; // 0x24 |
96 |
uint32 TxStartAddrD2; // 0x28 |
97 |
uint32 TxStartAddrD3; // 0x2c |
98 |
uint32 RxBufferStartAddr; // 0x30 |
99 |
uint16 EarlyRxByteCount; // 0x34 |
100 |
uint8 EarlyRxStatus; // 0x36 |
101 |
uint8 CommandRegister; // 0x37 |
102 |
uint16 CAPR; // 0x38 initial 0xfff0 |
103 |
uint16 CBA; // 0x3a initial 0x0000 |
104 |
uint16 InterruptMask; // 0x3c |
105 |
uint16 InterruptStatus; // 0x3e |
106 |
uint32 TxConfiguration; // 0x40 |
107 |
uint32 RxConfiguration; // 0x44 |
108 |
uint32 TimerCount; // 0x48 |
109 |
uint32 MissedPacketCounter; // 0x4c |
110 |
uint8 Command93C46; //0x50 |
111 |
uint8 Config0; // 0x51 |
112 |
uint8 Config1; // 0x52 |
113 |
uint8 rsvd1 ; // 0x53 |
114 |
uint32 TimerInterrupt; // 0x54 |
115 |
uint8 MediaStatus; // 0x58 |
116 |
uint8 Config3; // 0x59 |
117 |
uint8 Config4; // 0x5a |
118 |
uint8 rsvd2; // 0x5b |
119 |
uint16 MultipleInterruptSelect; // 0x5c |
120 |
uint8 PCIRevisionID; // 0x5e should be 0x10 |
121 |
uint8 rsvd3; // 0x5f |
122 |
uint16 TSAD; // 0x60 Transmit Status of All Descriptors |
123 |
uint16 BMCR; // 0x62 Basic Mode Control |
124 |
uint16 BMSR; // 0x64 Basic Mode Status |
125 |
uint16 ANAR; // 0x66 Auto-Negotiation Advertisement |
126 |
uint16 ANLPAR; // 0x68 "" Link Partner |
127 |
uint16 ANER; // 0x6a "" Expansion |
128 |
uint16 DisconnectCounter; // 0x6c |
129 |
uint16 FalseCarrierSenseCounter; // 0x6e |
130 |
uint16 NWayTest; // 0x70 |
131 |
uint16 RX_ER_Counter; //0x72 |
132 |
uint16 CSConfiguration; // 0x74 |
133 |
uint16 rsvd4; |
134 |
uint32 PHY1; //0x78 |
135 |
uint32 Twister; // 0x7c |
136 |
uint8 PHY2; // 0x80 |
137 |
} PACKED; |
138 |
|
139 |
struct Packet { |
140 |
uint32 pid; |
141 |
uint16 size; |
142 |
byte packet[MAX_PACKET_SIZE]; |
143 |
}; |
144 |
|
145 |
// IEEE 802.3 MAC, Ethernet-II |
146 |
struct EthFrameII { |
147 |
byte destMAC[6]; |
148 |
byte srcMAC[6]; |
149 |
byte type[2]; |
150 |
} PACKED; |
151 |
|
152 |
enum EEPROMField { |
153 |
EEPROM_NodeAddress0 = 0x00, |
154 |
EEPROM_NodeAddress1 = 0x01, |
155 |
EEPROM_NodeAddress2 = 0x02, |
156 |
EEPROM_DeviceID = 0x03, |
157 |
EEPROM_ManifacturerID = 0x07, |
158 |
EEPROM_PCIParam = 0x08, |
159 |
EEPROM_RomInfo = 0x09, |
160 |
EEPROM_OEMNodeAddress0 = 0x0a, |
161 |
EEPROM_OEMNodeAddress1 = 0x0b, |
162 |
EEPROM_OEMNodeAddress2 = 0x0c, |
163 |
EEPROM_SoftwareInfo = 0x0d, |
164 |
EEPROM_CompWord = 0x0e, |
165 |
EEPROM_SoftwareInfo2 = 0x0f, |
166 |
EEPROM_Caps = 0x10, |
167 |
EEPROM_InternalConfig0 = 0x12, |
168 |
EEPROM_InternalConfig1 = 0x13, |
169 |
EEPROM_SubsystemVendorID = 0x17, |
170 |
EEPROM_SubsystemID = 0x18, |
171 |
EEPROM_MediaOptions = 0x19, |
172 |
EEPROM_SmbAddress = 0x1b, |
173 |
EEPROM_PCIParam2 = 0x1c, |
174 |
EEPROM_PCIParam3 = 0x1d, |
175 |
EEPROM_Checksum = 0x20 |
176 |
}; |
177 |
|
178 |
/* |
179 |
* |
180 |
*/ |
181 |
class rtl8139_NIC: public PCI_Device { |
182 |
protected: |
183 |
uint16 mEEPROM[0x40]; |
184 |
bool mEEPROMWritable; |
185 |
Registers mRegisters; |
186 |
uint16 mIntStatus; |
187 |
int mRingBufferSize; |
188 |
bool mGoodBSA; |
189 |
EthTunDevice * mEthTun; |
190 |
sys_mutex mLock; |
191 |
int mVerbose; |
192 |
byte mHead; |
193 |
byte mTail; |
194 |
byte mActive; |
195 |
byte mWatermark; |
196 |
byte mLast; |
197 |
byte mLastPackets[2]; |
198 |
uint32 mPid; |
199 |
Packet mPackets[MAX_PACKETS]; |
200 |
byte mMAC[6]; |
201 |
|
202 |
void PCIReset() |
203 |
{ |
204 |
IO_RTL8139_TRACE("PCIReset()\n"); // PCI config |
205 |
memset(mConfig, 0, sizeof mConfig); |
206 |
// 0-3 set by totalReset() |
207 |
// mConfig[0x04] = 0x07; // io+memory+master |
208 |
|
209 |
mConfig[0x08] = 0x00; // revision |
210 |
mConfig[0x09] = 0x00; // |
211 |
mConfig[0x0a] = 0x00; // ClassCode 0x20000: Ethernet network controller |
212 |
mConfig[0x0b] = 0x02; // |
213 |
|
214 |
mConfig[0x0e] = 0x00; // header-type (single-function PCI device) |
215 |
|
216 |
mConfig[0x3c] = IO_PIC_IRQ_ETHERNET1; // interrupt line |
217 |
mConfig[0x3d] = 1; // interrupt pin (default is 1) |
218 |
mConfig[0x3e] = 5; // MinGnt (default is 5 = 0x05 = 0101b) |
219 |
mConfig[0x3f] = 48; // MaxLat (default is 48 = 0x30 = 110000b) |
220 |
|
221 |
mConfig[0x34] = 0xdc; |
222 |
|
223 |
mIORegSize[0] = 0x100; |
224 |
mIORegType[0] = PCI_ADDRESS_SPACE_IO; |
225 |
assignIOPort(0, 0x1800); |
226 |
} |
227 |
|
228 |
void totalReset() |
229 |
{ |
230 |
IO_RTL8139_TRACE("totalReset()\n"); // PCI config |
231 |
/* FIXME: resetting can be done more fine-grained (see TotalReset cmd). |
232 |
* this is reset ALL regs. |
233 |
*/ |
234 |
|
235 |
mIORegSize[0] = 256; // 128; |
236 |
mIORegType[0] = PCI_ADDRESS_SPACE_IO; |
237 |
// internals |
238 |
mEEPROMWritable = false; |
239 |
memset(&mRegisters, 0, sizeof mRegisters); |
240 |
mIntStatus = 0; |
241 |
mRingBufferSize = 8192; |
242 |
mHead = 0; |
243 |
mTail = 0; |
244 |
mActive = 0; |
245 |
mWatermark = 0; |
246 |
mLastPackets[0] = 0; |
247 |
mLastPackets[1] = 0; |
248 |
mGoodBSA = false; |
249 |
// EEPROM config (FIXME: endianess) |
250 |
|
251 |
// set up mac address |
252 |
byte *ptr = (byte*)&mRegisters; |
253 |
memcpy(ptr, mMAC, 6); |
254 |
// negotiate link, actually set it to valid 100 half duplex |
255 |
mRegisters.BMSR = 0x2025; // 0x4025; |
256 |
mRegisters.BMCR = 0x2000; // 0x3010; // 100mbs, no ane |
257 |
mRegisters.Config1 = 0x00; |
258 |
mRegisters.CommandRegister = 0x01; |
259 |
mRegisters.TxConfiguration = 0x63000000; // rtl8139 |
260 |
mRegisters.MediaStatus = 0x90; |
261 |
mRegisters.CBA = 0; |
262 |
mRegisters.CAPR = 0xfff0; |
263 |
|
264 |
memset(mEEPROM, 0, sizeof mEEPROM); |
265 |
mEEPROM[EEPROM_DeviceID] = 0x8139; //0x9200; |
266 |
mEEPROM[EEPROM_ManifacturerID] = 0x10ec; //0x6d50; |
267 |
mEEPROM[EEPROM_PCIParam] = 0; //0x2940; |
268 |
mEEPROM[EEPROM_RomInfo] = 0; // no ROM |
269 |
mEEPROM[EEPROM_OEMNodeAddress0] = mEEPROM[EEPROM_NodeAddress0]; |
270 |
mEEPROM[EEPROM_OEMNodeAddress1] = mEEPROM[EEPROM_NodeAddress1]; |
271 |
mEEPROM[EEPROM_OEMNodeAddress2] = mEEPROM[EEPROM_NodeAddress2]; |
272 |
mEEPROM[EEPROM_SoftwareInfo] = 0; //0x4010; |
273 |
mEEPROM[EEPROM_CompWord] = 0; |
274 |
mEEPROM[EEPROM_SoftwareInfo2] = 0; //0x00aa; |
275 |
mEEPROM[EEPROM_Caps] = 0x72a2; |
276 |
mEEPROM[EEPROM_InternalConfig0] = 0; |
277 |
mEEPROM[EEPROM_InternalConfig1] = 0; //0x0040; // default is 0x0180 |
278 |
mEEPROM[EEPROM_SubsystemVendorID] = 0x10ec; //0x10b7; |
279 |
mEEPROM[EEPROM_SubsystemID] = 0x8139; //0x9200; |
280 |
mEEPROM[EEPROM_MediaOptions] = 0x000a; |
281 |
mEEPROM[EEPROM_SmbAddress] = 0; //0x6300; |
282 |
mEEPROM[EEPROM_PCIParam2] = 0; //0xffb7; |
283 |
mEEPROM[EEPROM_PCIParam3] = 0; //0xb7b7; |
284 |
mEEPROM[EEPROM_Checksum] = 0; |
285 |
|
286 |
// PCI config follow-ups |
287 |
mConfig[0x00] = mEEPROM[EEPROM_SubsystemVendorID] & 0xff; // vendor ID |
288 |
mConfig[0x01] = mEEPROM[EEPROM_SubsystemVendorID] >> 8; |
289 |
mConfig[0x02] = mEEPROM[EEPROM_DeviceID] & 0xff; // unit ID |
290 |
mConfig[0x03] = mEEPROM[EEPROM_DeviceID] >> 8; |
291 |
} |
292 |
|
293 |
void setCR(uint8 cr) |
294 |
{ |
295 |
if (cr & 0x10) { |
296 |
// FIXME: care about params |
297 |
totalReset(); |
298 |
} |
299 |
if (cr & 0x08) { |
300 |
mRegisters.CommandRegister |= 0x08; |
301 |
// enable receiver |
302 |
} |
303 |
if (cr & 0x04) { |
304 |
mRegisters.CommandRegister |= 0x04; |
305 |
// enable transmitter |
306 |
} |
307 |
if (cr & ~(0x1c)) { |
308 |
IO_RTL8139_WARN("command register write invalid byte: %0x\n", cr); |
309 |
} |
310 |
} |
311 |
|
312 |
void maybeRaiseIntr() |
313 |
{ |
314 |
IO_RTL8139_TRACE("maybeRaiseIntr()\n"); |
315 |
if (mRegisters.InterruptMask & mIntStatus) { |
316 |
//mIntStatus |= IS_interruptLatch; |
317 |
IO_RTL8139_TRACE("Generating interrupt. mIntStatus=%04x\n", mIntStatus); |
318 |
pic_raise_interrupt(mConfig[0x3c]); |
319 |
} |
320 |
} |
321 |
|
322 |
void TxPacket(uint32 address, uint32 size) |
323 |
{ |
324 |
byte pbuf[MAX_PACKET_SIZE]; |
325 |
byte * p; |
326 |
uint32 crc; |
327 |
uint32 psize; |
328 |
|
329 |
p = pbuf; |
330 |
IO_RTL8139_TRACE("address: %08x, size: %04x\n", address, size); |
331 |
if (ppc_dma_read(pbuf, address, size)) { |
332 |
/* if (mVerbose > 1) { |
333 |
debugDumpMem(ppc_addr, size); |
334 |
}*/ |
335 |
crc = ether_crc(size, p); |
336 |
psize = size; |
337 |
pbuf[psize+0] = crc; |
338 |
pbuf[psize+1] = crc>>8; |
339 |
pbuf[psize+2] = crc>>16; |
340 |
pbuf[psize+3] = crc>>24; |
341 |
psize += 4; |
342 |
|
343 |
uint w = mEthTun->sendPacket(pbuf, psize); |
344 |
if (w) { |
345 |
if (w == psize) { |
346 |
IO_RTL8139_TRACE("EthTun: %d bytes sent.\n", psize); |
347 |
} else { |
348 |
IO_RTL8139_WARN("EthTun: ARGH! send error: only %d of %d bytes sent\n", w, psize); |
349 |
} |
350 |
} else { |
351 |
IO_RTL8139_WARN("EthTun: ARGH! send error in packet driver.\n"); |
352 |
} |
353 |
maybeRaiseIntr(); |
354 |
} |
355 |
} |
356 |
|
357 |
public: |
358 |
rtl8139_NIC(EthTunDevice *aEthTun, const byte *mac) |
359 |
: PCI_Device("rtl8139 Network interface card", 0x1, 0xd) |
360 |
{ |
361 |
int e; |
362 |
if ((e = sys_create_mutex(&mLock))) throw IOException(e); |
363 |
mEthTun = aEthTun; |
364 |
memcpy(mMAC, mac, 6); |
365 |
mPid = 0; |
366 |
PCIReset(); |
367 |
totalReset(); |
368 |
} |
369 |
|
370 |
void transferPacket(bool raiseIntr) |
371 |
{ |
372 |
uint32 addr; |
373 |
uint32 base = mRegisters.RxBufferStartAddr; |
374 |
bool good; |
375 |
|
376 |
if (mTail == mHead) { |
377 |
return; |
378 |
} |
379 |
addr = base + mRegisters.CBA; |
380 |
if (mRegisters.CBA > mRingBufferSize) {// sending outside, could cause problems? |
381 |
good = false; |
382 |
} else { |
383 |
good = true; |
384 |
} |
385 |
#if 0 |
386 |
if ((mRegisters.CBA) > mRingBufferSize) { |
387 |
IO_RTL8139_TRACE("client ring buffer wrap around [%d]\n", raiseIntr); |
388 |
addr = base; |
389 |
mRegisters.CBA = 0; |
390 |
mRegisters.CAPR = 0xfff0; |
391 |
// mRegisters.CommandRegister |= 1; |
392 |
return; |
393 |
} |
394 |
#endif |
395 |
ppc_dma_write(addr, mPackets[mTail].packet, mPackets[mTail].size); |
396 |
IO_RTL8139_TRACE("wrote %04x bytes to the ring buffer\n", mPackets[mTail].size); |
397 |
mRegisters.EarlyRxByteCount = mPackets[mTail].size; |
398 |
mRegisters.EarlyRxStatus = 8; |
399 |
mRegisters.CBA += mPackets[mTail].size; |
400 |
mRegisters.CommandRegister &= 0xfe; // RxBuffer has data |
401 |
mLastPackets[1] = mLastPackets[0]; |
402 |
mLastPackets[0] = mTail; |
403 |
mActive--; |
404 |
IO_RTL8139_TRACE("Outgoing - Addr: %08x, Pid: %08x, Size: %04x\n", addr, mPackets[mTail].pid, mPackets[mTail].size-4); |
405 |
if (good) { |
406 |
mTail = (mTail+1) % MAX_PACKETS; |
407 |
} |
408 |
if (raiseIntr) { |
409 |
mIntStatus |= 1; |
410 |
maybeRaiseIntr(); |
411 |
} |
412 |
} |
413 |
|
414 |
virtual ~rtl8139_NIC() |
415 |
{ |
416 |
mEthTun->shutdownDevice(); |
417 |
delete mEthTun; |
418 |
sys_destroy_mutex(mLock); |
419 |
} |
420 |
|
421 |
void readConfig(uint reg) |
422 |
{ |
423 |
//if (mVerbose) IO_RTL8139_TRACE("readConfig %02x\n", reg); |
424 |
if (reg >= 0xdc) { |
425 |
IO_RTL8139_WARN("readConfig(%x)\n", reg); |
426 |
} |
427 |
sys_lock_mutex(mLock); |
428 |
PCI_Device::readConfig(reg); |
429 |
sys_unlock_mutex(mLock); |
430 |
} |
431 |
|
432 |
void writeConfig(uint reg, int offset, int size) |
433 |
{ |
434 |
//if (mVerbose) IO_RTL8139_TRACE("writeConfig %02x, %d, %d\n", reg, offset, size); |
435 |
sys_lock_mutex(mLock); |
436 |
if (reg >= 0xdc) { |
437 |
IO_RTL8139_WARN("writeConfig(%x, %d, %d)\n", reg, offset, size); |
438 |
} |
439 |
PCI_Device::writeConfig(reg, offset, size); |
440 |
sys_unlock_mutex(mLock); |
441 |
} |
442 |
|
443 |
bool readDeviceIO(uint r, uint32 port, uint32 &data, uint size) |
444 |
{ |
445 |
if (r != 0) return false; |
446 |
bool retval = false; |
447 |
// IO_RTL8139_TRACE("readDevice waiting for mLock\n"); |
448 |
sys_lock_mutex(mLock); |
449 |
// IO_RTL8139_TRACE("readDevice has mLock\n"); |
450 |
|
451 |
if (port == 0x3e) { |
452 |
// IntStatus (no matter which window) |
453 |
if (size != 2) { |
454 |
IO_RTL8139_WARN("unaligned read from IntStatus\n"); |
455 |
} |
456 |
IO_RTL8139_TRACE("read IntStatus = %04x\n", mIntStatus); |
457 |
data = mIntStatus; |
458 |
mIntStatus = 0; // a read resets the interrupt status register |
459 |
retval = true; |
460 |
} else if ((port >= 0) && (port+size <= sizeof(Registers))) { |
461 |
// read from (standard) register |
462 |
data = 0; |
463 |
memcpy(&data, ((byte*)&mRegisters)+port, size); |
464 |
|
465 |
switch (port) { |
466 |
case 0x48: |
467 |
IO_RTL8139_TRACE("read Timer = %08x\n", data); |
468 |
break; |
469 |
case 0x37: { |
470 |
IO_RTL8139_TRACE("read Command Register = %02x\n", data); |
471 |
break; |
472 |
} |
473 |
case 0x64: { |
474 |
IO_RTL8139_TRACE("read Basic Mode Status = %04x\n", data); |
475 |
if ((mTail != mHead) && (mRegisters.CommandRegister & 0x01)) { |
476 |
transferPacket(true); |
477 |
} |
478 |
break; |
479 |
} |
480 |
default: |
481 |
IO_RTL8139_TRACE("read reg %04x (size %d) = %08x\n", port, size, data); |
482 |
break; |
483 |
} |
484 |
retval = true; |
485 |
} |
486 |
sys_unlock_mutex(mLock); |
487 |
// IO_RTL8139_TRACE("readDevice freed mLock\n"); |
488 |
return retval; |
489 |
} |
490 |
|
491 |
bool writeDeviceIO(uint r, uint32 port, uint32 data, uint size) |
492 |
{ |
493 |
uint32 original; |
494 |
|
495 |
if (r != 0) return false; |
496 |
bool retval = false; |
497 |
// IO_RTL8139_TRACE("writeDevice waiting for mLock\n"); |
498 |
sys_lock_mutex(mLock); |
499 |
// IO_RTL8139_TRACE("writeDevice has mLock\n"); |
500 |
original = data; |
501 |
if (port == 0x37) { |
502 |
// CommandReg (no matter which window) |
503 |
if (size != 1) { |
504 |
IO_RTL8139_WARN("unaligned write to CommandReg\n"); |
505 |
} |
506 |
setCR(data); |
507 |
retval = true; |
508 |
} else if ((port >= 0) && (port+size <= sizeof(Registers))) { |
509 |
switch (port) { |
510 |
case 0x3c: { |
511 |
IO_RTL8139_TRACE("write Interrupt Mask Register %04x (now = %04x)\n", data, mRegisters.InterruptMask); |
512 |
mRegisters.InterruptMask = data; |
513 |
break; |
514 |
} |
515 |
case 0x10: { |
516 |
IO_RTL8139_TRACE("write to TS0, got data to send %08x\n", data); |
517 |
TxPacket(mRegisters.TxStartAddrD0, data & 0x0fff); |
518 |
mRegisters.TxStatusD0 |= ((1 << 13)|(1<<15)); // set ownership |
519 |
mIntStatus |= 4; // Tx Ok |
520 |
break; |
521 |
} |
522 |
case 0x14: { |
523 |
IO_RTL8139_TRACE("write to TS1, got data to send %08x\n", data); |
524 |
TxPacket(mRegisters.TxStartAddrD1, data & 0x0fff); |
525 |
mRegisters.TxStatusD1 |= ((1 << 13)|(1<<15)); // set ownership |
526 |
mIntStatus |= 4; // Tx Ok |
527 |
break; |
528 |
} |
529 |
case 0x18: { |
530 |
IO_RTL8139_TRACE("write to TS2, got data to send %08x\n", data); |
531 |
TxPacket(mRegisters.TxStartAddrD2, data & 0x0fff); |
532 |
mRegisters.TxStatusD2 |= ((1 << 13)|(1<<15)); // set ownership |
533 |
mIntStatus |= 4; // Tx Ok |
534 |
break; |
535 |
} |
536 |
case 0x1c: { |
537 |
IO_RTL8139_TRACE("write to TS3, got data to send %08x\n", data); |
538 |
TxPacket(mRegisters.TxStartAddrD3, data & 0x0fff); |
539 |
mRegisters.TxStatusD3 |= ((1 << 13)|(1<<15)); // set ownership |
540 |
mIntStatus |= 4; // Tx Ok |
541 |
break; |
542 |
} |
543 |
case 0x20: { |
544 |
IO_RTL8139_TRACE("write to TxSA0, address %08x\n", data); |
545 |
mRegisters.TxStartAddrD0 = data; |
546 |
break; |
547 |
} |
548 |
case 0x24: { |
549 |
IO_RTL8139_TRACE("write to TxSA1, address %08x\n", data); |
550 |
mRegisters.TxStartAddrD1 = data; |
551 |
break; |
552 |
} |
553 |
case 0x28: { |
554 |
IO_RTL8139_TRACE("write to TxSA2, address %08x\n", data); |
555 |
mRegisters.TxStartAddrD2 = data; |
556 |
break; |
557 |
} |
558 |
case 0x2c: { |
559 |
IO_RTL8139_TRACE("write to TxSA3, address %08x\n", data); |
560 |
mRegisters.TxStartAddrD3 = data; |
561 |
break; |
562 |
} |
563 |
case 0x30: { |
564 |
IO_RTL8139_TRACE("write to RxBSA, address %08x\n", data); |
565 |
mRegisters.RxBufferStartAddr = data; |
566 |
mRegisters.CBA = 0; |
567 |
mRegisters.CAPR = 0xfff0; |
568 |
mRegisters.CommandRegister |= 1; |
569 |
mGoodBSA = true; |
570 |
transferPacket(true); |
571 |
break; |
572 |
} |
573 |
case 0x38: { |
574 |
IO_RTL8139_TRACE("update to CAPR: CAPR %04x, CBA %04x\n", data, mRegisters.CBA); |
575 |
mRegisters.CAPR = data; |
576 |
if (mRegisters.CAPR >= mRegisters.CBA) { |
577 |
IO_RTL8139_WARN("Bad packet read by client? Active %02x, CAPR %04x, CBA %04x, Tail %02x, Head %02x\n", mActive, mRegisters.CAPR, mRegisters.CBA, mTail, mHead); |
578 |
mRegisters.CBA = mRegisters.CAPR + 0x10; |
579 |
} |
580 |
if (mRegisters.CAPR > mRingBufferSize) { //client knows about wrap, so wrap |
581 |
mRegisters.CBA = 0; |
582 |
mIntStatus |= 1; // fake send |
583 |
maybeRaiseIntr(); |
584 |
IO_RTL8139_TRACE("client wrap on CAPR set Active %02x, CAPR %04x, CBA %04x, Tail %02x, Head %02x\n", mActive, mRegisters.CAPR, mRegisters.CBA, mTail, mHead); |
585 |
/* |
586 |
mIntStatus |= 1; // fake send |
587 |
maybeRaiseIntr(); |
588 |
mRegisters.CAPR = 0xfff0; |
589 |
mRegisters.CommandRegister |= 1; |
590 |
*/ |
591 |
} else { |
592 |
if (mTail != mHead) { |
593 |
transferPacket(false); |
594 |
} else { |
595 |
mRegisters.CommandRegister |= 1; |
596 |
} |
597 |
} |
598 |
break; |
599 |
} |
600 |
case 0x44: { |
601 |
IO_RTL8139_TRACE("write to RxConfiguration, data %08x\n", data); |
602 |
switch ((data & 0x1800)) { |
603 |
case 0x0000: mRingBufferSize = 8192; break; |
604 |
case 0x0800: mRingBufferSize = 16384; break; |
605 |
case 0x1000: mRingBufferSize = 32768; break; |
606 |
case 0x1800: mRingBufferSize = 65536; break; |
607 |
default: mRingBufferSize = 8192; |
608 |
}; |
609 |
IO_RTL8139_TRACE("RingBuffer Size: %08x\n", mRingBufferSize); |
610 |
break; |
611 |
} |
612 |
case 0x62: { |
613 |
IO_RTL8139_TRACE("write Basic Mode Control %04x\n", data); |
614 |
//mIntStatus |= 0x2000; // cable length changed, receive enabled |
615 |
mRegisters.BMCR = data & 0xfdff; |
616 |
break; |
617 |
} |
618 |
default: |
619 |
IO_RTL8139_TRACE("write to register port=%04x, size=%d, data=0x%08x\n", port, size, data); |
620 |
// write to (standard) register |
621 |
memcpy(((byte*)&mRegisters)+port, &data, size); |
622 |
} |
623 |
retval = true; |
624 |
} |
625 |
sys_unlock_mutex(mLock); |
626 |
return retval; |
627 |
} |
628 |
|
629 |
void handleRxQueue() |
630 |
{ |
631 |
uint16 header; |
632 |
uint16 psize; |
633 |
byte rxPacket[MAX_PACKET_SIZE]; |
634 |
uint16 rxPacketSize; |
635 |
byte tmp; |
636 |
static byte broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
637 |
|
638 |
while (1) { |
639 |
while (mEthTun->waitRecvPacket() != 0) { |
640 |
// don't block the system in case of (repeated) error(s) |
641 |
sys_suspend(); |
642 |
} |
643 |
rxPacketSize = mEthTun->recvPacket(rxPacket, sizeof rxPacket); |
644 |
if (!rxPacketSize) { |
645 |
// don't block the system in case of (repeated) error(s) |
646 |
sys_suspend(); |
647 |
continue; |
648 |
} |
649 |
IO_RTL8139_TRACE("got packet from the world at large\n"); |
650 |
if (!mGoodBSA) continue; |
651 |
/* if (mVerbose > 1) { |
652 |
debugDumpMem(rxPacket, rxPacketSize); |
653 |
}*/ |
654 |
header = 0; |
655 |
if (rxPacketSize < 64) { |
656 |
for ( ; rxPacketSize < 60; rxPacketSize++) { |
657 |
rxPacket[rxPacketSize] = 0; |
658 |
} |
659 |
//header |= Rx_RUNT; // set runt status |
660 |
} |
661 |
/* pad to a 4 byte boundary */ |
662 |
for (int i = 4-(rxPacketSize % 4); i != 0; i--) { |
663 |
rxPacket[rxPacketSize++] = 0; |
664 |
} |
665 |
if (memcmp(rxPacket, broadcast, 6) == 0) { |
666 |
header |= Rx_BAR; |
667 |
} |
668 |
// IO_RTL8139_TRACE("handleRxQueue waiting for mLock\n"); |
669 |
sys_lock_mutex(mLock); |
670 |
// IO_RTL8139_TRACE("handleRxQueue has mLock\n"); |
671 |
if (memcmp(rxPacket, (byte*)&(mRegisters.id0), 6) == 0) { |
672 |
/*if (mVerbose > 1) IO_RTL8139_TRACE("Physical Address Match\n");*/ |
673 |
header |= Rx_PAM; |
674 |
} |
675 |
// check crc? |
676 |
header |= Rx_ROK; |
677 |
psize = rxPacketSize; |
678 |
IO_RTL8139_TRACE("Incoming - Pid: %08x, Header: %04x, Size: %04x\n", mPid, header, psize); |
679 |
mPackets[mHead].packet[0] = header; |
680 |
mPackets[mHead].packet[1] = header>>8; |
681 |
mPackets[mHead].packet[2] = psize; |
682 |
mPackets[mHead].packet[3] = psize>>8; |
683 |
memcpy(&(mPackets[mHead].packet[4]), rxPacket, rxPacketSize); |
684 |
mPackets[mHead].size = rxPacketSize+4; |
685 |
mPackets[mHead].pid = mPid; |
686 |
tmp = mHead; |
687 |
if (mHead == mTail) { /* first recent packet buffer */ |
688 |
mHead = (mHead+1) % MAX_PACKETS; |
689 |
} else { |
690 |
mHead = (mHead+1) % MAX_PACKETS; |
691 |
if (mHead == mTail) { |
692 |
mHead = tmp; // reset it back |
693 |
IO_RTL8139_WARN("Internal Buffer wrapped around\n"); |
694 |
} |
695 |
} |
696 |
if (tmp != mHead) { |
697 |
mPid++; |
698 |
mActive++; |
699 |
if (mActive > mWatermark) { |
700 |
IO_RTL8139_TRACE("Watermark: %02x\n", mWatermark); |
701 |
mWatermark = mActive; |
702 |
} |
703 |
} |
704 |
if (mRegisters.CommandRegister & 1) { /* no packets in process, kick one out */ |
705 |
transferPacket(true); |
706 |
} |
707 |
sys_unlock_mutex(mLock); |
708 |
// IO_RTL8139_TRACE("handleRxQueue freed mLock\n"); |
709 |
} |
710 |
} |
711 |
|
712 |
}; // end of rtl8139 class |
713 |
|
714 |
static void *rtl8139HandleRxQueue(void *nic) |
715 |
{ |
716 |
rtl8139_NIC *NIC = (rtl8139_NIC *)nic; |
717 |
NIC->handleRxQueue(); |
718 |
return NULL; |
719 |
} |
720 |
|
721 |
bool rtl8139_installed = false; |
722 |
|
723 |
#include "configparser.h" |
724 |
#include "tools/strtools.h" |
725 |
|
726 |
#define RTL8139_KEY_INSTALLED "pci_rtl8139_installed" |
727 |
#define RTL8139_KEY_MAC "pci_rtl8139_mac" |
728 |
|
729 |
void rtl8139_init() |
730 |
{ |
731 |
if (gConfig->getConfigInt(RTL8139_KEY_INSTALLED)) { |
732 |
rtl8139_installed = true; |
733 |
byte mac[6]; |
734 |
mac[0] = 0xde; |
735 |
mac[1] = 0xad; |
736 |
mac[2] = 0xca; |
737 |
mac[3] = 0xfe; |
738 |
mac[4] = 0x12; |
739 |
mac[5] = 0x34; |
740 |
if (gConfig->haveKey(RTL8139_KEY_MAC)) { |
741 |
String macstr_; |
742 |
gConfig->getConfigString(RTL8139_KEY_MAC, macstr_); |
743 |
// do something useful with mac |
744 |
const char *macstr = macstr_.contentChar(); |
745 |
byte cfgmac[6]; |
746 |
for (uint i=0; i<6; i++) { |
747 |
uint64 v; |
748 |
if (!parseIntStr(macstr, v, 16) || (v>255) || ((*macstr != ':') && (i!=5))) { |
749 |
IO_RTL8139_ERR("error in config key %s:" |
750 |
"expected format: XX:XX:XX:XX:XX:XX, " |
751 |
"where X stands for any digit or the " |
752 |
"letters a-f, A-F (error at: %s)\n", |
753 |
RTL8139_KEY_MAC, macstr); |
754 |
} |
755 |
macstr++; |
756 |
cfgmac[i] = v; |
757 |
} |
758 |
memcpy(mac, cfgmac, sizeof mac); |
759 |
} |
760 |
EthTunDevice *ethTun = createEthernetTunnel(); |
761 |
if (!ethTun) { |
762 |
IO_3C90X_ERR("Couldn't create ethernet tunnel\n"); |
763 |
exit(1); |
764 |
} |
765 |
if (ethTun->initDevice()) { |
766 |
IO_3C90X_ERR("Couldn't initialize ethernet tunnel\n"); |
767 |
exit(1); |
768 |
} |
769 |
#if 0 |
770 |
printf("Creating RealTek rtl8139 NIC emulation with eth_addr = "); |
771 |
for (uint i=0; i<6; i++) { |
772 |
if (i<5) { |
773 |
printf("%02x:", mac[i]); |
774 |
} else { |
775 |
printf("%02x", mac[i]); |
776 |
} |
777 |
} |
778 |
printf("\n"); |
779 |
#endif |
780 |
rtl8139_NIC *MyNIC = new rtl8139_NIC(ethTun, mac); |
781 |
gPCI_Devices->insert(MyNIC); |
782 |
sys_thread rxthread; |
783 |
sys_create_thread(&rxthread, 0, rtl8139HandleRxQueue, MyNIC); |
784 |
} |
785 |
} |
786 |
|
787 |
void rtl8139_done() |
788 |
{ |
789 |
} |
790 |
|
791 |
void rtl8139_init_config() |
792 |
{ |
793 |
gConfig->acceptConfigEntryIntDef(RTL8139_KEY_INSTALLED, 0); |
794 |
gConfig->acceptConfigEntryString(RTL8139_KEY_MAC, false); |
795 |
} |