aboutsummaryrefslogtreecommitdiff
path: root/atmel-samd/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'atmel-samd/usb.c')
-rw-r--r--atmel-samd/usb.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/atmel-samd/usb.c b/atmel-samd/usb.c
new file mode 100644
index 000000000..11be282a2
--- /dev/null
+++ b/atmel-samd/usb.c
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "usb.h"
+
+#include <stdint.h>
+
+#include "hal/include/hal_gpio.h"
+#include "usb/class/cdc/device/cdcdf_acm.h"
+// #include "hiddf_mouse.h"
+// #include "hiddf_keyboard.h"
+#include "usb/class/hid/device/hiddf_generic.h"
+#include "usb/class/composite/device/composite_desc.h"
+#include "peripheral_clk_config.h"
+#include "hpl/pm/hpl_pm_base.h"
+#include "hpl/gclk/hpl_gclk_base.h"
+
+#include "lib/utils/interrupt_char.h"
+
+#include "supervisor/shared/autoreload.h"
+
+// Store received characters on our own so that we can filter control characters
+// and act immediately on CTRL-C for example.
+
+// Receive buffer
+static uint8_t usb_rx_buf[USB_RX_BUF_SIZE];
+
+// Receive buffer head
+static volatile uint8_t usb_rx_buf_head;
+
+// Receive buffer tail
+static volatile uint8_t usb_rx_buf_tail;
+
+// Number of bytes in receive buffer
+volatile uint8_t usb_rx_count;
+
+volatile bool mp_cdc_enabled = false;
+
+static uint8_t multi_desc_bytes[] = {
+ /* Device descriptors and Configuration descriptors list. */
+ COMPOSITE_DESCES_LS_FS,
+};
+
+static struct usbd_descriptors multi_desc = {multi_desc_bytes, multi_desc_bytes + sizeof(multi_desc_bytes)};
+
+/** Ctrl endpoint buffer */
+static uint8_t ctrl_buffer[64];
+
+static void init_hardware(void) {
+ #ifdef SAMD21
+ _pm_enable_bus_clock(PM_BUS_APBB, USB);
+ _pm_enable_bus_clock(PM_BUS_AHB, USB);
+ _gclk_enable_channel(USB_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val);
+ #endif
+
+ #ifdef SAMD51
+ hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, CONF_GCLK_USB_SRC | GCLK_PCHCTRL_CHEN);
+ hri_mclk_set_AHBMASK_USB_bit(MCLK);
+ hri_mclk_set_APBBMASK_USB_bit(MCLK);
+ #endif
+
+ usb_d_init();
+
+ gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
+ gpio_set_pin_level(PIN_PA24, false);
+ gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
+ gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
+ gpio_set_pin_level(PIN_PA25, false);
+ gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
+ #ifdef SAMD21
+ gpio_set_pin_function(PIN_PA24, PINMUX_PA24G_USB_DM);
+ gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP);
+ #endif
+ #ifdef SAMD51
+ gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM);
+ gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP);
+ #endif
+}
+
+extern uint32_t *_usb_ep1_cache;
+static bool usb_device_cb_bulk_out(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
+{
+ volatile hal_atomic_t flags;
+ atomic_enter_critical(&flags);
+ // If our buffer can't fit the data received, then error out.
+ if (count > (uint8_t) (USB_RX_BUF_SIZE - usb_rx_count)) {
+ atomic_leave_critical(&flags);
+ return true;
+ }
+
+ // We read the data but ignore it later. The data itself isn't correct but
+ // it does mark it as read so further data is received ok.
+ // TODO(tannewt): Get ASF4 fixed so the data is correct and then stop using
+ // _usb_ep1_cache directly below.
+ uint8_t buf[count];
+ int32_t result = cdcdf_acm_read(buf, count);
+ if (result != ERR_NONE) {
+ return true;
+ }
+
+ for (uint16_t i = 0; i < count; i++) {
+ uint8_t c = ((uint8_t*) &_usb_ep1_cache)[i];
+ if (c == mp_interrupt_char) {
+ atomic_leave_critical(&flags);
+ mp_keyboard_interrupt();
+ // Don't put the interrupt into the buffer, just continue.
+ return false;
+ } else {
+ // The count of characters present in receive buffer is
+ // incremented.
+ usb_rx_count++;
+ usb_rx_buf[usb_rx_buf_tail] = c;
+ usb_rx_buf_tail++;
+ if (usb_rx_buf_tail == USB_RX_BUF_SIZE) {
+ // Reached the end of buffer, revert back to beginning of
+ // buffer.
+ usb_rx_buf_tail = 0x00;
+ }
+ }
+ }
+ atomic_leave_critical(&flags);
+
+ /* No error. */
+ return false;
+}
+
+static bool usb_device_cb_bulk_in(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
+{
+ /* No error. */
+ return false;
+}
+
+static bool usb_device_cb_state_c(usb_cdc_control_signal_t state)
+{
+ //uint8_t buf[64];
+ if (state.rs232.DTR) {
+ // Start Rx and throw away packet.
+ //cdcdf_acm_read((uint8_t *)buf, 64);
+ }
+
+ /* No error. */
+ return false;
+}
+
+// static void usbd_sof_event(void)
+// {
+// // Triggers button state checks and HID response.
+// }
+
+void init_usb(void) {
+ init_hardware();
+
+ usbdc_init(ctrl_buffer);
+
+ /* usbdc_register_funcion inside */
+ cdcdf_acm_init();
+ // hiddf_mouse_init();
+ // hiddf_keyboard_init();
+
+ int32_t result = usbdc_start(&multi_desc);
+ while (result != ERR_NONE) {}
+ usbdc_attach();
+}
+
+static inline bool cdc_enabled(void) {
+ if (mp_cdc_enabled) {
+ return true;
+ }
+ if (!cdcdf_acm_is_enabled()) {
+ return false;
+ }
+ cdcdf_acm_register_callback(CDCDF_ACM_CB_READ, (FUNC_PTR)usb_device_cb_bulk_out);
+ cdcdf_acm_register_callback(CDCDF_ACM_CB_WRITE, (FUNC_PTR)usb_device_cb_bulk_in);
+ cdcdf_acm_register_callback(CDCDF_ACM_CB_STATE_C, (FUNC_PTR)usb_device_cb_state_c);
+ mp_cdc_enabled = true;
+
+ // Ignored read.
+ uint8_t buf[64];
+ cdcdf_acm_read(buf, 64);
+ return true;
+}
+
+bool usb_bytes_available(void) {
+ if (usb_rx_count == 0) {
+ cdc_enabled();
+ return false;
+ }
+ return usb_rx_count > 0;
+}
+
+int usb_read(void) {
+ if (!cdc_enabled() || usb_rx_count == 0) {
+ return 0;
+ }
+
+ // Disable autoreload if someone is using the repl.
+ // TODO(tannewt): Check that we're actually in the REPL. It could be an
+ // input() call from a script.
+ autoreload_disable();
+
+ // Copy from head.
+ int data;
+ CRITICAL_SECTION_ENTER();
+ data = usb_rx_buf[usb_rx_buf_head];
+ usb_rx_buf_head++;
+ usb_rx_count--;
+ if ((USB_RX_BUF_SIZE) == usb_rx_buf_head) {
+ usb_rx_buf_head = 0;
+ }
+ CRITICAL_SECTION_LEAVE();
+
+ //usb_write((uint8_t *)&data, 1);
+
+ return data;
+}
+
+void usb_write(const char* buffer, uint32_t len) {
+ if (!cdc_enabled()) {
+ return;
+ }
+ int32_t result = cdcdf_acm_write((uint8_t *)buffer, len);
+ while (result == USB_BUSY) {
+ #ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP
+ #endif
+ result = cdcdf_acm_write((uint8_t *)buffer, len);
+ }
+ while (result != ERR_NONE) {}
+}
+
+bool usb_connected(void) {
+ return cdc_enabled();
+}