bsp.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /*****************************************************************************
  2. * Product: DPP example, ThreadX kernel, Win32 emulation, console app.
  3. * Last updated for: @ref qpc_7_0_0
  4. * Last updated on 2021-12-03
  5. *
  6. * Q u a n t u m L e a P s
  7. * ------------------------
  8. * Modern Embedded Software
  9. *
  10. * Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
  11. *
  12. * This program is open source software: you can redistribute it and/or
  13. * modify it under the terms of the GNU General Public License as published
  14. * by the Free Software Foundation, either version 3 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * Alternatively, this program may be distributed and modified under the
  18. * terms of Quantum Leaps commercial licenses, which expressly supersede
  19. * the GNU General Public License and are specifically designed for
  20. * licensees interested in retaining the proprietary status of their code.
  21. *
  22. * This program is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU General Public License for more details.
  26. *
  27. * You should have received a copy of the GNU General Public License
  28. * along with this program. If not, see <www.gnu.org/licenses/>.
  29. *
  30. * Contact information:
  31. * <www.state-machine.com/licensing>
  32. * <info@state-machine.com>
  33. *****************************************************************************/
  34. /* Win32 configuration macros needed for proper WinSock includes */
  35. #define WIN32_LEAN_AND_MEAN
  36. #define _WIN32_WINNT 0x0501
  37. #include "qpc.h"
  38. #include "dpp.h"
  39. #include "bsp.h"
  40. #include <stdio.h> /* for printf_s() */
  41. #include <stdlib.h> /* for exit() */
  42. #include <conio.h> /* for keyboard input */
  43. Q_DEFINE_THIS_FILE
  44. /* Local-scope objects -----------------------------------------------------*/
  45. static uint32_t l_rnd; /* random seed */
  46. static TX_TIMER l_tick_timer; /* ThreadX timer to call QTIMEEVT_TICK_X() */
  47. #ifdef Q_SPY
  48. enum AppRecords { /* application-specific trace records */
  49. PHILO_STAT = QS_USER,
  50. COMMAND_STAT
  51. };
  52. /* QSpy source IDs */
  53. static QSpyId const l_clock_tick = { QS_AP_ID };
  54. static void QS_rx_input(void);
  55. static void QS_output(void);
  56. #endif
  57. /*..........................................................................*/
  58. void BSP_init(void) {
  59. printf_s("Dining Philosophers Problem example\n"
  60. "QP port to ThreadX\n"
  61. "ThreadX port to Win32\n"
  62. "QP %s\n"
  63. "Press 'p' to pause\n"
  64. "Press 's' to serve\n"
  65. "Press 'x' to quit...\n",
  66. QP_VERSION_STR);
  67. /* seed the random number generator */
  68. BSP_randomSeed(1234U);
  69. if (QS_INIT((void *)0) == 0U) { /* initialize the QS software tracing */
  70. Q_ERROR();
  71. }
  72. QS_USR_DICTIONARY(PHILO_STAT);
  73. QS_USR_DICTIONARY(COMMAND_STAT);
  74. /* setup the QS filters... */
  75. QS_GLB_FILTER(QS_ALL_RECORDS);
  76. QS_GLB_FILTER(-QS_QF_TICK);
  77. }
  78. /*..........................................................................*/
  79. void BSP_displayPhilStat(uint8_t n, char const *stat) {
  80. printf("Philo[%d] is %s\n", (int)n, stat);
  81. QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->prio) /* app-specific record */
  82. QS_U8(1, n); /* Philosopher number */
  83. QS_STR(stat); /* Philosopher status */
  84. QS_END()
  85. }
  86. /*..........................................................................*/
  87. void BSP_displayPaused(uint8_t paused) {
  88. printf("%s\n", paused ? "PAUSED" : "SERVING");
  89. }
  90. /*..........................................................................*/
  91. uint32_t BSP_random(void) { /* a very cheap pseudo-random-number generator */
  92. uint32_t rnd;
  93. /* Some flating point code is to exercise the VFP... */
  94. float volatile x = 3.1415926F;
  95. x = x + 2.7182818F;
  96. /* "Super-Duper" Linear Congruential Generator (LCG)
  97. * LCG(2^32, 3*7*11*13*23, 0, seed)
  98. */
  99. rnd = l_rnd * (3U*7U*11U*13U*23U);
  100. l_rnd = rnd; /* set for the next time */
  101. return (rnd >> 8);
  102. }
  103. /*..........................................................................*/
  104. void BSP_randomSeed(uint32_t seed) {
  105. l_rnd = seed;
  106. }
  107. /*..........................................................................*/
  108. void BSP_terminate(int16_t result) {
  109. (void)result;
  110. }
  111. /* QF callbacks ============================================================*/
  112. static VOID timer_expiration(ULONG id) {
  113. (void)id; /* unused parameter */
  114. QTIMEEVT_TICK_X(0U, &l_clock_tick); /* perform the QF clock tick processing */
  115. /* handle keyborad input... */
  116. if (_kbhit()) {
  117. int ch = _getwch();
  118. switch (ch) {
  119. case 'p': {
  120. QACTIVE_PUBLISH(Q_NEW(QEvt, PAUSE_SIG), &l_clock_tick);
  121. break;
  122. }
  123. case 's': {
  124. QACTIVE_PUBLISH(Q_NEW(QEvt, SERVE_SIG), &l_clock_tick);
  125. break;
  126. }
  127. case 'x': {
  128. QF_stop();
  129. break;
  130. }
  131. default: {
  132. break;
  133. }
  134. }
  135. }
  136. /* also in case of Win32, perform here QS RX/TX... */
  137. QS_RX_INPUT(); /* handle the QS-RX input */
  138. QS_OUTPUT(); /* handle the QS-TX output */
  139. }
  140. /*..........................................................................*/
  141. void QF_onStartup(void) {
  142. /*
  143. * NOTE:
  144. * This application uses the ThreadX timer to periodically call
  145. * the QTimeEvt_tick_(0) function. Here, only the clock tick rate of 0
  146. * is used, but other timers can be used to call QTimeEvt_tick_() for
  147. * other clock tick rates, if needed.
  148. *
  149. * The choice of a ThreadX timer is not the only option. Applications
  150. * might choose to call QTIMEEVT_TICK_X() directly from timer interrupts
  151. * or from active object(s).
  152. */
  153. Q_ALLEGE(tx_timer_create(&l_tick_timer, /* ThreadX timer object */
  154. (CHAR *)"QP-tick", /* name of the timer */
  155. &timer_expiration, /* expiration function */
  156. 0U, /* expiration function input (tick rate) */
  157. 1U, /* initial ticks */
  158. 1U, /* reschedule ticks */
  159. TX_AUTO_ACTIVATE) /* automatically activate timer */
  160. == TX_SUCCESS);
  161. }
  162. /*..........................................................................*/
  163. void QF_onCleanup(void) {
  164. #ifdef Q_SPY
  165. QS_onCleanup();
  166. #endif
  167. exit(0);
  168. }
  169. /*..........................................................................*/
  170. Q_NORETURN Q_onAssert(char const * const module, int_t const loc) {
  171. printf("Assertion failed in %s:%d\n", module, loc);
  172. QS_ASSERTION(module, loc, 10000U); /* report assertion to QS */
  173. }
  174. /* QS callbacks ============================================================*/
  175. #ifdef Q_SPY
  176. #include "qs_pkg.h" /* QS package-scope interface for QS_rxPriv_ */
  177. #include <ws2tcpip.h> /* for TCP/IP transport used by QS */
  178. #define QS_TX_SIZE (8*1024)
  179. #define QS_RX_SIZE (2*1024)
  180. #define QS_TX_CHUNK QS_TX_SIZE
  181. #define QS_TIMEOUT_MS 10
  182. /* local variables .........................................................*/
  183. static SOCKET l_sock = INVALID_SOCKET;
  184. /*..........................................................................*/
  185. uint8_t QS_onStartup(void const* arg) {
  186. static uint8_t qsBuf[QS_TX_SIZE]; /* buffer for QS-TX channel */
  187. static uint8_t qsRxBuf[QS_RX_SIZE]; /* buffer for QS-RX channel */
  188. char hostName[128];
  189. char const* serviceName = "6601"; /* default QSPY server port */
  190. char const* src;
  191. char* dst;
  192. int status;
  193. struct addrinfo* result = NULL;
  194. struct addrinfo* rp = NULL;
  195. struct addrinfo hints;
  196. BOOL sockopt_bool;
  197. ULONG ioctl_opt;
  198. WSADATA wsaData;
  199. /* initialize the QS transmit and receive buffers */
  200. QS_initBuf(qsBuf, sizeof(qsBuf));
  201. QS_rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
  202. /* initialize Windows sockets version 2.2 */
  203. if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
  204. fprintf_s(stderr, "<TARGET> ERROR %s\n",
  205. "Windows Sockets cannot be initialized");
  206. goto error;
  207. }
  208. /* extract hostName from 'arg' (hostName:port_remote)... */
  209. src = (arg != (void*)0)
  210. ? (char const*)arg
  211. : "localhost"; /* default QSPY host */
  212. dst = hostName;
  213. while ((*src != '\0')
  214. && (*src != ':')
  215. && (dst < &hostName[sizeof(hostName) - 1]))
  216. {
  217. *dst++ = *src++;
  218. }
  219. *dst = '\0'; /* zero-terminate hostName */
  220. /* extract serviceName from 'arg' (hostName:serviceName)... */
  221. if (*src == ':') {
  222. serviceName = src + 1;
  223. }
  224. memset(&hints, 0, sizeof(hints));
  225. hints.ai_family = AF_INET;
  226. hints.ai_socktype = SOCK_STREAM;
  227. hints.ai_protocol = IPPROTO_TCP;
  228. status = getaddrinfo(hostName, serviceName, &hints, &result);
  229. if (status != 0) {
  230. fprintf_s(stderr,
  231. "<TARGET> ERROR cannot resolve host Name=%s:%s,Err=%d\n",
  232. hostName, serviceName, status);
  233. goto error;
  234. }
  235. for (rp = result; rp != NULL; rp = rp->ai_next) {
  236. l_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
  237. if (l_sock != INVALID_SOCKET) {
  238. if (connect(l_sock, rp->ai_addr, (int)rp->ai_addrlen)
  239. == SOCKET_ERROR)
  240. {
  241. closesocket(l_sock);
  242. l_sock = INVALID_SOCKET;
  243. }
  244. break;
  245. }
  246. }
  247. freeaddrinfo(result);
  248. /* socket could not be opened & connected? */
  249. if (l_sock == INVALID_SOCKET) {
  250. fprintf_s(stderr, "<TARGET> ERROR cannot connect to QSPY at "
  251. "host=%s:%s\n",
  252. hostName, serviceName);
  253. goto error;
  254. }
  255. /* set the socket to non-blocking mode */
  256. ioctl_opt = 1;
  257. if (ioctlsocket(l_sock, FIONBIO, &ioctl_opt) != NO_ERROR) {
  258. fprintf_s(stderr, "<TARGET> ERROR %s WASErr=%d\n,",
  259. "Failed to set non-blocking socket", WSAGetLastError());
  260. goto error;
  261. }
  262. /* configure the socket to reuse the address and not to linger */
  263. sockopt_bool = TRUE;
  264. setsockopt(l_sock, SOL_SOCKET, SO_REUSEADDR,
  265. (const char*)&sockopt_bool, sizeof(sockopt_bool));
  266. sockopt_bool = TRUE;
  267. setsockopt(l_sock, SOL_SOCKET, SO_DONTLINGER,
  268. (const char*)&sockopt_bool, sizeof(sockopt_bool));
  269. QS_onFlush();
  270. return 1U; /* success */
  271. error:
  272. return 0U; /* failure */
  273. }
  274. /*..........................................................................*/
  275. void QS_onCleanup(void) {
  276. if (l_sock != INVALID_SOCKET) {
  277. closesocket(l_sock);
  278. l_sock = INVALID_SOCKET;
  279. }
  280. WSACleanup();
  281. printf_s("<TARGET> Disconnected from QSPY\n");
  282. }
  283. /*..........................................................................*/
  284. void QS_onReset(void) {
  285. QS_onCleanup();
  286. exit(0);
  287. }
  288. /*..........................................................................*/
  289. void QS_onFlush(void) {
  290. uint16_t nBytes;
  291. uint8_t const* data;
  292. QS_CRIT_STAT_
  293. if (l_sock == INVALID_SOCKET) { /* socket NOT initialized? */
  294. fprintf_s(stderr, "<TARGET> ERROR %s\n",
  295. "invalid TCP socket");
  296. return;
  297. }
  298. nBytes = QS_TX_CHUNK;
  299. QS_CRIT_E_();
  300. while ((data = QS_getBlock(&nBytes)) != (uint8_t*)0) {
  301. QS_CRIT_X_();
  302. for (;;) { /* for-ever until break or return */
  303. int nSent = send(l_sock, (char const*)data, (int)nBytes, 0);
  304. if (nSent == SOCKET_ERROR) { /* sending failed? */
  305. int err = WSAGetLastError();
  306. if (err == WSAEWOULDBLOCK) {
  307. /* sleep for the timeout and then loop back
  308. * to send() the SAME data again
  309. */
  310. Sleep(QS_TIMEOUT_MS);
  311. }
  312. else { /* some other socket error... */
  313. fprintf_s(stderr, "<TARGET> ERROR %s WASErr=%d\n",
  314. "sending data over TCP", err);
  315. return;
  316. }
  317. }
  318. else if (nSent < (int)nBytes) { /* sent fewer than requested? */
  319. Sleep(QS_TIMEOUT_MS); /* sleep for the timeout */
  320. /* adjust the data and loop back to send() the rest */
  321. data += nSent;
  322. nBytes -= (uint16_t)nSent;
  323. }
  324. else {
  325. break; /* break out of the for-ever loop */
  326. }
  327. }
  328. /* set nBytes for the next call to QS_getBlock() */
  329. nBytes = QS_TX_CHUNK;
  330. QS_CRIT_E_();
  331. }
  332. QS_CRIT_X_();
  333. }
  334. /*..........................................................................*/
  335. QSTimeCtr QS_onGetTime(void) {
  336. LARGE_INTEGER time;
  337. QueryPerformanceCounter(&time);
  338. return (QSTimeCtr)time.QuadPart;
  339. }
  340. /*..........................................................................*/
  341. /*! callback function to execute a user command (to be implemented in BSP) */
  342. void QS_onCommand(uint8_t cmdId,
  343. uint32_t param1, uint32_t param2, uint32_t param3)
  344. {
  345. (void)cmdId;
  346. (void)param1;
  347. (void)param2;
  348. (void)param3;
  349. QS_BEGIN_ID(COMMAND_STAT, 0U) /* app-specific record */
  350. QS_U8(2, cmdId);
  351. QS_U32(8, param1);
  352. QS_U32(8, param2);
  353. QS_U32(8, param3);
  354. QS_END()
  355. }
  356. /*..........................................................................*/
  357. static void QS_output(void) {
  358. uint16_t nBytes;
  359. uint8_t const* data;
  360. QS_CRIT_STAT_
  361. if (l_sock == INVALID_SOCKET) { /* socket NOT initialized? */
  362. fprintf_s(stderr, "<TARGET> ERROR %s\n",
  363. "invalid TCP socket");
  364. return;
  365. }
  366. nBytes = QS_TX_CHUNK;
  367. QS_CRIT_E_();
  368. if ((data = QS_getBlock(&nBytes)) != (uint8_t*)0) {
  369. QS_CRIT_X_();
  370. for (;;) { /* for-ever until break or return */
  371. int nSent = send(l_sock, (char const*)data, (int)nBytes, 0);
  372. if (nSent == SOCKET_ERROR) { /* sending failed? */
  373. int err = WSAGetLastError();
  374. if (err == WSAEWOULDBLOCK) {
  375. /* sleep for the timeout and then loop back
  376. * to send() the SAME data again
  377. */
  378. Sleep(QS_TIMEOUT_MS);
  379. }
  380. else { /* some other socket error... */
  381. fprintf_s(stderr, "<TARGET> ERROR sending data over TCP,"
  382. "WASErr=%d\n", err);
  383. return;
  384. }
  385. }
  386. else if (nSent < (int)nBytes) { /* sent fewer than requested? */
  387. Sleep(QS_TIMEOUT_MS); /* sleep for the timeout */
  388. /* adjust the data and loop back to send() the rest */
  389. data += nSent;
  390. nBytes -= (uint16_t)nSent;
  391. }
  392. else {
  393. break;
  394. }
  395. }
  396. }
  397. else {
  398. QS_CRIT_X_();
  399. }
  400. }
  401. /*..........................................................................*/
  402. static void QS_rx_input(void) {
  403. int status = recv(l_sock, (char*)QS_rxPriv_.buf, (int)QS_rxPriv_.end, 0);
  404. if (status > 0) { /* any data received? */
  405. QS_rxPriv_.tail = 0U;
  406. QS_rxPriv_.head = status; /* # bytes received */
  407. QS_rxParse(); /* parse all received bytes */
  408. }
  409. }
  410. #endif /* Q_SPY */
  411. /*--------------------------------------------------------------------------*/
  412. /*****************************************************************************
  413. * NOTE1:
  414. * This application uses the ThreadX thread of the lowest priority to perform
  415. * the QS data output to the host. This is not the only choice available, and
  416. * other applications might choose to peform the QS output some other way.
  417. *
  418. * The lowest-priority thread does not block, so in effect, it becomes the
  419. * idle loop. This presents no problems to ThreadX - its idle task in the
  420. * scheduler does not need to run.
  421. */