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