implemented scsi inquiry

This commit is contained in:
Bernd Herzog 2023-03-31 19:18:39 +02:00
parent a6eb430830
commit 776ac652a8
9 changed files with 1070 additions and 75 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -0,0 +1,46 @@
#ifndef __SCSI_H__
#define __SCSI_H__
#include <stddef.h>
#include <common/usb.h>
#include <common/usb_request.h>
#include <common/usb_standard_request.h>
#include "usb_device.h"
#include "usb_endpoint.h"
#include <libopencm3/lpc43xx/m4/nvic.h>
#include <libopencm3/lpc43xx/cgu.h>
#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__ */

View File

@ -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);
}
}
}

View File

@ -33,10 +33,11 @@
#include <libopencm3/lpc43xx/cgu.h>
#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__ */

View File

@ -0,0 +1,676 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* 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 <stdint.h>
#include <stdbool.h>
#include <common/usb.h>
#include "usb_type.h"
#include "usb_queue.h"
#include "usb_standard_request.h"
#include <libopencm3/lpc43xx/creg.h>
#include <libopencm3/lpc43xx/m4/nvic.h>
#include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/usb.h>
#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.
}
}

View File

@ -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

View File

@ -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__;