Add UF2 bootloader for USB firmware flashing

Vendored and trimmed from microsoft/uf2-samdx1, adapted to the actual
ATSAMD21G17D (128 KiB flash / 16 KiB RAM), fixing the flash/RAM-size
mismatch that the previously commented-out SAM-BA bootloader target
had (it assumed a 256 KiB SAMD21G18A). Builds as a standalone
PlatformIO environment (bootloader/platformio.ini), no python2/make
dependency. Compiles clean, fits in the 8 KiB bootloader region
(7292/8192 bytes). Not yet flashed/verified on real hardware.
This commit is contained in:
cjjohn 2026-08-03 20:56:58 +02:00
parent ce5db617a1
commit de52b57041
143 changed files with 69119 additions and 0 deletions

File diff suppressed because it is too large Load diff

262
bootloader/src/fat.c Normal file
View file

@ -0,0 +1,262 @@
#include "uf2.h"
#define SERIAL0 (*(uint32_t *)0x0080A00C)
#define SERIAL1 (*(uint32_t *)0x0080A040)
#define SERIAL2 (*(uint32_t *)0x0080A044)
#define SERIAL3 (*(uint32_t *)0x0080A048)
typedef struct {
uint8_t JumpInstruction[3];
uint8_t OEMInfo[8];
uint16_t SectorSize;
uint8_t SectorsPerCluster;
uint16_t ReservedSectors;
uint8_t FATCopies;
uint16_t RootDirectoryEntries;
uint16_t TotalSectors16;
uint8_t MediaDescriptor;
uint16_t SectorsPerFAT;
uint16_t SectorsPerTrack;
uint16_t Heads;
uint32_t HiddenSectors;
uint32_t TotalSectors32;
uint8_t PhysicalDriveNum;
uint8_t Reserved;
uint8_t ExtendedBootSig;
uint32_t VolumeSerialNumber;
uint8_t VolumeLabel[11];
uint8_t FilesystemIdentifier[8];
} __attribute__((packed)) FAT_BootBlock;
typedef struct {
char name[8];
char ext[3];
uint8_t attrs;
uint8_t reserved;
uint8_t createTimeFine;
uint16_t createTime;
uint16_t createDate;
uint16_t lastAccessDate;
uint16_t highStartCluster;
uint16_t updateTime;
uint16_t updateDate;
uint16_t startCluster;
uint32_t size;
} __attribute__((packed)) DirEntry;
STATIC_ASSERT(sizeof(DirEntry) == 32);
struct TextFile {
const char name[11];
const char *content;
};
#define STR0(x) #x
#define STR(x) STR0(x)
const char infoUf2File[] = //
"UF2 Bootloader " UF2_VERSION "\r\n"
"Model: " PRODUCT_NAME "\r\n"
"Board-ID: " BOARD_ID "\r\n";
#if USE_FAT
#if USE_INDEX_HTM
const char indexFile[] = //
"<!doctype html>\n"
"<html>"
"<body>"
"<script>\n"
"location.replace(\"" INDEX_URL "\");\n"
"</script>"
"</body>"
"</html>\n";
#endif
// WARNING -- code presumes only one NULL .content for .UF2 file
// and requires it be the last element of the array
static const struct TextFile info[] = {
{.name = "INFO_UF2TXT", .content = infoUf2File},
#if USE_INDEX_HTM
{.name = "INDEX HTM", .content = indexFile},
#endif
{.name = "CURRENT UF2"},
};
#define NUM_FILES (sizeof(info) / sizeof(info[0]))
#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry
#define UF2_SIZE (FLASH_SIZE * 2)
#define UF2_SECTORS (UF2_SIZE / 512)
#define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector
#define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1)
#endif
#define RESERVED_SECTORS 1
#define ROOT_DIR_SECTORS 4
#define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + 511) / 512)
#define START_FAT0 RESERVED_SECTORS
#define START_FAT1 (START_FAT0 + SECTORS_PER_FAT)
#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
// all directory entries must fit in a single sector
// because otherwise current code overflows buffer
#define DIRENTRIES_PER_SECTOR (512/sizeof(DirEntry))
#if USE_FAT
STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS);
#endif
static const FAT_BootBlock BootBlock = {
.JumpInstruction = {0xeb, 0x3c, 0x90},
.OEMInfo = "UF2 UF2 ",
.SectorSize = 512,
.SectorsPerCluster = 1,
.ReservedSectors = RESERVED_SECTORS,
.FATCopies = 2,
.RootDirectoryEntries = (ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR),
.TotalSectors16 = NUM_FAT_BLOCKS - 2,
.MediaDescriptor = 0xf0, // typical for unpartitioned disks
.SectorsPerFAT = SECTORS_PER_FAT,
.SectorsPerTrack = 1,
.Heads = 1,
.PhysicalDriveNum = 0x00, // to match MediaDescriptor of 0xf0
.ExtendedBootSig = 0x29,
.VolumeSerialNumber = 0x00420042,
.VolumeLabel = VOLUME_LABEL,
.FilesystemIdentifier = "FAT16 ",
};
void padded_memcpy(char *dst, const char *src, int len) {
for (int i = 0; i < len; ++i) {
if (*src)
*dst = *src++;
else
*dst = ' ';
dst++;
}
}
void read_block(uint32_t block_no, uint8_t *data) {
memset(data, 0, 512);
uint32_t sectionIdx = block_no;
if (block_no == 0) { // Requested boot block
memcpy(data, &BootBlock, sizeof(BootBlock));
data[510] = 0x55;
data[511] = 0xaa;
// logval("data[0]", data[0]);
} else if (block_no < START_ROOTDIR) { // Requested FAT table sector
sectionIdx -= START_FAT0;
// logval("sidx", sectionIdx);
if (sectionIdx >= SECTORS_PER_FAT)
sectionIdx -= SECTORS_PER_FAT; // second FAT is same as the first...
#if USE_FAT
if (sectionIdx == 0) {
data[0] = 0xf0; // must match MediaDescriptor
// WARNING -- code presumes only one NULL .content for .UF2 file
// and all non-NULL .content fit in one sector
// and requires it be the last element of the array
for (int i = 1; i < NUM_FILES * 2 + 4; ++i) {
data[i] = 0xff;
}
}
for (int i = 0; i < 256; ++i) { // Generate the FAT chain for the firmware "file"
uint32_t v = sectionIdx * 256 + i;
if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR)
((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1;
}
#else
if (sectionIdx == 0)
memcpy(data, "\xf0\xff\xff\xff", 4);
#endif
}
#if USE_FAT
else if (block_no < START_CLUSTERS) { // Requested sector of the root directory
sectionIdx -= START_ROOTDIR;
if (sectionIdx == 0) {
DirEntry *d = (void *)data;
padded_memcpy(d->name, BootBlock.VolumeLabel, 11);
d->attrs = 0x28;
for (int i = 0; i < NUM_FILES; ++i) {
d++;
const struct TextFile *inf = &info[i];
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
d->startCluster = i + 2;
padded_memcpy(d->name, inf->name, 11);
d->createDate = 0x4d99;
d->updateDate = 0x4d99;
}
}
} else { // Requested sector from file space
sectionIdx -= START_CLUSTERS;
// WARNING -- code presumes all but last file take exactly one sector
if (sectionIdx < NUM_FILES - 1) {
memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content));
} else {
sectionIdx -= NUM_FILES - 1;
uint32_t addr = sectionIdx * 256;
if (addr < FLASH_SIZE) {
UF2_Block *bl = (void *)data;
bl->magicStart0 = UF2_MAGIC_START0;
bl->magicStart1 = UF2_MAGIC_START1;
bl->magicEnd = UF2_MAGIC_END;
bl->blockNo = sectionIdx;
bl->numBlocks = FLASH_SIZE / 256;
bl->targetAddr = addr;
bl->payloadSize = 256;
bl->flags |= UF2_FLAG_FAMILYID_PRESENT;
bl->familyID = UF2_FAMILY;
memcpy(bl->data, (void *)addr, bl->payloadSize);
}
}
}
#endif
}
void write_block(uint32_t block_no, uint8_t *data, bool quiet, WriteState *state) {
UF2_Block *bl = (void *)data;
if (!is_uf2_block(bl) || !UF2_IS_MY_FAMILY(bl)) {
return;
}
if ((bl->flags & UF2_FLAG_NOFLASH) || bl->payloadSize != 256 || (bl->targetAddr & 0xff) ||
bl->targetAddr < APP_START_ADDRESS || bl->targetAddr >= FLASH_SIZE) {
#if USE_DBG_MSC
if (!quiet)
logval("invalid target addr", bl->targetAddr);
#endif
// this happens when we're trying to re-flash CURRENT.UF2 file previously
// copied from a device; we still want to count these blocks to reset properly
} else {
// logval("write block at", bl->targetAddr);
flash_write_row((void *)bl->targetAddr, (void *)bl->data);
}
if (state && bl->numBlocks) {
if (state->numBlocks != bl->numBlocks) {
if (bl->numBlocks >= MAX_BLOCKS || state->numBlocks)
state->numBlocks = 0xffffffff;
else
state->numBlocks = bl->numBlocks;
}
if (bl->blockNo < MAX_BLOCKS) {
uint8_t mask = 1 << (bl->blockNo % 8);
uint32_t pos = bl->blockNo / 8;
if (!(state->writtenMask[pos] & mask)) {
// logval("incr", state->numWritten);
state->writtenMask[pos] |= mask;
state->numWritten++;
}
if (state->numWritten >= state->numBlocks) {
// wait a little bit before resetting, to avoid Windows transmit error
// https://github.com/Microsoft/uf2-samd21/issues/11
if (!quiet)
resetHorizon = timerHigh + 30;
// resetIntoApp();
}
}
} else {
if (!quiet)
resetHorizon = timerHigh + 300;
}
}

View file

@ -0,0 +1,115 @@
#include "uf2.h"
// this actually generates less code than a function
#define wait_ready() \
while (NVMCTRL->INTFLAG.bit.READY == 0) \
;
void flash_erase_row(uint32_t *dst) {
wait_ready();
NVMCTRL->STATUS.reg = NVMCTRL_STATUS_MASK;
// Execute "ER" Erase Row
NVMCTRL->ADDR.reg = (uint32_t)dst / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
wait_ready();
}
void flash_erase_to_end(uint32_t *start_address) {
// Note: the flash memory is erased in ROWS, that is in
// block of 4 pages.
// Even if the starting address is the last byte
// of a ROW the entire
// ROW is erased anyway.
uint32_t dst_addr = (uint32_t) start_address; // starting address
while (dst_addr < FLASH_SIZE) {
flash_erase_row((void *)dst_addr);
dst_addr += FLASH_ROW_SIZE;
}
}
void copy_words(uint32_t *dst, uint32_t *src, uint32_t n_words) {
while (n_words--)
*dst++ = *src++;
}
void flash_write_words(uint32_t *dst, uint32_t *src, uint32_t n_words) {
// Set automatic page write
NVMCTRL->CTRLB.bit.MANW = 0;
while (n_words > 0) {
uint32_t len = (FLASH_PAGE_SIZE >> 2) < n_words ? (FLASH_PAGE_SIZE >> 2) : n_words;
n_words -= len;
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
wait_ready();
// make sure there are no other memory writes here
// otherwise we get lock-ups
while (len--)
*dst++ = *src++;
// Execute "WP" Write Page
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
wait_ready();
}
}
void flash_write(void) {
uint32_t *src = (void *)0x20006000;
uint32_t *dst = (void *)*src++;
uint32_t n_rows = *src++;
NVMCTRL->CTRLB.bit.MANW = 1;
while (n_rows--) {
wait_ready();
NVMCTRL->STATUS.reg = NVMCTRL_STATUS_MASK;
// Execute "ER" Erase Row
NVMCTRL->ADDR.reg = (uint32_t)dst / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
wait_ready();
// there are 4 pages to a row
for (int i = 0; i < 4; ++i) {
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
wait_ready();
uint32_t len = FLASH_PAGE_SIZE >> 2;
while (len--)
*dst++ = *src++;
// Execute "WP" Write Page
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
wait_ready();
}
}
}
// Skip writing blocks that are identical to the existing block.
// only disable for debugging/timing
#define QUICK_FLASH 1
void flash_write_row(uint32_t *dst, uint32_t *src) {
#if QUICK_FLASH
bool src_different = false;
for (int i = 0; i < FLASH_ROW_SIZE / 4; ++i) {
if (src[i] != dst[i]) {
src_different = true;
break;
}
}
if (!src_different) {
return;
}
#endif
flash_erase_row(dst);
flash_write_words(dst, src, FLASH_ROW_SIZE / 4);
}

220
bootloader/src/hid.c Normal file
View file

@ -0,0 +1,220 @@
#include "uf2.h"
#if USE_HID || USE_WEBUSB
typedef struct {
PacketBuffer pbuf;
uint16_t size;
uint8_t serial;
uint8_t ep;
union {
uint8_t buf[FLASH_ROW_SIZE + 64];
uint32_t buf32[(FLASH_ROW_SIZE + 64) / 4];
uint16_t buf16[(FLASH_ROW_SIZE + 64) / 2];
HF2_Command cmd;
HF2_Response resp;
};
} HID_InBuffer;
// Recieve HF2 message
// Does not block. Will store intermediate data in pkt.
// `serial` flag is cleared if we got a command message.
int recv_hf2(HID_InBuffer *pkt) {
if (!USB_ReadCore(NULL, 64, pkt->ep, &pkt->pbuf))
return 0; // no data
// logval("rhf2", pkt->pbuf.size);
assert(pkt->pbuf.ptr == 0 && pkt->pbuf.size > 0);
pkt->pbuf.size = 0;
uint8_t tag = pkt->pbuf.buf[0];
#if !USE_HID_SERIAL
if (tag & 0x80) {
return 0;
}
#endif
// serial packets not allowed when in middle of command packet
assert(pkt->size == 0 || !(tag & HF2_FLAG_SERIAL_OUT));
memcpy(pkt->buf + pkt->size, pkt->pbuf.buf + 1, tag & HF2_SIZE_MASK);
pkt->size += tag & HF2_SIZE_MASK;
assert(pkt->size <= sizeof(pkt->buf));
tag &= HF2_FLAG_MASK;
if (tag != HF2_FLAG_CMDPKT_BODY) {
#if USE_HID_SERIAL
pkt->serial = tag - 0x40;
#endif
int sz = pkt->size;
pkt->size = 0;
return sz;
}
return 0;
}
// Send HF2 message.
// Use command message when flag == HF2_FLAG_CMDPKT_BODY
// Use serial stdout for HF2_FLAG_SERIAL_OUT and stderr for HF2_FLAG_SERIAL_ERR.
void send_hf2(const void *data, int size, int ep, int flag) {
uint8_t buf[64];
const uint8_t *ptr = data;
for (;;) {
int s = 63;
if (size <= 63) {
s = size;
if (flag == HF2_FLAG_CMDPKT_BODY)
flag = HF2_FLAG_CMDPKT_LAST;
}
buf[0] = flag | s;
memcpy(buf + 1, ptr, s);
USB_WriteCore(buf, sizeof(buf), ep, true);
ptr += s;
size -= s;
if (!size)
break;
}
}
void send_hf2_response(HID_InBuffer *pkt, int size) {
logval("sendresp", size);
send_hf2(pkt->buf, 4 + size, pkt->ep, HF2_FLAG_CMDPKT_BODY);
}
static void checksum_pages(HID_InBuffer *pkt, int start, int num) {
for (int i = 0; i < num; ++i) {
uint8_t *data = (uint8_t *)start + i * FLASH_ROW_SIZE;
uint16_t crc = 0;
for (int j = 0; j < FLASH_ROW_SIZE; ++j) {
crc = add_crc(*data++, crc);
}
pkt->resp.data16[i] = crc;
}
send_hf2_response(pkt, num * 2);
}
void process_core(HID_InBuffer *pkt) {
int sz = recv_hf2(pkt);
if (!sz)
return;
uint32_t tmp;
#if USE_HID_SERIAL
if (pkt->serial) {
#if USE_LOGS
if (pkt->buf[0] == 'L') {
send_hf2(logStoreUF2.buffer, logStoreUF2.ptr, pkt->ep, HF2_FLAG_SERIAL_OUT);
} else
#endif
{
send_hf2("OK\n", 3, pkt->ep, HF2_FLAG_SERIAL_ERR);
}
return;
}
#endif
logwrite("HID sz=");
logwritenum(sz);
logval(" CMD", pkt->buf32[0]);
// one has to be careful dealing with these, as they share memory
HF2_Command *cmd = &pkt->cmd;
HF2_Response *resp = &pkt->resp;
uint32_t cmdId = cmd->command_id;
resp->tag = cmd->tag;
resp->status16 = HF2_STATUS_OK;
#define checkDataSize(str, add) assert(sz == 8 + sizeof(cmd->str) + (add))
switch (cmdId) {
case HF2_CMD_INFO:
tmp = strlen(infoUf2File);
memcpy(pkt->resp.data8, infoUf2File, tmp);
send_hf2_response(pkt, tmp);
return;
case HF2_CMD_BININFO:
resp->bininfo.mode = HF2_MODE_BOOTLOADER;
resp->bininfo.flash_page_size = FLASH_ROW_SIZE;
resp->bininfo.flash_num_pages = FLASH_SIZE / FLASH_ROW_SIZE;
resp->bininfo.max_message_size = sizeof(pkt->buf);
resp->bininfo.uf2_family = UF2_FAMILY;
send_hf2_response(pkt, sizeof(resp->bininfo));
return;
case HF2_CMD_RESET_INTO_APP:
resetIntoApp();
break;
case HF2_CMD_RESET_INTO_BOOTLOADER:
resetIntoBootloader();
break;
case HF2_CMD_START_FLASH:
// userspace app should reboot into bootloader on this command; we just ignore it
// userspace can also call hf2_handover() here
break;
case HF2_CMD_WRITE_FLASH_PAGE:
checkDataSize(write_flash_page, FLASH_ROW_SIZE);
// first send ACK and then start writing, while getting the next packet
send_hf2_response(pkt, 0);
if (cmd->write_flash_page.target_addr >= APP_START_ADDRESS) {
flash_write_row((void *)cmd->write_flash_page.target_addr, cmd->write_flash_page.data);
}
return;
#if USE_HID_EXT
case HF2_CMD_WRITE_WORDS:
checkDataSize(write_words, cmd->write_words.num_words << 2);
copy_words((void *)cmd->write_words.target_addr, cmd->write_words.words,
cmd->write_words.num_words);
break;
case HF2_CMD_READ_WORDS:
checkDataSize(read_words, 0);
tmp = cmd->read_words.num_words;
copy_words(resp->data32, (void *)cmd->read_words.target_addr, tmp);
send_hf2_response(pkt, tmp << 2);
return;
#endif
case HF2_CMD_CHKSUM_PAGES:
checkDataSize(chksum_pages, 0);
checksum_pages(pkt, cmd->chksum_pages.target_addr, cmd->chksum_pages.num_pages);
return;
default:
// command not understood
resp->status16 = HF2_STATUS_INVALID_CMD;
break;
}
send_hf2_response(pkt, 0);
}
#if USE_HID
static HID_InBuffer hidbufData;
#endif
#if USE_WEBUSB
static HID_InBuffer webbufData;
#endif
void process_hid() {
#if USE_HID
hidbufData.ep = USB_EP_HID;
process_core(&hidbufData);
#endif
#if USE_WEBUSB
webbufData.ep = USB_EP_WEB;
process_core(&webbufData);
#endif
}
#if USE_HID_HANDOVER
void hidHandoverLoop(int ep) {
handoverPrep();
HID_InBuffer buf = {0};
buf.ep = ep;
while (1) {
process_core(&buf);
}
}
#endif
#endif

118
bootloader/src/images.c Normal file
View file

@ -0,0 +1,118 @@
#include <stdint.h>
// all https://makecode.com/_VrfEKzV4xfvq
// https://makecode.com/_7VxXm3JMPXfM - file
// https://makecode.com/_LuEUCsPEKUbs - download
const uint8_t fileLogo[] = {
32, 32, 71, 140, 201, 151, 1, 2, 146, 1, 2, 146, 63, 2, 151, 9, 153, 9, 153, 9, 146, 1, 9, 146, 3, 9, 146, 7, 9, 137, 205, 72, 140, 206, 36, 139, 207, 18, 138, 206, 36, 139, 205, 72, 149, 7, 9, 146, 3, 9, 146, 1, 9, 153, 9, 153, 9, 153, 9, 148, 63, 2, 146, 1, 2, 146, 1, 2, 146, 201, 191, 191, 191, 174
};
// https://makecode.com/_9b0RcK5yRa12
const uint8_t pendriveLogo[] = {
32, 32, 59, 137, 215, 137, 1, 143, 1, 8, 146, 203, 149, 3, 8, 146, 3, 8, 146, 115, 8, 146, 115, 8, 146, 3, 8, 146, 3, 8, 146, 115, 8, 146, 115, 8, 146, 3, 8, 146, 3, 8, 146, 203, 149, 1, 8, 146, 1, 8, 146, 1, 120, 211, 191, 191, 191, 191, 191, 191, 191, 135
};
// https://makecode.com/_TTqbj705L4mr
const uint8_t arrowLogo[] = {
32, 32, 54, 137, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 151, 201, 146, 211, 142, 209, 144, 207, 146, 205, 148, 203, 150, 201, 152, 199, 154, 31, 154, 7, 154, 1, 191, 191, 191, 175
};
const uint8_t font8[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00,
0x28, 0xfe, 0x28, 0xfe, 0x28, 0x00,
0x4c, 0x92, 0xff, 0x92, 0x64, 0x00,
0x02, 0x65, 0x12, 0x48, 0xa6, 0x40,
0x6c, 0x92, 0x92, 0x6c, 0xa0, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0x82, 0x00, 0x00,
0x00, 0x00, 0x82, 0x7c, 0x00, 0x00,
0x54, 0x38, 0x10, 0x38, 0x54, 0x00,
0x10, 0x10, 0x7c, 0x10, 0x10, 0x00,
0x00, 0x00, 0x90, 0x70, 0x00, 0x00,
0x10, 0x10, 0x10, 0x10, 0x10, 0x00,
0x00, 0x00, 0x60, 0x60, 0x00, 0x00,
0x00, 0x60, 0x10, 0x08, 0x06, 0x00,
0x00, 0x3c, 0x42, 0x42, 0x3c, 0x00,
0x00, 0x44, 0x7e, 0x40, 0x00, 0x00,
0x00, 0x44, 0x62, 0x52, 0x4c, 0x00,
0x00, 0x42, 0x4a, 0x4e, 0x32, 0x00,
0x30, 0x28, 0x24, 0x7e, 0x20, 0x00,
0x00, 0x4e, 0x4a, 0x4a, 0x32, 0x00,
0x00, 0x3c, 0x4a, 0x4a, 0x30, 0x00,
0x00, 0x02, 0x62, 0x12, 0x0e, 0x00,
0x00, 0x34, 0x4a, 0x4a, 0x34, 0x00,
0x00, 0x0c, 0x52, 0x52, 0x3c, 0x00,
0x00, 0x00, 0x6c, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x96, 0x76, 0x00, 0x00,
0x10, 0x28, 0x28, 0x44, 0x44, 0x00,
0x28, 0x28, 0x28, 0x28, 0x28, 0x00,
0x44, 0x44, 0x28, 0x28, 0x10, 0x00,
0x00, 0x02, 0x59, 0x09, 0x06, 0x00,
0x3c, 0x42, 0x5a, 0x56, 0x08, 0x00,
0x78, 0x14, 0x12, 0x14, 0x78, 0x00,
0x7e, 0x4a, 0x4a, 0x4a, 0x34, 0x00,
0x00, 0x3c, 0x42, 0x42, 0x24, 0x00,
0x00, 0x7e, 0x42, 0x42, 0x3c, 0x00,
0x00, 0x7e, 0x4a, 0x4a, 0x42, 0x00,
0x00, 0x7e, 0x0a, 0x0a, 0x02, 0x00,
0x00, 0x3c, 0x42, 0x52, 0x34, 0x00,
0x00, 0x7e, 0x08, 0x08, 0x7e, 0x00,
0x00, 0x42, 0x7e, 0x42, 0x00, 0x00,
0x20, 0x40, 0x42, 0x3e, 0x02, 0x00,
0x00, 0x7e, 0x08, 0x14, 0x62, 0x00,
0x00, 0x7e, 0x40, 0x40, 0x40, 0x00,
0x7e, 0x04, 0x18, 0x04, 0x7e, 0x00,
0x00, 0x7e, 0x04, 0x08, 0x7e, 0x00,
0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00,
0x00, 0x7e, 0x12, 0x12, 0x0c, 0x00,
0x00, 0x3c, 0x52, 0x62, 0xbc, 0x00,
0x00, 0x7e, 0x12, 0x12, 0x6c, 0x00,
0x00, 0x24, 0x4a, 0x52, 0x24, 0x00,
0x02, 0x02, 0x7e, 0x02, 0x02, 0x00,
0x00, 0x3e, 0x40, 0x40, 0x3e, 0x00,
0x00, 0x1e, 0x70, 0x70, 0x1e, 0x00,
0x7e, 0x20, 0x18, 0x20, 0x7e, 0x00,
0x42, 0x24, 0x18, 0x24, 0x42, 0x00,
0x06, 0x08, 0x70, 0x08, 0x06, 0x00,
0x00, 0x62, 0x52, 0x4a, 0x46, 0x00,
0x00, 0x7e, 0x42, 0x42, 0x00, 0x00,
0x00, 0x06, 0x08, 0x10, 0x60, 0x00,
0x00, 0x42, 0x42, 0x7e, 0x00, 0x00,
0x08, 0x04, 0x02, 0x04, 0x08, 0x00,
0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
0x00, 0x00, 0x02, 0x04, 0x00, 0x00,
0x00, 0x30, 0x48, 0x48, 0x78, 0x00,
0x00, 0x7e, 0x48, 0x48, 0x30, 0x00,
0x00, 0x30, 0x48, 0x48, 0x48, 0x00,
0x00, 0x30, 0x48, 0x48, 0x7e, 0x00,
0x00, 0x30, 0x68, 0x58, 0x50, 0x00,
0x00, 0x10, 0x7c, 0x12, 0x04, 0x00,
0x00, 0x18, 0xa4, 0xa4, 0x78, 0x00,
0x00, 0x7e, 0x08, 0x08, 0x70, 0x00,
0x00, 0x48, 0x7a, 0x40, 0x00, 0x00,
0x00, 0x40, 0x84, 0x7d, 0x00, 0x00,
0x00, 0x7e, 0x10, 0x28, 0x40, 0x00,
0x00, 0x42, 0x7e, 0x40, 0x00, 0x00,
0x78, 0x08, 0x30, 0x08, 0x70, 0x00,
0x00, 0x78, 0x08, 0x08, 0x70, 0x00,
0x00, 0x30, 0x48, 0x48, 0x30, 0x00,
0x00, 0xfc, 0x24, 0x24, 0x18, 0x00,
0x00, 0x18, 0x24, 0x24, 0xfc, 0x00,
0x00, 0x78, 0x10, 0x08, 0x10, 0x00,
0x00, 0x50, 0x58, 0x68, 0x28, 0x00,
0x00, 0x08, 0x3e, 0x48, 0x20, 0x00,
0x00, 0x38, 0x40, 0x40, 0x78, 0x00,
0x00, 0x18, 0x60, 0x60, 0x18, 0x00,
0x38, 0x40, 0x30, 0x40, 0x38, 0x00,
0x00, 0x48, 0x30, 0x30, 0x48, 0x00,
0x00, 0x5c, 0xa0, 0xa0, 0x7c, 0x00,
0x00, 0x48, 0x68, 0x58, 0x48, 0x00,
0x00, 0x08, 0x36, 0x41, 0x00, 0x00,
0x00, 0x00, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00, 0x00,
0x00, 0x08, 0x04, 0x08, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View file

@ -0,0 +1,130 @@
#include "uf2.h"
#define SYSCTRL_FUSES_OSC32K_CAL_ADDR (NVMCTRL_OTP4 + 4)
#define SYSCTRL_FUSES_OSC32K_CAL_Pos 6
#define SYSCTRL_FUSES_OSC32K_ADDR SYSCTRL_FUSES_OSC32K_CAL_ADDR
#define SYSCTRL_FUSES_OSC32K_Pos SYSCTRL_FUSES_OSC32K_CAL_Pos
#define SYSCTRL_FUSES_OSC32K_Msk (0x7Fu << SYSCTRL_FUSES_OSC32K_Pos)
volatile bool g_interrupt_enabled = true;
static void gclk_sync(void) {
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
;
}
static void dfll_sync(void) {
while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0)
;
}
#define NVM_SW_CALIB_DFLL48M_COARSE_VAL 58
#define NVM_SW_CALIB_DFLL48M_FINE_VAL 64
void system_init(void) {
NVMCTRL->CTRLB.bit.RWS = 1;
#if defined(CRYSTALLESS)
/* Configure OSC8M as source for GCLK_GEN 2 */
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2); // Read GENERATOR_ID - GCLK_GEN_2
gclk_sync();
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_SRC_OSC8M_Val | GCLK_GENCTRL_GENEN;
gclk_sync();
// Turn on DFLL with USB correction and sync to internal 8 mhz oscillator
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
dfll_sync();
SYSCTRL_DFLLVAL_Type dfllval_conf = {0};
uint32_t coarse =( *((uint32_t *)(NVMCTRL_OTP4)
+ (NVM_SW_CALIB_DFLL48M_COARSE_VAL / 32))
>> (NVM_SW_CALIB_DFLL48M_COARSE_VAL % 32))
& ((1 << 6) - 1);
if (coarse == 0x3f) {
coarse = 0x1f;
}
dfllval_conf.bit.COARSE = coarse;
// TODO(tannewt): Load this from a well known flash location so that it can be
// calibrated during testing.
dfllval_conf.bit.FINE = 0x1ff;
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value
SYSCTRL_DFLLMUL_FSTEP( 10 ) |
48000;
SYSCTRL->DFLLVAL.reg = dfllval_conf.reg;
SYSCTRL->DFLLCTRL.reg = 0;
dfll_sync();
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE |
SYSCTRL_DFLLCTRL_CCDIS |
SYSCTRL_DFLLCTRL_USBCRM | /* USB correction */
SYSCTRL_DFLLCTRL_BPLCKC;
dfll_sync();
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_ENABLE ;
dfll_sync();
GCLK_CLKCTRL_Type clkctrl={0};
uint16_t temp;
GCLK->CLKCTRL.bit.ID = 2; // GCLK_ID - DFLL48M Reference
temp = GCLK->CLKCTRL.reg;
clkctrl.bit.CLKEN = 1;
clkctrl.bit.WRTLOCK = 0;
clkctrl.bit.GEN = GCLK_CLKCTRL_GEN_GCLK0_Val;
GCLK->CLKCTRL.reg = (clkctrl.reg | temp);
#else
SYSCTRL->XOSC32K.reg =
SYSCTRL_XOSC32K_STARTUP(6) | SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K;
SYSCTRL->XOSC32K.bit.ENABLE = 1;
while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY) == 0)
;
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1);
gclk_sync();
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_GENEN;
gclk_sync();
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0) | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_CLKEN;
gclk_sync();
SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0;
dfll_sync();
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(31) | SYSCTRL_DFLLMUL_FSTEP(511) |
SYSCTRL_DFLLMUL_MUL((CPU_FREQUENCY / (32 * 1024)));
dfll_sync();
SYSCTRL->DFLLCTRL.reg |=
SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK | SYSCTRL_DFLLCTRL_QLDIS;
dfll_sync();
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_ENABLE;
while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) == 0 ||
(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF) == 0)
;
dfll_sync();
#endif
// Configure DFLL48M as source for GCLK_GEN 0
GCLK->GENDIV.reg = GCLK_GENDIV_ID(0);
gclk_sync();
// Add GCLK_GENCTRL_OE below to output GCLK0 on the SWCLK pin.
GCLK->GENCTRL.reg =
GCLK_GENCTRL_ID(0) | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN;
gclk_sync();
SysTick_Config(1000);
// Uncomment these two lines to output GCLK0 on the SWCLK pin.
// PORT->Group[0].PINCFG[30].bit.PMUXEN = 1;
// Set the port mux mask for odd processor pin numbers, PA30 = 30 is even number, PMUXE = PMUX Even
// PORT->Group[0].PMUX[30 / 2].reg |= PORT_PMUX_PMUXE_H;
}
void SysTick_Handler(void) { LED_TICK(); }

278
bootloader/src/main.c Normal file
View file

@ -0,0 +1,278 @@
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2011-2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition is met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/**
* --------------------
* SAM-BA Implementation on SAMD21
* --------------------
* Requirements to use SAM-BA :
*
* Supported communication interfaces :
* --------------------
*
* SERCOM5 : RX:PB23 TX:PB22
* Baudrate : 115200 8N1
*
* USB : D-:PA24 D+:PA25
*
* Pins Usage
* --------------------
* The following pins are used by the program :
* PA25 : input/output
* PA24 : input/output
* PB23 : input
* PB22 : output
* PA15 : input
*
* The application board shall avoid driving the PA25,PA24,PB23,PB22 and PA15
* signals
* while the boot program is running (after a POR for example)
*
* Clock system
* --------------------
* CPU clock source (GCLK_GEN_0) - 8MHz internal oscillator (OSC8M)
* SERCOM5 core GCLK source (GCLK_ID_SERCOM5_CORE) - GCLK_GEN_0 (i.e., OSC8M)
* GCLK Generator 1 source (GCLK_GEN_1) - 48MHz DFLL in Clock Recovery mode
* (DFLL48M)
* USB GCLK source (GCLK_ID_USB) - GCLK_GEN_1 (i.e., DFLL in CRM mode)
*
* Memory Mapping
* --------------------
* SAM-BA code will be located at 0x0 and executed before any applicative code.
*
* Applications compiled to be executed along with the bootloader will start at
* 0x2000 (samd21) or 0x4000 (samd51)
* The bootloader doesn't changes the VTOR register, application code is
* taking care of this.
*
*/
#include "uf2.h"
static void check_start_application(void);
static volatile bool main_b_cdc_enable = false;
extern int8_t led_tick_step;
#ifdef SAMD21
#define RESET_CONTROLLER PM
#endif
#ifdef SAMD51
#define RESET_CONTROLLER RSTC
#endif
/**
* \brief Check the application startup condition
*
*/
static void check_start_application(void) {
uint32_t app_start_address;
// Check if there is an IO which will hold us inside the bootloader.
#if defined(HOLD_PIN) && defined(HOLD_STATE)
PORT_PINCFG_Type pincfg = {0};
pincfg.bit.PMUXEN = false;
pincfg.bit.INEN = true;
pincfg.bit.DRVSTR = true;
PINOP(HOLD_PIN, DIRCLR); // Pin is an input
#if defined(HOLD_PIN_PULLUP)
pincfg.bit.PULLEN = true;
PINOP(HOLD_PIN, OUTSET); // Pin is pulled up.
#elif defined(HOLD_PIN_PULLDOWN)
pincfg.bit.PULLEN = true;
PINOP(HOLD_PIN, OUTCLR); // Pin is pulled up.
#endif
PINCFG(HOLD_PIN) = pincfg.reg;
if (PINIP(HOLD_PIN) == HOLD_STATE) {
/* Stay in bootloader */
return;
}
#endif
/* Load the Reset Handler address of the application */
app_start_address = *(uint32_t *)(APP_START_ADDRESS + 4);
/**
* Test reset vector of application @APP_START_ADDRESS+4
* Sanity check on the Reset_Handler address
*/
if (app_start_address < APP_START_ADDRESS || app_start_address > FLASH_SIZE) {
/* Stay in bootloader */
return;
}
#if USE_SINGLE_RESET
if (SINGLE_RESET()) {
if (RESET_CONTROLLER->RCAUSE.bit.POR || *DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) {
// the second tap on reset will go into app
*DBL_TAP_PTR = DBL_TAP_MAGIC_QUICK_BOOT;
// this will be cleared after succesful USB enumeration
// this is around 1.5s
resetHorizon = timerHigh + 50;
return;
}
}
#endif
if (RESET_CONTROLLER->RCAUSE.bit.POR) {
*DBL_TAP_PTR = 0;
} else if (*DBL_TAP_PTR == DBL_TAP_MAGIC) {
*DBL_TAP_PTR = 0;
return; // stay in bootloader
} else {
if (*DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) {
*DBL_TAP_PTR = DBL_TAP_MAGIC;
delay(500);
}
*DBL_TAP_PTR = 0;
}
LED_MSC_OFF();
#if defined(__SAMD21E18A__)
RGBLED_set_color(COLOR_LEAVE);
#endif
/* Rebase the Stack Pointer */
__set_MSP(*(uint32_t *)APP_START_ADDRESS);
/* Rebase the vector table base address */
SCB->VTOR = ((uint32_t)APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);
/* Jump to application Reset Handler in the application */
asm("bx %0" ::"r"(app_start_address));
}
extern char _etext;
extern char _end;
/**
* \brief SAMD21 SAM-BA Main loop.
* \return Unused (ANSI-C compatibility).
*/
int main(void) {
// if VTOR is set, we're not running in bootloader mode; halt
if (SCB->VTOR)
while (1) {
}
#if (USB_VID == 0x239a) && (USB_PID == 0x0013) // Adafruit Metro M0
// Delay a bit so SWD programmer can have time to attach.
delay(15);
#endif
led_init();
logmsg("Start");
assert((uint32_t)&_etext < APP_START_ADDRESS);
// bossac writes at 0x20005000
assert(!USE_MONITOR || (uint32_t)&_end < 0x20005000);
assert(8 << NVMCTRL->PARAM.bit.PSZ == FLASH_PAGE_SIZE);
assert(FLASH_PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP == FLASH_SIZE);
/* Jump in application if condition is satisfied */
check_start_application();
/* We have determined we should stay in the monitor. */
/* System initialization */
system_init();
__DMB();
__enable_irq();
#if USE_UART
/* UART is enabled in all cases */
usart_open();
#endif
logmsg("Before main loop");
usb_init();
// not enumerated yet
RGBLED_set_color(COLOR_START);
led_tick_step = 10;
/* Wait for a complete enum on usb or a '#' char on serial line */
while (1) {
if (USB_Ok()) {
if (!main_b_cdc_enable) {
#if USE_SINGLE_RESET
// this might have been set
resetHorizon = 0;
#endif
RGBLED_set_color(COLOR_USB);
led_tick_step = 1;
#if USE_SCREEN
screen_init();
draw_drag();
#endif
}
main_b_cdc_enable = true;
}
#if USE_MONITOR
// Check if a USB enumeration has succeeded
// And com port was opened
if (main_b_cdc_enable) {
logmsg("entering monitor loop");
// SAM-BA on USB loop
while (1) {
sam_ba_monitor_run();
}
}
#if USE_UART
/* Check if a '#' has been received */
if (!main_b_cdc_enable && usart_sharp_received()) {
RGBLED_set_color(COLOR_UART);
sam_ba_monitor_init(SAM_BA_INTERFACE_USART);
/* SAM-BA on UART loop */
while (1) {
sam_ba_monitor_run();
}
}
#endif
#else // no monitor
if (main_b_cdc_enable) {
process_msc();
}
#endif
if (!main_b_cdc_enable) {
// get more predictable timings before the USB is enumerated
for (int i = 1; i < 256; ++i) {
asm("nop");
}
}
}
}

879
bootloader/src/msc.c Normal file
View file

@ -0,0 +1,879 @@
/**
* \file
*
* \brief USB Device Mass Storage Class (MSC) interface.
*
* Copyright (c) 2009-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
#define DEFINE_CONFIG_DATA 1
#include "uf2.h"
#include "lib/usb_msc/sbc_protocol.h"
#include "lib/usb_msc/spc_protocol.h"
#include "lib/usb_msc/usb_protocol.h"
#include "lib/usb_msc/usb_protocol_msc.h"
#if !USE_DBG_MSC
#undef logmsg
#undef logval
#define logmsg(...) NOOP
#define logval(...) NOOP
#endif
// From sam0/utils/compiler.h in ASF3
#define le32_to_cpu(x) (x)
#define cpu_to_le32(x) (x)
#define CPU_TO_BE32(x) ((uint32_t)__builtin_bswap32((uint32_t)(x)))
#define cpu_to_be16(x) ((uint16_t)(((uint16_t)(x) >> 8) |\
((uint16_t)(x) << 8)))
#define MSB3(u32) (((uint8_t *)&(u32))[0]) //!< Most significant byte of 4th rank of \a u32.
#define MSB2(u32) (((uint8_t *)&(u32))[1]) //!< Most significant byte of 3rd rank of \a u32.
#define MSB1(u32) (((uint8_t *)&(u32))[2]) //!< Most significant byte of 2nd rank of \a u32.
#define MSB0(u32) (((uint8_t *)&(u32))[3]) //!< Most significant byte of 1st rank of \a u32.
#define MSB(u16) (((uint8_t *)&(u16))[1]) //!< Most significant byte of \a u16.
#define LSB(u16) (((uint8_t *)&(u16))[0]) //!< Least significant byte of \a u16.
bool mscReset = false;
void msc_reset(void) {
mscReset = true;
reset_ep(USB_EP_MSC_IN);
reset_ep(USB_EP_MSC_OUT);
}
//! Structure to receive a CBW packet
static struct usb_msc_cbw udi_msc_cbw;
//! Structure to send a CSW packet
static struct usb_msc_csw udi_msc_csw = {.dCSWSignature = cpu_to_le32(USB_CSW_SIGNATURE)};
//! Structure with current SCSI sense data
static struct scsi_request_sense_data udi_msc_sense;
#if USE_MSC_CHECKS
/**
* \brief Stall CBW request
*/
static void udi_msc_cbw_invalid(void);
/**
* \brief Stall CSW request
*/
static void udi_msc_csw_invalid(void);
#endif
/**
* \brief Function to check the CBW length and direction
* Call it after SCSI command decode to check integrity of command
*
* \param alloc_len number of bytes that device want transfer
* \param dir_flag Direction of transfer (USB_CBW_DIRECTION_IN/OUT)
*
* \retval true if the command can be processed
*/
static bool udi_msc_cbw_validate(uint32_t alloc_len, uint8_t dir_flag);
//@}
/**
* \name Routines to process small data packet
*/
//@{
/**
* \brief Sends data on MSC IN endpoint
* Called by SCSI command which must send a data to host followed by a CSW
*
* \param buffer Internal RAM buffer to send
* \param buf_size Size of buffer to send
*/
static void udi_msc_data_send(uint8_t *buffer, uint8_t buf_size);
/**
* \name Routines to process CSW packet
*/
//@{
/**
* \brief Build CSW packet and send it
*
* Called at the end of SCSI command
*/
static void udi_msc_csw_process(void);
/**
* \brief Sends CSW
*
* Called by #udi_msc_csw_process()
* or UDD callback when endpoint halt is cleared
*/
void udi_msc_csw_send(void);
/**
* \name Routines manage sense data
*/
//@{
/**
* \brief Reinitialize sense data.
*/
static void udi_msc_clear_sense(void);
/**
* \brief Update sense data with new value to signal a fail
*
* \param sense_key Sense key
* \param add_sense Additional Sense Code
* \param lba LBA corresponding at error
*/
static void udi_msc_sense_fail(uint8_t sense_key, uint16_t add_sense, uint32_t lba);
/**
* \brief Update sense data with new value to signal success
*/
static void udi_msc_sense_pass(void);
/**
* \brief Update sense data to signal a hardware error on memory
*/
static void udi_msc_sense_fail_hardware(void);
#if USE_MSC_CHECKS
/**
* \brief Update sense data to signal that CDB fields are not valid
*/
static void udi_msc_sense_fail_cdb_invalid(void);
#endif
/**
* \brief Update sense data to signal that command is not supported
*/
static void udi_msc_sense_command_invalid(void);
//@}
/**
* \name Routines manage SCSI Commands
*/
//@{
/**
* \brief Process SPC Request Sense command
* Returns error information about last command
*/
static void udi_msc_spc_requestsense(void);
/**
* \brief Process SPC Inquiry command
* Returns information (name,version) about disk
*/
static void udi_msc_spc_inquiry(void);
/**
* \brief Checks state of disk
*
* \retval true if disk is ready, otherwise false and updates sense data
*/
static bool udi_msc_spc_testunitready_global(void);
/**
* \brief Process test unit ready command
* Returns state of logical unit
*/
static void udi_msc_spc_testunitready(void);
/**
* \brief Process prevent allow medium removal command
*/
static void udi_msc_spc_prevent_allow_medium_removal(void);
/**
* \brief Process mode sense command
*
* \param b_sense10 Sense10 SCSI command, if true
* \param b_sense10 Sense6 SCSI command, if false
*/
static void udi_msc_spc_mode_sense(bool b_sense10);
/**
* \brief Process start stop command
*/
static void udi_msc_sbc_start_stop(void);
/**
* \brief Process read capacity command
*/
static void udi_msc_sbc_read_capacity(void);
/**
* \brief Process read10 or write10 command
*
* \param b_read Read transfer, if true,
* \param b_read Write transfer, if false
*/
static void udi_msc_sbc_trans(bool b_read);
static void udi_msc_read_format_capacity(void);
void udd_ep_set_halt(uint8_t ep) {
stall_ep(ep);
reset_ep(ep);
}
#if USE_MSC_CHECKS
static void udi_msc_cbw_invalid(void) {
logmsg("MSC CBW Invalid; halt");
udd_ep_set_halt(USB_EP_MSC_OUT);
// TODO If stall cleared then re-stall it. Only Setup MSC Reset can clear it
}
static void udi_msc_csw_invalid(void) {
logmsg("MSC CSW Invalid; halt");
udd_ep_set_halt(USB_EP_MSC_IN);
// TODO If stall cleared then re-stall it. Only Setup MSC Reset can clear it
}
#endif
bool try_read_cbw(struct usb_msc_cbw *cbw, uint8_t ep, PacketBuffer *handoverCache) {
if (!USB_ReadCore(NULL, 1, ep, handoverCache))
return false; // no data available
uint32_t nb_received = USB_ReadCore((void *)cbw, sizeof(*cbw), ep, handoverCache);
#if USE_MSC_CHECKS
// Check CBW integrity:
// transfer status/CBW length/CBW signature
if ((sizeof(*cbw) != nb_received) || (cbw->dCBWSignature != cpu_to_le32(USB_CBW_SIGNATURE))) {
if (handoverCache)
resetIntoBootloader();
// (5.2.1) Devices receiving a CBW with an invalid signature should
// stall
// further traffic on the Bulk In pipe, and either stall further traffic
// or accept and discard further traffic on the Bulk Out pipe, until
// reset recovery.
udi_msc_cbw_invalid();
udi_msc_csw_invalid();
return false;
}
// in handover mode we don't care about LUN
if (handoverCache)
return true;
// Check LUN asked
cbw->bCBWLUN &= USB_CBW_LUN_MASK;
if (cbw->bCBWLUN > MAX_LUN) {
// Bad LUN, then stop command process
udi_msc_sense_fail_cdb_invalid();
udi_msc_csw_process();
return false;
}
#else
(void)nb_received;
#endif
return true;
}
void process_msc(void) {
#if USE_HID || USE_WEBUSB
process_hid();
#endif
if (!try_read_cbw(&udi_msc_cbw, USB_EP_MSC_OUT, false))
return; // no data
// Prepare CSW residue field with the size requested
udi_msc_csw.dCSWDataResidue = le32_to_cpu(udi_msc_cbw.dCBWDataTransferLength);
// if (SBC_WRITE10 != udi_msc_cbw.CDB[0])
// logval("MSC CMD", udi_msc_cbw.CDB[0]);
// Decode opcode
switch (udi_msc_cbw.CDB[0]) {
case SPC_REQUEST_SENSE:
udi_msc_spc_requestsense();
break;
case SPC_INQUIRY:
udi_msc_spc_inquiry();
break;
case SPC_MODE_SENSE6:
udi_msc_spc_mode_sense(false);
break;
case SPC_MODE_SENSE10:
udi_msc_spc_mode_sense(true);
break;
case SPC_TEST_UNIT_READY:
udi_msc_spc_testunitready();
break;
case SBC_READ_CAPACITY10:
udi_msc_sbc_read_capacity();
break;
case SBC_START_STOP_UNIT:
udi_msc_sbc_start_stop();
break;
// Accepts request to support plug/plug in case of card reader
case SPC_PREVENT_ALLOW_MEDIUM_REMOVAL:
udi_msc_spc_prevent_allow_medium_removal();
break;
// Accepts request to support full format from Windows
case SBC_VERIFY10:
udi_msc_sense_pass();
udi_msc_csw_process();
break;
case SBC_READ10:
udi_msc_sbc_trans(true);
break;
case SBC_WRITE10:
udi_msc_sbc_trans(false);
break;
case 0x23:
udi_msc_read_format_capacity();
break;
default:
logval("Invalid MSC command", udi_msc_cbw.CDB[0]);
udi_msc_sense_command_invalid();
udi_msc_csw_process();
break;
}
}
static bool udi_msc_cbw_validate(uint32_t alloc_len, uint8_t dir_flag) {
/*
* The following cases should result in a phase error:
* - Case 2: Hn < Di
* - Case 3: Hn < Do
* - Case 7: Hi < Di
* - Case 8: Hi <> Do
* - Case 10: Ho <> Di
* - Case 13: Ho < Do
*/
#if USE_MSC_CHECKS
if (((udi_msc_cbw.bmCBWFlags ^ dir_flag) & USB_CBW_DIRECTION_IN) ||
(udi_msc_csw.dCSWDataResidue < alloc_len)) {
udi_msc_sense_fail_cdb_invalid();
udi_msc_csw_process();
return false;
}
#endif
/*
* The following cases should result in a stall and nonzero
* residue:
* - Case 4: Hi > Dn
* - Case 5: Hi > Di
* - Case 9: Ho > Dn
* - Case 11: Ho > Do
*/
return true;
}
//---------------------------------------------
//------- Routines to process small data packet
static void udi_msc_data_send(uint8_t *buffer, uint8_t buf_size) {
if (USB_Write((void *)buffer, buf_size, USB_EP_MSC_IN) != buf_size) {
// If endpoint not available, then exit process command
udi_msc_sense_fail_hardware();
udi_msc_csw_process();
}
// Update sense data
udi_msc_sense_pass();
// Update CSW
udi_msc_csw.dCSWDataResidue -= buf_size;
udi_msc_csw_process();
}
//---------------------------------------------
//------- Routines to process CSW packet
static void udi_msc_csw_process(void) {
if (0 != udi_msc_csw.dCSWDataResidue) {
logval("left-over residue", udi_msc_csw.dCSWDataResidue);
/*
uint8_t buf[64] = {0};
while (udi_msc_csw.dCSWDataResidue > 0) {
size_t len = min(udi_msc_csw.dCSWDataResidue, 64);
USB_Write((void *)buf, len, USB_EP_MSC_IN);
udi_msc_csw.dCSWDataResidue -= len;
}
*/
/*
// Residue not NULL
// then STALL next request from USB host on corresponding endpoint
if (udi_msc_cbw.bmCBWFlags & USB_CBW_DIRECTION_IN)
udd_ep_set_halt(USB_EP_MSC_IN);
else
udd_ep_set_halt(USB_EP_MSC_OUT);
*/
}
// Prepare and send CSW
udi_msc_csw.dCSWTag = udi_msc_cbw.dCBWTag;
udi_msc_csw.dCSWDataResidue = cpu_to_le32(udi_msc_csw.dCSWDataResidue);
udi_msc_csw_send();
}
void udi_msc_csw_send(void) { USB_Write((void *)&udi_msc_csw, sizeof(udi_msc_csw), USB_EP_MSC_IN); }
//---------------------------------------------
//------- Routines manage sense data
static void udi_msc_clear_sense(void) {
memset((uint8_t *)&udi_msc_sense, 0, sizeof(struct scsi_request_sense_data));
udi_msc_sense.valid_reponse_code = SCSI_SENSE_VALID | SCSI_SENSE_CURRENT;
udi_msc_sense.AddSenseLen = SCSI_SENSE_ADDL_LEN(sizeof(udi_msc_sense));
}
static void udi_msc_sense_fail(uint8_t sense_key, uint16_t add_sense, uint32_t lba) {
logval("MSC sense fail", sense_key);
udi_msc_clear_sense();
udi_msc_csw.bCSWStatus = USB_CSW_STATUS_FAIL;
udi_msc_sense.sense_flag_key = sense_key;
udi_msc_sense.information[0] = lba >> 24;
udi_msc_sense.information[1] = lba >> 16;
udi_msc_sense.information[2] = lba >> 8;
udi_msc_sense.information[3] = lba;
udi_msc_sense.AddSense = add_sense;
}
static void udi_msc_sense_pass(void) {
udi_msc_clear_sense();
udi_msc_csw.bCSWStatus = USB_CSW_STATUS_PASS;
}
static void udi_msc_sense_fail_hardware(void) {
udi_msc_sense_fail(SCSI_SK_HARDWARE_ERROR, SCSI_ASC_NO_ADDITIONAL_SENSE_INFO, 0);
}
#if USE_MSC_CHECKS
static void udi_msc_sense_fail_cdb_invalid(void) {
udi_msc_sense_fail(SCSI_SK_ILLEGAL_REQUEST, SCSI_ASC_INVALID_FIELD_IN_CDB, 0);
}
#endif
static void udi_msc_sense_command_invalid(void) {
udi_msc_sense_fail(SCSI_SK_ILLEGAL_REQUEST, SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, 0);
}
//---------------------------------------------
//------- Routines manage SCSI Commands
static void udi_msc_spc_requestsense(void) {
uint8_t length = udi_msc_cbw.CDB[4];
// Can't send more than sense data length
if (length > sizeof(udi_msc_sense))
length = sizeof(udi_msc_sense);
if (!udi_msc_cbw_validate(length, USB_CBW_DIRECTION_IN))
return;
// Send sense data
udi_msc_data_send((uint8_t *)&udi_msc_sense, length);
}
static void udi_msc_read_format_capacity(void) {
uint8_t buf[12] = {0,
0,
0,
8, // length
(NUM_FAT_BLOCKS >> 24) & 0xFF,
(NUM_FAT_BLOCKS >> 16) & 0xFF,
(NUM_FAT_BLOCKS >> 8) & 0xFF,
(NUM_FAT_BLOCKS >> 0) & 0xFF,
2, // Descriptor Code: Formatted Media
0,
(512 >> 8) & 0xff,
0};
size_t length = 12;
if (udi_msc_csw.dCSWDataResidue > length)
udi_msc_csw.dCSWDataResidue = length;
if (!udi_msc_cbw_validate(length, USB_CBW_DIRECTION_IN))
return;
udi_msc_data_send(buf, length);
}
static void udi_msc_spc_inquiry(void) {
uint8_t length;
__attribute__((__aligned__(4)))
// Constant inquiry data for all LUNs
static struct scsi_inquiry_data udi_msc_inquiry_data = {
.pq_pdt = SCSI_INQ_PQ_CONNECTED | SCSI_INQ_DT_DIR_ACCESS,
.version = 2, // SCSI_INQ_VER_SPC,
.flags1 = SCSI_INQ_RMB,
.flags3 = SCSI_INQ_RSP_SPC2,
.addl_len = 36 - 4, // SCSI_INQ_ADDL_LEN(sizeof(struct scsi_inquiry_data)),
// Linux displays this; Windows shows it in Dev Mgr
.vendor_id = "",
.product_id = "",
.product_rev = {'1', '.', '0', '0'},
};
// we use both product_id and vendor_id fields and hope for the best
padded_memcpy(udi_msc_inquiry_data.vendor_id, PRODUCT_NAME, 8 + 16);
length = udi_msc_cbw.CDB[4];
// Can't send more than inquiry data length
if (length > sizeof(udi_msc_inquiry_data))
length = sizeof(udi_msc_inquiry_data);
if (!udi_msc_cbw_validate(length, USB_CBW_DIRECTION_IN))
return;
/*
if ((0 != (udi_msc_cbw.CDB[1] & (SCSI_INQ_REQ_EVPD | SCSI_INQ_REQ_CMDT))) ||
(0 != udi_msc_cbw.CDB[2])) {
logval("unsupp", udi_msc_cbw.CDB[1]);
// CMDT and EPVD bits are not at 0
// PAGE or OPERATION CODE fields are not empty
// = No standard inquiry asked
udi_msc_sense_fail_cdb_invalid(); // Command is unsupported
udi_msc_csw_process();
return;
}
*/
// logval("Sense Size", length);
// Send inquiry data
udi_msc_data_send((uint8_t *)&udi_msc_inquiry_data, length);
}
static bool udi_msc_spc_testunitready_global(void) { return true; }
static void udi_msc_spc_testunitready(void) {
if (udi_msc_spc_testunitready_global()) {
// LUN ready, then update sense data with status pass
udi_msc_sense_pass();
}
// Send status in CSW packet
udi_msc_csw_process();
}
static void udi_msc_spc_mode_sense(bool b_sense10) {
// Union of all mode sense structures
union sense_6_10 {
struct {
struct scsi_mode_param_header6 header;
struct spc_control_page_info_execpt sense_data;
} s6;
struct {
struct scsi_mode_param_header10 header;
struct spc_control_page_info_execpt sense_data;
} s10;
};
uint8_t data_sense_lgt;
uint8_t mode;
uint8_t request_lgt;
struct spc_control_page_info_execpt *ptr_mode;
__attribute__((__aligned__(4))) static union sense_6_10 sense;
// Clear all fields
memset(&sense, 0, sizeof(sense));
// Initialize process
if (b_sense10) {
request_lgt = udi_msc_cbw.CDB[8];
ptr_mode = &sense.s10.sense_data;
data_sense_lgt = sizeof(struct scsi_mode_param_header10);
} else {
request_lgt = udi_msc_cbw.CDB[4];
ptr_mode = &sense.s6.sense_data;
data_sense_lgt = sizeof(struct scsi_mode_param_header6);
}
// No Block descriptor
// Fill page(s)
mode = udi_msc_cbw.CDB[2] & SCSI_MS_MODE_ALL;
if ((SCSI_MS_MODE_INFEXP == mode) || (SCSI_MS_MODE_ALL == mode)) {
// Informational exceptions control page (from SPC)
ptr_mode->page_code = SCSI_MS_MODE_INFEXP;
ptr_mode->page_length = SPC_MP_INFEXP_PAGE_LENGTH;
ptr_mode->mrie = SPC_MP_INFEXP_MRIE_NO_SENSE;
data_sense_lgt += sizeof(struct spc_control_page_info_execpt);
}
// Can't send more than mode sense data length
if (request_lgt > data_sense_lgt)
request_lgt = data_sense_lgt;
if (!udi_msc_cbw_validate(request_lgt, USB_CBW_DIRECTION_IN))
return;
// Fill mode parameter header length
if (b_sense10) {
sense.s10.header.mode_data_length = cpu_to_be16((data_sense_lgt - 2));
} else {
sense.s6.header.mode_data_length = data_sense_lgt - 1;
}
// Send mode sense data
udi_msc_data_send((uint8_t *)&sense, request_lgt);
}
static void udi_msc_spc_prevent_allow_medium_removal(void) {
#if USE_MSC_CHECKS
uint8_t prevent = udi_msc_cbw.CDB[4];
if (0 == prevent) {
udi_msc_sense_pass();
} else {
udi_msc_sense_fail_cdb_invalid(); // Command is unsupported
}
#else
udi_msc_sense_pass();
#endif
udi_msc_csw_process();
}
static void udi_msc_sbc_start_stop(void) {
#if 0
bool start = 0x1 & udi_msc_cbw.CDB[4];
bool loej = 0x2 & udi_msc_cbw.CDB[4];
if (loej) {
mem_unload(udi_msc_cbw.bCBWLUN, !start);
}
#endif
udi_msc_sense_pass();
udi_msc_csw_process();
}
static void udi_msc_sbc_read_capacity(void) {
__attribute__((__aligned__(4))) static struct sbc_read_capacity10_data udi_msc_capacity;
if (!udi_msc_cbw_validate(sizeof(udi_msc_capacity), USB_CBW_DIRECTION_IN))
return;
udi_msc_capacity.max_lba = NUM_FAT_BLOCKS - 1;
// Format capacity data
udi_msc_capacity.block_len = CPU_TO_BE32(UDI_MSC_BLOCK_SIZE);
udi_msc_capacity.max_lba = CPU_TO_BE32(udi_msc_capacity.max_lba);
// Send the corresponding sense data
udi_msc_data_send((uint8_t *)&udi_msc_capacity, sizeof(udi_msc_capacity));
}
__attribute__((__aligned__(4))) static uint8_t block_buffer[UDI_MSC_BLOCK_SIZE];
static WriteState usbWriteState;
static void udi_msc_sbc_trans(bool b_read) {
uint32_t trans_size;
//! Memory address to execute the command
uint32_t udi_msc_addr;
//! Number of block to transfer
uint16_t udi_msc_nb_block;
// Read/Write command fields (address and number of block)
MSB0(udi_msc_addr) = udi_msc_cbw.CDB[2];
MSB1(udi_msc_addr) = udi_msc_cbw.CDB[3];
MSB2(udi_msc_addr) = udi_msc_cbw.CDB[4];
MSB3(udi_msc_addr) = udi_msc_cbw.CDB[5];
MSB(udi_msc_nb_block) = udi_msc_cbw.CDB[7];
LSB(udi_msc_nb_block) = udi_msc_cbw.CDB[8];
// Compute number of byte to transfer and valid it
trans_size = (uint32_t)udi_msc_nb_block * UDI_MSC_BLOCK_SIZE;
if (!udi_msc_cbw_validate(trans_size, (b_read) ? USB_CBW_DIRECTION_IN : USB_CBW_DIRECTION_OUT))
return;
#if USE_DBG_MSC
logwrite(b_read ? "read @" : "write @");
logwritenum(udi_msc_addr);
logwrite(" sz:");
logwritenum(trans_size);
logwrite("\n");
#endif
for (uint32_t i = 0; i < udi_msc_nb_block; ++i) {
if (!USB_Ok()) {
logmsg("Transfer aborted.");
return;
}
// logval("readblk", i);
if (b_read) {
read_block(udi_msc_addr + i, block_buffer);
USB_Write(block_buffer, UDI_MSC_BLOCK_SIZE, USB_EP_MSC_IN);
} else {
USB_ReadBlocking(block_buffer, UDI_MSC_BLOCK_SIZE, USB_EP_MSC_OUT, 0);
#if 0
check_uf2_handover(block_buffer, udi_msc_nb_block - i - 1, USB_EP_MSC_IN,
USB_EP_MSC_OUT, udi_msc_cbw.dCBWTag);
#endif
write_block(udi_msc_addr + i, block_buffer, false, &usbWriteState);
led_signal();
}
udi_msc_csw.dCSWDataResidue -= UDI_MSC_BLOCK_SIZE;
}
udi_msc_sense_pass();
// Send status of transfer in CSW packet
udi_msc_csw_process();
}
#if USE_MSC_HANDOVER
static void handover_flash(UF2_HandoverArgs *handover, PacketBuffer *handoverCache,
WriteState *state) {
for (uint32_t i = 0; i < handover->blocks_remaining; ++i) {
USB_ReadBlocking(handover->buffer, UDI_MSC_BLOCK_SIZE, handover->ep_out, handoverCache);
write_block(0x1000 + i, handover->buffer, true, state);
}
}
static void process_handover_initial(UF2_HandoverArgs *handover, PacketBuffer *handoverCache,
WriteState *state) {
struct usb_msc_csw csw = {.dCSWTag = handover->cbw_tag,
.dCSWSignature = cpu_to_le32(USB_CSW_SIGNATURE),
.bCSWStatus = USB_CSW_STATUS_PASS,
.dCSWDataResidue = 0};
// write out the block passed from user space
write_block(0xfff, handover->buffer, true, state);
// read-write remaining blocks
handover_flash(handover, handoverCache, state);
// send USB response, as the user space isn't gonna do it
USB_WriteCore((void *)&csw, sizeof(csw), handover->ep_in, true);
}
static void process_handover(UF2_HandoverArgs *handover, PacketBuffer *handoverCache,
WriteState *state) {
struct usb_msc_cbw cbw;
int num = 0;
while (!try_read_cbw(&cbw, handover->ep_out, handoverCache)) {
// TODO is this the right value?
if (num++ > TIMER_STEP * 50) {
resetIntoApp();
}
}
struct usb_msc_csw csw = {.dCSWTag = cbw.dCBWTag,
.dCSWSignature = cpu_to_le32(USB_CSW_SIGNATURE),
.bCSWStatus = USB_CSW_STATUS_PASS,
.dCSWDataResidue = le32_to_cpu(cbw.dCBWDataTransferLength)};
// if (SBC_WRITE10 != udi_msc_cbw.CDB[0])
// logval("MSC CMD", udi_msc_cbw.CDB[0]);
uint16_t udi_msc_nb_block;
switch (cbw.CDB[0]) {
case SPC_TEST_UNIT_READY:
// ready, nothing to do
break;
case SBC_WRITE10:
MSB(udi_msc_nb_block) = cbw.CDB[7];
LSB(udi_msc_nb_block) = cbw.CDB[8];
handover->blocks_remaining = udi_msc_nb_block;
handover_flash(handover, handoverCache, state);
csw.dCSWDataResidue -= UDI_MSC_BLOCK_SIZE * udi_msc_nb_block;
break;
default:
resetIntoBootloader();
break;
}
USB_WriteCore((void *)&csw, sizeof(csw), handover->ep_in, true);
}
void handoverPrep() {
__disable_irq();
__DMB();
USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTENCLR_MASK;
USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_MASK;
SCB->VTOR = 0;
}
static void handover(UF2_HandoverArgs *args) {
handoverPrep();
PacketBuffer cache = {0};
WriteState writeState = {0};
cache.read_job = 2;
// They may have 0x80 bit set
args->ep_in &= 0xf;
args->ep_out &= 0xf;
process_handover_initial(args, &cache, &writeState);
while (1) {
process_handover(args, &cache, &writeState);
}
}
#endif
__attribute__((section(".binfo"))) __attribute__((__used__)) const UF2_BInfo binfo = {
#ifdef HAS_CONFIG_DATA
.config_data = config_data,
#endif
#if USE_MSC_HANDOVER
.handoverMSC = handover,
#endif
#if USE_HID_HANDOVER
.handoverHID = hidHandoverLoop,
#endif
.info_uf2 = infoUf2File,
};

