From 776ac652a80e025ffe5335732bf82950e7629536 Mon Sep 17 00:00:00 2001 From: Bernd Herzog Date: Fri, 31 Mar 2023 19:18:39 +0200 Subject: [PATCH] implemented scsi inquiry --- firmware/baseband/CMakeLists.txt | 3 +- .../baseband/sd_over_usb/proc_sd_over_usb.cpp | 5 +- firmware/baseband/sd_over_usb/scsi.c | 170 +++++ firmware/baseband/sd_over_usb/scsi.h | 46 ++ firmware/baseband/sd_over_usb/sd_over_usb.c | 222 ++++-- firmware/baseband/sd_over_usb/sd_over_usb.h | 5 +- firmware/baseband/sd_over_usb/usb.c | 676 ++++++++++++++++++ .../baseband/sd_over_usb/usb_descriptor.c | 16 +- .../GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld | 2 + 9 files changed, 1070 insertions(+), 75 deletions(-) create mode 100644 firmware/baseband/sd_over_usb/scsi.c create mode 100644 firmware/baseband/sd_over_usb/scsi.h create mode 100644 firmware/baseband/sd_over_usb/usb.c diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index cab5fe56c..2d7cbb6c2 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -525,12 +525,13 @@ set(MODE_INCDIR set(MODE_CPPSRC sd_over_usb/proc_sd_over_usb.cpp + sd_over_usb/scsi.c sd_over_usb/sd_over_usb.c sd_over_usb/usb_descriptor.c sd_over_usb/usb_device.c sd_over_usb/usb_endpoint.c + sd_over_usb/usb.c - ${HACKRF_PATH}/firmware/common/usb.c ${HACKRF_PATH}/firmware/common/usb_queue.c ${HACKRF_PATH}/firmware/common/usb_request.c ${HACKRF_PATH}/firmware/common/usb_standard_request.c diff --git a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp index 5109dc64f..9dec3bbcc 100644 --- a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp +++ b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp @@ -33,6 +33,7 @@ extern "C" { void start_usb(void); void stop_usb(void); void irq_usb(void); +void usb_transfer(void); CH_IRQ_HANDLER(Vector60) { irq_usb(); @@ -68,7 +69,9 @@ int main() { start_usb(); //event_dispatcher.run(); - while (true); + while (true) { + usb_transfer(); + } stop_usb(); return 0; } diff --git a/firmware/baseband/sd_over_usb/scsi.c b/firmware/baseband/sd_over_usb/scsi.c new file mode 100644 index 000000000..b4b035364 --- /dev/null +++ b/firmware/baseband/sd_over_usb/scsi.c @@ -0,0 +1,170 @@ +#include "scsi.h" + + +#define HALT_UNTIL_DEBUGGING() \ + while (!((*(volatile uint32_t *)0xE000EDF0) & (1 << 0))) {} \ + __asm__ __volatile__("bkpt 1") + +typedef struct { + uint8_t peripheral; + uint8_t removable; + uint8_t version; + uint8_t response_data_format; + uint8_t additional_length; + uint8_t sccstp; + uint8_t bqueetc; + uint8_t cmdque; + uint8_t vendorID[8]; + uint8_t productID[16]; + uint8_t productRev[4]; +} scsi_inquiry_response_t; + + +static const scsi_inquiry_response_t default_scsi_inquiry_response = { + 0x00, /* direct access block device */ + 0x80, /* removable */ + 0x04, /* SPC-2 */ + 0x02, /* response data format */ + 0x20, /* response has 0x20 + 4 bytes */ + 0x00, + 0x00, + 0x00, + "Mayhem", + "Mass Storage", + {'v','1','.','6'} +}; + +typedef struct { + uint32_t signature; + uint32_t tag; + uint32_t data_residue; + uint8_t status; +} __attribute__((packed)) msd_csw_t; + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +// void handle_inquiry_4(void* user_data, unsigned int bytes_transferred) { +// HALT_UNTIL_DEBUGGING(); +// } + +// void handle_inquiry_3(void* user_data, unsigned int bytes_transferred) +// { +// //msd_cbw_t *msd_cbw_data = (msd_cbw_t *)user_data; + +// //HALT_UNTIL_DEBUGGING(); + + +// } + + +volatile bool inquiry_stage_one_send = false; +void inquiry_cb(void* user_data, unsigned int bytes_transferred) +{ + inquiry_stage_one_send = true; + + //HALT_UNTIL_DEBUGGING(); + + //faulty + // usb_transfer_schedule_block( + // &usb_endpoint_bulk_out, + // &usb_bulk_buffer[0], + // USB_TRANSFER_SIZE, + // handle_inquiry_3, + // msd_cbw_data); + + +//does not send + +} + +void handle_inquiry(msd_cbw_t *msd_cbw_data){ + inquiry_stage_one_send = false; + memcpy(&usb_bulk_buffer[0], &default_scsi_inquiry_response, sizeof(scsi_inquiry_response_t)); + usb_transfer_schedule_block( + &usb_endpoint_bulk_in, + &usb_bulk_buffer[0], + sizeof(scsi_inquiry_response_t), + inquiry_cb, + msd_cbw_data); + + while (!inquiry_stage_one_send); + + msd_csw_t csw = { + .signature = MSD_CSW_SIGNATURE, + .tag = msd_cbw_data->tag, + .data_residue = 0, + .status = 0 + }; + + memcpy(&usb_bulk_buffer[0x4000], &csw, sizeof(msd_csw_t)); + // does not gets send + usb_transfer_schedule_block( + &usb_endpoint_bulk_in, + &usb_bulk_buffer[0x4000], + sizeof(msd_csw_t), + NULL, //handle_inquiry_3, + NULL); //msd_cbw_data); + +} + +void scsi_command(msd_cbw_t *msd_cbw_data) { + + switch (msd_cbw_data->cmd_data[0]) { + case SCSI_CMD_INQUIRY: + handle_inquiry(msd_cbw_data); + + + break; + + default: + HALT_UNTIL_DEBUGGING(); + break; +/* + case SCSI_CMD_REQUEST_SENSE: + ret = request_sense(scsip, cmd); + break; + + case SCSI_CMD_READ_CAPACITY_10: + ret = read_capacity10(scsip, cmd); + break; + + case SCSI_CMD_READ_10: + ret = data_read_write10(scsip, cmd); + break; + + case SCSI_CMD_WRITE_10: + ret = data_read_write10(scsip, cmd); + break; + + case SCSI_CMD_TEST_UNIT_READY: + ret = test_unit_ready(scsip, cmd); + break; + + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + ret = cmd_ignored(scsip, cmd); + break; + + case SCSI_CMD_MODE_SENSE_6: + ret = mode_sense6(scsip, cmd); + break; + + case SCSI_CMD_READ_FORMAT_CAPACITIES: + ret = read_format_capacities(scsip, cmd); + break; + + case SCSI_CMD_VERIFY_10: + ret = cmd_ignored(scsip, cmd); + break; + + default: + ret = cmd_unhandled(scsip, cmd); + break; + */ + } + +// if (ret == SCSI_SUCCESS) +// set_sense_ok(scsip); + + +} \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/scsi.h b/firmware/baseband/sd_over_usb/scsi.h new file mode 100644 index 000000000..616c9aff9 --- /dev/null +++ b/firmware/baseband/sd_over_usb/scsi.h @@ -0,0 +1,46 @@ +#ifndef __SCSI_H__ +#define __SCSI_H__ + +#include +#include +#include +#include +#include "usb_device.h" +#include "usb_endpoint.h" +#include +#include +#include "platform_detect.h" +#include "hackrf_core.h" +#include "usb_bulk_buffer.h" + +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_MODE_SENSE_6 0x1A +#define SCSI_CMD_START_STOP_UNIT 0x1B +#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D +#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_WRITE_10 0x2A +#define SCSI_CMD_VERIFY_10 0x2F + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +#define USB_TRANSFER_SIZE 0x2000 + +typedef struct { + uint32_t signature; + uint32_t tag; + uint32_t data_len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd_data[16]; +} __attribute__((packed)) msd_cbw_t; + +void scsi_command(msd_cbw_t *msd_cbw_data); + +#endif /* __SCSI_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.c b/firmware/baseband/sd_over_usb/sd_over_usb.c index 594eb7846..5557d0ac7 100644 --- a/firmware/baseband/sd_over_usb/sd_over_usb.c +++ b/firmware/baseband/sd_over_usb/sd_over_usb.c @@ -1,102 +1,175 @@ #include "sd_over_usb.h" +#include "scsi.h" //extern usb_request_handlers_t usb_request_handlers; +#define HALT_UNTIL_DEBUGGING() \ + while (!((*(volatile uint32_t *)0xE000EDF0) & (1 << 0))) {} \ + __asm__ __volatile__("bkpt 1") + +// uint8_t usb_buffer[USB_TRANSFER_SIZE]; -usb_request_status_t dummy( +bool scsi_running = false; + +usb_request_status_t report_max_lun( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + endpoint->buffer[0] = 0; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + + usb_transfer_schedule_ack(endpoint->out); + + /* start loop here?*/ + scsi_running = true; + // Host to Device. receive inquiry LUN + return USB_REQUEST_STATUS_OK; + } + else if (stage == USB_TRANSFER_STAGE_DATA) { + return USB_REQUEST_STATUS_OK; + } + else if (stage == USB_TRANSFER_STAGE_STATUS) { + // response here? + return USB_REQUEST_STATUS_OK; + } +} + + +usb_request_status_t report_magic_scsi( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + + // return USB_REQUEST_STATUS_OK; + + + // if (endpoint->address != 0x02) { + // HALT_UNTIL_DEBUGGING(); + // return USB_REQUEST_STATUS_OK; + // } + + if (stage == USB_TRANSFER_STAGE_SETUP) { + return USB_REQUEST_STATUS_OK; + // usb_bulk_buffer[0] = 0x55; + // usb_bulk_buffer[1] = 0x55; + // usb_bulk_buffer[2] = 0x55; + // usb_bulk_buffer[3] = 0x55; + // usb_bulk_buffer[4] = 0; + + // usb_transfer_schedule_block( + // &usb_endpoint_bulk_out, + // &usb_bulk_buffer[0x0000], + // 5, + // NULL, + // NULL); + + // usb_transfer_schedule_ack(endpoint->out); + + + + } + else if (stage == USB_TRANSFER_STAGE_DATA) { + HALT_UNTIL_DEBUGGING(); + } + else if (stage == USB_TRANSFER_STAGE_STATUS) { + HALT_UNTIL_DEBUGGING(); + } + return USB_REQUEST_STATUS_OK; + + (void)(endpoint); (void)(stage); return USB_REQUEST_STATUS_OK; } -static usb_request_handler_fn vendor_request_handler[] = { - NULL, - dummy // USB_WCID_VENDOR_REQ -}; - -static const uint32_t vendor_request_handler_count = - sizeof(vendor_request_handler) / sizeof(vendor_request_handler[0]); - usb_request_status_t usb_vendor_request( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { usb_request_status_t status = USB_REQUEST_STATUS_STALL; - if (endpoint->setup.request < vendor_request_handler_count) { - usb_request_handler_fn handler = - vendor_request_handler[endpoint->setup.request]; - if (handler) { - status = handler(endpoint, stage); - } + volatile uint_fast8_t address = endpoint->address; + volatile uint8_t request = endpoint->setup.request; + volatile uint32_t b = 0; + + if (request == 25) { // unknown code + return report_magic_scsi(endpoint, stage); } + b = request + (address << 16); + HALT_UNTIL_DEBUGGING(); + + return status; } +usb_request_status_t usb_class_request( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + usb_request_status_t status = USB_REQUEST_STATUS_STALL; + + volatile uint_fast8_t address = endpoint->address; + volatile uint8_t request = endpoint->setup.request; + volatile uint32_t b = 0; + + if (request == 0xFE) { + return report_max_lun(endpoint, stage); + } + + b = request + (address << 16); + HALT_UNTIL_DEBUGGING(); + + // if (address == 0x80) { + + // HALT_UNTIL_DEBUGGING(); + // return USB_REQUEST_STATUS_OK; + // } + + // else if (address == 0x00) { + // if (request == 0xFE) { + // return USB_REQUEST_STATUS_OK; // ??????????????????? + // } + // b = request + (address << 16); + // HALT_UNTIL_DEBUGGING(); + // } + + // else if (address == 0x02) { + // if (request == 0xFE) { + // return USB_REQUEST_STATUS_OK; // ??????????????????? + // } + // //return USB_REQUEST_STATUS_OK; + // } + + + // HALT_UNTIL_DEBUGGING(); + return status + b; +} + const usb_request_handlers_t usb_request_handlers = { .standard = usb_standard_request, - .class = 0, + .class = usb_class_request, .vendor = usb_vendor_request, - .reserved = 0, + .reserved = 0 }; - void usb_configuration_changed(usb_device_t* const device) { - /* Reset transceiver to idle state until other commands are received */ - // request_transceiver_mode(TRANSCEIVER_MODE_OFF); - // if (device->configuration->number == 1) { - // // transceiver configuration - // led_on(LED1); - // } else { - // /* Configuration number equal 0 means usb bus reset. */ - // led_off(LED1); - // } usb_endpoint_init(&usb_endpoint_bulk_in); usb_endpoint_init(&usb_endpoint_bulk_out); } - void start_usb(void) { - - /* use XTAL_OSC as clock source for PLL0USB */ - // CGU_PLL0USB_CTRL = - // CGU_PLL0USB_CTRL_PD(1) /* PLL0 power down */ | - // CGU_PLL0USB_CTRL_AUTOBLOCK(1) /* Block clock automatically during frequency change */ | - // CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); /* Clock source selection */ - - // while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {} - - /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ - /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ - - // CGU_PLL0USB_MDIV = 0x06167FFA; - // CGU_PLL0USB_NP_DIV = 0x00302062; - - // CGU_PLL0USB_CTRL |= - // CGU_PLL0USB_CTRL_PD(1) /* PLL0 power down */ | - // CGU_PLL0USB_CTRL_DIRECTI(1) /* PLL0 direct input */ | - // CGU_PLL0USB_CTRL_DIRECTO(1) /* PLL0 direct output */ | - // CGU_PLL0USB_CTRL_CLKEN(1) /* PLL0 clock enable */; - - // /* power on PLL0USB and wait until stable */ - // CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK /* PLL0 power down */ ; - - // while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {} - - /* use PLL0USB as clock source for USB0 */ - // CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | - // CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); - detect_hardware_platform(); pin_setup(); cpu_clock_init(); - usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); @@ -113,16 +186,39 @@ void start_usb(void) { nvic_set_priority(NVIC_USB0_IRQ, 255); usb_run(&usb_device); - - } - void stop_usb(void) { usb_peripheral_reset(); - } void irq_usb(void) { usb0_isr(); } + +volatile bool transfer_complete = false; +void scsi_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) +{ + transfer_complete = true; +} + +void usb_transfer(void) { + if (scsi_running) { + transfer_complete = false; + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_bulk_buffer[0], + USB_TRANSFER_SIZE, + scsi_bulk_transfer_complete, + NULL); + + while (!transfer_complete); + + msd_cbw_t *msd_cbw_data = (msd_cbw_t *) &usb_bulk_buffer[0]; + + if (msd_cbw_data->signature == MSD_CBW_SIGNATURE){ + scsi_command(msd_cbw_data); + } + + } +} \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.h b/firmware/baseband/sd_over_usb/sd_over_usb.h index ecdb0d4dc..536ac3814 100644 --- a/firmware/baseband/sd_over_usb/sd_over_usb.h +++ b/firmware/baseband/sd_over_usb/sd_over_usb.h @@ -33,10 +33,11 @@ #include #include "platform_detect.h" #include "hackrf_core.h" +#include "usb_bulk_buffer.h" void start_usb(void); void stop_usb(void); - - +void irq_usb(void); +void usb_transfer(void); #endif /* __USB_SD_OVER_USB_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/usb.c b/firmware/baseband/sd_over_usb/usb.c new file mode 100644 index 000000000..fd212f311 --- /dev/null +++ b/firmware/baseband/sd_over_usb/usb.c @@ -0,0 +1,676 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include "usb_type.h" +#include "usb_queue.h" +#include "usb_standard_request.h" + +#include +#include +#include +#include + +#define HALT_UNTIL_DEBUGGING() \ + while (!((*(volatile uint32_t *)0xE000EDF0) & (1 << 0))) {} \ + __asm__ __volatile__("bkpt 1") + + +usb_device_t* usb_device_usb0 = 0; + +usb_queue_head_t usb_qh[12] ATTR_ALIGNED(2048); + +#define USB_QH_INDEX(endpoint_address) \ + (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) + +usb_queue_head_t* usb_queue_head(const uint_fast8_t endpoint_address) +{ + return &usb_qh[USB_QH_INDEX(endpoint_address)]; +} + +usb_endpoint_t* usb_endpoint_from_address(const uint_fast8_t endpoint_address) +{ + return (usb_endpoint_t*) usb_queue_head(endpoint_address)->_reserved_0; +} + +static uint_fast8_t usb_endpoint_address( + const usb_transfer_direction_t direction, + const uint_fast8_t number) +{ + return ((direction == USB_TRANSFER_DIRECTION_IN) ? 0x80 : 0x00) + number; +} + +static bool usb_endpoint_is_in(const uint_fast8_t endpoint_address) +{ + return (endpoint_address & 0x80) ? true : false; +} + +static uint_fast8_t usb_endpoint_number(const uint_fast8_t endpoint_address) +{ + return (endpoint_address & 0xF); +} + +void usb_peripheral_reset() +{ + RESET_CTRL0 = RESET_CTRL0_USB0_RST; + RESET_CTRL0 = 0; + + while ((RESET_ACTIVE_STATUS0 & RESET_CTRL0_USB0_RST) == 0) {} +} + +void usb_phy_enable() +{ + CREG_CREG0 &= ~CREG_CREG0_USB0PHY; +} + +static void usb_clear_pending_interrupts(const uint32_t mask) +{ + USB0_ENDPTNAK = mask; + USB0_ENDPTNAKEN = mask; + USB0_USBSTS_D = mask; + USB0_ENDPTSETUPSTAT = USB0_ENDPTSETUPSTAT & mask; + USB0_ENDPTCOMPLETE = USB0_ENDPTCOMPLETE & mask; +} + +static void usb_clear_all_pending_interrupts() +{ + usb_clear_pending_interrupts(0xFFFFFFFF); +} + +static void usb_wait_for_endpoint_priming_to_finish(const uint32_t mask) +{ + // Wait until controller has parsed new transfer descriptors and prepared + // receive buffers. + while (USB0_ENDPTPRIME & mask) {} +} + +static void usb_flush_endpoints(const uint32_t mask) +{ + // Clear any primed buffers. If a packet is in progress, that transfer + // will continue until completion. + USB0_ENDPTFLUSH = mask; +} + +static void usb_wait_for_endpoint_flushing_to_finish(const uint32_t mask) +{ + // Wait until controller has flushed all endpoints / cleared any primed + // buffers. + while (USB0_ENDPTFLUSH & mask) {} +} + +static void usb_flush_primed_endpoints(const uint32_t mask) +{ + usb_wait_for_endpoint_priming_to_finish(mask); + usb_flush_endpoints(mask); + usb_wait_for_endpoint_flushing_to_finish(mask); +} + +static void usb_flush_all_primed_endpoints() +{ + usb_flush_primed_endpoints(0xFFFFFFFF); +} + +static void usb_endpoint_set_type( + const usb_endpoint_t* const endpoint, + const usb_transfer_type_t transfer_type) +{ + // NOTE: UM10503 section 23.6.24 "Endpoint 1 to 5 control registers" says + // that the disabled side of an endpoint must be set to a non-control type + // (e.g. bulk, interrupt, or iso). + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + USB0_ENDPTCTRL(endpoint_number) = + (USB0_ENDPTCTRL(endpoint_number) & + ~(USB0_ENDPTCTRL_TXT1_0_MASK | USB0_ENDPTCTRL_RXT_MASK)) | + (USB0_ENDPTCTRL_TXT1_0(transfer_type) | + USB0_ENDPTCTRL_RXT(transfer_type)); +} + +static void usb_endpoint_enable(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + USB0_ENDPTCTRL(endpoint_number) |= + (USB0_ENDPTCTRL_TXE | USB0_ENDPTCTRL_TXR); + } else { + USB0_ENDPTCTRL(endpoint_number) |= + (USB0_ENDPTCTRL_RXE | USB0_ENDPTCTRL_RXR); + } +} + +static void usb_endpoint_clear_pending_interrupts(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + usb_clear_pending_interrupts( + USB0_ENDPTCOMPLETE_ETCE(1 << endpoint_number)); + } else { + usb_clear_pending_interrupts( + USB0_ENDPTCOMPLETE_ERCE(1 << endpoint_number)); + } +} + +void usb_endpoint_disable(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_TXE); + } else { + USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_RXE); + } + usb_queue_flush_endpoint(endpoint); + usb_endpoint_clear_pending_interrupts(endpoint); + usb_endpoint_flush(endpoint); +} + +void usb_endpoint_prime( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const first_td) +{ + usb_queue_head_t* const qh = usb_queue_head(endpoint->address); + + qh->next_dtd_pointer = first_td; + qh->total_bytes &= + ~(USB_TD_DTD_TOKEN_STATUS_ACTIVE | USB_TD_DTD_TOKEN_STATUS_HALTED); + + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + USB0_ENDPTPRIME = USB0_ENDPTPRIME_PETB(1 << endpoint_number); + } else { + USB0_ENDPTPRIME = USB0_ENDPTPRIME_PERB(1 << endpoint_number); + } +} + +static bool usb_endpoint_is_priming(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + return USB0_ENDPTPRIME & USB0_ENDPTPRIME_PETB(1 << endpoint_number); + } else { + return USB0_ENDPTPRIME & USB0_ENDPTPRIME_PERB(1 << endpoint_number); + } +} + +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, waiting until the endpoint has finished. +void usb_endpoint_schedule_wait( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const td) +{ + // Ensure that endpoint is ready to be primed. + // It may have been flushed due to an aborted transaction. + // TODO: This should be preceded by a flush? + while (usb_endpoint_is_ready(endpoint)) {} + + td->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + + usb_endpoint_prime(endpoint, td); +} + +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, appending to the end of the endpoint's queue if +// there are pending TDs. Note that this requires that one knows the +// tail of the endpoint's TD queue. Moreover, the user is responsible +// for setting the TERMINATE bit of next_dtd_pointer if needed. +void usb_endpoint_schedule_append( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const tail_td, + usb_transfer_descriptor_t* const new_td) +{ + bool done; + + tail_td->next_dtd_pointer = new_td; + + if (usb_endpoint_is_priming(endpoint)) { + return; + } + + do { + USB0_USBCMD_D |= USB0_USBCMD_D_ATDTW; + done = usb_endpoint_is_ready(endpoint); + } while (!(USB0_USBCMD_D & USB0_USBCMD_D_ATDTW)); + + USB0_USBCMD_D &= ~USB0_USBCMD_D_ATDTW; + if (!done) { + usb_endpoint_prime(endpoint, new_td); + } +} + +void usb_endpoint_flush(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + usb_queue_flush_endpoint(endpoint); + if (usb_endpoint_is_in(endpoint->address)) { + usb_flush_primed_endpoints(USB0_ENDPTFLUSH_FETB(1 << endpoint_number)); + } else { + usb_flush_primed_endpoints(USB0_ENDPTFLUSH_FERB(1 << endpoint_number)); + } +} + +/* +static bool usb_endpoint_is_flushing( + const usb_endpoint_t* const endpoint +) { + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if( usb_endpoint_is_in(endpoint->address) ) { + return USB0_ENDPTFLUSH & USB0_ENDPTFLUSH_FETB(1 << endpoint_number); + } else { + return USB0_ENDPTFLUSH & USB0_ENDPTFLUSH_FERB(1 << endpoint_number); + } +} +*/ +bool usb_endpoint_is_ready(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + return USB0_ENDPTSTAT & USB0_ENDPTSTAT_ETBR(1 << endpoint_number); + } else { + return USB0_ENDPTSTAT & USB0_ENDPTSTAT_ERBR(1 << endpoint_number); + } +} + +bool usb_endpoint_is_complete(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + return USB0_ENDPTCOMPLETE & USB0_ENDPTCOMPLETE_ETCE(1 << endpoint_number); + } else { + return USB0_ENDPTCOMPLETE & USB0_ENDPTCOMPLETE_ERCE(1 << endpoint_number); + } +} + +void usb_endpoint_stall(const usb_endpoint_t* const endpoint) +{ + // Endpoint is to be stalled as a pair -- both OUT and IN. + // See UM10503 section 23.10.5.2 "Stalling" + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + USB0_ENDPTCTRL(endpoint_number) |= (USB0_ENDPTCTRL_RXS | USB0_ENDPTCTRL_TXS); + + // TODO: Also need to reset data toggle in both directions? +} + +void usb_endpoint_reset_data_toggle(const usb_endpoint_t* const endpoint) +{ + const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + if (usb_endpoint_is_in(endpoint->address)) { + USB0_ENDPTCTRL(endpoint_number) |= USB0_ENDPTCTRL_TXR; + } else { + USB0_ENDPTCTRL(endpoint_number) |= USB0_ENDPTCTRL_RXR; + } +} + +static void usb_controller_run() +{ + USB0_USBCMD_D |= USB0_USBCMD_D_RS; +} + +static void usb_controller_stop() +{ + USB0_USBCMD_D &= ~USB0_USBCMD_D_RS; +} + +static uint_fast8_t usb_controller_is_resetting() +{ + return (USB0_USBCMD_D & USB0_USBCMD_D_RST) != 0; +} + +static void usb_controller_set_device_mode() +{ + // Set USB0 peripheral mode + USB0_USBMODE_D = USB0_USBMODE_D_CM1_0(2); + + // Set device-related OTG flags + // OTG termination: controls pull-down on USB_DM + USB0_OTGSC = USB0_OTGSC_OT; +} + +usb_speed_t usb_speed(const usb_device_t* const device) +{ + if (device == usb_device_usb0) { + switch (USB0_PORTSC1_D & USB0_PORTSC1_D_PSPD_MASK) { + case USB0_PORTSC1_D_PSPD(0): + return USB_SPEED_FULL; + + case USB0_PORTSC1_D_PSPD(2): + return USB_SPEED_HIGH; + + default: + // TODO: What to do/return here? Is this even possible? + return USB_SPEED_FULL; + } + } else { + // TODO: This should not be possible with a more class-like + // implementation. + return USB_SPEED_FULL; + } +} + +static void usb_clear_status(const uint32_t status) +{ + USB0_USBSTS_D = status; +} + +static uint32_t usb_get_status() +{ + // Mask status flags with enabled flag interrupts. + const uint32_t status = USB0_USBSTS_D & USB0_USBINTR_D; + + // Clear flags that were just read, leaving alone any flags that + // were just set (after the read). It's important to read and + // reset flags atomically! :-) + usb_clear_status(status); + + return status; +} + +static void usb_clear_endpoint_setup_status(const uint32_t endpoint_setup_status) +{ + USB0_ENDPTSETUPSTAT = endpoint_setup_status; +} + +static uint32_t usb_get_endpoint_setup_status() +{ + return USB0_ENDPTSETUPSTAT; +} + +static void usb_clear_endpoint_complete(const uint32_t endpoint_complete) +{ + USB0_ENDPTCOMPLETE = endpoint_complete; +} + +static uint32_t usb_get_endpoint_complete() +{ + return USB0_ENDPTCOMPLETE; +} + +static void usb_disable_all_endpoints() +{ + // Endpoint 0 is always enabled. TODO: So why set ENDPTCTRL0? + USB0_ENDPTCTRL0 &= ~(USB0_ENDPTCTRL0_RXE | USB0_ENDPTCTRL0_TXE); + USB0_ENDPTCTRL1 &= ~(USB0_ENDPTCTRL1_RXE | USB0_ENDPTCTRL1_TXE); + USB0_ENDPTCTRL2 &= ~(USB0_ENDPTCTRL2_RXE | USB0_ENDPTCTRL2_TXE); + USB0_ENDPTCTRL3 &= ~(USB0_ENDPTCTRL3_RXE | USB0_ENDPTCTRL3_TXE); + USB0_ENDPTCTRL4 &= ~(USB0_ENDPTCTRL4_RXE | USB0_ENDPTCTRL4_TXE); + USB0_ENDPTCTRL5 &= ~(USB0_ENDPTCTRL5_RXE | USB0_ENDPTCTRL5_TXE); +} + +void usb_set_address_immediate( + const usb_device_t* const device, + const uint_fast8_t address) +{ + if (device == usb_device_usb0) { + USB0_DEVICEADDR = USB0_DEVICEADDR_USBADR(address); + } +} + +void usb_set_address_deferred(const usb_device_t* const device, const uint_fast8_t address) +{ + if (device == usb_device_usb0) { + USB0_DEVICEADDR = + USB0_DEVICEADDR_USBADR(address) | USB0_DEVICEADDR_USBADRA; + } +} + +static void usb_reset_all_endpoints() +{ + usb_disable_all_endpoints(); + usb_clear_all_pending_interrupts(); + usb_flush_all_primed_endpoints(); +} + +static void usb_controller_reset() +{ + // TODO: Good to disable some USB interrupts to avoid priming new + // new endpoints before the controller is reset? + usb_reset_all_endpoints(); + usb_controller_stop(); + + // Reset controller. Resets internal pipelines, timers, counters, state + // machines to initial values. Not recommended when device is in attached + // state -- effect on attached host is undefined. Detach first by flushing + // all primed endpoints and stopping controller. + USB0_USBCMD_D = USB0_USBCMD_D_RST; + + while (usb_controller_is_resetting()) {} +} + +static void usb_bus_reset(usb_device_t* const device) +{ + // According to UM10503 v1.4 section 23.10.3 "Bus reset": + usb_reset_all_endpoints(); + usb_set_address_immediate(device, 0); + usb_set_configuration(device, 0); + + // TODO: Enable endpoint 0, which might not actually be necessary, + // as the datasheet claims it can't be disabled. + + //wait_ms(3); + // + //if( USB0_PORTSC1 & USB0_PORTSC1_PR ) { + // // Port still is in the reset state. + //} else { + // usb_hardware_reset(); + //} +} + +static void usb_interrupt_enable(usb_device_t* const device) +{ + if (device == usb_device_usb0) { + nvic_enable_irq(NVIC_USB0_IRQ); + } +} + +void usb_device_init(const uint_fast8_t device_ordinal, usb_device_t* const device) +{ + if (device_ordinal == 0) { + usb_device_usb0 = device; + + usb_phy_enable(); + usb_controller_reset(); + usb_controller_set_device_mode(); + + // Set interrupt threshold interval to 0 + USB0_USBCMD_D &= ~USB0_USBCMD_D_ITC_MASK; + + // Configure endpoint list address + USB0_ENDPOINTLISTADDR = (uint32_t) usb_qh; + + // Enable interrupts + USB0_USBINTR_D = USB0_USBINTR_D_UE | USB0_USBINTR_D_UEE | + USB0_USBINTR_D_PCE | + USB0_USBINTR_D_URE + //| USB0_USBINTR_D_SRE + | USB0_USBINTR_D_SLE + //| USB0_USBINTR_D_NAKE + ; + } +} + +void usb_run(usb_device_t* const device) +{ + usb_interrupt_enable(device); + usb_controller_run(device); +} + +static void copy_setup(usb_setup_t* const dst, const volatile uint8_t* const src) +{ + dst->request_type = src[0]; + dst->request = src[1]; + dst->value_l = src[2]; + dst->value_h = src[3]; + dst->index_l = src[4]; + dst->index_h = src[5]; + dst->length_l = src[6]; + dst->length_h = src[7]; +} + +void usb_endpoint_init(const usb_endpoint_t* const endpoint) +{ + usb_endpoint_flush(endpoint); + + uint_fast16_t max_packet_size = endpoint->device->descriptor[7]; + usb_transfer_type_t transfer_type = USB_TRANSFER_TYPE_CONTROL; + const uint8_t* const endpoint_descriptor = usb_endpoint_descriptor(endpoint); + if (endpoint_descriptor) { + max_packet_size = + usb_endpoint_descriptor_max_packet_size(endpoint_descriptor); + transfer_type = + usb_endpoint_descriptor_transfer_type(endpoint_descriptor); + } + + // TODO: There are more capabilities to adjust based on the endpoint + // descriptor. + usb_queue_head_t* const qh = usb_queue_head(endpoint->address); + qh->capabilities = USB_QH_CAPABILITIES_MULT(0) | USB_QH_CAPABILITIES_ZLT | + USB_QH_CAPABILITIES_MPL(max_packet_size) | + ((transfer_type == USB_TRANSFER_TYPE_CONTROL) ? USB_QH_CAPABILITIES_IOS : + 0); + qh->current_dtd_pointer = 0; + qh->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + qh->total_bytes = USB_TD_DTD_TOKEN_TOTAL_BYTES(0) | USB_TD_DTD_TOKEN_MULTO(0); + qh->buffer_pointer_page[0] = 0; + qh->buffer_pointer_page[1] = 0; + qh->buffer_pointer_page[2] = 0; + qh->buffer_pointer_page[3] = 0; + qh->buffer_pointer_page[4] = 0; + + // This is how we look up an endpoint structure from an endpoint address: + qh->_reserved_0 = (uint32_t) endpoint; + + // TODO: Should NAK be enabled? I'm kinda squishy on this... + //USB0_ENDPTNAKEN |= + // USB0_ENDPTNAKEN_EPRNE(1 << endpoint_out->number); + + usb_endpoint_set_type(endpoint, transfer_type); + + usb_endpoint_enable(endpoint); +} + +static void usb_check_for_setup_events() { + const uint32_t endptsetupstat = usb_get_endpoint_setup_status(); + if (endptsetupstat) { + for (uint_fast8_t i = 0; i < 6; i++) { + const uint32_t endptsetupstat_bit = USB0_ENDPTSETUPSTAT_ENDPTSETUPSTAT(1 << i); + if (endptsetupstat & endptsetupstat_bit) { + usb_endpoint_t* const endpoint = usb_endpoint_from_address(usb_endpoint_address(USB_TRANSFER_DIRECTION_OUT, i)); + if (endpoint && endpoint->setup_complete) { + usb_queue_head_t *head = usb_queue_head(endpoint->address); + + //HALT_UNTIL_DEBUGGING(); + + copy_setup(&endpoint->setup, head->setup); + // TODO: Clean up this duplicated effort by providing + // a cleaner way to get the SETUP data. + copy_setup(&endpoint->in->setup, head->setup); + + usb_clear_endpoint_setup_status(endptsetupstat_bit); + endpoint->setup_complete(endpoint); + } else { + usb_clear_endpoint_setup_status(endptsetupstat_bit); + } + } + } + } +} + +static void usb_check_for_transfer_events() { + const uint32_t endptcomplete = usb_get_endpoint_complete(); + + if (endptcomplete) { + for (uint_fast8_t i = 0; i < 6; i++) { + const uint32_t endptcomplete_out_bit = USB0_ENDPTCOMPLETE_ERCE(1 << i); + + if (endptcomplete & endptcomplete_out_bit) { + usb_clear_endpoint_complete(endptcomplete_out_bit); + usb_endpoint_t* const endpoint = usb_endpoint_from_address(usb_endpoint_address(USB_TRANSFER_DIRECTION_OUT, i)); + if (endpoint && endpoint->transfer_complete) { + endpoint->transfer_complete(endpoint); + } + } + + const uint32_t endptcomplete_in_bit = USB0_ENDPTCOMPLETE_ETCE(1 << i); + + if (endptcomplete & endptcomplete_in_bit) { + usb_clear_endpoint_complete(endptcomplete_in_bit); + usb_endpoint_t* const endpoint = usb_endpoint_from_address(usb_endpoint_address(USB_TRANSFER_DIRECTION_IN, i)); + if (endpoint && endpoint->transfer_complete) { + endpoint->transfer_complete(endpoint); + } + } + } + } +} + +void usb0_isr() +{ + const uint32_t status = usb_get_status(); + + if (status == 0) { + // Nothing to do. + return; + } + + if (status & USB0_USBSTS_D_UI) { + // USB: + // - Completed transaction transfer descriptor has IOC set. + // - Short packet detected. + // - SETUP packet received. + + usb_check_for_setup_events(); + usb_check_for_transfer_events(); + + // TODO: Reset ignored ENDPTSETUPSTAT and ENDPTCOMPLETE flags? + } + + if (status & USB0_USBSTS_D_SRI) { + // Start Of Frame received. + } + + if (status & USB0_USBSTS_D_PCI) { + // Port change detect: + // Port controller entered full- or high-speed operational state. + } + + if (status & USB0_USBSTS_D_SLI) { + // Device controller suspend. + } + + if (status & USB0_USBSTS_D_URI) { + // USB reset received. + usb_bus_reset(usb_device_usb0); + } + + if (status & USB0_USBSTS_D_UEI) { + // USB error: + // Completion of a USB transaction resulted in an error condition. + // Set along with USBINT if the TD on which the error interrupt + // occurred also had its interrupt on complete (IOC) bit set. + // The device controller detects resume signalling only. + } + + if (status & USB0_USBSTS_D_NAKI) { + // Both the TX/RX endpoint NAK bit and corresponding TX/RX endpoint + // NAK enable bit are set. + } +} diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.c b/firmware/baseband/sd_over_usb/usb_descriptor.c index 7f5adc474..fdba7b1dd 100644 --- a/firmware/baseband/sd_over_usb/usb_descriptor.c +++ b/firmware/baseband/sd_over_usb/usb_descriptor.c @@ -85,7 +85,7 @@ uint8_t usb_descriptor_configuration_full_speed[] = { USB_WORD(32), // wTotalLength 0x01, // bNumInterfaces 0x01, // bConfigurationValue - 0x03, // iConfiguration + 0x00, // iConfiguration 0x80, // bmAttributes: USB-powered 250, // bMaxPower: 500mA @@ -94,9 +94,9 @@ uint8_t usb_descriptor_configuration_full_speed[] = { 0x00, // bInterfaceNumber 0x00, // bAlternateSetting 0x02, // bNumEndpoints - 0xFF, // bInterfaceClass: vendor-specific - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol: vendor-specific + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific 0x00, // iInterface 7, // bLength @@ -122,7 +122,7 @@ uint8_t usb_descriptor_configuration_high_speed[] = { USB_WORD(32), // wTotalLength 0x01, // bNumInterfaces 0x01, // bConfigurationValue - 0x03, // iConfiguration + 0x00, // iConfiguration 0x80, // bmAttributes: USB-powered 250, // bMaxPower: 500mA @@ -131,9 +131,9 @@ uint8_t usb_descriptor_configuration_high_speed[] = { 0x00, // bInterfaceNumber 0x00, // bAlternateSetting 0x02, // bNumEndpoints - 0xFF, // bInterfaceClass: vendor-specific - 0xFF, // bInterfaceSubClass - 0xFF, // bInterfaceProtocol: vendor-specific + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific 0x00, // iInterface 7, // bLength diff --git a/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld index 6eb411753..08bcc093b 100755 --- a/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld +++ b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld @@ -25,8 +25,10 @@ MEMORY { flash (rx) : org = 0x00000000, len = 32752 /* Local SRAM @ 0x10080000 */ ram (rwx) : org = 0x10000000, len = 96k /* Local SRAM @ 0x10000000 */ + ram_usb (rwx) : org = 0x20008000, len = 32K } +usb_bulk_buffer = ORIGIN(ram_usb); __ram_start__ = ORIGIN(ram); __ram_size__ = LENGTH(ram); __ram_end__ = __ram_start__ + __ram_size__;