]>
Commit | Line | Data |
---|---|---|
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 Apple Computer | |
24 | * | |
25 | * Hardware independent (relatively) code for the Sun GEM Ethernet Controller | |
26 | * | |
27 | * HISTORY | |
28 | * | |
29 | * dd-mmm-yy | |
30 | * Created. | |
31 | * | |
32 | */ | |
33 | ||
34 | //void call_kdp(void); | |
35 | ||
36 | #include "UniNEnetPrivate.h" | |
37 | ||
38 | #define super IOEthernetController | |
39 | ||
40 | OSDefineMetaClassAndStructors( UniNEnet, IOEthernetController ) | |
41 | ||
42 | /*------------------------------------------------------------------------- | |
43 | * | |
44 | * | |
45 | * | |
46 | *-------------------------------------------------------------------------*/ | |
47 | ||
48 | bool UniNEnet::init(OSDictionary * properties) | |
49 | { | |
50 | if ( super::init(properties) == false ) | |
51 | return false; | |
52 | ||
53 | /* | |
54 | * Initialize my ivars. | |
55 | */ | |
56 | phyId = 0xff; | |
57 | linkStatusPrev = kLinkStatusUnknown; | |
58 | ||
59 | return true; | |
60 | } | |
61 | ||
62 | /*------------------------------------------------------------------------- | |
63 | * | |
64 | * | |
65 | * | |
66 | *-------------------------------------------------------------------------*/ | |
67 | ||
68 | bool UniNEnet::start(IOService * provider) | |
69 | { | |
70 | OSString *matchEntry; | |
71 | ||
72 | keyLargo_resetUniNEthernetPhy = OSSymbol::withCString("keyLargo_resetUniNEthernetPhy"); | |
73 | ||
74 | // Wait for KeyLargo to show up. | |
75 | keyLargo = waitForService(serviceMatching("KeyLargo")); | |
76 | if (keyLargo == 0) return false; | |
77 | ||
78 | nub = OSDynamicCast(IOPCIDevice, provider); | |
79 | ||
80 | if (!nub || !super::start(provider)) | |
81 | { | |
82 | return false; | |
83 | } | |
84 | ||
85 | // Create spinlock to protect TxElementQueue. | |
86 | ||
87 | txQueueLock = IOSimpleLockAlloc(); | |
88 | if ( txQueueLock == 0 ) return false; | |
89 | IOSimpleLockInit(txQueueLock); | |
90 | ||
91 | transmitQueue = (IOGatedOutputQueue *) getOutputQueue(); | |
92 | if (!transmitQueue) | |
93 | { | |
94 | IOLog("Ethernet(UniN): Output queue initialization failed\n"); | |
95 | return false; | |
96 | } | |
97 | transmitQueue->retain(); | |
98 | ||
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 | { | |
111 | return false; | |
112 | } | |
113 | ||
114 | /* | |
115 | * Allocate a IOMbufBigMemoryCursor instance. Currently, the maximum | |
116 | * number of segments is set to 1. The maximum length for each segment | |
117 | * is set to the maximum ethernet frame size (plus padding). | |
118 | */ | |
119 | mbufCursor = IOMbufBigMemoryCursor::withSpecification(NETWORK_BUFSIZE, 1); | |
120 | if (!mbufCursor) | |
121 | { | |
122 | IOLog("Ethernet(UniN): IOMbufBigMemoryCursor allocation failure\n"); | |
123 | return false; | |
124 | } | |
125 | ||
126 | matchEntry = OSDynamicCast( OSString, getProperty( gIONameMatchedKey ) ); | |
127 | if ( matchEntry == 0 ) | |
128 | { | |
129 | IOLog("Ethernet(UniN): Cannot obtain matching property.\n"); | |
130 | return false; | |
131 | } | |
132 | ||
133 | if ( matchEntry->isEqualTo( "gmac" ) == true ) | |
134 | { | |
135 | callPlatformFunction("EnableUniNEthernetClock", true, | |
136 | (void *)true, 0, 0, 0); | |
137 | } | |
138 | ||
139 | /* | |
140 | * BUS MASTER, MEM I/O Space, MEM WR & INV | |
141 | */ | |
142 | nub->configWrite32( 0x04, 0x16 ); | |
143 | ||
144 | /* | |
145 | * set Latency to Max , cache 32 | |
146 | */ | |
147 | nub->configWrite32( 0x0C, ((2 + (kGEMBurstSize * (0+1)))<< 8) | (CACHE_LINE_SIZE >> 2) ); | |
148 | ||
149 | ioMapEnet = nub->mapDeviceMemoryWithRegister( 0x10 ); | |
150 | if ( ioMapEnet == NULL ) | |
151 | { | |
152 | return false; | |
153 | } | |
154 | ioBaseEnet = (volatile IOPPCAddress)ioMapEnet->getVirtualAddress(); | |
155 | fpRegs = (GMAC_Registers*) ioBaseEnet; | |
156 | phyId = (UInt8) -1; | |
157 | ||
158 | /* | |
159 | * Get a reference to the IOWorkLoop in our superclass. | |
160 | */ | |
161 | IOWorkLoop * myWorkLoop = getWorkLoop(); | |
162 | ||
163 | /* | |
164 | * Allocate three IOInterruptEventSources. | |
165 | */ | |
166 | interruptSource = IOInterruptEventSource::interruptEventSource( | |
167 | (OSObject *) this, | |
168 | (IOInterruptEventAction) &UniNEnet::interruptOccurred, | |
169 | (IOService *) provider, | |
170 | (int) 0 ); | |
171 | ||
172 | if ( interruptSource == NULL ) | |
173 | { | |
174 | IOLog("Ethernet(UniN): Couldn't allocate Interrupt event source\n"); | |
175 | return false; | |
176 | } | |
177 | ||
178 | if ( myWorkLoop->addEventSource( interruptSource ) != kIOReturnSuccess ) | |
179 | { | |
180 | IOLog("Ethernet(UniN): Couldn't add Interrupt event source\n"); | |
181 | return false; | |
182 | } | |
183 | ||
184 | ||
185 | timerSource = IOTimerEventSource::timerEventSource | |
186 | (this, (IOTimerEventSource::Action) &UniNEnet::timeoutOccurred); | |
187 | if ( timerSource == NULL ) | |
188 | { | |
189 | IOLog("Ethernet(UniN): Couldn't allocate timer event source\n"); | |
190 | return false; | |
191 | } | |
192 | ||
193 | if ( myWorkLoop->addEventSource( timerSource ) != kIOReturnSuccess ) | |
194 | { | |
195 | IOLog("Ethernet(UniN): Couldn't add timer event source\n"); | |
196 | return false; | |
197 | } | |
198 | ||
199 | MGETHDR(txDebuggerPkt, M_DONTWAIT, MT_DATA); | |
200 | ||
201 | if (!txDebuggerPkt) | |
202 | { | |
203 | IOLog("Ethernet(UniN): Couldn't allocate KDB buffer\n"); | |
204 | return false; | |
205 | } | |
206 | ||
207 | /* | |
208 | * Perform a hardware reset. | |
209 | */ | |
210 | if ( resetAndEnable(false) == false ) | |
211 | { | |
212 | IOLog("Ethernet(UniN): resetAndEnable() failed\n"); | |
213 | return false; | |
214 | } | |
215 | ||
216 | /* | |
217 | * Cache my MAC address. | |
218 | */ | |
219 | if ( getHardwareAddress(&myAddress) != kIOReturnSuccess ) | |
220 | { | |
221 | IOLog("Ethernet(UniN): getHardwareAddress() failed\n"); | |
222 | return false; | |
223 | } | |
224 | ||
225 | /* | |
226 | * Allocate memory for ring buffers. | |
227 | */ | |
228 | if ( allocateMemory() == false) | |
229 | { | |
230 | IOLog("Ethernet(UniN): allocateMemory() failed\n"); | |
231 | return false; | |
232 | } | |
233 | ||
234 | if ( createMediumTables() == false ) | |
235 | { | |
236 | IOLog("Ethernet(UniN): createMediumTables() failed\n"); | |
237 | return false; | |
238 | } | |
239 | ||
240 | /* | |
241 | * Attach an IOEthernetInterface client. But don't registers it just yet. | |
242 | */ | |
243 | if ( !attachInterface((IONetworkInterface **) &networkInterface, false) ) | |
244 | { | |
245 | IOLog("Ethernet(UniN): attachInterface() failed\n"); | |
246 | return false; | |
247 | } | |
248 | ||
249 | /* | |
250 | * Attach a kernel debugger client. | |
251 | */ | |
252 | attachDebuggerClient(&debugger); | |
253 | ||
254 | /* | |
255 | * Ready to service interface requests. | |
256 | */ | |
257 | networkInterface->registerService(); | |
258 | ||
259 | return true; | |
260 | } | |
261 | ||
262 | /*------------------------------------------------------------------------- | |
263 | * | |
264 | * | |
265 | * | |
266 | *-------------------------------------------------------------------------*/ | |
267 | ||
268 | bool UniNEnet::configureInterface(IONetworkInterface * netif) | |
269 | { | |
270 | IONetworkData * nd; | |
271 | ||
272 | if ( super::configureInterface( netif ) == false ) | |
273 | return false; | |
274 | ||
275 | /* | |
276 | * Grab a pointer to the statistics structure in the interface. | |
277 | */ | |
278 | nd = netif->getNetworkData( kIONetworkStatsKey ); | |
279 | if (!nd || !(fpNetStats = (IONetworkStats *) nd->getBuffer())) | |
280 | { | |
281 | IOLog("EtherNet(UniN): invalid network statistics\n"); | |
282 | return false; | |
283 | } | |
284 | ||
285 | // Get the Ethernet statistics structure: | |
286 | nd = netif->getParameter( kIOEthernetStatsKey ); | |
287 | if ( !nd || !(fpEtherStats = (IOEthernetStats*)nd->getBuffer()) ) | |
288 | { | |
289 | IOLog("EtherNet(UniN): invalid ethernet statistics\n"); | |
290 | return false; | |
291 | } | |
292 | ||
293 | /* | |
294 | * Set the driver/stack reentrancy flag. This is meant to reduce | |
295 | * context switches. May become irrelevant in the future. | |
296 | */ | |
297 | return true; | |
298 | }/* end configureInterface */ | |
299 | ||
300 | ||
301 | /*------------------------------------------------------------------------- | |
302 | * | |
303 | * | |
304 | * | |
305 | *-------------------------------------------------------------------------*/ | |
306 | ||
307 | void UniNEnet::free() | |
308 | { | |
309 | TxQueueElement * txElement; | |
310 | ||
311 | resetAndEnable(false); | |
312 | ||
313 | if (debugger) | |
314 | debugger->release(); | |
315 | ||
316 | if (getWorkLoop()) | |
317 | { | |
318 | getWorkLoop()->disableAllEventSources(); | |
319 | } | |
320 | ||
321 | if (timerSource) | |
322 | { | |
323 | timerSource->release(); | |
324 | timerSource = 0; | |
325 | } | |
326 | ||
327 | if (interruptSource) | |
328 | { | |
329 | interruptSource->release(); | |
330 | } | |
331 | ||
332 | if (txDebuggerPkt) | |
333 | { | |
334 | freePacket(txDebuggerPkt); | |
335 | } | |
336 | ||
337 | if (transmitQueue) | |
338 | { | |
339 | transmitQueue->release(); | |
340 | } | |
341 | ||
342 | if (debugQueue) | |
343 | { | |
344 | debugQueue->release(); | |
345 | } | |
346 | ||
347 | if (networkInterface) | |
348 | { | |
349 | networkInterface->release(); | |
350 | } | |
351 | ||
352 | if (mbufCursor) | |
353 | { | |
354 | mbufCursor->release(); | |
355 | } | |
356 | ||
357 | if ( mediumDict ) | |
358 | { | |
359 | mediumDict->release(); | |
360 | } | |
361 | ||
362 | while ( ( txElement = getTxElement() ) ) | |
363 | { | |
364 | IOFree( txElement, sizeof(TxQueueElement) ); | |
365 | } | |
366 | ||
367 | if ( ioMapEnet ) | |
368 | { | |
369 | ioMapEnet->release(); | |
370 | } | |
371 | ||
372 | if ( dmaCommands != 0 ) | |
373 | { | |
374 | IOFreeContiguous( (void *)dmaCommands, dmaCommandsSize ); | |
375 | } | |
376 | ||
377 | if ( workLoop ) | |
378 | { | |
379 | workLoop->release(); | |
380 | workLoop = 0; | |
381 | } | |
382 | ||
383 | if ( txQueueLock ) | |
384 | { | |
385 | IOSimpleLockFree( txQueueLock ); | |
386 | txQueueLock = 0; | |
387 | } | |
388 | ||
389 | super::free(); | |
390 | } | |
391 | ||
392 | /*------------------------------------------------------------------------- | |
393 | * Override IONetworkController::createWorkLoop() method and create | |
394 | * a workloop. | |
395 | * | |
396 | *-------------------------------------------------------------------------*/ | |
397 | ||
398 | bool UniNEnet::createWorkLoop() | |
399 | { | |
400 | workLoop = IOWorkLoop::workLoop(); | |
401 | ||
402 | return ( workLoop != 0 ); | |
403 | } | |
404 | ||
405 | /*------------------------------------------------------------------------- | |
406 | * Override IOService::getWorkLoop() method to return our workloop. | |
407 | * | |
408 | * | |
409 | *-------------------------------------------------------------------------*/ | |
410 | ||
411 | IOWorkLoop * UniNEnet::getWorkLoop() const | |
412 | { | |
413 | return workLoop; | |
414 | } | |
415 | ||
416 | /*------------------------------------------------------------------------- | |
417 | * | |
418 | * | |
419 | * | |
420 | *-------------------------------------------------------------------------*/ | |
421 | ||
422 | void UniNEnet::interruptOccurred(IOInterruptEventSource * src, int /*count*/) | |
423 | { | |
424 | IODebuggerLockState lockState; | |
425 | UInt32 interruptStatus; | |
426 | bool doFlushQueue; | |
427 | bool doService; | |
428 | ||
429 | ||
430 | if ( ready == false ) return; | |
431 | ||
432 | do | |
433 | { | |
434 | lockState = IODebuggerLock( this ); | |
435 | ||
436 | interruptStatus = READ_REGISTER( Status ) | |
437 | & ( kStatus_TX_INT_ME | kStatus_RX_DONE ); | |
438 | ||
439 | doService = false; | |
440 | ||
441 | if ( interruptStatus & kStatus_TX_INT_ME ) | |
442 | { | |
443 | txWDInterrupts++; | |
444 | KERNEL_DEBUG(DBG_GEM_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 ); | |
445 | doService = transmitInterruptOccurred(); | |
446 | KERNEL_DEBUG(DBG_GEM_TXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 ); | |
447 | ETHERNET_STAT_ADD( dot3TxExtraEntry.interrupts ); | |
448 | } | |
449 | ||
450 | doFlushQueue = false; | |
451 | ||
452 | if ( interruptStatus & kStatus_RX_DONE ) | |
453 | { | |
454 | rxWDInterrupts++; | |
455 | KERNEL_DEBUG(DBG_GEM_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 ); | |
456 | doFlushQueue = receiveInterruptOccurred(); | |
457 | KERNEL_DEBUG(DBG_GEM_RXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 ); | |
458 | ETHERNET_STAT_ADD( dot3RxExtraEntry.interrupts ); | |
459 | } | |
460 | ||
461 | IODebuggerUnlock( lockState ); | |
462 | ||
463 | /* | |
464 | * Submit all received packets queued up by _receiveInterruptOccurred() | |
465 | * to the network stack. The up call is performed without holding the | |
466 | * debugger lock. | |
467 | */ | |
468 | if (doFlushQueue) | |
469 | { | |
470 | networkInterface->flushInputQueue(); | |
471 | } | |
472 | ||
473 | /* | |
474 | * Make sure the output queue is not stalled. | |
475 | */ | |
476 | if (doService && netifEnabled) | |
477 | { | |
478 | transmitQueue->service(); | |
479 | } | |
480 | } | |
481 | while ( interruptStatus ); | |
482 | ||
483 | // interruptSource->enable(); | |
484 | return; | |
485 | }/* end interruptOccurred */ | |
486 | ||
487 | ||
488 | /*------------------------------------------------------------------------- | |
489 | * | |
490 | * | |
491 | * | |
492 | *-------------------------------------------------------------------------*/ | |
493 | ||
494 | UInt32 UniNEnet::outputPacket(struct mbuf * pkt, void * param) | |
495 | { | |
496 | UInt32 ret = kIOReturnOutputSuccess; | |
497 | ||
498 | KERNEL_DEBUG( DBG_GEM_TXQUEUE | DBG_FUNC_NONE, | |
499 | (int) pkt, (int) pkt->m_pkthdr.len, 0, 0, 0 ); | |
500 | ||
501 | /* | |
502 | * Hold the debugger lock so the debugger can't interrupt us | |
503 | */ | |
504 | reserveDebuggerLock(); | |
505 | ||
506 | if ( linkStatusPrev != kLinkStatusUp ) | |
507 | { | |
508 | freePacket( pkt ); | |
509 | } | |
510 | else if ( transmitPacket(pkt) == false ) | |
511 | { | |
512 | ret = kIOReturnOutputStall; | |
513 | } | |
514 | ||
515 | releaseDebuggerLock(); | |
516 | ||
517 | return ret; | |
518 | } | |
519 | ||
520 | /*------------------------------------------------------------------------- | |
521 | * | |
522 | * | |
523 | * | |
524 | *-------------------------------------------------------------------------*/ | |
525 | ||
526 | bool UniNEnet::resetAndEnable(bool enable) | |
527 | { | |
528 | bool ret = true; | |
529 | ||
530 | reserveDebuggerLock(); | |
531 | ||
532 | ready = false; | |
533 | ||
534 | if (timerSource) | |
535 | { | |
536 | timerSource->cancelTimeout(); | |
537 | } | |
538 | ||
539 | disableAdapterInterrupts(); | |
540 | if (getWorkLoop()) | |
541 | { | |
542 | getWorkLoop()->disableAllInterrupts(); | |
543 | } | |
544 | ||
545 | if (enable) | |
546 | { | |
547 | phyId = 0xff; | |
548 | } | |
549 | ||
550 | if ( resetChip() == false ) | |
551 | { | |
552 | ret = false; | |
553 | goto resetAndEnable_exit; | |
554 | } | |
555 | ||
556 | // Initialize the link status. | |
557 | ||
558 | setLinkStatus( 0, 0 ); | |
559 | ||
560 | // Flush all mbufs from RX and TX rings. | |
561 | ||
562 | flushRings(); | |
563 | ||
564 | while (enable) | |
565 | { | |
566 | if (!initRxRing() || !initTxRing()) | |
567 | { | |
568 | ret = false; | |
569 | break; | |
570 | } | |
571 | ||
572 | if ( phyId != 0xff ) | |
573 | { | |
574 | miiInitializePHY(phyId); | |
575 | } | |
576 | ||
577 | if (initChip() == false) | |
578 | { | |
579 | ret = false; | |
580 | break; | |
581 | } | |
582 | ||
583 | // startChip(); | |
584 | ||
585 | timerSource->setTimeoutMS(WATCHDOG_TIMER_MS); | |
586 | ||
587 | if (getWorkLoop()) | |
588 | { | |
589 | getWorkLoop()->enableAllInterrupts(); | |
590 | } | |
591 | enableAdapterInterrupts(); | |
592 | ||
593 | ready = true; | |
594 | ||
595 | monitorLinkStatus( true ); | |
596 | ||
597 | break; | |
598 | } | |
599 | ||
600 | resetAndEnable_exit: ; | |
601 | ||
602 | releaseDebuggerLock(); | |
603 | ||
604 | return ret; | |
605 | } | |
606 | ||
607 | /*------------------------------------------------------------------------- | |
608 | * Called by IOEthernetInterface client to enable the controller. | |
609 | * This method is always called while running on the default workloop | |
610 | * thread. | |
611 | *-------------------------------------------------------------------------*/ | |
612 | ||
613 | IOReturn UniNEnet::enable(IONetworkInterface * netif) | |
614 | { | |
615 | /* | |
616 | * If an interface client has previously enabled us, | |
617 | * and we know there can only be one interface client | |
618 | * for this driver, then simply return true. | |
619 | */ | |
620 | if ( netifEnabled ) | |
621 | { | |
622 | IOLog("EtherNet(UniN): already enabled\n"); | |
623 | return kIOReturnSuccess; | |
624 | } | |
625 | ||
626 | if ( (ready == false) && !resetAndEnable(true) ) | |
627 | return kIOReturnIOError; | |
628 | ||
629 | /* | |
630 | * Mark the controller as enabled by the interface. | |
631 | */ | |
632 | netifEnabled = true; | |
633 | ||
634 | /* | |
635 | * Start our IOOutputQueue object. | |
636 | */ | |
637 | transmitQueue->setCapacity( TRANSMIT_QUEUE_SIZE ); | |
638 | transmitQueue->start(); | |
639 | ||
640 | return kIOReturnSuccess; | |
641 | } | |
642 | ||
643 | /*------------------------------------------------------------------------- | |
644 | * Called by IOEthernetInterface client to disable the controller. | |
645 | * This method is always called while running on the default workloop | |
646 | * thread. | |
647 | *-------------------------------------------------------------------------*/ | |
648 | ||
649 | IOReturn UniNEnet::disable(IONetworkInterface * /*netif*/) | |
650 | { | |
651 | /* | |
652 | * Disable our IOOutputQueue object. This will prevent the | |
653 | * outputPacket() method from being called. | |
654 | */ | |
655 | transmitQueue->stop(); | |
656 | ||
657 | /* | |
658 | * Flush all packets currently in the output queue. | |
659 | */ | |
660 | transmitQueue->setCapacity(0); | |
661 | transmitQueue->flush(); | |
662 | ||
663 | /* | |
664 | * If we have no active clients, then disable the controller. | |
665 | */ | |
666 | if ( debugEnabled == false ) | |
667 | { | |
668 | resetAndEnable(false); | |
669 | } | |
670 | ||
671 | netifEnabled = false; | |
672 | ||
673 | return kIOReturnSuccess; | |
674 | } | |
675 | ||
676 | /*------------------------------------------------------------------------- | |
677 | * This method is called by our debugger client to bring up the controller | |
678 | * just before the controller is registered as the debugger device. The | |
679 | * debugger client is attached in response to the attachDebuggerClient() | |
680 | * call. | |
681 | * | |
682 | * This method is always called while running on the default workloop | |
683 | * thread. | |
684 | *-------------------------------------------------------------------------*/ | |
685 | ||
686 | IOReturn UniNEnet::enable(IOKernelDebugger * /*debugger*/) | |
687 | { | |
688 | /* | |
689 | * Enable hardware and make it ready to support the debugger client. | |
690 | */ | |
691 | if ( (ready == false) && !resetAndEnable(true) ) | |
692 | { | |
693 | return kIOReturnIOError; | |
694 | } | |
695 | ||
696 | /* | |
697 | * Mark the controller as enabled by the debugger. | |
698 | */ | |
699 | debugEnabled = true; | |
700 | ||
701 | /* | |
702 | * Returning true will allow the kdp registration to continue. | |
703 | * If we return false, then we will not be registered as the | |
704 | * debugger device, and the attachDebuggerClient() call will | |
705 | * return NULL. | |
706 | */ | |
707 | return kIOReturnSuccess; | |
708 | } | |
709 | ||
710 | /*------------------------------------------------------------------------- | |
711 | * This method is called by our debugger client to stop the controller. | |
712 | * The debugger will call this method when we issue a detachDebuggerClient(). | |
713 | * | |
714 | * This method is always called while running on the default workloop | |
715 | * thread. | |
716 | *-------------------------------------------------------------------------*/ | |
717 | ||
718 | IOReturn UniNEnet::disable(IOKernelDebugger * /*debugger*/) | |
719 | { | |
720 | debugEnabled = false; | |
721 | ||
722 | /* | |
723 | * If we have no active clients, then disable the controller. | |
724 | */ | |
725 | if ( netifEnabled == false ) | |
726 | { | |
727 | resetAndEnable(false); | |
728 | } | |
729 | ||
730 | return kIOReturnSuccess; | |
731 | } | |
732 | ||
733 | /*------------------------------------------------------------------------- | |
734 | * | |
735 | * | |
736 | * | |
737 | *-------------------------------------------------------------------------*/ | |
738 | ||
739 | void UniNEnet::timeoutOccurred(IOTimerEventSource * /*timer*/) | |
740 | { | |
741 | IODebuggerLockState lockState; | |
742 | bool doService = false; | |
743 | UInt32 txRingIndex; | |
744 | UInt32 x; | |
745 | ||
746 | ||
747 | if ( ready == false ) | |
748 | { | |
749 | // IOLog("EtherNet(UniN): Spurious timeout event!!\n"); | |
750 | return; | |
751 | } | |
752 | ||
753 | ||
754 | /* Update statistics from the GMAC statistics registers: */ | |
755 | ||
756 | x = READ_REGISTER( LengthErrorCounter ); | |
757 | writeRegister( &fpRegs->LengthErrorCounter, 0 ); | |
758 | fpEtherStats->dot3StatsEntry.frameTooLongs += x; | |
759 | ||
760 | x = READ_REGISTER( AlignmentErrorCounter ); | |
761 | writeRegister( &fpRegs->AlignmentErrorCounter, 0 ); | |
762 | fpEtherStats->dot3StatsEntry.alignmentErrors += x; | |
763 | ||
764 | x = READ_REGISTER( FCSErrorCounter ); | |
765 | writeRegister( &fpRegs->FCSErrorCounter, 0 ); | |
766 | fpEtherStats->dot3StatsEntry.fcsErrors += x; | |
767 | ||
768 | x = READ_REGISTER( RxCodeViolationErrorCounter ); | |
769 | writeRegister( &fpRegs->RxCodeViolationErrorCounter, 0 ); | |
770 | fpEtherStats->dot3StatsEntry.internalMacTransmitErrors += x; | |
771 | ||
772 | x = READ_REGISTER( FirstAttemptSuccessfulCollisionCounter ); | |
773 | writeRegister( &fpRegs->FirstAttemptSuccessfulCollisionCounter, 0 ); | |
774 | fpEtherStats->dot3StatsEntry.singleCollisionFrames += x; | |
775 | ||
776 | x = READ_REGISTER( ExcessiveCollisionCounter ); | |
777 | writeRegister( &fpRegs->ExcessiveCollisionCounter, 0 ); | |
778 | fpEtherStats->dot3StatsEntry.excessiveCollisions += x; | |
779 | ||
780 | x = READ_REGISTER( LateCollisionCounter ); | |
781 | writeRegister( &fpRegs->LateCollisionCounter, 0 ); | |
782 | fpEtherStats->dot3StatsEntry.lateCollisions += x; | |
783 | ||
784 | lockState = IODebuggerLock( this ); | |
785 | ||
786 | monitorLinkStatus(); | |
787 | ||
788 | /* | |
789 | * If there are pending entries on the Tx ring | |
790 | */ | |
791 | if ( txCommandHead != txCommandTail ) | |
792 | { | |
793 | /* | |
794 | * If the hardware tx pointer did not move since the last | |
795 | * check, increment the txWDCount. | |
796 | */ | |
797 | txRingIndex = READ_REGISTER( TxCompletion ); | |
798 | if ( txRingIndex == txRingIndexLast ) | |
799 | { | |
800 | txWDCount++; | |
801 | } | |
802 | else | |
803 | { | |
804 | txWDCount = 0; | |
805 | txRingIndexLast = txRingIndex; | |
806 | } | |
807 | ||
808 | if ( txWDCount > 2 ) | |
809 | { | |
810 | /* | |
811 | * We only take interrupts every 64 tx completions, so we may be here just | |
812 | * to do normal clean-up of tx packets. We check if the hardware tx pointer | |
813 | * points to the next available tx slot. This indicates that we transmitted all | |
814 | * packets that were scheduled vs rather than the hardware tx being stalled. | |
815 | */ | |
816 | if ( txRingIndex != txCommandTail ) | |
817 | { | |
818 | UInt32 interruptStatus, compReg, kickReg; | |
819 | ||
820 | interruptStatus = READ_REGISTER( Status ); | |
821 | compReg = READ_REGISTER( TxCompletion ); | |
822 | kickReg = READ_REGISTER( TxKick ); | |
823 | ||
824 | IOLog( "Tx Int Timeout - Comp = %04x Kick = %04x Int = %08x\n\r", (int)compReg, (int)kickReg, (int)interruptStatus ); | |
825 | } | |
826 | ||
827 | // dumpRegisters(); | |
828 | ||
829 | transmitInterruptOccurred(); | |
830 | ||
831 | doService = true; | |
832 | ||
833 | txRingIndexLast = txRingIndex; | |
834 | txWDCount = 0; | |
835 | } | |
836 | } | |
837 | else | |
838 | { | |
839 | txWDCount = 0; | |
840 | } | |
841 | ||
842 | // Monitor receiver's health. | |
843 | ||
844 | if ( rxWDInterrupts == 0 ) | |
845 | { | |
846 | UInt32 rxMACStatus; | |
847 | ||
848 | switch ( rxWDCount ) | |
849 | { | |
850 | case 0: | |
851 | case 1: | |
852 | rxWDCount++; // Extend timeout | |
853 | break; | |
854 | ||
855 | default: | |
856 | // We could be less conservative here and restart the | |
857 | // receiver unconditionally. | |
858 | ||
859 | rxMACStatus = READ_REGISTER( RxMACStatus ); | |
860 | ||
861 | if ( rxMACStatus & kRX_MAC_Status_Rx_Overflow ) | |
862 | { | |
863 | // Bad news, the receiver may be deaf as a result of this | |
864 | // condition, and if so, a RX MAC reset is needed. Note | |
865 | // that reading this register will clear all bits. | |
866 | ||
867 | restartReceiver(); | |
868 | ||
869 | NETWORK_STAT_ADD( inputErrors ); | |
870 | ETHERNET_STAT_ADD( dot3RxExtraEntry.watchdogTimeouts ); | |
871 | } | |
872 | rxWDCount = 0; | |
873 | break; | |
874 | } | |
875 | } | |
876 | else | |
877 | { | |
878 | // Reset watchdog | |
879 | ||
880 | rxWDCount = 0; | |
881 | rxWDInterrupts = 0; | |
882 | } | |
883 | ||
884 | /* Clean-up after the debugger if the debugger was active: */ | |
885 | ||
886 | if ( debugTxPoll ) | |
887 | { | |
888 | debugQueue->flush(); | |
889 | debugTxPoll = false; | |
890 | doService = true; | |
891 | } | |
892 | IODebuggerUnlock( lockState ); | |
893 | ||
894 | /* | |
895 | * Make sure the queue is not stalled. | |
896 | */ | |
897 | if (doService && netifEnabled) | |
898 | { | |
899 | transmitQueue->service(); | |
900 | } | |
901 | ||
902 | /* | |
903 | * Restart the watchdog timer | |
904 | */ | |
905 | timerSource->setTimeoutMS(WATCHDOG_TIMER_MS); | |
906 | return; | |
907 | }/* end timeoutOccurred */ | |
908 | ||
909 | ||
910 | /*------------------------------------------------------------------------- | |
911 | * | |
912 | * | |
913 | * | |
914 | *-------------------------------------------------------------------------*/ | |
915 | ||
916 | const OSString * UniNEnet::newVendorString() const | |
917 | { | |
918 | return OSString::withCString("Apple"); | |
919 | } | |
920 | ||
921 | const OSString * UniNEnet::newModelString() const | |
922 | { | |
923 | return OSString::withCString("gmac+"); | |
924 | } | |
925 | ||
926 | const OSString * UniNEnet::newRevisionString() const | |
927 | { | |
928 | return OSString::withCString(""); | |
929 | } | |
930 | ||
931 | ||
932 | /*------------------------------------------------------------------------- | |
933 | * | |
934 | * | |
935 | * | |
936 | *-------------------------------------------------------------------------*/ | |
937 | ||
938 | IOReturn UniNEnet::setPromiscuousMode(IOEnetPromiscuousMode mode) | |
939 | { | |
940 | reserveDebuggerLock(); | |
941 | ||
942 | rxMacConfigReg = READ_REGISTER( RxMACConfiguration ); | |
943 | if (mode == kIOEnetPromiscuousModeOff) | |
944 | { | |
945 | rxMacConfigReg &= ~(kRxMACConfiguration_Promiscuous); | |
946 | isPromiscuous = false; | |
947 | ||
948 | } | |
949 | else | |
950 | { | |
951 | rxMacConfigReg |= kRxMACConfiguration_Promiscuous; | |
952 | isPromiscuous = true; | |
953 | ||
954 | } | |
955 | WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg ); | |
956 | ||
957 | releaseDebuggerLock(); | |
958 | ||
959 | return kIOReturnSuccess; | |
960 | } | |
961 | ||
962 | /*------------------------------------------------------------------------- | |
963 | * | |
964 | * | |
965 | * | |
966 | *-------------------------------------------------------------------------*/ | |
967 | ||
968 | IOReturn UniNEnet::setMulticastMode(IOEnetMulticastMode mode) | |
969 | { | |
970 | multicastEnabled = (mode == kIOEnetMulticastModeOff) ? false : true; | |
971 | ||
972 | return kIOReturnSuccess; | |
973 | } | |
974 | ||
975 | /*------------------------------------------------------------------------- | |
976 | * | |
977 | * | |
978 | * | |
979 | *-------------------------------------------------------------------------*/ | |
980 | ||
981 | IOReturn UniNEnet::setMulticastList(IOEthernetAddress *addrs, UInt32 count) | |
982 | { | |
983 | reserveDebuggerLock(); | |
984 | ||
985 | resetHashTableMask(); | |
986 | for (UInt32 i = 0; i < count; i++) | |
987 | { | |
988 | addToHashTableMask(addrs->bytes); | |
989 | addrs++; | |
990 | } | |
991 | updateHashTableMask(); | |
992 | ||
993 | releaseDebuggerLock(); | |
994 | return kIOReturnSuccess; | |
995 | } | |
996 | ||
997 | /*------------------------------------------------------------------------- | |
998 | * | |
999 | * | |
1000 | * | |
1001 | *-------------------------------------------------------------------------*/ | |
1002 | ||
1003 | IOOutputQueue* UniNEnet::createOutputQueue() | |
1004 | { | |
1005 | return IOBasicOutputQueue::withTarget( this, TRANSMIT_QUEUE_SIZE ); | |
1006 | }/* end createOutputQueue */ | |
1007 | ||
1008 | ||
1009 | /*------------------------------------------------------------------------- | |
1010 | * | |
1011 | * | |
1012 | * | |
1013 | *-------------------------------------------------------------------------*/ | |
1014 | ||
1015 | static struct MediumTable | |
1016 | { | |
1017 | UInt32 type; | |
1018 | UInt32 speed; | |
1019 | } | |
1020 | mediumTable[] = | |
1021 | { | |
1022 | { kIOMediumEthernetNone , 0 }, | |
1023 | { kIOMediumEthernetAuto , 0 }, | |
1024 | { kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex, 10 }, | |
1025 | { kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex, 10 }, | |
1026 | { kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex, 100 }, | |
1027 | { kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex, 100 }, | |
1028 | { kIOMediumEthernet1000BaseSX | kIOMediumOptionFullDuplex, 1000 }, | |
1029 | { kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex, 1000 } | |
1030 | }; | |
1031 | ||
1032 | ||
1033 | bool UniNEnet::createMediumTables() | |
1034 | { | |
1035 | IONetworkMedium *medium; | |
1036 | UInt32 i; | |
1037 | ||
1038 | mediumDict = OSDictionary::withCapacity( sizeof(mediumTable)/sizeof(mediumTable[0]) ); | |
1039 | if ( mediumDict == 0 ) return false; | |
1040 | ||
1041 | for ( i=0; i < sizeof(mediumTable)/sizeof(mediumTable[0]); i++ ) | |
1042 | { | |
1043 | medium = IONetworkMedium::medium( mediumTable[i].type, mediumTable[i].speed ); | |
1044 | if ( medium != 0 ) | |
1045 | { | |
1046 | IONetworkMedium::addMedium( mediumDict, medium ); | |
1047 | medium->release(); | |
1048 | } | |
1049 | } | |
1050 | ||
1051 | if ( publishMediumDictionary( mediumDict ) != true ) | |
1052 | { | |
1053 | return false; | |
1054 | } | |
1055 | ||
1056 | medium = IONetworkMedium::getMediumWithType( mediumDict, | |
1057 | kIOMediumEthernetAuto ); | |
1058 | ||
1059 | setCurrentMedium( medium ); | |
1060 | ||
1061 | return true; | |
1062 | } | |
1063 | ||
1064 | ||
1065 | void UniNEnet::writeRegister( UInt32 *pReg, UInt32 data ) | |
1066 | { | |
1067 | /// ELG( data, (UInt32)pReg - (UInt32)fpRegs, 'wReg', "writeRegister" ); | |
1068 | ||
1069 | OSWriteLittleInt32( pReg, 0, data ); | |
1070 | return; | |
1071 | }/* end writeRegister */ |