1/*
2 * LICENSE NOTICE.
3 *
4 * Use of the Microsoft Windows Rally Development Kit is covered under
5 * the Microsoft Windows Rally Development Kit License Agreement,
6 * which is provided within the Microsoft Windows Rally Development
7 * Kit or at http://www.microsoft.com/whdc/rally/rallykit.mspx. If you
8 * want a license from Microsoft to use the software in the Microsoft
9 * Windows Rally Development Kit, you must (1) complete the designated
10 * "licensee" information in the Windows Rally Development Kit License
11 * Agreement, and (2) sign and return the Agreement AS IS to Microsoft
12 * at the address provided in the Agreement.
13 */
14
15/*
16 * Copyright (c) Microsoft Corporation 2005.  All rights reserved.
17 * This software is provided with NO WARRANTY.
18 */
19
20/* This is the state machine that controls the topology mapping process, smT.
21 * Once enumeration by a Topology-Discovery Mapper has been completed, that
22 * mapper alone is enabled to make other requests of the Responder, until it
23 * relinquishes its lock on the Responder, either by sending a Reset or by
24 * timing out (Inactivity timeout).
25 *
26 * A session conceptually represents the association of a Mapper with this
27 * Responder, throughout the lifetime of that association. State-information
28 * for smT is maintained in one area of the process globals, since there is
29 * at most one session associated with the Responder process at a time.
30 *
31 **/
32
33#include <stdio.h>
34#include <string.h>
35#include <assert.h>
36
37#include "globals.h"
38
39#include "statemachines.h"
40#include "packetio.h"
41
42static
43int
44SufficientChargeAvailable()
45{
46    IF_DEBUG
47        dbgprintf("SCA: ctc_pkts=" FMT_UINT32 "   pkts_needed=" FMT_UINT32 \
48                  "    ctc_bytes=" FMT_UINT32 "   bytes_needed=" FMT_UINT32 "\n",
49                  g_ctc_packets, g_neededPackets, g_ctc_bytes, g_neededBytes);
50    END_DEBUG
51
52    if ((g_ctc_packets >= g_neededPackets) && (g_ctc_bytes >= g_neededBytes))
53        return TRUE;
54    else
55        return FALSE;
56}
57
58static void
59ProcessEmits()
60{
61    topo_ether_header_t    *ehdr;	/* pointer to ethernet header in emit buf */
62    topo_base_header_t     *bhdr;	/* pointer to base header in emit buf */
63    topo_emit_header_t     *emit;	/* pointer to emit header in emit buf */
64
65    /* copy current rxbuf into the emit buffer */
66    memcpy(g_emitbuf, g_rxbuf, g_rcvd_pkt_len);
67
68    /* setup the emit machinery */
69    assert(g_emit_remaining == 0);
70    g_emit_remaining = g_this_event.numDescrs;
71    g_emit_seqnum = g_sequencenum;		   /* might be zero for an unreliable Emit */
72    ehdr = (topo_ether_header_t*)(g_emitbuf);
73    bhdr = (topo_base_header_t*)(ehdr + 1);
74    emit = (topo_emit_header_t*)(bhdr + 1);
75    g_emitdesc = (topo_emitee_desc_t*)(emit + 1);  // pointer to the first descriptor
76}
77
78static
79void
80ChargeAdd(int pktlen)
81{
82    struct timeval now;
83
84    g_ctc_packets++;
85    if (g_ctc_packets > TOPO_CTC_PACKETS_MAX)
86	g_ctc_packets = TOPO_CTC_PACKETS_MAX;
87
88    g_ctc_bytes += pktlen;
89    if (g_ctc_bytes > TOPO_CTC_BYTES_MAX)
90	g_ctc_bytes = TOPO_CTC_BYTES_MAX;
91
92    IF_TRACED(TRC_CHARGE)
93	dbgprintf("ChargeAdd: CTC now has bytes=" FMT_UINT32 " & packets=" FMT_UINT32 "\n",
94		g_ctc_bytes, g_ctc_packets);
95    END_TRACE
96
97    /* Reset charge timer */
98    gettimeofday(&now, NULL);
99    timeval_add_ms(&now, TOPO_CHARGE_TIMEOUT);
100    CANCEL(g_charge_timer);
101    g_charge_timer = event_add(&now, state_charge_timeout, /*state:*/NULL);
102}
103
104
105static
106void
107ChargeConsume(int pktlen)
108{
109    struct timeval now;
110
111    if (g_ctc_packets)
112        g_ctc_packets--;
113
114    if (g_ctc_bytes)
115        g_ctc_bytes -= pktlen;
116
117    IF_TRACED(TRC_CHARGE)
118	dbgprintf("ChargeConsume: CTC now has bytes=" FMT_UINT32 " & packets=" FMT_UINT32 "\n",
119		g_ctc_bytes, g_ctc_packets);
120    END_TRACE
121
122    /* Reset charge timer */
123
124    gettimeofday(&now, NULL);
125    timeval_add_ms(&now, TOPO_CHARGE_TIMEOUT);
126    CANCEL(g_charge_timer);
127    g_charge_timer = event_add(&now, state_charge_timeout, /*state:*/NULL);
128}
129
130
131/***********************  Q U I E S C E N T   S T A T E  ***********************/
132
133static
134enum sm_Status
135smT_QuiescentHandler( protocol_event_t* evt )
136{
137    IF_TRACED(TRC_STATE)
138        if (evt->evtType != evtBlockTimeout)
139        {
140            printf("smT (Quiescent): Entered with event %s",smEvent_names[evt->evtType]);
141            if (g_this_event.evtType==evtPacketRcvd)
142            {
143                printf(" (%s)\n",Topo_opcode_names[g_opcode]);
144            } else {
145                puts("");
146            }
147        }
148    END_TRACE
149
150    switch (evt->evtType)
151    {
152      case evtPacketRcvd:
153        switch (g_opcode)
154        {
155          case Opcode_Charge:
156          case Opcode_Probe:
157          case Opcode_Query:
158          case Opcode_QueryLargeTlv:
159            IF_TRACED(TRC_PACKET)
160                printf("smT (Quiescent): Hoisting packet ignored.\n");
161            END_TRACE
162            break;
163          case Opcode_Emit:	// should never occur - explicit event
164          case Opcode_Reset:	// should never occur - explicit event
165          case Opcode_Discover:	// should never occur - explicit event
166          case Opcode_Hello:
167          case Opcode_Train:
168          case Opcode_ACK:
169          case Opcode_QueryResp:
170          case Opcode_Flat:
171          case Opcode_QueryLargeTlvResp:
172            IF_TRACED(TRC_STATE)
173                printf("smT (Quiescent): Ignored packet w/ known opcode: %s\n",
174                       Topo_opcode_names[g_opcode]);
175            END_TRACE
176            break;
177
178          default:
179            warn("smT (Quiescent): Ignored packet w/ unknown opcode: %d\n",g_opcode);
180            return PROCESSING_ABORTED;
181        }   /*** end of switch (g_opcode) ***/
182        break;
183
184      case evtDiscoverRcvd:
185        if (evt->isAckingMe)
186        {
187            uint16_t	gen = ntohs(g_discover_hdr->mh_gen);
188            if (evt->isInternalEvt == FALSE  &&  // if a real acking-Discover,
189                gen != 0                     &&  // has a non-zero gen,
190                g_generation != gen)             // that differs from ours,
191            {   g_generation = gen;   }   // then save it for future Hellos.
192            IF_TRACED(TRC_STATE)
193                printf("smT (Quiescent): Leaving for Command state\n");
194            END_TRACE
195            g_smT_state = smT_Command;
196        }
197        break;
198
199      case evtInactivityTimeout:
200      case evtResetRcvd:
201        /* If the Topo-session was reset, */
202        if (evt->ssn == g_topo_session)
203        {
204            /* zero the credits, and clear the charge-timer */
205            g_ctc_packets = g_ctc_bytes = 0;
206            CANCEL(g_charge_timer);
207            /* and NULL the g_topo_session ptr */
208            g_topo_session = NULL;
209        }
210        break;
211
212      case evtEmitRcvd:
213        IF_TRACED(TRC_PACKET)
214            printf("smT (Quiescent): Hoisting packet (Emit) ignored.\n");
215        END_TRACE
216        break;
217
218      case evtBlockTimeout:
219        break;
220
221      case evtChargeTimeout:
222      case evtEmitTimeout:
223      case evtHelloDelayTimeout:
224      default:
225        IF_DEBUG
226            printf("smT (Quiescent): Ignored event %s\n",smEvent_names[evt->evtType]);
227        END_DEBUG
228        break;
229    }   /*** end of switch (eventType) ***/
230
231    return PROCESSING_COMPLETED;
232}
233
234/***********************  C O M M A N D   S T A T E  ***********************/
235
236static
237enum sm_Status
238smT_CommandHandler( protocol_event_t* evt )
239{
240    IF_TRACED(TRC_STATE)
241        if (evt->evtType != evtBlockTimeout)
242        {
243            printf("smT (Command): Entered with event %s",smEvent_names[evt->evtType]);
244            if (g_this_event.evtType==evtPacketRcvd)
245            {
246                printf(" (%s)\n",Topo_opcode_names[g_opcode]);
247            } else {
248                puts("");
249            }
250        }
251    END_TRACE
252
253    switch (evt->evtType)
254    {
255      case evtPacketRcvd:
256        switch (g_opcode)
257        {
258          case Opcode_Charge:
259            ChargeAdd(g_rcvd_pkt_len);		// also restarts the charge-timeout timer
260            if (g_sequencenum)
261            {
262                packetio_tx_flat();
263                ChargeConsume(g_tx_len);
264            }
265            break;
266
267          case Opcode_Probe:
268            IF_TRACED(TRC_PACKET)
269                printf("smT (Command): Logging Probe from " ETHERADDR_FMT "\n",ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc));
270            END_TRACE
271            seeslist_enqueue(FALSE, &g_base_hdr->tbh_realsrc);
272            break;
273
274          case Opcode_Query:
275            IF_TRACED(TRC_PACKET)
276                printf("smT (Command): Sending query-Resp\n");
277            END_TRACE
278            packetio_tx_queryresp();
279            break;
280
281          case Opcode_QueryLargeTlv:
282            {
283                tlv_desc_t *tlvDescr = Tlvs;
284
285                /* search the TLVs, which may not be consecutive or in order, for one with the given number */
286                for (;tlvDescr->number != 0; tlvDescr++)
287                {
288                    if (tlvDescr->number == g_qltlv_hdr->qh_type)
289                    {
290                        size_t    offset;
291
292                        IF_TRACED(TRC_PACKET)
293                            printf("smT (Command): Sending qltlv-Resp with LTLV # 0x%X\n", tlvDescr->number);
294                        END_TRACE
295
296                        offset = ntohs(g_qltlv_hdr->qh_offset) + (g_qltlv_hdr->qh_rsvd1 << 16);
297                        packetio_tx_qltlvResp(g_sequencenum, tlvDescr, offset);
298                        break;
299                    }
300                }
301                /* if it falls out without finding a match, nothing will be sent */
302            }
303            break;
304
305          case Opcode_Emit:	// should never occur - explicit event
306          case Opcode_Reset:	// should never occur - explicit event
307          case Opcode_Discover: // should never occur - explicit event
308          case Opcode_QueryLargeTlvResp:
309          case Opcode_Hello:
310          case Opcode_Train:
311          case Opcode_ACK:
312          case Opcode_QueryResp:
313          case Opcode_Flat:
314            IF_TRACED(TRC_PACKET)
315                printf("smT (Command): Ignored packet w/ known opcode: %s\n",Topo_opcode_names[g_opcode]);
316            END_TRACE
317            break;
318
319          default:
320            IF_TRACED(TRC_PACKET)
321                printf("smT (Command): Ignored packet w/ unknown opcode: %d\n",g_opcode);
322            END_TRACE
323            return PROCESSING_ABORTED;
324        }   /*** end of switch (g_opcode) ***/
325        break;
326
327      case evtInactivityTimeout:
328      case evtResetRcvd:
329        /* If the Topo-session timed out, or was Reset, */
330        IF_TRACED(TRC_PACKET)
331            printf("smT (Command): ResetRcvd, %s for topo-session\n",evt->ssn == g_topo_session?"is":"is not");
332        END_TRACE
333        if (evt->ssn == g_topo_session)
334        {
335            /* zero the credits, clear the charge-timer, and NULL out the topo-session ptr */
336            IF_DEBUG
337                puts("zero -> ctc, ctc-timer is cancelled");
338                puts("NULL -> g_topo_session");
339                puts("clearing seeslist");
340            END_DEBUG
341            g_ctc_packets = g_ctc_bytes = 0;
342            CANCEL(g_charge_timer);
343            /* clear the sees-list, */
344            seeslist_clear();
345            /* NULL -> g_topo_session marks the topo-session as completely invalid */
346            g_topo_session = NULL;
347            /* and return to Quiescent state. */
348            IF_TRACED(TRC_PACKET)
349                printf("smT (Command): Leaving for Quiescent (Inactive or Reset).\n");
350            END_TRACE
351            g_smT_state = smT_Quiescent;
352        }
353        break;
354
355      case evtChargeTimeout:
356        /* zero the credits, and clear the charge-timer */
357        g_ctc_packets = g_ctc_bytes = 0;
358        CANCEL(g_charge_timer);
359        break;
360
361      case evtEmitRcvd:
362        /* Special case: Unsequenced Emits MUST NOT have more than one emitee to match Vista
363         *               interpretation of the spec. Any that do, MUST BE IGNORED!           */
364        {
365            topo_emit_header_t*     emit;	/* pointer to emit header in rxbuf */
366            uint16_t		    emiteeCnt;
367
368            emit = (topo_emit_header_t*)(g_base_hdr + 1);
369            emiteeCnt = ntohs(emit->eh_numdescs);
370            if (emiteeCnt > 1  &&  g_sequencenum==0)  break;
371        }
372        /* The response will require charge, so count this receipt */
373        ChargeAdd(g_rcvd_pkt_len);
374        /* If ctc is big enuff, process the emits */
375        if (SufficientChargeAvailable())
376        {
377            /* zero the credits, and clear the charge-timer,
378             * so we won't timeout for lack of charge */
379            g_ctc_packets = g_ctc_bytes = 0;
380            CANCEL(g_charge_timer);
381            /* ready the list of emitees and their count-remaining */
382            ProcessEmits();
383            DEBUG({printf("smT (Command): g_emit_remaining = %d\n",g_emit_remaining);})
384            if (g_emit_remaining > 0)
385            {
386                bool_t  pausing;
387
388                IF_TRACED(TRC_STATE)
389                    printf("smT (Command): Leaving for Emit\n");
390                END_TRACE
391                g_smT_state = smT_Emit;
392                pausing = set_emit_timer();
393                if (pausing!=TRUE)
394                {
395                    g_this_event.evtType = evtEmitTimeout;
396                    IF_TRACED(TRC_STATE)
397                        printf("smT (Command): No Pause in 1st emitee: Inject an EmitTimeout immediately.\n");
398                    END_TRACE
399                    return KEEP_GOING;
400                }
401            } else {
402                /* Emit with 0 descs: valid, but not very useful.
403                 * Stay in Command state (possibly ACKing) */
404                if (g_emit_seqnum)
405                {
406                    packetio_tx_ack(g_emit_seqnum);
407                    ChargeConsume(g_tx_len);
408                }
409            }
410        } else {
411            /* Otherwise, tx-Flat */
412            packetio_tx_flat();
413            ChargeConsume(g_tx_len);
414        }
415        break;
416
417      case evtBlockTimeout:
418        break;
419
420      case evtDiscoverRcvd:
421        { /* need block for declarations */
422            uint16_t	gen = ntohs(g_discover_hdr->mh_gen);
423            if (evt->isInternalEvt == FALSE  &&  // if a real Discover packet,
424                gen != 0                     &&  // has a non-zero gen,
425                g_generation != gen)             // that differs from ours,
426                g_generation = gen;      // then save it for future Hellos.
427        }
428        break;
429
430      case evtEmitTimeout:
431      case evtHelloDelayTimeout:
432      default :
433        IF_TRACED(TRC_STATE)
434            printf("smT (Command): Ignored event %s\n",smEvent_names[evt->evtType]);
435        END_TRACE
436        break;
437    }
438
439    return PROCESSING_COMPLETED;
440}
441
442/***********************  E M I T   S T A T E  ***********************/
443
444static
445enum sm_Status
446smT_EmitHandler( protocol_event_t* evt )
447{
448    IF_TRACED(TRC_STATE)
449        printf("smT (Emit): Entered with event %s",smEvent_names[evt->evtType]);
450        if (g_this_event.evtType==evtPacketRcvd)
451        {
452            printf(" (%s)\n",Topo_opcode_names[g_opcode]);
453        } else {
454            puts("");
455        }
456    END_TRACE
457
458    switch (evt->evtType)
459    {
460      case evtPacketRcvd:
461        switch (g_opcode)
462        {
463          case Opcode_Probe:
464            IF_TRACED(TRC_PACKET)
465                printf("smT (Command): Logging Probe from " ETHERADDR_FMT "\n",ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc));
466            END_TRACE
467            seeslist_enqueue(FALSE, &g_base_hdr->tbh_realsrc);
468            break;
469
470          case Opcode_Emit:	// should never occur - explicit event
471          case Opcode_Reset:	// should never occur - explicit event
472          case Opcode_Discover:	// should never occur - explicit event
473          case Opcode_Charge:
474          case Opcode_Hello:
475          case Opcode_Train:
476          case Opcode_ACK:
477          case Opcode_Query:
478          case Opcode_QueryResp:
479          case Opcode_Flat:
480          case Opcode_QueryLargeTlv:
481          case Opcode_QueryLargeTlvResp:
482            IF_TRACED(TRC_PACKET)
483                printf("smT (Emit): Ignored packet w/ known opcode: %s\n",Topo_opcode_names[g_opcode]);
484            END_TRACE
485            break;
486
487          default:
488            IF_TRACED(TRC_PACKET)
489                printf("smT (Emit): Ignored packet w/ unknown opcode: %d\n",g_opcode);
490            END_TRACE
491            return PROCESSING_ABORTED;
492        }   /*** end of switch (g_opcode) ***/
493        break;
494
495      case evtInactivityTimeout:
496      case evtResetRcvd:
497        /* If the Topo-session was reset, */
498        if (evt->ssn == g_topo_session)
499        {
500            /* zero the credits & the emits-left, and clear the charge-timer and emit-timer, */
501            g_ctc_packets = g_ctc_bytes = g_emit_remaining = 0;
502            CANCEL(g_charge_timer);
503            CANCEL(g_emit_timer);
504            /* clear the sees-list, */
505            seeslist_clear();
506            /* NULL -> g_topo_session marks the topo-session as completely invalid */
507            g_topo_session = NULL;
508            /* and return to Quiescent state. */
509            IF_TRACED(TRC_STATE)
510                printf("smT (Emit): Leaving for Quiescent (Reset or Inactive)\n");
511            END_TRACE
512            g_smT_state = smT_Quiescent;
513        }
514        break;
515
516      case evtEmitTimeout:
517        packetio_tx_emitee(g_emitdesc);
518        g_emit_remaining--;
519        g_emitdesc++;
520        /* any more? */
521        if (g_emit_remaining == 0)
522        {
523            /* go back to Command state (possibly sending ACK as we leave) */
524            IF_TRACED(TRC_STATE)
525                printf("smT (Emit): Leaving for Command\n");
526            END_TRACE
527            g_smT_state = smT_Command;
528            CANCEL(g_emit_timer);
529            if (g_emit_seqnum)
530                packetio_tx_ack(g_emit_seqnum);
531        } else {
532            /* schedule the transmission */
533            if (set_emit_timer() == FALSE)
534            {
535                /* send next with no pause, by re-processing this event */
536                return KEEP_GOING;
537            }
538        }
539        break;
540
541      case evtDiscoverRcvd:
542      case evtEmitRcvd:
543
544      case evtBlockTimeout:
545      case evtChargeTimeout:
546      case evtHelloDelayTimeout:
547      default :
548        IF_TRACED(TRC_STATE)
549            printf("smT (Emit): Ignored event %s\n",smEvent_names[evt->evtType]);
550        END_TRACE
551        break;
552    }
553
554    return PROCESSING_COMPLETED;
555}
556
557
558/***********************                             ***********************/
559/***********************  S T A T E   M A C H I N E  ***********************/
560/***********************                             ***********************/
561
562/* smT_process_event() - process an incoming event (rcvd pkt or timeout)
563 *                       and distribute to the current state's handler	*/
564
565enum sm_Status
566smT_process_event( protocol_event_t* evt )
567{
568    enum sm_Status	ClockControl = PROCESSING_COMPLETED;
569
570//        g_topo_session->ssn_is_valid != TRUE  ||
571
572    if (g_topo_session == NULL                ||	/* if there is no valid topo-session, or    */
573        (evt->ssn && evt->ssn != g_topo_session))       /* this ssn doesn't match the valid topo... */
574    {
575        return PROCESSING_ABORTED;	                /* Fail with an Abort */
576    }
577
578    IF_DEBUG
579//*/    printf("smT_process_event: Entered with event %s\n",smEvent_names[evt->evtType]);
580    END_DEBUG
581
582    /* allow this state machine to autoclock itself, and request further processing */
583    do {
584        IF_DEBUG
585//*/        printf("smT_process_event: Processing event %s\n",smEvent_names[evt->evtType]);
586        END_DEBUG
587        switch (g_smT_state)
588        {
589          case smT_Quiescent:
590            ClockControl = smT_QuiescentHandler( evt );
591            break;
592
593          case smT_Command:
594            ClockControl = smT_CommandHandler( evt );
595            break;
596
597          case smT_Emit:
598            ClockControl = smT_EmitHandler( evt );
599            break;
600
601          default:
602            ClockControl = PROCESSING_ABORTED;	/* Stop! smT in unknown state! */
603            break;
604        }
605        IF_TRACED(TRC_STATE)
606            if (ClockControl == KEEP_GOING) puts("smT: auto-clocking...");
607        END_TRACE
608    } while (ClockControl == KEEP_GOING);
609
610    return ClockControl;
611}
612