View file

@ -0,0 +1,300 @@
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2011-2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition is met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
#include "uf2.h"
static const char fullVersion[] = "v" SAM_BA_VERSION " [Arduino:XYZ] " __DATE__ " " __TIME__ "\n\r";
/* b_terminal_mode mode (ascii) or hex mode */
#if USE_CDC_TERMINAL
volatile bool b_terminal_mode = false;
#endif
volatile bool b_sam_ba_interface_usart = false;
void sam_ba_monitor_init(uint8_t com_interface) {
#if USE_UART
// Selects the requested interface for future actions
if (com_interface == SAM_BA_INTERFACE_USART) {
b_sam_ba_interface_usart = true;
}
#endif
}
/**
* \brief This function allows data rx by USART
*
* \param *data Data pointer
* \param length Length of the data
*/
void sam_ba_putdata_term(uint8_t *data, uint32_t length) {
#if USE_CDC_TERMINAL
uint8_t temp, buf[12], *data_ascii;
uint32_t i, int_value;
if (b_terminal_mode) {
if (length == 4)
int_value = *(uint32_t *)(void *)data;
else if (length == 2)
int_value = *(uint16_t *)(void *)data;
else
int_value = *(uint8_t *)(void *)data;
data_ascii = buf + 2;
data_ascii += length * 2 - 1;
for (i = 0; i < length * 2; i++) {
temp = (uint8_t)(int_value & 0xf);
if (temp <= 0x9)
*data_ascii = temp | 0x30;
else
*data_ascii = temp + 0x37;
int_value >>= 4;
data_ascii--;
}
buf[0] = '0';
buf[1] = 'x';
buf[length * 2 + 2] = '\n';
buf[length * 2 + 3] = '\r';
cdc_write_buf(buf, length * 2 + 4);
} else
#endif
cdc_write_buf(data, length);
return;
}
volatile uint32_t sp;
void call_applet(uint32_t address) {
uint32_t app_start_address;
/* Save current Stack Pointer */
sp = __get_MSP();
/* Rebase the Stack Pointer */
__set_MSP(*(uint32_t *)address);
/* Load the Reset Handler address of the application */
app_start_address = *(uint32_t *)(address + 4);
/* Jump to application Reset Handler in the application */
asm("blx %0" ::"r"(app_start_address):"r0","r1","r2","r3","lr");
/* Rebase the Stack Pointer */
__set_MSP(sp);
}
uint32_t current_number;
uint32_t i, length;
uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX + 1];
uint8_t j;
uint32_t u32tmp;
// Prints a 32-bit integer in hex.
void put_uint32(uint32_t n) {
char buff[8];
writeNum(buff, n, true);
cdc_write_buf(buff, 8);
}
/**
* \brief This function starts the SAM-BA monitor.
*/
void sam_ba_monitor_run(void) {
ptr_data = NULL;
command = 'z';
// Start waiting some cmd
while (1) {
process_msc();
length = cdc_read_buf(data, SIZEBUFMAX);
data[length] = 0;
if (length) {
logwrite("SERIAL:");
logmsg(data);
led_signal();
}
ptr = data;
for (i = 0; i < length; i++) {
if (*ptr != 0xff) {
if (*ptr == '#') {
#if USE_CDC_TERMINAL
if (b_terminal_mode) {
cdc_write_buf("\n\r", 2);
}
#endif
if (command == 'S') {
// Check if some data are remaining in the "data" buffer
if (length > i) {
// Move current indexes to next avail data
// (currently ptr points to "#")
ptr++;
i++;
// We need to add first the remaining data of the
// current buffer already read from usb
// read a maximum of "current_number" bytes
u32tmp = (length - i) < current_number ? (length - i) : current_number;
memcpy(ptr_data, ptr, u32tmp);
i += u32tmp;
ptr += u32tmp;
j = u32tmp;
}
// update i with the data read from the buffer
i--;
ptr--;
// Do we expect more data ?
if (j < current_number)
cdc_read_buf_xmd(ptr_data, current_number - j);
__asm("nop");
} else if (command == 'R') {
cdc_write_buf_xmd(ptr_data, current_number);
} else if (command == 'O') {
*ptr_data = (char)current_number;
} else if (command == 'H') {
*((uint16_t *)(void *)ptr_data) = (uint16_t)current_number;
} else if (command == 'W') {
// detect BOSSA resetting us
if ((uint32_t)ptr_data == 0xE000ED0C)
RGBLED_set_color(COLOR_LEAVE);
*((int *)(void *)ptr_data) = current_number;
} else if (command == 'o') {
sam_ba_putdata_term(ptr_data, 1);
} else if (command == 'h') {
current_number = *((uint16_t *)(void *)ptr_data);
sam_ba_putdata_term((uint8_t *)&current_number, 2);
} else if (command == 'w') {
current_number = *((uint32_t *)(void *)ptr_data);
sam_ba_putdata_term((uint8_t *)&current_number, 4);
} else if (command == 'G') {
call_applet(current_number);
if (b_sam_ba_interface_usart) {
cdc_write_buf("\x06", 1);
}
} else if (command == 'T') {
#if USE_CDC_TERMINAL
b_terminal_mode = 1;
cdc_write_buf("\n\r", 2);
#endif
} else if (command == 'N') {
#if USE_CDC_TERMINAL
if (b_terminal_mode == 0) {
cdc_write_buf("\n\r", 2);
}
b_terminal_mode = 0;
#endif
} else if (command == 'V') {
cdc_write_buf(fullVersion, sizeof(fullVersion));
} else if (command == 'X') {
// Syntax: X[ADDR]#
// Erase the flash memory starting from ADDR to the end
// of flash.
flash_erase_to_end((uint32_t *) current_number);
// Notify command completed
cdc_write_buf("X\n\r", 3);
} else if (command == 'Y') {
// This command writes the content of a buffer in SRAM
// into flash memory.
// Syntax: Y[ADDR],0#
// Set the starting address of the SRAM buffer.
// Syntax: Y[ROM_ADDR],[SIZE]#
// Write the first SIZE bytes from the SRAM buffer
// (previously set) into
// flash memory starting from address ROM_ADDR
static uint32_t *src_buff_addr = NULL;
if (current_number == 0) {
// Set buffer address
src_buff_addr = (void *)ptr_data;
} else {
flash_write_words((void *)ptr_data, src_buff_addr, current_number / 4);
}
// Notify command completed
cdc_write_buf("Y\n\r", 3);
} else if (command == 'Z') {
// This command calculate CRC for a given area of
// memory.
// It's useful to quickly check if a transfer has been
// done
// successfully.
// Syntax: Z[START_ADDR],[SIZE]#
// Returns: Z[CRC]#
uint8_t *data = (uint8_t *)ptr_data;
uint32_t size = current_number;
uint16_t crc = 0;
uint32_t i = 0;
for (i = 0; i < size; i++)
crc = add_crc(*data++, crc);
// Send response
cdc_write_buf("Z", 1);
put_uint32(crc);
cdc_write_buf("#\n\r", 3);
}
command = 'z';
current_number = 0;
#if USE_CDC_TERMINAL
if (b_terminal_mode) {
cdc_write_buf(">", 1);
}
#endif
} else {
if (('0' <= *ptr) && (*ptr <= '9')) {
current_number = (current_number << 4) | (*ptr - '0');
} else if (('A' <= *ptr) && (*ptr <= 'F')) {
current_number = (current_number << 4) | (*ptr - 'A' + 0xa);
} else if (('a' <= *ptr) && (*ptr <= 'f')) {
current_number = (current_number << 4) | (*ptr - 'a' + 0xa);
} else if (*ptr == ',') {
ptr_data = (uint8_t *)current_number;
current_number = 0;
} else {
command = *ptr;
current_number = 0;
}
}
ptr++;
}
}
}
}

489
bootloader/src/screen.c Normal file
View file

