Additions and fixes to CLCD.

- Fixed issue where temp updates stops updating in status screen.
    - Added feedrate controls.
    - Added tune print menu.
    - Added ability to extrude/retract from touch UI
    - Added support for M300 gcode.
    - Fixed glitches with the sound code.
    - Added USB drive code.
        - Note: This is pending the relicensing of the USB code from "GPL v2" to "GPL v2 and later"
                which the authors have agreed upon.
    - Added ability to browse directory hierarchy.
master
Marcio Teixeira 6 years ago
parent 6f8605bc63
commit b110fd01e7

@ -715,6 +715,7 @@ namespace FTDI {
enum note_t {
END_SONG = 0x00,
REST = 0x00,
NOTE_C3 = 0x32,
NOTE_D3 = 0x33,
NOTE_D3S = 0x34,
@ -733,7 +734,8 @@ namespace FTDI {
NOTE_F4 = 0x41,
NOTE_F4S = 0x42,
NOTE_G4 = 0x43,
NOTE_G4S = 0x44
NOTE_G4S = 0x44,
NOTE_A4 = 0x45
};
}

@ -304,7 +304,7 @@ class CLCD::CommandFifo {
}
template<typename T> FORCEDINLINE void Cmd_Draw_Button_Text(int16_t x, int16_t y, int16_t w, int16_t h, T text, int16_t font, uint16_t options = OPT_CENTER) {
Cmd_Draw_Text(
x + ((options & OPT_CENTERX) ? w/2 : 0),
x + ((options & OPT_CENTERX) ? w/2 : ((options & OPT_RIGHTX) ? w : 0)),
y + ((options & OPT_CENTERY) ? h/2 : h),
text, font, options);
}
@ -886,13 +886,14 @@ void CLCD::Init (void) {
/* tiny_interval() downsamples a 32-bit millis() value
into a 8-bit value which can record periods of
up to 4.096 seconds with a rougly 16 millisecond
a few seconds with a rougly 1/16th of second
resolution. This allows us to measure small
intervals without needing to use four-byte counters.
However, dues to wrap-arounds, this class may misfire
often and thus should only be used where memory savings
outweigh accuracy.
However, dues to wrap-arounds, this class may
have a burst of misfires every 16 seconds or so and
thus should only be used where this is harmless and
memory savings outweigh accuracy.
*/
class tiny_interval_t {
private:
@ -903,12 +904,18 @@ class tiny_interval_t {
}
inline void wait_for(uint32_t ms) {
end = tiny_interval(millis() + ms);
uint32_t now = millis();
end = tiny_interval(now + ms);
if(tiny_interval(now + ms*2) < end) {
// Avoid special case where timer
// might get wedged and stop firing.
end = 0;
}
}
inline bool elapsed() {
if(end == 0 || tiny_interval(millis()) > end) {
end = 0;
uint8_t now = tiny_interval(millis());
if(now > end) {
return true;
} else {
return false;
@ -929,22 +936,31 @@ class CLCD::SoundPlayer {
const uint8_t WAIT = 0;
static const PROGMEM sound_t silence[];
private:
const sound_t *sequence;
uint8_t next;
note_t frequencyToMidiNote(const uint16_t frequency);
public:
static void setVolume(uint8_t volume);
static void play(effect_t effect, note_t note = NOTE_C4);
static bool soundPlaying();
void play(const sound_t* seq);
void playTone(const uint16_t frequency_hz, const uint16_t duration_ms);
void onIdle();
bool hasMoreNotes() {return sequence != 0;};
};
const PROGMEM CLCD::SoundPlayer::sound_t CLCD::SoundPlayer::silence[] = {
{SILENCE, END_SONG, 0}
};
void CLCD::SoundPlayer::setVolume(uint8_t vol) {
CLCD::Mem_Write8(REG_VOL_SOUND, vol);
}
@ -961,9 +977,27 @@ void CLCD::SoundPlayer::play(effect_t effect, note_t note) {
#endif
}
note_t CLCD::SoundPlayer::frequencyToMidiNote(const uint16_t frequency_hz) {
const float f0 = 440;
return note_t(NOTE_A4 + (log(frequency_hz)-log(f0))*12/log(2) + 0.5);
}
// Plays a tone of a given frequency and duration. Since the FTDI FT810 only
// supports MIDI notes, we round down to the nearest note.
void CLCD::SoundPlayer::playTone(const uint16_t frequency_hz, const uint16_t duration_ms) {
play(ORGAN, frequencyToMidiNote(frequency_hz));
// Schedule silence to squelch the note after the duration expires.
sequence = silence;
next = tiny_interval_t::tiny_interval(millis() + duration_ms);
}
void CLCD::SoundPlayer::play(const sound_t* seq) {
sequence = seq;
next = tiny_interval_t::tiny_interval(millis()) + 1;
// Delaying the start of the sound seems to prevent glitches. Not sure why...
next = tiny_interval_t::tiny_interval(millis()+250);
}
bool CLCD::SoundPlayer::soundPlaying() {
@ -983,13 +1017,14 @@ void CLCD::SoundPlayer::onIdle() {
if(ms == 0 && fx == SILENCE && nt == 0) {
sequence = 0;
play(SILENCE, REST);
} else {
#if defined(UI_FRAMEWORK_DEBUG)
#if defined (SERIAL_PROTOCOLLNPAIR)
SERIAL_PROTOCOLLNPAIR("Scheduling note in ", ms);
#endif
#endif
next = (ms == WAIT) ? 0 : (tiny_millis + tiny_interval_t::tiny_interval(ms));
next = (ms == WAIT) ? 0 : (tiny_interval_t::tiny_interval(millis() + ms));
play(fx, (nt == 0) ? NOTE_C4 : nt);
sequence++;
}

@ -24,7 +24,7 @@
// cache, so we reserve a large chunk of memory for the DL cache
#define STATUS_SCREEN_DL_SIZE 2048
#define CONFIRMATION_SCREEN_DL_SIZE 3072
#define DIALOG_BOX_DL_SIZE 3072
#define N_ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
@ -110,8 +110,8 @@
// in a union. The values should be initialized in the onEntry method.
static union {
struct {uint8_t increment;} ValueAdjusters;
struct {uint8_t page, selected;} FilesScreen;
struct {uint8_t increment;} ValueAdjusters;
struct {uint8_t page, selected_tag;} FilesScreen;
} screen_data;
/************************* MENU SCREEN DECLARATIONS *************************/
@ -193,6 +193,12 @@ class MenuScreen : public UIScreen {
static bool onTouchStart(uint8_t tag);
};
class TuneScreen : public UIScreen {
public:
static void onRefresh();
static bool onTouchStart(uint8_t tag);
};
class CalibrationScreen : public UIScreen {
public:
static void onEntry();
@ -272,6 +278,12 @@ class ZOffsetScreen : public ValueAdjusters {
static bool onTouchHeld(uint8_t tag);
};
class FeedrateScreen : public ValueAdjusters {
public:
static void onRefresh();
static bool onTouchHeld(uint8_t tag);
};
class TemperatureScreen : public ValueAdjusters {
public:
static void onRefresh();
@ -281,6 +293,8 @@ class TemperatureScreen : public ValueAdjusters {
class FilesScreen : public UIScreen {
private:
static const char *getSelectedShortFilename();
static uint8_t getTagForIndex(uint16_t index);
static uint16_t getIndexForTag(uint8_t tag);
public:
static void onEntry();
static void onRefresh();
@ -298,10 +312,12 @@ SCREEN_TABLE {
DECL_SCREEN(CalibrationScreen),
DECL_SCREEN(StatusScreen),
DECL_SCREEN(MenuScreen),
DECL_SCREEN(TuneScreen),
DECL_SCREEN(MoveAxisScreen),
DECL_SCREEN(AdvancedSettingsScreen),
DECL_SCREEN(StepsScreen),
DECL_SCREEN(ZOffsetScreen),
DECL_SCREEN(FeedrateScreen),
DECL_SCREEN(TemperatureScreen),
DECL_SCREEN(CalibrationRegistersScreen),
DECL_SCREEN(FilesScreen),
@ -318,12 +334,14 @@ SCREEN_TABLE_POST
enum {
STATUS_SCREEN_CACHE,
MENU_SCREEN_CACHE,
CONFIRMATION_SCREEN_CACHE,
TUNE_SCREEN_CACHE,
DIALOG_BOX_CACHE,
ADVANCED_SETTINGS_SCREEN_CACHE,
MOVE_AXIS_SCREEN_CACHE,
TEMPERATURE_SCREEN_CACHE,
STEPS_SCREEN_CACHE,
ZOFFSET_SCREEN_CACHE,
FEEDRATE_SCREEN_CACHE,
FILES_SCREEN_CACHE
};
@ -341,6 +359,7 @@ namespace Theme {
const uint32_t y_axis = 0x005000;
const uint32_t z_axis = 0x000050;
const uint32_t e_axis = 0x000000;
const uint32_t feedrate = 0x000000;
const uint32_t toggle_on = theme_light;
const uint32_t toggle_off = theme_darkest;
@ -480,7 +499,7 @@ bool AboutScreen::onTouchStart(uint8_t tag) {
/**************************** GENERIC DIALOG BOX SCREEN ****************************/
void DialogBoxBaseClass::show(const progmem_str lines[], size_t n_lines, progmem_str btn1, progmem_str btn2 ) {
CLCD::DLCache dlcache(CONFIRMATION_SCREEN_CACHE);
CLCD::DLCache dlcache(DIALOG_BOX_CACHE);
CLCD::CommandFifo cmd;
cmd.Cmd(CMD_DLSTART);
@ -507,7 +526,7 @@ void DialogBoxBaseClass::show(const progmem_str lines[], size_t n_lines, progmem
cmd.Cmd(CMD_SWAP);
cmd.Cmd_Execute();
if(!dlcache.store(CONFIRMATION_SCREEN_DL_SIZE)) {
if(!dlcache.store(DIALOG_BOX_DL_SIZE)) {
#if defined (SERIAL_PROTOCOLLNPAIR)
SERIAL_PROTOCOLLN("Unable to set the confirmation message, not enough DL cache space");
#else
@ -519,7 +538,7 @@ void DialogBoxBaseClass::show(const progmem_str lines[], size_t n_lines, progmem
}
void DialogBoxBaseClass::onRefresh() {
CLCD::DLCache dlcache(CONFIRMATION_SCREEN_CACHE);
CLCD::DLCache dlcache(DIALOG_BOX_CACHE);
CLCD::CommandFifo cmd;
cmd.Cmd(CMD_DLSTART);
@ -1025,9 +1044,19 @@ bool StatusScreen::onTouchStart(uint8_t tag) {
GOTO_SCREEN(ConfirmAbortPrint);
break;
case 3: GOTO_SCREEN(FilesScreen); break;
case 4: GOTO_SCREEN(MenuScreen); break;
case 4:
if(Marlin_LCD_API::isPrinting()) {
GOTO_SCREEN(TuneScreen);
} else {
GOTO_SCREEN(MenuScreen);
}
break;
case 5: GOTO_SCREEN(TemperatureScreen); break;
case 6: GOTO_SCREEN(MoveAxisScreen); break;
case 6:
if(!Marlin_LCD_API::isPrinting()) {
GOTO_SCREEN(MoveAxisScreen);
}
break;
}
return true;
}
@ -1104,6 +1133,68 @@ bool MenuScreen::onTouchStart(uint8_t tag) {
return true;
}
/************************************ TUNE SCREEN *******************************/
#if defined(LCD_PORTRAIT)
#define GRID_ROWS 5
#define GRID_COLS 2
#else
#define GRID_ROWS 3
#define GRID_COLS 2
#endif
void TuneScreen::onRefresh() {
CLCD::DLCache dlcache(TUNE_SCREEN_CACHE);
CLCD::CommandFifo cmd;
cmd.Cmd(CMD_DLSTART);
if(dlcache.hasData()) {
dlcache.append();
} else {
cmd.Cmd_Set_Clear_Color(Theme::background);
cmd.Cmd_Clear(1,1,1);
#if defined(LCD_PORTRAIT)
BTN_TAG(2) BTN_ENABLED(1) BTN( BTN_POS(1,1), BTN_SIZE(2,1), F("Temperature"), MENU_BTN_STYLE);
BTN_TAG(3) BTN_ENABLED(0) BTN( BTN_POS(1,2), BTN_SIZE(2,1), F("Change Filament"), MENU_BTN_STYLE);
BTN_TAG(4) BTN_ENABLED(1) BTN( BTN_POS(1,3), BTN_SIZE(2,1), F("Z Offset"), MENU_BTN_STYLE);
BTN_TAG(5) BTN_ENABLED(1) BTN( BTN_POS(1,4), BTN_SIZE(2,1), F("Print Speed"), MENU_BTN_STYLE);
#else
BTN_TAG(2) BTN_ENABLED(1) BTN( BTN_POS(1,1), BTN_SIZE(1,1), F("Temperature"), MENU_BTN_STYLE);
BTN_TAG(3) BTN_ENABLED(0) BTN( BTN_POS(1,2), BTN_SIZE(1,1), F("Change Filament"), MENU_BTN_STYLE);
BTN_TAG(4) BTN_ENABLED(1) BTN( BTN_POS(2,1), BTN_SIZE(1,1), F("Z Offset"), MENU_BTN_STYLE);
BTN_TAG(5) BTN_ENABLED(1) BTN( BTN_POS(2,2), BTN_SIZE(2,1), F("Print Speed"), MENU_BTN_STYLE);
#endif
#if defined(LCD_PORTRAIT)
#define MARGIN_T 15
BTN_TAG(1) THEME(back_btn) BTN( BTN_POS(1,5), BTN_SIZE(2,1), F("Back"), MENU_BTN_STYLE);
#else
BTN_TAG(1) THEME(back_btn) BTN( BTN_POS(1,3), BTN_SIZE(2,1), F("Back"), MENU_BTN_STYLE);
#endif
#define MARGIN_T 5
dlcache.store();
}
cmd.Cmd(DL_DISPLAY);
cmd.Cmd(CMD_SWAP);
cmd.Cmd_Execute();
}
bool TuneScreen::onTouchStart(uint8_t tag) {
switch(tag) {
case 1: GOTO_PREVIOUS(); break;
case 2: GOTO_SCREEN(TemperatureScreen); break;
case 4: GOTO_SCREEN(ZOffsetScreen); break;
case 5: GOTO_SCREEN(FeedrateScreen); break;
default:
return false;
}
return true;
}
/******************************* CONFIGURATION SCREEN ****************************/
#if defined(LCD_PORTRAIT)
@ -1487,11 +1578,17 @@ void MoveAxisScreen::onRefresh() {
cmd.Cmd(CMD_DLSTART);
/* Tag Label: Units: Color: Precision: */
const heading_t h = { PSTR("Move Axis") };
const adjuster_t x = {2, PSTR("X:"), PSTR("mm"), Theme::x_axis, 1 };
const adjuster_t y = {4, PSTR("Y:"), PSTR("mm"), Theme::y_axis, 1 };
const adjuster_t z = {6, PSTR("Z:"), PSTR("mm"), Theme::z_axis, 1 };
const increment_t i = { 1 };
const heading_t h = { PSTR("Move Axis") };
const adjuster_t x = {2, PSTR("X:"), PSTR("mm"), Theme::x_axis, 1 };
const adjuster_t y = {4, PSTR("Y:"), PSTR("mm"), Theme::y_axis, 1 };
const adjuster_t z = {6, PSTR("Z:"), PSTR("mm"), Theme::z_axis, 1 };
#if EXTRUDERS == 1
const adjuster_t e0 = {8, PSTR("E:"), PSTR("mm"), Theme::e_axis, 1 };
#else EXTRUDERS == 2
const adjuster_t e0 = {8, PSTR("E0:"), PSTR("mm"), Theme::e_axis, 1 };
const adjuster_t e1 = {10, PSTR("E1:"), PSTR("mm"), Theme::e_axis, 1 };
#endif
const increment_t i = { 1 };
stacker_t s;
if(dlcache.hasData()) {
@ -1502,6 +1599,10 @@ void MoveAxisScreen::onRefresh() {
x.static_parts(s);
y.static_parts(s);
z.static_parts(s);
e0.static_parts(s);
#if EXTRUDERS == 2
e1.static_parts(s);
#endif
i.static_parts(s);
dlcache.store();
}
@ -1510,6 +1611,10 @@ void MoveAxisScreen::onRefresh() {
x.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::X));
y.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::Y));
z.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::Z));
e0.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::E0));
#if EXTRUDERS == 2
e1.dynamic_parts(s,Marlin_LCD_API::getAxisPosition_mm(Marlin_LCD_API::E1));
#endif
i.dynamic_parts(s);
cmd.Cmd(DL_DISPLAY);
@ -1529,12 +1634,16 @@ bool MoveAxisScreen::onTouchHeld(uint8_t tag) {
const float feedrate_mm_s = inc * TOUCH_REPEATS_PER_SECOND;
switch(tag) {
case 2: axis = Marlin_LCD_API::X; inc *= -1; break;
case 3: axis = Marlin_LCD_API::X; inc *= 1; break;
case 4: axis = Marlin_LCD_API::Y; inc *= -1; break;
case 5: axis = Marlin_LCD_API::Y; inc *= 1; break;
case 6: axis = Marlin_LCD_API::Z; inc *= -1; break;
case 7: axis = Marlin_LCD_API::Z; inc *= 1; break;
case 2: axis = Marlin_LCD_API::X; inc *= -1; break;
case 3: axis = Marlin_LCD_API::X; inc *= 1; break;
case 4: axis = Marlin_LCD_API::Y; inc *= -1; break;
case 5: axis = Marlin_LCD_API::Y; inc *= 1; break;
case 6: axis = Marlin_LCD_API::Z; inc *= -1; break;
case 7: axis = Marlin_LCD_API::Z; inc *= 1; break;
case 8: axis = Marlin_LCD_API::E0; inc *= -1; break;
case 9: axis = Marlin_LCD_API::E0; inc *= 1; break;
case 10: axis = Marlin_LCD_API::E1; inc *= -1; break;
case 11: axis = Marlin_LCD_API::E1; inc *= 1; break;
default:
return false;
}
@ -1724,6 +1833,50 @@ bool ZOffsetScreen::onTouchHeld(uint8_t tag) {
return true;
}
/***************************** FEEDRATE SCREEN ***************************/
void FeedrateScreen::onRefresh() {
CLCD::DLCache dlcache(FEEDRATE_SCREEN_CACHE);
CLCD::CommandFifo cmd;
cmd.Cmd(CMD_DLSTART);
/* Tag Label: Units: Color: Precision: */
const heading_t h = { PSTR("Print Speed") };
const adjuster_t f = {4, PSTR("Speed"), PSTR("%"), Theme::feedrate, 0 };
const increment_t i = { 0 };
stacker_t s;
if(dlcache.hasData()) {
dlcache.append();
} else {
s.static_parts();
h.static_parts(s);
f.static_parts(s);
i.static_parts(s);
dlcache.store();
}
s.dynamic_parts();
h.dynamic_parts(s);
f.dynamic_parts(s,Marlin_LCD_API::getFeedRate_percent());
i.dynamic_parts(s);
cmd.Cmd(DL_DISPLAY);
cmd.Cmd(CMD_SWAP);
cmd.Cmd_Execute();
}
bool FeedrateScreen::onTouchHeld(uint8_t tag) {
float inc = getIncrement();
switch(tag) {
case 4: Marlin_LCD_API::setFeedrate_percent(Marlin_LCD_API::getFeedRate_percent() - inc); break;
case 5: Marlin_LCD_API::setFeedrate_percent(Marlin_LCD_API::getFeedRate_percent() + inc); break;
default:
return false;
}
onRefresh();
return true;
}
/***************************** FILES SCREEN ***************************/
#if defined(LCD_PORTRAIT)
@ -1737,20 +1890,22 @@ bool ZOffsetScreen::onTouchHeld(uint8_t tag) {
const uint16_t filesPerPage = GRID_ROWS - 4;
void FilesScreen::onEntry() {
screen_data.FilesScreen.page = 0;
screen_data.FilesScreen.selected = 0xFF;
screen_data.FilesScreen.page = 0;
screen_data.FilesScreen.selected_tag = 0xFF;
UIScreen::onEntry();
}
const char *FilesScreen::getSelectedShortFilename() {
Marlin_LCD_API::Marlin_LCD_API::Media_Iterator iterator(screen_data.FilesScreen.page * filesPerPage);
Marlin_LCD_API::Media_Iterator iterator(getIndexForTag(screen_data.FilesScreen.selected_tag));
return iterator.shortFilename();
}
while(iterator.hasMore()) {
if(screen_data.FilesScreen.selected == iterator.value() + 1) {
return iterator.shortFilename();
}
iterator.next();
}
uint8_t FilesScreen::getTagForIndex(uint16_t fileIndex) {
return fileIndex + 1;
}
uint16_t FilesScreen::getIndexForTag(uint8_t tag) {
return tag - 1;
}
void FilesScreen::onRefresh() {
@ -1761,18 +1916,32 @@ void FilesScreen::onRefresh() {
cmd.Cmd_Set_Clear_Color(Theme::background);
cmd.Cmd_Clear(1,1,1);
Marlin_LCD_API::Marlin_LCD_API::Media_Iterator iterator(screen_data.FilesScreen.page * filesPerPage);
#define MARGIN_T 0
#define MARGIN_B 0
while(iterator.hasMore()) {
const uint16_t tag = iterator.value() + 1;
BTN_TAG(tag)
RGB(screen_data.FilesScreen.selected == tag ? Theme::files_selected : Theme::background)
BTN( BTN_POS(1,tag+2), BTN_SIZE(6,1), F(""), FONT_SML, OPT_FLAT);
BTX( BTN_POS(1,tag+2), BTN_SIZE(6,1), iterator.filename(), FONT_LRG, OPT_CENTERY);
iterator.next();
bool dirSelected = false;
Marlin_LCD_API::Media_Iterator iterator(screen_data.FilesScreen.page * filesPerPage);
if(iterator.count()) {
do {
const uint16_t tag = getTagForIndex(iterator.value());
const bool isDir = iterator.isDirectory();
BTN_TAG(tag)
if(screen_data.FilesScreen.selected_tag == tag) {
RGB(Theme::files_selected)
dirSelected = isDir;
} else {
RGB(Theme::background)
}
BTN( BTN_POS(1,tag+2), BTN_SIZE(6,1), F(""), FONT_SML, OPT_FLAT);
BTX( BTN_POS(1,tag+2), BTN_SIZE(6,1), iterator.filename(), FONT_LRG, OPT_CENTERY);
if(isDir) {
BTX( BTN_POS(1,tag+2), BTN_SIZE(6,1), F("> "), FONT_LRG, OPT_CENTERY | OPT_RIGHTX);
}
iterator.next();
} while(iterator.hasMore());
}
#define MARGIN_T 5
@ -1781,7 +1950,8 @@ void FilesScreen::onRefresh() {
const uint16_t pageCount = iterator.count() / filesPerPage + 1;
const bool prevEnabled = screen_data.FilesScreen.page > 0;
const bool nextEnabled = screen_data.FilesScreen.page < (pageCount - 1);
const bool fileSelected = screen_data.FilesScreen.selected != 0xFF;
const bool itemSelected = screen_data.FilesScreen.selected_tag != 0xFF;
const uint8_t backTag = Marlin_LCD_API::isAtRootDir() ? 240 : 245;
char page_str[15];
sprintf_P(page_str, PSTR("Page %d of %d"), screen_data.FilesScreen.page + 1, pageCount);
@ -1794,14 +1964,16 @@ void FilesScreen::onRefresh() {
if(nextEnabled) {BTN_TAG(242); BTN( BTN_POS(6,1), BTN_SIZE(1,2), F(">"), MENU_BTN_STYLE);}
#define MARGIN_T 15
BTN_TAG(backTag) THEME(back_btn) BTN( BTN_POS(5,13), BTN_SIZE(2,2), F("Back"), MENU_BTN_STYLE);
BTN_TAG(240) THEME(back_btn)
BTN( BTN_POS(5,13), BTN_SIZE(2,2), F("Back"), MENU_BTN_STYLE);
BTN_ENABLED(fileSelected)
BTN_TAG(243); BTN( BTN_POS(1,13), BTN_SIZE(4,2), F("Print"), MENU_BTN_STYLE);
BTN_ENABLED(itemSelected)
if(dirSelected) {
BTN_TAG(244); BTN( BTN_POS(1,13), BTN_SIZE(4,2), F("Open"), MENU_BTN_STYLE);
} else {
BTN_TAG(243); BTN( BTN_POS(1,13), BTN_SIZE(4,2), F("Print"), MENU_BTN_STYLE);
}
#else
BTN_TAG(240) THEME(back_btn) BTN( BTN_POS(1,4), BTN_SIZE(1,1), F("Back"), MENU_BTN_STYLE);
BTN_TAG(backTag) THEME(back_btn) BTN( BTN_POS(1,4), BTN_SIZE(1,1), F("Back"), MENU_BTN_STYLE);
#endif
#define MARGIN_T 5
@ -1818,14 +1990,20 @@ bool FilesScreen::onTouchStart(uint8_t tag) {
case 242: screen_data.FilesScreen.page++; break;
case 243:
Marlin_LCD_API::printFromSDCard(getSelectedShortFilename());
sound.play(start_print);
lcd_setstatusPGM(PSTR("Print Starting"), 0);
GOTO_SCREEN(StatusScreen);
sound.play(start_print);
return true;
case 244:
Marlin_LCD_API::changeDir(getSelectedShortFilename());
break;
case 245:
Marlin_LCD_API::upDir();
break;
default:
if(tag < 240) {
if(screen_data.FilesScreen.selected != tag) {
screen_data.FilesScreen.selected = tag;
if(screen_data.FilesScreen.selected_tag != tag) {
screen_data.FilesScreen.selected_tag = tag;
} else {
// Double clicked.
}
@ -2004,3 +2182,7 @@ void Marlin_LCD_API::onCardRemoved() {
lcd_setstatusPGM(PSTR(MSG_SD_REMOVED), 0);
sound.play(card_removed);
}
void Marlin_LCD_API::onPlayTone(const uint16_t frequency, const uint16_t duration) {
sound.playTone(frequency, duration);
}

@ -61,9 +61,12 @@ const PROGMEM CLCD::SoundPlayer::sound_t c_maj_arpeggio[] = {
const PROGMEM CLCD::SoundPlayer::sound_t start_print[] = {
{TRUMPET, NOTE_A3, 4},
{SILENCE, REST, 1},
{TRUMPET, NOTE_A3, 2},
{SILENCE, REST, 1},
{TRUMPET, NOTE_A3, 2},
{TRUMPET, NOTE_E4, 16},
{SILENCE, REST, 1},
{TRUMPET, NOTE_E4, 10},
{SILENCE, END_SONG, 0}
};

@ -15,6 +15,9 @@
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#ifndef __MARLIN_LCD_API_H__
#define __MARLIN_LCD_API_H__
class Marlin_LCD_API {
public:
typedef const __FlashStringHelper *progmem_str;
@ -38,7 +41,7 @@ class Marlin_LCD_API {
static float getAxisSteps_per_mm(const axis_t axis);
static uint8_t getProgress_percent();
static uint32_t getProgress_seconds_elapsed();
static uint8_t getFeedRate_percent();
static float getFeedRate_percent();
static float getZOffset_mm();
static bool isAxisPositionKnown(const axis_t axis);
static bool isMoving();
@ -50,6 +53,7 @@ class Marlin_LCD_API {
static void setAxisPosition_mm(const axis_t axis, float position, float _feedrate_mm_s);
static void setAxisSteps_per_mm(const axis_t axis, float steps_per_mm);
static void incrementZOffset_mm(const float z_offset);
static void setFeedrate_percent(const float percent);
static void runGCode(progmem_str gcode);
@ -58,6 +62,7 @@ class Marlin_LCD_API {
static void initMedia();
static void checkMedia();
static bool isPrintingFromMedia();
static bool isPrinting();
static bool isMediaInserted();
static void stopPrint();
static void pausePrint();
@ -66,10 +71,14 @@ class Marlin_LCD_API {
static void onCardInserted();
static void onCardRemoved();
static void onPrinterKilled(const char* lcd_msg);
static void onPlayTone(const uint16_t frequency, const uint16_t duration);
static uint16_t getMediaFileCount();
static void printFromSDCard(const char *filename);
static void changeDir(const char *dirname);
static void upDir();
static bool isAtRootDir();
class Media_Iterator;
};
@ -79,7 +88,7 @@ class Marlin_LCD_API::Media_Iterator {
uint16_t index;
uint16_t num_files;
public:
Media_Iterator(uint16_t start_index = 0);
Media_Iterator(uint16_t start_index /* = 0*/);
bool hasMore();
void seek(uint16_t);
void next();
@ -88,6 +97,7 @@ class Marlin_LCD_API::Media_Iterator {
const char *filename();
uint16_t value() {return index;}
uint16_t count() {return num_files;}
bool isDirectory();
};
#if defined(MSG_MARLIN)
@ -188,7 +198,7 @@ uint32_t Marlin_LCD_API::getProgress_seconds_elapsed() {
return elapsed.value;
}
uint8_t Marlin_LCD_API::getFeedRate_percent() {
float Marlin_LCD_API::getFeedRate_percent() {
return feedrate_percentage;
}
@ -226,10 +236,27 @@ void Marlin_LCD_API::setFan_percent(const uint8_t fan, float percent) {
}
}
void Marlin_LCD_API::setFeedrate_percent(const float percent) {
feedrate_percentage = clamp(percent, 10, 500);
}
void Marlin_LCD_API::printFromSDCard(const char *filename) {
card.openAndPrintFile(filename);
}
void Marlin_LCD_API::changeDir(const char *dirname) {
card.chdir(dirname);
}
void Marlin_LCD_API::upDir() {
card.updir();
}
bool Marlin_LCD_API::isAtRootDir() {
card.getWorkDirName();
return card.filename[0] == '/';
}
void lcd_setstatusPGM(const char * const message, int8_t level /* = 0 */);
uint8_t lcd_sd_status;
@ -272,8 +299,17 @@ bool Marlin_LCD_API::isPrintingFromMedia() {
#endif
}
bool Marlin_LCD_API::isPrinting() {
return (planner.movesplanned() || IS_SD_PRINTING ||
#if ENABLED(SDSUPPORT)
(card.cardOK && card.isFileOpen()));
#else
false;
#endif
}
bool Marlin_LCD_API::isMediaInserted() {
#if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
#if ENABLED(SDSUPPORT)
return IS_SD_INSERTED;
#else
return false;
@ -324,7 +360,7 @@ Marlin_LCD_API::Media_Iterator::Media_Iterator(uint16_t start_index /* = 0*/) {
}
bool Marlin_LCD_API::Media_Iterator::hasMore() {
return index < (num_files - 1);
return (index < (num_files - 1)) && (num_files > 0);
}
void Marlin_LCD_API::Media_Iterator::next() {
@ -349,7 +385,7 @@ void Marlin_LCD_API::Media_Iterator::seek(uint16_t index) {
}
const char *Marlin_LCD_API::Media_Iterator::filename() {
return card.longFilename[0] ? card.longFilename : card.filename;
return (card.longFilename && card.longFilename[0]) ? card.longFilename : card.filename;
}
const char *Marlin_LCD_API::Media_Iterator::shortFilename() {
@ -359,4 +395,10 @@ const char *Marlin_LCD_API::Media_Iterator::shortFilename() {
const char *Marlin_LCD_API::Media_Iterator::longFilename() {
return card.longFilename;
}
#endif
bool Marlin_LCD_API::Media_Iterator::isDirectory() {
return card.filenameIsDir;
}
#endif
#endif MARLIN_LCD_API__MARLIN_LCD_API_H__

@ -13,7 +13,7 @@
* got disabled.
*/
#define LULZBOT_FW_VERSION ".17" // Change this with each update
#define LULZBOT_FW_VERSION ".18" // Change this with each update
#if ( \
!defined(LULZBOT_Gladiola_Mini) && \
@ -143,6 +143,7 @@
#define LULZBOT_USE_32_MICROSTEPS_ON_Z
#define LULZBOT_UUID "e5502411-d46d-421d-ba3a-a20126d7930f"
#define LULZBOT_USE_EXPERIMENTAL_FEATURES
#define LULZBOT_USE_USB_STICK
#endif
#if defined(LULZBOT_Quiver_TAZ7)
@ -204,6 +205,7 @@
#define LULZBOT_USE_32_MICROSTEPS_ON_Z
#define LULZBOT_UUID "e5502411-d46d-421d-ba3a-a20126d7930f"
#define LULZBOT_USE_EXPERIMENTAL_FEATURES
#define LULZBOT_USE_USB_STICK
#endif
/****************************** DEBUGGING OPTIONS *******************************/
@ -1800,7 +1802,7 @@
#else
#define LCD_IS_FT800
#endif
#define UI_FRAMEWORK_DEBUG
//#define UI_FRAMEWORK_DEBUG
#define LULZBOT_SDSUPPORT
#define LULZBOT_DISABLE_SD_DETECT_INVERTED
#endif

@ -520,7 +520,7 @@ static millis_t stepper_inactive_time = (DEFAULT_STEPPER_DEACTIVE_TIME) * 1000UL
// Buzzer - I2C on the LCD or a BEEPER_PIN
#if ENABLED(LCD_USE_I2C_BUZZER)
#define BUZZ(d,f) lcd_buzz(d, f)
#elif PIN_EXISTS(BEEPER)
#elif PIN_EXISTS(BEEPER) || defined(LULZBOT_USE_TOUCH_UI)
Buzzer buzzer;
#define BUZZ(d,f) buzzer.tone(d, f)
#else

@ -28,7 +28,12 @@
*/
#include "MarlinConfig.h"
#if ENABLED(SDSUPPORT)
#if defined(LULZBOT_USE_USB_STICK)
#undef MACROS_H
#include "usb-flashdrive/Fake_Sd2Card_impl.h"
#endif
#if ENABLED(SDSUPPORT) && not defined(LULZBOT_USE_USB_STICK)
#include "Sd2Card.h"

@ -35,7 +35,11 @@
#define _SDVOLUME_H_
#include "SdFatConfig.h"
#include "Sd2Card.h"
#if defined(LULZBOT_USE_USB_STICK)
#include "usb-flashdrive/Fake_Sd2Card.h"
#else
#include "Sd2Card.h"
#endif
#include "SdFatStructs.h"
//==============================================================================

@ -30,6 +30,10 @@
#include "MarlinConfig.h"
#if ENABLED(LULZBOT_USE_TOUCH_UI)
#include "AO_UI_Marlin_LCD_API.h"
#endif
#define TONE_QUEUE_LENGTH 4
/**
@ -128,7 +132,11 @@ class Buzzer {
this->state.endtime = now + this->state.tone.duration;
if (this->state.tone.frequency > 0) {
#if ENABLED(SPEAKER)
#if ENABLED(LULZBOT_USE_TOUCH_UI)
CRITICAL_SECTION_START;
Marlin_LCD_API::onPlayTone(this->state.tone.frequency, this->state.tone.duration);
CRITICAL_SECTION_END;
#elif ENABLED(SPEAKER)
CRITICAL_SECTION_START;
::tone(BEEPER_PIN, this->state.tone.frequency, this->state.tone.duration);
CRITICAL_SECTION_END;

@ -189,7 +189,9 @@
//
#if ENABLED(ULTRA_LCD) || defined(LULZBOT_USE_TOUCH_UI)
#define KILL_PIN 32
#if not defined(LULZBOT_DISABLE_KILL_BUTTON)
#define KILL_PIN 32
#endif
#if ENABLED(NEWPANEL) || defined(LULZBOT_USE_TOUCH_UI)
@ -215,7 +217,9 @@
#define BTN_EN2 72
#define BTN_ENC 9 // the click
#define SD_DETECT_PIN 15
#if not defined(LULZBOT_USE_USB_STICK)
#define SD_DETECT_PIN 15
#endif
#endif // NEWPANEL
#endif // ULTRA_LCD

@ -0,0 +1,85 @@
/**
* Marlin 3D Printer Firmware
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* \file
* \brief Sd2Card class for V2 SD/SDHC cards
*/
#ifndef _FAKE_SD2CARD_H_
#define _FAKE_SD2CARD_H_
#include "SdFatConfig.h"
#include "SdInfo.h"
// SPI speed is F_CPU/2^(1 + index), 0 <= index <= 6
uint8_t const SPI_FULL_SPEED = 0, // Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate().
SPI_HALF_SPEED = 1, // Set SCK rate to F_CPU/4. See Sd2Card::setSckRate().
SPI_QUARTER_SPEED = 2, // Set SCK rate to F_CPU/8. See Sd2Card::setSckRate().
SPI_EIGHTH_SPEED = 3, // Set SCK rate to F_CPU/16. See Sd2Card::setSckRate().
SPI_SIXTEENTH_SPEED = 4; // Set SCK rate to F_CPU/32. See Sd2Card::setSckRate().
/**
* define SOFTWARE_SPI to use bit-bang SPI
*/
#if MEGA_SOFT_SPI
#define SOFTWARE_SPI
#elif USE_SOFTWARE_SPI
#define SOFTWARE_SPI
#endif
// SPI pin definitions - do not edit here - change in SdFatConfig.h
#if DISABLED(SOFTWARE_SPI)
// hardware pin defs
#define SD_CHIP_SELECT_PIN SS_PIN // The default chip select pin for the SD card is SS.
// The following three pins must not be redefined for hardware SPI.
#define SPI_MOSI_PIN MOSI_PIN // SPI Master Out Slave In pin
#define SPI_MISO_PIN MISO_PIN // SPI Master In Slave Out pin
#define SPI_SCK_PIN SCK_PIN // SPI Clock pin
#else // SOFTWARE_SPI
#define SD_CHIP_SELECT_PIN SOFT_SPI_CS_PIN // SPI chip select pin
#define SPI_MOSI_PIN SOFT_SPI_MOSI_PIN // SPI Master Out Slave In pin
#define SPI_MISO_PIN SOFT_SPI_MISO_PIN // SPI Master In Slave Out pin
#define SPI_SCK_PIN SOFT_SPI_SCK_PIN // SPI Clock pin
#endif // SOFTWARE_SPI
/**
* \class Sd2Card
* \brief Raw access to SD and SDHC flash memory cards.
*/
class Sd2Card {
public:
Sd2Card();
/**
* Initialize an SD flash memory card with default clock rate and chip
* select pin. See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
*
* \return true for success or false for failure.
*/
bool init(uint8_t sckRateID = 0, uint8_t chipSelectPin = SD_CHIP_SELECT_PIN);
bool readBlock(uint32_t block, uint8_t* dst);
bool writeBlock(uint32_t blockNumber, const uint8_t* src);
};
#endif // _FAKE_SD2CARD_H_

@ -0,0 +1,147 @@
/**
* Marlin 3D Printer Firmware
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
*
* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
*
*/
/********************************************************************************************
* This program/sketch is used to run a USB Thumb Drive. *
* *
* NOTE - This Arduino Sketch has been modified to initialize a MAX3421E USB Host Interface *
* chip, write 3 test files, print out the directory of the thumb drive and print out the *
* contents of a short .txt file. *
* *
* The code is leveraged from the following: *
* *
* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. *
* *
* This software may be distributed and modified under the terms of the GNU *
* General Public License version 2 (GPL2) as published by the Free Software *
* Foundation and appearing in the file GPL2.TXT included in the packaging of *
* this file. Please note that GPL2 Section 2[b] requires that all works based *
* on this software must also be made publicly available under the terms of *
* the GPL2 ("Copyleft"). *
* *
* Contact information *
* ------------------- *
* *
* Circuits At Home, LTD *
* Web : http://www.circuitsathome.com *
* e-mail : support@circuitsathome.com *
* *
* SPECIAL NOTE - In order to work with a modified Eisny or RAMBo, the SPI chip select pin *
* (CS) (D10) has been remapped from PORTB Pin-4 to PORTB Pin-0. This has been done in the *
* __AVR_ATmega2560__ section of the avrpins.h file. *
* *
********************************************************************************************/
#include <SPI.h>
//#define _usb_h_
#include "Marlin.h"
#include "../watchdog.h"
#undef MACROS_H
#define USB_HOST_SERIAL customizedSerial
#include "lib/masstorage.h"
#include "lib/masstorage.cpp"
#include "lib/message.cpp"
#include "lib/parsetools.cpp"
#include "lib/Usb.cpp"
#include "Fake_Sd2Card.h"
#define MAX_USB_RST 7
// USB host objects.v
USB usb;
BulkOnly bulk(&usb);
#define error(msg) {Serial.print("Error: "); Serial.println(msg);}
#define TIMEOUT_MILLIS 4000
//------------------------------------------------------------------------------
bool initUSB(USB* usb) {
uint8_t current_state = 0;
uint32_t m = millis();
for (uint8_t i = 0; usb->Init(1000) == -1; i++)
{
SERIAL_ECHOLNPGM("No USB HOST Shield?");
watchdog_reset();
if (i > 10) {
return false;
}
}
usb->vbusPower(vbus_on);
while ((millis() - m) < TIMEOUT_MILLIS) {
usb->Task();
current_state = usb->getUsbTaskState();
if(current_state == USB_STATE_RUNNING) {
return true;
}
watchdog_reset();
}
return false;
}
Sd2Card::Sd2Card() {
};
bool Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
if (!initUSB(&usb))
{
SERIAL_ECHOLNPGM("initUSB failed");
}
else
{
SERIAL_ECHOLNPGM("USB Initialized\n");
}
if(!bulk.LUNIsGood(0)) {
SERIAL_ECHOLNPGM("LUN zero is not good\n");
return false;
}
SERIAL_ECHOLNPAIR("LUN Capacity: ",bulk.GetCapacity(0));
const uint32_t sectorSize = bulk.GetSectorSize(0);
if(sectorSize != 512) {
SERIAL_ECHOLNPAIR("Expecting sector size of 512, got: ",sectorSize);
return false;
}
return true;
}
bool Sd2Card::readBlock(uint32_t block, uint8_t* dst) {
return bulk.Read(0, block, 512, 1, dst) == 0;
}
bool Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
return bulk.Write(0, blockNumber, 512, 1, src) == 0;
}

@ -0,0 +1,812 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
/* USB functions */
#include "Usb.h"
static uint8_t usb_error = 0;
static uint8_t usb_task_state;
/* constructor */
USB::USB() : bmHubPre(0) {
usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine
init();
}
/* Initialize data structures */
void USB::init() {
//devConfigIndex = 0;
bmHubPre = 0;
}
uint8_t USB::getUsbTaskState(void) {
return ( usb_task_state);
}
void USB::setUsbTaskState(uint8_t state) {
usb_task_state = state;
}
EpInfo* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) {
UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
if(!p || !p->epinfo)
return NULL;
EpInfo *pep = p->epinfo;
for(uint8_t i = 0; i < p->epcount; i++) {
if((pep)->epAddr == ep)
return pep;
pep++;
}
return NULL;
}
/* set device table entry */
/* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */
uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) {
if(!eprecord_ptr)
return USB_ERROR_INVALID_ARGUMENT;
UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
if(!p)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
p->address.devAddress = addr;
p->epinfo = eprecord_ptr;
p->epcount = epcount;
return 0;
}
uint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t &nak_limit) {
UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
if(!p)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
if(!p->epinfo)
return USB_ERROR_EPINFO_IS_NULL;
*ppep = getEpInfoEntry(addr, ep);
if(!*ppep)
return USB_ERROR_EP_NOT_FOUND_IN_TBL;
nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower));
nak_limit--;
/*
USBTRACE2("\r\nAddress: ", addr);
USBTRACE2(" EP: ", ep);
USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower);
USBTRACE2(" NAK Limit: ", nak_limit);
USBTRACE("\r\n");
*/
regWr(rPERADDR, addr); //set peripheral address
uint8_t mode = regRd(rMODE);
//Serial.print("\r\nMode: ");
//Serial.println( mode, HEX);
//Serial.print("\r\nLS: ");
//Serial.println(p->lowspeed, HEX);
// Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise
regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED));
return 0;
}
/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */
/* depending on request. Actual requests are defined as inlines */
/* return codes: */
/* 00 = success */
/* 01-0f = non-zero HRSLT */
uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) {
bool direction = false; //request direction, IN or OUT
uint8_t rcode;
SETUP_PKT setup_pkt;
EpInfo *pep = NULL;
uint16_t nak_limit = 0;
rcode = SetAddress(addr, ep, &pep, nak_limit);
if(rcode)
return rcode;
direction = ((bmReqType & 0x80) > 0);
/* fill in setup packet */
setup_pkt.ReqType_u.bmRequestType = bmReqType;
setup_pkt.bRequest = bRequest;
setup_pkt.wVal_u.wValueLo = wValLo;
setup_pkt.wVal_u.wValueHi = wValHi;
setup_pkt.wIndex = wInd;
setup_pkt.wLength = total;
bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); //transfer to setup packet FIFO
rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet
if(rcode) //return HRSLT if not zero
return ( rcode);
if(dataptr != NULL) //data stage, if present
{
if(direction) //IN transfer
{
uint16_t left = total;
pep->bmRcvToggle = 1; //bmRCVTOG1;
while(left) {
// Bytes read into buffer
uint16_t read = nbytes;
//uint16_t read = (left<nbytes) ? left : nbytes;
rcode = InTransfer(pep, nak_limit, &read, dataptr);
if(rcode == hrTOGERR) {
// yes, we flip it wrong here so that next time it is actually correct!
pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
continue;
}
if(rcode)
return rcode;
// Invoke callback function if inTransfer completed successfully and callback function pointer is specified
if(!rcode && p)
((USBReadParser*)p)->Parse(read, dataptr, total - left);
left -= read;
if(read < nbytes)
break;
}
} else //OUT transfer
{
pep->bmSndToggle = 1; //bmSNDTOG1;
rcode = OutTransfer(pep, nak_limit, nbytes, dataptr);
}
if(rcode) //return error
return ( rcode);
}
// Status stage
return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction
}
/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
/* Keep sending INs and writes data to memory area pointed by 'data' */
/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error,
fe USB xfer timeout */
uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data) {
EpInfo *pep = NULL;
uint16_t nak_limit = 0;
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit);
if(rcode) {
USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81);
USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81);
USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81);
return rcode;
}
return InTransfer(pep, nak_limit, nbytesptr, data);
}
uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) {
uint8_t rcode = 0;
uint8_t pktsize;
uint16_t nbytes = *nbytesptr;
//printf("Requesting %i bytes ", nbytes);
uint8_t maxpktsize = pep->maxPktSize;
*nbytesptr = 0;
regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value
// use a 'break' to exit this loop
while(1) {
rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS.
if(rcode == hrTOGERR) {
// yes, we flip it wrong here so that next time it is actually correct!
pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value
continue;
}
if(rcode) {
//printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode);
break; //should be 0, indicating ACK. Else return error code.
}
/* check for RCVDAVIRQ and generate error if not present */
/* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */
if((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) {
//printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n");
rcode = 0xf0; //receive error
break;
}
pktsize = regRd(rRCVBC); //number of received bytes
//printf("Got %i bytes \r\n", pktsize);
// This would be OK, but...
//assert(pktsize <= nbytes);
if(pktsize > nbytes) {
// This can happen. Use of assert on Arduino locks up the Arduino.
// So I will trim the value, and hope for the best.
//printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize);
pktsize = nbytes;
}
int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr);
if(mem_left < 0)
mem_left = 0;
data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data);
regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer
*nbytesptr += pktsize; // add this packet's byte count to total transfer length
/* The transfer is complete under two conditions: */
/* 1. The device sent a short packet (L.T. maxPacketSize) */
/* 2. 'nbytes' have been transferred. */
if((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes?
{
// Save toggle value
pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0;
//printf("\r\n");
rcode = 0;
break;
} // if
} //while( 1 )
return ( rcode);
}
/* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
/* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer */
/* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */
uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) {
EpInfo *pep = NULL;
uint16_t nak_limit = 0;
uint8_t rcode = SetAddress(addr, ep, &pep, nak_limit);
if(rcode)
return rcode;
return OutTransfer(pep, nak_limit, nbytes, data);
}
uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) {
uint8_t rcode = hrSUCCESS, retry_count;
uint8_t *data_p = data; //local copy of the data pointer
uint16_t bytes_tosend, nak_count;
uint16_t bytes_left = nbytes;
uint8_t maxpktsize = pep->maxPktSize;
if(maxpktsize < 1 || maxpktsize > 64)
return USB_ERROR_INVALID_MAX_PKT_SIZE;
unsigned long timeout = millis() + USB_XFER_TIMEOUT;
regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value
while(bytes_left) {
retry_count = 0;
nak_count = 0;
bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;
bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO
regWr(rSNDBC, bytes_tosend); //set number of bytes
regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet
while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ
regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ
rcode = (regRd(rHRSL) & 0x0f);
while(rcode && ((long)(millis() - timeout) < 0L)) {
switch(rcode) {
case hrNAK:
nak_count++;
if(nak_limit && (nak_count == nak_limit))
goto breakout;
//return ( rcode);
break;
case hrTIMEOUT:
retry_count++;
if(retry_count == USB_RETRY_LIMIT)
goto breakout;
//return ( rcode);
break;
case hrTOGERR:
// yes, we flip it wrong here so that next time it is actually correct!
pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value
break;
default:
goto breakout;
}//switch( rcode
/* process NAK according to Host out NAK bug */
regWr(rSNDBC, 0);
regWr(rSNDFIFO, *data_p);
regWr(rSNDBC, bytes_tosend);
regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet
while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ
regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ
rcode = (regRd(rHRSL) & 0x0f);
}//while( rcode && ....
bytes_left -= bytes_tosend;
data_p += bytes_tosend;
}//while( bytes_left...
breakout:
pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle
return ( rcode); //should be 0 in all cases
}
/* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty */
/* If NAK, tries to re-send up to nak_limit times */
/* If nak_limit == 0, do not count NAKs, exit after timeout */
/* If bus timeout, re-sends up to USB_RETRY_LIMIT times */
/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */
uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) {
unsigned long timeout = millis() + USB_XFER_TIMEOUT;
uint8_t tmpdata;
uint8_t rcode = hrSUCCESS;
uint8_t retry_count = 0;
uint16_t nak_count = 0;
while((long)(millis() - timeout) < 0L) {
regWr(rHXFR, (token | ep)); //launch the transfer
rcode = USB_ERROR_TRANSFER_TIMEOUT;
while((long)(millis() - timeout) < 0L) //wait for transfer completion
{
tmpdata = regRd(rHIRQ);
if(tmpdata & bmHXFRDNIRQ) {
regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt
rcode = 0x00;
break;
}//if( tmpdata & bmHXFRDNIRQ
}//while ( millis() < timeout
//if (rcode != 0x00) //exit if timeout
// return ( rcode);
rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result
switch(rcode) {
case hrNAK:
nak_count++;
if(nak_limit && (nak_count == nak_limit))
return (rcode);
break;
case hrTIMEOUT:
retry_count++;
if(retry_count == USB_RETRY_LIMIT)
return (rcode);
break;
default:
return (rcode);
}//switch( rcode
}//while( timeout > millis()
return ( rcode);
}
/* USB main task. Performs enumeration/cleanup */
void USB::Task(void) //USB state machine
{
uint8_t rcode;
uint8_t tmpdata;
static unsigned long delay = 0;
//USB_DEVICE_DESCRIPTOR buf;
bool lowspeed = false;
MAX3421E::Task();
tmpdata = getVbusState();
/* modify USB task state if Vbus changed */
switch(tmpdata) {
case SE1: //illegal state
usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL;
lowspeed = false;
break;
case SE0: //disconnected
if((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED)
usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
lowspeed = false;
break;
case LSHOST:
lowspeed = true;
//intentional fallthrough
case FSHOST: //attached
if((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) {
delay = millis() + USB_SETTLE_DELAY;
usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
}
break;
}// switch( tmpdata
for(uint8_t i = 0; i < USB_NUMDEVICES; i++)
if(devConfig[i])
rcode = devConfig[i]->Poll();
switch(usb_task_state) {
case USB_DETACHED_SUBSTATE_INITIALIZE:
init();
for(uint8_t i = 0; i < USB_NUMDEVICES; i++)
if(devConfig[i])
rcode = devConfig[i]->Release();
usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE;
break;
case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here
break;
case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here
break;
case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device
if((long)(millis() - delay) >= 0L)
usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE;
else break; // don't fall through
case USB_ATTACHED_SUBSTATE_RESET_DEVICE:
regWr(rHCTL, bmBUSRST); //issue bus reset
usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE;
break;
case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE:
if((regRd(rHCTL) & bmBUSRST) == 0) {
tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation
regWr(rMODE, tmpdata);
usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF;
//delay = millis() + 20; //20ms wait after reset per USB spec
}
break;
case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order
if(regRd(rHIRQ) & bmFRAMEIRQ) {
//when first SOF received _and_ 20ms has passed we can continue
/*
if (delay < millis()) //20ms passed
usb_task_state = USB_STATE_CONFIGURING;
*/
usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET;
delay = millis() + 20;
}
break;
case USB_ATTACHED_SUBSTATE_WAIT_RESET:
if((long)(millis() - delay) >= 0L) usb_task_state = USB_STATE_CONFIGURING;
else break; // don't fall through
case USB_STATE_CONFIGURING:
//Serial.print("\r\nConf.LS: ");
//Serial.println(lowspeed, HEX);
rcode = Configuring(0, 0, lowspeed);
if(rcode) {
if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) {
usb_error = rcode;
usb_task_state = USB_STATE_ERROR;
}
} else
usb_task_state = USB_STATE_RUNNING;
break;
case USB_STATE_RUNNING:
break;
case USB_STATE_ERROR:
//MAX3421E::Init();
break;
} // switch( usb_task_state )
}
uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) {
//uint8_t buf[12];
uint8_t rcode;
UsbDevice *p0 = NULL, *p = NULL;
// Get pointer to pseudo device with address 0 assigned
p0 = addrPool.GetUsbDevicePtr(0);
if(!p0)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
if(!p0->epinfo)
return USB_ERROR_EPINFO_IS_NULL;
p0->lowspeed = (lowspeed) ? true : false;
// Allocate new address according to device class
uint8_t bAddress = addrPool.AllocAddress(parent, false, port);
if(!bAddress)
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
p = addrPool.GetUsbDevicePtr(bAddress);
if(!p)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
p->lowspeed = lowspeed;
// Assign new address to the device
rcode = setAddr(0, 0, bAddress);
if(rcode) {
addrPool.FreeAddress(bAddress);
bAddress = 0;
return rcode;
}
return 0;
};
uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) {
//printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port);
uint8_t retries = 0;
again:
uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed);
if(rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) {
if(parent == 0) {
// Send a bus reset on the root interface.
regWr(rHCTL, bmBUSRST); //issue bus reset
DELAY(102); // delay 102ms, compensate for clock inaccuracy.
} else {
// reset parent port
devConfig[parent]->ResetHubPort(port);
}
} else if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works
DELAY(100);
retries++;
goto again;
} else if(rcode)
return rcode;
rcode = devConfig[driver]->Init(parent, port, lowspeed);
if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works
DELAY(100);
retries++;
goto again;
}
if(rcode) {
// Issue a bus reset, because the device may be in a limbo state
if(parent == 0) {
// Send a bus reset on the root interface.
regWr(rHCTL, bmBUSRST); //issue bus reset
DELAY(102); // delay 102ms, compensate for clock inaccuracy.
} else {
// reset parent port
devConfig[parent]->ResetHubPort(port);
}
}
return rcode;
}
/*
* This is broken. We need to enumerate differently.
* It causes major problems with several devices if detected in an unexpected order.
*
*
* Oleg - I wouldn't do anything before the newly connected device is considered sane.
* i.e.(delays are not indicated for brevity):
* 1. reset
* 2. GetDevDescr();
* 3a. If ACK, continue with allocating address, addressing, etc.
* 3b. Else reset again, count resets, stop at some number (5?).
* 4. When max.number of resets is reached, toggle power/fail
* If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD()
* it doesn't need to be reset again
* New steps proposal:
* 1: get address pool instance. exit on fail
* 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail.
* 3: bus reset, 100ms delay
* 4: set address
* 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail
* 6: while (configurations) {
* for(each configuration) {
* for (each driver) {
* 6a: Ask device if it likes configuration. Returns 0 on OK.
* If successful, the driver configured device.
* The driver now owns the endpoints, and takes over managing them.
* The following will need codes:
* Everything went well, instance consumed, exit with success.
* Instance already in use, ignore it, try next driver.
* Not a supported device, ignore it, try next driver.
* Not a supported configuration for this device, ignore it, try next driver.
* Could not configure device, fatal, exit with fail.
* }
* }
* }
* 7: for(each driver) {
* 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID
* 8: if we get here, no driver likes the device plugged in, so exit failure.
*
*/
uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) {
//uint8_t bAddress = 0;
//printf("Configuring: parent = %i, port = %i\r\n", parent, port);
uint8_t devConfigIndex;
uint8_t rcode = 0;
uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf);
UsbDevice *p = NULL;
EpInfo *oldep_ptr = NULL;
EpInfo epInfo;
epInfo.epAddr = 0;
epInfo.maxPktSize = 8;
epInfo.epAttribs = 0;
epInfo.bmNakPower = USB_NAK_MAX_POWER;
//DELAY(2000);
AddressPool &addrPool = GetAddressPool();
// Get pointer to pseudo device with address 0 assigned
p = addrPool.GetUsbDevicePtr(0);
if(!p) {
//printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n");
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
}
// Save old pointer to EP_RECORD of address 0
oldep_ptr = p->epinfo;
// Temporary assign new pointer to epInfo to p->epinfo in order to
// avoid toggle inconsistence
p->epinfo = &epInfo;
p->lowspeed = lowspeed;
// Get device descriptor
rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);
// Restore p->epinfo
p->epinfo = oldep_ptr;
if(rcode) {
//printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n");
return rcode;
}
// to-do?
// Allocate new address according to device class
//bAddress = addrPool.AllocAddress(parent, false, port);
uint16_t vid = udd->idVendor;
uint16_t pid = udd->idProduct;
uint8_t klass = udd->bDeviceClass;
uint8_t subklass = udd->bDeviceSubClass;
// Attempt to configure if VID/PID or device class matches with a driver
// Qualify with subclass too.
//
// VID/PID & class tests default to false for drivers not yet ported
// subclass defaults to true, so you don't have to define it if you don't have to.
//
for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
if(!devConfig[devConfigIndex]) continue; // no driver
if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed
if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) {
rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)
break;
}
}
if(devConfigIndex < USB_NUMDEVICES) {
return rcode;
}
// blindly attempt to configure
for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {
if(!devConfig[devConfigIndex]) continue;
if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed
if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above
rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
//printf("ERROR ENUMERATING %2.2x\r\n", rcode);
if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) {
// in case of an error dev_index should be reset to 0
// in order to start from the very beginning the
// next time the program gets here
//if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
// devConfigIndex = 0;
return rcode;
}
}
// if we get here that means that the device class is not supported by any of registered classes
rcode = DefaultAddressing(parent, port, lowspeed);
return rcode;
}
uint8_t USB::ReleaseDevice(uint8_t addr) {
if(!addr)
return 0;
for(uint8_t i = 0; i < USB_NUMDEVICES; i++) {
if(!devConfig[i]) continue;
if(devConfig[i]->GetAddress() == addr)
return devConfig[i]->Release();
}
return 0;
}
#if 1 //!defined(USB_METHODS_INLINE)
//get device descriptor
uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL));
}
//get configuration descriptor
uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL));
}
/* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this
total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */
uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) {
const uint8_t bufSize = 64;
uint8_t buf[bufSize];
USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);
uint8_t ret = getConfDescr(addr, ep, 9, conf, buf);
if(ret)
return ret;
uint16_t total = ucd->wTotalLength;
//USBTRACE2("\r\ntotal conf.size:", total);
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p));
}
//get string descriptor
uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL));
}
//set address
uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL);
//DELAY(2); //per USB 2.0 sect.9.2.6.3
DELAY(300); // Older spec says you should wait at least 200ms
return rcode;
//return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));
}
//set configuration
uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));
}
#endif // defined(USB_METHODS_INLINE)

@ -0,0 +1,39 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
/* USB functions */
#ifndef _usb_h_
#define _usb_h_
// WARNING: Do not change the order of includes, or stuff will break!
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
// None of these should ever be included by a driver, or a user's sketch.
#include "settings.h"
#include "printhex.h"
#include "message.h"
#include "max3421e.h"
#include "address.h"
#include "avrpins.h"
#include "usb_ch9.h"
#include "usbhost.h"
#include "UsbCore.h"
#include "parsetools.h"
#include "confdescparser.h"
#endif //_usb_h_

@ -0,0 +1,296 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(USBCORE_H)
#error "Never include UsbCore.h directly; include Usb.h instead"
#else
#define USBCORE_H
// Not used anymore? If anyone uses this, please let us know so that this may be
// moved to the proper place, settings.h.
//#define USB_METHODS_INLINE
/* shield pins. First parameter - SS pin, second parameter - INT pin */
#ifdef BOARD_BLACK_WIDDOW
typedef MAX3421e<P6, P3> MAX3421E; // Black Widow
#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
#if EXT_RAM
typedef MAX3421e<P20, P7> MAX3421E; // Teensy++ 2.0 with XMEM2
#else
typedef MAX3421e<P9, P8> MAX3421E; // Teensy++ 1.0 and 2.0
#endif
#elif defined(BOARD_MEGA_ADK)
typedef MAX3421e<P53, P54> MAX3421E; // Arduino Mega ADK
#elif defined(ARDUINO_AVR_BALANDUINO)
typedef MAX3421e<P20, P19> MAX3421E; // Balanduino
#else
typedef MAX3421e<P10, P9> MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0
#endif
/* Common setup data constant combinations */
#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //get descriptor request type
#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE //set request type for all but 'set feature' and 'set interface'
#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE //get interface request type
// D7 data transfer direction (0 - host-to-device, 1 - device-to-host)
// D6-5 Type (0- standard, 1 - class, 2 - vendor, 3 - reserved)
// D4-0 Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved)
// USB Device Classes
#define USB_CLASS_USE_CLASS_INFO 0x00 // Use Class Info in the Interface Descriptors
#define USB_CLASS_AUDIO 0x01 // Audio
#define USB_CLASS_COM_AND_CDC_CTRL 0x02 // Communications and CDC Control
#define USB_CLASS_HID 0x03 // HID
#define USB_CLASS_PHYSICAL 0x05 // Physical
#define USB_CLASS_IMAGE 0x06 // Image
#define USB_CLASS_PRINTER 0x07 // Printer
#define USB_CLASS_MASS_STORAGE 0x08 // Mass Storage
#define USB_CLASS_HUB 0x09 // Hub
#define USB_CLASS_CDC_DATA 0x0a // CDC-Data
#define USB_CLASS_SMART_CARD 0x0b // Smart-Card
#define USB_CLASS_CONTENT_SECURITY 0x0d // Content Security
#define USB_CLASS_VIDEO 0x0e // Video
#define USB_CLASS_PERSONAL_HEALTH 0x0f // Personal Healthcare
#define USB_CLASS_DIAGNOSTIC_DEVICE 0xdc // Diagnostic Device
#define USB_CLASS_WIRELESS_CTRL 0xe0 // Wireless Controller
#define USB_CLASS_MISC 0xef // Miscellaneous
#define USB_CLASS_APP_SPECIFIC 0xfe // Application Specific
#define USB_CLASS_VENDOR_SPECIFIC 0xff // Vendor Specific
// Additional Error Codes
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED 0xD1
#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE 0xD2
#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS 0xD3
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL 0xD4
#define USB_ERROR_HUB_ADDRESS_OVERFLOW 0xD5
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL 0xD6
#define USB_ERROR_EPINFO_IS_NULL 0xD7
#define USB_ERROR_INVALID_ARGUMENT 0xD8
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE 0xD9
#define USB_ERROR_INVALID_MAX_PKT_SIZE 0xDA
#define USB_ERROR_EP_NOT_FOUND_IN_TBL 0xDB
#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET 0xE0
#define USB_ERROR_FailGetDevDescr 0xE1
#define USB_ERROR_FailSetDevTblEntry 0xE2
#define USB_ERROR_FailGetConfDescr 0xE3
#define USB_ERROR_TRANSFER_TIMEOUT 0xFF
#define USB_XFER_TIMEOUT 10000 //30000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec
//#define USB_NAK_LIMIT 32000 //NAK limit for a transfer. 0 means NAKs are not counted
#define USB_RETRY_LIMIT 3 // 3 retry limit for a transfer
#define USB_SETTLE_DELAY 200 //settle delay in milliseconds
#define USB_NUMDEVICES 16 //number of USB devices
//#define HUB_MAX_HUBS 7 // maximum number of hubs that can be attached to the host controller
#define HUB_PORT_RESET_DELAY 20 // hub port reset delay 10 ms recomended, can be up to 20 ms
/* USB state machine states */
#define USB_STATE_MASK 0xf0
#define USB_STATE_DETACHED 0x10
#define USB_DETACHED_SUBSTATE_INITIALIZE 0x11
#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x12
#define USB_DETACHED_SUBSTATE_ILLEGAL 0x13
#define USB_ATTACHED_SUBSTATE_SETTLE 0x20
#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x30
#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x40
#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x50
#define USB_ATTACHED_SUBSTATE_WAIT_RESET 0x51
#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x60
#define USB_STATE_ADDRESSING 0x70
#define USB_STATE_CONFIGURING 0x80
#define USB_STATE_RUNNING 0x90
#define USB_STATE_ERROR 0xa0
class USBDeviceConfig {
public:
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed) {
return 0;
}
virtual uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
return 0;
}
virtual uint8_t Release() {
return 0;
}
virtual uint8_t Poll() {
return 0;
}
virtual uint8_t GetAddress() {
return 0;
}
virtual void ResetHubPort(uint8_t port) {
return;
} // Note used for hubs only!
virtual boolean VIDPIDOK(uint16_t vid, uint16_t pid) {
return false;
}
virtual boolean DEVCLASSOK(uint8_t klass) {
return false;
}
virtual boolean DEVSUBCLASSOK(uint8_t subklass) {
return true;
}
};
/* USB Setup Packet Structure */
typedef struct {
union { // offset description
uint8_t bmRequestType; // 0 Bit-map of request type
struct {
uint8_t recipient : 5; // Recipient of the request
uint8_t type : 2; // Type of request
uint8_t direction : 1; // Direction of data X-fer
} __attribute__((packed));
} ReqType_u;
uint8_t bRequest; // 1 Request
union {
uint16_t wValue; // 2 Depends on bRequest
struct {
uint8_t wValueLo;
uint8_t wValueHi;
} __attribute__((packed));
} wVal_u;
uint16_t wIndex; // 4 Depends on bRequest
uint16_t wLength; // 6 Depends on bRequest
} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;
// Base class for incoming data parser
class USBReadParser {
public:
virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0;
};
class USB : public MAX3421E {
AddressPoolImpl<USB_NUMDEVICES> addrPool;
USBDeviceConfig* devConfig[USB_NUMDEVICES];
uint8_t bmHubPre;
public:
USB(void);
void SetHubPreMask() {
bmHubPre |= bmHUBPRE;
};
void ResetHubPreMask() {
bmHubPre &= (~bmHUBPRE);
};
AddressPool& GetAddressPool() {
return (AddressPool&)addrPool;
};
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev) {
for(uint8_t i = 0; i < USB_NUMDEVICES; i++) {
if(!devConfig[i]) {
devConfig[i] = pdev;
return 0;
}
}
return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS;
};
void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
addrPool.ForEachUsbDevice(pfunc);
};
uint8_t getUsbTaskState(void);
void setUsbTaskState(uint8_t state);
EpInfo* getEpInfoEntry(uint8_t addr, uint8_t ep);
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr);
/* Control requests */
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr);
uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr);
uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p);
uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t* dataptr);
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr);
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value);
/**/
uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr, boolean direction);
uint8_t ctrlStatus(uint8_t ep, boolean direction, uint16_t nak_limit);
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data);
uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data);
uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);
void Task(void);
uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed);
uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed);
uint8_t ReleaseDevice(uint8_t addr);
uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p);
private:
void init();
uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t &nak_limit);
uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data);
uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed);
};
#if 0 //defined(USB_METHODS_INLINE)
//get device descriptor
inline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr));
}
//get configuration descriptor
inline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr));
}
//get string descriptor
inline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) {
return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr));
}
//set address
inline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL));
}
//set configuration
inline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL));
}
#endif // defined(USB_METHODS_INLINE)
#endif /* USBCORE_H */

@ -0,0 +1,147 @@
/********************************************************************************************
* This program/sketch is used to run a USB Thumb Drive. *
* *
* NOTE - This Arduino Sketch has been modified to initialize a MAX3421E USB Host Interface *
* chip, write 3 test files, print out the directory of the thumb drive and print out the *
* contents of a short .txt file. *
* *
* The code is leveraged from the following: *
* *
* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. *
* *
* This software may be distributed and modified under the terms of the GNU *
* General Public License version 2 (GPL2) as published by the Free Software *
* Foundation and appearing in the file GPL2.TXT included in the packaging of *
* this file. Please note that GPL2 Section 2[b] requires that all works based *
* on this software must also be made publicly available under the terms of *
* the GPL2 ("Copyleft"). *
* *
* Contact information *
* ------------------- *
* *
* Circuits At Home, LTD *
* Web : http://www.circuitsathome.com *
* e-mail : support@circuitsathome.com *
* *
* SPECIAL NOTE - In order to work with a modified Eisny or RAMBo, the SPI chip select pin *
* (CS) (D10) has been remapped from PORTB Pin-4 to PORTB Pin-0. This has been done in the *
* __AVR_ATmega2560__ section of the avrpins.h file. *
* *
********************************************************************************************/
#include <SPI.h>
#include "masstorage.h"
#define MAX_USB_RST 7
// USB host objects.v
USB usb;
BulkOnly bulk(&usb);
#define error(msg) {Serial.print("Error: "); Serial.println(msg);}
#define TIMEOUT_MILLIS 4000
//------------------------------------------------------------------------------
bool initUSB(USB* usb) {
uint8_t last_state = 0;
uint8_t current_state = 0;
uint32_t m = millis();
for (uint8_t i = 0; usb->Init(1000) == -1; i++)
{
//if (USB_FAT_DBG_MODE) {
Serial.println(F("No USB HOST Shield?"));
//}
if (i > 10) {
return false;
}
}
#if USB_FAT_DBG_MODE
Serial.print(F("Host initialized, ms: "));
Serial.println(millis() - m);
#endif // USB_FAT_DBG_MODE
usb->vbusPower(vbus_on);
#if USB_FAT_DBG_MODE
Serial.print(F("USB powered, ms: "));
Serial.println(millis() - m);
#endif // USB_FAT_DBG_MODE
while ((millis() - m) < TIMEOUT_MILLIS) {
usb->Task();
current_state = usb->getUsbTaskState();
#if USB_FAT_DBG_MODE
if (last_state != current_state) {
Serial.print(F("USB state: "));
Serial.print(current_state, HEX);
Serial.print(F(", ms: "));
Serial.println(millis() - m);
}
last_state = current_state;
#endif // USB_FAT_DBG_MODE
if(current_state == USB_STATE_RUNNING) {
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
void setup()
{
pinMode(MAX_USB_RST, OUTPUT);
digitalWrite(MAX_USB_RST, HIGH);
Serial.begin(9600);
Serial.print("USB THUMB DRIVE FILE TEST\n\n");
Serial.print("Initializing The USB Bus\n");
// Initialize the USB bus.
if (!initUSB(&usb))
{
error("initUSB failed");
}
else
{
Serial.print("USB Initialized\n");
}
if(bulk.LUNIsGood(0)) {
Serial.print("LUN Capacity: ");
Serial.println(bulk.GetCapacity(0));
const uint32_t sectorSize = bulk.GetSectorSize(0);
Serial.print("Sector Size: ");
Serial.println(sectorSize);
uint8_t buf[512];
const uint8_t lun = 0;
const uint32_t addr = 0;
if(bulk.Read(lun, addr, sectorSize, 1, buf) == 0) {
Serial.print("Read a block: \n");
Serial.println((char*)buf);
} else {
Serial.print("Failed to a read block\n");
}
const char *message = PSTR("This is a test of writing raw data!");
strcpy_P(buf, message);
if(bulk.Write(lun, addr, sectorSize, 1, buf) == 0) {
Serial.print("Wrote a block\n");
} else {
Serial.print("Failed to write a block\n");
}
} else {
Serial.print("LUN zero is not good\n");
}
}
//------------------------------------------------------------------------------
void loop () {}

@ -0,0 +1,282 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(__ADDRESS_H__)
#error "Never include address.h directly; include Usb.h instead"
#else
#define __ADDRESS_H__
/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */
/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */
#define USB_NAK_MAX_POWER 15 //NAK binary order maximum value
#define USB_NAK_DEFAULT 14 //default 32K-1 NAKs before giving up
#define USB_NAK_NOWAIT 1 //Single NAK stops transfer
#define USB_NAK_NONAK 0 //Do not count NAKs, stop retrying after USB Timeout
struct EpInfo {
uint8_t epAddr; // Endpoint address
uint8_t maxPktSize; // Maximum packet size
union {
uint8_t epAttribs;
struct {
uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise
uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise
uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value
} __attribute__((packed));
};
} __attribute__((packed));
// 7 6 5 4 3 2 1 0
// ---------------------------------
// | | H | P | P | P | A | A | A |
// ---------------------------------
//
// H - if 1 the address is a hub address
// P - parent hub address
// A - device address / port number in case of hub
//
struct UsbDeviceAddress {
union {
struct {
uint8_t bmAddress : 3; // device address/port number
uint8_t bmParent : 3; // parent hub address
uint8_t bmHub : 1; // hub flag
uint8_t bmReserved : 1; // reserved, must be zero
} __attribute__((packed));
uint8_t devAddress;
};
} __attribute__((packed));
#define bmUSB_DEV_ADDR_ADDRESS 0x07
#define bmUSB_DEV_ADDR_PARENT 0x38
#define bmUSB_DEV_ADDR_HUB 0x40
struct UsbDevice {
EpInfo *epinfo; // endpoint info pointer
UsbDeviceAddress address;
uint8_t epcount; // number of endpoints
bool lowspeed; // indicates if a device is the low speed one
// uint8_t devclass; // device class
} __attribute__((packed));
class AddressPool {
public:
virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0;
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0;
virtual void FreeAddress(uint8_t addr) = 0;
};
typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);
#define ADDR_ERROR_INVALID_INDEX 0xFF
#define ADDR_ERROR_INVALID_ADDRESS 0xFF
template <const uint8_t MAX_DEVICES_ALLOWED>
class AddressPoolImpl : public AddressPool {
EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device
uint8_t hubCounter; // hub counter is kept
// in order to avoid hub address duplication
UsbDevice thePool[MAX_DEVICES_ALLOWED];
// Initializes address pool entry
void InitEntry(uint8_t index) {
thePool[index].address.devAddress = 0;
thePool[index].epcount = 1;
thePool[index].lowspeed = 0;
thePool[index].epinfo = &dev0ep;
};
// Returns thePool index for a given address
uint8_t FindAddressIndex(uint8_t address = 0) {
for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) {
if(thePool[i].address.devAddress == address)
return i;
}
return 0;
};
// Returns thePool child index for a given parent
uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) {
for(uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) {
if(thePool[i].address.bmParent == addr.bmAddress)
return i;
}
return 0;
};
// Frees address entry specified by index parameter
void FreeAddressByIndex(uint8_t index) {
// Zero field is reserved and should not be affected
if(index == 0)
return;
UsbDeviceAddress uda = thePool[index].address;
// If a hub was switched off all port addresses should be freed
if(uda.bmHub == 1) {
for(uint8_t i = 1; (i = FindChildIndex(uda, i));)
FreeAddressByIndex(i);
// If the hub had the last allocated address, hubCounter should be decremented
if(hubCounter == uda.bmAddress)
hubCounter--;
}
InitEntry(index);
}
// Initializes the whole address pool at once
void InitAllAddresses() {
for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
InitEntry(i);
hubCounter = 0;
};
public:
AddressPoolImpl() : hubCounter(0) {
// Zero address is reserved
InitEntry(0);
thePool[0].address.devAddress = 0;
thePool[0].epinfo = &dev0ep;
dev0ep.epAddr = 0;
dev0ep.maxPktSize = 8;
dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0
dev0ep.bmNakPower = USB_NAK_MAX_POWER;
InitAllAddresses();
};
// Returns a pointer to a specified address entry
virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) {
if(!addr)
return thePool;
uint8_t index = FindAddressIndex(addr);
return (!index) ? NULL : thePool + index;
};
// Performs an operation specified by pfunc for each addressed device
void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
if(!pfunc)
return;
for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
if(thePool[i].address.devAddress)
pfunc(thePool + i);
};
// Allocates new address
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) {
/* if (parent != 0 && port == 0)
USB_HOST_SERIAL.println("PRT:0"); */
UsbDeviceAddress _parent;
_parent.devAddress = parent;
if(_parent.bmReserved || port > 7)
//if(parent > 127 || port > 7)
return 0;
if(is_hub && hubCounter == 7)
return 0;
// finds first empty address entry starting from one
uint8_t index = FindAddressIndex(0);
if(!index) // if empty entry is not found
return 0;
if(_parent.devAddress == 0) {
if(is_hub) {
thePool[index].address.devAddress = 0x41;
hubCounter++;
} else
thePool[index].address.devAddress = 1;
return thePool[index].address.devAddress;
}
UsbDeviceAddress addr;
addr.devAddress = 0; // Ensure all bits are zero
addr.bmParent = _parent.bmAddress;
if(is_hub) {
addr.bmHub = 1;
addr.bmAddress = ++hubCounter;
} else {
addr.bmHub = 0;
addr.bmAddress = port;
}
thePool[index].address = addr;
/*
USB_HOST_SERIAL.print("Addr:");
USB_HOST_SERIAL.print(addr.bmHub, HEX);
USB_HOST_SERIAL.print(".");
USB_HOST_SERIAL.print(addr.bmParent, HEX);
USB_HOST_SERIAL.print(".");
USB_HOST_SERIAL.println(addr.bmAddress, HEX);
*/
return thePool[index].address.devAddress;
};
// Empties pool entry
virtual void FreeAddress(uint8_t addr) {
// if the root hub is disconnected all the addresses should be initialized
if(addr == 0x41) {
InitAllAddresses();
return;
}
uint8_t index = FindAddressIndex(addr);
FreeAddressByIndex(index);
};
// Returns number of hubs attached
// It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs.
//uint8_t GetNumHubs()
//{
// return hubCounter;
//};
//uint8_t GetNumDevices()
//{
// uint8_t counter = 0;
// for (uint8_t i=1; i<MAX_DEVICES_ALLOWED; i++)
// if (thePool[i].address != 0);
// counter ++;
// return counter;
//};
};
#endif // __ADDRESS_H__

