mirror of
https://github.com/oliverschmidt/contiki.git
synced 2024-08-27 16:28:56 +00:00
532 lines
14 KiB
C
532 lines
14 KiB
C
#include "usb-msc-bulk.h"
|
|
#include <usb-api.h>
|
|
#include <usb-core.h>
|
|
#include <sys/process.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define PRINTF(...)
|
|
#endif
|
|
|
|
static const uint8_t max_lun = 0;
|
|
|
|
static USBBuffer data_usb_buffer[USB_MSC_BUFFERS];
|
|
static unsigned int buffer_lengths[USB_MSC_BUFFERS];
|
|
|
|
static unsigned int buf_first = 0; /* First prepared buffer */
|
|
static unsigned int buf_free = 0; /* First free buffer */
|
|
static unsigned int buf_submitted = 0; /* Oldest submitted buffer */
|
|
|
|
#define USB_BUFFER_ID_UNUSED 0
|
|
#define USB_BUFFER_ID_CBW 1
|
|
#define USB_BUFFER_ID_CSW 2
|
|
#define USB_BUFFER_ID_DATA 3
|
|
#define USB_BUFFER_ID_DISCARD 4
|
|
#define USB_BUFFER_ID_HALT 5
|
|
#define USB_BUFFER_ID_MASK 0x07
|
|
|
|
static struct usb_msc_bulk_cbw cbw_buffer;
|
|
static struct usb_msc_bulk_csw csw_buffer;
|
|
|
|
#define BULK_OUT 0x02
|
|
#define BULK_IN 0x81
|
|
|
|
PROCESS(usb_mass_bulk_process, "USB mass storage bulk only process");
|
|
|
|
static process_event_t reset_event;
|
|
|
|
static struct usb_msc_command_state state;
|
|
|
|
/* Handle wrapping */
|
|
#define PREV_BUF(x) (((x) == 0) ? USB_MSC_BUFFERS - 1 : (x) - 1)
|
|
#define NEXT_BUF(x) (((x) < (USB_MSC_BUFFERS-1)) ? (x) + 1 : 0)
|
|
void
|
|
usb_msc_send_data_buf_flags(const uint8_t *data, unsigned int len,
|
|
unsigned int flags, uint16_t buf_flags)
|
|
{
|
|
USBBuffer *buffer = &data_usb_buffer[buf_free];
|
|
if (buffer->id != USB_BUFFER_ID_UNUSED) {
|
|
printf("Data IN buffer busy\n");
|
|
return;
|
|
}
|
|
buffer->flags = USB_BUFFER_NOTIFY | buf_flags;
|
|
buffer->next = NULL;
|
|
buffer->data = (uint8_t*)data;
|
|
buffer->left = len;
|
|
buffer_lengths[buf_free] = len;
|
|
buffer->id = USB_BUFFER_ID_DATA | flags;
|
|
if (buf_free != buf_first) {
|
|
data_usb_buffer[PREV_BUF(buf_free)].next = buffer;
|
|
}
|
|
state.cmd_data_submitted += len;
|
|
buf_free = NEXT_BUF(buf_free);
|
|
/* PRINTF("usb_msc_send_data: %d\n", len); */
|
|
if (flags & USB_MSC_DATA_SEND) {
|
|
usb_submit_xmit_buffer(BULK_IN, &data_usb_buffer[buf_first]);
|
|
buf_first = buf_free;
|
|
/* PRINTF("usb_msc_send_data: sent\n"); */
|
|
} else if (flags & USB_MSC_DATA_LAST) {
|
|
/* Cancel transmission */
|
|
PRINTF("Send last\n");
|
|
buf_first = buf_free;
|
|
process_poll(&usb_mass_bulk_process);
|
|
}
|
|
}
|
|
|
|
void
|
|
usb_msc_send_data(const uint8_t *data, unsigned int len, unsigned int flags)
|
|
{
|
|
usb_msc_send_data_buf_flags(data, len, flags,0);
|
|
}
|
|
|
|
void
|
|
usb_msc_receive_data_buf_flags(uint8_t *data, unsigned int len,
|
|
unsigned int flags, uint16_t buf_flags)
|
|
{
|
|
USBBuffer *buffer = &data_usb_buffer[buf_free];
|
|
if (buffer->id != USB_BUFFER_ID_UNUSED) {
|
|
printf("Data OUT buffer busy\n");
|
|
return;
|
|
}
|
|
buffer->flags = USB_BUFFER_NOTIFY | buf_flags;
|
|
buffer->next = NULL;
|
|
buffer->data = data;
|
|
buffer->left = len;
|
|
buffer_lengths[buf_free] = len;
|
|
buffer->id = USB_BUFFER_ID_DATA | flags;
|
|
if (buf_free != buf_first) {
|
|
data_usb_buffer[PREV_BUF(buf_free)].next = buffer;
|
|
}
|
|
state.cmd_data_submitted += len;
|
|
buf_free = NEXT_BUF(buf_free);
|
|
if (flags & USB_MSC_DATA_RECEIVE) {
|
|
usb_submit_recv_buffer(BULK_OUT, &data_usb_buffer[buf_first]);
|
|
buf_first = buf_free;
|
|
} else if (flags & USB_MSC_DATA_LAST) {
|
|
usb_discard_all_buffers(BULK_OUT);
|
|
/* Mark the discarded buffers as unused */
|
|
while(buf_submitted != PREV_BUF(buf_free)) {
|
|
data_usb_buffer[buf_submitted].id = USB_BUFFER_ID_UNUSED;
|
|
buf_submitted = NEXT_BUF(buf_submitted);
|
|
}
|
|
buf_first = buf_free;
|
|
process_poll(&usb_mass_bulk_process);
|
|
}
|
|
}
|
|
|
|
void
|
|
usb_msc_receive_data(uint8_t *data, unsigned int len, unsigned int flags)
|
|
{
|
|
usb_msc_receive_data_buf_flags(data,len,flags, 0);
|
|
}
|
|
|
|
static unsigned int
|
|
handle_mass_bulk_requests()
|
|
{
|
|
switch(usb_setup_buffer.bmRequestType) {
|
|
case 0x21: /* interface OUT requests */
|
|
switch(usb_setup_buffer.bRequest) {
|
|
case MASS_BULK_RESET:
|
|
PRINTF("Mass storage reset\n");
|
|
process_post(&usb_mass_bulk_process, reset_event, NULL);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 0xa1: /* interface IN requests */
|
|
switch(usb_setup_buffer.bRequest) {
|
|
case MASS_BULK_GET_MAX_LUN:
|
|
PRINTF("Get LUN\n");
|
|
usb_send_ctrl_response(&max_lun, sizeof(max_lun));
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct USBRequestHandler mass_bulk_request_handler =
|
|
{
|
|
0x21, 0x7f,
|
|
0x00, 0x00,
|
|
handle_mass_bulk_requests
|
|
};
|
|
|
|
static struct USBRequestHandlerHook mass_bulk_request_hook =
|
|
{
|
|
NULL,
|
|
&mass_bulk_request_handler
|
|
};
|
|
|
|
static void
|
|
send_csw(void)
|
|
{
|
|
USBBuffer *buffer = &data_usb_buffer[buf_free];
|
|
if (buffer->id != USB_BUFFER_ID_UNUSED) {
|
|
printf("CSW buffer busy\n");
|
|
return;
|
|
}
|
|
|
|
csw_buffer.dCSWSignature = MASS_BULK_CSW_SIGNATURE;
|
|
csw_buffer.dCSWTag = cbw_buffer.dCBWTag;
|
|
csw_buffer.dCSWDataResidue =
|
|
cbw_buffer.dCBWDataTransferLength - state.cmd_data_submitted;
|
|
csw_buffer.bCSWStatus = state.status;
|
|
|
|
buffer->flags = USB_BUFFER_NOTIFY;
|
|
buffer->next = NULL;
|
|
buffer->data =(uint8_t*)&csw_buffer ;
|
|
buffer->left = sizeof(csw_buffer);
|
|
buffer->id = USB_BUFFER_ID_CSW;
|
|
if (buf_free != buf_first) {
|
|
data_usb_buffer[PREV_BUF(buf_free)].next = buffer;
|
|
}
|
|
buf_free = NEXT_BUF(buf_free);
|
|
usb_submit_xmit_buffer(BULK_IN, &data_usb_buffer[buf_first]);
|
|
buf_first = buf_free;
|
|
|
|
PRINTF("CSW sent: %ld\n", sizeof(csw_buffer));
|
|
}
|
|
|
|
static void
|
|
submit_cbw_buffer(void)
|
|
{
|
|
USBBuffer *buffer = &data_usb_buffer[buf_free];
|
|
if (buffer->id != USB_BUFFER_ID_UNUSED) {
|
|
printf("CBW buffer busy\n");
|
|
return;
|
|
}
|
|
buffer->flags = USB_BUFFER_NOTIFY;
|
|
buffer->next = NULL;
|
|
buffer->data = (uint8_t*)&cbw_buffer;
|
|
buffer->left = sizeof(cbw_buffer);
|
|
buffer->id = USB_BUFFER_ID_CBW;
|
|
if (buf_free != buf_first) {
|
|
data_usb_buffer[PREV_BUF(buf_free)].next = buffer;
|
|
}
|
|
buf_free = NEXT_BUF(buf_free);
|
|
usb_submit_recv_buffer(BULK_OUT, &data_usb_buffer[buf_first]);
|
|
PRINTF("CBW submitted: %d\n", buf_first);
|
|
buf_first = buf_free;
|
|
}
|
|
|
|
static void
|
|
submit_halt(uint8_t addr)
|
|
{
|
|
USBBuffer *buffer = &data_usb_buffer[buf_free];
|
|
if (buffer->id != USB_BUFFER_ID_UNUSED) {
|
|
printf("CBW buffer busy\n");
|
|
return;
|
|
}
|
|
buffer->flags = USB_BUFFER_NOTIFY | USB_BUFFER_HALT;
|
|
buffer->next = NULL;
|
|
buffer->data = NULL;
|
|
buffer->left = 0;
|
|
buffer->id = USB_BUFFER_ID_HALT;
|
|
if (buf_free != buf_first) {
|
|
data_usb_buffer[PREV_BUF(buf_free)].next = buffer;
|
|
}
|
|
buf_free = NEXT_BUF(buf_free);
|
|
if (addr & 0x80) {
|
|
usb_submit_xmit_buffer(addr, &data_usb_buffer[buf_first]);
|
|
} else {
|
|
usb_submit_recv_buffer(addr, &data_usb_buffer[buf_first]);
|
|
}
|
|
PRINTF("HALT submitted %p\n",buffer);
|
|
buf_first = buf_free;
|
|
}
|
|
|
|
static USBBuffer *
|
|
get_next_buffer(uint8_t addr, uint32_t id)
|
|
{
|
|
unsigned int events;
|
|
events = usb_get_ep_events(addr);
|
|
if (events & USB_EP_EVENT_NOTIFICATION) {
|
|
USBBuffer *buffer = &data_usb_buffer[buf_submitted];
|
|
if (!(buffer->flags & USB_BUFFER_SUBMITTED)) {
|
|
#ifdef DEBUG
|
|
if (id != (buffer->id & USB_BUFFER_ID_MASK)) {
|
|
printf("Wrong buffer ID expected %d, got %d\n",
|
|
(int)id, (int)buffer->id);
|
|
}
|
|
#endif
|
|
if ((buffer->id & USB_BUFFER_ID_MASK) == USB_BUFFER_ID_DATA) {
|
|
state.cmd_data_transfered +=
|
|
buffer_lengths[buf_submitted] - buffer->left;
|
|
}
|
|
buffer->id = USB_BUFFER_ID_UNUSED;
|
|
buf_submitted =NEXT_BUF(buf_submitted);
|
|
return buffer;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PROCESS(usb_mass_bulk_request_process, "USB mass storage request process");
|
|
|
|
PROCESS_THREAD(usb_mass_bulk_request_process, ev , data)
|
|
{
|
|
PROCESS_BEGIN();
|
|
reset_state:
|
|
usb_discard_all_buffers(BULK_OUT);
|
|
usb_discard_all_buffers(BULK_IN);
|
|
memset(data_usb_buffer, 0, sizeof(data_usb_buffer));
|
|
buf_first = 0;
|
|
buf_free = 0;
|
|
buf_submitted = 0;
|
|
submit_cbw_buffer();
|
|
receive_cbw_state:
|
|
PRINTF("receive_cbw_state\n");
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer;
|
|
if ((buffer = get_next_buffer(BULK_OUT, USB_BUFFER_ID_CBW))) {
|
|
|
|
/* CBW */
|
|
if (cbw_buffer.dCBWSignature == MASS_BULK_CBW_SIGNATURE) {
|
|
usb_msc_handler_status ret;
|
|
PRINTF("Got CBW seq %d\n",(int)cbw_buffer.dCBWTag);
|
|
state.command = cbw_buffer.CBWCB;
|
|
state.command_length = cbw_buffer.bCBWCBLength;
|
|
state.status = MASS_BULK_CSW_STATUS_FAILED;
|
|
state.data_cb = NULL;
|
|
state.cmd_data_submitted = 0;
|
|
state.cmd_data_transfered = 0;
|
|
ret = usb_msc_handle_command(&state);
|
|
if (ret == USB_MSC_HANDLER_OK) {
|
|
state.status = MASS_BULK_CSW_STATUS_PASSED;
|
|
} else if (ret == USB_MSC_HANDLER_FAILED) {
|
|
state.status = MASS_BULK_CSW_STATUS_FAILED;
|
|
}
|
|
if (ret != USB_MSC_HANDLER_DELAYED
|
|
&& buf_submitted == buf_free) {
|
|
if (cbw_buffer.dCBWDataTransferLength > 0) {
|
|
/* HALT the apropriate endpoint */
|
|
if (cbw_buffer.bmCBWFlags & MASS_BULK_CBW_FLAG_IN) {
|
|
submit_halt(BULK_IN);
|
|
} else {
|
|
submit_halt(BULK_OUT);
|
|
}
|
|
/* Wait for HALT */
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer =
|
|
get_next_buffer(BULK_IN, USB_BUFFER_ID_HALT);
|
|
if (buffer && (buffer->flags & USB_BUFFER_HALT)) break;
|
|
}
|
|
}
|
|
}
|
|
goto send_csw_state;
|
|
}
|
|
if (cbw_buffer.bmCBWFlags & MASS_BULK_CBW_FLAG_IN) {
|
|
goto send_data_state;
|
|
} else {
|
|
goto receive_data_state;
|
|
}
|
|
} else {
|
|
printf("Invalid CBW\n");
|
|
submit_halt(BULK_IN);
|
|
submit_halt(BULK_OUT);
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer = get_next_buffer(BULK_IN, USB_BUFFER_ID_HALT);
|
|
if (buffer && (buffer->flags & USB_BUFFER_HALT)) break;
|
|
}
|
|
}
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer = get_next_buffer(BULK_OUT, USB_BUFFER_ID_HALT);
|
|
if (buffer && (buffer->flags & USB_BUFFER_HALT)) break;
|
|
}
|
|
}
|
|
/* CBW */
|
|
goto receive_cbw_state;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
send_data_state:
|
|
PRINTF("send_data_state\n");
|
|
while(1) {
|
|
uint8_t id = 0;
|
|
/* Wait for any data to be sent */
|
|
while (buf_submitted == buf_free) {
|
|
PRINTF("Wait data\n");
|
|
PROCESS_WAIT_EVENT();
|
|
}
|
|
#if 0
|
|
/* Send CSW early to improve throughput, unless we need to HALT
|
|
the endpoint due to short data */
|
|
if ((data_usb_buffer[PREV_BUF(buf_free)].id & USB_MSC_DATA_LAST)
|
|
&& state.cmd_data_submitted == cbw_buffer.dCBWDataTransferLength) {
|
|
send_csw();
|
|
}
|
|
#endif
|
|
/* Wait until the current buffer is free */
|
|
while (data_usb_buffer[buf_submitted].flags & USB_BUFFER_SUBMITTED) {
|
|
PROCESS_WAIT_EVENT();
|
|
}
|
|
while (!(data_usb_buffer[buf_submitted].flags & USB_BUFFER_SUBMITTED)) {
|
|
id = data_usb_buffer[buf_submitted].id;
|
|
/* PRINTF("id: %02x\n", id); */
|
|
if (id == USB_BUFFER_ID_UNUSED) break;
|
|
state.cmd_data_transfered += buffer_lengths[buf_submitted];
|
|
data_usb_buffer[buf_submitted].id = USB_BUFFER_ID_UNUSED;
|
|
buf_submitted =NEXT_BUF(buf_submitted);
|
|
if (id & USB_MSC_DATA_DO_CALLBACK) {
|
|
if (state.data_cb) {
|
|
state.data_cb(&state);
|
|
}
|
|
}
|
|
|
|
|
|
if (id & USB_MSC_DATA_LAST) {
|
|
break;
|
|
}
|
|
}
|
|
if (id & USB_MSC_DATA_LAST) {
|
|
break;
|
|
}
|
|
}
|
|
if (state.cmd_data_submitted < cbw_buffer.dCBWDataTransferLength) {
|
|
submit_halt(BULK_IN);
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer = get_next_buffer(BULK_IN , USB_BUFFER_ID_HALT);
|
|
if (buffer) {
|
|
if (buffer->flags & USB_BUFFER_HALT) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
goto send_csw_state;
|
|
|
|
receive_data_state:
|
|
PRINTF("receive_data_state\n");
|
|
while(1) {
|
|
uint8_t id = 0;
|
|
/* Wait for any buffers to be submitted */
|
|
while (buf_submitted == buf_free) {
|
|
PROCESS_WAIT_EVENT();
|
|
}
|
|
/* Wait until the current buffer is free */
|
|
while (data_usb_buffer[buf_submitted].flags & USB_BUFFER_SUBMITTED) {
|
|
PROCESS_WAIT_EVENT();
|
|
}
|
|
while (!(data_usb_buffer[buf_submitted].flags & USB_BUFFER_SUBMITTED)) {
|
|
id = data_usb_buffer[buf_submitted].id;
|
|
/* PRINTF("id: %02x\n", id); */
|
|
state.cmd_data_transfered += buffer_lengths[buf_submitted];
|
|
if (id == USB_BUFFER_ID_UNUSED) break;
|
|
data_usb_buffer[buf_submitted].id = USB_BUFFER_ID_UNUSED;
|
|
buf_submitted =NEXT_BUF(buf_submitted);
|
|
if (id & USB_MSC_DATA_DO_CALLBACK) {
|
|
if (state.data_cb) {
|
|
state.data_cb(&state);
|
|
}
|
|
}
|
|
|
|
if (id & USB_MSC_DATA_LAST) {
|
|
break;
|
|
}
|
|
}
|
|
if (id & USB_MSC_DATA_LAST) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (state.cmd_data_submitted < cbw_buffer.dCBWDataTransferLength) {
|
|
submit_halt(BULK_OUT);
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == reset_event) goto reset_state;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer = get_next_buffer(BULK_OUT, USB_BUFFER_ID_HALT);
|
|
if (buffer && (buffer->flags & USB_BUFFER_HALT)) break;
|
|
}
|
|
}
|
|
}
|
|
goto send_csw_state;
|
|
|
|
|
|
send_csw_state:
|
|
PRINTF("send_csw_state\n");
|
|
if (data_usb_buffer[PREV_BUF(buf_free)].id != USB_BUFFER_ID_CSW) {
|
|
send_csw();
|
|
}
|
|
submit_cbw_buffer();
|
|
while(1) {
|
|
if (ev == reset_event) goto reset_state;
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
USBBuffer *buffer;
|
|
if ((buffer = get_next_buffer(BULK_IN, USB_BUFFER_ID_CSW))) {
|
|
goto receive_cbw_state;
|
|
}
|
|
}
|
|
}
|
|
goto receive_cbw_state;
|
|
PROCESS_END();
|
|
}
|
|
|
|
PROCESS_THREAD(usb_mass_bulk_process, ev , data)
|
|
{
|
|
PROCESS_BEGIN();
|
|
reset_event = process_alloc_event();
|
|
usb_msc_command_handler_init();
|
|
usb_setup();
|
|
usb_set_ep_event_process(BULK_IN, &usb_mass_bulk_request_process);
|
|
usb_set_ep_event_process(BULK_OUT, &usb_mass_bulk_request_process);
|
|
usb_set_global_event_process(process_current);
|
|
usb_register_request_handler(&mass_bulk_request_hook);
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if (ev == PROCESS_EVENT_EXIT) break;
|
|
if (ev == PROCESS_EVENT_POLL) {
|
|
unsigned int events = usb_get_global_events();
|
|
if (events) {
|
|
if (events & USB_EVENT_CONFIG) {
|
|
if (usb_get_current_configuration() != 0) {
|
|
PRINTF("Configured\n");
|
|
memset(data_usb_buffer, 0, sizeof(data_usb_buffer));
|
|
usb_setup_bulk_endpoint(BULK_IN);
|
|
usb_setup_bulk_endpoint(BULK_OUT);
|
|
process_start(&usb_mass_bulk_request_process,NULL);
|
|
} else {
|
|
process_exit(&usb_mass_bulk_request_process);
|
|
usb_disable_endpoint(BULK_IN);
|
|
usb_disable_endpoint(BULK_OUT);
|
|
}
|
|
}
|
|
if (events & USB_EVENT_RESET) {
|
|
PRINTF("RESET\n");
|
|
process_exit(&usb_mass_bulk_request_process);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PROCESS_END();
|
|
}
|
|
|
|
void
|
|
usb_msc_bulk_setup()
|
|
{
|
|
process_start(&usb_mass_bulk_process, NULL);
|
|
}
|