@ -0,0 +1,489 @@
#include "uf2.h"
#if USE_SCREEN
#include <string.h>
#define DISPLAY_WIDTH 160
#define DISPLAY_HEIGHT 128
// Overlap 4x chars by this much.
#define CHAR4_KERNING 2
// Width of a single 4x char, adjusted by kerning
#define CHAR4_KERNED_WIDTH (6 * 4 - CHAR4_KERNING)
#define ST7735_NOP 0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID 0x04
#define ST7735_RDDST 0x09
#define ST7735_SLPIN 0x10
#define ST7735_SLPOUT 0x11
#define ST7735_PTLON 0x12
#define ST7735_NORON 0x13
#define ST7735_INVOFF 0x20
#define ST7735_INVON 0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON 0x29
#define ST7735_CASET 0x2A
#define ST7735_RASET 0x2B
#define ST7735_RAMWR 0x2C
#define ST7735_RAMRD 0x2E
#define ST7735_PTLAR 0x30
#define ST7735_COLMOD 0x3A
#define ST7735_MADCTL 0x36
#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR 0xB4
#define ST7735_DISSET5 0xB6
#define ST7735_PWCTR1 0xC0
#define ST7735_PWCTR2 0xC1
#define ST7735_PWCTR3 0xC2
#define ST7735_PWCTR4 0xC3
#define ST7735_PWCTR5 0xC4
#define ST7735_VMCTR1 0xC5
#define ST7735_RDID1 0xDA
#define ST7735_RDID2 0xDB
#define ST7735_RDID3 0xDC
#define ST7735_RDID4 0xDD
#define ST7735_PWCTR6 0xFC
#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1
uint32_t lookupCfg(uint32_t key, uint32_t defl);
#define CFG(v) lookupCfg(CFG_##v, 0x42)
uint32_t lookupCfg(uint32_t key, uint32_t defl) {
const uint32_t *ptr = UF2_BINFO->config_data;
if (!ptr || (((uint32_t)ptr) & 3) || *ptr != CFG_MAGIC0) {
// no config data!
} else {
ptr += 4;
while (*ptr) {
if (*ptr == key)
return ptr[1];
ptr += 2;
}
}
if (defl == 0x42)
while (1)
;
return defl;
}
void pin_set(int pincfg, int v) {
int pin = lookupCfg(pincfg, -1);
if (pin < 0)
return;
if (v) {
PINOP(pin, OUTSET);
} else {
PINOP(pin, OUTCLR);
}
}
void setup_output_pin(int pincfg) {
int pin = lookupCfg(pincfg, -1);
if (pin < 0)
return;
PINOP(pin, DIRSET);
PINOP(pin, OUTCLR);
}
#define PINPORT(pin) PORT->Group[(pin) / 32]
#define pinmask(pin) (1 << (pin & 0x1f))
void transfer(uint8_t *ptr, uint32_t len) {
int mosi = CFG(PIN_DISPLAY_MOSI);
int sck = CFG(PIN_DISPLAY_SCK);
volatile uint32_t *mosi_set = &PINPORT(mosi).OUTSET.reg;
volatile uint32_t *mosi_clr = &PINPORT(mosi).OUTCLR.reg;
volatile uint32_t *sck_tgl = &PINPORT(sck).OUTTGL.reg;
uint32_t mosi_mask = pinmask(mosi);
uint32_t sck_mask = pinmask(sck);
PINOP(sck, OUTCLR);
uint8_t mask = 0, b;
for (;;) {
if (!mask) {
if (!len--)
break;
mask = 0x80;
b = *ptr++;
}
if (b & mask)
*mosi_set = mosi_mask;
else
*mosi_clr = mosi_mask;
*sck_tgl = sck_mask;
mask >>= 1;
*sck_tgl = sck_mask;
}
}
#define DELAY 0x80
// clang-format off
static const uint8_t initCmds[] = {
ST7735_SWRESET, DELAY, // 1: Software reset, 0 args, w/delay
120, // 150 ms delay
ST7735_SLPOUT , DELAY, // 2: Out of sleep mode, 0 args, w/delay
120, // 500 ms delay
ST7735_INVOFF , 0 , // 13: Don't invert display, no args, no delay
ST7735_COLMOD , 1 , // 15: set color mode, 1 arg, no delay:
0x05, // 16-bit color
ST7735_GMCTRP1, 16 , // 1: Magical unicorn dust, 16 args, no delay:
0x02, 0x1c, 0x07, 0x12,
0x37, 0x32, 0x29, 0x2d,
0x29, 0x25, 0x2B, 0x39,
0x00, 0x01, 0x03, 0x10,
ST7735_NORON , DELAY, // 3: Normal display on, no args, w/delay
10, // 10 ms delay
ST7735_DISPON , DELAY, // 4: Main screen turn on, no args w/delay
10,
0, 0 // END
};
// clang-format on
static uint8_t cmdBuf[20];
#define SET_DC(v) pin_set(CFG_PIN_DISPLAY_DC, v)
#define SET_CS(v) pin_set(CFG_PIN_DISPLAY_CS, v)
static void scr_delay(unsigned msec) {
int k = msec * 15000;
while (k--)
asm("nop");
}
static void sendCmd(uint8_t *buf, int len) {
// make sure cmd isn't on stack
if (buf != cmdBuf)
memcpy(cmdBuf, buf, len);
buf = cmdBuf;
SET_DC(0);
SET_CS(0);
transfer(buf, 1);
SET_DC(1);
len--;
buf++;
if (len > 0)
transfer(buf, len);
SET_CS(1);
}
static void sendCmdSeq(const uint8_t *buf) {
while (*buf) {
cmdBuf[0] = *buf++;
int v = *buf++;
int len = v & ~DELAY;
// note that we have to copy to RAM
memcpy(cmdBuf + 1, buf, len);
sendCmd(cmdBuf, len + 1);
buf += len;
if (v & DELAY) {
scr_delay(*buf++);
}
}
}
static uint32_t palXOR;
static void setAddrWindow(int x, int y, int w, int h) {
w += x - 1;
h += y - 1;
uint8_t cmd0[] = {ST7735_RASET, 0, (uint8_t)x, (uint8_t)(w >> 8), (uint8_t)w};
uint8_t cmd1[] = {ST7735_CASET, 0, (uint8_t)y, (uint8_t)(h >> 8), (uint8_t)h};
sendCmd(cmd1, sizeof(cmd1));
sendCmd(cmd0, sizeof(cmd0));
}
static void configure(uint8_t madctl, uint32_t frmctr1) {
uint8_t cmd0[] = {ST7735_MADCTL, madctl};
uint8_t cmd1[] = {ST7735_FRMCTR1, (uint8_t)(frmctr1 >> 16), (uint8_t)(frmctr1 >> 8),
(uint8_t)frmctr1};
sendCmd(cmd0, sizeof(cmd0));
sendCmd(cmd1, cmd1[3] == 0xff ? 3 : 4);
}
#define COL0(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
#define COL(c) COL0((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff)
const uint16_t palette[] = {
COL(0x000000), // 0
COL(0xffffff), // 1
COL(0xff2121), // 2
COL(0xff93c4), // 3
COL(0xff8135), // 4
COL(0xfff609), // 5
COL(0x249ca3), // 6
COL(0x78dc52), // 7
COL(0x003fad), // 8
COL(0x87f2ff), // 9
COL(0x8e2ec4), // 10
COL(0xa4839f), // 11
COL(0x5c406c), // 12
COL(0xe5cdc4), // 13
COL(0x91463d), // 14
COL(0x000000), // 15
};
uint8_t fb[168 * 128];
extern const uint8_t font8[];
extern const uint8_t fileLogo[];
extern const uint8_t pendriveLogo[];
extern const uint8_t arrowLogo[];
static void printch(int x, int y, int col, const uint8_t *fnt) {
for (int i = 0; i < 6; ++i) {
uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y;
uint8_t mask = 0x01;
for (int j = 0; j < 8; ++j) {
if (*fnt & mask)
*p = col;
p++;
mask <<= 1;
}
fnt++;
}
}
static void printch4(int x, int y, int col, const uint8_t *fnt) {
for (int i = 0; i < 6 * 4; ++i) {
uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y;
uint8_t mask = 0x01;
for (int j = 0; j < 8; ++j) {
for (int k = 0; k < 4; ++k) {
if (*fnt & mask)
*p = col;
p++;
}
mask <<= 1;
}
if ((i & 3) == 3)
fnt++;
}
}
void printicon(int x, int y, int col, const uint8_t *icon) {
int w = *icon++;
int h = *icon++;
int sz = *icon++;
uint8_t mask = 0x80;
int runlen = 0;
int runbit = 0;
uint8_t lastb = 0x00;
for (int i = 0; i < w; ++i) {
uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y;
for (int j = 0; j < h; ++j) {
int c = 0;
if (mask != 0x80) {
if (lastb & mask)
c = 1;
mask <<= 1;
} else if (runlen) {
if (runbit)
c = 1;
runlen--;
} else {
if (sz-- <= 0)
panic(10);
lastb = *icon++;
if (lastb & 0x80) {
runlen = lastb & 63;
runbit = lastb & 0x40;
} else {
mask = 0x01;
}
--j;
continue; // restart
}
if (c)
*p = col;
p++;
}
}
}
void print(int x, int y, int col, const char *text) {
int x0 = x;
while (*text) {
char c = *text++;
if (c == '\r')
continue;
if (c == '\n') {
x = x0;
y += 10;
continue;
}
/*
if (x + 8 > DISPLAY_WIDTH) {
x = x0;
y += 10;
}
*/
if (c < ' ')
c = '?';
if (c >= 0x7f)
c = '?';
c -= ' ';
printch(x, y, col, &font8[c * 6]);
x += 6;
}
}
void print4(int x, int y, int col, const char *text) {
while (*text) {
char c = *text++;
c -= ' ';
printch4(x, y, col, &font8[c * 6]);
x += CHAR4_KERNED_WIDTH;
if (x + CHAR4_KERNED_WIDTH > DISPLAY_WIDTH) {
// Next char won't fit.
return;
}
}
}
void draw_screen() {
if (lookupCfg(CFG_PIN_DISPLAY_SCK, 1000) == 1000)
return;
cmdBuf[0] = ST7735_RAMWR;
sendCmd(cmdBuf, 1);
SET_DC(1);
SET_CS(0);
uint8_t *p = fb;
if (lookupCfg(CFG_DISPLAY_TYPE, 7735) == 7735) {
for (int i = 0; i < DISPLAY_WIDTH; ++i) {
for (int j = 0; j < DISPLAY_HEIGHT; ++j) {
uint16_t color = palette[*p++ & 0xf];
uint8_t cc[] = {color >> 8, color & 0xff};
transfer(cc, 2);
}
}
} else {
// ILI9341/st7789(320*240*p8b) DISPLAY
for (int i = 0; i < DISPLAY_WIDTH; ++i) {
for (int j = 0; j < DISPLAY_HEIGHT - 8; j++) {
uint16_t color = palette[*(p + j) & 0xf];
uint8_t cc[] = {color >> 8, color & 0xff};
transfer(cc, 2);
transfer(cc, 2);
}
for (int j = 0; j < DISPLAY_HEIGHT - 8; j++) {
uint16_t color = palette[*(p + j) & 0xf];
uint8_t cc[] = {color >> 8, color & 0xff};
transfer(cc, 2);
transfer(cc, 2);
}
p += DISPLAY_HEIGHT;
}
}
SET_CS(1);
}
void drawBar(int y, int h, int c) {
for (int x = 0; x < DISPLAY_WIDTH; ++x) {
memset(fb + x * DISPLAY_HEIGHT + y, c, h);
}
}
void draw_hf2() {
print4(20, 22, 5, "<-->");
print(40, 110, 7, "flashing...");
draw_screen();
}
void draw_drag() {
drawBar(0, 52, 7);
drawBar(52, 55, 8);
drawBar(107, 14, 4);
// Center PRODUCT_NAME and UF2_VERSION_BASE.
int name_x = (DISPLAY_WIDTH - (6 * 4 - CHAR4_KERNING) * (int) strlen(PRODUCT_NAME)) / 2;
print4(name_x >= 0 ? name_x : 0, 5, 1, PRODUCT_NAME);
int version_x = (DISPLAY_WIDTH - 6 * (int) strlen(UF2_VERSION_BASE)) / 2;
print(version_x >= 0 ? version_x : 0, 40, 6, UF2_VERSION_BASE);
print(23, 110, 1, "arcade.makecode.com");
#define DRAG 70
#define DRAGX 10
printicon(DRAGX + 20, DRAG + 5, 1, fileLogo);
printicon(DRAGX + 66, DRAG, 1, arrowLogo);
printicon(DRAGX + 108, DRAG, 1, pendriveLogo);
print(10, DRAG - 12, 1, "arcade.uf2");
print(90, DRAG - 12, 1, VOLUME_LABEL);
draw_screen();
}
void screen_early_init() {
setup_output_pin(CFG_PIN_DISPLAY_BL);
}
void screen_init() {
if (lookupCfg(CFG_PIN_DISPLAY_SCK, 1000) == 1000)
return;
setup_output_pin(CFG_PIN_DISPLAY_SCK);
setup_output_pin(CFG_PIN_DISPLAY_MOSI);
setup_output_pin(CFG_PIN_DISPLAY_BL);
setup_output_pin(CFG_PIN_DISPLAY_DC);
setup_output_pin(CFG_PIN_DISPLAY_RST);
setup_output_pin(CFG_PIN_DISPLAY_CS);
SET_CS(1);
SET_DC(1);
pin_set(CFG_PIN_DISPLAY_BL, 1);
pin_set(CFG_PIN_DISPLAY_RST, 0);
scr_delay(20);
pin_set(CFG_PIN_DISPLAY_RST, 1);
scr_delay(20);
sendCmdSeq(initCmds);
uint32_t cfg0 = CFG(DISPLAY_CFG0);
//uint32_t cfg2 = CFG(DISPLAY_CFG2);
uint32_t frmctr1 = CFG(DISPLAY_CFG1);
palXOR = (cfg0 & 0x1000000) ? 0xffffff : 0x000000;
uint32_t madctl = cfg0 & 0xff;
uint32_t offX = (cfg0 >> 8) & 0xff;
uint32_t offY = (cfg0 >> 16) & 0xff;
//uint32_t freq = (cfg2 & 0xff);
//offX = (CFG(DISPLAY_WIDTH) - DISPLAY_WIDTH) / 2; //commented out, breaks ILI9341 compatibility
//offY = (CFG(DISPLAY_HEIGHT) - DISPLAY_HEIGHT) / 2;
// DMESG("configure screen: FRMCTR1=%p MADCTL=%p SPI at %dMHz", frmctr1, madctl, freq);
configure(madctl, frmctr1);
setAddrWindow(offX, offY, CFG(DISPLAY_WIDTH), CFG(DISPLAY_HEIGHT));
memset(fb, 0, sizeof(fb));
}
#endif

View file

