Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

382 lines
7.5 KiB

  1. /*-
  2. * Copyright 2022 John-Mark Gurney.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  14. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  17. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  19. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  20. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23. * SUCH DAMAGE.
  24. *
  25. */
  26. #include <si_usb.h>
  27. #include <rs485frame.h>
  28. #include <misc.h>
  29. #include <stdbool.h>
  30. #include <stdint.h>
  31. #include <string.h>
  32. #include <sysinit.h>
  33. #include <usb_device.h>
  34. SYSINIT_VF(usb_cdc, SI_SUB_USB, SI_ORDER_MIDDLE, MX_USB_DEVICE_Init);
  35. /* XXX - where's a better place? */
  36. extern PCD_HandleTypeDef hpcd_USB_FS;
  37. void
  38. USB_LP_IRQHandler(void)
  39. {
  40. HAL_PCD_IRQHandler(&hpcd_USB_FS);
  41. }
  42. static int dorx = 0;
  43. static uint8_t rxbuf[128];
  44. static int gotrxdone = 0;
  45. static int gottxdone = 0;
  46. static int goterr = 0;
  47. static int rxbufsize = 0;
  48. static int rxbuftrunc = 0;
  49. static void
  50. c13led(const void *none)
  51. {
  52. GPIO_InitTypeDef GPIO_InitStruct;
  53. __HAL_RCC_GPIOB_CLK_ENABLE();
  54. __HAL_RCC_GPIOC_CLK_ENABLE();
  55. GPIO_InitStruct = (GPIO_InitTypeDef){
  56. .Pin = GPIO_PIN_13,
  57. .Mode = GPIO_MODE_OUTPUT_PP,
  58. .Pull = GPIO_NOPULL,
  59. .Speed = GPIO_SPEED_FREQ_LOW,
  60. };
  61. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  62. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  63. }
  64. SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL);
  65. #if 0
  66. #undef usb_printf
  67. #define usb_printf debug_printf
  68. #endif
  69. char *
  70. findeol(char *pos, size_t len)
  71. {
  72. while (len) {
  73. if (*pos == '\r' || *pos == '\n')
  74. return pos;
  75. pos++;
  76. len--;
  77. }
  78. return NULL;
  79. }
  80. void
  81. hexdump(const uint8_t *ptr, size_t len)
  82. {
  83. int i;
  84. for (i = 0; i < len; i++)
  85. usb_printf("%02x", ptr[i]);
  86. }
  87. void
  88. txdone(void)
  89. {
  90. gottxdone = 1;
  91. }
  92. void
  93. errfunc(void)
  94. {
  95. goterr = 1;
  96. }
  97. void
  98. rxdone(const uint8_t *payload, size_t size)
  99. {
  100. if (size > sizeof rxbuf) {
  101. size = sizeof rxbuf;
  102. rxbuftrunc = 1;
  103. }
  104. memcpy(rxbuf, payload, size);
  105. rxbufsize = size;
  106. gotrxdone = 1;
  107. }
  108. static uint8_t
  109. hexchartonib(char s)
  110. {
  111. switch (s) {
  112. case '0'...'9':
  113. return s - '0';
  114. case 'a'...'f':
  115. return s - 'a' + 10;
  116. case 'A'...'F':
  117. return s - 'A' + 10;
  118. default:
  119. return -1;
  120. }
  121. }
  122. static bool
  123. hexdecode(char *buf, size_t len, uint8_t *out)
  124. {
  125. uint8_t topchr, botchr;
  126. if (len % 2)
  127. return false;
  128. /* NB: only needed to silence a bad gcc warning */
  129. topchr = -1;
  130. while (len) {
  131. if (len % 2) {
  132. /* bottom nibble */
  133. botchr = hexchartonib(*buf);
  134. if (topchr == -1 || botchr == -1)
  135. return false;
  136. *out = topchr << 4 | botchr;
  137. out++;
  138. } else {
  139. /* top nibble */
  140. topchr = hexchartonib(*buf);
  141. }
  142. len--;
  143. buf++;
  144. }
  145. return true;
  146. }
  147. static const char pktstart[] = "pkt:";
  148. static const size_t pktstartlen = sizeof pktstart - 1;
  149. static uint8_t pktbuf[128];
  150. static void
  151. process_line(char *start, char *end)
  152. {
  153. size_t len;
  154. /* trim off leading CR/NL */
  155. while (start < end && (*start == '\r' || *start == '\n'))
  156. start++;
  157. len = end - start;
  158. if (len >= pktstartlen && memcmp(start, pktstart, sizeof pktstart - 1) == 0) {
  159. start += pktstartlen;
  160. len -= pktstartlen;
  161. if (len % 2) {
  162. usb_printf("invalid pkt len\r\n");
  163. return;
  164. }
  165. if (!hexdecode(start, len, pktbuf)) {
  166. usb_printf("invalid pkt\r\n");
  167. return;
  168. }
  169. rs485_starttx(pktbuf, len / 2);
  170. return;
  171. }
  172. usb_printf("line: %.*s", end - start, start);
  173. fflush(vcp_usb);
  174. }
  175. static void
  176. WaitForIRQ()
  177. {
  178. __disable_irq();
  179. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
  180. __enable_irq( );
  181. }
  182. int
  183. main(void)
  184. {
  185. sysinit_run();
  186. debug_printf("starting...\n");
  187. #if 0
  188. int i;
  189. for (i = 0; i < 5; i++) {
  190. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
  191. HAL_Delay(250);
  192. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  193. HAL_Delay(250);
  194. }
  195. #endif
  196. setlinebuf(vcp_usb);
  197. #if 1
  198. wait_for_vcp();
  199. /*
  200. * This is required to use w/ FreeBSD. This is an issue w/ the
  201. * STM32 Core USB library:
  202. * https://github.com/STMicroelectronics/STM32CubeL1/issues/10
  203. */
  204. HAL_Delay(50);
  205. usb_printf("starting...\r\n");
  206. #endif
  207. char inpbuf[1024];
  208. char *lastcheck;
  209. char *endchr;
  210. int inpbufpos = 0;
  211. int cpylen;
  212. rs485_register(txdone, rxdone, errfunc);
  213. rs485_startrx();
  214. unsigned lasttick = -1;
  215. uint32_t gt;
  216. loop:
  217. /*
  218. * This is disabled as when it's enabled, it causes a hang
  219. * when sending USB data. The USB CDC end point never becomes
  220. * unbusy to allow the next send to progress.
  221. */
  222. #if 0
  223. /*
  224. * A ms delay isn't a big deal, so no special locking is
  225. * used to make sure we don't got to sleep w/ data pending.
  226. */
  227. WaitForIRQ();
  228. #else
  229. (void)WaitForIRQ;
  230. #endif
  231. gt = HAL_GetTick();
  232. if (lasttick != gt / 1000) {
  233. lasttick = gt / 1000;
  234. //usb_printf("tick: %u\r\n", lasttick);
  235. }
  236. if (goterr) {
  237. //usb_printf("error\r\n");
  238. goterr = 0;
  239. dorx = 1;
  240. }
  241. if (gottxdone) {
  242. usb_printf("txdone\r\n");
  243. dorx = 1;
  244. gottxdone = 0;
  245. }
  246. if (gotrxdone) {
  247. if (rxbuftrunc) {
  248. rxbuftrunc = 0;
  249. //usb_printf("rxdone: buffer overflow\r\n");
  250. } else {
  251. usb_printf("rxdone: size: %u\r\ndata: ", rxbufsize);
  252. hexdump(rxbuf, rxbufsize);
  253. usb_printf("\r\n");
  254. }
  255. dorx = 1;
  256. gotrxdone = 0;
  257. }
  258. if (dorx) {
  259. //usb_printf("dorx\r\n");
  260. rs485_startrx();
  261. //usb_printf("startrx res: %s\r\n", rs485err);
  262. dorx = 0;
  263. }
  264. /* while we have data */
  265. while (CDC_RX_LEN) {
  266. /* store last position */
  267. lastcheck = &inpbuf[inpbufpos];
  268. /* calculate how much space left */
  269. cpylen = MIN(sizeof inpbuf - inpbufpos, CDC_RX_LEN);
  270. /* copy into buffer */
  271. memcpy(&inpbuf[inpbufpos], CDC_RX_BUFFER, cpylen);
  272. /* and point to end of buffer */
  273. inpbufpos += cpylen;
  274. do {
  275. /* find first end of line characters */
  276. endchr = findeol(lastcheck, cpylen);
  277. if (endchr != NULL) {
  278. /* if so, process it */
  279. process_line(inpbuf, endchr);
  280. /* skip end of line char */
  281. endchr++;
  282. /* move remaining buffer to the beginning */
  283. memmove(inpbuf, endchr, inpbufpos - (endchr - inpbuf));
  284. /* and store new length */
  285. inpbufpos = inpbufpos - (endchr - inpbuf);
  286. /* mark begining of stream as last checked */
  287. lastcheck = inpbuf;
  288. /* and try to process another line */
  289. continue;
  290. } else if (inpbufpos == sizeof inpbuf) {
  291. /* we overflowed the buffer */
  292. /* XXX - best way is to throw away this line */
  293. inpbufpos = 0;
  294. }
  295. } while (0);
  296. /* if we copied all the data */
  297. if (cpylen == CDC_RX_LEN) {
  298. /* declare that we are ready to receive more data */
  299. CDC_RX_LEN = 0;
  300. USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  301. } else {
  302. /* if not, move the remaining to the begining and try again */
  303. memmove(CDC_RX_BUFFER, &CDC_RX_BUFFER[cpylen], CDC_RX_LEN - cpylen);
  304. CDC_RX_LEN -= cpylen;
  305. }
  306. }
  307. goto loop;
  308. }