/*- * Copyright 2022 John-Mark Gurney. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ #include #include #include #include #include #include #include #include SYSINIT_VF(usb_cdc, SI_SUB_USB, SI_ORDER_MIDDLE, MX_USB_DEVICE_Init); /* XXX - where's a better place? */ extern PCD_HandleTypeDef hpcd_USB_FS; void USB_LP_IRQHandler(void) { HAL_PCD_IRQHandler(&hpcd_USB_FS); } static int dorx = 0; static uint8_t rxbuf[128]; static int gotrxdone = 0; static int gottxdone = 0; static int goterr = 0; static int rxbufsize = 0; static int rxbuftrunc = 0; static void c13led(const void *none) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct = (GPIO_InitTypeDef){ .Pin = GPIO_PIN_13, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_LOW, }; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); } SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL); #if 0 #undef usb_printf #define usb_printf debug_printf #endif char * findeol(char *pos, size_t len) { while (len) { if (*pos == '\r' || *pos == '\n') return pos; pos++; len--; } return NULL; } void hexdump(const uint8_t *ptr, size_t len) { int i; for (i = 0; i < len; i++) usb_printf("%02x", ptr[i]); } void txdone(void) { gottxdone = 1; } void errfunc(void) { goterr = 1; } void rxdone(const uint8_t *payload, size_t size) { if (size > sizeof rxbuf) { size = sizeof rxbuf; rxbuftrunc = 1; } memcpy(rxbuf, payload, size); rxbufsize = size; gotrxdone = 1; } static uint8_t hexchartonib(char s) { switch (s) { case '0'...'9': return s - '0'; case 'a'...'f': return s - 'a' + 10; case 'A'...'F': return s - 'A' + 10; default: return -1; } } static bool hexdecode(char *buf, size_t len, uint8_t *out) { uint8_t topchr, botchr; if (len % 2) return false; /* NB: only needed to silence a bad gcc warning */ topchr = -1; while (len) { if (len % 2) { /* bottom nibble */ botchr = hexchartonib(*buf); if (topchr == -1 || botchr == -1) return false; *out = topchr << 4 | botchr; out++; } else { /* top nibble */ topchr = hexchartonib(*buf); } len--; buf++; } return true; } static const char pktstart[] = "pkt:"; static const size_t pktstartlen = sizeof pktstart - 1; static uint8_t pktbuf[128]; static void process_line(char *start, char *end) { size_t len; /* trim off leading CR/NL */ while (start < end && (*start == '\r' || *start == '\n')) start++; len = end - start; if (len >= pktstartlen && memcmp(start, pktstart, sizeof pktstart - 1) == 0) { start += pktstartlen; len -= pktstartlen; if (len % 2) { usb_printf("invalid pkt len\r\n"); return; } if (!hexdecode(start, len, pktbuf)) { usb_printf("invalid pkt\r\n"); return; } rs485_starttx(pktbuf, len / 2); return; } usb_printf("line: %.*s", end - start, start); fflush(vcp_usb); } static void WaitForIRQ() { __disable_irq(); HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); __enable_irq( ); } int main(void) { debug_printf("starting...\n"); #if 0 int i; for (i = 0; i < 5; i++) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_Delay(250); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_Delay(250); } #endif setlinebuf(vcp_usb); #if 1 wait_for_vcp(); /* * This is required to use w/ FreeBSD. This is an issue w/ the * STM32 Core USB library: * https://github.com/STMicroelectronics/STM32CubeL1/issues/10 */ HAL_Delay(50); usb_printf("starting...\r\n"); #endif char inpbuf[1024]; char *lastcheck; char *endchr; int inpbufpos = 0; int cpylen; rs485_register(txdone, rxdone, errfunc); rs485_startrx(); unsigned lasttick = -1; uint32_t gt; loop: /* * This is disabled as when it's enabled, it causes a hang * when sending USB data. The USB CDC end point never becomes * unbusy to allow the next send to progress. */ #if 0 /* * A ms delay isn't a big deal, so no special locking is * used to make sure we don't got to sleep w/ data pending. */ WaitForIRQ(); #else (void)WaitForIRQ; #endif gt = HAL_GetTick(); if (lasttick != gt / 1000) { lasttick = gt / 1000; //usb_printf("tick: %u\r\n", lasttick); } if (goterr) { //usb_printf("error\r\n"); goterr = 0; dorx = 1; } if (gottxdone) { usb_printf("txdone\r\n"); dorx = 1; gottxdone = 0; } if (gotrxdone) { if (rxbuftrunc) { rxbuftrunc = 0; //usb_printf("rxdone: buffer overflow\r\n"); } else { usb_printf("rxdone: size: %u\r\ndata: ", rxbufsize); hexdump(rxbuf, rxbufsize); usb_printf("\r\n"); } dorx = 1; gotrxdone = 0; } if (dorx) { //usb_printf("dorx\r\n"); rs485_startrx(); //usb_printf("startrx res: %s\r\n", rs485err); dorx = 0; } /* while we have data */ while (CDC_RX_LEN) { /* store last position */ lastcheck = &inpbuf[inpbufpos]; /* calculate how much space left */ cpylen = MIN(sizeof inpbuf - inpbufpos, CDC_RX_LEN); /* copy into buffer */ memcpy(&inpbuf[inpbufpos], CDC_RX_BUFFER, cpylen); /* and point to end of buffer */ inpbufpos += cpylen; do { /* find first end of line characters */ endchr = findeol(lastcheck, cpylen); if (endchr != NULL) { /* if so, process it */ process_line(inpbuf, endchr); /* skip end of line char */ endchr++; /* move remaining buffer to the beginning */ memmove(inpbuf, endchr, inpbufpos - (endchr - inpbuf)); /* and store new length */ inpbufpos = inpbufpos - (endchr - inpbuf); /* mark begining of stream as last checked */ lastcheck = inpbuf; /* and try to process another line */ continue; } else if (inpbufpos == sizeof inpbuf) { /* we overflowed the buffer */ /* XXX - best way is to throw away this line */ inpbufpos = 0; } } while (0); /* if we copied all the data */ if (cpylen == CDC_RX_LEN) { /* declare that we are ready to receive more data */ CDC_RX_LEN = 0; USBD_CDC_ReceivePacket(&hUsbDeviceFS); } else { /* if not, move the remaining to the begining and try again */ memmove(CDC_RX_BUFFER, &CDC_RX_BUFFER[cpylen], CDC_RX_LEN - cpylen); CDC_RX_LEN -= cpylen; } } goto loop; }