@ -0,0 +1,289 @@
/**
* \file
*
* \brief gcc starttup file for SAMD21
*
* Copyright (c) 2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
#include "board_config.h"
#include "samd21.h"
/* Initialize segments */
extern uint32_t _sfixed;
extern uint32_t _efixed;
extern uint32_t _etext;
extern uint32_t _srelocate;
extern uint32_t _erelocate;
extern uint32_t _szero;
extern uint32_t _ezero;
extern uint32_t _sstack;
extern uint32_t _estack;
/** \cond DOXYGEN_SHOULD_SKIP_THIS */
int main(void);
/** \endcond */
void __libc_init_array(void);
/* Default empty handler */
void Dummy_Handler(void);
/* Cortex-M0+ core handlers */
void NMI_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
//void HardFault_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SVC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void PendSV_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SysTick_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
/* Peripherals handlers */
void PM_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SYSCTRL_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void WDT_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void RTC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void EIC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void NVMCTRL_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void DMAC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#ifdef ID_USB
void USB_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
void EVSYS_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SERCOM0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SERCOM1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SERCOM2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SERCOM3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#ifdef ID_SERCOM4
void SERCOM4_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_SERCOM5
void SERCOM5_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
void TCC0_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void TCC1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void TCC2_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void TC3_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void TC4_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void TC5_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#ifdef ID_TC6
void TC6_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_TC7
void TC7_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_ADC
void ADC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_AC
void AC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_DAC
void DAC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_PTC
void PTC_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_I2S
void I2S_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
#ifdef ID_AC1
void AC1_Handler ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
#endif
/* Exception Table */
__attribute__ ((section(".vectors")))
const DeviceVectors exception_table = {
/* Configure Initial Stack Pointer, using linker-generated symbols */
.pvStack = (void*) (&_estack),
.pfnReset_Handler = (void*) Reset_Handler,
.pfnNMI_Handler = (void*) NMI_Handler,
.pfnHardFault_Handler = (void*) HardFault_Handler,
.pvReservedM12 = (void*) (0UL), /* Reserved */
.pvReservedM11 = (void*) (0UL), /* Reserved */
.pvReservedM10 = (void*) (0UL), /* Reserved */
.pvReservedM9 = (void*) (0UL), /* Reserved */
.pvReservedM8 = (void*) (0UL), /* Reserved */
.pvReservedM7 = (void*) (0UL), /* Reserved */
.pvReservedM6 = (void*) (0UL), /* Reserved */
.pfnSVC_Handler = (void*) SVC_Handler,
.pvReservedM4 = (void*) (0UL), /* Reserved */
.pvReservedM3 = (void*) (0UL), /* Reserved */
.pfnPendSV_Handler = (void*) PendSV_Handler,
.pfnSysTick_Handler = (void*) SysTick_Handler,
/* Configurable interrupts */
.pfnPM_Handler = (void*) PM_Handler, /* 0 Power Manager */
.pfnSYSCTRL_Handler = (void*) SYSCTRL_Handler, /* 1 System Control */
.pfnWDT_Handler = (void*) WDT_Handler, /* 2 Watchdog Timer */
.pfnRTC_Handler = (void*) RTC_Handler, /* 3 Real-Time Counter */
.pfnEIC_Handler = (void*) EIC_Handler, /* 4 External Interrupt Controller */
.pfnNVMCTRL_Handler = (void*) NVMCTRL_Handler, /* 5 Non-Volatile Memory Controller */
.pfnDMAC_Handler = (void*) DMAC_Handler, /* 6 Direct Memory Access Controller */
#ifdef ID_USB
.pfnUSB_Handler = (void*) USB_Handler, /* 7 Universal Serial Bus */
#else
.pvReserved7 = (void*) (0UL), /* 7 Reserved */
#endif
.pfnEVSYS_Handler = (void*) EVSYS_Handler, /* 8 Event System Interface */
.pfnSERCOM0_Handler = (void*) SERCOM0_Handler, /* 9 Serial Communication Interface 0 */
.pfnSERCOM1_Handler = (void*) SERCOM1_Handler, /* 10 Serial Communication Interface 1 */
.pfnSERCOM2_Handler = (void*) SERCOM2_Handler, /* 11 Serial Communication Interface 2 */
.pfnSERCOM3_Handler = (void*) SERCOM3_Handler, /* 12 Serial Communication Interface 3 */
#ifdef ID_SERCOM4
.pfnSERCOM4_Handler = (void*) SERCOM4_Handler, /* 13 Serial Communication Interface 4 */
#else
.pvReserved13 = (void*) (0UL), /* 13 Reserved */
#endif
#ifdef ID_SERCOM5
.pfnSERCOM5_Handler = (void*) SERCOM5_Handler, /* 14 Serial Communication Interface 5 */
#else
.pvReserved14 = (void*) (0UL), /* 14 Reserved */
#endif
.pfnTCC0_Handler = (void*) TCC0_Handler, /* 15 Timer Counter Control 0 */
.pfnTCC1_Handler = (void*) TCC1_Handler, /* 16 Timer Counter Control 1 */
.pfnTCC2_Handler = (void*) TCC2_Handler, /* 17 Timer Counter Control 2 */
.pfnTC3_Handler = (void*) TC3_Handler, /* 18 Basic Timer Counter 0 */
.pfnTC4_Handler = (void*) TC4_Handler, /* 19 Basic Timer Counter 1 */
.pfnTC5_Handler = (void*) TC5_Handler, /* 20 Basic Timer Counter 2 */
#ifdef ID_TC6
.pfnTC6_Handler = (void*) TC6_Handler, /* 21 Basic Timer Counter 3 */
#else
.pvReserved21 = (void*) (0UL), /* 21 Reserved */
#endif
#ifdef ID_TC7
.pfnTC7_Handler = (void*) TC7_Handler, /* 22 Basic Timer Counter 4 */
#else
.pvReserved22 = (void*) (0UL), /* 22 Reserved */
#endif
#ifdef ID_ADC
.pfnADC_Handler = (void*) ADC_Handler, /* 23 Analog Digital Converter */
#else
.pvReserved23 = (void*) (0UL), /* 23 Reserved */
#endif
#ifdef ID_AC
.pfnAC_Handler = (void*) AC_Handler, /* 24 Analog Comparators 0 */
#else
.pvReserved24 = (void*) (0UL), /* 24 Reserved */
#endif
#ifdef ID_DAC
.pfnDAC_Handler = (void*) DAC_Handler, /* 25 Digital Analog Converter */
#else
.pvReserved25 = (void*) (0UL), /* 25 Reserved */
#endif
#ifdef ID_PTC
.pfnPTC_Handler = (void*) PTC_Handler, /* 26 Peripheral Touch Controller */
#else
.pvReserved26 = (void*) (0UL), /* 26 Reserved */
#endif
#ifdef ID_I2S
.pfnI2S_Handler = (void*) I2S_Handler, /* 27 Inter-IC Sound Interface */
#else
.pvReserved27 = (void*) (0UL), /* 27 Reserved */
#endif
#ifdef ID_AC1
.pfnAC1_Handler = (void*) AC1_Handler /* 28 Analog Comparators 1 */
#else
.pvReserved28 = (void*) (0UL) /* 28 Reserved */
#endif
};
/**
* \brief This is the code that gets called on processor reset.
* To initialize the device, and call the main() routine.
*/
void Reset_Handler(void)
{
uint32_t *pSrc, *pDest;
/* Initialize the relocate segment */
pSrc = &_etext;
pDest = &_srelocate;
if (pSrc != pDest) {
for (; pDest < &_erelocate;) {
*pDest++ = *pSrc++;
}
}
/* Clear the zero segment */
for (pDest = &_szero; pDest < &_ezero;) {
*pDest++ = 0;
}
/* Set the vector table base address */
pSrc = (uint32_t *) & _sfixed;
SCB->VTOR = ((uint32_t) pSrc & SCB_VTOR_TBLOFF_Msk);
/* Change default QOS values to have the best performance and correct USB behaviour */
SBMATRIX->SFR[SBMATRIX_SLAVE_HMCRAMC0].reg = 2;
#if defined(ID_USB)
USB->DEVICE.QOSCTRL.bit.CQOS = 2;
USB->DEVICE.QOSCTRL.bit.DQOS = 2;
#endif
DMAC->QOSCTRL.bit.DQOS = 2;
DMAC->QOSCTRL.bit.FQOS = 2;
DMAC->QOSCTRL.bit.WRBQOS = 2;
/* Overwriting the default value of the NVMCTRL.CTRLB.MANW bit (errata reference 13134) */
NVMCTRL->CTRLB.bit.MANW = 1;
/* Initialize the C library */
//__libc_init_array();
/* Branch to main function */
main();
/* Infinite loop */
while (1);
}
/**
* \brief Default interrupt handler for unused IRQs.
*/
void Dummy_Handler(void)
{
while (1) {
}
}
void HardFault_Handler(void)
{
while (1) {
}
}

View file

@ -0,0 +1,137 @@
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2011-2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition is met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
#include "board_config.h"
#include "uart_driver.h"
bool uart_drv_error_flag = false;
uint32_t uart_get_sercom_index(Sercom *sercom_instance) {
/* Save all available SERCOM instances for compare. */
Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
/* Find index for sercom instance. */
for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) {
if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) {
return i;
}
}
return 0;
}
void uart_basic_init(Sercom *sercom, uint16_t baud_val, enum uart_pad_settings pad_conf) {
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.ENABLE)
;
/* Disable the SERCOM UART module */
sercom->USART.CTRLA.bit.ENABLE = 0;
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.SWRST)
;
/* Perform a software reset */
sercom->USART.CTRLA.bit.SWRST = 1;
/* Wait for synchronization */
while (sercom->USART.CTRLA.bit.SWRST)
;
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.SWRST || sercom->USART.SYNCBUSY.bit.ENABLE)
;
/* Update the UART pad settings, mode and data order settings */
sercom->USART.CTRLA.reg = pad_conf | SERCOM_USART_CTRLA_MODE(1) | SERCOM_USART_CTRLA_DORD;
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.CTRLB)
;
/* Enable transmit and receive and set data size to 8 bits */
sercom->USART.CTRLB.reg =
SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_CHSIZE(0);
/* Load the baud value */
sercom->USART.BAUD.reg = baud_val;
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.ENABLE)
;
/* Enable SERCOM UART */
sercom->USART.CTRLA.bit.ENABLE = 1;
}
void uart_disable(Sercom *sercom) {
/* Wait for synchronization */
while (sercom->USART.SYNCBUSY.bit.ENABLE)
;
/* Disable SERCOM UART */
sercom->USART.CTRLA.bit.ENABLE = 0;
}
void uart_write_byte(Sercom *sercom, uint8_t data) {
/* Wait for Data Register Empty flag */
while (!sercom->USART.INTFLAG.bit.DRE)
;
/* Write the data to DATA register */
sercom->USART.DATA.reg = (uint16_t)data;
}
uint8_t uart_read_byte(Sercom *sercom) {
/* Wait for Receive Complete flag */
while (!sercom->USART.INTFLAG.bit.RXC)
;
/* Check for errors */
if (sercom->USART.STATUS.bit.PERR || sercom->USART.STATUS.bit.FERR ||
sercom->USART.STATUS.bit.BUFOVF)
/* Set the error flag */
uart_drv_error_flag = true;
/* Return the read data */
return ((uint8_t)sercom->USART.DATA.reg);
}
void uart_write_buffer_polled(Sercom *sercom, uint8_t *ptr, uint16_t length) {
/* Do the following for specified length */
do {
/* Wait for Data Register Empty flag */
while (!sercom->USART.INTFLAG.bit.DRE)
;
/* Send data from the buffer */
sercom->USART.DATA.reg = (uint16_t)*ptr++;
} while (length--);
}
void uart_read_buffer_polled(Sercom *sercom, uint8_t *ptr, uint16_t length) {
/* Do the following for specified length */
do {
/* Wait for Receive Complete flag */
while (!sercom->USART.INTFLAG.bit.RXC)
;
/* Check for errors */
if (sercom->USART.STATUS.bit.PERR || sercom->USART.STATUS.bit.FERR ||
sercom->USART.STATUS.bit.BUFOVF)
/* Set the error flag */
uart_drv_error_flag = true;
/* Store the read data to the buffer */
*ptr++ = (uint8_t)sercom->USART.DATA.reg;
} while (length--);
}

View file

@ -0,0 +1,495 @@
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2011-2014, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition is met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
#include "uf2.h"
#include "uart_driver.h"
/* Variable to let the main task select the appropriate communication interface
*/
volatile uint8_t b_sharp_received;
/* RX and TX Buffers + rw pointers for each buffer */
volatile uint8_t buffer_rx_usart[USART_BUFFER_SIZE];
volatile uint8_t idx_rx_read;
volatile uint8_t idx_rx_write;
volatile uint8_t buffer_tx_usart[USART_BUFFER_SIZE];
volatile uint8_t idx_tx_read;
volatile uint8_t idx_tx_write;
/* Test for timeout in AT91F_GetChar */
uint8_t error_timeout;
uint16_t size_of_data;
uint8_t mode_of_transfer;
#define BOOT_USART_PAD(n) BOOT_USART_PAD##n
/**
* \brief Open the given USART
*/
void usart_open() {
uint32_t port;
uint8_t pin;
/* Configure the port pins for SERCOM_USART */
if (BOOT_USART_PAD0 != PINMUX_UNUSED) {
/* Mask 6th bit in pin number to check whether it is greater than 32
* i.e., PORTB pin */
port = (BOOT_USART_PAD0 & 0x200000) >> 21;
pin = BOOT_USART_PAD0 >> 16;
PORT->Group[port].PINCFG[(pin - (port * 32))].bit.PMUXEN = 1;
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg &= ~(0xF << (4 * (pin & 0x01u)));
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg |= (BOOT_USART_PAD0 & 0xFF)
<< (4 * (pin & 0x01u));
}
if (BOOT_USART_PAD1 != PINMUX_UNUSED) {
/* Mask 6th bit in pin number to check whether it is greater than 32
* i.e., PORTB pin */
port = (BOOT_USART_PAD1 & 0x200000) >> 21;
pin = BOOT_USART_PAD1 >> 16;
PORT->Group[port].PINCFG[(pin - (port * 32))].bit.PMUXEN = 1;
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg &= ~(0xF << (4 * (pin & 0x01u)));
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg |= (BOOT_USART_PAD1 & 0xFF)
<< (4 * (pin & 0x01u));
}
if (BOOT_USART_PAD2 != PINMUX_UNUSED) {
/* Mask 6th bit in pin number to check whether it is greater than 32
* i.e., PORTB pin */
port = (BOOT_USART_PAD2 & 0x200000) >> 21;
pin = BOOT_USART_PAD2 >> 16;
PORT->Group[port].PINCFG[(pin - (port * 32))].bit.PMUXEN = 1;
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg &= ~(0xF << (4 * (pin & 0x01u)));
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg |= (BOOT_USART_PAD2 & 0xFF)
<< (4 * (pin & 0x01u));
}
if (BOOT_USART_PAD3 != PINMUX_UNUSED) {
/* Mask 6th bit in pin number to check whether it is greater than 32
* i.e., PORTB pin */
port = (BOOT_USART_PAD3 & 0x200000) >> 21;
pin = BOOT_USART_PAD3 >> 16;
PORT->Group[port].PINCFG[(pin - (port * 32))].bit.PMUXEN = 1;
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg &= ~(0xF << (4 * (pin & 0x01u)));
PORT->Group[port].PMUX[(pin - (port * 32)) / 2].reg |= (BOOT_USART_PAD3 & 0xFF)
<< (4 * (pin & 0x01u));
}
#ifdef SAMD21
uint32_t inst = uart_get_sercom_index(BOOT_USART_MODULE);
/* Enable clock for BOOT_USART_MODULE */
PM->APBCMASK.reg |= (1u << (inst + PM_APBCMASK_SERCOM0_Pos));
/* Set GCLK_GEN0 as source for GCLK_ID_SERCOMx_CORE */
GCLK_CLKCTRL_Type clkctrl = {0};
uint16_t temp;
GCLK->CLKCTRL.bit.ID = inst + GCLK_ID_SERCOM0_CORE;
temp = GCLK->CLKCTRL.reg;
clkctrl.bit.CLKEN = true;
clkctrl.bit.WRTLOCK = false;
clkctrl.bit.GEN = GCLK_CLKCTRL_GEN_GCLK0_Val;
GCLK->CLKCTRL.reg = (clkctrl.reg | temp);
#endif
#ifdef SAMD51
GCLK->PCHCTRL[BOOT_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
GCLK->PCHCTRL[BOOT_GCLK_ID_SLOW].reg = GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
MCLK->BOOT_USART_MASK.reg |= BOOT_USART_BUS_CLOCK_INDEX ;
#endif
/* Baud rate 115200 - clock 8MHz -> BAUD value-50436 */
uart_basic_init(BOOT_USART_MODULE, 50436, BOOT_USART_PAD_SETTINGS);
// Initialize flag
b_sharp_received = false;
idx_rx_read = 0;
idx_rx_write = 0;
idx_tx_read = 0;
idx_tx_write = 0;
error_timeout = 0;
}
/**
* \brief Configures communication line
*
*/
void usart_close(void) { uart_disable(BOOT_USART_MODULE); }
/**
* \brief Puts a byte on usart line
* The type int is used to support printf redirection from compiler LIB.
*
* \param value Value to put
*
* \return \c 1 if function was successfully done, otherwise \c 0.
*/
int usart_putc(int value) {
uart_write_byte(BOOT_USART_MODULE, (uint8_t)value);
return 1;
}
int usart_getc(void) {
uint16_t retval;
// Wait until input buffer is filled
while (!(usart_is_rx_ready()))
;
retval = (uint16_t)uart_read_byte(BOOT_USART_MODULE);
// usart_read_wait(&usart_sam_ba, &retval);
return (int)retval;
}
int usart_sharp_received(void) {
if (usart_is_rx_ready()) {
if (usart_getc() == SHARP_CHARACTER)
return (true);
}
return (false);
}
bool usart_is_rx_ready(void) {
return (BOOT_USART_MODULE->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_RXC);
}
int usart_readc(void) {
int retval;
retval = buffer_rx_usart[idx_rx_read];
idx_rx_read = (idx_rx_read + 1) & (USART_BUFFER_SIZE - 1);
return (retval);
}
// Send given data (polling)
uint32_t usart_putdata(void const *data, uint32_t length) {
uint32_t i;
uint8_t *ptrdata;
ptrdata = (uint8_t *)data;
for (i = 0; i < length; i++) {
usart_putc(*ptrdata);
ptrdata++;
}
return (i);
}
// Get data from comm. device
uint32_t usart_getdata(void *data, uint32_t length) {
uint8_t *ptrdata;
ptrdata = (uint8_t *)data;
*ptrdata = usart_getc();
return (1);
}
static uint16_t crcCache[256];
//*----------------------------------------------------------------------------
//* \fn add_crc
//* \brief Compute the CRC
//*----------------------------------------------------------------------------
uint16_t add_crc(uint8_t ch, unsigned short crc0) {
if (!crcCache[1]) {
for (int ptr = 0; ptr < 256; ptr++) {
uint16_t crc = (int)ptr << 8;
for (uint16_t cmpt = 0; cmpt < 8; cmpt++) {
if (crc & 0x8000)
crc = crc << 1 ^ CRC16POLY;
else
crc = crc << 1;
}
crcCache[ptr] = crc;
}
}
return ((crc0 << 8) ^ crcCache[((crc0 >> 8) ^ ch) & 0xff]) & 0xffff;
}
//*----------------------------------------------------------------------------
//* \fn getbytes
//* \brief
//*----------------------------------------------------------------------------
static uint16_t getbytes(uint8_t *ptr_data, uint16_t length) {
uint16_t crc = 0;
uint16_t cpt;
uint8_t c;
for (cpt = 0; cpt < length; ++cpt) {
c = usart_getc();
if (error_timeout)
return 1;
crc = add_crc(c, crc);
// crc = (crc << 8) ^ xcrc16tab[(crc>>8) ^ c];
if (size_of_data || mode_of_transfer) {
*ptr_data++ = c;
if (length == PKTLEN_128)
size_of_data--;
}
}
return crc;
}
//*----------------------------------------------------------------------------
//* \fn putPacket
//* \brief Used by Xup to send packets.
//*----------------------------------------------------------------------------
static int putPacket(uint8_t *tmppkt, uint8_t sno) {
uint32_t i;
uint16_t chksm;
uint8_t data;
chksm = 0;
usart_putc(SOH);
usart_putc(sno);
usart_putc((uint8_t) ~(sno));
for (i = 0; i < PKTLEN_128; i++) {
if (size_of_data || mode_of_transfer) {
data = *tmppkt++;
size_of_data--;
} else
data = 0x00;
usart_putc(data);
// chksm = (chksm<<8) ^ xcrc16tab[(chksm>>8)^data];
chksm = add_crc(data, chksm);
}
/* An "endian independent way to extract the CRC bytes. */
usart_putc((uint8_t)(chksm >> 8));
usart_putc((uint8_t)chksm);
return (usart_getc()); /* Wait for ack */
}
//*----------------------------------------------------------------------------
//* \fn getPacket
//* \brief Used by Xdown to retrieve packets.
//*----------------------------------------------------------------------------
uint8_t getPacket(uint8_t *ptr_data, uint8_t sno) {
uint8_t seq[2];
uint16_t crc, xcrc;
getbytes(seq, 2);
xcrc = getbytes(ptr_data, PKTLEN_128);
if (error_timeout)
return (false);
/* An "endian independent way to combine the CRC bytes. */
crc = (uint16_t)usart_getc() << 8;
crc += (uint16_t)usart_getc();
if (error_timeout == 1)
return (false);
if ((crc != xcrc) || (seq[0] != sno) || (seq[1] != (uint8_t)(~sno))) {
usart_putc(CAN);
return (false);
}
usart_putc(ACK);
return (true);
}
//*----------------------------------------------------------------------------
//* \fn Xup
//* \brief Called when a transfer from target to host is being made (considered
//* an upload).
//*----------------------------------------------------------------------------
// static void Xup(char *ptr_data, uint16_t length)
// Send given data (polling) using xmodem (if necessary)
uint32_t usart_putdata_xmd(void const *data, uint32_t length) {
uint8_t c, sno = 1;
uint8_t done;
uint8_t *ptr_data = (uint8_t *)data;
error_timeout = 0;
if (!length)
mode_of_transfer = 1;
else {
size_of_data = length;
mode_of_transfer = 0;
}
if (length & (PKTLEN_128 - 1)) {
length += PKTLEN_128;
length &= ~(PKTLEN_128 - 1);
}
/* Startup synchronization... */
/* Wait to receive a NAK or 'C' from receiver. */
done = 0;
while (!done) {
c = (uint8_t)usart_getc();
if (error_timeout) { // Test for timeout in usart_getc
error_timeout = 0;
c = (uint8_t)usart_getc();
if (error_timeout) {
error_timeout = 0;
return (0);
}
}
switch (c) {
case NAK:
done = 1;
// ("CSM");
break;
case 'C':
done = 1;
// ("CRC");
break;
case 'q': /* ELS addition, not part of XMODEM spec. */
return (0);
default:
break;
}
}
done = 0;
sno = 1;
while (!done) {
c = (uint8_t)putPacket((uint8_t *)ptr_data, sno);
if (error_timeout) { // Test for timeout in usart_getc
error_timeout = 0;
return (0);
}
switch (c) {
case ACK:
++sno;
length -= PKTLEN_128;
ptr_data += PKTLEN_128;
// ("A");
break;
case NAK:
// ("N");
break;
case CAN:
case EOT:
default:
done = 0;
break;
}
if (!length) {
usart_putc(EOT);
usart_getc(); /* Flush the ACK */
break;
}
// ("!");
}
mode_of_transfer = 0;
// ("Xup_done.");
return (1);
// return(0);
}
//*----------------------------------------------------------------------------
//* \fn Xdown
//* \brief Called when a transfer from host to target is being made (considered
//* an download).
//*----------------------------------------------------------------------------
// static void Xdown(char *ptr_data, uint16_t length)
// Get data from comm. device using xmodem (if necessary)
uint32_t usart_getdata_xmd(void *data, uint32_t length) {
uint32_t timeout;
char c;
uint8_t *ptr_data = (uint8_t *)data;
uint32_t b_run, nbr_of_timeout = 100;
uint8_t sno = 0x01;
uint32_t data_transfered = 0;
// Copied from legacy source code ... might need some tweaking
uint32_t loops_per_second =
CPU_FREQUENCY / 10; /* system_clock_source_get_hz(BOOT_USART_GCLK_GEN_SOURCE) / 10; */
error_timeout = 0;
if (length == 0)
mode_of_transfer = 1;
else {
size_of_data = length;
mode_of_transfer = 0;
}
/* Startup synchronization... */
/* Continuously send NAK or 'C' until sender responds. */
// ("Xdown");
while (1) {
usart_putc('C');
timeout = loops_per_second;
while (!(usart_is_rx_ready()) && timeout)
timeout--;
if (timeout)
break;
if (!(--nbr_of_timeout))
return (0);
// return -1;
}
b_run = true;
// ("Got response");
while (b_run != false) {
c = (char)usart_getc();
if (error_timeout) { // Test for timeout in usart_getc
error_timeout = 0;
return (0);
// return (-1);
}
switch (c) {
case SOH: /* 128-byte incoming packet */
// ("O");
b_run = getPacket(ptr_data, sno);
if (error_timeout) { // Test for timeout in usart_getc
error_timeout = 0;
return (0);
// return (-1);
}
if (b_run == true) {
++sno;
ptr_data += PKTLEN_128;
data_transfered += PKTLEN_128;
}
break;
case EOT: // ("E");
usart_putc(ACK);
b_run = false;
break;
case CAN: // ("C");
case ESC: /* "X" User-invoked abort */
default:
b_run = false;
break;
}
// ("!");
}
mode_of_transfer = 0;
return (true);
// return(b_run);
}

218
bootloader/src/utils.c Normal file
View file

@ -0,0 +1,218 @@
#include "uf2.h"
#include "neopixel.h"
static uint32_t timerLow;
uint32_t timerHigh, resetHorizon;
void delay(uint32_t ms) {
// SAMD21 starts up at 1mhz by default.
#ifdef SAMD21
ms <<= 8;
#endif
// SAMD51 starts up at 48mhz by default.
#ifdef SAMD51
ms <<= 12;
#endif
for (int i = 1; i < ms; ++i) {
asm("nop");
}
}
void timerTick(void) {
if (timerLow-- == 0) {
timerLow = TIMER_STEP;
timerHigh++;
if (resetHorizon && timerHigh >= resetHorizon) {
resetHorizon = 0;
resetIntoApp();
}
}
}
void panic(int code) {
logval("PANIC", code);
while (1) {
}
}
int writeNum(char *buf, uint32_t n, bool full) {
int i = 0;
int sh = 28;
while (sh >= 0) {
int d = (n >> sh) & 0xf;
if (full || d || sh == 0 || i) {
buf[i++] = d > 9 ? 'A' + d - 10 : '0' + d;
}
sh -= 4;
}
return i;
}
void resetIntoApp() {
// reset without waiting for double tap (only works for one reset)
RGBLED_set_color(COLOR_LEAVE);
*DBL_TAP_PTR = DBL_TAP_MAGIC_QUICK_BOOT;
NVIC_SystemReset();
}
void resetIntoBootloader() {
// reset without waiting for double tap (only works for one reset)
*DBL_TAP_PTR = DBL_TAP_MAGIC;
NVIC_SystemReset();
}
#if USE_LOGS
struct LogStore logStoreUF2;
void logreset() {
logStoreUF2.ptr = 0;
logmsg("Reset logs.");
}
void logwritenum(uint32_t n) {
char buff[9];
buff[writeNum(buff, n, false)] = 0;
logwrite("0x");
logwrite(buff);
}
void logwrite(const char *msg) {
const int jump = sizeof(logStoreUF2.buffer) / 4;
if (logStoreUF2.ptr >= sizeof(logStoreUF2.buffer) - jump) {
logStoreUF2.ptr -= jump;
memmove(logStoreUF2.buffer, logStoreUF2.buffer + jump, logStoreUF2.ptr);
}
int l = strlen(msg);
if (l + logStoreUF2.ptr >= sizeof(logStoreUF2.buffer)) {
logwrite("TOO LONG!\n");
return;
}
memcpy(logStoreUF2.buffer + logStoreUF2.ptr, msg, l);
logStoreUF2.ptr += l;
logStoreUF2.buffer[logStoreUF2.ptr] = 0;
}
void logmsg(const char *msg) {
logwrite(msg);
logwrite("\n");
}
void logval(const char *lbl, uint32_t v) {
logwrite(lbl);
logwrite(": ");
logwritenum(v);
logwrite("\n");
}
#endif
static uint32_t now;
static uint32_t signal_end;
int8_t led_tick_step = 1;
static uint8_t limit = 200;
void led_tick() {
now++;
if (signal_end) {
if (now == signal_end - 1000) {
LED_MSC_ON();
}
if (now == signal_end) {
signal_end = 0;
}
} else {
uint8_t curr = now & 0xff;
if (curr == 0) {
LED_MSC_ON();
if (limit < 10 || limit > 250) {
led_tick_step = -led_tick_step;
}
limit += led_tick_step;
} else if (curr == limit) {
LED_MSC_OFF();
}
}
}
void led_signal() {
if (signal_end < now) {
signal_end = now + 2000;
LED_MSC_OFF();
}
}
void led_init() {
#if defined(LED_PIN)
PINOP(LED_PIN, DIRSET);
#endif
LED_MSC_ON();
#if defined(BOARD_RGBLED_CLOCK_PIN)
// using APA102, set pins to outputs
PINOP(BOARD_RGBLED_CLOCK_PIN, DIRSET);
PINOP(BOARD_RGBLED_DATA_PIN, DIRSET);
// This won't work for neopixel, because we're running at 1MHz or thereabouts...
RGBLED_set_color(COLOR_LEAVE);
#endif
#if USE_SCREEN
// turn display backlight
screen_early_init();
#endif
}
#if defined(BOARD_RGBLED_CLOCK_PIN)
void write_apa_byte(uint8_t x) {
for (uint8_t i = 0x80; i != 0; i >>= 1) {
if (x & i)
PINOP(BOARD_RGBLED_DATA_PIN, OUTSET);
else
PINOP(BOARD_RGBLED_DATA_PIN, OUTCLR);
PINOP(BOARD_RGBLED_CLOCK_PIN, OUTSET);
// for (uint8_t j=0; j<25; j++) /* 0.1ms */
// __asm__ __volatile__("");
PINOP(BOARD_RGBLED_CLOCK_PIN, OUTCLR);
// for (uint8_t j=0; j<25; j++) /* 0.1ms */
// __asm__ __volatile__("");
}
}
#endif
void RGBLED_set_color(uint32_t color) {
#if defined(BOARD_RGBLED_CLOCK_PIN)
write_apa_byte(0x0);
write_apa_byte(0x0);
write_apa_byte(0x0);
write_apa_byte(0x0);
write_apa_byte(0xFF);
write_apa_byte(color >> 16);
write_apa_byte(color >> 8);
write_apa_byte(color);
write_apa_byte(0xFF);
write_apa_byte(0xFF);
write_apa_byte(0xFF);
write_apa_byte(0xFF);
// set clock port low for ~10ms
delay(50);
#elif defined(BOARD_NEOPIXEL_PIN)
uint8_t buf[BOARD_NEOPIXEL_COUNT * 3];
#if 0
memset(buf, 0, sizeof(buf));
buf[0] = color >> 8;
buf[1] = color >> 16;
buf[2] = color;
#else
for (int i = 0; i < BOARD_NEOPIXEL_COUNT * 3; i += 3) {
buf[i + 0] = color >> 8;
buf[i + 1] = color >> 16;
buf[i + 2] = color;
}
#endif
neopixel_send_buffer(buf, BOARD_NEOPIXEL_COUNT * 3);
#endif
}