mirror of
https://github.com/mist-devel/mist-firmware.git
synced 2026-04-29 05:26:02 +00:00
Add USB storage device support
Activate with the mist.ini setting: usb_storage=1
This commit is contained in:
4
Makefile
4
Makefile
@@ -10,13 +10,13 @@ DUMP = $(BASE)-objdump
|
||||
TODAY = `date +"%m/%d/%y"`
|
||||
|
||||
PRJ = firmware
|
||||
SRC = hw/AT91SAM/Cstartup_SAM7.c hw/AT91SAM/hardware.c hw/AT91SAM/spi.c hw/AT91SAM/mmc.c hw/AT91SAM/cdc_enumerate.c
|
||||
SRC = hw/AT91SAM/Cstartup_SAM7.c hw/AT91SAM/hardware.c hw/AT91SAM/spi.c hw/AT91SAM/mmc.c hw/AT91SAM/at91sam_usb.c hw/AT91SAM/usbdev.c
|
||||
SRC += fdd.c firmware.c fpga.c hdd.c main.c menu.c osd.c state.c syscalls.c user_io.c data_io.c boot.c idxfile.c config.c tos.c ikbd.c xmodem.c ini_parser.c cue_parser.c mist_cfg.c archie.c pcecd.c arc_file.c font.c utils.c
|
||||
SRC += usb/max3421e.c usb/usb.c usb/usbdebug.c usb/hub.c usb/hid.c usb/hidparser.c usb/timer.c usb/asix.c usb/pl2303.c usb/usbrtc.c usb/joymapping.c
|
||||
SRC += fat_compat.c
|
||||
SRC += FatFs/diskio.c FatFs/ff.c FatFs/ffunicode.c
|
||||
# SRC += usb/storage.c
|
||||
SRC += cdc_control.c
|
||||
SRC += cdc_control.c storage_control.c
|
||||
|
||||
OBJ = $(SRC:.c=.o)
|
||||
DEP = $(SRC:.c=.d)
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cdc_enumerate.h"
|
||||
#include "cdc_control.h"
|
||||
#include "usbdev.h"
|
||||
#include "utils.h"
|
||||
#include "user_io.h"
|
||||
#include "tos.h"
|
||||
@@ -18,12 +18,6 @@ static unsigned long flush_timer = 0;
|
||||
|
||||
extern const char version[];
|
||||
|
||||
void cdc_control_open(void) {
|
||||
iprintf("CDC control open\n");
|
||||
|
||||
usb_cdc_open();
|
||||
}
|
||||
|
||||
// send everything in buffer
|
||||
void cdc_control_flush(void) {
|
||||
if(fill) usb_cdc_write(buffer, fill);
|
||||
@@ -62,13 +56,12 @@ void cdc_control_poll(void) {
|
||||
flush_timer = 0;
|
||||
}
|
||||
|
||||
// low level usb handling happens inside usb_cdc_poll
|
||||
if(usb_cdc_poll()) {
|
||||
if(usb_cdc_is_configured()) {
|
||||
uint16_t read, i;
|
||||
char data[AT91C_EP_OUT_SIZE];
|
||||
char data[BULK_OUT_SIZE];
|
||||
|
||||
// check for user input
|
||||
if((read = usb_cdc_read(data, AT91C_EP_OUT_SIZE)) != 0) {
|
||||
if((read = usb_cdc_read(data, BULK_OUT_SIZE)) != 0) {
|
||||
|
||||
switch(tos_get_cdc_control_redirect()) {
|
||||
case CDC_REDIRECT_RS232:
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#define CDC_REDIRECT_PARALLEL 0x04
|
||||
#define CDC_REDIRECT_MIDI 0x05
|
||||
|
||||
void cdc_control_open(void);
|
||||
void cdc_control_poll(void);
|
||||
void cdc_control_tx(char c);
|
||||
void cdc_control_flush(void);
|
||||
|
||||
260
hw/AT91SAM/at91sam_usb.c
Normal file
260
hw/AT91SAM/at91sam_usb.c
Normal file
@@ -0,0 +1,260 @@
|
||||
//*----------------------------------------------------------------------------
|
||||
//* ATMEL Microcontroller Software Support - ROUSSET -
|
||||
//*----------------------------------------------------------------------------
|
||||
//* The software is delivered "AS IS" without warranty or condition of any
|
||||
//* kind, either express, implied or statutory. This includes without
|
||||
//* limitation any warranty or condition with respect to merchantability or
|
||||
//* fitness for any particular purpose, or against the infringements of
|
||||
//* intellectual property rights of others.
|
||||
//*----------------------------------------------------------------------------
|
||||
//* File Name : cdc_enumerate.c
|
||||
//* Object : Handle CDC enumeration
|
||||
//*
|
||||
//* 1.0 Apr 20 200 : ODi Creation
|
||||
//* 1.1 14/Sep/2004 JPP : Minor change
|
||||
//* 1.1 15/12/2004 JPP : suppress warning
|
||||
//*----------------------------------------------------------------------------
|
||||
|
||||
// 12. Apr. 2006: added modification found in the mikrocontroller.net gcc-Forum
|
||||
// additional line marked with /* +++ */
|
||||
// 1. Sept. 2006: fixed case: board.h -> Board.h
|
||||
|
||||
#include "AT91SAM7S256.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "at91sam_usb.h"
|
||||
#include "hardware.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
#define WORD(a) (a)&0xff, ((a)>>8)&0xff
|
||||
|
||||
// Private members
|
||||
static unsigned int currentRcvBank;
|
||||
static AT91F_USB_Enumerate stp_callback;
|
||||
|
||||
static void usb_irq_handler(void) {
|
||||
AT91_REG isr = AT91C_BASE_UDP->UDP_ISR & AT91C_BASE_UDP->UDP_IMR;
|
||||
|
||||
// tx_hex("i: ", isr);
|
||||
|
||||
// handle all known interrupt sources
|
||||
if(isr & AT91C_UDP_ENDBUSRES) {
|
||||
AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_ENDBUSRES;
|
||||
|
||||
// tx_str("USB bus reset\r\n");
|
||||
|
||||
// reset all endpoints
|
||||
AT91C_BASE_UDP->UDP_RSTEP = (unsigned int)-1;
|
||||
AT91C_BASE_UDP->UDP_RSTEP = 0;
|
||||
// Enable the function
|
||||
AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN;
|
||||
// Configure endpoint 0
|
||||
AT91C_BASE_UDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);
|
||||
|
||||
// enable ep0 interrupt
|
||||
AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT0;
|
||||
}
|
||||
|
||||
// data received for endpoint 0
|
||||
if(isr & AT91C_UDP_EPINT0) {
|
||||
AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_EPINT0;
|
||||
(*stp_callback)();
|
||||
}
|
||||
|
||||
// clear all remaining irqs
|
||||
AT91C_BASE_UDP->UDP_ICR = AT91C_BASE_UDP->UDP_ISR;
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_Open
|
||||
//* \brief
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_Open(AT91F_USB_Enumerate setup_callback) {
|
||||
// Set the PLL USB Divider
|
||||
AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ;
|
||||
|
||||
// Specific Chip USB Initialisation
|
||||
// Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock
|
||||
AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP;
|
||||
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP);
|
||||
|
||||
// Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO
|
||||
// Set in PIO mode and Configure in Output with pullup
|
||||
AT91C_BASE_PIOA->PIO_PER = USB_PUP;
|
||||
AT91C_BASE_PIOA->PIO_OER = USB_PUP;
|
||||
AT91C_BASE_PIOA->PIO_CODR = USB_PUP;
|
||||
|
||||
currentRcvBank = AT91C_UDP_RX_DATA_BK0;
|
||||
|
||||
/* Enable usb_interrupt */
|
||||
AT91C_AIC_SMR[AT91C_ID_UDP] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | 4;
|
||||
AT91C_AIC_SVR[AT91C_ID_UDP] = (unsigned int)usb_irq_handler;
|
||||
*AT91C_AIC_IECR = (1 << AT91C_ID_UDP);
|
||||
stp_callback = setup_callback;
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_Is_Configured
|
||||
//* \brief Test if the device is configured
|
||||
//*----------------------------------------------------------------------------
|
||||
uint8_t AT91F_USB_Is_Configured(void) {
|
||||
return AT91C_BASE_UDP->UDP_GLBSTATE & AT91C_UDP_CONFG;
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_Read
|
||||
//* \brief Read available data from Endpoint OUT
|
||||
//*----------------------------------------------------------------------------
|
||||
uint16_t AT91F_USB_Read(char *pData, uint16_t length) {
|
||||
uint16_t packetSize, nbBytesRcv = 0;
|
||||
|
||||
if ( !AT91F_USB_Is_Configured() )
|
||||
return 0;
|
||||
|
||||
if ( AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] & currentRcvBank ) {
|
||||
packetSize = MIN(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] >> 16, length);
|
||||
|
||||
while(packetSize--)
|
||||
pData[nbBytesRcv++] = AT91C_BASE_UDP->UDP_FDR[AT91C_EP_OUT];
|
||||
|
||||
AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] &= ~(currentRcvBank);
|
||||
|
||||
// toggle banks
|
||||
if(currentRcvBank == AT91C_UDP_RX_DATA_BK0)
|
||||
currentRcvBank = AT91C_UDP_RX_DATA_BK1;
|
||||
else
|
||||
currentRcvBank = AT91C_UDP_RX_DATA_BK0;
|
||||
}
|
||||
|
||||
return nbBytesRcv;
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_Write
|
||||
//* \brief Send through endpoint 2
|
||||
//*----------------------------------------------------------------------------
|
||||
|
||||
static void wait4tx(char ep) {
|
||||
long to = GetTimer(2); // wait max 2ms for tx to succeed
|
||||
|
||||
// wait for host to acknowledge data reception
|
||||
while ( !(AT91C_BASE_UDP->UDP_CSR[ep] & AT91C_UDP_TXCOMP) )
|
||||
if(CheckTimer(to)) return;
|
||||
|
||||
// clear flag (clear irq)
|
||||
AT91C_BASE_UDP->UDP_CSR[ep] &= ~AT91C_UDP_TXCOMP;
|
||||
|
||||
// wait for register clear to succeed
|
||||
while (AT91C_BASE_UDP->UDP_CSR[ep] & AT91C_UDP_TXCOMP);
|
||||
}
|
||||
|
||||
// copy bytes to the endpoint fifo and start transmission
|
||||
static uint16_t ep_tx(char ep, const char *pData, uint16_t length) {
|
||||
uint16_t b2c = MIN(length, ep?AT91C_EP_IN_SIZE:8);
|
||||
uint16_t retval = b2c;
|
||||
|
||||
// copy all bytes into send buffer
|
||||
while (b2c--) AT91C_BASE_UDP->UDP_FDR[ep] = *pData++;
|
||||
AT91C_BASE_UDP->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
uint16_t AT91F_USB_Write(const char *pData, uint16_t length) {
|
||||
|
||||
if ( !AT91F_USB_Is_Configured() )
|
||||
return length;
|
||||
|
||||
while(length) {
|
||||
uint16_t sent = ep_tx(AT91C_EP_IN, pData, length);
|
||||
length -= sent;
|
||||
pData += sent;
|
||||
wait4tx(AT91C_EP_IN);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_SendData
|
||||
//* \brief Send Data through the control endpoint
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_SendData(const char *pData, uint16_t length) {
|
||||
AT91_REG csr;
|
||||
|
||||
do {
|
||||
long to = GetTimer(2); // wait max 2ms for tx to succeed
|
||||
|
||||
uint32_t sent = ep_tx(0, pData, length);
|
||||
length -= sent;
|
||||
pData += sent;
|
||||
|
||||
// wait for transmission with timeout
|
||||
do {
|
||||
csr = AT91C_BASE_UDP->UDP_CSR[0];
|
||||
|
||||
// Data IN stage has been stopped by a status OUT
|
||||
if (csr & AT91C_UDP_RX_DATA_BK0) {
|
||||
AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0);
|
||||
return;
|
||||
}
|
||||
} while (( !(csr & AT91C_UDP_TXCOMP) ) && !CheckTimer(to));
|
||||
|
||||
// clear flag (clear irq)
|
||||
AT91C_BASE_UDP->UDP_CSR[0] &= ~AT91C_UDP_TXCOMP;
|
||||
|
||||
// wait for register clear to succeed
|
||||
while (AT91C_BASE_UDP->UDP_CSR[0] & AT91C_UDP_TXCOMP);
|
||||
|
||||
} while (length);
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_SendStr
|
||||
//* \brief Send a string through the control endpoint
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_SendStr(const char *str, uint32_t max) {
|
||||
uint8_t len = 2*strlen(str)+2;
|
||||
char cmd[len], *p;
|
||||
|
||||
cmd[0] = len;
|
||||
cmd[1] = 0x03;
|
||||
|
||||
p = cmd+2;
|
||||
while(*str) {
|
||||
*p++ = *str++;
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
AT91F_USB_SendData(cmd, MIN(len, max));
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_SendWord
|
||||
//* \brief Send a 16 bit word through the control endpoint
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_SendWord(uint16_t data) {
|
||||
AT91F_USB_SendData((char *) &data, sizeof(data));
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_SendZlp
|
||||
//* \brief Send zero length packet through the control endpoint
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_SendZlp() {
|
||||
AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY;
|
||||
wait4tx(0);
|
||||
}
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* \fn AT91F_USB_SendStall
|
||||
//* \brief Stall the control endpoint
|
||||
//*----------------------------------------------------------------------------
|
||||
void AT91F_USB_SendStall() {
|
||||
AT91C_BASE_UDP->UDP_CSR[0] |= AT91C_UDP_FORCESTALL;
|
||||
while ( !(AT91C_BASE_UDP->UDP_CSR[0] & AT91C_UDP_ISOERROR) );
|
||||
AT91C_BASE_UDP->UDP_CSR[0] &= ~(AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR);
|
||||
while (AT91C_BASE_UDP->UDP_CSR[0] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR));
|
||||
}
|
||||
@@ -1,34 +1,38 @@
|
||||
//*----------------------------------------------------------------------------
|
||||
//* ATMEL Microcontroller Software Support - ROUSSET -
|
||||
//*----------------------------------------------------------------------------
|
||||
//* The software is delivered "AS IS" without warranty or condition of any
|
||||
//* kind, either express, implied or statutory. This includes without
|
||||
//* limitation any warranty or condition with respect to merchantability or
|
||||
//* fitness for any particular purpose, or against the infringements of
|
||||
//* intellectual property rights of others.
|
||||
//*----------------------------------------------------------------------------
|
||||
//* File Name : cdc_enumerate.h
|
||||
//* Object : Handle CDC enumeration
|
||||
//*
|
||||
//* 1.0 Apr 20 200 : ODi Creation
|
||||
//*----------------------------------------------------------------------------
|
||||
#ifndef CDC_ENUMERATE_H
|
||||
#define CDC_ENUMERATE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define AT91C_EP_OUT_SIZE 0x40
|
||||
#define AT91C_EP_OUT 1
|
||||
|
||||
#define AT91C_EP_IN_SIZE 0x40
|
||||
#define AT91C_EP_IN 2
|
||||
|
||||
void usb_cdc_open(void);
|
||||
unsigned char usb_cdc_is_configured(void);
|
||||
unsigned int usb_cdc_write(const char *pData, unsigned int length);
|
||||
uint16_t usb_cdc_read(char *pData, uint16_t length);
|
||||
|
||||
#define usb_cdc_poll() usb_cdc_is_configured()
|
||||
|
||||
#endif // CDC_ENUMERATE_H
|
||||
|
||||
//*----------------------------------------------------------------------------
|
||||
//* ATMEL Microcontroller Software Support - ROUSSET -
|
||||
//*----------------------------------------------------------------------------
|
||||
//* The software is delivered "AS IS" without warranty or condition of any
|
||||
//* kind, either express, implied or statutory. This includes without
|
||||
//* limitation any warranty or condition with respect to merchantability or
|
||||
//* fitness for any particular purpose, or against the infringements of
|
||||
//* intellectual property rights of others.
|
||||
//*----------------------------------------------------------------------------
|
||||
//* File Name : cdc_enumerate.h
|
||||
//* Object : Handle CDC enumeration
|
||||
//*
|
||||
//* 1.0 Apr 20 200 : ODi Creation
|
||||
//*----------------------------------------------------------------------------
|
||||
#ifndef AT91SAM_USB_H
|
||||
#define AT91SAM_USB_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define AT91C_EP_OUT_SIZE 0x40
|
||||
#define AT91C_EP_OUT 1
|
||||
|
||||
#define AT91C_EP_IN_SIZE 0x40
|
||||
#define AT91C_EP_IN 2
|
||||
|
||||
typedef void (*AT91F_USB_Enumerate)(void);
|
||||
|
||||
void AT91F_USB_Open(AT91F_USB_Enumerate setup_callback);
|
||||
uint8_t AT91F_USB_Is_Configured(void);
|
||||
uint16_t AT91F_USB_Read(char *pData, uint16_t length);
|
||||
uint16_t AT91F_USB_Write(const char *pData, uint16_t length);
|
||||
void AT91F_USB_SendData(const char *pData, uint16_t length);
|
||||
void AT91F_USB_SendStr(const char *str, uint32_t max);
|
||||
void AT91F_USB_SendWord(uint16_t data);
|
||||
void AT91F_USB_SendZlp();
|
||||
void AT91F_USB_SendStall();
|
||||
|
||||
#endif // AT91SAM_USB_H
|
||||
@@ -299,7 +299,7 @@ RAMFUNC static unsigned char MMC_ReceiveDataBlock(unsigned char *pReadBuffer)
|
||||
{
|
||||
if (timeout++ >= 1000000) // we can't wait forever
|
||||
{
|
||||
// iprintf("CMD17 (READ_BLOCK): no data token! (lba=%lu)\r", lba);
|
||||
iprintf("CMD17/18 (READ_BLOCK): no data token!\r");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
@@ -339,7 +339,7 @@ RAMFUNC unsigned char MMC_Read(unsigned long lba, unsigned char *pReadBuffer)
|
||||
|
||||
if (MMC_Command(CMD17, lba))
|
||||
{
|
||||
// iprintf("CMD17 (READ_BLOCK): invalid response 0x%02X (lba=%lu)\r", response, lba);
|
||||
iprintf("CMD17 (READ_BLOCK): invalid response 0x%02X (lba=%lu)\r", response, lba);
|
||||
DisableCard();
|
||||
return(0);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
37
hw/AT91SAM/usbdev.h
Normal file
37
hw/AT91SAM/usbdev.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
This file is part of MiST-firmware
|
||||
|
||||
MiST-firmware 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MiST-firmware 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef USBDEV_H
|
||||
#define USBDEV_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "at91sam_usb.h"
|
||||
|
||||
#define BULK_IN_SIZE AT91C_EP_IN_SIZE
|
||||
#define BULK_OUT_SIZE AT91C_EP_OUT_SIZE
|
||||
|
||||
void usb_dev_open(void);
|
||||
|
||||
uint8_t usb_cdc_is_configured(void);
|
||||
uint16_t usb_cdc_write(const char *pData, uint16_t length);
|
||||
uint16_t usb_cdc_read(char *pData, uint16_t length);
|
||||
|
||||
uint8_t usb_storage_is_configured(void);
|
||||
uint16_t usb_storage_write(const char *pData, uint16_t length);
|
||||
uint16_t usb_storage_read(char *pData, uint16_t length);
|
||||
|
||||
#endif // USBDEV_H
|
||||
8
main.c
8
main.c
@@ -53,11 +53,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "arc_file.h"
|
||||
#include "font.h"
|
||||
#include "tos.h"
|
||||
#include "cdc_control.h"
|
||||
#include "usb.h"
|
||||
#include "debug.h"
|
||||
#include "mist_cfg.h"
|
||||
#include "cdc_enumerate.h"
|
||||
#include "usbdev.h"
|
||||
#include "cdc_control.h"
|
||||
#include "storage_control.h"
|
||||
|
||||
#ifndef _WANT_IO_LONG_LONG
|
||||
#error "newlib lacks support of long long type in IO functions. Please use a toolchain that was compiled with option --enable-newlib-io-long-long."
|
||||
@@ -185,10 +186,11 @@ int main(void)
|
||||
fpga_init(s);
|
||||
}
|
||||
|
||||
cdc_control_open();
|
||||
usb_dev_open();
|
||||
|
||||
while (1) {
|
||||
cdc_control_poll();
|
||||
storage_control_poll();
|
||||
|
||||
user_io_poll();
|
||||
|
||||
|
||||
1
mist.ini
1
mist.ini
@@ -8,6 +8,7 @@ mouse_speed=100 ; set to scale mouse speed (in percentage, defaul
|
||||
joystick_emu_fixed_index=0 ; set to 1 for always emulating the first two joystick via keyboard
|
||||
joystick_remap=0583,2060,1,2,4,8,10,20,20,8,400,800,40,80
|
||||
key_menu_as_rgui=0 ; set to 1 to make the MENU key map to RGUI in Minimig (e.g. for Right Amiga)
|
||||
usb_storage=0 ; set to 1 to allow accessing the SD Card via the USB port
|
||||
|
||||
[minimig_config]
|
||||
;conf_default="68020 AGA"
|
||||
|
||||
@@ -109,6 +109,7 @@ const ini_var_t mist_ini_vars[] = {
|
||||
#endif
|
||||
{"ROM", (void*)ini_rom_upload, CUSTOM_HANDLER, 0, 0, 1},
|
||||
{"AMIGA_MOD_KEYS", (void*)(&(mist_cfg.amiga_mod_keys)), UINT8, 0, 1, 1},
|
||||
{"USB_STORAGE", (void*)(&(mist_cfg.usb_storage)), UINT8, 0, 1, 1},
|
||||
// [MINIMIG_CONFIG]
|
||||
{"KICK1X_MEMORY_DETECTION_PATCH", (void*)(&(minimig_cfg.kick1x_memory_detection_patch)), UINT8, 0, 1, 2},
|
||||
{"CLOCK_FREQ", (void*)(&(minimig_cfg.clock_freq)), UINT8, 0, 2, 2},
|
||||
|
||||
@@ -32,6 +32,7 @@ typedef struct {
|
||||
uint8_t led_animation;
|
||||
uint8_t sdram64;
|
||||
uint8_t amiga_mod_keys;
|
||||
uint8_t usb_storage;
|
||||
} mist_cfg_t;
|
||||
|
||||
|
||||
|
||||
296
storage_control.c
Normal file
296
storage_control.c
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
storage_control.c
|
||||
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "swab.h"
|
||||
#include "utils.h"
|
||||
#include "storage_control.h"
|
||||
#include "fat_compat.h"
|
||||
#include "usbdev.h"
|
||||
#include "mmc.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define SENSEKEY_NO_SENSE 0x0
|
||||
#define SENSEKEY_NOT_READY 0x2
|
||||
#define SENSEKEY_MEDIUM_ERROR 0x3
|
||||
#define SENSEKEY_HARDWARE_ERROR 0x4
|
||||
#define SENSEKEY_ILLEGAL_REQUEST 0x5
|
||||
#define SENSEKEY_UNIT_ATTENTION 0x6
|
||||
#define SENSEKEY_ABORTED_COMMAND 0xB
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t key;
|
||||
uint8_t asc;
|
||||
uint8_t ascq;
|
||||
} sense_t;
|
||||
|
||||
static sense_t sense;
|
||||
|
||||
typedef struct {
|
||||
uint32_t dCBWSignature;
|
||||
uint32_t dCBWTag;
|
||||
uint32_t dCBWDataTransferLength;
|
||||
uint8_t bmCBWFlags;
|
||||
uint8_t bCBWLUN;
|
||||
uint8_t bCBWCLength;
|
||||
uint8_t CBWCB[16];
|
||||
} __attribute__ ((packed)) CBW_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t dCSWSignature;
|
||||
uint32_t dCSWTag;
|
||||
uint32_t dCSWDataResidue;
|
||||
uint8_t bCSWStatus;
|
||||
} __attribute__ ((packed)) CSW_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t DeviceType : 5;
|
||||
uint8_t DeviceTypeQualifier : 3;
|
||||
uint8_t DeviceTypeModifier : 7;
|
||||
uint8_t RemovableMedia : 1;
|
||||
uint8_t Versions;
|
||||
uint8_t ResponseDataFormat : 4;
|
||||
uint8_t HiSupport : 1;
|
||||
uint8_t NormACA : 1;
|
||||
uint8_t ReservedBit : 1;
|
||||
uint8_t AERC : 1;
|
||||
uint8_t AdditionalLength;
|
||||
uint8_t Reserved[2];
|
||||
uint8_t SoftReset : 1;
|
||||
uint8_t CommandQueue : 1;
|
||||
uint8_t Reserved2 : 1;
|
||||
uint8_t LinkedCommands : 1;
|
||||
uint8_t Synchronous : 1;
|
||||
uint8_t Wide16Bit : 1;
|
||||
uint8_t Wide32Bit : 1;
|
||||
uint8_t RelativeAddressing : 1;
|
||||
uint8_t VendorId[8];
|
||||
uint8_t ProductId[16];
|
||||
uint8_t ProductRevisionLevel[4];
|
||||
uint8_t VendorSpecific[20];
|
||||
uint8_t Reserved3[2];
|
||||
uint8_t VersionDescriptors[8];
|
||||
uint8_t Reserved4[30];
|
||||
} __attribute__ ((packed)) INQUIRYDATA_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t ErrorCode :7;
|
||||
uint8_t Valid :1;
|
||||
uint8_t SegmentNumber;
|
||||
uint8_t SenseKey :4;
|
||||
uint8_t Reserved :1;
|
||||
uint8_t IncorrectLength :1;
|
||||
uint8_t EndOfMedia :1;
|
||||
uint8_t FileMark :1;
|
||||
uint8_t Information[4];
|
||||
uint8_t AdditionalSenseLength;
|
||||
uint8_t CommandSpecificInformation[4];
|
||||
uint8_t AdditionalSenseCode;
|
||||
uint8_t AdditionalSenseCodeQualifier;
|
||||
uint8_t FieldReplaceableUnitCode;
|
||||
uint8_t SenseKeySpecific[3];
|
||||
} __attribute__ ((packed)) SENSEDATA_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t LBA;
|
||||
uint32_t blocklen;
|
||||
} __attribute__ ((packed)) CAPACITYDATA_t;
|
||||
|
||||
|
||||
static void clear_sense() {
|
||||
sense.key = sense.asc = sense.ascq = 0;
|
||||
}
|
||||
|
||||
static void make_sense(uint8_t key, uint8_t asc, uint8_t ascq) {
|
||||
sense.key = key;
|
||||
sense.asc = asc;
|
||||
sense.ascq = ascq;
|
||||
}
|
||||
|
||||
static void scsi_inquiry(uint8_t *cmd) {
|
||||
uint16_t len = cmd[3]<<8 | cmd[4];
|
||||
INQUIRYDATA_t *data = (INQUIRYDATA_t*)sector_buffer;
|
||||
memset(data, 0, sizeof(INQUIRYDATA_t));
|
||||
data->RemovableMedia = 1;
|
||||
data->ResponseDataFormat = 2;
|
||||
data->AdditionalLength = 0x1f;
|
||||
memcpy(data->VendorId, "Lotharek", 8);
|
||||
memcpy(data->ProductId, "MiST Board ", 16);
|
||||
memcpy(data->ProductRevisionLevel, "1.3 ", 4);
|
||||
usb_storage_write(sector_buffer, MIN(len, sizeof(INQUIRYDATA_t)));
|
||||
}
|
||||
|
||||
static void scsi_readcapacity(uint8_t *cmd) {
|
||||
CAPACITYDATA_t cap;
|
||||
cap.LBA = swab32(MMC_GetCapacity()-1);
|
||||
cap.blocklen = swab32(512);
|
||||
usb_storage_write((const char*) &cap, sizeof(CAPACITYDATA_t));
|
||||
}
|
||||
|
||||
static void scsi_request_sense(uint8_t *cmd) {
|
||||
uint8_t len = cmd[4];
|
||||
SENSEDATA_t dat;
|
||||
|
||||
memset(&dat, 0, sizeof(SENSEDATA_t));
|
||||
dat.ErrorCode = 0x70;
|
||||
dat.Valid = 1;
|
||||
dat.SenseKey = sense.key;
|
||||
dat.AdditionalSenseLength = sizeof(SENSEDATA_t)-7;
|
||||
dat.AdditionalSenseCode = sense.asc;
|
||||
dat.AdditionalSenseCodeQualifier = sense.ascq;
|
||||
usb_storage_write((const char*) &dat, MIN(len, sizeof(SENSEDATA_t)));
|
||||
}
|
||||
|
||||
static void scsi_mode_sense(uint8_t *cmd) {
|
||||
hexdump(cmd, 6, 0);
|
||||
// TODO: more complete implementation
|
||||
uint8_t len = cmd[4];
|
||||
memset(sector_buffer, 0, len);
|
||||
usb_storage_write(sector_buffer, len);
|
||||
}
|
||||
|
||||
static uint8_t scsi_read(uint8_t *cmd) {
|
||||
uint32_t lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
|
||||
uint16_t len = cmd[7]<<8 | cmd[8];
|
||||
storage_debugf("Read lba=%d len=%d", lba, len);
|
||||
while (len) {
|
||||
uint8_t ret;
|
||||
uint16_t read = len>(SECTOR_BUFFER_SIZE/512) ? (SECTOR_BUFFER_SIZE/512) : len;
|
||||
DISKLED_ON
|
||||
if (len == 1) ret=MMC_Read(lba, sector_buffer);
|
||||
else ret=MMC_ReadMultiple(lba, sector_buffer, read);
|
||||
DISKLED_OFF
|
||||
if (!ret) {
|
||||
iprintf("STORAGE: Error reading from MMC (lba=%d, len=%d)\n", lba, len);
|
||||
return 0;
|
||||
}
|
||||
lba+=read;
|
||||
len-=read;
|
||||
usb_storage_write(sector_buffer, read*512);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t scsi_write(uint8_t *cmd) {
|
||||
uint32_t lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5];
|
||||
uint16_t len = cmd[7]<<8 | cmd[8];
|
||||
storage_debugf("Write lba=%d len=%d", lba, len);
|
||||
while (len) {
|
||||
uint16_t write = len>(SECTOR_BUFFER_SIZE/512) ? (SECTOR_BUFFER_SIZE/512) : len;
|
||||
uint16_t read, total_read = write*512;
|
||||
uint8_t *buf = sector_buffer;
|
||||
long to = GetTimer(100); // wait max 100ms for host
|
||||
while (total_read) {
|
||||
if (CheckTimer(to)) {
|
||||
iprintf("STORAGE: Timeout while waiting for USB host during write (lba=%d, len=%d)\n", lba, len);
|
||||
return 0;
|
||||
}
|
||||
read = usb_storage_read(buf, total_read);
|
||||
total_read -= read;
|
||||
buf += read;
|
||||
}
|
||||
//hexdump(sector_buffer, write*512, 0);
|
||||
DISKLED_ON
|
||||
if (write == 1) MMC_Write(lba, sector_buffer);
|
||||
else MMC_WriteMultiple(lba, sector_buffer, write);
|
||||
DISKLED_OFF
|
||||
lba+=write;
|
||||
len-=write;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void storage_control_send_csw(uint32_t tag, uint8_t status) {
|
||||
CSW_t* csw = (CSW_t*)sector_buffer;
|
||||
csw->dCSWSignature = 0x53425355;
|
||||
csw->dCSWTag = tag;
|
||||
csw->dCSWDataResidue = 0;
|
||||
csw->bCSWStatus = status;
|
||||
usb_storage_write(sector_buffer, sizeof(CSW_t));
|
||||
}
|
||||
|
||||
void storage_control_poll(void) {
|
||||
uint16_t read;
|
||||
uint32_t tag;
|
||||
uint8_t ret;
|
||||
|
||||
if (!usb_storage_is_configured()) return;
|
||||
|
||||
// read CSW
|
||||
if((read = usb_storage_read(sector_buffer, BULK_OUT_SIZE)) != 0) {
|
||||
CBW_t *cbw = (CBW_t*)sector_buffer;
|
||||
if (read != 31 || cbw->dCBWSignature != 0x43425355) {
|
||||
return;
|
||||
}
|
||||
tag = cbw->dCBWTag;
|
||||
//hexdump(sector_buffer, read, 0);
|
||||
//iprintf("\n");
|
||||
switch (cbw->CBWCB[0]) {
|
||||
case 0x00:
|
||||
storage_debugf("Test Unit Ready");
|
||||
clear_sense();
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x03:
|
||||
storage_debugf("Request sense");
|
||||
scsi_request_sense(cbw->CBWCB);
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x12:
|
||||
storage_debugf("Inquiry");
|
||||
clear_sense();
|
||||
scsi_inquiry(cbw->CBWCB);
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x1A:
|
||||
storage_debugf("Mode Sense");
|
||||
clear_sense();
|
||||
scsi_mode_sense(cbw->CBWCB);
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x1E:
|
||||
storage_debugf("Prevent Removal");
|
||||
clear_sense();
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x25:
|
||||
storage_debugf("Read Capacity");
|
||||
clear_sense();
|
||||
scsi_readcapacity(cbw->CBWCB);
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
case 0x28:
|
||||
ret = scsi_read(cbw->CBWCB);
|
||||
if (ret)
|
||||
clear_sense();
|
||||
else
|
||||
make_sense(SENSEKEY_MEDIUM_ERROR, 0x11, 0x00);
|
||||
storage_control_send_csw(tag, !ret);
|
||||
break;
|
||||
case 0x2A:
|
||||
ret = scsi_write(cbw->CBWCB);
|
||||
if (ret)
|
||||
clear_sense();
|
||||
else
|
||||
make_sense(SENSEKEY_MEDIUM_ERROR, 0x03, 0x00);
|
||||
storage_control_send_csw(tag, !ret);
|
||||
break;
|
||||
case 0x1B:
|
||||
storage_debugf("Start stop unit");
|
||||
clear_sense();
|
||||
storage_control_send_csw(tag, 0);
|
||||
break;
|
||||
default:
|
||||
iprintf("STORAGE: Unhandled cmd: %02x", cbw->CBWCB[0]);
|
||||
make_sense(SENSEKEY_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
storage_control_send_csw(tag, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
storage_control.h
Normal file
6
storage_control.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef STORAGE_CONTROL_H
|
||||
#define STORAGE_CONTROL_H
|
||||
|
||||
void storage_control_poll(void);
|
||||
|
||||
#endif
|
||||
4
utils.h
4
utils.h
@@ -1,6 +1,10 @@
|
||||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
unsigned char decval(unsigned char in, unsigned char min, unsigned char max);
|
||||
unsigned char incval(unsigned char in, unsigned char min, unsigned char max);
|
||||
unsigned char bin2bcd(unsigned char in);
|
||||
|
||||
Reference in New Issue
Block a user