forked from jappel/VersaMCU
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:
parent
ce5db617a1
commit
de52b57041
143 changed files with 69119 additions and 0 deletions
1152
bootloader/src/cdc_enumerate.c
Normal file
1152
bootloader/src/cdc_enumerate.c
Normal file
File diff suppressed because it is too large
Load diff
262
bootloader/src/fat.c
Normal file
262
bootloader/src/fat.c
Normal 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;
|
||||
}
|
||||
}
|
||||
115
bootloader/src/flash_samd21.c
Normal file
115
bootloader/src/flash_samd21.c
Normal 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
220
bootloader/src/hid.c
Normal 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
118
bootloader/src/images.c
Normal 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,
|
||||
};
|
||||
130
bootloader/src/init_samd21.c
Normal file
130
bootloader/src/init_samd21.c
Normal 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
278
bootloader/src/main.c
Normal 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
879
bootloader/src/msc.c
Normal 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,
|
||||
};
|
||||
300
bootloader/src/sam_ba_monitor.c
Normal file
300
bootloader/src/sam_ba_monitor.c
Normal 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 *)¤t_number, 2);
|
||||
} else if (command == 'w') {
|
||||
current_number = *((uint32_t *)(void *)ptr_data);
|
||||
sam_ba_putdata_term((uint8_t *)¤t_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
489
bootloader/src/screen.c
Normal 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
|
||||
289
bootloader/src/startup_samd21.c
Normal file
289
bootloader/src/startup_samd21.c
Normal 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) {
|
||||
}
|
||||
}
|
||||
137
bootloader/src/uart_driver.c
Normal file
137
bootloader/src/uart_driver.c
Normal 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--);
|
||||
}
|
||||
495
bootloader/src/usart_sam_ba.c
Normal file
495
bootloader/src/usart_sam_ba.c
Normal 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
218
bootloader/src/utils.c
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue