]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1998-1999 by Apple Computer, Inc., All rights reserved. | |
24 | * | |
25 | * Hardware independent (relatively) code for the BMac Ethernet Controller | |
26 | * | |
27 | * HISTORY | |
28 | * | |
29 | * dd-mmm-yy | |
30 | * Created. | |
31 | * | |
32 | * Dec 10, 1998 jliu | |
33 | * Converted to IOKit/C++. | |
34 | */ | |
35 | ||
36 | #include "BMacEnet.h" | |
37 | #include "BMacEnetPrivate.h" | |
38 | ||
39 | #include <IOKit/IORegistryEntry.h> | |
40 | #include <IOKit/IODeviceTreeSupport.h> | |
41 | #include <IOKit/platform/AppleMacIODevice.h> | |
42 | #include <IOKit/assert.h> | |
43 | ||
44 | // #define DEBUG_JOE 1 | |
45 | ||
46 | //------------------------------------------------------------------------ | |
47 | ||
48 | #define super IOEthernetController | |
49 | ||
50 | OSDefineMetaClassAndStructors( BMacEnet, IOEthernetController ) | |
51 | ||
52 | //------------------------------------------------------------------------ | |
53 | ||
54 | #define PROVIDER_DEV 0 | |
55 | #define PROVIDER_DMA_TX 1 | |
56 | #define PROVIDER_DMA_RX 2 | |
57 | ||
58 | /* | |
59 | * Public Instance Methods | |
60 | */ | |
61 | ||
62 | /*------------------------------------------------------------------------- | |
63 | * | |
64 | * | |
65 | * | |
66 | *-------------------------------------------------------------------------*/ | |
67 | ||
68 | bool BMacEnet::init(OSDictionary * properties = 0) | |
69 | { | |
70 | if ( super::init(properties) == false ) | |
71 | return false; | |
72 | ||
73 | /* | |
74 | * Initialize my ivars. | |
75 | */ | |
76 | phyId = 0xff; | |
77 | phyMIIDelay = MII_DEFAULT_DELAY; | |
78 | sromAddressBits = 6; | |
79 | enetAddressOffset = 20; | |
80 | phyStatusPrev = 0; | |
81 | ||
82 | return true; | |
83 | } | |
84 | ||
85 | bool BMacEnet::start(IOService * provider) | |
86 | { | |
87 | AppleMacIODevice *nub = OSDynamicCast(AppleMacIODevice, provider); | |
88 | IOInterruptEventSource *intES; | |
89 | ||
90 | if (!nub || !super::start(provider)) | |
91 | return false; | |
92 | ||
93 | transmitQueue = OSDynamicCast(IOGatedOutputQueue, getOutputQueue()); | |
94 | if (!transmitQueue) { | |
95 | IOLog("BMac: output queue initialization failed\n"); | |
96 | return false; | |
97 | } | |
98 | transmitQueue->retain(); | |
99 | ||
100 | // Allocate debug queue. This stores packets retired from the TX ring | |
101 | // by the polling routine. We cannot call freePacket() or m_free() within | |
102 | // the debugger context. | |
103 | // | |
104 | // The capacity of the queue is set at maximum to prevent the queue from | |
105 | // calling m_free() due to over-capacity. But we don't expect the size | |
106 | // of the queue to grow too large. | |
107 | // | |
108 | debugQueue = IOPacketQueue::withCapacity((UInt) -1); | |
109 | if (!debugQueue) | |
110 | return false; | |
111 | ||
112 | // Allocate a IOMbufBigMemoryCursor instance. Currently, the maximum | |
113 | // number of segments is set to 2. The maximum length for each segment | |
114 | // is set to the maximum ethernet frame size (plus padding). | |
115 | ||
116 | mbufCursor = IOMbufBigMemoryCursor::withSpecification(NETWORK_BUFSIZE, 2); | |
117 | if (!mbufCursor) { | |
118 | IOLog("Ethernet(BMac): IOMbufBigMemoryCursor allocation failure\n"); | |
119 | return false; | |
120 | } | |
121 | ||
122 | // | |
123 | // Our provider is the nub representing the BMacEnet hardware | |
124 | // controller. We will query it for our resource information. | |
125 | // | |
126 | ||
127 | for (int i = 0; i < MEMORY_MAP_HEATHROW_INDEX; i++) { | |
128 | IOMemoryMap * map; | |
129 | ||
130 | map = provider->mapDeviceMemoryWithIndex(i); | |
131 | if (!map) | |
132 | return false; | |
133 | #ifdef DEBUG_JOE | |
134 | IOLog("map %d: Phys:%08x Virt:%08x len:%d\n", | |
135 | i, | |
136 | (unsigned) map->getPhysicalAddress(), | |
137 | (unsigned) map->getVirtualAddress(), | |
138 | (unsigned) map->getLength()); | |
139 | #endif DEBUG_JOE | |
140 | ||
141 | switch (i) { | |
142 | case MEMORY_MAP_ENET_INDEX: | |
143 | ioBaseEnet = (IOPPCAddress) map->getVirtualAddress(); | |
144 | break; | |
145 | ||
146 | case MEMORY_MAP_TXDMA_INDEX: | |
147 | ioBaseEnetTxDMA = (IODBDMAChannelRegisters *) | |
148 | map->getVirtualAddress(); | |
149 | break; | |
150 | ||
151 | case MEMORY_MAP_RXDMA_INDEX: | |
152 | ioBaseEnetRxDMA = (IODBDMAChannelRegisters *) | |
153 | map->getVirtualAddress(); | |
154 | break; | |
155 | } | |
156 | ||
157 | maps[i] = map; | |
158 | } | |
159 | ||
160 | #ifdef DEBUG_JOE | |
161 | IOLog("ioBaseEnet: 0x%08x\n", ioBaseEnet); | |
162 | IOLog("ioBaseEnetTxDMA: 0x%08x\n", ioBaseEnetTxDMA); | |
163 | IOLog("ioBaseEnetRxDMA: 0x%08x\n", ioBaseEnetRxDMA); | |
164 | #endif DEBUG_JOE | |
165 | ||
166 | // | |
167 | // We need to get the I/O address for the Heathrow controller. | |
168 | // We ask the provider (bmac) for its device tree parent. | |
169 | // | |
170 | IOService *heathrow; | |
171 | if (!(heathrow = OSDynamicCast(IOService, | |
172 | provider->getParentEntry( gIODTPlane )))) | |
173 | return false; | |
174 | ||
175 | // Check whether the hardware is susceptible to the broken unicast | |
176 | // filter problem. | |
177 | // | |
178 | OSData * devIDData; | |
179 | devIDData = OSDynamicCast(OSData, heathrow->getProperty("device-id")); | |
180 | ||
181 | if (devIDData) { | |
182 | useUnicastFilter = ( *((UInt32 *) devIDData->getBytesNoCopy()) == | |
183 | 0x10 ); | |
184 | if (useUnicastFilter) | |
185 | IOLog("%s: Enabling workaround for broken unicast filter\n", | |
186 | getName()); | |
187 | } | |
188 | ||
189 | IOMemoryMap * map = heathrow->mapDeviceMemoryWithIndex(0); | |
190 | if (map) | |
191 | { | |
192 | #ifdef DEBUG_JOE | |
193 | IOLog("Heathrow: Phys:%08x Virt:%08x len:%d\n", | |
194 | (unsigned) map->getPhysicalAddress(), | |
195 | (unsigned) map->getVirtualAddress(), | |
196 | (unsigned) map->getLength()); | |
197 | #endif DEBUG_JOE | |
198 | ioBaseHeathrow = (IOPPCAddress) map->getVirtualAddress(); | |
199 | ||
200 | maps[MEMORY_MAP_HEATHROW_INDEX] = map; | |
201 | } | |
202 | else { | |
203 | return false; | |
204 | } | |
205 | ||
206 | // | |
207 | // Get a reference to the IOWorkLoop in our superclass. | |
208 | // | |
209 | IOWorkLoop * myWorkLoop = getWorkLoop(); | |
210 | assert(myWorkLoop); | |
211 | ||
212 | // | |
213 | // Allocate three IOInterruptEventSources. | |
214 | // | |
215 | rxIntSrc = IOInterruptEventSource::interruptEventSource | |
216 | (this, | |
217 | (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource, | |
218 | provider, PROVIDER_DMA_RX); | |
219 | if (!rxIntSrc | |
220 | || (myWorkLoop->addEventSource(rxIntSrc) != kIOReturnSuccess)) { | |
221 | IOLog("Ethernet(BMac): rxIntSrc init failure\n"); | |
222 | return false; | |
223 | } | |
224 | ||
225 | intES = IOInterruptEventSource::interruptEventSource | |
226 | (this, | |
227 | (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource, | |
228 | provider, PROVIDER_DMA_TX); | |
229 | if (intES) { | |
230 | bool res = (myWorkLoop->addEventSource(intES) != kIOReturnSuccess); | |
231 | intES->release(); | |
232 | if (res) { | |
233 | IOLog("Ethernet(BMac): PROVIDER_DMA_TX add failure\n"); | |
234 | return false; | |
235 | } | |
236 | } | |
237 | else { | |
238 | IOLog("Mace: PROVIDER_DMA_TX init failure\n"); | |
239 | return false; | |
240 | } | |
241 | ||
242 | intES = IOInterruptEventSource::interruptEventSource | |
243 | (this, | |
244 | (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource, | |
245 | provider, PROVIDER_DEV); | |
246 | if (intES) { | |
247 | bool res = (myWorkLoop->addEventSource(intES) != kIOReturnSuccess); | |
248 | intES->release(); | |
249 | if (res) { | |
250 | IOLog("Ethernet(BMac): PROVIDER_DEV add failure\n"); | |
251 | return false; | |
252 | } | |
253 | } | |
254 | else { | |
255 | IOLog("Ethernet(BMac): PROVIDER_DEV init failure\n"); | |
256 | return false; | |
257 | } | |
258 | ||
259 | timerSrc = IOTimerEventSource::timerEventSource | |
260 | (this, (IOTimerEventSource::Action) &BMacEnet::timeoutOccurred); | |
261 | if (!timerSrc | |
262 | || (myWorkLoop->addEventSource(timerSrc) != kIOReturnSuccess)) { | |
263 | IOLog("Ethernet(BMac): timerSrc init failure\n"); | |
264 | return false; | |
265 | } | |
266 | ||
267 | MGETHDR(txDebuggerPkt, M_DONTWAIT, MT_DATA); | |
268 | if (!txDebuggerPkt) { | |
269 | IOLog("Ethernet(BMac): Couldn't allocate KDB buffer\n"); | |
270 | return false; | |
271 | } | |
272 | ||
273 | #if 0 | |
274 | // Enable the interrupt event sources. The hardware interrupts | |
275 | // sources remain disabled until _resetAndEnable(true) is called. | |
276 | // | |
277 | // myWorkLoop->enableAllInterrupts(); | |
278 | #endif | |
279 | ||
280 | // Perform a hardware reset. | |
281 | // | |
282 | if ( !_resetAndEnable(false) ) | |
283 | { | |
284 | return false; | |
285 | } | |
286 | ||
287 | // Cache my MAC address. | |
288 | // | |
289 | getHardwareAddress(&myAddress); | |
290 | ||
291 | // Allocate memory for ring buffers. | |
292 | // | |
293 | if (_allocateMemory() == false) | |
294 | { | |
295 | return false; | |
296 | } | |
297 | ||
298 | // Create a table of supported media types. | |
299 | // | |
300 | if ( !createMediumTables() ) | |
301 | return false; | |
302 | ||
303 | // Attach an IOEthernetInterface client. | |
304 | // | |
305 | if ( !attachInterface((IONetworkInterface **) &networkInterface, false) ) | |
306 | return false; | |
307 | ||
308 | // Attach a kernel debugger client. | |
309 | // | |
310 | attachDebuggerClient(&debugger); | |
311 | ||
312 | // Ready to service interface requests. | |
313 | // | |
314 | networkInterface->registerService(); | |
315 | ||
316 | return true; | |
317 | } | |
318 | ||
319 | /*------------------------------------------------------------------------- | |
320 | * | |
321 | * | |
322 | * | |
323 | *-------------------------------------------------------------------------*/ | |
324 | ||
325 | void BMacEnet::free() | |
326 | { | |
327 | UInt i; | |
328 | ||
329 | _resetAndEnable(false); | |
330 | ||
331 | if (debugger) | |
332 | debugger->release(); | |
333 | ||
334 | if (getWorkLoop()) | |
335 | getWorkLoop()->disableAllEventSources(); | |
336 | ||
337 | if (timerSrc) | |
338 | timerSrc->release(); | |
339 | ||
340 | if (rxIntSrc) | |
341 | rxIntSrc->release(); | |
342 | ||
343 | if (txDebuggerPkt) | |
344 | freePacket(txDebuggerPkt); | |
345 | ||
346 | if (transmitQueue) | |
347 | transmitQueue->release(); | |
348 | ||
349 | if (debugQueue) | |
350 | debugQueue->release(); | |
351 | ||
352 | if (networkInterface) | |
353 | networkInterface->release(); | |
354 | ||
355 | if (mbufCursor) | |
356 | mbufCursor->release(); | |
357 | ||
358 | if ( mediumDict ) | |
359 | mediumDict->release(); | |
360 | ||
361 | for (i = 0; i < rxMaxCommand; i++) | |
362 | if (rxMbuf[i]) freePacket(rxMbuf[i]); | |
363 | ||
364 | for (i = 0; i < txMaxCommand; i++) | |
365 | if (txMbuf[i]) freePacket(txMbuf[i]); | |
366 | ||
367 | for (i = 0; i < MEMORY_MAP_COUNT; i++) | |
368 | if (maps[i]) maps[i]->release(); | |
369 | ||
370 | if (dmaMemory.ptr) | |
371 | { | |
372 | IOFree(dmaMemory.ptrReal, dmaMemory.sizeReal); | |
373 | dmaMemory.ptr = 0; | |
374 | } | |
375 | ||
376 | if ( workLoop ) | |
377 | { | |
378 | workLoop->release(); | |
379 | workLoop = 0; | |
380 | } | |
381 | ||
382 | super::free(); | |
383 | } | |
384 | ||
385 | /*------------------------------------------------------------------------- | |
386 | * Override IONetworkController::createWorkLoop() method and create | |
387 | * a workloop. | |
388 | * | |
389 | *-------------------------------------------------------------------------*/ | |
390 | ||
391 | bool BMacEnet::createWorkLoop() | |
392 | { | |
393 | workLoop = IOWorkLoop::workLoop(); | |
394 | ||
395 | return ( workLoop != 0 ); | |
396 | } | |
397 | ||
398 | /*------------------------------------------------------------------------- | |
399 | * Override IOService::getWorkLoop() method to return our workloop. | |
400 | * | |
401 | * | |
402 | *-------------------------------------------------------------------------*/ | |
403 | ||
404 | IOWorkLoop * BMacEnet::getWorkLoop() const | |
405 | { | |
406 | return workLoop; | |
407 | } | |
408 | ||
409 | /*------------------------------------------------------------------------- | |
410 | * | |
411 | * | |
412 | * | |
413 | *-------------------------------------------------------------------------*/ | |
414 | ||
415 | void BMacEnet::interruptOccurredForSource(IOInterruptEventSource *src, | |
416 | int /*count*/) | |
417 | { | |
418 | bool doFlushQueue = false; | |
419 | bool doService = false; | |
420 | ||
421 | reserveDebuggerLock(); | |
422 | ||
423 | statReg = ReadBigMacRegister( ioBaseEnet, kSTAT ); | |
424 | ||
425 | if (src == rxIntSrc) { | |
426 | KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 ); | |
427 | doFlushQueue = _receiveInterruptOccurred(); | |
428 | KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 ); | |
429 | } | |
430 | else { | |
431 | /* | |
432 | * On the transmit side, we use the chipset interrupt. Using the | |
433 | * transmit DMA interrupt (or having multiple transmit DMA entries) | |
434 | * would allows us to send the next frame to the chipset prior the | |
435 | * transmit fifo going empty. | |
436 | * However, this aggrevates a BMac chipset bug where the next frame going | |
437 | * out gets corrupted (first two bytes lost) if the chipset had to retry | |
438 | * the previous frame. | |
439 | */ | |
440 | txWDInterrupts++; | |
441 | KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 ); | |
442 | doService = _transmitInterruptOccurred(); | |
443 | KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 ); | |
444 | } | |
445 | ||
446 | releaseDebuggerLock(); | |
447 | ||
448 | /* | |
449 | * Submit all received packets queued up by _receiveInterruptOccurred() | |
450 | * to the network stack. The up call is performed without holding the | |
451 | * debugger lock. | |
452 | */ | |
453 | if ( doFlushQueue ) | |
454 | networkInterface->flushInputQueue(); | |
455 | ||
456 | /* | |
457 | * Unstall the output queue if some space was made available. | |
458 | */ | |
459 | if ( doService && netifEnabled ) | |
460 | transmitQueue->service(); | |
461 | } | |
462 | ||
463 | /*------------------------------------------------------------------------- | |
464 | * | |
465 | * | |
466 | * | |
467 | *-------------------------------------------------------------------------*/ | |
468 | ||
469 | UInt32 BMacEnet::outputPacket(struct mbuf * pkt, void * param) | |
470 | { | |
471 | u_int32_t i; | |
472 | UInt32 ret = kIOReturnOutputSuccess; | |
473 | ||
474 | KERNEL_DEBUG(DBG_BMAC_TXQUEUE | DBG_FUNC_NONE, (int) pkt, | |
475 | (int) pkt->m_pkthdr.len, 0, 0, 0 ); | |
476 | ||
477 | /* | |
478 | * Hold the debugger lock so the debugger can't interrupt us | |
479 | */ | |
480 | reserveDebuggerLock(); | |
481 | ||
482 | do | |
483 | { | |
484 | /* | |
485 | * Preliminary sanity checks | |
486 | */ | |
487 | assert( pkt && netifEnabled ); | |
488 | ||
489 | #if 0 | |
490 | /* | |
491 | * Remove any completed packets from the Tx ring | |
492 | */ | |
493 | if ( chipId >= kCHIPID_PaddingtonXmitStreaming ) | |
494 | { | |
495 | _transmitInterruptOccurred(); | |
496 | } | |
497 | #endif | |
498 | ||
499 | i = txCommandTail + 1; | |
500 | if ( i >= txMaxCommand ) i = 0; | |
501 | if ( i == txCommandHead ) | |
502 | { | |
503 | /* | |
504 | * Ring buffer is full. Disable the dequeueing process. | |
505 | * We reenable it when an entry is made available by the | |
506 | * transmit interrupt handler, or if a timeout occurs. | |
507 | */ | |
508 | ret = kIOReturnOutputStall; | |
509 | continue; | |
510 | } | |
511 | ||
512 | /* | |
513 | * If there is space on the Tx ring, add the packet directly to the | |
514 | * ring. | |
515 | */ | |
516 | _transmitPacket(pkt); | |
517 | } | |
518 | while ( 0 ); | |
519 | ||
520 | releaseDebuggerLock(); | |
521 | ||
522 | return ret; | |
523 | } | |
524 | ||
525 | /*------------------------------------------------------------------------- | |
526 | * | |
527 | * | |
528 | * | |
529 | *-------------------------------------------------------------------------*/ | |
530 | ||
531 | bool BMacEnet::_resetAndEnable(bool enable) | |
532 | { | |
533 | bool ret = true; | |
534 | ||
535 | // reserveDebuggerLock(); | |
536 | ||
537 | ready = false; | |
538 | ||
539 | if (timerSrc) timerSrc->cancelTimeout(); | |
540 | ||
541 | _disableAdapterInterrupts(); | |
542 | if (getWorkLoop()) getWorkLoop()->disableAllInterrupts(); | |
543 | ||
544 | if (enable) | |
545 | { | |
546 | phyId = 0xff; | |
547 | } | |
548 | ||
549 | _resetChip(); | |
550 | ||
551 | // Initialize the link status. | |
552 | ||
553 | phyStatusPrev = 0; | |
554 | setLinkStatus( 0, 0 ); | |
555 | ||
556 | while (enable) | |
557 | { | |
558 | if (!_initRxRing() || !_initTxRing()) | |
559 | { | |
560 | ret = false; | |
561 | break; | |
562 | } | |
563 | ||
564 | if ( phyId != 0xff ) | |
565 | { | |
566 | miiInitializePHY(phyId); | |
567 | } | |
568 | ||
569 | if (_initChip() == false) | |
570 | { | |
571 | ret = false; | |
572 | break; | |
573 | } | |
574 | ||
575 | _startChip(); | |
576 | ||
577 | timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS); | |
578 | ||
579 | if (getWorkLoop()) getWorkLoop()->enableAllInterrupts(); | |
580 | _enableAdapterInterrupts(); | |
581 | ||
582 | ready = true; | |
583 | ||
584 | _sendDummyPacket(); | |
585 | ||
586 | monitorLinkStatus( true ); | |
587 | ||
588 | break; | |
589 | } | |
590 | ||
591 | // releaseDebuggerLock(); | |
592 | ||
593 | return ret; | |
594 | } | |
595 | ||
596 | /*------------------------------------------------------------------------- | |
597 | * Grab a pointer to the statistics counters. | |
598 | * | |
599 | * | |
600 | *-------------------------------------------------------------------------*/ | |
601 | ||
602 | bool BMacEnet::configureInterface( IONetworkInterface * netif ) | |
603 | { | |
604 | IONetworkData * nd; | |
605 | ||
606 | if ( super::configureInterface( netif ) == false ) | |
607 | return false; | |
608 | ||
609 | /* | |
610 | * Grab a pointer to the statistics structure in the interface. | |
611 | */ | |
612 | nd = netif->getNetworkData( kIONetworkStatsKey ); | |
613 | ||
614 | if ( !nd || !(netStats = (IONetworkStats *) nd->getBuffer()) ) | |
615 | { | |
616 | IOLog("EtherNet(BMac): invalid network statistics\n"); | |
617 | return false; | |
618 | } | |
619 | ||
620 | return true; | |
621 | } | |
622 | ||
623 | /*------------------------------------------------------------------------- | |
624 | * Called by IOEthernetInterface client to enable the controller. | |
625 | * This method is always called while running on the default workloop | |
626 | * thread. | |
627 | *-------------------------------------------------------------------------*/ | |
628 | ||
629 | IOReturn BMacEnet::enable(IONetworkInterface * netif) | |
630 | { | |
631 | // If an interface client has previously enabled us, | |
632 | // and we know there can only be one interface client | |
633 | // for this driver, then simply return true. | |
634 | // | |
635 | if ( netifEnabled ) | |
636 | { | |
637 | IOLog("EtherNet(BMac): already enabled\n"); | |
638 | return kIOReturnSuccess; | |
639 | } | |
640 | ||
641 | if ( (ready == false) && !_resetAndEnable(true) ) | |
642 | return kIOReturnIOError; | |
643 | ||
644 | // Record the interface as an active client. | |
645 | // | |
646 | netifEnabled = true; | |
647 | ||
648 | // Start our IOOutputQueue object. | |
649 | // | |
650 | transmitQueue->setCapacity(TRANSMIT_QUEUE_SIZE); | |
651 | transmitQueue->start(); | |
652 | ||
653 | return kIOReturnSuccess; | |
654 | } | |
655 | ||
656 | /*------------------------------------------------------------------------- | |
657 | * Called by IOEthernetInterface client to disable the controller. | |
658 | * This method is always called while running on the default workloop | |
659 | * thread. | |
660 | *-------------------------------------------------------------------------*/ | |
661 | ||
662 | IOReturn BMacEnet::disable(IONetworkInterface * /*netif*/) | |
663 | { | |
664 | // Disable our IOOutputQueue object. This will prevent the | |
665 | // outputPacket() method from being called. | |
666 | // | |
667 | transmitQueue->stop(); | |
668 | ||
669 | // Flush all packets currently in the output queue. | |
670 | // | |
671 | transmitQueue->setCapacity(0); | |
672 | transmitQueue->flush(); | |
673 | ||
674 | // If we have no active clients, then disable the controller. | |
675 | // | |
676 | if ( debugEnabled == false ) | |
677 | { | |
678 | _resetAndEnable(false); | |
679 | } | |
680 | ||
681 | netifEnabled = false; | |
682 | ||
683 | return kIOReturnSuccess; | |
684 | } | |
685 | ||
686 | /*------------------------------------------------------------------------- | |
687 | * This method is called by our debugger client to bring up the controller | |
688 | * just before the controller is registered as the debugger device. The | |
689 | * debugger client is attached in response to the attachDebuggerClient() | |
690 | * call. | |
691 | * | |
692 | * This method is always called while running on the default workloop | |
693 | * thread. | |
694 | *-------------------------------------------------------------------------*/ | |
695 | ||
696 | IOReturn BMacEnet::enable(IOKernelDebugger *debugger) | |
697 | { | |
698 | // Enable hardware and make it ready to support the debugger client. | |
699 | // | |
700 | if ( (ready == false) && !_resetAndEnable(true) ) | |
701 | { | |
702 | return kIOReturnIOError; | |
703 | } | |
704 | ||
705 | // Record the debugger as an active client of ours. | |
706 | // | |
707 | debugEnabled = true; | |
708 | ||
709 | return kIOReturnSuccess; | |
710 | } | |
711 | ||
712 | /*------------------------------------------------------------------------- | |
713 | * This method is called by our debugger client to stop the controller. | |
714 | * The debugger will call this method when we issue a detachDebuggerClient(). | |
715 | * | |
716 | * This method is always called while running on the default workloop | |
717 | * thread. | |
718 | *-------------------------------------------------------------------------*/ | |
719 | ||
720 | IOReturn BMacEnet::disable(IOKernelDebugger * /*debugger*/) | |
721 | { | |
722 | debugEnabled = false; | |
723 | ||
724 | // If we have no active clients, then disable the controller. | |
725 | // | |
726 | if ( netifEnabled == false ) | |
727 | { | |
728 | _resetAndEnable(false); | |
729 | } | |
730 | ||
731 | return kIOReturnSuccess; | |
732 | } | |
733 | ||
734 | /*------------------------------------------------------------------------- | |
735 | * | |
736 | * | |
737 | * | |
738 | *-------------------------------------------------------------------------*/ | |
739 | ||
740 | void BMacEnet::timeoutOccurred(IOTimerEventSource * /*timer*/) | |
741 | { | |
742 | u_int32_t dmaStatus; | |
743 | bool doFlushQueue = false; | |
744 | bool doService = false; | |
745 | ||
746 | if ( ready == false ) | |
747 | { | |
748 | return; | |
749 | } | |
750 | ||
751 | // IOLog("Ethernet(BMac): watchdog timer\n"); | |
752 | ||
753 | reserveDebuggerLock(); | |
754 | ||
755 | /* | |
756 | * Check for DMA shutdown on receive channel | |
757 | */ | |
758 | dmaStatus = IOGetDBDMAChannelStatus( ioBaseEnetRxDMA ); | |
759 | if ( !(dmaStatus & kdbdmaActive) ) | |
760 | { | |
761 | #if 0 | |
762 | IOLog( "Ethernet(BMac): Timeout check - RxHead = %d RxTail = %d\n", | |
763 | rxCommandHead, rxCommandTail); | |
764 | ||
765 | IOLog( "Ethernet(BMac): Rx Commands = %08x(p) Rx DMA Ptr = %08x(p)\n\r", rxDMACommandsPhys, IOGetDBDMACommandPtr(ioBaseEnetRxDMA) ); | |
766 | [self _dumpDesc:(void *)rxDMACommands Size:rxMaxCommand * sizeof(enet_dma_cmd_t)]; | |
767 | #endif | |
768 | ||
769 | doFlushQueue = _receiveInterruptOccurred(); | |
770 | } | |
771 | ||
772 | /* | |
773 | * If there are pending entries on the Tx ring | |
774 | */ | |
775 | if ( txCommandHead != txCommandTail ) | |
776 | { | |
777 | /* | |
778 | * If we did not service the Tx ring during the last timeout interval, | |
779 | * then force servicing of the Tx ring | |
780 | */ | |
781 | if ( txWDInterrupts == 0 ) | |
782 | { | |
783 | if ( txWDCount++ > 0 ) | |
784 | { | |
785 | if (_transmitInterruptOccurred() == false) | |
786 | { | |
787 | #if 0 | |
788 | IOLog( "Ethernet(BMac): Timeout check - TxHead = %d TxTail = %d\n", | |
789 | txCommandHead, txCommandTail); | |
790 | #endif | |
791 | _restartTransmitter(); | |
792 | } | |
793 | doService = true; | |
794 | } | |
795 | } | |
796 | else | |
797 | { | |
798 | txWDInterrupts = 0; | |
799 | txWDCount = 0; | |
800 | } | |
801 | } | |
802 | else | |
803 | { | |
804 | txWDInterrupts = 0; | |
805 | txWDCount = 0; | |
806 | } | |
807 | ||
808 | // Poll link status periodically. | |
809 | ||
810 | monitorLinkStatus(); | |
811 | ||
812 | // Clean-up after the debugger if the debugger was active. | |
813 | // | |
814 | if ( debugTxPoll ) | |
815 | { | |
816 | debugQueue->flush(); | |
817 | debugTxPoll = false; | |
818 | doService = true; | |
819 | } | |
820 | ||
821 | releaseDebuggerLock(); | |
822 | ||
823 | /* | |
824 | * Submit all received packets queued up by _receiveInterruptOccurred() | |
825 | * to the network stack. This call is performed without holding the | |
826 | * debugger lock. | |
827 | */ | |
828 | if ( doFlushQueue ) | |
829 | { | |
830 | networkInterface->flushInputQueue(); | |
831 | } | |
832 | ||
833 | /* | |
834 | * Make sure the output queue is not stalled. | |
835 | */ | |
836 | if ( doService && netifEnabled ) | |
837 | { | |
838 | transmitQueue->service(); | |
839 | } | |
840 | ||
841 | /* | |
842 | * Restart the watchdog timer | |
843 | */ | |
844 | timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS); | |
845 | } | |
846 | ||
847 | /*------------------------------------------------------------------------- | |
848 | * | |
849 | * | |
850 | * | |
851 | *-------------------------------------------------------------------------*/ | |
852 | ||
853 | void BMacEnet::monitorLinkStatus( bool firstPoll ) | |
854 | { | |
855 | u_int16_t phyStatus; | |
856 | u_int16_t phyReg; | |
857 | u_int16_t phyStatusChange; | |
858 | bool fullDuplex; | |
859 | bool reportLinkStatus = false; | |
860 | UInt32 linkSpeed; | |
861 | UInt32 linkStatus; | |
862 | ||
863 | if ( phyId == 0xff ) | |
864 | { | |
865 | // For implementations without a PHY, query the link status bit from | |
866 | // the transceiver control register (kXCVRIF). | |
867 | ||
868 | phyStatus = ReadBigMacRegister(ioBaseEnet, kXCVRIF); | |
869 | ||
870 | if ( ( ( phyStatus ^ phyStatusPrev ) & kLinkStatus ) || firstPoll ) | |
871 | { | |
872 | linkStatus = kIONetworkLinkValid; | |
873 | linkSpeed = 0; | |
874 | fullDuplex = false; | |
875 | reportLinkStatus = true; | |
876 | ||
877 | if ( ( phyStatus & kLinkStatus ) == 0 ) | |
878 | { | |
879 | linkSpeed = 10; | |
880 | linkStatus |= kIONetworkLinkActive; | |
881 | } | |
882 | ||
883 | phyStatusPrev = phyStatus; | |
884 | } | |
885 | } | |
886 | else if ( miiReadWord(&phyStatus, MII_STATUS, phyId) == true ) | |
887 | { | |
888 | phyStatusChange = ( phyStatusPrev ^ phyStatus ) & | |
889 | ( MII_STATUS_LINK_STATUS | | |
890 | MII_STATUS_NEGOTIATION_COMPLETE); | |
891 | ||
892 | if ( phyStatusChange || firstPoll ) | |
893 | { | |
894 | if ( firstPoll ) | |
895 | { | |
896 | // For the initial link status poll, wait a bit, then | |
897 | // re-read the status register to clear any latched bits. | |
898 | // Why wait? Well, the debugger can kick in shortly after | |
899 | // this function returns, and we want the duplex setting | |
900 | // on the MAC to match the PHY. | |
901 | ||
902 | miiWaitForAutoNegotiation( phyId ); | |
903 | miiReadWord(&phyStatus, MII_STATUS, phyId); | |
904 | miiReadWord(&phyStatus, MII_STATUS, phyId); | |
905 | } | |
906 | ||
907 | if ( (phyStatus & MII_STATUS_LINK_STATUS) && | |
908 | (firstPoll || (phyStatus & MII_STATUS_NEGOTIATION_COMPLETE)) ) | |
909 | { | |
910 | if ( (phyType & MII_ST10040_MASK) == MII_ST10040_ID ) | |
911 | { | |
912 | miiReadWord(&phyReg, MII_ST10040_CHIPST, phyId); | |
913 | linkSpeed = (phyReg & MII_ST10040_CHIPST_SPEED) ? | |
914 | 100 : 10; | |
915 | fullDuplex = (phyReg & MII_ST10040_CHIPST_DUPLEX) ? | |
916 | true : false; | |
917 | } | |
918 | else if ( (phyType & MII_DP83843_MASK) == MII_DP83843_ID ) | |
919 | { | |
920 | miiReadWord(&phyReg, MII_DP83843_PHYSTS, phyId); | |
921 | linkSpeed = (phyReg & MII_DP83843_PHYSTS_SPEED10) ? | |
922 | 10 : 100; | |
923 | fullDuplex = (phyReg & MII_DP83843_PHYSTS_DUPLEX) ? | |
924 | true : false; | |
925 | } | |
926 | else | |
927 | { | |
928 | linkSpeed = 0; | |
929 | fullDuplex = false; | |
930 | } | |
931 | ||
932 | if ( fullDuplex != isFullDuplex ) | |
933 | { | |
934 | _setDuplexMode(fullDuplex); | |
935 | } | |
936 | ||
937 | linkStatus = kIONetworkLinkActive | kIONetworkLinkValid; | |
938 | } | |
939 | else | |
940 | { | |
941 | linkStatus = kIONetworkLinkValid; | |
942 | linkSpeed = 0; | |
943 | fullDuplex = false; | |
944 | } | |
945 | ||
946 | reportLinkStatus = true; | |
947 | phyStatusPrev = phyStatus; | |
948 | } | |
949 | } | |
950 | ||
951 | if ( reportLinkStatus ) | |
952 | { | |
953 | IONetworkMedium * medium; | |
954 | IOMediumType mediumType; | |
955 | ||
956 | switch ( linkSpeed ) | |
957 | { | |
958 | case 10: | |
959 | mediumType = kIOMediumEthernet10BaseT; | |
960 | mediumType |= (fullDuplex == true) ? | |
961 | kIOMediumOptionFullDuplex : | |
962 | kIOMediumOptionHalfDuplex; | |
963 | break; | |
964 | case 100: | |
965 | mediumType = kIOMediumEthernet100BaseTX; | |
966 | mediumType |= (fullDuplex == true) ? | |
967 | kIOMediumOptionFullDuplex : | |
968 | kIOMediumOptionHalfDuplex; | |
969 | break; | |
970 | default: | |
971 | mediumType = kIOMediumEthernetNone; | |
972 | break; | |
973 | } | |
974 | ||
975 | medium = IONetworkMedium::getMediumWithType(mediumDict, mediumType); | |
976 | ||
977 | setLinkStatus( linkStatus, medium, linkSpeed * 1000000 ); | |
978 | ||
979 | if ( linkStatus & kIONetworkLinkActive ) | |
980 | IOLog( "Ethernet(BMac): Link up at %ld Mbps - %s Duplex\n", | |
981 | linkSpeed, (fullDuplex) ? "Full" : "Half" ); | |
982 | else | |
983 | IOLog( "Ethernet(BMac): Link down\n" ); | |
984 | } | |
985 | } | |
986 | ||
987 | /*------------------------------------------------------------------------- | |
988 | * | |
989 | * | |
990 | * | |
991 | *-------------------------------------------------------------------------*/ | |
992 | ||
993 | const OSString * BMacEnet::newVendorString() const | |
994 | { | |
995 | return OSString::withCString("Apple"); | |
996 | } | |
997 | ||
998 | const OSString * BMacEnet::newModelString() const | |
999 | { | |
1000 | return OSString::withCString("BMac"); | |
1001 | } | |
1002 | ||
1003 | const OSString * BMacEnet::newRevisionString() const | |
1004 | { | |
1005 | return OSString::withCString(""); | |
1006 | } | |
1007 | ||
1008 | /*------------------------------------------------------------------------- | |
1009 | * | |
1010 | * | |
1011 | * | |
1012 | *-------------------------------------------------------------------------*/ | |
1013 | ||
1014 | IOReturn BMacEnet::setPromiscuousMode(IOEnetPromiscuousMode mode) | |
1015 | { | |
1016 | u_int16_t rxCFGVal; | |
1017 | ||
1018 | reserveDebuggerLock(); | |
1019 | ||
1020 | /* | |
1021 | * Turn off the receiver and wait for the chipset to acknowledge | |
1022 | */ | |
1023 | rxCFGVal = ReadBigMacRegister(ioBaseEnet, kRXCFG); | |
1024 | WriteBigMacRegister(ioBaseEnet, kRXCFG, rxCFGVal & ~kRxMACEnable ); | |
1025 | while( ReadBigMacRegister(ioBaseEnet, kRXCFG) & kRxMACEnable ) | |
1026 | ; | |
1027 | ||
1028 | /* | |
1029 | * Set or reset promiscuous mode and restore receiver state | |
1030 | */ | |
1031 | if (mode == kIOEnetPromiscuousModeOff) { | |
1032 | rxCFGVal &= ~kRxPromiscEnable; | |
1033 | isPromiscuous = false; | |
1034 | } | |
1035 | else { | |
1036 | rxCFGVal |= kRxPromiscEnable; | |
1037 | isPromiscuous = true; | |
1038 | } | |
1039 | ||
1040 | WriteBigMacRegister( ioBaseEnet, kRXCFG, rxCFGVal ); | |
1041 | ||
1042 | releaseDebuggerLock(); | |
1043 | ||
1044 | return kIOReturnSuccess; | |
1045 | } | |
1046 | ||
1047 | IOReturn BMacEnet::setMulticastMode(IOEnetMulticastMode mode) | |
1048 | { | |
1049 | multicastEnabled = (mode == kIOEnetMulticastModeOff) ? false : true; | |
1050 | ||
1051 | return kIOReturnSuccess; | |
1052 | } | |
1053 | ||
1054 | IOReturn BMacEnet::setMulticastList(IOEthernetAddress *addrs, UInt32 count) | |
1055 | { | |
1056 | reserveDebuggerLock(); | |
1057 | _resetHashTableMask(); | |
1058 | for (UInt32 i = 0; i < count; i++) { | |
1059 | _addToHashTableMask(addrs->bytes); | |
1060 | addrs++; | |
1061 | } | |
1062 | _updateBMacHashTableMask(); | |
1063 | releaseDebuggerLock(); | |
1064 | return kIOReturnSuccess; | |
1065 | } | |
1066 | ||
1067 | /*------------------------------------------------------------------------- | |
1068 | * | |
1069 | * | |
1070 | * | |
1071 | *-------------------------------------------------------------------------*/ | |
1072 | ||
1073 | static struct MediumTable | |
1074 | { | |
1075 | UInt32 type; | |
1076 | UInt32 speed; | |
1077 | } | |
1078 | mediumTable[] = | |
1079 | { | |
1080 | { kIOMediumEthernetNone , 0 }, | |
1081 | { kIOMediumEthernetAuto , 0 }, | |
1082 | { kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex, 10 }, | |
1083 | { kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex, 10 }, | |
1084 | { kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex, 100 }, | |
1085 | { kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex, 100 }, | |
1086 | }; | |
1087 | ||
1088 | ||
1089 | bool BMacEnet::createMediumTables() | |
1090 | { | |
1091 | IONetworkMedium *medium; | |
1092 | UInt32 i; | |
1093 | ||
1094 | mediumDict = OSDictionary::withCapacity( sizeof(mediumTable)/sizeof(mediumTable[0]) ); | |
1095 | if ( mediumDict == 0 ) return false; | |
1096 | ||
1097 | for ( i=0; i < sizeof(mediumTable)/sizeof(mediumTable[0]); i++ ) | |
1098 | { | |
1099 | medium = IONetworkMedium::medium( mediumTable[i].type, mediumTable[i].speed ); | |
1100 | if ( medium != 0 ) | |
1101 | { | |
1102 | IONetworkMedium::addMedium( mediumDict, medium ); | |
1103 | medium->release(); | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | if ( publishMediumDictionary( mediumDict ) != true ) | |
1108 | { | |
1109 | return false; | |
1110 | } | |
1111 | ||
1112 | medium = IONetworkMedium::getMediumWithType( mediumDict, | |
1113 | kIOMediumEthernetAuto ); | |
1114 | ||
1115 | setCurrentMedium( medium ); | |
1116 | ||
1117 | return true; | |
1118 | } | |
1119 | ||
1120 | /*------------------------------------------------------------------------- | |
1121 | * | |
1122 | * | |
1123 | * | |
1124 | *-------------------------------------------------------------------------*/ | |
1125 | ||
1126 | /* | |
1127 | * Kernel Debugger Support | |
1128 | */ | |
1129 | void BMacEnet::sendPacket( void * pkt, UInt32 pkt_len ) | |
1130 | { | |
1131 | _sendPacket(pkt, pkt_len); | |
1132 | } | |
1133 | ||
1134 | void BMacEnet::receivePacket( void * pkt, | |
1135 | UInt32 * pkt_len, | |
1136 | UInt32 timeout ) | |
1137 | { | |
1138 | _receivePacket(pkt, (UInt *) pkt_len, timeout); | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | * Create a WorkLoop serialized output queue object. | |
1143 | */ | |
1144 | IOOutputQueue * BMacEnet::createOutputQueue() | |
1145 | { | |
1146 | return IOGatedOutputQueue::withTarget( this, | |
1147 | getWorkLoop(), | |
1148 | TRANSMIT_QUEUE_SIZE ); | |
1149 | } | |
1150 | ||
1151 | /* | |
1152 | * Power management methods. | |
1153 | */ | |
1154 | ||
1155 | IOReturn BMacEnet::registerWithPolicyMaker(IOService * policyMaker) | |
1156 | { | |
1157 | #define number_of_power_states 2 | |
1158 | ||
1159 | static IOPMPowerState ourPowerStates[number_of_power_states] = { | |
1160 | {1,0,0,0,0,0,0,0,0,0,0,0}, | |
1161 | {1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0} | |
1162 | }; | |
1163 | ||
1164 | currentPowerState = 1; | |
1165 | ||
1166 | return policyMaker->registerPowerDriver( this, | |
1167 | ourPowerStates, | |
1168 | number_of_power_states ); | |
1169 | } | |
1170 | ||
1171 | IOReturn BMacEnet::setPowerState( unsigned long powerStateOrdinal, | |
1172 | IOService * whatDevice ) | |
1173 | { | |
1174 | IOReturn ret = IOPMAckImplied; | |
1175 | ||
1176 | // kprintf("Ethernet(BMac): setPowerState %d\n", powerStateOrdinal); | |
1177 | ||
1178 | if ( currentPowerState == powerStateOrdinal ) | |
1179 | return IOPMAckImplied; | |
1180 | ||
1181 | switch ( powerStateOrdinal ) | |
1182 | { | |
1183 | case 0: | |
1184 | kprintf("Ethernet(BMac): powering off\n"); | |
1185 | currentPowerState = powerStateOrdinal; | |
1186 | break; | |
1187 | ||
1188 | case 1: | |
1189 | kprintf("Ethernet(BMac): powering on\n"); | |
1190 | currentPowerState = powerStateOrdinal; | |
1191 | break; | |
1192 | ||
1193 | default: | |
1194 | ret = IOPMNoSuchState; | |
1195 | break; | |
1196 | } | |
1197 | ||
1198 | return ret; | |
1199 | } |