@ -0,0 +1,1024 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
/* derived from Konstantin Chizhov's AVR port templates */
#if !defined(_usb_h_) || defined(_avrpins_h_)
#error "Never include avrpins.h directly; include Usb.h instead"
#else
#define _avrpins_h_
#if defined(__AVR__)
// pointers are 16 bits on AVR
#define pgm_read_pointer(p) pgm_read_word(p)
// Support for these boards needs to be manually activated in settings.h or in a makefile
#if !defined(BOARD_MEGA_ADK) && defined(__AVR_ATmega2560__) && (USE_UHS_MEGA_ADK || defined(ARDUINO_AVR_ADK))
#define BOARD_MEGA_ADK
#elif !defined(BOARD_BLACK_WIDDOW) && USE_UHS_BLACK_WIDDOW
#define BOARD_BLACK_WIDDOW
#endif
#ifdef PORTA
#define USE_PORTA
#endif
#ifdef PORTB
#define USE_PORTB
#endif
#ifdef PORTC
#define USE_PORTC
#endif
#ifdef PORTD
#define USE_PORTD
#endif
#ifdef PORTE
#define USE_PORTE
#endif
#ifdef PORTF
#define USE_PORTF
#endif
#ifdef PORTG
#define USE_PORTG
#endif
#ifdef PORTH
#define USE_PORTH
#endif
#ifdef PORTJ
#define USE_PORTJ
#endif
#ifdef PORTK
#define USE_PORTK
#endif
#ifdef PORTL
#define USE_PORTL
#endif
#ifdef PORTQ
#define USE_PORTQ
#endif
#ifdef PORTR
#define USE_PORTR
#endif
#ifdef TCCR0A
#define USE_TCCR0A
#endif
#ifdef TCCR1A
#define USE_TCCR1A
#endif
#ifdef TCCR2A
#define USE_TCCR2A
#endif
//Port definitions for AtTiny, AtMega families.
#define MAKE_PORT(portName, ddrName, pinName, className, ID) \
class className{\
public:\
typedef uint8_t DataT;\
public:\
static void Write(DataT value){portName = value;}\
static void ClearAndSet(DataT clearMask, DataT value){portName = (portName & ~clearMask) | value;}\
static DataT Read(){return portName;}\
static void DirWrite(DataT value){ddrName = value;}\
static DataT DirRead(){return ddrName;}\
static void Set(DataT value){portName |= value;}\
static void Clear(DataT value){portName &= ~value;}\
static void Toggle(DataT value){portName ^= value;}\
static void DirSet(DataT value){ddrName |= value;}\
static void DirClear(DataT value){ddrName &= ~value;}\
static void DirToggle(DataT value){ddrName ^= value;}\
static DataT PinRead(){return pinName;}\
enum{Id = ID};\
enum{Width=sizeof(DataT)*8};\
};
// TCCR registers to set/clear Arduino PWM
#define MAKE_TCCR(TccrName, className) \
class className{\
public:\
typedef uint8_t DataT;\
public:\
static void Write(DataT value){TccrName = value;}\
static void ClearAndSet(DataT clearMask, DataT value){TccrName = (TccrName & ~clearMask) | value;}\
static DataT Read(){return TccrName;}\
static void Set(DataT value){TccrName |= value;}\
static void Clear(DataT value){TccrName &= ~value;}\
static void Toggle(DataT value){TccrName ^= value;}\
enum{Width=sizeof(DataT)*8};\
};
#ifdef USE_PORTA
MAKE_PORT(PORTA, DDRA, PINA, Porta, 'A')
#endif
#ifdef USE_PORTB
MAKE_PORT(PORTB, DDRB, PINB, Portb, 'B')
#endif
#ifdef USE_PORTC
MAKE_PORT(PORTC, DDRC, PINC, Portc, 'C')
#endif
#ifdef USE_PORTD
MAKE_PORT(PORTD, DDRD, PIND, Portd, 'D')
#endif
#ifdef USE_PORTE
MAKE_PORT(PORTE, DDRE, PINE, Porte, 'E')
#endif
#ifdef USE_PORTF
MAKE_PORT(PORTF, DDRF, PINF, Portf, 'F')
#endif
#ifdef USE_PORTG
MAKE_PORT(PORTG, DDRG, PING, Portg, 'G')
#endif
#ifdef USE_PORTH
MAKE_PORT(PORTH, DDRH, PINH, Porth, 'H')
#endif
#ifdef USE_PORTJ
MAKE_PORT(PORTJ, DDRJ, PINJ, Portj, 'J')
#endif
#ifdef USE_PORTK
MAKE_PORT(PORTK, DDRK, PINK, Portk, 'K')
#endif
#ifdef USE_PORTL
MAKE_PORT(PORTL, DDRL, PINL, Portl, 'L')
#endif
#ifdef USE_PORTQ
MAKE_PORT(PORTQ, DDRQ, PINQ, Portq, 'Q')
#endif
#ifdef USE_PORTR
MAKE_PORT(PORTR, DDRR, PINR, Portr, 'R')
#endif
#ifdef USE_TCCR0A
MAKE_TCCR(TCCR0A, Tccr0a)
#endif
#ifdef USE_TCCR1A
MAKE_TCCR(TCCR1A, Tccr1a)
#endif
#ifdef USE_TCCR2A
MAKE_TCCR(TCCR2A, Tccr2a)
#endif
// this class represents one pin in a IO port.
// It is fully static.
template<typename PORT, uint8_t PIN>
class TPin {
// BOOST_STATIC_ASSERT(PIN < PORT::Width);
public:
typedef PORT Port;
enum {
Number = PIN
};
static void Set() {
PORT::Set(1 << PIN);
}
static void Set(uint8_t val) {
if(val)
Set();
else Clear();
}
static void SetDir(uint8_t val) {
if(val)
SetDirWrite();
else SetDirRead();
}
static void Clear() {
PORT::Clear(1 << PIN);
}
static void Toggle() {
PORT::Toggle(1 << PIN);
}
static void SetDirRead() {
PORT::DirClear(1 << PIN);
}
static void SetDirWrite() {
PORT::DirSet(1 << PIN);
}
static uint8_t IsSet() {
return PORT::PinRead() & (uint8_t)(1 << PIN);
}
static void WaiteForSet() {
while(IsSet() == 0) {
}
}
static void WaiteForClear() {
while(IsSet()) {
}
}
}; //class TPin...
// this class represents one bit in TCCR port.
// used to set/clear TCCRx bits
// It is fully static.
template<typename TCCR, uint8_t COM>
class TCom {
// BOOST_STATIC_ASSERT(PIN < PORT::Width);
public:
typedef TCCR Tccr;
enum {
Com = COM
};
static void Set() {
TCCR::Set(1 << COM);
}
static void Clear() {
TCCR::Clear(1 << COM);
}
static void Toggle() {
TCCR::Toggle(1 << COM);
}
}; //class TCom...
//Short pin definitions
#ifdef USE_PORTA
typedef TPin<Porta, 0 > Pa0;
typedef TPin<Porta, 1 > Pa1;
typedef TPin<Porta, 2 > Pa2;
typedef TPin<Porta, 3 > Pa3;
typedef TPin<Porta, 4 > Pa4;
typedef TPin<Porta, 5 > Pa5;
typedef TPin<Porta, 6 > Pa6;
typedef TPin<Porta, 7 > Pa7;
#endif
#ifdef USE_PORTB
typedef TPin<Portb, 0 > Pb0;
typedef TPin<Portb, 1 > Pb1;
typedef TPin<Portb, 2 > Pb2;
typedef TPin<Portb, 3 > Pb3;
typedef TPin<Portb, 4 > Pb4;
typedef TPin<Portb, 5 > Pb5;
typedef TPin<Portb, 6 > Pb6;
typedef TPin<Portb, 7 > Pb7;
#endif
#ifdef USE_PORTC
typedef TPin<Portc, 0 > Pc0;
typedef TPin<Portc, 1 > Pc1;
typedef TPin<Portc, 2 > Pc2;
typedef TPin<Portc, 3 > Pc3;
typedef TPin<Portc, 4 > Pc4;
typedef TPin<Portc, 5 > Pc5;
typedef TPin<Portc, 6 > Pc6;
typedef TPin<Portc, 7 > Pc7;
#endif
#ifdef USE_PORTD
typedef TPin<Portd, 0 > Pd0;
typedef TPin<Portd, 1 > Pd1;
typedef TPin<Portd, 2 > Pd2;
typedef TPin<Portd, 3 > Pd3;
typedef TPin<Portd, 4 > Pd4;
typedef TPin<Portd, 5 > Pd5;
typedef TPin<Portd, 6 > Pd6;
typedef TPin<Portd, 7 > Pd7;
#endif
#ifdef USE_PORTE
typedef TPin<Porte, 0 > Pe0;
typedef TPin<Porte, 1 > Pe1;
typedef TPin<Porte, 2 > Pe2;
typedef TPin<Porte, 3 > Pe3;
typedef TPin<Porte, 4 > Pe4;
typedef TPin<Porte, 5 > Pe5;
typedef TPin<Porte, 6 > Pe6;
typedef TPin<Porte, 7 > Pe7;
#endif
#ifdef USE_PORTF
typedef TPin<Portf, 0 > Pf0;
typedef TPin<Portf, 1 > Pf1;
typedef TPin<Portf, 2 > Pf2;
typedef TPin<Portf, 3 > Pf3;
typedef TPin<Portf, 4 > Pf4;
typedef TPin<Portf, 5 > Pf5;
typedef TPin<Portf, 6 > Pf6;
typedef TPin<Portf, 7 > Pf7;
#endif
#ifdef USE_PORTG
typedef TPin<Portg, 0 > Pg0;
typedef TPin<Portg, 1 > Pg1;
typedef TPin<Portg, 2 > Pg2;
typedef TPin<Portg, 3 > Pg3;
typedef TPin<Portg, 4 > Pg4;
typedef TPin<Portg, 5 > Pg5;
typedef TPin<Portg, 6 > Pg6;
typedef TPin<Portg, 7 > Pg7;
#endif
#ifdef USE_PORTH
typedef TPin<Porth, 0 > Ph0;
typedef TPin<Porth, 1 > Ph1;
typedef TPin<Porth, 2 > Ph2;
typedef TPin<Porth, 3 > Ph3;
typedef TPin<Porth, 4 > Ph4;
typedef TPin<Porth, 5 > Ph5;
typedef TPin<Porth, 6 > Ph6;
typedef TPin<Porth, 7 > Ph7;
#endif
#ifdef USE_PORTJ
typedef TPin<Portj, 0 > Pj0;
typedef TPin<Portj, 1 > Pj1;
typedef TPin<Portj, 2 > Pj2;
typedef TPin<Portj, 3 > Pj3;
typedef TPin<Portj, 4 > Pj4;
typedef TPin<Portj, 5 > Pj5;
typedef TPin<Portj, 6 > Pj6;
typedef TPin<Portj, 7 > Pj7;
#endif
#ifdef USE_PORTK
typedef TPin<Portk, 0 > Pk0;
typedef TPin<Portk, 1 > Pk1;
typedef TPin<Portk, 2 > Pk2;
typedef TPin<Portk, 3 > Pk3;
typedef TPin<Portk, 4 > Pk4;
typedef TPin<Portk, 5 > Pk5;
typedef TPin<Portk, 6 > Pk6;
typedef TPin<Portk, 7 > Pk7;
#endif
#ifdef USE_PORTL
typedef TPin<Portl, 0 > Pl0;
typedef TPin<Portl, 1 > Pl1;
typedef TPin<Portl, 2 > Pl2;
typedef TPin<Portl, 3 > Pl3;
typedef TPin<Portl, 4 > Pl4;
typedef TPin<Portl, 5 > Pl5;
typedef TPin<Portl, 6 > Pl6;
typedef TPin<Portl, 7 > Pl7;
#endif
#ifdef USE_PORTQ
typedef TPin<Portq, 0 > Pq0;
typedef TPin<Portq, 1 > Pq1;
typedef TPin<Portq, 2 > Pq2;
typedef TPin<Portq, 3 > Pq3;
typedef TPin<Portq, 4 > Pq4;
typedef TPin<Portq, 5 > Pq5;
typedef TPin<Portq, 6 > Pq6;
typedef TPin<Portq, 7 > Pq7;
#endif
#ifdef USE_PORTR
typedef TPin<Portr, 0 > Pr0;
typedef TPin<Portr, 1 > Pr1;
typedef TPin<Portr, 2 > Pr2;
typedef TPin<Portr, 3 > Pr3;
typedef TPin<Portr, 4 > Pr4;
typedef TPin<Portr, 5 > Pr5;
typedef TPin<Portr, 6 > Pr6;
typedef TPin<Portr, 7 > Pr7;
#endif
#ifdef USE_TCCR0A
typedef TCom<Tccr0a, COM0A1> Tc0a; //P6
typedef TCom<Tccr0a, COM0B1> Tc0b; //P5
#endif
#ifdef USE_TCCR1A
typedef TCom<Tccr1a, COM1A1> Tc1a; //P9
typedef TCom<Tccr1a, COM1B1> Tc1b; //P10
#endif
#ifdef USE_TCCR2A
typedef TCom<Tccr2a, COM2A1> Tc2a; //P11
typedef TCom<Tccr2a, COM2B1> Tc2b; //P3
#endif
template<typename Tp_pin, typename Tc_bit>
class Tp_Tc {
public:
static void SetDir(uint8_t val) {
if(val)
SetDirWrite();
else SetDirRead();
}
static void SetDirRead() {
Tp_pin::SetDirRead(); //set pin direction
Tc_bit::Clear(); //disconnect pin from PWM
}
static void SetDirWrite() {
Tp_pin::SetDirWrite();
Tc_bit::Clear();
}
};
/* pin definitions for cases where it's necessary to clear compare output mode bits */
//typedef Tp_Tc<Pd3, Tc2b> P3; //Arduino pin 3
//typedef Tp_Tc<Pd5, Tc0b> P5; //Arduino pin 5
//typedef Tp_Tc<Pd6, Tc0a> P6; //Arduino pin 6
//typedef Tp_Tc<Pb1, Tc1a> P9; //Arduino pin 9
//typedef Tp_Tc<Pb2, Tc1b> P10; //Arduino pin 10
//typedef Tp_Tc<Pb3, Tc2a> P11; //Arduino pin 11
/* Arduino pin definitions */
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// "Mega" Arduino pin numbers
#define P0 Pe0
#define P1 Pe1
#define P2 Pe4
#define P3 Pe5
#define P4 Pg5
#define P5 Pe3
#define P6 Ph3
#define P7 Ph4
#define P8 Ph5
#define P9 Ph6
//#define P10 Pb4 //Pb4 - Regular Arduino
#define P10 Pb0 //Pb0 - Einsy Board
#define P11 Pb5
#define P12 Pb6
#define P13 Pb7
#define P14 Pj1
#define P15 Pj0
#define P16 Ph1
#define P17 Ph0
#define P18 Pd3
#define P19 Pd2
#define P20 Pd1
#define P21 Pd0
#define P22 Pa0
#define P23 Pa1
#define P24 Pa2
#define P25 Pa3
#define P26 Pa4
#define P27 Pa5
#define P28 Pa6
#define P29 Pa7
#define P30 Pc7
#define P31 Pc6
#define P32 Pc5
#define P33 Pc4
#define P34 Pc3
#define P35 Pc2
#define P36 Pc1
#define P37 Pc0
#define P38 Pd7
#define P39 Pg2
#define P40 Pg1
#define P41 Pg0
#define P42 Pl7
#define P43 Pl6
#define P44 Pl5
#define P45 Pl4
#define P46 Pl3
#define P47 Pl2
#define P48 Pl1
#define P49 Pl0
#define P50 Pb3
#define P51 Pb2
#define P52 Pb1
// #define P53 Pb0
#ifdef BOARD_MEGA_ADK // These pins are not broken out on the Arduino ADK
#define P54 Pe6 // INT on Arduino ADK
#define P55 Pj2 // MAX_RESET on Arduino ADK
#endif
// "Mega" pin numbers
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
// "Classic" Arduino pin numbers
#define P0 Pd0
#define P1 Pd1
#define P2 Pd2
#define P3 Pd3
#define P4 Pd4
#define P5 Pd5
#define P6 Pd6
#define P7 Pd7
#define P8 Pb0
#define P9 Pb1
#define P10 Pb2
#define P11 Pb3
#define P12 Pb4
#define P13 Pb5
#define P14 Pc0
#define P15 Pc1
#define P16 Pc2
#define P17 Pc3
#define P18 Pc4
#define P19 Pc5
// "Classic" Arduino pin numbers
#elif defined(CORE_TEENSY) && defined(__AVR_ATmega32U4__)
// Teensy 2.0 pin numbers
// http://www.pjrc.com/teensy/pinout.html
#define P0 Pb0
#define P1 Pb1
#define P2 Pb2
#define P3 Pb3
#define P4 Pb7
#define P5 Pd0
#define P6 Pd1
#define P7 Pd2
#define P8 Pd3
#define P9 Pc6
#define P10 Pc7
#define P11 Pd6
#define P12 Pd7
#define P13 Pb4
#define P14 Pb5
#define P15 Pb6
#define P16 Pf7
#define P17 Pf6
#define P18 Pf5
#define P19 Pf4
#define P20 Pf1
#define P21 Pf0
#define P22 Pd4
#define P23 Pd5
#define P24 Pe6
// Teensy 2.0
#elif defined(__AVR_ATmega32U4__)
// Arduino Leonardo pin numbers
#define P0 Pd2 // D0 - PD2
#define P1 Pd3 // D1 - PD3
#define P2 Pd1 // D2 - PD1
#define P3 Pd0 // D3 - PD0
#define P4 Pd4 // D4 - PD4
#define P5 Pc6 // D5 - PC6
#define P6 Pd7 // D6 - PD7
#define P7 Pe6 // D7 - PE6
#define P8 Pb4 // D8 - PB4
#define P9 Pb5 // D9 - PB5
#define P10 Pb6 // D10 - PB6
#define P11 Pb7 // D11 - PB7
#define P12 Pd6 // D12 - PD6
#define P13 Pc7 // D13 - PC7
#define P14 Pb3 // D14 - MISO - PB3
#define P15 Pb1 // D15 - SCK - PB1
#define P16 Pb2 // D16 - MOSI - PB2
#define P17 Pb0 // D17 - SS - PB0
#define P18 Pf7 // D18 - A0 - PF7
#define P19 Pf6 // D19 - A1 - PF6
#define P20 Pf5 // D20 - A2 - PF5
#define P21 Pf4 // D21 - A3 - PF4
#define P22 Pf1 // D22 - A4 - PF1
#define P23 Pf0 // D23 - A5 - PF0
#define P24 Pd4 // D24 / D4 - A6 - PD4
#define P25 Pd7 // D25 / D6 - A7 - PD7
#define P26 Pb4 // D26 / D8 - A8 - PB4
#define P27 Pb5 // D27 / D9 - A9 - PB5
#define P28 Pb6 // D28 / D10 - A10 - PB6
#define P29 Pd6 // D29 / D12 - A11 - PD6
// Arduino Leonardo pin numbers
#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
// Teensy++ 1.0 and 2.0 pin numbers
// http://www.pjrc.com/teensy/pinout.html
#define P0 Pd0
#define P1 Pd1
#define P2 Pd2
#define P3 Pd3
#define P4 Pd4
#define P5 Pd5
#define P6 Pd6
#define P7 Pd7
#define P8 Pe0
#define P9 Pe1
#define P10 Pc0
#define P11 Pc1
#define P12 Pc2
#define P13 Pc3
#define P14 Pc4
#define P15 Pc5
#define P16 Pc6
#define P17 Pc7
#define P18 Pe6
#define P19 Pe7
#define P20 Pb0
#define P21 Pb1
#define P22 Pb2
#define P23 Pb3
#define P24 Pb4
#define P25 Pb5
#define P26 Pb6
#define P27 Pb7
#define P28 Pa0
#define P29 Pa1
#define P30 Pa2
#define P31 Pa3
#define P32 Pa4
#define P33 Pa5
#define P34 Pa6
#define P35 Pa7
#define P36 Pe4
#define P37 Pe5
#define P38 Pf0
#define P39 Pf1
#define P40 Pf2
#define P41 Pf3
#define P42 Pf4
#define P43 Pf5
#define P44 Pf6
#define P45 Pf7
// Teensy++ 1.0 and 2.0
#elif defined(ARDUINO_AVR_BALANDUINO) && (defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284P__))
// Balanduino pin numbers
// http://balanduino.net/
#define P0 Pd0 /* 0 - PD0 */
#define P1 Pd1 /* 1 - PD1 */
#if BALANDUINO_REVISION < 13
#define P2 Pb2 /* 2 - PB2 */
#define P3 Pd6 /* 3 - PD6 */
#define P4 Pd7 /* 4 - PD7 */
#define P5 Pb3 /* 5 - PB3 */
#else
#define P2 Pd2 /* 2 - PD2 */
#define P3 Pd3 /* 3 - PD3 */
#define P4 Pd6 /* 4 - PD6 */
#define P5 Pd7 /* 5 - PD7 */
#endif
#define P6 Pb4 /* 6 - PB4 */
#define P7 Pa0 /* 7 - PA0 */
#define P8 Pa1 /* 8 - PA1 */
#define P9 Pa2 /* 9 - PA2 */
#define P10 Pa3 /* 10 - PA3 */
#define P11 Pa4 /* 11 - PA4 */
#define P12 Pa5 /* 12 - PA5 */
#define P13 Pc1 /* 13 - PC1 */
#define P14 Pc0 /* 14 - PC0 */
#if BALANDUINO_REVISION < 13
#define P15 Pd2 /* 15 - PD2 */
#define P16 Pd3 /* 16 - PD3 */
#else
#define P15 Pb2 /* 15 - PB2 */
#define P16 Pb3 /* 16 - PB2 */
#endif
#define P17 Pd4 /* 17 - PD4 */
#define P18 Pd5 /* 18 - PD5 */
#define P19 Pc2 /* 19 - PC2 */
#define P20 Pc3 /* 20 - PC3 */
#define P21 Pc4 /* 21 - PC4 */
#define P22 Pc5 /* 22 - PC5 */
#define P23 Pc6 /* 23 - PC6 */
#define P24 Pc7 /* 24 - PC7 */
#define P25 Pb0 /* 25 - PB0 */
#define P26 Pb1 /* 26 - PB1 */
#define P27 Pb5 /* 27 - PB5 */
#define P28 Pb6 /* 28 - PB6 */
#define P29 Pb7 /* 29 - PB7 */
#define P30 Pa6 /* 30 - PA6 */
#define P31 Pa7 /* 31 - PA7 */
// Balanduino
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
// Sanguino pin numbers
// Homepage: http://sanguino.cc/hardware
// Hardware add-on: https://github.com/Lauszus/Sanguino
#define P0 Pb0
#define P1 Pb1
#define P2 Pb2
#define P3 Pb3
#define P4 Pb4
#define P5 Pb5
#define P6 Pb6
#define P7 Pb7
#define P8 Pd0
#define P9 Pd1
#define P10 Pd2
#define P11 Pd3
#define P12 Pd4
#define P13 Pd5
#define P14 Pd6
#define P15 Pd7
#define P16 Pc0
#define P17 Pc1
#define P18 Pc2
#define P19 Pc3
#define P20 Pc4
#define P21 Pc5
#define P22 Pc6
#define P23 Pc7
#define P24 Pa0
#define P25 Pa1
#define P26 Pa2
#define P27 Pa3
#define P28 Pa4
#define P29 Pa5
#define P30 Pa6
#define P31 Pa7
// Sanguino
#else
#error "Please define board in avrpins.h"
#endif // Arduino pin definitions
#endif // __AVR__
#if defined(__arm__)
// pointers are 32 bits on ARM
#define pgm_read_pointer(p) pgm_read_dword(p)
#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))
#include "core_pins.h"
#include "avr_emulation.h"
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
#define GPIO_BITBAND_PTR(reg, bit) ((uint8_t *)GPIO_BITBAND_ADDR((reg), (bit)))
#define MAKE_PIN(className, baseReg, pinNum, configReg) \
class className { \
public: \
static void Set() { \
*GPIO_BITBAND_PTR(baseReg, pinNum) = 1; \
} \
static void Clear() { \
*GPIO_BITBAND_PTR(baseReg, pinNum) = 0; \
} \
static void SetDirRead() { \
configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \
*(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 0; \
} \
static void SetDirWrite() { \
configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \
*(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 1; \
} \
static uint8_t IsSet() { \
return *(GPIO_BITBAND_PTR(baseReg, pinNum) + 512); \
} \
};
MAKE_PIN(P0, CORE_PIN0_PORTREG, CORE_PIN0_BIT, CORE_PIN0_CONFIG);
MAKE_PIN(P1, CORE_PIN1_PORTREG, CORE_PIN1_BIT, CORE_PIN1_CONFIG);
MAKE_PIN(P2, CORE_PIN2_PORTREG, CORE_PIN2_BIT, CORE_PIN2_CONFIG);
MAKE_PIN(P3, CORE_PIN3_PORTREG, CORE_PIN3_BIT, CORE_PIN3_CONFIG);
MAKE_PIN(P4, CORE_PIN4_PORTREG, CORE_PIN4_BIT, CORE_PIN4_CONFIG);
MAKE_PIN(P5, CORE_PIN5_PORTREG, CORE_PIN5_BIT, CORE_PIN5_CONFIG);
MAKE_PIN(P6, CORE_PIN6_PORTREG, CORE_PIN6_BIT, CORE_PIN6_CONFIG);
MAKE_PIN(P7, CORE_PIN7_PORTREG, CORE_PIN7_BIT, CORE_PIN7_CONFIG);
MAKE_PIN(P8, CORE_PIN8_PORTREG, CORE_PIN8_BIT, CORE_PIN8_CONFIG);
MAKE_PIN(P9, CORE_PIN9_PORTREG, CORE_PIN9_BIT, CORE_PIN9_CONFIG);
MAKE_PIN(P10, CORE_PIN10_PORTREG, CORE_PIN10_BIT, CORE_PIN10_CONFIG);
MAKE_PIN(P11, CORE_PIN11_PORTREG, CORE_PIN11_BIT, CORE_PIN11_CONFIG);
MAKE_PIN(P12, CORE_PIN12_PORTREG, CORE_PIN12_BIT, CORE_PIN12_CONFIG);
MAKE_PIN(P13, CORE_PIN13_PORTREG, CORE_PIN13_BIT, CORE_PIN13_CONFIG);
MAKE_PIN(P14, CORE_PIN14_PORTREG, CORE_PIN14_BIT, CORE_PIN14_CONFIG);
MAKE_PIN(P15, CORE_PIN15_PORTREG, CORE_PIN15_BIT, CORE_PIN15_CONFIG);
MAKE_PIN(P16, CORE_PIN16_PORTREG, CORE_PIN16_BIT, CORE_PIN16_CONFIG);
MAKE_PIN(P17, CORE_PIN17_PORTREG, CORE_PIN17_BIT, CORE_PIN17_CONFIG);
MAKE_PIN(P18, CORE_PIN18_PORTREG, CORE_PIN18_BIT, CORE_PIN18_CONFIG);
MAKE_PIN(P19, CORE_PIN19_PORTREG, CORE_PIN19_BIT, CORE_PIN19_CONFIG);
MAKE_PIN(P20, CORE_PIN20_PORTREG, CORE_PIN20_BIT, CORE_PIN20_CONFIG);
MAKE_PIN(P21, CORE_PIN21_PORTREG, CORE_PIN21_BIT, CORE_PIN21_CONFIG);
MAKE_PIN(P22, CORE_PIN22_PORTREG, CORE_PIN22_BIT, CORE_PIN22_CONFIG);
MAKE_PIN(P23, CORE_PIN23_PORTREG, CORE_PIN23_BIT, CORE_PIN23_CONFIG);
MAKE_PIN(P24, CORE_PIN24_PORTREG, CORE_PIN24_BIT, CORE_PIN24_CONFIG);
MAKE_PIN(P25, CORE_PIN25_PORTREG, CORE_PIN25_BIT, CORE_PIN25_CONFIG);
MAKE_PIN(P26, CORE_PIN26_PORTREG, CORE_PIN26_BIT, CORE_PIN26_CONFIG);
MAKE_PIN(P27, CORE_PIN27_PORTREG, CORE_PIN27_BIT, CORE_PIN27_CONFIG);
MAKE_PIN(P28, CORE_PIN28_PORTREG, CORE_PIN28_BIT, CORE_PIN28_CONFIG);
MAKE_PIN(P29, CORE_PIN29_PORTREG, CORE_PIN29_BIT, CORE_PIN29_CONFIG);
MAKE_PIN(P30, CORE_PIN30_PORTREG, CORE_PIN30_BIT, CORE_PIN30_CONFIG);
MAKE_PIN(P31, CORE_PIN31_PORTREG, CORE_PIN31_BIT, CORE_PIN31_CONFIG);
MAKE_PIN(P32, CORE_PIN32_PORTREG, CORE_PIN32_BIT, CORE_PIN32_CONFIG);
MAKE_PIN(P33, CORE_PIN33_PORTREG, CORE_PIN33_BIT, CORE_PIN33_CONFIG);
#undef MAKE_PIN
#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)
// SetDirRead:
// Disable interrupts
// Disable the pull up resistor
// Set to INPUT
// Enable PIO
// SetDirWrite:
// Disable interrupts
// Disable the pull up resistor
// Set to OUTPUT
// Enable PIO
#define MAKE_PIN(className, pio, pinMask) \
class className { \
public: \
static void Set() { \
pio->PIO_SODR = pinMask; \
} \
static void Clear() { \
pio->PIO_CODR = pinMask; \
} \
static void SetDirRead() { \
pio->PIO_IDR = pinMask ; \
pio->PIO_PUDR = pinMask; \
pio->PIO_ODR = pinMask; \
pio->PIO_PER = pinMask; \
} \
static void SetDirWrite() { \
pio->PIO_IDR = pinMask ; \
pio->PIO_PUDR = pinMask; \
pio->PIO_OER = pinMask; \
pio->PIO_PER = pinMask; \
} \
static uint8_t IsSet() { \
return pio->PIO_PDSR & pinMask; \
} \
};
// See: http://arduino.cc/en/Hacking/PinMappingSAM3X and variant.cpp
MAKE_PIN(P0, PIOA, PIO_PA8);
MAKE_PIN(P1, PIOA, PIO_PA9);
MAKE_PIN(P2, PIOB, PIO_PB25);
MAKE_PIN(P3, PIOC, PIO_PC28);
MAKE_PIN(P4, PIOC, PIO_PC26);
MAKE_PIN(P5, PIOC, PIO_PC25);
MAKE_PIN(P6, PIOC, PIO_PC24);
MAKE_PIN(P7, PIOC, PIO_PC23);
MAKE_PIN(P8, PIOC, PIO_PC22);
MAKE_PIN(P9, PIOC, PIO_PC21);
MAKE_PIN(P10, PIOC, PIO_PC29);
MAKE_PIN(P11, PIOD, PIO_PD7);
MAKE_PIN(P12, PIOD, PIO_PD8);
MAKE_PIN(P13, PIOB, PIO_PB27);
MAKE_PIN(P14, PIOD, PIO_PD4);
MAKE_PIN(P15, PIOD, PIO_PD5);
MAKE_PIN(P16, PIOA, PIO_PA13);
MAKE_PIN(P17, PIOA, PIO_PA12);
MAKE_PIN(P18, PIOA, PIO_PA11);
MAKE_PIN(P19, PIOA, PIO_PA10);
MAKE_PIN(P20, PIOB, PIO_PB12);
MAKE_PIN(P21, PIOB, PIO_PB13);
MAKE_PIN(P22, PIOB, PIO_PB26);
MAKE_PIN(P23, PIOA, PIO_PA14);
MAKE_PIN(P24, PIOA, PIO_PA15);
MAKE_PIN(P25, PIOD, PIO_PD0);
MAKE_PIN(P26, PIOD, PIO_PD1);
MAKE_PIN(P27, PIOD, PIO_PD2);
MAKE_PIN(P28, PIOD, PIO_PD3);
MAKE_PIN(P29, PIOD, PIO_PD6);
MAKE_PIN(P30, PIOD, PIO_PD9);
MAKE_PIN(P31, PIOA, PIO_PA7);
MAKE_PIN(P32, PIOD, PIO_PD10);
MAKE_PIN(P33, PIOC, PIO_PC1);
MAKE_PIN(P34, PIOC, PIO_PC2);
MAKE_PIN(P35, PIOC, PIO_PC3);
MAKE_PIN(P36, PIOC, PIO_PC4);
MAKE_PIN(P37, PIOC, PIO_PC5);
MAKE_PIN(P38, PIOC, PIO_PC6);
MAKE_PIN(P39, PIOC, PIO_PC7);
MAKE_PIN(P40, PIOC, PIO_PC8);
MAKE_PIN(P41, PIOC, PIO_PC9);
MAKE_PIN(P42, PIOA, PIO_PA19);
MAKE_PIN(P43, PIOA, PIO_PA20);
MAKE_PIN(P44, PIOC, PIO_PC19);
MAKE_PIN(P45, PIOC, PIO_PC18);
MAKE_PIN(P46, PIOC, PIO_PC17);
MAKE_PIN(P47, PIOC, PIO_PC16);
MAKE_PIN(P48, PIOC, PIO_PC15);
MAKE_PIN(P49, PIOC, PIO_PC14);
MAKE_PIN(P50, PIOC, PIO_PC13);
MAKE_PIN(P51, PIOC, PIO_PC12);
MAKE_PIN(P52, PIOB, PIO_PB21);
MAKE_PIN(P53, PIOB, PIO_PB14);
MAKE_PIN(P54, PIOA, PIO_PA16);
MAKE_PIN(P55, PIOA, PIO_PA24);
MAKE_PIN(P56, PIOA, PIO_PA23);
MAKE_PIN(P57, PIOA, PIO_PA22);
MAKE_PIN(P58, PIOA, PIO_PA6);
MAKE_PIN(P59, PIOA, PIO_PA4);
MAKE_PIN(P60, PIOA, PIO_PA3);
MAKE_PIN(P61, PIOA, PIO_PA2);
MAKE_PIN(P62, PIOB, PIO_PB17);
MAKE_PIN(P63, PIOB, PIO_PB18);
MAKE_PIN(P64, PIOB, PIO_PB19);
MAKE_PIN(P65, PIOB, PIO_PB20);
MAKE_PIN(P66, PIOB, PIO_PB15);
MAKE_PIN(P67, PIOB, PIO_PB16);
MAKE_PIN(P68, PIOA, PIO_PA1);
MAKE_PIN(P69, PIOA, PIO_PA0);
MAKE_PIN(P70, PIOA, PIO_PA17);
MAKE_PIN(P71, PIOA, PIO_PA18);
MAKE_PIN(P72, PIOC, PIO_PC30);
MAKE_PIN(P73, PIOA, PIO_PA21);
MAKE_PIN(P74, PIOA, PIO_PA25); // MISO
MAKE_PIN(P75, PIOA, PIO_PA26); // MOSI
MAKE_PIN(P76, PIOA, PIO_PA27); // CLK
MAKE_PIN(P77, PIOA, PIO_PA28);
MAKE_PIN(P78, PIOB, PIO_PB23); // Unconnected
#undef MAKE_PIN
#elif defined(RBL_NRF51822)
#define MAKE_PIN(className, pin) \
class className { \
public: \
static void Set() { \
nrf_gpio_pin_set(pin); \
} \
static void Clear() { \
nrf_gpio_pin_clear(pin); \
} \
static void SetDirRead() { \
nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL); \
} \
static void SetDirWrite() { \
nrf_gpio_cfg_output(pin); \
} \
static uint8_t IsSet() { \
return (uint8_t)nrf_gpio_pin_read(pin); \
} \
};
// See: pin_transform.c in RBL nRF51822 SDK
MAKE_PIN(P0, Pin_nRF51822_to_Arduino(D0));
MAKE_PIN(P1, Pin_nRF51822_to_Arduino(D1));
MAKE_PIN(P2, Pin_nRF51822_to_Arduino(D2));
MAKE_PIN(P3, Pin_nRF51822_to_Arduino(D3));
MAKE_PIN(P4, Pin_nRF51822_to_Arduino(D4));
MAKE_PIN(P5, Pin_nRF51822_to_Arduino(D5));
MAKE_PIN(P6, Pin_nRF51822_to_Arduino(D6));
MAKE_PIN(P7, Pin_nRF51822_to_Arduino(D7));
MAKE_PIN(P8, Pin_nRF51822_to_Arduino(D8));
MAKE_PIN(P9, Pin_nRF51822_to_Arduino(D9)); // INT
MAKE_PIN(P10, Pin_nRF51822_to_Arduino(D10)); // SS
MAKE_PIN(P11, Pin_nRF51822_to_Arduino(D11));
MAKE_PIN(P12, Pin_nRF51822_to_Arduino(D12));
MAKE_PIN(P13, Pin_nRF51822_to_Arduino(D13));
MAKE_PIN(P14, Pin_nRF51822_to_Arduino(D14));
MAKE_PIN(P15, Pin_nRF51822_to_Arduino(D15));
MAKE_PIN(P17, Pin_nRF51822_to_Arduino(D17)); // MISO
MAKE_PIN(P18, Pin_nRF51822_to_Arduino(D18)); // MOSI
MAKE_PIN(P16, Pin_nRF51822_to_Arduino(D16)); // CLK
MAKE_PIN(P19, Pin_nRF51822_to_Arduino(D19));
MAKE_PIN(P20, Pin_nRF51822_to_Arduino(D20));
MAKE_PIN(P21, Pin_nRF51822_to_Arduino(D21));
MAKE_PIN(P22, Pin_nRF51822_to_Arduino(D22));
MAKE_PIN(P23, Pin_nRF51822_to_Arduino(D23));
MAKE_PIN(P24, Pin_nRF51822_to_Arduino(D24));
#undef MAKE_PIN
#else
#error "Please define board in avrpins.h"
#endif
#endif // __arm__
#endif //_avrpins_h_

@ -0,0 +1,217 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(__CONFDESCPARSER_H__)
#error "Never include confdescparser.h directly; include Usb.h instead"
#else
#define __CONFDESCPARSER_H__
class UsbConfigXtracter {
public:
//virtual void ConfigXtract(const USB_CONFIGURATION_DESCRIPTOR *conf) = 0;
//virtual void InterfaceXtract(uint8_t conf, const USB_INTERFACE_DESCRIPTOR *iface) = 0;
virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep) = 0;
};
#define CP_MASK_COMPARE_CLASS 1
#define CP_MASK_COMPARE_SUBCLASS 2
#define CP_MASK_COMPARE_PROTOCOL 4
#define CP_MASK_COMPARE_ALL 7
// Configuration Descriptor Parser Class Template
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
class ConfigDescParser : public USBReadParser {
UsbConfigXtracter *theXtractor;
MultiValueBuffer theBuffer;
MultiByteValueParser valParser;
ByteSkipper theSkipper;
uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/];
uint8_t stateParseDescr; // ParseDescriptor state
uint8_t dscrLen; // Descriptor length
uint8_t dscrType; // Descriptor type
bool isGoodInterface; // Apropriate interface flag
uint8_t confValue; // Configuration value
uint8_t protoValue; // Protocol value
uint8_t ifaceNumber; // Interface number
uint8_t ifaceAltSet; // Interface alternate settings
bool UseOr;
bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn);
void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);
public:
void SetOR(void) {
UseOr = true;
}
ConfigDescParser(UsbConfigXtracter *xtractor);
virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);
};
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :
theXtractor(xtractor),
stateParseDescr(0),
dscrLen(0),
dscrType(0),
UseOr(false) {
theBuffer.pValue = varBuffer;
valParser.Initialize(&theBuffer);
theSkipper.Initialize(&theBuffer);
};
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) {
uint16_t cntdn = (uint16_t)len;
uint8_t *p = (uint8_t*)pbuf;
while(cntdn)
if(!ParseDescriptor(&p, &cntdn))
return;
}
/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and
compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) {
USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(varBuffer);
USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_INTERFACE_DESCRIPTOR*>(varBuffer);
switch(stateParseDescr) {
case 0:
theBuffer.valueSize = 2;
valParser.Initialize(&theBuffer);
stateParseDescr = 1;
case 1:
if(!valParser.Parse(pp, pcntdn))
return false;
dscrLen = *((uint8_t*)theBuffer.pValue);
dscrType = *((uint8_t*)theBuffer.pValue + 1);
stateParseDescr = 2;
case 2:
// This is a sort of hack. Assuming that two bytes are all ready in the buffer
// the pointer is positioned two bytes ahead in order for the rest of descriptor
// to be read right after the size and the type fields.
// This should be used carefully. varBuffer should be used directly to handle data
// in the buffer.
theBuffer.pValue = varBuffer + 2;
stateParseDescr = 3;
case 3:
switch(dscrType) {
case USB_DESCRIPTOR_INTERFACE:
isGoodInterface = false;
case USB_DESCRIPTOR_CONFIGURATION:
theBuffer.valueSize = sizeof (USB_CONFIGURATION_DESCRIPTOR) - 2;
break;
case USB_DESCRIPTOR_ENDPOINT:
theBuffer.valueSize = sizeof (USB_ENDPOINT_DESCRIPTOR) - 2;
break;
case HID_DESCRIPTOR_HID:
theBuffer.valueSize = dscrLen - 2;
break;
}
valParser.Initialize(&theBuffer);
stateParseDescr = 4;
case 4:
switch(dscrType) {
case USB_DESCRIPTOR_CONFIGURATION:
if(!valParser.Parse(pp, pcntdn))
return false;
confValue = ucd->bConfigurationValue;
break;
case USB_DESCRIPTOR_INTERFACE:
if(!valParser.Parse(pp, pcntdn))
return false;
if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)
break;
if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)
break;
if(UseOr) {
if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol)))
break;
} else {
if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)
break;
}
isGoodInterface = true;
ifaceNumber = uid->bInterfaceNumber;
ifaceAltSet = uid->bAlternateSetting;
protoValue = uid->bInterfaceProtocol;
break;
case USB_DESCRIPTOR_ENDPOINT:
if(!valParser.Parse(pp, pcntdn))
return false;
if(isGoodInterface)
if(theXtractor)
theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
break;
//case HID_DESCRIPTOR_HID:
// if (!valParser.Parse(pp, pcntdn))
// return false;
// PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
// break;
default:
if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2))
return false;
}
theBuffer.pValue = varBuffer;
stateParseDescr = 0;
}
return true;
}
template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {
Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
Notify(PSTR("bDescLength:\t\t"), 0x80);
PrintHex<uint8_t > (pDesc->bLength, 0x80);
Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
//Notify(PSTR("\r\nbDescrType:\t\t"));
//PrintHex<uint8_t>(pDesc->bDescrType);
//
//Notify(PSTR("\r\nwDescriptorLength:\t"));
//PrintHex<uint16_t>(pDesc->wDescriptorLength);
for(uint8_t i = 0; i < pDesc->bNumDescriptors; i++) {
HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);
Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);
}
Notify(PSTR("\r\n"), 0x80);
}
#endif // __CONFDESCPARSER_H__

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 of the License, 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; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

@ -0,0 +1,361 @@
####################################################
# Syntax Coloring Map For USB Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
USB KEYWORD1
USBHub KEYWORD1
####################################################
# Syntax Coloring Map For BTD (Bluetooth) Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
BTD KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
Task KEYWORD2
####################################################
# Syntax Coloring Map For PS3/PS4 Bluetooth/USB Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
PS3BT KEYWORD1
PS3USB KEYWORD1
PS4BT KEYWORD1
PS4USB KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
setBdaddr KEYWORD2
getBdaddr KEYWORD2
setMoveBdaddr KEYWORD2
getMoveBdaddr KEYWORD2
getMoveCalibration KEYWORD2
getButtonPress KEYWORD2
getButtonClick KEYWORD2
getAnalogButton KEYWORD2
getAnalogHat KEYWORD2
getSensor KEYWORD2
getAngle KEYWORD2
get9DOFValues KEYWORD2
getStatus KEYWORD2
printStatusString KEYWORD2
getTemperature KEYWORD2
disconnect KEYWORD2
setAllOff KEYWORD2
setRumbleOff KEYWORD2
setRumbleOn KEYWORD2
setLedOff KEYWORD2
setLedOn KEYWORD2
setLedToggle KEYWORD2
setLedFlash KEYWORD2
moveSetBulb KEYWORD2
moveSetRumble KEYWORD2
attachOnInit KEYWORD2
PS3Connected KEYWORD2
PS3MoveConnected KEYWORD2
PS3NavigationConnected KEYWORD2
isReady KEYWORD2
watingForConnection KEYWORD2
isTouching KEYWORD2
getX KEYWORD2
getY KEYWORD2
getTouchCounter KEYWORD2
getUsbStatus KEYWORD2
getAudioStatus KEYWORD2
getMicStatus KEYWORD2
####################################################
# Constants and enums (LITERAL1)
####################################################
OFF LITERAL1
LED1 LITERAL1
LED2 LITERAL1
LED3 LITERAL1
LED4 LITERAL1
LED5 LITERAL1
LED6 LITERAL1
LED7 LITERAL1
LED8 LITERAL1
LED9 LITERAL1
LED10 LITERAL1
Red LITERAL1
Green LITERAL1
Blue LITERAL1
Yellow LITERAL1
Lightblue LITERAL1
Purble LITERAL1
White LITERAL1
Off LITERAL1
SELECT LITERAL1
L3 LITERAL1
R3 LITERAL1
START LITERAL1
UP LITERAL1
RIGHT LITERAL1
DOWN LITERAL1
LEFT LITERAL1
L2 LITERAL1
R2 LITERAL1
L1 LITERAL1
R1 LITERAL1
TRIANGLE LITERAL1
CIRCLE LITERAL1
CROSS LITERAL1
SQUARE LITERAL1
PS LITERAL1
MOVE LITERAL1
T LITERAL1
SHARE LITERAL1
OPTIONS LITERAL1
TOUCHPAD LITERAL1
LeftHatX LITERAL1
LeftHatY LITERAL1
RightHatX LITERAL1
RightHatY LITERAL1
aX LITERAL1
aY LITERAL1
aZ LITERAL1
gX LITERAL1
gY LITERAL1
gZ LITERAL1
aXmove LITERAL1
aYmove LITERAL1
aZmove LITERAL1
gXmove LITERAL1
gYmove LITERAL1
gZmove LITERAL1
tempMove LITERAL1
mXmove LITERAL1
mZmove LITERAL1
mYmove LITERAL1
Pitch LITERAL1
Roll LITERAL1
Plugged LITERAL1
Unplugged LITERAL1
Charging LITERAL1
NotCharging LITERAL1
Shutdown LITERAL1
Dying LITERAL1
Low LITERAL1
High LITERAL1
Full LITERAL1
MoveCharging LITERAL1
MoveNotCharging LITERAL1
MoveShutdown LITERAL1
MoveDying LITERAL1
MoveLow LITERAL1
MoveHigh LITERAL1
MoveFull LITERAL1
CableRumble LITERAL1
Cable LITERAL1
BluetoothRumble LITERAL1
Bluetooth LITERAL1
RumbleHigh LITERAL1
RumbleLow LITERAL1
####################################################
# Syntax Coloring Map For Xbox 360 Libraries
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
XBOXUSB KEYWORD1
XBOXOLD KEYWORD1
XBOXRECV KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
setLedRaw KEYWORD2
setLedBlink KEYWORD2
setLedMode KEYWORD2
getBatteryLevel KEYWORD2
buttonChanged KEYWORD2
XboxReceiverConnected KEYWORD2
Xbox360Connected KEYWORD2
####################################################
# Constants and enums (LITERAL1)
####################################################
ALL LITERAL1
ROTATING LITERAL1
FASTBLINK LITERAL1
SLOWBLINK LITERAL1
ALTERNATING LITERAL1
BACK LITERAL1
XBOX LITERAL1
SYNC LITERAL1
BLACK LITERAL1
WHITE LITERAL1
A LITERAL1
B LITERAL1
X LITERAL1
Y LITERAL1
####################################################
# Syntax Coloring Map For RFCOMM/SPP Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
SPP KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
connected KEYWORD2
discard KEYWORD2
####################################################
# Syntax Coloring Map For Wiimote Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
WII KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
wiimoteConnected KEYWORD2
nunchuckConnected KEYWORD2
motionPlusConnected KEYWORD2
wiiUProControllerConnected KEYWORD2
setRumbleToggle KEYWORD2
getPitch KEYWORD2
getRoll KEYWORD2
getYaw KEYWORD2
getWiimotePitch KEYWORD2
getWiimoteRoll KEYWORD2
getNunchuckPitch KEYWORD2
getNunchuckRoll KEYWORD2
PAIR KEYWORD2
statusRequest KEYWORD2
getBatteryLevel KEYWORD2
getWiiState KEYWORD2
####################################################
# Constants and enums (LITERAL1)
####################################################
PLUS LITERAL1
MINUS LITERAL1
ONE LITERAL1
TWO LITERAL1
HOME LITERAL1
Z LITERAL1
C LITERAL1
L LITERAL1
R LITERAL1
ZL LITERAL1
ZR LITERAL1
HatX LITERAL1
HatY LITERAL1
####################################################
# Methods and Functions for the IR Camera
####################################################
IRinitialize KEYWORD2
isIRCameraEnabled KEYWORD2
getIRx1 KEYWORD2
getIRy1 KEYWORD2
getIRs1 KEYWORD2
getIRx2 KEYWORD2
getIRy2 KEYWORD2
getIRs2 KEYWORD2
getIRx3 KEYWORD2
getIRy3 KEYWORD2
getIRs3 KEYWORD2
getIRx4 KEYWORD2
getIRy4 KEYWORD2
getIRs4 KEYWORD2
####################################################
# Syntax Coloring Map For BTHID Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
BTHID KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
SetReportParser KEYWORD2
setProtocolMode KEYWORD2
####################################################
# Syntax Coloring Map For PS Buzz Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
PSBuzz KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
setLedOnAll KEYWORD2
setLedOffAll KEYWORD2
####################################################
# Constants and enums (LITERAL1)
####################################################
RED LITERAL1
YELLOW LITERAL1
GREEN LITERAL1
ORANGE LITERAL1
BLUE LITERAL1

@ -0,0 +1,46 @@
{
"name": "USB-Host-Shield-20",
"keywords": "usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, buzz, xbox, wii, mass storage",
"description": "Revision 2.0 of MAX3421E-based USB Host Shield Library",
"authors":
[
{
"name": "Oleg Mazurov",
"email": "mazurov@circuitsathome.com",
"url": "http://www.circuitsathome.com",
"maintainer": true
},
{
"name": "Alexei Glushchenko",
"email": "alex-gl@mail.ru"
},
{
"name": "Kristian Lauszus",
"email": "kristianl@tkjelectronics.com",
"url": "http://tkjelectronics.com",
"maintainer": true
},
{
"name": "Andrew Kroll",
"email": "xxxajk@gmail.com",
"maintainer": true
}
],
"repository":
{
"type": "git",
"url": "https://github.com/felis/USB_Host_Shield_2.0.git"
},
"examples":
[
"examples/*/*.ino",
"examples/*/*/*.ino"
],
"frameworks": "arduino",
"platforms":
[
"atmelavr",
"teensy",
"atmelsam"
]
}

@ -0,0 +1,82 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(MACROS_H)
#error "Never include macros.h directly; include Usb.h instead"
#else
#define MACROS_H
////////////////////////////////////////////////////////////////////////////////
// HANDY MACROS
////////////////////////////////////////////////////////////////////////////////
#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h)))
#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))
#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el)
#define output_if_between(v,l,h,wa,fp,mp,el) if(VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el);
#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
#ifndef __BYTE_GRABBING_DEFINED__
#define __BYTE_GRABBING_DEFINED__ 1
#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN
// Note: Use this if your compiler generates horrible assembler!
#define BGRAB0(__usi__) (((uint8_t *)&(__usi__))[0])
#define BGRAB1(__usi__) (((uint8_t *)&(__usi__))[1])
#define BGRAB2(__usi__) (((uint8_t *)&(__usi__))[2])
#define BGRAB3(__usi__) (((uint8_t *)&(__usi__))[3])
#define BGRAB4(__usi__) (((uint8_t *)&(__usi__))[4])
#define BGRAB5(__usi__) (((uint8_t *)&(__usi__))[5])
#define BGRAB6(__usi__) (((uint8_t *)&(__usi__))[6])
#define BGRAB7(__usi__) (((uint8_t *)&(__usi__))[7])
#else
// Note: The cast alone to uint8_t is actually enough.
// GCC throws out the "& 0xff", and the size is no different.
// Some compilers need it.
#define BGRAB0(__usi__) ((uint8_t)((__usi__) & 0xff ))
#define BGRAB1(__usi__) ((uint8_t)(((__usi__) >> 8) & 0xff))
#define BGRAB2(__usi__) ((uint8_t)(((__usi__) >> 16) & 0xff))
#define BGRAB3(__usi__) ((uint8_t)(((__usi__) >> 24) & 0xff))
#define BGRAB4(__usi__) ((uint8_t)(((__usi__) >> 32) & 0xff))
#define BGRAB5(__usi__) ((uint8_t)(((__usi__) >> 40) & 0xff))
#define BGRAB6(__usi__) ((uint8_t)(((__usi__) >> 48) & 0xff))
#define BGRAB7(__usi__) ((uint8_t)(((__usi__) >> 56) & 0xff))
#endif
#define BOVER1(__usi__) ((uint16_t)(__usi__) << 8)
#define BOVER2(__usi__) ((uint32_t)(__usi__) << 16)
#define BOVER3(__usi__) ((uint32_t)(__usi__) << 24)
#define BOVER4(__usi__) ((uint64_t)(__usi__) << 32)
#define BOVER5(__usi__) ((uint64_t)(__usi__) << 40)
#define BOVER6(__usi__) ((uint64_t)(__usi__) << 48)
#define BOVER7(__usi__) ((uint64_t)(__usi__) << 56)
// These are the smallest and fastest ways I have found so far in pure C/C++.
#define BMAKE16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)BOVER1(__usc1__)))
#define BMAKE32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | (uint32_t)BOVER1(__usc1__) | (uint32_t)BOVER2(__usc2__) | (uint32_t)BOVER3(__usc3__)))
#define BMAKE64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | (uint64_t)BOVER1(__usc1__) | (uint64_t)BOVER2(__usc2__) | (uint64_t)BOVER3(__usc3__) | (uint64_t)BOVER4(__usc4__) | (uint64_t)BOVER5(__usc5__) | (uint64_t)BOVER6(__usc6__) | (uint64_t)BOVER1(__usc7__)))
#endif
/*
* Debug macros: Strings are stored in progmem (flash) instead of RAM.
*/
#define USBTRACE(s) (Notify(PSTR(s), 0x80))
#define USBTRACE1(s,l) (Notify(PSTR(s), l))
#define USBTRACE2(s,r) (Notify(PSTR(s), 0x80), D_PrintHex((r), 0x80), Notify(PSTR("\r\n"), 0x80))
#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l))
#endif /* MACROS_H */

@ -0,0 +1,1270 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#include "masstorage.h"
const uint8_t BulkOnly::epDataInIndex = 1;
const uint8_t BulkOnly::epDataOutIndex = 2;
const uint8_t BulkOnly::epInterruptInIndex = 3;
////////////////////////////////////////////////////////////////////////////////
// Interface code
////////////////////////////////////////////////////////////////////////////////
/**
* Get the capacity of the media
*
* @param lun Logical Unit Number
* @return media capacity
*/
uint32_t BulkOnly::GetCapacity(uint8_t lun) {
if(LUNOk[lun])
return CurrentCapacity[lun];
return 0LU;
}
/**
* Get the sector (block) size used on the media
*
* @param lun Logical Unit Number
* @return media sector size
*/
uint16_t BulkOnly::GetSectorSize(uint8_t lun) {
if(LUNOk[lun])
return CurrentSectorSize[lun];
return 0U;
}
/**
* Test if LUN is ready for use
*
* @param lun Logical Unit Number
* @return true if LUN is ready for use
*/
bool BulkOnly::LUNIsGood(uint8_t lun) {
return LUNOk[lun];
}
/**
* Test if LUN is write protected
*
* @param lun Logical Unit Number
* @return cached status of write protect switch
*/
boolean BulkOnly::WriteProtected(uint8_t lun) {
return WriteOk[lun];
}
/**
* Wrap and execute a SCSI CDB with length of 6
*
* @param cdb CDB to execute
* @param buf_size Size of expected transaction
* @param buf Buffer
* @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
* @return
*/
uint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
// promote buf_size to 32bits.
CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
//SetCurLUN(cdb->LUN);
return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
}
/**
* Wrap and execute a SCSI CDB with length of 10
*
* @param cdb CDB to execute
* @param buf_size Size of expected transaction
* @param buf Buffer
* @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
* @return
*/
uint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
// promote buf_size to 32bits.
CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
//SetCurLUN(cdb->LUN);
return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
}
/**
* Lock or Unlock the tray or door on device.
* Caution: Some devices with buggy firmware will lock up.
*
* @param lun Logical Unit Number
* @param lock 1 to lock, 0 to unlock
* @return
*/
uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) {
Notify(PSTR("\r\nLockMedia\r\n"), 0x80);
Notify(PSTR("---------\r\n"), 0x80);
CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock);
return SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_IN);
}
/**
* Media control, for spindle motor and media tray or door.
* This includes CDROM, TAPE and anything with a media loader.
*
* @param lun Logical Unit Number
* @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media
* @return 0 on success
*/
uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) {
Notify(PSTR("\r\nMediaCTL\r\n"), 0x80);
Notify(PSTR("-----------------\r\n"), 0x80);
uint8_t rcode = MASS_ERR_UNIT_NOT_READY;
if(bAddress) {
CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0);
rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT);
} else {
SetCurLUN(lun);
}
return rcode;
}
/**
* Read data from media
*
* @param lun Logical Unit Number
* @param addr LBA address on media to read
* @param bsize size of a block (we should probably use the cached size)
* @param blocks how many blocks to read
* @param buf memory that is able to hold the requested data
* @return 0 on success
*/
uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {
if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
Notify(PSTR("\r\nRead LUN:\t"), 0x80);
D_PrintHex<uint8_t > (lun, 0x90);
Notify(PSTR("\r\nLBA:\t\t"), 0x90);
D_PrintHex<uint32_t > (addr, 0x90);
Notify(PSTR("\r\nblocks:\t\t"), 0x90);
D_PrintHex<uint8_t > (blocks, 0x90);
Notify(PSTR("\r\nblock size:\t"), 0x90);
D_PrintHex<uint16_t > (bsize, 0x90);
Notify(PSTR("\r\n---------\r\n"), 0x80);
CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr);
again:
uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN);
if(er == MASS_ERR_STALL) {
MediaCTL(lun, 1);
DELAY(150);
if(!TestUnitReady(lun)) goto again;
}
return er;
}
/**
* Write data to media
*
* @param lun Logical Unit Number
* @param addr LBA address on media to write
* @param bsize size of a block (we should probably use the cached size)
* @param blocks how many blocks to write
* @param buf memory that contains the data to write
* @return 0 on success
*/
uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {
if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
if(!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED;
Notify(PSTR("\r\nWrite LUN:\t"), 0x80);
D_PrintHex<uint8_t > (lun, 0x90);
Notify(PSTR("\r\nLBA:\t\t"), 0x90);
D_PrintHex<uint32_t > (addr, 0x90);
Notify(PSTR("\r\nblocks:\t\t"), 0x90);
D_PrintHex<uint8_t > (blocks, 0x90);
Notify(PSTR("\r\nblock size:\t"), 0x90);
D_PrintHex<uint16_t > (bsize, 0x90);
Notify(PSTR("\r\n---------\r\n"), 0x80);
CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr);
again:
uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT);
if(er == MASS_ERR_WRITE_STALL) {
MediaCTL(lun, 1);
DELAY(150);
if(!TestUnitReady(lun)) goto again;
}
return er;
}
// End of user functions, the remaining code below is driver internals.
// Only developer serviceable parts below!
////////////////////////////////////////////////////////////////////////////////
// Main driver code
////////////////////////////////////////////////////////////////////////////////
BulkOnly::BulkOnly(USB *p) :
pUsb(p),
bAddress(0),
bIface(0),
bNumEP(1),
qNextPollTime(0),
bPollEnable(false),
//dCBWTag(0),
bLastUsbError(0) {
ClearAllEP();
dCBWTag = 0;
if(pUsb)
pUsb->RegisterDeviceClass(this);
}
/**
* USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success
* We need to standardize either the rcode, or change the API to return values
* so a signal that additional actions are required can be produced.
* Some of these codes do exist already.
*
* TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance.
* Doing so would save some program memory when using multiple drivers.
*
* @param parent USB address of parent
* @param port address of port on parent
* @param lowspeed true if device is low speed
* @return
*/
uint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
uint8_t buf[constBufSize];
USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
uint8_t rcode;
UsbDevice *p = NULL;
EpInfo *oldep_ptr = NULL;
USBTRACE("MS ConfigureDevice\r\n");
ClearAllEP();
AddressPool &addrPool = pUsb->GetAddressPool();
if(bAddress)
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
// <TECHNICAL>
// Get pointer to pseudo device with address 0 assigned
p = addrPool.GetUsbDevicePtr(0);
if(!p) {
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
}
if(!p->epinfo) {
USBTRACE("epinfo\r\n");
return USB_ERROR_EPINFO_IS_NULL;
}
// Save old pointer to EP_RECORD of address 0
oldep_ptr = p->epinfo;
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
p->epinfo = epInfo;
p->lowspeed = lowspeed;
// Get device descriptor
rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
// Restore p->epinfo
p->epinfo = oldep_ptr;
if(rcode) {
goto FailGetDevDescr;
}
// Allocate new address according to device class
bAddress = addrPool.AllocAddress(parent, false, port);
if(!bAddress)
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
// Extract Max Packet Size from the device descriptor
epInfo[0].maxPktSize = udd->bMaxPacketSize0;
// Steal and abuse from epInfo structure to save on memory.
epInfo[1].epAddr = udd->bNumConfigurations;
// </TECHNICAL>
return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
FailGetDevDescr:
#ifdef DEBUG_USB_HOST
NotifyFailGetDevDescr(rcode);
#endif
rcode = USB_ERROR_FailGetDevDescr;
Release();
return rcode;
};
/**
*
* @param parent (not used)
* @param port (not used)
* @param lowspeed true if device is low speed
* @return 0 for success
*/
uint8_t BulkOnly::Init(uint8_t parent, uint8_t port, bool lowspeed) {
uint8_t rcode;
uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations
epInfo[1].epAddr = 0;
USBTRACE("MS Init\r\n");
AddressPool &addrPool = pUsb->GetAddressPool();
UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress);
if(!p)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
// Assign new address to the device
DELAY(2000);
rcode = pUsb->setAddr(0, 0, bAddress);
if(rcode) {
p->lowspeed = false;
addrPool.FreeAddress(bAddress);
bAddress = 0;
USBTRACE2("setAddr:", rcode);
return rcode;
}
USBTRACE2("Addr:", bAddress);
p->lowspeed = false;
p = addrPool.GetUsbDevicePtr(bAddress);
if(!p)
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
p->lowspeed = lowspeed;
// Assign epInfo to epinfo pointer
rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
if(rcode)
goto FailSetDevTblEntry;
USBTRACE2("NC:", num_of_conf);
for(uint8_t i = 0; i < num_of_conf; i++) {
ConfigDescParser< USB_CLASS_MASS_STORAGE,
MASS_SUBCLASS_SCSI,
MASS_PROTO_BBB,
CP_MASK_COMPARE_CLASS |
CP_MASK_COMPARE_SUBCLASS |
CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this);
rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser);
if(rcode)
goto FailGetConfDescr;
if(bNumEP > 1)
break;
}
if(bNumEP < 3)
return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
// Assign epInfo to epinfo pointer
pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
USBTRACE2("Conf:", bConfNum);
// Set Configuration Value
rcode = pUsb->setConf(bAddress, 0, bConfNum);
if(rcode)
goto FailSetConfDescr;
//Linux does a 1sec delay after this.
DELAY(1000);
rcode = GetMaxLUN(&bMaxLUN);
if(rcode)
goto FailGetMaxLUN;
if(bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1;
ErrorMessage<uint8_t > (PSTR("MaxLUN"), bMaxLUN);
DELAY(1000); // Delay a bit for slow firmware.
for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
InquiryResponse response;
rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response);
if(rcode) {
ErrorMessage<uint8_t > (PSTR("Inquiry"), rcode);
} else {
#if 0
printf("LUN %i `", lun);
uint8_t *buf = response.VendorID;
for(int i = 0; i < 28; i++) printf("%c", buf[i]);
printf("'\r\nQualifier %1.1X ", response.PeripheralQualifier);
printf("Device type %2.2X ", response.DeviceType);
printf("RMB %1.1X ", response.Removable);
printf("SSCS %1.1X ", response.SCCS);
uint8_t sv = response.Version;
printf("SCSI version %2.2X\r\nDevice conforms to ", sv);
switch(sv) {
case 0:
printf("No specific");
break;
case 1:
printf("ANSI X3.131-1986 (ANSI 1)");
break;
case 2:
printf("ANSI X3.131-1994 (ANSI 2)");
break;
case 3:
printf("ANSI INCITS 301-1997 (SPC)");
break;
case 4:
printf("ANSI INCITS 351-2001 (SPC-2)");
break;
case 5:
printf("ANSI INCITS 408-2005 (SPC-4)");
break;
case 6:
printf("T10/1731-D (SPC-4)");
break;
default:
printf("unknown");
}
printf(" standards.\r\n");
#endif
uint8_t tries = 0xf0;
while((rcode = TestUnitReady(lun))) {
if(rcode == 0x08) break; // break on no media, this is OK to do.
// try to lock media and spin up
if(tries < 14) {
LockMedia(lun, 1);
MediaCTL(lun, 1); // I actually have a USB stick that needs this!
} else DELAY(2 * (tries + 1));
tries++;
if(!tries) break;
}
if(!rcode) {
DELAY(1000);
LUNOk[lun] = CheckLUN(lun);
if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
}
}
}
CheckMedia();
rcode = OnInit();
if(rcode)
goto FailOnInit;
#ifdef DEBUG_USB_HOST
USBTRACE("MS configured\r\n\r\n");
#endif
bPollEnable = true;
//USBTRACE("Poll enabled\r\n");
return 0;
FailSetConfDescr:
#ifdef DEBUG_USB_HOST
NotifyFailSetConfDescr();
goto Fail;
#endif
FailOnInit:
#ifdef DEBUG_USB_HOST
USBTRACE("OnInit:");
goto Fail;
#endif
FailGetMaxLUN:
#ifdef DEBUG_USB_HOST
USBTRACE("GetMaxLUN:");
goto Fail;
#endif
//#ifdef DEBUG_USB_HOST
//FailInvalidSectorSize:
// USBTRACE("Sector Size is NOT VALID: ");
// goto Fail;
//#endif
FailSetDevTblEntry:
#ifdef DEBUG_USB_HOST
NotifyFailSetDevTblEntry();
goto Fail;
#endif
FailGetConfDescr:
#ifdef DEBUG_USB_HOST
NotifyFailGetConfDescr();
#endif
#ifdef DEBUG_USB_HOST
Fail:
NotifyFail(rcode);
#endif
Release();
return rcode;
}
/**
* For driver use only.
*
* @param conf
* @param iface
* @param alt
* @param proto
* @param pep
*/
void BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR * pep) {
ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
bConfNum = conf;
uint8_t index;
#if 1
if((pep->bmAttributes & 0x02) == 2) {
index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
// Fill in the endpoint info structure
epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
epInfo[index].epAttribs = 0;
bNumEP++;
PrintEndpointDescriptor(pep);
}
#else
if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)
index = epInterruptInIndex;
else
if((pep->bmAttributes & 0x02) == 2)
index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
else
return;
// Fill in the endpoint info structure
epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
epInfo[index].epAttribs = 0;
bNumEP++;
PrintEndpointDescriptor(pep);
#endif
}
/**
* For driver use only.
*
* @return
*/
uint8_t BulkOnly::Release() {
ClearAllEP();
pUsb->GetAddressPool().FreeAddress(bAddress);
return 0;
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @return true if LUN is ready for use.
*/
boolean BulkOnly::CheckLUN(uint8_t lun) {
uint8_t rcode;
Capacity capacity;
for(uint8_t i = 0; i < 8; i++) capacity.data[i] = 0;
rcode = ReadCapacity10(lun, (uint8_t*)capacity.data);
if(rcode) {
//printf(">>>>>>>>>>>>>>>>ReadCapacity returned %i\r\n", rcode);
return false;
}
ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>CAPACITY OK ON LUN"), lun);
for(uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++)
D_PrintHex<uint8_t > (capacity.data[i], 0x80);
Notify(PSTR("\r\n\r\n"), 0x80);
// Only 512/1024/2048/4096 are valid values!
uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]);
if(c != 0x0200LU && c != 0x0400LU && c != 0x0800LU && c != 0x1000LU) {
return false;
}
// Store capacity information.
CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF);
CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1;
if(CurrentCapacity[lun] == /*0xffffffffLU */ 0x01LU || CurrentCapacity[lun] == 0x00LU) {
// Buggy firmware will report 0xffffffff or 0 for no media
if(CurrentCapacity[lun])
ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN"), lun);
return false;
}
DELAY(20);
Page3F(lun);
if(!TestUnitReady(lun)) return true;
return false;
}
/**
* For driver use only.
*
* Scan for media change on all LUNs
*/
void BulkOnly::CheckMedia() {
for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
if(TestUnitReady(lun)) {
LUNOk[lun] = false;
continue;
}
if(!LUNOk[lun])
LUNOk[lun] = CheckLUN(lun);
}
#if 0
printf("}}}}}}}}}}}}}}}}STATUS ");
for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
if(LUNOk[lun])
printf("#");
else printf(".");
}
printf("\r\n");
#endif
qNextPollTime = millis() + 2000;
}
/**
* For driver use only.
*
* @return
*/
uint8_t BulkOnly::Poll() {
//uint8_t rcode = 0;
if(!bPollEnable)
return 0;
if((long)(millis() - qNextPollTime) >= 0L) {
CheckMedia();
}
//rcode = 0;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// SCSI code
////////////////////////////////////////////////////////////////////////////////
/**
* For driver use only.
*
* @param plun
* @return
*/
uint8_t BulkOnly::GetMaxLUN(uint8_t *plun) {
uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, NULL);
if(ret == hrSTALL)
*plun = 0;
return 0;
}
/**
* For driver use only. Used during Driver Init
*
* @param lun Logical Unit Number
* @param bsize
* @param buf
* @return
*/
uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) {
Notify(PSTR("\r\nInquiry\r\n"), 0x80);
Notify(PSTR("---------\r\n"), 0x80);
CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0LU, (uint8_t)bsize, 0);
uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN);
return rc;
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @return
*/
uint8_t BulkOnly::TestUnitReady(uint8_t lun) {
//SetCurLUN(lun);
if(!bAddress)
return MASS_ERR_UNIT_NOT_READY;
Notify(PSTR("\r\nTestUnitReady\r\n"), 0x80);
Notify(PSTR("-----------------\r\n"), 0x80);
CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0);
return SCSITransaction6(&cdb, 0, NULL, (uint8_t)MASS_CMD_DIR_IN);
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @param pc
* @param page
* @param subpage
* @param len
* @param pbuf
* @return
*/
uint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) {
Notify(PSTR("\r\rModeSense\r\n"), 0x80);
Notify(PSTR("------------\r\n"), 0x80);
CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0);
return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN);
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @param bsize
* @param buf
* @return
*/
uint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) {
Notify(PSTR("\r\nReadCapacity\r\n"), 0x80);
Notify(PSTR("---------------\r\n"), 0x80);
CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun);
return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN);
}
/**
* For driver use only.
*
* Page 3F contains write protect status.
*
* @param lun Logical Unit Number to test.
* @return Write protect switch status.
*/
uint8_t BulkOnly::Page3F(uint8_t lun) {
uint8_t buf[192];
for(int i = 0; i < 192; i++) {
buf[i] = 0x00;
}
WriteOk[lun] = true;
#if SKIP_WRITE_PROTECT
return 0;
#else // SKIP_WRITE_PROTECT
uint8_t rc = ModeSense6(lun, 0, 0x3f, 0, 192, buf);
if(!rc) {
WriteOk[lun] = ((buf[2] & 0x80) == 0);
Notify(PSTR("Mode Sense: "), 0x80);
for(int i = 0; i < 4; i++) {
D_PrintHex<uint8_t > (buf[i], 0x80);
Notify(PSTR(" "), 0x80);
}
Notify(PSTR("\r\n"), 0x80);
}
return rc;
#endif // SKIP_WRITE_PROTECT
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @param size
* @param buf
* @return
*/
uint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) {
Notify(PSTR("\r\nRequestSense\r\n"), 0x80);
Notify(PSTR("----------------\r\n"), 0x80);
CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0LU, (uint8_t)size, 0);
CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN);
//SetCurLUN(lun);
return Transaction(&cbw, size, buf);
}
////////////////////////////////////////////////////////////////////////////////
// USB code
////////////////////////////////////////////////////////////////////////////////
/**
* For driver use only.
*
* @param index
* @return
*/
uint8_t BulkOnly::ClearEpHalt(uint8_t index) {
if(index == 0)
return 0;
uint8_t ret = 0;
while((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, NULL, NULL)) == 0x01))
DELAY(6);
if(ret) {
ErrorMessage<uint8_t > (PSTR("ClearEpHalt"), ret);
ErrorMessage<uint8_t > (PSTR("EP"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr));
return ret;
}
epInfo[index].bmSndToggle = 0;
epInfo[index].bmRcvToggle = 0;
// epAttribs = 0;
return 0;
}
/**
* For driver use only.
*
*/
void BulkOnly::Reset() {
while(pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, NULL, NULL) == 0x01) DELAY(6);
}
/**
* For driver use only.
*
* @return 0 if successful
*/
uint8_t BulkOnly::ResetRecovery() {
Notify(PSTR("\r\nResetRecovery\r\n"), 0x80);
Notify(PSTR("-----------------\r\n"), 0x80);
DELAY(6);
Reset();
DELAY(6);
ClearEpHalt(epDataInIndex);
DELAY(6);
bLastUsbError = ClearEpHalt(epDataOutIndex);
DELAY(6);
return bLastUsbError;
}
/**
* For driver use only.
*
* Clear all EP data and clear all LUN status
*/
void BulkOnly::ClearAllEP() {
for(uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) {
epInfo[i].epAddr = 0;
epInfo[i].maxPktSize = (i) ? 0 : 8;
epInfo[i].epAttribs = 0;
epInfo[i].bmNakPower = USB_NAK_DEFAULT;
}
for(uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) {
LUNOk[i] = false;
WriteOk[i] = false;
CurrentCapacity[i] = 0lu;
CurrentSectorSize[i] = 0;
}
bIface = 0;
bNumEP = 1;
bAddress = 0;
qNextPollTime = 0;
bPollEnable = false;
bLastUsbError = 0;
bMaxLUN = 0;
bTheLUN = 0;
}
/**
* For driver use only.
*
* @param pcsw
* @param pcbw
* @return
*/
bool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) {
if(pcsw->dCSWSignature != MASS_CSW_SIGNATURE) {
Notify(PSTR("CSW:Sig error\r\n"), 0x80);
return false;
}
if(pcsw->dCSWTag != pcbw->dCBWTag) {
Notify(PSTR("CSW:Wrong tag\r\n"), 0x80);
return false;
}
return true;
}
/**
* For driver use only.
*
* @param error
* @param index
* @return
*/
uint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) {
uint8_t count = 3;
bLastUsbError = error;
//if (error)
//ClearEpHalt(index);
while(error && count) {
if(error != hrSUCCESS) {
ErrorMessage<uint8_t > (PSTR("USB Error"), error);
ErrorMessage<uint8_t > (PSTR("Index"), index);
}
switch(error) {
// case hrWRONGPID:
case hrSUCCESS:
return MASS_ERR_SUCCESS;
case hrBUSY:
// SIE is busy, just hang out and try again.
return MASS_ERR_UNIT_BUSY;
case hrTIMEOUT:
case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED;
case hrSTALL:
if(index == 0)
return MASS_ERR_STALL;
ClearEpHalt(index);
if(index != epDataInIndex)
return MASS_ERR_WRITE_STALL;
return MASS_ERR_STALL;
case hrNAK:
if(index == 0)
return MASS_ERR_UNIT_BUSY;
return MASS_ERR_UNIT_BUSY;
case hrTOGERR:
// Handle a very super rare corner case, where toggles become de-synched.
// I have only ran into one device that has this firmware bug, and this is
// the only clean way to get back into sync with the buggy device firmware.
// --AJK
if(bAddress && bConfNum) {
error = pUsb->setConf(bAddress, 0, bConfNum);
if(error)
break;
}
return MASS_ERR_SUCCESS;
default:
ErrorMessage<uint8_t > (PSTR("\r\nUSB"), error);
return MASS_ERR_GENERAL_USB_ERROR;
}
count--;
} // while
return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS);
}
#if MS_WANT_PARSER
uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) {
return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0);
}
#endif
/**
* For driver use only.
*
* @param pcbw
* @param buf_size
* @param buf
* @param flags
* @return
*/
uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf
#if MS_WANT_PARSER
, uint8_t flags
#endif
) {
#if MS_WANT_PARSER
uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength;
printf("Transfersize %i\r\n", bytes);
DELAY(1000);
boolean callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK;
#else
uint16_t bytes = buf_size;
#endif
boolean write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN;
uint8_t ret = 0;
uint8_t usberr;
CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles.
SetCurLUN(pcbw->bmCBWLUN);
ErrorMessage<uint32_t > (PSTR("CBW.dCBWTag"), pcbw->dCBWTag);
while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) DELAY(1);
ret = HandleUsbError(usberr, epDataOutIndex);
//ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex);
if(ret) {
ErrorMessage<uint8_t > (PSTR("============================ CBW"), ret);
} else {
if(bytes) {
if(!write) {
#if MS_WANT_PARSER
if(callback) {
uint8_t rbuf[bytes];
while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) DELAY(1);
if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0);
} else {
#endif
while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) DELAY(1);
#if MS_WANT_PARSER
}
#endif
ret = HandleUsbError(usberr, epDataInIndex);
} else {
while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) DELAY(1);
ret = HandleUsbError(usberr, epDataOutIndex);
}
if(ret) {
ErrorMessage<uint8_t > (PSTR("============================ DAT"), ret);
}
}
}
{
bytes = sizeof (CommandStatusWrapper);
int tries = 2;
while(tries--) {
while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) DELAY(1);
if(!usberr) break;
ClearEpHalt(epDataInIndex);
if(tries) ResetRecovery();
}
if(!ret) {
Notify(PSTR("CBW:\t\tOK\r\n"), 0x80);
Notify(PSTR("Data Stage:\tOK\r\n"), 0x80);
} else {
// Throw away csw, IT IS NOT OF ANY USE.
ResetRecovery();
return ret;
}
ret = HandleUsbError(usberr, epDataInIndex);
if(ret) {
ErrorMessage<uint8_t > (PSTR("============================ CSW"), ret);
}
if(usberr == hrSUCCESS) {
if(IsValidCSW(&csw, pcbw)) {
//ErrorMessage<uint32_t > (PSTR("CSW.dCBWTag"), csw.dCSWTag);
//ErrorMessage<uint8_t > (PSTR("bCSWStatus"), csw.bCSWStatus);
//ErrorMessage<uint32_t > (PSTR("dCSWDataResidue"), csw.dCSWDataResidue);
Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80);
return csw.bCSWStatus;
} else {
// NOTE! Sometimes this is caused by the reported residue being wrong.
// Get a different device. It isn't compliant, and should have never passed Q&A.
// I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter.
// Other devices that exhibit this behavior exist in the wild too.
// Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk
Notify(PSTR("Invalid CSW\r\n"), 0x80);
ResetRecovery();
//return MASS_ERR_SUCCESS;
return MASS_ERR_INVALID_CSW;
}
}
}
return ret;
}
/**
* For driver use only.
*
* @param lun Logical Unit Number
* @return
*/
uint8_t BulkOnly::SetCurLUN(uint8_t lun) {
if(lun > bMaxLUN)
return MASS_ERR_INVALID_LUN;
bTheLUN = lun;
return MASS_ERR_SUCCESS;
};
/**
* For driver use only.
*
* @param status
* @return
*/
uint8_t BulkOnly::HandleSCSIError(uint8_t status) {
uint8_t ret = 0;
switch(status) {
case 0: return MASS_ERR_SUCCESS;
case 2:
ErrorMessage<uint8_t > (PSTR("Phase Error"), status);
ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
ResetRecovery();
return MASS_ERR_GENERAL_SCSI_ERROR;
case 1:
ErrorMessage<uint8_t > (PSTR("SCSI Error"), status);
ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
RequestSenseResponce rsp;
ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp);
if(ret) {
return MASS_ERR_GENERAL_SCSI_ERROR;
}
ErrorMessage<uint8_t > (PSTR("Response Code"), rsp.bResponseCode);
if(rsp.bResponseCode & 0x80) {
Notify(PSTR("Information field: "), 0x80);
for(int i = 0; i < 4; i++) {
D_PrintHex<uint8_t > (rsp.CmdSpecificInformation[i], 0x80);
Notify(PSTR(" "), 0x80);
}
Notify(PSTR("\r\n"), 0x80);
}
ErrorMessage<uint8_t > (PSTR("Sense Key"), rsp.bmSenseKey);
ErrorMessage<uint8_t > (PSTR("Add Sense Code"), rsp.bAdditionalSenseCode);
ErrorMessage<uint8_t > (PSTR("Add Sense Qual"), rsp.bAdditionalSenseQualifier);
// warning, this is not testing ASQ, only SK and ASC.
switch(rsp.bmSenseKey) {
case SCSI_S_UNIT_ATTENTION:
switch(rsp.bAdditionalSenseCode) {
case SCSI_ASC_MEDIA_CHANGED:
return MASS_ERR_MEDIA_CHANGED;
default:
return MASS_ERR_UNIT_NOT_READY;
}
case SCSI_S_NOT_READY:
switch(rsp.bAdditionalSenseCode) {
case SCSI_ASC_MEDIUM_NOT_PRESENT:
return MASS_ERR_NO_MEDIA;
default:
return MASS_ERR_UNIT_NOT_READY;
}
case SCSI_S_ILLEGAL_REQUEST:
switch(rsp.bAdditionalSenseCode) {
case SCSI_ASC_LBA_OUT_OF_RANGE:
return MASS_ERR_BAD_LBA;
default:
return MASS_ERR_CMD_NOT_SUPPORTED;
}
default:
return MASS_ERR_GENERAL_SCSI_ERROR;
}
// case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later.
// case 0x05/0x14: we stalled out
// case 0x15/0x16: we naked out.
default:
ErrorMessage<uint8_t > (PSTR("Gen SCSI Err"), status);
ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
return status;
} // switch
}
////////////////////////////////////////////////////////////////////////////////
// Debugging code
////////////////////////////////////////////////////////////////////////////////
/**
*
* @param ep_ptr
*/
void BulkOnly::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR * ep_ptr) {
Notify(PSTR("Endpoint descriptor:"), 0x80);
Notify(PSTR("\r\nLength:\t\t"), 0x80);
D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
Notify(PSTR("\r\nType:\t\t"), 0x80);
D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
Notify(PSTR("\r\nAddress:\t"), 0x80);
D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
Notify(PSTR("\r\nAttributes:\t"), 0x80);
D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
Notify(PSTR("\r\n"), 0x80);
}
////////////////////////////////////////////////////////////////////////////////
// misc/to kill/to-do
////////////////////////////////////////////////////////////////////////////////
/* We won't be needing this... */
uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser * prs) {
#if MS_WANT_PARSER
if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
Notify(PSTR("\r\nRead (With parser)\r\n"), 0x80);
Notify(PSTR("---------\r\n"), 0x80);
CommandBlockWrapper cbw = CommandBlockWrapper();
cbw.dCBWSignature = MASS_CBW_SIGNATURE;
cbw.dCBWTag = ++dCBWTag;
cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks);
cbw.bmCBWFlags = MASS_CMD_DIR_IN,
cbw.bmCBWLUN = lun;
cbw.bmCBWCBLength = 10;
cbw.CBWCB[0] = SCSI_CMD_READ_10;
cbw.CBWCB[8] = blocks;
cbw.CBWCB[2] = ((addr >> 24) & 0xff);
cbw.CBWCB[3] = ((addr >> 16) & 0xff);
cbw.CBWCB[4] = ((addr >> 8) & 0xff);
cbw.CBWCB[5] = (addr & 0xff);
return HandleSCSIError(Transaction(&cbw, bsize, prs, 1));
#else
return MASS_ERR_NOT_IMPLEMENTED;
#endif
}

