| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| // Get NULL from here |
| #include "usb_simpleserial.h" |
| |
| #include <stddef.h> |
| |
| #include "sw/device/lib/common.h" |
| #include "usbdev.h" |
| |
| #define MAX_GATHER 16 |
| |
| static void ss_rx(void *ssctx_v, usbbufid_t buf, int size, int setup) { |
| usb_ss_ctx_t *ssctx = (usb_ss_ctx_t *)ssctx_v; |
| void *ctx = ssctx->ctx; |
| volatile uint8_t *bp = (volatile uint8_t *)usbdev_buf_idtoaddr(ctx, buf); |
| |
| if (size > BUF_LENGTH) { |
| size = BUF_LENGTH; |
| } |
| |
| while (size--) { |
| ssctx->got_byte(*bp++); |
| } |
| } |
| |
| // Called periodically by the main loop to ensure characters don't |
| // stick around too long |
| static void ss_flush(void *ssctx_v) { |
| usb_ss_ctx_t *ssctx = (usb_ss_ctx_t *)ssctx_v; |
| void *ctx = ssctx->ctx; |
| volatile uint32_t *bp_w; |
| if ((ssctx->cur_buf == -1) || (ssctx->cur_cpos <= 0)) { |
| return; |
| } |
| if ((ssctx->cur_cpos & 0x3) != 0) { |
| // unwritten data to copy over |
| bp_w = usbdev_buf_idtoaddr(ctx, ssctx->cur_buf); |
| // no -1 here because cpos is in the word we are writing |
| bp_w[(ssctx->cur_cpos / 4)] = ssctx->chold.data_w; |
| } |
| usbdev_sendbuf_byid(ctx, ssctx->cur_buf, ssctx->cur_cpos, ssctx->ep); |
| ssctx->cur_buf = -1; // given it to the hardware |
| } |
| |
| // Simple send byte will gather data for a while and send |
| void usb_simpleserial_send_byte(usb_ss_ctx_t *ssctx, uint8_t c) { |
| volatile uint32_t *bp_w; |
| if (ssctx->cur_buf == -1) { |
| ssctx->cur_buf = usbdev_buf_allocate_byid(ssctx->ctx); |
| ssctx->cur_cpos = 0; |
| } |
| // Abort if completely out of buffers and allocation returned -1 |
| if (ssctx->cur_buf < 0) { |
| return; |
| } |
| ssctx->chold.data_b[ssctx->cur_cpos++ & 0x3] = c; |
| if ((ssctx->cur_cpos & 0x3) == 0) { |
| // just wrote last byte in word |
| bp_w = usbdev_buf_idtoaddr(ssctx->ctx, ssctx->cur_buf); |
| // -1 here because cpos already incremented to next word |
| bp_w[(ssctx->cur_cpos / 4) - 1] = ssctx->chold.data_w; |
| if (ssctx->cur_cpos >= MAX_GATHER) { |
| usbdev_sendbuf_byid(ssctx->ctx, ssctx->cur_buf, ssctx->cur_cpos, |
| ssctx->ep); |
| ssctx->cur_buf = -1; // given it to the hardware |
| } |
| } |
| } |
| |
| void usb_simpleserial_init(usb_ss_ctx_t *ssctx, usbdev_ctx_t *ctx, int ep, |
| void (*got_byte)(uint8_t)) { |
| usbdev_endpoint_setup(ctx, ep, 1, ssctx, NULL, ss_rx, ss_flush); |
| ssctx->ctx = ctx; |
| ssctx->ep = ep; |
| ssctx->got_byte = got_byte; |
| ssctx->cur_buf = -1; |
| } |