mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
Merge branch 'usb'
This commit is contained in:
5
Makefile
5
Makefile
@@ -46,8 +46,11 @@ products += $(dependencies)
|
||||
|
||||
$(all_objs): $(generated_headers)
|
||||
|
||||
epsilon.$(EXE): $(objs)
|
||||
test.$(EXE): $(objs)
|
||||
|
||||
.SECONDARY: $(objs)
|
||||
%.$(EXE): $(objs)
|
||||
%.$(EXE):
|
||||
@echo "LD $@"
|
||||
$(Q) $(LD) $^ $(LDFLAGS) -o $@
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ include apps/shared/Makefile
|
||||
include apps/home/Makefile
|
||||
include apps/on_boarding/Makefile
|
||||
include apps/hardware_test/Makefile
|
||||
include apps/usb/Makefile
|
||||
snapshots =
|
||||
|
||||
# All selected apps are included. Each Makefile below is responsible for setting
|
||||
@@ -28,7 +29,6 @@ app_objs += $(addprefix apps/,\
|
||||
shift_alpha_lock_view.o\
|
||||
suspend_timer.o\
|
||||
title_bar_view.o\
|
||||
usb_timer.o\
|
||||
variable_box_controller.o\
|
||||
variable_box_leaf_cell.o\
|
||||
)
|
||||
|
||||
@@ -18,12 +18,12 @@ AppsContainer::AppsContainer() :
|
||||
m_updateController(),
|
||||
m_ledTimer(LedTimer()),
|
||||
m_batteryTimer(BatteryTimer(this)),
|
||||
m_USBTimer(USBTimer(this)),
|
||||
m_suspendTimer(SuspendTimer(this)),
|
||||
m_backlightDimmingTimer(),
|
||||
m_homeSnapshot(),
|
||||
m_onBoardingSnapshot(),
|
||||
m_hardwareTestSnapshot()
|
||||
m_hardwareTestSnapshot(),
|
||||
m_usbConnectedSnapshot()
|
||||
{
|
||||
m_emptyBatteryWindow.setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height));
|
||||
Poincare::Expression::setCircuitBreaker(AppsContainer::poincareCircuitBreaker);
|
||||
@@ -42,6 +42,10 @@ App::Snapshot * AppsContainer::onBoardingAppSnapshot() {
|
||||
return &m_onBoardingSnapshot;
|
||||
}
|
||||
|
||||
App::Snapshot * AppsContainer::usbConnectedAppSnapshot() {
|
||||
return &m_usbConnectedSnapshot;
|
||||
}
|
||||
|
||||
void AppsContainer::reset() {
|
||||
Clipboard::sharedClipboard()->reset();
|
||||
for (int i = 0; i < numberOfApps(); i++) {
|
||||
@@ -80,7 +84,27 @@ void AppsContainer::suspend(bool checkIfPowerKeyReleased) {
|
||||
bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
|
||||
bool alphaLockWantsRedraw = updateAlphaLock();
|
||||
|
||||
bool didProcessEvent = Container::dispatchEvent(event);
|
||||
bool didProcessEvent = false;
|
||||
|
||||
if (event == Ion::Events::USBPlug) {
|
||||
if (Ion::USB::isPlugged()) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) {
|
||||
displayExamModePopUp(false);
|
||||
}
|
||||
Ion::USB::enable();
|
||||
Ion::Backlight::setBrightness(Ion::Backlight::MaxBrightness);
|
||||
} else {
|
||||
Ion::USB::disable();
|
||||
}
|
||||
didProcessEvent = true;
|
||||
} else if (event == Ion::Events::USBEnumeration) {
|
||||
switchTo(usbConnectedAppSnapshot());
|
||||
Ion::USB::DFU();
|
||||
switchTo(appSnapshotAtIndex(0));
|
||||
didProcessEvent = true;
|
||||
} else {
|
||||
didProcessEvent = Container::dispatchEvent(event);
|
||||
}
|
||||
|
||||
if (!didProcessEvent) {
|
||||
didProcessEvent = processEvent(event);
|
||||
@@ -126,10 +150,10 @@ void AppsContainer::switchTo(App::Snapshot * snapshot) {
|
||||
|
||||
void AppsContainer::run() {
|
||||
window()->setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height));
|
||||
refreshPreferences();
|
||||
#if EPSILON_ONBOARDING_APP
|
||||
switchTo(onBoardingAppSnapshot());
|
||||
#else
|
||||
refreshPreferences();
|
||||
if (numberOfApps() == 2) {
|
||||
switchTo(appSnapshotAtIndex(1));
|
||||
} else {
|
||||
@@ -189,11 +213,11 @@ Window * AppsContainer::window() {
|
||||
}
|
||||
|
||||
int AppsContainer::numberOfContainerTimers() {
|
||||
return 4+(GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate);
|
||||
return 3+(GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate);
|
||||
}
|
||||
|
||||
Timer * AppsContainer::containerTimerAtIndex(int i) {
|
||||
Timer * timers[5] = {&m_batteryTimer, &m_USBTimer, &m_suspendTimer, &m_backlightDimmingTimer, &m_ledTimer};
|
||||
Timer * timers[4] = {&m_batteryTimer, &m_suspendTimer, &m_backlightDimmingTimer, &m_ledTimer};
|
||||
return timers[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "on_boarding/app.h"
|
||||
#include "hardware_test/app.h"
|
||||
#include "on_boarding/update_controller.h"
|
||||
#include "usb/app.h"
|
||||
#include "apps_window.h"
|
||||
#include "empty_battery_window.h"
|
||||
#include "math_toolbox.h"
|
||||
@@ -12,7 +13,6 @@
|
||||
#include "exam_pop_up_controller.h"
|
||||
#include "led_timer.h"
|
||||
#include "battery_timer.h"
|
||||
#include "usb_timer.h"
|
||||
#include "suspend_timer.h"
|
||||
#include "backlight_dimming_timer.h"
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
virtual App::Snapshot * appSnapshotAtIndex(int index) = 0;
|
||||
App::Snapshot * hardwareTestAppSnapshot();
|
||||
App::Snapshot * onBoardingAppSnapshot();
|
||||
App::Snapshot * usbConnectedAppSnapshot();
|
||||
void reset();
|
||||
Poincare::Context * globalContext();
|
||||
MathToolbox * mathToolbox();
|
||||
@@ -67,12 +68,12 @@ private:
|
||||
OnBoarding::UpdateController m_updateController;
|
||||
LedTimer m_ledTimer;
|
||||
BatteryTimer m_batteryTimer;
|
||||
USBTimer m_USBTimer;
|
||||
SuspendTimer m_suspendTimer;
|
||||
BacklightDimmingTimer m_backlightDimmingTimer;
|
||||
Home::App::Snapshot m_homeSnapshot;
|
||||
OnBoarding::App::Snapshot m_onBoardingSnapshot;
|
||||
HardwareTest::App::Snapshot m_hardwareTestSnapshot;
|
||||
USB::App::Snapshot m_usbConnectedSnapshot;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include <ion/events.h>
|
||||
#include "../shared/message_controller.h"
|
||||
#include "menu_controller.h"
|
||||
#include "script_store.h"
|
||||
#include "python_toolbox.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
UpdateAvailable = "UPDATE VERFUGBAR"
|
||||
UpdateMessage1 = "Wichtige Verbesserungen fur Ihren"
|
||||
UpdateMessage2 = "Rechner stehen zur Verfugung."
|
||||
UpdateMessage3 = "Melden Sie sich von Ihrem Computer an"
|
||||
UpdateMessage3 = "Verbinden Sie sich von Ihrem Computer an"
|
||||
UpdateMessage4 = "www.numworks.com/update"
|
||||
Skip = "Uberspringen"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
UpdateAvailable = "ACTUALIZACION DISPONIBLE"
|
||||
UpdateMessage1 = "Hay mejoras importantes"
|
||||
UpdateMessage2 = "para su calculadora."
|
||||
UpdateMessage3 = "Visita nuestra pagina desde su ordenador"
|
||||
UpdateMessage3 = "Visite nuestra pagina desde su ordenador"
|
||||
UpdateMessage4 = "www.numworks.com/update"
|
||||
Skip = "Saltar"
|
||||
|
||||
@@ -4,12 +4,63 @@
|
||||
|
||||
namespace OnBoarding {
|
||||
|
||||
static I18n::Message sOnBoardingMessages[] = {I18n::Message::UpdateAvailable, I18n::Message::UpdateMessage1, I18n::Message::UpdateMessage2, I18n::Message::UpdateMessage3, I18n::Message::UpdateMessage4};
|
||||
UpdateController::MessageViewWithSkip::MessageViewWithSkip(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages) :
|
||||
MessageView(messages, colors, numberOfMessages),
|
||||
m_skipView(KDText::FontSize::Small, I18n::Message::Skip, 1.0f, 0.5f),
|
||||
m_okView()
|
||||
{
|
||||
}
|
||||
|
||||
static KDColor sOnBoardingColors[] = {KDColorBlack, KDColorBlack, KDColorBlack, KDColorBlack, Palette::YellowDark};
|
||||
int UpdateController::MessageViewWithSkip::numberOfSubviews() const {
|
||||
return MessageView::numberOfSubviews() + 2;
|
||||
}
|
||||
|
||||
View * UpdateController::MessageViewWithSkip::subviewAtIndex(int index) {
|
||||
uint8_t numberOfMainMessages = MessageView::numberOfSubviews();
|
||||
if (index < numberOfMainMessages) {
|
||||
return MessageView::subviewAtIndex(index);
|
||||
}
|
||||
if (index == numberOfMainMessages) {
|
||||
return &m_skipView;
|
||||
}
|
||||
if (index == numberOfMainMessages + 1) {
|
||||
return &m_okView;
|
||||
}
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UpdateController::MessageViewWithSkip::layoutSubviews() {
|
||||
// Layout the main message
|
||||
MessageView::layoutSubviews();
|
||||
// Layout the "skip (OK)"
|
||||
KDCoordinate height = bounds().height();
|
||||
KDCoordinate width = bounds().width();
|
||||
KDCoordinate textHeight = KDText::charSize(KDText::FontSize::Small).height();
|
||||
KDSize okSize = m_okView.minimalSizeForOptimalDisplay();
|
||||
m_skipView.setFrame(KDRect(0, height-k_bottomMargin-textHeight, width-okSize.width()-k_okMargin-k_skipMargin, textHeight));
|
||||
m_okView.setFrame(KDRect(width - okSize.width()-k_okMargin, height-okSize.height()-k_okMargin, okSize));
|
||||
}
|
||||
|
||||
static I18n::Message sOnBoardingMessages[] = {
|
||||
I18n::Message::UpdateAvailable,
|
||||
I18n::Message::UpdateMessage1,
|
||||
I18n::Message::UpdateMessage2,
|
||||
I18n::Message::BlankMessage,
|
||||
I18n::Message::UpdateMessage3,
|
||||
I18n::Message::UpdateMessage4};
|
||||
|
||||
static KDColor sOnBoardingColors[] = {
|
||||
KDColorBlack,
|
||||
KDColorBlack,
|
||||
KDColorBlack,
|
||||
KDColorWhite,
|
||||
KDColorBlack,
|
||||
Palette::YellowDark};
|
||||
|
||||
UpdateController::UpdateController() :
|
||||
MessageController(sOnBoardingMessages, sOnBoardingColors)
|
||||
ViewController(nullptr),
|
||||
m_messageViewWithSkip(sOnBoardingMessages, sOnBoardingColors, 6)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
#ifndef ON_BOARDING_UPDATE_CONTROLLER_H
|
||||
#define ON_BOARDING_UPDATE_CONTROLLER_H
|
||||
|
||||
#include "../shared/message_controller.h"
|
||||
#include <escher.h>
|
||||
#include "../i18n.h"
|
||||
#include "../shared/message_view.h"
|
||||
#include "../shared/ok_view.h"
|
||||
|
||||
namespace OnBoarding {
|
||||
|
||||
class UpdateController : public MessageController {
|
||||
class UpdateController : public ViewController {
|
||||
public:
|
||||
UpdateController();
|
||||
View * view() override { return &m_messageViewWithSkip; }
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
private:
|
||||
class MessageViewWithSkip : public MessageView {
|
||||
public:
|
||||
MessageViewWithSkip(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages);
|
||||
protected:
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews() override;
|
||||
private:
|
||||
constexpr static KDCoordinate k_bottomMargin = 13;
|
||||
constexpr static KDCoordinate k_okMargin = 10;
|
||||
constexpr static KDCoordinate k_skipMargin = 4;
|
||||
MessageTextView m_skipView;
|
||||
Shared::OkView m_okView;
|
||||
};
|
||||
MessageViewWithSkip m_messageViewWithSkip;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -75,12 +75,13 @@ void CurveViewRange::setTrigonometric() {
|
||||
}
|
||||
|
||||
void CurveViewRange::setDefault() {
|
||||
if (m_delegate) {
|
||||
m_xMax = m_delegate->interestingXRange();
|
||||
m_xMin = -k_displayLeftMarginRatio*m_xMax;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
setYAuto(true);
|
||||
if (m_delegate == nullptr) {
|
||||
return;
|
||||
}
|
||||
m_xMax = m_delegate->interestingXRange();
|
||||
m_xMin = -k_displayLeftMarginRatio*m_xMax;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
setYAuto(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -191,13 +191,15 @@ void SubController::willDisplayCellForIndex(HighlightCell * cell, int index) {
|
||||
}
|
||||
if (m_messageTreeModel->label() == I18n::Message::About) {
|
||||
myCell->setMessageFontSize(KDText::FontSize::Small);
|
||||
const char * accessoryMessage = Ion::softwareVersion();
|
||||
const char * accessoryMessage = nullptr;
|
||||
char serialNumber[Ion::SerialNumberLength+1];
|
||||
switch (index) {
|
||||
case 0:
|
||||
accessoryMessage = Ion::softwareVersion();
|
||||
break;
|
||||
case 1:
|
||||
accessoryMessage = Ion::serialNumber();
|
||||
Ion::getSerialNumber(serialNumber);
|
||||
accessoryMessage = serialNumber;
|
||||
break;
|
||||
case 2:
|
||||
accessoryMessage = Ion::fccId();
|
||||
|
||||
@@ -8,6 +8,7 @@ AsinhCommandWithArg = "asinh(x)"
|
||||
AtanhCommandWithArg = "atanh(x)"
|
||||
B = "b"
|
||||
BinomialCommandWithArg = "binomial(n,k)"
|
||||
BlankMessage = " "
|
||||
CapitalAlpha = "ALPHA"
|
||||
CeilCommandWithArg = "ceil(x)"
|
||||
ConfidenceCommandWithArg = "confidence(f,n)"
|
||||
|
||||
@@ -29,7 +29,7 @@ app_objs += $(addprefix apps/shared/,\
|
||||
list_controller.o\
|
||||
list_parameter_controller.o\
|
||||
memoized_curve_view_range.o\
|
||||
message_controller.o\
|
||||
message_view.o\
|
||||
new_function_cell.o\
|
||||
ok_view.o\
|
||||
parameter_text_field_delegate.o\
|
||||
|
||||
@@ -167,11 +167,12 @@ void InteractiveCurveViewRange::setTrigonometric() {
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setDefault() {
|
||||
if (m_delegate) {
|
||||
m_xMax = m_delegate->interestingXRange();
|
||||
MemoizedCurveViewRange::setXMin(-m_xMax);
|
||||
setYAuto(true);
|
||||
if (m_delegate == nullptr) {
|
||||
return;
|
||||
}
|
||||
m_xMax = m_delegate->interestingXRange();
|
||||
MemoizedCurveViewRange::setXMin(-m_xMax);
|
||||
setYAuto(true);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) {
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
#include "message_controller.h"
|
||||
#include "../apps_container.h"
|
||||
#include <assert.h>
|
||||
|
||||
MessageController::MessageController(I18n::Message * messages, KDColor * colors) :
|
||||
MessageController::MessageController(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages) :
|
||||
ViewController(nullptr),
|
||||
m_contentView(messages, colors)
|
||||
m_messageViewWithSkip(messages, colors, numberOfMessages)
|
||||
{
|
||||
}
|
||||
|
||||
View * MessageController::view() {
|
||||
return &m_contentView;
|
||||
}
|
||||
|
||||
bool MessageController::handleEvent(Ion::Events::Event event) {
|
||||
if (event != Ion::Events::Back && event != Ion::Events::OnOff && event != Ion::Events::Home) {
|
||||
app()->dismissModalViewController();
|
||||
@@ -19,59 +14,3 @@ bool MessageController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MessageController::ContentView::ContentView(I18n::Message * messages, KDColor * colors) :
|
||||
m_titleTextView(KDText::FontSize::Large, messages[0], 0.5f, 0.5f, colors[0]),
|
||||
m_messageTextView1(KDText::FontSize::Small, messages[1], 0.5f, 0.5f, colors[1]),
|
||||
m_messageTextView2(KDText::FontSize::Small, messages[2], 0.5f, 0.5f, colors[2]),
|
||||
m_messageTextView3(KDText::FontSize::Small, messages[3], 0.5f, 0.5f, colors[3]),
|
||||
m_messageTextView4(KDText::FontSize::Small, messages[4], 0.5f, 0.5f, colors[4]),
|
||||
m_skipView(KDText::FontSize::Small, I18n::Message::Skip, 1.0f, 0.5f),
|
||||
m_okView()
|
||||
{
|
||||
}
|
||||
|
||||
void MessageController::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
ctx->fillRect(bounds(), KDColorWhite);
|
||||
}
|
||||
|
||||
int MessageController::ContentView::numberOfSubviews() const {
|
||||
return 7;
|
||||
}
|
||||
|
||||
View * MessageController::ContentView::subviewAtIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return &m_titleTextView;
|
||||
case 1:
|
||||
return &m_messageTextView1;
|
||||
case 2:
|
||||
return &m_messageTextView2;
|
||||
case 3:
|
||||
return &m_messageTextView3;
|
||||
case 4:
|
||||
return &m_messageTextView4;
|
||||
case 5:
|
||||
return &m_skipView;
|
||||
case 6:
|
||||
return &m_okView;
|
||||
default:
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageController::ContentView::layoutSubviews() {
|
||||
KDCoordinate height = bounds().height();
|
||||
KDCoordinate width = bounds().width();
|
||||
KDCoordinate titleHeight = m_titleTextView.minimalSizeForOptimalDisplay().height();
|
||||
KDCoordinate textHeight = KDText::charSize(KDText::FontSize::Small).height();
|
||||
m_titleTextView.setFrame(KDRect(0, k_titleMargin, width, titleHeight));
|
||||
m_messageTextView1.setFrame(KDRect(0, k_paragraphHeight, width, textHeight));
|
||||
m_messageTextView2.setFrame(KDRect(0, k_paragraphHeight+textHeight, width, textHeight));
|
||||
m_messageTextView3.setFrame(KDRect(0, k_paragraphHeight+2*textHeight+k_paragraphMargin, width, textHeight));
|
||||
m_messageTextView4.setFrame(KDRect(0, k_paragraphHeight+3*textHeight+k_paragraphMargin, width, textHeight));
|
||||
KDSize okSize = m_okView.minimalSizeForOptimalDisplay();
|
||||
m_skipView.setFrame(KDRect(0, height-k_bottomMargin-textHeight, width-okSize.width()-k_okMargin-k_skipMargin, textHeight));
|
||||
m_okView.setFrame(KDRect(width - okSize.width()-k_okMargin, height-okSize.height()-k_okMargin, okSize));
|
||||
}
|
||||
|
||||
@@ -2,37 +2,15 @@
|
||||
#define SHARED_MESSAGE_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "ok_view.h"
|
||||
#include "message_view_with_skip.h"
|
||||
|
||||
class MessageController : public ViewController {
|
||||
public:
|
||||
MessageController(I18n::Message * messages, KDColor * colors);
|
||||
View * view() override;
|
||||
MessageController(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages);
|
||||
View * view() override { return &m_messageViewWithSkip; }
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
private:
|
||||
class ContentView : public View {
|
||||
public:
|
||||
ContentView(I18n::Message * messages, KDColor * colors);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
private:
|
||||
constexpr static KDCoordinate k_titleMargin = 40;
|
||||
constexpr static KDCoordinate k_paragraphHeight = 100;
|
||||
constexpr static KDCoordinate k_paragraphMargin = 13;
|
||||
constexpr static KDCoordinate k_bottomMargin = 13;
|
||||
constexpr static KDCoordinate k_okMargin = 10;
|
||||
constexpr static KDCoordinate k_skipMargin = 4;
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews() override;
|
||||
MessageTextView m_titleTextView;
|
||||
MessageTextView m_messageTextView1;
|
||||
MessageTextView m_messageTextView2;
|
||||
MessageTextView m_messageTextView3;
|
||||
MessageTextView m_messageTextView4;
|
||||
MessageTextView m_skipView;
|
||||
Shared::OkView m_okView;
|
||||
};
|
||||
ContentView m_contentView;
|
||||
MessageViewWithSkip m_messageViewWithSkip;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
37
apps/shared/message_view.cpp
Normal file
37
apps/shared/message_view.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "message_view.h"
|
||||
#include <assert.h>
|
||||
|
||||
MessageView::MessageView(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages) {
|
||||
m_numberOfMessages = numberOfMessages < k_maxNumberOfMessages ? numberOfMessages : k_maxNumberOfMessages;
|
||||
for (uint8_t i = 0; i < m_numberOfMessages; i++) {
|
||||
m_messageTextViews[i].setFontSize(i == 0 ? KDText::FontSize::Large : KDText::FontSize::Small);
|
||||
m_messageTextViews[i].setMessage(messages[i]);
|
||||
m_messageTextViews[i].setAlignment(0.5f, 0.5f);
|
||||
m_messageTextViews[i].setTextColor(colors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
ctx->fillRect(bounds(), KDColorWhite);
|
||||
}
|
||||
|
||||
View * MessageView::subviewAtIndex(int index) {
|
||||
if (index >= m_numberOfMessages) {
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
return &(m_messageTextViews[index]);
|
||||
}
|
||||
|
||||
void MessageView::layoutSubviews() {
|
||||
if (m_numberOfMessages == 0) {
|
||||
return;
|
||||
}
|
||||
KDCoordinate width = bounds().width();
|
||||
KDCoordinate titleHeight = m_messageTextViews[0].minimalSizeForOptimalDisplay().height();
|
||||
KDCoordinate textHeight = KDText::charSize(KDText::FontSize::Small).height();
|
||||
m_messageTextViews[0].setFrame(KDRect(0, k_titleMargin, width, titleHeight));
|
||||
for (uint8_t i = 1; i < m_numberOfMessages; i++) {
|
||||
m_messageTextViews[i].setFrame(KDRect(0, k_paragraphHeight + (i-1) * textHeight, width, textHeight));
|
||||
}
|
||||
}
|
||||
23
apps/shared/message_view.h
Normal file
23
apps/shared/message_view.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef SHARED_MESSAGE_VIEW_H
|
||||
#define SHARED_MESSAGE_VIEW_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
class MessageView : public View {
|
||||
public:
|
||||
MessageView(I18n::Message * messages, KDColor * colors, uint8_t numberOfMessages);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
protected:
|
||||
int numberOfSubviews() const override { return m_numberOfMessages; }
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews() override;
|
||||
private:
|
||||
constexpr static KDCoordinate k_titleMargin = 40;
|
||||
constexpr static KDCoordinate k_paragraphHeight = 100;
|
||||
constexpr static uint8_t k_maxNumberOfMessages = 8;
|
||||
MessageTextView m_messageTextViews[k_maxNumberOfMessages];
|
||||
uint8_t m_numberOfMessages;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
13
apps/usb/Makefile
Normal file
13
apps/usb/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
app_objs += $(addprefix apps/usb/,\
|
||||
app.o\
|
||||
usb_connected_controller.o\
|
||||
)
|
||||
|
||||
i18n_files += $(addprefix apps/usb/,\
|
||||
base.de.i18n\
|
||||
base.en.i18n\
|
||||
base.es.i18n\
|
||||
base.fr.i18n\
|
||||
base.pt.i18n\
|
||||
base.universal.i18n\
|
||||
)
|
||||
33
apps/usb/app.cpp
Normal file
33
apps/usb/app.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "app.h"
|
||||
#include "../apps_container.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace USB {
|
||||
|
||||
I18n::Message App::Descriptor::name() {
|
||||
return upperName();
|
||||
}
|
||||
|
||||
I18n::Message App::Descriptor::upperName() {
|
||||
return I18n::Message::USBAppCapital;
|
||||
}
|
||||
|
||||
App * App::Snapshot::unpack(Container * container) {
|
||||
return new App(container, this);
|
||||
}
|
||||
|
||||
App::Descriptor * App::Snapshot::descriptor() {
|
||||
static Descriptor descriptor;
|
||||
return &descriptor;
|
||||
}
|
||||
|
||||
App::App(Container * container, Snapshot * snapshot) :
|
||||
::App(container, snapshot, &m_usbConnectedController)
|
||||
{
|
||||
}
|
||||
|
||||
bool App::processEvent(Ion::Events::Event e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
29
apps/usb/app.h
Normal file
29
apps/usb/app.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef USB_APP_H
|
||||
#define USB_APP_H
|
||||
|
||||
#include "escher/include/escher/app.h"
|
||||
#include "usb_connected_controller.h"
|
||||
|
||||
namespace USB {
|
||||
|
||||
class App : public ::App {
|
||||
public:
|
||||
class Descriptor : public ::App::Descriptor {
|
||||
public:
|
||||
I18n::Message name() override;
|
||||
I18n::Message upperName() override;
|
||||
};
|
||||
class Snapshot : public ::App::Snapshot {
|
||||
public:
|
||||
App * unpack(Container * container) override;
|
||||
Descriptor * descriptor() override;
|
||||
};
|
||||
bool processEvent(Ion::Events::Event) override;
|
||||
private:
|
||||
App(Container * container, Snapshot * snapshot);
|
||||
USBConnectedController m_usbConnectedController;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
7
apps/usb/base.de.i18n
Normal file
7
apps/usb/base.de.i18n
Normal file
@@ -0,0 +1,7 @@
|
||||
USBConnected = "DER RECHNER ANGESCHLOSSEN IST"
|
||||
ConnectedMessage1 = "Um Daten zu übertragen, verbinden"
|
||||
ConnectedMessage2 = "Sie sich von Ihrem Computer an"
|
||||
ConnectedMessage3 = "workshop.numworks.com"
|
||||
ConnectedMessage4 = "Drücken Sie die RETURN-Taste am"
|
||||
ConnectedMessage5 = "Taschenrechner oder ausstecken Sie ihn,"
|
||||
ConnectedMessage6 = "um die Verbindung zu trennen."
|
||||
7
apps/usb/base.en.i18n
Normal file
7
apps/usb/base.en.i18n
Normal file
@@ -0,0 +1,7 @@
|
||||
USBConnected = "THE CALCULATOR IS CONNECTED"
|
||||
ConnectedMessage1 = "To transfer data, browse"
|
||||
ConnectedMessage2 = "our page from your computer"
|
||||
ConnectedMessage3 = "workshop.numworks.com"
|
||||
ConnectedMessage4 = "Press the BACK key of your"
|
||||
ConnectedMessage5 = "calculator or unplug it to"
|
||||
ConnectedMessage6 = "disconnect it."
|
||||
7
apps/usb/base.es.i18n
Normal file
7
apps/usb/base.es.i18n
Normal file
@@ -0,0 +1,7 @@
|
||||
USBConnected = "CALCULADORA CONECTADA"
|
||||
ConnectedMessage1 = "Para transferir datos, visite"
|
||||
ConnectedMessage2 = "nuestra pagina desde su ordenador"
|
||||
ConnectedMessage3 = "workshop.numworks.com"
|
||||
ConnectedMessage4 = "Pulse el boton RETURN de la"
|
||||
ConnectedMessage5 = "calculadora o desenchufela para"
|
||||
ConnectedMessage6 = "desconectarla."
|
||||
7
apps/usb/base.fr.i18n
Normal file
7
apps/usb/base.fr.i18n
Normal file
@@ -0,0 +1,7 @@
|
||||
USBConnected = "LA CALCULATRICE EST CONNECTEE"
|
||||
ConnectedMessage1 = "Pour transferer des donnees, connectez-"
|
||||
ConnectedMessage2 = "vous depuis votre ordinateur sur le site"
|
||||
ConnectedMessage3 = "workshop.numworks.com"
|
||||
ConnectedMessage4 = "Appuyez sur la touche RETOUR de la"
|
||||
ConnectedMessage5 = "calculatrice ou debranchez-la pour la"
|
||||
ConnectedMessage6 = "deconnecter."
|
||||
7
apps/usb/base.pt.i18n
Normal file
7
apps/usb/base.pt.i18n
Normal file
@@ -0,0 +1,7 @@
|
||||
USBConnected = "A CALCULADORA ESTA CONECTADA"
|
||||
ConnectedMessage1 = "Para transferir dados, navegue"
|
||||
ConnectedMessage2 = "na nossa pagina no seu computador"
|
||||
ConnectedMessage3 = "workshop.numworks.com"
|
||||
ConnectedMessage4 = "Pressione o botao RETURN na"
|
||||
ConnectedMessage5 = "calculadora ou desligue-la para"
|
||||
ConnectedMessage6 = "desconectar-la."
|
||||
1
apps/usb/base.universal.i18n
Normal file
1
apps/usb/base.universal.i18n
Normal file
@@ -0,0 +1 @@
|
||||
USBAppCapital = "USB"
|
||||
32
apps/usb/usb_connected_controller.cpp
Normal file
32
apps/usb/usb_connected_controller.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "usb_connected_controller.h"
|
||||
#include "../i18n.h"
|
||||
|
||||
namespace USB {
|
||||
|
||||
static I18n::Message sUSBConnectedMessages[] = {
|
||||
I18n::Message::USBConnected,
|
||||
I18n::Message::ConnectedMessage1,
|
||||
I18n::Message::ConnectedMessage2,
|
||||
I18n::Message::ConnectedMessage3,
|
||||
I18n::Message::BlankMessage,
|
||||
I18n::Message::ConnectedMessage4,
|
||||
I18n::Message::ConnectedMessage5,
|
||||
I18n::Message::ConnectedMessage6};
|
||||
|
||||
static KDColor sUSBConnectedColors[] = {
|
||||
KDColorBlack,
|
||||
KDColorBlack,
|
||||
KDColorBlack,
|
||||
Palette::YellowDark,
|
||||
KDColorWhite,
|
||||
KDColorBlack,
|
||||
KDColorBlack,
|
||||
KDColorBlack};
|
||||
|
||||
USBConnectedController::USBConnectedController() :
|
||||
ViewController(nullptr),
|
||||
m_messageView(sUSBConnectedMessages, sUSBConnectedColors, 8)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
21
apps/usb/usb_connected_controller.h
Normal file
21
apps/usb/usb_connected_controller.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef USB_USB_CONNECTED_CONTROLLER_H
|
||||
#define USB_USB_CONNECTED_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "../shared/message_view.h"
|
||||
|
||||
namespace USB {
|
||||
|
||||
class USBConnectedController : public ViewController {
|
||||
public:
|
||||
USBConnectedController();
|
||||
View * view() override { return &m_messageView; }
|
||||
bool handleEvent(Ion::Events::Event event) override { return false; }
|
||||
private:
|
||||
MessageView m_messageView;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "usb_timer.h"
|
||||
#include "global_preferences.h"
|
||||
#include "apps_container.h"
|
||||
|
||||
USBTimer::USBTimer(AppsContainer * container) :
|
||||
Timer(1),
|
||||
m_container(container),
|
||||
m_previousPluggedState(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool USBTimer::fire() {
|
||||
bool needRedrawing = false;
|
||||
if (Ion::USB::isPlugged()) {
|
||||
if (!m_previousPluggedState && GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) {
|
||||
m_container->displayExamModePopUp(false);
|
||||
needRedrawing = true;
|
||||
}
|
||||
#if LED_WHILE_CHARGING
|
||||
KDColor LEDColor = Ion::Battery::isCharging() ? KDColorYellow : KDColorGreen;
|
||||
Ion::LED::setColor(LEDColor);
|
||||
#endif
|
||||
if (!m_previousPluggedState) {
|
||||
Ion::Backlight::setBrightness(Ion::Backlight::MaxBrightness);
|
||||
}
|
||||
m_previousPluggedState = true;
|
||||
} else {
|
||||
if (m_previousPluggedState) {
|
||||
#if LED_WHILE_CHARGING
|
||||
Ion::LED::setColor(KDColorBlack);
|
||||
#endif
|
||||
m_previousPluggedState = false;
|
||||
}
|
||||
}
|
||||
return needRedrawing;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef APPS_USB_TIMER_H
|
||||
#define APPS_USB_TIMER_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
class AppsContainer;
|
||||
|
||||
class USBTimer : public Timer {
|
||||
public:
|
||||
USBTimer(AppsContainer * container);
|
||||
private:
|
||||
bool fire() override;
|
||||
AppsContainer * m_container;
|
||||
bool m_previousPluggedState;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,4 +2,7 @@ TOOLCHAIN ?= arm-gcc
|
||||
USE_LIBA = 1
|
||||
EXE = elf
|
||||
|
||||
EPSILON_DEVICE_BENCH ?= 1
|
||||
SFLAGS += -DEPSILON_DEVICE_BENCH=$(EPSILON_DEVICE_BENCH)
|
||||
|
||||
python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1
|
||||
|
||||
@@ -4,11 +4,11 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products)))
|
||||
|
||||
%.hex: %.$(EXE)
|
||||
@echo "OBJCOPY $@"
|
||||
@$(OBJCOPY) -O ihex $< $@
|
||||
$(Q) $(OBJCOPY) -O ihex $< $@
|
||||
|
||||
%.bin: %.$(EXE)
|
||||
@echo "OBJCOPY $@"
|
||||
@$(OBJCOPY) -O binary $< $@
|
||||
$(Q) $(OBJCOPY) -O binary $< $@
|
||||
|
||||
.PHONY: %_size
|
||||
%_size: %.$(EXE)
|
||||
@@ -36,10 +36,20 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products)))
|
||||
@echo "DFU $@"
|
||||
@echo "INFO About to flash your device. Please plug your device to your computer"
|
||||
@echo " using an USB cable and press the RESET button the back of your device."
|
||||
@until dfu-util -l | grep "Internal Flash" > /dev/null 2>&1; do sleep 1;done
|
||||
@until dfu-util -l | grep "Flash" > /dev/null 2>&1; do sleep 1;done
|
||||
@echo "DFU $@"
|
||||
@dfu-util -i 0 -a 0 -s 0x08000000:leave -D $<
|
||||
$(Q) dfu-util -i 0 -a 0 -s 0x08000000:leave -D $<
|
||||
|
||||
.PHONY: openocd
|
||||
openocd:
|
||||
openocd -f build/device/openocd.cfg
|
||||
|
||||
ifeq ($(EPSILON_USB_DFU_XIP)$(EPSILON_DEVICE_BENCH),10)
|
||||
flasher.$(EXE): LDFLAGS = --gc-sections -T ion/src/device/usb/flasher.ld
|
||||
flasher.$(EXE): $(objs) $(usb_objs) ion/src/device/usb/flasher.o
|
||||
else
|
||||
flasher.$(EXE):
|
||||
@echo "Error: flasher.elf requires EPSILON_DEVICE_BENCH=0 EPSILON_USB_DFU_XIP=1"
|
||||
endif
|
||||
|
||||
products += flasher.$(EXE) flasher.bin
|
||||
|
||||
@@ -5,10 +5,10 @@ GDB = arm-none-eabi-gdb
|
||||
OBJCOPY = arm-none-eabi-objcopy
|
||||
SIZE = arm-none-eabi-size
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
#ifeq ($(DEBUG),1)
|
||||
SFLAGS += -ggdb3
|
||||
else
|
||||
#else
|
||||
SFLAGS += -fdata-sections -ffunction-sections
|
||||
LDFLAGS += --gc-sections
|
||||
endif
|
||||
#endif
|
||||
SFLAGS += -mthumb -march=armv7e-m -mfloat-abi=hard -mcpu=cortex-m4 -mfpu=fpv4-sp-d16
|
||||
|
||||
@@ -30,10 +30,11 @@ bool RunLoop::step() {
|
||||
// Fetch the event, if any
|
||||
int eventDuration = Timer::TickDuration;
|
||||
int timeout = eventDuration;
|
||||
|
||||
Ion::Events::Event event = Ion::Events::getEvent(&timeout);
|
||||
assert(event.isDefined());
|
||||
eventDuration -= timeout;
|
||||
|
||||
eventDuration -= timeout;
|
||||
assert(eventDuration >= 0);
|
||||
assert(eventDuration <= Timer::TickDuration);
|
||||
|
||||
@@ -65,7 +66,7 @@ bool RunLoop::step() {
|
||||
Ion::Console::writeLine(name);
|
||||
#endif
|
||||
|
||||
if (event.isKeyboardEvent()) {
|
||||
if (event != Ion::Events::None) {
|
||||
dispatchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,12 @@ namespace Ion {
|
||||
void msleep(long ms);
|
||||
void usleep(long us);
|
||||
|
||||
const char * serialNumber();
|
||||
constexpr static int SerialNumberLength = 24;
|
||||
void getSerialNumber(char * buffer);
|
||||
const char * softwareVersion();
|
||||
const char * patchLevel();
|
||||
const char * fccId();
|
||||
|
||||
/* CAUTION: This is a complete reset! */
|
||||
void reset(bool jump = false);
|
||||
|
||||
// CRC32 : non xor-ed, non reversed, direct, polynomial 4C11DB7
|
||||
// Only accepts whole 32bit values
|
||||
uint32_t crc32(const uint32_t * data, size_t length);
|
||||
|
||||
@@ -215,6 +215,8 @@ constexpr Event UpperZ = Event::ShiftAlphaKey(Keyboard::Key::H4);
|
||||
constexpr Event None = Event::Special(0);
|
||||
constexpr Event Termination = Event::Special(1);
|
||||
constexpr Event TimerFire = Event::Special(2);
|
||||
constexpr Event USBEnumeration = Event::Special(3);
|
||||
constexpr Event USBPlug = Event::Special(4);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,13 @@ namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
bool isPlugged();
|
||||
bool isEnumerated(); // Speed-enumerated, to be accurate
|
||||
|
||||
void removeSoftDisconnect();
|
||||
|
||||
void DFU();
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
include ion/src/device/boot/Makefile
|
||||
include ion/src/device/bench/Makefile
|
||||
include ion/src/device/usb/Makefile
|
||||
|
||||
ion/src/shared/platform_info.o: SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" -DFORCE_LINK="__attribute__((used))"
|
||||
|
||||
|
||||
@@ -15,5 +15,6 @@ objs += $(addprefix ion/src/device/bench/command/, \
|
||||
led.o \
|
||||
mcu_serial.o \
|
||||
ping.o \
|
||||
print.o \
|
||||
suspend.o \
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ constexpr CommandHandler handles[] = {
|
||||
CommandHandler("LED", Command::LED),
|
||||
CommandHandler("MCU_SERIAL", Command::MCUSerial),
|
||||
CommandHandler("PING", Command::Ping),
|
||||
CommandHandler("PRINT", Command::Print),
|
||||
CommandHandler("SUSPEND", Command::Suspend),
|
||||
CommandHandler(nullptr, nullptr)
|
||||
};
|
||||
@@ -25,6 +26,7 @@ constexpr const CommandList sCommandList = CommandList(handles);
|
||||
constexpr int kMaxCommandLength = 255;
|
||||
|
||||
void run() {
|
||||
Ion::Display::pushRectUniform(KDRect(0,0,Ion::Display::Width,Ion::Display::Height), KDColorWhite);
|
||||
char command[kMaxCommandLength];
|
||||
while (true) {
|
||||
Ion::Console::readLine(command, kMaxCommandLength);
|
||||
|
||||
@@ -32,8 +32,9 @@ bool isHex(char c) {
|
||||
|
||||
uint32_t hexNumber(const char * s) {
|
||||
uint32_t result = 0;
|
||||
while (*s != NULL) {
|
||||
result = (result << 4) | hexChar(*s++);
|
||||
int8_t digit = 0;
|
||||
while ((digit = hexChar(*s++)) >= 0) {
|
||||
result = (result << 4) | digit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ void Keyboard(const char * input);
|
||||
void LED(const char * input);
|
||||
void MCUSerial(const char * input);
|
||||
void Ping(const char * input);
|
||||
void Print(const char * input);
|
||||
void Suspend(const char * input);
|
||||
|
||||
extern const char * const sOK;
|
||||
|
||||
@@ -11,8 +11,8 @@ void MCUSerial(const char * input) {
|
||||
reply(sSyntaxError);
|
||||
return;
|
||||
}
|
||||
char response[11+24+1] = {'M', 'C', 'U', '_', 'S', 'E', 'R', 'I', 'A', 'L', '=', 0};
|
||||
strlcpy(response+11, Ion::serialNumber(), 25);
|
||||
char response[11+Ion::SerialNumberLength + 1] = {'M', 'C', 'U', '_', 'S', 'E', 'R', 'I', 'A', 'L', '=', 0};
|
||||
Ion::getSerialNumber(response+11);
|
||||
reply(response);
|
||||
}
|
||||
|
||||
|
||||
30
ion/src/device/bench/command/print.cpp
Normal file
30
ion/src/device/bench/command/print.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "command.h"
|
||||
#include <ion.h>
|
||||
#include <ion/src/device/led.h>
|
||||
#include <kandinsky.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Bench {
|
||||
namespace Command {
|
||||
|
||||
// Input must be of the form "XX,YY,STRING"
|
||||
void Print(const char * input) {
|
||||
if (input == nullptr || !isHex(input[0]) || !isHex(input[1]) || input[2] != ',' || !isHex(input[3]) || !isHex(input[4]) || input[5] != ',') {
|
||||
reply(sKO);
|
||||
return;
|
||||
}
|
||||
|
||||
char x = hexNumber(input);
|
||||
char y = hexNumber(input+3);
|
||||
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->drawString(input+6, KDPoint(x, y));
|
||||
|
||||
reply(sOK);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ void abort() {
|
||||
while (1) {
|
||||
}
|
||||
#else
|
||||
Ion::reset();
|
||||
Ion::Device::coreReset();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -66,27 +66,7 @@ uint32_t Ion::random() {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void coreReset() {
|
||||
// Perform a full core reset
|
||||
CM4.AIRCR()->requestReset();
|
||||
}
|
||||
|
||||
static void jumpReset() {
|
||||
Ion::Device::shutdown();
|
||||
uint32_t * stackPointerAddress = reinterpret_cast<uint32_t *>(0x08000000);
|
||||
uint32_t * resetHandlerAddress = reinterpret_cast<uint32_t *>(0x08000004);
|
||||
set_msp(*stackPointerAddress);
|
||||
void (*ResetHandler)(void) = (void (*)())(*resetHandlerAddress);
|
||||
ResetHandler();
|
||||
}
|
||||
|
||||
void Ion::reset(bool jump) {
|
||||
if (jump) {
|
||||
jumpReset();
|
||||
} else {
|
||||
coreReset();
|
||||
}
|
||||
}
|
||||
|
||||
static inline char hex(uint8_t d) {
|
||||
if (d > 9) {
|
||||
@@ -95,18 +75,14 @@ static inline char hex(uint8_t d) {
|
||||
return '0'+d;
|
||||
}
|
||||
|
||||
const char * Ion::serialNumber() {
|
||||
static char serialNumber[25] = {0};
|
||||
if (serialNumber[0] == 0) {
|
||||
uint8_t * rawUniqueID = (uint8_t *)0x1FFF7A10;
|
||||
for (int i=0; i<12; i++) {
|
||||
uint8_t d = *rawUniqueID++;
|
||||
serialNumber[2*i] = hex(d >> 4);
|
||||
serialNumber[2*i+1] = hex(d & 0xF);
|
||||
}
|
||||
serialNumber[24] = 0;
|
||||
void Ion::getSerialNumber(char * buffer) {
|
||||
uint8_t * rawUniqueID = (uint8_t *)0x1FFF7A10;
|
||||
for (int i=0; i<SerialNumberLength/2; i++) {
|
||||
uint8_t d = *rawUniqueID++;
|
||||
buffer[2*i] = hex(d >> 4);
|
||||
buffer[2*i+1] = hex(d & 0xF);
|
||||
}
|
||||
return serialNumber;
|
||||
buffer[SerialNumberLength] = 0;
|
||||
}
|
||||
|
||||
// Private Ion::Device methods
|
||||
@@ -121,6 +97,20 @@ void initFPU() {
|
||||
// FIXME: The pipeline should be flushed at this point
|
||||
}
|
||||
|
||||
void coreReset() {
|
||||
// Perform a full core reset
|
||||
CM4.AIRCR()->requestReset();
|
||||
}
|
||||
|
||||
void jumpReset() {
|
||||
shutdown();
|
||||
uint32_t * stackPointerAddress = reinterpret_cast<uint32_t *>(0x08000000);
|
||||
uint32_t * resetHandlerAddress = reinterpret_cast<uint32_t *>(0x08000004);
|
||||
set_msp(*stackPointerAddress);
|
||||
void (*ResetHandler)(void) = (void (*)())(*resetHandlerAddress);
|
||||
ResetHandler();
|
||||
}
|
||||
|
||||
void init() {
|
||||
initClocks();
|
||||
|
||||
@@ -140,13 +130,17 @@ void init() {
|
||||
GPIO(g).PUPDR()->set(0x00000000); // All to "None"
|
||||
}
|
||||
|
||||
#if EPSILON_DEVICE_BENCH
|
||||
bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected();
|
||||
#endif
|
||||
|
||||
initPeripherals();
|
||||
|
||||
#if EPSILON_DEVICE_BENCH
|
||||
if (consolePeerConnectedOnBoot) {
|
||||
Ion::Device::Bench::run();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
@@ -201,12 +195,26 @@ void initClocks() {
|
||||
FLASH.ACR()->setDCEN(true);
|
||||
FLASH.ACR()->setICEN(true);
|
||||
|
||||
/* We're using the high-speed internal oscillator as a clock source. It runs
|
||||
* at a fixed 16 MHz frequency, but by piping it through the PLL we can derive
|
||||
* faster oscillations. Combining default values and a PLLQ of 4 can provide
|
||||
* us with a 96 MHz frequency for SYSCLK. */
|
||||
/* After reset, the device is using the high-speed internal oscillator (HSI)
|
||||
* as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not
|
||||
* accurate enough for reliable USB operation, so we need to use the external
|
||||
* high-speed oscillator (HSE). */
|
||||
|
||||
// Enable the HSE and wait for it to be ready
|
||||
RCC.CR()->setHSEON(true);
|
||||
while(!RCC.CR()->getHSERDY()) {
|
||||
}
|
||||
|
||||
/* Given the crystal used on our device, the HSE will oscillate at 25 MHz. By
|
||||
* piping it through a phase-locked loop (PLL) we can derive other frequencies
|
||||
* for use in different parts of the system. Combining the default PLL values
|
||||
* with a PLLM of 25 and a PLLQ of 4 yields both a 96 MHz frequency for SYSCLK
|
||||
* and the required 48 MHz USB clock. */
|
||||
|
||||
// Configure the PLL ratios and use HSE as a PLL input
|
||||
RCC.PLLCFGR()->setPLLM(25);
|
||||
RCC.PLLCFGR()->setPLLQ(4);
|
||||
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSI);
|
||||
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE);
|
||||
// 96 MHz is too fast for APB1. Divide it by two to reach 48 MHz
|
||||
RCC.CFGR()->setPPRE1(RCC::CFGR::AHBRatio::DivideBy2);
|
||||
|
||||
@@ -215,11 +223,14 @@ void initClocks() {
|
||||
while(!RCC.CR()->getPLLRDY()) {
|
||||
}
|
||||
|
||||
// Last but not least, use the PLL output as a SYSCLK source
|
||||
// Use the PLL output as a SYSCLK source
|
||||
RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
|
||||
while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
|
||||
}
|
||||
|
||||
// Now that we don't need use it anymore, turn the HSI off
|
||||
RCC.CR()->setHSION(false);
|
||||
|
||||
// Peripheral clocks
|
||||
|
||||
// AHB1 bus
|
||||
@@ -234,6 +245,12 @@ void initClocks() {
|
||||
ahb1enr.setDMA2EN(true);
|
||||
RCC.AHB1ENR()->set(ahb1enr);
|
||||
|
||||
// AHB2 bus
|
||||
RCC.AHB2ENR()->setOTGFSEN(true);
|
||||
|
||||
// AHB3 bus
|
||||
RCC.AHB3ENR()->setFSMCEN(true);
|
||||
|
||||
// APB1 bus
|
||||
// We're using TIM3
|
||||
RCC.APB1ENR()->setTIM3EN(true);
|
||||
@@ -247,8 +264,6 @@ void initClocks() {
|
||||
apb2enr.setSDIOEN(true);
|
||||
#endif
|
||||
RCC.APB2ENR()->set(apb2enr);
|
||||
|
||||
RCC.AHB3ENR()->setFSMCEN(true);
|
||||
}
|
||||
|
||||
void shutdownClocks() {
|
||||
|
||||
@@ -8,6 +8,9 @@ void init();
|
||||
void shutdown();
|
||||
|
||||
void initFPU();
|
||||
void coreReset();
|
||||
void jumpReset();
|
||||
|
||||
void initPeripherals();
|
||||
void shutdownPeripherals();
|
||||
void initClocks();
|
||||
|
||||
@@ -18,6 +18,8 @@ static bool sleepWithTimeout(int duration, int * timeout) {
|
||||
|
||||
Event sLastEvent = Events::None;
|
||||
Keyboard::State sLastKeyboardState;
|
||||
bool sLastUSBPlugged = false;
|
||||
bool sLastUSBEnumerated = false;
|
||||
bool sEventIsRepeating = 0;
|
||||
constexpr int delayBeforeRepeat = 200;
|
||||
constexpr int delayBetweenRepeat = 50;
|
||||
@@ -33,6 +35,21 @@ Event getEvent(int * timeout) {
|
||||
uint64_t keysSeenUp = 0;
|
||||
uint64_t keysSeenTransitionningFromUpToDown = 0;
|
||||
while (true) {
|
||||
// First, check if the USB plugged status has changed
|
||||
bool usbPlugged = USB::isPlugged();
|
||||
if (usbPlugged != sLastUSBPlugged) {
|
||||
sLastUSBPlugged = usbPlugged;
|
||||
return Events::USBPlug;
|
||||
}
|
||||
|
||||
// Second, check if the USB device has been connected to an USB host
|
||||
bool usbEnumerated = USB::isEnumerated();
|
||||
bool previousUsbEnumerated = sLastUSBEnumerated;
|
||||
sLastUSBEnumerated = usbEnumerated;
|
||||
if (usbEnumerated && !previousUsbEnumerated) {
|
||||
return Events::USBEnumeration;
|
||||
}
|
||||
|
||||
Keyboard::State state = Keyboard::scan();
|
||||
keysSeenUp |= ~state;
|
||||
keysSeenTransitionningFromUpToDown = keysSeenUp & state;
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
* means the corresponding key is pressed.
|
||||
*/
|
||||
|
||||
#include <ion.h>
|
||||
#include "keyboard.h"
|
||||
|
||||
// Public Ion::Keyboard methods
|
||||
@@ -52,24 +51,15 @@ State scan() {
|
||||
uint64_t state = 0;
|
||||
|
||||
for (uint8_t i=0; i<Device::numberOfRows; i++) {
|
||||
/* In open-drain mode, a 0 in the register drives the pin low, and a 1 lets
|
||||
* the pin floating (Hi-Z). So we want to set the current row to zero and
|
||||
* all the others to 1. */
|
||||
uint16_t rowState = ~(1<<(Device::numberOfRows-1-i));
|
||||
|
||||
// TODO: Assert pin numbers are sequentials and dynamically find 9 and 0
|
||||
Device::RowGPIO.ODR()->setBitRange(9, 0, rowState);
|
||||
|
||||
// TODO: 100 us seems to work, but wasn't really calculated
|
||||
usleep(100);
|
||||
Device::activateRow(Device::numberOfRows-1-i);
|
||||
|
||||
// TODO: Assert pin numbers are sequentials and dynamically find 8 and 0
|
||||
uint8_t column = Device::ColumnGPIO.IDR()->getBitRange(5,0);
|
||||
uint8_t columns = Device::ColumnGPIO.IDR()->getBitRange(5,0);
|
||||
|
||||
/* The key is down if the input is brought low by the output. In other
|
||||
* words, we want to return true if the input is low (false). So we need to
|
||||
* append 6 bits of (not column) to state. */
|
||||
state = (state << 6) | (~column & 0x3F);
|
||||
* append 6 bits of (not columns) to state. */
|
||||
state = (state << 6) | (~columns & 0x3F);
|
||||
}
|
||||
|
||||
/* Last but not least, keys number 8, 9, 10, 11, 35, 41, 47 and 53 are not
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ION_DEVICE_KEYBOARD_H
|
||||
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion.h>
|
||||
#include "regs/regs.h"
|
||||
|
||||
namespace Ion {
|
||||
@@ -45,6 +46,23 @@ inline uint8_t columnForKey(Key key) {
|
||||
return (int)key%numberOfColumns;
|
||||
}
|
||||
|
||||
inline void activateRow(uint8_t row) {
|
||||
/* In open-drain mode, a 0 in the register drives the pin low, and a 1 lets
|
||||
* the pin floating (Hi-Z). So we want to set the current row to zero and all
|
||||
* the others to 1. */
|
||||
uint16_t rowState = ~(1<<row);
|
||||
|
||||
// TODO: Assert pin numbers are sequentials and dynamically find 9 and 0
|
||||
Device::RowGPIO.ODR()->setBitRange(9, 0, rowState);
|
||||
|
||||
// TODO: 100 us seems to work, but wasn't really calculated
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
inline bool columnIsActive(uint8_t column) {
|
||||
return !(Device::ColumnGPIO.IDR()->getBitRange(column,column));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,36 @@ public:
|
||||
REGS_BOOL_FIELD(DCEN, 10);
|
||||
};
|
||||
|
||||
class KEYR : public Register32 {
|
||||
};
|
||||
|
||||
class CR : public Register32 {
|
||||
public:
|
||||
enum class PSIZE : uint8_t {
|
||||
X8 = 0,
|
||||
X16 = 1,
|
||||
X32 = 2,
|
||||
X64 = 3
|
||||
};
|
||||
REGS_BOOL_FIELD(PG, 0);
|
||||
REGS_BOOL_FIELD(SER, 1);
|
||||
REGS_BOOL_FIELD(MER, 2);
|
||||
REGS_FIELD(SNB, uint8_t, 6, 3);
|
||||
REGS_TYPE_FIELD(PSIZE, 9, 8);
|
||||
REGS_BOOL_FIELD(STRT, 16);
|
||||
REGS_BOOL_FIELD(LOCK, 31);
|
||||
};
|
||||
|
||||
class SR : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(BSY, 16);
|
||||
};
|
||||
|
||||
constexpr FLASH() {};
|
||||
REGS_REGISTER_AT(ACR, 0x00);
|
||||
REGS_REGISTER_AT(KEYR, 0x04);
|
||||
REGS_REGISTER_AT(SR, 0x0C);
|
||||
REGS_REGISTER_AT(CR, 0x10);
|
||||
private:
|
||||
constexpr uint32_t Base() const {
|
||||
return 0x40023C00;
|
||||
|
||||
195
ion/src/device/regs/otg.h
Normal file
195
ion/src/device/regs/otg.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef REGS_OTG_H
|
||||
#define REGS_OTG_H
|
||||
|
||||
#include "register.h"
|
||||
|
||||
class OTG {
|
||||
public:
|
||||
class GAHBCFG : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(GINTMSK, 0);
|
||||
};
|
||||
|
||||
class GUSBCFG : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(PHYSEL, 6);
|
||||
REGS_FIELD(TRDT, uint8_t, 13, 10);
|
||||
REGS_BOOL_FIELD(FDMOD, 30);
|
||||
};
|
||||
|
||||
class GRSTCTL : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(CSRST, 0);
|
||||
REGS_BOOL_FIELD(RXFFLSH, 4);
|
||||
REGS_BOOL_FIELD(TXFFLSH, 5);
|
||||
REGS_FIELD(TXFNUM, uint8_t, 10, 6);
|
||||
REGS_BOOL_FIELD(AHBIDL, 31);
|
||||
};
|
||||
|
||||
class GINTSTS : public Register32 {
|
||||
public:
|
||||
using Register32::Register32;
|
||||
REGS_BOOL_FIELD(MMIS, 1);
|
||||
REGS_BOOL_FIELD(SOF, 3);
|
||||
REGS_BOOL_FIELD(RXFLVL, 4);
|
||||
REGS_BOOL_FIELD(USBSUSP, 11);
|
||||
REGS_BOOL_FIELD(USBRST, 12);
|
||||
REGS_BOOL_FIELD(ENUMDNE, 13);
|
||||
REGS_BOOL_FIELD(IEPINT, 18);
|
||||
REGS_BOOL_FIELD(WKUPINT, 31);
|
||||
};
|
||||
|
||||
class GINTMSK : public Register32 {
|
||||
public:
|
||||
using Register32::Register32;
|
||||
REGS_BOOL_FIELD(RXFLVLM, 4);
|
||||
REGS_BOOL_FIELD(USBSUSPM, 11);
|
||||
REGS_BOOL_FIELD(USBRST, 12);
|
||||
REGS_BOOL_FIELD(ENUMDNEM, 13);
|
||||
REGS_BOOL_FIELD(IEPINT, 18);
|
||||
REGS_BOOL_FIELD(WUIM, 31);
|
||||
};
|
||||
|
||||
class GRXSTSP : public Register32 {
|
||||
public:
|
||||
using Register32::Register32;
|
||||
enum class PKTSTS {
|
||||
GlobalOutNAK = 1,
|
||||
OutReceived = 2,
|
||||
OutTransferCompleted = 3, // After each Out Transaction
|
||||
SetupTransactionCompleted = 4, // Supposed to be after each SETUP transaction
|
||||
SetupReceived = 6
|
||||
};
|
||||
REGS_FIELD(EPNUM, uint8_t, 3, 0);
|
||||
REGS_FIELD(BCNT, uint16_t, 14, 4);
|
||||
PKTSTS getPKTSTS() volatile { return (PKTSTS)getBitRange(20, 17); }
|
||||
};
|
||||
|
||||
class GRXFSIZ : public Register32 {
|
||||
public:
|
||||
REGS_FIELD(RXFD, uint16_t, 15, 0);
|
||||
};
|
||||
|
||||
class DIEPTXF0 : public Register32 {
|
||||
public:
|
||||
REGS_FIELD(TX0FSA, uint16_t, 15, 0);
|
||||
REGS_FIELD(TX0FD, uint16_t, 31, 16);
|
||||
};
|
||||
|
||||
class GCCFG : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(PWRDWN, 16);
|
||||
REGS_BOOL_FIELD(VBDEN, 21);
|
||||
};
|
||||
|
||||
class DCFG : public Register32 {
|
||||
public:
|
||||
enum class DSPD {
|
||||
FullSpeed = 3,
|
||||
};
|
||||
void setDSPD(DSPD s) volatile { setBitRange(1, 0, (uint8_t)s); }
|
||||
REGS_FIELD(DAD, uint8_t, 10, 4);
|
||||
};
|
||||
|
||||
class DCTL : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(SDIS, 1);
|
||||
};
|
||||
|
||||
class DIEPMSK : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(XFRCM, 0);
|
||||
};
|
||||
|
||||
class DAINTMSK : public Register32 {
|
||||
public:
|
||||
REGS_FIELD(IEPM, uint16_t, 15, 0);
|
||||
REGS_FIELD(OEPM, uint16_t, 31, 16);
|
||||
};
|
||||
|
||||
class DIEPCTL0 : public Register32 {
|
||||
public:
|
||||
enum class MPSIZ {
|
||||
Size64 = 0,
|
||||
Size32 = 1,
|
||||
Size16 = 2,
|
||||
Size8 = 3
|
||||
};
|
||||
using Register32::Register32;
|
||||
void setMPSIZ(MPSIZ s) volatile { setBitRange(1, 0, (uint8_t)s); }
|
||||
REGS_BOOL_FIELD(STALL, 21);
|
||||
REGS_FIELD(TXFNUM, uint8_t, 25, 22);
|
||||
REGS_BOOL_FIELD(CNAK, 26);
|
||||
REGS_BOOL_FIELD(SNAK, 27);
|
||||
REGS_BOOL_FIELD(EPENA, 31);
|
||||
};
|
||||
|
||||
class DOEPCTL0 : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(CNAK, 26);
|
||||
REGS_BOOL_FIELD(SNAK, 27);
|
||||
REGS_BOOL_FIELD(EPENA, 31);
|
||||
};
|
||||
|
||||
class DIEPINT : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(XFRC, 0);
|
||||
REGS_BOOL_FIELD(INEPNE, 6);
|
||||
};
|
||||
|
||||
class DIEPTSIZ0 : public Register32 {
|
||||
public:
|
||||
using Register32::Register32;
|
||||
REGS_FIELD(XFRSIZ, uint8_t, 6, 0);
|
||||
REGS_FIELD(PKTCNT, uint8_t, 20, 19);
|
||||
};
|
||||
|
||||
class DOEPTSIZ0 : public Register32 {
|
||||
public:
|
||||
using Register32::Register32;
|
||||
REGS_FIELD(XFRSIZ, uint8_t, 6, 0);
|
||||
REGS_BOOL_FIELD(PKTCNT, 19);
|
||||
REGS_FIELD(STUPCNT, uint8_t, 30, 29);
|
||||
};
|
||||
|
||||
class PCGCCTL : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(STPPCLK, 0);
|
||||
REGS_BOOL_FIELD(GATEHCLK, 1);
|
||||
};
|
||||
|
||||
class DFIFO0 : public Register32 {
|
||||
};
|
||||
|
||||
constexpr OTG() {};
|
||||
REGS_REGISTER_AT(GAHBCFG, 0x008);
|
||||
REGS_REGISTER_AT(GUSBCFG, 0x00C);
|
||||
REGS_REGISTER_AT(GRSTCTL, 0x010);
|
||||
REGS_REGISTER_AT(GINTSTS, 0x014);
|
||||
REGS_REGISTER_AT(GINTMSK, 0x018);
|
||||
REGS_REGISTER_AT(GRXSTSP, 0x020);
|
||||
REGS_REGISTER_AT(GRXFSIZ, 0x024);
|
||||
REGS_REGISTER_AT(DIEPTXF0, 0x28);
|
||||
REGS_REGISTER_AT(GCCFG, 0x038);
|
||||
REGS_REGISTER_AT(DCFG, 0x800);
|
||||
REGS_REGISTER_AT(DCTL, 0x804);
|
||||
REGS_REGISTER_AT(DIEPMSK, 0x810);
|
||||
REGS_REGISTER_AT(DAINTMSK, 0x81C);
|
||||
REGS_REGISTER_AT(DIEPCTL0, 0x900);
|
||||
REGS_REGISTER_AT(DIEPTSIZ0, 0x910);
|
||||
REGS_REGISTER_AT(DOEPCTL0, 0xB00);
|
||||
REGS_REGISTER_AT(DOEPTSIZ0, 0xB10);
|
||||
REGS_REGISTER_AT(PCGCCTL, 0xE00);
|
||||
REGS_REGISTER_AT(DFIFO0, 0x1000);
|
||||
constexpr volatile DIEPINT * DIEPINT(int i) const {
|
||||
return (class DIEPINT *)(Base() + 0x908 + i*0x20);
|
||||
}
|
||||
private:
|
||||
constexpr uint32_t Base() const {
|
||||
return 0x50000000;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr OTG OTG;
|
||||
|
||||
#endif
|
||||
@@ -7,8 +7,11 @@ class RCC {
|
||||
public:
|
||||
class CR : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(PLLRDY, 25);
|
||||
REGS_BOOL_FIELD(HSION, 0);
|
||||
REGS_BOOL_FIELD(HSEON, 16);
|
||||
REGS_BOOL_FIELD(HSERDY, 17);
|
||||
REGS_BOOL_FIELD(PLLON, 24);
|
||||
REGS_BOOL_FIELD(PLLRDY, 25);
|
||||
};
|
||||
|
||||
class PLLCFGR : public Register32 {
|
||||
@@ -63,6 +66,7 @@ public:
|
||||
class AHB2ENR : Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(RNGEN, 6);
|
||||
REGS_BOOL_FIELD(OTGFSEN, 7);
|
||||
};
|
||||
|
||||
class AHB3ENR : Register32 {
|
||||
|
||||
@@ -56,6 +56,6 @@ typedef Register<uint64_t> Register64;
|
||||
#define REGS_BOOL_FIELD(name,bit) REGS_FIELD(name,bool,bit,bit)
|
||||
#define REGS_BOOL_FIELD_R(name,bit) REGS_FIELD_R(name,bool,bit,bit)
|
||||
#define REGS_BOOL_FIELD_W(name,bit) REGS_FIELD_W(name,bool,bit,bit)
|
||||
#define REGS_REGISTER_AT(name, offset) volatile name * name() const { return (class name *)(Base() + offset); };
|
||||
#define REGS_REGISTER_AT(name, offset) constexpr volatile name * name() const { return (class name *)(Base() + offset); };
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "pwr.h"
|
||||
#include "rcc.h"
|
||||
#include "rng.h"
|
||||
#include "otg.h"
|
||||
#include "sdio.h"
|
||||
#include "spi.h"
|
||||
#include "syscfg.h"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include <ion/usb.h>
|
||||
#include "usb.h"
|
||||
#include <ion/display.h>
|
||||
#include "device.h"
|
||||
#include "display.h"
|
||||
#include "regs/regs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
@@ -9,6 +13,23 @@ bool isPlugged() {
|
||||
return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin());
|
||||
}
|
||||
|
||||
bool isEnumerated() {
|
||||
/* Note: This implementation is not perfect. One would assume isEnumerated to
|
||||
* return true for as long as the device is enumerated. But the GINTSTS
|
||||
* register will be cleared in the poll() routine. */
|
||||
return OTG.GINTSTS()->getENUMDNE();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
// Get out of soft-disconnected state
|
||||
OTG.DCTL()->setSDIS(false);
|
||||
}
|
||||
|
||||
void disable() {
|
||||
// Get into soft-disconnected state
|
||||
OTG.DCTL()->setSDIS(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +39,143 @@ namespace Device {
|
||||
|
||||
void init() {
|
||||
initGPIO();
|
||||
}
|
||||
|
||||
void initGPIO() {
|
||||
/* Configure the GPIO
|
||||
* The VBUS pin is connected to the USB VBUS port. To read if the USB is
|
||||
* plugged, the pin must be pulled down. */
|
||||
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::Input);
|
||||
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::Down);
|
||||
initOTG();
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::Analog);
|
||||
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::None);
|
||||
shutdownOTG();
|
||||
shutdownGPIO();
|
||||
}
|
||||
|
||||
static inline void DEBUGTOGGLE() {
|
||||
bool state = GPIOC.ODR()->get(11);
|
||||
GPIOC.ODR()->set(11, !state);
|
||||
}
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void initGPIO() {
|
||||
|
||||
// DEBUG GPIO pin
|
||||
GPIOC.MODER()->setMode(11, GPIO::MODER::Mode::Output);
|
||||
GPIOC.ODR()->set(11, false);
|
||||
|
||||
/* Configure the GPIO
|
||||
* The VBUS pin is connected to the USB VBUS port. To read if the USB is
|
||||
* plugged, the pin must be pulled down. */
|
||||
// FIXME: Understand how the Vbus pin really works!
|
||||
#if 0
|
||||
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::Input);
|
||||
VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::Down);
|
||||
#else
|
||||
VbusPin.group().MODER()->setMode(VbusPin.pin(), GPIO::MODER::Mode::AlternateFunction);
|
||||
VbusPin.group().AFR()->setAlternateFunction(VbusPin.pin(), GPIO::AFR::AlternateFunction::AF10);
|
||||
#endif
|
||||
|
||||
DmPin.group().MODER()->setMode(DmPin.pin(), GPIO::MODER::Mode::AlternateFunction);
|
||||
DmPin.group().AFR()->setAlternateFunction(DmPin.pin(), GPIO::AFR::AlternateFunction::AF10);
|
||||
|
||||
DpPin.group().MODER()->setMode(DpPin.pin(), GPIO::MODER::Mode::AlternateFunction);
|
||||
DpPin.group().AFR()->setAlternateFunction(DpPin.pin(), GPIO::AFR::AlternateFunction::AF10);
|
||||
}
|
||||
|
||||
void shutdownGPIO() {
|
||||
constexpr static GPIOPin USBPins[] = {DpPin, DmPin, VbusPin};
|
||||
for (const GPIOPin & g : USBPins) {
|
||||
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
|
||||
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
|
||||
}
|
||||
}
|
||||
|
||||
void initOTG() {
|
||||
// Wait for AHB idle
|
||||
while (!OTG.GRSTCTL()->getAHBIDL()) {
|
||||
}
|
||||
|
||||
/* Core soft reset: Clears the interrupts and many of the CSR register bits,
|
||||
* resets state machines, flushes the FIFOs and terminates USB transactions.*/
|
||||
OTG.GRSTCTL()->setCSRST(true);
|
||||
while (OTG.GRSTCTL()->getCSRST()) {
|
||||
}
|
||||
|
||||
/* Enable the transceiver module of the PHY. It must be done to allow any USB
|
||||
* operation */
|
||||
OTG.GCCFG()->setPWRDWN(true);
|
||||
|
||||
/* Enable VBUS sensing comparators to detect valid levels for USB operation.
|
||||
* This is used for instance to end the session if the host is switched off.*/
|
||||
OTG.GCCFG()->setVBDEN(true);
|
||||
|
||||
// Force peripheral only mode
|
||||
OTG.GUSBCFG()->setFDMOD(true);
|
||||
|
||||
// Configure the USB turnaround time, depending on the AHB clock speed (96MHz)
|
||||
OTG.GUSBCFG()->setTRDT(0x6);
|
||||
|
||||
// Clear the interrupts
|
||||
OTG.GINTSTS()->set(0);
|
||||
|
||||
// Full speed device
|
||||
OTG.DCFG()->setDSPD(OTG::DCFG::DSPD::FullSpeed);
|
||||
|
||||
/* RxFIFO size. The value is in terms of 32-bit words.
|
||||
* According to the reference manual, it should be, at minimum:
|
||||
* (4 * number of control endpoints + 6)
|
||||
* To receive SETUP packets on control endpoint
|
||||
* + ((largest USB packet used / 4) + 1)
|
||||
* To receive 1 USB packet + 1 packet status
|
||||
* + (2 * number of OUT endpoints)
|
||||
* Transfer complete status information
|
||||
* + 1 for Global NAK
|
||||
* So, for the calculator: (4*1+6) + (64/4 + 1) + (2*1) + 1 = 30
|
||||
* As the RAM size is 1.25kB, the size should be at most 320, minus the space
|
||||
* for the Tx FIFOs.
|
||||
* However, we tested and found that only values between 40 and 255 actually
|
||||
* work. We arbitrarily chose 128. */
|
||||
OTG.GRXFSIZ()->setRXFD(128);
|
||||
|
||||
// Unmask the interrupt line assertions
|
||||
OTG.GAHBCFG()->setGINTMSK(true);
|
||||
|
||||
// Restart the PHY clock
|
||||
OTG.PCGCCTL()->setSTPPCLK(false);
|
||||
|
||||
// Pick which interrupts we're interested in
|
||||
class OTG::GINTMSK intMask(0); // Reset value
|
||||
intMask.setENUMDNEM(true); // Speed enumeration done
|
||||
intMask.setRXFLVLM(true); // Receive FIFO non empty
|
||||
intMask.setIEPINT(true); // IN endpoint interrupt
|
||||
OTG.GINTMSK()->set(intMask);
|
||||
|
||||
// Unmask IN interrupts for endpoint 0 only
|
||||
OTG.DAINTMSK()->setIEPM(1);
|
||||
|
||||
/* Unmask the IN transfer completed interrupt for all endpoints. This
|
||||
* interrupt warns that a IN transaction happened on the endpoint. */
|
||||
OTG.DIEPMSK()->setXFRCM(true);
|
||||
|
||||
/* To communicate with a USB host, the device still needs to get out of soft-
|
||||
* disconnected state (SDIS in the DCTL register). We do this when we detect
|
||||
* that the USB cable is plugged. */
|
||||
}
|
||||
|
||||
void shutdownOTG() {
|
||||
// Core soft reset
|
||||
OTG.GRSTCTL()->setCSRST(true);
|
||||
while (OTG.GRSTCTL()->getCSRST()) {
|
||||
}
|
||||
|
||||
// Get into soft-disconnected state
|
||||
OTG.DCTL()->setSDIS(true);
|
||||
|
||||
// Stop the PHY clock
|
||||
OTG.PCGCCTL()->setSTPPCLK(true);
|
||||
|
||||
// Stop VBUS sensing
|
||||
OTG.GCCFG()->setVBDEN(false);
|
||||
|
||||
// Disable the transceiver module of the PHY
|
||||
OTG.GCCFG()->setPWRDWN(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define ION_DEVICE_USB_H
|
||||
|
||||
#include "regs/regs.h"
|
||||
#include "ion.h"
|
||||
#include "usb/calculator.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
@@ -9,14 +11,21 @@ namespace Device {
|
||||
|
||||
/* Pin | Role | Mode | Function
|
||||
* -----+-------------------+-----------------------+----------
|
||||
* PA9 | VBUS | Input, pulled down | Low = unplugged, high = plugged
|
||||
* PA9 | VBUS | Input, pulled down//TODO | Low = unplugged, high = plugged
|
||||
* PA11 | USB D- | Alternate Function 10 |
|
||||
* PA12 | USB D+ | Alternate Function 10 |
|
||||
*/
|
||||
|
||||
void init();
|
||||
void initGPIO();
|
||||
void shutdown();
|
||||
|
||||
constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9);
|
||||
constexpr static GPIOPin DmPin = GPIOPin(GPIOA, 11);
|
||||
constexpr static GPIOPin DpPin = GPIOPin(GPIOA, 12);
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
void initGPIO();
|
||||
void shutdownGPIO();
|
||||
void initOTG();
|
||||
void shutdownOTG();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
61
ion/src/device/usb/Makefile
Normal file
61
ion/src/device/usb/Makefile
Normal file
@@ -0,0 +1,61 @@
|
||||
usb_objs += $(addprefix ion/src/device/usb/, \
|
||||
calculator.o \
|
||||
device.o\
|
||||
dfu_interface.o\
|
||||
endpoint0.o \
|
||||
interface.o\
|
||||
request_recipient.o\
|
||||
setup_packet.o\
|
||||
)
|
||||
|
||||
usb_objs += $(addprefix ion/src/device/usb/stack/, \
|
||||
bos_descriptor.o\
|
||||
configuration_descriptor.o \
|
||||
descriptor.o\
|
||||
device_descriptor.o\
|
||||
device_capability_descriptor.o\
|
||||
dfu_functional_descriptor.o\
|
||||
extended_compat_id_descriptor.o \
|
||||
interface_descriptor.o\
|
||||
language_id_string_descriptor.o \
|
||||
microsoft_os_string_descriptor.o\
|
||||
platform_device_capability_descriptor.o\
|
||||
streamable.o\
|
||||
string_descriptor.o\
|
||||
url_descriptor.o\
|
||||
webusb_platform_descriptor.o\
|
||||
)
|
||||
|
||||
EPSILON_USB_DFU_XIP ?= 0
|
||||
|
||||
ifeq ($(EPSILON_USB_DFU_XIP),1)
|
||||
|
||||
objs += ion/src/device/usb/dfu_xip.o
|
||||
objs += $(usb_objs)
|
||||
|
||||
else
|
||||
|
||||
dfu_objs += liba/src/assert.o
|
||||
dfu_objs += liba/src/strlen.o
|
||||
dfu_objs += liba/src/strlcpy.o
|
||||
dfu_objs += liba/src/memset.o
|
||||
dfu_objs += liba/src/memcpy.o
|
||||
dfu_objs += libaxx/src/cxxabi/pure_virtual.o
|
||||
dfu_objs += ion/src/device/usb/boot.o
|
||||
dfu_objs += ion/src/device/keyboard.o
|
||||
dfu_objs += ion/src/device/device.o
|
||||
dfu_objs += ion/src/device/usb.o
|
||||
|
||||
ion/src/device/usb/dfu.elf: LDFLAGS = --gc-sections -T ion/src/device/usb/dfu.ld
|
||||
ion/src/device/usb/dfu.elf: $(usb_objs) $(dfu_objs)
|
||||
|
||||
ion/src/device/usb/dfu.o: ion/src/device/usb/dfu.bin
|
||||
@echo "OBJCOPY $@"
|
||||
$(Q) $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@
|
||||
|
||||
objs += ion/src/device/usb/dfu.o
|
||||
objs += ion/src/device/usb/dfu_relocated.o
|
||||
|
||||
products += $(usb_objs) $(addprefix ion/src/device/usb/dfu, .elf .bin)
|
||||
|
||||
endif
|
||||
2
ion/src/device/usb/boot.cpp
Normal file
2
ion/src/device/usb/boot.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
extern "C" void abort() {
|
||||
}
|
||||
86
ion/src/device/usb/calculator.cpp
Normal file
86
ion/src/device/usb/calculator.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "calculator.h"
|
||||
#include "../regs/regs.h"
|
||||
#include "../keyboard.h"
|
||||
#include <ion/usb.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
bool Calculator::PollAndReset(bool exitWithKeyboard) {
|
||||
char serialNumber[Ion::SerialNumberLength+1];
|
||||
Ion::getSerialNumber(serialNumber);
|
||||
Calculator c(serialNumber);
|
||||
|
||||
/* Leave DFU mode if the Back key is pressed, the calculator unplugged or the
|
||||
* USB core soft-disconnected. */
|
||||
Ion::Keyboard::Key exitKey = Ion::Keyboard::Key::A6;
|
||||
uint8_t exitKeyRow = Ion::Keyboard::Device::rowForKey(exitKey);
|
||||
uint8_t exitKeyColumn = Ion::Keyboard::Device::columnForKey(exitKey);
|
||||
|
||||
Ion::Keyboard::Device::activateRow(exitKeyRow);
|
||||
|
||||
while (!(exitWithKeyboard && Ion::Keyboard::Device::columnIsActive(exitKeyColumn)) &&
|
||||
Ion::USB::isPlugged() &&
|
||||
!c.isSoftDisconnected()) {
|
||||
c.poll();
|
||||
}
|
||||
if (!c.isSoftDisconnected()) {
|
||||
c.detach();
|
||||
}
|
||||
return c.resetOnDisconnect();
|
||||
}
|
||||
|
||||
Descriptor * Calculator::descriptor(uint8_t type, uint8_t index) {
|
||||
/* Special case: Microsoft OS String Descriptor should be returned when
|
||||
* searching for string descriptor at index 0xEE. */
|
||||
if (type == m_microsoftOSStringDescriptor.type() && index == 0xEE) {
|
||||
return &m_microsoftOSStringDescriptor;
|
||||
}
|
||||
int typeCount = 0;
|
||||
for (size_t i=0; i<sizeof(m_descriptors)/sizeof(m_descriptors[0]); i++) {
|
||||
Descriptor * descriptor = m_descriptors[i];
|
||||
if (descriptor->type() != type) {
|
||||
continue;
|
||||
}
|
||||
if (typeCount == index) {
|
||||
return descriptor;
|
||||
} else {
|
||||
typeCount++;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Calculator::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (Device::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
|
||||
return true;
|
||||
}
|
||||
if (request->requestType() == SetupPacket::RequestType::Vendor) {
|
||||
if (request->bRequest() == k_webUSBVendorCode && request->wIndex() == 2) {
|
||||
// This is a WebUSB, GET_URL request
|
||||
assert(request->wValue() == k_webUSBLandingPageIndex);
|
||||
return getURLCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
}
|
||||
if (request->bRequest() == k_microsoftOSVendorCode && request->wIndex() == 0x0004) {
|
||||
// This is a Microsoft OS descriptor, Extended Compat ID request
|
||||
assert(request->wValue() == 0);
|
||||
return getExtendedCompatIDCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Calculator::getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = m_workshopURLDescriptor.copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Calculator::getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = m_extendedCompatIdDescriptor.copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
163
ion/src/device/usb/calculator.h
Normal file
163
ion/src/device/usb/calculator.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef ION_DEVICE_USB_CALCULATOR_H
|
||||
#define ION_DEVICE_USB_CALCULATOR_H
|
||||
|
||||
#include "device.h"
|
||||
#include "dfu_interface.h"
|
||||
#include "stack/bos_descriptor.h"
|
||||
#include "stack/configuration_descriptor.h"
|
||||
#include "stack/descriptor.h"
|
||||
#include "stack/device_descriptor.h"
|
||||
#include "stack/dfu_functional_descriptor.h"
|
||||
#include "stack/extended_compat_id_descriptor.h"
|
||||
#include "stack/interface_descriptor.h"
|
||||
#include "stack/language_id_string_descriptor.h"
|
||||
#include "stack/microsoft_os_string_descriptor.h"
|
||||
#include "stack/string_descriptor.h"
|
||||
#include "stack/url_descriptor.h"
|
||||
#include "stack/webusb_platform_descriptor.h"
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class Calculator : public Device {
|
||||
public:
|
||||
static bool PollAndReset(bool exitWithKeyboard); // Return true if reset is needed
|
||||
Calculator(const char * serialNumber) :
|
||||
Device(&m_dfuInterface),
|
||||
m_deviceDescriptor(
|
||||
0x0210, /* bcdUSB: USB Specification Number which the device complies
|
||||
* to. Must be greater than 0x0200 to use the BOS. */
|
||||
0, // bDeviceClass: The class is defined by the interface.
|
||||
0, // bDeviceSUBClass: The subclass is defined by the interface.
|
||||
0, // bDeviceProtocol: The protocol is defined by the interface.
|
||||
64, // bMaxPacketSize0: Maximum packet size for endpoint 0
|
||||
0x0483, // idVendor
|
||||
0xA291, // idProduct
|
||||
0x0100, // bcdDevice: Device Release Number
|
||||
1, // iManufacturer: Index of the manufacturer name string, see m_descriptor
|
||||
2, // iProduct: Index of the product name string, see m_descriptor
|
||||
3, // iSerialNumber: Index of the SerialNumber string, see m_descriptor
|
||||
1), // bNumConfigurations
|
||||
m_dfuFunctionalDescriptor(
|
||||
0b0011, /* bmAttributes:
|
||||
* - bitWillDetach: If true, the device will perform a bus
|
||||
* detach-attach sequence when it receives a DFU_DETACH
|
||||
* request. The host must not issue a USB Reset.
|
||||
* - bitManifestationTolerant: if true, the device is able to
|
||||
* communicate via USB after Manifestation phase. The
|
||||
* manifestation phase implies a reset in the calculator, so,
|
||||
* even if the device is still plugged, it needs to be
|
||||
* re-enumerated to communicate.
|
||||
* - bitCanUpload
|
||||
* - bitCanDnload */
|
||||
0, /* wDetachTimeOut: Time, in milliseconds, that the device in APP
|
||||
* mode will wait after receipt of the DFU_DETACH request before
|
||||
* switching to DFU mode. It does not apply to the calculator.*/
|
||||
2048, // wTransferSize: Maximum number of bytes that the device can accept per control-write transaction
|
||||
0x0100),// bcdDFUVersion
|
||||
m_interfaceDescriptor(
|
||||
0, // bInterfaceNumber
|
||||
k_dfuInterfaceAlternateSetting, // bAlternateSetting
|
||||
0, // bNumEndpoints: Other than endpoint 0
|
||||
0xFE, // bInterfaceClass: DFU (http://www.usb.org/developers/defined_class)
|
||||
1, // bInterfaceSubClass: DFU
|
||||
2, // bInterfaceProtocol: DFU Mode (not DFU Runtime, which would be 1)
|
||||
4, // iInterface: Index of the Interface string, see m_descriptor
|
||||
&m_dfuFunctionalDescriptor),
|
||||
m_configurationDescriptor(
|
||||
9 + 9 + 9, // wTotalLength: configuration descriptor + interface descriptor + dfu functional descriptor lengths
|
||||
1, // bNumInterfaces
|
||||
k_bConfigurationValue, // bConfigurationValue
|
||||
0, // iConfiguration: No string descriptor for the configuration
|
||||
0x80, /* bmAttributes:
|
||||
* Bit 7: Reserved, set to 1
|
||||
* Bit 6: Self Powered
|
||||
* Bit 5: Remote Wakeup (allows the device to wake up the host when the host is in suspend)
|
||||
* Bit 4..0: Reserved, set to 0 */
|
||||
0x32, // bMaxPower: half of the Maximum Power Consumption
|
||||
&m_interfaceDescriptor),
|
||||
m_webUSBPlatformDescriptor(
|
||||
k_webUSBVendorCode,
|
||||
k_webUSBLandingPageIndex),
|
||||
m_bosDescriptor(
|
||||
5 + 24, // wTotalLength: BOS descriptor + webusb platform descriptor lengths
|
||||
1, // bNumDeviceCapabilities
|
||||
&m_webUSBPlatformDescriptor),
|
||||
m_languageStringDescriptor(),
|
||||
m_manufacturerStringDescriptor("NumWorks"),
|
||||
m_productStringDescriptor("NumWorks Calculator"),
|
||||
m_serialNumberStringDescriptor(serialNumber),
|
||||
m_interfaceStringDescriptor("@Flash/0x08000000/04*016Kg,01*064Kg,07*128Kg"),
|
||||
//m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"),
|
||||
/* Switch to this descriptor to use dfu-util to write in the SRAM.
|
||||
* FIXME Should be an alternate Interface. */
|
||||
m_microsoftOSStringDescriptor(k_microsoftOSVendorCode),
|
||||
m_workshopURLDescriptor(URLDescriptor::Scheme::HTTPS, "workshop.numworks.com"),
|
||||
m_extendedCompatIdDescriptor("WINUSB"),
|
||||
m_descriptors{
|
||||
&m_deviceDescriptor, // Type = Device, Index = 0
|
||||
&m_configurationDescriptor, // Type = Configuration, Index = 0
|
||||
&m_languageStringDescriptor, // Type = String, Index = 0
|
||||
&m_manufacturerStringDescriptor, // Type = String, Index = 1
|
||||
&m_productStringDescriptor, // Type = String, Index = 2
|
||||
&m_serialNumberStringDescriptor, // Type = String, Index = 3
|
||||
&m_interfaceStringDescriptor, // Type = String, Index = 4
|
||||
&m_bosDescriptor // Type = BOS, Index = 0
|
||||
},
|
||||
m_dfuInterface(this, &m_ep0, k_dfuInterfaceAlternateSetting)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual Descriptor * descriptor(uint8_t type, uint8_t index) override;
|
||||
virtual void setActiveConfiguration(uint8_t configurationIndex) override {
|
||||
assert(configurationIndex == k_bConfigurationValue);
|
||||
}
|
||||
virtual uint8_t getActiveConfiguration() override {
|
||||
return k_bConfigurationValue;
|
||||
}
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t k_bConfigurationValue = 1;
|
||||
static constexpr uint8_t k_dfuInterfaceAlternateSetting = 0;
|
||||
static constexpr uint8_t k_webUSBVendorCode = 1;
|
||||
static constexpr uint8_t k_webUSBLandingPageIndex = 1;
|
||||
static constexpr uint8_t k_microsoftOSVendorCode = 2;
|
||||
|
||||
// WebUSB and MicrosoftOSDescriptor commands
|
||||
bool getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
|
||||
// Descriptors
|
||||
DeviceDescriptor m_deviceDescriptor;
|
||||
DFUFunctionalDescriptor m_dfuFunctionalDescriptor;
|
||||
InterfaceDescriptor m_interfaceDescriptor;
|
||||
ConfigurationDescriptor m_configurationDescriptor;
|
||||
WebUSBPlatformDescriptor m_webUSBPlatformDescriptor;
|
||||
BOSDescriptor m_bosDescriptor;
|
||||
LanguageIDStringDescriptor m_languageStringDescriptor;
|
||||
StringDescriptor m_manufacturerStringDescriptor;
|
||||
StringDescriptor m_productStringDescriptor;
|
||||
StringDescriptor m_serialNumberStringDescriptor;
|
||||
StringDescriptor m_interfaceStringDescriptor;
|
||||
MicrosoftOSStringDescriptor m_microsoftOSStringDescriptor;
|
||||
URLDescriptor m_workshopURLDescriptor;
|
||||
ExtendedCompatIDDescriptor m_extendedCompatIdDescriptor;
|
||||
|
||||
Descriptor * m_descriptors[8];
|
||||
/* m_descriptors contains only descriptors that sould be returned via the
|
||||
* method descriptor(uint8_t type, uint8_t index), so do not count descriptors
|
||||
* included in other descriptors or returned by other functions. */
|
||||
|
||||
// Interface
|
||||
DFUInterface m_dfuInterface;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
156
ion/src/device/usb/device.cpp
Normal file
156
ion/src/device/usb/device.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "device.h"
|
||||
#include "../regs/regs.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
static inline uint16_t min(uint16_t x, uint16_t y) { return (x<y ? x : y); }
|
||||
|
||||
void Device::poll() {
|
||||
// Read the interrupts
|
||||
class OTG::GINTSTS intsts(OTG.GINTSTS()->get());
|
||||
|
||||
/* SETUP or OUT transaction
|
||||
* If the Rx FIFO is not empty, there is a SETUP or OUT transaction.
|
||||
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
|
||||
if (intsts.getRXFLVL()) {
|
||||
class OTG::GRXSTSP grxstsp(OTG.GRXSTSP()->get());
|
||||
|
||||
// Store the packet status
|
||||
OTG::GRXSTSP::PKTSTS pktsts = grxstsp.getPKTSTS();
|
||||
|
||||
// We only use endpoint 0
|
||||
assert(grxstsp.getEPNUM() == 0);
|
||||
|
||||
if (pktsts == OTG::GRXSTSP::PKTSTS::OutTransferCompleted || pktsts == OTG::GRXSTSP::PKTSTS::SetupTransactionCompleted) {
|
||||
// There is no data associated with this interrupt.
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pktsts != OTG::GRXSTSP::PKTSTS::GlobalOutNAK);
|
||||
/* We did not enable the GONAKEFFM (Global OUT NAK effective mask) bit in
|
||||
* GINTSTS, so we should never get this interrupt. */
|
||||
|
||||
assert(pktsts == OTG::GRXSTSP::PKTSTS::OutReceived || pktsts == OTG::GRXSTSP::PKTSTS::SetupReceived);
|
||||
|
||||
TransactionType type = (pktsts == OTG::GRXSTSP::PKTSTS::OutReceived) ? TransactionType::Out : TransactionType::Setup;
|
||||
|
||||
if (type == TransactionType::Setup && OTG.DIEPTSIZ0()->getPKTCNT()) {
|
||||
// SETUP received but there is a packet in the Tx FIFO. Flush it.
|
||||
m_ep0.flushTxFifo();
|
||||
}
|
||||
|
||||
// Save the received packet byte count
|
||||
m_ep0.setReceivedPacketSize(grxstsp.getBCNT());
|
||||
|
||||
if (type == TransactionType::Setup) {
|
||||
m_ep0.readAndDispatchSetupPacket();
|
||||
} else {
|
||||
assert(type == TransactionType::Out);
|
||||
m_ep0.processOUTpacket();
|
||||
}
|
||||
|
||||
m_ep0.discardUnreadData();
|
||||
}
|
||||
|
||||
/* IN transactions.
|
||||
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
|
||||
if (OTG.DIEPINT(0)->getXFRC()) { // We only check endpoint 0.
|
||||
m_ep0.processINpacket();
|
||||
// Clear the Transfer Completed Interrupt
|
||||
OTG.DIEPINT(0)->setXFRC(true);
|
||||
}
|
||||
|
||||
// Handle USB RESET. ENUMDNE = **SPEED** Enumeration Done
|
||||
if (intsts.getENUMDNE()) {
|
||||
// Clear the ENUMDNE bit
|
||||
OTG.GINTSTS()->setENUMDNE(true);
|
||||
/* After a USB reset, the host talks to the device by sending messages to
|
||||
* address 0; */
|
||||
setAddress(0);
|
||||
// Flush the FIFOs
|
||||
m_ep0.reset();
|
||||
m_ep0.setup();
|
||||
/* In setup(), we should set the MPSIZ field in OTG_DIEPCTL0 to the maximum
|
||||
* packet size depending on the enumeration speed (found in OTG_DSTS). We
|
||||
* should always get FullSpeed, so we set the packet size accordingly. */
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::isSoftDisconnected() const {
|
||||
return OTG.DCTL()->getSDIS();
|
||||
}
|
||||
|
||||
void Device::detach() {
|
||||
// Get in soft-disconnected state
|
||||
OTG.DCTL()->setSDIS(true);
|
||||
}
|
||||
|
||||
bool Device::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
// Device only handles standard requests.
|
||||
if (request->requestType() != SetupPacket::RequestType::Standard) {
|
||||
return false;
|
||||
}
|
||||
switch (request->bRequest()) {
|
||||
case (int) Request::GetStatus:
|
||||
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetAddress:
|
||||
// Make sure the request is adress is valid.
|
||||
assert(request->wValue() < 128);
|
||||
/* According to the reference manual, the address should be set after the
|
||||
* Status stage of the current transaction, but this is not true.
|
||||
* It should be set here, after the Data stage. */
|
||||
setAddress(request->wValue());
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
case (int) Request::GetDescriptor:
|
||||
return getDescriptor(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetConfiguration:
|
||||
*transferBufferLength = 0;
|
||||
return setConfiguration(request);
|
||||
case (int) Request::GetConfiguration:
|
||||
return getConfiguration(transferBuffer, transferBufferLength);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Device::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = min(2, transferBufferMaxLength);
|
||||
for (int i = 0; i<*transferBufferLength; i++) {
|
||||
transferBuffer[i] = 0; // No remote wakeup, not self-powered.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::setAddress(uint8_t address) {
|
||||
OTG.DCFG()->setDAD(address);
|
||||
}
|
||||
|
||||
bool Device::getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
Descriptor * wantedDescriptor = descriptor(request->descriptorType(), request->descriptorIndex());
|
||||
if (wantedDescriptor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*transferBufferLength = wantedDescriptor->copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength) {
|
||||
*transferBufferLength = 1;
|
||||
transferBuffer[0] = getActiveConfiguration();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::setConfiguration(SetupPacket * request) {
|
||||
// We support one configuration only
|
||||
setActiveConfiguration(request->wValue());
|
||||
/* There is one configuration only, we no need to set it again, just reset the
|
||||
* endpoint. */
|
||||
m_ep0.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
66
ion/src/device/usb/device.h
Normal file
66
ion/src/device/usb/device.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef ION_DEVICE_USB_DEVICE_H
|
||||
#define ION_DEVICE_USB_DEVICE_H
|
||||
|
||||
#include "stack/descriptor.h"
|
||||
#include "endpoint0.h"
|
||||
#include "interface.h"
|
||||
#include "request_recipient.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
// We only handle control transfers, on EP0.
|
||||
class Device : public RequestRecipient {
|
||||
public:
|
||||
Device(Interface * interface) :
|
||||
RequestRecipient(&m_ep0),
|
||||
m_ep0(this, interface),
|
||||
m_resetOnDisconnect(false)
|
||||
{
|
||||
}
|
||||
void poll();
|
||||
bool isSoftDisconnected() const;
|
||||
void detach();
|
||||
bool resetOnDisconnect() { return m_resetOnDisconnect; }
|
||||
void setResetOnDisconnect(bool reset) { m_resetOnDisconnect = reset; }
|
||||
protected:
|
||||
virtual Descriptor * descriptor(uint8_t type, uint8_t index) = 0;
|
||||
virtual void setActiveConfiguration(uint8_t configurationIndex) = 0;
|
||||
virtual uint8_t getActiveConfiguration() = 0;
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
Endpoint0 m_ep0;
|
||||
private:
|
||||
// USB Standard Device Request Codes
|
||||
enum class Request {
|
||||
GetStatus = 0,
|
||||
ClearFeature = 1,
|
||||
SetFeature = 3,
|
||||
SetAddress = 5,
|
||||
GetDescriptor = 6,
|
||||
SetDescriptor = 7,
|
||||
GetConfiguration = 8,
|
||||
SetConfiguration = 9,
|
||||
};
|
||||
|
||||
enum class TransactionType {
|
||||
Setup,
|
||||
In,
|
||||
Out
|
||||
};
|
||||
|
||||
void setAddress(uint8_t address);
|
||||
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength);
|
||||
bool setConfiguration(SetupPacket * request);
|
||||
|
||||
bool m_resetOnDisconnect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
50
ion/src/device/usb/dfu.ld
Normal file
50
ion/src/device/usb/dfu.ld
Normal file
@@ -0,0 +1,50 @@
|
||||
/* DFU transfers can serve two purposes:
|
||||
* - Transfering RAM data between the machine and the host, e.g. Python scripts
|
||||
* - Upgrading the flash memory to perform a software update
|
||||
*
|
||||
* The second case raises a huge issue: code cannot be executed from memory that
|
||||
* is being modified. We're solving this issue by copying the DFU code in RAM.
|
||||
*
|
||||
* This linker script will generate some code that expects to be executed from a
|
||||
* fixed address in RAM. The corresponding instructions will be embedded in the
|
||||
* main Epsilon ELF file, and copied to that address before execution.
|
||||
*
|
||||
* This address needs to live in RAM, and needs to be temporarily overwriteable
|
||||
* when the program is being run. Epsilon has a large stack to allow deeply
|
||||
* recursive code to run. But when doing DFU transfers it is safe to assume we
|
||||
* will need very little stack space. We're therefore using the topmost 8K of
|
||||
* the stack reserved by Epsilon.
|
||||
*
|
||||
* Last but not least, we'll want to jump to a known entry point when running
|
||||
* the DFU code (namely, Ion::USB::Device::Calculator::Poll). We're simply
|
||||
* making sure this is the first symbol output. */
|
||||
|
||||
EPSILON_STACK_END = 0x20000000 + 256K - 32K;
|
||||
|
||||
MEMORY {
|
||||
RAM_BUFFER (rw) : ORIGIN = EPSILON_STACK_END, LENGTH = 8K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.text._ZN3Ion3USB6Device10Calculator12PollAndResetEb))
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
/DISCARD/ : {
|
||||
/* For now, we do not need .bss and .data sections. This allows us to simply
|
||||
* skip any rt0-style initialization and jump straight into the PollAndReset
|
||||
* routine. */
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
}
|
||||
}
|
||||
358
ion/src/device/usb/dfu_interface.cpp
Normal file
358
ion/src/device/usb/dfu_interface.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
#include "dfu_interface.h"
|
||||
#include "../regs/cm4.h"
|
||||
#include "../regs/flash.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
static inline uint32_t min(uint32_t x, uint32_t y) { return (x<y ? x : y); }
|
||||
|
||||
void DFUInterface::StatusData::push(Channel * c) const {
|
||||
c->push(m_bStatus);
|
||||
c->push(m_bwPollTimeout[2]);
|
||||
c->push(m_bwPollTimeout[1]);
|
||||
c->push(m_bwPollTimeout[0]);
|
||||
c->push(m_bState);
|
||||
c->push(m_iString);
|
||||
}
|
||||
|
||||
void DFUInterface::StateData::push(Channel * c) const {
|
||||
c->push(m_bState);
|
||||
}
|
||||
|
||||
void DFUInterface::wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
|
||||
if (request->bRequest() == (uint8_t) DFURequest::Download) {
|
||||
// Handle a download request
|
||||
if (request->wValue() == 0) {
|
||||
// The request is a special command
|
||||
switch (transferBuffer[0]) {
|
||||
case (uint8_t) DFUDownloadCommand::SetAddressPointer:
|
||||
setAddressPointerCommand(request, transferBuffer, *transferBufferLength);
|
||||
return;
|
||||
case (uint8_t) DFUDownloadCommand::Erase:
|
||||
eraseCommand(transferBuffer, *transferBufferLength);
|
||||
return;
|
||||
default:
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errSTALLEDPKT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (request->wValue() == 1) {
|
||||
m_ep0->stallTransaction();
|
||||
return;
|
||||
}
|
||||
if (request->wLength() > 0) {
|
||||
// The request is a "real" download. Compute the writing address.
|
||||
m_writeAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
|
||||
// Store the received data until we copy it on the flash.
|
||||
memcpy(m_largeBuffer, transferBuffer, *transferBufferLength);
|
||||
m_largeBufferLength = *transferBufferLength;
|
||||
m_state = State::dfuDNLOADSYNC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DFUInterface::wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
|
||||
if (request->bRequest() == (uint8_t) DFURequest::GetStatus) {
|
||||
// Do any needed action after the GetStatus request.
|
||||
if (m_state == State::dfuMANIFEST) {
|
||||
// Leave DFU routine: Leave DFU, reset device, jump to application code
|
||||
leaveDFUAndReset();
|
||||
} else if (m_state == State::dfuDNBUSY) {
|
||||
if (m_largeBufferLength != 0) {
|
||||
// Here, copy the data from the transfer buffer to the flash memory
|
||||
writeOnMemory();
|
||||
}
|
||||
changeAddressPointerIfNeeded();
|
||||
eraseMemoryIfNeeded();
|
||||
m_state = State::dfuDNLOADIDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (Interface::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
|
||||
return true;
|
||||
}
|
||||
switch (request->bRequest()) {
|
||||
case (uint8_t) DFURequest::Detach:
|
||||
m_device->detach();
|
||||
return true;
|
||||
case (uint8_t) DFURequest::Download:
|
||||
return processDownloadRequest(request->wLength(), transferBufferLength);
|
||||
case (uint8_t) DFURequest::Upload:
|
||||
return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t) DFURequest::GetStatus:
|
||||
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t) DFURequest::ClearStatus:
|
||||
return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t) DFURequest::GetState:
|
||||
return getState(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t) DFURequest::Abort:
|
||||
return dfuAbort(transferBufferLength);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength) {
|
||||
if (m_state != State::dfuIDLE && m_state != State::dfuDNLOADIDLE) {
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errNOTDONE;
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
}
|
||||
if (wLength == 0) {
|
||||
// Leave DFU routine: Reset the device and jump to application code
|
||||
m_state = State::dfuMANIFESTSYNC;
|
||||
} else {
|
||||
// Prepare to receive the download data
|
||||
m_ep0->clearForOutTransactions(wLength);
|
||||
m_state = State::dfuDNLOADSYNC;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DFUInterface::processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (m_state != State::dfuIDLE && m_state != State::dfuUPLOADIDLE) {
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
}
|
||||
if (request->wValue() == 0) {
|
||||
/* The host requests to read the commands supported by the bootloader. After
|
||||
* receiving this command, the device should returns N bytes representing
|
||||
* the command codes for :
|
||||
* Get command / Set Address Pointer / Erase / Read Unprotect
|
||||
* We no not need it for now. */
|
||||
return false;
|
||||
} else if (request->wValue() == 1) {
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
} else {
|
||||
/* We decided to never protect Read operation. Else we would have to check
|
||||
* here it is not protected before reading. */
|
||||
|
||||
// Compute the reading address
|
||||
uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
|
||||
// Copy the requested memory zone into the transfer buffer.
|
||||
uint16_t copySize = min(transferBufferMaxLength, request->wLength());
|
||||
memcpy(transferBuffer, (void *)readAddress, copySize);
|
||||
*transferBufferLength = copySize;
|
||||
}
|
||||
m_state = State::dfuUPLOADIDLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DFUInterface::setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength) {
|
||||
assert(transferBufferLength == 5);
|
||||
// Compute the new address but change it after the next getStatus request.
|
||||
m_potentialNewAddressPointer = transferBuffer[1]
|
||||
+ (transferBuffer[2] << 8)
|
||||
+ (transferBuffer[3] << 16)
|
||||
+ (transferBuffer[4] << 24);
|
||||
m_state = State::dfuDNLOADSYNC;
|
||||
}
|
||||
|
||||
void DFUInterface::changeAddressPointerIfNeeded() {
|
||||
if (m_potentialNewAddressPointer == 0) {
|
||||
// There was no address change waiting.
|
||||
return;
|
||||
}
|
||||
// If there is a new address pointer waiting, change the pointer address.
|
||||
m_addressPointer = m_potentialNewAddressPointer;
|
||||
m_potentialNewAddressPointer = 0;
|
||||
m_state = State::dfuDNLOADIDLE;
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength) {
|
||||
/* We determine whether the commands asks for a mass erase or which sector to
|
||||
* erase. The erase must be done after the next getStatus request. */
|
||||
if (transferBufferLength == 1) {
|
||||
// Mass erase
|
||||
m_erasePage = k_flashMemorySectorsCount;
|
||||
} else {
|
||||
// Sector erase
|
||||
assert(transferBufferLength == 5);
|
||||
/* Find the sector number to erase. If the address is not a valid start of
|
||||
* sector, return an error. */
|
||||
uint32_t sectorAddresses[k_flashMemorySectorsCount] = {
|
||||
0x08000000,
|
||||
0x08004000,
|
||||
0x08008000,
|
||||
0x0800C000,
|
||||
0x08010000,
|
||||
0x08020000,
|
||||
0x08040000,
|
||||
0x08060000,
|
||||
0x08080000,
|
||||
0x080A0000,
|
||||
0x080C0000,
|
||||
0x080E0000
|
||||
};
|
||||
uint32_t eraseAddress = transferBuffer[1]
|
||||
+ (transferBuffer[2] << 8)
|
||||
+ (transferBuffer[3] << 16)
|
||||
+ (transferBuffer[4] << 24);
|
||||
m_erasePage = k_flashMemorySectorsCount + 1;
|
||||
for (uint8_t i = 0; i < k_flashMemorySectorsCount; i++) {
|
||||
if (sectorAddresses[i] == eraseAddress) {
|
||||
m_erasePage = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_erasePage == k_flashMemorySectorsCount + 1) {
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errTARGET;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_state = State::dfuDNLOADSYNC;
|
||||
}
|
||||
|
||||
|
||||
void DFUInterface::eraseMemoryIfNeeded() {
|
||||
if (m_erasePage == k_flashMemorySectorsCount + 1) {
|
||||
// There was no erase waiting.
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock the Flash and check that no memory operation is ongoing
|
||||
unlockFlashMemory();
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
|
||||
if (m_erasePage == k_flashMemorySectorsCount) {
|
||||
// Mass erase
|
||||
FLASH.CR()->setMER(true);
|
||||
} else {
|
||||
// Sector erase
|
||||
FLASH.CR()->setSER(true);
|
||||
FLASH.CR()->setSNB(m_erasePage);
|
||||
}
|
||||
// Trigger the erase operation
|
||||
FLASH.CR()->setSTRT(true);
|
||||
|
||||
// Lock the Flash after all operations are done
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
lockFlashMemory();
|
||||
|
||||
/* The Reference manual says: "If a Flash memory write access concerns some
|
||||
* data in the data cache, the Flash write access modifies the data in the
|
||||
* Flash memory and the data in the cache.
|
||||
* If an erase operation in Flash memory also concerns data in the data or
|
||||
* instruction cache, you have to make sure that these data are rewritten
|
||||
* before they are accessed during code execution. If this cannot be done
|
||||
* safely, it is recommended to flush the caches by setting the DCRST and
|
||||
* ICRST bits in the FLASH_CR register.
|
||||
* The I/D cache should be flushed only when it is disabled (I/DCEN = 0).
|
||||
*
|
||||
* We normally do a reset after erasing and writing on the Flash, so this
|
||||
* should not be needed. */
|
||||
|
||||
/* Put an out of range value in m_erasePage to indicate that no erase is
|
||||
* waiting. */
|
||||
m_erasePage = k_flashMemorySectorsCount + 1;
|
||||
m_state = State::dfuDNLOADIDLE;
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
void DFUInterface::writeOnMemory() {
|
||||
if (m_writeAddress >= k_flashStartAddress && m_writeAddress <= k_flashEndAddress) {
|
||||
// Write ont the Flash
|
||||
|
||||
/* We should check here that the destination is not the option bytes: it
|
||||
* won't happen for us. */
|
||||
|
||||
// Unlock the Flash and check that no memory operation is ongoing
|
||||
unlockFlashMemory();
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
FLASH.CR()->setPG(true);
|
||||
|
||||
uint32_t * source = reinterpret_cast<uint32_t *>(m_largeBuffer);
|
||||
uint32_t * destination = reinterpret_cast<uint32_t *>(m_writeAddress);
|
||||
for (uint16_t i=0; i<m_largeBufferLength/sizeof(uint32_t); i++) {
|
||||
*destination++ = *source++;
|
||||
}
|
||||
|
||||
// Lock the Flash after all operations are done
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
lockFlashMemory();
|
||||
} else if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
|
||||
// Write on SRAM
|
||||
// FIXME We should check that we are not overriding the current instructions.
|
||||
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
|
||||
} else {
|
||||
// Invalid write address
|
||||
m_largeBufferLength = 0;
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errTARGET;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the buffer length
|
||||
m_largeBufferLength = 0;
|
||||
// Change the interface state and status
|
||||
m_state = State::dfuDNLOADIDLE;
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
void DFUInterface::unlockFlashMemory() {
|
||||
/* After a reset, program and erase operations are forbidden on the flash.
|
||||
* They can be unlocked by writting the appropriate keys in the FLASH_KEY
|
||||
* register. */
|
||||
if (FLASH.CR()->getLOCK()) {
|
||||
FLASH.KEYR()->set(0x45670123);
|
||||
FLASH.KEYR()->set(0xCDEF89AB);
|
||||
// Set the parallelism size
|
||||
FLASH.CR()->setPSIZE(FLASH::CR::PSIZE::X32);
|
||||
}
|
||||
}
|
||||
|
||||
void DFUInterface::lockFlashMemory() {
|
||||
FLASH.CR()->setLOCK(true);
|
||||
}
|
||||
|
||||
bool DFUInterface::getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
// Change the status if needed
|
||||
if (m_state == State::dfuMANIFESTSYNC) {
|
||||
m_state = State::dfuMANIFEST;
|
||||
} else if (m_state == State::dfuDNLOADSYNC) {
|
||||
m_state = State::dfuDNBUSY;
|
||||
}
|
||||
// Copy the status on the TxFifo
|
||||
*transferBufferLength = StatusData(m_status, m_state).copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DFUInterface::clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
m_status = Status::OK;
|
||||
m_state = State::dfuIDLE;
|
||||
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
}
|
||||
|
||||
bool DFUInterface::getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize) {
|
||||
*transferBufferLength = StateData(m_state).copy(transferBuffer, maxSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) {
|
||||
m_status = Status::OK;
|
||||
m_state = State::dfuIDLE;
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DFUInterface::leaveDFUAndReset() {
|
||||
m_device->setResetOnDisconnect(true);
|
||||
m_device->detach();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
174
ion/src/device/usb/dfu_interface.h
Normal file
174
ion/src/device/usb/dfu_interface.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#ifndef ION_DEVICE_USB_DFU_INTERFACE_H
|
||||
#define ION_DEVICE_USB_DFU_INTERFACE_H
|
||||
|
||||
#include "device.h"
|
||||
#include "interface.h"
|
||||
#include "endpoint0.h"
|
||||
#include "setup_packet.h"
|
||||
#include "stack/streamable.h"
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class DFUInterface : public Interface {
|
||||
|
||||
public:
|
||||
DFUInterface(Device * device, Endpoint0 * ep0, uint8_t bInterfaceAlternateSetting) :
|
||||
Interface(ep0),
|
||||
m_device(device),
|
||||
m_status(Status::OK),
|
||||
m_state(State::dfuIDLE),
|
||||
m_addressPointer(0),
|
||||
m_potentialNewAddressPointer(0),
|
||||
m_erasePage(k_flashMemorySectorsCount+1),
|
||||
m_largeBuffer{0},
|
||||
m_largeBufferLength(0),
|
||||
m_writeAddress(0),
|
||||
m_bInterfaceAlternateSetting(bInterfaceAlternateSetting)
|
||||
{
|
||||
}
|
||||
void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
|
||||
void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
|
||||
|
||||
protected:
|
||||
void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override {
|
||||
assert(interfaceAlternativeIndex == m_bInterfaceAlternateSetting);
|
||||
}
|
||||
uint8_t getActiveInterfaceAlternative() override {
|
||||
return m_bInterfaceAlternateSetting;
|
||||
}
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
|
||||
private:
|
||||
// DFU Request Codes
|
||||
enum class DFURequest {
|
||||
Detach = 0,
|
||||
Download = 1,
|
||||
Upload = 2,
|
||||
GetStatus = 3,
|
||||
ClearStatus = 4,
|
||||
GetState = 5,
|
||||
Abort = 6
|
||||
};
|
||||
|
||||
// DFU Download Commmand Codes
|
||||
enum class DFUDownloadCommand {
|
||||
GetCommand = 0x00,
|
||||
SetAddressPointer = 0x21,
|
||||
Erase = 0x41,
|
||||
ReadUnprotect = 0x92
|
||||
};
|
||||
|
||||
enum class Status : uint8_t {
|
||||
OK = 0x00,
|
||||
errTARGET = 0x01,
|
||||
errFILE = 0x02,
|
||||
errWRITE = 0x03,
|
||||
errERASE = 0x04,
|
||||
errCHECK_ERASED = 0x05,
|
||||
errPROG = 0x06,
|
||||
errVERIFY = 0x07,
|
||||
errADDRESS = 0x08,
|
||||
errNOTDONE = 0x09,
|
||||
errFIRMWARE = 0x0A,
|
||||
errVENDOR = 0x0B,
|
||||
errUSBR = 0x0C,
|
||||
errPOR = 0x0D,
|
||||
errUNKNOWN = 0x0E,
|
||||
errSTALLEDPKT = 0x0F
|
||||
};
|
||||
|
||||
enum class State : uint8_t {
|
||||
appIDLE = 0,
|
||||
appDETACH = 1,
|
||||
dfuIDLE = 2,
|
||||
dfuDNLOADSYNC = 3,
|
||||
dfuDNBUSY = 4,
|
||||
dfuDNLOADIDLE = 5,
|
||||
dfuMANIFESTSYNC = 6,
|
||||
dfuMANIFEST = 7,
|
||||
dfuMANIFESTWAITRESET = 8,
|
||||
dfuUPLOADIDLE = 9,
|
||||
dfuERROR = 10
|
||||
};
|
||||
|
||||
class StatusData : public Streamable {
|
||||
public:
|
||||
StatusData(Status status, State state, uint32_t pollTimeout = 1) :
|
||||
/* We put a default pollTimeout value of 1ms: if the device is busy, the
|
||||
* host has to wait 1ms before sending a getStatus Request. */
|
||||
m_bStatus((uint8_t)status),
|
||||
m_bwPollTimeout{uint8_t((pollTimeout>>16) & 0xFF), uint8_t((pollTimeout>>8) & 0xFF), uint8_t(pollTimeout & 0xFF)},
|
||||
m_bState((uint8_t)state),
|
||||
m_iString(0)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
uint8_t m_bStatus; // Status resulting from the execution of the most recent request
|
||||
uint8_t m_bwPollTimeout[3]; // m_bwPollTimeout is 24 bits
|
||||
uint8_t m_bState; // State of the device immediately following transmission of this response
|
||||
uint8_t m_iString;
|
||||
};
|
||||
|
||||
class StateData : public Streamable {
|
||||
public:
|
||||
StateData(State state) : m_bState((uint8_t)state) {}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
uint8_t m_bState; // Current state of the device
|
||||
};
|
||||
|
||||
constexpr static uint8_t k_flashMemorySectorsCount = 12;
|
||||
|
||||
/* The Flash and SRAM addresses are in flash.ld. However, dfu_interface is
|
||||
* linked with dfu.ld, so we cannot access the values. */
|
||||
constexpr static uint32_t k_flashStartAddress = 0x08000000;
|
||||
constexpr static uint32_t k_flashEndAddress = 0x08100000;
|
||||
constexpr static uint32_t k_sramStartAddress = 0x20000000;
|
||||
constexpr static uint32_t k_sramEndAddress = 0x2003E800;
|
||||
|
||||
// Download and upload
|
||||
bool processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength);
|
||||
bool processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
// Address pointer
|
||||
void setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength);
|
||||
void changeAddressPointerIfNeeded();
|
||||
// Access memory
|
||||
void eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength);
|
||||
void eraseMemoryIfNeeded();
|
||||
void writeOnMemory();
|
||||
void unlockFlashMemory();
|
||||
void lockFlashMemory();
|
||||
// Status
|
||||
bool getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
// State
|
||||
bool getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize);
|
||||
// Abort
|
||||
bool dfuAbort(uint16_t * transferBufferLength);
|
||||
// Leave DFU
|
||||
void leaveDFUAndReset();
|
||||
|
||||
Device * m_device;
|
||||
Status m_status;
|
||||
State m_state;
|
||||
uint32_t m_addressPointer;
|
||||
uint32_t m_potentialNewAddressPointer;
|
||||
uint32_t m_erasePage;
|
||||
uint8_t m_largeBuffer[Endpoint0::MaxTransferSize];
|
||||
uint16_t m_largeBufferLength;
|
||||
uint32_t m_writeAddress;
|
||||
uint8_t m_bInterfaceAlternateSetting;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
79
ion/src/device/usb/dfu_relocated.cpp
Normal file
79
ion/src/device/usb/dfu_relocated.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <ion/usb.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../device.h"
|
||||
|
||||
extern char _stack_end;
|
||||
extern char _dfu_bootloader_flash_start;
|
||||
extern char _dfu_bootloader_flash_end;
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
typedef bool (*PollFunctionPointer)(bool exitWithKeyboard);
|
||||
|
||||
void DFU() {
|
||||
|
||||
/* DFU transfers can serve two purposes:
|
||||
* - Transfering RAM data between the machine and a host, e.g. Python scripts
|
||||
* - Upgrading the flash memory to perform a software update
|
||||
*
|
||||
* The second case raises a huge issue: code cannot be executed from memory
|
||||
* that is being modified. We're solving this issue by copying the DFU code in
|
||||
* RAM.
|
||||
*
|
||||
* The new DFU address in RAM needs to be temporarily overwriteable when the
|
||||
* program is being run. Epsilon has a large stack to allow deeply recursive
|
||||
* code to run, but when doing DFU transfers it is safe to assume we will need
|
||||
* very little stack space. We're therefore using the topmost 8K of the stack
|
||||
* reserved by Epsilon. */
|
||||
|
||||
/* 1- The stack being in reverse order, the end of the stack will be the
|
||||
* beginning of the DFU bootloader copied in RAM. */
|
||||
|
||||
size_t dfu_bootloader_size = &_dfu_bootloader_flash_end - &_dfu_bootloader_flash_start;
|
||||
char * dfu_bootloader_ram_start = reinterpret_cast<char *>(&_stack_end);
|
||||
assert(&_stack_end == (void *)(0x20000000 + 256*1024 - 32*1024));
|
||||
|
||||
/* 2- Verify there is enough free space on the stack to copy the DFU code. */
|
||||
|
||||
char foo;
|
||||
char * stackPointer = &foo;
|
||||
if (dfu_bootloader_ram_start + dfu_bootloader_size > stackPointer) {
|
||||
// There is not enough room on the stack to copy the DFU bootloader.
|
||||
return;
|
||||
}
|
||||
|
||||
/* 3- Copy the DFU bootloader from Flash to RAM. */
|
||||
|
||||
memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size);
|
||||
|
||||
/* 4- Jump to DFU bootloader code. We made sure in the linker script that the
|
||||
* first function we want to call is at the beginning of the DFU code. */
|
||||
|
||||
PollFunctionPointer dfu_bootloader_entry = reinterpret_cast<PollFunctionPointer>(dfu_bootloader_ram_start);
|
||||
|
||||
/* To have the right debug symbols for the reallocated code, break here and:
|
||||
* - Get the address of the new .text section
|
||||
* In a terminal: arm-none-eabi-readelf -a ion/src/device/usb/dfu.elf
|
||||
* - Delete the current symbol table
|
||||
* symbol-file
|
||||
* - Add the new symbol table, with the address of the new .text section
|
||||
* add-symbol-file ion/src/device/usb/dfu.elf 0x20038000
|
||||
*/
|
||||
|
||||
if (dfu_bootloader_entry(true)) {
|
||||
/* We don't perform a core reset because at this point in time the USB cable
|
||||
* is most likely plugged in. Doing a full core reset would be the clean
|
||||
* thing to do but would therefore result in the device entering the ROMed
|
||||
* DFU bootloader, which we want to avoid. By performing a jump-reset, we
|
||||
* will enter the newly flashed firmware. */
|
||||
Ion::Device::jumpReset();
|
||||
}
|
||||
|
||||
/* 5- That's all. The DFU bootloader on the stack is now dead code that will
|
||||
* be overwritten when the stack grows. */
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
14
ion/src/device/usb/dfu_xip.cpp
Normal file
14
ion/src/device/usb/dfu_xip.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "calculator.h"
|
||||
#include "../device.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
void DFU() {
|
||||
if (Ion::USB::Device::Calculator::PollAndReset(true)) {
|
||||
Ion::Device::jumpReset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
344
ion/src/device/usb/endpoint0.cpp
Normal file
344
ion/src/device/usb/endpoint0.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
#include "endpoint0.h"
|
||||
#include "device.h"
|
||||
#include "interface.h"
|
||||
#include "request_recipient.h"
|
||||
#include "../regs/regs.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void Endpoint0::setup() {
|
||||
// Setup the IN direction
|
||||
|
||||
// Reset the device IN endpoint 0 transfer size register
|
||||
class OTG::DIEPTSIZ0 dieptsiz0(0);
|
||||
/* Transfer size. The core interrupts the application only after it has
|
||||
* exhausted the transfer size amount of data. The transfer size is set to the
|
||||
* maximum packet size, to be interrupted at the end of each packet. */
|
||||
dieptsiz0.setXFRSIZ(k_maxPacketSize);
|
||||
OTG.DIEPTSIZ0()->set(dieptsiz0);
|
||||
|
||||
// Reset the device IN endpoint 0 control register
|
||||
class OTG::DIEPCTL0 diepctl0(0); // Reset value
|
||||
// Set the maximum packet size
|
||||
diepctl0.setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64);
|
||||
// Set the NAK bit: all IN transactions on endpoint 0 receive a NAK answer
|
||||
diepctl0.setSNAK(true);
|
||||
// Enable the endpoint
|
||||
diepctl0.setEPENA(true);
|
||||
OTG.DIEPCTL0()->set(diepctl0);
|
||||
|
||||
// Setup the OUT direction
|
||||
|
||||
setupOut();
|
||||
// Set the NAK bit
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
// Enable the endpoint
|
||||
enableOut();
|
||||
|
||||
// Setup the Tx FIFO
|
||||
|
||||
/* Tx FIFO depth
|
||||
* We process each packet as soon as it arrives, so we only need
|
||||
* k_maxPacketSize bytes. TX0FD being in terms of 32-bit words, we divide
|
||||
* k_maxPacketSize by 4. */
|
||||
OTG.DIEPTXF0()->setTX0FD(k_maxPacketSize/4);
|
||||
/* Tx FIFO RAM start address. It starts just after the Rx FIFOso the value is
|
||||
* Rx FIFO start address (0) + Rx FIFO depth. the Rx FIFO depth is set in
|
||||
* usb.cpp, but because the code is linked separately, we cannot get it. */
|
||||
OTG.DIEPTXF0()->setTX0FSA(128);
|
||||
}
|
||||
|
||||
void Endpoint0::setupOut() {
|
||||
class OTG::DOEPTSIZ0 doeptsiz0(0);
|
||||
// Number of back-to-back SETUP data packets the endpoint can receive
|
||||
doeptsiz0.setSTUPCNT(1);
|
||||
// Packet count, false if a packet is written into the Rx FIFO
|
||||
doeptsiz0.setPKTCNT(true);
|
||||
/* Transfer size. The core interrupts the application only after it has
|
||||
* exhausted the transfer size amount of data. The transfer size is set to the
|
||||
* maximum packet size, to be interrupted at the end of each packet. */
|
||||
doeptsiz0.setXFRSIZ(64);
|
||||
OTG.DOEPTSIZ0()->set(doeptsiz0);
|
||||
}
|
||||
|
||||
void Endpoint0::setOutNAK(bool nak) {
|
||||
m_forceNAK = nak;
|
||||
/* We need to keep track of the NAK state of the endpoint to use the value
|
||||
* after a setupOut in poll() of device.cpp. */
|
||||
if (nak) {
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
} else {
|
||||
OTG.DOEPCTL0()->setCNAK(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::enableOut() {
|
||||
OTG.DOEPCTL0()->setEPENA(true);
|
||||
}
|
||||
|
||||
void Endpoint0::reset() {
|
||||
flushTxFifo();
|
||||
flushRxFifo();
|
||||
}
|
||||
|
||||
void Endpoint0::readAndDispatchSetupPacket() {
|
||||
setOutNAK(true);
|
||||
|
||||
// Read the 8-bytes Setup packet
|
||||
if (readPacket(m_largeBuffer, sizeof(SetupPacket)) != sizeof(SetupPacket)) {
|
||||
stallTransaction();
|
||||
return;
|
||||
};
|
||||
|
||||
m_request = SetupPacket(m_largeBuffer);
|
||||
uint16_t maxBufferLength = MIN(m_request.wLength(), MaxTransferSize);
|
||||
|
||||
// Forward the request to the request recipient
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::processINpacket() {
|
||||
switch (m_state) {
|
||||
case State::DataIn:
|
||||
sendSomeData();
|
||||
break;
|
||||
case State::LastDataIn:
|
||||
m_state = State::StatusOut;
|
||||
// Prepare to receive the OUT Data[] transaction.
|
||||
setOutNAK(false);
|
||||
break;
|
||||
case State::StatusIn:
|
||||
{
|
||||
m_state = State::Idle;
|
||||
// All the data has been received. Callback the request recipient.
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stallTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::processOUTpacket() {
|
||||
switch (m_state) {
|
||||
case State::DataOut:
|
||||
if (receiveSomeData() < 0) {
|
||||
break;
|
||||
}
|
||||
if ((m_request.wLength() - m_transferBufferLength) <= k_maxPacketSize) {
|
||||
m_state = State::LastDataOut;
|
||||
}
|
||||
break;
|
||||
case State::LastDataOut:
|
||||
if (receiveSomeData() < 0) {
|
||||
break;
|
||||
}
|
||||
// Send the DATA1[] to the host.
|
||||
writePacket(NULL, 0);
|
||||
m_state = State::StatusIn;
|
||||
break;
|
||||
case State::StatusOut:
|
||||
{
|
||||
// Read the DATA1[] sent by the host.
|
||||
readPacket(NULL, 0);
|
||||
m_state = State::Idle;
|
||||
// All the data has been sent. Callback the request recipient.
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stallTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::flushTxFifo() {
|
||||
// Set IN endpoint NAK
|
||||
OTG.DIEPCTL0()->setSNAK(true);
|
||||
|
||||
// Wait for core to respond
|
||||
while (!OTG.DIEPINT(0)->getINEPNE()) {
|
||||
}
|
||||
|
||||
// Get the Tx FIFO number
|
||||
uint32_t fifo = OTG.DIEPCTL0()->getTXFNUM();
|
||||
|
||||
// Wait for AHB idle
|
||||
while (!OTG.GRSTCTL()->getAHBIDL()) {
|
||||
}
|
||||
|
||||
// Flush Tx FIFO
|
||||
OTG.GRSTCTL()->setTXFNUM(fifo);
|
||||
OTG.GRSTCTL()->setTXFFLSH(true);
|
||||
|
||||
// Reset packet counter
|
||||
OTG.DIEPTSIZ0()->set(0);
|
||||
|
||||
// Wait for the flush
|
||||
while (OTG.GRSTCTL()->getTXFFLSH()) {
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::flushRxFifo() {
|
||||
// Set OUT endpoint NAK
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
|
||||
// Wait for AHB idle
|
||||
while (!OTG.GRSTCTL()->getAHBIDL()) {
|
||||
}
|
||||
|
||||
// Flush Rx FIFO
|
||||
OTG.GRSTCTL()->setRXFFLSH(true);
|
||||
|
||||
// Reset packet counter
|
||||
OTG.DOEPTSIZ0()->set(0);
|
||||
|
||||
// Wait for the flush
|
||||
while (OTG.GRSTCTL()->getRXFFLSH()) {
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::discardUnreadData() {
|
||||
for (int i = 0; i < m_receivedPacketSize; i += 4) {
|
||||
OTG.DFIFO0()->get();
|
||||
}
|
||||
m_receivedPacketSize = 0;
|
||||
}
|
||||
|
||||
void Endpoint0::sendSomeData() {
|
||||
if (k_maxPacketSize < m_transferBufferLength) {
|
||||
// More than one packet needs to be sent
|
||||
writePacket(m_largeBuffer + m_bufferOffset, k_maxPacketSize);
|
||||
m_state = State::DataIn;
|
||||
m_bufferOffset += k_maxPacketSize;
|
||||
m_transferBufferLength -= k_maxPacketSize;
|
||||
return;
|
||||
}
|
||||
// Last data packet sent
|
||||
writePacket(m_largeBuffer + m_bufferOffset, m_transferBufferLength);
|
||||
if (m_zeroLengthPacketNeeded) {
|
||||
m_state = State::DataIn;
|
||||
} else {
|
||||
m_state = State::LastDataIn;
|
||||
}
|
||||
m_bufferOffset = 0;
|
||||
m_zeroLengthPacketNeeded = false;
|
||||
m_transferBufferLength = 0;
|
||||
}
|
||||
|
||||
void Endpoint0::clearForOutTransactions(uint16_t wLength) {
|
||||
m_transferBufferLength = 0;
|
||||
m_state = (wLength > k_maxPacketSize) ? State::DataOut : State::LastDataOut;
|
||||
setOutNAK(false);
|
||||
}
|
||||
|
||||
uint16_t Endpoint0::receiveSomeData() {
|
||||
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
|
||||
uint16_t packetSize = MIN(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
|
||||
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);
|
||||
if (sizeOfPacketRead != packetSize) {
|
||||
stallTransaction();
|
||||
return -1;
|
||||
}
|
||||
m_transferBufferLength += packetSize;
|
||||
return packetSize;
|
||||
}
|
||||
|
||||
uint16_t Endpoint0::readPacket(void * buffer, uint16_t length) {
|
||||
uint32_t * buffer32 = (uint32_t *) buffer;
|
||||
uint16_t buffer32Length = MIN(length, m_receivedPacketSize);
|
||||
|
||||
int i;
|
||||
// The RX FIFO is read 4 bytes by 4 bytes
|
||||
for (i = buffer32Length; i >= 4; i -= 4) {
|
||||
*buffer32++ = OTG.DFIFO0()->get();
|
||||
m_receivedPacketSize -= 4;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
/* If there are remaining bytes that should be read, read the next 4 bytes
|
||||
* and copy only the wanted bytes. */
|
||||
uint32_t extraData = OTG.DFIFO0()->get();
|
||||
memcpy(buffer32, &extraData, i);
|
||||
if (m_receivedPacketSize < 4) {
|
||||
m_receivedPacketSize = 0;
|
||||
} else {
|
||||
m_receivedPacketSize -= 4;
|
||||
}
|
||||
}
|
||||
return buffer32Length;
|
||||
}
|
||||
|
||||
uint16_t Endpoint0::writePacket(const void * buffer, uint16_t length) {
|
||||
const uint32_t * buffer32 = (uint32_t *) buffer;
|
||||
|
||||
// Return if there is already a packet waiting to be read in the TX FIFO
|
||||
if (OTG.DIEPTSIZ0()->getPKTCNT()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enable transmission
|
||||
|
||||
class OTG::DIEPTSIZ0 dieptsiz0(0);
|
||||
// Indicate that the Transfer Size is one packet
|
||||
dieptsiz0.setPKTCNT(1);
|
||||
// Indicate the length of the Transfer Size
|
||||
dieptsiz0.setXFRSIZ(length);
|
||||
OTG.DIEPTSIZ0()->set(dieptsiz0);
|
||||
// Enable the endpoint
|
||||
OTG.DIEPCTL0()->setEPENA(true);
|
||||
// Clear the NAK bit
|
||||
OTG.DIEPCTL0()->setCNAK(true);
|
||||
|
||||
// Copy the buffer to the TX FIFO by writing data 32bits by 32 bits.
|
||||
for (int i = length; i > 0; i -= 4) {
|
||||
OTG.DFIFO0()->set(*buffer32++);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void Endpoint0::stallTransaction() {
|
||||
OTG.DIEPCTL0()->setSTALL(true);
|
||||
m_state = State::Idle;
|
||||
}
|
||||
|
||||
void Endpoint0::computeZeroLengthPacketNeeded() {
|
||||
if (m_transferBufferLength
|
||||
&& m_transferBufferLength < m_request.wLength()
|
||||
&& m_transferBufferLength % k_maxPacketSize == 0)
|
||||
{
|
||||
m_zeroLengthPacketNeeded = true;
|
||||
return;
|
||||
}
|
||||
m_zeroLengthPacketNeeded = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
80
ion/src/device/usb/endpoint0.h
Normal file
80
ion/src/device/usb/endpoint0.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef ION_DEVICE_USB_ENDPOINT0_H
|
||||
#define ION_DEVICE_USB_ENDPOINT0_H
|
||||
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class RequestRecipient;
|
||||
|
||||
class Endpoint0 {
|
||||
public:
|
||||
enum class State {
|
||||
Idle,
|
||||
Stalled,
|
||||
DataIn,
|
||||
LastDataIn,
|
||||
StatusIn,
|
||||
DataOut,
|
||||
LastDataOut,
|
||||
StatusOut,
|
||||
};
|
||||
|
||||
constexpr static int k_maxPacketSize = 64;
|
||||
constexpr static int MaxTransferSize = 2048;
|
||||
|
||||
//constexpr Endpoint0(RequestRecipient * device, RequestRecipient * interface) :
|
||||
Endpoint0(RequestRecipient * device, RequestRecipient * interface) :
|
||||
m_forceNAK(false),
|
||||
m_bufferOffset(0),
|
||||
m_transferBufferLength(0),
|
||||
m_receivedPacketSize(0),
|
||||
m_zeroLengthPacketNeeded(false),
|
||||
m_request(),
|
||||
m_requestRecipients{device, interface},
|
||||
m_state(State::Idle),
|
||||
m_largeBuffer{0}
|
||||
{
|
||||
}
|
||||
void setup();
|
||||
void setupOut();
|
||||
void setOutNAK(bool nak);
|
||||
void enableOut();
|
||||
void reset();
|
||||
bool NAKForced() const { return m_forceNAK; }
|
||||
void readAndDispatchSetupPacket();
|
||||
void processINpacket();
|
||||
void processOUTpacket();
|
||||
void flushTxFifo();
|
||||
void flushRxFifo();
|
||||
void setReceivedPacketSize(uint16_t size) { m_receivedPacketSize = size; }
|
||||
void discardUnreadData();
|
||||
void stallTransaction();
|
||||
void computeZeroLengthPacketNeeded();
|
||||
void setState(State state) { m_state = state; }
|
||||
void sendSomeData(); // Writes the next data packet and updates the state.
|
||||
void clearForOutTransactions(uint16_t wLength);
|
||||
|
||||
private:
|
||||
uint16_t receiveSomeData();
|
||||
uint16_t readPacket(void * buffer, uint16_t length);
|
||||
uint16_t writePacket(const void * buffer, uint16_t length);
|
||||
|
||||
bool m_forceNAK;
|
||||
int m_bufferOffset; // When sending large data stored in the buffer, the offset keeps tracks of which data packet should be sent next.
|
||||
uint16_t m_transferBufferLength;
|
||||
uint16_t m_receivedPacketSize;
|
||||
bool m_zeroLengthPacketNeeded;
|
||||
SetupPacket m_request;
|
||||
RequestRecipient * m_requestRecipients[2];
|
||||
State m_state;
|
||||
uint8_t m_largeBuffer[MaxTransferSize];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ion/src/device/usb/flasher.cpp
Normal file
13
ion/src/device/usb/flasher.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "../regs/regs.h"
|
||||
#include "../usb/calculator.h"
|
||||
#include <ion.h>
|
||||
|
||||
void ion_main(int argc, char * argv[]) {
|
||||
Ion::Display::pushRectUniform(KDRect(0,0,Ion::Display::Width,Ion::Display::Height), KDColor::RGB24(0xFFFF00));
|
||||
while (true) {
|
||||
Ion::USB::enable();
|
||||
while (!OTG.GINTSTS()->getENUMDNE()) {
|
||||
}
|
||||
Ion::USB::Device::Calculator::PollAndReset(false);
|
||||
}
|
||||
}
|
||||
68
ion/src/device/usb/flasher.ld
Normal file
68
ion/src/device/usb/flasher.ld
Normal file
@@ -0,0 +1,68 @@
|
||||
/* Create a flash bridge.
|
||||
* Load it at address 0x20004000 and execute it from there
|
||||
* TODO: Explain the 16K offset (ST's DFU ROMed bootloader)
|
||||
*/
|
||||
|
||||
MEMORY {
|
||||
RAM_BUFFER (rw) : ORIGIN = 0x20000000 + 16K, LENGTH = 256K - 16K
|
||||
}
|
||||
|
||||
STACK_SIZE = 4K;
|
||||
|
||||
SECTIONS {
|
||||
.isr_vector_table ORIGIN(RAM_BUFFER) : {
|
||||
KEEP(*(.isr_vector_table))
|
||||
} >RAM_BUFFER
|
||||
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.init_array : {
|
||||
. = ALIGN(4);
|
||||
_init_array_start = .;
|
||||
KEEP (*(.init_array*))
|
||||
_init_array_end = .;
|
||||
} >RAM_BUFFER
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.data : {
|
||||
. = ALIGN(4);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.bss : {
|
||||
. = ALIGN(4);
|
||||
_bss_section_start_ram = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
_bss_section_end_ram = .;
|
||||
} >RAM_BUFFER
|
||||
|
||||
.stack : {
|
||||
. = ALIGN(8);
|
||||
_stack_end = .;
|
||||
. += (STACK_SIZE - 8);
|
||||
. = ALIGN(8);
|
||||
_stack_start = .;
|
||||
} >RAM_BUFFER
|
||||
|
||||
.phony : {
|
||||
/* We won't do dynamic memory allocation */
|
||||
_heap_start = .;
|
||||
_heap_end = .;
|
||||
/* Effectively bypass copying .data to RAM */
|
||||
_data_section_start_flash = .;
|
||||
_data_section_start_ram = .;
|
||||
_data_section_end_ram = .;
|
||||
} >RAM_BUFFER
|
||||
}
|
||||
66
ion/src/device/usb/interface.cpp
Normal file
66
ion/src/device/usb/interface.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "interface.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
static inline uint16_t min(uint16_t x, uint16_t y) { return (x<y ? x : y); }
|
||||
|
||||
bool Interface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (request->requestType() != SetupPacket::RequestType::Standard) {
|
||||
return false;
|
||||
}
|
||||
switch (request->bRequest()) {
|
||||
case (int) Request::GetStatus:
|
||||
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetInterface:
|
||||
return setInterface(request, transferBufferLength);
|
||||
case (int) Request::GetInterface:
|
||||
return getInterface(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::ClearFeature:
|
||||
return clearFeature(transferBufferLength);
|
||||
case (int) Request::SetFeature:
|
||||
return setFeature(transferBufferLength);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Interface::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = min(2, transferBufferMaxLength);
|
||||
for (int i = 0; i<*transferBufferLength; i++) {
|
||||
transferBuffer[i] = 0; // Reserved, must be set to 0
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = min(1, transferBufferMaxLength);;
|
||||
if (*transferBufferLength > 0) {
|
||||
transferBuffer[0] = getActiveInterfaceAlternative();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::setInterface(SetupPacket * request, uint16_t * transferBufferLength) {
|
||||
// We support one interface only
|
||||
setActiveInterfaceAlternative(request->wValue());
|
||||
// There is one interface alternative only, we no need to set it again.
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::clearFeature(uint16_t * transferBufferLength) {
|
||||
// Not needed for now
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::setFeature(uint16_t * transferBufferLength) {
|
||||
// Not needed for now
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
42
ion/src/device/usb/interface.h
Normal file
42
ion/src/device/usb/interface.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef ION_DEVICE_USB_INTERFACE_H
|
||||
#define ION_DEVICE_USB_INTERFACE_H
|
||||
|
||||
#include "endpoint0.h"
|
||||
#include "request_recipient.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class Interface : public RequestRecipient {
|
||||
public:
|
||||
Interface(Endpoint0 * ep0) :
|
||||
RequestRecipient(ep0)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) = 0;
|
||||
virtual uint8_t getActiveInterfaceAlternative() = 0;
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
private:
|
||||
// USB Standard Interface Request Codes
|
||||
enum class Request {
|
||||
GetStatus = 0,
|
||||
ClearFeature = 1,
|
||||
SetFeature = 3,
|
||||
GetInterface = 10,
|
||||
SetInterface = 11,
|
||||
};
|
||||
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool setInterface(SetupPacket * request, uint16_t * transferBufferLength);
|
||||
bool clearFeature(uint16_t * transferBufferLength);
|
||||
bool setFeature(uint16_t * transferBufferLength);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
32
ion/src/device/usb/request_recipient.cpp
Normal file
32
ion/src/device/usb/request_recipient.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "request_recipient.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
bool RequestRecipient::processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (request->followingTransaction() == SetupPacket::TransactionType::InTransaction) {
|
||||
// There is no data stage in this transaction, or the data stage will be in IN direction.
|
||||
if (!processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
}
|
||||
if (*transferBufferLength > 0) {
|
||||
m_ep0->computeZeroLengthPacketNeeded();
|
||||
m_ep0->sendSomeData();
|
||||
} else {
|
||||
m_ep0->sendSomeData();
|
||||
// On seeing a zero length packet, sendSomeData changed endpoint0 state to
|
||||
// LastDataIn, but it should be StatusIn as there was no data stage.
|
||||
m_ep0->setState(Endpoint0::State::StatusIn);
|
||||
}
|
||||
} else {
|
||||
// The following transaction will be an OUT transaction.
|
||||
m_ep0->clearForOutTransactions(request->wLength());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ion/src/device/usb/request_recipient.h
Normal file
29
ion/src/device/usb/request_recipient.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef ION_DEVICE_USB_REQUEST_RECIPIENT_H
|
||||
#define ION_DEVICE_USB_REQUEST_RECIPIENT_H
|
||||
|
||||
#include "endpoint0.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class RequestRecipient {
|
||||
public:
|
||||
RequestRecipient(Endpoint0 * ep0):
|
||||
m_ep0(ep0)
|
||||
{
|
||||
}
|
||||
bool processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
virtual void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
|
||||
virtual void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
|
||||
protected:
|
||||
virtual bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) = 0;
|
||||
Endpoint0 * m_ep0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
38
ion/src/device/usb/setup_packet.cpp
Normal file
38
ion/src/device/usb/setup_packet.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "setup_packet.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
SetupPacket::SetupPacket(void * buffer) {
|
||||
memcpy(this, buffer, sizeof(SetupPacket));
|
||||
}
|
||||
|
||||
SetupPacket::TransactionType SetupPacket::followingTransaction() const {
|
||||
if (m_wLength == 0 || (m_bmRequestType & 0b10000000) != 0) {
|
||||
return TransactionType::InTransaction;
|
||||
} else {
|
||||
return TransactionType::OutTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
SetupPacket::RequestType SetupPacket::requestType() const {
|
||||
return (RequestType) ((m_bmRequestType & 0b01100000) >> 5);
|
||||
}
|
||||
|
||||
SetupPacket::RecipientType SetupPacket::recipientType() const {
|
||||
return (RecipientType) (m_bmRequestType & 0b00001111);
|
||||
}
|
||||
|
||||
int SetupPacket::descriptorIndex() {
|
||||
return m_wValue & 0xFF;
|
||||
}
|
||||
|
||||
int SetupPacket::descriptorType() {
|
||||
return m_wValue >> 8;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
65
ion/src/device/usb/setup_packet.h
Normal file
65
ion/src/device/usb/setup_packet.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef ION_DEVICE_USB_SETUP_PACKET_H
|
||||
#define ION_DEVICE_USB_SETUP_PACKET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class SetupPacket {
|
||||
public:
|
||||
enum class TransactionType {
|
||||
SetupTransaction,
|
||||
InTransaction,
|
||||
OutTransaction
|
||||
};
|
||||
|
||||
enum class RequestType {
|
||||
Standard = 0,
|
||||
Class = 1,
|
||||
Vendor = 2
|
||||
};
|
||||
|
||||
enum class RecipientType {
|
||||
Device = 0,
|
||||
Interface = 1,
|
||||
Endpoint = 2,
|
||||
Other = 3
|
||||
};
|
||||
|
||||
constexpr SetupPacket() :
|
||||
m_bmRequestType(0),
|
||||
m_bRequest(0),
|
||||
m_wValue(0),
|
||||
m_wIndex(0),
|
||||
m_wLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
SetupPacket(void * buffer);
|
||||
TransactionType followingTransaction() const;
|
||||
RequestType requestType() const;
|
||||
RecipientType recipientType() const;
|
||||
int descriptorIndex();
|
||||
int descriptorType();
|
||||
uint8_t bmRequestType() { return m_bmRequestType; }
|
||||
uint8_t bRequest() { return m_bRequest; }
|
||||
uint16_t wValue() { return m_wValue; }
|
||||
uint16_t wIndex() { return m_wIndex; }
|
||||
uint16_t wLength() { return m_wLength; }
|
||||
private:
|
||||
uint8_t m_bmRequestType;
|
||||
uint8_t m_bRequest;
|
||||
uint16_t m_wValue;
|
||||
uint16_t m_wIndex;
|
||||
uint16_t m_wLength;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SetupPacket) == 8, "SetupData must be packed");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
ion/src/device/usb/stack/bos_descriptor.cpp
Normal file
22
ion/src/device/usb/stack/bos_descriptor.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "bos_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void BOSDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_wTotalLength);
|
||||
c->push(m_bNumDeviceCaps);
|
||||
for (uint8_t i = 0; i < m_bNumDeviceCaps; i++) {
|
||||
m_deviceCapabilities[i].push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BOSDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
36
ion/src/device/usb/stack/bos_descriptor.h
Normal file
36
ion/src/device/usb/stack/bos_descriptor.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_BOS_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_BOS_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class BOSDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr BOSDescriptor(
|
||||
uint16_t wTotalLength,
|
||||
uint8_t bNumDeviceCapabilities,
|
||||
const DeviceCapabilityDescriptor * deviceCapabilities) :
|
||||
Descriptor(0x0F),
|
||||
m_wTotalLength(wTotalLength),
|
||||
m_bNumDeviceCaps(bNumDeviceCapabilities),
|
||||
m_deviceCapabilities(deviceCapabilities)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_wTotalLength;
|
||||
uint8_t m_bNumDeviceCaps;
|
||||
const DeviceCapabilityDescriptor * m_deviceCapabilities;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
26
ion/src/device/usb/stack/configuration_descriptor.cpp
Normal file
26
ion/src/device/usb/stack/configuration_descriptor.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "configuration_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void ConfigurationDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_wTotalLength);
|
||||
c->push(m_bNumInterfaces);
|
||||
c->push(m_bConfigurationValue);
|
||||
c->push(m_iConfiguration);
|
||||
c->push(m_bmAttributes);
|
||||
c->push(m_bMaxPower);
|
||||
for (uint8_t i = 0; i < m_bNumInterfaces; i++) {
|
||||
m_interfaces[i].push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ConfigurationDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + 5*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
48
ion/src/device/usb/stack/configuration_descriptor.h
Normal file
48
ion/src/device/usb/stack/configuration_descriptor.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_CONFIGURATION_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_CONFIGURATION_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
#include "interface_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class ConfigurationDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr ConfigurationDescriptor(
|
||||
uint16_t wTotalLength,
|
||||
uint8_t bNumInterfaces,
|
||||
uint8_t bConfigurationValue,
|
||||
uint8_t iConfiguration,
|
||||
uint8_t bmAttributes,
|
||||
uint8_t bMaxPower,
|
||||
const InterfaceDescriptor * interfaces) :
|
||||
Descriptor(0x02),
|
||||
m_wTotalLength(wTotalLength),
|
||||
m_bNumInterfaces(bNumInterfaces),
|
||||
m_bConfigurationValue(bConfigurationValue),
|
||||
m_iConfiguration(iConfiguration),
|
||||
m_bmAttributes(bmAttributes),
|
||||
m_bMaxPower(bMaxPower),
|
||||
m_interfaces(interfaces)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_wTotalLength;
|
||||
uint8_t m_bNumInterfaces;
|
||||
uint8_t m_bConfigurationValue;
|
||||
uint8_t m_iConfiguration;
|
||||
uint8_t m_bmAttributes;
|
||||
uint8_t m_bMaxPower;
|
||||
const InterfaceDescriptor * m_interfaces;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
15
ion/src/device/usb/stack/descriptor.cpp
Normal file
15
ion/src/device/usb/stack/descriptor.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void Descriptor::push(Channel * c) const {
|
||||
c->push(bLength());
|
||||
c->push(m_bDescriptorType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
32
ion/src/device/usb/stack/descriptor.h
Normal file
32
ion/src/device/usb/stack/descriptor.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_DESCRIPTOR_H
|
||||
|
||||
#include "streamable.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class InterfaceDescriptor;
|
||||
|
||||
class Descriptor : public Streamable {
|
||||
friend class InterfaceDescriptor;
|
||||
public:
|
||||
constexpr Descriptor(uint8_t bDescriptorType) :
|
||||
m_bDescriptorType(bDescriptorType)
|
||||
{
|
||||
}
|
||||
uint8_t type() const { return m_bDescriptorType; }
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const { return 2*sizeof(uint8_t); }
|
||||
private:
|
||||
uint8_t m_bDescriptorType;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
18
ion/src/device/usb/stack/device_capability_descriptor.cpp
Normal file
18
ion/src/device/usb/stack/device_capability_descriptor.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void DeviceCapabilityDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bDeviceCapabilityType);
|
||||
}
|
||||
|
||||
uint8_t DeviceCapabilityDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
31
ion/src/device/usb/stack/device_capability_descriptor.h
Normal file
31
ion/src/device/usb/stack/device_capability_descriptor.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class BOSDescriptor;
|
||||
|
||||
class DeviceCapabilityDescriptor : public Descriptor {
|
||||
friend class BOSDescriptor;
|
||||
public:
|
||||
constexpr DeviceCapabilityDescriptor(uint8_t bDeviceCapabilityType) :
|
||||
Descriptor(0x10),
|
||||
m_bDeviceCapabilityType(bDeviceCapabilityType)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bDeviceCapabilityType;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
29
ion/src/device/usb/stack/device_descriptor.cpp
Normal file
29
ion/src/device/usb/stack/device_descriptor.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "device_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void DeviceDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bcdUSB);
|
||||
c->push(m_bDeviceClass);
|
||||
c->push(m_bDeviceSubClass);
|
||||
c->push(m_bDeviceProtocol);
|
||||
c->push(m_bMaxPacketSize0);
|
||||
c->push(m_idVendor);
|
||||
c->push(m_idProduct);
|
||||
c->push(m_bcdDevice);
|
||||
c->push(m_iManufacturer);
|
||||
c->push(m_iProduct);
|
||||
c->push(m_iSerialNumber);
|
||||
c->push(m_bNumConfigurations);
|
||||
}
|
||||
|
||||
uint8_t DeviceDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + 4*sizeof(uint8_t) + 3*sizeof(uint16_t) + 4*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
62
ion/src/device/usb/stack/device_descriptor.h
Normal file
62
ion/src/device/usb/stack/device_descriptor.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_DEVICE_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_DEVICE_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class DeviceDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr DeviceDescriptor(
|
||||
uint16_t bcdUSB,
|
||||
uint8_t bDeviceClass,
|
||||
uint8_t bDeviceSubClass,
|
||||
uint8_t bDeviceProtocol,
|
||||
uint8_t bMaxPacketSize0,
|
||||
uint16_t idVendor,
|
||||
uint16_t idProduct,
|
||||
uint16_t bcdDevice,
|
||||
uint8_t iManufacturer,
|
||||
uint8_t iProduct,
|
||||
uint8_t iSerialNumber,
|
||||
uint8_t bNumConfigurations) :
|
||||
Descriptor(0x01),
|
||||
m_bcdUSB(bcdUSB),
|
||||
m_bDeviceClass(bDeviceClass),
|
||||
m_bDeviceSubClass(bDeviceSubClass),
|
||||
m_bDeviceProtocol(bDeviceProtocol),
|
||||
m_bMaxPacketSize0(bMaxPacketSize0),
|
||||
m_idVendor(idVendor),
|
||||
m_idProduct(idProduct),
|
||||
m_bcdDevice(bcdDevice),
|
||||
m_iManufacturer(iManufacturer),
|
||||
m_iProduct(iProduct),
|
||||
m_iSerialNumber(iSerialNumber),
|
||||
m_bNumConfigurations(bNumConfigurations)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_bcdUSB;
|
||||
uint8_t m_bDeviceClass;
|
||||
uint8_t m_bDeviceSubClass;
|
||||
uint8_t m_bDeviceProtocol;
|
||||
uint8_t m_bMaxPacketSize0;
|
||||
uint16_t m_idVendor;
|
||||
uint16_t m_idProduct;
|
||||
uint16_t m_bcdDevice;
|
||||
uint8_t m_iManufacturer;
|
||||
uint8_t m_iProduct;
|
||||
uint8_t m_iSerialNumber;
|
||||
uint8_t m_bNumConfigurations;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
21
ion/src/device/usb/stack/dfu_functional_descriptor.cpp
Normal file
21
ion/src/device/usb/stack/dfu_functional_descriptor.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "dfu_functional_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void DFUFunctionalDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bmAttributes);
|
||||
c->push(m_wDetachTimeOut);
|
||||
c->push(m_wTransferSize);
|
||||
c->push(m_bcdDFUVersion);
|
||||
}
|
||||
|
||||
uint8_t DFUFunctionalDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint8_t) + 3*sizeof(uint16_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
38
ion/src/device/usb/stack/dfu_functional_descriptor.h
Normal file
38
ion/src/device/usb/stack/dfu_functional_descriptor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class DFUFunctionalDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr DFUFunctionalDescriptor(
|
||||
uint8_t bmAttributes,
|
||||
uint16_t wDetachTimeOut,
|
||||
uint16_t wTransferSize,
|
||||
uint16_t bcdDFUVersion) :
|
||||
Descriptor(0x21),
|
||||
m_bmAttributes(bmAttributes),
|
||||
m_wDetachTimeOut(wDetachTimeOut),
|
||||
m_wTransferSize(wTransferSize),
|
||||
m_bcdDFUVersion(bcdDFUVersion)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bmAttributes;
|
||||
uint16_t m_wDetachTimeOut;
|
||||
uint16_t m_wTransferSize;
|
||||
uint16_t m_bcdDFUVersion;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
61
ion/src/device/usb/stack/extended_compat_id_descriptor.cpp
Normal file
61
ion/src/device/usb/stack/extended_compat_id_descriptor.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "extended_compat_id_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
ExtendedCompatIDDescriptor::ExtendedCompatIDDescriptor(const char * compatibleID) :
|
||||
m_dwLength(sizeof(uint32_t)
|
||||
+ 2*sizeof(uint16_t)
|
||||
+ sizeof(uint8_t)
|
||||
+ k_reserved1Size * sizeof(uint8_t)
|
||||
+ 2*sizeof(uint8_t)
|
||||
+ k_compatibleIDSize * sizeof(uint8_t)
|
||||
+ k_compatibleIDSize * sizeof(uint8_t)
|
||||
+ k_reserved2Size * sizeof(uint8_t)),
|
||||
m_bcdVersion(0x0100), // Microsoft OS Descriptors version 1
|
||||
m_wIndex(Index),
|
||||
m_bCount(1), // We assume one function only.
|
||||
m_reserved1{0, 0, 0, 0, 0, 0, 0},
|
||||
m_bFirstInterfaceNumber(0),
|
||||
m_bReserved(1),
|
||||
m_subCompatibleID{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
m_reserved2{0, 0, 0, 0, 0, 0}
|
||||
{
|
||||
/* Compatible ID has size k_compatibleIDSize, and any unused bytes should be
|
||||
* filled with 0. */
|
||||
size_t compatibleIDSize = strlen(compatibleID);
|
||||
size_t compatibleIDCopySize = k_compatibleIDSize < compatibleIDSize ? k_compatibleIDSize : compatibleIDSize;
|
||||
for (size_t i = 0; i < compatibleIDCopySize; i++) {
|
||||
m_compatibleID[i] = compatibleID[i];
|
||||
}
|
||||
for (size_t i = compatibleIDCopySize; i < k_compatibleIDSize; i++) {
|
||||
m_compatibleID[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtendedCompatIDDescriptor::push(Channel * c) const {
|
||||
c->push(m_dwLength);
|
||||
c->push(m_bcdVersion);
|
||||
c->push(m_wIndex);
|
||||
c->push(m_bCount);
|
||||
for (uint8_t i = 0; i < k_reserved1Size; i++) {
|
||||
c->push(m_reserved1[i]);
|
||||
}
|
||||
c->push(m_bFirstInterfaceNumber);
|
||||
c->push(m_bReserved);
|
||||
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
|
||||
c->push(m_compatibleID[i]);
|
||||
}
|
||||
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
|
||||
c->push(m_subCompatibleID[i]);
|
||||
}
|
||||
for (uint8_t i = 0; i < k_reserved2Size; i++) {
|
||||
c->push(m_reserved2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
43
ion/src/device/usb/stack/extended_compat_id_descriptor.h
Normal file
43
ion/src/device/usb/stack/extended_compat_id_descriptor.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
|
||||
|
||||
#include "streamable.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
/* We use this descriptor to tell the Windows OS that the device should be
|
||||
* treated as a WinUSB device. The Extended Compat ID Descriptor can set
|
||||
* differents compat IDs according to the interface and function of the device,
|
||||
* but we assume there is only one. */
|
||||
|
||||
class ExtendedCompatIDDescriptor : public Streamable {
|
||||
public:
|
||||
static constexpr uint8_t Index = 0x0004;
|
||||
ExtendedCompatIDDescriptor(const char * compatibleID);
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
constexpr static uint8_t k_reserved1Size = 7;
|
||||
constexpr static uint8_t k_compatibleIDSize = 8;
|
||||
constexpr static uint8_t k_reserved2Size = 6;
|
||||
// Header
|
||||
uint32_t m_dwLength; // The length, in bytes, of the complete extended compat ID descriptor
|
||||
uint16_t m_bcdVersion; // The descriptor’s version number, in binary coded decimal format
|
||||
uint16_t m_wIndex; // An index that identifies the particular OS feature descriptor
|
||||
uint8_t m_bCount; // The number of function sections
|
||||
uint8_t m_reserved1[k_reserved1Size];
|
||||
// Function
|
||||
uint8_t m_bFirstInterfaceNumber; // The interface or function number
|
||||
uint8_t m_bReserved;
|
||||
uint8_t m_compatibleID[k_compatibleIDSize];
|
||||
uint8_t m_subCompatibleID[k_compatibleIDSize];
|
||||
uint8_t m_reserved2[k_reserved2Size];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
27
ion/src/device/usb/stack/interface_descriptor.cpp
Normal file
27
ion/src/device/usb/stack/interface_descriptor.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "interface_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void InterfaceDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bInterfaceNumber);
|
||||
c->push(m_bAlternateSetting);
|
||||
c->push(m_bNumEndpoints);
|
||||
c->push(m_bInterfaceClass);
|
||||
c->push(m_bInterfaceSubClass);
|
||||
c->push(m_bInterfaceProtocol);
|
||||
c->push(m_iInterface);
|
||||
if (m_additionalDescriptor != nullptr) {
|
||||
m_additionalDescriptor->push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t InterfaceDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + 7*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
55
ion/src/device/usb/stack/interface_descriptor.h
Normal file
55
ion/src/device/usb/stack/interface_descriptor.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_INTERFACE_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_INTERFACE_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class ConfigurationDescriptor;
|
||||
|
||||
class InterfaceDescriptor : public Descriptor {
|
||||
friend class ConfigurationDescriptor;
|
||||
public:
|
||||
constexpr InterfaceDescriptor(
|
||||
uint8_t bInterfaceNumber,
|
||||
uint8_t bAlternateSetting,
|
||||
uint8_t bNumEndpoints,
|
||||
uint8_t bInterfaceClass,
|
||||
uint8_t bInterfaceSubClass,
|
||||
uint8_t bInterfaceProtocol,
|
||||
uint8_t iInterface,
|
||||
Descriptor * additionalDescriptor) :
|
||||
Descriptor(0x04),
|
||||
m_bInterfaceNumber(bInterfaceNumber),
|
||||
m_bAlternateSetting(bAlternateSetting),
|
||||
m_bNumEndpoints(bNumEndpoints),
|
||||
m_bInterfaceClass(bInterfaceClass),
|
||||
m_bInterfaceSubClass(bInterfaceSubClass),
|
||||
m_bInterfaceProtocol(bInterfaceProtocol),
|
||||
m_iInterface(iInterface),
|
||||
m_additionalDescriptor(additionalDescriptor)
|
||||
/* There could be more than one additional descriptor, but we do not need
|
||||
* this for now. */
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bInterfaceNumber;
|
||||
uint8_t m_bAlternateSetting;
|
||||
uint8_t m_bNumEndpoints;
|
||||
uint8_t m_bInterfaceClass;
|
||||
uint8_t m_bInterfaceSubClass;
|
||||
uint8_t m_bInterfaceProtocol;
|
||||
uint8_t m_iInterface;
|
||||
const Descriptor * m_additionalDescriptor;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
19
ion/src/device/usb/stack/language_id_string_descriptor.cpp
Normal file
19
ion/src/device/usb/stack/language_id_string_descriptor.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "language_id_string_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void LanguageIDStringDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push((uint16_t)(0x0409));
|
||||
}
|
||||
|
||||
uint8_t LanguageIDStringDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
25
ion/src/device/usb/stack/language_id_string_descriptor.h
Normal file
25
ion/src/device/usb/stack/language_id_string_descriptor.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
// For now this LanguageIDStringDescriptor only ever returns American English
|
||||
|
||||
class LanguageIDStringDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr LanguageIDStringDescriptor() :
|
||||
Descriptor(0x03) { }
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
19
ion/src/device/usb/stack/microsoft_os_string_descriptor.cpp
Normal file
19
ion/src/device/usb/stack/microsoft_os_string_descriptor.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "microsoft_os_string_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void MicrosoftOSStringDescriptor::push(Channel * c) const {
|
||||
StringDescriptor::push(c);
|
||||
c->push(m_bMSVendorCode);
|
||||
c->push(m_bPad);
|
||||
}
|
||||
|
||||
uint8_t MicrosoftOSStringDescriptor::bLength() const {
|
||||
return StringDescriptor::bLength() + 2 * sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ion/src/device/usb/stack/microsoft_os_string_descriptor.h
Normal file
30
ion/src/device/usb/stack/microsoft_os_string_descriptor.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef ION_DEVICE_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
|
||||
#define ION_DEVICE_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
|
||||
|
||||
#include "string_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
class MicrosoftOSStringDescriptor : public StringDescriptor {
|
||||
public:
|
||||
constexpr MicrosoftOSStringDescriptor(uint8_t bMSVendorCode) :
|
||||
StringDescriptor("MSFT100"),
|
||||
m_bMSVendorCode(bMSVendorCode),
|
||||
m_bPad(0)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bMSVendorCode;
|
||||
uint8_t m_bPad;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "platform_device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
namespace Device {
|
||||
|
||||
void PlatformDeviceCapabilityDescriptor::push(Channel * c) const {
|
||||
DeviceCapabilityDescriptor::push(c);
|
||||
c->push(m_bReserved);
|
||||
for (int i = 0; i < k_platformCapabilityUUIDSize; i++) {
|
||||
c->push(m_platformCapabilityUUID[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PlatformDeviceCapabilityDescriptor::bLength() const {
|
||||
return DeviceCapabilityDescriptor::bLength() + sizeof(uint8_t) + k_platformCapabilityUUIDSize*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user