@ -0,0 +1,571 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(__MASSTORAGE_H__)
#define __MASSTORAGE_H__
// Cruft removal, makes driver smaller, faster.
#ifndef MS_WANT_PARSER
#define MS_WANT_PARSER 0
#endif
#include "Usb.h"
#define bmREQ_MASSOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
#define bmREQ_MASSIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
// Mass Storage Subclass Constants
#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00 // De facto use
#define MASS_SUBCLASS_RBC 0x01
#define MASS_SUBCLASS_ATAPI 0x02 // MMC-5 (ATAPI)
#define MASS_SUBCLASS_OBSOLETE1 0x03 // Was QIC-157
#define MASS_SUBCLASS_UFI 0x04 // Specifies how to interface Floppy Disk Drives to USB
#define MASS_SUBCLASS_OBSOLETE2 0x05 // Was SFF-8070i
#define MASS_SUBCLASS_SCSI 0x06 // SCSI Transparent Command Set
#define MASS_SUBCLASS_LSDFS 0x07 // Specifies how host has to negotiate access before trying SCSI
#define MASS_SUBCLASS_IEEE1667 0x08
// Mass Storage Class Protocols
#define MASS_PROTO_CBI 0x00 // CBI (with command completion interrupt)
#define MASS_PROTO_CBI_NO_INT 0x01 // CBI (without command completion interrupt)
#define MASS_PROTO_OBSOLETE 0x02
#define MASS_PROTO_BBB 0x50 // Bulk Only Transport
#define MASS_PROTO_UAS 0x62
// Request Codes
#define MASS_REQ_ADSC 0x00
#define MASS_REQ_GET 0xFC
#define MASS_REQ_PUT 0xFD
#define MASS_REQ_GET_MAX_LUN 0xFE
#define MASS_REQ_BOMSR 0xFF // Bulk-Only Mass Storage Reset
#define MASS_CBW_SIGNATURE 0x43425355
#define MASS_CSW_SIGNATURE 0x53425355
#define MASS_CMD_DIR_OUT 0 // (0 << 7)
#define MASS_CMD_DIR_IN 0x80 //(1 << 7)
/*
* Reference documents from T10 (http://www.t10.org)
* SCSI Primary Commands - 3 (SPC-3)
* SCSI Block Commands - 2 (SBC-2)
* Multi-Media Commands - 5 (MMC-5)
*/
/* Group 1 commands (CDB's here are should all be 6-bytes) */
#define SCSI_CMD_TEST_UNIT_READY 0x00
#define SCSI_CMD_REQUEST_SENSE 0x03
#define SCSI_CMD_FORMAT_UNIT 0x04
#define SCSI_CMD_READ_6 0x08
#define SCSI_CMD_WRITE_6 0x0A
#define SCSI_CMD_INQUIRY 0x12
#define SCSI_CMD_MODE_SELECT_6 0x15
#define SCSI_CMD_MODE_SENSE_6 0x1A
#define SCSI_CMD_START_STOP_UNIT 0x1B
#define SCSI_CMD_PREVENT_REMOVAL 0x1E
/* Group 2 Commands (CDB's here are 10-bytes) */
#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23
#define SCSI_CMD_READ_CAPACITY_10 0x25
#define SCSI_CMD_READ_10 0x28
#define SCSI_CMD_WRITE_10 0x2A
#define SCSI_CMD_SEEK_10 0x2B
#define SCSI_CMD_ERASE_10 0x2C
#define SCSI_CMD_WRITE_AND_VERIFY_10 0x2E
#define SCSI_CMD_VERIFY_10 0x2F
#define SCSI_CMD_SYNCHRONIZE_CACHE 0x35
#define SCSI_CMD_WRITE_BUFFER 0x3B
#define SCSI_CMD_READ_BUFFER 0x3C
#define SCSI_CMD_READ_SUBCHANNEL 0x42
#define SCSI_CMD_READ_TOC 0x43
#define SCSI_CMD_READ_HEADER 0x44
#define SCSI_CMD_PLAY_AUDIO_10 0x45
#define SCSI_CMD_GET_CONFIGURATION 0x46
#define SCSI_CMD_PLAY_AUDIO_MSF 0x47
#define SCSI_CMD_PLAY_AUDIO_TI 0x48
#define SCSI_CMD_PLAY_TRACK_REL_10 0x49
#define SCSI_CMD_GET_EVENT_STATUS 0x4A
#define SCSI_CMD_PAUSE_RESUME 0x4B
#define SCSI_CMD_READ_DISC_INFORMATION 0x51
#define SCSI_CMD_READ_TRACK_INFORMATION 0x52
#define SCSI_CMD_RESERVE_TRACK 0x53
#define SCSI_CMD_SEND_OPC_INFORMATION 0x54
#define SCSI_CMD_MODE_SELECT_10 0x55
#define SCSI_CMD_REPAIR_TRACK 0x58
#define SCSI_CMD_MODE_SENSE_10 0x5A
#define SCSI_CMD_CLOSE_TRACK_SESSION 0x5B
#define SCSI_CMD_READ_BUFFER_CAPACITY 0x5C
#define SCSI_CMD_SEND_CUE_SHEET 0x5D
/* Group 5 Commands (CDB's here are 12-bytes) */
#define SCSI_CMD_REPORT_LUNS 0xA0
#define SCSI_CMD_BLANK 0xA1
#define SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2
#define SCSI_CMD_SEND_KEY 0xA3
#define SCSI_CMD_REPORT_KEY 0xA4
#define SCSI_CMD_PLAY_AUDIO_12 0xA5
#define SCSI_CMD_LOAD_UNLOAD 0xA6
#define SCSI_CMD_SET_READ_AHEAD 0xA7
#define SCSI_CMD_READ_12 0xA8
#define SCSI_CMD_PLAY_TRACK_REL_12 0xA9
#define SCSI_CMD_WRITE_12 0xAA
#define SCSI_CMD_READ_MEDIA_SERIAL_12 0xAB
#define SCSI_CMD_GET_PERFORMANCE 0xAC
#define SCSI_CMD_READ_DVD_STRUCTURE 0xAD
#define SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5
#define SCSI_CMD_SET_STREAMING 0xB6
#define SCSI_CMD_READ_MSF 0xB9
#define SCSI_CMD_SET_SPEED 0xBB
#define SCSI_CMD_MECHANISM_STATUS 0xBD
#define SCSI_CMD_READ_CD 0xBE
#define SCSI_CMD_SEND_DISC_STRUCTURE 0xBF
/* Vendor-unique Commands, included for completeness */
#define SCSI_CMD_CD_PLAYBACK_STATUS 0xC4 /* SONY unique */
#define SCSI_CMD_PLAYBACK_CONTROL 0xC9 /* SONY unique */
#define SCSI_CMD_READ_CDDA 0xD8 /* Vendor unique */
#define SCSI_CMD_READ_CDXA 0xDB /* Vendor unique */
#define SCSI_CMD_READ_ALL_SUBCODES 0xDF /* Vendor unique */
/* SCSI error codes */
#define SCSI_S_NOT_READY 0x02
#define SCSI_S_MEDIUM_ERROR 0x03
#define SCSI_S_ILLEGAL_REQUEST 0x05
#define SCSI_S_UNIT_ATTENTION 0x06
#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21
#define SCSI_ASC_MEDIA_CHANGED 0x28
#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A
/* USB error codes */
#define MASS_ERR_SUCCESS 0x00
#define MASS_ERR_PHASE_ERROR 0x02
#define MASS_ERR_UNIT_NOT_READY 0x03
#define MASS_ERR_UNIT_BUSY 0x04
#define MASS_ERR_STALL 0x05
#define MASS_ERR_CMD_NOT_SUPPORTED 0x06
#define MASS_ERR_INVALID_CSW 0x07
#define MASS_ERR_NO_MEDIA 0x08
#define MASS_ERR_BAD_LBA 0x09
#define MASS_ERR_MEDIA_CHANGED 0x0A
#define MASS_ERR_DEVICE_DISCONNECTED 0x11
#define MASS_ERR_UNABLE_TO_RECOVER 0x12 // Reset recovery error
#define MASS_ERR_INVALID_LUN 0x13
#define MASS_ERR_WRITE_STALL 0x14
#define MASS_ERR_READ_NAKS 0x15
#define MASS_ERR_WRITE_NAKS 0x16
#define MASS_ERR_WRITE_PROTECTED 0x17
#define MASS_ERR_NOT_IMPLEMENTED 0xFD
#define MASS_ERR_GENERAL_SCSI_ERROR 0xFE
#define MASS_ERR_GENERAL_USB_ERROR 0xFF
#define MASS_ERR_USER 0xA0 // For subclasses to define their own error codes
#define MASS_TRANS_FLG_CALLBACK 0x01 // Callback is involved
#define MASS_TRANS_FLG_NO_STALL_CHECK 0x02 // STALL condition is not checked
#define MASS_TRANS_FLG_NO_PHASE_CHECK 0x04 // PHASE_ERROR is not checked
#define MASS_MAX_ENDPOINTS 3
struct Capacity {
uint8_t data[8];
//uint32_t dwBlockAddress;
//uint32_t dwBlockLength;
} __attribute__((packed));
struct BASICCDB {
uint8_t Opcode;
unsigned unused : 5;
unsigned LUN : 3;
uint8_t info[12];
} __attribute__((packed));
typedef BASICCDB BASICCDB_t;
struct CDB6 {
uint8_t Opcode;
unsigned LBAMSB : 5;
unsigned LUN : 3;
uint8_t LBAHB;
uint8_t LBALB;
uint8_t AllocationLength;
uint8_t Control;
public:
CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :
Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1f), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)),
AllocationLength(_AllocationLength), Control(_Control) {
}
CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) :
Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0),
AllocationLength(_AllocationLength), Control(_Control) {
}
} __attribute__((packed));
typedef CDB6 CDB6_t;
struct CDB10 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned LUN : 3;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t Misc2;
uint8_t ALC_MB;
uint8_t ALC_LB;
uint8_t Control;
public:
CDB10(uint8_t _Opcode, uint8_t _LUN) :
Opcode(_Opcode), Service_Action(0), LUN(_LUN),
LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0),
Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) {
}
CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :
Opcode(_Opcode), Service_Action(0), LUN(_LUN),
LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)),
Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) {
}
} __attribute__((packed));
typedef CDB10 CDB10_t;
struct CDB12 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned Misc : 3;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Control;
} __attribute__((packed));
typedef CDB12 CDB12_t;
struct CDB_LBA32_16 {
uint8_t Opcode;
unsigned Service_Action : 5;
unsigned Misc : 3;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t A_M_M_MB;
uint8_t A_M_M_LB;
uint8_t A_M_L_MB;
uint8_t A_M_L_LB;
uint8_t ALC_M_MB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Misc2;
uint8_t Control;
} __attribute__((packed));
struct CDB_LBA64_16 {
uint8_t Opcode;
uint8_t Misc;
uint8_t LBA_M_M_MB;
uint8_t LBA_M_M_LB;
uint8_t LBA_M_L_MB;
uint8_t LBA_M_L_LB;
uint8_t LBA_L_M_MB;
uint8_t LBA_L_M_LB;
uint8_t LBA_L_L_MB;
uint8_t LBA_L_L_LB;
uint8_t ALC_M_MB;
uint8_t ALC_M_LB;
uint8_t ALC_L_MB;
uint8_t ALC_L_LB;
uint8_t Misc2;
uint8_t Control;
} __attribute__((packed));
struct InquiryResponse {
uint8_t DeviceType : 5;
uint8_t PeripheralQualifier : 3;
unsigned Reserved : 7;
unsigned Removable : 1;
uint8_t Version;
unsigned ResponseDataFormat : 4;
unsigned HISUP : 1;
unsigned NormACA : 1;
unsigned TrmTsk : 1;
unsigned AERC : 1;
uint8_t AdditionalLength;
//uint8_t Reserved3[2];
unsigned PROTECT : 1;
unsigned Res : 2;
unsigned ThreePC : 1;
unsigned TPGS : 2;
unsigned ACC : 1;
unsigned SCCS : 1;
unsigned ADDR16 : 1;
unsigned R1 : 1;
unsigned R2 : 1;
unsigned MCHNGR : 1;
unsigned MULTIP : 1;
unsigned VS : 1;
unsigned ENCSERV : 1;
unsigned BQUE : 1;
unsigned SoftReset : 1;
unsigned CmdQue : 1;
unsigned Reserved4 : 1;
unsigned Linked : 1;
unsigned Sync : 1;
unsigned WideBus16Bit : 1;
unsigned WideBus32Bit : 1;
unsigned RelAddr : 1;
uint8_t VendorID[8];
uint8_t ProductID[16];
uint8_t RevisionID[4];
} __attribute__((packed));
struct CommandBlockWrapperBase {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
public:
CommandBlockWrapperBase() {
}
CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :
dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {
}
} __attribute__((packed));
struct CommandBlockWrapper : public CommandBlockWrapperBase {
struct {
uint8_t bmCBWLUN : 4;
uint8_t bmReserved1 : 4;
};
struct {
uint8_t bmCBWCBLength : 4;
uint8_t bmReserved2 : 4;
};
uint8_t CBWCB[16];
public:
// All zeroed.
CommandBlockWrapper() :
CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
}
// Generic Wrap, CDB zeroed.
CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :
CommandBlockWrapperBase(tag, xflen, flgs),
bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {
for(int i = 0; i < 16; i++) CBWCB[i] = 0;
// Type punning can cause optimization problems and bugs.
// Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this.
//(((BASICCDB_t *) CBWCB)->LUN) = cmd;
BASICCDB_t *x = reinterpret_cast<BASICCDB_t *>(CBWCB);
x->LUN = cmd;
}
// Wrap for CDB of 6
CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) :
CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {
memcpy(&CBWCB, cdb, 6);
}
// Wrap for CDB of 10
CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) :
CommandBlockWrapperBase(tag, xflen, dir),
bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {
memcpy(&CBWCB, cdb, 10);
}
} __attribute__((packed));
struct CommandStatusWrapper {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));
struct RequestSenseResponce {
uint8_t bResponseCode;
uint8_t bSegmentNumber;
uint8_t bmSenseKey : 4;
uint8_t bmReserved : 1;
uint8_t bmILI : 1;
uint8_t bmEOM : 1;
uint8_t bmFileMark : 1;
uint8_t Information[4];
uint8_t bAdditionalLength;
uint8_t CmdSpecificInformation[4];
uint8_t bAdditionalSenseCode;
uint8_t bAdditionalSenseQualifier;
uint8_t bFieldReplaceableUnitCode;
uint8_t SenseKeySpecific[3];
} __attribute__((packed));
class BulkOnly : public USBDeviceConfig, public UsbConfigXtracter {
protected:
static const uint8_t epDataInIndex; // DataIn endpoint index
static const uint8_t epDataOutIndex; // DataOUT endpoint index
static const uint8_t epInterruptInIndex; // InterruptIN endpoint index
USB *pUsb;
uint8_t bAddress;
uint8_t bConfNum; // configuration number
uint8_t bIface; // interface value
uint8_t bNumEP; // total number of EP in the configuration
uint32_t qNextPollTime; // next poll time
bool bPollEnable; // poll enable flag
EpInfo epInfo[MASS_MAX_ENDPOINTS];
uint32_t dCBWTag; // Tag
//uint32_t dCBWDataTransferLength; // Data Transfer Length
uint8_t bLastUsbError; // Last USB error
uint8_t bMaxLUN; // Max LUN
uint8_t bTheLUN; // Active LUN
uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors
uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits
bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.
bool WriteOk[MASS_MAX_SUPPORTED_LUN];
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
// Additional Initialization Method for Subclasses
virtual uint8_t OnInit() {
return 0;
};
public:
BulkOnly(USB *p);
uint8_t GetLastUsbError() {
return bLastUsbError;
};
uint8_t GetbMaxLUN() {
return bMaxLUN; // Max LUN
}
uint8_t GetbTheLUN() {
return bTheLUN; // Active LUN
}
boolean WriteProtected(uint8_t lun);
uint8_t MediaCTL(uint8_t lun, uint8_t ctl);
uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);
uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs);
uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf);
uint8_t LockMedia(uint8_t lun, uint8_t lock);
bool LUNIsGood(uint8_t lun);
uint32_t GetCapacity(uint8_t lun);
uint16_t GetSectorSize(uint8_t lun);
// USBDeviceConfig implementation
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
virtual uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
virtual uint8_t Release();
virtual uint8_t Poll();
virtual uint8_t GetAddress() {
return bAddress;
};
// UsbConfigXtracter implementation
virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
virtual boolean DEVCLASSOK(uint8_t klass) {
return (klass == USB_CLASS_MASS_STORAGE);
}
uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
private:
uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf);
uint8_t TestUnitReady(uint8_t lun);
uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf);
uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf);
uint8_t GetMaxLUN(uint8_t *max_lun);
uint8_t SetCurLUN(uint8_t lun);
void Reset();
uint8_t ResetRecovery();
uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);
void ClearAllEP();
void CheckMedia();
boolean CheckLUN(uint8_t lun);
uint8_t Page3F(uint8_t lun);
bool IsValidCBW(uint8_t size, uint8_t *pcbw);
bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);
bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw);
uint8_t ClearEpHalt(uint8_t index);
#if MS_WANT_PARSER
uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags);
#endif
uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf);
uint8_t HandleUsbError(uint8_t error, uint8_t index);
uint8_t HandleSCSIError(uint8_t status);
};
#endif // __MASSTORAGE_H__

@ -0,0 +1,233 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(_max3421e_h_)
#error "Never include max3421e.h directly; include Usb.h instead"
#else
#define _max3421e_h_
/* MAX3421E register/bit names and bitmasks */
/* Arduino pin definitions */
/* pin numbers to port numbers */
//#define MAX_INT 9 // Duemielanove
//#define MAX_GPX 8
#define SE0 0
#define SE1 1
#define FSHOST 2
#define LSHOST 3
/* MAX3421E command byte format: rrrrr0wa where 'r' is register number */
//
// MAX3421E Registers in HOST mode.
//
#define rRCVFIFO 0x08 //1<<3
#define rSNDFIFO 0x10 //2<<3
#define rSUDFIFO 0x20 //4<<3
#define rRCVBC 0x30 //6<<3
#define rSNDBC 0x38 //7<<3
#define rUSBIRQ 0x68 //13<<3
/* USBIRQ Bits */
#define bmVBUSIRQ 0x40 //b6
#define bmNOVBUSIRQ 0x20 //b5
#define bmOSCOKIRQ 0x01 //b0
#define rUSBIEN 0x70 //14<<3
/* USBIEN Bits */
#define bmVBUSIE 0x40 //b6
#define bmNOVBUSIE 0x20 //b5
#define bmOSCOKIE 0x01 //b0
#define rUSBCTL 0x78 //15<<3
/* USBCTL Bits */
#define bmCHIPRES 0x20 //b5
#define bmPWRDOWN 0x10 //b4
#define rCPUCTL 0x80 //16<<3
/* CPUCTL Bits */
#define bmPUSLEWID1 0x80 //b7
#define bmPULSEWID0 0x40 //b6
#define bmIE 0x01 //b0
#define rPINCTL 0x88 //17<<3
/* PINCTL Bits */
#define bmFDUPSPI 0x10 //b4
#define bmINTLEVEL 0x08 //b3
#define bmPOSINT 0x04 //b2
#define bmGPXB 0x02 //b1
#define bmGPXA 0x01 //b0
// GPX pin selections
#define GPX_OPERATE 0x00
#define GPX_VBDET 0x01
#define GPX_BUSACT 0x02
#define GPX_SOF 0x03
#define rREVISION 0x90 //18<<3
#define rIOPINS1 0xa0 //20<<3
/* IOPINS1 Bits */
#define bmGPOUT0 0x01
#define bmGPOUT1 0x02
#define bmGPOUT2 0x04
#define bmGPOUT3 0x08
#define bmGPIN0 0x10
#define bmGPIN1 0x20
#define bmGPIN2 0x40
#define bmGPIN3 0x80
#define rIOPINS2 0xa8 //21<<3
/* IOPINS2 Bits */
#define bmGPOUT4 0x01
#define bmGPOUT5 0x02
#define bmGPOUT6 0x04
#define bmGPOUT7 0x08
#define bmGPIN4 0x10
#define bmGPIN5 0x20
#define bmGPIN6 0x40
#define bmGPIN7 0x80
#define rGPINIRQ 0xb0 //22<<3
/* GPINIRQ Bits */
#define bmGPINIRQ0 0x01
#define bmGPINIRQ1 0x02
#define bmGPINIRQ2 0x04
#define bmGPINIRQ3 0x08
#define bmGPINIRQ4 0x10
#define bmGPINIRQ5 0x20
#define bmGPINIRQ6 0x40
#define bmGPINIRQ7 0x80
#define rGPINIEN 0xb8 //23<<3
/* GPINIEN Bits */
#define bmGPINIEN0 0x01
#define bmGPINIEN1 0x02
#define bmGPINIEN2 0x04
#define bmGPINIEN3 0x08
#define bmGPINIEN4 0x10
#define bmGPINIEN5 0x20
#define bmGPINIEN6 0x40
#define bmGPINIEN7 0x80
#define rGPINPOL 0xc0 //24<<3
/* GPINPOL Bits */
#define bmGPINPOL0 0x01
#define bmGPINPOL1 0x02
#define bmGPINPOL2 0x04
#define bmGPINPOL3 0x08
#define bmGPINPOL4 0x10
#define bmGPINPOL5 0x20
#define bmGPINPOL6 0x40
#define bmGPINPOL7 0x80
#define rHIRQ 0xc8 //25<<3
/* HIRQ Bits */
#define bmBUSEVENTIRQ 0x01 // indicates BUS Reset Done or BUS Resume
#define bmRWUIRQ 0x02
#define bmRCVDAVIRQ 0x04
#define bmSNDBAVIRQ 0x08
#define bmSUSDNIRQ 0x10
#define bmCONDETIRQ 0x20
#define bmFRAMEIRQ 0x40
#define bmHXFRDNIRQ 0x80
#define rHIEN 0xd0 //26<<3
/* HIEN Bits */
#define bmBUSEVENTIE 0x01
#define bmRWUIE 0x02
#define bmRCVDAVIE 0x04
#define bmSNDBAVIE 0x08
#define bmSUSDNIE 0x10
#define bmCONDETIE 0x20
#define bmFRAMEIE 0x40
#define bmHXFRDNIE 0x80
#define rMODE 0xd8 //27<<3
/* MODE Bits */
#define bmHOST 0x01
#define bmLOWSPEED 0x02
#define bmHUBPRE 0x04
#define bmSOFKAENAB 0x08
#define bmSEPIRQ 0x10
#define bmDELAYISO 0x20
#define bmDMPULLDN 0x40
#define bmDPPULLDN 0x80
#define rPERADDR 0xe0 //28<<3
#define rHCTL 0xe8 //29<<3
/* HCTL Bits */
#define bmBUSRST 0x01
#define bmFRMRST 0x02
#define bmSAMPLEBUS 0x04
#define bmSIGRSM 0x08
#define bmRCVTOG0 0x10
#define bmRCVTOG1 0x20
#define bmSNDTOG0 0x40
#define bmSNDTOG1 0x80
#define rHXFR 0xf0 //30<<3
/* Host transfer token values for writing the HXFR register (R30) */
/* OR this bit field with the endpoint number in bits 3:0 */
#define tokSETUP 0x10 // HS=0, ISO=0, OUTNIN=0, SETUP=1
#define tokIN 0x00 // HS=0, ISO=0, OUTNIN=0, SETUP=0
#define tokOUT 0x20 // HS=0, ISO=0, OUTNIN=1, SETUP=0
#define tokINHS 0x80 // HS=1, ISO=0, OUTNIN=0, SETUP=0
#define tokOUTHS 0xA0 // HS=1, ISO=0, OUTNIN=1, SETUP=0
#define tokISOIN 0x40 // HS=0, ISO=1, OUTNIN=0, SETUP=0
#define tokISOOUT 0x60 // HS=0, ISO=1, OUTNIN=1, SETUP=0
#define rHRSL 0xf8 //31<<3
/* HRSL Bits */
#define bmRCVTOGRD 0x10
#define bmSNDTOGRD 0x20
#define bmKSTATUS 0x40
#define bmJSTATUS 0x80
#define bmSE0 0x00 //SE0 - disconnect state
#define bmSE1 0xc0 //SE1 - illegal state
/* Host error result codes, the 4 LSB's in the HRSL register */
#define hrSUCCESS 0x00
#define hrBUSY 0x01
#define hrBADREQ 0x02
#define hrUNDEF 0x03
#define hrNAK 0x04
#define hrSTALL 0x05
#define hrTOGERR 0x06
#define hrWRONGPID 0x07
#define hrBADBC 0x08
#define hrPIDERR 0x09
#define hrPKTERR 0x0A
#define hrCRCERR 0x0B
#define hrKERR 0x0C
#define hrJERR 0x0D
#define hrTIMEOUT 0x0E
#define hrBABBLE 0x0F
#define MODE_FS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)
#define MODE_LS_HOST (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)
#endif //_max3421e_h_

@ -0,0 +1,116 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#include "Usb.h"
// 0x80 is the default (i.e. trace) to turn off set this global to something lower.
// this allows for 126 other debugging levels.
// TO-DO: Allow assignment to a different serial port by software
int UsbDEBUGlvl = 0x80;
void E_Notifyc(char c, int lvl) {
if(UsbDEBUGlvl < lvl) return;
#if defined(ARDUINO) && ARDUINO >=100
USB_HOST_SERIAL.print(c);
#else
USB_HOST_SERIAL.print(c, BYTE);
#endif
//USB_HOST_SERIAL.flush();
}
void E_Notify(char const * msg, int lvl) {
if(UsbDEBUGlvl < lvl) return;
if(!msg) return;
char c;
while((c = pgm_read_byte(msg++))) E_Notifyc(c, lvl);
}
void E_NotifyStr(char const * msg, int lvl) {
if(UsbDEBUGlvl < lvl) return;
if(!msg) return;
char c;
while((c = *msg++)) E_Notifyc(c, lvl);
}
void E_Notify(uint8_t b, int lvl) {
if(UsbDEBUGlvl < lvl) return;
#if defined(ARDUINO) && ARDUINO >=100
USB_HOST_SERIAL.print(b);
#else
USB_HOST_SERIAL.print(b, DEC);
#endif
//USB_HOST_SERIAL.flush();
}
void E_Notify(double d, int lvl) {
if(UsbDEBUGlvl < lvl) return;
USB_HOST_SERIAL.print(d);
//USB_HOST_SERIAL.flush();
}
#ifdef DEBUG_USB_HOST
void NotifyFailGetDevDescr(void) {
Notify(PSTR("\r\ngetDevDescr "), 0x80);
}
void NotifyFailSetDevTblEntry(void) {
Notify(PSTR("\r\nsetDevTblEn "), 0x80);
}
void NotifyFailGetConfDescr(void) {
Notify(PSTR("\r\ngetConf "), 0x80);
}
void NotifyFailSetConfDescr(void) {
Notify(PSTR("\r\nsetConf "), 0x80);
}
void NotifyFailGetDevDescr(uint8_t reason) {
NotifyFailGetDevDescr();
NotifyFail(reason);
}
void NotifyFailSetDevTblEntry(uint8_t reason) {
NotifyFailSetDevTblEntry();
NotifyFail(reason);
}
void NotifyFailGetConfDescr(uint8_t reason) {
NotifyFailGetConfDescr();
NotifyFail(reason);
}
void NotifyFailSetConfDescr(uint8_t reason) {
NotifyFailSetConfDescr();
NotifyFail(reason);
}
void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID) {
Notify(PSTR("\r\nUnknown Device Connected - VID: "), 0x80);
D_PrintHex<uint16_t > (VID, 0x80);
Notify(PSTR(" PID: "), 0x80);
D_PrintHex<uint16_t > (PID, 0x80);
}
void NotifyFail(uint8_t rcode) {
D_PrintHex<uint8_t > (rcode, 0x80);
Notify(PSTR("\r\n"), 0x80);
}
#endif

@ -0,0 +1,78 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(__MESSAGE_H__)
#error "Never include message.h directly; include Usb.h instead"
#else
#define __MESSAGE_H__
extern int UsbDEBUGlvl;
void E_Notify(char const * msg, int lvl);
void E_Notify(uint8_t b, int lvl);
void E_NotifyStr(char const * msg, int lvl);
void E_Notifyc(char c, int lvl);
#ifdef DEBUG_USB_HOST
#define Notify E_Notify
#define NotifyStr E_NotifyStr
#define Notifyc E_Notifyc
void NotifyFailGetDevDescr(uint8_t reason);
void NotifyFailSetDevTblEntry(uint8_t reason);
void NotifyFailGetConfDescr(uint8_t reason);
void NotifyFailSetConfDescr(uint8_t reason);
void NotifyFailGetDevDescr(void);
void NotifyFailSetDevTblEntry(void);
void NotifyFailGetConfDescr(void);
void NotifyFailSetConfDescr(void);
void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID);
void NotifyFail(uint8_t rcode);
#else
#define Notify(...) ((void)0)
#define NotifyStr(...) ((void)0)
#define Notifyc(...) ((void)0)
#define NotifyFailGetDevDescr(...) ((void)0)
#define NotifyFailSetDevTblEntry(...) ((void)0)
#define NotifyFailGetConfDescr(...) ((void)0)
#define NotifyFailGetDevDescr(...) ((void)0)
#define NotifyFailSetDevTblEntry(...) ((void)0)
#define NotifyFailGetConfDescr(...) ((void)0)
#define NotifyFailSetConfDescr(...) ((void)0)
#define NotifyFailUnknownDevice(...) ((void)0)
#define NotifyFail(...) ((void)0)
#endif
template <class ERROR_TYPE>
void ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) {
#ifdef DEBUG_USB_HOST
Notify(msg, level);
Notify(PSTR(": "), level);
D_PrintHex<ERROR_TYPE > (rcode, level);
Notify(PSTR("\r\n"), level);
#endif
}
template <class ERROR_TYPE>
void ErrorMessage(char const * msg, ERROR_TYPE rcode = 0) {
#ifdef DEBUG_USB_HOST
Notify(msg, 0x80);
Notify(PSTR(": "), 0x80);
D_PrintHex<ERROR_TYPE > (rcode, 0x80);
Notify(PSTR("\r\n"), 0x80);
#endif
}
#endif // __MESSAGE_H__

@ -0,0 +1,67 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#include "Usb.h"
bool MultiByteValueParser::Parse(uint8_t **pp, uint16_t *pcntdn) {
if(!pBuf) {
Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80);
return false;
}
for(; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++)
pBuf[valueSize - countDown] = (**pp);
if(countDown)
return false;
countDown = valueSize;
return true;
}
bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) {
switch(nStage) {
case 0:
pBuf->valueSize = lenSize;
theParser.Initialize(pBuf);
nStage = 1;
case 1:
if(!theParser.Parse(pp, pcntdn))
return false;
arLen = 0;
arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));
arLenCntdn = arLen;
nStage = 2;
case 2:
pBuf->valueSize = valSize;
theParser.Initialize(pBuf);
nStage = 3;
case 3:
for(; arLenCntdn; arLenCntdn--) {
if(!theParser.Parse(pp, pcntdn))
return false;
if(pf)
pf(pBuf, (arLen - arLenCntdn), me);
}
nStage = 0;
}
return true;
}

@ -0,0 +1,140 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(__PARSETOOLS_H__)
#error "Never include parsetools.h directly; include Usb.h instead"
#else
#define __PARSETOOLS_H__
struct MultiValueBuffer {
uint8_t valueSize;
void *pValue;
} __attribute__((packed));
class MultiByteValueParser {
uint8_t * pBuf;
uint8_t countDown;
uint8_t valueSize;
public:
MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) {
};
const uint8_t* GetBuffer() {
return pBuf;
};
void Initialize(MultiValueBuffer * const pbuf) {
pBuf = (uint8_t*)pbuf->pValue;
countDown = valueSize = pbuf->valueSize;
};
bool Parse(uint8_t **pp, uint16_t *pcntdn);
};
class ByteSkipper {
uint8_t *pBuf;
uint8_t nStage;
uint16_t countDown;
public:
ByteSkipper() : pBuf(NULL), nStage(0), countDown(0) {
};
void Initialize(MultiValueBuffer *pbuf) {
pBuf = (uint8_t*)pbuf->pValue;
countDown = 0;
};
bool Skip(uint8_t **pp, uint16_t *pcntdn, uint16_t bytes_to_skip) {
switch(nStage) {
case 0:
countDown = bytes_to_skip;
nStage++;
case 1:
for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);
if(!countDown)
nStage = 0;
};
return (!countDown);
};
};
// Pointer to a callback function triggered for each element of PTP array when used with PTPArrayParser
typedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t count, const void *me);
class PTPListParser {
public:
enum ParseMode {
modeArray, modeRange/*, modeEnum*/
};
private:
uint8_t nStage;
uint8_t enStage;
uint32_t arLen;
uint32_t arLenCntdn;
uint8_t lenSize; // size of the array length field in bytes
uint8_t valSize; // size of the array element in bytes
MultiValueBuffer *pBuf;
// The only parser for both size and array element parsing
MultiByteValueParser theParser;
uint8_t /*ParseMode*/ prsMode;
public:
PTPListParser() :
nStage(0),
enStage(0),
arLen(0),
arLenCntdn(0),
lenSize(0),
valSize(0),
pBuf(NULL),
prsMode(modeArray) {
};
void Initialize(const uint8_t len_size, const uint8_t val_size, MultiValueBuffer * const p, const uint8_t mode = modeArray) {
pBuf = p;
lenSize = len_size;
valSize = val_size;
prsMode = mode;
if(prsMode == modeRange) {
arLenCntdn = arLen = 3;
nStage = 2;
} else {
arLenCntdn = arLen = 0;
nStage = 0;
}
enStage = 0;
theParser.Initialize(p);
};
bool Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me = NULL);
};
#endif // __PARSETOOLS_H__

