зеркало из https://github.com/microsoft/uf2-linux.git
Initial import
This commit is contained in:
Родитель
7bd27eab3f
Коммит
148edc6068
|
@ -1,330 +1,4 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
built/
|
||||
tmp
|
||||
uf2daemon/uf2d
|
||||
uf2daemon/uf2d86
|
||||
|
|
52
README.md
52
README.md
|
@ -1,5 +1,55 @@
|
|||
|
||||
# Contributing
|
||||
# UF2 Flashing for Linux
|
||||
|
||||
This repo contains scripts and patches to build a sample Linux image
|
||||
based on [piCore](http://www.tinycorelinux.net/ports.html)
|
||||
for Raspberry Pi Zero.
|
||||
The image is meant to boot very quickly (currently at around 7s),
|
||||
and expose a USB mass storage device (pen drive), which can be used
|
||||
to program a Raspberry Pi Zero with [UF2 files](https://github.com/Microsoft/uf2),
|
||||
usually generated from [Microsoft MakeCode](https://github.com/Microsoft/pxt)
|
||||
and in particular from [MakeCode Arcade](https://arcade.microsoft.com).
|
||||
|
||||
The image was tested on a Raspberry Pi Zero Rev 1.3 and Zero W Rev 1.3.
|
||||
It could theoretically work on the original Pi A/A+, but wasn't
|
||||
tested. Other models lack the OTG ID pin, and thus cannot be used in
|
||||
USB device mode.
|
||||
|
||||
PRs are welcome!
|
||||
|
||||
## Building
|
||||
|
||||
Building the image requires [Docker](https://www.docker.com/).
|
||||
|
||||
Go to `image/` and run `./build.sh`. The image will land in `built/boot/*`.
|
||||
|
||||
### "Burning" image
|
||||
|
||||
All files in `built/boot/` need to be copied to a FAT32-formatted SD card.
|
||||
There is no ext4 partition to worry about, and you don't need to use any
|
||||
special software to "burn" the image.
|
||||
|
||||
Regular SD cards come preformatted as FAT32. If you have a previous
|
||||
Raspberry Pi image on the card you can format it, or just move all files in
|
||||
the first partition into a sub-folder if it's reasonably big.
|
||||
|
||||
Any SD card should do. You don't need much space (currently around 13MB),
|
||||
and the Pi will only read a few MBs upon startup, so the speed isn't very important.
|
||||
|
||||
### Docker image
|
||||
|
||||
If you want to build the Docker image (`pext/rpi`) yourself,
|
||||
use the `docker/build.sh` script. Usually, you can just pull it
|
||||
from Docker Hub (which will just happen automatically).
|
||||
The image is based on
|
||||
[sdthirlwall/raspberry-pi-cross-compiler](https://hub.docker.com/r/sdthirlwall/raspberry-pi-cross-compiler/)
|
||||
and contains stock piCore 9.0.3 and sources of its kernel.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
# Pull base image
|
||||
FROM sdthirlwall/raspberry-pi-cross-compiler
|
||||
MAINTAINER Michal Moskal <michal@moskal.me>
|
||||
|
||||
USER root
|
||||
|
||||
RUN curl https://deb.nodesource.com/node_6.x/pool/main/n/nodejs/nodejs_6.11.0-1nodesource1~jessie1_amd64.deb > node.deb \
|
||||
&& dpkg -i node.deb \
|
||||
&& rm node.deb
|
||||
|
||||
RUN apt-get install mtools cpio bc p7zip-full squashfs-tools
|
||||
|
||||
|
||||
RUN mkdir -p /picore/kernel
|
||||
WORKDIR /picore/kernel
|
||||
RUN curl http://www.tinycorelinux.net/9.x/armv6/releases/RPi/src/kernel/linux-rpi-4.9.22.tar.xz > linux-rpi-4.9.22.tar.xz
|
||||
RUN tar xf linux-rpi-4.9.22.tar.xz
|
||||
RUN mv linux-rpi-4.9.22 linux-rpi
|
||||
RUN rm linux-rpi-4.9.22.tar.xz
|
||||
RUN curl http://www.tinycorelinux.net/9.x/armv6/releases/RPi/src/kernel/4.9.22-piCore.config.xz | xzcat > linux-rpi/.config
|
||||
RUN curl http://www.tinycorelinux.net/9.x/armv6/releases/RPi/src/kernel/4.9.22-piCore_Module.symvers.xz | xzcat > linux-rpi/Module.symvers
|
||||
RUN curl http://www.tinycorelinux.net/9.x/armv6/releases/RPi/src/kernel/4.9.22-piCore_System.map.xz | xzcat > linux-rpi/System.map
|
||||
|
||||
WORKDIR /picore/kernel/linux-rpi
|
||||
|
||||
RUN make ARCH=arm CROSS_COMPILE=/rpxc/bin/arm-linux-gnueabihf- modules_prepare
|
||||
RUN echo "#!/bin/sh" > mkusb.sh
|
||||
RUN echo "make ARCH=arm CROSS_COMPILE=/rpxc/bin/arm-linux-gnueabihf- SUBDIRS=drivers/usb -j10 modules" >> mkusb.sh
|
||||
RUN chmod +x mkusb.sh
|
||||
RUN ./mkusb.sh
|
||||
|
||||
RUN mkdir /picore/img
|
||||
WORKDIR /picore/img
|
||||
RUN curl http://www.tinycorelinux.net/9.x/armv6/releases/RPi/piCore-9.0.3.zip > picore.zip
|
||||
RUN 7z x picore.zip
|
||||
RUN mkdir /picore/boot
|
||||
RUN mcopy -s -i piCore-9.0.3.img@@4096K ::* ../boot
|
||||
WORKDIR /picore
|
||||
RUN rm -rf img
|
||||
RUN mkdir rootfs
|
||||
RUN cd rootfs && zcat ../boot/9.0.3.gz | cpio -i -H newc -d
|
||||
RUN mkdir rootfsv7
|
||||
RUN cd rootfsv7 && zcat ../boot/9.0.3v7.gz | cpio -i -H newc -d
|
||||
|
||||
WORKDIR /picore
|
||||
RUN git clone https://github.com/WiringPi/WiringPi
|
||||
WORKDIR /picore/WiringPi/wiringPi
|
||||
# WiringPi build script doesn't do cross-compile
|
||||
RUN arm-linux-gnueabihf-gcc -g -ffunction-sections -fdata-sections -Os -c *.c -I .
|
||||
RUN arm-linux-gnueabihf-ar rcs libwiringPi.a *.o
|
||||
RUN cp libwiringPi.a /rpxc/arm-linux-gnueabihf/lib/
|
||||
RUN cp *.h /rpxc/arm-linux-gnueabihf/libc/usr/include/
|
||||
WORKDIR /picore
|
||||
RUN rm -rf WiringPi
|
||||
|
||||
RUN useradd -m build
|
||||
USER root
|
||||
COPY go.js /home/build
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
docker build -t pext/rpi -f Dockerfile .
|
|
@ -0,0 +1,17 @@
|
|||
var fs = require("fs")
|
||||
var child_process = require("child_process")
|
||||
|
||||
process.stdin.setEncoding("utf8")
|
||||
process.stdout.setEncoding("utf8")
|
||||
|
||||
var buf = ""
|
||||
process.stdin.on("data", function(d) { buf += d })
|
||||
process.stdin.on("end", function() {
|
||||
handle(JSON.parse(buf))
|
||||
})
|
||||
|
||||
function handle(req) {
|
||||
fs.writeFileSync("builder.js", req.builderJs);
|
||||
global.buildReq = req;
|
||||
require("./builder")
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 root=/dev/ram0 elevator=deadline rootwait quiet nortc loglevel=5 noembed waitusb=1 norestore
|
|
@ -0,0 +1,85 @@
|
|||
# For more options and information see
|
||||
# http://www.raspberrypi.org/documentation/configuration/config-txt.md
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
[PI0]
|
||||
initramfs 9.0.3.gz followkernel
|
||||
kernel kernel4922.img
|
||||
cmdline cmdline.txt
|
||||
|
||||
[PI1]
|
||||
initramfs 9.0.3.gz followkernel
|
||||
kernel kernel4922.img
|
||||
cmdline cmdline.txt
|
||||
|
||||
[PI2]
|
||||
initramfs 9.0.3v7.gz followkernel
|
||||
kernel kernel4922v7.img
|
||||
cmdline cmdline.txt
|
||||
|
||||
[PI3]
|
||||
initramfs 9.0.3v7.gz followkernel
|
||||
kernel kernel4922v7.img
|
||||
cmdline cmdline3.txt
|
||||
|
||||
[ALL]
|
||||
|
||||
# uncomment if you get no picture on HDMI for a default "safe" mode
|
||||
#hdmi_safe=1
|
||||
|
||||
# uncomment this if your display has a black border of unused pixels visible
|
||||
# and your display can output without overscan
|
||||
#disable_overscan=1
|
||||
|
||||
# uncomment the following to adjust overscan. Use positive numbers if console
|
||||
# goes off screen, and negative if there is too much border
|
||||
#overscan_left=16
|
||||
#overscan_right=16
|
||||
#overscan_top=16
|
||||
#overscan_bottom=16
|
||||
|
||||
# uncomment to force a console size. By default it will be display's size minus
|
||||
# overscan.
|
||||
#framebuffer_width=1280
|
||||
#framebuffer_height=720
|
||||
|
||||
# uncomment if hdmi display is not detected and composite is being output
|
||||
#hdmi_force_hotplug=1
|
||||
|
||||
# uncomment to force a specific HDMI mode (this will force VGA)
|
||||
#hdmi_group=1
|
||||
#hdmi_mode=1
|
||||
|
||||
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
|
||||
# DMT (computer monitor) modes
|
||||
#hdmi_drive=2
|
||||
|
||||
# uncomment to increase signal to HDMI, if you have interference, blanking, or
|
||||
# no display
|
||||
#config_hdmi_boost=4
|
||||
|
||||
# uncomment for composite PAL
|
||||
#sdtv_mode=2
|
||||
|
||||
#uncomment to overclock the arm. 700 MHz is the default.
|
||||
#arm_freq=800
|
||||
|
||||
#----------------------------------------------------
|
||||
# Enable peripheral buses
|
||||
|
||||
dtparam=i2c=on,spi=on,i2s=on
|
||||
|
||||
# Enable onboard audio
|
||||
|
||||
dtparam=audio=on
|
||||
dtoverlay=dwc2
|
||||
|
||||
# Enable serial console
|
||||
|
||||
enable_uart=1
|
||||
|
||||
|
||||
[PI3]
|
||||
dtoverlay=pi3-disable-bt
|
||||
|
||||
[ALL]
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
TCZ="gdb alsa-modules-4.9.22-piCore alsa-oss alsa-plugins alsa alsa-utils libasound libasound-dev"
|
||||
|
||||
f="$1"
|
||||
if [ "X$f" = X ] ; then
|
||||
mkdir -p ../built/tcz
|
||||
for t in $TCZ ; do
|
||||
test -f ../built/tcz/$t.tcz || curl http://www.tinycorelinux.net/9.x/armv6/tcz/$t.tcz > ../built/tcz/$t.tcz
|
||||
done
|
||||
rm -rf ../built/boot
|
||||
f=/build/image/inner.sh
|
||||
fi
|
||||
|
||||
docker run -i -t --rm -v `cd .. && pwd`:/build pext/rpi "$f"
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
# build uf2
|
||||
cd /build/uf2daemon
|
||||
make
|
||||
|
||||
cd /picore/boot
|
||||
# remove stuff we don't support yet anyway
|
||||
rm *v7* *_cd.* *_x.* *_db.*
|
||||
# overlay files
|
||||
cp -r /build/image/boot/* .
|
||||
|
||||
# extract TCZs
|
||||
cd /picore
|
||||
mkdir sq
|
||||
for f in /build/built/tcz/*.tcz ; do
|
||||
unsquashfs $f
|
||||
cp -r squashfs-root/* sq/
|
||||
rm -rf squashfs-root
|
||||
done
|
||||
cp sq/usr/local/bin/gdbserver rootfs/usr/bin/
|
||||
for mod in snd-pcm-oss snd-mixer-oss snd-soc-core snd-bcm2835 snd-pcm-dmaengine snd-pcm snd-timer snd-compress snd ; do
|
||||
p=`find sq -name $mod.ko`
|
||||
cp $p rootfs/lib/modules/4.9.22-piCore/kernel/drivers/
|
||||
done
|
||||
|
||||
#cp -r sq/* rootfs/
|
||||
|
||||
cp -r /build/image/rootfs/* rootfs/
|
||||
cp /build/uf2daemon/uf2d rootfs/sbin/
|
||||
|
||||
cd rootfs
|
||||
patch -p1 < /build/image/rootfs.patch
|
||||
|
||||
# kernel modules
|
||||
cd /picore/kernel/linux-rpi
|
||||
patch drivers/usb/gadget/function/f_mass_storage.c < /build/kernel/f_mass_storage.c.sync.patch
|
||||
./mkusb.sh
|
||||
dst=/picore/rootfs/lib/modules/4.9.22-piCore/kernel
|
||||
|
||||
for d in drivers/usb/dwc2 drivers/usb/gadget \
|
||||
drivers/usb/gadget/legacy drivers/usb/gadget/function drivers/usb/gadget/udc ; do
|
||||
mkdir -p $dst/$d
|
||||
cp $d/*.ko $dst/$d
|
||||
done
|
||||
|
||||
# create new image
|
||||
cd /picore/rootfs
|
||||
find | cpio -o -R 0:0 -H newc | gzip -4 > ../boot/9.0.3.gz
|
||||
|
||||
# Copy out results to host
|
||||
mkdir -p /build/built
|
||||
cp -r /picore/boot /build/built/boot
|
|
@ -0,0 +1,48 @@
|
|||
Only in orig/etc/init.d/: busybox-aliases
|
||||
Only in orig/etc/init.d/: dhcp.sh
|
||||
Only in orig/etc/init.d/: rc.shutdown
|
||||
Only in orig/etc/init.d/: rcS
|
||||
Only in orig/etc/init.d/: services
|
||||
Only in orig/etc/init.d/: settime.sh
|
||||
diff -ur orig/etc/init.d/tc-config rootfs/etc/init.d/tc-config
|
||||
--- orig/etc/init.d/tc-config 2018-06-15 09:35:38.000000000 -0700
|
||||
+++ rootfs/etc/init.d/tc-config 2018-06-16 23:20:09.000000000 -0700
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
# Main
|
||||
|
||||
-clear
|
||||
-echo "${GREEN}Booting ${YELLOW}Core $VERSION ${NORMAL}"
|
||||
+#clear
|
||||
+echo "${GREEN}Booting ${YELLOW}Core $VERSION - Arcade Pi ${NORMAL}"
|
||||
echo "${GREEN}Running Linux Kernel ${YELLOW}$KERNEL${GREEN}.${NORMAL}"
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:"$PATH"
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
done
|
||||
fi
|
||||
|
||||
+depmod -a
|
||||
+
|
||||
# Start Udev to populate /dev and handle hotplug events
|
||||
echo -n "${BLUE}Starting udev daemon for hotplug support...${NORMAL}"
|
||||
#/sbin/udevd --daemon 2>/dev/null >/dev/null
|
||||
@@ -536,6 +538,8 @@
|
||||
sync
|
||||
|
||||
wait $fstab_pid
|
||||
+
|
||||
+if false ; then
|
||||
MSSG="${BLUE}Loading extensions...${NORMAL}"
|
||||
if [ -n "$SHOWAPPS" ]; then
|
||||
touch /etc/sysconfig/showapps
|
||||
@@ -557,6 +561,7 @@
|
||||
echo -n "${RED}Press Enter key.${NORMAL}"; read ans
|
||||
fi
|
||||
fi
|
||||
+fi
|
||||
|
||||
[ -n "$KEYMAP" ] || KEYMAP="us"
|
||||
if [ -f "/usr/share/kmap/$KEYMAP.kmap" ]; then
|
||||
Only in orig/etc/init.d/: tc-restore.sh
|
||||
Only in orig/etc/init.d/: tc_noscan.lst
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Start serial terminal
|
||||
/usr/sbin/startserialtty &
|
||||
|
||||
# Set CPU frequency governor to ondemand (default is performance)
|
||||
echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
|
||||
# Load modules
|
||||
/sbin/modprobe i2c-dev
|
||||
|
||||
/opt/menustart.sh &
|
||||
|
||||
# ------ Put other system startup commands below this line
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
# put other system startup commands here, the boot process will wait until they complete.
|
||||
# Use bootlocal.sh for system startup commands that can run in the background
|
||||
# and therefore not slow down the boot process.
|
||||
/usr/bin/sethostname box
|
||||
set -x
|
||||
modprobe snd_pcm_oss
|
||||
modprobe nbd
|
||||
modprobe dwc2
|
||||
modprobe libcomposite
|
||||
mkdir /sd
|
||||
mount /dev/mmcblk0p1 /sd/
|
||||
/sbin/uf2d /dev/nbd0
|
||||
/opt/bootlocal.sh &
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
ok=1
|
||||
while : ; do
|
||||
if kill -0 `cat /tmp/pxt-pid` 2>/dev/null ; then
|
||||
ok=1
|
||||
else
|
||||
if [ $ok = 0 ] ; then
|
||||
if test -x /sd/prj/.menu.elf ; then
|
||||
/sd/prj/.menu.elf
|
||||
fi
|
||||
sleep 5
|
||||
ok=1
|
||||
else
|
||||
ok=0
|
||||
fi
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
set -x
|
||||
rm /tmp/msd-ok
|
||||
killall gdbserver 2>/dev/null
|
||||
rmmod g_multi
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
set -xe
|
||||
test -f /tmp/msd-ok && exit
|
||||
modprobe g_multi file=/dev/nbd0 iSerialNumber=123456789 stall=0
|
||||
touch /tmp/msd-ok
|
||||
/opt/rungdb.sh &
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
killall gdbserver 2>/dev/null
|
||||
sleep 3
|
||||
gdbserver --multi /dev/ttyGS0
|
|
@ -0,0 +1 @@
|
|||
http://repo.tinycorelinux.net/
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
model=`cat /proc/device-tree/model`
|
||||
|
||||
if [ "${model:0:20}" = "Raspberry Pi 3 Model" -o "${model:0:19}" = "Raspberry Pi Zero W" ]; then
|
||||
port=ttyS0
|
||||
else
|
||||
port=ttyAMA0
|
||||
fi
|
||||
|
||||
# Start serial terminal on Raspberry Pi
|
||||
while :
|
||||
do
|
||||
echo Start serial on $port
|
||||
/sbin/getty -L $port 115200 screen
|
||||
done
|
|
@ -0,0 +1,11 @@
|
|||
--- f_mass_storage.c 2018-06-16 23:26:01.000000000 -0700
|
||||
+++ kernel/f_mass_storage.c 2018-06-15 16:15:16.000000000 -0700
|
||||
@@ -781,7 +781,7 @@
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock(&curlun->filp->f_lock);
|
||||
- curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */
|
||||
+ curlun->filp->f_flags |= O_SYNC; /* Default is to wait - we're using local NBD */
|
||||
spin_unlock(&curlun->filp->f_lock);
|
||||
|
||||
/*
|
|
@ -0,0 +1,8 @@
|
|||
CFLAGS = -std=c99 -W -Wall
|
||||
SRC = main.c fat.c
|
||||
|
||||
all:
|
||||
arm-linux-gnueabihf-gcc -Os -s $(CFLAGS) $(SRC) -o uf2d
|
||||
|
||||
x86:
|
||||
gcc -DX86=1 -g $(CFLAGS) $(SRC) -o uf2d86
|
|
@ -0,0 +1,867 @@
|
|||
|
||||
#define PRODUCT_NAME "Arcade Pi"
|
||||
#define VOLUME_LABEL "ARCADE"
|
||||
#define INDEX_URL "https://arcade.makecode.com"
|
||||
|
||||
#define BOARD_ID "Arcade-RPi-v0"
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
|
||||
#define max(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define min(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#include "uf2.h"
|
||||
|
||||
#define DBG LOG
|
||||
|
||||
typedef struct {
|
||||
uint8_t JumpInstruction[3];
|
||||
uint8_t OEMInfo[8];
|
||||
uint16_t SectorSize;
|
||||
uint8_t SectorsPerCluster;
|
||||
uint16_t ReservedSectors;
|
||||
uint8_t FATCopies;
|
||||
uint16_t RootDirectoryEntries;
|
||||
uint16_t TotalSectors16;
|
||||
uint8_t MediaDescriptor;
|
||||
uint16_t SectorsPerFAT;
|
||||
uint16_t SectorsPerTrack;
|
||||
uint16_t Heads;
|
||||
uint32_t HiddenSectors;
|
||||
uint32_t TotalSectors32;
|
||||
uint8_t PhysicalDriveNum;
|
||||
uint8_t Reserved;
|
||||
uint8_t ExtendedBootSig;
|
||||
uint32_t VolumeSerialNumber;
|
||||
char VolumeLabel[11];
|
||||
uint8_t FilesystemIdentifier[8];
|
||||
} __attribute__((packed)) FAT_BootBlock;
|
||||
|
||||
typedef struct {
|
||||
char name[8];
|
||||
char ext[3];
|
||||
uint8_t attrs;
|
||||
uint8_t reserved;
|
||||
uint8_t createTimeFine;
|
||||
uint16_t createTime;
|
||||
uint16_t createDate;
|
||||
uint16_t lastAccessDate;
|
||||
uint16_t highStartCluster;
|
||||
uint16_t updateTime;
|
||||
uint16_t updateDate;
|
||||
uint16_t startCluster;
|
||||
uint32_t size;
|
||||
} __attribute__((packed)) DirEntry;
|
||||
|
||||
typedef struct {
|
||||
uint8_t seqno;
|
||||
uint16_t name0[5];
|
||||
uint8_t attrs;
|
||||
uint8_t type;
|
||||
uint8_t checksum;
|
||||
uint16_t name1[6];
|
||||
uint16_t startCluster;
|
||||
uint16_t name2[2];
|
||||
} __attribute__((packed)) VFatEntry;
|
||||
|
||||
STATIC_ASSERT(sizeof(DirEntry) == 32);
|
||||
|
||||
#define STR0(x) #x
|
||||
#define STR(x) STR0(x)
|
||||
const char infoUf2File[] = //
|
||||
"UF2 Bootloader " UF2_VERSION "\r\n"
|
||||
"Model: " PRODUCT_NAME "\r\n"
|
||||
"Board-ID: " BOARD_ID "\r\n";
|
||||
|
||||
const char indexFile[] = //
|
||||
"<!doctype html>\n"
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<script>\n"
|
||||
"location.replace(\"" INDEX_URL "\");\n"
|
||||
"</script>"
|
||||
"</body>"
|
||||
"</html>\n";
|
||||
|
||||
#define RESERVED_SECTORS 1
|
||||
#define ROOT_DIR_SECTORS 4
|
||||
#define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + 511) / 512)
|
||||
|
||||
#define START_FAT0 RESERVED_SECTORS
|
||||
#define START_FAT1 (START_FAT0 + SECTORS_PER_FAT)
|
||||
#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
|
||||
#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
|
||||
#define ROOT_DIR_ENTRIES (ROOT_DIR_SECTORS * 512 / 32)
|
||||
|
||||
#define F_TEXT 1
|
||||
#define F_UF2 2
|
||||
#define F_DIR 4
|
||||
#define F_CONT 8
|
||||
#define F_RAW 16
|
||||
|
||||
static const FAT_BootBlock BootBlock = {
|
||||
.JumpInstruction = {0xeb, 0x3c, 0x90},
|
||||
.OEMInfo = "UF2 UF2 ",
|
||||
.SectorSize = 512,
|
||||
.SectorsPerCluster = 1,
|
||||
.ReservedSectors = RESERVED_SECTORS,
|
||||
.FATCopies = 2,
|
||||
.RootDirectoryEntries = ROOT_DIR_ENTRIES,
|
||||
.TotalSectors16 = NUM_FAT_BLOCKS - 2,
|
||||
.MediaDescriptor = 0xF8,
|
||||
.SectorsPerFAT = SECTORS_PER_FAT,
|
||||
.SectorsPerTrack = 1,
|
||||
.Heads = 1,
|
||||
.ExtendedBootSig = 0x29,
|
||||
.VolumeSerialNumber = 0x00420042,
|
||||
.VolumeLabel = VOLUME_LABEL,
|
||||
.FilesystemIdentifier = "FAT16 ",
|
||||
};
|
||||
|
||||
int currCluster = 2;
|
||||
struct FsEntry *rootDir;
|
||||
struct ClusterData *firstCluster, *lastCluster;
|
||||
int numWrites = 0;
|
||||
|
||||
#define MAX_BLOCKS 8000
|
||||
typedef struct {
|
||||
uint32_t numBlocks;
|
||||
uint32_t numWritten;
|
||||
uint8_t writtenMask[MAX_BLOCKS / 8 + 1];
|
||||
} WriteState;
|
||||
char execPath[300];
|
||||
static WriteState wrState;
|
||||
|
||||
#define ZERO(v) memset(&v, 0, sizeof(v))
|
||||
|
||||
void setupFs();
|
||||
void enableMSD(int enabled);
|
||||
|
||||
#define MAGIC_CLUSTER 0x4242c180
|
||||
#define MAGIC_FSENTRY 0x4200c180
|
||||
|
||||
typedef struct ClusterData {
|
||||
int magic;
|
||||
int flags;
|
||||
int numclusters;
|
||||
struct stat st;
|
||||
struct ClusterData *dnext;
|
||||
struct ClusterData *cnext;
|
||||
struct FsEntry *dirdata;
|
||||
struct FsEntry *myfile;
|
||||
char name[0];
|
||||
} ClusterData;
|
||||
|
||||
typedef struct FsEntry {
|
||||
int magic;
|
||||
int startCluster;
|
||||
uint8_t attrs;
|
||||
int size;
|
||||
int numdirentries;
|
||||
time_t ctime, mtime;
|
||||
struct FsEntry *next;
|
||||
struct ClusterData *data;
|
||||
char fatname[12];
|
||||
char vfatname[0];
|
||||
} FsEntry;
|
||||
|
||||
struct DirMap {
|
||||
const char *mapName;
|
||||
const char *fsName;
|
||||
int mode;
|
||||
};
|
||||
|
||||
static void freeChain(FsEntry *p) {
|
||||
FsEntry *n;
|
||||
while (p) {
|
||||
n = p->next;
|
||||
assert(p->magic == MAGIC_FSENTRY);
|
||||
p->magic = 0xdead;
|
||||
free(p);
|
||||
p = n;
|
||||
}
|
||||
}
|
||||
|
||||
void rereadData() {
|
||||
lastCluster = NULL;
|
||||
numWrites = 0;
|
||||
currCluster = 2;
|
||||
for (FsEntry *p = rootDir; p; p = p->next) {
|
||||
if (p->data)
|
||||
freeChain(p->data->dirdata);
|
||||
}
|
||||
freeChain(rootDir);
|
||||
rootDir = NULL;
|
||||
while (firstCluster) {
|
||||
lastCluster = firstCluster;
|
||||
firstCluster = firstCluster->cnext;
|
||||
assert(lastCluster->magic == MAGIC_CLUSTER);
|
||||
lastCluster->magic = 0xdead;
|
||||
free(lastCluster);
|
||||
}
|
||||
ZERO(wrState);
|
||||
setupFs();
|
||||
}
|
||||
|
||||
struct DirMap dirMaps[] = { //
|
||||
#ifdef X86
|
||||
{"foo qux baz", "dirs/bar", F_UF2}, //
|
||||
{"foo", "dirs/foo", F_UF2}, //
|
||||
{"xyz", "dirs/bar2", F_UF2}, //
|
||||
{"Temporary Logs", "/tmp/logs", F_RAW},
|
||||
#else
|
||||
{"Projects", "/sd/prj", F_UF2},
|
||||
{"Temporary Logs", "/tmp/logs", F_RAW},
|
||||
{"Permanent Logs", "/sd/logs", F_RAW},
|
||||
#endif
|
||||
{NULL, NULL, 0}};
|
||||
|
||||
void timeToFat(time_t t, uint16_t *dateP, uint16_t *timeP) {
|
||||
struct tm tm;
|
||||
|
||||
localtime_r(&t, &tm);
|
||||
|
||||
if (timeP)
|
||||
*timeP = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec / 2);
|
||||
|
||||
if (dateP)
|
||||
*dateP = (max(0, tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
|
||||
}
|
||||
|
||||
void padded_memcpy(char *dst, const char *src, int len) {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (*src)
|
||||
*dst = *src++;
|
||||
else
|
||||
*dst = ' ';
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
int lastMapMode;
|
||||
char *expandMap(const char *mapName) {
|
||||
static char mapbuf[300];
|
||||
|
||||
const char *rest = "";
|
||||
for (int i = 0; i < (int)sizeof(mapbuf); ++i) {
|
||||
char c = mapName[i];
|
||||
if (c == '/' || c == 0) {
|
||||
mapbuf[i] = 0;
|
||||
rest = mapName + i;
|
||||
break;
|
||||
}
|
||||
mapbuf[i] = c;
|
||||
}
|
||||
for (int i = 0; dirMaps[i].mapName; ++i) {
|
||||
if (strcmp(dirMaps[i].mapName, mapbuf) == 0) {
|
||||
strcpy(mapbuf, dirMaps[i].fsName);
|
||||
strcat(mapbuf, rest);
|
||||
lastMapMode = dirMaps[i].mode;
|
||||
return mapbuf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ClusterData *mkClusterData(int namelen) {
|
||||
ClusterData *c = malloc(sizeof(*c) + namelen + 1);
|
||||
memset(c, 0, sizeof(*c) + namelen + 1);
|
||||
c->magic = MAGIC_CLUSTER;
|
||||
return c;
|
||||
}
|
||||
|
||||
ClusterData *readDir(const char *mapName) {
|
||||
DIR *d = opendir(expandMap(mapName));
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
ClusterData *res = NULL;
|
||||
for (;;) {
|
||||
struct dirent *ent = readdir(d);
|
||||
if (!ent)
|
||||
break;
|
||||
|
||||
ClusterData *c = mkClusterData(strlen(mapName) + 1 + strlen(ent->d_name));
|
||||
|
||||
c->flags = lastMapMode;
|
||||
c->dnext = res;
|
||||
sprintf(c->name, "%s/%s", mapName, ent->d_name);
|
||||
|
||||
int err = stat(expandMap(c->name), &c->st);
|
||||
assert(err >= 0);
|
||||
|
||||
if (S_ISREG(c->st.st_mode) && strlen(c->name) < UF2_FILENAME_MAX && ent->d_name[0] != '.') {
|
||||
if (c->flags & F_RAW)
|
||||
c->numclusters = (c->st.st_size + 511) / 512;
|
||||
else
|
||||
c->numclusters = (c->st.st_size + 255) / 256;
|
||||
} else {
|
||||
free(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = c;
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return res;
|
||||
}
|
||||
|
||||
int filechar(int c) {
|
||||
if (!c)
|
||||
return 0;
|
||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
|
||||
strchr("_-", c);
|
||||
}
|
||||
|
||||
void copyFsChars(char *dst, const char *src, int len) {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (filechar(*src))
|
||||
dst[i] = toupper(*src++);
|
||||
else {
|
||||
if (*src == '.')
|
||||
src = "";
|
||||
if (*src == 0)
|
||||
dst[i] = ' ';
|
||||
else
|
||||
dst[i] = '_';
|
||||
while (*src && !filechar(*src))
|
||||
src++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FsEntry *mkFsEntry(const char *name) {
|
||||
int sz = sizeof(FsEntry) + strlen(name) + 1;
|
||||
FsEntry *e = malloc(sz);
|
||||
memset(e, 0, sz);
|
||||
e->magic = MAGIC_FSENTRY;
|
||||
e->startCluster = currCluster;
|
||||
e->next = NULL;
|
||||
// +1 for final 0x0000, and +12 for alignment
|
||||
e->numdirentries = 1 + (strlen(name) + 1 + 12) / 13;
|
||||
strcpy(e->vfatname, name);
|
||||
|
||||
const char *src = name;
|
||||
copyFsChars(e->fatname, src, 8);
|
||||
while (*src && *src != '.')
|
||||
src++;
|
||||
if (*src == '.')
|
||||
src++;
|
||||
else
|
||||
src = "";
|
||||
copyFsChars(e->fatname + 8, src, 3);
|
||||
return e;
|
||||
}
|
||||
|
||||
void addClusterData(ClusterData *c, FsEntry *e) {
|
||||
currCluster += c->numclusters;
|
||||
|
||||
if (firstCluster == NULL) {
|
||||
firstCluster = c;
|
||||
} else {
|
||||
lastCluster->cnext = c;
|
||||
}
|
||||
lastCluster = c;
|
||||
|
||||
if (c->st.st_ctime)
|
||||
e->ctime = min(e->ctime, c->st.st_ctime);
|
||||
e->mtime = max(e->mtime, c->st.st_mtime);
|
||||
|
||||
c->myfile = e;
|
||||
|
||||
DBG("add cluster: flags=%d size=%d numcl=%d", c->flags, (int)c->st.st_size, c->numclusters);
|
||||
}
|
||||
|
||||
FsEntry *addRootText(const char *filename, const char *contents) {
|
||||
FsEntry *e = mkFsEntry(filename);
|
||||
e->next = rootDir;
|
||||
rootDir = e;
|
||||
|
||||
int sz = strlen(contents);
|
||||
e->size = sz;
|
||||
if (sz > 0) {
|
||||
assert(sz <= 512);
|
||||
ClusterData *c = mkClusterData(sz);
|
||||
c->st.st_mtime = c->st.st_ctime = time(NULL);
|
||||
|
||||
c->flags = F_TEXT;
|
||||
strcpy(c->name, contents);
|
||||
c->st.st_size = sz;
|
||||
c->numclusters = 1;
|
||||
addClusterData(c, e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
int baseLen(const char *a) {
|
||||
int len = 0;
|
||||
while (*a && *a != '.') {
|
||||
a++;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int nameMatches(const char *a, const char *b) {
|
||||
for (;;) {
|
||||
if ((*a == 0 || *a == '.') && (*b == 0 || *b == '.'))
|
||||
return 1;
|
||||
|
||||
if (*a != *b)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
void setFatNames(FsEntry *dirent) {
|
||||
for (FsEntry *p = dirent; p; p = p->next) {
|
||||
// check for collisions
|
||||
int k = 1;
|
||||
retry:
|
||||
for (FsEntry *o = dirent; o && o != p; o = o->next) {
|
||||
if (strcmp(o->fatname, p->fatname) == 0) {
|
||||
char buf[20];
|
||||
sprintf(buf, "~%d", k++);
|
||||
int len = strlen(buf);
|
||||
memcpy(p->fatname + 8 - len, buf, len);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
DBG("setname: %s [%s] cl=%s @ %d sz=%d dents=%d", p->vfatname, p->fatname,
|
||||
p->data ? p->data->name : "(no data)", p->startCluster, p->size, p->numdirentries);
|
||||
}
|
||||
}
|
||||
|
||||
void addFullDir(const char *mapName) {
|
||||
int numEntries = 0;
|
||||
FsEntry *dirents = NULL;
|
||||
|
||||
time_t mtime = 0, ctime = 0;
|
||||
|
||||
for (ClusterData *cl = readDir(mapName); cl; cl = cl->dnext) {
|
||||
if (cl->cnext || cl == lastCluster)
|
||||
continue; // already done
|
||||
|
||||
// vfat entries
|
||||
const char *filename = strchr(cl->name, '/') + 1;
|
||||
int len = baseLen(filename) + 4;
|
||||
char namebuf[len];
|
||||
if (cl->flags & F_UF2) {
|
||||
memcpy(namebuf, filename, len - 4);
|
||||
strcpy(namebuf + len - 4, ".uf2");
|
||||
filename = namebuf;
|
||||
}
|
||||
|
||||
FsEntry *fent = mkFsEntry(filename);
|
||||
numEntries += fent->numdirentries;
|
||||
fent->next = dirents;
|
||||
fent->data = cl;
|
||||
fent->size = cl->flags & F_UF2 ? cl->numclusters * 512 : cl->st.st_size;
|
||||
dirents = fent;
|
||||
addClusterData(cl, fent);
|
||||
|
||||
if (cl->flags & F_UF2)
|
||||
for (ClusterData *other = cl->dnext; other; other = other->dnext) {
|
||||
if (nameMatches(cl->name, other->name)) {
|
||||
other->flags |= F_CONT;
|
||||
fent->size += other->numclusters * 512;
|
||||
addClusterData(other, fent);
|
||||
}
|
||||
}
|
||||
|
||||
if (mtime == 0) {
|
||||
mtime = fent->mtime;
|
||||
ctime = fent->ctime;
|
||||
} else {
|
||||
mtime = max(mtime, fent->mtime);
|
||||
ctime = min(ctime, fent->ctime);
|
||||
}
|
||||
}
|
||||
|
||||
if (numEntries == 0)
|
||||
return; // skip empty dirs
|
||||
|
||||
setFatNames(dirents);
|
||||
|
||||
FsEntry *dent = mkFsEntry(mapName);
|
||||
dent->data = mkClusterData(0);
|
||||
dent->data->dirdata = dirents;
|
||||
dent->data->numclusters = (numEntries + 16) / 16; // at least 1
|
||||
addClusterData(dent->data, dent);
|
||||
dent->mtime = mtime;
|
||||
dent->ctime = ctime;
|
||||
dent->next = rootDir;
|
||||
dent->attrs = 0x10;
|
||||
dent->data->flags = F_DIR;
|
||||
rootDir = dent;
|
||||
}
|
||||
|
||||
void addFileForward(const char *filename, const char *srcFilename, int size) {
|
||||
FsEntry *e = mkFsEntry(filename);
|
||||
e->next = rootDir;
|
||||
rootDir = e;
|
||||
|
||||
e->size = size;
|
||||
ClusterData *c = mkClusterData(strlen(srcFilename));
|
||||
int err = stat(srcFilename, &c->st);
|
||||
|
||||
if (err < 0) {
|
||||
// if the file doesn't exists (yet), use current time in hope it will appear
|
||||
c->st.st_mtime = c->st.st_ctime = time(NULL);
|
||||
}
|
||||
|
||||
c->flags = F_RAW;
|
||||
strcpy(c->name, srcFilename);
|
||||
c->st.st_size = size;
|
||||
c->numclusters = (c->st.st_size + 255) / 256;
|
||||
addClusterData(c, e);
|
||||
}
|
||||
|
||||
void setupFs() {
|
||||
addRootText("info_uf2.txt", infoUf2File);
|
||||
addRootText("index.html", indexFile);
|
||||
addFileForward("dmesg.txt", "/tmp/dmesg.txt", 128 * 1024);
|
||||
|
||||
for (int i = 0; dirMaps[i].mapName; ++i) {
|
||||
addFullDir(dirMaps[i].mapName);
|
||||
}
|
||||
|
||||
setFatNames(rootDir); // make names unique
|
||||
|
||||
FsEntry *e = addRootText(BootBlock.VolumeLabel, "");
|
||||
e->numdirentries = 1;
|
||||
e->attrs = 0x28;
|
||||
}
|
||||
|
||||
#define WRITE_ENT(v) \
|
||||
do { \
|
||||
if (skip++ >= 0) \
|
||||
*dest++ = v; \
|
||||
if (skip >= 256) \
|
||||
return; \
|
||||
cl++; \
|
||||
} while (0)
|
||||
|
||||
void readFat(uint16_t *dest, int skip) {
|
||||
int cl = 0;
|
||||
skip = -skip;
|
||||
WRITE_ENT(0xfff0);
|
||||
WRITE_ENT(0xffff);
|
||||
for (ClusterData *c = firstCluster; c; c = c->cnext) {
|
||||
for (int i = 0; i < c->numclusters - 1; i++)
|
||||
WRITE_ENT(cl + 1);
|
||||
if (c->cnext && c->cnext->flags & F_CONT)
|
||||
WRITE_ENT(cl + 1);
|
||||
else
|
||||
WRITE_ENT(0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
// note that ptr might be unaligned
|
||||
const char *copyVFatName(const char *ptr, void *dest, int len) {
|
||||
uint8_t *dst = dest;
|
||||
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (ptr == NULL) {
|
||||
*dst++ = 0xff;
|
||||
*dst++ = 0xff;
|
||||
} else {
|
||||
*dst++ = *ptr;
|
||||
*dst++ = 0;
|
||||
if (*ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint8_t fatChecksum(const char *name) {
|
||||
uint8_t sum = 0;
|
||||
for (int i = 0; i < 11; ++i)
|
||||
sum = ((sum & 1) << 7) + (sum >> 1) + *name++;
|
||||
return sum;
|
||||
}
|
||||
|
||||
void readDirData(uint8_t *dest, FsEntry *dirdata, int blkno) {
|
||||
DirEntry *d = (void *)dest;
|
||||
int idx = blkno * -16;
|
||||
for (FsEntry *e = dirdata; e; e = e->next) {
|
||||
if (idx >= 16)
|
||||
break;
|
||||
|
||||
// DBG("dir idx=%d %s", idx, e->vfatname);
|
||||
|
||||
for (int i = 0; i < e->numdirentries; ++i, ++idx) {
|
||||
if (0 <= idx && idx < 16) {
|
||||
if (i == e->numdirentries - 1) {
|
||||
memcpy(d->name, e->fatname, 11);
|
||||
d->attrs = e->attrs;
|
||||
d->size = e->size;
|
||||
d->startCluster = e->startCluster;
|
||||
timeToFat(e->mtime, &d->updateDate, &d->updateTime);
|
||||
timeToFat(e->ctime, &d->createDate, &d->createTime);
|
||||
} else {
|
||||
VFatEntry *f = (void *)d;
|
||||
int seq = e->numdirentries - i - 2;
|
||||
f->seqno = seq + 1; // they start at 1
|
||||
if (i == 0)
|
||||
f->seqno |= 0x40;
|
||||
f->attrs = 0x0F;
|
||||
f->type = 0x00;
|
||||
f->checksum = fatChecksum(e->fatname);
|
||||
f->startCluster = 0;
|
||||
|
||||
const char *ptr = e->vfatname + (13 * seq);
|
||||
ptr = copyVFatName(ptr, f->name0, 5);
|
||||
ptr = copyVFatName(ptr, f->name1, 6);
|
||||
ptr = copyVFatName(ptr, f->name2, 2);
|
||||
}
|
||||
d++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void readBlock(uint8_t *dest, int blkno) {
|
||||
// DBG("readbl %d", blkno);
|
||||
int blkno0 = blkno;
|
||||
for (ClusterData *c = firstCluster; c; c = c->cnext) {
|
||||
// DBG("off=%d sz=%d", blkno, c->numclusters);
|
||||
if (blkno >= c->numclusters) {
|
||||
blkno -= c->numclusters;
|
||||
continue;
|
||||
}
|
||||
// DBG("readbl off=%d %p", blkno, c);
|
||||
if (c->dirdata) {
|
||||
readDirData(dest, c->dirdata, blkno);
|
||||
} else if (c->flags & F_TEXT) {
|
||||
strcpy((char *)dest, c->name);
|
||||
} else if (c->flags & F_RAW) {
|
||||
memset(dest, '\n', 512);
|
||||
int fd = open(c->name[0] == '/' ? c->name : expandMap(c->name), O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
lseek(fd, blkno * 512, SEEK_SET);
|
||||
read(fd, dest, 512);
|
||||
close(fd);
|
||||
}
|
||||
} else if (c->flags & F_UF2) {
|
||||
UF2_Block *bl = (void *)dest;
|
||||
|
||||
bl->magicStart0 = UF2_MAGIC_START0;
|
||||
bl->magicStart1 = UF2_MAGIC_START1;
|
||||
bl->magicEnd = UF2_MAGIC_END;
|
||||
bl->flags = UF2_FLAG_FILE;
|
||||
bl->blockNo = blkno0 - (c->myfile->startCluster - 2);
|
||||
bl->numBlocks = c->myfile->size / 512;
|
||||
bl->targetAddr = blkno * 256;
|
||||
bl->payloadSize = 256;
|
||||
bl->fileSize = c->st.st_size;
|
||||
|
||||
int fd = open(expandMap(c->name), O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
lseek(fd, bl->targetAddr, SEEK_SET);
|
||||
bl->payloadSize = read(fd, bl->data, 256);
|
||||
close(fd);
|
||||
} else {
|
||||
bl->payloadSize = -1;
|
||||
}
|
||||
|
||||
if (bl->payloadSize < 475 - strlen(c->name))
|
||||
strcpy((char *)bl->data + bl->payloadSize, c->name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void read_block(uint32_t block_no, uint8_t *data) {
|
||||
memset(data, 0, 512);
|
||||
uint32_t sectionIdx = block_no;
|
||||
|
||||
if (block_no == 0) {
|
||||
memcpy(data, &BootBlock, sizeof(BootBlock));
|
||||
data[510] = 0x55;
|
||||
data[511] = 0xaa;
|
||||
// logval("data[0]", data[0]);
|
||||
} else if (block_no < START_ROOTDIR) {
|
||||
sectionIdx -= START_FAT0;
|
||||
if (sectionIdx >= SECTORS_PER_FAT) // second copy of fat?
|
||||
sectionIdx -= SECTORS_PER_FAT;
|
||||
|
||||
readFat((void *)data, sectionIdx * 256);
|
||||
} else if (block_no < START_CLUSTERS) {
|
||||
sectionIdx -= START_ROOTDIR;
|
||||
readDirData(data, rootDir, sectionIdx);
|
||||
} else {
|
||||
sectionIdx -= START_CLUSTERS;
|
||||
readBlock(data, sectionIdx);
|
||||
}
|
||||
}
|
||||
|
||||
extern char **environ;
|
||||
|
||||
void restartProgram() {
|
||||
LOG("start %s", execPath);
|
||||
pid_t pid;
|
||||
if (!(pid = fork())) {
|
||||
if (!fork()) {
|
||||
LOG("syncing");
|
||||
if (execPath[0]) {
|
||||
int fd = open(execPath, O_RDWR);
|
||||
fsync(fd);
|
||||
close(fd);
|
||||
}
|
||||
LOG("restart FS");
|
||||
enableMSD(0);
|
||||
sleep(2);
|
||||
enableMSD(1);
|
||||
exit(0);
|
||||
} else {
|
||||
if (execPath[0] && !fork()) {
|
||||
char *argv[] = {execPath, "--uf2", NULL};
|
||||
execve(execPath, argv, environ);
|
||||
exit(127);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int wstatus = 0;
|
||||
waitpid(pid, &wstatus, 0);
|
||||
LOG("started %s", execPath);
|
||||
}
|
||||
}
|
||||
|
||||
void write_block(uint32_t block_no, uint8_t *data) {
|
||||
WriteState *state = &wrState;
|
||||
|
||||
UF2_Block *bl = (void *)data;
|
||||
|
||||
#if 0
|
||||
if (bl->magicStart0 == 0x20da6d81 && bl->magicStart1 == 0x747e09d4) {
|
||||
DBG("restart req, #wr=%d", numWrites);
|
||||
if (numWrites) {
|
||||
restartFs();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
numWrites++;
|
||||
|
||||
if (!is_uf2_block(bl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)block_no;
|
||||
|
||||
bl->data[475] = 0; // make sure we have NUL terminator
|
||||
char *fn0 = (char *)bl->data + bl->payloadSize;
|
||||
int namelen = 0;
|
||||
if (bl->payloadSize <= UF2_MAX_PAYLOAD) {
|
||||
namelen = strlen(fn0);
|
||||
}
|
||||
|
||||
if ((bl->flags & UF2_FLAG_FILE) && bl->fileSize <= UF2_MAX_FILESIZE &&
|
||||
bl->targetAddr < bl->fileSize && 1 <= namelen && namelen <= UF2_FILENAME_MAX) {
|
||||
|
||||
char *firstSL = strchr(fn0, '/');
|
||||
char *lastSL = strrchr(fn0, '/');
|
||||
if (!lastSL)
|
||||
lastSL = fn0;
|
||||
else
|
||||
lastSL++;
|
||||
int baseLen = strlen(lastSL);
|
||||
char fallback[strlen(dirMaps[0].fsName) + 1 + baseLen + 1];
|
||||
sprintf(fallback, "%s/%s", dirMaps[0].fsName, lastSL);
|
||||
char *fn = NULL;
|
||||
|
||||
if (firstSL && firstSL + 1 == lastSL)
|
||||
fn = expandMap(fn0);
|
||||
if (!fn)
|
||||
fn = fallback;
|
||||
|
||||
char *p = strrchr(fn, '/');
|
||||
*p = 0;
|
||||
mkdir(fn, 0777);
|
||||
*p = '/';
|
||||
|
||||
int fd = open(fn, O_WRONLY | O_CREAT, 0777);
|
||||
if (fd < 0 && errno == ETXTBSY) {
|
||||
unlink(fn);
|
||||
fd = open(fn, O_WRONLY | O_CREAT, 0777);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
ftruncate(fd, bl->fileSize);
|
||||
lseek(fd, bl->targetAddr, SEEK_SET);
|
||||
// DBG("write %d bytes at %d to %s", bl->payloadSize, bl->targetAddr, fn);
|
||||
write(fd, bl->data, bl->payloadSize);
|
||||
close(fd);
|
||||
|
||||
if (strlen(fn) > 4 && !strcmp(fn + strlen(fn) - 4, ".elf")) {
|
||||
strcpy(execPath, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state && bl->numBlocks) {
|
||||
if (state->numBlocks != bl->numBlocks) {
|
||||
if (bl->numBlocks >= MAX_BLOCKS || state->numBlocks)
|
||||
state->numBlocks = 0xffffffff;
|
||||
else
|
||||
state->numBlocks = bl->numBlocks;
|
||||
}
|
||||
if (bl->blockNo < MAX_BLOCKS) {
|
||||
uint8_t mask = 1 << (bl->blockNo % 8);
|
||||
uint32_t pos = bl->blockNo / 8;
|
||||
if (!(state->writtenMask[pos] & mask)) {
|
||||
// logval("incr", state->numWritten);
|
||||
state->writtenMask[pos] |= mask;
|
||||
state->numWritten++;
|
||||
// DBG("write %d/%d #%d", state->numWritten, state->numBlocks, bl->blockNo);
|
||||
}
|
||||
if (state->numWritten >= state->numBlocks) {
|
||||
LOG("writing done");
|
||||
rereadData();
|
||||
restartProgram();
|
||||
ZERO(execPath);
|
||||
LOG("forked");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO timeout for restart?
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
#define _GNU_SOURCE 1
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <linux/nbd.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "uf2.h"
|
||||
|
||||
const char *dev_file = "/dev/nbd0";
|
||||
|
||||
#define NUM_BLOCKS NUM_FAT_BLOCKS
|
||||
|
||||
uint64_t ntohll(uint64_t a) {
|
||||
return ((uint64_t)ntohl(a & 0xffffffff) << 32) | ntohl(a >> 32);
|
||||
}
|
||||
#define htonll ntohll
|
||||
|
||||
void mylog(const char *fmt, ...) {
|
||||
va_list args;
|
||||
char *p, *p2;
|
||||
|
||||
va_start(args, fmt);
|
||||
vasprintf(&p, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (p[0] != '<')
|
||||
asprintf(&p2, "<4>%s\n", p);
|
||||
else
|
||||
asprintf(&p2, "%s\n", p);
|
||||
|
||||
int len = strlen(p2);
|
||||
|
||||
#ifdef X86
|
||||
write(2, p2, len);
|
||||
#else
|
||||
int fd = open("/dev/kmsg", O_WRONLY);
|
||||
write(fd, p2, len);
|
||||
close(fd);
|
||||
#endif
|
||||
|
||||
free(p);
|
||||
free(p2);
|
||||
}
|
||||
|
||||
void readAll(int fd, void *dst, uint32_t length) {
|
||||
while (length) {
|
||||
int curr = read(fd, dst, length);
|
||||
if (curr < 0)
|
||||
FAIL("read failed on fd:%d", fd);
|
||||
length -= curr;
|
||||
dst = (char *)dst + curr;
|
||||
}
|
||||
}
|
||||
|
||||
void writeAll(int fd, void *dst, uint32_t length) {
|
||||
while (length) {
|
||||
int curr = write(fd, dst, length);
|
||||
if (curr < 0)
|
||||
FAIL("write failed on fd:%d", fd);
|
||||
length -= curr;
|
||||
dst = (char *)dst + curr;
|
||||
}
|
||||
}
|
||||
|
||||
int nbd;
|
||||
int sock;
|
||||
int sockets[2];
|
||||
struct nbd_request request;
|
||||
struct nbd_reply reply;
|
||||
|
||||
void nbd_ioctl(unsigned id, int arg) {
|
||||
int err = ioctl(nbd, id, arg);
|
||||
if (err < 0)
|
||||
FAIL("ioctl(%u) failed [%s]", id, strerror(errno));
|
||||
}
|
||||
|
||||
void startclient() {
|
||||
close(sockets[0]);
|
||||
nbd_ioctl(NBD_SET_SOCK, sockets[1]);
|
||||
nbd_ioctl(NBD_DO_IT, 0);
|
||||
nbd_ioctl(NBD_CLEAR_QUE, 0);
|
||||
nbd_ioctl(NBD_CLEAR_SOCK, 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void handleread(int off, int len) {
|
||||
uint8_t buf[512];
|
||||
// LOG("read @%d len=%d", off, len);
|
||||
reply.error = 0; // htonl(EPERM);
|
||||
writeAll(sock, &reply, sizeof(struct nbd_reply));
|
||||
for (int i = 0; i < len; ++i) {
|
||||
read_block(off + i, buf);
|
||||
writeAll(sock, buf, 512);
|
||||
}
|
||||
}
|
||||
|
||||
void handlewrite(int off, int len) {
|
||||
uint8_t buf[512];
|
||||
// LOG("write @%d len=%d", off, len);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
readAll(sock, buf, 512);
|
||||
write_block(off + i, buf);
|
||||
}
|
||||
reply.error = 0;
|
||||
writeAll(sock, &reply, sizeof(struct nbd_reply));
|
||||
}
|
||||
|
||||
void setupFs();
|
||||
|
||||
void runNBD() {
|
||||
setupFs();
|
||||
|
||||
//struct sigaction sigchld_action = {.sa_handler = SIG_DFL, .sa_flags = SA_NOCLDWAIT};
|
||||
//sigaction(SIGCHLD, &sigchld_action, NULL);
|
||||
|
||||
int err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
|
||||
assert(err >= 0);
|
||||
|
||||
nbd = open(dev_file, O_RDWR);
|
||||
assert(nbd >= 0);
|
||||
|
||||
nbd_ioctl(BLKFLSBUF, 0);
|
||||
nbd_ioctl(NBD_SET_BLKSIZE, 512);
|
||||
nbd_ioctl(NBD_SET_SIZE_BLOCKS, NUM_BLOCKS);
|
||||
nbd_ioctl(NBD_CLEAR_SOCK, 0);
|
||||
|
||||
if (!fork())
|
||||
startclient();
|
||||
|
||||
int fd = open(dev_file, O_RDONLY);
|
||||
assert(fd != -1);
|
||||
close(fd);
|
||||
|
||||
close(sockets[1]);
|
||||
sock = sockets[0];
|
||||
|
||||
reply.magic = htonl(NBD_REPLY_MAGIC);
|
||||
reply.error = htonl(0);
|
||||
|
||||
LOG("NBD loop");
|
||||
|
||||
for (;;) {
|
||||
// nbd_ioctl(BLKFLSBUF, 0); // flush buffers - we don't want the kernel to cache the writes
|
||||
int nread = read(sock, &request, sizeof(request));
|
||||
|
||||
if (nread < 0) {
|
||||
LOG("nbd read err %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
if (nread == 0)
|
||||
return;
|
||||
assert(nread == sizeof(request));
|
||||
memcpy(reply.handle, request.handle, sizeof(reply.handle));
|
||||
reply.error = htonl(0);
|
||||
|
||||
assert(request.magic == htonl(NBD_REQUEST_MAGIC));
|
||||
|
||||
uint32_t len = ntohl(request.len);
|
||||
assert((len & 511) == 0);
|
||||
len >>= 9;
|
||||
uint64_t from = ntohll(request.from);
|
||||
assert((from & 511) == 0);
|
||||
from >>= 9;
|
||||
|
||||
switch (ntohl(request.type)) {
|
||||
case NBD_CMD_READ:
|
||||
handleread(from, len);
|
||||
break;
|
||||
case NBD_CMD_WRITE:
|
||||
handlewrite(from, len);
|
||||
break;
|
||||
case NBD_CMD_DISC:
|
||||
return;
|
||||
default:
|
||||
FAIL("invalid cmd: %d", ntohl(request.type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enableMSD(int enabled) {
|
||||
#ifndef X86
|
||||
LOG("%sable MSD", enabled ? "en" : "dis");
|
||||
if (enabled)
|
||||
system("/opt/msdon.sh");
|
||||
else
|
||||
system("/opt/msdoff.sh");
|
||||
if (!enabled && nbd)
|
||||
nbd_ioctl(BLKFLSBUF, 0);
|
||||
#else
|
||||
LOG("fake enable MSD: %d", enabled);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#ifndef X86
|
||||
daemon(0, 1);
|
||||
#endif
|
||||
|
||||
if (argc > 1)
|
||||
dev_file = argv[1];
|
||||
|
||||
for (;;) {
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
runNBD();
|
||||
return 0;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
enableMSD(1);
|
||||
|
||||
int wstatus = 0;
|
||||
waitpid(child, &wstatus, 0);
|
||||
enableMSD(0); // force "eject"
|
||||
|
||||
if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
|
||||
LOG("abnormal child return, %d, exit: %d, signal: %d", child,
|
||||
WIFEXITED(wstatus) ? WEXITSTATUS(wstatus) : -1,
|
||||
WIFSIGNALED(wstatus) ? WTERMSIG(wstatus) : -1);
|
||||
sleep(5);
|
||||
} else {
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef UF2_H
|
||||
#define UF2_H 1
|
||||
|
||||
#include "uf2format.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef INDEX_URL
|
||||
#define INDEX_URL "https://www.pxt.io/"
|
||||
#endif
|
||||
|
||||
#define UF2_VERSION_BASE "v0.1.0"
|
||||
|
||||
// needs to be more than ~4200 and less than ~65000 (to force FAT16)
|
||||
#define NUM_FAT_BLOCKS 65000
|
||||
|
||||
#define UF2_VERSION UF2_VERSION_BASE " F"
|
||||
|
||||
//! Static block size for all memories
|
||||
#define UDI_MSC_BLOCK_SIZE 512L
|
||||
|
||||
void read_block(uint32_t block_no, uint8_t *data);
|
||||
|
||||
void write_block(uint32_t block_no, uint8_t *data);
|
||||
|
||||
#define CONCAT_1(a, b) a##b
|
||||
#define CONCAT_0(a, b) CONCAT_1(a, b)
|
||||
#define STATIC_ASSERT(e) enum { CONCAT_0(_static_assert_, __LINE__) = 1 / ((e) ? 1 : 0) }
|
||||
|
||||
extern const char infoUf2File[];
|
||||
|
||||
void readAll(int fd, void *dst, uint32_t length);
|
||||
|
||||
STATIC_ASSERT(sizeof(UF2_Block) == 512);
|
||||
|
||||
void mylog(const char *fmt, ...);
|
||||
|
||||
#define FAIL(args...) \
|
||||
do { \
|
||||
mylog("<4>" args); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
#define LOG mylog
|
||||
|
||||
#endif
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef UF2FORMAT_H
|
||||
#define UF2FORMAT_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// All entries are little endian.
|
||||
|
||||
// if you increase that, you will also need to update the linker script file
|
||||
#define APP_START_ADDRESS 0x00002000
|
||||
|
||||
#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n"
|
||||
#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected
|
||||
#define UF2_MAGIC_END 0x0AB16F30UL // Ditto
|
||||
|
||||
// If set, the block is "comment" and should not be flashed to the device
|
||||
#define UF2_FLAG_NOFLASH 0x00000001
|
||||
#define UF2_FLAG_FILE 0x00001000
|
||||
#define UF2_FILENAME_MAX 150
|
||||
#define UF2_MAX_PAYLOAD (476 - 10) // leaving some space for filename
|
||||
// for this bootloader
|
||||
#define UF2_MAX_FILESIZE (64 * 1024 * 1024)
|
||||
|
||||
typedef struct {
|
||||
// 32 byte header
|
||||
uint32_t magicStart0;
|
||||
uint32_t magicStart1;
|
||||
uint32_t flags;
|
||||
uint32_t targetAddr;
|
||||
uint32_t payloadSize;
|
||||
uint32_t blockNo;
|
||||
uint32_t numBlocks;
|
||||
uint32_t fileSize;
|
||||
|
||||
// raw data, followed by filename (NUL-terminated) at payloadSize
|
||||
uint8_t data[476];
|
||||
|
||||
// store magic also at the end to limit damage from partial block reads
|
||||
uint32_t magicEnd;
|
||||
} UF2_Block;
|
||||
|
||||
static inline bool is_uf2_block(void *data) {
|
||||
UF2_Block *bl = (UF2_Block *)data;
|
||||
return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 &&
|
||||
bl->magicEnd == UF2_MAGIC_END;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef UF2_HID_H
|
||||
#define UF2_HID_H 1
|
||||
|
||||
#define HF2_CMD_BININFO 0x0001
|
||||
// no arguments
|
||||
#define HF2_MODE_BOOTLOADER 0x01
|
||||
#define HF2_MODE_USERSPACE 0x02
|
||||
struct HF2_BININFO_Result {
|
||||
uint32_t mode;
|
||||
uint32_t flash_page_size;
|
||||
uint32_t flash_num_pages;
|
||||
uint32_t max_message_size;
|
||||
};
|
||||
|
||||
#define HF2_CMD_INFO 0x0002
|
||||
// no arguments
|
||||
// results is utf8 character array
|
||||
|
||||
#define HF2_CMD_RESET_INTO_APP 0x0003
|
||||
// no arguments, no result
|
||||
|
||||
#define HF2_CMD_RESET_INTO_BOOTLOADER 0x0004
|
||||
// no arguments, no result
|
||||
|
||||
#define HF2_CMD_START_FLASH 0x0005
|
||||
// no arguments, no result
|
||||
|
||||
#define HF2_CMD_WRITE_FLASH_PAGE 0x0006
|
||||
struct HF2_WRITE_FLASH_PAGE_Command {
|
||||
uint32_t target_addr;
|
||||
uint32_t data[0];
|
||||
};
|
||||
// no result
|
||||
|
||||
#define HF2_CMD_CHKSUM_PAGES 0x0007
|
||||
struct HF2_CHKSUM_PAGES_Command {
|
||||
uint32_t target_addr;
|
||||
uint32_t num_pages;
|
||||
};
|
||||
struct HF2_CHKSUM_PAGES_Result {
|
||||
uint16_t chksums[0 /* num_pages */];
|
||||
};
|
||||
|
||||
#define HF2_CMD_READ_WORDS 0x0008
|
||||
struct HF2_READ_WORDS_Command {
|
||||
uint32_t target_addr;
|
||||
uint32_t num_words;
|
||||
};
|
||||
struct HF2_READ_WORDS_Result {
|
||||
uint32_t words[0 /* num_words */];
|
||||
};
|
||||
|
||||
#define HF2_CMD_WRITE_WORDS 0x0009
|
||||
struct HF2_WRITE_WORDS_Command {
|
||||
uint32_t target_addr;
|
||||
uint32_t num_words;
|
||||
uint32_t words[0 /* num_words */];
|
||||
};
|
||||
// no result
|
||||
|
||||
#define HF2_CMD_DMESG 0x0010
|
||||
// no arguments
|
||||
// results is utf8 character array
|
||||
|
||||
typedef struct {
|
||||
uint32_t command_id;
|
||||
uint16_t tag;
|
||||
uint8_t reserved0;
|
||||
uint8_t reserved1;
|
||||
|
||||
union {
|
||||
struct HF2_WRITE_FLASH_PAGE_Command write_flash_page;
|
||||
struct HF2_WRITE_WORDS_Command write_words;
|
||||
struct HF2_READ_WORDS_Command read_words;
|
||||
struct HF2_CHKSUM_PAGES_Command chksum_pages;
|
||||
};
|
||||
} HF2_Command;
|
||||
|
||||
typedef struct {
|
||||
uint16_t tag;
|
||||
union {
|
||||
struct {
|
||||
uint8_t status;
|
||||
uint8_t status_info;
|
||||
};
|
||||
uint16_t status16;
|
||||
};
|
||||
union {
|
||||
struct HF2_BININFO_Result bininfo;
|
||||
uint8_t data8[0];
|
||||
uint16_t data16[0];
|
||||
uint32_t data32[0];
|
||||
};
|
||||
} HF2_Response;
|
||||
|
||||
#define HF2_FLAG_SERIAL_OUT 0x80
|
||||
#define HF2_FLAG_SERIAL_ERR 0xC0
|
||||
#define HF2_FLAG_CMDPKT_LAST 0x40
|
||||
#define HF2_FLAG_CMDPKT_BODY 0x00
|
||||
#define HF2_FLAG_MASK 0xC0
|
||||
#define HF2_SIZE_MASK 63
|
||||
|
||||
#define HF2_STATUS_OK 0x00
|
||||
#define HF2_STATUS_INVALID_CMD 0x01
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче