8 Commits

Author SHA1 Message Date
1a9ef41962 Merge pull request 'feat/filesystem_viewer' (#70) from feat/filesystem_viewer into main
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m51s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
Reviewed-on: #70
Reviewed-by: Matthias Blankertz <matthias@blankertz.org>
2026-01-27 19:26:59 +00:00
69fbb15bca feat: unify bottom margin between scroll container and buttons
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m56s
Check code formatting / Check-C-Format (push) Successful in 8s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
2026-01-27 20:20:22 +01:00
f9a82c121e feat: make intents explicit and rename screen accordingly 2026-01-27 20:19:07 +01:00
245b76e04e Merge pull request 'fix: frontend: Correctly escape filenames in URL parameters' (#69) from fix-url-filenames into main
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m51s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
Reviewed-on: #69
2026-01-27 18:39:32 +00:00
fa4d8debd0 fix: webserver: catch and report exceptions from open and mkdir, too
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m49s
Check code formatting / Check-C-Format (push) Successful in 8s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 4s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 10s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2026-01-27 19:11:47 +01:00
4e9a902a1c doc: added documentation for CORS error mitigation during ui development
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m54s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 8s
Run pytests / Check-Pytest (push) Successful in 11s
2026-01-27 18:41:51 +01:00
bd15a45090 feat: filesystem browser button in nav bar
Previously, the playlist_filebrowser always contained a 'cancel' and
'add tracks' button, because it was used in context of the playlist
editor only.

The playlist_filebrowser now differentiates between the intents it is
being used with. In context of the new filesystem browser button, the
'cancel' and 'add tracks' buttons are actively hidden.
2026-01-27 18:29:33 +01:00
0a20b70478 fix: frontend: Correctly escape filenames in URL parameters
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 4m56s
Check code formatting / Check-C-Format (push) Successful in 8s
Check code formatting / Check-Python-Flake8 (push) Successful in 10s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 9s
Run pytests / Check-Pytest (push) Successful in 11s
Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
2026-01-27 18:15:15 +01:00
10 changed files with 105 additions and 176 deletions

View File

@@ -52,3 +52,23 @@ would be stored in the following key/value pairs in the btree db:
* 00aa11bb22/playlist/00000: a.mp3
* 00aa11bb22/playlist/00001: b.mp3
* 00aa11bb22/playlistpos: 00000
## Notes for UI development with chromium
Features for the web interface are best prototyped in a browser directly. By using the built-in developmer tools and
and their "override" feature, the web contents are replaced by a locally stored copy, which can be used to directly
test the modifications without going all the way through the build and flash process.
However, modern browsers may restrict or even completely forbid the execution of dynamic content like JavaScript, if
the content is stored on the local machine and/or the content is accessed using http. In such a case, chromium issues
an error message similar to the following one:
> Access to fetch at 'http://192.168.4.1/api/v1/audiofiles' from origin 'http://192.168.4.1' has been blocked by CORS
> policy: The request client is not a secure context and the resource is in more-private address space `local`.
To mitigate this, chromium offers two flags that need modification:
- 'chrome://flags/#local-network-access-check' must be `Disabled`
- 'chrome://flags/#unsafely-treat-insecure-origin-as-secure' must be `Enabled`
Note that these settings leave the browser susceptible to security issues and should be returned to
their default values as soon as possible.

View File

@@ -147,6 +147,7 @@
<button onclick="showScreen('menu')">🏠 Main Menu</button>
<button onclick="showScreen('config')">⚙️ Config Editor</button>
<button onclick="showScreen('playlist')">🖹 Playlist Editor</button>
<button onclick="showScreen('filebrowser', 'filesystem')">📂 Filesystem</button>
</nav>
<!-- MAIN MENU -->
@@ -242,10 +243,10 @@
</div>
<!-- PLAYLIST EDITOR SCREEN 3: file browser -->
<div id="screen-playlist_filebrowser" class="screen">
<h2>Playlist Editor</h2>
<div id="screen-filebrowser" class="screen">
<h2 id="playlist-filebrowser-title">Playlist Editor</h2>
<div id="playlist-filebrowser-container">
<div class="scroll-container">
<div class="scroll-container" style="margin-bottom: 10px">
<div class="tree" id="playlist-filebrowser-tree">
<ul><li>Loading...</li></ul>
</div>
@@ -654,7 +655,7 @@
document.getElementById('playlist-edit-removetrack').addEventListener("click", (e) => deleteSelectedTracks());
document.getElementById('playlist-edit-back').addEventListener("click", (e) => showScreen('playlist'));
document.getElementById('playlist-edit-addtrack').addEventListener("click", (e) => {
showScreen("playlist_filebrowser");
showScreen("filebrowser", "playlist");
});
document.getElementById('playlist-edit-save').addEventListener("click", (e) => save());
}
@@ -786,7 +787,9 @@
/* ----------------------------------------
PLAYLIST EDITOR LOGIC - ADD FILES SCREEN
------------------------------------------- */
Screens.playlist_filebrowser = (() => {
Screens.filebrowser = (() => {
let isFilesystemMode = false;
function init() {
document.getElementById('playlist-filebrowser-cancel').addEventListener("click", (e) => {
showScreen("playlist_edit", {});
@@ -805,13 +808,32 @@
});
tree.init();
}
async function onShow(intent) {
console.log(intent)
document.getElementById('playlist-filebrowser-addtrack').disabled = true;
const title = document.getElementById('playlist-filebrowser-title');
const cancelButton = document.getElementById('playlist-filebrowser-cancel');
const addTracksButton = document.getElementById('playlist-filebrowser-addtrack');
if (intent !== 'refresh') {
isFilesystemMode = (intent === 'filesystem');
document.getElementById('playlist-filebrowser-upload-progress').value = 0;
document.getElementById("playlist-filebrowser-upload-files").value = "";
}
if (isFilesystemMode) {
title.innerText = "Filesystem";
cancelButton.style.display = 'none';
addTracksButton.style.display = 'none';
} else {
title.innerText = "Playlist Editor";
cancelButton.style.display = ''
addTracksButton.style.display = ''
}
tree = document.getElementById("playlist-filebrowser-tree");
tree.innerHTML = "Loading...";
fetch('/api/v1/audiofiles')
@@ -938,7 +960,7 @@
}
}
};
xhr.open("POST", `/api/v1/audiofiles?type=file&location=${location}`);
xhr.open("POST", `/api/v1/audiofiles?type=file&location=${encodeURIComponent(location)}`);
xhr.overrideMimeType("audio/mpeg");
xhr.send(files[0]);
}
@@ -956,7 +978,7 @@
const location = selectedNodes.length === 1
? selectedNodes[0].getAttribute('data-path') + '/' + name.value
: '/' + name.value;
const saveRes = await fetch(`/api/v1/audiofiles?type=directory&location=${location}`,
const saveRes = await fetch(`/api/v1/audiofiles?type=directory&location=${encodeURIComponent(location)}`,
{method: 'POST'});
// Reload file list from device
onShow('refresh');
@@ -973,7 +995,7 @@
items.sort();
items.reverse();
for (const item of items) {
const saveRes = await fetch(`/api/v1/audiofiles?location=${item}`,
const saveRes = await fetch(`/api/v1/audiofiles?location=${encodeURIComponent(item)}`,
{method: 'DELETE'});
if (!saveRes.ok) {
alert(`Failed to delete item ${item}: ${await saveRes.text()}`);

View File

@@ -1,14 +1,11 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
#include <stdarg.h>
#include "py/obj.h"
#include "sd.h"
// Include MicroPython API.
#include "py/mperrno.h"
#include "py/mpprint.h"
#include "py/runtime.h"
// This module is RP2 specific
@@ -16,14 +13,6 @@
#include <string.h>
void sd_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
mp_vprintf(&mp_sys_stdout_print, fmt, ap);
va_end(ap);
}
const mp_obj_type_t sdcard_type;
struct sdcard_obj {
mp_obj_base_t base;
@@ -100,14 +89,11 @@ static mp_obj_t sdcard_writeblocks(mp_obj_t self_obj, mp_obj_t block_obj, mp_obj
if (bufinfo.len % SD_SECTOR_SIZE != 0)
mp_raise_ValueError(MP_ERROR_TEXT("Buffer length is invalid"));
const int nblocks = bufinfo.len / SD_SECTOR_SIZE;
bool ret;
if (nblocks > 1) {
ret = sd_writeblocks(&self->sd_context, start_block, nblocks, bufinfo.buf);
} else {
ret = sd_writeblock(&self->sd_context, start_block, bufinfo.buf);
for (int block = 0; block < nblocks; block++) {
// TODO: Implement CMD25 write multiple blocks
if (!sd_writeblock(&self->sd_context, start_block + block, bufinfo.buf + block * SD_SECTOR_SIZE))
mp_raise_OSError(MP_EIO);
}
if (!ret)
mp_raise_OSError(MP_EIO);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_3(sdcard_writeblocks_obj, sdcard_writeblocks);

View File

@@ -6,7 +6,7 @@
#include <stdio.h>
#include <string.h>
extern void sd_printf(const char *fmt, ...);
// #define SD_DEBUG
#define SD_R1_ILLEGAL_COMMAND (1 << 2)
@@ -26,14 +26,14 @@ static bool sd_early_init(void)
for (int i = 0; i < 500; ++i) {
if (sd_cmd(0, 0, 1, &buf)) {
#ifdef SD_DEBUG
sd_printf("CMD0 resp %02x\n", buf);
printf("CMD0 resp %02hhx\n", buf);
#endif
if (buf == 0x01) {
return true;
}
}
#ifdef SD_DEBUG
sd_printf("CMD0 timeout, try again...\n");
printf("CMD0 timeout, try again...\n");
#endif
}
return false;
@@ -44,14 +44,14 @@ static bool sd_check_interface_condition(void)
uint8_t buf[5];
if (sd_cmd(8, 0x000001AA, 5, buf)) {
if ((buf[3] & 0xf) != 0x1 || buf[4] != 0xAA) {
sd_printf("sd_init: check interface condition failed\n");
printf("sd_init: check interface condition failed\n");
return false;
}
} else {
if (buf[0] & SD_R1_ILLEGAL_COMMAND) {
sd_printf("sd_init: check interface condition returned illegal command - old card?\n");
printf("sd_init: check interface condition returned illegal command - old card?\n");
} else {
sd_printf("sd_init: check interface condition failed\n");
printf("sd_init: check interface condition failed\n");
return false;
}
}
@@ -72,12 +72,12 @@ static bool sd_send_op_cond(void)
if (!result) {
if (use_acmd && buf & SD_R1_ILLEGAL_COMMAND) {
#ifdef SD_DEBUG
sd_printf("sd_init: card does not understand ACMD41, try CMD1...\n");
printf("sd_init: card does not understand ACMD41, try CMD1...\n");
#endif
use_acmd = false;
continue;
} else if (buf != 0x01) {
sd_printf("sd_init: send_op_cond failed\n");
printf("sd_init: send_op_cond failed\n");
return false;
} else {
continue;
@@ -87,7 +87,7 @@ static bool sd_send_op_cond(void)
return true;
}
}
sd_printf("sd_init: send_op_cond: timeout waiting for !idle\n");
printf("sd_init: send_op_cond: timeout waiting for !idle\n");
return false;
}
@@ -107,7 +107,7 @@ static void sd_dump_cid [[maybe_unused]] (void)
const uint8_t crc = sd_crc7(15, buf);
const uint8_t card_crc = buf[15] >> 1;
if (card_crc != crc) {
sd_printf("CRC mismatch: Got %02x, expected %02x\n", card_crc, crc);
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc);
// Some cheap SD cards always report CRC=0, don't fail in that case
if (card_crc != 0) {
return;
@@ -122,8 +122,8 @@ static void sd_dump_cid [[maybe_unused]] (void)
uint32_t psn = buf[9] << 24 | buf[10] << 16 | buf[11] << 8 | buf[12];
int mdt_year = 2000 + ((buf[13] & 0xf) << 4 | (buf[14] & 0xf0) >> 4);
int mdt_month = buf[14] & 0x0f;
sd_printf("CID: mid: %02x, oid: %.2s, pnm: %.5s, prv: %02x, psn: %08" PRIx32 ", mdt_year: %d, mdt_month: %d\n",
mid, oid, pnm, prv, psn, mdt_year, mdt_month);
printf("CID: mid: %02hhx, oid: %.2s, pnm: %.5s, prv: %02hhx, psn: %08" PRIx32 ", mdt_year: %d, mdt_month: %d\n",
mid, oid, pnm, prv, psn, mdt_year, mdt_month);
}
}
@@ -131,13 +131,13 @@ static bool sd_read_csd(struct sd_context *sd_context)
{
uint8_t buf[16];
if (!sd_cmd_read(9, 0, 16, buf)) {
sd_printf("Failed to read CSD\n");
printf("Failed to read CSD\n");
return false;
}
const uint8_t crc = sd_crc7(15, buf);
const uint8_t card_crc = buf[15] >> 1;
if (card_crc != crc) {
sd_printf("CRC mismatch: Got %02x, expected %02x\n", card_crc, crc);
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc);
// Some cheap SD cards always report CRC=0, don't fail in that case
if (card_crc != 0) {
return false;
@@ -151,12 +151,12 @@ static bool sd_read_csd(struct sd_context *sd_context)
switch (csd_ver) {
case 0: {
if (sd_context->sdhc_sdxc) {
sd_printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n");
printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n");
return false;
}
const unsigned read_bl_len = buf[5] & 0xf;
if (read_bl_len < 9 || read_bl_len > 11) {
sd_printf("Invalid read_bl_len in CSD 1.0\n");
printf("Invalid read_bl_len in CSD 1.0\n");
return false;
}
blocksize = 1 << (buf[5] & 0xf);
@@ -176,15 +176,15 @@ static bool sd_read_csd(struct sd_context *sd_context)
break;
}
case 2: {
sd_printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n");
printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n");
return false;
}
}
sd_context->blocks = blocks;
sd_context->blocksize = blocksize;
#ifdef SD_DEBUG
sd_printf("CSD version %u.0, blocksize %u, blocks %u, capacity %u MiB, max speed %u\n", version, blocksize, blocks,
(uint32_t)(((uint64_t)blocksize * blocks) / (1024 * 1024)), max_speed);
printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB, max speed %u\n", version, blocksize, blocks,
((uint64_t)blocksize * blocks) / (1024 * 1024), max_speed);
#endif
return true;
}
@@ -205,11 +205,11 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
uint32_t ocr;
if (!sd_read_ocr(&ocr)) {
sd_printf("sd_init: read OCR failed\n");
printf("sd_init: read OCR failed\n");
goto out_spi;
}
if ((ocr & 0x00380000) != 0x00380000) {
sd_printf("sd_init: unsupported card voltage range\n");
printf("sd_init: unsupported card voltage range\n");
goto out_spi;
}
@@ -219,11 +219,11 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
sd_spi_set_bitrate(rate);
if (!sd_read_ocr(&ocr)) {
sd_printf("sd_init: read OCR failed\n");
printf("sd_init: read OCR failed\n");
goto out_spi;
}
if (!(ocr & (1 << 31))) {
sd_printf("sd_init: card not powered up but !idle?\n");
printf("sd_init: card not powered up but !idle?\n");
goto out_spi;
}
sd_context->sdhc_sdxc = (ocr & (1 << 30));
@@ -234,20 +234,20 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
if (sd_context->blocksize != SD_SECTOR_SIZE) {
if (sd_context->blocksize != 1024 && sd_context->blocksize != 2048) {
sd_printf("sd_init: Unsupported block size %u\n", sd_context->blocksize);
printf("sd_init: Unsupported block size %u\n", sd_context->blocksize);
goto out_spi;
}
// Attempt SET_BLOCKLEN command
uint8_t resp[1];
if (!sd_cmd(16, SD_SECTOR_SIZE, 1, resp)) {
sd_printf("sd_init: SET_BLOCKLEN failed\n");
printf("sd_init: SET_BLOCKLEN failed\n");
goto out_spi;
}
// Successfully set blocksize to SD_SECTOR_SIZE, adjust context
sd_context->blocks *= sd_context->blocksize / SD_SECTOR_SIZE;
#ifdef SD_DEBUG
sd_printf("Adjusted blocksize from %u to 512, card now has %u blocks\n", sd_context->blocksize,
sd_context->blocks);
printf("Adjusted blocksize from %u to 512, card now has %u blocks\n", sd_context->blocksize,
sd_context->blocks);
#endif
sd_context->blocksize = SD_SECTOR_SIZE;
}
@@ -307,7 +307,7 @@ bool sd_readblock_complete(struct sd_context *sd_context)
bool sd_readblock_is_complete(struct sd_context *sd_context) { return sd_cmd_read_is_complete(); }
bool sd_writeblock(struct sd_context *sd_context, const size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE])
bool sd_writeblock(struct sd_context *sd_context, size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE])
{
if (!sd_context->initialized || sector_num >= sd_context->blocks)
return false;
@@ -319,20 +319,3 @@ bool sd_writeblock(struct sd_context *sd_context, const size_t sector_num, uint8
}
return sd_cmd_write(24, addr, SD_SECTOR_SIZE, buffer);
}
bool sd_writeblocks(struct sd_context *sd_context, const size_t sector_num, const size_t sectors, uint8_t *const buffer)
{
if (!sd_context->initialized || sector_num + sectors >= sd_context->blocks)
return false;
if (!sd_context->sdhc_sdxc) {
// Don't use multi-block writes for SDSC for now
// Need to configure WRITE_BL_LEN correctly
for (size_t sector = 0; sector < sectors; ++sector) {
if (!sd_writeblock(sd_context, sector_num + sector, buffer + sector * SD_SECTOR_SIZE))
return false;
}
return true;
}
return sd_cmd_write_multiple(25, sector_num, sectors, SD_SECTOR_SIZE, buffer);
}

View File

@@ -24,4 +24,3 @@ bool sd_readblock_complete(struct sd_context *context);
bool sd_readblock_is_complete(struct sd_context *context);
bool sd_writeblock(struct sd_context *context, size_t sector_num, uint8_t buffer[const static SD_SECTOR_SIZE]);
bool sd_writeblocks(struct sd_context *sd_context, const size_t sector_num, const size_t sectors, uint8_t *buffer);

View File

@@ -12,8 +12,6 @@
#include <pico/time.h>
#include <string.h>
extern void sd_printf(const char *fmt, ...);
typedef enum { DMA_READ_TOKEN, DMA_READ, DMA_IDLE } sd_dma_state;
struct sd_dma_context {
@@ -224,7 +222,7 @@ bool sd_cmd_read_complete(void)
sd_spi_read_blocking(0xff, &buf, 1);
if (sd_spi_context.sd_dma_context.read_token_buf != 0xfe) {
#ifdef SD_DEBUG
sd_printf("read failed: invalid read token %02x\n", sd_spi_context.sd_dma_context.read_token_buf);
printf("read failed: invalid read token %02hhx\n", sd_spi_context.sd_dma_context.read_token_buf);
#endif
return false;
}
@@ -233,7 +231,7 @@ bool sd_cmd_read_complete(void)
const uint16_t act_crc = sd_spi_context.sd_dma_context.crc_buf[0] << 8 | sd_spi_context.sd_dma_context.crc_buf[1];
if (act_crc != expect_crc) {
#ifdef SD_DEBUG
sd_printf("read CRC fail: got %04x, expected %04x\n", act_crc, expect_crc);
printf("read CRC fail: got %04hx, expected %04hx\n", act_crc, expect_crc);
#endif
return false;
}
@@ -241,9 +239,10 @@ bool sd_cmd_read_complete(void)
return true;
}
static bool sd_cmd_write_begin(uint8_t cmd, uint32_t arg)
bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen])
{
uint8_t buf[1];
uint8_t buf[2];
const uint16_t crc = sd_crc16(datalen, data);
sd_spi_cmd_send(cmd, arg);
// Read up to 8 garbage bytes (0xff), followed by R1 (MSB is zero)
bool got_r1 = false;
@@ -254,20 +253,9 @@ static bool sd_cmd_write_begin(uint8_t cmd, uint32_t arg)
break;
}
}
if (!got_r1 || buf[0] != 0x00) {
#ifdef SD_DEBUG
sd_printf("write cmd fail: %02x\n", buf[0]);
#endif
return false;
}
return true;
}
static bool sd_cmd_write_block(uint8_t token, unsigned datalen, uint8_t data[const static datalen])
{
uint8_t buf[2];
const uint16_t crc = sd_crc16(datalen, data);
buf[0] = token;
if (!got_r1 || buf[0] != 0x00)
goto abort;
buf[0] = 0xfe;
sd_spi_write_blocking(buf, 1);
sd_spi_write_blocking(data, datalen);
buf[0] = crc >> 8;
@@ -276,16 +264,11 @@ static bool sd_cmd_write_block(uint8_t token, unsigned datalen, uint8_t data[con
sd_spi_read_blocking(0xff, buf, 1);
if ((buf[0] & 0x1f) != 0x5) {
#ifdef SD_DEBUG
sd_printf("Write fail: %2x\n", buf[0]);
printf("Write fail: %2hhx\n", buf[0]);
#endif
return false;
goto abort;
}
return true;
}
static bool sd_cmd_write_wait_nbusy(void)
{
uint8_t buf[1];
int timeout = 0;
bool got_done = false;
for (timeout = 0; timeout < 131072; ++timeout) {
@@ -296,73 +279,18 @@ static bool sd_cmd_write_wait_nbusy(void)
}
}
#ifdef SD_DEBUG
sd_printf("dbg write end: %d, %2x\n", timeout, buf[0]);
printf("dbg write end: %d, %2hhx\n", timeout, buf[0]);
#endif
return got_done;
}
bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen])
{
#ifdef SD_DEBUG
sd_printf("write 1 block at %u\n", arg);
#endif
uint8_t buf[2];
if (!sd_cmd_write_begin(cmd, arg))
goto abort;
if (!sd_cmd_write_block(0xfe, datalen, data))
goto abort;
if (!sd_cmd_write_wait_nbusy())
if (!got_done)
goto abort;
gpio_put(sd_spi_context.ss, true);
sd_spi_read_blocking(0xff, buf, 1);
#ifdef SD_DEBUG
sd_printf("write ok\n");
#endif
return true;
abort:
gpio_put(sd_spi_context.ss, true);
sd_spi_read_blocking(0xff, buf, 1);
#ifdef SD_DEBUG
sd_printf("write fail\n");
#endif
return false;
}
bool sd_cmd_write_multiple(uint8_t cmd, uint32_t arg, unsigned blocks, unsigned datalen, uint8_t *const data)
{
#ifdef SD_DEBUG
sd_printf("write %u blocks at %u\n", blocks, arg);
#endif
uint8_t buf[2];
if (!sd_cmd_write_begin(cmd, arg))
goto abort;
for (unsigned i = 0; i < blocks; ++i) {
if (!sd_cmd_write_block(0b11111100, datalen, data + datalen * i))
goto abort;
if (!sd_cmd_write_wait_nbusy())
goto abort;
}
buf[0] = 0b11111101;
buf[1] = 0xff;
sd_spi_write_blocking(buf, 2);
if (!sd_cmd_write_wait_nbusy())
goto abort;
gpio_put(sd_spi_context.ss, true);
sd_spi_read_blocking(0xff, buf, 1);
#ifdef SD_DEBUG
sd_printf("write ok\n");
#endif
return true;
abort:
gpio_put(sd_spi_context.ss, true);
sd_spi_read_blocking(0xff, buf, 1);
#ifdef SD_DEBUG
sd_printf("write fail\n");
#endif
return false;
}

