1 |
dpavlin |
1 |
/* |
2 |
|
|
* PearPC |
3 |
|
|
* 3c90x.cc |
4 |
|
|
* |
5 |
|
|
* 3Com 3C905C Emulation |
6 |
|
|
* References: |
7 |
|
|
* [1] 3c90xc.pdf ("3C90xC NICs Technical Reference" 3Com(r) part number 89-0931-000) |
8 |
|
|
* [2] Linux Kernel 2.4.22 (drivers/net/3c59x.c) |
9 |
|
|
* |
10 |
|
|
* Copyright (C) 2004 John Kelley (pearpc@kelley.ca) |
11 |
|
|
* Copyright (C) 2003 Stefan Weyergraf |
12 |
|
|
* |
13 |
|
|
* This program is free software; you can redistribute it and/or modify |
14 |
|
|
* it under the terms of the GNU General Public License version 2 as |
15 |
|
|
* published by the Free Software Foundation. |
16 |
|
|
* |
17 |
|
|
* This program is distributed in the hope that it will be useful, |
18 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 |
|
|
* GNU General Public License for more details. |
21 |
|
|
* |
22 |
|
|
* You should have received a copy of the GNU General Public License |
23 |
|
|
* along with this program; if not, write to the Free Software |
24 |
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include <cerrno> |
28 |
|
|
#include <cstdlib> |
29 |
|
|
#include <cstring> |
30 |
|
|
#include <unistd.h> |
31 |
|
|
|
32 |
|
|
#include "system/sys.h" |
33 |
|
|
#include "system/systhread.h" |
34 |
|
|
#include "cpu/debug.h" |
35 |
|
|
#include "cpu/mem.h" |
36 |
|
|
#include "system/sysethtun.h" |
37 |
|
|
#include "system/arch/sysendian.h" |
38 |
|
|
#include "tools/crc32.h" |
39 |
|
|
#include "tools/data.h" |
40 |
|
|
#include "tools/endianess.h" |
41 |
|
|
#include "tools/except.h" |
42 |
|
|
#include "tools/snprintf.h" |
43 |
|
|
#include "io/pic/pic.h" |
44 |
|
|
#include "io/pci/pci.h" |
45 |
|
|
#include "debug/tracers.h" |
46 |
|
|
#include "3c90x.h" |
47 |
|
|
|
48 |
|
|
#ifdef HAVE_CONFIG_H |
49 |
|
|
#include "config.h" |
50 |
|
|
#endif |
51 |
|
|
|
52 |
|
|
#define MAX_PACKET_SIZE 16384 |
53 |
|
|
|
54 |
|
|
enum Command { |
55 |
|
|
CmdTotalReset = 0<<11, |
56 |
|
|
CmdSelectWindow = 1<<11, |
57 |
|
|
CmdEnableDC = 2<<11, // CmdStartCoax |
58 |
|
|
CmdRxDisable = 3<<11, |
59 |
|
|
CmdRxEnable = 4<<11, |
60 |
|
|
CmdRxReset = 5<<11, |
61 |
|
|
CmdStall = 6<<11, |
62 |
|
|
CmdTxDone = 7<<11, |
63 |
|
|
CmdRxDiscard = 8<<11, |
64 |
|
|
CmdTxEnable = 9<<11, |
65 |
|
|
CmdTxDisable = 10<<11, |
66 |
|
|
CmdTxReset = 11<<11, |
67 |
|
|
CmdReqIntr = 12<<11, // CmdFakeIntr |
68 |
|
|
CmdAckIntr = 13<<11, |
69 |
|
|
CmdSetIntrEnb = 14<<11, |
70 |
|
|
CmdSetIndicationEnable = 15<<11, // CmdSetStatusEnb |
71 |
|
|
CmdSetRxFilter = 16<<11, |
72 |
|
|
CmdSetRxEarlyThresh = 17<<11, |
73 |
|
|
CmdSetTxThreshold = 18<<11, // aka TxAgain ? |
74 |
|
|
CmdSetTxStartThresh = 19<<11, // set TxStartTresh |
75 |
|
|
// CmdStartDMAUp = 20<<11, |
76 |
|
|
// CmdStartDMADown = (20<<11)+1, |
77 |
|
|
CmdStatsEnable = 21<<11, |
78 |
|
|
CmdStatsDisable = 22<<11, |
79 |
|
|
CmdDisableDC = 23<<11, // CmdStopCoax |
80 |
|
|
CmdSetTxReclaimThresh = 24<<11, |
81 |
|
|
CmdSetHashFilterBit = 25<<11 |
82 |
|
|
}; |
83 |
|
|
|
84 |
|
|
/* |
85 |
|
|
* IntStatusBits |
86 |
|
|
*/ |
87 |
|
|
enum IntStatusBits { |
88 |
|
|
IS_interruptLatch = 1<<0, |
89 |
|
|
IS_hostError = 1<<1, |
90 |
|
|
IS_txComplete = 1<<2, |
91 |
|
|
/* bit 3 is unspecified */ |
92 |
|
|
IS_rxComplete = 1<<4, |
93 |
|
|
IS_rxEarly = 1<<5, |
94 |
|
|
IS_intRequested = 1<<6, |
95 |
|
|
IS_updateStats = 1<<7, |
96 |
|
|
IS_linkEvent = 1<<8, |
97 |
|
|
IS_dnComplete = 1<<9, |
98 |
|
|
IS_upComplete = 1<<10, |
99 |
|
|
IS_cmdInProgress = 1<<11, |
100 |
|
|
/* bit 12 is unspecified */ |
101 |
|
|
/* [15:13] is currently selected window */ |
102 |
|
|
}; |
103 |
|
|
|
104 |
|
|
/* |
105 |
|
|
* DmaCtrlBits ([1] p.96) |
106 |
|
|
*/ |
107 |
|
|
enum DmaCtrlBits { |
108 |
|
|
/* bit 0 unspecified */ |
109 |
|
|
DC_dnCmplReq = 1<<1, |
110 |
|
|
DC_dnStalled = 1<<2, |
111 |
|
|
DC_upComplete = 1<<3, // FIXME: same as in IntStatus, but always visible |
112 |
|
|
DC_dnComplete = 1<<4, // same as above ^^^ |
113 |
|
|
DC_upRxEarlyEnable = 1<<5, |
114 |
|
|
DC_armCountdown = 1<<6, |
115 |
|
|
DC_dnInProg = 1<<7, |
116 |
|
|
DC_counterSpeed = 1<<8, |
117 |
|
|
DC_countdownMode = 1<<9, |
118 |
|
|
/* bits 10-15 unspecified */ |
119 |
|
|
DC_upAltSeqDisable = 1<<16, |
120 |
|
|
DC_dnAltSeqDisable = 1<<17, |
121 |
|
|
DC_defeatMWI = 1<<20, |
122 |
|
|
DC_defeatMRL = 1<<21, |
123 |
|
|
DC_upOverDiscEnable = 1<<22, |
124 |
|
|
DC_targetAbort = 1<<30, |
125 |
|
|
DC_masterAbort = 1<<31 |
126 |
|
|
}; |
127 |
|
|
|
128 |
|
|
/* |
129 |
|
|
* MII Registers |
130 |
|
|
*/ |
131 |
|
|
/*enum MIIControlBits { |
132 |
|
|
MIIC_collision = 1<<7, |
133 |
|
|
MIIC_fullDuplex = 1<<8, |
134 |
|
|
MIIC_restartNegote = 1<<9, |
135 |
|
|
MIIC_collision = 1<<7, |
136 |
|
|
rest missing |
137 |
|
|
};*/ |
138 |
|
|
|
139 |
|
|
struct MIIRegisters { |
140 |
|
|
uint16 control; |
141 |
|
|
uint16 status; |
142 |
|
|
uint16 id0; |
143 |
|
|
uint16 id1; |
144 |
|
|
uint16 advert; |
145 |
|
|
uint16 linkPartner; |
146 |
|
|
uint16 expansion; |
147 |
|
|
uint16 nextPage; |
148 |
|
|
} PACKED; |
149 |
|
|
|
150 |
|
|
/* |
151 |
|
|
* Registers |
152 |
|
|
*/ |
153 |
|
|
struct RegWindow { |
154 |
|
|
byte b[16]; |
155 |
|
|
uint16 u16[8]; |
156 |
|
|
}; |
157 |
|
|
|
158 |
|
|
struct Registers { |
159 |
|
|
// 0x10 bytes missing (current window) |
160 |
|
|
uint32 r0; |
161 |
|
|
uint32 r1; |
162 |
|
|
uint8 TxPktId; |
163 |
|
|
uint8 r2; |
164 |
|
|
uint8 Timer; |
165 |
|
|
uint8 TxStatus; |
166 |
|
|
uint16 r3; |
167 |
|
|
uint16 __dontUseMe;// really: uint16 IntStatusAuto; |
168 |
|
|
uint32 DmaCtrl; // [1] p.95 (dn), p.100 (up) |
169 |
|
|
uint32 DnListPtr; // [1] p.98 |
170 |
|
|
uint16 r4; |
171 |
|
|
uint8 DnBurstThresh; // [1] p.97 |
172 |
|
|
uint8 r5; |
173 |
|
|
uint8 DnPriorityThresh; |
174 |
|
|
uint8 DnPoll; // [1] p.100 |
175 |
|
|
uint16 r6; |
176 |
|
|
uint32 UpPktStatus; |
177 |
|
|
uint16 FreeTimer; |
178 |
|
|
uint16 Countdown; |
179 |
|
|
uint32 UpListPtr; // [1] p.115 |
180 |
|
|
uint8 UpPriorityThresh; |
181 |
|
|
uint8 UpPoll; |
182 |
|
|
uint8 UpBurstThresh; |
183 |
|
|
uint8 r7; |
184 |
|
|
uint32 RealTimeCount; |
185 |
|
|
uint8 ConfigAddress; |
186 |
|
|
uint8 r8; |
187 |
|
|
uint8 r9; |
188 |
|
|
uint8 r10; |
189 |
|
|
uint8 ConfigData; |
190 |
|
|
uint8 r11; |
191 |
|
|
uint8 r12; |
192 |
|
|
uint8 r13; |
193 |
|
|
uint32 r14[9]; |
194 |
|
|
uint32 DebugData; |
195 |
|
|
uint16 DebugControl; |
196 |
|
|
uint16 r15; |
197 |
|
|
uint16 DnMaxBurst; |
198 |
|
|
uint16 UpMaxBurst; |
199 |
|
|
uint16 PowerMgmtCtrl; |
200 |
|
|
uint16 r16; |
201 |
|
|
} PACKED; |
202 |
|
|
|
203 |
|
|
#define RA_INV 0 |
204 |
|
|
|
205 |
|
|
static byte gRegAccess[0x70] = |
206 |
|
|
{ |
207 |
|
|
/* 0x10 */ |
208 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
209 |
|
|
/* 0x14 */ |
210 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
211 |
|
|
/* 0x18 */ |
212 |
|
|
1, /* TxPktId */ |
213 |
|
|
RA_INV, |
214 |
|
|
1, /* Timer */ |
215 |
|
|
1, /* TxStatus */ |
216 |
|
|
/* 0x1c */ |
217 |
|
|
RA_INV, RA_INV, |
218 |
|
|
RA_INV, RA_INV, /* IntStatusAuto */ |
219 |
|
|
/* 0x20 */ |
220 |
|
|
4, RA_INV, RA_INV, RA_INV, /* DmaCtrl */ |
221 |
|
|
/* 0x24 */ |
222 |
|
|
4, RA_INV, RA_INV, RA_INV, /* DnListPtr */ |
223 |
|
|
/* 0x28 */ |
224 |
|
|
RA_INV, RA_INV, |
225 |
|
|
1, /* DnBurstThresh */ |
226 |
|
|
RA_INV, |
227 |
|
|
/* 0x2c */ |
228 |
|
|
1, /* DnPriorityThresh */ |
229 |
|
|
1, /* DnPoll */ |
230 |
|
|
RA_INV, |
231 |
|
|
1, |
232 |
|
|
/* 0x30 */ |
233 |
|
|
4, RA_INV, RA_INV, RA_INV, /* UpPktStatus */ |
234 |
|
|
/* 0x34 */ |
235 |
|
|
2, RA_INV, /* FreeTimer */ |
236 |
|
|
2, RA_INV, /* Countdown */ |
237 |
|
|
/* 0x38 */ |
238 |
|
|
4, RA_INV, RA_INV, RA_INV, /* UpListPtr */ |
239 |
|
|
/* 0x3c */ |
240 |
|
|
1, /* UpPriorityThresh */ |
241 |
|
|
1, /* UpPoll */ |
242 |
|
|
1, /* UpBurstThresh */ |
243 |
|
|
RA_INV, |
244 |
|
|
/* 0x40 */ |
245 |
|
|
4, RA_INV, RA_INV, RA_INV, /* RealTimeCount */ |
246 |
|
|
/* 0x44 */ |
247 |
|
|
1, /* ConfigAddress */ |
248 |
|
|
RA_INV, |
249 |
|
|
RA_INV, |
250 |
|
|
RA_INV, |
251 |
|
|
/* 0x48 */ |
252 |
|
|
1, /* ConfigData */ |
253 |
|
|
RA_INV, |
254 |
|
|
RA_INV, |
255 |
|
|
RA_INV, |
256 |
|
|
/* 0x4c */ |
257 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
258 |
|
|
/* 0x50 */ |
259 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
260 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
261 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
262 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
263 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
264 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
265 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
266 |
|
|
RA_INV, RA_INV, RA_INV, RA_INV, |
267 |
|
|
/* 0x70 */ |
268 |
|
|
4, RA_INV, RA_INV, RA_INV, /* DebugData */ |
269 |
|
|
/* 0x74 */ |
270 |
|
|
2, RA_INV, /* DebugControl */ |
271 |
|
|
RA_INV, RA_INV, |
272 |
|
|
/* 0x78 */ |
273 |
|
|
2, RA_INV, /* DnMaxBurst */ |
274 |
|
|
2, RA_INV, /* UpMaxBurst */ |
275 |
|
|
/* 0x7c */ |
276 |
|
|
2, RA_INV, /* PowerMgmtCtrl */ |
277 |
|
|
RA_INV, RA_INV |
278 |
|
|
}; |
279 |
|
|
|
280 |
|
|
/* |
281 |
|
|
* Window 0 |
282 |
|
|
*/ |
283 |
|
|
struct RegWindow0 { |
284 |
|
|
uint32 r0; |
285 |
|
|
uint32 BiosRomAddr; |
286 |
|
|
uint8 BiosRomData; |
287 |
|
|
uint8 r1; |
288 |
|
|
uint16 EepromCommand; |
289 |
|
|
uint16 EepromData; |
290 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
291 |
|
|
} PACKED; |
292 |
|
|
|
293 |
|
|
enum W0_Offsets { |
294 |
|
|
W0_EEPROMCmd = 0xa, |
295 |
|
|
W0_EEPROMData = 0xc |
296 |
|
|
}; |
297 |
|
|
|
298 |
|
|
enum W0_EEPROMOpcode { |
299 |
|
|
EEOP_SubCmd = 0<<6, |
300 |
|
|
EEOP_WriteReg = 1<<6, |
301 |
|
|
EEOP_ReadReg = 2<<6, |
302 |
|
|
EEOP_EraseReg = 3<<6 |
303 |
|
|
}; |
304 |
|
|
|
305 |
|
|
enum W0_EEPROMSubCmd { |
306 |
|
|
EESC_WriteDisable = 0<<4, |
307 |
|
|
EESC_WriteAll = 1<<4, |
308 |
|
|
EESC_EraseAll = 2<<4, |
309 |
|
|
EESC_WriteEnable = 3<<4 |
310 |
|
|
}; |
311 |
|
|
|
312 |
|
|
/* |
313 |
|
|
* Window 2 |
314 |
|
|
*/ |
315 |
|
|
struct RegWindow2 { |
316 |
|
|
uint16 StationAddress[6]; |
317 |
|
|
uint16 StationMask[6]; |
318 |
|
|
uint16 ResetOptions; |
319 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
320 |
|
|
} PACKED; |
321 |
|
|
|
322 |
|
|
/* |
323 |
|
|
* Window 3 |
324 |
|
|
*/ |
325 |
|
|
struct RegWindow3 { |
326 |
|
|
uint32 InternalConfig; // [1] p.58,76 |
327 |
|
|
uint16 MaxPktSize; |
328 |
|
|
uint16 MacControl; // [1] p.179 |
329 |
|
|
uint16 MediaOptions; // [1] p.78 (EE), p.181 |
330 |
|
|
uint16 RxFree; |
331 |
|
|
uint16 TxFree; // [1] p.101 |
332 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
333 |
|
|
} PACKED; |
334 |
|
|
|
335 |
|
|
/* |
336 |
|
|
* Window 4 |
337 |
|
|
*/ |
338 |
|
|
enum W4_PhysMgmtBits { |
339 |
|
|
PM_mgmtClk = 1<<0, |
340 |
|
|
PM_mgmtData = 1<<1, |
341 |
|
|
PM_mgmtDir = 1<<2 |
342 |
|
|
}; |
343 |
|
|
|
344 |
|
|
struct RegWindow4 { |
345 |
|
|
uint16 r0; |
346 |
|
|
uint16 r1; |
347 |
|
|
uint16 FifoDiagnostic; |
348 |
|
|
uint16 NetDiagnostic; // [1] p.184 |
349 |
|
|
uint16 PhysMgmt; // [1] p.186 |
350 |
|
|
uint16 MediaStatus; // [1] p.182 |
351 |
|
|
byte BadSSD; |
352 |
|
|
byte UpperBytesOK; |
353 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
354 |
|
|
} PACKED; |
355 |
|
|
|
356 |
|
|
/* |
357 |
|
|
* Window 5 |
358 |
|
|
*/ |
359 |
|
|
enum RxFilterBits { // [1] p.112 |
360 |
|
|
RXFILT_receiveIndividual = 1, |
361 |
|
|
RXFILT_receiveMulticast = 2, |
362 |
|
|
RXFILT_receiveBroadcast = 4, |
363 |
|
|
RXFILT_receiveAllFrames = 8, |
364 |
|
|
RXFILT_receiveMulticastHash = 16 |
365 |
|
|
}; |
366 |
|
|
|
367 |
|
|
struct RegWindow5 { |
368 |
|
|
uint16 TxStartThresh; |
369 |
|
|
uint16 r0; |
370 |
|
|
uint16 r1; |
371 |
|
|
uint16 RxEarlyThresh; |
372 |
|
|
byte RxFilter; // [1] p.112 |
373 |
|
|
byte TxReclaimThresh; |
374 |
|
|
uint16 InterruptEnable; // [1] p.120 |
375 |
|
|
uint16 IndicationEnable;// [1] p.120 |
376 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
377 |
|
|
} PACKED; |
378 |
|
|
|
379 |
|
|
/* |
380 |
|
|
* Window 6 |
381 |
|
|
*/ |
382 |
|
|
struct RegWindow6 { |
383 |
|
|
uint8 CarrierLost; |
384 |
|
|
uint8 SqeErrors; |
385 |
|
|
uint8 MultipleCollisions; |
386 |
|
|
uint8 SingleCollisions; |
387 |
|
|
uint8 LateCollisions; |
388 |
|
|
uint8 RxOverruns; |
389 |
|
|
uint8 FramesXmittedOk; |
390 |
|
|
uint8 FramesRcvdOk; |
391 |
|
|
uint8 FramesDeferred; |
392 |
|
|
uint8 UpperFramesOk; |
393 |
|
|
uint16 BytesRcvdOk; |
394 |
|
|
uint16 BytesXmittedOk; |
395 |
|
|
uint16 XXX; // IntStatus/CommandRegister |
396 |
|
|
} PACKED; |
397 |
|
|
|
398 |
|
|
/* |
399 |
|
|
* EEPROM |
400 |
|
|
*/ |
401 |
|
|
enum EEPROMField { |
402 |
|
|
EEPROM_NodeAddress0 = 0x00, |
403 |
|
|
EEPROM_NodeAddress1 = 0x01, |
404 |
|
|
EEPROM_NodeAddress2 = 0x02, |
405 |
|
|
EEPROM_DeviceID = 0x03, |
406 |
|
|
EEPROM_ManifacturerID = 0x07, |
407 |
|
|
EEPROM_PCIParam = 0x08, |
408 |
|
|
EEPROM_RomInfo = 0x09, |
409 |
|
|
EEPROM_OEMNodeAddress0 = 0x0a, |
410 |
|
|
EEPROM_OEMNodeAddress1 = 0x0b, |
411 |
|
|
EEPROM_OEMNodeAddress2 = 0x0c, |
412 |
|
|
EEPROM_SoftwareInfo = 0x0d, |
413 |
|
|
EEPROM_CompWord = 0x0e, |
414 |
|
|
EEPROM_SoftwareInfo2 = 0x0f, |
415 |
|
|
EEPROM_Caps = 0x10, |
416 |
|
|
EEPROM_InternalConfig0 = 0x12, |
417 |
|
|
EEPROM_InternalConfig1 = 0x13, |
418 |
|
|
EEPROM_SubsystemVendorID = 0x17, |
419 |
|
|
EEPROM_SubsystemID = 0x18, |
420 |
|
|
EEPROM_MediaOptions = 0x19, |
421 |
|
|
EEPROM_SmbAddress = 0x1b, |
422 |
|
|
EEPROM_PCIParam2 = 0x1c, |
423 |
|
|
EEPROM_PCIParam3 = 0x1d, |
424 |
|
|
EEPROM_Checksum = 0x20 |
425 |
|
|
}; |
426 |
|
|
|
427 |
|
|
/* |
428 |
|
|
* Up/Downloading |
429 |
|
|
*/ |
430 |
|
|
|
431 |
|
|
// must be on 8-byte physical address boundary |
432 |
|
|
struct DPD0 { |
433 |
|
|
uint32 DnNextPtr; |
434 |
|
|
uint32 FrameStartHeader; |
435 |
|
|
/* DPDFragDesc Frags[n] */ |
436 |
|
|
}; |
437 |
|
|
|
438 |
|
|
enum FrameStartHeaderBits { |
439 |
|
|
FSH_rndupBndry = 3<<0, |
440 |
|
|
FSH_pktId = 15<<2, |
441 |
|
|
/* 12:10 unspecified */ |
442 |
|
|
FSH_crcAppendDisable = 1<<13, |
443 |
|
|
FSH_txIndicate = 1<<15, |
444 |
|
|
FSH_dnComplete = 1<<16, |
445 |
|
|
FSH_reArmDisable = 1<<23, |
446 |
|
|
FSH_lastKap = 1<<24, |
447 |
|
|
FSH_addIpChecksum = 1<<25, |
448 |
|
|
FSH_addTcpChecksum = 1<<26, |
449 |
|
|
FSH_addUdpChecksum = 1<<27, |
450 |
|
|
FSH_rndupDefeat = 1<<28, |
451 |
|
|
FSH_dpdEmpty = 1<<29, |
452 |
|
|
/* 30 unspecified */ |
453 |
|
|
FSH_dnIndicate = 1<<31 |
454 |
|
|
}; |
455 |
|
|
|
456 |
|
|
// must be on 16-byte physical address boundary |
457 |
|
|
struct DPD1 { |
458 |
|
|
uint32 DnNextPtr; |
459 |
|
|
uint32 ScheduleTime; |
460 |
|
|
uint32 FrameStartHeader; |
461 |
|
|
uint32 res; |
462 |
|
|
/* DPDFragDesc Frags[n] */ |
463 |
|
|
}; |
464 |
|
|
|
465 |
|
|
struct DPDFragDesc { |
466 |
|
|
uint32 DnFragAddr; |
467 |
|
|
uint32 DnFragLen; // [12:0] fragLen, [31] lastFrag |
468 |
|
|
} PACKED; |
469 |
|
|
|
470 |
|
|
// must be on 8-byte physical address boundary |
471 |
|
|
struct UPD { |
472 |
|
|
uint32 UpNextPtr; |
473 |
|
|
uint32 UpPktStatus; |
474 |
|
|
/* UPDFragDesc Frags[n] */ |
475 |
|
|
}; |
476 |
|
|
|
477 |
|
|
struct UPDFragDesc { |
478 |
|
|
uint32 UpFragAddr; |
479 |
|
|
uint32 UpFragLen; // [12:0] fragLen, [31] lastFrag |
480 |
|
|
} PACKED; |
481 |
|
|
|
482 |
|
|
#define MAX_DPD_FRAGS 63 |
483 |
|
|
#define MAX_UPD_FRAGS 63 |
484 |
|
|
#define MAX_UPD_SIZE (sizeof(UPD) + sizeof(UPDFragDesc)*MAX_UPD_FRAGS) // 512 |
485 |
|
|
|
486 |
|
|
enum UpPktStatusBits { |
487 |
|
|
UPS_upPktLen = 0x1fff, |
488 |
|
|
/* 13 unspecified */ |
489 |
|
|
UPS_upError = 1<<14, |
490 |
|
|
UPS_upComplete = 1<<15, |
491 |
|
|
UPS_upOverrun = 1<<16, |
492 |
|
|
UPS_runtFrame = 1<<17, |
493 |
|
|
UPS_alignmentError = 1<<18, |
494 |
|
|
UPS_crcError = 1<<19, |
495 |
|
|
UPS_oversizedFrame = 1<<20, |
496 |
|
|
/* 22:21 unspecified */ |
497 |
|
|
UPS_dribbleBits = 1<<23, |
498 |
|
|
UPS_upOverflow = 1<<24, |
499 |
|
|
UPS_ipChecksumError = 1<<25, |
500 |
|
|
UPS_tcpChecksumError = 1<<26, |
501 |
|
|
UPS_udpChecksumError = 1<<27, |
502 |
|
|
UPD_impliedBufferEnable = 1<<28, |
503 |
|
|
UPS_ipChecksumChecked = 1<<29, |
504 |
|
|
UPS_tcpChecksumChecked = 1<<30, |
505 |
|
|
UPS_udpChecksumChecked = 1<<31 |
506 |
|
|
}; |
507 |
|
|
|
508 |
|
|
// IEEE 802.3 MAC, Ethernet-II |
509 |
|
|
struct EthFrameII { |
510 |
|
|
byte destMAC[6]; |
511 |
|
|
byte srcMAC[6]; |
512 |
|
|
byte type[2]; |
513 |
|
|
} PACKED; |
514 |
|
|
|
515 |
|
|
/* |
516 |
|
|
* misc |
517 |
|
|
*/ |
518 |
|
|
static int compareMACs(byte a[6], byte b[6]) |
519 |
|
|
{ |
520 |
|
|
for (uint i = 0; i < 6; i++) { |
521 |
|
|
if (a[i] != b[i]) return a[i] - b[i]; |
522 |
|
|
} |
523 |
|
|
return 0; |
524 |
|
|
} |
525 |
|
|
|
526 |
|
|
/* |
527 |
|
|
* |
528 |
|
|
*/ |
529 |
|
|
class _3c90x_NIC: public PCI_Device { |
530 |
|
|
protected: |
531 |
|
|
uint16 mEEPROM[0x40]; |
532 |
|
|
bool mEEPROMWritable; |
533 |
|
|
Registers mRegisters; |
534 |
|
|
RegWindow mWindows[8]; |
535 |
|
|
uint16 mIntStatus; |
536 |
|
|
bool mRxEnabled; |
537 |
|
|
bool mTxEnabled; |
538 |
|
|
bool mUpStalled; |
539 |
|
|
bool mDnStalled; |
540 |
|
|
byte mRxPacket[MAX_PACKET_SIZE]; |
541 |
|
|
uint mRxPacketSize; |
542 |
|
|
EthTunDevice * mEthTun; |
543 |
|
|
sys_mutex mLock; |
544 |
|
|
|
545 |
|
|
union { |
546 |
|
|
MIIRegisters s; |
547 |
|
|
uint16 reg[8]; |
548 |
|
|
} mMIIRegs; |
549 |
|
|
|
550 |
|
|
uint32 mMIIReadWord; |
551 |
|
|
uint64 mMIIWriteWord; |
552 |
|
|
uint mMIIWrittenBits; |
553 |
|
|
uint16 mLastHiClkPhysMgmt; |
554 |
|
|
byte mMAC[6]; |
555 |
|
|
|
556 |
|
|
void PCIReset() |
557 |
|
|
{ |
558 |
|
|
// PCI config |
559 |
|
|
memset(mConfig, 0, sizeof mConfig); |
560 |
|
|
// 0-3 set by totalReset() |
561 |
|
|
// mConfig[0x04] = 0x07; // io+memory+master |
562 |
|
|
|
563 |
|
|
mConfig[0x08] = 0x00; // revision |
564 |
|
|
mConfig[0x09] = 0x00; // |
565 |
|
|
mConfig[0x0a] = 0x00; // ClassCode 0x20000: Ethernet network controller |
566 |
|
|
mConfig[0x0b] = 0x02; // |
567 |
|
|
|
568 |
|
|
mConfig[0x0e] = 0x00; // header-type (single-function PCI device) |
569 |
|
|
|
570 |
|
|
mConfig[0x3c] = IO_PIC_IRQ_ETHERNET0; // interrupt line |
571 |
|
|
mConfig[0x3d] = 1; // interrupt pin (default is 1) |
572 |
|
|
mConfig[0x3e] = 5; // MinGnt (default is 5 = 0x05 = 0101b) |
573 |
|
|
mConfig[0x3f] = 48; // MaxLat (default is 48 = 0x30 = 110000b) |
574 |
|
|
|
575 |
|
|
mConfig[0x34] = 0xdc; |
576 |
|
|
|
577 |
|
|
mIORegSize[0] = 0x100; |
578 |
|
|
mIORegType[0] = PCI_ADDRESS_SPACE_IO; |
579 |
|
|
assignIOPort(0, 0x1000); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
void totalReset() |
583 |
|
|
{ |
584 |
|
|
/* FIXME: resetting can be done more fine-grained (see TotalReset cmd). |
585 |
|
|
* this is reset ALL regs. |
586 |
|
|
*/ |
587 |
|
|
if (sizeof (Registers) != 0x70) { |
588 |
|
|
IO_3C90X_ERR("sizeof Registers = %08x/%d\n", sizeof (Registers), sizeof (Registers)); |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
RegWindow3 &w3 = (RegWindow3&)mWindows[3]; |
592 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
593 |
|
|
|
594 |
|
|
// internals |
595 |
|
|
mEEPROMWritable = false; |
596 |
|
|
memset(&mWindows, 0, sizeof mWindows); |
597 |
|
|
memset(&mRegisters, 0, sizeof mRegisters); |
598 |
|
|
mIntStatus = 0; |
599 |
|
|
mRxEnabled = false; |
600 |
|
|
mTxEnabled = false; |
601 |
|
|
mUpStalled = false; |
602 |
|
|
mDnStalled = false; |
603 |
|
|
w3.MaxPktSize = 1514 /* FIXME: should depend on sizeof mRxPacket*/; |
604 |
|
|
w3.RxFree = 16*1024; |
605 |
|
|
w3.TxFree = 16*1024; |
606 |
|
|
mRxPacketSize = 0; |
607 |
|
|
w5.TxStartThresh = 8188; |
608 |
|
|
memset(mEEPROM, 0, sizeof mEEPROM); |
609 |
|
|
mEEPROM[EEPROM_NodeAddress0] = (mMAC[0]<<8) | mMAC[1]; |
610 |
|
|
mEEPROM[EEPROM_NodeAddress1] = (mMAC[2]<<8) | mMAC[3]; |
611 |
|
|
mEEPROM[EEPROM_NodeAddress2] = (mMAC[4]<<8) | mMAC[5]; |
612 |
|
|
mEEPROM[EEPROM_DeviceID] = 0x9200; |
613 |
|
|
mEEPROM[EEPROM_ManifacturerID] = 0x6d50; |
614 |
|
|
mEEPROM[EEPROM_PCIParam] = 0x2940; |
615 |
|
|
mEEPROM[EEPROM_RomInfo] = 0; // no ROM |
616 |
|
|
mEEPROM[EEPROM_OEMNodeAddress0] = mEEPROM[EEPROM_NodeAddress0]; |
617 |
|
|
mEEPROM[EEPROM_OEMNodeAddress1] = mEEPROM[EEPROM_NodeAddress1]; |
618 |
|
|
mEEPROM[EEPROM_OEMNodeAddress2] = mEEPROM[EEPROM_NodeAddress2]; |
619 |
|
|
mEEPROM[EEPROM_SoftwareInfo] = 0x4010; |
620 |
|
|
mEEPROM[EEPROM_CompWord] = 0; |
621 |
|
|
mEEPROM[EEPROM_SoftwareInfo2] = 0x00aa; |
622 |
|
|
mEEPROM[EEPROM_Caps] = 0x72a2; |
623 |
|
|
mEEPROM[EEPROM_InternalConfig0] = 0; |
624 |
|
|
mEEPROM[EEPROM_InternalConfig1] = 0x0050; // default is 0x0180 |
625 |
|
|
mEEPROM[EEPROM_SubsystemVendorID] = 0x10b7; |
626 |
|
|
mEEPROM[EEPROM_SubsystemID] = 0x9200; |
627 |
|
|
mEEPROM[EEPROM_MediaOptions] = 0x000a; |
628 |
|
|
mEEPROM[EEPROM_SmbAddress] = 0x6300; |
629 |
|
|
mEEPROM[EEPROM_PCIParam2] = 0xffb7; |
630 |
|
|
mEEPROM[EEPROM_PCIParam3] = 0xb7b7; |
631 |
|
|
mEEPROM[EEPROM_Checksum] = 0; |
632 |
|
|
|
633 |
|
|
// MII |
634 |
|
|
memset(&mMIIRegs, 0, sizeof mMIIRegs); |
635 |
|
|
mMIIRegs.s.status = (1<<14) | (1<<13) | (1<<12) | (1<<11) | (1<<5) | (1<<3) | (1<<2) | 1; |
636 |
|
|
mMIIRegs.s.linkPartner = (1<<14) | (1<<7) | 1; |
637 |
|
|
mMIIRegs.s.advert = (1<<14) | (1 << 10) | (1<<7) | 1; |
638 |
|
|
mMIIReadWord = 0; |
639 |
|
|
mMIIWriteWord = 0; |
640 |
|
|
mMIIWrittenBits = 0; |
641 |
|
|
mLastHiClkPhysMgmt = 0; |
642 |
|
|
|
643 |
|
|
// Register follow-ups |
644 |
|
|
w3.MediaOptions = mEEPROM[EEPROM_MediaOptions]; |
645 |
|
|
w3.InternalConfig = mEEPROM[EEPROM_InternalConfig0] | |
646 |
|
|
(mEEPROM[EEPROM_InternalConfig1] << 16); |
647 |
|
|
|
648 |
|
|
// PCI config follow-ups |
649 |
|
|
mConfig[0x00] = mEEPROM[EEPROM_SubsystemVendorID] & 0xff; // vendor ID |
650 |
|
|
mConfig[0x01] = mEEPROM[EEPROM_SubsystemVendorID] >> 8; |
651 |
|
|
mConfig[0x02] = mEEPROM[EEPROM_DeviceID] & 0xff; // unit ID |
652 |
|
|
mConfig[0x03] = mEEPROM[EEPROM_DeviceID] >> 8; |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
void readRegWindow(uint window, uint32 port, uint32 &data, uint size) |
656 |
|
|
{ |
657 |
|
|
IO_3C90X_TRACE("readRegWindow(%d, %08x, %08x)\n", window, port, size); |
658 |
|
|
switch (window) { |
659 |
|
|
/* window 0 */ |
660 |
|
|
case 0: { |
661 |
|
|
RegWindow0 &w0 = (RegWindow0&)mWindows[0]; |
662 |
|
|
switch (port) { |
663 |
|
|
case W0_EEPROMCmd: { |
664 |
|
|
if (size != 2) { |
665 |
|
|
IO_3C90X_WARN("EepromCommand, size != 2\n"); |
666 |
|
|
SINGLESTEP(""); |
667 |
|
|
} |
668 |
|
|
data = w0.EepromCommand; |
669 |
|
|
break; |
670 |
|
|
} |
671 |
|
|
case W0_EEPROMData: { |
672 |
|
|
if (size != 2) { |
673 |
|
|
IO_3C90X_WARN("EepromData, size != 2\n"); |
674 |
|
|
SINGLESTEP(""); |
675 |
|
|
} |
676 |
|
|
data = w0.EepromData; |
677 |
|
|
break; |
678 |
|
|
} |
679 |
|
|
default: |
680 |
|
|
IO_3C90X_WARN("reading here unimpl.0\n"); |
681 |
|
|
SINGLESTEP(""); |
682 |
|
|
break; |
683 |
|
|
} |
684 |
|
|
break; |
685 |
|
|
} |
686 |
|
|
/* window 1 */ |
687 |
|
|
case 1: { |
688 |
|
|
data = 0; |
689 |
|
|
//RegWindow1 &w1 = (RegWindow1&)mWindows[1]; |
690 |
|
|
memcpy(&data, &mWindows[1].b[port], size); |
691 |
|
|
break; |
692 |
|
|
} |
693 |
|
|
/* window 2 */ |
694 |
|
|
case 2: { |
695 |
|
|
data = 0; |
696 |
|
|
//RegWindow2 &w2 = (RegWindow2&)mWindows[2]; |
697 |
|
|
memcpy(&data, &mWindows[2].b[port], size); |
698 |
|
|
break; |
699 |
|
|
} |
700 |
|
|
/* window 3 */ |
701 |
|
|
case 3: { |
702 |
|
|
data = 0; |
703 |
|
|
//RegWindow3 &w3 = (RegWindow3&)mWindows[3]; |
704 |
|
|
memcpy(&data, &mWindows[3].b[port], size); |
705 |
|
|
break; |
706 |
|
|
} |
707 |
|
|
/* window 4 */ |
708 |
|
|
case 4: { |
709 |
|
|
RegWindow4 &w4 = (RegWindow4&)mWindows[4]; |
710 |
|
|
data = 0; |
711 |
|
|
switch (port) { |
712 |
|
|
case 8: { |
713 |
|
|
// MII-interface |
714 |
|
|
if (size != 2) { |
715 |
|
|
IO_3C90X_WARN("alignment.4.8.read\n"); |
716 |
|
|
SINGLESTEP(""); |
717 |
|
|
} |
718 |
|
|
bool mgmtData = mMIIReadWord & 0x80000000; |
719 |
|
|
// IO_3C90X_TRACE("Read cycle mgmtData=%d\n", mgmtData ? 1 : 0); |
720 |
|
|
if (mgmtData) { |
721 |
|
|
data = w4.PhysMgmt | PM_mgmtData; |
722 |
|
|
} else { |
723 |
|
|
data = w4.PhysMgmt & (~PM_mgmtData); |
724 |
|
|
} |
725 |
|
|
/* IO_3C90X_TRACE("read PhysMgmt = %04x (mgmtData = %d)\n", |
726 |
|
|
data, mgmtData ? 1 : 0);*/ |
727 |
|
|
break; |
728 |
|
|
} |
729 |
|
|
case 0xc: { |
730 |
|
|
if (size != 1) { |
731 |
|
|
IO_3C90X_WARN("alignment.4.c.read\n"); |
732 |
|
|
} |
733 |
|
|
// reading clears |
734 |
|
|
w4.BadSSD = 0; |
735 |
|
|
memcpy(&data, &mWindows[4].b[port], size); |
736 |
|
|
break; |
737 |
|
|
} |
738 |
|
|
default: |
739 |
|
|
memcpy(&data, &mWindows[4].b[port], size); |
740 |
|
|
} |
741 |
|
|
break; |
742 |
|
|
} |
743 |
|
|
/* Window 5 */ |
744 |
|
|
case 5: { |
745 |
|
|
data = 0; |
746 |
|
|
//RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
747 |
|
|
memcpy(&data, &mWindows[5].b[port], size); |
748 |
|
|
break; |
749 |
|
|
} |
750 |
|
|
/* Window 6 */ |
751 |
|
|
case 6: { |
752 |
|
|
RegWindow6 &w6 = (RegWindow6&)mWindows[6]; |
753 |
|
|
// reading clears |
754 |
|
|
if ((port == 0xa) && (size == 2)) { |
755 |
|
|
// FIXME: BytesRcvdOk really is 20 bits ! |
756 |
|
|
// when reading here, write upper 4 bits |
757 |
|
|
// in w4.UpperBytesOk[3:0]. no clearing. |
758 |
|
|
w6.BytesRcvdOk = 0; |
759 |
|
|
} else if ((port == 0xc) && (size == 2)) { |
760 |
|
|
// FIXME: BytesXmittedOk really is 20 bits ! |
761 |
|
|
// when reading here, write upper 4 bits |
762 |
|
|
// in w4.UpperBytesOk[7:4]. no clearing. |
763 |
|
|
w6.BytesXmittedOk = 0; |
764 |
|
|
} else if ((port == 0) && (size == 1)) { |
765 |
|
|
w6.CarrierLost = 0; |
766 |
|
|
} else if ((port == 8) && (size == 1)) { |
767 |
|
|
w6.FramesDeferred = 0; |
768 |
|
|
} else if ((port == 7) && (size == 1)) { |
769 |
|
|
// FIXME: FramesRcvdOk really is 10 bits ! |
770 |
|
|
// when reading here, write upper 2 bits |
771 |
|
|
// in w6.UpperFramesOk[1:0]. no clearing. |
772 |
|
|
} else if ((port == 6) && (size == 1)) { |
773 |
|
|
// FIXME: FramesXmittedOk really is 10 bits ! |
774 |
|
|
// when reading here, write upper 2 bits |
775 |
|
|
// in w6.UpperFramesOk[5:4]. no clearing. |
776 |
|
|
} else if ((port == 4) && (size == 1)) { |
777 |
|
|
w6.LateCollisions = 0; |
778 |
|
|
} else if ((port == 2) && (size == 1)) { |
779 |
|
|
w6.MultipleCollisions = 0; |
780 |
|
|
} else if ((port == 5) && (size == 1)) { |
781 |
|
|
w6.RxOverruns = 0; |
782 |
|
|
} else if ((port == 3) && (size == 1)) { |
783 |
|
|
w6.SingleCollisions = 0; |
784 |
|
|
} else if ((port == 1) && (size == 1)) { |
785 |
|
|
w6.SqeErrors = 0; |
786 |
|
|
} |
787 |
|
|
data = 0; |
788 |
|
|
memcpy(&data, &mWindows[6].b[port], size); |
789 |
|
|
break; |
790 |
|
|
} |
791 |
|
|
/* Window 7 */ |
792 |
|
|
case 7: { |
793 |
|
|
data = 0; |
794 |
|
|
//RegWindow7 &w7 = (RegWindow7&)mWindows[7]; |
795 |
|
|
memcpy(&data, &mWindows[7].b[port], size); |
796 |
|
|
break; |
797 |
|
|
} |
798 |
|
|
default: |
799 |
|
|
IO_3C90X_WARN("reading here unimpl.\n"); |
800 |
|
|
SINGLESTEP(""); |
801 |
|
|
} |
802 |
|
|
IO_3C90X_TRACE("= %04x\n", data); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
void writeRegWindow(uint window, uint32 port, uint32 data, uint size) |
806 |
|
|
{ |
807 |
|
|
IO_3C90X_TRACE("writeRegWindow(%d, %08x, %08x, %08x)\n", window, port, data, size); |
808 |
|
|
switch (window) { |
809 |
|
|
/* Window 0 */ |
810 |
|
|
case 0: { |
811 |
|
|
RegWindow0 &w0 = (RegWindow0&)mWindows[0]; |
812 |
|
|
switch (port) { |
813 |
|
|
case W0_EEPROMCmd: { |
814 |
|
|
if (size != 2) { |
815 |
|
|
IO_3C90X_WARN("EepromCommand, size != 2\n"); |
816 |
|
|
SINGLESTEP(""); |
817 |
|
|
} |
818 |
|
|
w0.EepromCommand = data & 0xff7f; // clear eepromBusy |
819 |
|
|
uint eeprom_addr = ((data >> 2) & 0xffc0) | (data & 0x3f); |
820 |
|
|
switch (data & 0xc0) { |
821 |
|
|
case EEOP_SubCmd: |
822 |
|
|
switch (data & 0x30) { |
823 |
|
|
case EESC_WriteDisable: |
824 |
|
|
IO_3C90X_TRACE("EESC_WriteDisable\n"); |
825 |
|
|
mEEPROMWritable = false; |
826 |
|
|
break; |
827 |
|
|
case EESC_WriteAll: |
828 |
|
|
IO_3C90X_WARN("WriteAll not impl.\n"); |
829 |
|
|
SINGLESTEP(""); |
830 |
|
|
memset(mEEPROM, 0xff, sizeof mEEPROM); |
831 |
|
|
mEEPROMWritable = false; |
832 |
|
|
break; |
833 |
|
|
case EESC_EraseAll: |
834 |
|
|
IO_3C90X_WARN("EraseAll not impl.\n"); |
835 |
|
|
SINGLESTEP(""); |
836 |
|
|
memset(mEEPROM, 0, sizeof mEEPROM); |
837 |
|
|
mEEPROMWritable = false; |
838 |
|
|
break; |
839 |
|
|
case EESC_WriteEnable: |
840 |
|
|
IO_3C90X_TRACE("EESC_WriteEnable\n"); |
841 |
|
|
mEEPROMWritable = true; |
842 |
|
|
break; |
843 |
|
|
default: |
844 |
|
|
IO_3C90X_WARN("impossible\n"); |
845 |
|
|
SINGLESTEP(""); |
846 |
|
|
} |
847 |
|
|
break; |
848 |
|
|
case EEOP_WriteReg: |
849 |
|
|
if (mEEPROMWritable) { |
850 |
|
|
if (eeprom_addr*2 < sizeof mEEPROM) { |
851 |
|
|
// disabled |
852 |
|
|
IO_3C90X_WARN("EEOP_WriteReg(addr = %04x, %04x) oldvalue = %04x\n", eeprom_addr, w0.EepromData, mEEPROM[eeprom_addr]); |
853 |
|
|
SINGLESTEP(""); |
854 |
|
|
mEEPROM[eeprom_addr] = w0.EepromData; |
855 |
|
|
} else { |
856 |
|
|
IO_3C90X_WARN("FAILED(out of bounds): EEOP_WriteReg(addr = %04x, %04x) oldvalue = %04x\n", eeprom_addr, w0.EepromData, mEEPROM[eeprom_addr]); |
857 |
|
|
SINGLESTEP(""); |
858 |
|
|
} |
859 |
|
|
mEEPROMWritable = false; |
860 |
|
|
} else { |
861 |
|
|
IO_3C90X_WARN("FAILED(not writable): EEOP_WriteReg(addr = %04x, %04x) oldvalue = %04x\n", eeprom_addr, w0.EepromData, mEEPROM[eeprom_addr]); |
862 |
|
|
SINGLESTEP(""); |
863 |
|
|
} |
864 |
|
|
break; |
865 |
|
|
case EEOP_ReadReg: |
866 |
|
|
if (eeprom_addr*2 < sizeof mEEPROM) { |
867 |
|
|
w0.EepromData = mEEPROM[eeprom_addr]; |
868 |
|
|
IO_3C90X_TRACE("EEOP_ReadReg(addr = %04x) = %04x\n", eeprom_addr, w0.EepromData); |
869 |
|
|
} else { |
870 |
|
|
IO_3C90X_WARN("FAILED(out of bounds): EEOP_ReadReg(addr = %04x)\n", eeprom_addr); |
871 |
|
|
SINGLESTEP(""); |
872 |
|
|
} |
873 |
|
|
break; |
874 |
|
|
case EEOP_EraseReg: |
875 |
|
|
if (mEEPROMWritable) { |
876 |
|
|
if (eeprom_addr*2 < sizeof mEEPROM) { |
877 |
|
|
// disabled |
878 |
|
|
IO_3C90X_WARN("EEOP_EraseReg(addr = %04x) oldvalue = %04x\n", eeprom_addr, mEEPROM[eeprom_addr]); |
879 |
|
|
SINGLESTEP(""); |
880 |
|
|
mEEPROM[eeprom_addr] = 0; |
881 |
|
|
} else { |
882 |
|
|
IO_3C90X_WARN("FAILED(out of bounds): EEOP_EraseReg(addr = %04x) oldvalue = %04x\n", eeprom_addr, mEEPROM[eeprom_addr]); |
883 |
|
|
SINGLESTEP(""); |
884 |
|
|
} |
885 |
|
|
mEEPROMWritable = false; |
886 |
|
|
} else { |
887 |
|
|
IO_3C90X_WARN("FAILED(not writable): EEOP_EraseReg(addr = %04x) oldvalue = %04x\n", eeprom_addr, mEEPROM[eeprom_addr]); |
888 |
|
|
SINGLESTEP(""); |
889 |
|
|
} |
890 |
|
|
break; |
891 |
|
|
default: |
892 |
|
|
IO_3C90X_WARN("impossible\n"); |
893 |
|
|
SINGLESTEP(""); |
894 |
|
|
} |
895 |
|
|
break; |
896 |
|
|
} |
897 |
|
|
case W0_EEPROMData: |
898 |
|
|
if (size != 2) { |
899 |
|
|
IO_3C90X_WARN("EepromData, size != 2\n"); |
900 |
|
|
SINGLESTEP(""); |
901 |
|
|
} |
902 |
|
|
w0.EepromData = data; |
903 |
|
|
break; |
904 |
|
|
default: |
905 |
|
|
IO_3C90X_WARN("writing here unimpl.0\n"); |
906 |
|
|
SINGLESTEP(""); |
907 |
|
|
break; |
908 |
|
|
} |
909 |
|
|
break; |
910 |
|
|
} |
911 |
|
|
/* Window 2 */ |
912 |
|
|
case 2: { |
913 |
|
|
// RegWindow2 &w2 = (RegWindow2&)mWindows[2]; |
914 |
|
|
if (port+size<=0xc) { |
915 |
|
|
IO_3C90X_TRACE("StationAddress or StationMask\n"); |
916 |
|
|
/* StationAddress or StationMask */ |
917 |
|
|
memcpy(&mWindows[2].b[port], &data, size); |
918 |
|
|
} else { |
919 |
|
|
IO_3C90X_WARN("writing here unimpl.2\n"); |
920 |
|
|
SINGLESTEP(""); |
921 |
|
|
} |
922 |
|
|
break; |
923 |
|
|
} |
924 |
|
|
/* Window 3 */ |
925 |
|
|
case 3: { |
926 |
|
|
/* uint32 InternalConfig PACKED; |
927 |
|
|
uint16 MaxPktSize PACKED; |
928 |
|
|
uint16 MacControl PACKED; |
929 |
|
|
uint16 MediaOptions PACKED; |
930 |
|
|
uint16 RxFree PACKED; |
931 |
|
|
uint16 TxFree PACKED;*/ |
932 |
|
|
RegWindow3 &w3 = (RegWindow3&)mWindows[3]; |
933 |
|
|
switch (port) { |
934 |
|
|
case 0: |
935 |
|
|
if (size != 4) { |
936 |
|
|
IO_3C90X_WARN("alignment.3.0\n"); |
937 |
|
|
SINGLESTEP(""); |
938 |
|
|
} |
939 |
|
|
IO_3C90X_TRACE("InternalConfig\n"); |
940 |
|
|
w3.InternalConfig = data; |
941 |
|
|
break; |
942 |
|
|
case 4: |
943 |
|
|
if (size != 2) { |
944 |
|
|
IO_3C90X_WARN("alignment.3.4\n"); |
945 |
|
|
SINGLESTEP(""); |
946 |
|
|
} |
947 |
|
|
IO_3C90X_ERR("MaxPktSize\n"); |
948 |
|
|
w3.MaxPktSize = data; |
949 |
|
|
break; |
950 |
|
|
case 6: |
951 |
|
|
if (size != 2) { |
952 |
|
|
IO_3C90X_WARN("alignment.3.6\n"); |
953 |
|
|
SINGLESTEP(""); |
954 |
|
|
} |
955 |
|
|
IO_3C90X_TRACE("MacControl\n"); |
956 |
|
|
if (data != 0) { |
957 |
|
|
IO_3C90X_WARN("setting MacControl != 0\n"); |
958 |
|
|
SINGLESTEP(""); |
959 |
|
|
} |
960 |
|
|
w3.MacControl = data; |
961 |
|
|
break; |
962 |
|
|
case 8: |
963 |
|
|
if (size != 2) { |
964 |
|
|
IO_3C90X_WARN("alignment.3.8\n"); |
965 |
|
|
SINGLESTEP(""); |
966 |
|
|
} |
967 |
|
|
IO_3C90X_TRACE("MediaOptions\n"); |
968 |
|
|
w3.MediaOptions = data; |
969 |
|
|
break; |
970 |
|
|
case 10: |
971 |
|
|
if (size != 2) { |
972 |
|
|
IO_3C90X_WARN("alignment.3.10\n"); |
973 |
|
|
SINGLESTEP(""); |
974 |
|
|
} |
975 |
|
|
IO_3C90X_WARN("RxFree\n"); |
976 |
|
|
SINGLESTEP(""); |
977 |
|
|
w3.RxFree = data; |
978 |
|
|
break; |
979 |
|
|
case 12: |
980 |
|
|
if (size != 2) { |
981 |
|
|
IO_3C90X_WARN("alignment.3.12\n"); |
982 |
|
|
SINGLESTEP(""); |
983 |
|
|
} |
984 |
|
|
IO_3C90X_WARN("TxFree\n"); |
985 |
|
|
SINGLESTEP(""); |
986 |
|
|
w3.TxFree = data; |
987 |
|
|
break; |
988 |
|
|
default: |
989 |
|
|
IO_3C90X_WARN("writing here unimpl.3\n"); |
990 |
|
|
SINGLESTEP(""); |
991 |
|
|
} |
992 |
|
|
break; |
993 |
|
|
} |
994 |
|
|
/* Window 4 */ |
995 |
|
|
case 4: { |
996 |
|
|
RegWindow4 &w4 = (RegWindow4&)mWindows[4]; |
997 |
|
|
switch (port) { |
998 |
|
|
case 6: { |
999 |
|
|
if (size != 2) { |
1000 |
|
|
IO_3C90X_WARN("alignment.4.6\n"); |
1001 |
|
|
SINGLESTEP(""); |
1002 |
|
|
} |
1003 |
|
|
uint mask = 0xf341; |
1004 |
|
|
IO_3C90X_TRACE("NetDiagnostic = %04x, old = %04x\n", ((w4.NetDiagnostic)&~mask)|(data&mask), w4.NetDiagnostic); |
1005 |
|
|
w4.NetDiagnostic &= ~mask; |
1006 |
|
|
w4.NetDiagnostic |= data & mask; |
1007 |
|
|
break; |
1008 |
|
|
} |
1009 |
|
|
case 8: { |
1010 |
|
|
// MII-interface |
1011 |
|
|
if (size != 2) { |
1012 |
|
|
IO_3C90X_WARN("alignment.4.8\n"); |
1013 |
|
|
SINGLESTEP(""); |
1014 |
|
|
} |
1015 |
|
|
/* IO_3C90X_TRACE("PhysMgmt = %04x (clk=%d, data=%d, dir=%d), old = %04x\n", |
1016 |
|
|
data, (data & PM_mgmtClk) ? 1 : 0, (data & PM_mgmtData) ? 1 : 0, |
1017 |
|
|
(data & PM_mgmtDir) ? 1 : 0, w4.PhysMgmt);*/ |
1018 |
|
|
bool hiClk = !(w4.PhysMgmt & PM_mgmtClk) && (data & PM_mgmtClk); |
1019 |
|
|
if (hiClk) { |
1020 |
|
|
// Z means lo edge of mgmtDir |
1021 |
|
|
bool Z = (mLastHiClkPhysMgmt & PM_mgmtDir) && !(data & PM_mgmtDir); |
1022 |
|
|
// IO_3C90X_TRACE("hi-edge, Z=%d\n", Z ? 1 : 0); |
1023 |
|
|
if (Z) { |
1024 |
|
|
// IO_3C90X_TRACE("Z-cycle, %016qx, written bits=%d\n", &mMIIWriteWord, mMIIWrittenBits); |
1025 |
|
|
// check if the 5 frames have been sent |
1026 |
|
|
if (((mMIIWriteWord >> (mMIIWrittenBits-32-2)) & 0x3ffffffffULL) == 0x3fffffffdULL) { |
1027 |
|
|
uint opcode = (mMIIWriteWord >> (mMIIWrittenBits-32-2-2)) & 3; |
1028 |
|
|
uint PHYaddr = (mMIIWriteWord >> (mMIIWrittenBits-32-2-2-5)) & 0x1f; |
1029 |
|
|
uint REGaddr = (mMIIWriteWord >> (mMIIWrittenBits-32-2-2-5-5)) & 0x1f; |
1030 |
|
|
// IO_3C90X_TRACE("prefixed Z-cycle, opcode=%d, PHY=%02x, REG=%02x\n", opcode, PHYaddr, REGaddr); |
1031 |
|
|
if ((PHYaddr == 0x18 /* hardcoded address [1] p.196 */) |
1032 |
|
|
&& (REGaddr < 0x10)) { |
1033 |
|
|
switch (opcode) { |
1034 |
|
|
case 1: { |
1035 |
|
|
// Opcode Write |
1036 |
|
|
IO_3C90X_TRACE("Opcode Write\n"); |
1037 |
|
|
if (mMIIWrittenBits == 64) { |
1038 |
|
|
uint32 value = mMIIWriteWord & 0xffff; |
1039 |
|
|
// IO_3C90X_TRACE("NOT writing 0x%04x to register. feature disabled. (old = 0x%04x)\n", value, mMIIRegs[REGaddr]); |
1040 |
|
|
IO_3C90X_TRACE("Writing 0x%04x to MII register %d (old = 0x%04x)\n", value, REGaddr, mMIIRegs.reg[REGaddr]); |
1041 |
|
|
mMIIRegs.reg[REGaddr] = value; |
1042 |
|
|
} else { |
1043 |
|
|
IO_3C90X_TRACE("But invalid write count=%d\n", mMIIWrittenBits); |
1044 |
|
|
} |
1045 |
|
|
mMIIWriteWord = 0; |
1046 |
|
|
break; |
1047 |
|
|
} |
1048 |
|
|
case 2: { |
1049 |
|
|
// Opcode Read |
1050 |
|
|
IO_3C90X_TRACE("Opcode Read\n"); |
1051 |
|
|
if (mMIIWrittenBits == 32+2+2+5+5) { |
1052 |
|
|
// msb gets sent first and is zero to indicated success |
1053 |
|
|
// the register to be sent follows msb to lsb |
1054 |
|
|
mMIIReadWord = mMIIRegs.reg[REGaddr] << 15; |
1055 |
|
|
IO_3C90X_TRACE("Read 0x%04x from register %d\n", mMIIRegs.reg[REGaddr], REGaddr); |
1056 |
|
|
} else { |
1057 |
|
|
IO_3C90X_TRACE("But invalid write count=%d\n", mMIIWrittenBits); |
1058 |
|
|
} |
1059 |
|
|
mMIIWriteWord = 0; |
1060 |
|
|
break; |
1061 |
|
|
} |
1062 |
|
|
default: |
1063 |
|
|
// error |
1064 |
|
|
IO_3C90X_TRACE("Invalid opcode %d\n", (mMIIWriteWord >> 10) & 3); |
1065 |
|
|
mMIIReadWord = 0xffffffff; |
1066 |
|
|
} |
1067 |
|
|
} else { |
1068 |
|
|
// error |
1069 |
|
|
IO_3C90X_TRACE("Invalid PHY or REG\n"); |
1070 |
|
|
mMIIReadWord = 0xffffffff; |
1071 |
|
|
} |
1072 |
|
|
} |
1073 |
|
|
mMIIWrittenBits = 0; |
1074 |
|
|
w4.PhysMgmt = data; |
1075 |
|
|
} else if (data & PM_mgmtDir) { |
1076 |
|
|
// write |
1077 |
|
|
bool mgmtData = data & PM_mgmtData; |
1078 |
|
|
// IO_3C90X_TRACE("Write cycle mgmtData=%d\n", mgmtData ? 1 : 0); |
1079 |
|
|
w4.PhysMgmt = data; |
1080 |
|
|
mMIIWriteWord <<= 1; |
1081 |
|
|
mMIIWriteWord |= mgmtData ? 1 : 0; |
1082 |
|
|
mMIIWrittenBits++; |
1083 |
|
|
} else { |
1084 |
|
|
// read |
1085 |
|
|
bool mgmtData = mMIIReadWord & 0x80000000; |
1086 |
|
|
// IO_3C90X_TRACE("Read cycle mgmtData=%d\n", mgmtData ? 1 : 0); |
1087 |
|
|
w4.PhysMgmt = data; |
1088 |
|
|
if (mgmtData) { |
1089 |
|
|
w4.PhysMgmt = w4.PhysMgmt | PM_mgmtData; |
1090 |
|
|
} else { |
1091 |
|
|
w4.PhysMgmt = w4.PhysMgmt & (~PM_mgmtData); |
1092 |
|
|
} |
1093 |
|
|
mMIIReadWord <<= 1; |
1094 |
|
|
} |
1095 |
|
|
mLastHiClkPhysMgmt = w4.PhysMgmt; |
1096 |
|
|
} else { |
1097 |
|
|
w4.PhysMgmt = data; |
1098 |
|
|
} |
1099 |
|
|
break; |
1100 |
|
|
} |
1101 |
|
|
case 10: { |
1102 |
|
|
if (size != 2) { |
1103 |
|
|
IO_3C90X_WARN("alignment.4.10\n"); |
1104 |
|
|
SINGLESTEP(""); |
1105 |
|
|
} |
1106 |
|
|
uint mask = 0x10cc; |
1107 |
|
|
IO_3C90X_TRACE("MediaStatus = %04x, old = %04x\n", ((w4.MediaStatus)&~mask)|(data&mask), w4.MediaStatus); |
1108 |
|
|
w4.MediaStatus &= ~mask; |
1109 |
|
|
w4.MediaStatus |= data & mask; |
1110 |
|
|
w4.MediaStatus |= 0x8000; // auiDisable always on |
1111 |
|
|
break; |
1112 |
|
|
} |
1113 |
|
|
default: |
1114 |
|
|
IO_3C90X_WARN("generic to window 4\n"); |
1115 |
|
|
SINGLESTEP(""); |
1116 |
|
|
memcpy(&mWindows[4].b[port], &data, size); |
1117 |
|
|
} |
1118 |
|
|
break; |
1119 |
|
|
} |
1120 |
|
|
/**/ |
1121 |
|
|
default: |
1122 |
|
|
IO_3C90X_WARN("writing here unimpl.\n"); |
1123 |
|
|
SINGLESTEP(""); |
1124 |
|
|
} |
1125 |
|
|
} |
1126 |
|
|
|
1127 |
|
|
void setCR(uint16 cr) |
1128 |
|
|
{ |
1129 |
|
|
IO_3C90X_TRACE("setCR(cr = %x)\n", cr); |
1130 |
|
|
switch (cr & (31<<11)) { |
1131 |
|
|
case CmdTotalReset: |
1132 |
|
|
// FIXME: care about params |
1133 |
|
|
IO_3C90X_TRACE("TotalReset\n"); |
1134 |
|
|
totalReset(); |
1135 |
|
|
break; |
1136 |
|
|
case CmdSelectWindow: { |
1137 |
|
|
IO_3C90X_TRACE("SelectWindow (window = %d) oldwindow = %d\n", cr & 7, mIntStatus >> 13); |
1138 |
|
|
mIntStatus &= 0x1fff; |
1139 |
|
|
mIntStatus |= (cr & 7)<<13; |
1140 |
|
|
break; |
1141 |
|
|
} |
1142 |
|
|
case CmdTxReset: |
1143 |
|
|
IO_3C90X_TRACE("TxReset\n"); |
1144 |
|
|
break; |
1145 |
|
|
case CmdRxReset: |
1146 |
|
|
IO_3C90X_TRACE("RxReset\n"); |
1147 |
|
|
break; |
1148 |
|
|
case CmdSetIndicationEnable: { |
1149 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1150 |
|
|
IO_3C90X_TRACE("SetIndicationEnable(%04x) oldvalue = %04x\n", cr & 0x7fe, w5.IndicationEnable); |
1151 |
|
|
w5.IndicationEnable = cr & 0x7fe; |
1152 |
|
|
break; |
1153 |
|
|
} |
1154 |
|
|
case CmdSetIntrEnb: { |
1155 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1156 |
|
|
IO_3C90X_TRACE("SetIntrEnab(%04x) oldvalue = %04x\n", cr & 0x7fe, w5.InterruptEnable); |
1157 |
|
|
w5.InterruptEnable = cr & 0x7fe; |
1158 |
|
|
break; |
1159 |
|
|
} |
1160 |
|
|
case CmdStatsEnable: |
1161 |
|
|
/* implement me */ |
1162 |
|
|
IO_3C90X_TRACE("StatsEnable\n"); |
1163 |
|
|
break; |
1164 |
|
|
case CmdStatsDisable: |
1165 |
|
|
/* implement me */ |
1166 |
|
|
IO_3C90X_TRACE("StatsDisable\n"); |
1167 |
|
|
break; |
1168 |
|
|
case CmdEnableDC: |
1169 |
|
|
/* implement me */ |
1170 |
|
|
IO_3C90X_TRACE("EnableDC\n"); |
1171 |
|
|
break; |
1172 |
|
|
case CmdDisableDC: |
1173 |
|
|
/* implement me */ |
1174 |
|
|
IO_3C90X_TRACE("DisableDC\n"); |
1175 |
|
|
break; |
1176 |
|
|
case CmdStall: { |
1177 |
|
|
/* FIXME: threading */ |
1178 |
|
|
switch (cr & 3) { |
1179 |
|
|
case 0: /* UpStall */ |
1180 |
|
|
case 1: /* UpUnstall */ { |
1181 |
|
|
IO_3C90X_TRACE("Stall(%s)\n", ((cr & 3) == 0) ? "UpStall" : "UpUnstall"); |
1182 |
|
|
bool stall = !(cr & 1); |
1183 |
|
|
mUpStalled = stall; |
1184 |
|
|
/* bool stall = cr & 1; |
1185 |
|
|
mRegisters.DmaCtrl &= ~DC_upStalled; |
1186 |
|
|
if (stall) mRegisters.DmaCtrl |= DC_upStalled;*/ |
1187 |
|
|
checkUpWork(); |
1188 |
|
|
break; |
1189 |
|
|
} |
1190 |
|
|
case 2: /* DnStall */ |
1191 |
|
|
case 3: /* DnUnstall */ { |
1192 |
|
|
IO_3C90X_TRACE("Stall(%s)\n", ((cr & 3) == 2) ? "DnStall" : "DnUnstall"); |
1193 |
|
|
bool stall = !(cr & 1); |
1194 |
|
|
mDnStalled = stall; |
1195 |
|
|
mRegisters.DmaCtrl &= ~DC_dnStalled; |
1196 |
|
|
if (stall) mRegisters.DmaCtrl |= DC_dnStalled; |
1197 |
|
|
checkDnWork(); |
1198 |
|
|
break; |
1199 |
|
|
} |
1200 |
|
|
} |
1201 |
|
|
break; |
1202 |
|
|
} |
1203 |
|
|
case CmdSetRxFilter: { |
1204 |
|
|
IO_3C90X_TRACE("SetRxFilter(%02x)\n", cr & 31); |
1205 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1206 |
|
|
w5.RxFilter = cr & 31; |
1207 |
|
|
break; |
1208 |
|
|
} |
1209 |
|
|
case CmdSetTxReclaimThresh: { |
1210 |
|
|
IO_3C90X_TRACE("SetTxReclaimHash(%02x)\n", cr & 255); |
1211 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1212 |
|
|
w5.TxReclaimThresh = cr & 255; |
1213 |
|
|
break; |
1214 |
|
|
} |
1215 |
|
|
case CmdSetTxStartThresh: { |
1216 |
|
|
IO_3C90X_WARN("SetTxStartTresh(%02x)\n", (cr & 0x7ff) << 2); |
1217 |
|
|
// SINGLESTEP(""); |
1218 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1219 |
|
|
w5.TxStartThresh = (cr & 0x7ff) << 2; |
1220 |
|
|
break; |
1221 |
|
|
} |
1222 |
|
|
case CmdSetHashFilterBit: { |
1223 |
|
|
bool value = cr & 0x400; |
1224 |
|
|
uint which = cr & 0x3f; |
1225 |
|
|
IO_3C90X_WARN("SetHashFilterBit(which=%d, value=%d)\n", which, value ? 1 : 0); |
1226 |
|
|
break; |
1227 |
|
|
} |
1228 |
|
|
case CmdSetRxEarlyThresh: { |
1229 |
|
|
IO_3C90X_TRACE("SetTxStartTresh(%02x)\n", (cr & 0x7ff) << 2); |
1230 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1231 |
|
|
w5.RxEarlyThresh = (cr & 0x7ff) << 2; |
1232 |
|
|
break; |
1233 |
|
|
} |
1234 |
|
|
case CmdRxEnable: { |
1235 |
|
|
IO_3C90X_TRACE("RxEnable\n"); |
1236 |
|
|
mRxEnabled = true; |
1237 |
|
|
break; |
1238 |
|
|
} |
1239 |
|
|
case CmdRxDisable: { |
1240 |
|
|
IO_3C90X_TRACE("ExDisable\n"); |
1241 |
|
|
mRxEnabled = false; |
1242 |
|
|
break; |
1243 |
|
|
} |
1244 |
|
|
case CmdTxEnable: { |
1245 |
|
|
IO_3C90X_TRACE("TxEnable\n"); |
1246 |
|
|
mTxEnabled = true; |
1247 |
|
|
break; |
1248 |
|
|
} |
1249 |
|
|
case CmdTxDisable: { |
1250 |
|
|
IO_3C90X_TRACE("TxDisable\n"); |
1251 |
|
|
mTxEnabled = false; |
1252 |
|
|
break; |
1253 |
|
|
} |
1254 |
|
|
case CmdAckIntr: { |
1255 |
|
|
/* |
1256 |
|
|
0x1 interruptLatchAck |
1257 |
|
|
0x2 linkEventAck |
1258 |
|
|
0x20 rxEarlyAck |
1259 |
|
|
0x40 intRequestedAck |
1260 |
|
|
0x200 dnCompleteAck |
1261 |
|
|
0x400 upCompleteAck |
1262 |
|
|
*/ |
1263 |
|
|
IO_3C90X_TRACE("AckIntr(%04x)\n", cr & 0x7ff); |
1264 |
|
|
// ack/clear corresponding bits in IntStatus |
1265 |
|
|
uint ISack = 0; |
1266 |
|
|
if (cr & 0x01) ISack |= IS_interruptLatch; |
1267 |
|
|
if (cr & 0x02) ISack |= IS_linkEvent; |
1268 |
|
|
if (cr & 0x20) ISack |= IS_rxEarly; |
1269 |
|
|
if (cr & 0x40) ISack |= IS_intRequested; |
1270 |
|
|
if (cr & 0x200) ISack |= IS_dnComplete; |
1271 |
|
|
if (cr & 0x400) ISack |= IS_upComplete; |
1272 |
|
|
acknowledge(ISack); |
1273 |
|
|
break; |
1274 |
|
|
} |
1275 |
|
|
/* case CmdReqIntr: { |
1276 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1277 |
|
|
// set intRequested in IntStatus |
1278 |
|
|
mIntStatus |= IS_intRequested; |
1279 |
|
|
|
1280 |
|
|
// FIXME: generate Interrupt (if enabled) |
1281 |
|
|
break; |
1282 |
|
|
}*/ |
1283 |
|
|
|
1284 |
|
|
/* |
1285 |
|
|
case CmdTxDone: |
1286 |
|
|
case CmdRxDiscard: |
1287 |
|
|
case CmdSetTxThreshold: |
1288 |
|
|
*/ |
1289 |
|
|
default: |
1290 |
|
|
IO_3C90X_WARN("command not implemented: %x\n", cr); |
1291 |
|
|
SINGLESTEP(""); |
1292 |
|
|
} |
1293 |
|
|
} |
1294 |
|
|
|
1295 |
|
|
void txDPD0(DPD0 *dpd) |
1296 |
|
|
{ |
1297 |
|
|
// FIXME: createHostStruct() |
1298 |
|
|
DPDFragDesc *frags = (DPDFragDesc*)(dpd+1); |
1299 |
|
|
uint32 fsh = dpd->FrameStartHeader; |
1300 |
|
|
IO_3C90X_TRACE("fsh = %08x\n", fsh); |
1301 |
|
|
if (fsh & FSH_dpdEmpty) { |
1302 |
|
|
// modify FrameStartHeader in DPD (!) |
1303 |
|
|
dpd->FrameStartHeader |= FSH_dnComplete; |
1304 |
|
|
// set next DnListPtr |
1305 |
|
|
mRegisters.DnListPtr = dpd->DnNextPtr; |
1306 |
|
|
IO_3C90X_TRACE("dpd empty\n"); |
1307 |
|
|
return; |
1308 |
|
|
} |
1309 |
|
|
byte pbuf[MAX_PACKET_SIZE]; |
1310 |
|
|
byte *p = pbuf; |
1311 |
|
|
// some packet drivers need padding |
1312 |
|
|
uint framePrefix = mEthTun->getWriteFramePrefix(); |
1313 |
|
|
memset(p, 0, framePrefix); |
1314 |
|
|
p += framePrefix; |
1315 |
|
|
// |
1316 |
|
|
uint i = 0; |
1317 |
|
|
// assemble packet from fragments (up to MAX_DPD_FRAGS fragments) |
1318 |
|
|
while (i < MAX_DPD_FRAGS) { |
1319 |
|
|
uint addr = frags->DnFragAddr; |
1320 |
|
|
uint len = frags->DnFragLen & 0x1fff; |
1321 |
|
|
IO_3C90X_TRACE("frag %d: %08x, len %04x (full: %08x)\n", i, addr, len, frags->DnFragLen); |
1322 |
|
|
// dumpMem(addr, len); |
1323 |
|
|
if (p-pbuf+len >= sizeof pbuf) { |
1324 |
|
|
IO_3C90X_WARN("packet too big ! (%d >= %d)\n", p-pbuf+len, sizeof pbuf); |
1325 |
|
|
SINGLESTEP(""); |
1326 |
|
|
return; |
1327 |
|
|
} |
1328 |
|
|
if (!ppc_dma_read(p, addr, len)) { |
1329 |
|
|
IO_3C90X_WARN("frag addr invalid! cancelling\n"); |
1330 |
|
|
SINGLESTEP(""); |
1331 |
|
|
return; |
1332 |
|
|
} |
1333 |
|
|
p += len; |
1334 |
|
|
// last fragment ? |
1335 |
|
|
if (frags->DnFragLen & 0x80000000) break; |
1336 |
|
|
frags++; |
1337 |
|
|
i++; |
1338 |
|
|
} |
1339 |
|
|
uint psize = p-pbuf; |
1340 |
|
|
if (!(fsh & FSH_rndupDefeat)) { |
1341 |
|
|
// round packet length |
1342 |
|
|
switch (fsh & FSH_rndupBndry) { |
1343 |
|
|
case 0: { |
1344 |
|
|
// 4 bytes |
1345 |
|
|
uint gap = ((psize+3) & ~3) -psize; |
1346 |
|
|
memset(pbuf+psize, 0, gap); |
1347 |
|
|
psize += gap; |
1348 |
|
|
break; |
1349 |
|
|
} |
1350 |
|
|
case 2: { |
1351 |
|
|
// 2 bytes |
1352 |
|
|
uint gap = ((psize+1) & ~1) -psize; |
1353 |
|
|
memset(pbuf+psize, 0, gap); |
1354 |
|
|
psize += gap; |
1355 |
|
|
break; |
1356 |
|
|
} |
1357 |
|
|
} |
1358 |
|
|
} |
1359 |
|
|
//FSH_reArmDisable = 1<<23, |
1360 |
|
|
//FSH_lastKap = 1<<24, |
1361 |
|
|
//FSH_addIpChecksum = 1<<25, |
1362 |
|
|
//FSH_addTcpChecksum = 1<<26, |
1363 |
|
|
//FSH_addUdpChecksum = 1<<27, |
1364 |
|
|
if (fsh & (0x1f << 23)) { |
1365 |
|
|
IO_3C90X_WARN("unsupported flags in fsh, fsh = %08x\n", fsh); |
1366 |
|
|
SINGLESTEP(""); |
1367 |
|
|
} |
1368 |
|
|
|
1369 |
|
|
if (psize<60) { |
1370 |
|
|
// pad packet to at least 60 bytes (+4 bytes crc = 64 bytes) |
1371 |
|
|
memset(pbuf+psize, 0, (60-psize)); |
1372 |
|
|
psize = 60; |
1373 |
|
|
} |
1374 |
|
|
// append crc |
1375 |
|
|
if (!(fsh & FSH_crcAppendDisable)) { |
1376 |
|
|
uint32 crc = ether_crc(psize, pbuf); |
1377 |
|
|
pbuf[psize+0] = crc; |
1378 |
|
|
pbuf[psize+1] = crc>>8; |
1379 |
|
|
pbuf[psize+2] = crc>>16; |
1380 |
|
|
pbuf[psize+3] = crc>>24; |
1381 |
|
|
psize += 4; |
1382 |
|
|
IO_3C90X_TRACE("packet has crc: %08x\n", crc); |
1383 |
|
|
} |
1384 |
|
|
|
1385 |
|
|
// IO_3C90X_TRACE("tx(%d):\n", psize); |
1386 |
|
|
// dumpMem(pbuf, psize); |
1387 |
|
|
uint w = mEthTun->sendPacket(pbuf, psize); |
1388 |
|
|
if (w) { |
1389 |
|
|
if (w == psize) { |
1390 |
|
|
IO_3C90X_TRACE("EthTun: %d bytes sent.\n", psize); |
1391 |
|
|
} else { |
1392 |
|
|
IO_3C90X_TRACE("EthTun: ARGH! send error: only %d of %d bytes sent\n", w, psize); |
1393 |
|
|
} |
1394 |
|
|
} else { |
1395 |
|
|
IO_3C90X_TRACE("EthTun: ARGH! send error in packet driver.\n"); |
1396 |
|
|
} |
1397 |
|
|
// indications |
1398 |
|
|
mRegisters.DmaCtrl |= DC_dnComplete; |
1399 |
|
|
uint inds = 0; |
1400 |
|
|
if (fsh & FSH_dnIndicate) inds |= IS_dnComplete; |
1401 |
|
|
if (fsh & FSH_txIndicate) inds |= IS_txComplete; |
1402 |
|
|
indicate(inds); |
1403 |
|
|
// modify FrameStartHeader in DPD (!) |
1404 |
|
|
dpd->FrameStartHeader |= FSH_dnComplete; |
1405 |
|
|
// set next DnListPtr, TxPktId |
1406 |
|
|
mRegisters.DnListPtr = dpd->DnNextPtr; |
1407 |
|
|
uint pktId = (fsh & FSH_pktId) >> 2; |
1408 |
|
|
mRegisters.TxPktId = pktId; |
1409 |
|
|
// maybe generate interrupt |
1410 |
|
|
maybeRaiseIntr(); |
1411 |
|
|
} |
1412 |
|
|
|
1413 |
|
|
bool passesRxFilter(byte *pbuf, uint psize) |
1414 |
|
|
{ |
1415 |
|
|
EthFrameII *f = (EthFrameII*)pbuf; |
1416 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1417 |
|
|
if (w5.RxFilter & RXFILT_receiveAllFrames) return true; |
1418 |
|
|
// FIXME: Multicast hashing not implemented |
1419 |
|
|
if (w5.RxFilter & RXFILT_receiveMulticastHash) return true; |
1420 |
|
|
// FIXME: Multicasting not understood |
1421 |
|
|
if (w5.RxFilter & RXFILT_receiveMulticast) return true; |
1422 |
|
|
if (w5.RxFilter & RXFILT_receiveBroadcast) { |
1423 |
|
|
byte broadcastMAC[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; |
1424 |
|
|
if (compareMACs(f->destMAC, broadcastMAC) == 0) return true; |
1425 |
|
|
} |
1426 |
|
|
if (w5.RxFilter & RXFILT_receiveIndividual) { |
1427 |
|
|
byte destMAC[6]; |
1428 |
|
|
byte thisMAC[6]; |
1429 |
|
|
RegWindow2 &w2 = (RegWindow2&)mWindows[2]; |
1430 |
|
|
for (uint i = 0; i < 6; i++) { |
1431 |
|
|
destMAC[i] = f->destMAC[i] & ~w2.StationMask[i]; |
1432 |
|
|
thisMAC[i] = w2.StationAddress[i] & ~w2.StationMask[i]; |
1433 |
|
|
} |
1434 |
|
|
return compareMACs(destMAC, thisMAC) == 0; |
1435 |
|
|
} |
1436 |
|
|
return false; |
1437 |
|
|
} |
1438 |
|
|
|
1439 |
|
|
void rxUPD(UPD *upd) |
1440 |
|
|
{ |
1441 |
|
|
// FIXME: threading to care about (mRegisters.DmaCtrl & DC_upAltSeqDisable) |
1442 |
|
|
IO_3C90X_TRACE("rxUPD()\n"); |
1443 |
|
|
|
1444 |
|
|
bool error = false; |
1445 |
|
|
|
1446 |
|
|
if (upd->UpPktStatus & UPS_upComplete) { |
1447 |
|
|
// IO_3C90X_WARN("UPD already upComplete!\n"); |
1448 |
|
|
|
1449 |
|
|
// the top of the ring buffer is already used, |
1450 |
|
|
// stall the upload and throw away the packet. |
1451 |
|
|
// the ring buffers are filled. |
1452 |
|
|
|
1453 |
|
|
mUpStalled = true; |
1454 |
|
|
return; |
1455 |
|
|
} |
1456 |
|
|
|
1457 |
|
|
uint upPktStatus = 0; |
1458 |
|
|
|
1459 |
|
|
if (mRegisters.UpPoll) { |
1460 |
|
|
IO_3C90X_WARN("UpPoll unsupported\n"); |
1461 |
|
|
SINGLESTEP(""); |
1462 |
|
|
return; |
1463 |
|
|
} |
1464 |
|
|
// FIXME: |
1465 |
|
|
// if (mRegisters.DmaCtrl & DC_upRxEarlyEnable) |
1466 |
|
|
// IO_3C90X_ERR("DC_upRxEarlyEnable unsupported\n"); |
1467 |
|
|
|
1468 |
|
|
if ((mRxPacketSize > 0x1fff) || (mRxPacketSize > sizeof mRxPacket)) { |
1469 |
|
|
IO_3C90X_TRACE("oversized frame\n"); |
1470 |
|
|
upd->UpPktStatus = UPS_upError | UPS_oversizedFrame; |
1471 |
|
|
error = true; |
1472 |
|
|
} |
1473 |
|
|
|
1474 |
|
|
if (mRxPacketSize < 60) { |
1475 |
|
|
// pad packet to at least 60 bytes (+4 bytes crc = 64 bytes) |
1476 |
|
|
memset(mRxPacket+mRxPacketSize, 0, (60-mRxPacketSize)); |
1477 |
|
|
mRxPacketSize = 60; |
1478 |
|
|
} |
1479 |
|
|
|
1480 |
|
|
// IO_3C90X_TRACE("rx(%d):\n", mRxPacketSize); |
1481 |
|
|
// dumpMem((unsigned char*)mRxPacket, mRxPacketSize); |
1482 |
|
|
|
1483 |
|
|
/* RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1484 |
|
|
if ((mRxPacketSize < 60) && (w5.RxEarlyThresh >= 60)) { |
1485 |
|
|
IO_3C90X_TRACE("runt frame\n"); |
1486 |
|
|
upPktStatus |= UPS_upError | UPS_runtFrame; |
1487 |
|
|
upd->UpPktStatus = upPktStatus; |
1488 |
|
|
error = true; |
1489 |
|
|
}*/ |
1490 |
|
|
if (upd->UpPktStatus & UPD_impliedBufferEnable) { |
1491 |
|
|
IO_3C90X_WARN("UPD_impliedBufferEnable unsupported\n"); |
1492 |
|
|
SINGLESTEP(""); |
1493 |
|
|
return; |
1494 |
|
|
} |
1495 |
|
|
UPDFragDesc *frags = (UPDFragDesc*)(upd+1); |
1496 |
|
|
|
1497 |
|
|
byte *p = mRxPacket; |
1498 |
|
|
uint i = 0; |
1499 |
|
|
while (!error && i < MAX_UPD_FRAGS) { // (up to MAX_UPD_FRAGS fragments) |
1500 |
|
|
uint32 addr = frags->UpFragAddr; |
1501 |
|
|
uint len = frags->UpFragLen & 0x1fff; |
1502 |
|
|
IO_3C90X_TRACE("frag %d: %08x, len %04x (full: %08x)\n", i, addr, len, frags->UpFragLen); |
1503 |
|
|
if (p-mRxPacket+len > sizeof mRxPacket) { |
1504 |
|
|
upPktStatus |= UPS_upError | UPS_upOverflow; |
1505 |
|
|
upd->UpPktStatus = upPktStatus; |
1506 |
|
|
IO_3C90X_TRACE("UPD overflow!\n"); |
1507 |
|
|
SINGLESTEP(""); |
1508 |
|
|
error = true; |
1509 |
|
|
break; |
1510 |
|
|
} |
1511 |
|
|
|
1512 |
|
|
if (!ppc_dma_write(addr, p, len)) { |
1513 |
|
|
upPktStatus |= UPS_upError; |
1514 |
|
|
upd->UpPktStatus = upPktStatus; |
1515 |
|
|
IO_3C90X_WARN("invalid UPD fragment address! (%08x)\n", addr); |
1516 |
|
|
SINGLESTEP(""); |
1517 |
|
|
error = true; |
1518 |
|
|
break; |
1519 |
|
|
} |
1520 |
|
|
p += len; |
1521 |
|
|
// last fragment ? |
1522 |
|
|
if (frags->UpFragLen & 0x80000000) break; |
1523 |
|
|
frags++; |
1524 |
|
|
i++; |
1525 |
|
|
} |
1526 |
|
|
|
1527 |
|
|
if (!error) { |
1528 |
|
|
IO_3C90X_TRACE("successfully uploaded packet of %d bytes\n", mRxPacketSize); |
1529 |
|
|
} |
1530 |
|
|
upPktStatus |= mRxPacketSize & 0x1fff; |
1531 |
|
|
upPktStatus |= UPS_upComplete; |
1532 |
|
|
upd->UpPktStatus = upPktStatus; |
1533 |
|
|
|
1534 |
|
|
mRxPacketSize = 0; |
1535 |
|
|
|
1536 |
|
|
/* the client OS is waiting for a change in status, but won't see it */ |
1537 |
|
|
/* until we dma our local copy upd->UpPktStatus back to the client address space */ |
1538 |
|
|
if (!ppc_dma_write(mRegisters.UpListPtr+4, &upd->UpPktStatus, sizeof(upd->UpPktStatus))) { |
1539 |
|
|
upPktStatus |= UPS_upError; |
1540 |
|
|
upd->UpPktStatus = upPktStatus; /* can't get this error out, anyways */ |
1541 |
|
|
IO_3C90X_WARN("invalid UPD UpListPtr address! (%08x)\n",mRegisters.UpListPtr+4); |
1542 |
|
|
SINGLESTEP(""); |
1543 |
|
|
error = true; |
1544 |
|
|
} |
1545 |
|
|
|
1546 |
|
|
mRegisters.UpListPtr = upd->UpNextPtr; |
1547 |
|
|
|
1548 |
|
|
// indications |
1549 |
|
|
mRegisters.DmaCtrl |= DC_upComplete; |
1550 |
|
|
indicate(IS_upComplete); |
1551 |
|
|
maybeRaiseIntr(); |
1552 |
|
|
} |
1553 |
|
|
|
1554 |
|
|
void indicate(uint indications) |
1555 |
|
|
{ |
1556 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1557 |
|
|
if (w5.IndicationEnable & indications != indications) { |
1558 |
|
|
IO_3C90X_TRACE("some masked: %08x\n", w5.IndicationEnable & indications); |
1559 |
|
|
} |
1560 |
|
|
mIntStatus |= w5.IndicationEnable & indications; |
1561 |
|
|
if (indications & IS_upComplete) { |
1562 |
|
|
mRegisters.DmaCtrl |= DC_upComplete; |
1563 |
|
|
} |
1564 |
|
|
if (indications & IS_dnComplete) { |
1565 |
|
|
mRegisters.DmaCtrl |= DC_dnComplete; |
1566 |
|
|
} |
1567 |
|
|
IO_3C90X_TRACE("indicate(%08x) mIntStatus now = %08x\n", indications, mIntStatus); |
1568 |
|
|
} |
1569 |
|
|
|
1570 |
|
|
void acknowledge(uint indications) |
1571 |
|
|
{ |
1572 |
|
|
// RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1573 |
|
|
mIntStatus &= ~indications; |
1574 |
|
|
if (indications & IS_upComplete) { |
1575 |
|
|
mRegisters.DmaCtrl &= ~DC_upComplete; |
1576 |
|
|
} |
1577 |
|
|
if (indications & IS_dnComplete) { |
1578 |
|
|
mRegisters.DmaCtrl &= ~DC_dnComplete; |
1579 |
|
|
} |
1580 |
|
|
IO_3C90X_TRACE("acknowledge(%08x) mIntStatus now = %08x\n", indications, mIntStatus); |
1581 |
|
|
} |
1582 |
|
|
|
1583 |
|
|
void maybeRaiseIntr() |
1584 |
|
|
{ |
1585 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1586 |
|
|
if (w5.IndicationEnable & w5.InterruptEnable & mIntStatus) { |
1587 |
|
|
mIntStatus |= IS_interruptLatch; |
1588 |
|
|
IO_3C90X_TRACE("Generating interrupt. mIntStatus=%04x\n", mIntStatus); |
1589 |
|
|
pic_raise_interrupt(mConfig[0x3c]); |
1590 |
|
|
} |
1591 |
|
|
} |
1592 |
|
|
|
1593 |
|
|
void checkDnWork() |
1594 |
|
|
{ |
1595 |
|
|
while (!mDnStalled && (mRegisters.DnListPtr != 0)) { |
1596 |
|
|
byte dpd[512]; |
1597 |
|
|
|
1598 |
|
|
if (ppc_dma_read(dpd, mRegisters.DnListPtr, sizeof dpd)) { |
1599 |
|
|
// get packet type |
1600 |
|
|
byte type = dpd[7]>>6; |
1601 |
|
|
switch (type) { |
1602 |
|
|
case 0: |
1603 |
|
|
case 2: { |
1604 |
|
|
DPD0 *p = (DPD0*)dpd; |
1605 |
|
|
IO_3C90X_TRACE("Got a type 0 DPD !\n"); |
1606 |
|
|
IO_3C90X_TRACE("DnNextPtr is %08x\n", p->DnNextPtr); |
1607 |
|
|
txDPD0(p); |
1608 |
|
|
break; |
1609 |
|
|
} |
1610 |
|
|
case 1: { |
1611 |
|
|
IO_3C90X_TRACE("Got a type 1 DPD ! not implemented !\n"); |
1612 |
|
|
IO_3C90X_TRACE("DnNextPtr is %08x\n", ((DPD1*)dpd)->DnNextPtr); |
1613 |
|
|
SINGLESTEP(""); |
1614 |
|
|
mRegisters.DnListPtr = 0; |
1615 |
|
|
break; |
1616 |
|
|
} |
1617 |
|
|
default: |
1618 |
|
|
IO_3C90X_TRACE("unsupported packet type 3\n"); |
1619 |
|
|
mRegisters.DnListPtr = 0; |
1620 |
|
|
SINGLESTEP(""); |
1621 |
|
|
break; |
1622 |
|
|
} |
1623 |
|
|
} else { |
1624 |
|
|
IO_3C90X_WARN("DnListPtr invalid!\n"); |
1625 |
|
|
break; |
1626 |
|
|
} |
1627 |
|
|
} |
1628 |
|
|
} |
1629 |
|
|
|
1630 |
|
|
void checkUpWork() |
1631 |
|
|
{ |
1632 |
|
|
if (mRxEnabled && !mUpStalled && mRxPacketSize && (mRegisters.UpListPtr != 0) ) { |
1633 |
|
|
byte upd[MAX_UPD_SIZE]; |
1634 |
|
|
if (ppc_dma_read(upd, mRegisters.UpListPtr, sizeof upd)) { |
1635 |
|
|
UPD *p = (UPD*)upd; |
1636 |
|
|
rxUPD(p); |
1637 |
|
|
} else { |
1638 |
|
|
IO_3C90X_WARN("invalid address in UpListPtr!\n"); |
1639 |
|
|
SINGLESTEP(""); |
1640 |
|
|
} |
1641 |
|
|
} else { |
1642 |
|
|
IO_3C90X_TRACE("Not uploading, because: mUpStalled(=%d) or UpListPtr == 0 (=%08x) or not mRxPacketSize(=%d)\n", mUpStalled, mRegisters.UpListPtr, mRxPacketSize); |
1643 |
|
|
} |
1644 |
|
|
} |
1645 |
|
|
|
1646 |
|
|
public: |
1647 |
|
|
_3c90x_NIC(EthTunDevice *aEthTun, const byte *mac) |
1648 |
|
|
: PCI_Device("3c90x Network interface card", 0x1, 0xc) |
1649 |
|
|
{ |
1650 |
|
|
int e; |
1651 |
|
|
if ((e = sys_create_mutex(&mLock))) throw IOException(e); |
1652 |
|
|
mEthTun = aEthTun; |
1653 |
|
|
memcpy(mMAC, mac, 6); |
1654 |
|
|
PCIReset(); |
1655 |
|
|
totalReset(); |
1656 |
|
|
} |
1657 |
|
|
|
1658 |
|
|
virtual ~_3c90x_NIC() |
1659 |
|
|
{ |
1660 |
|
|
mEthTun->shutdownDevice(); |
1661 |
|
|
delete mEthTun; |
1662 |
|
|
sys_destroy_mutex(mLock); |
1663 |
|
|
} |
1664 |
|
|
|
1665 |
|
|
void readConfig(uint reg) |
1666 |
|
|
{ |
1667 |
|
|
if (reg >= 0xdc) { |
1668 |
|
|
IO_3C90X_WARN("re\n"); |
1669 |
|
|
SINGLESTEP(""); |
1670 |
|
|
} |
1671 |
|
|
sys_lock_mutex(mLock); |
1672 |
|
|
PCI_Device::readConfig(reg); |
1673 |
|
|
sys_unlock_mutex(mLock); |
1674 |
|
|
} |
1675 |
|
|
|
1676 |
|
|
void writeConfig(uint reg, int offset, int size) |
1677 |
|
|
{ |
1678 |
|
|
sys_lock_mutex(mLock); |
1679 |
|
|
if (reg >= 0xdc) { |
1680 |
|
|
IO_3C90X_WARN("jg\n"); |
1681 |
|
|
SINGLESTEP(""); |
1682 |
|
|
} |
1683 |
|
|
PCI_Device::writeConfig(reg, offset, size); |
1684 |
|
|
sys_unlock_mutex(mLock); |
1685 |
|
|
} |
1686 |
|
|
|
1687 |
|
|
bool readDeviceIO(uint r, uint32 port, uint32 &data, uint size) |
1688 |
|
|
{ |
1689 |
|
|
if (r != 0) return false; |
1690 |
|
|
bool retval = false; |
1691 |
|
|
sys_lock_mutex(mLock); |
1692 |
|
|
if (port == 0xe) { |
1693 |
|
|
// IntStatus (no matter which window) |
1694 |
|
|
if (size != 2) { |
1695 |
|
|
IO_3C90X_WARN("unaligned read from IntStatus\n"); |
1696 |
|
|
SINGLESTEP(""); |
1697 |
|
|
} |
1698 |
|
|
IO_3C90X_TRACE("read IntStatus = %04x\n", mIntStatus); |
1699 |
|
|
data = mIntStatus; |
1700 |
|
|
retval = true; |
1701 |
|
|
} else if (port >= 0 && (port+size <= 0x0e)) { |
1702 |
|
|
// read from window |
1703 |
|
|
uint curwindow = mIntStatus >> 13; |
1704 |
|
|
readRegWindow(curwindow, port, data, size); |
1705 |
|
|
retval = true; |
1706 |
|
|
} else if ((port+size > 0x1e) && (port <= 0x1f)) { |
1707 |
|
|
if ((port != 0x1e) || (size != 2)) { |
1708 |
|
|
IO_3C90X_WARN("unaligned read from IntStatusAuto\n"); |
1709 |
|
|
SINGLESTEP(""); |
1710 |
|
|
} |
1711 |
|
|
RegWindow5 &w5 = (RegWindow5&)mWindows[5]; |
1712 |
|
|
// side-effects of reading IntStatusAuto: |
1713 |
|
|
// 1.clear InterruptEnable |
1714 |
|
|
w5.InterruptEnable = 0; |
1715 |
|
|
// 2.clear some flags |
1716 |
|
|
acknowledge(IS_dnComplete | IS_upComplete |
1717 |
|
|
| IS_rxEarly | IS_intRequested |
1718 |
|
|
| IS_interruptLatch | IS_linkEvent); |
1719 |
|
|
data = mIntStatus; |
1720 |
|
|
IO_3C90X_TRACE("read IntStatusAuto = %04x\n", data); |
1721 |
|
|
retval = true; |
1722 |
|
|
} else if ((port >= 0x10) && (port+size <= 0x10 + sizeof(Registers))) { |
1723 |
|
|
byte l = gRegAccess[port-0x10]; |
1724 |
|
|
if (l != size) { |
1725 |
|
|
IO_3C90X_WARN("invalid/unaligned read from register port=%04x, size=%d (expecting size %d)\n", port, size, l); |
1726 |
|
|
SINGLESTEP(""); |
1727 |
|
|
} |
1728 |
|
|
// read from (standard) register |
1729 |
|
|
data = 0; |
1730 |
|
|
memcpy(&data, ((byte*)&mRegisters)+port-0x10, size); |
1731 |
|
|
switch (port) { |
1732 |
|
|
case 0x1a: |
1733 |
|
|
IO_3C90X_TRACE("read Timer = %08x\n", data); |
1734 |
|
|
break; |
1735 |
|
|
case 0x20: |
1736 |
|
|
IO_3C90X_TRACE("read DmaCtrl = %08x\n", data); |
1737 |
|
|
break; |
1738 |
|
|
case 0x24: |
1739 |
|
|
IO_3C90X_TRACE("read DownListPtr = %08x\n", data); |
1740 |
|
|
break; |
1741 |
|
|
case 0x38: |
1742 |
|
|
IO_3C90X_TRACE("read UpListPtr = %08x\n", data); |
1743 |
|
|
break; |
1744 |
|
|
default: |
1745 |
|
|
IO_3C90X_WARN("read reg %04x (size %d) = %08x\n", port, size, data); |
1746 |
|
|
SINGLESTEP(""); |
1747 |
|
|
break; |
1748 |
|
|
} |
1749 |
|
|
retval = true; |
1750 |
|
|
} |
1751 |
|
|
sys_unlock_mutex(mLock); |
1752 |
|
|
return retval; |
1753 |
|
|
} |
1754 |
|
|
|
1755 |
|
|
bool writeDeviceIO(uint r, uint32 port, uint32 data, uint size) |
1756 |
|
|
{ |
1757 |
|
|
if (r != 0) return false; |
1758 |
|
|
bool retval = false; |
1759 |
|
|
sys_lock_mutex(mLock); |
1760 |
|
|
if (port == 0xe) { |
1761 |
|
|
// CommandReg (no matter which window) |
1762 |
|
|
if (size != 2) { |
1763 |
|
|
IO_3C90X_WARN("unaligned write to CommandReg\n"); |
1764 |
|
|
SINGLESTEP(""); |
1765 |
|
|
} |
1766 |
|
|
setCR(data); |
1767 |
|
|
retval = true; |
1768 |
|
|
} else if (port >= 0 && (port+size <= 0x0e)) { |
1769 |
|
|
// write to window |
1770 |
|
|
uint curwindow = mIntStatus >> 13; |
1771 |
|
|
writeRegWindow(curwindow, port, data, size); |
1772 |
|
|
retval = true; |
1773 |
|
|
} else if (port >= 0x10 && (port + size <= 0x10 + sizeof(Registers))) { |
1774 |
|
|
byte l = gRegAccess[port-0x10]; |
1775 |
|
|
if (l != size) { |
1776 |
|
|
IO_3C90X_WARN("invalid/unaligned write to register port=%04x, size=%d\n", port, size); |
1777 |
|
|
SINGLESTEP(""); |
1778 |
|
|
} |
1779 |
|
|
switch (port) { |
1780 |
|
|
case 0x20: { |
1781 |
|
|
uint DmaCtrlRWMask = DC_upRxEarlyEnable | DC_counterSpeed | |
1782 |
|
|
DC_countdownMode | DC_defeatMWI | DC_defeatMRL | |
1783 |
|
|
DC_upOverDiscEnable; |
1784 |
|
|
mRegisters.DmaCtrl &= ~DmaCtrlRWMask; |
1785 |
|
|
mRegisters.DmaCtrl |= data & DmaCtrlRWMask; |
1786 |
|
|
IO_3C90X_TRACE("write DmaCtrl %08x (now = %08x)\n", data, mRegisters.DmaCtrl); |
1787 |
|
|
break; |
1788 |
|
|
} |
1789 |
|
|
case 0x24: { |
1790 |
|
|
if (!mRegisters.DnListPtr) { |
1791 |
|
|
mRegisters.DnListPtr = data; |
1792 |
|
|
IO_3C90X_TRACE("write DnListPtr (now = %08x)\n", mRegisters.DnListPtr); |
1793 |
|
|
} else { |
1794 |
|
|
IO_3C90X_TRACE("didn't write DnListPtr cause it's not 0 (now = %08x)\n", mRegisters.DnListPtr); |
1795 |
|
|
} |
1796 |
|
|
checkDnWork(); |
1797 |
|
|
break; |
1798 |
|
|
} |
1799 |
|
|
case 0x38: { |
1800 |
|
|
mRegisters.UpListPtr = data; |
1801 |
|
|
IO_3C90X_TRACE("write UpListPtr (now = %08x)\n", mRegisters.UpListPtr); |
1802 |
|
|
checkUpWork(); |
1803 |
|
|
break; |
1804 |
|
|
} |
1805 |
|
|
case 0x2d: |
1806 |
|
|
IO_3C90X_WARN("DnPoll\n"); |
1807 |
|
|
SINGLESTEP(""); |
1808 |
|
|
break; |
1809 |
|
|
case 0x2a: |
1810 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1811 |
|
|
IO_3C90X_TRACE("write DnBurstThresh\n"); |
1812 |
|
|
break; |
1813 |
|
|
case 0x2c: |
1814 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1815 |
|
|
IO_3C90X_TRACE("write DnPriorityThresh\n"); |
1816 |
|
|
break; |
1817 |
|
|
case 0x2f: |
1818 |
|
|
// used by Darwin as TxFreeThresh. Not documented in [1]. |
1819 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1820 |
|
|
IO_3C90X_TRACE("write TxFreeThresh\n"); |
1821 |
|
|
break; |
1822 |
|
|
case 0x3c: |
1823 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1824 |
|
|
IO_3C90X_TRACE("write UpPriorityThresh\n"); |
1825 |
|
|
break; |
1826 |
|
|
case 0x3e: |
1827 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1828 |
|
|
IO_3C90X_TRACE("write UpBurstThresh\n"); |
1829 |
|
|
break; |
1830 |
|
|
default: |
1831 |
|
|
IO_3C90X_WARN("write to register port=%04x, size=%d\n", port, size); |
1832 |
|
|
SINGLESTEP(""); |
1833 |
|
|
// write to (standard) register |
1834 |
|
|
memcpy(((byte*)&mRegisters)+port-0x10, &data, size); |
1835 |
|
|
} |
1836 |
|
|
retval = true; |
1837 |
|
|
} |
1838 |
|
|
sys_unlock_mutex(mLock); |
1839 |
|
|
return retval; |
1840 |
|
|
} |
1841 |
|
|
|
1842 |
|
|
/* new */ |
1843 |
|
|
void handleRxQueue() |
1844 |
|
|
{ |
1845 |
|
|
while (1) { |
1846 |
|
|
while (mEthTun->waitRecvPacket() != 0) { |
1847 |
|
|
// don't block the system in case of (repeated) error(s) |
1848 |
|
|
sys_suspend(); |
1849 |
|
|
} |
1850 |
|
|
sys_lock_mutex(mLock); |
1851 |
|
|
if (mRxPacketSize) { |
1852 |
|
|
IO_3C90X_TRACE("Argh. old packet not yet uploaded. waiting some more...\n"); |
1853 |
|
|
} else { |
1854 |
|
|
mRxPacketSize = mEthTun->recvPacket(mRxPacket, sizeof mRxPacket); |
1855 |
|
|
if (mRxEnabled && (mRxPacketSize > sizeof(EthFrameII))) { |
1856 |
|
|
indicate(IS_rxComplete); |
1857 |
|
|
maybeRaiseIntr(); |
1858 |
|
|
acknowledge(IS_rxComplete); |
1859 |
|
|
if (!passesRxFilter(mRxPacket, mRxPacketSize)) { |
1860 |
|
|
IO_3C90X_TRACE("EthTun: %d bytes received. But they don't pass the filter.\n", mRxPacketSize); |
1861 |
|
|
mRxPacketSize = 0; |
1862 |
|
|
} else { |
1863 |
|
|
IO_3C90X_TRACE("EthTun: %d bytes received.\n", mRxPacketSize); |
1864 |
|
|
} |
1865 |
|
|
} else { |
1866 |
|
|
// don't block the system in case of (repeated) error(s) |
1867 |
|
|
mRxPacketSize = 0; |
1868 |
|
|
sys_suspend(); |
1869 |
|
|
} |
1870 |
|
|
} |
1871 |
|
|
checkUpWork(); |
1872 |
|
|
sys_unlock_mutex(mLock); |
1873 |
|
|
} |
1874 |
|
|
} |
1875 |
|
|
|
1876 |
|
|
}; |
1877 |
|
|
|
1878 |
|
|
static void *_3c90xHandleRxQueue(void *nic) |
1879 |
|
|
{ |
1880 |
|
|
_3c90x_NIC *NIC = (_3c90x_NIC *)nic; |
1881 |
|
|
NIC->handleRxQueue(); |
1882 |
|
|
return NULL; |
1883 |
|
|
} |
1884 |
|
|
|
1885 |
|
|
#include "configparser.h" |
1886 |
|
|
#include "tools/strtools.h" |
1887 |
|
|
|
1888 |
|
|
bool _3c90x_installed = false; |
1889 |
|
|
|
1890 |
|
|
#define _3C90X_KEY_INSTALLED "pci_3c90x_installed" |
1891 |
|
|
#define _3C90X_KEY_MAC "pci_3c90x_mac" |
1892 |
|
|
|
1893 |
|
|
void _3c90x_init() |
1894 |
|
|
{ |
1895 |
|
|
if (gConfig->getConfigInt(_3C90X_KEY_INSTALLED)) { |
1896 |
|
|
_3c90x_installed = true; |
1897 |
|
|
byte mac[6]; |
1898 |
|
|
mac[0] = 0xde; |
1899 |
|
|
mac[1] = 0xad; |
1900 |
|
|
mac[2] = 0xca; |
1901 |
|
|
mac[3] = 0xfe; |
1902 |
|
|
mac[4] = 0x12; |
1903 |
|
|
mac[5] = 0x34; |
1904 |
|
|
if (gConfig->haveKey(_3C90X_KEY_MAC)) { |
1905 |
|
|
String macstr_; |
1906 |
|
|
gConfig->getConfigString(_3C90X_KEY_MAC, macstr_); |
1907 |
|
|
// do something useful with mac |
1908 |
|
|
const char *macstr = macstr_.contentChar(); |
1909 |
|
|
byte cfgmac[6]; |
1910 |
|
|
for (uint i = 0; i < 6; i++) { |
1911 |
|
|
uint64 v; |
1912 |
|
|
if (!parseIntStr(macstr, v, 16) || (v>255) || ((*macstr != ':') && (i!=5))) { |
1913 |
|
|
IO_3C90X_ERR("error in config key %s:" |
1914 |
|
|
"expected format: XX:XX:XX:XX:XX:XX, " |
1915 |
|
|
"where X stands for any digit or the letters a-f, A-F (error at: %s)\n",_3C90X_KEY_MAC, macstr); |
1916 |
|
|
} |
1917 |
|
|
macstr++; |
1918 |
|
|
cfgmac[i] = v; |
1919 |
|
|
} |
1920 |
|
|
memcpy(mac, cfgmac, sizeof mac); |
1921 |
|
|
} |
1922 |
|
|
EthTunDevice *ethTun = createEthernetTunnel(); |
1923 |
|
|
if (!ethTun) { |
1924 |
|
|
IO_3C90X_ERR("Couldn't create ethernet tunnel\n"); |
1925 |
|
|
exit(1); |
1926 |
|
|
} |
1927 |
|
|
if (ethTun->initDevice()) { |
1928 |
|
|
IO_3C90X_ERR("Couldn't initialize ethernet tunnel\n"); |
1929 |
|
|
exit(1); |
1930 |
|
|
} |
1931 |
|
|
#if 0 |
1932 |
|
|
printf("Creating 3com 3c90x NIC emulation with eth_addr = "); |
1933 |
|
|
for (uint i = 0; i < 6; i++) { |
1934 |
|
|
if (i < 5) { |
1935 |
|
|
printf("%02x:", mac[i]); |
1936 |
|
|
} else { |
1937 |
|
|
printf("%02x", mac[i]); |
1938 |
|
|
} |
1939 |
|
|
} |
1940 |
|
|
printf("\n"); |
1941 |
|
|
#endif |
1942 |
|
|
_3c90x_NIC *MyNIC = new _3c90x_NIC(ethTun, mac); |
1943 |
|
|
gPCI_Devices->insert(MyNIC); |
1944 |
|
|
sys_thread rxthread; |
1945 |
|
|
sys_create_thread(&rxthread, 0, _3c90xHandleRxQueue, MyNIC); |
1946 |
|
|
} |
1947 |
|
|
} |
1948 |
|
|
|
1949 |
|
|
void _3c90x_done() |
1950 |
|
|
{ |
1951 |
|
|
} |
1952 |
|
|
|
1953 |
|
|
void _3c90x_init_config() |
1954 |
|
|
{ |
1955 |
|
|
gConfig->acceptConfigEntryIntDef(_3C90X_KEY_INSTALLED, 0); |
1956 |
|
|
gConfig->acceptConfigEntryString(_3C90X_KEY_MAC, false); |
1957 |
|
|
} |