1/* IrDAComm.h - Start up IrDA and the IrComm client */
2
3#include <IOKit/IOTimerEventSource.h>
4#include <IOKit/IOCommandGate.h>
5#include "IrDAComm.h"
6#include "IrGlue.h"
7#include "IrComm.h"
8#include "CTimer.h"
9#include "IrDALog.h"
10
11#if (hasTracing > 0 && hasIrDACommTracing > 0)
12
13enum tracecodes
14{
15    kLogNew = 1,
16    kLogFree,
17    kLogInit,
18    kLogStop,
19    kLogStop1,
20    kLogStopThread,
21
22    kLogTxBufferAvailable,
23    kLogWrite,
24    kLogReadComplete,
25    kLogReturnCredit,
26    kLogConnectionStatus,
27    kLogTransmitComplete,
28    kLogSetSpeedComplete,
29    kLogBackEnable,
30
31    kLogTimer,
32    kLogTimerFinished,
33
34    kLogXmitCompleteErr,
35    kLogReturnCreditErr,
36
37    kLogDoSomething,
38    kLogStateChange
39};
40
41static
42EventTraceCauseDesc gTraceEvents[] = {
43    {kLogNew,               "IrDAComm: new, obj="},
44    {kLogFree,              "IrDAComm: free, obj="},
45    {kLogInit,              "IrDAComm: init, obj="},
46    {kLogStop,              "IrDAComm: stop, obj="},
47    {kLogStop1,             "IrDAComm: stop waiting for disconnect"},
48    {kLogStopThread,        "IrDAComm: stop thread"},
49
50    {kLogTxBufferAvailable, "IrDAComm: tx buffer available"},
51    {kLogWrite,             "IrDAComm: write, state=,length="},
52    {kLogReadComplete,      "IrDAComm: pkt read complete, length="},
53    {kLogReturnCredit,      "IrDAComm: return credit, bytecount="},
54    {kLogConnectionStatus,  "IrDAComm: new connection status, state=, connected="},
55    {kLogTransmitComplete,  "IrDAComm: pkt transmit complete, worked="},
56    {kLogSetSpeedComplete,  "IrDAComm: set speed complete"},
57    {kLogBackEnable,        "IrDAComm: back enable, write active="},
58
59    {kLogTimer,             "IrDAComm: timer routine entry, state="},
60    {kLogTimerFinished,     "IrDAComm: timer routine exit"},
61
62    {kLogXmitCompleteErr,   "IrDAComm: ERROR. transmit complete while stack is active"},
63    {kLogReturnCreditErr,   "IrDAComm: ERROR. return credit while stack is active"},
64
65    {kLogDoSomething,       "IrDAComm: run command in gate, cmd code="},
66    {kLogStateChange,       "IrDAComm: state change entry, event=, current state="}
67};
68
69#define XTRACE(x, y, z) IrDALogAdd ( x, y, (uintptr_t)z & 0xffff, gTraceEvents, true)
70#else
71#define XTRACE(x, y, z) ((void)0)
72#endif
73
74extern "C" void timeoutRoutine(OSObject *owner, IOTimerEventSource *sender);
75
76enum {          // command codes for DoSomething
77    cmdTxBufferAvailable,
78    cmdWrite,
79    cmdReturnCredit,
80    cmdStop,
81    cmdStopEvent,
82    cmdReadComplete,
83    cmdXmitComplete,
84    cmdSetSpeedComplete
85};
86
87/****************************************************************************************************/
88#define super OSObject
89
90    OSDefineMetaClassAndStructors(IrDAComm, OSObject);
91
92
93//
94// factory create
95//
96/*static*/
97IrDAComm *
98IrDAComm::irDAComm(AppleIrDASerial *driver, AppleIrDA *appleirda)
99{
100    IrDAComm *obj = new IrDAComm;   // create an IrDAComm object
101
102    XTRACE(kLogNew, 0, obj);
103
104    if (obj && !obj->init(driver, appleirda)) {
105	obj->release();
106	obj = nil;
107    }
108
109    return obj;
110}
111
112
113void IrDAComm::free()
114{
115    IOWorkLoop *workloop;
116
117    XTRACE(kLogFree, 0, this);
118
119    this->Stop();       // make sure we're stopped before releasing memory
120
121    if (fDriver) {
122	workloop = fDriver->getWorkLoop();
123	if (workloop) {
124	    if (fGate)
125		workloop->removeEventSource(fGate);
126	    //if (fTimerSrc)
127	    //  workloop->removeEventSource(fTimerSrc);
128	}
129    }
130
131#define FREE(x) { if (x) { (x)->release(); x = nil; }}
132
133    FREE(fGate);
134    FREE(fTimer);
135    FREE(fIrComm);          // free the ircomm object before the rest of irda ...
136    FREE(fIrDA);
137
138#undef FREE
139
140#define THREAD_FREE(x) do { if (x) {               \
141			    thread_call_cancel(x); \
142			    thread_call_free(x);   \
143			    x = NULL; } } while(0)
144
145    THREAD_FREE(fStop_thread);
146#undef THREAD_FREE
147
148    super::free();      // we're done, call super
149}
150
151
152/****************************************************************************************************/
153//
154//      Method:     IrDAComm::Init
155//
156//      Inputs:     pointer to the usb irda driver
157//
158//      Outputs:    return code - true (initialized ok), false (it didn't)
159//
160//      Desc:       Initialize the IrDAComm class
161//
162/****************************************************************************************************/
163
164bool IrDAComm::init(AppleIrDASerial *driver, AppleIrDA *appleirda)
165{
166    IOReturn rc;
167    IOWorkLoop *workloop;
168
169    XTRACE(kLogInit, 0, this);
170#if (hasTracing > 0)
171    DebugLog("log info at 0x%lx", (uintptr_t)IrDALogGetInfo());
172#endif
173    require(driver, Fail);
174
175    fState = kIrDACommStateStart;
176    fDriver = driver;
177    //fTimerSrc = nil;
178    fTimer = nil;
179    fQoS = nil;
180    fIrDA = nil;
181    fIrComm = nil;
182    fWriteBusy = false;
183    fGate = nil;
184    fStartCounter = 0;          // counter for initial connection attempts
185    fStop_thread = nil;
186
187    if (!super::init())
188	return false;
189
190    fQoS = driver->GetIrDAQoS();
191    require(fQoS, Fail);
192
193    workloop = fDriver->getWorkLoop();
194    require(workloop, Fail);
195
196    fStop_thread = thread_call_allocate(stop_thread, this);
197    require(fStop_thread, Fail);
198
199    fIrDA = TIrGlue::tIrGlue(fDriver, appleirda, workloop, fQoS);       // create irda stack
200    require(fIrDA, Fail);
201
202    fIrComm = IrComm::irComm(fIrDA, this);                                          // create an ircomm object
203    require(fIrComm, Fail);
204
205    fGate = IOCommandGate::commandGate(this, 0);    // create a new command gate for our access to IrDA
206    require(fGate, Fail);
207
208    rc = workloop->addEventSource(fGate);           // add it to the usb workloop
209    require(rc == kIOReturnSuccess, Fail);
210
211    fTimer = CTimer::cTimer(workloop, this, &IrDAComm::TimerRoutine);
212    require(fTimer, Fail);
213
214    fTimer->StartTimer(100, 0);                     // 100ms delay after init and then startup
215
216    //fTimerSrc = IOTimerEventSource::timerEventSource ( driver, &::timeoutRoutine);    // create an io timer
217    //require(fTimerSrc, Fail);
218    //
219    //rc = workloop->addEventSource(fTimerSrc);
220    //require(rc == kIOReturnSuccess, Fail);
221    //
222    //rc = fTimerSrc->setTimeoutMS(100);            // 100 ms delay after init'ing
223    //require(rc == kIOReturnSuccess, Fail);
224
225    return true;
226
227Fail:
228    return false;
229
230} /* end Initialize */
231
232
233/****************************************************************************************************/
234//
235//      Method:     IrDAComm::Stop
236//
237//      Inputs:
238//
239//      Outputs:    return code - kIOReturnSuccess
240//
241//      Desc:       Stops the class (clean up etc.)
242//
243/****************************************************************************************************/
244
245IOReturn IrDAComm::Stop(void)
246{
247    int i;
248    IOReturn rc = kIOReturnSuccess;
249
250    XTRACE(kLogStop, 0, this);
251    require(fGate, Fail);
252    require(fIrDA, Fail);           // sanity
253    require(fDriver, Fail);         // sanity
254
255    if (fState != kIrDACommStateStopped) {          // if not already stopped
256	boolean_t bt;
257
258	if (fDriver->getWorkLoop()->inGate())   {       // if we have the gate, just call it
259	    rc = fGate->runAction(&DoSomething, (void *)cmdStopEvent, nil, nil, nil);
260	    check(rc == kIOReturnSuccess);
261	}
262	else {                                          // we don't have the gate, run stop in another thread and wait for it
263	    bt = thread_call_enter(fStop_thread);       // run stop logic in another thread
264	    check(bt == false);                         // true here means it was already running and we're confused
265
266	    for (i = 0 ; i < 10; i++) {                 // max wait of a second (should be more than enough)
267		XTRACE(kLogStop1, i, fState);
268		if (fState == kIrDACommStateStopped &&              // if ircomm is stopped and the irlap link is down
269		    fIrDA->IsLAPConnected() == false) break;        // then we're really stopped
270		IOSleep(100);                                       // wait 1/10 of a second per state poll
271	    }
272	}
273	check(fState == kIrDACommStateStopped);     // this will fail if in broken-beam, just debugging
274
275	rc = fGate->runAction(&DoSomething, (void *)cmdStop, nil, nil, nil);
276	check(rc == kIOReturnSuccess);
277    }
278
279Fail:
280    fState = kIrDACommStateStopped;     // it's really stopped now, regardless of above
281    XTRACE(kLogStop, 0xffff, 0xffff);
282
283    return rc;
284}
285
286/****************************************************************************************************/
287//
288//      Method:     IrDAComm::TXBufferAvailable
289//
290//      Inputs:
291//
292//      Outputs:    size - number of bytes available for a write
293//
294//      Desc:       Returns the number of bytes IrDA can currently accept for a write
295//
296/****************************************************************************************************/
297
298size_t IrDAComm::TXBufferAvailable()
299{
300    IOReturn rc;
301    size_t  result = 0;
302
303    XTRACE(kLogTxBufferAvailable, 0, 0);
304
305    // we're getting called from outside the usb workloop, so run through our command gate
306
307    if (fIrComm && fGate) {
308	rc = fGate->runAction(&DoSomething, (void *)cmdTxBufferAvailable, &result);
309	check(rc == kIOReturnSuccess);
310    }
311
312    XTRACE(kLogTxBufferAvailable, 0xffff, result);
313    return result;
314
315} /* end TXBufferAvailable */
316
317/****************************************************************************************************/
318//
319//      Method:     IrDAComm::Write
320//
321//      Inputs:     Buf - the data to be written, Length - the size of the data
322//
323//      Outputs:    size - number of bytes written
324//
325//      Desc:       Queue the data to be written by IrComm
326//
327/****************************************************************************************************/
328
329size_t IrDAComm::Write(UInt8 *buf, size_t length)
330{
331    UInt32 result = length;
332    IOReturn rc;
333
334    XTRACE(kLogWrite, fState, length);
335
336    // this is coming from outside our workloop, send through our gate
337
338    if (fState == kIrDACommStateConnected && fIrComm && fGate) {
339	rc = fGate->runAction(&DoSomething, (void *)cmdWrite, buf, (void *)length, &result);
340	check(rc == kIOReturnSuccess);
341    }
342
343    return result;          // pretend it worked if no connection (i.e. sink to /dev/null)
344
345} /* end Write */
346
347/****************************************************************************************************/
348//
349//      Method:     IrDAComm::ReadComplete
350//
351//      Inputs:     Frame - Incoming IrDA frame
352//
353//      Outputs:    Return code - kIOReturnSuccess or
354//
355//      Desc:       Process an incoming frame
356//
357/****************************************************************************************************/
358
359IOReturn IrDAComm::ReadComplete(UInt8 *buf, size_t length)
360{
361    IOReturn rc = -1;
362
363    XTRACE(kLogReadComplete, length >> 16, length);
364
365    if (fGate && fIrComm && fIrDA) {
366	rc = fGate->runAction(&DoSomething, (void *)cmdReadComplete, buf, (void *)length, nil);
367    }
368
369    return rc;
370
371} /* end ReadComplete */
372
373//
374// Sending back flow-control to the peer
375//
376void
377IrDAComm::ReturnCredit(size_t byte_count)       // serial client has consumed count bytes of data
378{
379    IOReturn rc;
380
381    XTRACE(kLogReturnCredit, byte_count >> 16, byte_count);
382
383    // this is coming from outside our workloop, send to irda via our command gate
384
385    if (fState == kIrDACommStateConnected && fIrComm && fGate) {
386	rc = fGate->runAction(&DoSomething, (void *)cmdReturnCredit, (void *)byte_count);
387	check(rc == kIOReturnSuccess);
388    }
389
390    return;
391}
392
393
394void
395IrDAComm::Transmit_Complete(Boolean worked)
396{
397    IOReturn rc;
398
399    XTRACE(kLogTransmitComplete, 0, worked);
400
401    if (fGate && fIrDA) {
402	rc = fGate->runAction(&DoSomething, (void *)cmdXmitComplete, (void *)worked);
403    }
404
405    XTRACE(kLogTransmitComplete, 0xffff, 0xffff);
406}
407
408void
409IrDAComm::SetSpeedComplete(Boolean worked)
410{
411    IOReturn rc;
412
413    XTRACE(kLogSetSpeedComplete, 0, worked);
414
415    if (fGate && fIrDA) {
416	rc = fGate->runAction(&DoSomething, (void *)cmdSetSpeedComplete, (void *)worked);
417    }
418
419    XTRACE(kLogSetSpeedComplete, 0xffff, 0xffff);
420}
421
422
423
424//
425// Called by IrCOMM to send data back to the pseudo tty
426//
427void
428IrDAComm::IrCommDataRead(UInt8 *buf, UInt32 length)     // ircomm data to pass back to the tty
429{
430    if (fDriver)                                // if we're not stopped
431	fDriver->Add_RXBytes(buf, length);      // error return?
432    else
433	DebugLog("IrDAComm data read but no driver");
434}
435
436//
437// IrComm calls this when our tinytp peer has extended more credit
438// to us .. i.e. previously blocked writes may now work.
439//
440void
441IrDAComm::BackEnable(void)
442{
443    XTRACE(kLogBackEnable, 0, fWriteBusy);
444
445    if (fDriver                     // if we're alive and ...
446     /* && fWriteBusy */    )               // last time they asked we were busy, then
447    // above 'optimization' test stopped the tx queue wheneveer we break up a too-big
448    // write into several small writes.  radar 2890966
449	    fDriver->SetUpTransmit();       // ask driver code to try to transmit again
450}
451
452//
453// The state engine for keeping irda in a connect/listen loop.
454//
455
456void
457IrDAComm::StateChange(int event)
458{
459    XTRACE(kLogStateChange, event, fState);
460
461    require(fIrComm, Fail);
462    require(fIrDA, Fail);
463
464    switch (fState) {
465
466	case kIrDACommStateStart:                   // Starting up and doing initial discovery/connect attempts
467		switch (event) {
468
469		    case kIrDACommEventTimer:                   // start off the state engine.
470			check(fStartCounter == 0);              // sanity
471			if (fStartCounter == 0) {
472			    fIrDA->Start();                     // start irda stack up
473			    fIrComm->TryConnect(1);             // and do the first discovery
474			}
475			break;
476
477		    case kIrDACommEventConnected:               // we're connected!
478			fState = kIrDACommStateConnected;
479			break;
480
481		    case kIrDACommEventDisconnected:            // initial discover/connect failed.  Try again 3 times
482			if (fStartCounter++ < 3)
483			    fIrComm->TryConnect(1);
484			else
485			    fState = kIrDACommStateIdle;        // could listen here, but main timer is about to fire soon
486			break;
487
488		    case kIrDACommEventStop:
489			if (fStartCounter == 0)                 // if stopped before we really started
490			    fState = kIrDACommStateStopped;     // then we're done w/out doing anything
491			else {                                  // else we have a connect pending
492			    fState = kIrDACommStateStopping2;   // wait for two callbacks
493			    fIrComm->Disconnect();              // abort the connect attempt
494			}
495			break;
496		}
497		break;
498
499	case kIrDACommStateIdle:                    // not doing much
500		switch (event) {
501
502		    case kIrDACommEventTimer:               // time to wake up.  Let's try a discover/connect
503			fState = kIrDACommStateConnecting;
504			fIrComm->TryConnect(6);
505			break;
506
507		    case kIrDACommEventConnected:           // connected w/out my doing anything?
508			DebugLog("logic error event=%d, state=%d", event, fState);
509			break;
510
511		    case kIrDACommEventDisconnected:        // disconnect when idle?  seems unlikley
512			DebugLog("logic error event=%d, state=%d", event, fState);
513			break;
514
515		    case kIrDACommEventStop:                // stopped when we're idle
516			fState = kIrDACommStateStopped;     // then we're done w/out doing anything
517			break;
518
519		}
520		break;
521
522	case kIrDACommStateConnecting:              // trying to connect
523		switch (event) {
524
525		    case kIrDACommEventTimer:                   // connect is stuck, likely in broken beam
526			DebugLog("connect timing out");
527			break;
528
529		    case kIrDACommEventConnected:
530			fState = kIrDACommStateConnected;       // we've connected!
531			break;
532
533		    case kIrDACommEventDisconnected:            // connect failed, switch to listen
534			fState = kIrDACommStateListening;
535			fIrComm->Listen();
536			break;
537
538		    case kIrDACommEventStop:                    // stop request with connect pending
539			fState = kIrDACommStateStopping2;       // wait for two callbacks
540			fIrComm->Disconnect();                  // abort the connect attempt
541			break;
542		}
543		break;
544
545	case kIrDACommStateListening:               // listening for a peer to discover and connect to us
546		switch (event) {
547
548		    case kIrDACommEventTimer:                           // Time to stop listening and try a connect again.
549			fState = kIrDACommStateStoppingListen;          // Issue a disconnect to abort listen.  We'll get two
550			fIrComm->Disconnect();                          // completes, one for listen stopped, and one for disconnect done.
551			break;
552
553		    case kIrDACommEventConnected:                       // the listen worked, we've got a peer!
554			fState = kIrDACommStateConnected;
555			break;
556
557		    case kIrDACommEventDisconnected:                    // we can get a disconnect if the link bounces, just
558			fIrComm->Listen();                              // just keep the listen going
559			break;
560
561		    case kIrDACommEventStop:                            // stop request when listen pending
562			fState = kIrDACommStateStopping2;               // wait for two callbacks
563			fIrComm->Disconnect();                          // abort the listen
564			break;
565		}
566		break;
567
568	case kIrDACommStateStoppingListen:          // waiting for listen abort to finish it's disconnect
569		switch (event) {
570
571		    case kIrDACommEventTimer:                           // stuck waiting for listen abort.  likely in broken beam
572			DebugLog("stopping listen timer fired, are we stuck?");
573			break;
574
575		    case kIrDACommEventConnected:                       // connected?  have a disconnect pending.  wait for it.
576			DebugLog("listen disconnect race condition");
577			break;
578
579		    case kIrDACommEventDisconnected:                    // listen aborted.  Now wait for the disconnect complete
580			fState = kIrDACommStateDisconnecting;
581			break;
582
583		    case kIrDACommEventStop:                            // stopped after issuing disconnect to stop listen
584			fState = kIrDACommStateStopping2;               // wait for two callbacks
585			break;
586		}
587		break;
588
589	case kIrDACommStateDisconnecting:           // waiting for a disconnect request to finish
590		switch (event) {
591
592		    case kIrDACommEventTimer:                           // stuck doing disconnect, most likely in broken beam
593			DebugLog("disconnect timing out?");
594			break;
595
596		    case kIrDACommEventConnected:                       // connected?  race condition.  Expect a disconnect soon.
597			DebugLog("disconnect race condition");
598			break;
599
600		    case kIrDACommEventDisconnected:                    // listen abort finished, let's try a connect
601			fState = kIrDACommStateConnecting;              // switch from listen to connect
602			fIrComm->TryConnect(6);                         // a reasonable number of discover slots after quick start
603			break;
604
605		    case kIrDACommEventStop:                            // stopped after issuing disconnect
606			fState = kIrDACommStateStopping;                // wait for one more callback
607			break;
608
609		}
610		break;
611
612	case kIrDACommStateConnected:               // ircomm channel open, data can flow
613		switch (event) {
614
615		    case kIrDACommEventTimer:                           // nothing to do, we're a happy camper
616			break;
617
618		    case kIrDACommEventConnected:                       // connected again?  logic error
619			DebugLog("logic error event=%d, state=%d", event, fState);
620			break;
621
622		    case kIrDACommEventDisconnected:                    // lost our connection
623			fState = kIrDACommStateIdle;                    // wait for timer before trying to connect again
624			break;
625
626		    case kIrDACommEventStop:                            // stopped when we're up and running
627			fState = kIrDACommStateStopping;                // wait for one callback
628			fIrComm->Disconnect();                          // disconnect
629			break;
630		}
631		break;
632
633	case kIrDACommStateStopping2:
634		switch (event) {
635		    case kIrDACommEventTimer:                           // timer should be stopped
636			DebugLog("logic error event=%d, state=%d", event, fState);
637			break;
638
639		    case kIrDACommEventConnected:                       // race condition
640			fState = kIrDACommStateStopping;                // should disconnect "real soon" now
641			break;
642
643		    case kIrDACommEventDisconnected:
644			fState = kIrDACommStateStopping;                // normal, listen/connect has aborted
645			break;
646
647		    case kIrDACommEventStop:
648			DebugLog("logic error event=%d, state=%d", event, fState);
649			break;
650		}
651		break;
652
653	case kIrDACommStateStopping:
654		switch (event) {
655		    case kIrDACommEventTimer:
656			DebugLog("logic error event=%d, state=%d", event, fState);
657			break;
658
659		    case kIrDACommEventConnected:
660			DebugLog("logic error event=%d, state=%d", event, fState);
661			break;
662
663		    case kIrDACommEventDisconnected:
664			fState = kIrDACommStateStopped;             // stop completed
665			if (fIrDA->IsLAPConnected())                // if lap is connected, then
666			    fIrDA->DoIdleDisconnect();              // don't wait 1 second, disconnect now
667			break;
668
669		    case kIrDACommEventStop:
670			DebugLog("logic error event=%d, state=%d", event, fState);
671			break;
672		}
673		break;
674
675	case kIrDACommStateStopped:         // we're stopped, shouldn't be getting requests
676		DebugLog("logic error event=%d, state=%d", event, fState);
677		break;
678
679    }
680Fail:
681    return;
682}
683
684//
685// called by IrComm to let us know of major connection state changes
686//
687void
688IrDAComm::ConnectionStatus(Boolean connected)
689{
690    static Boolean last_connected = false;      // cut down on the debug log noise
691
692    XTRACE(kLogConnectionStatus, fState, connected);
693    if (connected != last_connected) {
694	DebugLog("connection status %d", connected);
695	last_connected = connected;
696    }
697    check(fState != kIrDACommStateIdle);
698
699    if (connected)  StateChange(kIrDACommEventConnected);
700    else            StateChange(kIrDACommEventDisconnected);
701}
702
703//
704// 5 second timer, keep connection attempts going.
705//
706/* static */
707void
708IrDAComm::TimerRoutine(OSObject *owner, IrDATimerEventSource *iotimer)
709{
710    IrDAComm *obj;
711
712    XTRACE(kLogTimer, 0, 0);
713
714    obj = OSDynamicCast(IrDAComm, owner);
715    require(obj, Fail);
716
717    XTRACE(kLogTimer, 0x1111, obj->fState);
718
719    obj->StateChange(kIrDACommEventTimer);
720
721    if (obj->fIrDA) {                   // now run the irda event queue until it's done
722	obj->fIrDA->RunQueue();
723    }
724
725    require(obj->fTimer, Fail);
726    obj->fTimer->StartTimer(5 * 1000, 0);       // wake up again in 5 seconds
727
728    XTRACE(kLogTimerFinished, 0, 0);
729    return;
730
731Fail:
732    XTRACE(kLogTimerFinished, 0xdead, 0xbeef);
733    return;
734}
735
736/****************************************************************************************************/
737//
738//      Method:     timeoutRoutine
739//
740//      Inputs:     object handle, timer source
741//
742//      Outputs:    none
743//
744//      Desc:       call the real C++ method to handle the timeout
745//
746/****************************************************************************************************/
747/*
748extern "C"
749void
750timeoutRoutine(OSObject *owner, IOTimerEventSource *sender)
751{
752    AppleIrDASerial *driver = (AppleIrDASerial *)owner;
753    IrDAComm *me = driver->GetIrDAComm();
754
755    me->TimerRoutine(sender);
756}
757*/
758
759//
760//
761//static
762IOReturn
763IrDAComm::DoSomething(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
764{
765    IrDAComm *obj;
766    uintptr_t cmd = (uintptr_t)arg1;
767
768    XTRACE(kLogDoSomething, 0, (uintptr_t)arg1);        // log the command code
769
770    obj = OSDynamicCast(IrDAComm, owner);
771    require(obj, Fail);
772
773    switch (cmd) {
774	case cmdTxBufferAvailable:
775			{
776			    UInt32  count = 0;
777			    UInt32  *result = (UInt32 *)arg2;
778			    if (obj->fIrComm)
779				count = obj->fIrComm->TxBufferAvailable();
780			    obj->fWriteBusy = (count == 0);
781			    if (result)
782				*result = count;
783			}
784			break;
785
786	case cmdWrite:
787			{
788			    UInt8   *buf    = (UInt8 *)arg2;
789			    uintptr_t  length  = (uintptr_t)arg3;
790			    UInt32  *result = (UInt32 *)arg4;
791
792			    if (result)
793				*result = obj->fIrComm->Write(buf, length);
794			}
795			break;
796
797	case cmdReturnCredit:
798			{
799			    uintptr_t  byte_count = (uintptr_t)arg2;
800			    if (obj->fIrComm)
801				obj->fIrComm->ReturnCredit(byte_count);
802			}
803			break;
804
805	case cmdStop:
806			{
807			    if (obj->fTimer) {
808				obj->fTimer->StopTimer();
809				obj->fTimer->release();
810				obj->fTimer = nil;
811			    }
812
813			    if (obj->fIrDA) {
814				obj->fIrDA->Stop();         // tell irda not to use the driver anymore
815			    }
816			}
817			break;
818
819	case cmdStopEvent:
820			check(obj->fTimer);
821			if (obj->fTimer) {
822			    obj->fTimer->StopTimer();                   // stop the timer
823			    obj->StateChange(kIrDACommEventStop);       // get the state engine to stop
824			}
825			if (obj->fIrDA) {                   // now run the irda event queue until it's done
826			    obj->fIrDA->RunQueue();
827			}
828			break;
829
830	case cmdReadComplete:
831			{
832			    UInt8   *buf    = (UInt8 *)arg2;
833			    uintptr_t  length  = (uintptr_t)arg3;
834			    if (obj->fIrDA) {
835				obj->fIrDA->ReadComplete(buf, length);
836				obj->fIrDA->RunQueue();
837			    }
838			}
839			break;
840
841	case cmdXmitComplete:
842			{
843			    bool    worked = (bool)arg2;
844			    if (obj->fIrDA) {
845				obj->fIrDA->TransmitComplete(worked);
846				XTRACE(kLogTransmitComplete, 0x1111, 0x1111);
847				obj->fIrDA->RunQueue();
848				XTRACE(kLogTransmitComplete, 0x2222, 0x2222);
849			    }
850			}
851			break;
852
853	case cmdSetSpeedComplete:
854			{
855			    bool    worked = (bool)arg2;
856			    if (obj->fIrDA) {
857				obj->fIrDA->SetSpeedComplete(worked);
858				XTRACE(kLogSetSpeedComplete, 0x1111, 0x1111);
859				obj->fIrDA->RunQueue();
860				XTRACE(kLogSetSpeedComplete, 0x2222, 0x2222);
861			    }
862			}
863			break;
864
865	default:
866			check(0);
867			break;
868    }
869
870    return kIOReturnSuccess;
871
872Fail:
873    return kIOReturnBadArgument;
874}
875
876//
877// return irda status to user-client
878//
879void
880IrDAComm::GetIrDAStatus(IrDAStatus *status)
881{
882    if (fIrDA && status)
883	fIrDA->GetIrDAStatus(status);
884}
885
886//
887// return true if starting up (initial connection attempt)
888//
889bool
890IrDAComm::Starting()
891{
892    return fState == kIrDACommStateStart;
893}
894
895
896void IrDAComm::stop_thread(thread_call_param_t param0, thread_call_param_t param1)
897{
898    IrDAComm *obj;
899    IOReturn rc;
900
901    XTRACE(kLogStopThread, 0, 0);
902
903    require(param0, Fail);
904    obj = OSDynamicCast(IrDAComm, (OSObject *)param0);
905    require(obj, Fail);
906
907    rc = obj->fGate->runAction(&DoSomething, (void *)cmdStopEvent, nil, nil, nil);
908    check(rc == kIOReturnSuccess);
909
910Fail:
911    return;
912}
913