1: /*
     2:  *  Guitar-ZyX(tm)::MasterControlProgram - portable guitar F/X controller
     3:  *  Copyright (C) 2009  Douglas McClendon
     4:  *
     5:  *  This program is free software: you can redistribute it and/or modify
     6:  *  it under the terms of the GNU General Public License as published by
     7:  *  the Free Software Foundation, version 3 of the License.
     8:  *
     9:  *  This program is distributed in the hope that it will be useful,
    10:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    11:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12:  *  GNU General Public License for more details.
    13:  *
    14:  *  You should have received a copy of the GNU General Public License
    15:  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16: */
    17: /*
    18: #############################################################################
    19: #############################################################################
    20: ## 
    21: ## gzmcpc::network: network functions
    22: ##
    23: #############################################################################
    24: ##
    25: ## Copyright 2008-2009 Douglas McClendon <dmc AT filteredperception DOT org>
    26: ##
    27: #############################################################################
    28: #############################################################################
    29: #
    30: */
    31: 
    32: 
    33: 
    34: 
    35: #include <nds.h> 
    36: 
    37: #include <dswifi9.h> 
    38: 
    39: #include <sys/socket.h> 
    40: 
    41: #include <netinet/in.h> 
    42: 
    43: #include <netdb.h> 
    44: 
    45: #include <unistd.h> 
    46: 
    47: #include <fcntl.h> 
    48: 
    49: #include <stdio.h> 
    50: 
    51: #include <stdarg.h> 
    52: 
    53: #include <string.h> 
    54: 
    55: #include <errno.h> 
    56: 
    57: 
    58: 
    59: #include "mode__tpw__jam.h" 
    60: 
    61: #include "debug.h" 
    62: 
    63: #include "graphics.h" 
    64: 
    65: #include "network.h" 
    66: 
    67: #include "sound.h" 
    68: 
    69: 
    70: #include "sounds.h" 
    71: 
    72: #include "sounds_bin.h" 
    73: 
    74: 
    75: 
    76: 
    77: network_state_mode network_state = NSM_START;
    78: network_state_mode last_network_state = NSM_START;
    79: int nsm_init = 0;
    80: 
    81: u8 network_event_rate = DEFAULT_NETWORK_EVENT_RATE; 
    82: 
    83: int zyx_ap_id = -1;
    84: 
    85: Wifi_AccessPoint zyx_ap;
    86: 
    87: short cmd_port_number = GZMPCD_CMD_PORTNUM;
    88: short bcast_port_number = GZMPCD_BCAST_PORTNUM;
    89: 
    90: unsigned char wepkeys[4][32];
    91: 
    92: int wepkeyid;
    93: 
    94: int wepmode;
    95: 
    96: int dhcp;
    97: 
    98: struct in_addr gzmcpc_ipaddr = {0,};
    99: int gzmcpc_ipaddr_last_byte = {0,};
   100: struct in_addr gzmcpc_subnet_mask = {0,};
   101: struct in_addr gzmcpc_gateway = {0,};
   102: struct in_addr gzmcpc_dns1 = {0,};
   103: struct in_addr gzmcpc_dns2 = {0,};
   104: 
   105: char ap_ssid[SSID_MAXLEN + 1];
   106: 
   107: time_val next_sync_with_server = {0, 0};
   108: 
   109: 
   110: int cstate_preset = CM_PRESET_X_NUM;
   111: int cstate_wpx = 63;
   112: int cstate_wpy = 63;
   113: 
   114: 
   115: int server_socket_fd = 0;
   116: 
   117: 
   118: 
   119: 
   120: struct sockaddr_in bind_address;
   121: 
   122: struct sockaddr_in bcaster_address;
   123: 
   124: int bcast_socket_fd = 0;
   125: long bcast_socket_fd_opts = 0;
   126: 
   127: int network_vanished = 0;
   128: 
   129: gzmcp_cmd newcmd;
   130: 
   131: long magic_cookie;
   132: 
   133: gzmcp_cmd handshake_cmd;
   134: 
   135: time_val net_nap = {0, 0};
   136: 
   137: time_val nsm_timeout = {0, 0};
   138: 
   139: 
   140: int sstate_preset = -1;
   141: int sstate_wpx = -1;
   142: int sstate_wpy = -1;
   143: 
   144: 
   145: 
   146: 
   147: 
   148: 
   149: void gzmcpc_init_net(void) {
   150:   
   151:   // gratuitous (test) debug logging
   152:   debug_log("in gzmcpc_init_net...\n");
   153: 
   154:   // initialize system wireless networking capabilities
   155:   gzmcpc_init_wifi();
   156: 
   157:   // initialize server_socket_fd
   158:   server_socket_fd = 0;
   159:   // initialize bcast_socket_fd
   160:   bcast_socket_fd = 0;
   161:   
   162:   // initialize network status
   163:   textout(TLT_NETWORK_STATUS, "offline");
   164: 
   165:   //
   166:   // initialize handshake
   167:   //
   168: 
   169:   // put the handshake magic cookie where it can be sizeof()d
   170:   magic_cookie = (long)GZMCP_DEF_MAGIC_COOKIE;
   171: 
   172:   // create the handshake_cmd packet (aka the handshake)
   173:   handshake_cmd.type = GZMCP_CMD_HANDSHAKE;
   174:   memcpy((void *)handshake_cmd.data.handshake.string, 
   175: 	 (void *)&magic_cookie,
   176: 	 sizeof(magic_cookie));
   177: 
   178:   // XXX: stackcorruption?
   179:   // this ugliness stays until I find out how the .type field
   180:   // is getting corrupted.
   181:   debug_log_more("handshake_cmd.type is %d\n",
   182: 	    handshake_cmd.type);
   183:   debug_log_more("GZMCP_CMD_HANDSHAKE (gch) is %d\n",
   184: 	    GZMCP_CMD_HANDSHAKE);
   185:   debug_log_more("sizeof magic_cookie is %d\n",
   186: 	    sizeof(magic_cookie));
   187:   debug_log_more("handshake created...\n");
   188: 
   189: }
   190: 
   191: 
   192: 
   193: void gzmcpc_init_wifi(void) {
   194:   
   195:   // gratuitous (test) debug logging
   196:   debug_log("in gzmcpc_init_wifi...\n");
   197: 
   198:   // XXX: document the parameter (replace this comment)
   199:   Wifi_InitDefault(false);
   200:   
   201:   // init/cleanup (?unknown if needed, vaguely recall recommended)
   202:   Wifi_DisconnectAP();
   203: 
   204:   // init/cleanup (?unknown if needed, vaguely recall recommended)
   205:   Wifi_DisableWifi();
   206: 
   207: }
   208: 
   209: 
   210: void socket_set_blocking(int socket_fd) {
   211: 
   212:   int val_arg = 0;
   213: 
   214:   retval = ioctl(socket_fd, FIONBIO,  &val_arg);
   215: 
   216:   // effective assertion
   217:   if (retval < 0) {
   218:     textout(TLT_ERROR, "socket ioctl failed");
   219:     die();
   220:   }
   221: 
   222: }
   223: 
   224: 
   225: void socket_set_nonblocking(int socket_fd) {
   226: 
   227:   int val_arg = 1;
   228: 
   229:   retval = ioctl(socket_fd, FIONBIO,  &val_arg);
   230: 
   231:   // effective assertion
   232:   if (retval < 0) {
   233:     textout(TLT_ERROR, "socket ioctl failed");
   234:     die();
   235:   }
   236: 
   237: }
   238: 
   239: 
   240: 
   241: void debug_dump_cmd(gzmcp_cmd cmd) {
   242:   debug_log_more("debug_dump_command: cmd.type: %08x\n",
   243: 	    cmd.type);
   244:   debug_log_more("debug_dump_command: cmd[1-4]: %08x\n",
   245: 	    ((long *)&cmd)[0]);
   246:   debug_log_more("debug_dump_command: cmd[5-8]: %08x\n",
   247: 	    ((long *)&cmd)[1]);
   248:   debug_log_more("debug_dump_command: cmd[9-12]: %08x\n",
   249: 	    ((long *)&cmd)[2]);
   250:   debug_log_more("debug_dump_command: cmd[13-16]: %08x\n",
   251: 	    ((long *)&cmd)[3]);
   252: }
   253: 
   254: 
   255: 
   256: int xmit_cmd(int socket_fd, gzmcp_cmd cmd) {
   257: 
   258:   // for read/write return values
   259:   int numbytes;
   260: 
   261:   // only do work if needed
   262:   if ((network_state != NSM_ONLINE) &&
   263:       (cmd.type != GZMCP_CMD_HANDSHAKE)) {
   264:     return 0;
   265:   }
   266: 
   267:   debug_dump_cmd(cmd);
   268: 
   269:   // write the test message to the socket
   270:   numbytes = send(socket_fd,
   271: 		  (void *)&cmd,
   272: 		  sizeof(gzmcp_cmd),
   273: 		  0);
   274: 
   275:   // check for errors
   276:   //  if (numbytes < 0) textout(TLT_ERROR, "xmit_cmd:send");
   277:   // or not since non-blocking (but still should check for other errors)
   278: 
   279:   return numbytes;
   280: 
   281: }
   282: 
   283: 
   284: 
   285: void sync_with_server(void) {
   286: 
   287:   if (cstate_preset != sstate_preset) {
   288: 
   289:     //
   290:     // create a command packet and transmit to server
   291:     //
   292:     newcmd.type = GZMCP_CMD_PRESET;
   293:     newcmd.data.preset.channel = 1;
   294:     newcmd.data.preset.preset = cstate_preset;
   295:     xmit_cmd(server_socket_fd, newcmd);
   296: 
   297:     // update state
   298:     sstate_preset = cstate_preset;
   299:   }
   300: 
   301:   if (touch_whammy_enabled) {
   302: 
   303:     // XXX add optional decaying logic, i.e. only half the difference each time
   304:     
   305:     if (cstate_wpx != sstate_wpx) {
   306:       //
   307:       // create a command packet and transmit to server
   308:       //
   309:       newcmd.type = GZMCP_CMD_PARAMETER;
   310:       newcmd.data.parameter.channel = 1;
   311:       newcmd.data.parameter.parameter = touch_whammy_x_midi_parm;
   312:       newcmd.data.parameter.value = cstate_wpx;
   313:       xmit_cmd(server_socket_fd, 
   314: 	       newcmd);
   315:       
   316:       // update state
   317:       sstate_wpx = cstate_wpx;
   318:     }
   319: 
   320:     if (cstate_wpy != sstate_wpy) {
   321:       //
   322:       // create a command packet and transmit to server
   323:       //
   324:       newcmd.type = GZMCP_CMD_PARAMETER;
   325:       newcmd.data.parameter.channel = 1;
   326:       newcmd.data.parameter.parameter = touch_whammy_y_midi_parm;
   327:       newcmd.data.parameter.value = cstate_wpy;
   328:       xmit_cmd(server_socket_fd, 
   329: 	       newcmd);
   330:       
   331:       // update state
   332:       sstate_wpy = cstate_wpy;
   333:     }
   334: 
   335:   }
   336: 
   337: }
   338: 
   339: 
   340: 
   341: void netmode_go_offline(void) {
   342: 
   343:   // may be nothing to shutdown, so ignore return value
   344:   shutdown(server_socket_fd, 0);
   345:   closesocket(server_socket_fd);
   346:   shutdown(bcast_socket_fd, 0);
   347:   closesocket(bcast_socket_fd);
   348: 
   349:   // disconnect from network/AP
   350:   Wifi_DisconnectAP();
   351:   Wifi_DisableWifi();
   352: 
   353:   // cursor rotating is online indicator
   354:   cursor_rotating = 0;
   355:   
   356:   // print meaningful data to top lcd
   357:   textout(TLT_MODE, "L+start to go online");
   358:   // leave STATUS alone, as it may have an error message
   359:   //  textout(TLT_STATUS, "XXX");
   360:   textout(TLT_NETWORK_STATUS, "offline");
   361:   textout(TLT_FX_MODE, "N/A");
   362:   textout(TLT_TOUCH_VAL, "X=NA /\\/ Y=NA");
   363:   
   364:   if (!user_vstrobe_enabled) {
   365:     vstrobe_enabled = 0;
   366:   }
   367: 
   368:   // ooh, ahh, earcandy
   369:   mmEffectEx(&sounds[SFX_OFFLINE]);
   370: 
   371: }
   372: 
   373: 
   374: 
   375: void netmode_offline_init(void) {
   376: 
   377:   netmode_go_offline();
   378: 
   379: }
   380: 
   381: 
   382: 
   383: network_state_mode netmode_offline_handle(void) {
   384:   // nothing to do while OFFLINE
   385:   return NSM_OFFLINE;
   386: }
   387: 
   388: 
   389: 
   390: void netmode_wscanning_init(void) {
   391: 
   392:   // extra cleanslate attempt (is start button bypassing go_offline somehow?)
   393:   Wifi_DisconnectAP();
   394:   Wifi_DisableWifi();
   395: 
   396:   Wifi_EnableWifi();
   397: 
   398:   vstrobe_enabled = 1;
   399:   
   400:   // initialize text display 
   401:   // TODO generate progrometer from start time
   402:   textout(TLT_MODE, "net::going online");
   403:   textout(TLT_STATUS, "net init...");
   404:   textout(TLT_NETWORK_STATUS, "scanning nets...");
   405: 
   406:   Wifi_ScanMode();
   407: 
   408:   // set the network mode timeout relative to now
   409:   nsm_timeout = time_val_add_ms(num_ticks, 
   410: 				1000 * WSCANNING_TIMEOUT_S);
   411:     
   412: }
   413: 
   414: 
   415: 
   416: network_state_mode netmode_wscanning_handle(void) {
   417: 
   418:   // for storing data of ap's from scan 
   419:   Wifi_AccessPoint ap;
   420:   // and the ssid thereof
   421:   char ssidbuf[SSID_MAXLEN + 1];
   422: 
   423:   int i;
   424:   int num_networks;
   425: 
   426: 
   427:   // check for timeout condition
   428:   if (time_val_compare(num_ticks, nsm_timeout) < 0) {
   429:     // timeout expired
   430:     textout(TLT_STATUS, "wifi ap search timeout");
   431:     return NSM_OFFLINE;
   432:   } // end if timeout expired
   433: 
   434:   // note: could add logic here to make sure the search code
   435:   //       is only executed 10 times a second.  Curious to
   436:   //       see if it effects the UI.
   437: 
   438:   num_networks = Wifi_GetNumAP();
   439:       
   440:   textout(TLT_STATUS, "%d networks found", num_networks);
   441: 
   442:   zyx_ap_id = -1;
   443:   
   444:   for (i = 0 ; i < num_networks ; i++) {
   445:     
   446:     if (WIFI_RETURN_OK == Wifi_GetAPData(i,&ap)) {
   447:       
   448:       strncpy(ssidbuf, ap.ssid, ap.ssid_len);
   449:       
   450:       // assumption: Wifi_GetAPData's returned ap. will never
   451:       //             have ap.ssid_len (and .ssid) of >32, as
   452:       //             per wifi spec.  ssidbuf is 64.
   453:       // null terminate
   454:       ssidbuf[(int)ap.ssid_len] = 0;
   455:       
   456:       if (! strncmp(ap_ssid, ssidbuf, strlen(ap_ssid))) {
   457: 	zyx_ap_id = i;
   458:       }
   459:       
   460:     } // end got data for ap
   461:     
   462:   } // end iteration over networks
   463:   
   464:   if (zyx_ap_id != -1) {
   465:     
   466:     return NSM_WASSOCIATING;
   467: 
   468:   }
   469: 
   470:   // havent found a good wifi ap yet, keep trying
   471:   return NSM_WSCANNING;
   472: }
   473: 
   474: 
   475: 
   476: void netmode_wassociating_init(void) {
   477: 
   478:   unsigned long dns1, dns2;
   479: 
   480:   // found a Guitar-ZyX Access Point
   481:   textout(TLT_MODE, "net::going online");
   482:   textout(TLT_STATUS, "found mcpnet...");
   483:   textout(TLT_NETWORK_STATUS, "connecting to net...");
   484: 
   485:   // in this case, flag _handle to go offline with appropriate message
   486:   if (Wifi_GetAPData(zyx_ap_id, &zyx_ap) != WIFI_RETURN_OK) {
   487:     network_vanished = 1;
   488:     // don't try to do rest of init
   489:     return;
   490:   } else {
   491:     network_vanished = 0;
   492:   }
   493: 
   494:   //
   495:   // initialize wep config
   496:   //
   497:   
   498:   wepkeyid = 0;
   499:   
   500:   // initialize wepkeys to null terminated strings
   501:   wepkeys[0][0] = 0;
   502:   wepkeys[1][0] = 0;
   503:   wepkeys[2][0] = 0;
   504:   wepkeys[3][0] = 0;
   505:   
   506:   wepmode = 0;
   507:   
   508:   // dns1 = 192.168.1.1
   509:   // dns1 = 0x0101A8C0;
   510:   // dns1 = 0xC0A80101;
   511:   dns1 = 0;
   512:   
   513:   // dns2 = 0.0.0.0
   514:   dns2 = 0;
   515:   
   516:   // dhcp IP/not dns, because gzmcp does not use dns
   517:   Wifi_SetIP(0, 0, 0, dns1, dns2);
   518:   
   519:   if (strncmp(ap_ssid, "wfc", strlen(ap_ssid)) == 0) {
   520:     // use user's firmware WFC connection info 
   521:     // (as configured by another wifi enabled game/app)
   522:     Wifi_AutoConnect();
   523:   } else {
   524:     // use the user supplied ap_ssid
   525:     Wifi_ConnectAP(&zyx_ap, wepmode, wepkeyid, wepkeys[0]);
   526:   }
   527: 
   528:   textout(TLT_STATUS, "connecting to ap...");
   529:   
   530:   // set the scan timeout relative to now
   531:   nsm_timeout = time_val_add_ms(num_ticks, 
   532: 				1000 * WASSOCIATING_TIMEOUT_S);
   533: 
   534: }
   535: 
   536: 
   537: 
   538: network_state_mode netmode_wassociating_handle(void) {
   539: 
   540:   int associated;
   541: 
   542:   // check for timeout condition
   543:   if (time_val_compare(num_ticks, nsm_timeout) < 0) {
   544:     // timeout expired
   545:     textout(TLT_STATUS, "net assoc timeout");
   546:     return NSM_OFFLINE;
   547:   } // end if timeout expired
   548: 
   549:   if (network_vanished) {
   550:     textout(TLT_STATUS, "network vanished");
   551:     return NSM_OFFLINE;
   552:   }
   553: 
   554:   //
   555:   // check associating status
   556:   //
   557:   associated = Wifi_AssocStatus();
   558: 
   559:   if (associated == ASSOCSTATUS_ASSOCIATED) {
   560: 
   561:     // clear mode&status now that connection is done
   562:     textout(TLT_MODE, "");
   563:     textout(TLT_STATUS, "");
   564: 
   565:     // store gzmcpc's ip info
   566:     gzmcpc_ipaddr = Wifi_GetIPInfo(&gzmcpc_gateway,
   567: 				   &gzmcpc_subnet_mask,
   568: 				   &gzmcpc_dns1,
   569: 				   &gzmcpc_dns2);
   570: 
   571:     // transmogrify network byte order into host byte order
   572:     gzmcpc_ipaddr.s_addr = ntohl(gzmcpc_ipaddr.s_addr);
   573:     gzmcpc_gateway.s_addr = ntohl(gzmcpc_gateway.s_addr);
   574:     gzmcpc_subnet_mask.s_addr = ntohl(gzmcpc_subnet_mask.s_addr);
   575:     gzmcpc_dns1.s_addr = ntohl(gzmcpc_dns1.s_addr);
   576:     gzmcpc_dns2.s_addr = ntohl(gzmcpc_dns2.s_addr);
   577:     
   578:     // get the last byte of our ipaddr, for possible debug output
   579:     //    gzmcpc_ipaddr_last_byte = 
   580:     //      (int)(gzmcpc_ipaddr.s_addr - 
   581:     //	    (gzmcpc_ipaddr.s_addr & gzmcpc_subnet_mask.s_addr));
   582: 
   583:     return NSM_BCAST_LISTEN;
   584: 
   585:   } // end case of assoc status == associated
   586: 
   587:   if (associated == ASSOCSTATUS_CANNOTCONNECT) {
   588: 
   589:     textout(TLT_STATUS, "net assoc failed");
   590: 
   591:     return NSM_OFFLINE;
   592: 
   593:   }
   594: 
   595:   // not yet associated, keep on trying...
   596:   return NSM_WASSOCIATING;
   597: 
   598: }
   599: 
   600: 
   601: 
   602: void netmode_bcast_listen_init(void) {
   603: 
   604:   int optval;
   605: 
   606: 
   607:   textout(TLT_MODE, "net::going online");
   608:   textout(TLT_STATUS, "joined network");
   609:   textout(TLT_NETWORK_STATUS, "server discovery...");
   610: 
   611:   debug_log("listening for server broadcasts on udp port %d\n",
   612: 	    bcast_port_number);
   613:   
   614:   debug_log("listening for magic cookie - 0x%08x\n",
   615: 	    magic_cookie);
   616:   
   617:   // create the broadcast reception udp socket
   618:   //   - AF_INET means ipv4, vs AF_INET6 vs AF_UNIX/AF_LOCAL
   619:   //   - SOCK_DGRAM means udp, vs SOCK_STREAM for tcp
   620:   //   - 0 means default protocol type for family
   621:   bcast_socket_fd = socket(AF_INET, 
   622: 			   SOCK_DGRAM,
   623: 			   0);
   624:   if (bcast_socket_fd < 0) {
   625:     perror("socket()");
   626:     textout(TLT_ERROR, "couldn't create broadcast socket");
   627:     die();
   628:   }
   629:   
   630:   // enable broadcast
   631:   // note: perhaps this is unnecessary, since we only receive
   632:   optval = 1;
   633:   if (setsockopt(bcast_socket_fd, 
   634: 		 SOL_SOCKET, 
   635: 		 SO_BROADCAST, 
   636: 		 &optval, sizeof(optval)) < 0) {
   637:     perror("setsockopt()");
   638:     debug_log("failed to set broadcast socket options\n");
   639:     textout(TLT_ERROR, "failed to set broadcast socket options");
   640:     die();
   641:   }
   642:       
   643:   // create a destination address structure, initialize...
   644:   memset((void *)&bind_address, 0, sizeof(bind_address));
   645:   
   646:   // configure the address structure for our purposes
   647:   // INET, vs e.g. UNIX for local non network sockets
   648:   bind_address.sin_family = AF_INET;
   649:   bind_address.sin_port = htons(bcast_port_number);
   650:   // we're going to bind to all local interfaces
   651:   bind_address.sin_addr.s_addr = htonl(INADDR_ANY);
   652:   
   653:   if (bind(bcast_socket_fd, 
   654: 	   (struct sockaddr *)&bind_address,
   655: 	   sizeof(bind_address)) < 0) {
   656:     perror("bind()");
   657:     textout(TLT_ERROR, "failed to bind to broadcast socket");
   658:     die();
   659:   }
   660:   
   661:   debug_log("now setting bcast socket to nonblocking...\n");
   662: 
   663:   // set socket to non-blocking mode
   664:   socket_set_nonblocking(bcast_socket_fd);
   665: 
   666:   // set the scan timeout relative to now
   667:   nsm_timeout = time_val_add_ms(num_ticks, 
   668: 				1000 * BCAST_LISTEN_TIMEOUT_S);
   669:   
   670: }
   671: 
   672: 
   673: 
   674: network_state_mode netmode_bcast_listen_handle(void) {
   675: 
   676:   // buffer to receive broadcast messages
   677:   char beacon_msg[GZMCP_BEACON_MSG_MAXLEN + 1];
   678: 
   679:   // size of returned address structure
   680:   unsigned int from_length;
   681: 
   682:   // size of broadcast message data received
   683:   int bytes_received;
   684: 
   685: 
   686:   // check for timeout condition
   687:   if (time_val_compare(num_ticks, nsm_timeout) < 0) {
   688:     // timeout expired
   689:     textout(TLT_STATUS, "server discovery timeout");
   690:     return NSM_OFFLINE;
   691:   } // end if timeout expired
   692:   
   693:   
   694:   // initialize the length of the address result
   695:   from_length = sizeof(bcaster_address);
   696:   
   697:   // note: cast (int *) is due to different nds devsuite impl??
   698:   bytes_received = recvfrom(bcast_socket_fd,
   699: 			    beacon_msg,
   700: 			    GZMCP_BEACON_MSG_MAXLEN,
   701: 			    0,
   702: 			    (struct sockaddr*)&bcaster_address,
   703: 			    (int *)&from_length);
   704: 
   705:   debug_log("debug: bytes_received for beacon was %d\n", 
   706:   	   bytes_received);
   707: 
   708:   // check for locally failed recv
   709:   if (bytes_received < 0) {
   710:     // udp makes no guarantees
   711:     //      perror("sendto()");
   712:     debug_log_more("could not receive message\n");
   713:     return NSM_BCAST_LISTEN;
   714:   }
   715: 
   716:   // check if message is a gzmcp server broadcast message
   717:   if (bcmp((void *)beacon_msg, 
   718: 	   (void *)&magic_cookie, 
   719: 	   4) == 0) {
   720:     // a correct magic cookie
   721:     debug_log("server at %s had the right magic cookie!\n", 
   722: 	      inet_ntoa(bcaster_address.sin_addr));
   723:     
   724:     // throttle a bit?
   725:     net_nap = time_val_add_ms(num_ticks, 420);
   726: 
   727:     // success, target server is in bcaster_address
   728:     return NSM_UNCONNECTED;
   729:     
   730:   } else {
   731:     // not a correct magic cookie
   732:     debug_log("got an incorrect magic cookie! ignoring...\n");
   733:   }
   734: 
   735:   // keep listening...
   736:   return NSM_BCAST_LISTEN;
   737: }
   738: 
   739: 
   740: 
   741: void netmode_unconnected_init(void) {
   742: 
   743:   debug_log("server found at ip address -- %s\n",
   744: 	    inet_ntoa(bcaster_address.sin_addr));
   745: 
   746:   textout(TLT_MODE, "net::going online");
   747:   textout(TLT_STATUS, 
   748: 	  "server found"); 
   749:   //  textout(TLT_STATUS, 
   750:   //	  "srv::%s",
   751:   //	  inet_ntoa(bcaster_address.sin_addr));
   752:   textout(TLT_NETWORK_STATUS, "connecting...");
   753: 
   754:   //
   755:   // open an internet stream socket for the client
   756:   //
   757:   
   758:   // pointless init
   759:   server_socket_fd = 0;
   760:   
   761:   // create socket
   762:   server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
   763:   
   764:   // error check socket()
   765:   if (server_socket_fd < 0) {
   766:     
   767:     // TODO: make something like this the assertion function
   768:     textout(TLT_ERROR, "socket open failed");
   769:     
   770:     // assertion: should not fail
   771:     die();
   772:     
   773:   }
   774:       
   775:   // connect to cmd_port_number on the found server
   776:   bcaster_address.sin_port = htons(cmd_port_number);
   777:       
   778:   // set socket to non-blocking mode
   779:   socket_set_nonblocking(server_socket_fd);
   780:   
   781:   // debug throttling
   782:   net_nap = time_val_add_ms(num_ticks, 420);
   783:   
   784: }
   785: 
   786: 
   787: 
   788: network_state_mode netmode_unconnected_handle(void) {
   789: 
   790:   // NOTE: I was confused a bit, and had const as part of the cast of
   791:   //       arg 2, but then realized the confusion was because const
   792:   //       wasn't a necessary (or sensical) part of the cast.  Actually
   793:   //       I'm still a bit confused.  The const in the man 2 connect
   794:   //       prototype confuses me...  I.e. the below doesn't generate
   795:   //       warnings, but theoretically the data in bcaster_address is
   796:   //       not const.  (could be modified by another thread?).
   797:   
   798:   retval = connect(server_socket_fd,
   799: 		   (struct sockaddr *)&bcaster_address,
   800: 		   sizeof(bcaster_address));
   801: 
   802:   debug_log_more("connect retval for server connect was - %d ...\n", 
   803: 	    retval);
   804: 
   805:   // historic(?) note: bad things happened with a textout+delay of errno here
   806:   // that was a delay with the archaic wait_a_bit function which spun around
   807:   // vblank intr
   808:   
   809:   // XXX catch and check errno appropriately 
   810: 
   811:   // network throttling (in other words, probably a network race condition)
   812:   // (or maybe closesocket fixed that)
   813:   net_nap = time_val_add_ms(num_ticks, 420);
   814:   
   815:   // nonblocking, nonfatal
   816:   if (retval < 0) {
   817:     // keep trying...
   818:     return NSM_UNCONNECTED;
   819:   }
   820:   
   821:   // throttle a bit?
   822:   net_nap = time_val_add_ms(num_ticks, 420);
   823: 
   824:   debug_log("server socket now connected...\n");
   825: 
   826:   return NSM_CONNECTED;
   827: 
   828: }
   829: 
   830: 
   831: 
   832: void netmode_connected_init(void) {
   833: 
   834: }
   835: 
   836: 
   837: 
   838: network_state_mode netmode_connected_handle(void) {
   839: 
   840:   //
   841:   // connected, time to send handshake
   842:   //
   843:   // MAYBETODO: add current datestamp as part of handshake (maybe hashed)
   844:   //            - or app version hash, password from flash, ...
   845: 
   846:   
   847:   // XXX: this is a workaround to ?stackcorruption?  I.e. should only
   848:   //      need to set this once, but somehow the .type field is getting
   849:   //      reset to 0???
   850:   // create the handshake_cmd packet (aka the handshake)
   851:   handshake_cmd.type = GZMCP_CMD_HANDSHAKE;
   852:   memcpy((void *)handshake_cmd.data.handshake.string, 
   853: 	 (void *)&magic_cookie,
   854: 	 sizeof(magic_cookie));
   855: 
   856:   // XXX: stackcorruption? (see other XXX comments)
   857:   debug_log_more("dumping handshake_cmd before sending it\n");
   858:   debug_dump_cmd(handshake_cmd);
   859: 
   860:   //
   861:   // transmit handshake to server
   862:   //
   863:   retval = xmit_cmd(server_socket_fd, 
   864: 		    handshake_cmd);
   865: 
   866:   debug_log_more("trying to send handshake, xmit_cmd retval is %d\n",
   867: 	    retval);
   868: 
   869:   // XXX: stackcorruption? (see other XXX comments)
   870:   debug_log_more("dumping handshake_cmd after sending it\n");
   871:   debug_dump_cmd(handshake_cmd);
   872: 
   873:   if (retval == sizeof(gzmcp_cmd)) {
   874:     
   875:     textout(TLT_STATUS, "sent handshake"); 
   876:     
   877:     //net_nap = time_val_add_ms(num_ticks, 42);
   878: 
   879:     return NSM_HANDSHAKE_SENT;
   880:     
   881:   } else {
   882:     
   883:     // this is acceptable nonblocking behavior
   884:     
   885:   } // end if/else handshake xmit_cmd-ed correctly
   886:   
   887: 
   888:   // keep trying...
   889:   return NSM_CONNECTED;
   890: 
   891: }
   892: 
   893: 
   894: 
   895: void netmode_handshake_sent_init(void) {
   896: 
   897:   // TODO: either init textouts here, or collapse this mode into unconnected
   898: 
   899: }
   900: 
   901: 
   902: 
   903: network_state_mode netmode_handshake_sent_handle(void) {
   904: 
   905:   int numbytes;
   906:   gzmcp_cmd reflected_handshake_cmd;
   907: 
   908:   //
   909:   // handshake sent, listen for reciprocation
   910:   //
   911: 
   912:   // XXX: stackcorruption? (see other XXX comments)
   913:   handshake_cmd.type = GZMCP_CMD_HANDSHAKE;
   914:   
   915:   // XXX: stackcorruption? (see other XXX comments)
   916:   debug_log_more("dumping handshake_cmd before receiving reflected\n");
   917:   debug_dump_cmd(handshake_cmd);
   918: 
   919:   ////
   920:   //// receive the reflected handshake
   921:   ////
   922:   numbytes = recv(server_socket_fd,
   923: 		  (void *)&reflected_handshake_cmd,
   924: 		  sizeof(gzmcp_cmd), 
   925: 		  0);
   926:   // nonblocking, nonfatal
   927:   // XXX catch errno and handle server disconnecting, etc... (prob elsewhere too)
   928:   if (numbytes < 0) {
   929:     // note, this (strerror) workes, but I'm getting "no more processes here"??
   930:     debug_log_more("perror...%s\n",
   931: 		   strerror(errno));
   932:     debug_log_more("failed to receive handshake -nb-%d\n",
   933: 	      numbytes);
   934:     // keep trying...
   935:     return NSM_HANDSHAKE_SENT;
   936:   }
   937: 
   938:   if (numbytes != sizeof(gzmcp_cmd)) {
   939:     debug_log_more("failed to receive handshake -nb-%d\n",
   940: 		   numbytes);
   941:     //    textout(TLT_DEBUG,
   942:     //	    "hndshk_recv_sz:%d",
   943:     //	    numbytes);
   944: 
   945:     // keep trying...
   946:     return NSM_HANDSHAKE_SENT;
   947:   }
   948: 
   949:   // XXX: stackcorruption? (see other XXX comments)
   950:   debug_log_more("dumping handshake_cmd before memcmp\n");
   951:   debug_dump_cmd(handshake_cmd);
   952: 
   953:   // check reflected handshake against the original
   954:   if (memcmp((void*)&reflected_handshake_cmd,
   955: 	     (void*)&handshake_cmd,
   956: 	     sizeof(gzmcp_cmd)) != 0 ) {
   957: 
   958:     // XXX: stackcorruption? (see other XXX comments)
   959:     debug_log_more("dumping handshake_cmd after memcmp\n");
   960:     debug_dump_cmd(handshake_cmd);
   961:     debug_log_more("dumping reflected_handshake_cmd after memcmp\n");
   962:     debug_dump_cmd(reflected_handshake_cmd);
   963:     
   964:     
   965:     //
   966:     // incorrect handshake
   967:     //
   968:     textout(TLT_STATUS, 
   969: 	    "bad handshake reply");
   970:     textout(TLT_DEBUG, 
   971: 	    "bhsr:%s:", 
   972: 	    reflected_handshake_cmd.data.handshake.string);
   973:     
   974:     debug_log("bad handshake received :: %s ::\n", 
   975: 	      reflected_handshake_cmd.data.handshake.string);
   976: 	
   977:     // keep trying as this is not a fatal error...
   978:     return NSM_HANDSHAKE_SENT;
   979: 	
   980:   } else {
   981:     //
   982:     // correct handshake
   983:     //
   984:     textout(TLT_STATUS, "handshake received");
   985: 
   986:     return NSM_ONLINE;
   987: 	
   988:   } // end if correct handshake
   989: 
   990: }
   991: 
   992: 
   993: 
   994: void netmode_online_init(void) {
   995: 
   996:   // clear mode now that we are connected
   997:   textout(TLT_MODE, "online! :)");
   998:   
   999:   // reset socket to blocking mode
  1000:   // TODO: investigate using nonblocking mode for user data
  1001:   // TODO: investigate using udp for user data
  1002:   socket_set_blocking(server_socket_fd);
  1003: 
  1004:   // send initial command
  1005:   // TODO this should be a function (duplication with other code, abstract button...)
  1006: 
  1007:   //
  1008:   // create and send the new command/packet
  1009:   //
  1010:   newcmd.type = GZMCP_CMD_PRESET;
  1011:   newcmd.data.preset.channel = 1;
  1012:   newcmd.data.preset.preset = button_b_preset_num;
  1013:   xmit_cmd(server_socket_fd, newcmd);
  1014: 
  1015:   current_preset = button_b_preset_num;
  1016:   
  1017:   // ooh, ahh, earcandy
  1018:   mmEffectEx(&sounds[SFX_ONLINE]);
  1019: 
  1020:   // initialize text display 
  1021:   textout(TLT_MODE, "cloudsession live!");
  1022:   textout(TLT_STATUS, "jam on!!!!!!!");
  1023:   textout(TLT_NETWORK_STATUS, "online");
  1024:   
  1025:   if (!user_vstrobe_enabled) {
  1026:     vstrobe_enabled = 0;
  1027:   }
  1028: 
  1029: }
  1030: 
  1031: 
  1032: 
  1033: network_state_mode netmode_online_handle(void) {
  1034:   // nothing to do while ONLINE
  1035:   return NSM_ONLINE;
  1036: }
  1037: 
  1038: 
  1039: /* TEMPLATE NETWORK STATE MODE FUNCTION PAIR
  1040: 
  1041: void netmode_xxx_init(void) {
  1042: 
  1043: 
  1044: }
  1045: 
  1046: 
  1047: 
  1048: network_state_mode netmode_xxx_handle(void) {
  1049:   // nothing to do while XXX
  1050:   return NSM_XXX;
  1051: }
  1052: 
  1053: 
  1054: END TEMPLATE NETWORK STATE MODE FUNCTION PAIR */
  1055: 
  1056: 
  1057: 
  1058: network_state_mode gzmcpc_net_next_state(network_state_mode current) {
  1059: 
  1060:   network_state_mode next_state;
  1061: 
  1062: 
  1063:   debug_log_more("net_next_state: %d\n", current);
  1064: 
  1065:   // do nothing interesting by default
  1066:   next_state = current;
  1067: 
  1068:   // check for net_nap throttling
  1069:   // i.e. "if the current time is smaller than the nap marker, do nothing"
  1070:   if (time_val_compare(net_nap, num_ticks) < 0) {
  1071:     debug_log_more("net napping %d %d...\n",
  1072: 		   num_ticks.s, num_ticks.ms);
  1073:     return current;
  1074:   }
  1075: 
  1076:   debug_log_more("not net napping...\n");
  1077: 
  1078:   //
  1079:   // handle all possible present states
  1080:   //
  1081:   // TODO: could convert this to an array of funcpointers like I do for main mode
  1082:   //       (i.e. no code redundancy)
  1083:   switch (current) {
  1084:     
  1085:   case NSM_START:
  1086:     // START is somewhat bogus
  1087: 
  1088:     // jump strait to WSCANNING
  1089:     return NSM_WSCANNING;
  1090:     
  1091:     break;
  1092: 
  1093:   case NSM_OFFLINE:
  1094: 
  1095:     if (!nsm_init) {
  1096:       netmode_offline_init();
  1097:       nsm_init = 1;
  1098:     }
  1099: 
  1100:     return netmode_offline_handle();
  1101: 
  1102:     break;
  1103: 
  1104:   case NSM_WSCANNING:
  1105: 
  1106:     if (!nsm_init) {
  1107:       netmode_wscanning_init();
  1108:       nsm_init = 1;
  1109:     }
  1110: 
  1111:     return netmode_wscanning_handle();
  1112: 
  1113:     break;
  1114: 
  1115:   case NSM_WASSOCIATING:
  1116: 
  1117:     if (!nsm_init) {
  1118:       netmode_wassociating_init();
  1119:       nsm_init = 1;
  1120:     }
  1121: 
  1122:     return netmode_wassociating_handle();
  1123: 
  1124:     break;
  1125: 
  1126:   case NSM_BCAST_LISTEN:
  1127: 
  1128:     if (!nsm_init) {
  1129:       netmode_bcast_listen_init();
  1130:       nsm_init = 1;
  1131:     }
  1132: 
  1133:     return netmode_bcast_listen_handle();
  1134: 
  1135:     break;
  1136: 
  1137:   case NSM_UNCONNECTED:
  1138: 
  1139:     if (!nsm_init) {
  1140:       netmode_unconnected_init();
  1141:       nsm_init = 1;
  1142:     }
  1143: 
  1144:     return netmode_unconnected_handle();
  1145: 
  1146:     break;
  1147: 
  1148:   case NSM_CONNECTED:
  1149: 
  1150:     if (!nsm_init) {
  1151:       netmode_connected_init();
  1152:       nsm_init = 1;
  1153:     }
  1154: 
  1155:     return netmode_connected_handle();
  1156: 
  1157:     break;
  1158: 
  1159:   case NSM_HANDSHAKE_SENT:
  1160: 
  1161:     if (!nsm_init) {
  1162:       netmode_handshake_sent_init();
  1163:       nsm_init = 1;
  1164:     }
  1165: 
  1166:     return netmode_handshake_sent_handle();
  1167: 
  1168:     break;
  1169: 
  1170:   case NSM_ONLINE:
  1171: 
  1172:     if (!nsm_init) {
  1173:       netmode_online_init();
  1174:       nsm_init = 1;
  1175:     }
  1176: 
  1177:     return netmode_online_handle();
  1178: 
  1179:     break;
  1180: 
  1181:   } // end switch (current) // current state
  1182: 
  1183:   // default to same state, though code should never reach here
  1184:   return next_state;
  1185: 
  1186: } // end gzmcpc_net_next_state()
  1187: 
  1188: