Compare commits

...

65 commits

Author SHA1 Message Date
Solomon Peachy
1f30d9efe3 FS#13769 - Updated Italian translation (Alessio Lenzi)
Change-Id: Ieeefb31f70f1924c8de25ea640ad75e17cac40f5
2026-01-29 17:16:51 -05:00
Solomon Peachy
2a4cb29b27 synopsis-dwmac: Make ep0_buffer (and friends) static.
Looks like an oversight

Change-Id: Ia730fd1f9e19ca31add581400372dfacead7b452
2026-01-28 15:55:45 -05:00
Solomon Peachy
62200ff4d7 FS#13768: Updated Serbian Translation (Ivan Pesic)
Change-Id: Iae502c6757823ae00ec22a65d83c3e151d0c4c55
2026-01-27 19:48:37 -05:00
Solomon Peachy
83420e0a95 voice: Switch default en_GB TTS voice to Piper's 'alba' #2
The previous commit just switched the voice used for the nightlies; this
changes the default used when using the cmdline voice.pl tool without
the user overriding it with something else.

Change-Id: I5144fe66e355f3c41677ca37226a743667d291bf
2026-01-27 17:45:19 -05:00
Solomon Peachy
30ff611529 voice: Switch default en_GB TTS voice to Piper's 'alba'.
Does better for short phrases/words than 'semaine'. "No/Yes" and
numbers in were pretty bad in particular.

Change-Id: I795ad57b7ca8c5b8a3fe0c2b721ef167d0dd9f6d
2026-01-27 17:27:20 -05:00
Solomon Peachy
b349eea2c9 usb: fix yellow in eb69211791
Bootloaders with HAVE_USBSTACK but without HAVE_BOOTLOADER_USB_MODE end
up with USB_NUM_DRIVERS of 0 which leads to a warning due to a signed
number being checked to see if it's >= 0.

Work around this temporarily; the proper fix is to not build usb_core
and its class drivers when BOOTLOADER & !HAVE_BOOTLOADER_USB_MODE

Change-Id: I1b41140d31ba9df6b4c760478c4265d4e5584963
2026-01-27 15:23:47 -05:00
mojyack
eb69211791 usb: introduce USB_NOTIFY_CLASS_DRIVER
this invokes specified class driver's notify_event method.
initial purpose is to trigger a callback from isr, like a timer event.

Change-Id: Id600e9f0d8840a12da779d5a15783edf14bd76b5
2026-01-27 13:39:58 -05:00
Solomon Peachy
fce8248267 usb: Hopefully resolve the red in 41f9285def
First, leave USB_FULL_INIT on for all non-bootloader builds; this is
needed for devices that don't have a USBSTACK (such as most hosted
targets and ones that provide USB<>ATA in hardware)

Then unwind another hack that is no longer needed now that USB_FULL_INIT
is not set in most bootloaders.

Change-Id: I00881ac76b2469e5cd7700bad2203c58ef1e09e7
2026-01-27 13:05:57 -05:00
Solomon Peachy
a5b589cf5b vx747: Target needs HAVE_BOOTLOADER_USB_MODE
Change-Id: I735cbf5575b0368741f2b8147eb80a71e0a67c98
2026-01-27 12:21:42 -05:00
Solomon Peachy
41f9285def usb: Clean up the pile of USB_FULL_INIT exceptions
The intent here is that when HAVE_USBSTACK is not defined, or we are
in a bootloader wthout HAVE_BOOTLOADER_USB_MODE, a device may still
some of USB subsystem initialized.  For example, this may be needed
to enable USB-based charging functionality.

So, get rid of the blanket enables of USB_FULL_INIT based on target SoC,
enabling HAVE_BOOTLOADER_USB_MODE on targets that need it, and clean up
the initial mess. Most of this mess is because usb_core.c has no sense
of USB_FULL_INIT or not, and is always included when HAVE_USBSTACK is
set (even in bootloaders without BOOTLOADER_USB_MODE), but dealing with
that latter case will come later.

Change-Id: I7f805b89dded39aeea2db9038209780069e3b600
2026-01-27 10:27:09 -05:00
Aidan MacDonald
7b77752aff echoplayer: reduce USB storage buffer size for bootloader
This saves 64k of BSS, freeing up space in AXI SRAM.

Change-Id: I37079c5fa5ed2b969d70622491dbf0f4dca2c759
2026-01-27 10:05:20 -05:00
Aidan MacDonald
d15bb5f848 stm32h7: move LCD framebuffer to AXI SRAM
Framebuffer access consumes a lot of SDRAM bandwidth.
Moving it to AXI SRAM should be a big improvement as
it's around 8x faster (2x clock speed, 4x bus width).

Also take advantage of explicitly assigning sections
to the special :NONE segment, which means they will
not appear in any ELF program header and thus aren't
visible to the bootloader. They can then overlap
areas used by the bootloader -- by the time they're
written, the bootloader will be long gone -- making
it easier to make efficient use of SRAM.

Change-Id: I2392fd23b17472cc08dc5fe4556f6def3cc186ed
2026-01-27 10:04:59 -05:00
Aidan MacDonald
77f30202d0 tools: detect Git revision correctly in non-default worktrees
In a non-default Git worktree, the .git directory is replaced
by a file containing the path to the real .git directory. This
breaks the version detection logic because it expects .git to
be a directory.

Passing the root of the source tree via "git -C", letting Git
figure out if we're in a repo or not, solves this problem.

Change-Id: I595f1a694258cad490b1a4964f8ae9d51ae76de1
2026-01-27 09:22:31 -05:00
Aidan MacDonald
2b09078b92 stm32h7: set 8-byte alignment on main and irq stack
Explicitly set 8-byte alignment as per the AAPCS, which
says the stack should be 8-byte aligned at a public ABI
boundary.

Change-Id: Ie60b664718119ea576e7c6b5efaac011eb907531
2026-01-27 13:31:12 +00:00
Solomon Peachy
e1d10c938a config: Targets must opt-in to bootloader USB storage support
ie by only using HAVE_BOOTLOADER_USB_MODE, instead of blanket-enabling
USB_ENABLE_STORAGE for numerous SoC families

Change-Id: Ief433a1d693876072779e714883438c0012ba2e0
2026-01-27 07:36:40 -05:00
Solomon Peachy
33678c6c3c s3c2440: Fix some compilation failures in the mini2440 bootloader
This whole platform has bitrotten, may be prudent to just nuke it.

Change-Id: Ia12e7ebc160484e57a74dd689d6095d22f3dbfd9
2026-01-27 07:11:32 -05:00
Solomon Peachy
b562c9d58f Fix red introduced in 33d0a3ef
Many bootloaders broke because they have only partial USB
implementations -- HAVE_USBSTACK but without HAVE_BOOTLOADER_USB_MODE...
and a pile of exceptions.

Those exceptions need to be cleaned up properly, but for now, get the
build going again by wrapping the new ack-tracking bits with the
USB_FULL_INIT define created by the above conditions.

Change-Id: I936d4989b8c8195ee30d53808f61104a4986942a
2026-01-26 22:56:47 -05:00
Solomon Peachy
89cf5b57e6 Fix yellow from 1951c17e0b on targets with USB_NONE
gui_usb_screen_run() is a do{} while(0) macro, resulting in an unused
variable warning in the "caller"

Change-Id: I4b4b00ef38decfb5cc9db0da3d81ad0c9a4207d1
2026-01-26 20:36:58 -05:00
mojyack
33d0a3efa3 usb: enter to exclusive disk mode only when required by usb config
currently, exclusive disk mode was enabled at the same time as the
usb insertion. when multiple usb configurations exist, this is not
appropriate.

manage it in the core and enable it only when necessary.

Change-Id: Iadbec05fad1d1319471233227ae0e72c12079295
2026-01-26 19:52:22 -05:00
mojyack
1951c17e0b pass event data to usb_acknowledge
add second argument to usb_acknowledge.
it can be used for more appropriate connection tracking that does not
rely on timeout in the future.

Change-Id: I8a44366b7c7a1f944524c4ba8ecd6d9673746a65
2026-01-26 19:47:09 -05:00
Aidan MacDonald
d5506dfa22 echoplayer: implement boot from debugger
Add a 'make start' target which starts Rockbox using a
debugger. This only works to load the main binary, but
makes it much faster to test changes that don't affect
plugins/codecs.

Because SDRAM isn't accessible at reset and the main
binary is usually too big to fit in SRAM, the bootloader
must be flashed first before Rockbox can be loaded in
this way.

The boot protocol involves GDB writing a check pattern
to SRAM while the CPU is held in reset. The bootloader
detects the pattern and takes a breakpoint, by which
time SDRAM is accessible; GDB can then upload a binary
ELF image (copied as a raw file, since the ELF will be
loaded using RB's ELF loader) and leave the breakpoint
to continue booting.

From there the bootloader can load the ELF binary from
memory, exactly like a normal SD card boot.

Change-Id: I4eb971b4162ea422e38660455cfa0958cefaa18d
2026-01-26 08:55:01 -05:00
Aidan MacDonald
603c5bb2be echoplayer: load Rockbox from SD card in bootloader
To avoid problems with SDMMC DMA not being able to
access all SRAMs equally, the ELF binary is loaded
at the top of SDRAM and then copied into place.

Change-Id: Icf16d02bc15605539cbe781dd27709225abca8f9
2026-01-25 19:51:56 -05:00
Aidan MacDonald
7868e45edb elf_loader: add elf_loadmem() to load file from memory
Change-Id: Ib68b0e8b610d45713a2d5480da015cc4295f2676
2026-01-25 19:51:07 -05:00
Aidan MacDonald
843322f898 elf_loader: add generic callback-based loader
Change-Id: I1dd972a585bc7c805e32c9665d13e248663ccc73
2026-01-25 19:50:12 -05:00
Aidan MacDonald
a610998ea4 firmware: add simple ELF loader for static binaries
This is a small & simple ELF loader which just copies
program segments from disk to memory. It only supports
static binaries right now.

Change-Id: I8944feb5b9dafcc5c56e12383aed25e2718ad7ea
2026-01-25 18:19:51 -05:00
Aidan MacDonald
21ba79d431 echoplayer: disable parts of system_init() for application
Most of system_init() should only be performed once after
reset, and is not safe to re-run when the bootloader jumps
to the application.

Change-Id: I4d2e804ce4884da13b9167ddcda860ef3b5ba7d0
2026-01-25 14:12:29 -05:00
Aidan MacDonald
f642d19c20 echoplayer: initial Echo R1 keymap
Change-Id: Id720378a55425070ef69ca002c2e954cc1e147cf
2026-01-25 17:26:44 +00:00
Solomon Peachy
ecb8014a06 FS#13766: Updated Vietnamese Translation (Chu Khanh Hanh)
Change-Id: I3cf9cf9814a549e49eecd7bed0e364c2b2b1439b
2026-01-25 08:59:10 -05:00
Aidan MacDonald
98990df08f stm32h7: update linker scripts for ELF boot
Use flash & AXI SRAM for the bootloader, ITCM/DTCM and
SDRAM for Rockbox. Hardly the most optimal use of SRAMs
but it's good enough to get started.

Fixes the Echo R1 app build, which wasn't fitting in
AXI SRAM.

Change-Id: I4f7e5f618d27b553e5ff8dec1d5c4c61ac9d8eb0
2026-01-25 08:48:44 -05:00
Aidan MacDonald
174b33cc07 stm32h7: rename startup code .init.text section to .init
Do this for compatibility with INIT_ATTR, which uses the
.init section.

Change-Id: I473c29e3d38e5d5a563c98fc910d725024072735
2026-01-25 08:47:44 -05:00
Aidan MacDonald
bd73e0dd42 stm32h7: do bss/data setup in crt0 for bootloader only
Change-Id: I67798a4370b01127364c40328549a6d3081545cf
2026-01-25 07:47:32 -05:00
Aidan MacDonald
e3bf9210ab echoplayer: disable boot data
The current bootdata mechanism won't work well for
ELF binaries so disable it.

Change-Id: If6721ef88a38af76711bbdc6b561529dc3b33557
2026-01-25 07:47:22 -05:00
Aidan MacDonald
0f5c42122c echoplayer: implement bootloader power on/off logic
On the Echo R1, the main regulator is enabled primarily by
the power button and USB input, and secondarily by the CPU's
own output pins (cpu_power_on signal or RTC alarm output).

From a user perspective, the player should appear to power
up and down only if the power button is long pressed, which
must be implemented in software. These logical power states
are called "active" and "inactive" in the bootloader.

Going from inactive to active will attempt to boot Rockbox
unless a button (d-pad down) is held to enter bootloader
USB mode instead. Going from active to inactive will shut
down the player. The bootloader will also automatically
shut down after a short timeout, if USB is not plugged in.

In the inactive state, the player is supposed to enumerate
over USB so it can negotiate a charging current, but should
otherwise appear "off". In particular it shouldn't expose
mass storage or even power up the SD card, nor power up the
LCD/backight. This isn't implemented yet, because there's
no way to dynamically change USB configurations (eg. going
from active to inactive should trigger re-enumeration to
switch to charge only mode). To avoid surprising behavior,
the bootloader will just boot Rockbox immediately if USB is
plugged in at boot.

Change-Id: Icd1d48ef49a31eb32b54d440e9211aaf40c6b974
2026-01-25 07:46:31 -05:00
Roman Artiukhin
58ace97a4e hiby: r1_patcher: embed hiby_player.sh
Make it a single-file, self-contained script.

Change-Id: Idb3e375f1f9774c9328288765c7d1c7c1e01e2d3
2026-01-24 20:20:01 -05:00
Roman Artiukhin
db8494d4e0 hiby: r1_patcher: quote path variables
Change-Id: I5d03b4ecec1847c404a2f2400b648469023942e2
2026-01-24 20:20:01 -05:00
Solomon Peachy
b056191e89 Translation updates:
* German (Wilfried Winkler)
 * Korean (Hoseok Seo)
 * Polish (Adam Rak)
 * Slovak (Matej Golian)
 * US English (Myself)

Change-Id: Ief4de3c8f6aa88aba6ffae9969d22763a1969743
2026-01-24 17:44:36 -05:00
Aidan MacDonald
721bfac475 echoplayer: don't wake LCD automatically in bootloader
Normally the bootloader won't enable the LCD, but we
still want to call lcd_init() at startup to keep the
code simple.

Change-Id: I866ecd7c81b6c5e6acdd57f5d7680400df3f54f4
2026-01-24 14:05:21 -05:00
Aidan MacDonald
1afa2ca50d echoplayer: implement LCD enable and shutdown
This also gets rid of the long startup delays which were
leftovers from debugging.

Change-Id: Iee9fec1fb210a006a1af53bf47535d7ba2ffa904
2026-01-24 14:02:57 -05:00
Aidan MacDonald
025d641d1f echoplayer: detect boot reason during system_init
Change-Id: I6fdc16e7476044b43e11d3f63d971bd8de860c58
2026-01-24 14:02:26 -05:00
Aidan MacDonald
86c975ee88 echoplayer: set cpu_power_on pin high as early as possible
Change-Id: I6b7343cc81973c4c0a14396b2ecd622fc1c65d30
2026-01-24 13:02:03 -05:00
Aidan MacDonald
319fdcc506 echoplayer: implement reboot using RTC_OUT pin
Change-Id: Ibeda362030c2885abe1366fb3256e925ea27538c
2026-01-24 13:01:56 -05:00
William Wilgus
6035b1fc1b [lua][BUGFIX] splash_scroller FS#13753, 13754 unicode + default font/fg/bg
FS#13753 - Rocklua error splash don't display part or full filename when
it is non-ASCII
FS#13754 - Rocklua error splash don't reset gui settings (bg, fg and font)

when using signed char unicode codepoints can show up as < '0'
code was looking to remove control chars < ' ' which also removed
unicode code points in the process

Change-Id: I48074a7854f2bee195ca554e1f86505fe5567db6
2026-01-24 09:19:19 -05:00
Solomon Peachy
4e4d5ac25b Fix yellow in echoplayer bootloader
Regression introduced in 7f0bc4bd95

Change-Id: I1341319d23a2813b8473e3fceb3370bf6371ef86
2026-01-24 09:13:49 -05:00
Aidan MacDonald
65b97917ca echoplayer: replace stm32 clock init with target specific init
Change-Id: Ib858f95b4cedba261ea669d3339ea1497e970982
2026-01-24 08:09:42 -05:00
Aidan MacDonald
eea0c128f4 echoplayer: move system_init() out of generic stm32 code
With only one target, it doesn't make sense to have a
common system_init() yet.

Change-Id: I0f6d37709d60bb309fb16ecb9b0870297a189cc4
2026-01-24 08:08:40 -05:00
Aidan MacDonald
386be9dfcc stm32h7: refactor and simplify clock helper functions
The clock helpers are only used for leaf clocks of single
peripherals, which don't benefit from reference counting.

Change-Id: Ica5685e7bc0fce621ae46f758f0ad0b1dcfb2789
2026-01-24 08:07:25 -05:00
Aidan MacDonald
9471cec2ab stm32h7: make cache initialization function public
Change-Id: I460887977f18bfb411d33b6d4efc24ac04cb4050
2026-01-24 08:04:34 -05:00
Aidan MacDonald
62332841cf stm32h7: refactor systick code
Make systick setup less hardcoded, and create a public API
for use by targets, in preparation for moving system_init()
into target-specific code.

Improve the implementation of udelay to make it more robust
against timer wraparound.

Change-Id: I21bb8821cfd1d7e4049fac6e6a4548d80a4276f7
2026-01-24 08:03:50 -05:00
Solomon Peachy
b76cb3bf62 xduoox3: A couple of minor cleanups in the bootloader
* When SHOW_LOGO is not defined, print the version at the
   same time as the logo would have been displayed
 * Don't re-init the display after every message in USB mode

Change-Id: Ida0f5643b1d57004877ec5c42fc14028f53b1c89
2026-01-24 07:55:56 -05:00
Solomon Peachy
7f0bc4bd95 bootloader: Share definition of show_logo via common.h
For some reason it was locally defined everywhere.  Move a single
definition into common.h instead.

Change-Id: Ie2fad74acccd89e40fcbb0f47258d2e14e0f8285
2026-01-24 07:54:24 -05:00
mojyack
5af0a50031 arm: pp: usb-fw-pp502x: remove hack in usb_attach()
not only is this unnecessary, but it causes a bus reset on certain iPod
docks.

Change-Id: I2c2cbf3e5660d0cf25fc6ff4367c23396c521a3f
2026-01-24 07:25:59 -05:00
William Wilgus
2b6029ae8d rliimg add flip image example (#FS13745) Try #2
FS#13745 - Add transpose (like in python pillow module) and smooth resizing in rocklua

most of this is supplied 'require image'

flipping an image is built-in to the image generator function

add image flip example to rliimg

pushed the wrong script

Change-Id: I2c512a70ed26b0457e46f2f2a187d3365783af67
2026-01-24 01:43:58 -05:00
William Wilgus
b3683c84d8 [FIX Yellow] kbd_helper.c unsigned / signed
Change-Id: I10382b5ad930d350945da950a5d829c000e18f31
2026-01-23 23:00:12 -05:00
William Wilgus
a0bd28a408 rliimg add flip image example (#FS13745)
FS#13745 - Add transpose (like in python pillow module) and smooth resizing in rocklua

most of this is supplied 'require image'

flipping an image is built-in to the image generator function

add image flip example to rliimg

Change-Id: Ia53933c9ad09d2a5b92af45eb7a1140b64b6ae01
2026-01-23 22:43:08 -05:00
William Wilgus
1194a968bd [BUGFIX] pluginlib kbd_helper.c buffer ovfl
sizeof is in bytes len counts as a ucschar
should also be added to total_len

yep buffer ovfl

Change-Id: Ibebb8574e6dbab0ca78d1cdfeb04cbbda38f05e9
2026-01-23 21:58:32 -05:00
William Wilgus
163c3723bc announce_status re-fix splashf voice prompts, add missing lang entry
%s thru an ID2P() won't be voiced

Change-Id: Ibce1e818a6ae35c388f75182c3a8226371b420b3
2026-01-23 21:05:44 -05:00
Aidan MacDonald
d7a2aa7208 arm: add more Cortex-M SCB registers
Fix a typo in the CCR register while here.

Change-Id: I9b41ca48f466557683c4b678831f3e5eccec5587
2026-01-23 23:29:47 +00:00
Aidan MacDonald
72dd8bc4d0 sdmmc_host: implement sd_enable()
This is mainly useful for bootloaders that want to safely
disable the SD/MMC controller before booting. Disabling a
controller will reset and power down the bus; all attempts
to read or write to a disabled controller will fail.

Change-Id: I4a7ec4287f2b8510a35d964cc806c74be8c86406
2026-01-23 17:48:08 -05:00
Aidan MacDonald
80fec463df stm32h7: panic on sdmmc FIFO or DMA errors
FIFO errors shouldn't be possible with hardware flow
control enabled. DMA errors shouldn't occur unless a
bad memory address was passed.

Don't bother checking for ITCM/DTCM in the transfer
setup and instead just wait for the IDMATE error; if
the RM0433 reference manual is to be believed then
SDMMC1 _only_ has access to AXI bus memories, and
checking for all invalid destinations would be very
verbose.

Change-Id: I2b22b56009933e16c5adde4d36b7a906cee57791
2026-01-23 17:47:46 -05:00
Aidan MacDonald
a74ee4c04f sdm32h7: enable sdmmc hardware flow control
Hardware flow control prevents FIFO underruns/overruns
by stopping the bus clock if one would occur. This can
slow down transfers, but that's better than having data
transfer fail due to AXI/AHB bus contention.

Change-Id: I8696d3aff78c17dbbe85907160fa37fd4ee11e85
2026-01-23 17:47:27 -05:00
Aidan MacDonald
5442622d88 arm: fix Cortex-M IRQ masking
The following inline assembly in set_irq_level() turned out
to have incorrect constraints:

    int newvalue = /* input parameter */;
    int oldvalue;

    asm volatile ("mrs %0, primask\n"
                  "msr primask, %1\n"
                  : "=r"(oldvalue) : "r"(newvalue));

leading to incorrect code generation for common cases like
disable_irq_save(), which compiles to:

    mov r5, #1
    mrs r5, primask
    msr primask, r5

...which doesn't disable IRQs at all, since both of the
operands got assigned to the same register; the write of
'oldvalue' clobbers the 'newvalue' input before it's used.

Apparently GCC assumes that input operands are read before
output operands are written. One way to fix this is adding
the '&' constraint: "=&r"(oldvalue), but it's better to
break things down into separate, simpler asm statements
which GCC can figure out itself.

Also add compiler memory barriers where primask is modified
to ensure loads/stores aren't incorrectly moved outside of
critical sections.

While here, optimize disable_irq_save() a bit by using the
cpsid instruction, which avoids the extra "mov" and register
allocation needed by "msr primask".

Change-Id: Iac94a76db5bac399a1cf028da4241a0473259a46
2026-01-23 16:47:46 -05:00
Aidan MacDonald
0474dca7c3 x1000: add XT26G01CWSIGA flash chip
Recently seen in Hifi Walker H2 units. Links:

- https://datasheet.lcsc.com/lcsc/2108141930_XTX-XT26G01CWSIGA_C2833378.pdf
- https://www.xtxtech.com/en/Products/info.aspx?productModel=XT26G01CWSIGA

Change-Id: Ieef9974d2b13723047b020ff0ab2498bf18ca00d
2026-01-23 16:26:28 -05:00
Aidan MacDonald
17faa6abc7 stm32h743: fix 'make zip' error caused by config header
Change-Id: I12521cbfdab3c768015f4eafdfe95adf81ba520a
2026-01-23 16:21:57 +00:00
William Wilgus
c80723b539 announce_status plugin command line arguments + force_enque_next
I had commented out 'talk_force_enqueue_next()' in the voice prompts
pretty sure it was because really long prompts just keep droning on
problem being as soon as the plugin returns in some cases the prompt
gets cut off by the parent calling shutup on us

so it is now a setting (on by default)

Now allows command line arguments
so you can announce whatever prompt you want whenever / wherever you want
(checkout open plugins for a way to create a plugin with command line arguments)
you can then add this .opx shortcut to your shortcuts or quick screen

and as before you can use a hotkey which also will allow command line args..

Change-Id: Id8883c0a4e85e1cb813959ab72e89817f388a397
2026-01-23 11:02:36 -05:00
William Wilgus
3c6b9bb458 FS#13748 - The imageviewer plugin decodes the file infinitely when opening a empty JPEG file.
Couldn't reproduce this on most of my devices or the sims
turns out the bug depends on uninitialized memory being FF
and the file empty

Uninitialized memory fixed what I saw at least..

Change-Id: Ie267af0a20685003ee28ff42d008dd7942a7c6fc
2026-01-22 23:59:00 -05:00
107 changed files with 2585 additions and 999 deletions

View file

@ -101,7 +101,7 @@ static void NORETURN_ATTR audio_thread(void)
case SYS_USB_CONNECTED:
LOGFQUEUE("audio < SYS_USB_CONNECTED");
voice_stop();
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
usb_wait_for_disconnect(&audio_queue);
break;
}

View file

@ -248,7 +248,7 @@ static void usb_screens_draw(struct usb_screen_vps_t *usb_screen_vps_ar)
}
}
void gui_usb_screen_run(bool early_usb)
void gui_usb_screen_run(bool early_usb, intptr_t seqnum)
{
#ifdef SIMULATOR /* the sim allows toggling USB fast enough to overflow viewportmanagers stack */
static bool in_usb_screen = false;
@ -297,7 +297,7 @@ void gui_usb_screen_run(bool early_usb)
font_disable_all();
}
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, seqnum);
while (1)
{

View file

@ -21,10 +21,12 @@
#ifndef _USB_SCREEN_H_
#define _USB_SCREEN_H_
#include <stdint.h>
#ifdef USB_NONE
#define gui_usb_screen_run(early_usb) do {} while(0)
#define gui_usb_screen_run(early_usb, seqnum) do {(void)seqnum;} while(0)
#else
extern void gui_usb_screen_run(bool early_usb);
extern void gui_usb_screen_run(bool early_usb, intptr_t seqnum);
#endif
#endif

View file

@ -404,7 +404,7 @@ static void iap_thread(void)
/* Ack USB thread */
case SYS_USB_CONNECTED:
{
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
break;
}
}

View file

@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,14 +29,166 @@
/* {Action Code, Button code, Prereq button code } */
static const struct button_mapping button_context_standard[] = {
{ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE},
{ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_OK, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE},
{ACTION_STD_CONTEXT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_QUICKSCREEN, BUTTON_B, BUTTON_NONE},
{ACTION_STD_MENU, BUTTON_Y, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_standard */
static const struct button_mapping button_context_wps[] = {
{ACTION_WPS_PLAY, BUTTON_DOWN|BUTTON_REL, BUTTON_DOWN},
{ACTION_WPS_STOP, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_VIEW_PLAYLIST, BUTTON_UP|BUTTON_REL, BUTTON_UP},
{ACTION_WPS_CONTEXT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
{ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT},
{ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT},
{ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT},
{ACTION_WPS_QUICKSCREEN, BUTTON_B, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_wps */
static const struct button_mapping button_context_tree[] = {
{ACTION_TREE_WPS, BUTTON_X, BUTTON_NONE},
{ACTION_TREE_HOTKEY, BUTTON_Y, BUTTON_NONE},
{ACTION_TREE_STOP, BUTTON_START|BUTTON_REPEAT, BUTTON_START},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
}; /* button_context_tree */
static const struct button_mapping button_context_list[] = {
{ACTION_LISTTREE_PGUP, BUTTON_UP|BUTTON_SELECT, BUTTON_NONE},
{ACTION_LISTTREE_PGUP, BUTTON_UP|BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LISTTREE_PGDOWN, BUTTON_DOWN|BUTTON_SELECT, BUTTON_NONE},
{ACTION_LISTTREE_PGDOWN, BUTTON_DOWN|BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LIST_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_list */
static const struct button_mapping button_context_settings[] = {
{ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE},
{ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_OK, BUTTON_A, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_X, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
static const struct button_mapping button_context_settings_eq[] = {
{ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_STD_OK, BUTTON_A, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_X, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_eq */
static const struct button_mapping button_context_quickscreen[] = {
{ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE},
{ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE},
{ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_CONTEXT, BUTTON_B|BUTTON_REPEAT, BUTTON_B},
{ACTION_STD_CANCEL, BUTTON_B|BUTTON_REL, BUTTON_B},
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
static const struct button_mapping button_context_yesnoscreen[] = {
{ACTION_YESNO_ACCEPT, BUTTON_A, BUTTON_NONE},
{ACTION_YESNO_ACCEPT, BUTTON_X, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_B, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_Y, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_START, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_VOL_DOWN, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_yesnoscreen */
static const struct button_mapping button_context_keyboard[] = {
{ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE},
{ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE},
{ACTION_KBD_BACKSPACE, BUTTON_X, BUTTON_NONE},
{ACTION_KBD_BACKSPACE, BUTTON_X|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_DONE, BUTTON_A, BUTTON_NONE},
{ACTION_KBD_ABORT, BUTTON_POWER, BUTTON_NONE},
{ACTION_KBD_PAGE_FLIP, BUTTON_START, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_Y, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_Y|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_B, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_B|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_keyboard */
const struct button_mapping* get_context_mapping(int context)
{
switch (context)
{
default:
case CONTEXT_STD:
return button_context_standard;
case CONTEXT_WPS:
return button_context_wps;
case CONTEXT_TREE:
case CONTEXT_MAINMENU:
return button_context_tree;
case CONTEXT_LIST:
return button_context_list;
case CONTEXT_SETTINGS:
case CONTEXT_SETTINGS_TIME:
return button_context_settings;
case CONTEXT_SETTINGS_EQ:
case CONTEXT_SETTINGS_COLOURCHOOSER:
return button_context_settings_eq;
case CONTEXT_QUICKSCREEN:
return button_context_quickscreen;
case CONTEXT_YESNOSCREEN:
return button_context_yesnoscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
}
}

View file

@ -16964,3 +16964,17 @@
general_purpose_led: "LED Indikatoren verwenden"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Status mitteilen"
</dest>
<voice>
*: "Status mitteilen"
</voice>
</phrase>

View file

@ -16942,3 +16942,17 @@
general_purpose_led: "Use LED indicators"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Announce Status"
</dest>
<voice>
*: "Announce Status"
</voice>
</phrase>

View file

@ -15077,6 +15077,20 @@
hotkey: "Announcement format"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Announce Status"
</dest>
<voice>
*: "Announce Status"
</voice>
</phrase>
<phrase>
id: LANG_REMAIN
desc: for constructs such as number of tracks remaining etc

View file

@ -16946,3 +16946,17 @@
general_purpose_led: "Usa indicatori led"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Annuncia Lo Stato"
</dest>
<voice>
*: "Annuncia Lo Stato"
</voice>
</phrase>

View file

@ -16960,3 +16960,17 @@
general_purpose_led: "LED 표시기 사용"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "상태 발표"
</dest>
<voice>
*: "상태 발표"
</voice>
</phrase>

View file

@ -16952,3 +16952,17 @@
general_purpose_led: "Użyj wskaźników led"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Informuj o stanie"
</dest>
<voice>
*: "Informuj o stanie"
</voice>
</phrase>

View file

@ -16946,3 +16946,17 @@
general_purpose_led: "Používať LED indikátory"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Oznámiť status"
</dest>
<voice>
*: "Oznámiť status"
</voice>
</phrase>

View file

@ -12615,20 +12615,6 @@
*: "Просечни битски проток"
</voice>
</phrase>
<phrase>
id: LANG_PLAYTIME_ERROR
desc: playing time screen
user: core
<source>
*: "Error while gathering info"
</source>
<dest>
*: "Грешка током прикупљања инфо"
</dest>
<voice>
*: "Грешка током прикупљања инфо"
</voice>
</phrase>
<phrase>
id: LANG_PLAYING_TIME
desc: onplay menu
@ -15940,20 +15926,6 @@
*: "Подраз. прегледач"
</voice>
</phrase>
<phrase>
id: LANG_AMAZE_MENU
desc: Amaze game
user: core
<source>
*: "Amaze Main Menu"
</source>
<dest>
*: "Amaze главни мени"
</dest>
<voice>
*: "Амејз главни мени"
</voice>
</phrase>
<phrase>
id: LANG_SET_MAZE_SIZE
desc: Maze size in Amaze game
@ -16206,34 +16178,6 @@
*: "Мик мод подешавања"
</voice>
</phrase>
<phrase>
id: LANG_MIKMOD_MENU
desc: mikmod plugin
user: core
<source>
*: "Mikmod Menu"
</source>
<dest>
*: "Mikmod мени"
</dest>
<voice>
*: "Мик мод мени"
</voice>
</phrase>
<phrase>
id: LANG_CHESSBOX_MENU
desc: chessbox plugin
user: core
<source>
*: "Chessbox Menu"
</source>
<dest>
*: "Chessbox мени"
</dest>
<voice>
*: "Чес бокс мени"
</voice>
</phrase>
<phrase>
id: VOICE_INVALID_VOICE_FILE
desc: played if the voice file fails to load
@ -16983,3 +16927,34 @@
usbdac: "У Ес Бе - ДАК активан"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Објави статус"
</dest>
<voice>
*: "Објави статус"
</voice>
</phrase>
<phrase>
id: LANG_USE_LED_INDICATORS
desc: LED indicators setting
user: core
<source>
*: none
general_purpose_led: "Use LED indicators"
</source>
<dest>
*: none
general_purpose_led: "Користи LED индикаторе"
</dest>
<voice>
*: none
general_purpose_led: "Користи LED индикаторе"
</voice>
</phrase>

View file

@ -16944,3 +16944,17 @@
general_purpose_led: "Dùng đèn hiệu LED"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "Thông báo Trạng thái"
</dest>
<voice>
*: "Thông báo trạng thái"
</voice>
</phrase>

View file

@ -618,7 +618,7 @@ static void init(void)
(mmc_remove_request() == SYS_HOTSWAP_EXTRACTED))
#endif
{
gui_usb_screen_run(true);
gui_usb_screen_run(true, button_get_data());
mounted = true; /* mounting done @ end of USB mode */
}
#ifdef HAVE_USB_POWER
@ -675,7 +675,7 @@ static void init(void)
#ifndef USB_NONE
usb_start_monitoring();
while(button_get(true) != SYS_USB_CONNECTED) {};
gui_usb_screen_run(true);
gui_usb_screen_run(true, button_get_data());
#elif !defined(DEBUG) && !(CONFIG_STORAGE & STORAGE_RAMDISK)
sleep(HZ*5);
#endif

View file

@ -656,24 +656,25 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
}
break;
case SYS_USB_CONNECTED:
{
intptr_t seqnum = button_get_data();
if (callback != NULL)
callback(parameter);
{
system_flush();
system_flush();
#ifdef BOOTFILE
#if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF)
check_bootfile(false); /* gets initial size */
check_bootfile(false); /* gets initial size */
#endif
#endif
gui_usb_screen_run(false);
gui_usb_screen_run(false, seqnum);
#ifdef BOOTFILE
#if !defined(USB_NONE) && !defined(USB_HANDLED_BY_OF)
check_bootfile(true);
check_bootfile(true);
#endif
#endif
system_restore();
}
system_restore();
return SYS_USB_CONNECTED;
}
case SYS_POWEROFF:
case SYS_REBOOT:

View file

@ -1882,7 +1882,7 @@ static void dc_thread_playlist(void)
}
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
usb_wait_for_disconnect(&playlist_queue);
break;
}

View file

@ -948,7 +948,7 @@ struct plugin_api {
/* usb */
bool (*usb_inserted)(void);
void (*usb_acknowledge)(long id);
void (*usb_acknowledge)(long id, intptr_t seqnum);
#ifdef USB_ENABLE_HID
void (*usb_hid_send)(usage_page_t usage_page, int id);
#endif

View file

@ -70,7 +70,7 @@ static const char keybd_layout[] =
* - \n does not create a key, but it also consumes one element
* - the final null terminator is equivalent to \n
* - since sizeof includes the null terminator we don't need +1 for that. */
static ucschar_t kbd_buf[sizeof(keybd_layout)];
static ucschar_t kbd_buf[sizeof(keybd_layout) + 1];
/****************** prototypes ******************/
void print_scroll(char* string); /* implements a scrolling screen */
@ -84,7 +84,7 @@ void thread_create(void);
void thread(void); /* the thread running it all */
void thread_quit(void);
static int voice_general_info(bool testing);
static unsigned char* voice_info_group(unsigned char* current_token, bool testing);
static const char* voice_info_group(const char* current_token, bool testing);
int plugin_main(const void* parameter); /* main loop */
enum plugin_status plugin_start(const void* parameter); /* entry */
@ -115,8 +115,9 @@ static struct
int bin_added;
bool show_prompt;
bool force_enqueue;
unsigned char wps_fmt[MAX_ANNOUNCE_WPS+1];
char wps_fmt[MAX_ANNOUNCE_WPS+1];
} gAnnounce;
static struct configdata config[] =
@ -126,6 +127,7 @@ static struct configdata config[] =
{TYPE_INT, 0, 10, { .int_p = &gAnnounce.grouping }, "Grouping", NULL},
{TYPE_INT, 0, 10000, { .int_p = &gAnnounce.bin_added }, "Added", NULL},
{TYPE_BOOL, 0, 1, { .bool_p = &gAnnounce.show_prompt }, "Prompt", NULL},
{TYPE_BOOL, 0, 1, { .bool_p = &gAnnounce.force_enqueue }, "Enqueue", NULL},
{TYPE_STRING, 0, MAX_ANNOUNCE_WPS+1,
{ .string = (char*)&gAnnounce.wps_fmt }, "Fmt", NULL},
};
@ -165,6 +167,7 @@ static void config_set_defaults(void)
gAnnounce.grouping = 0;
gAnnounce.wps_fmt[0] = '\0';
gAnnounce.show_prompt = true;
gAnnounce.force_enqueue = true;
}
static void config_reset_voice(void)
@ -173,10 +176,10 @@ static void config_reset_voice(void)
int interval = gAnnounce.interval;
int announce = gAnnounce.announce_on;
int grouping = gAnnounce.grouping;
if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
int status = configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER);
if (status < 0)
{
rb->splash(100, "ERROR!");
rb->splashf(100, ID2P(LANG_FILE_ERROR), status);
return;
}
@ -194,7 +197,8 @@ void announce(void)
voice_general_info(false);
if (rb->talk_id(VOICE_PAUSE, true) < 0)
rb->beep_play(800, 100, 1000);
//rb->talk_force_enqueue_next();
if (gAnnounce.force_enqueue)
rb->talk_force_enqueue_next();
}
static void announce_test(void)
@ -202,8 +206,8 @@ static void announce_test(void)
rb->talk_force_shutup();
rb->sleep(HZ / 2);
voice_info_group(gAnnounce.wps_fmt, true);
rb->splash(HZ, "...");
//rb->talk_force_enqueue_next();
if (gAnnounce.force_enqueue)
rb->talk_force_enqueue_next();
}
static void announce_add(const char *str)
@ -329,7 +333,7 @@ static int announce_menu(void)
{
int selection = 0;
MENUITEM_STRINGLIST(announce_menu, "Announcements", announce_menu_cb,
MENUITEM_STRINGLIST(announce_menu, ID2P(LANG_ANNOUNCEMENT_FMT), announce_menu_cb,
ID2P(LANG_TIME),
ID2P(LANG_DATE),
ID2P(LANG_TRACK),
@ -359,11 +363,12 @@ static int settings_menu(void)
int selection = 0;
//bool old_val;
MENUITEM_STRINGLIST(settings_menu, "Announce Settings", NULL,
MENUITEM_STRINGLIST(settings_menu, ID2P(LANG_ANNOUNCE_STATUS), NULL,
ID2P(LANG_TIMEOUT),
ID2P(LANG_ANNOUNCE_ON),
ID2P(LANG_GROUPING),
ID2P(LANG_ANNOUNCEMENT_FMT),
ID2P(LANG_QUEUE_FIRST),
ID2P(VOICE_BLANK),
ID2P(LANG_MENU_QUIT),
ID2P(LANG_SAVE_EXIT));
@ -392,12 +397,15 @@ static int settings_menu(void)
case 3:
announce_menu();
break;
case 4: /*sep*/
case 4:
rb->set_bool(rb->str(LANG_QUEUE_FIRST), &gAnnounce.force_enqueue);
break;
case 5: /*sep*/
continue;
case 5: /* quit the plugin */
case 6: /* quit the plugin */
return -1;
break;
case 6:
case 7:
configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
return 0;
break;
@ -427,7 +435,7 @@ void thread(void)
switch (ev.id)
{
case SYS_USB_CONNECTED:
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
in_usb = true;
break;
case SYS_USB_DISCONNECTED:
@ -538,13 +546,15 @@ int plugin_main(const void* parameter)
}
else
{
rb->splash(HZ / 2, "Announce Status");
rb->splashf(HZ / 2, "%s", rb->str(LANG_ANNOUNCE_STATUS)); /* no talking */
if (gAnnounce.show_prompt)
{
#if 0 /* splash should announce for us */
if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
{
rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
}
#endif
rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
}
@ -580,6 +590,19 @@ enum plugin_status plugin_start(const void* parameter)
return PLUGIN_USB_CONNECTED;
config_set_defaults();
if (parameter && parameter != rb->plugin_tsr)
{
const char *param = (const char*) parameter;
if (param[0] != '\0')
{
voice_info_group(param, false);
rb->talk_force_enqueue_next();
return PLUGIN_OK;
}
}
if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
{
/* If the loading failed, save a new config file */
@ -594,7 +617,7 @@ enum plugin_status plugin_start(const void* parameter)
static int voice_general_info(bool testing)
{
unsigned char* infotemplate = gAnnounce.wps_fmt;
const char* infotemplate = gAnnounce.wps_fmt;
if (gAnnounce.index >= rb->strlen(gAnnounce.wps_fmt))
gAnnounce.index = 0;
@ -629,9 +652,9 @@ static int voice_general_info(bool testing)
return 0;
}
static unsigned char* voice_info_group(unsigned char* current_token, bool testing)
static const char* voice_info_group(const char* current_token, bool testing)
{
unsigned char current_char;
char current_char;
bool skip_next_group = false;
gAnnounce.count = 0;

View file

@ -491,7 +491,7 @@ static void thread(void)
{
case SYS_USB_CONNECTED:
in_usb_mode = true;
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
break;
case SYS_USB_DISCONNECTED:
in_usb_mode = false;

View file

@ -74,6 +74,7 @@ extern int TELL(void)
extern void *OPEN(char *f)
{
memset(buff, 0, sizeof(buff));
printf("Opening %s\n", f);
cur_buff_pos = length = file_pos = 0;
fd = rb->open(f,O_RDONLY);

View file

@ -4,8 +4,8 @@ static int fd;
extern int GETC(void)
{
unsigned char x;
rb->read(fd, &x, 1);
unsigned char x = 0;
rb->read(fd, &x, 1)
return x;
}

View file

@ -36,12 +36,11 @@
*/
int kbd_create_layout(const char *layout, ucschar_t *buf, int bufsz)
{
ucschar_t *pbuf;
ucschar_t *pbuf = buf;
const unsigned char *p = layout;
int len = 0;
int total_len = 0;
pbuf = buf;
while (*p && (pbuf - buf + (ptrdiff_t) sizeof(ucschar_t)) < bufsz)
while (*p && (pbuf - buf + (ptrdiff_t) sizeof(*buf)) < bufsz)
{
p = rb->utf8decode(p, &pbuf[len+1]);
if (pbuf[len+1] == '\n')
@ -55,13 +54,14 @@ int kbd_create_layout(const char *layout, ucschar_t *buf, int bufsz)
len++;
}
if (len+1 < bufsz)
if ((total_len + len + 1) * (int)sizeof(*buf) < bufsz)
{
*pbuf = len;
pbuf[len+1] = 0xFEFF; /* mark end of characters */
total_len += len + 1;
return total_len * sizeof(ucschar_t);
return total_len * sizeof(*buf);
}
//rb->logf("%s %d %d\n", __func__, bufsz, (total_len + len + 1) * sizeof(*buf));
return 0;
}

View file

@ -27,7 +27,7 @@
#ifdef PLUGIN
#include "plugin.h"
#include "lib/pluginlib_actions.h"
#define lcd_getstringsize rb->lcd_getstringsize
#define font_getstringsize rb->font_getstringsize
#define lcd_clear_display rb->lcd_clear_display
#define lcd_putsxy rb->lcd_putsxy
#define lcd_update rb->lcd_update
@ -66,7 +66,13 @@ int splash_scroller(int timeout, const char* str)
if (!str)
str = "[nil]";
int w, ch_w, ch_h;
lcd_getstringsize("W", &ch_w, &ch_h);
struct viewport vp;
rb->viewport_set_defaults(&vp, SCREEN_MAIN);
struct viewport *last_vp = rb->lcd_set_viewport(&vp);
int fontnum = vp.font;
font_getstringsize("W", &ch_w, &ch_h, fontnum);
const int max_w = LCD_WIDTH - (ch_w * 2);
const int max_lines = LCD_HEIGHT / ch_h - 1;
@ -103,13 +109,13 @@ int splash_scroller(int timeout, const char* str)
line[linepos] = ' ';
beep_play(1000, HZ, 1000);
}
else if (ch[0] < ' ') /* Dont copy control characters */
else if (ch[0] < ' ' && ch[0] > '\0') /* Dont copy control characters */
line[linepos] = (linepos == 0) ? '\0' : ' ';
else
line[linepos] = ch[0];
line[linepos + 1] = '\0'; /* terminate to check text extent */
lcd_getstringsize(line, &w, NULL);
font_getstringsize(line, &w, NULL, fontnum);
/* try to not split in middle of words */
if (w + wrap_thresh >= max_w &&
@ -174,6 +180,8 @@ int splash_scroller(int timeout, const char* str)
else
break;
}
rb->lcd_set_viewport(last_vp);
return action;
}

View file

@ -672,6 +672,51 @@ function rotate_image(img)
rb.sleep(rb.HZ * 10)]]
end -- rotate_image
function flip_image(img)
local blitop = _blit.BOR
local d = 0
local x, y, w, h
w = img:width()
h = img:height()
x = (_lcd.W - w) / 2
y = (_lcd.H - h) / 2
--make a copy of original screen for restoration
local screen_img -- = _lcd:duplicate()
screen_img =_img.new(w, h)
screen_img :copy(_LCD, 1, 1, x, y, w, h)
--[[--Profiling code
local timer = _timer.start()]]
while d >= 0 do
-- copy our flipped image onto the background
if d == 0 then
_lcd:copy(img, x, y, 1, 1, w, h, false, blitop)
elseif d == 1 then
_lcd:copy(img, x, y, 1, 1, -w, h, false, blitop)
elseif d == 2 then
_lcd:copy(img, x, y, 1, 1, w, -h, false, blitop)
elseif d == 3 then
_lcd:copy(img, x, y, 1, 1, -w, -h, false, blitop)
d = -1
end
_lcd:update()
--restore the portion of the background we destroyed
_lcd:copy(screen_img, x, y, 1, 1)
d = d + i
if rb.get_plugin_action(rb.HZ) == CANCEL_BUTTON then
break;
end
end
--[[-- Profiling code
_print.f("%d", _timer.stop(timer))
rb.sleep(rb.HZ * 10)]]
end -- flip_image
-- shows blitting with a mask
function blit_mask(dst)
local timer = _timer()
@ -826,12 +871,13 @@ function main_menu()
[6] = "Bouncing Ball (olive)",
[7] = "The Twist",
[8] = "Image Rotation",
[9] = "Long Text",
[10] = "Rainbow Image",
[11] = "Random Image",
[12] = "Clear Screen",
[13] = "Save Screen",
[14] = "Exit"
[9] = "Image Flip",
[10] = "Long Text",
[11] = "Rainbow Image",
[12] = "Random Image",
[13] = "Clear Screen",
[14] = "Save Screen",
[15] = "Exit"
}
local ft = {
[0] = exit_now, --if user cancels do this function
@ -846,16 +892,17 @@ function main_menu()
[6] = function(BOUNC) bounce_image(create_ball()) end,
[7] = function(TWIST) twist(get_logo()) end,
[8] = function(ROTAT) rotate_image(get_logo()) end,
[9] = long_text,
[10] = function(RAINB)
[9] = function(FLIP) flip_image(get_logo()) end,
[10] = long_text,
[11] = function(RAINB)
rainbow_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ)
end,
[11] = function(RANDM)
[12] = function(RANDM)
random_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ)
end,
[12] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end,
[13] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end,
[14] = function(EXIT_) return true end
[13] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end,
[14] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end,
[15] = function(EXIT_) return true end
}
if LCD_DEPTH < 2 then

View file

@ -979,7 +979,7 @@ static void thread(void)
switch (ev.id)
{
case SYS_USB_CONNECTED:
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
logenabled = false;
break;
case SYS_USB_DISCONNECTED:

View file

@ -58,7 +58,7 @@ static void main_loop(void)
state = "connected";
logf("test_usb: connect ack %ld", *rb->current_tick);
DEBUGF("test_usb: connect ack %ld\n", *rb->current_tick);
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK);
rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
break;
case SYS_USB_DISCONNECTED:

View file

@ -5357,7 +5357,7 @@ static void tagcache_thread(void)
case SYS_USB_CONNECTED:
logf("USB: TagCache");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
usb_wait_for_disconnect(&tagcache_queue);
break ;
}

View file

@ -96,5 +96,4 @@ x1000/recovery.c
x1000/utils.c
#elif defined(ECHO_R1)
echoplayer.c
show_logo.c
#endif

View file

@ -39,3 +39,4 @@ int load_raw_firmware(unsigned char* buf, char* firmware, int buffer_size);
#ifdef ROCKBOX_HAS_LOGF
void display_logf(void);
#endif
void show_logo(void);

View file

@ -47,13 +47,13 @@ void main(void)
int buffer_size;
int(*kernel_entry)(void);
int ret;
/* Make sure interrupts are disabled */
set_irq_level(IRQ_DISABLED);
set_fiq_status(FIQ_DISABLED);
system_init();
kernel_init();
/* Now enable interrupts */
set_irq_level(IRQ_ENABLED);
set_fiq_status(FIQ_ENABLED);
@ -70,18 +70,18 @@ void main(void)
reset_screen();
printf("Rockbox boot loader");
printf("Version %s", rbversion);
ret = storage_init();
if(ret)
printf("ATA error: %d", ret);
filesystem_init();
/* If no button is held, start the OF */
if(button_read_device() == 0)
{
printf("Loading Creative firmware...");
loadbuffer = (unsigned char*)0x00A00000;
ret = load_minifs_file("creativeos.jrm", loadbuffer);
if(ret != -1)
@ -99,7 +99,7 @@ void main(void)
ret = disk_mount_all();
if (ret <= 0)
error(EDISK, ret, true);
printf("Loading Rockbox firmware...");
loadbuffer = (unsigned char*)0x00900000;
@ -113,8 +113,8 @@ void main(void)
ret = kernel_entry();
printf("FAILED!");
}
storage_sleepnow();
while(1);
}

View file

@ -21,155 +21,457 @@
#include "kernel/kernel-internal.h"
#include "system.h"
#include "power.h"
#include "rtc.h"
#include "lcd.h"
#include "backlight.h"
#include "button.h"
#include "timefuncs.h"
#include "storage.h"
#include "disk.h"
#include "file.h"
#include "file_internal.h"
#include "usb.h"
#include "elf.h"
#include "elf_loader.h"
#include "rbversion.h"
#include "system-echoplayer.h"
#include "gpio-stm32h7.h"
static bool is_usb_connected = false;
#define SDRAM_SIZE (MEMORYSIZE * 1024 * 1024)
extern void show_logo(void);
/* Address where Rockbox .elf binary will be cached in RAM */
#define LOAD_SIZE (2 * 1024 * 1024)
#define LOAD_BUFFER_ADDR (STM32_SDRAM1_BASE + SDRAM_SIZE - LOAD_SIZE)
static void demo_rtc(void)
/* Values at GDB_MAGICx */
#define GDB_MAGICVAL1 0x726f636b
#define GDB_MAGICVAL2 0x424f4f54
#define GDB_MAGICVAL3 0x6764626c
#define GDB_MAGICVAL4 0x6f616455
/* Addresses used by GDB boot protocol */
#define GDB_MAGIC1 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x00))
#define GDB_MAGIC2 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x04))
#define GDB_MAGIC3 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x08))
#define GDB_MAGIC4 (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x0c))
#define GDB_ELFADDR (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x10))
#define GDB_ELFSIZE (*(volatile uint32_t*)(STM32_SRAM4_BASE + 0x14))
/* Events for the monitor callback to signal the main thread */
#define EV_POWER_PRESSED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0)
#define EV_POWER_RELEASED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 1)
#define EV_USB_UNPLUGGED MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 2)
/* Long press duration for power button */
#define POWERBUTTON_LONG_PRESS_TIME (HZ)
/* Time to remain powered with no USB cable inserted */
#define USB_UNPLUGGED_ACTIVE_TIME (30 * HZ)
#define USB_UNPLUGGED_INACTIVE_TIME (3 * HZ)
static const struct elf_memory_map rb_elf_mmap[] = {
{
.addr = STM32_ITCM_BASE,
.size = STM32_ITCM_SIZE,
.flags = PF_R | PF_X,
},
{
.addr = STM32_DTCM_BASE,
.size = STM32_DTCM_SIZE,
.flags = PF_R | PF_W,
},
{
.addr = STM32_SDRAM1_BASE,
.size = SDRAM_SIZE - LOAD_SIZE,
.flags = PF_R | PF_W | PF_X,
},
};
static const struct elf_load_context rb_elf_ctx = {
.mmap = rb_elf_mmap,
.num_mmap = ARRAYLEN(rb_elf_mmap),
};
/* Power button monitor state */
static bool pwr_curr_state;
static bool pwr_prev_state;
struct timeout pwr_stable_tmo;
/* USB monitor state */
static bool usb_curr_state;
static bool usb_prev_state;
struct timeout usb_unplugged_tmo;
static volatile bool restart_pwr_stable_tmo;
static volatile bool restart_usb_unplugged_tmo;
/*
* Because power is always enabled while USB is plugged in the
* bootloader decides whether to appear "active" or "inactive"
* to the user.
*/
static bool is_active;
/*
* This flag is set if the bootloader is entered after software
* poweroff. The user may still be holding the power button and
* we don't want to boot Rockbox because of this, so we have to
* wait for the power button to be released first.
*/
static bool wait_for_power_released;
/* Optional error message displayed on LCD */
static const char *status_msg = NULL;
/* Location of Rockbox ELF binary in memory */
static void *elf_load_addr = NULL;
static size_t elf_load_size = 0;
/* Helper functions */
static bool is_power_button_pressed(void)
{
int y = 0;
struct tm *time = get_time();
lcd_clear_display();
lcd_putsf(0, y++, "time: %02d:%02d:%02d",
time->tm_hour, time->tm_min, time->tm_sec);
lcd_putsf(0, y++, "year: %d", time->tm_year + 1900);
lcd_putsf(0, y++, "month: %d", time->tm_mon);
lcd_putsf(0, y++, "day: %d", time->tm_mday);
lcd_update();
return button_status() & BUTTON_POWER;
}
static void demo_storage(void)
static bool is_usbmode_button_pressed(void)
{
int y = 0;
return button_status() & BUTTON_DOWN;
}
lcd_clear_display();
lcd_putsf(0, y++, "tick %ld", current_tick);
static int send_event_on_tmo(struct timeout *tmo)
{
button_queue_post(tmo->data, 0);
return 0;
}
if (is_usb_connected)
/*
* Monitors the state of the power button and USB cable
* insertion status. It will post an event to the main
* thread when (a) the power button is continously held
* or released for long enough, or (b) the USB cable is
* unplugged for long enough.
*/
static void monitor_tick(void)
{
/* Power button state */
pwr_prev_state = pwr_curr_state;
pwr_curr_state = is_power_button_pressed();
if (pwr_curr_state != pwr_prev_state || restart_pwr_stable_tmo)
{
lcd_puts(0, y++, "storage disabled by USB");
lcd_update();
long event = pwr_curr_state ? EV_POWER_PRESSED : EV_POWER_RELEASED;
int ticks = POWERBUTTON_LONG_PRESS_TIME;
restart_pwr_stable_tmo = false;
timeout_register(&pwr_stable_tmo, send_event_on_tmo, ticks, event);
}
/* USB cable state */
usb_prev_state = usb_curr_state;
usb_curr_state = usb_inserted();
/* Ignore cable state change in inactive state */
if (usb_curr_state != usb_prev_state || restart_usb_unplugged_tmo)
{
long event = EV_USB_UNPLUGGED;
int ticks = is_active ? USB_UNPLUGGED_ACTIVE_TIME : USB_UNPLUGGED_INACTIVE_TIME;
restart_usb_unplugged_tmo = false;
if (usb_curr_state)
timeout_cancel(&usb_unplugged_tmo);
else
timeout_register(&usb_unplugged_tmo, send_event_on_tmo, ticks, event);
}
}
static void monitor_init(void)
{
pwr_curr_state = is_power_button_pressed();
usb_curr_state = usb_inserted();
/* Make sure events fire even if inputs don't change after boot */
restart_pwr_stable_tmo = true;
restart_usb_unplugged_tmo = true;
tick_add_task(monitor_tick);
}
static void go_active(void)
{
is_active = true;
restart_usb_unplugged_tmo = true;
gpio_set_level(GPIO_CPU_POWER_ON, 1);
storage_enable(true);
disk_mount_all();
}
static void go_inactive(void)
{
is_active = false;
restart_usb_unplugged_tmo = true;
storage_enable(false);
gpio_set_level(GPIO_CPU_POWER_ON, 0);
}
static void refresh_display(void)
{
if (!is_active)
{
lcd_shutdown();
return;
}
struct partinfo pinfo;
if (storage_present(IF_MD(0,)) && disk_partinfo(0, &pinfo))
{
lcd_putsf(0, y++, "start %d", (int)pinfo.start);
lcd_putsf(0, y++, "count %d", (int)pinfo.size);
lcd_putsf(0, y++, "type %d", (int)pinfo.type);
DIR *d = opendir("/");
struct dirent *ent;
while ((ent = readdir(d)))
{
lcd_putsf(0, y++, "/%s", ent->d_name);
}
closedir(d);
}
lcd_update();
}
static void demo_usb(void)
{
static const char *phyname[] = {
[STM32H743_USBOTG_PHY_ULPI_HS] = "ULPI HS",
[STM32H743_USBOTG_PHY_ULPI_FS] = "ULPI FS",
[STM32H743_USBOTG_PHY_INT_FS] = "internal FS",
};
int y = 0;
lcd_clear_display();
lcd_putsf(0, y++, "tick %ld", current_tick);
lcd_putsf(0, y++, "usb connected %d", (int)is_usb_connected);
lcd_putsf(0, y++, "instance = USB%d", STM32H743_USBOTG_INSTANCE + 1);
lcd_putsf(0, y++, "phy = %s", phyname[STM32H743_USBOTG_PHY]);
lcd_putsf(0, y++, "Rockbox on %s", MODEL_NAME);
y++;
if (status_msg)
{
lcd_putsf(0, y++, "Error: %s", status_msg);
y++;
}
if (!usb_inserted())
{
lcd_putsf(0, y++, "Hold POWER to power off");
lcd_putsf(0, y++, "Connect USB cable for USB mode");
y++;
}
else
{
lcd_putsf(0, y++, "Bootloader USB mode");
if (charging_state())
{
lcd_putsf(0, y++, "Battery charging (%d mA)",
usb_charging_maxcurrent());
}
else
{
lcd_putsf(0, y++, "Battery charged");
}
y++;
}
lcd_putsf(0, y++, "Version: %s", RBVERSION);
lcd_update();
lcd_enable(true);
}
static void (*demo_funcs[]) (void) = {
demo_rtc,
demo_storage,
demo_usb,
};
static bool load_rockbox(void)
{
int fd = open(BOOTDIR "/" BOOTFILE, O_RDONLY);
if (fd < 0)
{
status_msg = "Rockbox not found";
return false;
}
void *tmp_buf = (void *)LOAD_BUFFER_ADDR;
size_t tmp_size = LOAD_SIZE;
ssize_t ret = read(fd, tmp_buf, tmp_size);
if (ret < 0)
{
status_msg = "I/O error loading Rockbox";
goto out;
}
if ((size_t)ret == tmp_size)
{
status_msg = "Rockbox binary too large";
goto out;
}
elf_load_addr = tmp_buf;
elf_load_size = tmp_size;
out:
close(fd);
return elf_load_addr != NULL;
}
static void launch_elf(void)
{
void *entrypoint = NULL;
void (*entry_fn) (void) = NULL;
if (elf_load_addr == NULL || elf_load_size == 0)
return;
int err = elf_loadmem(elf_load_addr, elf_load_size, &rb_elf_ctx, &entrypoint);
if (err)
{
status_msg = "Failed to execute Rockbox";
return;
}
disk_unmount_all();
storage_enable(false);
lcd_shutdown();
entry_fn = entrypoint;
commit_discard_idcache();
disable_irq();
stm32_systick_disable();
entry_fn();
}
static void launch(void)
{
/* No-op if USB mode was requested */
if (is_usbmode_button_pressed())
return;
load_rockbox();
launch_elf();
}
static bool handle_gdb_boot(void)
{
/* Look for magic values that signal the GDB boot protocol */
bool gdb_boot = (GDB_MAGIC1 == GDB_MAGICVAL1 &&
GDB_MAGIC2 == GDB_MAGICVAL2 &&
GDB_MAGIC3 == GDB_MAGICVAL3 &&
GDB_MAGIC4 == GDB_MAGICVAL4);
/* Clear them so they won't hang around on a system reset */
GDB_MAGIC1 = 0;
GDB_MAGIC2 = 0;
GDB_MAGIC3 = 0;
GDB_MAGIC4 = 0;
if (!gdb_boot)
return false;
/* "Call" GDB by entering breakpoint */
GDB_ELFADDR = 0;
GDB_ELFSIZE = 0;
asm volatile("bkpt");
/* Read location of the loaded binary */
elf_load_addr = (void *)GDB_ELFADDR;
elf_load_size = (size_t)GDB_ELFSIZE;
return true;
}
void main(void)
{
system_init();
kernel_init();
power_init();
rtc_init();
lcd_init();
button_init();
/* Start monitoring power button / usb state */
monitor_init();
/* Prepare LCD in case we need to display something */
lcd_init();
backlight_init();
backlight_on();
show_logo();
/*
* Prepare storage subsystem, but keep SD card unpowered
* until we actually need to access it.
*/
storage_init();
storage_enable(false);
/*
* Initialize fs/disk internal state, the disk will not
* be mountable due to being disabled but this will not
* cause any fatal errors.
*/
filesystem_init();
disk_mount_all();
/* GDB assisted boot takes precedence */
if (handle_gdb_boot())
launch_elf();
if (echoplayer_boot_reason == ECHOPLAYER_BOOT_REASON_SW_REBOOT ||
usb_detect() == USB_INSERTED)
{
/*
* For software reboot we want to immediately launch Rockbox.
*
* We also do so if USB is plugged in at boot, because there's
* no way to dynamically switch between charge only and mass
* storage mode depending on the active/inactive state; to avoid
* confusion, it is simpler to just boot Rockbox.
*/
go_active();
launch();
}
else if (echoplayer_boot_reason == ECHOPLAYER_BOOT_REASON_SW_POWEROFF)
{
/* Ignore power button pressed event until button is first released */
wait_for_power_released = true;
}
/*
* Initialize USB so we can enumerate with the host and
* negotiate charge current (needed even in inactive mode)
*/
usb_init();
usb_start_monitoring();
usb_charging_enable(USB_CHARGING_FORCE);
int demo_page = 0;
const int num_pages = ARRAYLEN(demo_funcs);
while (1)
for (;;)
{
int btn = button_get_w_tmo(HZ);
switch (btn)
refresh_display();
long refresh_tmo = is_active ? HZ : TIMEOUT_BLOCK;
switch (button_get_w_tmo(refresh_tmo))
{
case BUTTON_START:
demo_page += 1;
if (demo_page >= num_pages)
demo_page = 0;
case EV_POWER_PRESSED:
if (wait_for_power_released)
break;
if (!is_active)
{
/* Launch Rockbox due to user pressing power button */
go_active();
launch();
}
else
{
/*
* NOTE: The check for USB insertion here is only
* because we can't change to charging only mode.
*/
if (!usb_inserted())
go_inactive();
}
break;
case BUTTON_SELECT:
if (demo_page == 0)
demo_page = num_pages - 1;
else
demo_page -= 1;
case EV_POWER_RELEASED:
/*
* This cuts power if the power button is not
* held and we're not in the active state due
* to USB mode, etc.
*/
wait_for_power_released = false;
gpio_set_level(GPIO_CPU_POWER_ON, is_active);
break;
case EV_USB_UNPLUGGED:
go_inactive();
break;
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
is_usb_connected = true;
break;
case SYS_USB_DISCONNECTED:
is_usb_connected = false;
case BUTTON_X:
power_off();
break;
default:
go_active();
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
break;
}
demo_funcs[demo_page]();
}
}

View file

@ -43,9 +43,6 @@
#include "lcd.h"
#include "version.h"
/* Show the Rockbox logo - in show_logo.c */
extern void show_logo(void);
#define TAR_CHUNK 512
#define TAR_HEADER_SIZE 157
@ -126,7 +123,7 @@ static void handle_usb(int connect_timeout)
/* Got the message - wait for disconnect */
printf("Bootloader USB mode");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while (1)
{

View file

@ -96,7 +96,7 @@ static void usb_mode(int connect_timeout)
adc_init();
/* ack the SYS_USB_CONNECTED polled from the button queue */
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while(1)
{

View file

@ -129,7 +129,7 @@ static void usb_mode(void)
printf("Bootloader USB mode");
/* Ack the SYS_USB_CONNECTED polled from the button queue */
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while(1)
{

View file

@ -23,9 +23,6 @@
#include "../kernel-internal.h"
#include "system.h"
/* Show the Rockbox logo - in show_logo.c */
extern void show_logo(void);
int main(void)
{
/* Initialize Rockbox kernel */

View file

@ -11,7 +11,7 @@
*
* Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
* and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@ -60,9 +60,6 @@
void lcd_reset(void);
#endif
/* Show the Rockbox logo - in show_logo.c */
extern void show_logo(void);
/* Button definitions */
#if CONFIG_KEYPAD == IRIVER_H10_PAD
#define BOOTLOADER_BOOT_OF BUTTON_LEFT
@ -132,21 +129,21 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo,
struct mi4header_t mi4header;
struct ppmi_header_t ppmi_header;
unsigned long sum;
/* Read header to find out how long the mi4 file is. */
storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET,
PPMI_SECTORS, &ppmi_header);
/* The first four characters at 0x80000 (sector 1024) should be PPMI*/
if( memcmp(ppmi_header.magic, "PPMI", 4) )
return EFILE_NOT_FOUND;
printf("BL mi4 size: %x", ppmi_header.length);
/* Read mi4 header of the OF */
storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS
storage_read_sectors(IF_MD(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS
+ (ppmi_header.length/512), MI4_HEADER_SECTORS, &mi4header);
/* We don't support encrypted mi4 files yet */
if( (mi4header.plaintext) != (mi4header.mi4size-MI4_HEADER_SIZE))
return EINVALID_FORMAT;
@ -178,14 +175,14 @@ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo,
if(sum != mi4header.crc32)
return EBAD_CHKSUM;
#ifdef SANSA_E200
#ifdef SANSA_E200
if (disable_rebuild)
{
char block[512];
printf("Disabling database rebuild");
storage_read_sectors(IF_MD(0,) pinfo->start + 0x3c08, 1, block);
block[0xe1] = 0;
storage_write_sectors(IF_MD(0,) pinfo->start + 0x3c08, 1, block);
@ -206,7 +203,7 @@ static int handle_usb(int connect_timeout)
struct queue_event ev;
int usb = USB_EXTRACTED;
long end_tick = 0;
if (!usb_plugged())
return USB_EXTRACTED;
@ -233,7 +230,7 @@ static int handle_usb(int connect_timeout)
printf("Bootloader USB mode");
usb = USB_HANDLED;
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
#if defined(SANSA_E200) && defined(HAVE_BOOTLOADER_USB_MODE)
/* E200 misses unplug randomly
probably fine for other targets too but needs tested */
@ -394,7 +391,7 @@ void* main(void)
error(EDISK,num_partitions, true);
}
/* Just list the first 2 partitions since we don't have any devices yet
/* Just list the first 2 partitions since we don't have any devices yet
that have more than that */
for(i=0; i<NUM_PARTITIONS; i++)
{
@ -496,7 +493,7 @@ void* main(void)
#endif
goto main_exit;
}
error(0, 0, true);
}

View file

@ -39,7 +39,6 @@
#include "adc.h"
#include "version.h"
extern void show_logo(void);
extern void power_off(void);
static void show_splash(int timeout, const char *msg)
@ -76,7 +75,7 @@ static void usb_mode(void)
/* Got the message - wait for disconnect */
show_splash(0, "Bootloader USB mode");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while (1)
{

View file

@ -32,8 +32,6 @@
*/
#define LOAD_SIZE 0x700000
extern void show_logo( void );
/* This function setup bare minimum
* and jumps to rom in order to activate
* hardcoded rkusb mode
@ -153,9 +151,9 @@ void main(void)
error(EDISK, ret, true);
loadbuffer = (unsigned char*)DRAM_ORIG; /* DRAM */
if (boot == rb)
snprintf(filename,sizeof(filename), BOOTDIR "/%s", BOOTFILE);
snprintf(filename,sizeof(filename), BOOTDIR "/%s", BOOTFILE);
else if (boot == of)
snprintf(filename,sizeof(filename), BOOTDIR "/%s", "BASE.RKW");
@ -170,7 +168,7 @@ void main(void)
/* if we boot rockbox we shutdown on error
* if we boot OF we fall back to rkusb mode on error
*/
*/
if (boot == rb)
{
power_off();
@ -213,7 +211,7 @@ void main(void)
lcd_update();
enter_rkusb();
}
}
/* hang */
while(1);

View file

@ -196,7 +196,7 @@ static void handle_usb(int connect_timeout)
if (button_get_w_tmo(HZ/2) == SYS_USB_CONNECTED)
{
printf("Bootloader USB mode");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while (button_get_w_tmo(HZ/2) != SYS_USB_DISCONNECTED)
{
storage_spin();
@ -215,8 +215,6 @@ static void handle_usb(int connect_timeout)
usb_close();
}
extern void show_logo(void);
void main(void)
{
unsigned char* loadbuffer;

View file

@ -10,7 +10,7 @@
* Copyright (C) 2007 by Dave Chapman
*
* Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@ -46,9 +46,6 @@
#include "loader_strerror.h"
#include "version.h"
/* Show the Rockbox logo - in show_logo.c */
extern void show_logo(void);
/* Address to load main Rockbox image to */
#define LOAD_ADDRESS 0x20000000 /* DRAM_START */
@ -63,12 +60,12 @@ void show_debug_screen(void)
int power_count = 0;
int count = 0;
bool do_power_off = false;
lcd_puts_scroll(0,0,"+++ this is a very very long line to test scrolling. ---");
while (!do_power_off) {
line = 1;
button = button_get(false);
/* Power-off if POWER button has been held for a time
This loop is currently running at about 100 iterations/second
*/
@ -131,10 +128,10 @@ void* main(void)
system_init();
power_init();
kernel_init();
enable_irq();
lcd_init();
adc_init();
@ -143,7 +140,7 @@ void* main(void)
font_init();
lcd_setfont(FONT_SYSFIXED);
show_logo();
backlight_hw_on();

View file

@ -34,6 +34,7 @@
static bool lcd_inited = false;
extern bool is_usb_connected;
extern intptr_t usb_connection_seqnum;
void clearscreen(void)
{
@ -134,6 +135,7 @@ int get_button(int timeout)
case SYS_USB_CONNECTED:
case SYS_USB_DISCONNECTED:
is_usb_connected = (btn == SYS_USB_CONNECTED);
usb_connection_seqnum = button_get_data();
break;
#ifdef HAVE_SCREENDUMP
case BL_SCREENSHOT:

View file

@ -38,6 +38,7 @@
* Handled by the gui code since that's how events are delivered
* TODO: this is an ugly kludge */
bool is_usb_connected = false;
intptr_t usb_connection_seqnum = 0;
static bool screenshot_enabled = false;
@ -71,7 +72,7 @@ void usb_mode(void)
return;
splashf(0, "USB mode");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, usb_connection_seqnum);
while(is_usb_connected)
get_button(TIMEOUT_BLOCK);

View file

@ -45,7 +45,6 @@
#define SHOW_LOGO
extern void show_logo(void);
extern void power_off(void);
static int lcd_inited = 0;
@ -69,7 +68,6 @@ void init_lcd(void)
#ifdef HAVE_BOOTLOADER_USB_MODE
static void show_splash(int timeout, const char *msg)
{
init_lcd();
reset_screen();
lcd_putsxy( (LCD_WIDTH - (SYSFONT_WIDTH * strlen(msg))) / 2,
(LCD_HEIGHT - SYSFONT_HEIGHT) / 2, msg);
@ -107,7 +105,7 @@ static void usb_mode(void)
/* Got the message - wait for disconnect */
show_splash(0, "Bootloader USB mode");
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, button_get_data());
while(1) {
button = button_get_w_tmo(HZ/2);
@ -172,6 +170,9 @@ int main(void)
init_lcd();
#ifdef SHOW_LOGO
show_logo();
#else
printf(MODEL_NAME" Rockbox Bootloader");
printf("Version %s", rbversion);
#endif
button_init();
@ -198,11 +199,6 @@ int main(void)
}
#endif /* HAVE_BOOTLOADER_USB_MODE */
#ifndef SHOW_LOGO
printf(MODEL_NAME" Rockbox Bootloader");
printf("Version %s", rbversion);
#endif
rc = boot_rockbox();
if(rc <= EFILE_EMPTY) {

View file

@ -2996,9 +2996,10 @@ void unregister_storage_idle_func(void (*function)(void), bool run)
\param run
\description
void usb_acknowledge(long id)
void usb_acknowledge(long id, intptr_t seqnum)
\group usb
\param id
\param seqnum
\description
void usb_hid_send(usage_page_t usage_page, int id)

View file

@ -58,6 +58,10 @@ target/hosted/rolo.c
lc-rock.c
#endif
#if defined(HAVE_ELF)
elf_loader.c
#endif
#if defined(HAVE_BOOTDATA) || defined(HAVE_MULTIBOOT)
common/multiboot.c
#ifndef BOOTLOADER
@ -212,7 +216,7 @@ target/hosted/xduoo/xduoolinux_codec.c
target/hosted/button-devinput.c
#endif
#if defined(HIBY_LINUX) && !defined(HIBY_R3PROII) && !defined(HIBY_R1) && !defined(SIMULATOR)
#if defined(HIBY_LINUX) && !defined(HIBY_R3PROII) && !defined(HIBY_R1) && !defined(SIMULATOR)
target/hosted/usb-hiby.c
#endif
@ -945,6 +949,7 @@ target/arm/s5l8702/lcd-asm-s5l8702.S
#endif
/* USB Stack */
// TODO: This needs to be HAVE_USBSTACK && (!BOOTLOADER || HAVE_USB_BOOTLOADER_MODE)
#ifdef HAVE_USBSTACK
usbstack/usb_core.c
#ifdef USB_ENABLE_STORAGE
@ -2024,7 +2029,6 @@ target/arm/rk27xx/ihifi2/audio-ihifi800.c
target/arm/stm32/crt0-stm32h7.S
target/arm/stm32/vectors-stm32h7.S
target/arm/stm32/adc-stm32h7.c
target/arm/stm32/clock-stm32h7.c
target/arm/stm32/debug-stm32h7.c
target/arm/stm32/gpio-stm32h7.c
target/arm/stm32/i2c-stm32h7.c

View file

@ -605,7 +605,7 @@ void backlight_thread(void)
#endif /* HAVE_REMOTE_LCD/ HAVE_REMOTE_LCD_AS_MAIN */
#endif /* !SIMULATOR */
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
break;
#ifdef BACKLIGHT_DRIVER_CLOSE

View file

@ -114,6 +114,7 @@ void sdmmc_host_init(struct sdmmc_host *host,
host->ops = ops;
host->controller = controller;
host->present = !config->is_removable;
host->enabled = true;
mutex_init(&host->lock);
array[i] = host;
@ -188,6 +189,26 @@ static void sdmmc_host_bus_reset(struct sdmmc_host *host)
memset(&host->cardinfo, 0, sizeof(host->cardinfo));
}
/*
* Call to enable/disable the host controller. If disabled then the
* controller and device are powered off and no access to the storage
* device is allowed.
*/
static void sdmmc_host_set_enabled(struct sdmmc_host *host, bool enabled)
{
mutex_lock(&host->lock);
if (enabled != host->enabled)
{
if (host->enabled)
sdmmc_host_bus_reset(host);
host->enabled = enabled;
}
mutex_unlock(&host->lock);
}
#ifdef HAVE_HOTSWAP
static void sdmmc_host_hotswap_event(struct sdmmc_host *host, bool is_present)
{
@ -629,6 +650,9 @@ static int sdmmc_host_transfer(struct sdmmc_host *host,
mutex_lock(&host->lock);
if (!host->enabled)
goto out;
if (!sdmmc_host_medium_present(host))
goto out;
@ -721,6 +745,16 @@ tCardInfo *card_get_info_target(int drive)
return &sdmmc_sd_hosts[drive]->cardinfo;
}
void sd_enable(bool enabled)
{
for (size_t i = 0; i < ARRAYLEN(sdmmc_sd_hosts); ++i)
{
struct sdmmc_host *host = sdmmc_sd_hosts[i];
sdmmc_host_set_enabled(host, enabled);
}
}
long sd_last_disk_activity(void)
{
return sdmmc_sd_last_activity;

View file

@ -186,8 +186,8 @@ static const char* const dw_resp_str[3] =
static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS];
static struct usb_dw_ep0 ep0;
uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */
static uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
static uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */
static uint32_t usb_endpoints; /* available EPs mask */

211
firmware/elf_loader.c Normal file
View file

@ -0,0 +1,211 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "elf_loader.h"
#include "elf-target.h"
#include "file.h"
#if defined(ROCKBOX_LITTLE_ENDIAN)
# define NATIVE_ELF_DATA ELFDATA2LSB
#elif defined(ROCKBOX_BIG_ENDIAN)
# define NATIVE_ELF_DATA ELFDATA2MSB
#else
# error "Unknown endianness!"
#endif
_Static_assert(TARGET_ELF_DATA == NATIVE_ELF_DATA,
"ELF binaries with foreign endianness not supported");
static int elf_validate_header(const struct elf_header *header)
{
if (header->ei_magic[0] != ELFMAG0 ||
header->ei_magic[1] != ELFMAG1 ||
header->ei_magic[2] != ELFMAG2 ||
header->ei_magic[3] != ELFMAG3)
return ELF_ERR_BADMAGIC;
if (header->ei_class != TARGET_ELF_CLASS ||
header->ei_data != TARGET_ELF_DATA)
return ELF_ERR_UNSUPP_ARCH;
if (header->ei_version != EV_CURRENT ||
header->e_version != EV_CURRENT)
return ELF_ERR_UNSUPP_VERSION;
if (header->ei_osabi != TARGET_ELF_OSABI ||
header->ei_abiversion != TARGET_ELF_ABIVERSION)
return ELF_ERR_UNSUPP_OS;
if (header->e_type != ET_EXEC)
return ELF_ERR_UNSUPP_TYPE;
if (header->e_machine != TARGET_ELF_MACHINE)
return ELF_ERR_UNSUPP_ARCH;
if (header->e_phoff == 0)
return ELF_ERR_EMPTY;
if (header->e_phentsize < sizeof(struct elf_phdr))
return ELF_ERR_UNSUPP_PHSIZE;
if (header->e_phnum == PN_XNUM)
return ELF_ERR_TOO_MANY_PHDRS;
return ELF_OK;
}
static int elf_validate_phdr(const struct elf_phdr *phdr,
const struct elf_load_context *ctx)
{
if (phdr->p_type != PT_LOAD)
return ELF_ERR_UNSUPP_PHDR;
if (phdr->p_filesz > phdr->p_memsz)
return ELF_ERR_INVALID_PHDR;
/* Ignore headers which occupy zero memory */
if (phdr->p_memsz == 0)
return 0;
for (size_t i = 0; i < ctx->num_mmap; ++i)
{
const struct elf_memory_map *entry = &ctx->mmap[i];
if (phdr->p_vaddr >= entry->addr &&
phdr->p_vaddr < entry->addr + entry->size &&
phdr->p_memsz <= entry->size)
{
if ((phdr->p_flags & entry->flags) != phdr->p_flags)
return ELF_ERR_MEM_INCOMPAT_FLAGS;
return ELF_OK;
}
}
return ELF_ERR_MEM_UNMAPPED;
}
int elf_load(elf_read_callback_t read_cb,
intptr_t read_arg,
const struct elf_load_context *ctx,
void **entrypoint)
{
struct elf_header ehdr;
struct elf_phdr phdr;
int err;
if (read_cb(read_arg, 0, &ehdr, sizeof(ehdr)))
return ELF_ERR_IO;
err = elf_validate_header(&ehdr);
if (err != ELF_OK)
return err;
/*
* Iterate over program headers, copying segments to memory
*/
for (size_t ph_index = 0; ph_index < ehdr.e_phnum; ++ph_index)
{
off_t ph_off = ehdr.e_phoff + (ph_index * ehdr.e_phentsize);
if (read_cb(read_arg, ph_off, &phdr, sizeof(phdr)))
return ELF_ERR_IO;
err = elf_validate_phdr(&phdr, ctx);
if (err)
return err;
/* Load file data to memory if needed */
if (phdr.p_filesz > 0)
{
if (read_cb(read_arg, phdr.p_offset, (void *)phdr.p_vaddr, phdr.p_filesz))
return ELF_ERR_IO;
}
/* Zero out any memory not backed by file data */
if (phdr.p_memsz > phdr.p_filesz)
{
void *addr = (void *)phdr.p_vaddr + phdr.p_filesz;
size_t size = phdr.p_memsz - phdr.p_filesz;
memset(addr, 0, size);
}
}
*entrypoint = (void *)ehdr.e_entry;
return ELF_OK;
}
int elf_loadfd(int fd,
const struct elf_load_context *ctx,
void **entrypoint)
{
return elf_load(elf_read_fd_callback, fd, ctx, entrypoint);
}
int elf_read_fd_callback(intptr_t fd, off_t pos, void *buf, size_t size)
{
if (lseek(fd, pos, SEEK_SET) == (off_t)-1)
return -1;
ssize_t nread = read(fd, buf, size);
if (nread < 0 || (size_t)nread != size)
return -1;
return 0;
}
int elf_loadpath(const char *filename,
const struct elf_load_context *ctx,
void **entrypoint)
{
int fd = open(filename, O_RDONLY);
if (fd < 0)
return ELF_ERR_FILE_NOT_FOUND;
int err = elf_loadfd(fd, ctx, entrypoint);
close(fd);
return err;
}
int elf_loadmem(const void *elf_buffer,
size_t elf_size,
const struct elf_load_context *ctx,
void **entrypoint)
{
struct elf_loadmem_state state = {
.buffer = elf_buffer,
.size = elf_size,
};
return elf_load(elf_read_mem_callback, (intptr_t)&state, ctx, entrypoint);
}
int elf_read_mem_callback(intptr_t loadmem_state, off_t pos, void *buf, size_t size)
{
struct elf_loadmem_state *state = (void *)loadmem_state;
if (pos < 0 || (size_t)pos >= state->size)
return -1;
if (state->size - (size_t)pos < size)
return -1;
memcpy(buf, state->buffer + pos, size);
return 0;
}

View file

@ -1025,12 +1025,12 @@ Lyre prototype 1 */
#define USB_DETECT_BY_REQUEST
#endif
#if defined(HAVE_USBSTACK) && CONFIG_USBOTG == USBOTG_ARC
#if CONFIG_USBOTG == USBOTG_ARC
#define INCLUDE_TIMEOUT_API
#define USB_DRIVER_CLOSE
#endif
#if defined(HAVE_USBSTACK) && CONFIG_USBOTG == USBOTG_TNETV105
#if CONFIG_USBOTG == USBOTG_TNETV105
#define INCLUDE_TIMEOUT_API
#define USB_DRIVER_CLOSE
#endif
@ -1384,11 +1384,7 @@ Lyre prototype 1 */
#ifdef BOOTLOADER
/* enable usb storage for targets that do bootloader usb */
#if defined(HAVE_BOOTLOADER_USB_MODE) || \
defined(CREATIVE_ZVx) || defined(CPU_TCC780X) || \
CONFIG_USBOTG == USBOTG_JZ4740 || CONFIG_USBOTG == USBOTG_AS3525 || \
CONFIG_USBOTG == USBOTG_S3C6400X || CONFIG_USBOTG == USBOTG_DESIGNWARE || \
CONFIG_USBOTG == USBOTG_JZ4760
#if defined(HAVE_BOOTLOADER_USB_MODE)
#define USB_ENABLE_STORAGE
#endif

View file

@ -36,6 +36,8 @@
#define HAVE_LCD_COLOR
#define HAVE_LCD_BITMAP
#define HAVE_LCD_ENABLE
#define HAVE_LCD_SHUTDOWN
#define IRAM_LCDFRAMEBUFFER __attribute__((section(".framebuffer")))
/* Backlight defines */
#define HAVE_BACKLIGHT
@ -88,9 +90,9 @@
#define BATTERY_CAPACITY_MAX 1100
#define BATTERY_CAPACITY_INC 0
/* Multiboot */
#define HAVE_BOOTDATA
#define BOOT_REDIR "rockbox_main.echor1"
/* TODO: Multiboot */
//#define HAVE_BOOTDATA
//#define BOOT_REDIR "rockbox_main.echor1"
/* USB support */
#ifndef SIMULATOR
@ -112,6 +114,11 @@
#define HAVE_BOOTLOADER_USB_MODE
#endif
#ifdef BOOTLOADER
# define USB_READ_BUFFER_SIZE (32 * 1024)
# define USB_WRITE_BUFFER_SIZE (32 * 1024)
#endif
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
@ -123,3 +130,4 @@
#define HAVE_HOTKEY
#define AB_REPEAT_ENABLE
#define HAVE_BOOTLOADER_SCREENDUMP
#define HAVE_ELF

View file

@ -222,6 +222,7 @@
/* logf() over USB serial (http://www.rockbox.org/wiki/PortalPlayerUsb) */
//#define USB_ENABLE_SERIAL
#define HAVE_USBSTACK
//#define HAVE_BOOTLOADER_USB_MODE
#define HAVE_USB_HID_MOUSE
#define USB_VENDOR_ID 0x05AC
#define USB_PRODUCT_ID 0x1260

View file

@ -201,6 +201,7 @@ No access to the NAND yet..
#define CONFIG_USBOTG USBOTG_JZ4740
#define HAVE_USBSTACK
#define HAVE_BOOTLOADER_USB_MODE
/* Connect by events, not by tick polling */
#define USB_STATUS_BY_EVENT

View file

@ -157,7 +157,8 @@
#define CONFIG_USBOTG USBOTG_ISP1583
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x041e
//#define HAVE_BOOTLOADER_USB_MODE
#define USB_VENDOR_ID 0x041e
#define USB_PRODUCT_ID 0x4133
#define USB_NUM_ENDPOINTS 7

View file

@ -158,7 +158,8 @@
#define CONFIG_USBOTG USBOTG_ISP1583
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x041e
//#define HAVE_BOOTLOADER_USB_MODE
#define USB_VENDOR_ID 0x041e
#define USB_PRODUCT_ID 0x4133
#define USB_NUM_ENDPOINTS 7

View file

@ -158,7 +158,8 @@
/* #define CONFIG_USBOTG USBOTG_ISP1761 */
#define CONFIG_USBOTG USBOTG_ISP1583
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x041e
//#define HAVE_BOOTLOADER_USB_MODE
#define USB_VENDOR_ID 0x041e
#define USB_PRODUCT_ID 0x4133
#define USB_NUM_ENDPOINTS 7

View file

@ -231,6 +231,7 @@ struct sdmmc_host
struct mutex lock;
/* Bus & device state flags; must only be accessed with lock held */
bool enabled : 1;
bool need_reset : 1;
bool powered : 1;
bool initialized : 1;

View file

@ -70,8 +70,8 @@
* mass storage mode, it will require exclusive access to the disk and ask all
* threads to release any file handle and stop using the disks. It does so by
* broadcasting a SYS_USB_CONNECTED message, which threads must acknowledge using
* usb_acknowledge(SYS_USB_CONNECTED_ACK). They must not access the disk until
* SYS_USB_DISCONNECTED is broadcast. To ease waiting, threads can call
* usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data). They must not access the disk
* until SYS_USB_DISCONNECTED is broadcast. To ease waiting, threads can call
* usb_wait_for_disconnect() or usb_wait_for_disconnect_w_tmo() on their waiting
* queue.
*
@ -119,6 +119,7 @@ enum
USB_NOTIFY_SET_ADDR, /* Event */
USB_NOTIFY_SET_CONFIG, /* Event */
USB_NOTIFY_BUS_RESET, /* Event */
USB_NOTIFY_CLASS_DRIVER, /* Event - notify_event() of specified class driver */
#endif
#ifdef USB_FIREWIRE_HANDLING
USB_REQUEST_REBOOT, /* Event */
@ -189,16 +190,14 @@ struct usb_transfer_completion_event_data
void usb_init(void) INIT_ATTR;
/* target must implement this to enable/disable the usb transceiver/core */
void usb_enable(bool on);
/* when one or more driver requires exclusive mode, this is called after all threads have acknowledged
* exclusive mode and disk have been umounted; otherwise it is called immediately after host has
* been detected */
/* called after host has been detected */
void usb_attach(void);
/* enable usb detection monitoring; before this function is called, all usb
* detection changes are ignored */
void usb_start_monitoring(void) INIT_ATTR;
void usb_close(void);
/* acknowledge usb connection, typically with SYS_USB_CONNECTED_ACK */
void usb_acknowledge(long id);
void usb_acknowledge(long id, intptr_t seqnum);
/* block the current thread until SYS_USB_DISCONNECTED has been broadcast */
void usb_wait_for_disconnect(struct event_queue *q);
/* same as usb_wait_for_disconnect() but with a timeout, returns 1 on timeout */
@ -240,18 +239,34 @@ void usb_set_mode(int mode);
/* USB driver call this function to notify that a transfer has completed */
void usb_signal_transfer_completion(
struct usb_transfer_completion_event_data *event_data);
/* Clear all signaled transfer completion events from event queue */
void usb_clear_pending_transfer_completion_events(void);
/* notify the USB code that some important event has occurred which influences the
* USB state (like USB_NOTIFY_SET_ADDR). USB drivers should call usb_core_notify_*
* functions and not this function. */
* functions and not this function.
* for USB_NOTIFY_CLASS_DRIVER, use usb_signal_class_notify() instead */
void usb_signal_notify(long id, intptr_t data);
/* wrapper for usb_signal_notify(USB_NOTIFY_CLASS_DRIVER)
* class_num: target driver. USB_DRIVER_*
* data: optional data. note that its upper 8 bits will be masked */
static inline void usb_signal_class_notify(int8_t class_num, uint32_t data) {
usb_signal_notify(USB_NOTIFY_CLASS_DRIVER, class_num << 24 | (data & 0x00ffffff));
}
/* returns whether a USB_DRIVER_* is enabled (like HID, mass storage, ...) */
bool usb_driver_enabled(int driver);
/* returns whether exclusive storage is available for USB */
bool usb_exclusive_storage(void);
#endif /* HAVE_USBSTACK */
/* broadcast usb insertion event to enable exclusive storage */
void usb_request_exclusive_storage(void);
/* finish exclusive storage access if enabled and mount volumes */
void usb_release_exclusive_storage(void);
#ifdef USB_FIREWIRE_HANDLING
bool firewire_detect(void);
void usb_firewire_connect_event(void);

View file

@ -56,11 +56,12 @@ void usb_core_control_complete(int status);
void usb_core_legacy_control_request(struct usb_ctrlrequest* req);
void usb_core_transfer_complete(int endpoint,int dir,int status,int length);
void usb_core_bus_reset(void);
bool usb_core_any_exclusive_storage(void);
void usb_core_enable_driver(int driver,bool enabled);
bool usb_core_driver_enabled(int driver);
#ifdef HAVE_USBSTACK
void usb_core_handle_transfer_completion(
struct usb_transfer_completion_event_data* event);
#endif
void usb_core_handle_notify(long id, intptr_t data);
/* For controllers which handle SET ADDR and/or SET CONFIG in hardware */
void usb_core_notify_set_address(uint8_t addr);
@ -71,4 +72,3 @@ void usb_core_hotswap_event(int volume,bool inserted);
#endif
#endif

View file

@ -0,0 +1,50 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ELF_TARGET_H__
#define __ELF_TARGET_H__
#include "elf.h"
#include "config.h"
#define TARGET_ELF_CLASS ELFCLASS32
#define TARGET_ELF_OSABI ELFOSABI_SYSV
#define TARGET_ELF_ABIVERSION 0
#define elf_phdr elf32_phdr
#if defined(CPU_ARM)
# define TARGET_ELF_MACHINE EM_ARM
#elif defined(CPU_MIPS)
# define TARGET_ELF_MACHINE EM_MIPS
#elif defined(CPU_COLDFIRE)
# define TARGET_ELF_MACHINE EM_68K
#else
# error "Unknown CPU architecture!"
#endif
#if defined(ROCKBOX_LITTLE_ENDIAN)
# define TARGET_ELF_DATA ELFDATA2LSB
#elif defined(ROCKBOX_BIG_ENDIAN)
# define TARGET_ELF_DATA ELFDATA2MSB
#else
# error "Unknown endianness!"
#endif
#endif /* __ELF_TARGET_H__ */

143
firmware/include/elf.h Normal file
View file

@ -0,0 +1,143 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ELF_H__
#define __ELF_H__
#include <stdint.h>
/* Field offsets in the e_ident array */
#define EI_MAG0 0
#define EI_MAG1 1
#define EI_MAG2 2
#define EI_MAG3 3
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
#define EI_ABIVERSION 8
#define EI_PAD 9
#define EI_NIDENT 16
/* Values for ei_magic */
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
/* Values for ei_class */
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
/* Values for ei_data */
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
/* Values for ei_version and e_version */
#define EV_NONE 0
#define EV_CURRENT 1
/* Values for ei_osabi */
#define ELFOSABI_NONE ELFOSABI_SYSV
#define ELFOSABI_SYSV 0
/* Values for e_type */
#define ET_NONE 0
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
/* Values for e_machine */
#define EM_NONE 0
#define EM_68K 4
#define EM_MIPS 8
#define EM_ARM 40
/*
* Special value for e_phnum when the real number of
* program headers is too large.
*/
#define PN_XNUM 0xFFFFu
/* ELF basic types */
typedef uint32_t elf_addr_t;
typedef uint32_t elf_off_t;
/* ELF file header */
struct elf_header
{
union {
uint8_t e_ident[EI_NIDENT];
struct {
uint8_t ei_magic[4];
uint8_t ei_class;
uint8_t ei_data;
uint8_t ei_version;
uint8_t ei_osabi;
uint8_t ei_abiversion;
};
};
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf_addr_t e_entry;
elf_off_t e_phoff;
elf_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
/* Values for p_type */
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
/* Values for p_flags */
#define PF_X (1 << 0) /* Segment is executable */
#define PF_W (1 << 1) /* Segment is writable */
#define PF_R (1 << 2) /* Segment is readable */
/* ELF 32-bit program header */
struct elf32_phdr
{
uint32_t p_type;
elf_off_t p_offset;
elf_addr_t p_vaddr;
elf_addr_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
#endif /* __ELF_H__ */

View file

@ -0,0 +1,115 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ELF_LOADER_H__
#define __ELF_LOADER_H__
/*
* Primitive ELF loader -- allows loading an ELF binary from
* a file descriptor into RAM. Only static binaries linked at
* a fixed address are supported.
*
* The caller supplies a memory map which limits where the
* ELF file can be loaded; if a program header requests any
* memory outside of this mapping, loading will fail. There
* is no support for relocation, PIC code, or any form of
* dynamic linking.
*
* The loader can return the entry point of the ELF binary.
* It does not support arbitrary symbol lookups.
*/
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
enum elf_error
{
ELF_OK,
ELF_ERR_FILE_NOT_FOUND,
ELF_ERR_IO,
ELF_ERR_EMPTY,
ELF_ERR_BADMAGIC,
ELF_ERR_UNSUPP_ARCH,
ELF_ERR_UNSUPP_OS,
ELF_ERR_UNSUPP_VERSION,
ELF_ERR_UNSUPP_TYPE,
ELF_ERR_UNSUPP_FORMAT,
ELF_ERR_UNSUPP_PHSIZE,
ELF_ERR_TOO_MANY_PHDRS,
ELF_ERR_UNSUPP_PHDR,
ELF_ERR_INVALID_PHDR,
ELF_ERR_MEM_INCOMPAT_FLAGS,
ELF_ERR_MEM_UNMAPPED,
};
/*
* Describes an entry in the virtual memory map specified
* when loading an ELF file.
*
* ELF segments must fit completely within a single entry
* in the memory map. Segments crossing multiple mappings
* are rejected even if the mappings are contiguous.
*
* A segment may also be rejected if it has flags (R/W/X)
* not present in the corresponding memory map entry.
*/
struct elf_memory_map
{
uintptr_t addr;
size_t size;
uint32_t flags;
};
struct elf_load_context
{
const struct elf_memory_map *mmap;
size_t num_mmap;
};
typedef int (*elf_read_callback_t) (intptr_t arg, off_t pos, void *buf, size_t size);
int elf_load(elf_read_callback_t read_cb,
intptr_t read_arg,
const struct elf_load_context *ctx,
void **entrypoint);
int elf_loadfd(int fd,
const struct elf_load_context *ctx,
void **entrypoint);
int elf_read_fd_callback(intptr_t fd, off_t pos, void *buf, size_t size);
int elf_loadpath(const char *filename,
const struct elf_load_context *ctx,
void **entrypoint);
struct elf_loadmem_state
{
const void *buffer;
size_t size;
};
int elf_loadmem(const void *elf_buffer,
size_t elf_size,
const struct elf_load_context *ctx,
void **entrypoint);
int elf_read_mem_callback(intptr_t loadmem_state, off_t pos, void *buf, size_t size);
#endif /* __ELF_LOADER_H__ */

View file

@ -32,7 +32,36 @@ CM_NVIC @ 0xe000e000 : block {
// System control block
CM_SCB @ 0xe000ed00 : block {
ICSR @ 0x04 : reg {
-- 31 NMIPENDSET
-- 28 PENDSVSET
-- 27 PENDSVCLR
-- 26 PENDSTSET
-- 25 PENDSTCLR
-- 23 ISRPREEMPT
-- 22 ISRPENDING
20 12 VECTPENDING
-- 11 RETTOBASE
08 00 VECTACTIVE
}
VTOR @ 0x08 : reg
AIRCR @ 0x0c : reg {
31 16 VECTKEY : { 0x05FA = KEY }
-- 15 ENDIANNESS : { 0 = LITTLE; 1 = BIG }
10 08 PRIGROUP
-- 02 SYSRESETREQ
-- 01 VECTCLRACTIVE
-- 00 VECTRESET
}
SCR @ 0x10 : reg {
4 SEVONPEND
2 SLEEPDEEP
1 SLEEPONEXIT
}
CCR @ 0x14 : reg {
18 BP
17 IC
@ -41,9 +70,79 @@ CM_SCB @ 0xe000ed00 : block {
08 BFHFNMIGN
04 DIV_0_TRP
03 UNALIGN_TRP
01 USERETMPEND
01 USERSETMPEND
00 NONBASETHRDENA
}
SHPR1 @ 0x18 : reg {
23 16 PRI_USAGEFAULT
15 08 PRI_BUSFAULT
07 00 PRI_MEMMANAGE
}
SHPR2 @ 0x1c : reg {
31 24 PRI_SVCALL
}
SHPR3 @ 0x20 : reg {
31 24 PRI_SYSTICK
23 16 PRI_PENDSV
07 00 PRI_DEBUGMONITOR
}
SHCSR @ 0x24 : reg {
18 USGFAULTENA
17 BUSFAULTENA
16 MEMFAULTENA
15 SVCALLPENDED
14 BUSFAULTPENDED
13 MEMFAULTPENDED
12 USGFAULTPENDED
11 SYSTICKACT
10 PENDSVACT
08 MONITORACT
07 SVCALLACT
03 USGFAULTACT
01 BUSFAULTACT
00 MEMFAULTACT
}
CFSR @ 0x28 : reg {
// UFSR bits
25 DIVBYZERO
24 UNALIGNED
19 NOCP
18 INVPC
17 INVSTATE
16 UNDEFINSTR
// BFSR bits
15 BFARVALID
13 LSPERR
12 STKERR
11 UNSTKERR
10 IMPRECISERR
09 PRECISERR
08 IBUSERR
// MMSR bits
07 MMARVALID
05 MLSPERR
04 MSTKERR
03 MUNSTKERR
01 DACCVIOL
00 IACCVIOL
}
HFSR @ 0x2c : reg {
31 DEBUGEVT
30 FORCED
01 VECTTBL
}
MMFAR @ 0x34 : reg
BFAR @ 0x38 : reg
CPACR @ 0x88 : reg
}
// System timer

View file

@ -254,6 +254,20 @@ RCC @ 0x58024400 : block {
0 LSION
}
RSR @ 0xd0 : reg {
30 LPWRRSTF
28 WWDG1RSTF
26 IWDGRSTF
24 SFTRSTF
23 PORRSTF
22 PINRSTF
21 BORRSTF
20 D2RSTF
19 D1RSTF
17 CPURSTF
16 RMVF
}
AHB1ENR @ 0xd8 : reg {
28 USB2OTGHSULPIEN
27 USB2OTGHSEN
@ -587,6 +601,10 @@ RTC @ 0x58004000 : block {
14 00 PREDIV_S
}
WUTR @ 0x14 : reg {
15 00 VALUE
}
WPR @ 0x24 : reg {
07 00 KEY : { 0xCA = KEY1; 0x53 = KEY2 }
}

View file

@ -89,7 +89,7 @@ static bool scroll_process_message(int delay)
case SYS_TIMEOUT:
return false;
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
usb_wait_for_disconnect(&scroll_queue);
sync_display_ticks();
return true;

View file

@ -236,7 +236,7 @@ static void NORETURN_ATTR storage_thread(void)
storage_event_send(CONFIG_STORAGE, ev.id, (intptr_t)&bdcast);
usb_mode = ev.id == SYS_USB_CONNECTED;
if (usb_mode) {
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
}
else {
bdcast = CONFIG_STORAGE;

View file

@ -108,7 +108,7 @@ static void piezo_thread(void)
/*logf("USB: Piezo core");*/
piezo_hw_stop();
queue_clear(&piezo_queue);
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
usb_wait_for_disconnect(&piezo_queue);
break ;
#endif

View file

@ -197,11 +197,6 @@ void usb_enable(bool on)
void usb_attach(void)
{
#if defined(IPOD_VIDEO)
/* FIXME: Some iPod Video's need this 2nd call of usb_drv_init() to establish
* an USB connection. */
usb_drv_init();
#endif
}
bool usb_plugged(void)

View file

@ -34,13 +34,13 @@ static int dma_used = 0;
/* Status flags */
#define STATUS_CHANNEL_ACTIVE (1<<0)
static struct dma_channel_state
static struct dma_channel_state
{
volatile unsigned status;
void (*callback)(void);
} dma_state [NUM_CHANNELS];
struct dma_channel_regs
struct dma_channel_regs
{
volatile unsigned long disrc;
volatile unsigned long disrcc;
@ -51,7 +51,7 @@ struct dma_channel_regs
volatile unsigned long dcsrc;
volatile unsigned long dcdst;
volatile unsigned long dmasktrig;
volatile unsigned long reserved [7]; /* pad to 0x40 bytes */
volatile unsigned long reserved [7]; /* pad to 0x40 bytes */
};
@ -61,7 +61,7 @@ struct dma_channel_regs *dma_regs [4] =
(struct dma_channel_regs *) &DISRC1,
(struct dma_channel_regs *) &DISRC2,
(struct dma_channel_regs *) &DISRC3
}
}
;
@ -73,7 +73,7 @@ void dma_init(void)
/* Clear pending source */
SRCPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
INTPND = DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK;
/* Enable interrupt in controller */
bitclr32(&INTMOD, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
bitclr32(&INTMSK, DMA0_MASK | DMA1_MASK | DMA2_MASK | DMA3_MASK);
@ -101,15 +101,15 @@ void dma_release(void)
}
inline void dma_disable_channel(int channel)
void dma_disable_channel(int channel)
{
struct dma_channel_regs *regs = dma_regs [channel];
struct dma_channel_regs *regs = dma_regs [channel];
/* disable the specified channel */
/* Reset the channel */
regs->dmasktrig |= DMASKTRIG_STOP;
/* Wait for DMA controller to be ready */
while(regs->dmasktrig & DMASKTRIG_ON)
;
@ -120,35 +120,35 @@ inline void dma_disable_channel(int channel)
void dma_enable_channel(int channel, struct dma_request *request)
{
struct dma_channel_regs *regs = dma_regs [channel];
/* TODO - transfer sizes (assumes word) */
if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID)
panicf ("DMA: invalid channel");
/* setup a transfer on specified channel */
dma_disable_channel (channel);
dma_disable_channel(channel);
if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR)
regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR;
else
regs->disrc = (unsigned long)request->source_addr;
regs->disrcc = request->source_control;
if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR)
regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR;
else
regs->didst = (unsigned long)request->dest_addr;
regs->didstc = request->dest_control;
regs->dcon = request->control | request->count |
regs->dcon = request->control | request->count |
DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL;
dma_state [channel].callback = request->callback;
/* Activate the channel */
commit_discard_dcache_range((void *)request->dest_addr, request->count * 4);
dma_state [channel].status |= STATUS_CHANNEL_ACTIVE;
regs->dmasktrig = DMASKTRIG_ON;
@ -157,7 +157,7 @@ void dma_enable_channel(int channel, struct dma_request *request)
/* Start DMA */
regs->dmasktrig |= DMASKTRIG_SW_TRIG;
}
}
/* ISRs */
@ -168,7 +168,7 @@ static inline void generic_isr (unsigned channel)
if (dma_state [channel].callback)
/* call callback for relevant channel */
dma_state [channel].callback();
dma_state [channel].status &= ~STATUS_CHANNEL_ACTIVE;
}
}

View file

@ -36,7 +36,7 @@
#error Unsupported target
#endif
struct dma_request
struct dma_request
{
volatile void *source_addr;
volatile void *dest_addr;
@ -51,7 +51,7 @@ struct dma_request
void dma_init(void);
void dma_enable_channel(int channel, struct dma_request *request);
inline void dma_disable_channel(int channel);
void dma_disable_channel(int channel);
void dma_retain(void);
void dma_release(void);

View file

@ -1,6 +1,6 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___void
* Open \______ \ ____ ____ | | _\_ |__ _______ ___void
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
@ -25,6 +25,7 @@
#include "system.h"
#include "stdlib.h"
#include "button.h"
#include "tick.h"
#include "touchscreen.h"
#define NO_OF_TOUCH_DATA 5
@ -99,9 +100,9 @@ void touchscreen_scan_device()
static int touch_data_index = 0;
int saveADCDLY;
/* check touch state */
if(ADCDAT1 & (1<<15))
/* check touch state */
if(ADCDAT1 & (1<<15))
{
return;
}
@ -111,23 +112,23 @@ void touchscreen_scan_device()
/* resets the index if the last touch could not be read 5 times */
touch_data_index = 0;
}
/* read touch data */
/* read touch data */
saveADCDLY = ADCDLY;
ADCDLY = 40000; /*delay ~0.8ms (1/50M)*4000 */
ADCDLY = 40000; /*delay ~0.8ms (1/50M)*4000 */
ADCTSC = (1<<3)|(1<<2); /* pullup disable, seq x,y pos measure */
/* start adc */
ADCCON|= 0x1;
ADCCON|= 0x1;
/* wait for start and end */
while(ADCCON & 0x1);
while(!(ADCCON & 0x8000));
x[touch_data_index] = ADCDAT0&0x3ff;
y[touch_data_index] = ADCDAT1&0x3ff;
ADCTSC = 0xd3; /* back to interrupt mode */
ADCDLY = saveADCDLY;
touch_data_index++;
if (touch_data_index > NO_OF_TOUCH_DATA - 1)
@ -161,18 +162,18 @@ int touchscreen_read_device(int *data, int *old_data)
/* sort the 5 data taken and use the median value */
qsort(x, NO_OF_TOUCH_DATA, sizeof(short), short_cmp);
qsort(y, NO_OF_TOUCH_DATA, sizeof(short), short_cmp);
x_touch = last_x = x[(NO_OF_TOUCH_DATA - 1)/2];
y_touch = last_y = y[(NO_OF_TOUCH_DATA - 1)/2];
last_touch = current_tick;
touch_hold = true;
touch_available = false;
}
*old_data = *data = touch_to_pixels(x_touch, y_touch);
btn |= touchscreen_to_pixels((*data&0xffff0000) >> 16,
(*data&0x0000ffff),
data);
@ -183,8 +184,6 @@ int touchscreen_read_device(int *data, int *old_data)
/* put the touchscreen back into interrupt mode */
touch_hold = false;
}
return btn;
}

View file

@ -799,7 +799,7 @@ int sd_read_sectors(IF_MD(int card_no,) sector_t start, int incount,
ret = 0; /* assume success */
else
#endif
ret = sd_transfer_sectors(card_no, start, incount, inbuf, false);
ret = sd_transfer_sectors(IF_MD_DRV(card_no), start, incount, inbuf, false);
dbgprintf ("sd_read, ret=%d\n", ret);
return ret;
}
@ -827,7 +827,7 @@ int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
return 0; /* assume success */
else
#endif
return sd_transfer_sectors(drive, start, count, (void*)outbuf, true);
return sd_transfer_sectors(IF_MD_DRV(drive), start, count, (void*)outbuf, true);
#endif
}
/*****************************************************************************/

View file

@ -1,70 +1,34 @@
#include "cpu.h"
/*
* TODO: this is temporary and has not been tested
*/
ENTRY(main)
ENTRY(crt0_start)
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
STARTUP(target/arm/stm32/crt0-stm32h7.o)
MEMORY
{
SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE
ITCM (rwx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE
SDRAM (rwx) : ORIGIN = STM32_SDRAM1_BASE, LENGTH = MEMORYSIZE * 1024 * 1024
DTCM (rw) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE
ITCM (rx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE
SRAM_AXI (rw) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
SDRAM (rwx) : ORIGIN = STM32_SDRAM1_BASE, LENGTH = MEMORYSIZE * 1024 * 1024
}
/*
* to control section alignment (only affects on-disk alignment):
* -Wl,-z,max-page-size=0x1
*/
PHDRS
{
sram_rx PT_LOAD ;
sram_ro PT_LOAD ;
sram_rw PT_LOAD ;
itcm PT_LOAD ;
dtcm PT_LOAD ;
sdram_rx PT_LOAD ;
sdram_rw PT_LOAD ;
itcm PT_LOAD;
sdram PT_LOAD;
}
SECTIONS
{
.text :
{
loadaddress = .; /* only needed to keep ROLO happy */
KEEP(*(.bootdata))
*(.init.text*)
*(.text*)
} > SRAM_AXI :sram_rx
.rodata :
{
*(.rodata*)
} > SRAM_AXI :sram_ro
.data :
{
_databegin = .;
*(.data*)
_dataend = .;
} > SRAM_AXI :sram_rw
_datacopy = LOADADDR(.data);
.itext :
{
KEEP(*(.vectors.arm))
KEEP(*(.vectors.platform))
KEEP(*(.vectors.arm));
KEEP(*(.vectors.platform));
*(.icode*);
} > ITCM :itcm
.stack (NOLOAD) :
.stack (NOLOAD) : ALIGN(8)
{
irqstackbegin = .;
. += 0x400;
@ -73,9 +37,30 @@ SECTIONS
stackbegin = .;
. += 0x2000;
stackend = .;
} > DTCM :NONE
*(.stack);
} > DTCM :dtcm
.framebuffer (NOLOAD) :
{
*(.framebuffer);
} > SRAM_AXI :NONE
.text :
{
loadaddress = .; /* only needed to keep ROLO happy */
*(.init*);
*(.text*);
} > SDRAM :sdram
.rodata :
{
*(.rodata*);
} > SDRAM :sdram
.data :
{
*(.data*);
} > SDRAM :sdram
.bss (NOLOAD) :
{
@ -83,7 +68,7 @@ SECTIONS
*(.bss*);
*(COMMON);
_bssend = .;
} > SDRAM :sdram_rw
} > SDRAM :sdram
audiobuffer = ALIGN(32);
audiobufend = ORIGIN(SDRAM) + LENGTH(SDRAM) - CODEC_SIZE - PLUGIN_BUFFER_SIZE;

View file

@ -7,26 +7,29 @@ STARTUP(target/arm/stm32/crt0-stm32h7.o)
MEMORY
{
SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE
FLASH1 (rx) : ORIGIN = STM32_FLASH_BANK1_BASE, LENGTH = STM32_FLASH_BANK1_SIZE
SRAM_AXI (rw) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
FLASH1 (rx) : ORIGIN = STM32_FLASH_BANK1_BASE, LENGTH = STM32_FLASH_BANK1_SIZE
}
SECTIONS
{
.text :
{
KEEP(*(.vectors.arm))
KEEP(*(.vectors.platform))
*(.init.text*)
*(.text*)
*(.rodata*)
KEEP(*(.vectors.arm));
KEEP(*(.vectors.platform));
*(.init*);
*(.text*);
} > FLASH1
.rodata :
{
*(.rodata*);
} > FLASH1
.data :
{
_databegin = .;
*(.data*)
*(.data*);
_dataend = .;
} > SRAM_AXI AT> FLASH1
_datacopy = LOADADDR(.data);
@ -39,7 +42,7 @@ SECTIONS
_bssend = .;
} > SRAM_AXI
.stack (NOLOAD) : ALIGN(4)
.stack (NOLOAD) : ALIGN(8)
{
irqstackbegin = .;
. += 0x400;
@ -48,9 +51,12 @@ SECTIONS
stackbegin = .;
. += 0x2000;
stackend = .;
} > SRAM_AXI
*(.stack);
} > DTCM
.framebuffer (NOLOAD) :
{
*(.framebuffer);
} > SRAM_AXI
}
EXTERN(__vectors_arm);

View file

@ -1,65 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "clock-stm32h7.h"
#include "mutex.h"
#include "panic.h"
#include <stdint.h>
struct stm_clock_state
{
struct mutex mutex;
uint8_t refcount[STM_NUM_CLOCKS];
};
static struct stm_clock_state stm_clocks;
void stm_clock_init(void)
{
mutex_init(&stm_clocks.mutex);
stm_target_clock_init();
}
void stm_clock_enable(enum stm_clock clock)
{
mutex_lock(&stm_clocks.mutex);
if (stm_clocks.refcount[clock] == UINT8_MAX)
panicf("%s: clock %d overflow", __func__, (int)clock);
if (stm_clocks.refcount[clock]++ == 0)
stm_target_clock_enable(clock, true);
mutex_unlock(&stm_clocks.mutex);
}
void stm_clock_disable(enum stm_clock clock)
{
mutex_lock(&stm_clocks.mutex);
if (stm_clocks.refcount[clock] == 0)
panicf("%s: clock %d underflow", __func__, (int)clock);
if (--stm_clocks.refcount[clock] == 0)
stm_target_clock_enable(clock, false);
mutex_unlock(&stm_clocks.mutex);
}

View file

@ -25,65 +25,47 @@
#include <stdbool.h>
#include <stddef.h>
enum stm_clock
struct stm32_clock
{
STM_CLOCK_SPI1_KER,
STM_CLOCK_SPI2_KER,
STM_CLOCK_SPI3_KER,
STM_CLOCK_SPI4_KER,
STM_CLOCK_SPI5_KER,
STM_CLOCK_SPI6_KER,
STM_CLOCK_LTDC_KER,
STM_CLOCK_SDMMC1_KER,
STM_CLOCK_SDMMC2_KER,
STM_NUM_CLOCKS,
uint32_t frequency;
uint32_t en_reg;
uint32_t en_bit;
uint32_t lpen_reg;
uint32_t lpen_bit;
};
/*
* Implemented by the target to initialize all oscillators,
* system, CPU, and bus clocks that need to be enabled from
* early boot.
* Enables a clock by setting its enable bits in the RCC.
*/
void stm_target_clock_init(void) INIT_ATTR;
static inline void stm32_clock_enable(const struct stm32_clock *clk)
{
if (clk->en_reg)
*(volatile uint32_t *)clk->en_reg |= clk->en_bit;
if (clk->lpen_reg)
*(volatile uint32_t *)clk->lpen_reg |= clk->lpen_bit;
}
/*
* Callback to be implemented by the target when the hardware
* clock needs to be turned on or off. Clocks are internally
* reference counted so only the first / last user will change
* the hardware state.
*
* Only clocks that are actually used need to be implemented,
* and unless otherwise noted it is allowed for enable/disable
* to be a no-op if the clock is always enabled.
* Disables a clock in the RCC.
*/
void stm_target_clock_enable(enum stm_clock clock, bool enable);
static inline void stm32_clock_disable(const struct stm32_clock *clk)
{
if (clk->en_reg)
*(volatile uint32_t *)clk->en_reg &= ~clk->en_bit;
/*
* Callback to return a specific clock's frequency. For most
* peripherals the frequency must be known at initialization
* and not change afterwards; see peripheral drivers for the
* details, as their exact requirements may vary.
*/
size_t stm_target_clock_get_frequency(enum stm_clock clock);
/*
* Called from system_init(). Sets up internal book-keeping
* and then calls stm_target_clock_init().
*/
void stm_clock_init(void) INIT_ATTR;
/*
* Enable or disable a clock. Not safe to call from an IRQ handler.
*/
void stm_clock_enable(enum stm_clock clock);
void stm_clock_disable(enum stm_clock clock);
if (clk->lpen_reg)
*(volatile uint32_t *)clk->lpen_reg &= ~clk->lpen_bit;
}
/*
* Get a clock's frequency in Hz.
*/
static inline size_t stm_clock_get_frequency(enum stm_clock clock)
static inline uint32_t stm32_clock_get_frequency(const struct stm32_clock *clk)
{
return stm_target_clock_get_frequency(clock);
return clk->frequency;
}
#endif /* __CLOCK_STM32H7_H__ */

View file

@ -19,19 +19,12 @@
*
****************************************************************************/
#include "config.h"
#include "bootdata.h"
#include "regs/stm32h743/pwr.h"
.syntax unified
.text
#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER)
.section .bootdata
put_boot_data_here
#endif
.section .init.text,"ax",%progbits
.section .init,"ax",%progbits
.global reset_handler
.type reset_handler, function
@ -59,9 +52,15 @@ reset_handler:
b crt0_start
.global start
.type start, function
.global crt0_start
.type crt0_start, function
crt0_start:
#ifdef BOOTLOADER
/*
* Initialize data/bss for bootloader
* (not needed on app since ELF loader takes care of it)
*/
/* Zero out BSS */
ldr a1, =_bssbegin
ldr a2, =_bssend
@ -73,6 +72,7 @@ crt0_start:
ldr a2, =_dataend
ldr a3, =_datacopy
bl crt0_area_copy
#endif
/* Clear the main thread stack */
ldr a1, =stackbegin
@ -103,6 +103,7 @@ crt0_start:
b main
#ifdef BOOTLOADER
.local crt0_area_copy
.type crt0_area_copy, function
/* crt0_area_copy(uint32_t *dst, uint32_t *dst_end, const void *src) */
@ -112,7 +113,7 @@ crt0_area_copy:
strhi v1, [a1], #4
bhi crt0_area_copy
bx lr
#endif
.local crt0_area_clear
.type crt0_area_clear, function

View file

@ -32,14 +32,14 @@
/* Flag to use VOS0 */
#define STM32H743_USE_VOS0 (CPU_FREQ > 400000000)
static void init_hse(void)
INIT_ATTR static void init_hse(void)
{
reg_writef(RCC_CR, HSEON(1));
while (!reg_readf(RCC_CR, HSERDY));
}
static void init_pll(void)
INIT_ATTR static void init_pll(void)
{
/* For simplicity, PLL parameters are hardcoded */
_Static_assert(STM32_HSE_FREQ == 24000000,
@ -97,7 +97,7 @@ static void init_pll(void)
while (!reg_readf(RCC_CR, PLL3RDY));
}
static void init_vos(void)
INIT_ATTR static void init_vos(void)
{
reg_writef(PWR_D3CR, VOS_V(VOS1));
while (!reg_readf(PWR_D3CR, VOSRDY));
@ -114,7 +114,7 @@ static void init_vos(void)
}
}
static void init_system_clock(void)
INIT_ATTR static void init_system_clock(void)
{
/* Enable HCLK /2 divider (CPU is at 480 MHz, HCLK limit is 240 MHz) */
reg_writef(RCC_D1CFGR, HPRE(8));
@ -134,7 +134,7 @@ static void init_system_clock(void)
while (reg_readf(FLASH_ACR, LATENCY) != 4);
}
static void init_lse(void)
INIT_ATTR static void init_lse(void)
{
/*
* Skip if LSE and RTC are already enabled.
@ -161,7 +161,7 @@ static void init_lse(void)
reg_writef(PWR_CR1, DBP(0));
}
static void init_periph_clock(void)
INIT_ATTR static void init_periph_clock(void)
{
reg_writef(RCC_D1CCIPR, SDMMCSEL_V(PLL1Q));
reg_writef(RCC_D2CCIP1R, SPI45SEL_V(HSE));
@ -170,7 +170,7 @@ static void init_periph_clock(void)
reg_writef(RCC_AHB3LPENR, AXISRAMEN(1));
}
void stm_target_clock_init(void)
void echoplayer_clock_init(void)
{
init_hse();
init_pll();
@ -180,43 +180,26 @@ void stm_target_clock_init(void)
init_periph_clock();
}
void stm_target_clock_enable(enum stm_clock clock, bool enable)
{
switch (clock)
{
case STM_CLOCK_SPI5_KER:
reg_writef(RCC_APB2ENR, SPI5EN(enable));
reg_writef(RCC_APB2LPENR, SPI5EN(enable));
break;
const struct stm32_clock sdmmc1_ker_clock = {
.frequency = PLL1Q_FREQ,
.en_reg = ITA_RCC_AHB3ENR,
.en_bit = BM_RCC_AHB3ENR_SDMMC1EN,
.lpen_reg = ITA_RCC_AHB3LPENR,
.lpen_bit = BM_RCC_AHB3LPENR_SDMMC1EN,
};
case STM_CLOCK_LTDC_KER:
reg_writef(RCC_APB3ENR, LTDCEN(enable));
reg_writef(RCC_APB3LPENR, LTDCEN(enable));
break;
const struct stm32_clock ltdc_ker_clock = {
.frequency = LCD_DOTCLOCK_FREQ,
.en_reg = ITA_RCC_APB3ENR,
.en_bit = BM_RCC_APB3ENR_LTDCEN,
.lpen_reg = ITA_RCC_APB3LPENR,
.lpen_bit = BM_RCC_APB3ENR_LTDCEN,
};
case STM_CLOCK_SDMMC1_KER:
reg_writef(RCC_AHB3ENR, SDMMC1EN(enable));
reg_writef(RCC_AHB3LPENR, SDMMC1EN(enable));
break;
default:
panicf("%s: unsupported clock %d", __func__, (int)clock);
break;
}
}
size_t stm_target_clock_get_frequency(enum stm_clock clock)
{
switch (clock)
{
case STM_CLOCK_SPI5_KER:
return STM32_HSE_FREQ;
case STM_CLOCK_SDMMC1_KER:
return PLL1Q_FREQ;
default:
panicf("%s: unsupported clock %d", __func__, (int)clock);
return 0;
}
}
const struct stm32_clock spi5_ker_clock = {
.frequency = STM32_HSE_FREQ,
.en_reg = ITA_RCC_APB2ENR,
.en_bit = BM_RCC_APB2ENR_SPI5EN,
.lpen_reg = ITA_RCC_APB2LPENR,
.lpen_bit = BM_RCC_APB2ENR_SPI5EN,
};

View file

@ -0,0 +1,32 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __CLOCK_ECHOPLAYER_H__
#define __CLOCK_ECHOPLAYER_H__
#include "clock-stm32h7.h"
void echoplayer_clock_init(void) INIT_ATTR;
extern struct stm32_clock sdmmc1_ker_clock;
extern struct stm32_clock ltdc_ker_clock;
extern struct stm32_clock spi5_ker_clock;
#endif /* __CLOCK_ECHOPLAYER_H__ */

View file

@ -22,6 +22,7 @@
#include "kernel.h"
#include "lcd.h"
#include "lcd-echoplayer.h"
#include "clock-echoplayer.h"
#include "nvic-arm.h"
#include "spi-stm32h7.h"
#include "gpio-stm32h7.h"
@ -30,6 +31,9 @@
#include "regs/stm32h743/spi.h"
#include "regs/stm32h743/ltdc.h"
#define MS_TO_TICKS(x) \
(((x) + (1000 / HZ - 1)) / (1000 / HZ))
/*
* ILI9342C specifies 10 MHz max
*
@ -38,9 +42,17 @@
*/
#define LCD_SPI_FREQ 12000000
#define ili_cmd(cmd, ...) \
do { \
uint16_t arr[] = {cmd, __VA_ARGS__}; \
for (size_t i = 1; i < ARRAYLEN(arr); ++i) \
arr[i] |= 0x100; \
stm_spi_transmit(&spi, arr, sizeof(arr)); \
} while (0)
struct stm_spi_config spi_cfg = {
.instance = ITA_SPI5,
.clock = STM_CLOCK_SPI5_KER,
.clock = &spi5_ker_clock,
.freq = LCD_SPI_FREQ,
.mode = STM_SPIMODE_HALF_DUPLEX,
.proto = STM_SPIPROTO_MOTOROLA,
@ -50,18 +62,19 @@ struct stm_spi_config spi_cfg = {
struct stm_spi spi;
#define ili_cmd(cmd, ...) \
do { \
uint16_t arr[] = {cmd, __VA_ARGS__}; \
for (size_t i = 1; i < ARRAYLEN(arr); ++i) \
arr[i] |= 0x100; \
stm_spi_transmit(&spi, arr, sizeof(arr)); \
} while (0)
enum lcd_controller_state
{
RESET, /* Controller in hardware reset, LTDC disabled */
SLEEP, /* Controller in sleep-in mode, LTDC disabled */
AWAKE, /* Controller in sleep-out mode, LTDC enabled */
};
static void init_ltdc(void)
static enum lcd_controller_state lcd_controller_state = RESET;
static void enable_ltdc(void)
{
/* Enable LTDC clock */
stm_clock_enable(STM_CLOCK_LTDC_KER);
stm32_clock_enable(&ltdc_ker_clock);
/* Set timing parameters */
const uint32_t hsw = LCD_HSW - 1;
@ -100,63 +113,145 @@ static void init_ltdc(void)
reg_writef(LTDC_GCR, LTDCEN(1));
}
static void disable_ltdc(void)
{
reg_writef(LTDC_GCR, LTDCEN(0));
stm32_clock_disable(&ltdc_ker_clock);
}
/* TODO: thread safety, is a mutex needed here? */
static void reset_lcd(void)
{
if (lcd_controller_state != RESET)
{
if (lcd_controller_state == AWAKE)
disable_ltdc();
/* Must be >= 10 us to take effect */
gpio_set_level(GPIO_LCD_RESET, 0);
udelay(10);
lcd_controller_state = RESET;
}
}
static void sleep_lcd(void)
{
if (lcd_controller_state == AWAKE)
{
/*
* Send sleep in command -- empirically this seems to
* require a delay of 10ms before disabling the LTDC.
* The clock output appears to be necessary to fully
* blank the screen before entering sleep mode.
*
* Sometimes there is an effect where the screen is
* only partly blanked and only later fully blanked,
* which goes away with a 20ms delay.
*/
ili_cmd(0x10);
sleep(MS_TO_TICKS(20));
/* Disable LTDC */
disable_ltdc();
lcd_controller_state = SLEEP;
}
}
static void wake_lcd(void)
{
if (lcd_controller_state == RESET)
{
/* Release reset line */
gpio_set_level(GPIO_LCD_RESET, 1);
sleep(MS_TO_TICKS(5));
/* Memory access control (X/Y invert, BGR panel) */
ili_cmd(0x36, 0xc8);
/* Pixel format set (18bpp for RGB bus, 16bpp for SPI bus) */
ili_cmd(0x3a, 0x65);
/* Send set EXTC command to allow configuring RGB interface */
ili_cmd(0xc8, 0xff, 0x93, 0x42);
/*
* Enable RGB interface transferring to internal GRAM.
*
* Direct to shift register mode doesn't work; for one, the
* framebuffer doesn't get transferred properly which might
* just be timing issues. Two, the display is horizontally
* flipped and there doesn't seem to be a way to change it
* in the shift register mode.
*/
ili_cmd(0xb0, 0xc0);
ili_cmd(0xf6, 0x01, 0x00, 0x06);
/* Display ON */
ili_cmd(0x29);
}
if (lcd_controller_state != AWAKE)
{
/* Sync framebuffer & enable LTDC output */
commit_dcache();
enable_ltdc();
/* Sleep out command */
ili_cmd(0x11);
sleep(MS_TO_TICKS(5));
lcd_controller_state = AWAKE;
send_event(LCD_EVENT_ACTIVATION, NULL);
}
}
void lcd_init_device(void)
{
/* Configure SPI bus */
stm_spi_init(&spi, &spi_cfg);
nvic_enable_irq(NVIC_IRQN_SPI5);
/* Enable LCD controller */
init_ltdc();
/* Ensure controller is reset */
gpio_set_level(GPIO_LCD_RESET, 0);
sleep(12);
gpio_set_level(GPIO_LCD_RESET, 1);
sleep(12);
/* Sleep out */
ili_cmd(0x11);
sleep(12);
/* memory access control (X/Y invert, BGR panel) */
ili_cmd(0x36, 0xc8);
/* pixel format set (18bpp for RGB bus, 16bpp for SPI bus) */
ili_cmd(0x3a, 0x65);
/* send set EXTC command to allow configuring RGB interface */
ili_cmd(0xc8, 0xff, 0x93, 0x42);
/*
* Enable RGB interface transferring to internal GRAM.
*
* Direct to shift register mode doesn't work; for one, the
* framebuffer doesn't get transferred properly which might
* just be timing issues. Two, the display is horizontally
* flipped and there doesn't seem to be a way to change it
* in the shift register mode.
*/
ili_cmd(0xb0, 0xc0);
ili_cmd(0xf6, 0x01, 0x00, 0x06);
/* display ON */
ili_cmd(0x29);
#ifndef BOOTLOADER
/* Enable LTDC and LCD controller */
wake_lcd();
#endif
}
bool lcd_active(void)
{
return true;
return lcd_controller_state == AWAKE;
}
void lcd_enable(bool enable)
{
if (enable)
wake_lcd();
else
sleep_lcd();
}
void lcd_shutdown(void)
{
reset_lcd();
}
void lcd_update(void)
{
if (!lcd_active())
return;
commit_dcache();
}
void lcd_update_rect(int x, int y, int width, int height)
{
if (!lcd_active())
return;
if (x < 0)
x = 0;
else if (x >= LCD_WIDTH)

View file

@ -21,6 +21,8 @@
#include "power.h"
#include "mutex.h"
#include "gpio-stm32h7.h"
#include "system-echoplayer.h"
#include "regs/cortex-m/cm_scb.h"
static struct mutex power_1v8_lock;
static int power_1v8_refcount;
@ -68,23 +70,34 @@ void power_init(void)
void power_off(void)
{
/*
* Disable power and reset to the bootloader immediately.
* The system can't really be powered off as long as USB
* is plugged or the power button is pressed -- it's the
* bootloader's job to monitor those inputs and decide
* when the system needs to "really" power on.
*/
gpio_set_level(GPIO_CPU_POWER_ON, 0);
/* TODO: reset to bootloader if USB is plugged in */
while (1)
core_idle();
reg_writef(CM_SCB_AIRCR, VECTKEY_V(KEY), SYSRESETREQ(1));
while (1);
}
void system_reboot(void)
{
/*
* TODO: support reboot
*
* For R1-Rev1 PCBs doing a CPU reset will cut power when
* running on battery (because cpu_power_on is no longer
* being driven high). The RTC alarm could be used to wake
* the system instead.
* Disable IRQs to ensure an errant panic can't disturb
* the RTC reconfig.
*/
disable_irq();
/*
* Configure RTC_OUT pin to keep power enabled during
* reset and then do a normal power off. If this fails
* to reset back to the bootloader then the RTC_OUT
* pin will go low automatically after some timeout.
*/
echoplayer_set_rtcout_mode(ECHOPLAYER_RTCOUT_REBOOT);
power_off();
}

View file

@ -19,6 +19,7 @@
*
****************************************************************************/
#include "sdmmc_host.h"
#include "clock-echoplayer.h"
#include "sdmmc-stm32h7.h"
#include "gpio-stm32h7.h"
#include "nvic-arm.h"
@ -95,7 +96,7 @@ static struct sdmmc_poll sdcard_poll;
void sdmmc_host_target_init(void)
{
/* Initialize controller */
stm32h7_sdmmc_init(&sdmmc1_ctl, ITA_SDMMC1, STM_CLOCK_SDMMC1_KER,
stm32h7_sdmmc_init(&sdmmc1_ctl, ITA_SDMMC1, &sdmmc1_ker_clock,
stm32h7_reset_sdmmc1, NULL);
nvic_enable_irq(NVIC_IRQN_SDMMC1);

View file

@ -19,9 +19,21 @@
*
****************************************************************************/
#include "system.h"
#include "button.h"
#include "gpio-stm32h7.h"
#include "regs/stm32h743/rcc.h"
#include "clock-echoplayer.h"
#include "system-echoplayer.h"
#include "regs/stm32h743/fmc.h"
#include "regs/stm32h743/pwr.h"
#include "regs/stm32h743/rcc.h"
#include "regs/stm32h743/rtc.h"
#include "regs/cortex-m/cm_scb.h"
#ifdef BOOTLOADER
# define BOOTLOADER_INIT 1
#else
# define BOOTLOADER_INIT 0
#endif
#define F_INPUT GPIOF_INPUT(GPIO_PULL_DISABLED)
#define F_INPUT_PU GPIOF_INPUT(GPIO_PULL_UP)
@ -48,6 +60,8 @@
# define F_MCO1 GPIOF_ANALOG()
#endif
enum echoplayer_boot_reason echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_NORMAL;
static const struct gpio_setting gpios[] = {
STM_DEFGPIO(GPIO_BUTTON_A, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_B, F_INPUT_PU),
@ -63,7 +77,6 @@ static const struct gpio_setting gpios[] = {
STM_DEFGPIO(GPIO_BUTTON_VOL_DOWN, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_POWER, F_INPUT_PD),
STM_DEFGPIO(GPIO_BUTTON_HOLD, F_INPUT_PU),
STM_DEFGPIO(GPIO_CPU_POWER_ON, F_OUT_LS(1)), /* active high */
STM_DEFGPIO(GPIO_POWER_1V8, F_OUT_LS(0)), /* active high */
STM_DEFGPIO(GPIO_CODEC_AVDD_EN, F_OUT_LS(1)), /* active low */
STM_DEFGPIO(GPIO_CODEC_DVDD_EN, F_OUT_LS(1)), /* active low */
@ -117,25 +130,7 @@ static const struct pingroup_setting pingroups[] = {
STM_DEFPINS(GPIO_I, 0x06e7, F_LCD_AF14),
};
void gpio_init(void)
{
/* Enable clocks for all used GPIO banks */
reg_writef(RCC_AHB4ENR,
GPIOAEN(1), GPIOBEN(1), GPIOCEN(1), GPIODEN(1),
GPIOEEN(1), GPIOFEN(1), GPIOGEN(1), GPIOHEN(1), GPIOIEN(1));
/*
* NOTE: I think it's possible to disable clocks for the banks which
* we don't need to access at runtime because these are only clocking
* register access. Probably a micro-optimization but it supposedly
* does save a few uA/MHz.
*/
gpio_configure_all(gpios, ARRAYLEN(gpios),
pingroups, ARRAYLEN(pingroups));
}
void fmc_init(void)
INIT_ATTR static void fmc_init(void)
{
/* configure clock */
reg_writef(RCC_D1CCIPR, FMCSEL_V(AHB));
@ -181,3 +176,117 @@ void fmc_init(void)
*/
reg_writef(FMC_SDRTR, REIE(0), COUNT(917), CRE(0));
}
void system_init(void)
{
if (BOOTLOADER_INIT)
{
/* Enable clocks for all used GPIO banks */
reg_writef(RCC_AHB4ENR,
GPIOAEN(1), GPIOBEN(1), GPIOCEN(1), GPIODEN(1),
GPIOEEN(1), GPIOFEN(1), GPIOGEN(1), GPIOHEN(1), GPIOIEN(1));
/*
* NOTE: I think it's possible to disable clocks for the banks which
* we don't need to access at runtime because these are only clocking
* register access. Probably a micro-optimization but it supposedly
* does save a few uA/MHz.
*/
}
/*
* Set cpu_power_on high as early as possible to
* ensure we won't brown out if the power button
* isn't pressed.
*/
gpio_configure_single(GPIO_CPU_POWER_ON, F_OUT_LS(1));
/* Set vector table address */
extern char __vectors_arm[];
reg_var(CM_SCB_VTOR) = (uint32_t)__vectors_arm;
#if defined(DEBUG)
system_debug_enable(true);
#endif
if (BOOTLOADER_INIT)
{
/* Enable CPU cache */
stm32_enable_caches();
/* Initialize system clocks */
echoplayer_clock_init();
}
/* Enable systick early due to udelay() needed for FMC init */
stm32_systick_enable();
if (BOOTLOADER_INIT)
{
/* Configure GPIOs and start FMC */
gpio_configure_all(gpios, ARRAYLEN(gpios),
pingroups, ARRAYLEN(pingroups));
fmc_init();
/* Read & clear reset source */
uint32_t rsr = reg_var(RCC_RSR);
reg_assignf(RCC_RSR, RMVF(1));
/*
* Determine boot reason -- SFTRST means a software reset
* occurred, which may be a reboot or a power off
*/
if (reg_vreadf(rsr, RCC_RSR, SFTRSTF))
{
reg_writef(RCC_APB4ENR, RTCAPBEN(1));
if (reg_readf(RTC_CR, WUTE))
echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_SW_REBOOT;
else
echoplayer_boot_reason = ECHOPLAYER_BOOT_REASON_SW_POWEROFF;
}
}
/* Disable RTC_OUT pin */
echoplayer_set_rtcout_mode(ECHOPLAYER_RTCOUT_DISABLED);
}
void system_exception_wait(void)
{
while (button_read_device() != (BUTTON_POWER | BUTTON_START));
}
void echoplayer_set_rtcout_mode(enum echoplayer_rtcout_mode mode)
{
reg_writef(RCC_APB4ENR, RTCAPBEN(1));
reg_writef(PWR_CR1, DBP(1));
reg_writef(RTC_WPR, KEY_V(KEY1));
reg_writef(RTC_WPR, KEY_V(KEY2));
reg_writef(RTC_OR, OUT_RMP(0), ALARM_TYPE_V(PUSH_PULL));
switch (mode)
{
case ECHOPLAYER_RTCOUT_REBOOT:
/*
* Use the inverted wakeup timer output to keep power
* enabled during reset. If, somehow, the system does
* not reset properly then the wakeup timer will drive
* the RTC_OUT pin low after 1 second and cut power.
*/
while (!reg_readf(RTC_ISR, WUTWF));
reg_writef(RTC_ISR, WUTF(0));
reg_writef(RTC_WUTR, VALUE(STM32_LSE_FREQ / 8));
reg_writef(RTC_CR, OSEL_V(WAKEUP), POL(1), WUCKSEL(0), WUTE(1));
break;
case ECHOPLAYER_RTCOUT_DISABLED:
default:
reg_writef(RTC_CR, OSEL_V(DISABLED), POL(0), WUTE(0));
break;
}
reg_writef(RTC_WPR, KEY(0));
reg_writef(PWR_CR1, DBP(0));
}

View file

@ -0,0 +1,41 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __SYSTEM_ECHOPLAYER_H__
#define __SYSTEM_ECHOPLAYER_H__
enum echoplayer_rtcout_mode
{
ECHOPLAYER_RTCOUT_DISABLED,
ECHOPLAYER_RTCOUT_REBOOT,
};
enum echoplayer_boot_reason
{
ECHOPLAYER_BOOT_REASON_NORMAL,
ECHOPLAYER_BOOT_REASON_SW_POWEROFF,
ECHOPLAYER_BOOT_REASON_SW_REBOOT,
};
void echoplayer_set_rtcout_mode(enum echoplayer_rtcout_mode mode);
extern enum echoplayer_boot_reason echoplayer_boot_reason;
#endif /* __SYSTEM_ECHOPLAYER_H__ */

View file

@ -80,7 +80,7 @@ void stm32h7_reset_sdmmc1(void)
void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *ctl,
uint32_t instance,
enum stm_clock clock,
const struct stm32_clock *clock,
void (*reset_sdmmc)(void),
void (*vcc_enable)(bool))
{
@ -109,7 +109,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled)
sleep(1);
/* Bus clock is now needed, so enable kernel clock */
stm_clock_enable(ctl->clock);
stm32_clock_enable(ctl->clock);
/* Configure bus parameters */
stm32h7_sdmmc_set_bus_width(ctl, SDMMC_BUS_WIDTH_1BIT);
@ -121,7 +121,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled)
udelay(200);
/* Automatically stop clock when bus is not in use */
reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1));
reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1), HWFC_EN(1));
}
else
{
@ -136,7 +136,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled)
* and the bus is powered down; some quick testing shows this
* seems to be true.
*/
stm_clock_disable(ctl->clock);
stm32_clock_disable(ctl->clock);
/* Disable VCC */
if (ctl->vcc_enable)
@ -174,7 +174,7 @@ void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock)
if (stm32h7_sdmmc_is_powered_off(ctl))
return;
size_t ker_freq = stm_clock_get_frequency(ctl->clock);
size_t ker_freq = stm32_clock_get_frequency(ctl->clock);
size_t bus_freq = get_sdmmc_bus_freq(clock);
if (!bus_freq)
panicf("%s", __func__);
@ -273,27 +273,6 @@ int stm32h7_sdmmc_submit_command(void *controller,
if (buff_size > MAX_DATA_LEN)
panicf("%s: buffer too big", __func__);
/*
* IDMA on the SDMMC controller can't access the DTCM.
* This is only possible by bounce-buffering in one of
* the other memories accessible to IDMA, then using
* another DMA process to copy the resulting buffer to
* DTCM, which seems unnecessarily convoluted.
*/
if ((uintptr_t)buff_addr >= STM32_DTCM_BASE &&
(uintptr_t)buff_addr < STM32_DTCM_BASE + STM32_DTCM_SIZE)
panicf("%s: buffer in DTCM not supported", __func__);
/*
* Must assign to a variable to prevent GCC from whining
* about 'limited range of data type', because the ITCM
* is mapped at address 0.
*/
static const uintptr_t itcm_base = STM32_ITCM_BASE;
if ((uintptr_t)buff_addr >= itcm_base &&
(uintptr_t)buff_addr < itcm_base + STM32_ITCM_SIZE)
panicf("%s: buffer in ITCM not supported", __func__);
/* Set block size */
uint32_t dctrl = 0;
uint32_t dblocksize = find_first_set_bit(cmd->block_len);
@ -481,8 +460,12 @@ void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *ctl)
ctl->cmd_error = SDMMC_STATUS_TIMEOUT;
else if (reg_vreadf(star, SDMMC_STAR, DCRCFAIL))
ctl->cmd_error = SDMMC_STATUS_INVALID_CRC;
else if (star & DATA_ERROR_BITS)
else if (reg_vreadf(star, SDMMC_STAR, DABORT))
ctl->cmd_error = SDMMC_STATUS_ERROR;
else if (reg_vreadf(star, SDMMC_STAR, IDMATE))
panicf("sdmmc dma err: %08lx", reg_readl(ctl->regs, SDMMC_IDMABASE0R));
else if (star & DATA_ERROR_BITS)
panicf("sdmmc data error: %08lx", star);
}
ctl->cmd_wait &= ~WAIT_DATA;

View file

@ -31,7 +31,7 @@ struct stm32h7_sdmmc_controller
uint32_t regs;
/* SDMMC kernel clock */
enum stm_clock clock;
const struct stm32_clock *clock;
/* Callback to reset SDMMC instance in RCC */
void (*reset_sdmmc)(void);
@ -59,7 +59,7 @@ void stm32h7_reset_sdmmc1(void);
void stm32h7_sdmmc_init(struct stm32h7_sdmmc_controller *controller,
uint32_t instance,
enum stm_clock clock,
const struct stm32_clock *clock,
void (*reset_sdmmc)(void),
void (*vcc_enable)(bool));

View file

@ -37,7 +37,7 @@ static void stm_spi_enable(struct stm_spi *spi, bool hd_tx, size_t size)
if (tsize > TSIZE_MAX)
panicf("%s: tsize > TSIZE_MAX", __func__);
stm_clock_enable(spi->clock);
stm32_clock_enable(spi->clock);
if (spi->set_cs)
spi->set_cs(spi, true);
@ -59,7 +59,7 @@ static void stm_spi_disable(struct stm_spi *spi)
if (spi->set_cs)
spi->set_cs(spi, false);
stm_clock_disable(spi->clock);
stm32_clock_disable(spi->clock);
}
static uint32_t stm_spi_pack(const void **bufp, size_t *sizep)
@ -110,7 +110,7 @@ static void stm_spi_unpack(void **bufp, size_t *sizep, uint32_t data)
static uint32_t stm_spi_calc_mbr(const struct stm_spi_config *config)
{
size_t ker_freq = stm_clock_get_frequency(config->clock);
size_t ker_freq = stm32_clock_get_frequency(config->clock);
for (uint32_t mbr = 0; mbr <= 7; mbr++)
{
if (ker_freq / (2 << mbr) <= config->freq)
@ -162,7 +162,7 @@ void stm_spi_init(struct stm_spi *spi,
ftlevel *= 2;
}
stm_clock_enable(spi->clock);
stm32_clock_enable(spi->clock);
/* TODO: allow setting MBR here */
reg_writelf(spi->regs, SPI_CFG1,
@ -191,7 +191,7 @@ void stm_spi_init(struct stm_spi *spi,
MIDI(0),
MSSI(0));
stm_clock_disable(spi->clock);
stm32_clock_disable(spi->clock);
}
int stm_spi_xfer(struct stm_spi *spi, size_t size,

View file

@ -58,7 +58,7 @@ struct stm_spi_config
* such the kernel clock should not be changed after
* the SPI peripheral is initialized.
*/
enum stm_clock clock;
const struct stm32_clock *clock;
size_t freq;
enum stm_spi_mode mode;
@ -77,7 +77,7 @@ struct stm_spi_config
struct stm_spi
{
uint32_t regs;
enum stm_clock clock;
const struct stm32_clock *clock;
enum stm_spi_mode mode;
stm_spi_set_cs_t set_cs;
uint32_t frame_size;

View file

@ -27,26 +27,29 @@
#include "regs/cortex-m/cm_systick.h"
#include "regs/stm32h743/dbgmcu.h"
/* EXT timer is 1/8th of CPU clock */
#define SYSTICK_FREQ (CPU_FREQ / 8)
#define SYSTICK_PER_MS (SYSTICK_FREQ / 1000)
#define SYSTICK_PER_US (SYSTICK_FREQ / 1000000)
/* Assumed initial CPU frequency for calculating systick */
#ifndef CPUFREQ_INITIAL
# define CPUFREQ_INITIAL CPU_FREQ
#endif
/* Max delay is limited by kernel tick interval + safety margin */
#define SYSTICK_DELAY_MAX_US (1000000 / HZ / 2)
#define SYSTICK_DELAY_MAX_MS (SYSTICK_DELAY_MAX_US / 1000)
/* Tick interval in milliseconds */
#ifndef SYSTICK_INTERVAL_INITIAL
# define SYSTICK_INTERVAL_INITIAL (1000 / HZ)
#endif
/* Base address of vector table */
extern char __vectors_arm[];
/* Use EXT source which is equal to CPU frequency divided by 8 */
#define SYSTICK_SOURCE BV_CM_SYSTICK_CSR_CLKSOURCE_EXT
#define SYSTICK_PRESCALER 8
static void systick_init(unsigned int interval_in_ms)
{
reg_writef(CM_SYSTICK_RVR, VALUE(SYSTICK_PER_MS * interval_in_ms - 1));
reg_writef(CM_SYSTICK_CVR, VALUE(0));
reg_writef(CM_SYSTICK_CSR, CLKSOURCE_V(EXT), ENABLE(1));
}
/* Convert CPU frequency to number of systick ticks in 1 ms */
#define CPUFREQ_TO_SYSTICK_PER_MS(f) \
((f) / (SYSTICK_PRESCALER * 1000))
static void stm_enable_caches(void)
/* SysTick related state */
static uint32_t systick_per_ms = CPUFREQ_TO_SYSTICK_PER_MS(CPUFREQ_INITIAL);
static uint32_t systick_interval_in_ms = SYSTICK_INTERVAL_INITIAL;
void stm32_enable_caches(void)
{
__discard_idcache();
@ -56,28 +59,43 @@ static void stm_enable_caches(void)
arm_isb();
}
void system_init(void)
static void stm32_recalc_systick_rvr(void)
{
#if defined(DEBUG)
system_debug_enable(true);
#endif
uint32_t ticks = systick_per_ms * systick_interval_in_ms;
/* Ensure IRQs are disabled and set vector table address */
disable_irq();
reg_var(CM_SCB_VTOR) = (uint32_t)__vectors_arm;
reg_writef(CM_SYSTICK_RVR, VALUE(ticks - 1));
}
/* Enable CPU caches */
stm_enable_caches();
static void stm32_set_systick_interval(uint32_t interval_in_ms)
{
if (interval_in_ms != systick_interval_in_ms)
{
systick_interval_in_ms = interval_in_ms;
stm32_recalc_systick_rvr();
}
}
/* Initialize system clocks */
stm_clock_init();
void stm32_systick_set_cpu_freq(uint32_t freq)
{
uint32_t ticks_per_ms = CPUFREQ_TO_SYSTICK_PER_MS(freq);
/* TODO: move this */
systick_init(1000/HZ);
if (ticks_per_ms != systick_per_ms)
{
systick_per_ms = ticks_per_ms;
stm32_recalc_systick_rvr();
}
}
/* Call target-specific initialization */
gpio_init();
fmc_init();
void stm32_systick_enable(void)
{
stm32_recalc_systick_rvr();
reg_writef(CM_SYSTICK_CVR, VALUE(0));
reg_writef(CM_SYSTICK_CSR, CLKSOURCE(SYSTICK_SOURCE), ENABLE(1));
}
void stm32_systick_disable(void)
{
reg_writef(CM_SYSTICK_CSR, ENABLE(0), TICKINT(0));
}
void system_debug_enable(bool enable)
@ -99,7 +117,8 @@ void system_debug_enable(bool enable)
void tick_start(unsigned int interval_in_ms)
{
(void)interval_in_ms;
stm32_set_systick_interval(interval_in_ms);
stm32_systick_enable();
reg_writef(CM_SYSTICK_CSR, TICKINT(1));
}
@ -110,58 +129,35 @@ void systick_handler(void)
}
/*
* NOTE: This assumes that the CPU cannot be reclocked during an interrupt.
* If that happens, the systick interval and reload value would be modified
* to maintain the kernel tick interval and the code here will break.
* This makes two assumptions:
*
* 1. the CPU frequency must not change while udelay() is running;
* otherwise the delay time will be wrong.
* 2. interrupt handlers should not block execution for more than
* one systick interval; if this happens the delay may be much
* longer than necessary.
*/
static void __udelay(uint32_t us)
void udelay(uint32_t us)
{
uint32_t delay_ticks = (us * systick_per_ms / 1000);
uint32_t start = reg_readf(CM_SYSTICK_CVR, VALUE);
uint32_t max = reg_readf(CM_SYSTICK_RVR, VALUE);
uint32_t delay = us * SYSTICK_PER_US;
for (;;)
while (delay_ticks > 0)
{
uint32_t value = reg_readf(CM_SYSTICK_CVR, VALUE);
uint32_t diff = start - value;
if (value > start)
diff += max;
if (diff >= delay)
if (diff >= delay_ticks)
break;
delay_ticks -= diff;
start = value;
}
}
void udelay(uint32_t us)
{
while (us > SYSTICK_DELAY_MAX_US)
{
__udelay(SYSTICK_DELAY_MAX_US);
us -= SYSTICK_DELAY_MAX_US;
}
__udelay(us);
}
void mdelay(uint32_t ms)
{
while (ms > SYSTICK_DELAY_MAX_MS)
{
__udelay(SYSTICK_DELAY_MAX_MS * 1000);
ms -= SYSTICK_DELAY_MAX_MS;
}
__udelay(ms * 1000);
}
void system_exception_wait(void)
{
#if defined(ECHO_R1)
while (button_read_device() != (BUTTON_POWER | BUTTON_START));
#else
while (1);
#endif
}
int system_memory_guard(int newmode)
{
/* TODO -- maybe use MPU here to give some basic protection */

View file

@ -25,14 +25,22 @@
#include "cpucache-armv7m.h"
#include <stdbool.h>
/* Enable CPU caches. Must be called after a reset. */
void stm32_enable_caches(void) INIT_ATTR;
/* Enables the SysTick timer -- SysTick interrupt won't be enabled */
void stm32_systick_enable(void);
/* Disables the SysTick timer -- also disables SysTick interrupt */
void stm32_systick_disable(void);
/* Update the CPU frequency to recalibrate SysTick timer */
void stm32_systick_set_cpu_freq(uint32_t freq);
/* Enable/disable debug clock domain during sleep mode. */
void system_debug_enable(bool enable);
/* Implemented by the target -- can be a no-op if not needed */
void gpio_init(void) INIT_ATTR;
void fmc_init(void) INIT_ATTR;
/* Busy loop delay based on systick */
void udelay(uint32_t us);
void mdelay(uint32_t ms);
#endif /* __STM32_SYSTEM_TARGET_H__ */

View file

@ -104,7 +104,7 @@ static inline int set_interrupt_status(int status, int mask)
unsigned long cpsr;
int oldstatus;
/* Read the old levels and set the new ones */
#if (defined(CREATIVE_ZVM) || defined(CREATIVE_ZV)) && defined(BOOTLOADER)
#if defined(CREATIVE_ZVx) && defined(BOOTLOADER)
// FIXME: This workaround is for a problem with inlining;
// for some reason 'mask' gets treated as a variable/non-immediate constant
// but only on this build. All others (including the nearly-identical mrobe500boot) are fine

View file

@ -26,9 +26,6 @@
#define IRQ_STATUS 0x01
#define HIGHEST_IRQ_LEVEL IRQ_DISABLED
#define disable_irq_save() \
set_irq_level(IRQ_DISABLED)
/* For compatibility with ARM classic */
#define CPU_MODE_THREAD_CONTEXT 0
@ -47,39 +44,51 @@
__func__, __mproc, __massert); })
/* Core-level interrupt masking */
static inline int set_irq_level(int primask)
{
int oldvalue;
asm volatile ("mrs %0, primask\n"
"msr primask, %1\n"
: "=r"(oldvalue) : "r"(primask));
return oldvalue;
}
static inline void restore_irq(int primask)
{
asm volatile ("msr primask, %0" :: "r"(primask));
}
static inline void enable_irq(void)
{
asm volatile ("cpsie i");
asm volatile ("cpsie i" ::: "memory");
}
static inline void disable_irq(void)
{
asm volatile ("cpsid i");
asm volatile ("cpsid i" ::: "memory");
}
static inline void restore_irq(int primask)
{
asm volatile ("msr primask, %0" :: "r"(primask) : "memory");
}
static inline int get_irq_level(void)
{
int primask;
asm volatile("mrs %0, primask" : "=r"(primask));
return primask;
}
static inline int disable_irq_save(void)
{
int oldlevel = get_irq_level();
disable_irq();
return oldlevel;
}
static inline int set_irq_level(int primask)
{
int oldvalue = get_irq_level();
restore_irq(primask);
return oldvalue;
}
static inline bool irq_enabled(void)
{
int primask;
asm volatile ("mrs %0, primask" : "=r"(primask));
return !(primask & 1);
return get_irq_level() == IRQ_ENABLED;
}
static inline unsigned long get_interrupt_number(void)

View file

@ -838,7 +838,7 @@ void avr_thread(void)
if (ev.id == SYS_USB_CONNECTED)
{
/* Allow USB to gain exclusive storage access */
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
disk_access_available = false;
}
else if (ev.id == SYS_USB_DISCONNECTED)

View file

@ -747,7 +747,7 @@ void usb_test(void)
usb_init();
usb_start_monitoring();
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_acknowledge(SYS_USB_CONNECTED_ACK, 0);
while (1) {
sleep(HZ);

View file

@ -93,6 +93,28 @@ static const struct nand_chip chip_gd5f1gq4xexx = {
.cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
};
static const struct nand_chip chip_xt26g01cwsiga = {
.log2_ppb = 6, /* 64 pages */
.page_size = 2048,
.oob_size = 128,
.nr_blocks = 1024,
.bbm_pos = 2048,
.clock_freq = 150000000,
.dev_conf = jz_orf(SFC_DEV_CONF,
CE_DL(1), HOLD_DL(1), WP_DL(1),
CPHA(0), CPOL(0),
TSH(7), TSETUP(0), THOLD(0),
STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
SMP_DELAY(1)),
.flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT |
NAND_CHIPFLAG_ON_DIE_ECC,
.cmd_page_read = NANDCMD_PAGE_READ,
.cmd_program_execute = NANDCMD_PROGRAM_EXECUTE,
.cmd_block_erase = NANDCMD_BLOCK_ERASE,
.cmd_read_cache = NANDCMD_READ_CACHE_x4,
.cmd_program_load = NANDCMD_PROGRAM_LOAD_x4,
};
#define chip_ds35x1gaxxx chip_gd5f1gq4xexx
#define chip_gd5f1gq5xexxg chip_gd5f1gq4xexx
@ -105,6 +127,7 @@ const struct nand_chip_id supported_nand_chips[] = {
NAND_CHIP_ID(&chip_ds35x1gaxxx, NAND_READID_ADDR, 0xe5, 0x21), /* 1.8 V */
NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x51), /* 3.3 V */
NAND_CHIP_ID(&chip_gd5f1gq5xexxg, NAND_READID_ADDR, 0xc8, 0x41), /* 1.8 V */
NAND_CHIP_ID(&chip_xt26g01cwsiga, NAND_READID_ADDR, 0x0b, 0x11),
};
const size_t nr_supported_nand_chips = ARRAYLEN(supported_nand_chips);

View file

@ -59,17 +59,7 @@
#include "iap.h"
#endif
/* Conditions under which we want the entire driver */
#if !defined(BOOTLOADER) || \
(defined(HAVE_USBSTACK) && defined(HAVE_BOOTLOADER_USB_MODE)) || \
(defined(HAVE_USBSTACK) && defined(IPOD_NANO2G)) || \
(defined(HAVE_USBSTACK) && (defined(CREATIVE_ZVx))) || \
(defined(HAVE_USBSTACK) && (defined(OLYMPUS_MROBE_500))) || \
defined(CPU_TCC780X) || \
(CONFIG_USBOTG == USBOTG_JZ4740) || \
(CONFIG_USBOTG == USBOTG_JZ4760)
/* TODO: condition should be reset to be only the original
(defined(HAVE_USBSTACK) && defined(HAVE_BOOTLOADER_USB_MODE)) */
#if (!defined(BOOTLOADER) || defined(HAVE_BOOTLOADER_USB_MODE))
#define USB_FULL_INIT
#endif
@ -91,8 +81,8 @@ static int usb_state = USB_EXTRACTED;
static int usb_mmc_countdown = 0;
#endif
/* Make sure there's enough stack space for screendump */
#ifdef USB_FULL_INIT
/* Make sure there's enough stack space for screendump */
#ifndef USB_EXTRA_STACK
# define USB_EXTRA_STACK 0x0 /*Define in firmware/export/config/[target].h*/
#endif
@ -100,27 +90,23 @@ static long usb_stack[(DEFAULT_STACK_SIZE*4 + DUMP_BMP_LINESIZE + USB_EXTRA_STAC
static const char usb_thread_name[] = "usb";
static unsigned int usb_thread_entry = 0;
static bool usb_monitor_enabled = false;
#endif /* USB_FULL_INIT */
static bool exclusive_storage_enabled = false;
static bool exclusive_storage_requested = false;
static struct event_queue usb_queue SHAREDBSS_ATTR;
static bool exclusive_storage_access = false;
#ifdef USB_ENABLE_HID
static bool usb_hid = true;
#endif
#ifdef USB_ENABLE_AUDIO
static int usb_audio = 0;
#endif
#ifdef USB_FULL_INIT
static bool usb_host_present = false;
static int usb_num_acks_to_expect = 0;
static long usb_last_broadcast_tick = 0;
static uint32_t usb_broadcast_seqnum = 0x80000000;
#ifdef HAVE_USB_POWER
static int usb_mode = USBMODE_DEFAULT;
static int new_usbmode = USBMODE_DEFAULT;
static bool usb_power_only = false;
#endif
static int usb_release_exclusive_storage(void);
#if defined(USB_FIREWIRE_HANDLING)
static void try_reboot(void)
{
@ -206,7 +192,7 @@ static inline void usb_handle_hotswap(long id)
}
#endif /* HAVE_HOTSWAP */
static inline bool usb_configure_drivers(int for_state)
static inline void usb_configure_drivers(int for_state)
{
#ifdef USB_ENABLE_AUDIO
// FIXME: doesn't seem to get set when loaded at boot...
@ -232,9 +218,8 @@ static inline bool usb_configure_drivers(int for_state)
#ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true);
#endif
exclusive_storage_access = false;
usb_attach(); /* Powered only: attach now. */
usb_attach();
break;
/* USB_POWERED: */
@ -251,25 +236,17 @@ static inline bool usb_configure_drivers(int for_state)
#ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false);
#endif
/* Check any drivers enabled at this point for exclusive storage
* access requirements. */
exclusive_storage_access = usb_core_any_exclusive_storage();
if(exclusive_storage_access)
return true;
usb_attach(); /* Not exclusive: attach now. */
usb_attach();
break;
/* USB_INSERTED: */
case USB_EXTRACTED:
if(exclusive_storage_access)
usb_release_exclusive_storage();
/* do not call usb_release_exclusive_storage.
* usb core handles it */
break;
/* USB_EXTRACTED: */
}
return false;
}
static inline void usb_slave_mode(bool on)
@ -283,9 +260,8 @@ static inline void usb_slave_mode(bool on)
thread_set_priority(thread_self(), PRIORITY_REALTIME);
#endif
disk_unmount_all();
usb_attach();
}
else /* usb_state == USB_INSERTED (only!) */
else
{
#ifdef HAVE_PRIORITY_SCHEDULING
thread_set_priority(thread_self(), PRIORITY_SYSTEM);
@ -350,23 +326,20 @@ static inline void usb_handle_hotswap(long id)
}
#endif /* HAVE_HOTSWAP */
static inline bool usb_configure_drivers(int for_state)
static inline void usb_configure_drivers(int for_state)
{
switch(for_state)
{
case USB_POWERED:
exclusive_storage_access = false;
exclusive_storage_requested = false;
break;
case USB_INSERTED:
exclusive_storage_access = true;
return true;
usb_request_exclusive_storage();
break;
case USB_EXTRACTED:
if(exclusive_storage_access)
usb_release_exclusive_storage();
usb_release_exclusive_storage();
break;
}
return false;
}
static inline void usb_slave_mode(bool on)
@ -420,7 +393,7 @@ static void usb_set_host_present(bool present)
}
#ifdef HAVE_USB_POWER
if (new_usbmode == USB_MODE_CHARGE || new_usbmode == USB_MODE_ADB)
if (usb_power_only)
{
/* Only charging is desired */
usb_configure_drivers(USB_POWERED);
@ -428,44 +401,7 @@ static void usb_set_host_present(bool present)
}
#endif
if(!usb_configure_drivers(USB_INSERTED))
return; /* Exclusive storage access not required */
/* Tell all threads that they have to back off the storage.
We subtract one for our own thread. Expect an ACK for every
listener for each broadcast they received. If it has been too
long, the user might have entered a screen that didn't ACK
when inserting the cable, such as a debugging screen. In that
case, reset the count or else USB would be locked out until
rebooting because it most likely won't ever come. Simply
resetting to the most recent broadcast count is racy. */
if(TIME_AFTER(current_tick, usb_last_broadcast_tick + HZ*5))
{
usb_num_acks_to_expect = 0;
usb_last_broadcast_tick = current_tick;
}
usb_num_acks_to_expect += queue_broadcast(SYS_USB_CONNECTED, 0) - 1;
DEBUGF("usb: waiting for %d acks...\n", usb_num_acks_to_expect);
}
static bool usb_handle_connected_ack(void)
{
if(usb_num_acks_to_expect > 0 && --usb_num_acks_to_expect == 0)
{
DEBUGF("usb: all threads have acknowledged the connect.\n");
if(usb_host_present)
{
usb_slave_mode(true);
return true;
}
}
else
{
DEBUGF("usb: got ack, %d to go...\n", usb_num_acks_to_expect);
}
return false;
usb_configure_drivers(USB_INSERTED);
}
/*--- General driver code ---*/
@ -487,6 +423,7 @@ static void NORETURN_ATTR usb_thread(void)
case USB_NOTIFY_SET_ADDR:
case USB_NOTIFY_SET_CONFIG:
case USB_NOTIFY_BUS_RESET:
case USB_NOTIFY_CLASS_DRIVER:
if(usb_state <= USB_EXTRACTED)
break;
usb_core_handle_notify(ev.id, ev.data);
@ -496,6 +433,7 @@ static void NORETURN_ATTR usb_thread(void)
break;
#ifdef USB_DETECT_BY_REQUEST
usb_state = USB_INSERTED;
usb_set_host_present(true);
#endif
@ -523,32 +461,40 @@ static void NORETURN_ATTR usb_thread(void)
#endif
send_event(SYS_EVENT_USB_INSERTED, &usb_mode);
#endif
/* Power (charging-only) button */
#ifdef HAVE_USB_POWER
new_usbmode = usb_mode;
switch (usb_mode) {
case USB_MODE_CHARGE:
case USB_MODE_ADB:
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_MASS_STORAGE;
break;
default:
case USB_MODE_MASS_STORAGE:
if (button_status() & ~USBPOWER_BTN_IGNORE)
new_usbmode = USB_MODE_CHARGE;
break;
}
/* Power (charging-only) button */
usb_power_only = usb_mode != USB_MODE_MASS_STORAGE;
if(button_status() & ~USBPOWER_BTN_IGNORE) {
usb_power_only = !usb_power_only;
}
#endif
#ifndef USB_DETECT_BY_REQUEST
usb_state = USB_INSERTED;
usb_set_host_present(true);
#endif
break;
/* USB_INSERTED */
case SYS_USB_CONNECTED_ACK:
if(usb_handle_connected_ack())
usb_state = USB_INSERTED;
if((uint32_t)ev.data != usb_broadcast_seqnum) {
DEBUGF("usb: late ack %lX < %lX", ev.data, usb_broadcast_seqnum);
break;
}
if(usb_num_acks_to_expect == 0) {
DEBUGF("usb: unexpected ack");
break;
}
if(--usb_num_acks_to_expect > 0) {
DEBUGF("usb: got ack, %d to go...\n", usb_num_acks_to_expect);
break;
}
DEBUGF("usb: all threads have acknowledged the connect.\n");
if(usb_host_present && exclusive_storage_requested) {
usb_slave_mode(true);
exclusive_storage_enabled = true;
}
break;
/* SYS_USB_CONNECTED_ACK */
@ -563,15 +509,7 @@ static void NORETURN_ATTR usb_thread(void)
iap_reset_state(IF_IAP_MP(0));
#endif
/* Only disable the USB slave mode if we really have enabled
it. Some expected acks may not have been received. */
if(usb_state == USB_INSERTED)
usb_slave_mode(false);
usb_state = USB_EXTRACTED;
#ifdef HAVE_USB_POWER
new_usbmode = usb_mode;
#endif
#ifndef BOOTLOADER
send_event(SYS_EVENT_USB_EXTRACTED, NULL);
#endif
@ -757,18 +695,43 @@ static void usb_tick(void)
}
#endif
}
void usb_start_monitoring(void)
{
usb_monitor_enabled = true;
}
#endif /* USB_STATUS_BY_EVENT */
#endif /* USB_FULL_INIT */
void usb_acknowledge(long id)
void usb_acknowledge(long id, intptr_t seqnum)
{
queue_post(&usb_queue, id, 0);
queue_post(&usb_queue, id, seqnum);
}
#else /* !USB_FULL_INIT */
/* TODO: All of this can go away once usb_core.c is no longer built
with BOOTLOADER && !HAVE_USB_BOOTLOADER_MODE */
#ifdef HAVE_USBSTACK
void usb_signal_transfer_completion(
struct usb_transfer_completion_event_data* event_data)
{
(void)event_data;
}
#endif
void usb_clear_pending_transfer_completion_events(void)
{
}
void usb_release_exclusive_storage(void)
{
}
void usb_signal_notify(long id, intptr_t data)
{
(void)id;
(void)data;
}
void usb_acknowledge(long id, intptr_t seqnum)
{
(void)id;
(void)seqnum;
}
#endif /* !USB_FULL_INIT */
void usb_init(void)
{
@ -853,22 +816,83 @@ bool usb_inserted(void)
return usb_state == USB_INSERTED || usb_state == USB_POWERED;
}
#ifdef HAVE_USBSTACK
#if defined(USB_FULL_INIT)
bool usb_exclusive_storage(void)
{
/* Storage isn't actually exclusive until slave mode has been entered */
return exclusive_storage_access && usb_state == USB_INSERTED;
return exclusive_storage_enabled;
}
#endif /* HAVE_USBSTACK */
int usb_release_exclusive_storage(void)
/* exclusive storage mode transision
* HAVE_USBSTACK:
* (inserted)
* usb_set_host_present(true)
* usb_configure_drivers(USB_INSERTED)
* ...
* (SET_CONFIG(n) which requires exclusive storage)
* usb_core_do_set_config(n)
* usb_request_exclusive_storage()
* exclusive_storage_requested = true
* ...
* (all threads acked)
* usb_slave_mode(true)
* disk_unmount_all()
* exclusive_storage_enabled = true
* (exclusive mode done)
* ...
* (extracted, or SET_CONFIG(m) which does not require exclusive storage)
* usb_core_do_set_config(m)
* usb_release_exclusive_storage()
* exclusive_storage_requested = false
* exclusive_storage_enabled = false
* usb_slave_mode(false)
* disk_mount_all()
*
* !HAVE_USBSTACK:
* (inserted)
* usb_set_host_present(true)
* usb_configure_drivers(USB_INSERTED)
* usb_request_exclusive_storage()
* exclusive_storage_requested = true
* ...
* (all threads acked)
* usb_slave_mode(true)
* disk_unmount_all()
* exclusive_storage_enabled = true
* ...
* (extracted)
* usb_set_host_present(false)
* usb_configure_drivers(USB_EXTRACTED)
* usb_release_exclusive_storage()
* ..
* */
void usb_request_exclusive_storage(void)
{
int bccount;
exclusive_storage_access = false;
exclusive_storage_requested = true;
usb_broadcast_seqnum += 1;
usb_num_acks_to_expect = queue_broadcast(SYS_USB_CONNECTED, usb_broadcast_seqnum) - 1;
DEBUGF("usb: waiting for %d acks...\n", usb_num_acks_to_expect);
}
void usb_release_exclusive_storage(void)
{
if(!exclusive_storage_requested) {
return;
}
exclusive_storage_requested = false;
if(exclusive_storage_enabled) {
usb_slave_mode(false);
}
exclusive_storage_enabled = false;
#ifdef DEBUG
/* Tell all threads that we are back in business */
bccount = queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
int bccount = queue_broadcast(SYS_USB_DISCONNECTED, 0) - 1;
DEBUGF("USB extracted. Broadcast to %d threads...\n", bccount);
return bccount;
#endif
return;
}
#ifdef USB_ENABLE_HID
@ -889,10 +913,12 @@ void usb_set_audio(int value)
#ifdef HAVE_USB_POWER
bool usb_powered_only(void)
{
return usb_state == USB_POWERED;
return usb_power_only;
}
#endif /* HAVE_USB_POWER */
#endif /* HAVE_USBSTACK && defined(USB_FULL_INIT) */
#elif defined(USB_NONE)
/* Dummy functions for USB_NONE */
@ -901,9 +927,10 @@ bool usb_inserted(void)
return false;
}
void usb_acknowledge(long id)
void usb_acknowledge(long id, intptr_t seqnum)
{
(void)id;
(void)seqnum;
}
void usb_init(void)

View file

@ -118,6 +118,10 @@ struct usb_class_driver {
* Returns value on success and -1 on error.
* Mandatory function if alternate interface support is needed */
int (*get_interface)(int interface);
/* Invoked by USB_NOTIFY_CLASS_DRIVER
Optional function */
void (*notify_event)(intptr_t data);
};
#define PACK_DATA(dest, data) pack_data(dest, &(data), sizeof(data))

View file

@ -544,16 +544,6 @@ bool usb_core_driver_enabled(int driver)
return drivers[driver].enabled;
}
bool usb_core_any_exclusive_storage(void)
{
int i;
for(i = 0; i < USB_NUM_DRIVERS; i++)
if(drivers[i].enabled && drivers[i].needs_exclusive_storage)
return true;
return false;
}
#ifdef HAVE_HOTSWAP
void usb_core_hotswap_event(int volume, bool inserted)
{
@ -874,16 +864,28 @@ static int usb_core_do_set_config(uint8_t new_config)
usb_config = new_config;
usb_state = usb_config == 0 ? ADDRESS : CONFIGURED;
bool require_exclusive = false;
/* activate new config */
if(usb_config != 0) {
init_deinit_endpoints(usb_config - 1, true);
for(int i = 0; i < USB_NUM_DRIVERS; i++) {
if(is_active(drivers[i]) && drivers[i].init_connection != NULL) {
drivers[i].init_connection();
require_exclusive |= drivers[i].needs_exclusive_storage;
}
}
}
if(require_exclusive) {
if(!usb_exclusive_storage()) {
usb_release_exclusive_storage();
usb_request_exclusive_storage();
}
} else {
usb_release_exclusive_storage();
}
#ifdef HAVE_USB_CHARGING_ENABLE
usb_charging_maxcurrent_change(usb_charging_maxcurrent());
#endif
@ -1179,6 +1181,20 @@ void usb_core_handle_notify(long id, intptr_t data)
usb_charging_maxcurrent_change(usb_charging_maxcurrent());
#endif
break;
case USB_NOTIFY_CLASS_DRIVER: {
/* HACK: index is uint8 but promoted to int to avoid a compiler
warning when USB_NUM_DRIVERS is 0, mainly in bootloaders.
This hack can be removed once usb_core is no longer built
for BOOTLOADER && !HAVE_BOOTLOADER_USB_MODE */
int index = data >> 24;
if(index < 0 || index >= USB_NUM_DRIVERS) {
logf("usb_core: invalid notification destination index=%u", index);
return;
}
if(is_active(drivers[index]) && drivers[index].notify_event != NULL) {
drivers[index].notify_event(data & 0x00ffffff);
}
} break;
default:
break;
}

Some files were not shown because too many files have changed in this diff Show more