int6kp.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*====================================================================*
  2. *
  3. * Copyright (c) 2013 Qualcomm Atheros, Inc.
  4. *
  5. * All rights reserved.
  6. *
  7. *====================================================================*/
  8. /*====================================================================*
  9. *
  10. * int6kp.c -
  11. *
  12. * Contributor(s):
  13. * Charles Maier <cmaier@qca.qualcomm.com>
  14. *
  15. *--------------------------------------------------------------------*/
  16. /*====================================================================*
  17. * system header files;
  18. *--------------------------------------------------------------------*/
  19. #include <unistd.h>
  20. #include <stdlib.h>
  21. #include <stdint.h>
  22. #include <limits.h>
  23. /*====================================================================*
  24. * custom header files;
  25. *--------------------------------------------------------------------*/
  26. #include "../tools/getoptv.h"
  27. #include "../tools/putoptv.h"
  28. #include "../tools/memory.h"
  29. #include "../tools/number.h"
  30. #include "../tools/symbol.h"
  31. #include "../tools/types.h"
  32. #include "../tools/flags.h"
  33. #include "../tools/files.h"
  34. #include "../tools/error.h"
  35. #include "../key/HPAVKey.h"
  36. #include "../key/keys.h"
  37. #include "../ram/sdram.h"
  38. #include "../pib/pib.h"
  39. #include "../nvm/nvm.h"
  40. #include "../plc/plc.h"
  41. #include "../nda/nda.h"
  42. /*====================================================================*
  43. * custom source files;
  44. *--------------------------------------------------------------------*/
  45. #ifndef MAKEFILE
  46. #include "../plc/Attributes1.c"
  47. #include "../plc/chipset.c"
  48. #include "../plc/Confirm.c"
  49. #include "../plc/Devices.c"
  50. #include "../plc/Display.c"
  51. #include "../plc/FactoryDefaults.c"
  52. #include "../plc/Failure.c"
  53. #include "../plc/Identity1.c"
  54. #include "../plc/HostActionResponse.c"
  55. #include "../plc/NetInfo1.c"
  56. #include "../plc/NVRAMInfo.c"
  57. #include "../plc/PushButton.c"
  58. #include "../plc/ReadFirmware1.c"
  59. #include "../plc/ReadParameters1.c"
  60. #include "../plc/Request.c"
  61. #include "../plc/ResetDevice.c"
  62. #include "../plc/ReadMME.c"
  63. #include "../plc/ReadMFG.c"
  64. #include "../plc/SendMME.c"
  65. #include "../plc/SetNMK.c"
  66. #include "../plc/SDRAMInfo.c"
  67. #include "../plc/StationRole.c"
  68. #include "../plc/VersionInfo1.c"
  69. #include "../plc/WaitForReset.c"
  70. #include "../plc/WaitForRestart.c"
  71. #include "../plc/WaitForStart.c"
  72. #include "../plc/WriteCFG.c"
  73. #include "../plc/WriteNVM.c"
  74. #include "../plc/WritePIB.c"
  75. #include "../plc/WatchdogReport.c"
  76. #include "../plc/PLCSelect.c"
  77. #endif
  78. #ifndef MAKEFILE
  79. #include "../nda/FlashPTS.c"
  80. #include "../nda/EraseFlashMemory1.c"
  81. #include "../nda/ReadFlashFirmware.c"
  82. #include "../nda/ReadFlashParameters.c"
  83. #endif
  84. #ifndef MAKEFILE
  85. #include "../tools/getoptv.c"
  86. #include "../tools/putoptv.c"
  87. #include "../tools/version.c"
  88. #include "../tools/uintspec.c"
  89. #include "../tools/basespec.c"
  90. #include "../tools/hexdump.c"
  91. #include "../tools/hexencode.c"
  92. #include "../tools/hexdecode.c"
  93. #include "../tools/hexstring.c"
  94. #include "../tools/todigit.c"
  95. #include "../tools/checkfilename.c"
  96. #include "../tools/checksum32.c"
  97. #include "../tools/fdchecksum32.c"
  98. #include "../tools/error.c"
  99. #include "../tools/strfbits.c"
  100. #include "../tools/synonym.c"
  101. #include "../tools/typename.c"
  102. #endif
  103. #ifndef MAKEFILE
  104. #include "../ether/openchannel.c"
  105. #include "../ether/closechannel.c"
  106. #include "../ether/readpacket.c"
  107. #include "../ether/sendpacket.c"
  108. #include "../ether/channel.c"
  109. #endif
  110. #ifndef MAKEFILE
  111. #include "../ram/nvram.c"
  112. #include "../ram/sdramfile.c"
  113. #include "../ram/sdrampeek.c"
  114. #endif
  115. #ifndef MAKEFILE
  116. #include "../nvm/lightning_nvm_file.c"
  117. #endif
  118. #ifndef MAKEFILE
  119. #include "../pib/lightning_pib_file.c"
  120. #include "../pib/lightning_pib_peek.c"
  121. #endif
  122. #ifndef MAKEFILE
  123. #include "../mme/MMECode.c"
  124. #include "../mme/EthernetHeader.c"
  125. #include "../mme/QualcommHeader.c"
  126. #include "../mme/QualcommHeader1.c"
  127. #include "../mme/UnwantedMessage.c"
  128. #endif
  129. #ifndef MAKEFILE
  130. #include "../key/keys.c"
  131. #endif
  132. /*====================================================================*
  133. * program constants;
  134. *--------------------------------------------------------------------*/
  135. #define DEFAULT_LOOP 1
  136. #define DEFAULT_WAIT 0
  137. /*====================================================================*
  138. * program variables;
  139. *--------------------------------------------------------------------*/
  140. static struct _term_ const modules [] =
  141. {
  142. {
  143. "nvm",
  144. "1"
  145. },
  146. {
  147. "pib",
  148. "2"
  149. },
  150. {
  151. "both",
  152. "3"
  153. }
  154. };
  155. static struct _term_ const buttons [] =
  156. {
  157. {
  158. "join",
  159. "1"
  160. },
  161. {
  162. "leave",
  163. "2"
  164. },
  165. {
  166. "reset",
  167. "4"
  168. },
  169. {
  170. "status",
  171. "3"
  172. }
  173. };
  174. /*====================================================================*
  175. *
  176. * void manager (struct plc *plc, signed loop, signed wait);
  177. *
  178. * perform operations in logical order despite any order specfied
  179. * on the command line; for example, read PIB before writing PIB;
  180. *
  181. * operation order is controlled by the order of "if" statements
  182. * shown here; the entire operation sequence can be repeated with
  183. * an optional pause between each iteration;
  184. *
  185. *--------------------------------------------------------------------*/
  186. static void manager (struct plc * plc, signed loop, signed wait)
  187. {
  188. while (loop-- > 0)
  189. {
  190. if (_anyset (plc->flags, PLC_VERSION))
  191. {
  192. VersionInfo1 (plc);
  193. }
  194. if (_anyset (plc->flags, PLC_ATTRIBUTES))
  195. {
  196. Attributes1 (plc);
  197. }
  198. if (_anyset (plc->flags, PLC_WATCHDOG_REPORT))
  199. {
  200. WatchdogReport (plc);
  201. }
  202. if (_anyset (plc->flags, PLC_NVRAM_INFO))
  203. {
  204. NVRAMInfo (plc);
  205. }
  206. if (_anyset (plc->flags, PLC_SDRAM_CONFIG))
  207. {
  208. WriteCFG (plc);
  209. }
  210. if (_anyset (plc->flags, PLC_SDRAM_INFO))
  211. {
  212. SDRAMInfo (plc);
  213. }
  214. if (_anyset (plc->flags, PLC_READ_IDENTITY))
  215. {
  216. Identity1 (plc);
  217. }
  218. if (_anyset (plc->flags, PLC_NETWORK))
  219. {
  220. NetInfo1 (plc);
  221. }
  222. if (_anyset (plc->flags, PLC_READ_MAC))
  223. {
  224. ReadFlashFirmware (plc);
  225. }
  226. if (_anyset (plc->flags, PLC_WRITE_MAC))
  227. {
  228. WriteNVM (plc);
  229. }
  230. if (_anyset (plc->flags, PLC_READ_PIB))
  231. {
  232. ReadFlashParameters (plc);
  233. }
  234. if (_anyset (plc->flags, PLC_WRITE_PIB))
  235. {
  236. WritePIB (plc);
  237. }
  238. if (_anyset (plc->flags, PLC_HOST_ACTION))
  239. {
  240. HostActionResponse (plc);
  241. }
  242. if (_anyset (plc->flags, PLC_PUSH_BUTTON))
  243. {
  244. PushButton (plc);
  245. }
  246. if (_anyset (plc->flags, (PLC_SETLOCALKEY | PLC_SETREMOTEKEY)))
  247. {
  248. SetNMK (plc);
  249. }
  250. if (_anyset (plc->flags, PLC_FACTORY_DEFAULTS))
  251. {
  252. FactoryDefaults (plc);
  253. }
  254. if (_anyset (plc->flags, PLC_ERASE_DEVICE))
  255. {
  256. EraseFlashMemory1 (plc);
  257. }
  258. if (_anyset (plc->flags, PLC_FLASH_DEVICE))
  259. {
  260. FlashPTS (plc);
  261. WaitForRestart (plc);
  262. }
  263. if (_anyset (plc->flags, PLC_RESET_DEVICE))
  264. {
  265. ResetDevice (plc);
  266. }
  267. sleep (wait);
  268. }
  269. return;
  270. }
  271. /*====================================================================*
  272. *
  273. * int main (int argc, char const * argv[]);
  274. *
  275. * parse command line, populate plc structure and perform selected
  276. * operations; show help summary if asked; see getoptv and putoptv
  277. * to understand command line parsing and help summary display; see
  278. * plc.h for the definition of struct plc;
  279. *
  280. * the command line accepts multiple MAC addresses and the program
  281. * performs the specified operations on each address, in turn; the
  282. * address order is significant but the option order is not; the
  283. * default address is a local broadcast that causes all devices on
  284. * the local H1 interface to respond but not those at the remote
  285. * end of the powerline;
  286. *
  287. * the default address is 00:B0:52:00:00:01; omitting the address
  288. * will automatically address the local device; some options will
  289. * cancel themselves if this makes no sense;
  290. *
  291. * the default interface is eth1 because most people use eth0 as
  292. * their principle network connection; you can specify another
  293. * interface with -i or define environment string PLC to make
  294. * that the default interface and save typing;
  295. *
  296. *--------------------------------------------------------------------*/
  297. int main (int argc, char const * argv [])
  298. {
  299. extern struct channel channel;
  300. extern struct _term_ const devices [];
  301. extern struct _term_ const daks [];
  302. extern struct _term_ const nmks [];
  303. static char const * optv [] =
  304. {
  305. "AaB:C:d:D:eEfFi:IJ:K:l:mMn:N:qQp:P:rRS:st:Tvw:x12",
  306. "device [device] [...]",
  307. "Qualcomm Atheros INT6x00 Powerline Device Manager",
  308. "a\tread device attributes using VS_OP_ATTRIBUTES",
  309. "B n\tperform pushbutton action (n) using MS_PB_ENC [1|2|3|4|'join'|'leave'|'status'|'reset']",
  310. "C n\tflash NVRAM with module (n) using VS_MOD_NVM [1|2|3|'nvm'|'pib'|'both']",
  311. "d f\tdump and clear watchdog report to file (f) using VS_WD_RPT",
  312. "D x\tset DAK to (x) for option -E, -F and -J [" DAK1 "]",
  313. "e\tredirect stderr messages to stdout",
  314. "E\terase NVRAM then reset using VS_PTS_NVM",
  315. "f\tread NVRAM Configuration using VS_GET_NVM",
  316. "F[F]\tflash [force] NVRAM with firmware and PIB using VS_PTS_NVM",
  317. #if defined (WINPCAP) || defined (LIBPCAP)
  318. "i n\thost interface is (n) [" LITERAL (CHANNEL_ETHNUMBER) "]",
  319. #else
  320. "i s\thost interface is (s) [" LITERAL (CHANNEL_ETHDEVICE) "]",
  321. #endif
  322. "I\tread device identity using VS_RD_MOD",
  323. "J\tchange Network Membership Key (NMK) on remote device using VS_SET_KEY (see -D and -K)",
  324. "K x\tset Network Membership Key (NMK) to (x) for options -J or -M [" NMK1 "]",
  325. "l n\tloop (n) times [" LITERAL (DEFAULT_LOOP) "]",
  326. "m\tread network membership information using VS_NW_INFO",
  327. "M\tchange Network Membership Key (NMK) on local device using VS_SET_KEY (see -K)",
  328. "n f\tread NVM from SDRAM to file (f) using VS_RD_MOD",
  329. "N f\twrite NVM file (f) to SDRAM using VS_WR_MOD",
  330. "p f\tread PIB from SDRAM to file (f) using VS_RD_MOD",
  331. "P f\twrite PIB file (f) to SDRAM using VS_WR_MOD",
  332. "q\tquiet mode",
  333. "Q\tquick flash",
  334. "r\tread hardware and firmware revision using VS_SW_VER",
  335. "R\treset device using VS_RS_DEV",
  336. "s\tread SDRAM Configuration using VS_RD_CBLOCK",
  337. "S f\tWrite an SDRAM Configuration file using VS_SET_SDRAM",
  338. "t n\tread timeout is (n) milliseconds [" LITERAL (CHANNEL_TIMEOUT) "]",
  339. "T\trestore factory defaults using VS_FAC_DEFAULTS",
  340. "v\tverbose mode",
  341. "w n\tpause (n) seconds [" LITERAL (DEFAULT_WAIT) "]",
  342. "x\texit on error",
  343. "1\tsuppress confirm MME when resetting device [option -R only]",
  344. "2\tboot from host after reset [option -R only]",
  345. (char const *) (0)
  346. };
  347. #include "../plc/plc.c"
  348. signed loop = DEFAULT_LOOP;
  349. signed wait = DEFAULT_WAIT;
  350. signed c;
  351. if (getenv (PLCDEVICE))
  352. {
  353. #if defined (WINPCAP) || defined (LIBPCAP)
  354. channel.ifindex = atoi (getenv (PLCDEVICE));
  355. #else
  356. channel.ifname = strdup (getenv (PLCDEVICE));
  357. #endif
  358. }
  359. optind = 1;
  360. while (~ (c = getoptv (argc, argv, optv)))
  361. {
  362. switch (c)
  363. {
  364. case 'a':
  365. _setbits (plc.flags, PLC_ATTRIBUTES);
  366. break;
  367. case 'B':
  368. _setbits (plc.flags, PLC_PUSH_BUTTON);
  369. plc.pushbutton = (unsigned) (uintspec (synonym (optarg, buttons, SIZEOF (buttons)), 0, UCHAR_MAX));
  370. break;
  371. case 'C':
  372. _setbits (plc.flags, PLC_FLASH_DEVICE);
  373. plc.module = (unsigned) (uintspec (synonym (optarg, modules, SIZEOF (modules)), 0, UCHAR_MAX));
  374. break;
  375. case 'd':
  376. _setbits (plc.flags, PLC_WATCHDOG_REPORT);
  377. if (! checkfilename (optarg))
  378. {
  379. error (1, EINVAL, "%s", optarg);
  380. }
  381. if ((plc.rpt.file = open (plc.rpt.name = optarg, O_BINARY | O_CREAT | O_RDWR | O_TRUNC, FILE_FILEMODE)) == -1)
  382. {
  383. error (1, errno, "%s", optarg);
  384. }
  385. #ifndef WIN32
  386. chown (optarg, getuid (), getgid ());
  387. #endif
  388. plc.readaction = 3;
  389. break;
  390. case 'D':
  391. if (! hexencode (plc.DAK, sizeof (plc.DAK), synonym (optarg, daks, SIZEOF (daks))))
  392. {
  393. error (1, errno, PLC_BAD_DAK, optarg);
  394. }
  395. break;
  396. case 'e':
  397. dup2 (STDOUT_FILENO, STDERR_FILENO);
  398. break;
  399. case 'E':
  400. _clrbits (plc.flags, PLC_FLASH_DEVICE);
  401. _setbits (plc.flags, PLC_ERASE_DEVICE);
  402. plc.module = 0x0C;
  403. break;
  404. case 'f':
  405. _setbits (plc.flags, PLC_NVRAM_INFO);
  406. break;
  407. case 'F':
  408. _setbits (plc.module, (VS_MODULE_MAC | VS_MODULE_PIB));
  409. if (_anyset (plc.flags, PLC_FLASH_DEVICE))
  410. {
  411. _setbits (plc.module, VS_MODULE_FORCE);
  412. }
  413. _setbits (plc.flags, PLC_FLASH_DEVICE);
  414. break;
  415. case 'H':
  416. _setbits (plc.flags, PLC_HOST_ACTION);
  417. break;
  418. case 'I':
  419. _setbits (plc.flags, PLC_READ_IDENTITY);
  420. break;
  421. case 'i':
  422. #if defined (WINPCAP) || defined (LIBPCAP)
  423. channel.ifindex = atoi (optarg);
  424. #else
  425. channel.ifname = optarg;
  426. #endif
  427. break;
  428. case 'J':
  429. if (! hexencode (plc.RDA, sizeof (plc.RDA), (char const *) (optarg)))
  430. {
  431. error (1, errno, PLC_BAD_MAC, optarg);
  432. }
  433. _setbits (plc.flags, PLC_SETREMOTEKEY);
  434. break;
  435. case 'K':
  436. if (! hexencode (plc.NMK, sizeof (plc.NMK), synonym (optarg, nmks, SIZEOF (nmks))))
  437. {
  438. error (1, errno, PLC_BAD_NMK, optarg);
  439. }
  440. break;
  441. case 'M':
  442. _setbits (plc.flags, PLC_SETLOCALKEY);
  443. break;
  444. case 'l':
  445. loop = (unsigned) (uintspec (optarg, 0, UINT_MAX));
  446. break;
  447. case 'm':
  448. _setbits (plc.flags, PLC_NETWORK);
  449. break;
  450. case 'N':
  451. if (! checkfilename (optarg))
  452. {
  453. error (1, EINVAL, "%s", optarg);
  454. }
  455. if ((plc.NVM.file = open (plc.NVM.name = optarg, O_BINARY | O_RDONLY)) == -1)
  456. {
  457. error (1, errno, "%s", optarg);
  458. }
  459. if (lightning_nvm_file (& plc.NVM))
  460. {
  461. error (1, errno, "Bad thunderbolt image file: %s", plc.NVM.name);
  462. }
  463. _setbits (plc.flags, PLC_WRITE_MAC);
  464. break;
  465. case 'n':
  466. if (! checkfilename (optarg))
  467. {
  468. error (1, EINVAL, "%s", optarg);
  469. }
  470. if ((plc.nvm.file = open (plc.nvm.name = optarg, O_BINARY | O_CREAT | O_RDWR | O_TRUNC, FILE_FILEMODE)) == -1)
  471. {
  472. error (1, errno, "%s", optarg);
  473. }
  474. #ifndef WIN32
  475. chown (optarg, getuid (), getgid ());
  476. #endif
  477. _setbits (plc.flags, PLC_READ_MAC);
  478. break;
  479. case 'P':
  480. if (! checkfilename (optarg))
  481. {
  482. error (1, EINVAL, "%s", optarg);
  483. }
  484. if ((plc.PIB.file = open (plc.PIB.name = optarg, O_BINARY | O_RDONLY)) == -1)
  485. {
  486. error (1, errno, "%s", optarg);
  487. }
  488. if (lightning_pib_file (& plc.PIB))
  489. {
  490. error (1, errno, "Bad thunderbolt parameter file: %s", plc.PIB.name);
  491. }
  492. _setbits (plc.flags, PLC_WRITE_PIB);
  493. break;
  494. case 'p':
  495. if (! checkfilename (optarg))
  496. {
  497. error (1, EINVAL, "%s", optarg);
  498. }
  499. if ((plc.pib.file = open (plc.pib.name = optarg, O_BINARY | O_CREAT | O_RDWR | O_TRUNC, FILE_FILEMODE)) == -1)
  500. {
  501. error (1, errno, "%s", optarg);
  502. }
  503. #ifndef WIN32
  504. chown (optarg, getuid (), getgid ());
  505. #endif
  506. _setbits (plc.flags, PLC_READ_PIB);
  507. break;
  508. case 'Q':
  509. _setbits (plc.flags, PLC_QUICK_FLASH);
  510. break;
  511. case 'q':
  512. _setbits (channel.flags, CHANNEL_SILENCE);
  513. _setbits (plc.flags, PLC_SILENCE);
  514. break;
  515. case 'R':
  516. _setbits (plc.flags, PLC_RESET_DEVICE);
  517. break;
  518. case 'r':
  519. _setbits (plc.flags, PLC_VERSION);
  520. break;
  521. case 'S':
  522. if (! checkfilename (optarg))
  523. {
  524. error (1, EINVAL, "%s", optarg);
  525. }
  526. if ((plc.CFG.file = open (plc.CFG.name = optarg, O_BINARY | O_RDONLY)) == -1)
  527. {
  528. error (1, errno, "%s", optarg);
  529. }
  530. if (sdramfile (plc.CFG.file, optarg, plc.flags))
  531. {
  532. error (1, ECANCELED, "SDRAM config file %s is corrupt", optarg);
  533. }
  534. _setbits (plc.flags, PLC_SDRAM_CONFIG);
  535. break;
  536. case 's':
  537. _setbits (plc.flags, PLC_SDRAM_INFO);
  538. break;
  539. case 't':
  540. channel.timeout = (signed) (uintspec (optarg, 0, UINT_MAX));
  541. break;
  542. case 'T':
  543. _setbits (plc.flags, PLC_FACTORY_DEFAULTS);
  544. break;
  545. case 'v':
  546. _setbits (channel.flags, CHANNEL_VERBOSE);
  547. _setbits (plc.flags, PLC_VERBOSE);
  548. break;
  549. case 'w':
  550. wait = (unsigned) (uintspec (optarg, 0, 3600));
  551. break;
  552. case 'x':
  553. _setbits (plc.flags, PLC_BAILOUT);
  554. break;
  555. case '1':
  556. _setbits (plc.flag2, PLC_FLAG2_CFM_ACT);
  557. break;
  558. case '2':
  559. _setbits (plc.flag2, PLC_FLAG2_BT_SRC);
  560. break;
  561. default:
  562. break;
  563. }
  564. }
  565. argc -= optind;
  566. argv += optind;
  567. if (argc != 1)
  568. {
  569. if (plc.nvm.file != -1)
  570. {
  571. error (1, ECANCELED, PLC_NODEVICE);
  572. }
  573. if (plc.pib.file != -1)
  574. {
  575. error (1, ECANCELED, PLC_NODEVICE);
  576. }
  577. if (plc.rpt.file != -1)
  578. {
  579. error (1, ECANCELED, PLC_NODEVICE);
  580. }
  581. }
  582. openchannel (& channel);
  583. if (! (plc.message = malloc (sizeof (* plc.message))))
  584. {
  585. error (1, errno, PLC_NOMEMORY);
  586. }
  587. if (! argc)
  588. {
  589. manager (& plc, loop, wait);
  590. }
  591. while ((argc) && (* argv))
  592. {
  593. if (! hexencode (channel.peer, sizeof (channel.peer), synonym (* argv, devices, SIZEOF (devices))))
  594. {
  595. error (1, errno, PLC_BAD_MAC, * argv);
  596. }
  597. manager (& plc, loop, wait);
  598. argc--;
  599. argv++;
  600. }
  601. free (plc.message);
  602. closechannel (& channel);
  603. exit (0);
  604. }