View File

@@ -27,4 +27,3 @@ bool sd_cmd_read_complete(void);
bool sd_cmd_read_is_complete(void);
bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen]);
bool sd_cmd_write_multiple(uint8_t cmd, uint32_t arg, unsigned blocks, unsigned datalen, uint8_t *const data);

View File

@@ -235,16 +235,19 @@ async def audiofile_upload(request):
if type_ == 'directory':
if length != 0:
return 'directory request may not have content', 400
os.mkdir(path)
return '', 204
with open(path, 'wb') as newfile:
try:
os.mkdir(path)
except OSError as ex:
return f'error creating directory: {ex}', 500
return '', 204
try:
with open(path, 'wb') as newfile:
if length > Request.max_body_length:
bytes_copied = await stream_to_file(request.stream, newfile, length)
else:
bytes_copied = newfile.write(request.body)
except OSError as ex:
return f'error writing data to file: {ex}', 500
except OSError as ex:
return f'error writing data to file: {ex}', 500
if bytes_copied == length:
return '', 204
else:

View File

@@ -15,8 +15,6 @@ option(ENABLE_PLAY_TEST "Enable mp3 playback test" OFF)
option(ENABLE_SD_READ_CRC "Enable crc check when reading from sd card" OFF)
option(ENABLE_SD_DEBUG "Enable debug output for sd card driver" OFF)
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
# initialize the Raspberry Pi Pico SDK
pico_sdk_init()
@@ -64,7 +62,7 @@ add_subdirectory(../../lib/helix_mp3 helix_mp3)
target_link_libraries(standalone_mp3 PRIVATE pico_stdlib hardware_dma hardware_spi hardware_sync hardware_pio helix_mp3)
target_include_directories(standalone_mp3 PRIVATE ${SD_LIB_DIR})
target_compile_options(standalone_mp3 PRIVATE -Og)
target_compile_options(standalone_mp3 PRIVATE -Og -DSD_DEBUG)
pico_add_extra_outputs(standalone_mp3)

View File

@@ -2,7 +2,6 @@
#include "sd.h"
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
@@ -21,14 +20,6 @@ extern void sd_spi_dbg_clk(const int div, const int frac);
extern void sd_spi_dbg_loop(void);
void sd_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
#define MAX_VOLUME 0x8000u
void __time_critical_func(volume_adjust)(int16_t *restrict buf, size_t samples, uint16_t scalef)
@@ -166,7 +157,7 @@ static void write_test(struct sd_context *sd_context)
data_buffer[i] ^= 0xff;
}
if (!sd_writeblocks(sd_context, 0, sizeof(data_buffer) / SD_SECTOR_SIZE, data_buffer)) {
if (!sd_writeblock(sd_context, 0, data_buffer)) {
printf("sd_writeblock failed\n");
return;
}