/*====================================================================* * * Copyright (c) 2013 Qualcomm Atheros, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted (subject to the limitations * in the disclaimer below) provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of Qualcomm Atheros nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *--------------------------------------------------------------------*/ /*====================================================================* * * pev.c - QCA Plug-in Electric Vehicle Emulator; * * This program, in the current state, is not a finished product; * It has been released so that interested parties can begin to * see how the SLAC protocol might be implemented; * * Some key design features are: * * 1) the use of a channel variable to abstract ISO Layer 2 I/O; * the variable is used by functions openchannel, readmessage, * sendmessage and closechannel; * * 2) the use of a message variable to represent an IEEE 802.3 * Ethernet frame; the variable allows one frame to be used * and re-used throughout the program but supports multiple * frame buffers if needed; * * 3) the use of a session variable to support multiple PEV-EVSE * interactions without using threads or subrocesses; this has * not demonstrated in this version of the program; some more * work is needed; * * 4) the absence of threads or subprocesses so that the program * can be ported to hosts without a multi-tasking operating * system; * * 5) lots of debugging messages; these can be suppressed or * deleted if not wanted; * * 6) simplified state machine; * *--------------------------------------------------------------------*/ /*====================================================================* * system header files; *--------------------------------------------------------------------*/ #include <unistd.h> #include <stdlib.h> #include <limits.h> #include <string.h> #include <errno.h> #include <time.h> /*====================================================================* * custom header files; *--------------------------------------------------------------------*/ #include "../tools/getoptv.h" #include "../tools/putoptv.h" #include "../tools/memory.h" #include "../tools/number.h" #include "../tools/types.h" #include "../tools/flags.h" #include "../tools/files.h" #include "../tools/error.h" #include "../tools/config.h" #include "../ether/channel.h" #include "../iso15118/slac.h" /*====================================================================* * custom source files; *--------------------------------------------------------------------*/ #ifndef MAKEFILE #include "../tools/getoptv.c" #include "../tools/putoptv.c" #include "../tools/version.c" #include "../tools/hexdump.c" #include "../tools/hexdecode.c" #include "../tools/hexencode.c" #include "../tools/hexstring.c" #include "../tools/decdecode.c" #include "../tools/decstring.c" #include "../tools/uintspec.c" #include "../tools/todigit.c" #include "../tools/strfbits.c" #include "../tools/config.c" #include "../tools/memincr.c" #include "../tools/error.c" #endif #ifndef MAKEFILE #include "../plc/Devices.c" #endif #ifndef MAKEFILE #include "../mme/EthernetHeader.c" #include "../mme/QualcommHeader.c" #include "../mme/HomePlugHeader1.c" #include "../mme/UnwantedMessage.c" #include "../mme/readmessage.c" #include "../mme/sendmessage.c" #endif #ifndef MAKEFILE #include "../ether/channel.c" #include "../ether/openchannel.c" #include "../ether/closechannel.c" #include "../ether/sendpacket.c" #include "../ether/readpacket.c" #endif #ifndef MAKEFILE #include "../iso15118/slac_session.c" #include "../iso15118/slac_connect.c" #include "../iso15118/slac_debug.c" #include "../iso15118/pev_cm_slac_param.c" #include "../iso15118/pev_cm_start_atten_char.c" #include "../iso15118/pev_cm_atten_char.c" #include "../iso15118/pev_cm_mnbc_sound.c" #include "../iso15118/pev_cm_slac_match.c" #include "../iso15118/pev_cm_set_key.c" #endif /*====================================================================* * program constants; *--------------------------------------------------------------------*/ #define PLCDEVICE "PLC" #define PROFILE "pev.ini" #define SECTION "default" #define PEV_STATE_DISCONNECTED 1 #define PEV_STATE_UNMATCHED 2 #define PEV_STATE_MATCHED 3 #define PEV_VID "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // VehicleIdentifier #define PEV_NMK "50D3E4933F855B7040784DF815AA8DB7" // HomePlugAV #define PEV_NID "B0F2E695666B03" // HomePlugAV /*====================================================================* * program variables; *--------------------------------------------------------------------*/ unsigned state = 0; /*====================================================================* * * static void configure (); * * print template PEV-HLE configuration file on stdout so that * profile, section and element names match; * *--------------------------------------------------------------------*/ static void configure () { printf ("# file: %s\n", PROFILE); printf ("# ====================================================================\n"); printf ("# PEV-HLE initialization;\n"); printf ("# --------------------------------------------------------------------\n"); printf ("[%s]\n", SECTION); printf ("vehicle identifier = %s\n", PEV_VID); printf ("network membership key = %s\n", PEV_NMK); printf ("network identifier = %s\n", PEV_NID); printf ("attenuation threshold = %d\n", SLAC_LIMIT); printf ("msound pause = %d\n", SLAC_PAUSE); printf ("charge time = %d\n", SLAC_CHARGETIME); printf ("settle time = %d\n", SLAC_SETTLETIME); return; } /*====================================================================* * * void initialize (struct session * session, char const * profile, char const * section); * * read PEV-HLE configuration profile; initialize session variable; * *--------------------------------------------------------------------*/ static void initialize (struct session * session, char const * profile, char const * section) { session->next = session->prev = session; hexencode (session->PEV_ID, sizeof (session->PEV_ID), configstring (profile, section, "VehicleIdentifier", PEV_VID)); hexencode (session->NMK, sizeof (session->NMK), configstring (profile, section, "NetworkMembershipKey", PEV_NMK)); hexencode (session->NID, sizeof (session->NID), configstring (profile, section, "NetworkIdentifier", PEV_NID)); session->limit = confignumber_range (profile, section, "AttenuationThreshold", SLAC_LIMIT, 0, UINT_MAX); session->pause = confignumber_range (profile, section, "MSoundPause", SLAC_PAUSE, 0, UINT_MAX); session->settletime = confignumber_range (profile, section, "SettleTime", SLAC_SETTLETIME, 0, UINT_MAX); session->chargetime = confignumber_range (profile, section, "ChargeTime", SLAC_CHARGETIME, 0, UINT_MAX); session->state = PEV_STATE_DISCONNECTED; memcpy (session->original_nmk, session->NMK, sizeof (session->original_nmk)); memcpy (session->original_nid, session->NID, sizeof (session->original_nid)); slac_session (session); return; } /*====================================================================* * * signed identifier (struct session * session, struct channel * channel); * * generate the run identifier and store in session variable; * * copy channel host address to session PEV MAC address; set session * PEV identifier to zeros; * *--------------------------------------------------------------------*/ static signed identifier (struct session * session, struct channel * channel) { time_t now; time (& now); memset (session->RunID, 0, sizeof (session->RunID)); memcpy (session->RunID, channel->host, ETHER_ADDR_LEN); memcpy (session->PEV_MAC, channel->host, sizeof (session->PEV_MAC)); return (0); } /*====================================================================* * * void DisconnectedState (struct session * session, struct channel * channel, struct message * message); * *--------------------------------------------------------------------*/ static void DisconnectedState (struct session * session, struct channel * channel, struct message * message) { slac_session (session); slac_debug (session, 0, __func__, "Probing ..."); memincr (session->RunID, sizeof (session->RunID)); while (pev_cm_slac_param (session, channel, message)); session->state = PEV_STATE_UNMATCHED; return; } /*====================================================================* * * void MatchingState (struct session * session, struct channel * channel, struct message * message); * * The PEV-EVSE perform GreenPPEA protocol in this state; * * the cm_start_atten_char and cm_mnbc_sound messages are sent * broadcast; the application may receive multiple cm_atten_char * messages before sending the cm_slac_match message; * *--------------------------------------------------------------------*/ static void UnmatchedState (struct session * session, struct channel * channel, struct message * message) { slac_session (session); slac_debug (session, 0, __func__, "Sounding ..."); if (pev_cm_start_atten_char (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } if (pev_cm_mnbc_sound (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } if (pev_cm_atten_char (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } if (slac_connect (session)) { session->state = PEV_STATE_DISCONNECTED; return; } slac_debug (session, 0, __func__, "Matching ..."); if (pev_cm_slac_match (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } session->state = PEV_STATE_MATCHED; return; } /*====================================================================* * * void MatchedState (struct session * session, struct channel * channel, struct message * message); * * charge vehicle; restore original NMK/NID and disconnect; loop * if SLAC_CONTINUE is set; * *--------------------------------------------------------------------*/ static void MatchedState (struct session * session, struct channel * channel, struct message * message) { slac_session (session); slac_debug (session, 0, __func__, "Connecting ..."); #if SLAC_AVLN_EVSE slac_debug (session, 0, __func__, "waiting for evse to settle ..."); sleep (session->settletime); #endif #if SLAC_AVLN_PEV if (pev_cm_set_key (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } sleep (session->settletime); #endif slac_debug (session, 0, __func__, "Charging (%d) ...\n\n", session->counter++); sleep (session->chargetime); slac_debug (session, 0, __func__, "Disconnecting ..."); #if SLAC_AVLN_EVSE slac_debug (session, 0, __func__, "waiting for evse to settle ..."); sleep (session->settletime); #endif #if SLAC_AVLN_PEV memcpy (session->NMK, session->original_nmk, sizeof (session->NMK)); memcpy (session->NID, session->original_nid, sizeof (session->NID)); if (pev_cm_set_key (session, channel, message)) { session->state = PEV_STATE_DISCONNECTED; return; } sleep (session->settletime); #endif session->state = state; return; } /*====================================================================* * * int main (int argc, char * argv[]); * * *--------------------------------------------------------------------*/ int main (int argc, char const * argv []) { extern struct channel channel; static char const * optv [] = { "cCdi:lp:qs:t:vx", "", "Plug-in Electric Vehicle Emulator", "c\tprint template configuration file on stdout", "C\tstop on count mismatch", "d\tdisplay debug information", #if defined (WINPCAP) || defined (LIBPCAP) "i n\thost interface is (n) [" LITERAL (CHANNEL_ETHNUMBER) "]", #else "i s\thost interface is (s) [" LITERAL (CHANNEL_ETHDEVICE) "]", #endif "l\tloop indefinitely", "p s\tconfiguration profile is (s) [" LITERAL (PROFILE) "]", "q\tsuppress normal output", "s s\tconfiguration section is (s) [" LITERAL (SECTION) "]", "t n\tread timeout is (n) milliseconds [" LITERAL (SLAC_TIMEOUT) "]", "v\tverbose messages on stdout", "x\texit on error", (char const *) (0) }; struct session session; struct message message; char const * profile = PROFILE; char const * section = SECTION; signed c; memset (& session, 0, sizeof (session)); memset (& message, 0, sizeof (message)); channel.timeout = SLAC_TIMEOUT; if (getenv (PLCDEVICE)) { #if defined (WINPCAP) || defined (LIBPCAP) channel.ifindex = atoi (getenv (PLCDEVICE)); #else channel.ifname = strdup (getenv (PLCDEVICE)); #endif } optind = 1; while (~ (c = getoptv (argc, argv, optv))) { switch (c) { case 'c': configure (); return (0); case 'C': _setbits (session.flags, SLAC_COMPARE); break; case 'd': _setbits (session.flags, (SLAC_VERBOSE | SLAC_SESSION)); break; case 'i': #if defined (WINPCAP) || defined (LIBPCAP) channel.ifindex = atoi (optarg); #else channel.ifname = optarg; #endif break; case 'l': state = PEV_STATE_DISCONNECTED; break; case 'p': profile = optarg; break; case 's': section = optarg; break; case 'q': _setbits (channel.flags, CHANNEL_SILENCE); _setbits (session.flags, SLAC_SILENCE); break; case 't': channel.timeout = (unsigned) (uintspec (optarg, 0, UINT_MAX)); break; case 'v': _setbits (channel.flags, CHANNEL_VERBOSE); break; case 'x': session.exit = session.exit? 0: 1; break; default: break; } } argc -= optind; argv += optind; if (argc) { slac_debug (& session, 1, __func__, ERROR_TOOMANY); } openchannel (& channel); identifier (& session, & channel); initialize (& session, profile, section); if (pev_cm_set_key (& session, & channel, & message)) { slac_debug (& session, 1, __func__, "Can't set key"); } sleep (session.settletime); while (session.state) { if (session.state == PEV_STATE_DISCONNECTED) { DisconnectedState (& session, & channel, & message); continue; } if (session.state == PEV_STATE_UNMATCHED) { UnmatchedState (& session, & channel, & message); continue; } if (session.state == PEV_STATE_MATCHED) { MatchedState (& session, & channel, & message); continue; } slac_debug (& session, 1, __func__, "Illegal state!"); } closechannel (& channel); return (0); }