@ -0,0 +1,84 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(__PRINTHEX_H__)
#error "Never include printhex.h directly; include Usb.h instead"
#else
#define __PRINTHEX_H__
void E_Notifyc(char c, int lvl);
template <class T>
void PrintHex(T val, int lvl) {
int num_nibbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
if(v > 57) v += 7;
E_Notifyc(v, lvl);
} while(--num_nibbles);
}
template <class T>
void PrintBin(T val, int lvl) {
for(T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1)
if(val & mask)
E_Notifyc('1', lvl);
else
E_Notifyc('0', lvl);
}
template <class T>
void SerialPrintHex(T val) {
int num_nibbles = sizeof (T) * 2;
do {
char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
if(v > 57) v += 7;
USB_HOST_SERIAL.print(v);
} while(--num_nibbles);
}
/*template <class T>
void PrintHex2(Print *prn, T val) {
T mask = (((T)1) << (((sizeof (T) << 1) - 1) << 2));
while(mask > 1) {
if(val < mask)
prn->print("0");
mask >>= 4;
}
prn->print((T)val, HEX);
}*/
template <class T> void D_PrintHex(T val, int lvl) {
#ifdef DEBUG_USB_HOST
PrintHex<T > (val, lvl);
#endif
}
template <class T>
void D_PrintBin(T val, int lvl) {
#ifdef DEBUG_USB_HOST
PrintBin<T > (val, lvl);
#endif
}
#endif // __PRINTHEX_H__

@ -0,0 +1,143 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#ifndef USB_HOST_SHIELD_SETTINGS_H
#define USB_HOST_SHIELD_SETTINGS_H
#include "macros.h"
////////////////////////////////////////////////////////////////////////////////
/* Added by Bill Greiman to speed up mass storage initialization with USB
* flash drives and simple USB hard drives.
* Disable this by defining DELAY(x) to be delay(x).
*/
#define DELAY(x) if((x) < 200)delay(x)
/* Almost all USB flash drives and simple USB hard drives fail the write
* protect test and add 20 - 30 seconds to USB init. Set SKIP_WRITE_PROTECT
* to nonzero to skip the test and assume the drive is writable.
*/
#define SKIP_WRITE_PROTECT 1
////////////////////////////////////////////////////////////////////////////////
// DEBUGGING
////////////////////////////////////////////////////////////////////////////////
/* Set this to 1 to activate serial debugging */
#define ENABLE_UHS_DEBUGGING 0
/* This can be used to select which serial port to use for debugging if
* multiple serial ports are available.
* For example Serial3.
*/
#ifndef USB_HOST_SERIAL
#define USB_HOST_SERIAL Serial
#endif
////////////////////////////////////////////////////////////////////////////////
// Manual board activation
////////////////////////////////////////////////////////////////////////////////
/* Set this to 1 if you are using an Arduino Mega ADK board with MAX3421e built-in */
#define USE_UHS_MEGA_ADK 0 // If you are using Arduino 1.5.5 or newer there is no need to do this manually
/* Set this to 1 if you are using a Black Widdow */
#define USE_UHS_BLACK_WIDDOW 0
/* Set this to a one to use the xmem2 lock. This is needed for multitasking and threading */
#define USE_XMEM_SPI_LOCK 0
////////////////////////////////////////////////////////////////////////////////
// MASS STORAGE
////////////////////////////////////////////////////////////////////////////////
// <<<<<<<<<<<<<<<< IMPORTANT >>>>>>>>>>>>>>>
// Set this to 1 to support single LUN devices, and save RAM. -- I.E. thumb drives.
// Each LUN needs ~13 bytes to be able to track the state of each unit.
#ifndef MASS_MAX_SUPPORTED_LUN
#define MASS_MAX_SUPPORTED_LUN 1 ///// 8 WHG
#endif
////////////////////////////////////////////////////////////////////////////////
// Set to 1 to use the faster spi4teensy3 driver.
////////////////////////////////////////////////////////////////////////////////
#ifndef USE_SPI4TEENSY3
#define USE_SPI4TEENSY3 1
#endif
////////////////////////////////////////////////////////////////////////////////
// AUTOMATIC Settings
////////////////////////////////////////////////////////////////////////////////
// No user serviceable parts below this line.
// DO NOT change anything below here unless you are a developer!
#if defined(ARDUINO) && ARDUINO >=100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <pins_arduino.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#define F(str) (str)
#endif
#if defined(__GNUC__) && defined(__AVR__)
#ifndef GCC_VERSION
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#endif
#if GCC_VERSION < 40602 // Test for GCC < 4.6.2
#ifdef PROGMEM
#undef PROGMEM
#define PROGMEM __attribute__((section(".progmem.data"))) // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734#c4
#ifdef PSTR
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) // Copied from pgmspace.h in avr-libc source
#endif
#endif
#endif
#endif
#if !defined(DEBUG_USB_HOST) && ENABLE_UHS_DEBUGGING
#define DEBUG_USB_HOST
#endif
// To use some other locking (e.g. freertos),
// define XMEM_ACQUIRE_SPI and XMEM_RELEASE_SPI to point to your lock and unlock.
// NOTE: NO argument is passed. You have to do this within your routine for
// whatever you are using to lock and unlock.
#if !defined(XMEM_ACQUIRE_SPI)
#if USE_XMEM_SPI_LOCK || defined(USE_MULTIPLE_APP_API)
#include <xmem.h>
#else
#define XMEM_ACQUIRE_SPI() (void(0))
#define XMEM_RELEASE_SPI() (void(0))
#endif
#endif
#if !defined(EXT_RAM) && defined(EXT_RAM_STACK) || defined(EXT_RAM_HEAP)
#include <xmem.h>
#else
#define EXT_RAM 0
#endif
#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))
#define USING_SPI4TEENSY3 USE_SPI4TEENSY3
#else
#define USING_SPI4TEENSY3 0
#endif
#if (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822)
#include <SPI.h> // Use the Arduino SPI library for the Arduino Due and RedBearLab nRF51822
#endif
#endif /* SETTINGS_H */

@ -0,0 +1,166 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
#if !defined(_usb_h_) || defined(_ch9_h_)
#error "Never include usb_ch9.h directly; include Usb.h instead"
#else
/* USB chapter 9 structures */
#define _ch9_h_
/* Misc.USB constants */
#define DEV_DESCR_LEN 18 //device descriptor length
#define CONF_DESCR_LEN 9 //configuration descriptor length
#define INTR_DESCR_LEN 9 //interface descriptor length
#define EP_DESCR_LEN 7 //endpoint descriptor length
/* Standard Device Requests */
#define USB_REQUEST_GET_STATUS 0 // Standard Device Request - GET STATUS
#define USB_REQUEST_CLEAR_FEATURE 1 // Standard Device Request - CLEAR FEATURE
#define USB_REQUEST_SET_FEATURE 3 // Standard Device Request - SET FEATURE
#define USB_REQUEST_SET_ADDRESS 5 // Standard Device Request - SET ADDRESS
#define USB_REQUEST_GET_DESCRIPTOR 6 // Standard Device Request - GET DESCRIPTOR
#define USB_REQUEST_SET_DESCRIPTOR 7 // Standard Device Request - SET DESCRIPTOR
#define USB_REQUEST_GET_CONFIGURATION 8 // Standard Device Request - GET CONFIGURATION
#define USB_REQUEST_SET_CONFIGURATION 9 // Standard Device Request - SET CONFIGURATION
#define USB_REQUEST_GET_INTERFACE 10 // Standard Device Request - GET INTERFACE
#define USB_REQUEST_SET_INTERFACE 11 // Standard Device Request - SET INTERFACE
#define USB_REQUEST_SYNCH_FRAME 12 // Standard Device Request - SYNCH FRAME
#define USB_FEATURE_ENDPOINT_HALT 0 // CLEAR/SET FEATURE - Endpoint Halt
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // CLEAR/SET FEATURE - Device remote wake-up
#define USB_FEATURE_TEST_MODE 2 // CLEAR/SET FEATURE - Test mode
/* Setup Data Constants */
#define USB_SETUP_HOST_TO_DEVICE 0x00 // Device Request bmRequestType transfer direction - host to device transfer
#define USB_SETUP_DEVICE_TO_HOST 0x80 // Device Request bmRequestType transfer direction - device to host transfer
#define USB_SETUP_TYPE_STANDARD 0x00 // Device Request bmRequestType type - standard
#define USB_SETUP_TYPE_CLASS 0x20 // Device Request bmRequestType type - class
#define USB_SETUP_TYPE_VENDOR 0x40 // Device Request bmRequestType type - vendor
#define USB_SETUP_RECIPIENT_DEVICE 0x00 // Device Request bmRequestType recipient - device
#define USB_SETUP_RECIPIENT_INTERFACE 0x01 // Device Request bmRequestType recipient - interface
#define USB_SETUP_RECIPIENT_ENDPOINT 0x02 // Device Request bmRequestType recipient - endpoint
#define USB_SETUP_RECIPIENT_OTHER 0x03 // Device Request bmRequestType recipient - other
/* USB descriptors */
#define USB_DESCRIPTOR_DEVICE 0x01 // bDescriptorType for a Device Descriptor.
#define USB_DESCRIPTOR_CONFIGURATION 0x02 // bDescriptorType for a Configuration Descriptor.
#define USB_DESCRIPTOR_STRING 0x03 // bDescriptorType for a String Descriptor.
#define USB_DESCRIPTOR_INTERFACE 0x04 // bDescriptorType for an Interface Descriptor.
#define USB_DESCRIPTOR_ENDPOINT 0x05 // bDescriptorType for an Endpoint Descriptor.
#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // bDescriptorType for a Device Qualifier.
#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // bDescriptorType for a Other Speed Configuration.
#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // bDescriptorType for Interface Power.
#define USB_DESCRIPTOR_OTG 0x09 // bDescriptorType for an OTG Descriptor.
#define HID_DESCRIPTOR_HID 0x21
/* OTG SET FEATURE Constants */
#define OTG_FEATURE_B_HNP_ENABLE 3 // SET FEATURE OTG - Enable B device to perform HNP
#define OTG_FEATURE_A_HNP_SUPPORT 4 // SET FEATURE OTG - A device supports HNP
#define OTG_FEATURE_A_ALT_HNP_SUPPORT 5 // SET FEATURE OTG - Another port on the A device supports HNP
/* USB Endpoint Transfer Types */
#define USB_TRANSFER_TYPE_CONTROL 0x00 // Endpoint is a control endpoint.
#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x01 // Endpoint is an isochronous endpoint.
#define USB_TRANSFER_TYPE_BULK 0x02 // Endpoint is a bulk endpoint.
#define USB_TRANSFER_TYPE_INTERRUPT 0x03 // Endpoint is an interrupt endpoint.
#define bmUSB_TRANSFER_TYPE 0x03 // bit mask to separate transfer type from ISO attributes
/* Standard Feature Selectors for CLEAR_FEATURE Requests */
#define USB_FEATURE_ENDPOINT_STALL 0 // Endpoint recipient
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 // Device recipient
#define USB_FEATURE_TEST_MODE 2 // Device recipient
/* descriptor data structures */
/* Device descriptor structure */
typedef struct {
uint8_t bLength; // Length of this descriptor.
uint8_t bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE).
uint16_t bcdUSB; // USB Spec Release Number (BCD).
uint8_t bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
uint8_t bDeviceSubClass; // Subclass code (assigned by the USB-IF).
uint8_t bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0.
uint16_t idVendor; // Vendor ID (assigned by the USB-IF).
uint16_t idProduct; // Product ID (assigned by the manufacturer).
uint16_t bcdDevice; // Device release number (BCD).
uint8_t iManufacturer; // Index of String Descriptor describing the manufacturer.
uint8_t iProduct; // Index of String Descriptor describing the product.
uint8_t iSerialNumber; // Index of String Descriptor with the device's serial number.
uint8_t bNumConfigurations; // Number of possible configurations.
} __attribute__((packed)) USB_DEVICE_DESCRIPTOR;
/* Configuration descriptor structure */
typedef struct {
uint8_t bLength; // Length of this descriptor.
uint8_t bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).
uint16_t wTotalLength; // Total length of all descriptors for this configuration.
uint8_t bNumInterfaces; // Number of interfaces in this configuration.
uint8_t bConfigurationValue; // Value of this configuration (1 based).
uint8_t iConfiguration; // Index of String Descriptor describing the configuration.
uint8_t bmAttributes; // Configuration characteristics.
uint8_t bMaxPower; // Maximum power consumed by this configuration.
} __attribute__((packed)) USB_CONFIGURATION_DESCRIPTOR;
/* Interface descriptor structure */
typedef struct {
uint8_t bLength; // Length of this descriptor.
uint8_t bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).
uint8_t bInterfaceNumber; // Number of this interface (0 based).
uint8_t bAlternateSetting; // Value of this alternate interface setting.
uint8_t bNumEndpoints; // Number of endpoints in this interface.
uint8_t bInterfaceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
uint8_t bInterfaceSubClass; // Subclass code (assigned by the USB-IF).
uint8_t bInterfaceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
uint8_t iInterface; // Index of String Descriptor describing the interface.
} __attribute__((packed)) USB_INTERFACE_DESCRIPTOR;
/* Endpoint descriptor structure */
typedef struct {
uint8_t bLength; // Length of this descriptor.
uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
uint8_t bmAttributes; // Endpoint transfer type.
uint16_t wMaxPacketSize; // Maximum packet size.
uint8_t bInterval; // Polling interval in frames.
} __attribute__((packed)) USB_ENDPOINT_DESCRIPTOR;
/* HID descriptor */
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdHID; // HID class specification release
uint8_t bCountryCode;
uint8_t bNumDescriptors; // Number of additional class specific descriptors
uint8_t bDescrType; // Type of class descriptor
uint16_t wDescriptorLength; // Total size of the Report descriptor
} __attribute__((packed)) USB_HID_DESCRIPTOR;
typedef struct {
uint8_t bDescrType; // Type of class descriptor
uint16_t wDescriptorLength; // Total size of the Report descriptor
} __attribute__((packed)) HID_CLASS_DESCRIPTOR_LEN_AND_TYPE;
#endif // _ch9_h_

@ -0,0 +1,462 @@
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Circuits At Home, LTD
Web : http://www.circuitsathome.com
e-mail : support@circuitsathome.com
*/
/* MAX3421E-based USB Host Library header file */
#if !defined(_usb_h_) || defined(_USBHOST_H_)
#error "Never include usbhost.h directly; include Usb.h instead"
#else
#define _USBHOST_H_
#if USING_SPI4TEENSY3
#include <spi4teensy3.h>
#include <sys/types.h>
#endif
/* SPI initialization */
template< typename SPI_CLK, typename SPI_MOSI, typename SPI_MISO, typename SPI_SS > class SPi {
public:
#if USING_SPI4TEENSY3
static void init() {
// spi4teensy3 inits everything for us, except /SS
// CLK, MOSI and MISO are hard coded for now.
// spi4teensy3::init(0,0,0); // full speed, cpol 0, cpha 0
spi4teensy3::init(); // full speed, cpol 0, cpha 0
SPI_SS::SetDirWrite();
SPI_SS::Set();
}
#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)
static void init() {
SPI_SS::SetDirWrite();
SPI_SS::Set();
SPI.begin();
SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz
}
#elif defined(RBL_NRF51822)
static void init() {
SPI_SS::SetDirWrite();
SPI_SS::Set();
SPI.begin();
// SPI.setFrequency(SPI_FREQUENCY_8M);
}
#else
static void init() {
//uint8_t tmp;
SPI_CLK::SetDirWrite();
SPI_MOSI::SetDirWrite();
SPI_MISO::SetDirRead();
SPI_SS::SetDirWrite();
/* mode 00 (CPOL=0, CPHA=0) master, fclk/2. Mode 11 (CPOL=11, CPHA=11) is also supported by MAX3421E */
SPCR = 0x50;
SPSR = 0x01; // 0x01
/**/
//tmp = SPSR;
//tmp = SPDR;
}
#endif
};
/* SPI pin definitions. see avrpins.h */
#if defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
typedef SPi< Pb1, Pb2, Pb3, Pb0 > spi;
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
typedef SPi< Pb5, Pb3, Pb4, Pb2 > spi;
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi;
#elif defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))
typedef SPi< P13, P11, P12, P10 > spi;
#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)
typedef SPi< P76, P75, P74, P10 > spi;
#elif defined(RBL_NRF51822)
typedef SPi< P16, P18, P17, P10 > spi;
#else
#error "No SPI entry in usbhost.h"
#endif
typedef enum {
vbus_on = 0,
vbus_off = GPX_VBDET
} VBUS_t;
template< typename SPI_SS, typename INTR > class MAX3421e /* : public spi */ {
static uint8_t vbusState;
public:
MAX3421e();
void regWr(uint8_t reg, uint8_t data);
uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p);
void gpioWr(uint8_t data);
uint8_t regRd(uint8_t reg);
uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p);
uint8_t gpioRd();
uint16_t reset();
int8_t Init();
int8_t Init(int mseconds);
void vbusPower(VBUS_t state) {
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state));
}
uint8_t getVbusState(void) {
return vbusState;
};
void busprobe();
uint8_t GpxHandler();
uint8_t IntHandler();
uint8_t Task();
};
template< typename SPI_SS, typename INTR >
uint8_t MAX3421e< SPI_SS, INTR >::vbusState = 0;
/* constructor */
template< typename SPI_SS, typename INTR >
MAX3421e< SPI_SS, INTR >::MAX3421e() {
// Leaving ADK hardware setup in here, for now. This really belongs with the other parts.
#ifdef BOARD_MEGA_ADK
// For Mega ADK, which has a Max3421e on-board, set MAX_RESET to output mode, and then set it to HIGH
P55::SetDirWrite();
P55::Set();
#endif
};
/* write single byte into MAX3421 register */
template< typename SPI_SS, typename INTR >
void MAX3421e< SPI_SS, INTR >::regWr(uint8_t reg, uint8_t data) {
XMEM_ACQUIRE_SPI();
SPI_SS::Clear();
#if USING_SPI4TEENSY3
uint8_t c[2];
c[0] = reg | 0x02;
c[1] = data;
spi4teensy3::send(c, 2);
#elif (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822)
SPI.transfer(reg | 0x02);
SPI.transfer(data);
#else
SPDR = (reg | 0x02);
while(!(SPSR & (1 << SPIF)));
SPDR = data;
while(!(SPSR & (1 << SPIF)));
#endif
SPI_SS::Set();
XMEM_RELEASE_SPI();
return;
};
/* multiple-byte write */
/* returns a pointer to memory position after last written */
template< typename SPI_SS, typename INTR >
uint8_t* MAX3421e< SPI_SS, INTR >::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
XMEM_ACQUIRE_SPI();
SPI_SS::Clear();
#if USING_SPI4TEENSY3
spi4teensy3::send(reg | 0x02);
spi4teensy3::send(data_p, nbytes);
data_p += nbytes;
#elif (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822)
SPI.transfer(reg | 0x02);
while(nbytes) {
SPI.transfer(*data_p);
nbytes--;
data_p++; // advance data pointer
}
#else
SPDR = (reg | 0x02); //set WR bit and send register number
while(nbytes) {
while(!(SPSR & (1 << SPIF))); //check if previous byte was sent
SPDR = (*data_p); // send next data byte
nbytes--;
data_p++; // advance data pointer
}
while(!(SPSR & (1 << SPIF)));
#endif
SPI_SS::Set();
XMEM_RELEASE_SPI();
return ( data_p);
}
/* GPIO write */
/*GPIO byte is split between 2 registers, so two writes are needed to write one byte */
/* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */
template< typename SPI_SS, typename INTR >
void MAX3421e< SPI_SS, INTR >::gpioWr(uint8_t data) {
regWr(rIOPINS1, data);
data >>= 4;
regWr(rIOPINS2, data);
return;
}
/* single host register read */
template< typename SPI_SS, typename INTR >
uint8_t MAX3421e< SPI_SS, INTR >::regRd(uint8_t reg) {
XMEM_ACQUIRE_SPI();
SPI_SS::Clear();
#if USING_SPI4TEENSY3
spi4teensy3::send(reg);
uint8_t rv = spi4teensy3::receive();
SPI_SS::Set();
#elif (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822)
SPI.transfer(reg);
uint8_t rv = SPI.transfer(0);
SPI_SS::Set();
#else
SPDR = reg;
while(!(SPSR & (1 << SPIF)));
SPDR = 0; //send empty byte
while(!(SPSR & (1 << SPIF)));
SPI_SS::Set();
uint8_t rv = SPDR;
#endif
XMEM_RELEASE_SPI();
return (rv);
}
/* multiple-byte register read */
/* returns a pointer to a memory position after last read */
template< typename SPI_SS, typename INTR >
uint8_t* MAX3421e< SPI_SS, INTR >::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {
XMEM_ACQUIRE_SPI();
SPI_SS::Clear();
#if USING_SPI4TEENSY3
spi4teensy3::send(reg);
spi4teensy3::receive(data_p, nbytes);
data_p += nbytes;
#elif (defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822)
SPI.transfer(reg);
while(nbytes) {
*data_p++ = SPI.transfer(0);
nbytes--;
}
#else
SPDR = reg;
while(!(SPSR & (1 << SPIF))); //wait
while(nbytes) {
SPDR = 0; //send empty byte
nbytes--;
while(!(SPSR & (1 << SPIF)));
#if 0
{
*data_p = SPDR;
printf("%2.2x ", *data_p);
}
data_p++;
}
printf("\r\n");
#else
*data_p++ = SPDR;
}
#endif
#endif
SPI_SS::Set();
XMEM_RELEASE_SPI();
return ( data_p);
}
/* GPIO read. See gpioWr for explanation */
/* GPIN pins are in high nibbles of IOPINS1, IOPINS2 */
template< typename SPI_SS, typename INTR >
uint8_t MAX3421e< SPI_SS, INTR >::gpioRd() {
uint8_t gpin = 0;
gpin = regRd(rIOPINS2); //pins 4-7
gpin &= 0xf0; //clean lower nibble
gpin |= (regRd(rIOPINS1) >> 4); //shift low bits and OR with upper from previous operation.
return ( gpin);
}
/* reset MAX3421E. Returns number of cycles it took for PLL to stabilize after reset
or zero if PLL haven't stabilized in 65535 cycles */
template< typename SPI_SS, typename INTR >
uint16_t MAX3421e< SPI_SS, INTR >::reset() {
uint16_t i = 0;
regWr(rUSBCTL, bmCHIPRES);
regWr(rUSBCTL, 0x00);
while(++i) {
if((regRd(rUSBIRQ) & bmOSCOKIRQ)) {
break;
}
}
return ( i);
}
/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */
template< typename SPI_SS, typename INTR >
int8_t MAX3421e< SPI_SS, INTR >::Init() {
XMEM_ACQUIRE_SPI();
// Moved here.
// you really should not init hardware in the constructor when it involves locks.
// Also avoids the vbus flicker issue confusing some devices.
/* pin and peripheral setup */
SPI_SS::SetDirWrite();
SPI_SS::Set();
spi::init();
INTR::SetDirRead();
XMEM_RELEASE_SPI();
/* MAX3421E - full-duplex SPI, level interrupt */
// GPX pin on. Moved here, otherwise we flicker the vbus.
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
if(reset() == 0) { //OSCOKIRQ hasn't asserted in time
return ( -1);
}
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection
/* check if device is connected */
regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish
busprobe(); //check if anything is connected
regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
regWr(rCPUCTL, 0x01); //enable interrupt pin
return ( 0);
}
/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */
template< typename SPI_SS, typename INTR >
int8_t MAX3421e< SPI_SS, INTR >::Init(int mseconds) {
XMEM_ACQUIRE_SPI();
// Moved here.
// you really should not init hardware in the constructor when it involves locks.
// Also avoids the vbus flicker issue confusing some devices.
/* pin and peripheral setup */
SPI_SS::SetDirWrite();
SPI_SS::Set();
spi::init();
INTR::SetDirRead();
XMEM_RELEASE_SPI();
/* MAX3421E - full-duplex SPI, level interrupt, vbus off */
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));
if(reset() == 0) { //OSCOKIRQ hasn't asserted in time
return ( -1);
}
// Delay a minimum of 1 second to ensure any capacitors are drained.
// 1 second is required to make sure we do not smoke a Microdrive!
if(mseconds < 1000) mseconds = 1000;
delay(mseconds);
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection
/* check if device is connected */
regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish
busprobe(); //check if anything is connected
regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
regWr(rCPUCTL, 0x01); //enable interrupt pin
// GPX pin on. This is done here so that busprobe will fail if we have a switch connected.
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
return ( 0);
}
/* probe bus to determine device presence and speed and switch host to this speed */
template< typename SPI_SS, typename INTR >
void MAX3421e< SPI_SS, INTR >::busprobe() {
uint8_t bus_sample;
bus_sample = regRd(rHRSL); //Get J,K status
bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the byte
switch(bus_sample) { //start full-speed or low-speed host
case( bmJSTATUS):
if((regRd(rMODE) & bmLOWSPEED) == 0) {
regWr(rMODE, MODE_FS_HOST); //start full-speed host
vbusState = FSHOST;
} else {
regWr(rMODE, MODE_LS_HOST); //start low-speed host
vbusState = LSHOST;
}
break;
case( bmKSTATUS):
if((regRd(rMODE) & bmLOWSPEED) == 0) {
regWr(rMODE, MODE_LS_HOST); //start low-speed host
vbusState = LSHOST;
} else {
regWr(rMODE, MODE_FS_HOST); //start full-speed host
vbusState = FSHOST;
}
break;
case( bmSE1): //illegal state
vbusState = SE1;
break;
case( bmSE0): //disconnected state
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ);
vbusState = SE0;
break;
}//end switch( bus_sample )
}
/* MAX3421 state change task and interrupt handler */
template< typename SPI_SS, typename INTR >
uint8_t MAX3421e< SPI_SS, INTR >::Task(void) {
uint8_t rcode = 0;
uint8_t pinvalue;
//USB_HOST_SERIAL.print("Vbus state: ");
//USB_HOST_SERIAL.println( vbusState, HEX );
pinvalue = INTR::IsSet(); //Read();
//pinvalue = digitalRead( MAX_INT );
if(pinvalue == 0) {
rcode = IntHandler();
}
// pinvalue = digitalRead( MAX_GPX );
// if( pinvalue == LOW ) {
// GpxHandler();
// }
// usbSM(); //USB state machine
return ( rcode);
}
template< typename SPI_SS, typename INTR >
uint8_t MAX3421e< SPI_SS, INTR >::IntHandler() {
uint8_t HIRQ;
uint8_t HIRQ_sendback = 0x00;
HIRQ = regRd(rHIRQ); //determine interrupt source
//if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler
// HIRQ_sendback |= bmFRAMEIRQ;
//}//end FRAMEIRQ handling
if(HIRQ & bmCONDETIRQ) {
busprobe();
HIRQ_sendback |= bmCONDETIRQ;
}
/* End HIRQ interrupts handling, clear serviced IRQs */
regWr(rHIRQ, HIRQ_sendback);
return ( HIRQ_sendback);
}
//template< typename SPI_SS, typename INTR >
//uint8_t MAX3421e< SPI_SS, INTR >::GpxHandler()
//{
// uint8_t GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register
//// if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload
//// vbusPwr( OFF ); //attempt powercycle
//// delay( 1000 );
//// vbusPwr( ON );
//// regWr( rGPINIRQ, bmGPINIRQ7 );
//// }
// return( GPINIRQ );
//}
#endif //_USBHOST_H_
Loading…
Cancel
Save