From 3bb656625b94b7515c7577d58deaae07de159101 Mon Sep 17 00:00:00 2001 From: mojyack Date: Fri, 19 Dec 2025 20:45:16 +0900 Subject: [PATCH] usb: add usb iAP driver add class driver source files. also register iap audio sink. usbstack/iap/libiap directory is imported from libiap. Change-Id: I776c5caec33fe9efadc448e2e3b37d500bf19c9f --- firmware/SOURCES | 14 + firmware/export/config.h | 4 + firmware/export/iap-usb.h | 39 + firmware/export/pcm_sink.h | 5 + firmware/export/usb.h | 3 + firmware/pcm.c | 7 + firmware/usb.c | 6 + firmware/usbstack/iap/audio.c | 268 ++++ firmware/usbstack/iap/audio.h | 28 + firmware/usbstack/iap/buffer.c | 47 + firmware/usbstack/iap/buffer.h | 30 + firmware/usbstack/iap/debug.c | 89 ++ firmware/usbstack/iap/debug.h | 42 + firmware/usbstack/iap/libiap-sync.sh | 17 + firmware/usbstack/iap/libiap/HEAD | 1 + firmware/usbstack/iap/libiap/LICENSE | 21 + firmware/usbstack/iap/libiap/README.rockbox | 5 + firmware/usbstack/iap/libiap/bool.h | 5 + firmware/usbstack/iap/libiap/constants.h | 11 + firmware/usbstack/iap/libiap/context.h | 107 ++ firmware/usbstack/iap/libiap/datetime.h | 11 + firmware/usbstack/iap/libiap/debug.c | 1052 +++++++++++++ firmware/usbstack/iap/libiap/endian.h | 28 + .../usbstack/iap/libiap/fid-token-values.c | 141 ++ firmware/usbstack/iap/libiap/hid.c | 181 +++ firmware/usbstack/iap/libiap/iap.c | 1361 +++++++++++++++++ firmware/usbstack/iap/libiap/iap.h | 55 + firmware/usbstack/iap/libiap/macros.h | 27 + firmware/usbstack/iap/libiap/notification.c | 200 +++ firmware/usbstack/iap/libiap/notification.h | 20 + firmware/usbstack/iap/libiap/pack-util.h | 41 + firmware/usbstack/iap/libiap/platform.h | 116 ++ firmware/usbstack/iap/libiap/span.c | 46 + firmware/usbstack/iap/libiap/span.h | 32 + firmware/usbstack/iap/libiap/spec/hid.h | 14 + firmware/usbstack/iap/libiap/spec/iap.h | 54 + .../libiap/spec/lingoes/accessory-equalizer.h | 18 + .../accessory-equalizer/current-eq-index.h | 10 + .../accessory-equalizer/eq-index-name.h | 11 + .../accessory-equalizer/eq-setting-count.h | 6 + .../iap/libiap/spec/lingoes/accessory-power.h | 8 + .../iap/libiap/spec/lingoes/digital-audio.h | 16 + .../accessory-sample-rate-caps.h | 8 + .../lingoes/digital-audio/set-video-delay.h | 6 + .../track-new-audio-attributes.h | 8 + .../iap/libiap/spec/lingoes/display-remote.h | 56 + .../lingoes/display-remote/artwork-formats.h | 21 + .../display-remote/current-eq-profile-index.h | 11 + .../lingoes/display-remote/genius-playlist.h | 10 + .../display-remote/indexed-eq-profile-name.h | 12 + .../indexed-playing-track-info.h | 85 + .../spec/lingoes/display-remote/ipod-state.h | 204 +++ .../lingoes/display-remote/num-eq-profiles.h | 6 + .../display-remote/num-playing-tracks.h | 6 + .../spec/lingoes/display-remote/play-status.h | 9 + .../display-remote/power-battery-state.h | 7 + .../display-remote/remote-event-status.h | 6 + .../set-current-playing-track.h | 6 + .../display-remote/track-artwork-data.h | 26 + .../display-remote/track-artwork-times.h | 15 + .../libiap/spec/lingoes/extended-interface.h | 109 ++ .../lingoes/extended-interface/artwork-data.h | 58 + .../extended-interface/artwork-times.h | 48 + .../extended-interface/audiobook-speed.h | 11 + .../color-display-image-limits.h | 16 + .../current-playing-track-chapter.h | 30 + .../current-playing-track-index.h | 6 + .../lingoes/extended-interface/database.h | 42 + .../extended-interface/db-itunes-info.h | 42 + .../db-selection-hierarchy.h | 13 + .../extended-interface/genius-playlist.h | 23 + .../indexed-playing-track-info.h | 81 + .../indexed-playing-track-string.h | 20 + .../lingoes/extended-interface/ipod-ack.h | 7 + .../mono-display-image-limits.h | 10 + .../lingoes/extended-interface/play-control.h | 25 + .../play-current-selection.h | 8 + .../play-status-change-notification.h | 137 ++ .../lingoes/extended-interface/play-status.h | 15 + .../extended-interface/playlist-info.h | 18 + .../spec/lingoes/extended-interface/repeat.h | 13 + .../extended-interface/set-display-image.h | 18 + .../spec/lingoes/extended-interface/shuffle.h | 13 + .../lingoes/extended-interface/track-info.h | 59 + .../lingoes/extended-interface/uid-list.h | 15 + .../iap/libiap/spec/lingoes/general.h | 112 ++ .../iap/libiap/spec/lingoes/general/acc-ack.h | 7 + .../spec/lingoes/general/acc-auth-info.h | 27 + .../spec/lingoes/general/acc-auth-sig.h | 22 + .../libiap/spec/lingoes/general/acc-info.h | 83 + .../lingoes/general/acc-status-notification.h | 18 + .../spec/lingoes/general/cancel-command.h | 8 + .../spec/lingoes/general/data-session.h | 11 + .../spec/lingoes/general/data-transfer.h | 12 + .../libiap/spec/lingoes/general/end-idps.h | 33 + .../spec/lingoes/general/event-notification.h | 31 + .../lingoes/general/extended-interface-mode.h | 8 + .../spec/lingoes/general/fid-token-values.h | 58 + .../general/fid-token-values/acc-caps.h | 31 + .../acc-digital-audio-sample-rates.h | 16 + .../acc-digital-audio-video-delay.h | 16 + .../general/fid-token-values/acc-info.h | 33 + .../fid-token-values/bundle-seed-id-pref.h | 16 + .../fid-token-values/ea-protocol-metadata.h | 23 + .../general/fid-token-values/ea-protocol.h | 20 + .../general/fid-token-values/identify.h | 28 + .../fid-token-values/ipod-preference.h | 21 + .../fid-token-values/microphone-caps.h | 24 + .../general/fid-token-values/screen-info.h | 27 + .../lingoes/general/identify-device-lingoes.h | 30 + .../libiap/spec/lingoes/general/identify.h | 15 + .../libiap/spec/lingoes/general/ipod-ack.h | 57 + .../spec/lingoes/general/ipod-auth-info.h | 14 + .../spec/lingoes/general/ipod-auth-sig.h | 17 + .../libiap/spec/lingoes/general/ipod-name.h | 8 + .../spec/lingoes/general/ipod-notification.h | 120 ++ .../lingoes/general/ipod-options-for-lingo.h | 114 ++ .../spec/lingoes/general/ipod-options.h | 11 + .../spec/lingoes/general/ipod-preferences.h | 95 ++ .../spec/lingoes/general/ipod-serial-num.h | 6 + .../lingoes/general/ipod-software-version.h | 8 + .../lingoes/general/lingo-protocol-version.h | 12 + .../spec/lingoes/general/localization-info.h | 16 + .../general/notify-ipod-state-change.h | 13 + .../general/now-playing-app-bundle-name.h | 8 + .../general/request-application-launch.h | 9 + .../lingoes/general/set-available-current.h | 6 + .../set-internal-battery-charging-state.h | 11 + .../general/transport-max-payload-size.h | 6 + .../iap/libiap/spec/lingoes/general/ui-mode.h | 13 + .../lingoes/general/wifi-connection-info.h | 24 + .../iap/libiap/spec/lingoes/ipod-out.h | 14 + .../ipod-out/accessory-state-change-event.h | 18 + .../spec/lingoes/ipod-out/ipod-out-options.h | 29 + .../iap/libiap/spec/lingoes/location.h | 19 + .../iap/libiap/spec/lingoes/microphone.h | 22 + .../libiap/spec/lingoes/microphone/acc-caps.h | 14 + .../libiap/spec/lingoes/microphone/acc-ctrl.h | 22 + .../lingoes/microphone/ipod-mode-change.h | 14 + .../iap/libiap/spec/lingoes/rf-tuner.h | 69 + .../libiap/spec/lingoes/rf-tuner/acc-ack.h | 13 + .../libiap/spec/lingoes/rf-tuner/hd-data.h | 42 + .../libiap/spec/lingoes/rf-tuner/hd-program.h | 17 + .../libiap/spec/lingoes/rf-tuner/rds-data.h | 18 + .../spec/lingoes/rf-tuner/rds-notify-mask.h | 12 + .../spec/lingoes/rf-tuner/rds-ready-status.h | 18 + .../lingoes/rf-tuner/status-notification.h | 16 + .../libiap/spec/lingoes/rf-tuner/tuner-band.h | 19 + .../libiap/spec/lingoes/rf-tuner/tuner-caps.h | 42 + .../libiap/spec/lingoes/rf-tuner/tuner-ctrl.h | 18 + .../libiap/spec/lingoes/rf-tuner/tuner-freq.h | 13 + .../libiap/spec/lingoes/rf-tuner/tuner-mode.h | 22 + .../spec/lingoes/rf-tuner/tuner-seek-rssi.h | 12 + .../libiap/spec/lingoes/rf-tuner/tuner-seek.h | 33 + .../spec/lingoes/rf-tuner/tuner-status.h | 17 + .../iap/libiap/spec/lingoes/simple-remote.h | 39 + .../lingoes/simple-remote/button-status.h | 98 ++ .../simple-remote/camera-button-status.h | 11 + .../current-voice-over-item-property.h | 72 + .../libiap/spec/lingoes/simple-remote/hid.h | 75 + .../simple-remote/ipod-out-button-status.h | 26 + .../simple-remote/radio-button-status.h | 11 + .../simple-remote/rotation-input-status.h | 33 + .../simple-remote/set-voice-over-context.h | 17 + .../lingoes/simple-remote/voice-over-event.h | 85 + .../simple-remote/voice-over-parameter.h | 28 + .../libiap/spec/lingoes/sound-check-state.h | 11 + .../usbstack/iap/libiap/spec/lingoes/sports.h | 25 + .../spec/lingoes/sports/accessory-caps.h | 11 + .../spec/lingoes/sports/accessory-version.h | 7 + .../libiap/spec/lingoes/sports/ipod-caps.h | 12 + .../libiap/spec/lingoes/sports/user-data.h | 73 + .../libiap/spec/lingoes/sports/user-index.h | 6 + .../iap/libiap/spec/lingoes/storage.h | 27 + .../iap/libiap/spec/lingoes/storage/acc-ack.h | 8 + .../spec/lingoes/storage/accessory-caps.h | 11 + .../libiap/spec/lingoes/storage/ipod-ack.h | 10 + .../libiap/spec/lingoes/storage/ipod-caps.h | 13 + .../spec/lingoes/storage/ipod-file-handle.h | 30 + .../spec/lingoes/storage/ipod-free-space.h | 8 + .../lingoes/storage/write-ipod-file-data.h | 10 + .../iap/libiap/spec/lingoes/usb-host-mode.h | 14 + .../spec/lingoes/usb-host-mode/usb-mode.h | 22 + firmware/usbstack/iap/libiap/time.h | 11 + firmware/usbstack/iap/libiap/unaligned.h | 7 + firmware/usbstack/iap/macros.h | 44 + firmware/usbstack/iap/notification.c | 111 ++ firmware/usbstack/iap/platform-macros.h | 35 + firmware/usbstack/iap/platform.c | 482 ++++++ firmware/usbstack/iap/platform.h | 49 + firmware/usbstack/usb_core.c | 34 +- firmware/usbstack/usb_iap.c | 679 ++++++++ firmware/usbstack/usb_iap.h | 39 + 193 files changed, 9650 insertions(+), 1 deletion(-) create mode 100644 firmware/export/iap-usb.h create mode 100644 firmware/usbstack/iap/audio.c create mode 100644 firmware/usbstack/iap/audio.h create mode 100644 firmware/usbstack/iap/buffer.c create mode 100644 firmware/usbstack/iap/buffer.h create mode 100644 firmware/usbstack/iap/debug.c create mode 100644 firmware/usbstack/iap/debug.h create mode 100755 firmware/usbstack/iap/libiap-sync.sh create mode 100644 firmware/usbstack/iap/libiap/HEAD create mode 100644 firmware/usbstack/iap/libiap/LICENSE create mode 100644 firmware/usbstack/iap/libiap/README.rockbox create mode 100644 firmware/usbstack/iap/libiap/bool.h create mode 100644 firmware/usbstack/iap/libiap/constants.h create mode 100644 firmware/usbstack/iap/libiap/context.h create mode 100644 firmware/usbstack/iap/libiap/datetime.h create mode 100644 firmware/usbstack/iap/libiap/debug.c create mode 100644 firmware/usbstack/iap/libiap/endian.h create mode 100644 firmware/usbstack/iap/libiap/fid-token-values.c create mode 100644 firmware/usbstack/iap/libiap/hid.c create mode 100644 firmware/usbstack/iap/libiap/iap.c create mode 100644 firmware/usbstack/iap/libiap/iap.h create mode 100644 firmware/usbstack/iap/libiap/macros.h create mode 100644 firmware/usbstack/iap/libiap/notification.c create mode 100644 firmware/usbstack/iap/libiap/notification.h create mode 100644 firmware/usbstack/iap/libiap/pack-util.h create mode 100644 firmware/usbstack/iap/libiap/platform.h create mode 100644 firmware/usbstack/iap/libiap/span.c create mode 100644 firmware/usbstack/iap/libiap/span.h create mode 100644 firmware/usbstack/iap/libiap/spec/hid.h create mode 100644 firmware/usbstack/iap/libiap/spec/iap.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/location.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/microphone.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h create mode 100644 firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h create mode 100644 firmware/usbstack/iap/libiap/time.h create mode 100644 firmware/usbstack/iap/libiap/unaligned.h create mode 100644 firmware/usbstack/iap/macros.h create mode 100644 firmware/usbstack/iap/notification.c create mode 100644 firmware/usbstack/iap/platform-macros.h create mode 100644 firmware/usbstack/iap/platform.c create mode 100644 firmware/usbstack/iap/platform.h create mode 100644 firmware/usbstack/usb_iap.c create mode 100644 firmware/usbstack/usb_iap.h diff --git a/firmware/SOURCES b/firmware/SOURCES index 888d296fd0..b80dc1a272 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -957,6 +957,20 @@ usbstack/usb_charging_only.c #ifdef USB_ENABLE_HID usbstack/usb_hid.c #endif +#ifdef USB_ENABLE_IAP +usbstack/iap/audio.c +usbstack/iap/buffer.c +usbstack/iap/debug.c +usbstack/iap/libiap/debug.c +usbstack/iap/libiap/fid-token-values.c +usbstack/iap/libiap/hid.c +usbstack/iap/libiap/iap.c +usbstack/iap/libiap/notification.c +usbstack/iap/libiap/span.c +usbstack/iap/notification.c +usbstack/iap/platform.c +usbstack/usb_iap.c +#endif #if CONFIG_USBOTG == USBOTG_M66591 drivers/m66591.c #elif CONFIG_USBOTG == USBOTG_ARC diff --git a/firmware/export/config.h b/firmware/export/config.h index bcea581141..4b0a16f0f1 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -1400,6 +1400,10 @@ Lyre prototype 1 */ #define USB_ENABLE_IAP #endif +#if defined(USB_ENABLE_IAP) +#define HAVE_MULTIMEDIA_KEYS +#endif + #endif /* BOOTLOADER */ #endif /* HAVE_USBSTACK */ diff --git a/firmware/export/iap-usb.h b/firmware/export/iap-usb.h new file mode 100644 index 0000000000..24f837981b --- /dev/null +++ b/firmware/export/iap-usb.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +#include "config.h" + +#ifdef USB_ENABLE_IAP +/* usbstack/iap/notification.c */ +void iap_on_track_time_position(uint32_t pos_ms); +void iap_on_track_playback_index(uint32_t index, bool track_ready); +void iap_on_tracks_count(uint32_t count); +void iap_on_play_status(int status /* AUDIO_STATUS_* */); +void iap_on_volume(int volume); +void iap_on_shuffle_state(bool state); +void iap_on_repeat_state(int state); +#else +static inline void iap_on_track_time_position(uint32_t pos_ms) { + (void)pos_ms; +} +static inline void iap_on_track_playback_index(uint32_t index, bool track_ready) { + (void)index; + (void)track_ready; +} +static inline void iap_on_tracks_count(uint32_t count) { + (void)count; +} +static inline void iap_on_play_status(int status) { + (void)status; +} +static inline void iap_on_volume(int volume) { + (void)volume; +} +static inline void iap_on_shuffle_state(bool state) { + (void)state; +} +static inline void iap_on_repeat_state(int state) { + (void)state; +} +#endif diff --git a/firmware/export/pcm_sink.h b/firmware/export/pcm_sink.h index 24a1962189..a4945d3534 100644 --- a/firmware/export/pcm_sink.h +++ b/firmware/export/pcm_sink.h @@ -21,6 +21,8 @@ #include #include +#include "config.h" + struct pcm_sink_caps { const unsigned long* samprs; uint16_t num_samprs; @@ -52,6 +54,9 @@ struct pcm_sink { enum pcm_sink_ids { PCM_SINK_BUILTIN = 0, +#ifdef USB_ENABLE_IAP + PCM_SINK_IAP, +#endif PCM_SINK_NUM }; diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 6d9784f93e..ed76ade521 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -172,6 +172,9 @@ enum { #endif #ifdef USB_ENABLE_AUDIO USB_DRIVER_AUDIO, +#endif +#ifdef USB_ENABLE_IAP + USB_DRIVER_IAP, #endif USB_NUM_DRIVERS }; diff --git a/firmware/pcm.c b/firmware/pcm.c index bb4fd588b5..be5b47673a 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -77,8 +77,15 @@ * */ +#ifdef USB_ENABLE_IAP +extern struct pcm_sink iap_pcm_sink; +#endif + static struct pcm_sink* sinks[PCM_SINK_NUM] = { [PCM_SINK_BUILTIN] = &builtin_pcm_sink, +#ifdef USB_ENABLE_IAP + [PCM_SINK_IAP] = &iap_pcm_sink, +#endif }; static enum pcm_sink_ids cur_sink = PCM_SINK_BUILTIN; diff --git a/firmware/usb.c b/firmware/usb.c index 2645d3b290..c3e35d22b3 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -214,6 +214,9 @@ static inline void usb_configure_drivers(int for_state) #ifdef USB_ENABLE_AUDIO usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 2)); // while "always" or "only in charge-only mode" #endif /* USB_ENABLE_AUDIO */ +#ifdef USB_ENABLE_IAP + usb_core_enable_driver(USB_DRIVER_IAP, false); +#endif #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); @@ -233,6 +236,9 @@ static inline void usb_configure_drivers(int for_state) #ifdef USB_ENABLE_AUDIO usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 3)); // while "always" or "only in mass-storage mode" #endif /* USB_ENABLE_AUDIO */ +#ifdef USB_ENABLE_IAP + usb_core_enable_driver(USB_DRIVER_IAP, true); +#endif #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); #endif diff --git a/firmware/usbstack/iap/audio.c b/firmware/usbstack/iap/audio.c new file mode 100644 index 0000000000..d01b133d69 --- /dev/null +++ b/firmware/usbstack/iap/audio.c @@ -0,0 +1,268 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "core_alloc.h" +#include "pcm-internal.h" +#include "pcm_sampr.h" +#include "pcm_sink.h" +#include "system.h" +#include "usb_drv.h" + +#include "../usb_iap.h" +#include "buffer.h" +#include "libiap/iap.h" +#include "macros.h" +#include "platform.h" + +static const unsigned long samprs[] = { + SAMPR_48, + SAMPR_44, +}; + +struct StagingBuffer { + struct IAPAllocResult buf; + uint16_t cursor; +}; +static struct StagingBuffer staging_buffers[USB_BATCH_SLOTS + 1]; /* +1 for silent buffer */ +static int staging_buffer_index; + +#define zero_buffer (staging_buffers[USB_BATCH_SLOTS]) + +static const uint8_t* pulled_buf; +static size_t pulled_buf_size; +static size_t pulled_buf_cursor; +static int8_t set_freq; /* requested freq from rockbox */ +static int8_t cur_freq; /* requested freq from accessory */ +static uint8_t packet_count; + +static bool enabled; +static bool exhausted; +static bool track_attrs_sent; + +extern struct pcm_sink iap_pcm_sink; + +static void sink_set_freq(uint16_t freq) { + LOG("freq=%d", freq); + + track_attrs_sent = true; + + set_freq = freq; + + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_select_sampr(ctx, samprs[freq]), ); + _iap_release_ctx(); +} + +static size_t calc_packet_size(uint8_t cur_sampr, uint8_t packet_count) { + /* packet size calculation + * (4 = sizeof(int16_t) * channels) + * (1000 = usb frames per second) + * 48000Hz: + * 48000 * 4 / 1000 = 192.0 + * => 192 + ... + * 44100Hz: + * 44100 * 4 / 1000 = 176.4 + * => (9 * 176 + 180) + (... + */ + if(cur_sampr == 0) { + /*48k*/ + return 192; + } else { + /*44.1k*/ + return packet_count % 10 == 0 ? 180 : 176; + } +} + +static void batch_get_more(const void** ptr, size_t* len) { + const size_t packet_size = calc_packet_size(cur_freq, packet_count); +#if 0 + const int cur_frame = usb_drv_get_frame_number(); + LOG("ex=%d set=%d cur=%d", exhausted, set_freq, cur_freq); +#endif + +start: + if(exhausted || cur_freq != set_freq) { + *ptr = zero_buffer.buf.ptr; + *len = packet_size; + packet_count += 1; + return; + } + + if(pulled_buf_cursor == pulled_buf_size) { + /* run out of previously pulled data. + * let's start filling them, by requesting new data from upstream */ + if(!pcm_play_dma_complete_callback(PCM_DMAST_OK, (const void**)&pulled_buf, &pulled_buf_size)) { + /* no more data, but we have to keep sending something as long as the audio stream interface is enabled */ + exhausted = true; + goto start; + } + + /* pushing_{buf,buf_size} are filled. reset cursor and continue filling */ + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + pulled_buf_cursor = 0; + } + + /* fill this single packet */ + struct StagingBuffer* stage = &staging_buffers[staging_buffer_index]; + const size_t copy = MIN(packet_size - stage->cursor, pulled_buf_size - pulled_buf_cursor); + memcpy(stage->buf.ptr + stage->cursor, pulled_buf + pulled_buf_cursor, copy); + pulled_buf_cursor += copy; + stage->cursor += copy; +#define AUDIO_STAT 0 +#if AUDIO_STAT == 1 + static int last_hz; + static int sample; +#endif + if(stage->cursor == packet_size) { + *ptr = stage->buf.ptr; + *len = stage->cursor; + stage->cursor = 0; + staging_buffer_index = (staging_buffer_index + 1) % USB_BATCH_SLOTS; + packet_count += 1; +#if AUDIO_STAT == 1 + sample += packet_size / 4; + if(current_tick >= last_hz + HZ) { + logf("pushed %d %d", packet_size, sample); + sample = 0; + last_hz = current_tick; + } +#endif + } else { /* pushing_buf_cursor == pushing_buf_size */ + goto start; + } +} + +static void sink_play(const void* addr, size_t size) { + LOG("play"); + + pulled_buf = addr; + pulled_buf_size = size; + pulled_buf_cursor = 0; + exhausted = false; + + /* resolve pending play request before sending TrackNewAudioAttributes (by sink_set_freq). + * some accessories will get confused when receiving it while waiting for an ack */ + struct IAPContext* ctx = _iap_acquire_ctx(true); + struct Platform* plt = ctx->platform; + if(plt->control_pending) { + check_act(iap_control_response(ctx, plt->pending_control, true), ); + plt->control_pending = false; + } + _iap_release_ctx(); + + /* rockbox only calls set_freq when changes occur, but we must call + * set_freq(and iap_select_sampr) at least once per connection. */ + if(!track_attrs_sent) { + sink_set_freq(iap_pcm_sink.configured_freq); + } +} + +static void sink_stop(void) { + LOG("stop"); + /* we don't call usb_drv_batch_stop() here, + * because we need to send something, even not playing. */ +} + +static void sink_nop(void) { +} + +struct pcm_sink iap_pcm_sink = { + .caps = { + .samprs = samprs, + .num_samprs = ARRAYLEN(samprs), + .default_freq = 0, + }, + .ops = { + .init = sink_nop, + .postinit = sink_nop, + .set_freq = sink_set_freq, + .lock = sink_nop, + .unlock = sink_nop, + .play = sink_play, + .stop = sink_stop, + }, +}; + +bool iap_audio_init(void) { + check_act(usb_drv_batch_init(AS_EP_IN, batch_get_more) == 0, return false); + + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + check_act(iap_alloc_usb_send_buffer(AS_PACKET_SIZE, &staging_buffers[i].buf), goto error); + staging_buffers[i].cursor = 0; + } + staging_buffer_index = 0; + + memset(zero_buffer.buf.ptr, 0, AS_PACKET_SIZE); + + set_freq = -1; + cur_freq = -2; /* something <0 && !=set_freq */ + enabled = false; + exhausted = true; + track_attrs_sent = false; + packet_count = 0; + + return true; + +error: + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + if(staging_buffers[i].buf.ptr != NULL) { + core_free(staging_buffers[i].buf.handle); + staging_buffers[i].buf.ptr = NULL; + } + } + return false; +} + +bool iap_audio_deinit(void) { + check_act(usb_drv_batch_deinit() == 0, ); + for(size_t i = 0; i < ARRAYLEN(staging_buffers); i += 1) { + core_free(staging_buffers[i].buf.handle); + staging_buffers[i].buf.ptr = NULL; + } + return true; +} + +bool iap_audio_enable(void) { + LOG("enabled=%d", enabled); + check_act(enabled || usb_drv_batch_start() == 0, return false); + enabled = true; + return true; +} + +bool iap_audio_disable(void) { + LOG("enabled=%d", enabled); + check_act(!enabled || usb_drv_batch_stop() == 0, return false); + enabled = false; + return true; +} + +bool iap_audio_set_sampr(uint32_t sampr) { + uint16_t freq = 0; + for(; freq < ARRAYLEN(samprs); freq += 1) { + if(samprs[freq] == sampr) { + break; + } + } + check_act(freq < ARRAYLEN(samprs), return false); + cur_freq = freq; + + LOG("sampr=%lu, set_freq=%d cur_freq=%d", sampr, set_freq, cur_freq); + + return true; +} diff --git a/firmware/usbstack/iap/audio.h b/firmware/usbstack/iap/audio.h new file mode 100644 index 0000000000..334ce41ebb --- /dev/null +++ b/firmware/usbstack/iap/audio.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include +#include + +bool iap_audio_init(void); +bool iap_audio_deinit(void); +bool iap_audio_enable(void); +bool iap_audio_disable(void); +bool iap_audio_set_sampr(uint32_t sampr); diff --git a/firmware/usbstack/iap/buffer.c b/firmware/usbstack/iap/buffer.c new file mode 100644 index 0000000000..32b6cbdcfd --- /dev/null +++ b/firmware/usbstack/iap/buffer.c @@ -0,0 +1,47 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "core_alloc.h" + +#include "buffer.h" +#include "macros.h" + +bool iap_alloc_buffer(size_t size, struct IAPAllocResult* result) { + int handle = core_alloc_ex(size, &buflib_ops_locked); + check_act(handle > 0, return false); + uint8_t* ptr = core_get_data(handle); + + result->handle = handle; + result->ptr = ptr; + return true; +} + +bool iap_alloc_usb_send_buffer(size_t size, struct IAPAllocResult* result) { + /* + 31 to handle worst-case misalignment */ + check_act(iap_alloc_buffer(size + 31, result), return false); + + /* align to 32 */ + result->ptr = (void*)((uintptr_t)(result->ptr + 31) & 0xffffffe0); + /* uncached */ +#if defined(UNCACHED_ADDR) && CONFIG_CPU != AS3525 + result->ptr = UNCACHED_ADDR(result->ptr); +#endif + + return true; +} diff --git a/firmware/usbstack/iap/buffer.h b/firmware/usbstack/iap/buffer.h new file mode 100644 index 0000000000..2620fe3960 --- /dev/null +++ b/firmware/usbstack/iap/buffer.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include +#include + +struct IAPAllocResult { + void* ptr; + int handle; +}; + +bool iap_alloc_buffer(size_t size, struct IAPAllocResult* result); +bool iap_alloc_usb_send_buffer(size_t size, struct IAPAllocResult* result); diff --git a/firmware/usbstack/iap/debug.c b/firmware/usbstack/iap/debug.c new file mode 100644 index 0000000000..80aa02741e --- /dev/null +++ b/firmware/usbstack/iap/debug.c @@ -0,0 +1,89 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 +#include +#include + +#include "logf.h" +#include "system.h" +#include "tick.h" + +#include "font.h" +#include "lcd.h" + +#define MAX_COLS 64 + +static int rows; +static int columns; +static unsigned count; + +static void update_color(void) { + unsigned avail = rows - 1; + if((count % (avail * 2)) > avail) { + lcd_set_drawinfo(DRMODE_SOLID, LCD_BLACK, LCD_WHITE); + } else { + lcd_set_drawinfo(DRMODE_SOLID, LCD_WHITE, LCD_BLACK); + } +} + +void iap_lcd_scatter(const char* fmt, ...) { + if(rows == 0) { + int w, h; + font_getstringsize((unsigned char*)"A", &w, &h, FONT_SYSFIXED); + columns = MIN(LCD_WIDTH / w, MAX_COLS); + rows = LCD_HEIGHT / h; + } + + va_list ap; + char buf[256]; + va_start(ap, fmt); + int len = vsnprintf(buf, sizeof(buf), (char*)fmt, ap); + va_end(ap); + + logf("%s", buf); + + lcd_set_backdrop(NULL); + lcd_setfont(FONT_SYSFIXED); + + update_color(); + lcd_putsf(0, 0, (unsigned char*)"count %u", count); + for(int i = 0; i < len;) { + char line[MAX_COLS]; + int copy = MIN(len - i, columns - 1); + memcpy(line, buf + i, copy); + memset(line + copy, ' ', columns - 1 - copy); + line[columns - 1] = '\0'; + update_color(); + lcd_puts(0, 1 + count % (rows - 1), (unsigned char*)line); + count += 1; + i += copy; + } + lcd_update(); +} + +static unsigned long timestamp_epoch; + +unsigned long iap_debug_timestamp(void) { + return current_tick - timestamp_epoch; +} + +void iap_debug_reset_timestamp(void) { + timestamp_epoch = current_tick; +} diff --git a/firmware/usbstack/iap/debug.h b/firmware/usbstack/iap/debug.h new file mode 100644 index 0000000000..034a050f7f --- /dev/null +++ b/firmware/usbstack/iap/debug.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once + +/* debug switches */ +#define DEBUG_ENABLE_INFO 0 +#define DEBUG_ENABLE_ERROR 1 +#define DEBUG_LCD_PRINT 0 +#define DEBUG_DUMP_TX 0 +#define DEBUG_DUMP_RX 0 +#define DEBUG_HEXDUMP_NOLIMIT 0 + +#if DEBUG_ENABLE_INFO == 1 || DEBUG_ENABLE_ERROR == 1 +#define LOGF_ENABLE +#include "logf.h" +#endif + +void iap_lcd_scatter(const char* fmt, ...); +unsigned long iap_debug_timestamp(void); +void iap_debug_reset_timestamp(void); + +#if DEBUG_LCD_PRINT == 1 +#undef logf +#define logf(...) iap_lcd_scatter(__VA_ARGS__) +#endif diff --git a/firmware/usbstack/iap/libiap-sync.sh b/firmware/usbstack/iap/libiap-sync.sh new file mode 100755 index 0000000000..030c29f370 --- /dev/null +++ b/firmware/usbstack/iap/libiap-sync.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +url=https://github.com/mojyack/libiap.git +src=/tmp/libiap +dst=$PWD/libiap + +if [[ ! -e $src ]]; then + git clone "$url" "$src" +else + git -C "$src" pull +fi + +rsync --recursive --delete "$src/src/iap/" "$dst/" --exclude /README.rockbox +cp "$src/LICENSE" "$dst/LICENSE" +git -C "$src" rev-parse HEAD > "$dst/HEAD" diff --git a/firmware/usbstack/iap/libiap/HEAD b/firmware/usbstack/iap/libiap/HEAD new file mode 100644 index 0000000000..30d689eb0d --- /dev/null +++ b/firmware/usbstack/iap/libiap/HEAD @@ -0,0 +1 @@ +ad06cdfc66f5db55d16b741df265269309105af3 diff --git a/firmware/usbstack/iap/libiap/LICENSE b/firmware/usbstack/iap/libiap/LICENSE new file mode 100644 index 0000000000..fb320d67e7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 mojyack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/firmware/usbstack/iap/libiap/README.rockbox b/firmware/usbstack/iap/libiap/README.rockbox new file mode 100644 index 0000000000..e6dbf7f555 --- /dev/null +++ b/firmware/usbstack/iap/libiap/README.rockbox @@ -0,0 +1,5 @@ +Files under this directory were imported from libiap(https://github.com/mojyack/libiap) except for: +- README.rockbox: this file, Rockbox specific notes +- HEAD: libiap commit hash + +To sync with the upstream, use ../libiap-sync.sh script. diff --git a/firmware/usbstack/iap/libiap/bool.h b/firmware/usbstack/iap/libiap/bool.h new file mode 100644 index 0000000000..f94595ede1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/bool.h @@ -0,0 +1,5 @@ +#pragma once + +typedef unsigned char IAPBool; +#define iap_true 1 +#define iap_false 0 diff --git a/firmware/usbstack/iap/libiap/constants.h b/firmware/usbstack/iap/libiap/constants.h new file mode 100644 index 0000000000..2dca15d05f --- /dev/null +++ b/firmware/usbstack/iap/libiap/constants.h @@ -0,0 +1,11 @@ +#pragma once + +#define HID_BUFFER_SIZE 1024 +#define SEND_BUFFER_SIZE 767 /* 0x2ff - 1: input_report_size_table_hs[0x0C].size - sizeof(report_id) */ + +enum IAPPhase { + IAPPhase_Connected = 0, /* initial state, waiting for StartIDPS */ + IAPPhase_IDPS, /* idps started */ + IAPPhase_Auth, /* idps completed, authenticating accessory */ + IAPPhase_Authed, /* authentication completed, processing requests */ +}; diff --git a/firmware/usbstack/iap/libiap/context.h b/firmware/usbstack/iap/libiap/context.h new file mode 100644 index 0000000000..f7e6ca3600 --- /dev/null +++ b/firmware/usbstack/iap/libiap/context.h @@ -0,0 +1,107 @@ +#pragma once +#include +#include + +#include "bool.h" +#include "notification.h" +#include "platform.h" + +struct IAPContext; +struct IAPSpan; + +typedef IAPBool (*IAPOnSendComplete)(struct IAPContext* ctx); + +struct IAPOpts { + /* indicate usb transport is highspeed */ + IAPBool usb_highspeed : 1; + /* needed to support some accessories which don't set correct hid report id */ + IAPBool ignore_hid_report_id : 1; + /* limit packet size while sending artworks for stability */ + IAPBool artwork_single_report : 1; + /* dump each packets */ + IAPBool enable_packet_dump : 1; +}; + +struct IAPContextButtons { + IAPBool play_pause : 1; + IAPBool volume_up : 1; + IAPBool volume_down : 1; + IAPBool next_track : 1; + IAPBool prev_track : 1; + IAPBool next_album : 1; + IAPBool prev_album : 1; + IAPBool stop : 1; + IAPBool play : 1; + IAPBool pause : 1; + IAPBool mute_toggle : 1; + IAPBool next_chapter : 1; + IAPBool prev_chapter : 1; + IAPBool next_playlist : 1; + IAPBool prev_playlist : 1; + IAPBool shuffle_advance : 1; + IAPBool repeat_advance : 1; +}; + +struct IAPActiveEvent; + +typedef IAPBool (*IAPActiveEventReadyCallback)(struct IAPContext* ctx, struct IAPActiveEvent* event); + +struct IAPActiveEvent { + IAPActiveEventReadyCallback callback; + union { + uint32_t sampr; + struct { + struct IAPPlatformPendingControl control; + IAPBool result; + } control_response; + }; +}; + +struct IAPContext { + /* set by user */ + void* platform; /* opaque to platform functions */ + struct IAPOpts opts; /* options */ + + /* iap_feed_hid_report */ + uint8_t* hid_recv_buf; + size_t hid_recv_buf_cursor; + /* _iap_send_packet, _iap_send_hid_reports */ + uint8_t* send_buf; + size_t send_buf_sending_cursor; + size_t send_buf_sending_range_begin; + size_t send_buf_sending_range_end; + /* _iap_send_next_report */ + /* for library-oriented, manageable events */ + IAPOnSendComplete on_send_complete; + /* for user-oriented, unexpectable events */ + struct IAPActiveEvent active_events[2]; + /* _iap_feed_packet */ + int32_t handling_trans_id; + /* iap.c */ + struct IAPContextButtons context_button_state; + struct IAPPlatformArtwork artwork; + size_t artwork_cursor; + uint16_t trans_id; + uint16_t artwork_chunk_index; + int32_t artwork_trans_id; + uint16_t artwork_data_command; + uint8_t artwork_data_lingo; + uint8_t trans_id_support; /* TransIDSupport */ + /* notification.c */ + /* DisplayRemote::SetRemoteEventNotification */ + uint32_t enabled_notifications_3; + uint32_t notifications_3; + /* ExtendedInterface::SetPlayStatusChangeNotification */ + uint32_t enabled_notifications_4; + uint32_t notifications_4; + /* notification data */ + struct _IAPNotifyState notification_data; + uint8_t notification_tick; + /* _iap_send_hid_reports */ + uint8_t* hid_send_staging_buf; + IAPBool send_busy : 1; + IAPBool flushing_notifications : 1; + IAPBool waiting_for_audio_attrs_ack : 1; + + uint8_t phase; /* IAPPhase */ +}; diff --git a/firmware/usbstack/iap/libiap/datetime.h b/firmware/usbstack/iap/libiap/datetime.h new file mode 100644 index 0000000000..00f0b13336 --- /dev/null +++ b/firmware/usbstack/iap/libiap/datetime.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPDateTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t seconds; +}; diff --git a/firmware/usbstack/iap/libiap/debug.c b/firmware/usbstack/iap/libiap/debug.c new file mode 100644 index 0000000000..c6386eb82a --- /dev/null +++ b/firmware/usbstack/iap/libiap/debug.c @@ -0,0 +1,1052 @@ +#include +#include + +#include "endian.h" +#include "macros.h" +#include "span.h" +#include "spec/iap.h" + +#define entry(lingo, command) [IAP##lingo##CommandID_##command] = #command + +static const char* strs_general[] = { + entry(General, RequestIdentify), + entry(General, Identify), + entry(General, IPodAck), + entry(General, RequestExtendedInterfaceMode), + entry(General, ReturnExtendedInterfaceMode), + entry(General, EnterExtendedInterfaceMode), + entry(General, ExitExtendedInterfaceMode), + entry(General, RequestIPodName), + entry(General, ReturnIPodName), + entry(General, RequestIPodSoftwareVersion), + entry(General, ReturnIPodSoftwareVersion), + entry(General, RequestIPodSerialNum), + entry(General, ReturnIPodSerialNum), + entry(General, RequestIPodModelNum), + entry(General, ReturnIPodModelNum), + entry(General, RequestLingoProtocolVersion), + entry(General, ReturnLingoProtocolVersion), + entry(General, RequestTransportMaxPayloadSize), + entry(General, ReturnTransportMaxPayloadSize), + entry(General, IdentifyDeviceLingoes), + entry(General, GetAccessoryAuthenticationInfo), + entry(General, RetAccessoryAuthenticationInfo), + entry(General, AckAccessoryAuthenticationInfo), + entry(General, GetAccessoryAuthenticationSignature), + entry(General, RetAccessoryAuthenticationSignature), + entry(General, AckAccessoryAuthenticationStatus), + entry(General, GetIPodAuthenticationInfo), + entry(General, RetIPodAuthenticationInfo), + entry(General, AckIPodAuthenticationInfo), + entry(General, GetIPodAuthenticationSignature), + entry(General, RetIPodAuthenticationSignature), + entry(General, AckIPodAuthenticationStatus), + entry(General, NotifyIPodStateChange), + entry(General, GetIPodOptions), + entry(General, RetIPodOptions), + entry(General, GetAccessoryInfo), + entry(General, RetAccessoryInfo), + entry(General, GetIPodPreferences), + entry(General, RetIPodPreferences), + entry(General, SetIPodPreferences), + entry(General, GetUIMode), + entry(General, RetUIMode), + entry(General, SetUIMode), + entry(General, StartIDPS), + entry(General, SetFIDTokenValues), + entry(General, AckFIDTokenValues), + entry(General, EndIDPS), + entry(General, IDPSStatus), + entry(General, OpenDataSessionForProtocol), + entry(General, CloseDataSession), + entry(General, AccessoryAck), + entry(General, AccessoryDataTransfer), + entry(General, IPodDataTransfer), + entry(General, SetAccessoryStatusNotification), + entry(General, RetAccessoryStatusNotification), + entry(General, AccessoryStatusNotification), + entry(General, SetEventNotification), + entry(General, IPodNotification), + entry(General, GetIPodOptionsForLingo), + entry(General, RetIPodOptionsForLingo), + entry(General, GetEventNotification), + entry(General, RetEventNotification), + entry(General, GetSupportedEventNotification), + entry(General, CancelCommand), + entry(General, RetSupportedEventNotification), + entry(General, SetAvailableCurrent), + entry(General, SetInternalBatteryChargingState), + entry(General, RequestApplicationLaunch), + entry(General, GetNowPlayingApplicationBundleName), + entry(General, RetNowPlayingApplicationBundleName), + entry(General, GetLocalizationInfo), + entry(General, RetLocalizationInfo), + entry(General, RequestWiFiConnectionInfo), + entry(General, WiFiConnectionInfo), +}; +static const char* strs_simple[] = { + entry(SimpleRemote, ContextButtonStatus), + entry(SimpleRemote, IPodAck), + entry(SimpleRemote, ImageButtonStatus), + entry(SimpleRemote, VideoButtonStatus), + entry(SimpleRemote, AudioButtonStatus), + entry(SimpleRemote, IPodOutButtonStatus), + entry(SimpleRemote, RotationInputStatus), + entry(SimpleRemote, RadioButtonStatus), + entry(SimpleRemote, CameraButtonStatus), + entry(SimpleRemote, RegisterDescriptor), + entry(SimpleRemote, IPodHIDReport), + entry(SimpleRemote, AccessoryHIDReport), + entry(SimpleRemote, UnregisterDescriptor), + entry(SimpleRemote, VoiceOverEvent), + entry(SimpleRemote, GetVoiceOverParameter), + entry(SimpleRemote, RetVoiceOverParameter), + entry(SimpleRemote, SetVoiceOverParameter), + entry(SimpleRemote, GetCurrentVoiceOverItemProperty), + entry(SimpleRemote, RetCurrentVoiceOverItemProperty), + entry(SimpleRemote, SetVoiceOverContext), + entry(SimpleRemote, VoiceOverParameterChanged), + entry(SimpleRemote, AccessoryAck), +}; +static const char* strs_display[] = { + entry(DisplayRemote, IPodAck), + entry(DisplayRemote, GetCurrentEQProfileIndex), + entry(DisplayRemote, RetCurrentEQProfileIndex), + entry(DisplayRemote, SetCurrentEQProfileIndex), + entry(DisplayRemote, GetNumEQProfiles), + entry(DisplayRemote, RetNumEQProfiles), + entry(DisplayRemote, GetIndexedEQProfileName), + entry(DisplayRemote, RetIndexedEQProfileName), + entry(DisplayRemote, SetRemoteEventNotification), + entry(DisplayRemote, RemoteEventNotification), + entry(DisplayRemote, GetRemoteEventStatus), + entry(DisplayRemote, RetRemoteEventStatus), + entry(DisplayRemote, GetIPodStateInfo), + entry(DisplayRemote, RetIPodStateInfo), + entry(DisplayRemote, SetIPodStateInfo), + entry(DisplayRemote, GetPlayStatus), + entry(DisplayRemote, RetPlayStatus), + entry(DisplayRemote, SetCurrentPlayingTrack), + entry(DisplayRemote, GetIndexedPlayingTrackInfo), + entry(DisplayRemote, RetIndexedPlayingTrackInfo), + entry(DisplayRemote, GetNumPlayingTracks), + entry(DisplayRemote, RetNumPlayingTracks), + entry(DisplayRemote, GetArtworkFormats), + entry(DisplayRemote, RetArtworkFormats), + entry(DisplayRemote, GetTrackArtworkData), + entry(DisplayRemote, RetTrackArtworkData), + entry(DisplayRemote, GetPowerBatteryState), + entry(DisplayRemote, RetPowerBatteryState), + entry(DisplayRemote, GetSoundCheckState), + entry(DisplayRemote, RetSoundCheckState), + entry(DisplayRemote, SetSoundCheckState), + entry(DisplayRemote, GetTrackArtworkTimes), + entry(DisplayRemote, RetTrackArtworkTimes), + entry(DisplayRemote, CreateGeniusPlaylist), + entry(DisplayRemote, IsGeniusAvailableForTrack), +}; +static const char* strs_ext[] = { + entry(ExtendedInterface, IPodAck), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterInfo), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterInfo), + entry(ExtendedInterface, SetCurrentPlayingTrackChapter), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterPlayStatus), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterPlayStatus), + entry(ExtendedInterface, GetCurrentPlayingTrackChapterName), + entry(ExtendedInterface, ReturnCurrentPlayingTrackChapterName), + entry(ExtendedInterface, GetAudiobookSpeed), + entry(ExtendedInterface, RetAudiobookSpeed), + entry(ExtendedInterface, SetAudiobookSpeed), + entry(ExtendedInterface, GetIndexedPlayingTrackInfo), + entry(ExtendedInterface, ReturnIndexedPlayingTrackInfo), + entry(ExtendedInterface, GetArtworkFormats), + entry(ExtendedInterface, RetArtworkFormats), + entry(ExtendedInterface, GetTrackArtworkData), + entry(ExtendedInterface, RetTrackArtworkData), + entry(ExtendedInterface, RequestProtocolVersion), + entry(ExtendedInterface, ReturnProtocolVersion), + entry(ExtendedInterface, RequestIPodName), + entry(ExtendedInterface, ReturnIPodName), + entry(ExtendedInterface, ResetDBSelection), + entry(ExtendedInterface, SelectDBRecord), + entry(ExtendedInterface, GetNumberCategorizedDBRecords), + entry(ExtendedInterface, ReturnNumberCategorizedDBRecords), + entry(ExtendedInterface, RetrieveCategorizedDatabaseRecords), + entry(ExtendedInterface, ReturnCategorizedDatabaseRecords), + entry(ExtendedInterface, GetPlayStatus), + entry(ExtendedInterface, ReturnPlayStatus), + entry(ExtendedInterface, GetCurrentPlayingTrackIndex), + entry(ExtendedInterface, ReturnCurrentPlayingTrackIndex), + entry(ExtendedInterface, GetIndexedPlayingTrackTitle), + entry(ExtendedInterface, ReturnIndexedPlayingTrackTitle), + entry(ExtendedInterface, GetIndexedPlayingTrackArtistName), + entry(ExtendedInterface, ReturnIndexedPlayingTrackArtistName), + entry(ExtendedInterface, GetIndexedPlayingTrackAlbumName), + entry(ExtendedInterface, ReturnIndexedPlayingTrackAlbumName), + entry(ExtendedInterface, SetPlayStatusChangeNotification), + entry(ExtendedInterface, PlayStatusChangeNotification), + entry(ExtendedInterface, PlayCurrentSelection), + entry(ExtendedInterface, PlayControl), + entry(ExtendedInterface, GetTrackArtworkTimes), + entry(ExtendedInterface, RetTrackArtworkTimes), + entry(ExtendedInterface, GetShuffle), + entry(ExtendedInterface, ReturnShuffle), + entry(ExtendedInterface, SetShuffle), + entry(ExtendedInterface, GetRepeat), + entry(ExtendedInterface, ReturnRepeat), + entry(ExtendedInterface, SetRepeat), + entry(ExtendedInterface, SetDisplayImage), + entry(ExtendedInterface, GetMonoDisplayImageLimits), + entry(ExtendedInterface, ReturnMonoDisplayImageLimits), + entry(ExtendedInterface, GetNumPlayingTracks), + entry(ExtendedInterface, ReturnNumPlayingTracks), + entry(ExtendedInterface, SetCurrentPlayingTrack), + entry(ExtendedInterface, SelectSortDBRecord), + entry(ExtendedInterface, GetColorDisplayImageLimits), + entry(ExtendedInterface, ReturnColorDisplayImageLimits), + entry(ExtendedInterface, ResetDBSelectionHierarchy), + entry(ExtendedInterface, GetDBITunesInfo), + entry(ExtendedInterface, RetDBITunesInfo), + entry(ExtendedInterface, GetUIDTrackInfo), + entry(ExtendedInterface, RetUIDTrackInfo), + entry(ExtendedInterface, GetDBTrackInfo), + entry(ExtendedInterface, RetDBTrackInfo), + entry(ExtendedInterface, GetPBTrackInfo), + entry(ExtendedInterface, RetPBTrackInfo), + entry(ExtendedInterface, CreateGeniusPlaylist), + entry(ExtendedInterface, RefreshGeniusPlaylist), + entry(ExtendedInterface, IsGeniusAvailableForTrack), + entry(ExtendedInterface, GetPlaylistInfo), + entry(ExtendedInterface, RetPlaylistInfo), + entry(ExtendedInterface, PrepareUIDList), + entry(ExtendedInterface, PlayPreparedUIDList), + entry(ExtendedInterface, GetArtworkTimes), + entry(ExtendedInterface, RetArtworkTimes), + entry(ExtendedInterface, GetArtworkData), + entry(ExtendedInterface, RetArtworkData), +}; +static const char* strs_da[] = { + entry(DigitalAudio, AccessoryAck), + entry(DigitalAudio, IPodAck), + entry(DigitalAudio, GetAccessorySampleRateCaps), + entry(DigitalAudio, RetAccessorySampleRateCaps), + entry(DigitalAudio, TrackNewAudioAttributes), + entry(DigitalAudio, SetVideoDelay), +}; + +#undef entry + +#define entry(lingo, strs, size) [IAPLingoID_##lingo] = {#lingo, \ + strs, \ + size} +static struct { + const char* lingo; + const char** strs; + size_t size; +} strs[] = { + entry(General, strs_general, array_size(strs_general)), + entry(Microphone, NULL, 0), + entry(SimpleRemote, strs_simple, array_size(strs_simple)), + entry(DisplayRemote, strs_display, array_size(strs_display)), + entry(ExtendedInterface, strs_ext, array_size(strs_ext)), + entry(AccessoryPower, NULL, 0), + entry(USBHostMode, NULL, 0), + entry(RFTuner, NULL, 0), + entry(AccessoryEqualizer, NULL, 0), + entry(Sports, NULL, 0), + entry(DigitalAudio, strs_da, array_size(strs_da)), + entry(Storage, NULL, 0), + entry(IPodOut, NULL, 0), + entry(Location, NULL, 0), +}; + +#undef entry + +const char* _iap_lingo_str_or_null(uint8_t lingo) { + check_ret(lingo < array_size(strs), NULL); + return strs[lingo].lingo; +} + +const char* _iap_lingo_str(uint8_t lingo) { + const char* ret = _iap_lingo_str_or_null(lingo); + return ret ? ret : "?"; +} + +const char* _iap_command_str_or_null(uint8_t lingo, uint16_t command) { + check_ret(lingo < array_size(strs), NULL); + check_ret(command < strs[lingo].size, NULL); + return strs[lingo].strs[command]; +} + +const char* _iap_command_str(uint8_t lingo, uint16_t command) { + const char* ret = _iap_command_str_or_null(lingo, command); + return ret ? ret : "?"; +} + +IAPBool _iap_span_is_str(const struct IAPSpan* span) { + return span->size > 0 && span->ptr[span->size - 1] == '\0'; +} + +const char* _iap_span_as_str(const struct IAPSpan* span) { + return _iap_span_is_str(span) ? (char*)span->ptr : "(invalid)"; +} + +#if !defined(IAP_LOGF_MUTED) +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span) { + const char* lingo_str = _iap_lingo_str_or_null(lingo); + const char* command_str = _iap_command_str_or_null(lingo, command); + if(lingo_str == NULL) { + IAP_LOGF("?(0x%02" PRIX8 ") trans=%" PRIi32, lingo, trans_id); + return; + } else if(command_str == NULL) { + IAP_LOGF("%s:?(0x%02" PRIX8 ") trans=%" PRIi32, lingo_str, command, trans_id); + return; + } else { + IAP_LOGF("%s:%s trans=%" PRIi32, lingo_str, command_str, trans_id); + } + +#define span_read(Type) \ + const struct Type* payload = iap_span_read(&span, sizeof(*payload)); \ + check_act(payload != NULL, return); + + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPGeneralCommandID_ReturnExtendedInterfaceMode: { + span_read(IAPReturnExtendedInterfaceModePayload); + IAP_LOGF(" mode=%" PRIu8, payload->is_ext_mode); + } break; + case IAPGeneralCommandID_ReturnIPodSoftwareVersion: { + span_read(IAPReturnIPodSoftwareVersionPayload); + IAP_LOGF(" version=%" PRIu8 ".%" PRIu8 ".%" PRIu8, payload->major, payload->minor, payload->revision); + } break; + case IAPGeneralCommandID_ReturnIPodSerialNum: { + IAP_LOGF(" serial=%s", _iap_span_as_str(&span)); + } break; + case IAPGeneralCommandID_ReturnIPodModelNum: { + IAP_LOGF(" model=%s", _iap_span_as_str(&span)); + } break; + case IAPGeneralCommandID_RequestLingoProtocolVersion: { + span_read(IAPRequestLingoProtocolVersionPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo)); + } break; + case IAPGeneralCommandID_ReturnLingoProtocolVersion: { + span_read(IAPReturnLingoProtocolVersionPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo)); + IAP_LOGF(" version=%" PRIu8 ".%" PRIu8, payload->major, payload->minor); + } break; + case IAPGeneralCommandID_ReturnTransportMaxPayloadSize: { + span_read(IAPReturnTransportMaxPayloadSizePayload); + IAP_LOGF(" size=%" PRIu16, swap_16(payload->max_payload_size)); + } break; + case IAPGeneralCommandID_IdentifyDeviceLingoes: { + span_read(IAPIdentifyDeviceLingoesPayload); + uint32_t bits = swap_32(payload->lingoes_bits); + for(int i = 0; i < 16; i += 1) { + if(bits & (1u << i)) { + IAP_LOGF(" supports %s", _iap_lingo_str(i)); + } + } + IAP_LOGF(" option=0x%02" PRIX32, swap_32(payload->options)); + IAP_LOGF(" device_id=0x%04" PRIX32, swap_32(payload->device_id)); + } break; + case IAPGeneralCommandID_RetIPodOptions: { + span_read(IAPRetIPodOptionsPayload); + IAP_LOGF(" state=0x%08" PRIX64, swap_64(payload->state)); + } break; + case IAPGeneralCommandID_GetIPodPreferences: { + span_read(IAPGetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02" PRIX8, payload->class_id); + } break; + case IAPGeneralCommandID_RetIPodPreferences: { + span_read(IAPSetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02X", payload->class_id); + IAP_LOGF(" setting=0x%02X", payload->setting_id); + } break; + case IAPGeneralCommandID_SetIPodPreferences: { + span_read(IAPSetIPodPreferencesPayload); + IAP_LOGF(" class=0x%02" PRIX8, payload->class_id); + IAP_LOGF(" setting=0x%02" PRIX8, payload->setting_id); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPGeneralCommandID_SetUIMode: { + span_read(IAPSetUIModePayload); + IAP_LOGF(" mode=0x%02" PRIX8, payload->ui_mode); + } break; + case IAPGeneralCommandID_SetFIDTokenValues: { + span_read(IAPSetFIDTokenValuesPayload); + for(int i = 0; i < payload->num_token_values; i += 1) { + check_act(span.size > sizeof(struct IAPFIDTokenValuesToken), return); + struct IAPFIDTokenValuesToken* token_header = (void*)span.ptr; + struct IAPSpan token_span = { + span.ptr, + token_header->length + 1 /* length does not include sizeof itself */, + }; + check_act(span.size >= token_span.size, return); + iap_span_read(&span, token_span.size); + + switch(token_header->type << 8 | token_header->subtype) { + case IAPFIDTokenTypes_Identify: { + /* IAPFIDTokenValuesIdentifyToken contains vla, need to parse manually */ + const struct IAPFIDTokenValuesIdentifyTokenHead* token_head = iap_span_read(&token_span, sizeof(*token_head)); + check_act(token_head != NULL, return); + print("accessory supported lingoes(%" PRIu8 "):", token_head->num_lingoes); + for(int i = 0; i < token_head->num_lingoes; i += 1) { + uint8_t lingo_id; + check_act(iap_span_read_8(&token_span, &lingo_id), return); + IAP_LOGF(" %s(0x%" PRIX8 ")", _iap_lingo_str(lingo_id), lingo_id); + } + const struct IAPFIDTokenValuesIdentifyTokenTail* token_tail = iap_span_read(&token_span, sizeof(*token_tail)); + check_act(token_tail != NULL, return); + const uint32_t opt = swap_32(token_tail->device_option); + const uint32_t id = swap_32(token_tail->device_id); + print("options=%04" PRIX32 " device_id=%04" PRIX32, opt, id); + } break; + case IAPFIDTokenTypes_AccCaps: { + const struct IAPFIDTokenValuesAccCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + const uint64_t caps = swap_64(token->caps_bits); + print("accessory caps: %" PRIX64, caps); + } break; + case IAPFIDTokenTypes_AccInfo: { + const struct IAPFIDTokenValuesAccInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + switch(token->info_type) { + case IAPFIDTokenValuesAccInfoTypes_AccName: + print("accessory name: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_FirmwareVersion: + check_act(token_span.size == 3, return); + print("accessory firmware version: %" PRIX8 ".%" PRIX8 ".%" PRIX8, token_span.ptr[0], token_span.ptr[1], token_span.ptr[2]); + break; + case IAPFIDTokenValuesAccInfoTypes_HardwareVersion: + check_act(token_span.size == 3, return); + print("accessory hardware version: %" PRIX8 ".%" PRIX8 ".%" PRIX8, token_span.ptr[0], token_span.ptr[1], token_span.ptr[2]); + break; + case IAPFIDTokenValuesAccInfoTypes_Manufacture: + print("accessory manufacture: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_ModelNumber: + print("accessory model number: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_SerialNumber: + print("accessory serial number: %s", _iap_span_as_str(&token_span)); + break; + case IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize: { + uint16_t val; + check_act(iap_span_read_16(&token_span, &val), return); + print("accessory max payload size: %" PRIu16, val); + } break; + case IAPFIDTokenValuesAccInfoTypes_AccStatus: { + uint32_t val; + check_act(iap_span_read_32(&token_span, &val), return); + print("accessory status: %" PRIX32, val); + } break; + case IAPFIDTokenValuesAccInfoTypes_RFCerts: { + uint32_t val; + check_act(iap_span_read_32(&token_span, &val), return); + print("accessory rf cert: %" PRIX32, val); + } break; + } + } break; + case IAPFIDTokenTypes_IPodPreference: { + const struct IAPFIDTokenValuesIPodPreferenceToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory setting %" PRIX8 "=%" PRIX8, token->class_id, token->setting_id); + } break; + case IAPFIDTokenTypes_EAProtocol: { + const struct IAPFIDTokenValuesEAProtocolToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("ea protocol %" PRIX8 "=%s", token->protocol_index, _iap_span_as_str(&token_span)); + } break; + case IAPFIDTokenTypes_BundleSeedIDPref: { + const struct IAPFIDTokenValuesBundleSeedIDPrefToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("bundle seed id %.10s", token->bundle_seed_id_string); + } break; + case IAPFIDTokenTypes_ScreenInfo: { + const struct IAPFIDTokenValuesScreenInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("screen info:"); + IAP_LOGF(" screen size(inch): %" PRIu16 "x%" PRIu16, swap_16(token->total_screen_width_inches), swap_16(token->total_screen_height_inches)); + IAP_LOGF(" screen size(pixel): %" PRIu16 "x%" PRIu16, swap_16(token->total_screen_width_pixels), swap_16(token->total_screen_height_pixels)); + IAP_LOGF(" ipod out size(pixel): %" PRIu16 "x%" PRIu16, swap_16(token->ipod_out_screen_width_pixels), swap_16(token->ipod_out_screen_height_pixels)); + IAP_LOGF(" feature: %" PRIX8, token->screen_feature_mask); + IAP_LOGF(" gamma: %" PRIu8, token->screen_gamma_value); + } break; + case IAPFIDTokenTypes_EAProtocolMetadata: { + const struct IAPFIDTokenValuesEAProtocolMetadataToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("ea protocol metadata %" PRIX8 "=%" PRIX8, token->protocol_index, token->metadata_type); + } break; + case IAPFIDTokenTypes_AccDigitalAudioSampleRates: { + const struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory supported audio sample rates:"); + while(token_span.size > 0) { + uint32_t rate; + check_act(iap_span_read_32(&token_span, &rate), return); + IAP_LOGF(" %" PRIu32, rate); + } + } break; + case IAPFIDTokenTypes_AccDigitalAudioVideoDelay: { + const struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory video delay: %" PRIu32, token->delay); + } break; + case IAPFIDTokenTypes_MicrophoneCaps: { + const struct IAPFIDTokenValuesMicrophoneCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_act(token != NULL, return); + print("accessory microphone caps: %" PRIX32, token->caps_bits); + } break; + default: + print("unknown fid %04" PRIX16, token_header->type << 8 | token_header->subtype); + } + } + } break; + case IAPGeneralCommandID_EndIDPS: { + span_read(IAPEndIDPSPayload); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPGeneralCommandID_GetIPodOptionsForLingo: { + span_read(IAPGetIPodOptionsForLingoPayload); + IAP_LOGF(" lingo=%s", _iap_lingo_str(payload->lingo_id)); + } break; + case IAPGeneralCommandID_RetSupportedEventNotification: { + span_read(IAPRetSupportedEventNotificationPayload); + IAP_LOGF(" mask=0x%08" PRIX64, swap_64(payload->mask)); + } break; + case IAPGeneralCommandID_SetAvailableCurrent: { + span_read(IAPSetAvailableCurrentPayload); + IAP_LOGF(" current=%" PRIu16 "mA", swap_16(payload->current_limit_ma)); + } break; + case IAPGeneralCommandID_SetEventNotification: { + span_read(IAPSetEventNotificationPayload); + IAP_LOGF(" mask=0x%08" PRIX64, swap_64(payload->mask)); + } break; + } + break; + case IAPLingoID_SimpleRemote: + switch(command) { + case IAPSimpleRemoteCommandID_ContextButtonStatus: { + uint8_t bits; + check_ret(iap_span_read_8(&span, &bits), ); + IAP_LOGF(" bits0=0x%02" PRIX8, bits); + uint8_t index = 1; + while(span.size > 0) { + iap_span_read_8(&span, &bits); + IAP_LOGF(" bits%d=0x%02" PRIX8, index, bits); + index += 1; + } + } break; + case IAPSimpleRemoteCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + } + break; + case IAPLingoID_DisplayRemote: + switch(command) { + case IAPDisplayRemoteCommandID_IPodAck: { + span_read(IAPIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex: { + span_read(IAPSetCurrentEQProfileIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPDisplayRemoteCommandID_SetRemoteEventNotification: { + span_read(IAPSetRemoteEventNotificationPayload); + IAP_LOGF(" mask=0x%04" PRIX32, swap_32(payload->mask)); + } break; + case IAPDisplayRemoteCommandID_RemoteEventNotification: { + check_ret(span.size >= 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIPodStateType_TrackTimePositionMSec: { + span_read(IAPIPodStateTrackTimePositionMSecPayload); + IAP_LOGF(" position=%" PRIu32 "ms", swap_32(payload->position_ms)); + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + span_read(IAPIPodStateTrackPlaybackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPIPodStateType_ChapterIndex: { + span_read(IAPIPodStateChapterIndexPayload); + IAP_LOGF(" chapter_index=%" PRIu16, swap_16(payload->chapter_index)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIPodStateType_PlayStatus: { + span_read(IAPIPodStatePlayStatusPayload); + IAP_LOGF(" play_status=%" PRIu8, payload->status); + } break; + case IAPIPodStateType_Volume: { + span_read(IAPIPodStateVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" volume=%" PRIu8, payload->ui_volume); + } break; + case IAPIPodStateType_Power: { + span_read(IAPIPodStatePowerPayload); + IAP_LOGF(" power_state=%" PRIu8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPIPodStateType_EQSetting: { + span_read(IAPIPodStateEQSettingPayload); + IAP_LOGF(" eq_index=%" PRIu32, swap_32(payload->eq_index)); + } break; + case IAPIPodStateType_ShuffleSetting: { + span_read(IAPIPodStateShuffleSettingPayload); + IAP_LOGF(" shuffle_state=%" PRIu8, payload->shuffle_state); + } break; + case IAPIPodStateType_RepeatSetting: { + span_read(IAPIPodStateRepeatSettingPayload); + IAP_LOGF(" repeat_state=%" PRIu8, payload->repeat_state); + } break; + case IAPIPodStateType_DateTimeSetting: { + span_read(IAPIPodStateDateTimeSettingPayload); + IAP_LOGF(" time=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hour, payload->minute); + } break; + case IAPIPodStateType_AlarmSetting: { + span_read(IAPIPodStateAlarmSettingPayload); + IAP_LOGF(" alarm"); + } break; + case IAPIPodStateType_BacklightLevel: { + span_read(IAPIPodStateBacklightLevelPayload); + IAP_LOGF(" backlight_level=%" PRIu8, payload->level); + } break; + case IAPIPodStateType_HoldSwitchState: { + span_read(IAPIPodStateHoldSwitchStatePayload); + IAP_LOGF(" hold_switch_state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_SoundCheckState: { + span_read(IAPIPodStateSoundCheckStatePayload); + IAP_LOGF(" sound_check_state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_AudiobookSpeeed: { + span_read(IAPIPodStateAudiobookSpeeedPayload); + IAP_LOGF(" audio_book_speed=%" PRIu8, payload->speed); + } break; + case IAPIPodStateType_TrackTimePositionSec: { + span_read(IAPIPodStateTrackTimePositionSecPayload); + IAP_LOGF(" position=%us", swap_16(payload->position_s)); + } break; + case IAPIPodStateType_AbsoluteVolume: { + span_read(IAPIPodStateAbsoluteVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" absolute_volume=%" PRIu8, payload->absolute_volume); + } break; + case IAPIPodStateType_TrackCaps: { + span_read(IAPIPodStateTrackCapsPayload); + IAP_LOGF(" track_caps=0x%04" PRIX32, swap_32(payload->caps)); + } break; + case IAPIPodStateType_PlaybackEngineContents: { + span_read(IAPIPodStatePlaybackEngineContentsPayload); + IAP_LOGF(" playback_engine_contents=%" PRIu32, swap_32(payload->count)); + } break; + } + } break; + case IAPDisplayRemoteCommandID_GetIPodStateInfo: { + span_read(IAPGetIPodStateInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + } break; + case IAPDisplayRemoteCommandID_RetIPodStateInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIPodStateType_TrackTimePositionMSec: { + span_read(IAPIPodStateTrackTimePositionMSecPayload); + IAP_LOGF(" position=%" PRIu32 "ms", swap_32(payload->position_ms)); + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + span_read(IAPIPodStateTrackPlaybackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPIPodStateType_ChapterIndex: { + span_read(IAPIPodStateChapterIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + IAP_LOGF(" cindex=%" PRIu16, swap_16(payload->chapter_index)); + IAP_LOGF(" ccount=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIPodStateType_PlayStatus: { + span_read(IAPIPodStatePlayStatusPayload); + IAP_LOGF(" status=%" PRIu8, payload->status); + } break; + case IAPIPodStateType_Volume: { + span_read(IAPIPodStateVolumePayload); + IAP_LOGF(" ui_volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" mute_state=%" PRIu8, payload->mute_state); + } break; + case IAPIPodStateType_Power: { + span_read(IAPIPodStatePowerPayload); + IAP_LOGF(" power_state=0x%02" PRIX8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPIPodStateType_EQSetting: { + span_read(IAPIPodStateEQSettingPayload); + IAP_LOGF(" eq_index=%" PRIu32, swap_32(payload->eq_index)); + } break; + case IAPIPodStateType_ShuffleSetting: { + span_read(IAPIPodStateShuffleSettingPayload); + IAP_LOGF(" shuffle_state=%" PRIu8, payload->shuffle_state); + } break; + case IAPIPodStateType_RepeatSetting: { + span_read(IAPIPodStateRepeatSettingPayload); + IAP_LOGF(" repeat_state=%" PRIu8, payload->repeat_state); + } break; + case IAPIPodStateType_DateTimeSetting: { + span_read(IAPIPodStateDateTimeSettingPayload); + IAP_LOGF(" time=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hour, payload->minute); + } break; + case IAPIPodStateType_AlarmSetting: { + } break; + case IAPIPodStateType_BacklightLevel: { + span_read(IAPIPodStateBacklightLevelPayload); + IAP_LOGF(" level=%" PRIu8, payload->level); + } break; + case IAPIPodStateType_HoldSwitchState: { + span_read(IAPIPodStateHoldSwitchStatePayload); + IAP_LOGF(" state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_SoundCheckState: { + span_read(IAPIPodStateSoundCheckStatePayload); + IAP_LOGF(" state=%" PRIu8, payload->state); + } break; + case IAPIPodStateType_AudiobookSpeeed: { + span_read(IAPIPodStateAudiobookSpeeedPayload); + IAP_LOGF(" speed=%" PRIu8, payload->speed); + } break; + case IAPIPodStateType_TrackTimePositionSec: { + span_read(IAPIPodStateTrackTimePositionSecPayload); + IAP_LOGF(" position=%" PRIu16 "s", swap_16(payload->position_s)); + } break; + case IAPIPodStateType_AbsoluteVolume: { + span_read(IAPIPodStateAbsoluteVolumePayload); + IAP_LOGF(" mute=%" PRIu8, payload->mute_state); + IAP_LOGF(" ui_volume=%" PRIu8, payload->ui_volume); + IAP_LOGF(" absolute_volume=%" PRIu8, payload->absolute_volume); + } break; + case IAPIPodStateType_TrackCaps: { + span_read(IAPIPodStateTrackCapsPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->caps)); + } break; + case IAPIPodStateType_PlaybackEngineContents: { + span_read(IAPIPodStatePlaybackEngineContentsPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + } break; + } + } break; + case IAPDisplayRemoteCommandID_RetPlayStatus: { + span_read(IAPRetPlayStatusPayload); + IAP_LOGF(" state=0x%02" PRIX8, payload->state); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" pos=%" PRIu32 "ms", swap_32(payload->track_pos_ms)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + } break; + case IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo: { + span_read(IAPGetIndexedPlayingTrackInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + IAP_LOGF(" track=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" chapter=%" PRIu16, swap_16(payload->chapter_index)); + } break; + case IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPIndexedPlayingTrackInfoType_TrackCapsInfo: { + span_read(IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->track_caps)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPIndexedPlayingTrackInfoType_ChapterTimeName: { + span_read(IAPRetIndexedPlayingTrackInfoChapterTimeNamePayload); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + IAP_LOGF(" name=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_ArtistName: + case IAPIndexedPlayingTrackInfoType_AlbumName: + case IAPIndexedPlayingTrackInfoType_GenreName: + case IAPIndexedPlayingTrackInfoType_ComposerName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_Lyrics: { + span_read(IAPRetIndexedPlayingTrackInfoLyricsPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" lyrics=%s", _iap_span_as_str(&span)); + } break; + case IAPIndexedPlayingTrackInfoType_ArtworkCount: { + span_read(IAPRetIndexedPlayingTrackInfoArtworkCountPayload); + while(span.size >= sizeof(struct IAPArtworkCount)) { + const struct IAPArtworkCount* count = iap_span_read(&span, sizeof(*count)); + IAP_LOGF(" format=%" PRIu16 ", count=%" PRIu16, swap_16(count->format), swap_16(count->count)); + } + } break; + } + } break; + case IAPDisplayRemoteCommandID_RetNumPlayingTracks: { + span_read(IAPRetNumPlayingTracksPayload); + IAP_LOGF(" tracks=%" PRIu32, swap_32(payload->num_playing_tracks)); + } break; + case IAPDisplayRemoteCommandID_RetArtworkFormats: { + while(span.size >= sizeof(struct IAPArtworkFormat)) { + const struct IAPArtworkFormat* format = iap_span_read(&span, sizeof(*format)); + IAP_LOGF(" id=%" PRIu16 ", format=%" PRIu8 ", width=%" PRIu16 ", height=%" PRIu16, swap_16(format->format_id), format->pixel_format, swap_16(format->image_width), swap_16(format->image_height)); + } + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkData: { + span_read(IAPGetTrackArtworkDataPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu8, payload->format_id); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + } break; + case IAPDisplayRemoteCommandID_RetTrackArtworkData: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + if(index == 0) { + span_read(IAPRetTrackArtworkDataFirstPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->pixel_format)); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } else { + span_read(IAPRetTrackArtworkDataSubsequenctPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + } + } break; + case IAPDisplayRemoteCommandID_RetPowerBatteryState: { + span_read(IAPRetPowerBatteryStatePayload); + IAP_LOGF(" power_state=%" PRIu8, payload->power_state); + IAP_LOGF(" battery_level=%" PRIu8, payload->battery_level); + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkTimes: { + span_read(IAPGetTrackArtworkTimesPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->format_id)); + IAP_LOGF(" artwork_index=%" PRIu32, swap_32(payload->artwork_index)); + IAP_LOGF(" artwork_count=%" PRIu32, swap_32(payload->artwork_count)); + } break; + case IAPDisplayRemoteCommandID_RetTrackArtworkTimes: { + while(span.size >= sizeof(uint16_t)) { + uint16_t time; + iap_span_read_16(&span, &time); + IAP_LOGF(" time=%" PRIu16, time); + } + } break; + } + break; + case IAPLingoID_ExtendedInterface: + switch(command) { + case IAPExtendedInterfaceCommandID_IPodAck: { + span_read(IAPExtendedIPodAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, swap_16(payload->id))); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo: { + span_read(IAPReturnCurrentPlayingTrackChapterInfoPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_RetAudiobookSpeed: { + span_read(IAPRetAudiobookSpeedPayload); + IAP_LOGF(" speed=0x%02" PRIX8, payload->speed); + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo: { + span_read(IAPExtendedGetIndexedPlayingTrackInfoPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + IAP_LOGF(" track=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" chapter=%" PRIu16, swap_16(payload->chapter_index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo: { + check_ret(span.size > 1, ); + IAP_LOGF(" type=0x%02" PRIX8, span.ptr[0]); + switch(span.ptr[0]) { + case IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + IAP_LOGF(" caps=0x%04" PRIX32, swap_32(payload->track_caps)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + IAP_LOGF(" chapter_count=%" PRIu16, swap_16(payload->chapter_count)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_PodcastName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload); + IAP_LOGF(" release=%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8 ".%02" PRIu8, swap_16(payload->year), payload->month, payload->day, payload->hours, payload->minutes, payload->seconds); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackDescription: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%" PRIu8, swap_16(payload->index)); + IAP_LOGF(" desc=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics: { + span_read(IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload); + IAP_LOGF(" info=0x%02" PRIX8, payload->info_bits); + IAP_LOGF(" index=%u" PRIu16, swap_16(payload->index)); + IAP_LOGF(" lyrics=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackGenre: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackComposer: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount: { + span_read(IAPRetIndexedPlayingTrackInfoArtworkCountPayload); + while(span.size >= sizeof(struct IAPArtworkCount)) { + const struct IAPArtworkCount* count = iap_span_read(&span, sizeof(*count)); + IAP_LOGF(" format=%" PRIu16 ", count=%" PRIu16, swap_16(count->format), swap_16(count->count)); + } + } break; + } + } break; + case IAPExtendedInterfaceCommandID_RetArtworkFormats: { + while(span.size >= sizeof(struct IAPArtworkFormat)) { + const struct IAPArtworkFormat* format = iap_span_read(&span, sizeof(*format)); + IAP_LOGF(" id=%" PRIu16 ", format=%" PRIu8 ", width=%" PRIu16 ", height=%" PRIu16, swap_16(format->format_id), format->pixel_format, swap_16(format->image_width), swap_16(format->image_height)); + } + } break; + case IAPExtendedInterfaceCommandID_GetTrackArtworkData: { + span_read(IAPGetTrackArtworkDataPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->track_index)); + IAP_LOGF(" format=%" PRIu8, payload->format_id); + IAP_LOGF(" offset=%" PRIu32 "ms", swap_32(payload->offset_ms)); + } break; + case IAPExtendedInterfaceCommandID_RetTrackArtworkData: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + if(index == 0) { + span_read(IAPRetTrackArtworkDataFirstPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + IAP_LOGF(" format=%" PRIu16, swap_16(payload->pixel_format)); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } else { + span_read(IAPRetTrackArtworkDataSubsequenctPayload); + IAP_LOGF(" index=%" PRIu16, swap_16(payload->index)); + } + } break; + case IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords: { + span_read(IAPGetNumberCategorizedDBRecordsPayload); + IAP_LOGF(" type=0x%02" PRIX8, payload->type); + } break; + case IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords: { + span_read(IAPReturnNumberCategorizedDBRecordsPayload); + IAP_LOGF(" count=%" PRIu32, swap_32(payload->count)); + } break; + case IAPExtendedInterfaceCommandID_ReturnPlayStatus: { + span_read(IAPExtendedRetPlayStatusPayload); + IAP_LOGF(" state=0x%02" PRIX8, payload->state); + IAP_LOGF(" pos=%" PRIu32 "ms", swap_32(payload->track_pos_ms)); + IAP_LOGF(" total=%" PRIu32 "ms", swap_32(payload->track_total_ms)); + } break; + case IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex: { + span_read(IAPReturnCurrentPlayingTrackIndexPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle: + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName: + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName: { + span_read(IAPGetIndexedPlayingTrackStringPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle: + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName: + case IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName: { + IAP_LOGF(" str=%s", _iap_span_as_str(&span)); + } break; + case IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification: { + if(span.size == sizeof(struct IAPSetPlayStatusChangeNotification1BytePayload)) { + span_read(IAPSetPlayStatusChangeNotification1BytePayload); + IAP_LOGF(" enable=%" PRIu8, payload->enable); + } else { + span_read(IAPSetPlayStatusChangeNotification4BytesPayload); + IAP_LOGF(" mask=0x%08" PRIX32, swap_32(payload->mask)); + } + } break; + case IAPExtendedInterfaceCommandID_PlayControl: { + span_read(IAPPlayControlPayload); + IAP_LOGF(" code=0x%02" PRIX8, payload->code); + } break; + case IAPExtendedInterfaceCommandID_ReturnShuffle: { + span_read(IAPReturnShufflePayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + } break; + case IAPExtendedInterfaceCommandID_SetShuffle: { + span_read(IAPSetShufflePayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPExtendedInterfaceCommandID_ReturnRepeat: { + span_read(IAPReturnRepeatPayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + } break; + case IAPExtendedInterfaceCommandID_SetRepeat: { + span_read(IAPSetRepeatPayload); + IAP_LOGF(" mode=%" PRIu8, payload->mode); + IAP_LOGF(" roe=%" PRIu8, payload->restore_on_exit); + } break; + case IAPExtendedInterfaceCommandID_SetDisplayImage: { + uint16_t index; + check_ret(iap_span_peek_16(&span, &index), ); + IAP_LOGF(" index=%" PRIu16, index); + if(index == 0) { + span_read(IAPSetDisplayImageFirstPayload); + IAP_LOGF(" format=%02" PRIX8, payload->pixel_format); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->pixel_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->pixel_height)); + } + } break; + case IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks: { + span_read(IAPRetNumPlayingTracksPayload); + IAP_LOGF(" tracks=%" PRIu32, swap_32(payload->num_playing_tracks)); + } break; + case IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack: { + span_read(IAPSetCurrentPlayingTrackPayload); + IAP_LOGF(" index=%" PRIu32, swap_32(payload->index)); + } break; + case IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits: { + span_read(IAPColorDisplayImageLimit); + IAP_LOGF(" width=%" PRIu16, swap_16(payload->max_width)); + IAP_LOGF(" height=%" PRIu16, swap_16(payload->max_height)); + IAP_LOGF(" format=0x%02" PRIX8, payload->pixel_format); + } break; + } + break; + case IAPLingoID_DigitalAudio: + switch(command) { + case IAPDigitalAudioCommandID_AccessoryAck: { + span_read(IAPAccAckPayload); + IAP_LOGF(" id=%s", _iap_command_str(lingo, payload->id)); + IAP_LOGF(" status=0x%02" PRIX8, payload->status); + } break; + case IAPDigitalAudioCommandID_RetAccessorySampleRateCaps: { + while(span.size >= sizeof(uint32_t)) { + uint32_t sample_rate; + iap_span_read_32(&span, &sample_rate); + IAP_LOGF(" rate=%" PRIu32, sample_rate); + } + } break; + } + break; + } +#undef span_read +} +#else +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span) { + (void)lingo; + (void)command; + (void)trans_id; + (void)span; +} +#endif diff --git a/firmware/usbstack/iap/libiap/endian.h b/firmware/usbstack/iap/libiap/endian.h new file mode 100644 index 0000000000..1a3f011ee8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/endian.h @@ -0,0 +1,28 @@ +#pragma once +#include + +__attribute__((unused)) static uint8_t swap_8(uint8_t num) { + return num; +} + +__attribute__((unused)) static uint16_t swap_16(uint16_t num) { + return (num >> 8) | (num << 8); +} + +__attribute__((unused)) static uint32_t swap_32(uint32_t num) { + return (num & 0xFF000000) >> 24 | + (num & 0x00FF0000) >> 8 | + (num & 0x0000FF00) << 8 | + (num & 0x000000FF) << 24; +} + +__attribute__((unused)) static uint64_t swap_64(uint64_t num) { + return (num & 0xFF00000000000000) >> 56 | + (num & 0x00FF000000000000) >> 40 | + (num & 0x0000FF0000000000) << 24 | + (num & 0x000000FF00000000) << 8 | + (num & 0x000000000FF00000) << 8 | + (num & 0x00000000000FF000) << 24 | + (num & 0x000000000000FF00) << 40 | + (num & 0x00000000000000FF) << 56; +} diff --git a/firmware/usbstack/iap/libiap/fid-token-values.c b/firmware/usbstack/iap/libiap/fid-token-values.c new file mode 100644 index 0000000000..eeca46533b --- /dev/null +++ b/firmware/usbstack/iap/libiap/fid-token-values.c @@ -0,0 +1,141 @@ +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "span.h" +#include "spec/iap.h" + +#define pack_accepted(Ack) \ + struct Ack* const ack = iap_span_alloc(response, sizeof(*ack)); \ + check_ret(ack != NULL, -IAPAckStatus_EOutOfResource); \ + ack->length = sizeof(struct Ack) - 1; \ + ack->type = token_header->type; \ + ack->subtype = token_header->subtype; \ + ack->status = IAPFIDTokenValuesIdentifyAckStatus_Accepted; + +int _iap_hanlde_set_fid_token_values(struct IAPSpan* request, struct IAPSpan* response) { + const struct IAPSetFIDTokenValuesPayload* request_payload = iap_span_read(request, sizeof(*request_payload)); + check_ret(request_payload != NULL, -IAPAckStatus_EBadParameter); + check_ret(iap_span_write_8(response, request_payload->num_token_values), -IAPAckStatus_EOutOfResource); + for(int i = 0; i < request_payload->num_token_values; i += 1) { + check_ret(request->size > sizeof(struct IAPFIDTokenValuesToken), -IAPAckStatus_EBadParameter); + struct IAPFIDTokenValuesToken* token_header = (void*)request->ptr; + struct IAPSpan token_span = { + request->ptr, + token_header->length + 1 /* length does not include sizeof itself */, + }; + check_ret(request->size >= token_span.size, -IAPAckStatus_EBadParameter); + iap_span_read(request, token_span.size); + + switch(token_header->type << 8 | token_header->subtype) { + case IAPFIDTokenTypes_Identify: { + /* IAPFIDTokenValuesIdentifyToken contains vla, need to parse manually */ + const struct IAPFIDTokenValuesIdentifyTokenHead* token_head = iap_span_read(&token_span, sizeof(*token_head)); + check_ret(token_head != NULL, -IAPAckStatus_EBadParameter); + for(int i = 0; i < token_head->num_lingoes; i += 1) { + uint8_t lingo_id; + check_ret(iap_span_read_8(&token_span, &lingo_id), -IAPAckStatus_EBadParameter); + } + const struct IAPFIDTokenValuesIdentifyTokenTail* token_tail = iap_span_read(&token_span, sizeof(*token_tail)); + check_ret(token_tail != NULL, -IAPAckStatus_EBadParameter); + const uint32_t opt = swap_32(token_tail->device_option); + const uint32_t id = swap_32(token_tail->device_id); + pack_accepted(IAPFIDTokenValuesIdentifyAck); + if(opt != IAPIdentifyDeviceLingoesOptions_ImmediateAuth) { + ack->status = IAPFIDTokenValuesIdentifyAckStatus_RequiredFailed; + } + (void)id; + } break; + case IAPFIDTokenTypes_AccCaps: { + const struct IAPFIDTokenValuesAccCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + const uint64_t caps = swap_64(token->caps_bits); + (void)caps; + pack_accepted(IAPFIDTokenValuesAccCapsAck); + } break; + case IAPFIDTokenTypes_AccInfo: { + const struct IAPFIDTokenValuesAccInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + switch(token->info_type) { + case IAPFIDTokenValuesAccInfoTypes_AccName: + break; + case IAPFIDTokenValuesAccInfoTypes_FirmwareVersion: + check_ret(token_span.size == 3, -IAPAckStatus_EBadParameter); + break; + case IAPFIDTokenValuesAccInfoTypes_HardwareVersion: + check_ret(token_span.size == 3, -IAPAckStatus_EBadParameter); + break; + case IAPFIDTokenValuesAccInfoTypes_Manufacture: + break; + case IAPFIDTokenValuesAccInfoTypes_ModelNumber: + break; + case IAPFIDTokenValuesAccInfoTypes_SerialNumber: + break; + case IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize: { + uint16_t val; + check_ret(iap_span_read_16(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + case IAPFIDTokenValuesAccInfoTypes_AccStatus: { + uint32_t val; + check_ret(iap_span_read_32(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + case IAPFIDTokenValuesAccInfoTypes_RFCerts: { + uint32_t val; + check_ret(iap_span_read_32(&token_span, &val), -IAPAckStatus_EBadParameter); + } break; + } + pack_accepted(IAPFIDTokenValuesAccInfoAck); + ack->info_type = token->info_type; + } break; + case IAPFIDTokenTypes_IPodPreference: { + const struct IAPFIDTokenValuesIPodPreferenceToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesIPodPreferenceAck); + ack->class_id = token->class_id; + } break; + case IAPFIDTokenTypes_EAProtocol: { + const struct IAPFIDTokenValuesEAProtocolToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesEAProtocolAck); + ack->protocol_index = token->protocol_index; + } break; + case IAPFIDTokenTypes_BundleSeedIDPref: { + const struct IAPFIDTokenValuesBundleSeedIDPrefToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesBundleSeedIDPrefAck); + } break; + case IAPFIDTokenTypes_ScreenInfo: { + const struct IAPFIDTokenValuesScreenInfoToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesScreenInfoAck); + } break; + case IAPFIDTokenTypes_EAProtocolMetadata: { + const struct IAPFIDTokenValuesEAProtocolMetadataToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesEAProtocolMetadataAck); + } break; + case IAPFIDTokenTypes_AccDigitalAudioSampleRates: { + const struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + while(token_span.size > 0) { + uint32_t rate; + check_ret(iap_span_read_32(&token_span, &rate), -IAPAckStatus_EBadParameter); + } + pack_accepted(IAPFIDTokenValuesAccDigitalAudioSampleRatesAck); + } break; + case IAPFIDTokenTypes_AccDigitalAudioVideoDelay: { + const struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesAccDigitalAudioVideoDelayAck); + } break; + case IAPFIDTokenTypes_MicrophoneCaps: { + const struct IAPFIDTokenValuesMicrophoneCapsToken* token = iap_span_read(&token_span, sizeof(*token)); + check_ret(token != NULL, -IAPAckStatus_EBadParameter); + pack_accepted(IAPFIDTokenValuesMicrophoneCapsAck); + } break; + default: + warn("unknown fid %04X", token_header->type << 8 | token_header->subtype); + return -IAPAckStatus_EBadParameter; + } + } + return 0; +} diff --git a/firmware/usbstack/iap/libiap/hid.c b/firmware/usbstack/iap/libiap/hid.c new file mode 100644 index 0000000000..4be2ff389c --- /dev/null +++ b/firmware/usbstack/iap/libiap/hid.c @@ -0,0 +1,181 @@ +#include + +#include "constants.h" +#include "iap.h" +#include "macros.h" +#include "platform.h" +#include "spec/hid.h" + +struct ReportSize { + uint8_t id; + uint16_t size; /* including link control byte */ +}; + +/* must match to in-driver hid report descriptor */ + +static struct ReportSize input_report_size_table_fs[] = { + {.id = 0x01, .size = 0x0C}, + {.id = 0x02, .size = 0x0E}, + {.id = 0x03, .size = 0x14}, + {.id = 0x04, .size = 0x3F}, +}; + +static struct ReportSize output_report_size_table_fs[] = { + {.id = 0x05, .size = 0x08}, + {.id = 0x06, .size = 0x0A}, + {.id = 0x07, .size = 0x0E}, + {.id = 0x08, .size = 0x14}, + {.id = 0x09, .size = 0x3F}, +}; + +static struct ReportSize input_report_size_table_hs[] = { + {.id = 0x01, .size = 0x0005}, + {.id = 0x02, .size = 0x0009}, + {.id = 0x03, .size = 0x000D}, + {.id = 0x04, .size = 0x0011}, + {.id = 0x05, .size = 0x0019}, + {.id = 0x06, .size = 0x0031}, + {.id = 0x07, .size = 0x005F}, + {.id = 0x08, .size = 0x00C1}, + {.id = 0x09, .size = 0x0101}, + {.id = 0x0A, .size = 0x0181}, + {.id = 0x0B, .size = 0x0201}, + {.id = 0x0C, .size = 0x02FF}, +}; + +static struct ReportSize output_report_size_table_hs[] = { + {.id = 0x0D, .size = 0x05}, + {.id = 0x0E, .size = 0x09}, + {.id = 0x1F, .size = 0x0D}, + {.id = 0x10, .size = 0x11}, + {.id = 0x11, .size = 0x19}, + {.id = 0x12, .size = 0x31}, + {.id = 0x13, .size = 0x5F}, + {.id = 0x14, .size = 0xC1}, + {.id = 0x15, .size = 0xFF}, +}; + +static int find_output_report_size(struct IAPContext* ctx, uint8_t id) { + const struct ReportSize* ptr = ctx->opts.usb_highspeed ? output_report_size_table_hs : output_report_size_table_fs; + const size_t len = ctx->opts.usb_highspeed ? array_size(output_report_size_table_hs) : array_size(output_report_size_table_fs); + for(size_t i = 0; i < len; i += 1) { + if(ptr[i].id == id) { + return ptr[i].size; + } + } + return -1; +} + +IAPBool iap_feed_hid_report(struct IAPContext* ctx, const uint8_t* const data, const size_t size) { + check_ret(size > sizeof(struct IAPHIDReport), iap_false); + struct IAPHIDReport* report = (struct IAPHIDReport*)data; + + int report_size; + if(ctx->opts.ignore_hid_report_id) { + report_size = size - 1; + } else { + report_size = find_output_report_size(ctx, report->report_id); + check_ret(report_size == (int)size - 1, iap_false, "%d != %d", report_size, (int)size - 1); + } + + const uint8_t payload_size = report_size - 1; + check_act(ctx->hid_recv_buf_cursor + payload_size <= HID_BUFFER_SIZE, { ctx->hid_recv_buf_cursor = 0; return iap_false; }, "hid buffer overflow"); + if(!(report->link_control & IAPHIDReportLinkControlBits_Continue)) { + /* not continue, first packet */ + if(ctx->hid_recv_buf_cursor != 0) { + warn("not continue and cursor was set"); + ctx->hid_recv_buf_cursor = 0; + } + } else { + check_ret(ctx->hid_recv_buf_cursor > 0, iap_false); + } + memcpy(ctx->hid_recv_buf + ctx->hid_recv_buf_cursor, report->data, payload_size); + ctx->hid_recv_buf_cursor += payload_size; + if(!(report->link_control & IAPHIDReportLinkControlBits_MoreToFollow)) { + /* no more to follow, last packet */ + const IAPBool ret = _iap_feed_packet(ctx, ctx->hid_recv_buf, ctx->hid_recv_buf_cursor); + ctx->hid_recv_buf_cursor = 0; + check_ret(ret, iap_false); + } + return iap_true; +} + +IAPBool iap_notify_send_complete(struct IAPContext* ctx) { + ctx->send_busy = iap_false; + check_ret(_iap_send_next_report(ctx), iap_false); + return iap_true; +} + +IAPBool _iap_send_hid_reports(struct IAPContext* ctx, size_t begin, size_t end) { + if(ctx->send_buf_sending_cursor < ctx->send_buf_sending_range_end) { + warn("another transmission in progress, aborting it"); + } + ctx->send_buf_sending_cursor = begin; + ctx->send_buf_sending_range_begin = begin; + ctx->send_buf_sending_range_end = end; + if(!ctx->send_busy) { + check_ret(_iap_send_next_report(ctx), iap_false); + } + return iap_true; +} + +static const struct ReportSize* find_optimal_report_size(struct IAPContext* ctx, size_t size) { + const struct ReportSize* ptr = ctx->opts.usb_highspeed ? input_report_size_table_hs : input_report_size_table_fs; + const size_t len = ctx->opts.usb_highspeed ? array_size(input_report_size_table_hs) : array_size(input_report_size_table_fs); + for(size_t i = 0; i < len; i += 1) { + if(ptr[i].size >= size + 1 /* link control byte*/) { + return &ptr[i]; + } + } + return &ptr[len - 1]; +} + +IAPBool _iap_send_next_report(struct IAPContext* ctx) { + if(ctx->send_buf_sending_cursor >= ctx->send_buf_sending_range_end) { + if(ctx->on_send_complete != NULL) { + IAPOnSendComplete cb = ctx->on_send_complete; + ctx->on_send_complete = NULL; + check_ret(cb(ctx), iap_false); + } else if(ctx->active_events[0].callback != NULL) { + struct IAPActiveEvent event = ctx->active_events[0]; + /* shift queue */ + size_t i = 0; + for(; i < array_size(ctx->active_events) - 1 && ctx->active_events[i + 1].callback != NULL; i += 1) { + ctx->active_events[i] = ctx->active_events[i + 1]; + } + ctx->active_events[i].callback = NULL; + /* process event */ + check_ret(event.callback(ctx, &event), iap_false); + } else if(ctx->flushing_notifications) { + check_ret(_iap_flush_notification(ctx), iap_false); + } + return iap_true; + } + + check_ret(!ctx->send_busy, iap_false); + + const size_t send_buf_left = ctx->send_buf_sending_range_end - ctx->send_buf_sending_cursor; + const struct ReportSize* const report_size = find_optimal_report_size(ctx, send_buf_left); + const size_t take_size = min((size_t)report_size->size - 1 /* link control */, send_buf_left); + + struct IAPHIDReport* const report = (struct IAPHIDReport*)ctx->hid_send_staging_buf; + + report->report_id = report_size->id; + + const IAPBool is_first = ctx->send_buf_sending_cursor == ctx->send_buf_sending_range_begin; + const IAPBool is_last = ctx->send_buf_sending_cursor + take_size == ctx->send_buf_sending_range_end; + report->link_control = + (!is_first ? IAPHIDReportLinkControlBits_Continue : 0) | + (!is_last ? IAPHIDReportLinkControlBits_MoreToFollow : 0); + + memcpy(report->data, ctx->send_buf + ctx->send_buf_sending_cursor, take_size); + memset(report->data + take_size, 0, report_size->size - 1 - take_size); /* clear rest */ + + ctx->send_buf_sending_cursor += take_size; + ctx->send_busy = iap_true; + + const size_t send_size = 1 + report_size->size; + check_ret(iap_platform_send_hid_report(ctx, report, send_size) == (int)send_size, iap_false); + + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/iap.c b/firmware/usbstack/iap/libiap/iap.c new file mode 100644 index 0000000000..b5f700b0a2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/iap.c @@ -0,0 +1,1361 @@ +#include +#include + +#include "constants.h" +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "pack-util.h" +#include "platform.h" +#include "span.h" +#include "spec/iap.h" +#include "unaligned.h" + +enum TransIDSupport { + TransIDUnknown, + TransIDSupported, + TransIDNotSupported, +}; + +static struct IAPContextButtons parse_context_button_bits(uint8_t bits[4], struct IAPContextButtons* current) { + struct IAPContextButtons new; + struct IAPContextButtons released; + +#define process(field, Mask, bits) \ + new.field = !!(bits & IAPContextButtonStatusButtons_##Mask); \ + released.field = current->field && !new.field; + + process(play_pause, PlayPause, bits[0]); + process(volume_up, VolumeUp, bits[0]); + process(volume_down, VolumeDown, bits[0]); + process(next_track, NextTrack, bits[0]); + process(prev_track, PrevTrack, bits[0]); + process(next_album, NextAlbum, bits[0]); + process(prev_album, PrevAlbum, bits[0]); + process(stop, Stop, bits[0]); + process(play, PlayResume, bits[1]); + process(pause, Pause, bits[1]); + process(mute_toggle, MuteToggle, bits[1]); + process(next_chapter, NextChapter, bits[1]); + process(prev_chapter, PrevChapter, bits[1]); + process(next_playlist, NextPlaylist, bits[1]); + process(prev_playlist, PrevPlaylist, bits[1]); + process(shuffle_advance, ShuffleSettingAdvance, bits[1]); + process(repeat_advance, RepeatSettingAdvance, bits[2]); + +#undef process + + *current = new; + return released; +} + +IAPBool iap_init_ctx(struct IAPContext* ctx, struct IAPOpts opts, void* platform) { + const uint16_t max_input_hid_desc_size = opts.usb_highspeed ? 0x02FF : 0x3F; + + memset(ctx, 0, sizeof(*ctx)); + ctx->opts = opts; + ctx->platform = platform; + + ctx->hid_recv_buf = iap_platform_malloc(ctx, HID_BUFFER_SIZE, 0); + check_ret(ctx->hid_recv_buf != NULL, iap_false); + ctx->send_buf = iap_platform_malloc(ctx, SEND_BUFFER_SIZE, 0); + check_ret(ctx->send_buf != NULL, iap_false); + ctx->handling_trans_id = -1; + ctx->trans_id_support = TransIDUnknown; + ctx->hid_send_staging_buf = iap_platform_malloc(ctx, max_input_hid_desc_size + 1 /* report id */, IAPPlatformMallocFlags_Uncached); + check_ret(ctx->hid_send_staging_buf != NULL, iap_false); + ctx->phase = IAPPhase_Connected; + return iap_true; +} + +IAPBool iap_deinit_ctx(struct IAPContext* ctx) { + if(ctx->artwork.valid) { + iap_platform_close_artwork(ctx, &ctx->artwork); + } + iap_platform_free(ctx, ctx->hid_send_staging_buf); + iap_platform_free(ctx, ctx->hid_recv_buf); + iap_platform_free(ctx, ctx->send_buf); + return iap_true; +} + +#define read_request(Type) \ + const struct Type* request = iap_span_read(request_span, sizeof(*request)); \ + check_ret(request != NULL, -IAPAckStatus_EBadParameter); + +#define alloc_response_extra(Type, extra) \ + struct Type* response = iap_span_alloc(response_span, sizeof(*response) + extra); \ + check_ret(response != NULL, -IAPAckStatus_EOutOfResource); + +#define alloc_response(Type) alloc_response_extra(Type, 0) + +static uint32_t play_stage_change_notification_set_mask_to_type_mask(uint32_t mask) { + uint32_t ret = 0; + if(mask & IAPStatusChangeNotificationBits_Basic) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackStopped | + 1 << IAPStatusChangeNotificationType_PlaybackFEWSeekStop | + 1 << IAPStatusChangeNotificationType_PlaybackREWSeekStop; + } + if(mask & IAPStatusChangeNotificationBits_Extended) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackStatusExtended; + } + if(mask & IAPStatusChangeNotificationBits_TrackIndex) { + ret |= 1 << IAPStatusChangeNotificationType_TrackIndex; + } + if(mask & IAPStatusChangeNotificationBits_TrackTimeOffsetMSec) { + ret |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec; + } + if(mask & IAPStatusChangeNotificationBits_TrackTimeOffsetSec) { + ret |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetSec; + } + if(mask & IAPStatusChangeNotificationBits_PlaybackEngineContentsChanged) { + ret |= 1 << IAPStatusChangeNotificationType_PlaybackEngineContentsChanged; + } + return ret; +} + +static IAPBool send_artwork_chunk_cb(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + if(ctx->artwork_chunk_index == 0) { + struct IAPRetTrackArtworkDataFirstPayload* payload = iap_span_alloc(&request, sizeof(*payload)); + + payload->index = swap_16(ctx->artwork_chunk_index); + payload->pixel_format = ctx->artwork.color ? IAPArtworkPixelFormats_RGB565LE : IAPArtworkPixelFormats_Mono; + payload->pixel_width = swap_16(ctx->artwork.width); + payload->pixel_height = swap_16(ctx->artwork.height); + payload->inset_top_left_x = 0; + payload->inset_top_left_y = 0; + payload->inset_bottom_right_x = payload->pixel_width; + payload->inset_bottom_right_y = payload->pixel_height; + payload->stride = swap_32(ctx->artwork.width * 2); /* TODO: support stride */ + } else { + struct IAPRetTrackArtworkDataSubsequenctPayload* payload = iap_span_alloc(&request, sizeof(*payload)); + + payload->index = swap_16(ctx->artwork_chunk_index); + } + struct IAPSpan artwork; + size_t copy_size = 0; + if(!ctx->opts.artwork_single_report || ctx->artwork_chunk_index != 0) { + check_ret(iap_platform_get_artwork_ptr(ctx, &ctx->artwork, &artwork), iap_false); + check_ret(iap_span_read(&artwork, ctx->artwork_cursor) != NULL, iap_false); /* skip already read chunk */ + copy_size = min((ctx->opts.artwork_single_report ? 48 : request.size), artwork.size); + memcpy(iap_span_alloc(&request, copy_size), iap_span_read(&artwork, copy_size), copy_size); + } + check_ret(_iap_send_packet(ctx, ctx->artwork_data_lingo, ctx->artwork_data_command, ctx->artwork_trans_id, request.ptr), iap_false); + if(artwork.size > 0) { + /* more to send, ask to call again */ + ctx->artwork_cursor += copy_size; + ctx->artwork_chunk_index += 1; + ctx->on_send_complete = send_artwork_chunk_cb; + print("track artwork left %zu bytes", artwork.size); + } else { + /* finished, free artwork */ + check_ret(iap_platform_close_artwork(ctx, &ctx->artwork), iap_false); + ctx->artwork.valid = iap_false; + print("track artwork done"); + } + return iap_true; +} + +static int32_t start_artwork_data(struct IAPContext* ctx, struct IAPSpan* request_span, IAPBool ext) { + read_request(IAPGetTrackArtworkDataPayload); + check_ret(request->format_id == 0, -IAPAckStatus_EBadParameter); + check_ret(request->offset_ms == 0, -IAPAckStatus_EBadParameter); + check_ret(!ctx->artwork.valid, -IAPAckStatus_EBadParameter); + + check_ret(iap_platform_open_artwork(ctx, swap_32(request->track_index), &ctx->artwork), -IAPAckStatus_EBadParameter); + ctx->artwork.valid = iap_true; + ctx->artwork_cursor = 0; + ctx->artwork_chunk_index = 0; + ctx->artwork_trans_id = ctx->handling_trans_id; + if(ext) { + ctx->artwork_data_lingo = IAPLingoID_ExtendedInterface; + ctx->artwork_data_command = IAPExtendedInterfaceCommandID_RetTrackArtworkData; + } else { + ctx->artwork_data_lingo = IAPLingoID_DisplayRemote; + ctx->artwork_data_command = IAPDisplayRemoteCommandID_RetTrackArtworkData; + } + check_ret(send_artwork_chunk_cb(ctx), -IAPAckStatus_ECommandFailed); + return 0; +} + +static int32_t ipod_ack(uint16_t command, enum IAPAckStatus status, struct IAPSpan* response_span, uint16_t ret) { + alloc_response(IAPIPodAckPayload); + response->status = status; + response->id = command; + return ret; +} + +static int32_t ipod_ack_ext(uint16_t command, enum IAPAckStatus status, struct IAPSpan* response_span) { + alloc_response(IAPExtendedIPodAckPayload); + response->id = swap_16(command); + response->status = status; + return IAPExtendedInterfaceCommandID_IPodAck; +} + +static int32_t handle_command(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_RequestExtendedInterfaceMode: { + alloc_response(IAPReturnExtendedInterfaceModePayload); + response->is_ext_mode = 1; + return IAPGeneralCommandID_ReturnExtendedInterfaceMode; + } break; + case IAPGeneralCommandID_RequestIPodSoftwareVersion: { + alloc_response(IAPReturnIPodSoftwareVersionPayload); + response->major = 18; + response->minor = 7; + response->revision = 2; + return IAPGeneralCommandID_ReturnIPodSoftwareVersion; + } break; + case IAPGeneralCommandID_RequestIPodSerialNum: { + check_ret(iap_platform_get_ipod_serial_num(ctx, response_span), -IAPAckStatus_ECommandFailed); + return IAPGeneralCommandID_ReturnIPodSerialNum; + } break; + case IAPGeneralCommandID_RequestIPodModelNum: { + static const char* model_num = "MTAY2J/A"; + check_ret(iap_span_append(response_span, model_num, strlen(model_num) + 1), -IAPAckStatus_EOutOfResource); + return IAPGeneralCommandID_ReturnIPodModelNum; + } break; + case IAPGeneralCommandID_RequestTransportMaxPayloadSize: { + alloc_response(IAPReturnTransportMaxPayloadSizePayload); + response->max_payload_size = swap_16(HID_BUFFER_SIZE - 1 /*sync*/ - 1 /*sof*/ - 3 /*length*/ - 1 /*checksum*/); + return IAPGeneralCommandID_ReturnTransportMaxPayloadSize; + } break; + case IAPGeneralCommandID_RequestLingoProtocolVersion: { + static const struct { + uint8_t major; + uint8_t minor; + } table[] = { + [IAPLingoID_General] = {1, 9}, + [IAPLingoID_Microphone] = {1, 1}, + [IAPLingoID_SimpleRemote] = {1, 4}, + [IAPLingoID_DisplayRemote] = {1, 5}, + [IAPLingoID_ExtendedInterface] = {1, 14}, + [IAPLingoID_AccessoryPower] = {1, 1}, + [IAPLingoID_USBHostMode] = {1, 0}, + [IAPLingoID_RFTuner] = {1, 1}, + [IAPLingoID_AccessoryEqualizer] = {1, 0}, + [IAPLingoID_Sports] = {1, 1}, + [IAPLingoID_DigitalAudio] = {1, 3}, + [IAPLingoID_Storage] = {1, 2}, + [IAPLingoID_IPodOut] = {1, 0}, + [IAPLingoID_Location] = {1, 0}, + }; + + read_request(IAPRequestLingoProtocolVersionPayload); + check_ret(request->lingo < array_size(table), -IAPAckStatus_EBadParameter); + + alloc_response(IAPReturnLingoProtocolVersionPayload); + response->lingo = request->lingo; + response->major = table[request->lingo].major; + response->minor = table[request->lingo].minor; + return IAPGeneralCommandID_ReturnLingoProtocolVersion; + } break; + case IAPGeneralCommandID_GetIPodOptions: { + alloc_response(IAPRetIPodOptionsPayload); + response->state = 0; + return IAPGeneralCommandID_RetIPodOptions; + } break; + case IAPGeneralCommandID_GetIPodPreferences: { + read_request(IAPGetIPodPreferencesPayload); + alloc_response(IAPRetIPodPreferencesPayload); + response->class_id = request->class_id; + response->setting_id = 0; /* TODO: return actual value */ + return IAPGeneralCommandID_RetIPodPreferences; + } break; + case IAPGeneralCommandID_SetIPodPreferences: { + read_request(IAPSetIPodPreferencesPayload); + /* TODO: handle preferences */ + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_SetUIMode: { + read_request(IAPSetUIModePayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_GetIPodOptionsForLingo: { + read_request(IAPGetIPodOptionsForLingoPayload); + alloc_response(IAPRetIPodOptionsForLingoPayload); + response->lingo_id = request->lingo_id; + switch(request->lingo_id) { + case IAPLingoID_SimpleRemote: + response->bits = swap_64(IAPRetIPodOptionsForLingoSimpleRemoteBits_ContextSpecificControls); + break; + case IAPLingoID_General: + case IAPLingoID_DisplayRemote: + case IAPLingoID_ExtendedInterface: + case IAPLingoID_DigitalAudio: + case IAPLingoID_Storage: /* TODO: this is not supported */ + response->bits = 0; + break; + case IAPLingoID_USBHostMode: + case IAPLingoID_RFTuner: + case IAPLingoID_Sports: + case IAPLingoID_IPodOut: + case IAPLingoID_Location: { /* not supported */ + return -IAPAckStatus_EBadParameter; + } + } + return IAPGeneralCommandID_RetIPodOptionsForLingo; + } break; + case IAPGeneralCommandID_GetSupportedEventNotification: { + alloc_response(IAPRetSupportedEventNotificationPayload); + response->mask = swap_64(IAPSetEventNotificationEvents_FlowControl); + return IAPGeneralCommandID_RetSupportedEventNotification; + } break; + case IAPGeneralCommandID_SetAvailableCurrent: { + read_request(IAPSetAvailableCurrentPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_SetEventNotification: { + read_request(IAPSetEventNotificationPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + } + break; + case IAPLingoID_SimpleRemote: + switch(command) { + case IAPSimpleRemoteCommandID_ContextButtonStatus: { + response_span->ptr = NULL; + + uint8_t bits[4] = {0}; + for(int i = 0; i < 4 && request_span->size > 0; i += 1) { + iap_span_read_8(request_span, &bits[i]); + } + const struct IAPContextButtons released = parse_context_button_bits(bits, &ctx->context_button_state); + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = -1, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + const struct { + IAPBool released; + enum IAPPlatformControl control; + } table[] = { + {released.play_pause, IAPPlatformControl_TogglePlayPause}, + {released.volume_up, IAPPlatformControl_VolumeUp}, + {released.volume_down, IAPPlatformControl_VolumeDown}, + {released.next_track, IAPPlatformControl_Next}, + {released.prev_track, IAPPlatformControl_Prev}, + {released.next_album, IAPPlatformControl_Next}, + {released.prev_album, IAPPlatformControl_Prev}, + {released.stop, IAPPlatformControl_Stop}, + {released.play, IAPPlatformControl_Play}, + {released.pause, IAPPlatformControl_Pause}, + {released.mute_toggle, IAPPlatformControl_ToggleMute}, + {released.next_chapter, IAPPlatformControl_Next}, + {released.prev_chapter, IAPPlatformControl_Prev}, + {released.next_playlist, IAPPlatformControl_Next}, + {released.prev_playlist, IAPPlatformControl_Prev}, + }; + for(size_t i = 0; i < array_size(table); i += 1) { + if(table[i].released) { + iap_platform_control(ctx, table[i].control, pending); + } + } + if(released.shuffle_advance) { + uint8_t current; + check_act(iap_platform_get_shuffle_setting(ctx, ¤t), return 0); + current = (current + 1) % IAPIPodStateShuffleSettingState_Albums; + check_act(iap_platform_set_shuffle_setting(ctx, current), return 0); + } + if(released.repeat_advance) { + uint8_t current; + check_act(iap_platform_get_repeat_setting(ctx, ¤t), return 0); + current = (current + 1) % IAPIPodStateRepeatSettingState_All; + check_act(iap_platform_set_repeat_setting(ctx, current), return 0); + } + return 0; + } break; + } + break; + case IAPLingoID_DisplayRemote: + switch(command) { + case IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex: { + read_request(IAPSetCurrentEQProfileIndexPayload); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPDisplayRemoteCommandID_IPodAck); + } break; + case IAPDisplayRemoteCommandID_SetRemoteEventNotification: { + read_request(IAPSetRemoteEventNotificationPayload); + ctx->notifications_3 = 0; + ctx->enabled_notifications_3 = swap_32(request->mask); + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPDisplayRemoteCommandID_IPodAck); + } break; + case IAPDisplayRemoteCommandID_GetIPodStateInfo: { + read_request(IAPGetIPodStateInfoPayload); + check_ret(response_span->size >= sizeof(struct IAPIPodStatePayload), -IAPAckStatus_EOutOfResource); + ((struct IAPIPodStatePayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPIPodStateType_TrackTimePositionMSec: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackTimePositionMSecPayload); + response->position_ms = swap_32(status.track_pos_ms); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackPlaybackIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackPlaybackIndexPayload); + response->index = swap_32(status.track_index); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_ChapterIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateChapterIndexPayload); + response->index = swap_32(status.track_index); + /* no chapters */ + response->chapter_count = 0; + response->chapter_index = -1; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_PlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStatePlayStatusPayload); + response->status = status.state; /* TODO: convert enum */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_Volume: { + struct IAPPlatformVolumeStatus status; + check_ret(iap_platform_get_volume(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateVolumePayload); + response->mute_state = status.muted; + response->ui_volume = status.volume; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_Power: { + struct IAPPlatformPowerStatus status; + check_ret(iap_platform_get_power_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStatePowerPayload); + response->power_state = status.state; /* TODO: convert enum */ + response->battery_level = status.battery_level; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_EQSetting: { + alloc_response(IAPIPodStateEQSettingPayload); + /* no eq setting support yet */ + response->eq_index = 0; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_ShuffleSetting: { + alloc_response(IAPIPodStateShuffleSettingPayload); + check_ret(iap_platform_get_shuffle_setting(ctx, &response->shuffle_state), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_RepeatSetting: { + alloc_response(IAPIPodStateRepeatSettingPayload); + check_ret(iap_platform_get_repeat_setting(ctx, &response->repeat_state), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_DateTimeSetting: { + struct IAPDateTime time; + check_ret(iap_platform_get_date_time(ctx, &time), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateDateTimeSettingPayload); + response->year = swap_16(time.year); + response->month = time.month; + response->day = time.day; + response->hour = time.hour; + response->minute = time.minute; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AlarmSetting: { + alloc_response(IAPIPodStateAlarmSettingPayload); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_BacklightLevel: { + alloc_response(IAPIPodStateBacklightLevelPayload); + check_ret(iap_platform_get_backlight_level(ctx, &response->level), -IAPAckStatus_ECommandFailed); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_HoldSwitchState: { + IAPBool state; + check_ret(iap_platform_get_hold_switch_state(ctx, &state), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateHoldSwitchStatePayload); + response->state = state; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_SoundCheckState: { + alloc_response(IAPIPodStateSoundCheckStatePayload); + response->state = 0; /* no sound check */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AudiobookSpeeed: { + alloc_response(IAPIPodStateAudiobookSpeeedPayload); + response->speed = IAPIPodStateAudiobookSpeeed_Normal; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackTimePositionSec: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackTimePositionSecPayload); + response->position_s = swap_16(status.track_pos_ms / 1000); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_AbsoluteVolume: { + struct IAPPlatformVolumeStatus status; + check_ret(iap_platform_get_volume(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateAbsoluteVolumePayload); + response->mute_state = status.muted; + response->ui_volume = status.volume; + response->absolute_volume = status.volume; + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_TrackCaps: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPIPodStateTrackCapsPayload); + response->caps = swap_32(status.track_caps); + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + case IAPIPodStateType_PlaybackEngineContents: { + alloc_response(IAPIPodStatePlaybackEngineContentsPayload); + response->count = 0; /* TODO: shoud be supported? */ + return IAPDisplayRemoteCommandID_RetIPodStateInfo; + } break; + default: + warn("invalid request type 0x%02" PRIX8, request->type); + return -IAPAckStatus_EBadParameter; + } + } break; + case IAPDisplayRemoteCommandID_GetPlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetPlayStatusPayload); + response->state = status.state; /* TODO: convert enum */ + response->track_index = swap_32(status.track_index); + response->track_pos_ms = swap_32(status.track_pos_ms); + response->track_total_ms = swap_32(status.track_total_ms); + return IAPDisplayRemoteCommandID_RetPlayStatus; + } break; + case IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo: { + read_request(IAPGetIndexedPlayingTrackInfoPayload); + check_ret(response_span->size > sizeof(struct IAPRetIndexedPlayingTrackInfoPayload), -IAPAckStatus_EBadParameter); + ((struct IAPRetIndexedPlayingTrackInfoPayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPIndexedPlayingTrackInfoType_TrackCapsInfo: { + uint32_t length; + uint32_t caps; + struct IAPPlatformTrackInfo info = {.total_ms = &length, .caps = &caps}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + response->track_caps = swap_32(caps); + response->track_total_ms = swap_32(length); + response->chapter_count = 0; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ChapterTimeName: { + return -IAPAckStatus_EBadParameter; + } break; + case IAPIndexedPlayingTrackInfoType_ArtistName: { + alloc_response(IAPRetIndexedPlayingTrackInfoArtistNamePayload); + struct IAPPlatformTrackInfo info = {.artist = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_AlbumName: { + alloc_response(IAPRetIndexedPlayingTrackInfoAlbumNamePayload); + struct IAPPlatformTrackInfo info = {.album = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_GenreName: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoGenreNamePayload, 1); + response->name[0] = '\0'; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_TrackTitle: { + alloc_response(IAPRetIndexedPlayingTrackInfoTrackTitlePayload); + struct IAPPlatformTrackInfo info = {.title = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ComposerName: { + alloc_response(IAPRetIndexedPlayingTrackInfoComposerNamePayload); + struct IAPPlatformTrackInfo info = {.composer = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_Lyrics: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoLyricsPayload, 1); + response->info_bits = 0; + response->index = 0; + response->lyrics[0] = '\0'; + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + case IAPIndexedPlayingTrackInfoType_ArtworkCount: { + alloc_response_extra(IAPRetIndexedPlayingTrackInfoArtworkCountPayload, sizeof(struct IAPArtworkCount)); + response->data[0].format = 0; + response->data[0].count = swap_16(1); + return IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo; + } break; + } + } break; + case IAPDisplayRemoteCommandID_GetArtworkFormats: { + alloc_response_extra(IAPArtworkFormat, sizeof(struct IAPArtworkFormat)); + response->format_id = 0; + response->pixel_format = IAP_COLOR_ARTWORK ? IAPArtworkPixelFormats_RGB565LE : IAPArtworkPixelFormats_Mono; + response->image_width = swap_16(IAP_ARTWORK_WIDTH); + response->image_height = swap_16(IAP_ARTWORK_HEIGHT); + return IAPDisplayRemoteCommandID_RetArtworkFormats; + } break; + case IAPDisplayRemoteCommandID_GetNumPlayingTracks: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetNumPlayingTracksPayload); + response->num_playing_tracks = swap_32(status.track_count); + return IAPDisplayRemoteCommandID_RetNumPlayingTracks; + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkData: { + const int32_t ret = start_artwork_data(ctx, request_span, iap_false); + check_ret(ret == 0, ret); + /* responded in send_artwork_chunk_cb, no need to do it here */ + response_span->ptr = NULL; + return 0; + } break; + case IAPDisplayRemoteCommandID_GetPowerBatteryState: { + struct IAPPlatformPowerStatus status; + check_ret(iap_platform_get_power_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetPowerBatteryStatePayload); + response->power_state = status.state; /* TODO: convert enum */ + response->battery_level = status.battery_level; + return IAPDisplayRemoteCommandID_RetPowerBatteryState; + } break; + case IAPDisplayRemoteCommandID_GetTrackArtworkTimes: { + read_request(IAPGetTrackArtworkTimesPayload); + const uint16_t count = swap_16(request->artwork_count); + check_ret(count == 0 || count == 1, -IAPAckStatus_ECommandFailed, "not implemented"); + + void* payload = iap_span_alloc(response_span, sizeof(uint32_t) * count); + check_ret(payload != NULL, iap_false); + memset(payload, 0, sizeof(uint32_t) * count); + return IAPDisplayRemoteCommandID_RetTrackArtworkTimes; + } break; + } + break; + case IAPLingoID_ExtendedInterface: + switch(command) { + case IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterInfo: { + alloc_response(IAPReturnCurrentPlayingTrackChapterInfoPayload); + /* no chapters */ + response->count = 0; + response->index = -1; + return IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo; + } break; + case IAPExtendedInterfaceCommandID_GetAudiobookSpeed: { + alloc_response(IAPRetAudiobookSpeedPayload); + response->speed = IAPIPodStateAudiobookSpeeed_Normal; + return IAPExtendedInterfaceCommandID_RetAudiobookSpeed; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo: { + read_request(IAPExtendedGetIndexedPlayingTrackInfoPayload); + check_ret(response_span->size > sizeof(struct IAPExtendedRetIndexedPlayingTrackInfoPayload), -IAPAckStatus_EBadParameter); + ((struct IAPExtendedRetIndexedPlayingTrackInfoPayload*)response_span->ptr)->type = request->type; + switch(request->type) { + case IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo: { + uint32_t length; + uint32_t caps; + struct IAPPlatformTrackInfo info = {.total_ms = &length, .caps = &caps}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload); + response->track_caps = swap_32(caps); + response->track_total_ms = swap_32(length); + response->chapter_count = 0; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_PodcastName: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoPodcastNamePayload, 1); + response->name[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate: { + struct IAPDateTime time; + struct IAPPlatformTrackInfo info = {.release_date = &time}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload); + response->seconds = time.seconds; + response->minutes = time.minute; + response->hours = time.hour; + response->day = time.day; + response->month = time.month; + response->year = swap_16(time.year); + response->weekday = 0; /* TODO: set weekday? */ + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackDescription: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload, 1); + response->info_bits = 0; + response->index = 0; + response->description[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload, 1); + response->info_bits = 0; + response->index = 0; + response->lyrics[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackGenre: { + alloc_response_extra(IAPExtendedRetIndexedPlayingTrackInfoTrackGenrePayload, 1); + response->genre[0] = '\0'; + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackComposer: { + alloc_response(IAPExtendedRetIndexedPlayingTrackInfoTrackComposerPayload); + struct IAPPlatformTrackInfo info = {.composer = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->track_index), &info), -IAPAckStatus_EBadParameter); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo; + } break; + case IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount: { + // IAPArtworkPixelFormats_RGB565LE; + // struct IAPExtendedRetIndexedPlayingTrackInfoTrackArtworkCountPayload payload = { + // .}; + warn("artwork not implemented"); + return -IAPAckStatus_ECommandFailed; + } break; + default: + warn("invalid request type 0x%02" PRIX8, request->type); + return -IAPAckStatus_EBadParameter; + } + } break; + case IAPExtendedInterfaceCommandID_GetArtworkFormats: { + /* same as DisplayRemote::GetArtworkFormats */ + const int32_t ret = handle_command(ctx, IAPLingoID_DisplayRemote, IAPDisplayRemoteCommandID_GetArtworkFormats, request_span, response_span); + check_ret(ret == IAPDisplayRemoteCommandID_RetArtworkFormats, ret); + return IAPExtendedInterfaceCommandID_RetArtworkFormats; + } break; + case IAPExtendedInterfaceCommandID_GetTrackArtworkData: { + const int32_t ret = start_artwork_data(ctx, request_span, iap_true); + check_ret(ret == 0, ret); + /* responded in send_artwork_chunk_cb, no need to do it here */ + response_span->ptr = NULL; + return 0; + } break; + case IAPExtendedInterfaceCommandID_ResetDBSelection: { + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords: { + read_request(IAPGetNumberCategorizedDBRecordsPayload); + uint32_t count; + switch(request->type) { + case IAPDatabaseType_Playlist: { + /* TODO: implement platform callback */ + count = 99; + } break; + case IAPDatabaseType_Track: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + /* track_count is invalid while stopped. + * return non-zero dummy value in this case, because reporting zero tracks + * may cause empty library error. */ + /* TODO: maybe add dedicated platform callback? */ + count = status.state == IAPIPodStatePlayStatus_PlaybackStopped ? 99 : status.track_count; + } break; + default: { + warn("unsupported type 0x%02" PRIX8, request->type); + count = 0; + } break; + } + + alloc_response(IAPReturnNumberCategorizedDBRecordsPayload); + response->count = swap_32(count); + return IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords; + } break; + case IAPExtendedInterfaceCommandID_GetPlayStatus: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPExtendedRetPlayStatusPayload); + response->state = status.state; /* TODO: convert enum */ + response->track_pos_ms = swap_32(status.track_pos_ms); + response->track_total_ms = swap_32(status.track_total_ms); + return IAPExtendedInterfaceCommandID_ReturnPlayStatus; + } break; + case IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackIndex: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPReturnCurrentPlayingTrackIndexPayload); + response->index = swap_32(status.state == IAPIPodStatePlayStatus_PlaybackStopped ? (uint32_t)-1 : status.track_index); + return IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.title = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.artist = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName; + } break; + case IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName: { + read_request(IAPGetIndexedPlayingTrackStringPayload); + struct IAPPlatformTrackInfo info = {.album = response_span}; + check_ret(iap_platform_get_indexed_track_info(ctx, swap_32(request->index), &info), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName; + } break; + case IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification: { + if(request_span->size == sizeof(struct IAPSetPlayStatusChangeNotification1BytePayload)) { + read_request(IAPSetPlayStatusChangeNotification1BytePayload); + ctx->notifications_4 = 0; + if(request->enable) { + ctx->enabled_notifications_4 = 1 << IAPStatusChangeNotificationType_PlaybackStopped | + 1 << IAPStatusChangeNotificationType_TrackIndex | + 1 << IAPStatusChangeNotificationType_PlaybackFEWSeekStop | + 1 << IAPStatusChangeNotificationType_PlaybackREWSeekStop | + 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec | + 1 << IAPStatusChangeNotificationType_ChapterIndex; + } else { + ctx->enabled_notifications_4 = 0; + } + } else if(request_span->size == sizeof(struct IAPSetPlayStatusChangeNotification4BytesPayload)) { + read_request(IAPSetPlayStatusChangeNotification4BytesPayload); + ctx->enabled_notifications_4 = play_stage_change_notification_set_mask_to_type_mask(swap_32(request->mask)); + } + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_PlayCurrentSelection: { + read_request(IAPPlayCurrentSelectionPayload); + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = IAPExtendedInterfaceCommandID_IPodAck, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + iap_platform_control(ctx, IAPPlatformControl_Play, pending); + response_span->ptr = NULL; + return 0; + } break; + case IAPExtendedInterfaceCommandID_PlayControl: { + read_request(IAPPlayControlPayload); + static const int enum_table[][2] = { + {IAPPlayControlCode_TogglePlayPause, IAPPlatformControl_TogglePlayPause}, + {IAPPlayControlCode_Stop, IAPPlatformControl_Stop}, + {IAPPlayControlCode_NextTrack, IAPPlatformControl_Next}, + {IAPPlayControlCode_PrevTrack, IAPPlatformControl_Prev}, + {IAPPlayControlCode_StartFF, -1}, + {IAPPlayControlCode_StartRew, -1}, + {IAPPlayControlCode_EndFFRew, -1}, + {IAPPlayControlCode_Next, IAPPlatformControl_Next}, + {IAPPlayControlCode_Prev, IAPPlatformControl_Prev}, + {IAPPlayControlCode_Play, IAPPlatformControl_Play}, + {IAPPlayControlCode_Pause, IAPPlatformControl_Pause}, + {IAPPlayControlCode_NextChapter, IAPPlatformControl_Next}, + {IAPPlayControlCode_PrevChapter, IAPPlatformControl_Prev}, + {IAPPlayControlCode_ResumeIPod, -1}, + }; + int control = -1; + for(size_t i = 0; i < array_size(enum_table); i += 1) { + if(enum_table[i][0] == request->code) { + control = enum_table[i][1]; + break; + } + } + if(control >= 0) { + const struct IAPPlatformPendingControl pending = { + .req_command = command, + .ack_command = IAPExtendedInterfaceCommandID_IPodAck, + .trans_id = ctx->handling_trans_id, + .lingo = lingo, + }; + iap_platform_control(ctx, control, pending); + response_span->ptr = NULL; + return 0; + } + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetShuffle: { + alloc_response(IAPReturnShufflePayload); + check_ret(iap_platform_get_shuffle_setting(ctx, &response->mode), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnShuffle; + } break; + case IAPExtendedInterfaceCommandID_SetShuffle: { + read_request(IAPSetShufflePayload); + check_ret(iap_platform_set_shuffle_setting(ctx, request->mode), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetRepeat: { + alloc_response(IAPReturnRepeatPayload); + check_ret(iap_platform_get_repeat_setting(ctx, &response->mode), -IAPAckStatus_ECommandFailed); + return IAPExtendedInterfaceCommandID_ReturnRepeat; + } break; + case IAPExtendedInterfaceCommandID_SetRepeat: { + read_request(IAPSetRepeatPayload); + check_ret(iap_platform_set_repeat_setting(ctx, request->mode), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_SetDisplayImage: { + /* TODO: pass downloaded image to user */ + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetNumPlayingTracks: { + struct IAPPlatformPlayStatus status; + check_ret(iap_platform_get_play_status(ctx, &status), -IAPAckStatus_ECommandFailed); + alloc_response(IAPRetNumPlayingTracksPayload); + response->num_playing_tracks = swap_32(status.track_count); + return IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks; + } break; + case IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack: { + read_request(IAPSetCurrentPlayingTrackPayload); + check_ret(iap_platform_set_playing_track(ctx, swap_32(request->index)), -IAPAckStatus_ECommandFailed); + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_SelectSortDBRecord: { + /* ignored */ + return ipod_ack_ext(command, IAPAckStatus_Success, response_span); + } break; + case IAPExtendedInterfaceCommandID_GetColorDisplayImageLimits: { + alloc_response(IAPColorDisplayImageLimit); + response->max_width = 0; + response->max_height = 0; + response->pixel_format = IAPArtworkPixelFormats_RGB565LE; + return IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits; + } break; + } + break; + case IAPLingoID_DigitalAudio: + switch(command) { + case IAPDigitalAudioCommandID_AccessoryAck: { + read_request(IAPAccAckPayload); + response_span->ptr = NULL; + if(request->id == IAPDigitalAudioCommandID_TrackNewAudioAttributes) { + check_ret(ctx->waiting_for_audio_attrs_ack, 0, "unexpected ack"); + ctx->waiting_for_audio_attrs_ack = iap_false; + } + check_ret(request->status == IAPAckStatus_Success, 0); + return 0; + } break; + case IAPDigitalAudioCommandID_RetAccessorySampleRateCaps: { + check_ret(iap_platform_on_acc_samprs_received(ctx, request_span), -IAPAckStatus_ECommandFailed); + response_span->ptr = NULL; /* no response */ + return 0; + } break; + } + break; + } + + return -IAPAckStatus_EUnknownID; +} + +static IAPBool transition_idps_to_auth_cb(struct IAPContext* ctx) { + print("starting accessory authentication"); + ctx->phase = IAPPhase_Auth; + check_ret(_iap_send_packet(ctx, IAPLingoID_General, IAPGeneralCommandID_GetAccessoryAuthenticationInfo, _iap_next_trans_id(ctx), _iap_get_buffer_for_send_payload(ctx).ptr), iap_false); + return iap_true; +} + +static int32_t handle_in_connected(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_IdentifyDeviceLingoes: { + read_request(IAPIdentifyDeviceLingoesPayload); + switch(swap_32(request->options)) { + case IAPIdentifyDeviceLingoesOptions_NoAuth: + break; + case IAPIdentifyDeviceLingoesOptions_DeferAuth: + warn("unsupported option 0x%04" PRIX32, swap_32(request->options)); + return -IAPAckStatus_EBadParameter; + case IAPIdentifyDeviceLingoesOptions_ImmediateAuth: + ctx->on_send_complete = transition_idps_to_auth_cb; + break; + } + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + case IAPGeneralCommandID_StartIDPS: { + ctx->phase = IAPPhase_IDPS; + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } break; + } + break; + } + return -IAPAckStatus_EUnknownID; +} + +static int32_t handle_in_idps(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_SetFIDTokenValues: { + const int ret = _iap_hanlde_set_fid_token_values(request_span, response_span); + check_ret(ret == 0, ret); + return IAPGeneralCommandID_AckFIDTokenValues; + } break; + case IAPGeneralCommandID_EndIDPS: { + read_request(IAPEndIDPSPayload); + check_ret(request->status == IAPEndIDPSStatus_Success, -IAPAckStatus_ECommandFailed); + ctx->on_send_complete = transition_idps_to_auth_cb; + alloc_response(IAPIDPSStatusPayload); + response->status = IAPIDPSStatus_Success; + return IAPGeneralCommandID_IDPSStatus; + } break; + } + break; + } + return iap_false; +} + +static IAPBool send_auth_challenge_sig_cb(struct IAPContext* ctx) { + check_ret(ctx->phase == IAPPhase_Auth, iap_false); + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + struct IAPGetAccAuthSigPayload2p0* request = iap_span_alloc(&request_span, sizeof(*request)); + check_ret(request != NULL, iap_false); + request->retry = 1; + check_ret(_iap_send_packet(ctx, IAPLingoID_General, IAPGeneralCommandID_GetAccessoryAuthenticationSignature, _iap_next_trans_id(ctx), request_span.ptr), iap_false); + return iap_true; +} + +static IAPBool send_sample_rate_caps_cb(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + check_ret(_iap_send_packet(ctx, IAPLingoID_DigitalAudio, IAPDigitalAudioCommandID_GetAccessorySampleRateCaps, _iap_next_trans_id(ctx), request.ptr), iap_false); + return iap_true; +} + +static int32_t handle_in_auth(struct IAPContext* ctx, uint8_t lingo, uint16_t command, struct IAPSpan* request_span, struct IAPSpan* response_span) { + switch(lingo) { + case IAPLingoID_General: + switch(command) { + case IAPGeneralCommandID_RetAccessoryAuthenticationInfo: { + read_request(IAPRetAccAuthInfoPayload2p0); + print("accessory cert %" PRIu8 "/%" PRIu8, request->cert_current_section_index, request->cert_max_section_index); + /* iap_platform_dump_hex(request->ptr, request->size); */ + if(request->cert_current_section_index < request->cert_max_section_index) { + return ipod_ack(command, IAPAckStatus_Success, response_span, IAPGeneralCommandID_IPodAck); + } else { + ctx->on_send_complete = send_auth_challenge_sig_cb; + alloc_response(IAPAckAccAuthInfoPayload); + response->status = IAPAckAccAuthInfoStatus_Supported; + return IAPGeneralCommandID_AckAccessoryAuthenticationInfo; + } + } break; + case IAPGeneralCommandID_RetAccessoryAuthenticationSignature: { + print("accessory signature"); + /* iap_platform_dump_hex(request->ptr, request->size); */ + + alloc_response(IAPAckAccAuthSigPayload); + response->status = IAPAckStatus_Success; + + ctx->phase = IAPPhase_Authed; + ctx->on_send_complete = send_sample_rate_caps_cb; + + return IAPGeneralCommandID_AckAccessoryAuthenticationStatus; + } break; + } + break; + } + return -IAPAckStatus_EUnknownID; +} + +static int32_t build_ipod_ack_response(uint8_t lingo, uint16_t command, uint8_t status, struct IAPSpan* response_span) { + static int32_t ack_commmand_ids[] = { + IAPGeneralCommandID_IPodAck, + -1, + IAPSimpleRemoteCommandID_IPodAck, + IAPDisplayRemoteCommandID_IPodAck, + IAPExtendedInterfaceCommandID_IPodAck, + -1, + IAPUSBHostModeCommandID_IPodAck, + -1, + -1, + IAPSportsCommandID_IPodAck, + IAPDigitalAudioCommandID_IPodAck, + -1, + IAPStorageCommandID_IPodAck, + IAPIPodOutCommandID_IPodAck, + IAPLocationCommandID_IPodAck, + }; + check_ret(lingo < array_size(ack_commmand_ids) && ack_commmand_ids[lingo] >= 0, -1); + switch(lingo) { + case IAPLingoID_General: + case IAPLingoID_SimpleRemote: + case IAPLingoID_DisplayRemote: + case IAPLingoID_USBHostMode: + case IAPLingoID_Sports: + case IAPLingoID_DigitalAudio: + case IAPLingoID_IPodOut: + case IAPLingoID_Location: { + alloc_response(IAPIPodAckPayload); + response->id = command; + response->status = status; + } break; + case IAPLingoID_ExtendedInterface: { + alloc_response(IAPExtendedIPodAckPayload); + response->id = swap_16(command); + response->status = status; + } break; + case IAPLingoID_Storage: { + alloc_response(IAPStorageIPodAckPayload); + response->id = command; + response->status = status; + response->handle = -1; /* TODO: set proper handle */ + } break; + } + return ack_commmand_ids[lingo]; +} + +IAPBool _iap_feed_packet(struct IAPContext* ctx, const uint8_t* const data, const size_t size) { + union { + uint8_t u8; + uint16_t u16; + } buf; + struct IAPSpan span = {(uint8_t*)data, size}; + + /* read sof byte */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + if(buf.u8 == IAP_SYNC_BYTE) { + /* skip sync byte */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + } + check_ret(buf.u8 == IAP_SOF_BYTE, iap_false, "%x != %x", buf.u8, IAP_SOF_BYTE); + const uint8_t* const checksum_range_begin = span.ptr; + /* read size */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + size_t length; + if(buf.u8 == 0) { + /* long packet */ + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + length = buf.u16; + } else { + length = buf.u8; + } + /* we have length, strip span so that it contains lingo,command,payload */ + check_ret(span.size >= length + 1 /* checksum */, iap_false); + span.size = length; + /* verify checksum */ + const uint8_t* const checksum_range_end = span.ptr + span.size + 1 /* checksum */; + uint8_t checksum = 0; + for(const uint8_t* ptr = checksum_range_begin; ptr < checksum_range_end; ptr += 1) { + checksum += *ptr; + } + check_ret(checksum == 0, iap_false); + /* read lingo id */ + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + uint8_t lingo = buf.u8; + /* read command id */ + uint16_t command; + if(lingo == IAPLingoID_ExtendedInterface) { + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + command = buf.u16; + } else { + check_ret(iap_span_read_8(&span, &buf.u8), iap_false); + command = buf.u8; + } + + /* now span contains only payload */ + + /* request handling */ + if(ctx->trans_id_support == TransIDUnknown) { + check_ret(lingo == IAPLingoID_General, iap_false); + if(command == IAPGeneralCommandID_StartIDPS) { + ctx->trans_id_support = TransIDSupported; + } else if(command == IAPGeneralCommandID_IdentifyDeviceLingoes) { + ctx->trans_id_support = TransIDNotSupported; + ctx->handling_trans_id = -1; + } else { + warn("the first command(%02X:%04X) must be StartIDPS or IdentifyDeviceLingoes", lingo, command); + return iap_false; + } + } + if(ctx->trans_id_support == TransIDSupported) { + check_ret(iap_span_read_16(&span, &buf.u16), iap_false); + ctx->handling_trans_id = buf.u16; + } + + if(ctx->opts.enable_packet_dump) { + IAP_LOGF("==== acc ===="); + _iap_dump_packet(lingo, command, ctx->handling_trans_id, span); + } + + struct IAPSpan response = _iap_get_buffer_for_send_payload(ctx); + int32_t ret = handle_command(ctx, lingo, command, &span, &response); + if(response.ptr == NULL) { + /* handler disabled response */ + return iap_true; + } + if(ret >= 0) { + /* handled successfully */ + goto respond; + } + if(ret != -IAPAckStatus_EUnknownID) { + /* handled, but error */ + goto error_ack; + } + + /* not a standard request, try authentication handlers */ + ret = -IAPAckStatus_EBadParameter; + response = _iap_get_buffer_for_send_payload(ctx); + switch(ctx->phase) { + case IAPPhase_Connected: + ret = handle_in_connected(ctx, lingo, command, &span, &response); + break; + case IAPPhase_IDPS: + ret = handle_in_idps(ctx, lingo, command, &span, &response); + break; + case IAPPhase_Auth: + ret = handle_in_auth(ctx, lingo, command, &span, &response); + break; + } + if(response.ptr == NULL) { + /* handler disabled response */ + return iap_true; + } + if(ret >= 0) { + /* handled successfully */ + goto respond; + } +error_ack: + /* handling failed, replace response with ipod ack */ + warn("command handling failed 0x%02X(%s):0x%04X", lingo, _iap_lingo_str(lingo), command); + response = _iap_get_buffer_for_send_payload(ctx); + ret = build_ipod_ack_response(lingo, command, -ret, &response); + check_ret(ret >= 0, iap_false); +respond: + check_ret(_iap_send_packet(ctx, lingo, ret, ctx->handling_trans_id, response.ptr), iap_false); + return iap_true; +} + +struct IAPSpan _iap_get_buffer_for_send_payload(struct IAPContext* ctx) { + const size_t header_size = 1 /* sof */ + + 3 /* long format length */ + + 1 /* lingo */ + + 2 /* largest command id */ + + 2 /* trans id */; + const size_t footer_size = 1 /* checksum */; + + struct IAPSpan buf = {ctx->send_buf + header_size, SEND_BUFFER_SIZE - header_size - footer_size}; + return buf; +} + +int32_t _iap_next_trans_id(struct IAPContext* ctx) { + if(ctx->trans_id_support == TransIDSupported) { + return ctx->trans_id += 1; + } else { + return -1; + } +} + +IAPBool _iap_send_packet(struct IAPContext* ctx, uint8_t lingo, uint16_t command, int32_t trans_id, uint8_t* final_ptr) { + uint8_t* ptr = _iap_get_buffer_for_send_payload(ctx).ptr; + size_t payload_size = final_ptr - ptr; + + if(ctx->opts.enable_packet_dump) { + IAP_LOGF("==== dev ===="); + struct IAPSpan payload_span = { + .ptr = _iap_get_buffer_for_send_payload(ctx).ptr, + .size = payload_size, + }; + _iap_dump_packet(lingo, command, trans_id, payload_span); + } + +#define pack_8(val) \ + ptr -= 1; \ + *(uint8_t*)ptr = val; +#define pack_16(val) \ + ptr -= 2; \ + *(uu16*)ptr = swap_16(val); + + /* fill header in reverse order */ + /* trans id */ + if(trans_id >= 0) { + pack_16(trans_id); + } + /* command id */ + if(lingo == IAPLingoID_ExtendedInterface) { + pack_16(command); + } else { + pack_8(command); + } + /* lingo */ + pack_8(lingo); + /* length */ + const uint16_t length = 1 /*lingo*/ + (lingo == IAPLingoID_ExtendedInterface ? 2 : 1) /*command*/ + (trans_id >= 0 ? 2 : 0) + payload_size; + if(length <= 0xFC) { + pack_8(length); + } else { + pack_16(length); + pack_8(0); + } + /* sof */ + pack_8(IAP_SOF_BYTE); + + /* set checksum */ + uint8_t checksum = 0; + for(uint8_t* p = ptr + 1 /* exclude sof byte */; p < final_ptr; p += 1) { + checksum += *p; + } + checksum *= -1; + *final_ptr = checksum; + + check_ret(_iap_send_hid_reports(ctx, ptr - ctx->send_buf, final_ptr - ctx->send_buf + 1 /* include checksum */), iap_false); + return iap_true; +} + +static IAPBool push_active_event(struct IAPContext* ctx, struct IAPActiveEvent event) { + if(!ctx->send_busy) { + check_ret(event.callback(ctx, &event), iap_false); + return iap_true; + } + + for(size_t i = 0; i < array_size(ctx->active_events); i += 1) { + if(ctx->active_events[i].callback == NULL) { + ctx->active_events[i] = event; + return iap_true; + } + } + return iap_false; +} + +static IAPBool process_select_sampr(struct IAPContext* ctx, struct IAPActiveEvent* event) { + if(ctx->waiting_for_audio_attrs_ack) { + IAP_ERRORF("another sampr request pending"); + } + ctx->waiting_for_audio_attrs_ack = iap_true; + + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + struct IAPTrackNewAudioAttributesPayload* request = iap_span_alloc(&request_span, sizeof(*request)); + request->sample_rate = swap_32(event->sampr); + request->sound_check = 0; + request->volume_adjustment = 0; + check_ret(_iap_send_packet(ctx, IAPLingoID_DigitalAudio, IAPDigitalAudioCommandID_TrackNewAudioAttributes, _iap_next_trans_id(ctx), request_span.ptr), iap_false); + return iap_true; +} + +IAPBool iap_select_sampr(struct IAPContext* ctx, uint32_t sampr) { + struct IAPActiveEvent event = { + .callback = process_select_sampr, + .sampr = sampr, + }; + check_ret(push_active_event(ctx, event), iap_false); + return iap_true; +} + +static IAPBool process_control_response(struct IAPContext* ctx, struct IAPActiveEvent* event) { + struct IAPSpan request_span = _iap_get_buffer_for_send_payload(ctx); + const struct IAPPlatformPendingControl* ctrl = &event->control_response.control; + const uint8_t status = event->control_response.result ? IAPAckStatus_Success : IAPAckStatus_ECommandFailed; + if(ctrl->lingo == IAPLingoID_ExtendedInterface) { + ipod_ack_ext(ctrl->req_command, status, &request_span); + } else { + ipod_ack(ctrl->req_command, status, &request_span, ctrl->ack_command); + } + check_ret(_iap_send_packet(ctx, ctrl->lingo, ctrl->ack_command, ctrl->trans_id, request_span.ptr), iap_false); + return iap_true; +} + +IAPBool iap_control_response(struct IAPContext* ctx, struct IAPPlatformPendingControl pending, IAPBool result) { + struct IAPActiveEvent event = { + .callback = process_control_response, + .control_response = {pending, result}, + }; + if(pending.lingo == IAPLingoID_SimpleRemote && pending.req_command == IAPSimpleRemoteCommandID_ContextButtonStatus) { + /* no response */ + return iap_true; + } + check_ret(push_active_event(ctx, event), iap_false); + + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/iap.h b/firmware/usbstack/iap/libiap/iap.h new file mode 100644 index 0000000000..86ad0412e1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/iap.h @@ -0,0 +1,55 @@ +#pragma once +#include "context.h" +#include "span.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* iap.c */ +IAPBool iap_init_ctx(struct IAPContext* ctx, struct IAPOpts opts, void* platform); +IAPBool iap_deinit_ctx(struct IAPContext* ctx); +IAPBool _iap_feed_packet(struct IAPContext* ctx, const uint8_t* data, size_t size); +struct IAPSpan _iap_get_buffer_for_send_payload(struct IAPContext* ctx); +int32_t _iap_next_trans_id(struct IAPContext* ctx); +IAPBool _iap_send_packet(struct IAPContext* ctx, uint8_t lingo, uint16_t command, int32_t trans_id, uint8_t* final_ptr); +/* must be called after iap_platform_on_acc_samprs_received */ +IAPBool iap_select_sampr(struct IAPContext* ctx, uint32_t sampr); +IAPBool iap_control_response(struct IAPContext* ctx, struct IAPPlatformPendingControl pending, IAPBool result); + +/* hid.c */ +IAPBool iap_feed_hid_report(struct IAPContext* ctx, const uint8_t* data, size_t size); +IAPBool iap_notify_send_complete(struct IAPContext* ctx); +IAPBool _iap_send_hid_reports(struct IAPContext* ctx, size_t begin, size_t end); /* data is passed by ctx->send_buf */ +IAPBool _iap_send_next_report(struct IAPContext* ctx); + +/* fid-token-values.c */ +int _iap_hanlde_set_fid_token_values(struct IAPSpan* request, struct IAPSpan* response); + +/* notification.c */ +void iap_notify_track_time_position(struct IAPContext* ctx, uint32_t pos_ms); +void iap_notify_track_playback_index(struct IAPContext* ctx, uint32_t index); +void iap_notify_track_caps(struct IAPContext* ctx, uint32_t caps); +void iap_notify_tracks_count(struct IAPContext* ctx, uint32_t count); +void iap_notify_play_status(struct IAPContext* ctx, uint8_t status /* IAPIPodStatePlayStatus */); +void iap_notify_volume(struct IAPContext* ctx, uint8_t volume, IAPBool muted); +void iap_notify_power_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStatePowerState */, uint8_t battery_level); +void iap_notify_shuffle_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStateShuffleSettingState */); +void iap_notify_repeat_state(struct IAPContext* ctx, uint8_t state /* IAPIPodStateRepeatSettingState */); +void iap_notify_time_setting(struct IAPContext* ctx, const struct IAPDateTime* time); +void iap_notify_hold_switch_state(struct IAPContext* ctx, uint8_t state); + +IAPBool iap_periodic_tick(struct IAPContext* ctx); /* call every 100ms */ + +IAPBool _iap_flush_notification(struct IAPContext* ctx); + +/* debug.c */ +const char* _iap_lingo_str(uint8_t lingo); +const char* _iap_command_str(uint8_t lingo, uint16_t command); +IAPBool _iap_span_is_str(const struct IAPSpan* span); +const char* _iap_span_as_str(const struct IAPSpan* span); +void _iap_dump_packet(uint8_t lingo, uint16_t command, int32_t trans_id, struct IAPSpan span); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/macros.h b/firmware/usbstack/iap/libiap/macros.h new file mode 100644 index 0000000000..f2cab9bb67 --- /dev/null +++ b/firmware/usbstack/iap/libiap/macros.h @@ -0,0 +1,27 @@ +#pragma once +#include "../platform-macros.h" + +#if !defined(IAP_LOGF) +#define IAP_LOGF_MUTED +#define IAP_LOGF(...) +#endif +#define print(fmt, ...) IAP_LOGF("%s:%d: " fmt, __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); + +#if !defined(IAP_ERRORF) +#define IAP_ERRORF_MUTED +#define IAP_ERRORF(...) +#endif +#define warn(fmt, ...) IAP_ERRORF("%s:%d: " fmt, __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); + +#define check_act(cond, act, ...) \ + if(!(cond)) { \ + warn("assertion failed" __VA_OPT__(": " __VA_ARGS__)); \ + act; \ + } + +#define check_ret(cond, ret, ...) check_act(cond, return ret, __VA_ARGS__) + +#define array_size(arr) (sizeof(arr) / sizeof(arr[0])) + +#define max(a, b) (a > b ? a : b) +#define min(a, b) (a < b ? a : b) diff --git a/firmware/usbstack/iap/libiap/notification.c b/firmware/usbstack/iap/libiap/notification.c new file mode 100644 index 0000000000..2d12f4ca35 --- /dev/null +++ b/firmware/usbstack/iap/libiap/notification.c @@ -0,0 +1,200 @@ +#include "context.h" +#include "endian.h" +#include "iap.h" +#include "macros.h" +#include "spec/iap.h" + +void iap_notify_track_time_position(struct IAPContext* ctx, uint32_t pos_ms) { + ctx->notification_data.track_time_position_ms = pos_ms; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackTimePositionMSec; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackTimePositionSec; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetMSec; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackTimeOffsetSec; +} + +void iap_notify_track_playback_index(struct IAPContext* ctx, uint32_t index) { + ctx->notification_data.track_playback_index = index; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackPlaybackIndex; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_TrackIndex; +} + +void iap_notify_track_caps(struct IAPContext* ctx, uint32_t caps) { + ctx->notification_data.track_caps = caps; + ctx->notifications_3 |= 1 << IAPIPodStateType_TrackCaps; +} + +void iap_notify_tracks_count(struct IAPContext* ctx, uint32_t count) { + ctx->notification_data.tracks_count = count; + ctx->notifications_3 |= 1 << IAPIPodStateType_PlaybackEngineContents; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackEngineContentsChanged; +} + +void iap_notify_play_status(struct IAPContext* ctx, uint8_t status) { + ctx->notification_data.play_status = status; + ctx->notifications_3 |= 1 << IAPIPodStateType_PlayStatus; + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackStatusExtended; + if(status == IAPIPodStatePlayStatus_PlaybackStopped) { + ctx->notifications_4 |= 1 << IAPStatusChangeNotificationType_PlaybackStopped; + } + /* should set Playback{FEW,REW}SeekStop, but no way to find which from EndFastForwardRewind */ +} + +void iap_notify_volume(struct IAPContext* ctx, uint8_t volume, IAPBool muted) { + ctx->notification_data.volume = volume; + ctx->notification_data.mute_state = muted; + ctx->notifications_3 |= 1 << IAPIPodStateType_Volume; +} + +void iap_notify_power_state(struct IAPContext* ctx, uint8_t state, uint8_t battery_level) { + ctx->notification_data.power_state = state; + ctx->notification_data.battery_level = battery_level; + ctx->notifications_3 |= 1 << IAPIPodStateType_Power; +} + +void iap_notify_shuffle_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.shuffle_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_ShuffleSetting; +} + +void iap_notify_repeat_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.repeat_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_RepeatSetting; +} + +void iap_notify_time_setting(struct IAPContext* ctx, const struct IAPDateTime* time) { + ctx->notification_data.time_setting = *time; + ctx->notifications_3 |= 1 << IAPIPodStateType_DateTimeSetting; +} + +void iap_notify_hold_switch_state(struct IAPContext* ctx, uint8_t state) { + ctx->notification_data.hold_switch_state = state; + ctx->notifications_3 |= 1 << IAPIPodStateType_HoldSwitchState; +} + +IAPBool iap_periodic_tick(struct IAPContext* ctx) { + if(ctx->waiting_for_audio_attrs_ack) { + return iap_true; + } + + ctx->flushing_notifications = iap_true; + ctx->notification_tick += 1; + if(!ctx->send_busy) { + check_ret(_iap_flush_notification(ctx), iap_false); + } + return iap_true; +} + +static uint8_t play_status_to_extended(uint8_t status) { + switch(status) { + case IAPIPodStatePlayStatus_PlaybackStopped: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped; + case IAPIPodStatePlayStatus_Playing: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Playing; + case IAPIPodStatePlayStatus_PlaybackPaused: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Paused; + case IAPIPodStatePlayStatus_FastForward: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWSeekStarted; + case IAPIPodStatePlayStatus_FastRewind: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_REWSeekStarted; + case IAPIPodStatePlayStatus_EndFastForwardRewind: + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWREWSeekStopped; + } + /* unreachable */ + return IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped; +} + +#define send_notify_3(PayloadType, StateType, set) \ + if(ctx->enabled_notifications_3 & ctx->notifications_3 & (1 << StateType)) { \ + struct PayloadType* payload = iap_span_alloc(&request, sizeof(*payload)); \ + check_ret(payload != NULL, iap_false); \ + payload->type = StateType; \ + set; \ + check_ret(_iap_send_packet(ctx, IAPLingoID_DisplayRemote, IAPDisplayRemoteCommandID_RemoteEventNotification, _iap_next_trans_id(ctx), request.ptr), iap_false); \ + ctx->notifications_3 &= ~(1 << StateType); \ + return iap_true; \ + } + +#define send_notify_4(PayloadType, StateType, set) \ + if(ctx->enabled_notifications_4 & ctx->notifications_4 & (1 << StateType)) { \ + print("notification " #StateType); \ + struct PayloadType* payload = iap_span_alloc(&request, sizeof(*payload)); \ + check_ret(payload != NULL, iap_false); \ + payload->type = StateType; \ + set; \ + check_ret(_iap_send_packet(ctx, IAPLingoID_ExtendedInterface, IAPExtendedInterfaceCommandID_PlayStatusChangeNotification, _iap_next_trans_id(ctx), request.ptr), iap_false); \ + ctx->notifications_4 &= ~(1 << StateType); \ + return iap_true; \ + } + +IAPBool _iap_flush_notification(struct IAPContext* ctx) { + struct IAPSpan request = _iap_get_buffer_for_send_payload(ctx); + + /* [1] P.257: + * Notifications for enabled events are sent every 500 ms, + * with the exception of volume change notifications, which are sent every 100 ms. + */ + if(ctx->notification_tick % 5 != 0) { + goto freq_events; + } + + send_notify_3(IAPIPodStateTrackTimePositionMSecPayload, + IAPIPodStateType_TrackTimePositionMSec, + payload->position_ms = swap_32(ctx->notification_data.track_time_position_ms)); + send_notify_3(IAPIPodStateTrackPlaybackIndexPayload, + IAPIPodStateType_TrackPlaybackIndex, + payload->index = swap_32(ctx->notification_data.track_playback_index)); + send_notify_3(IAPIPodStateTrackCapsPayload, + IAPIPodStateType_TrackCaps, + payload->caps = swap_32(ctx->notification_data.track_caps)); + send_notify_3(IAPIPodStateTrackTimePositionSecPayload, + IAPIPodStateType_TrackTimePositionSec, + payload->position_s = swap_16(ctx->notification_data.track_time_position_ms / 1000)); + send_notify_3(IAPIPodStatePlaybackEngineContentsPayload, + IAPIPodStateType_PlaybackEngineContents, + payload->count = swap_32(ctx->notification_data.tracks_count)); + send_notify_3(IAPIPodStatePlayStatusPayload, + IAPIPodStateType_PlayStatus, + payload->status = ctx->notification_data.play_status); + send_notify_3(IAPIPodStatePowerPayload, + IAPIPodStateType_Power, + payload->power_state = ctx->notification_data.power_state; + payload->battery_level = ctx->notification_data.battery_level); + send_notify_3(IAPIPodStateShuffleSettingPayload, + IAPIPodStateType_ShuffleSetting, + payload->shuffle_state = ctx->notification_data.shuffle_state); + send_notify_3(IAPIPodStateRepeatSettingPayload, + IAPIPodStateType_RepeatSetting, + payload->repeat_state = ctx->notification_data.repeat_state); + send_notify_3(IAPIPodStateDateTimeSettingPayload, + IAPIPodStateType_DateTimeSetting, + payload->year = swap_16(ctx->notification_data.time_setting.year); + payload->month = ctx->notification_data.time_setting.month; + payload->day = ctx->notification_data.time_setting.day; + payload->hour = ctx->notification_data.time_setting.hour; + payload->minute = ctx->notification_data.time_setting.minute); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackStoppedPayload, + IAPStatusChangeNotificationType_PlaybackStopped, ); + send_notify_4(IAPPlayStatusChangeNotificationTrackIndexPayload, + IAPStatusChangeNotificationType_TrackIndex, + payload->index = swap_32(ctx->notification_data.track_playback_index)); + send_notify_4(IAPPlayStatusChangeNotificationTrackTimeOffsetMSecPayload, + IAPStatusChangeNotificationType_TrackTimeOffsetMSec, + payload->offset_ms = swap_32(ctx->notification_data.track_time_position_ms)); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackStatusExtendedPayload, + IAPStatusChangeNotificationType_PlaybackStatusExtended, + payload->state = play_status_to_extended(ctx->notification_data.play_status)); + send_notify_4(IAPPlayStatusChangeNotificationTrackTimeOffsetSecPayload, + IAPStatusChangeNotificationType_TrackTimeOffsetSec, + payload->offset_s = swap_32(ctx->notification_data.track_time_position_ms / 1000)); + send_notify_4(IAPPlayStatusChangeNotificationPlaybackEngineContentsChangedPayload, + IAPStatusChangeNotificationType_PlaybackEngineContentsChanged, + payload->count = swap_32(ctx->notification_data.tracks_count)); +freq_events: + send_notify_3(IAPIPodStateVolumePayload, + IAPIPodStateType_Volume, + payload->mute_state = ctx->notification_data.mute_state; + payload->ui_volume = ctx->notification_data.volume); + + ctx->flushing_notifications = iap_false; + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/notification.h b/firmware/usbstack/iap/libiap/notification.h new file mode 100644 index 0000000000..977f380b09 --- /dev/null +++ b/firmware/usbstack/iap/libiap/notification.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#include "datetime.h" + +struct _IAPNotifyState { + uint32_t track_time_position_ms; + uint32_t track_playback_index; + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t tracks_count; + uint8_t play_status; /* IAPIPodStatePlayStatus */ + uint8_t mute_state; + uint8_t volume; + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; + uint8_t shuffle_state; /* IAPIPodStateShuffleSettingState */ + uint8_t repeat_state; /* IAPIPodStateRepeatSettingState */ + struct IAPDateTime time_setting; + uint8_t hold_switch_state; +}; diff --git a/firmware/usbstack/iap/libiap/pack-util.h b/firmware/usbstack/iap/libiap/pack-util.h new file mode 100644 index 0000000000..bc5dae8fc8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/pack-util.h @@ -0,0 +1,41 @@ +#include +#include + +#include "bool.h" +#include "macros.h" + +__attribute__((unused)) static IAPBool pack_u8(uint8_t** data, size_t* size, uint8_t value) { + check_ret(*size >= 1, iap_false); + (*data)[0] = value; + *data += 1; + *size -= 1; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_u16(uint8_t** data, size_t* size, uint16_t value) { + check_ret(*size >= 2, iap_false); + (*data)[0] = value >> 8; + (*data)[1] = value; + *data += 2; + *size -= 2; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_u32(uint8_t** data, size_t* size, uint32_t value) { + check_ret(*size >= 2, iap_false); + (*data)[0] = value >> 24; + (*data)[1] = value >> 16; + (*data)[2] = value >> 8; + (*data)[3] = value; + *data += 4; + *size -= 4; + return iap_true; +} + +__attribute__((unused)) static IAPBool pack_data(uint8_t** data, size_t* size, const void* payload, size_t payload_size) { + check_ret(*size >= payload_size, iap_false); + memcpy(*data, payload, payload_size); + *data += payload_size; + *size -= payload_size; + return iap_true; +} diff --git a/firmware/usbstack/iap/libiap/platform.h b/firmware/usbstack/iap/libiap/platform.h new file mode 100644 index 0000000000..880f6f4c58 --- /dev/null +++ b/firmware/usbstack/iap/libiap/platform.h @@ -0,0 +1,116 @@ +#pragma once +#include +#include + +#include "datetime.h" +#include "span.h" +#include "spec/iap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct IAPContext; + +/* iap_platform_malloc */ +enum IAPPlatformMallocFlags { + IAPPlatformMallocFlags_Uncached = 1 << 0, +}; + +/* iap_platform_get_play_status */ +struct IAPPlatformPlayStatus { + uint32_t track_total_ms; + uint32_t track_pos_ms; + uint32_t track_index; + uint32_t track_count; + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint8_t state; /* IAPIPodStatePlayStatus */ +}; + +/* iap_platform_control */ +enum IAPPlatformControl { + IAPPlatformControl_TogglePlayPause, + IAPPlatformControl_Play, + IAPPlatformControl_Pause, + IAPPlatformControl_Stop, + IAPPlatformControl_Next, + IAPPlatformControl_Prev, + IAPPlatformControl_VolumeUp, + IAPPlatformControl_VolumeDown, + IAPPlatformControl_ToggleMute, +}; + +struct IAPPlatformPendingControl { + uint16_t req_command; + uint16_t ack_command; + int32_t trans_id; + uint8_t lingo; +}; + +/* iap_platform_get_volume */ +struct IAPPlatformVolumeStatus { + uint8_t volume; + IAPBool muted; +}; + +/* iap_platform_get_power_status */ +struct IAPPlatformPowerStatus { + uint8_t state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +}; + +/* iap_platform_get_indexed_track_info */ +struct IAPPlatformTrackInfo { + uint32_t* total_ms; + uint32_t* caps; /* IAPIPodStateTrackCapBits */ + struct IAPDateTime* release_date; + struct IAPSpan* artist; + struct IAPSpan* composer; + struct IAPSpan* album; + struct IAPSpan* title; +}; + +/* iap_platform_open_artwork */ +struct IAPPlatformArtwork { + uint16_t width; /* user */ + uint16_t height; /* user */ + IAPBool color; /* user */ + IAPBool valid; /* library */ + uintptr_t opaque; /* user */ +}; + +/* library routines */ +void* iap_platform_malloc(struct IAPContext* iap_ctx, size_t size, int flags); +void iap_platform_free(struct IAPContext* iap_ctx, void* ptr); +int iap_platform_send_hid_report(struct IAPContext* iap_ctx, const void* ptr, size_t size); + +/* system info */ +IAPBool iap_platform_get_ipod_serial_num(struct IAPContext* iap_ctx, struct IAPSpan* serial); + +/* audio controls */ +IAPBool iap_platform_get_play_status(struct IAPContext* iap_ctx, struct IAPPlatformPlayStatus* status); +void iap_platform_control(struct IAPContext* iap_ctx, enum IAPPlatformControl control, struct IAPPlatformPendingControl pending); +IAPBool iap_platform_get_volume(struct IAPContext* iap_ctx, struct IAPPlatformVolumeStatus* status); +IAPBool iap_platform_get_power_status(struct IAPContext* iap_ctx, struct IAPPlatformPowerStatus* status); +IAPBool iap_platform_get_shuffle_setting(struct IAPContext* iap_ctx, uint8_t* status /* IAPIPodStateShuffleSettingState */); +IAPBool iap_platform_set_shuffle_setting(struct IAPContext* iap_ctx, uint8_t status /* IAPIPodStateShuffleSettingState */); +IAPBool iap_platform_get_repeat_setting(struct IAPContext* iap_ctx, uint8_t* status /* IAPIPodStateRepeatSettingState */); +IAPBool iap_platform_set_repeat_setting(struct IAPContext* iap_ctx, uint8_t status /* IAPIPodStateRepeatSettingState */); +IAPBool iap_platform_get_date_time(struct IAPContext* iap_ctx, struct IAPDateTime* time); +IAPBool iap_platform_get_backlight_level(struct IAPContext* iap_ctx, uint8_t* level); +IAPBool iap_platform_get_hold_switch_state(struct IAPContext* iap_ctx, IAPBool* state); +IAPBool iap_platform_get_indexed_track_info(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformTrackInfo* info); +IAPBool iap_platform_set_playing_track(struct IAPContext* iap_ctx, uint32_t index); +IAPBool iap_platform_open_artwork(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformArtwork* artwork); +IAPBool iap_platform_get_artwork_ptr(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork, struct IAPSpan* span); +IAPBool iap_platform_close_artwork(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork); + +/* other callbacks */ +IAPBool iap_platform_on_acc_samprs_received(struct IAPContext* iap_ctx, struct IAPSpan* samprs); + +/* debugging */ +void iap_platform_dump_hex(const void* ptr, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/span.c b/firmware/usbstack/iap/libiap/span.c new file mode 100644 index 0000000000..98c16989ac --- /dev/null +++ b/firmware/usbstack/iap/libiap/span.c @@ -0,0 +1,46 @@ +#include + +#include "endian.h" +#include "macros.h" +#include "span.h" +#include "unaligned.h" + +const void* iap_span_read(struct IAPSpan* span, size_t count) { + check_ret(span->size >= count, NULL); + span->ptr += count; + span->size -= count; + return span->ptr - count; +} + +void* iap_span_alloc(struct IAPSpan* span, size_t count) __attribute__((alias("iap_span_read"))); + +IAPBool iap_span_append(struct IAPSpan* span, const void* ptr, size_t size) { + void* dest = iap_span_alloc(span, size); + check_ret(dest != NULL, iap_false); + memcpy(dest, ptr, size); + return iap_true; +} + +#define iap_span_template(width) \ + IAPBool iap_span_peek_##width(struct IAPSpan* span, uint##width##_t* value) { \ + check_ret(span->size >= width / 8, iap_false); \ + *value = swap_##width(*(uu##width*)span->ptr); \ + return iap_true; \ + } \ + IAPBool iap_span_read_##width(struct IAPSpan* span, uint##width##_t* value) { \ + const uu##width* ptr = iap_span_read(span, width / 8); \ + check_ret(ptr != NULL, iap_false); \ + *value = swap_##width(*ptr); \ + return iap_true; \ + } \ + IAPBool iap_span_write_##width(struct IAPSpan* span, uint##width##_t value) { \ + uint##width##_t* ptr = iap_span_alloc(span, width / 8); \ + check_ret(ptr != NULL, iap_false); \ + *ptr = swap_##width(value); \ + return iap_true; \ + } +iap_span_template(8); +iap_span_template(16); +iap_span_template(32); +iap_span_template(64); +#undef iap_span_template diff --git a/firmware/usbstack/iap/libiap/span.h b/firmware/usbstack/iap/libiap/span.h new file mode 100644 index 0000000000..ddb5b084f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/span.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bool.h" + +struct IAPSpan { + uint8_t* ptr; + size_t size; +}; + +const void* iap_span_read(struct IAPSpan* span, size_t count); +void* iap_span_alloc(struct IAPSpan* span, size_t count); +IAPBool iap_span_append(struct IAPSpan* span, const void* ptr, size_t size); + +#define iap_span_template(width) \ + IAPBool iap_span_peek_##width(struct IAPSpan* span, uint##width##_t* value); \ + IAPBool iap_span_read_##width(struct IAPSpan* span, uint##width##_t* value); \ + IAPBool iap_span_write_##width(struct IAPSpan* span, uint##width##_t value) +iap_span_template(8); +iap_span_template(16); +iap_span_template(32); +iap_span_template(64); +#undef iap_span_template + +#ifdef __cplusplus +} +#endif diff --git a/firmware/usbstack/iap/libiap/spec/hid.h b/firmware/usbstack/iap/libiap/spec/hid.h new file mode 100644 index 0000000000..a2c79b0fba --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/hid.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [2] P.56 Table 3-2 Link control byte usage */ +enum IAPHIDReportLinkControlBits { + IAPHIDReportLinkControlBits_Continue = 1 << 0, + IAPHIDReportLinkControlBits_MoreToFollow = 1 << 1, +}; + +struct IAPHIDReport { + uint8_t report_id; + uint8_t link_control; /* IAPHIDReportLinkControlBits */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/iap.h b/firmware/usbstack/iap/libiap/spec/iap.h new file mode 100644 index 0000000000..fe1d51ebb8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/iap.h @@ -0,0 +1,54 @@ +#pragma once +/* References: + * [1]: MFi Accessory Firmware Specification R46 + * [2]: MFI Accessory Hardware Specification R9 + * [3]: MFi Accessory Interface Specification For Apple Devices R2 + */ + +#define IAP_SYNC_BYTE 0xFF +#define IAP_SOF_BYTE 0x55 + +/* [1] P.109 Table 2-10 iAP command packet format + * | name | size | description | + * | sync | 0 or 1 | IAP_SYNC_BYTE, exists if UART transport is used | + * | sof | 1 | IAP_SOF_BYTE | + * | length | 1 or 3 | 0xNN or 0x00NNNN. sum of length of {lingo,command,trans}_id,payload | + * | lingo_id | 1 | IAP_LINGO_ID, lingo identifier | + * | command_id | 1 or 2 | 2 bytes long if lingo == 4 | + * | trans_id | 0 or 2 | exists for some commands | + * | payload | N | data | + * | checksum | 1 | crc checksum | + */ + +/* [1] P.211 Table 4-1 Additional iAP lingoes */ +enum IAPLingoID { + IAPLingoID_General = 0x00, + IAPLingoID_Microphone = 0x01, + IAPLingoID_SimpleRemote = 0x02, + IAPLingoID_DisplayRemote = 0x03, + IAPLingoID_ExtendedInterface = 0x04, + IAPLingoID_AccessoryPower = 0x05, + IAPLingoID_USBHostMode = 0x06, + IAPLingoID_RFTuner = 0x07, + IAPLingoID_AccessoryEqualizer = 0x08, + IAPLingoID_Sports = 0x09, + IAPLingoID_DigitalAudio = 0x0A, + IAPLingoID_Storage = 0x0C, + IAPLingoID_IPodOut = 0x0D, + IAPLingoID_Location = 0x0E, +}; + +#include "lingoes/accessory-equalizer.h" +#include "lingoes/accessory-power.h" +#include "lingoes/digital-audio.h" +#include "lingoes/display-remote.h" +#include "lingoes/extended-interface.h" +#include "lingoes/general.h" +#include "lingoes/ipod-out.h" +#include "lingoes/location.h" +#include "lingoes/microphone.h" +#include "lingoes/rf-tuner.h" +#include "lingoes/simple-remote.h" +#include "lingoes/sports.h" +#include "lingoes/storage.h" +#include "lingoes/usb-host-mode.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h new file mode 100644 index 0000000000..f34386a5ae --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.331 Table 4-200 Accessory Equalizer lingo command summary */ +enum IAPAccessoryEqualizerCommandID { + IAPAccessoryEqualizerCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPAccessoryEqualizerCommandID_GetCurrentEQIndex = 0x01, /* from dev, no payload */ + IAPAccessoryEqualizerCommandID_RetCurrentEQIndex = 0x02, /* from acc, current-eq-index.h */ + IAPAccessoryEqualizerCommandID_SetCurrentEQIndex = 0x03, /* from dev, current-eq-index.h */ + IAPAccessoryEqualizerCommandID_GetEQSettingCount = 0x04, /* from dev, no payload */ + IAPAccessoryEqualizerCommandID_RetEQSettingCount = 0x05, /* from acc, eq-setting-count.h */ + IAPAccessoryEqualizerCommandID_GetEQIndexName = 0x06, /* from dev, */ + IAPAccessoryEqualizerCommandID_RetEQIndexName = 0x07, /* from acc, */ +}; + +#include "accessory-equalizer/current-eq-index.h" +#include "accessory-equalizer/eq-index-name.h" +#include "accessory-equalizer/eq-setting-count.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h new file mode 100644 index 0000000000..4cd2fc0845 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/current-eq-index.h @@ -0,0 +1,10 @@ +#pragma once +#include + +struct IAPRetCurrentEQIndexPayload { + uint8_t index; +} __attribute__((packed)); + +struct IAPSetCurrentEQIndexPayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h new file mode 100644 index 0000000000..4d7f0a4cc5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-index-name.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAGetEQIndexNamePayload { + uint8_t index; +} __attribute__((packed)); + +struct IARetEQIndexNamePayload { + uint8_t index; + char name[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h new file mode 100644 index 0000000000..b798803c6e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-equalizer/eq-setting-count.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetEQSettingCountPayload { + uint8_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h new file mode 100644 index 0000000000..040040a9ee --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/accessory-power.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.548 Table C-37 Accessory Power lingo command summary */ +enum IAPAccessoryPowerCommandID { + IAPAccessoryPowerCommandID_BeginHighPower = 0x02, + IAPAccessoryPowerCommandID_EndHighPower = 0x03, +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h new file mode 100644 index 0000000000..804c7db444 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.346 Table 4-232 Digital Audio lingo command summary */ +enum IAPDigitalAudioCommandID { + IAPDigitalAudioCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPDigitalAudioCommandID_IPodAck = 0x01, /* from dev, general/ipod-ack.h */ + IAPDigitalAudioCommandID_GetAccessorySampleRateCaps = 0x02, /* from dev, no payload */ + IAPDigitalAudioCommandID_RetAccessorySampleRateCaps = 0x03, /* from acc, accessory-sample-rate-caps.h */ + IAPDigitalAudioCommandID_TrackNewAudioAttributes = 0x04, /* from acc, track-new-audio-attributes.h */ + IAPDigitalAudioCommandID_SetVideoDelay = 0x05, /* from acc, set-video-delay.h */ +}; + +#include "digital-audio/accessory-sample-rate-caps.h" +#include "digital-audio/set-video-delay.h" +#include "digital-audio/track-new-audio-attributes.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h new file mode 100644 index 0000000000..4cda0456b2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/accessory-sample-rate-caps.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPRetAccessorySampleRateCapsPayload { + uint32_t sample_rates[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h new file mode 100644 index 0000000000..851040d3cd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/set-video-delay.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetVideoDelayPayload { + uint32_t delay_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h new file mode 100644 index 0000000000..e43638c016 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/digital-audio/track-new-audio-attributes.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPTrackNewAudioAttributesPayload { + uint32_t sample_rate; + uint32_t sound_check; + uint32_t volume_adjustment; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h new file mode 100644 index 0000000000..62185f3b8f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote.h @@ -0,0 +1,56 @@ +#pragma once +#include + +/* [1] P.249 Table 4-48 Display Remote lingo command summary */ +enum IAPDisplayRemoteCommandID { + IAPDisplayRemoteCommandID_IPodAck = 0x00, /* from acc, general/ipod-ack.h */ + IAPDisplayRemoteCommandID_GetCurrentEQProfileIndex = 0x01, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetCurrentEQProfileIndex = 0x02, /* from dev, current-eq-profile-index.h */ + IAPDisplayRemoteCommandID_SetCurrentEQProfileIndex = 0x03, /* from acc, current-eq-profile-index.h */ + IAPDisplayRemoteCommandID_GetNumEQProfiles = 0x04, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetNumEQProfiles = 0x05, /* from dev, num-eq-profiles.h */ + IAPDisplayRemoteCommandID_GetIndexedEQProfileName = 0x06, /* from acc, indexed-eq-profile-name.h */ + IAPDisplayRemoteCommandID_RetIndexedEQProfileName = 0x07, /* from dev, indexed-eq-profile-name.h */ + IAPDisplayRemoteCommandID_SetRemoteEventNotification = 0x08, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_RemoteEventNotification = 0x09, /* from dev, ipod-state.h */ + IAPDisplayRemoteCommandID_GetRemoteEventStatus = 0x0A, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetRemoteEventStatus = 0x0B, /* from dev, remote-event-status.h */ + IAPDisplayRemoteCommandID_GetIPodStateInfo = 0x0C, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_RetIPodStateInfo = 0x0D, /* from dev, ipod-state.h */ + IAPDisplayRemoteCommandID_SetIPodStateInfo = 0x0E, /* from acc, ipod-state.h */ + IAPDisplayRemoteCommandID_GetPlayStatus = 0x0F, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetPlayStatus = 0x10, /* from dev, play-status.h */ + IAPDisplayRemoteCommandID_SetCurrentPlayingTrack = 0x11, /* from acc, set-current-playing-track.h */ + IAPDisplayRemoteCommandID_GetIndexedPlayingTrackInfo = 0x12, /* from acc, indexed-playing-track-info.h */ + IAPDisplayRemoteCommandID_RetIndexedPlayingTrackInfo = 0x13, /* from dev, indexed-playing-track-info.h */ + IAPDisplayRemoteCommandID_GetNumPlayingTracks = 0x14, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetNumPlayingTracks = 0x15, /* from dev, num-playing-tracks.h */ + IAPDisplayRemoteCommandID_GetArtworkFormats = 0x16, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetArtworkFormats = 0x17, /* from dev, artwork-formats.h */ + IAPDisplayRemoteCommandID_GetTrackArtworkData = 0x18, /* from acc, track-artwork-data.h */ + IAPDisplayRemoteCommandID_RetTrackArtworkData = 0x19, /* from dev, track-artwork-data.h */ + IAPDisplayRemoteCommandID_GetPowerBatteryState = 0x1A, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetPowerBatteryState = 0x1B, /* from dev, power-battery-state.h */ + IAPDisplayRemoteCommandID_GetSoundCheckState = 0x1C, /* from acc, no payload */ + IAPDisplayRemoteCommandID_RetSoundCheckState = 0x1D, /* from dev, sound-check-status.h */ + IAPDisplayRemoteCommandID_SetSoundCheckState = 0x1E, /* from acc, sound-check-status.h */ + IAPDisplayRemoteCommandID_GetTrackArtworkTimes = 0x1F, /* from acc, track-artwork-times.h */ + IAPDisplayRemoteCommandID_RetTrackArtworkTimes = 0x20, /* from dev, track-artwork-times.h */ + IAPDisplayRemoteCommandID_CreateGeniusPlaylist = 0x21, /* from acc, genius_playlist.h */ + IAPDisplayRemoteCommandID_IsGeniusAvailableForTrack = 0x22, /* from acc, genius_playlist.h */ +}; + +#include "display-remote/artwork-formats.h" +#include "display-remote/current-eq-profile-index.h" +#include "display-remote/genius-playlist.h" +#include "display-remote/indexed-eq-profile-name.h" +#include "display-remote/indexed-playing-track-info.h" +#include "display-remote/ipod-state.h" +#include "display-remote/num-eq-profiles.h" +#include "display-remote/num-playing-tracks.h" +#include "display-remote/play-status.h" +#include "display-remote/power-battery-state.h" +#include "display-remote/remote-event-status.h" +#include "display-remote/set-current-playing-track.h" +#include "display-remote/track-artwork-data.h" +#include "display-remote/track-artwork-times.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h new file mode 100644 index 0000000000..5c4b072532 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/artwork-formats.h @@ -0,0 +1,21 @@ +#pragma once +#include + +enum IAPArtworkPixelFormats { + IAPArtworkPixelFormats_Mono = 0x01, + IAPArtworkPixelFormats_RGB565LE = 0x02, + IAPArtworkPixelFormats_RGB565BE = 0x03, +}; + +struct IAPArtworkFormat { + uint16_t format_id; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t image_width; + uint16_t image_height; +} __attribute__((packed)); + +/* +struct IAPRetArtworkFormatsPayload { + struct IAPArtworkFormat formats[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h new file mode 100644 index 0000000000..3c00f06f28 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/current-eq-profile-index.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetCurrentEQProfileIndexPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPSetCurrentEQProfileIndexPayload { + uint32_t index; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h new file mode 100644 index 0000000000..b1da0f9f2f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/genius-playlist.h @@ -0,0 +1,10 @@ +#pragma once +#include + +struct IAPCreateGeniusPlaylistPayload { + uint32_t playback_index; +} __attribute__((packed)); + +struct IAPIsGeniusAvailableForTrackPayload { + uint32_t playback_index; +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h new file mode 100644 index 0000000000..d526e1df10 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-eq-profile-name.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPGetIndexedEQProfileNamePayload { + uint32_t index; +} __attribute__((packed)); + +/* +struct IAPRetIndexedEQProfileNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h new file mode 100644 index 0000000000..4baeeed66c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/indexed-playing-track-info.h @@ -0,0 +1,85 @@ +#pragma once +#include + +enum IAPIndexedPlayingTrackInfoType { + IAPIndexedPlayingTrackInfoType_TrackCapsInfo = 0x00, + IAPIndexedPlayingTrackInfoType_ChapterTimeName = 0x01, + IAPIndexedPlayingTrackInfoType_ArtistName = 0x02, + IAPIndexedPlayingTrackInfoType_AlbumName = 0x03, + IAPIndexedPlayingTrackInfoType_GenreName = 0x04, + IAPIndexedPlayingTrackInfoType_TrackTitle = 0x05, + IAPIndexedPlayingTrackInfoType_ComposerName = 0x06, + IAPIndexedPlayingTrackInfoType_Lyrics = 0x07, + IAPIndexedPlayingTrackInfoType_ArtworkCount = 0x08, +}; + +struct IAPGetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPIndexedPlayingTrackInfoType */ + uint32_t track_index; + uint16_t chapter_index; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPIndexedPlayingTrackInfoType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoTrackCapsInfoPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_TrackCapsInfo */ + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t track_total_ms; + uint16_t chapter_count; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoChapterTimeNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ChapterTimeName */ + uint32_t offset_ms; + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoArtistNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ArtistName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoAlbumNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_AlbumName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoGenreNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_GenreName */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoTrackTitlePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_TrackTitle */ + char name[]; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoComposerNamePayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ComposerName */ + char name[]; +} __attribute__((packed)); + +enum IAPIndexedPlayingTrackInfoLyricsInfoBits { + IAPIndexedPlayingTrackInfoLyricsInfoBits_Series = 1 << 0, + IAPIndexedPlayingTrackInfoLyricsInfoBits_Last = 1 << 1, +}; + +struct IAPRetIndexedPlayingTrackInfoLyricsPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_Lyrics */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char lyrics[]; +} __attribute__((packed)); + +struct IAPArtworkCount { + uint16_t format; /* IAPArtworkPixelFormats */ + uint16_t count; +} __attribute__((packed)); + +struct IAPRetIndexedPlayingTrackInfoArtworkCountPayload { + uint8_t type; /* = IAPIndexedPlayingTrackInfoType_ArtworkCount */ + struct IAPArtworkCount data[]; /* or 0x08 to indicate no counts */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h new file mode 100644 index 0000000000..a861ec3667 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/ipod-state.h @@ -0,0 +1,204 @@ +#pragma once +#include + +/* common state types */ + +/* [1] P.266 Table 4-74 Apple device state data */ +enum IAPIPodStateType { + IAPIPodStateType_TrackTimePositionMSec = 0x00, + IAPIPodStateType_TrackPlaybackIndex = 0x01, + IAPIPodStateType_ChapterIndex = 0x02, + IAPIPodStateType_PlayStatus = 0x03, + IAPIPodStateType_Volume = 0x04, + IAPIPodStateType_Power = 0x05, + IAPIPodStateType_EQSetting = 0x06, + IAPIPodStateType_ShuffleSetting = 0x07, + IAPIPodStateType_RepeatSetting = 0x08, + IAPIPodStateType_DateTimeSetting = 0x09, + IAPIPodStateType_AlarmSetting = 0x0A, + IAPIPodStateType_BacklightLevel = 0x0B, + IAPIPodStateType_HoldSwitchState = 0x0C, + IAPIPodStateType_SoundCheckState = 0x0D, + IAPIPodStateType_AudiobookSpeeed = 0x0E, + IAPIPodStateType_TrackTimePositionSec = 0x0F, + IAPIPodStateType_AbsoluteVolume = 0x10, + IAPIPodStateType_TrackCaps = 0x11, + IAPIPodStateType_PlaybackEngineContents = 0x12, +}; + +struct IAPIPodStatePayload { + uint8_t type; /* IAPIPodStateType */ + uint8_t data[]; +} __attribute__((packed)); + +/* [1] P.257 Table 4-61 Event notification data */ + +struct IAPIPodStateTrackTimePositionMSecPayload { + uint8_t type; /* = IAPIPodStateType_TrackTimePositionMSec */ + uint32_t position_ms; +} __attribute__((packed)); + +struct IAPIPodStateTrackPlaybackIndexPayload { + uint8_t type; /* = IAPIPodStateType_TrackPlaybackIndex */ + uint32_t index; +} __attribute__((packed)); + +struct IAPIPodStateChapterIndexPayload { + uint8_t type; /* = IAPIPodStateType_ChapterIndex */ + uint32_t index; + uint16_t chapter_count; + uint16_t chapter_index; +} __attribute__((packed)); + +enum IAPIPodStatePlayStatus { + IAPIPodStatePlayStatus_PlaybackStopped = 0x00, + IAPIPodStatePlayStatus_Playing = 0x01, + IAPIPodStatePlayStatus_PlaybackPaused = 0x02, + IAPIPodStatePlayStatus_FastForward = 0x03, + IAPIPodStatePlayStatus_FastRewind = 0x04, + IAPIPodStatePlayStatus_EndFastForwardRewind = 0x05, +}; + +struct IAPIPodStatePlayStatusPayload { + uint8_t type; /* = IAPIPodStateType_PlayStatus */ + uint8_t status; /* IAPIPodStatePlayStatus */ +} __attribute__((packed)); + +struct IAPIPodStateVolumePayload { + uint8_t type; /* = IAPIPodStateType_Volume */ + uint8_t mute_state; + uint8_t ui_volume; +} __attribute__((packed)); + +enum IAPIPodStatePowerState { + IAPIPodStatePowerState_InternalLow = 0x00, + IAPIPodStatePowerState_Internal = 0x01, + IAPIPodStatePowerState_ExternalBattery = 0x02, + IAPIPodStatePowerState_External = 0x03, + IAPIPodStatePowerState_ExternalCharging = 0x04, + IAPIPodStatePowerState_ExternalCharged = 0x05, +}; + +struct IAPIPodStatePowerPayload { + uint8_t type; /* = IAPIPodStateType_Power */ + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +} __attribute__((packed)); + +struct IAPIPodStateEQSettingPayload { + uint8_t type; /* = IAPIPodStateType_EQSetting */ + uint32_t eq_index; +} __attribute__((packed)); + +enum IAPIPodStateShuffleSettingState { + IAPIPodStateShuffleSettingState_Off = 0x00, + IAPIPodStateShuffleSettingState_Tracks = 0x01, + IAPIPodStateShuffleSettingState_Albums = 0x02, +}; + +struct IAPIPodStateShuffleSettingPayload { + uint8_t type; /* = IAPIPodStateType_ShuffleSetting */ + uint8_t shuffle_state; /* IAPIPodStateShuffleSettingState */ +} __attribute__((packed)); + +enum IAPIPodStateRepeatSettingState { + IAPIPodStateRepeatSettingState_Off = 0x00, + IAPIPodStateRepeatSettingState_One = 0x01, + IAPIPodStateRepeatSettingState_All = 0x02, +}; + +struct IAPIPodStateRepeatSettingPayload { + uint8_t type; /* = IAPIPodStateType_RepeatSetting */ + uint8_t repeat_state; /* IAPIPodStateRepeatSettingState */ +} __attribute__((packed)); + +struct IAPIPodStateDateTimeSettingPayload { + uint8_t type; /* = IAPIPodStateType_DateTimeSetting */ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; +} __attribute__((packed)); + +struct IAPIPodStateAlarmSettingPayload { + uint8_t type; /* = IAPIPodStateType_AlarmSetting */ + uint8_t deprecated[3]; +} __attribute__((packed)); + +struct IAPIPodStateBacklightLevelPayload { + uint8_t type; /* = IAPIPodStateType_BacklightLevel */ + uint8_t level; +} __attribute__((packed)); + +struct IAPIPodStateHoldSwitchStatePayload { + uint8_t type; /* = IAPIPodStateType_HoldSwitchState */ + uint8_t state; +} __attribute__((packed)); + +struct IAPIPodStateSoundCheckStatePayload { + uint8_t type; /* = IAPIPodStateType_SoundCheckState */ + uint8_t state; +} __attribute__((packed)); + +enum IAPIPodStateAudiobookSpeeed { + IAPIPodStateAudiobookSpeeed_Slower = 0xFF, + IAPIPodStateAudiobookSpeeed_Normal = 0x00, + IAPIPodStateAudiobookSpeeed_Faster = 0x01, +}; + +struct IAPIPodStateAudiobookSpeeedPayload { + uint8_t type; /* = IAPIPodStateType_AudiobookSpeeed */ + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ +} __attribute__((packed)); + +struct IAPIPodStateTrackTimePositionSecPayload { + uint8_t type; /* = IAPIPodStateType_TrackTimePositionSec */ + uint16_t position_s; +} __attribute__((packed)); + +struct IAPIPodStateAbsoluteVolumePayload { + uint8_t type; /* = IAPIPodStateType_AbsoluteVolume */ + uint8_t mute_state; + uint8_t ui_volume; + uint8_t absolute_volume; +} __attribute__((packed)); + +enum IAPIPodStateTrackCapBits { + IAPIPodStateTrackCapBits_IsAudiobook = 1 << 0, + IAPIPodStateTrackCapBits_HasChapters = 1 << 1, + IAPIPodStateTrackCapBits_HasAlbumArts = 1 << 2, + IAPIPodStateTrackCapBits_HasLyrics = 1 << 3, + IAPIPodStateTrackCapBits_IsPodcast = 1 << 4, + IAPIPodStateTrackCapBits_HasReleaseDate = 1 << 5, + IAPIPodStateTrackCapBits_HasDescription = 1 << 6, + IAPIPodStateTrackCapBits_HasVideo = 1 << 7, + IAPIPodStateTrackCapBits_IsQueued = 1 << 8, + IAPIPodStateTrackCapBits_GenerateGeniusPlaylist = 1 << 13, + IAPIPodStateTrackCapBits_IsITunesUEpisode = 1 << 14, +}; + +struct IAPIPodStateTrackCapsPayload { + uint8_t type; /* = IAPIPodStateType_TrackCaps */ + uint32_t caps; /* IAPIPodStateTrackCapBits */ +} __attribute__((packed)); + +struct IAPIPodStatePlaybackEngineContentsPayload { + uint8_t type; /* = IAPIPodStateType_PlaybackEngineContents */ + uint32_t count; +} __attribute__((packed)); + +/* event notification */ +struct IAPSetRemoteEventNotificationPayload { + uint32_t mask; /* (1 << IAPIPodStateType) | ... */ +} __attribute__((packed)); + +/* IAPRemoteEventNotificationPayload = IAPIPodStatePayload */ + +/* get/ret state info */ +struct IAPGetIPodStateInfoPayload { + uint8_t type; /* = IAPIPodStateType */ +} __attribute__((packed)); + +/* RetIPodStateInfoPayload = IAPIPodStatePayload */ +/* SetIPodStateInfoPayload = IAPIPodStatePayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h new file mode 100644 index 0000000000..bb40c6dacf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-eq-profiles.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetNumEQProfilesPayload { + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h new file mode 100644 index 0000000000..b8d7a59d6a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/num-playing-tracks.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetNumPlayingTracksPayload { + uint32_t num_playing_tracks; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h new file mode 100644 index 0000000000..6ddfd38d74 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/play-status.h @@ -0,0 +1,9 @@ +#pragma once +#include + +struct IAPRetPlayStatusPayload { + uint8_t state; /* IAPIPodStatePlayStatus */ + uint32_t track_index; + uint32_t track_total_ms; + uint32_t track_pos_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h new file mode 100644 index 0000000000..43f03f5e9f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/power-battery-state.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPRetPowerBatteryStatePayload { + uint8_t power_state; /* IAPIPodStatePowerState */ + uint8_t battery_level; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h new file mode 100644 index 0000000000..7b7e6a4a39 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/remote-event-status.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetRemoteEventStatusPayload { + uint32_t mask; /* (1 << IAPRemoteEventNotifications) | ... */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h new file mode 100644 index 0000000000..b2522186a8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/set-current-playing-track.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetCurrentPlayingTrackPayload { + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h new file mode 100644 index 0000000000..f540c63f26 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-data.h @@ -0,0 +1,26 @@ +#pragma once +#include + +struct IAPGetTrackArtworkDataPayload { + uint32_t track_index; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPRetTrackArtworkDataFirstPayload { + uint16_t index; /* = 0x0000 */ + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; + uint16_t inset_top_left_x; + uint16_t inset_top_left_y; + uint16_t inset_bottom_right_x; + uint16_t inset_bottom_right_y; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetTrackArtworkDataSubsequenctPayload { + uint16_t index; /* > 0x0000 */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h new file mode 100644 index 0000000000..670b4b5732 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/display-remote/track-artwork-times.h @@ -0,0 +1,15 @@ +#pragma once +#include + +struct IAPGetTrackArtworkTimesPayload { + uint32_t track_index; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +/* +struct IAPRetTrackArtworkTimesPayload { + uint32_t offsets_ms[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h new file mode 100644 index 0000000000..b40c4342ea --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface.h @@ -0,0 +1,109 @@ +#pragma once +#include + +/* [1] P.398 Table 5-1 Extended Interface lingo command summary */ +enum IAPExtendedInterfaceCommandID { + IAPExtendedInterfaceCommandID_IPodAck = 0x0001, /* 1.00, NoAuth, from acc, ipod-ack.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterInfo = 0x0002, /* 1.06, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterInfo = 0x0003, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_SetCurrentPlayingTrackChapter = 0x0004, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterPlayStatus = 0x0005, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterPlayStatus = 0x0006, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackChapterName = 0x0007, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackChapterName = 0x0008, /* 1.06, NoAuth, from dev, current-playing-track-chapter.h */ + IAPExtendedInterfaceCommandID_GetAudiobookSpeed = 0x0009, /* 1.06, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_RetAudiobookSpeed = 0x000A, /* 1.06, NoAuth, from dev, audiobook-speed.h */ + IAPExtendedInterfaceCommandID_SetAudiobookSpeed = 0x000B, /* 1.06, NoAuth, from acc, audiobook-speed.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackInfo = 0x000C, /* 1.08, NoAuth, from acc, indexed-playing-track-info.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackInfo = 0x000D, /* 1.08, NoAuth, from dev, indexed-playing-track-info.h */ + IAPExtendedInterfaceCommandID_GetArtworkFormats = 0x000E, /* 1.10, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_RetArtworkFormats = 0x000F, /* 1.10, NoAuth, from dev, display-remote/artwork-formats.h*/ + IAPExtendedInterfaceCommandID_GetTrackArtworkData = 0x0010, /* 1.10, NoAuth, from acc, display-remote/track-artwork-data.h*/ + IAPExtendedInterfaceCommandID_RetTrackArtworkData = 0x0011, /* 1.10, NoAuth, from dev, display-remote/track-artwork-data.h*/ + IAPExtendedInterfaceCommandID_RequestProtocolVersion = 0x0012, /* deprecated */ + IAPExtendedInterfaceCommandID_ReturnProtocolVersion = 0x0013, /* deprecated */ + IAPExtendedInterfaceCommandID_RequestIPodName = 0x0014, /* deprecated */ + IAPExtendedInterfaceCommandID_ReturnIPodName = 0x0015, /* deprecated */ + IAPExtendedInterfaceCommandID_ResetDBSelection = 0x0016, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_SelectDBRecord = 0x0017, /* 1.00, NoAuth, from dev, database.h*/ + IAPExtendedInterfaceCommandID_GetNumberCategorizedDBRecords = 0x0018, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_ReturnNumberCategorizedDBRecords = 0x0019, /* 1.00, NoAuth, from dev, database.h */ + IAPExtendedInterfaceCommandID_RetrieveCategorizedDatabaseRecords = 0x001A, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_ReturnCategorizedDatabaseRecords = 0x001B, /* 1.00, NoAuth, from acc, database.h */ + IAPExtendedInterfaceCommandID_GetPlayStatus = 0x001C, /* 1.00, NoAuth, no payload */ + IAPExtendedInterfaceCommandID_ReturnPlayStatus = 0x001D, /* 1.00, NoAuth, play-status.h */ + IAPExtendedInterfaceCommandID_GetCurrentPlayingTrackIndex = 0x001E, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnCurrentPlayingTrackIndex = 0x001F, /* 1.00, NoAuth, from dev, current-playing-index.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackTitle = 0x0020, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackTitle = 0x0021, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackArtistName = 0x0022, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackArtistName = 0x0023, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_GetIndexedPlayingTrackAlbumName = 0x0024, /* 1.00, NoAuth, from acc, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_ReturnIndexedPlayingTrackAlbumName = 0x0025, /* 1.00, NoAuth, from dev, indexed-playing-track-string.h */ + IAPExtendedInterfaceCommandID_SetPlayStatusChangeNotification = 0x0026, /* 1.00, NoAuth, from acc, play-status-change-notification.h */ + IAPExtendedInterfaceCommandID_PlayStatusChangeNotification = 0x0027, /* 1.00, NoAuth, from dev, play-status-change-notification.h */ + IAPExtendedInterfaceCommandID_PlayCurrentSelection = 0x0028, /* 1.00, NoAuth, from acc, play-current-selection.h */ + IAPExtendedInterfaceCommandID_PlayControl = 0x0029, /* 1.00, NoAuth, from acc, play-control.h */ + IAPExtendedInterfaceCommandID_GetTrackArtworkTimes = 0x002A, /* 1.10, NoAuth, from acc, display-remote/track-artwork-times.h */ + IAPExtendedInterfaceCommandID_RetTrackArtworkTimes = 0x002B, /* 1.10, NoAuth, from dev, display-remote/track-artwork-times.h */ + IAPExtendedInterfaceCommandID_GetShuffle = 0x002C, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnShuffle = 0x002D, /* 1.00, NoAuth, from dev, shuffle.h */ + IAPExtendedInterfaceCommandID_SetShuffle = 0x002E, /* 1.00, NoAuth, from acc, shuffle.h */ + IAPExtendedInterfaceCommandID_GetRepeat = 0x002F, /* 1.00, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnRepeat = 0x0030, /* 1.00, NoAuth, from dev, repeat.h */ + IAPExtendedInterfaceCommandID_SetRepeat = 0x0031, /* 1.00, NoAuth, from acc, repeat.h */ + IAPExtendedInterfaceCommandID_SetDisplayImage = 0x0032, /* 1.01, NoAuth, from acc, set-display-image.h */ + IAPExtendedInterfaceCommandID_GetMonoDisplayImageLimits = 0x0033, /* 1.01, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnMonoDisplayImageLimits = 0x0034, /* 1.01, NoAuth, from dev, mono-display-image-limits.h */ + IAPExtendedInterfaceCommandID_GetNumPlayingTracks = 0x0035, /* 1.01, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnNumPlayingTracks = 0x0036, /* 1.01, NoAuth, from dev, display-remote/num-playing-tracks.h */ + IAPExtendedInterfaceCommandID_SetCurrentPlayingTrack = 0x0037, /* 1.01, NoAuth, from acc, display-remote/set-current-playing-track.h */ + IAPExtendedInterfaceCommandID_SelectSortDBRecord = 0x0038, /* deprecated */ + IAPExtendedInterfaceCommandID_GetColorDisplayImageLimits = 0x0039, /* 1.09, NoAuth, from acc, no payload */ + IAPExtendedInterfaceCommandID_ReturnColorDisplayImageLimits = 0x003A, /* 1.09, NoAuth, from dev, color-display-image-limits.h */ + IAPExtendedInterfaceCommandID_ResetDBSelectionHierarchy = 0x003B, /* 1.11, Auth, from acc, db-selection-hierarchy.h */ + IAPExtendedInterfaceCommandID_GetDBITunesInfo = 0x003C, /* 1.13, Auth, from acc, db-itunes-info.h */ + IAPExtendedInterfaceCommandID_RetDBITunesInfo = 0x003D, /* 1.13, Auth, from dev, db-itunes-info.h */ + IAPExtendedInterfaceCommandID_GetUIDTrackInfo = 0x003E, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetUIDTrackInfo = 0x003F, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_GetDBTrackInfo = 0x0040, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetDBTrackInfo = 0x0041, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_GetPBTrackInfo = 0x0042, /* 1.13, Auth, from acc, track-info.h */ + IAPExtendedInterfaceCommandID_RetPBTrackInfo = 0x0043, /* 1.13, Auth, from dev, track-info.h */ + IAPExtendedInterfaceCommandID_CreateGeniusPlaylist = 0x0044, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_RefreshGeniusPlaylist = 0x0045, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_IsGeniusAvailableForTrack = 0x0047, /* 1.13, Auth, from acc, genius-playlist.h */ + IAPExtendedInterfaceCommandID_GetPlaylistInfo = 0x0048, /* 1.13, Auth, from acc, playlist-info.h */ + IAPExtendedInterfaceCommandID_RetPlaylistInfo = 0x0049, /* 1.13, Auth, from dev, playlist-info.h */ + IAPExtendedInterfaceCommandID_PrepareUIDList = 0x004A, /* 1.14, Auth, from dev, uid-list.h */ + IAPExtendedInterfaceCommandID_PlayPreparedUIDList = 0x004B, /* 1.14, Auth, from dev, uid-list.h */ + IAPExtendedInterfaceCommandID_GetArtworkTimes = 0x004C, /* 1.14, Auth, from acc, artwork-times.h */ + IAPExtendedInterfaceCommandID_RetArtworkTimes = 0x004D, /* 1.14, Auth, from dev, artwork-times.h */ + IAPExtendedInterfaceCommandID_GetArtworkData = 0x004E, /* 1.14, Auth, from acc, artwork-data.h */ + IAPExtendedInterfaceCommandID_RetArtworkData = 0x004F, /* 1.14, Auth, from dev, artwork-data.h */ +}; + +#include "extended-interface/artwork-data.h" +#include "extended-interface/artwork-times.h" +#include "extended-interface/audiobook-speed.h" +#include "extended-interface/color-display-image-limits.h" +#include "extended-interface/current-playing-track-chapter.h" +#include "extended-interface/current-playing-track-index.h" +#include "extended-interface/database.h" +#include "extended-interface/db-itunes-info.h" +#include "extended-interface/db-selection-hierarchy.h" +#include "extended-interface/genius-playlist.h" +#include "extended-interface/indexed-playing-track-info.h" +#include "extended-interface/indexed-playing-track-string.h" +#include "extended-interface/ipod-ack.h" +#include "extended-interface/mono-display-image-limits.h" +#include "extended-interface/play-control.h" +#include "extended-interface/play-current-selection.h" +#include "extended-interface/play-status-change-notification.h" +#include "extended-interface/play-status.h" +#include "extended-interface/playlist-info.h" +#include "extended-interface/repeat.h" +#include "extended-interface/set-display-image.h" +#include "extended-interface/shuffle.h" +#include "extended-interface/track-info.h" +#include "extended-interface/uid-list.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h new file mode 100644 index 0000000000..67c0569750 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-data.h @@ -0,0 +1,58 @@ +#pragma once +#include + +/* [1] P.459 5.1.72 Command 0x004E: GetArtworkData */ + +struct IAPGetArtworkDataPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPGetArtworkDataUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPGetArtworkDataIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint16_t format_id; + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPRetArtworkDataPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkDataBody { + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; /* [1] P.461 "Table 5-104 imageDescriptionAndData format" misses this field, but should be here */ + uint16_t inset_top_left_x; + uint16_t inset_top_left_y; + uint16_t inset_bottom_right_x; + uint16_t inset_bottom_right_y; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkDataUIDPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + struct IAPRetArtworkDataBody body; +} __attribute__((packed)); + +struct IAPRetArtworkDataIndexPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + struct IAPRetArtworkDataBody body; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h new file mode 100644 index 0000000000..41207efe5e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/artwork-times.h @@ -0,0 +1,48 @@ +#pragma once +#include + +/* [1] P.458 5.1.70 Command 0x004C: GetArtworkTimes */ + +enum IAPArtworkTrackIDType { + IAPGetArtworkTrackIDType_UID = 0x00, + IAPGetArtworkTrackIDType_PlaybackListIndex = 0x01, + IAPGetArtworkTrackIDType_DatabaseIndex = 0x02, +}; + +struct IAPGetArtworkTimesPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPGetArtworkTimesUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +struct IAPGetArtworkTimesIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint16_t format_id; + uint16_t artwork_index; + uint16_t artwork_count; +} __attribute__((packed)); + +struct IAPRetArtworkTimesPayload { + uint8_t id_type; /* IAPGetArtworkTrackIDType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetArtworkTimesUIDPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_UID */ + uint8_t uid[8]; + uint32_t offsets_ms[]; +} __attribute__((packed)); + +struct IAPRetArtworkTimesIndexPayload { + uint8_t id_type; /* = IAPGetArtworkTrackIDType_{PlaybackListIndex,DatabaseIndex} */ + uint32_t index; + uint32_t offsets_ms[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h new file mode 100644 index 0000000000..770cf06039 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/audiobook-speed.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetAudiobookSpeedPayload { + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ +} __attribute__((packed)); + +struct IAPSetAudiobookSpeedPayload { + uint8_t speed; /* IAPIPodStateAudiobookSpeeed */ + uint8_t restore_on_exit; /* optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h new file mode 100644 index 0000000000..c5e75fd3b7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/color-display-image-limits.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.443 5.1.53 Command 0x003A: ReturnColorDisplayImageLimits */ + +struct IAPColorDisplayImageLimit { + uint16_t max_width; + uint16_t max_height; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ +} __attribute__((packed)); + +/* +struct IAPReturnColorDisplayImageLimitsPayload { + struct IAPColorDisplayImageLimit limits[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h new file mode 100644 index 0000000000..f059b53c49 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-chapter.h @@ -0,0 +1,30 @@ +#pragma once +#include + +struct IAPReturnCurrentPlayingTrackChapterInfoPayload { + uint32_t index; + uint32_t count; +} __attribute__((packed)); + +struct IAPSetCurrentPlayingTrackChapterPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPGetCurrentPlayingTrackChapterPlayStatusPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPReturnCurrentPlayingTrackChapterPlayStatusPayload { + uint32_t length_ms; + uint32_t elapsed_ms; +} __attribute__((packed)); + +struct IAPGetCurrentPlayingTrackChapterNamePayload { + uint32_t index; +} __attribute__((packed)); + +/* +struct IAPReturnCurrentPlayingTrackChapterNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h new file mode 100644 index 0000000000..1f86a18e83 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/current-playing-track-index.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnCurrentPlayingTrackIndexPayload { + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h new file mode 100644 index 0000000000..55a548ba7e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/database.h @@ -0,0 +1,42 @@ +#pragma once +#include + +enum IAPDatabaseType { + IAPDatabaseType_TopLevel = 0x00, /* 1.14 */ + IAPDatabaseType_Playlist = 0x01, /* 1.00 */ + IAPDatabaseType_Artist = 0x02, /* 1.00 */ + IAPDatabaseType_Album = 0x03, /* 1.00 */ + IAPDatabaseType_Genre = 0x04, /* 1.00 */ + IAPDatabaseType_Track = 0x05, /* 1.00 */ + IAPDatabaseType_Composer = 0x06, /* 1.00 */ + IAPDatabaseType_Audiobook = 0x07, /* 1.06 */ + IAPDatabaseType_Podcast = 0x08, /* 1.08 */ + IAPDatabaseType_NestedPlaylist = 0x09, /* 1.13 */ + IAPDatabaseType_GeniusMixes = 0x0A, /* 1.14 */ + IAPDatabaseType_ITunesU = 0x0B, /* 1.14 */ + +}; + +struct IAPSelectDBRecord { + uint8_t type; /* IAPDatabaseType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPGetNumberCategorizedDBRecordsPayload { + uint8_t type; /* IAPDatabaseType */ +} __attribute__((packed)); + +struct IAPReturnNumberCategorizedDBRecordsPayload { + uint32_t count; +} __attribute__((packed)); + +struct IAPRetrieveCategorizedDatabaseRecordsPayload { + uint8_t type; /* IAPDatabaseType */ + uint32_t index; + uint32_t count; +} __attribute__((packed)); + +struct IAPReturnCategorizedDatabaseRecordsPayload { + uint32_t index; + char record[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h new file mode 100644 index 0000000000..11618be6eb --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-itunes-info.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.444 5.1.55 Command 0x003C: GetDBiTunesInfo */ + +enum IAPITunesMetadataType { + IAPITunesMetadataType_UID = 0x00, + IAPITunesMetadataType_LastSyncDate = 0x01, + IAPITunesMetadataType_AudioTrackCount = 0x02, + IAPITunesMetadataType_VideoTrackCount = 0x03, + IAPITunesMetadataType_AudiobookCount = 0x04, + IAPITunesMetadataType_PhotoCount = 0x05, +}; + +struct IAPGetDBITunesInfoPayload { + uint8_t metadata_type; /* IAPITunesMetadataType */ +} __attribute__((packed)); + +struct IAPRetDBITunesInfoPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoUIDPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_UID */ + uint8_t uid[8]; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoLastSyncDatePayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_LastSyncDate */ + uint8_t seconds; + uint8_t minute; + uint8_t hour; + uint8_t day; + uint8_t month; + uint16_t year; +} __attribute__((packed)); + +struct IAPRetDBITunesInfoCountPayload { + uint8_t metadata_type; /* = IAPITunesMetadataType_{AudioTrack,VideoTrack,Audiobook,Photo}Count */ + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h new file mode 100644 index 0000000000..434b21bab3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/db-selection-hierarchy.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.443 5.1.54 Command 0x003B: ResetDBSelectionHierarchy */ + +enum IAPResetDBSelectionHierarchySelection { + IAPResetDBSelectionHierarchySelection_Audio = 0x01, + IAPResetDBSelectionHierarchySelection_Video = 0x02, +}; + +struct IAPResetDBSelectionHierarchyPayload { + uint8_t selection; /* IAPResetDBSelectionHierarchySelection */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h new file mode 100644 index 0000000000..81601886e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/genius-playlist.h @@ -0,0 +1,23 @@ +#pragma once +#include + +/* [1] P.454 5.1.63 Command 0x0044: CreateGeniusPlaylist */ + +enum IAPCreateGeniusPlaylistIndexType { + IAPCreateGeniusPlaylistIndexType_DatabaseEngine = 0x00, + IAPCreateGeniusPlaylistIndexType_PlaybackEngine = 0x01, +}; + +struct IAPExtendedCreateGeniusPlaylistPayload { + uint8_t index_type; /* IAPCreateGeniusPlaylistIndexType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPExtendedRefreshGeniusPlaylistPayload { + uint32_t index; +} __attribute__((packed)); + +struct IAPExtendedIsGeniusAvailableForTrackPayload { + uint8_t index_type; /* IAPCreateGeniusPlaylistIndexType */ + uint32_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h new file mode 100644 index 0000000000..ed7615d1f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-info.h @@ -0,0 +1,81 @@ +#pragma once +#include + +/* [1] P.408 5.1.13 Command 0x000C: GetIndexedPlayingTrackInfo */ + +enum IAPExtendedIndexedPlayingTrackInfoType { + IAPExtendedIndexedPlayingTrackInfoType_TrackCapsInfo = 0x00, + IAPExtendedIndexedPlayingTrackInfoType_PodcastName = 0x01, + IAPExtendedIndexedPlayingTrackInfoType_TrackReleaseDate = 0x02, + IAPExtendedIndexedPlayingTrackInfoType_TrackDescription = 0x03, + IAPExtendedIndexedPlayingTrackInfoType_TrackSongLyrics = 0x04, + IAPExtendedIndexedPlayingTrackInfoType_TrackGenre = 0x05, + IAPExtendedIndexedPlayingTrackInfoType_TrackComposer = 0x06, + IAPExtendedIndexedPlayingTrackInfoType_TrackArtworkCount = 0x07, +}; + +struct IAPExtendedGetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPExtendedIndexedPlayingTrackInfoType */ + uint32_t track_index; + uint16_t chapter_index; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoPayload { + uint8_t type; /* IAPExtendedIndexedPlayingTrackInfoType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackCapsInfoPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackCapsInfo */ + uint32_t track_caps; /* IAPIPodStateTrackCapBits */ + uint32_t track_total_ms; + uint16_t chapter_count; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoPodcastNamePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_PodcastName */ + char name[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackReleaseDatePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackReleaseDate */ + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t weekday; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackDescriptionPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackDescription */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char description[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackSongLyricsPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackSongLyrics */ + uint8_t info_bits; /* IAPIndexedPlayingTrackInfoLyricsInfoBits */ + uint16_t index; + char lyrics[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackGenrePayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackGenre */ + char genre[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackComposerPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackComposer */ + char composer[]; +} __attribute__((packed)); + +struct IAPExtendedRetIndexedPlayingTrackInfoTrackArtworkCountPayload { + uint8_t type; /* = IAPExtendedRetIndexedPlayingTrackInfo_TrackArtworkCount */ + struct { + uint16_t format; /* IAPArtworkPixelFormats */ + uint16_t count; + } __attribute__((packed)) data[]; /* or 0x08 to indicate no counts */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h new file mode 100644 index 0000000000..895fa2eba3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/indexed-playing-track-string.h @@ -0,0 +1,20 @@ +#pragma once +#include + +struct IAPGetIndexedPlayingTrackStringPayload { + uint32_t index; +} __attribute__((packed)); + +/* IAPGetIndexedPlayingTrackTitlePayload = IAPGetIndexedPlayingTrackStringPayload */ +/* IAPGetIndexedPlayingTrackArtistNamePayload = IAPGetIndexedPlayingTrackStringPayload */ +/* IAPGetIndexedPlayingTrackAlbumNamePayload = IAPGetIndexedPlayingTrackStringPayload */ + +/* +struct IAPReturnIndexedPlayingTrackStringPayload { + char string[]; +} __attribute__((packed)); +*/ + +/* IAPReturnIndexedPlayingTrackTitlePayload = IAPReturnIndexedPlayingTrackStringPayload */ +/* IAPReturnIndexedPlayingTrackArtistNamePayload = IAPReturnIndexedPlayingTrackStringPayload */ +/* IAPReturnIndexedPlayingTrackAlbumNamePayload = IAPReturnIndexedPlayingTrackStringPayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h new file mode 100644 index 0000000000..bcaf1e2203 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/ipod-ack.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPExtendedIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint16_t id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h new file mode 100644 index 0000000000..6f8d8ac375 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/mono-display-image-limits.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.440 5.1.48 Command 0x0034: ReturnMonoDisplayImageLimits */ + +struct IAPReturnMonoDisplayImageLimitsPayload { + uint16_t max_width; + uint16_t max_height; + uint8_t pixel_format; /* IAPArtworkPixelFormats */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h new file mode 100644 index 0000000000..0360026daa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-control.h @@ -0,0 +1,25 @@ +#pragma once +#include + +/* [1] P.428 5.1.37 Command 0x0029: PlayControl */ + +enum IAPPlayControlCode { + IAPPlayControlCode_TogglePlayPause = 0x01, /* 1.00 */ + IAPPlayControlCode_Stop = 0x02, /* 1.00 */ + IAPPlayControlCode_NextTrack = 0x03, /* 1.00 */ + IAPPlayControlCode_PrevTrack = 0x04, /* 1.00 */ + IAPPlayControlCode_StartFF = 0x05, /* 1.00 */ + IAPPlayControlCode_StartRew = 0x06, /* 1.00 */ + IAPPlayControlCode_EndFFRew = 0x07, /* 1.00 */ + IAPPlayControlCode_Next = 0x08, /* 1.06 */ + IAPPlayControlCode_Prev = 0x09, /* 1.06 */ + IAPPlayControlCode_Play = 0x0A, /* 1.13 */ + IAPPlayControlCode_Pause = 0x0B, /* 1.13 */ + IAPPlayControlCode_NextChapter = 0x0C, /* 1.14 */ + IAPPlayControlCode_PrevChapter = 0x0D, /* 1.14 */ + IAPPlayControlCode_ResumeIPod = 0x0E, /* 1.14 */ +}; + +struct IAPPlayControlPayload { + uint8_t code; /* IAPPlayControlCode */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h new file mode 100644 index 0000000000..a543ae7b86 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-current-selection.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.546 Table C-34 PlayCurrentSelection packet */ + +struct IAPPlayCurrentSelectionPayload { + uint32_t track_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h new file mode 100644 index 0000000000..a7ce5095f5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status-change-notification.h @@ -0,0 +1,137 @@ +#pragma once +#include + +struct IAPSetPlayStatusChangeNotification1BytePayload { + uint8_t enable; +} __attribute__((packed)); + +enum IAPStatusChangeNotificationBits { + IAPStatusChangeNotificationBits_Basic = 1 << 0, + IAPStatusChangeNotificationBits_Extended = 1 << 1, + IAPStatusChangeNotificationBits_TrackIndex = 1 << 2, + IAPStatusChangeNotificationBits_TrackTimeOffsetMSec = 1 << 3, + IAPStatusChangeNotificationBits_TrackTimeOffsetSec = 1 << 4, + IAPStatusChangeNotificationBits_ChapterIndex = 1 << 5, + IAPStatusChangeNotificationBits_ChapterTimeOffsetMSec = 1 << 6, + IAPStatusChangeNotificationBits_ChapterTimeOffsetSec = 1 << 7, + IAPStatusChangeNotificationBits_TrackUniqueID = 1 << 8, + IAPStatusChangeNotificationBits_TrackMediaType = 1 << 9, + IAPStatusChangeNotificationBits_TrackLyricsReady = 1 << 10, + IAPStatusChangeNotificationBits_TrackCapsChanged = 1 << 11, + IAPStatusChangeNotificationBits_PlaybackEngineContentsChanged = 1 << 12, + +}; + +struct IAPSetPlayStatusChangeNotification4BytesPayload { + uint32_t mask; /* IAPStatusChangeNotificationBits */ +} __attribute__((packed)); + +/* [1] P.426 5.1.36 Command 0x0027: PlayStatusChangeNotification */ + +enum IAPStatusChangeNotificationType { + IAPStatusChangeNotificationType_PlaybackStopped = 0x00, + IAPStatusChangeNotificationType_TrackIndex = 0x01, + IAPStatusChangeNotificationType_PlaybackFEWSeekStop = 0x02, + IAPStatusChangeNotificationType_PlaybackREWSeekStop = 0x03, + IAPStatusChangeNotificationType_TrackTimeOffsetMSec = 0x04, + IAPStatusChangeNotificationType_ChapterIndex = 0x05, + IAPStatusChangeNotificationType_PlaybackStatusExtended = 0x06, + IAPStatusChangeNotificationType_TrackTimeOffsetSec = 0x07, + IAPStatusChangeNotificationType_ChapterTimeOffsetMSec = 0x08, + IAPStatusChangeNotificationType_ChapterTimeOffsetSec = 0x09, + IAPStatusChangeNotificationType_TrackUniqueID = 0x0A, + IAPStatusChangeNotificationType_TrackPlaybackMode = 0x0B, + IAPStatusChangeNotificationType_TrackLyricsReady = 0x0C, + IAPStatusChangeNotificationType_TrackCapsChanged = 0x0D, + IAPStatusChangeNotificationType_PlaybackEngineContentsChanged = 0x0E, +}; + +struct IAPPlayStatusChangeNotificationPayload { + uint8_t type; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackStoppedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackStopped */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackIndexPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackIndex */ + uint32_t index; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackFEWSeekStopPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackFEWSeekStop */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackREWSeekStopPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackREWSeekStop */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackTimeOffsetMSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackTimeOffsetMSec */ + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterIndexPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterIndex */ + uint32_t index; +} __attribute__((packed)); + +enum IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates { + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Stopped = 0x02, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWSeekStarted = 0x05, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_REWSeekStarted = 0x06, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_FFWREWSeekStopped = 0x07, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Playing = 0x0A, + IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates_Paused = 0x0B, +}; + +struct IAPPlayStatusChangeNotificationPlaybackStatusExtendedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackStatusExtended */ + uint8_t state; /* IAPPlayStatusChangeNotificationPlaybackStatusExtendedStates */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackTimeOffsetSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackTimeOffsetSec */ + uint32_t offset_s; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterTimeOffsetMSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterTimeOffsetMSec */ + uint32_t offset_ms; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationChapterTimeOffsetSecPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_ChapterTimeOffsetSec */ + uint32_t offset_s; +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackUniqueIDPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackUniqueID */ + uint8_t id[8]; +} __attribute__((packed)); + +enum IAPPlayStatusChangeNotificationTrackMediaTypePlayMode { + IAPPlayStatusChangeNotificationTrackMediaTypePlayMode_Audio = 0x00, + IAPPlayStatusChangeNotificationTrackMediaTypePlayMode_Video = 0x01, +}; + +struct IAPPlayStatusChangeNotificationTrackPlaybackModePayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackPlaybackMode */ + uint8_t play_mode; /* IAPPlayStatusChangeNotificationTrackMediaTypePlayMode */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackLyricsReadyPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackLyricsReady */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationTrackCapsChangedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_TrackCapsChanged */ + uint32_t caps; /* IAPIPodStateTrackCapBits */ +} __attribute__((packed)); + +struct IAPPlayStatusChangeNotificationPlaybackEngineContentsChangedPayload { + uint8_t type; /* = IAPStatusChangeNotificationType_PlaybackEngineContentsChanged */ + uint32_t count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h new file mode 100644 index 0000000000..66d4e00a79 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/play-status.h @@ -0,0 +1,15 @@ +#pragma once +#include + +enum IAPPlayStatus { + IAPPlayStatus_Stopped = 0x00, + IAPPlayStatus_Playing = 0x01, + IAPPlayStatus_Paused = 0x02, + IAPPlayStatus_Error = 0xFF, +}; + +struct IAPExtendedRetPlayStatusPayload { + uint32_t track_total_ms; + uint32_t track_pos_ms; + uint8_t state; /* IAPPlayStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h new file mode 100644 index 0000000000..7693e5a002 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/playlist-info.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.456 5.1.66 Command 0x0048: GetPlaylistInfo */ + +enum IAPPlaylistInfoType { + PlaylistInfo = 0x00, +}; + +struct IAPGetPlaylistInfoPayload { + uint8_t info_type; /* IAPPlaylistInfoType */ + uint32_t index; +} __attribute__((packed)); + +struct IAPRetPlaylistInfoPayload { + uint8_t info_type; /* IAPPlaylistInfoType */ + uint8_t data; /* TODO: add definitions */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h new file mode 100644 index 0000000000..619e5dacef --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/repeat.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.434 5.1.44 Command 0x0030: ReturnRepeat */ + +struct IAPReturnRepeatPayload { + uint8_t mode; /* IAPIPodStateRepeaetSettingState */ +}; + +struct IAPSetRepeatPayload { + uint8_t mode; /* IAPIPodStateRepeatSettingState */ + uint8_t restore_on_exit; /* optional */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h new file mode 100644 index 0000000000..4687fe8f35 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/set-display-image.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.435 5.1.46 Command 0x0032: SetDisplayImage */ + +struct IAPSetDisplayImageFirstPayload { + uint16_t index; /* = 0x0000 */ + uint8_t pixel_format; /* IAPArtworkPixelFormats */ + uint16_t pixel_width; + uint16_t pixel_height; + uint32_t stride; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPSetDisplayImageSubsequenctPayload { + uint16_t index; /* > 0x0000 */ + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h new file mode 100644 index 0000000000..f94a0e6e1e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/shuffle.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.431 5.1.41 Command 0x002D: ReturnShuffle */ + +struct IAPReturnShufflePayload { + uint8_t mode; /* IAPIPodStateShuffleSettingState */ +}; + +struct IAPSetShufflePayload { + uint8_t mode; /* IAPIPodStateShuffleSettingState */ + uint8_t restore_on_exit; /* optional */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h new file mode 100644 index 0000000000..2eab33ed67 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/track-info.h @@ -0,0 +1,59 @@ +#pragma once +#include + +/* [1] P.446 5.1.57 Command 0x003E: GetUIDTrackInfo */ + +enum IAPTrakcInfoTypeBits { + /* mask[0] */ + IAPTrakcInfoTypeBits_Caps = 1 << 0, + IAPTrakcInfoTypeBits_TrackName = 1 << 1, + IAPTrakcInfoTypeBits_ArtistName = 1 << 2, + IAPTrakcInfoTypeBits_AlbumName = 1 << 3, + IAPTrakcInfoTypeBits_GenreName = 1 << 4, + IAPTrakcInfoTypeBits_ComposerName = 1 << 5, + IAPTrakcInfoTypeBits_TotalTrackTimeDuration = 1 << 6, + IAPTrakcInfoTypeBits_UniqueTrackID = 1 << 7, + /* mask[1] */ + IAPTrakcInfoTypeBits_ChapterCount = 1 << 0, + IAPTrakcInfoTypeBits_ChapterTimes = 1 << 1, + IAPTrakcInfoTypeBits_ChapterNames = 1 << 2, + IAPTrakcInfoTypeBits_Lyrics = 1 << 3, + IAPTrakcInfoTypeBits_Description = 1 << 4, + IAPTrakcInfoTypeBits_AlbumTrackIndex = 1 << 5, + IAPTrakcInfoTypeBits_DiscSetAlbumIndex = 1 << 6, + IAPTrakcInfoTypeBits_PlayCount = 1 << 7, + /* mask[2] */ + IAPTrakcInfoTypeBits_SkipCount = 1 << 0, + IAPTrakcInfoTypeBits_PodcastReleaseDate = 1 << 1, + IAPTrakcInfoTypeBits_LastPlayedDate = 1 << 2, + IAPTrakcInfoTypeBits_Year = 1 << 3, + IAPTrakcInfoTypeBits_StarRating = 1 << 4, + IAPTrakcInfoTypeBits_SeriesName = 1 << 5, + IAPTrakcInfoTypeBits_SeasonNumber = 1 << 6, + IAPTrakcInfoTypeBits_TrackVolumeAdjust = 1 << 7, + /* mask[3] */ + IAPTrakcInfoTypeBits_TrackEQPreset = 1 << 0, + IAPTrakcInfoTypeBits_TrackDataRate = 1 << 1, + IAPTrakcInfoTypeBits_BookmarkOffset = 1 << 2, + IAPTrakcInfoTypeBits_StartStopTimeOffset = 1 << 3, + +}; + +struct IAPGetUIDTrackInfoPayload { + uint8_t uid[8]; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +struct IAPGetDBTrackInfoPayload { + uint8_t database_index; + uint8_t track_count; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +struct IAPRetDBTrackInfoPayload { + uint8_t playing_index; + uint8_t track_count; + uint32_t mask[]; /* IAPTrakcInfoTypeBits */ +} __attribute__((packed)); + +/* TODO: define IAPRet{UID,DB,PB}TrackInfoPayload, but who really needs this? */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h new file mode 100644 index 0000000000..4947e70f56 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/extended-interface/uid-list.h @@ -0,0 +1,15 @@ +#pragma once +#include + +/* [1] P.457 5.1.68 Command 0x004A: PrepareUIDList */ + +struct IAPPrepareUIDListPayload { + uint16_t current_sector; + uint16_t max_sectors; + uint8_t uids[][8]; +} __attribute__((packed)); + +struct IAPPlayPreparedUIDListPayload { + uint8_t reserved; + uint8_t uid[8]; +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general.h b/firmware/usbstack/iap/libiap/spec/lingoes/general.h new file mode 100644 index 0000000000..5bcae5276d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general.h @@ -0,0 +1,112 @@ +#pragma once +#include + +/* [1] P.119 Table 3-1 General lingo commands */ +enum IAPGeneralCommandID { + IAPGeneralCommandID_RequestIdentify = 0x00, /* 0.00, from dev, no payload */ + IAPGeneralCommandID_Identify = 0x01, /* 0.00, from acc, identify.h, deprecated */ + IAPGeneralCommandID_IPodAck = 0x02, /* 1.00, from dev, ipod-ack.h */ + IAPGeneralCommandID_RequestExtendedInterfaceMode = 0x03, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnExtendedInterfaceMode = 0x04, /* 1.00, from dev, extended-interface-mode.h */ + IAPGeneralCommandID_EnterExtendedInterfaceMode = 0x05, /* 1.00, from acc, no payload, deprecated */ + IAPGeneralCommandID_ExitExtendedInterfaceMode = 0x06, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_RequestIPodName = 0x07, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodName = 0x08, /* 1.00, from dev, ipod-name.h */ + IAPGeneralCommandID_RequestIPodSoftwareVersion = 0x09, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodSoftwareVersion = 0x0A, /* 1.00, from dev, ipod-software-version.h */ + IAPGeneralCommandID_RequestIPodSerialNum = 0x0B, /* 1.00, from acc, no payload */ + IAPGeneralCommandID_ReturnIPodSerialNum = 0x0C, /* 1.00, from dev, ipod-serial-num.h */ + IAPGeneralCommandID_RequestIPodModelNum = 0x0D, /* 1.00, from acc, deprecated */ + IAPGeneralCommandID_ReturnIPodModelNum = 0x0E, /* 1.00, from dev, deprecated */ + IAPGeneralCommandID_RequestLingoProtocolVersion = 0x0F, /* 1.00, from acc, lingo-protocol-version.h */ + IAPGeneralCommandID_ReturnLingoProtocolVersion = 0x10, /* 1.00, from dev, lingo-protocol-version.h */ + IAPGeneralCommandID_RequestTransportMaxPayloadSize = 0x11, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_ReturnTransportMaxPayloadSize = 0x12, /* 1.09, from dev, transport-max-payload-size.h */ + IAPGeneralCommandID_IdentifyDeviceLingoes = 0x13, /* 1.01, from acc, identify-device-lingoes.h */ + IAPGeneralCommandID_GetAccessoryAuthenticationInfo = 0x14, /* 1.01, from dev, no payload */ + IAPGeneralCommandID_RetAccessoryAuthenticationInfo = 0x15, /* 1.01, from acc, acc-auth-info.h */ + IAPGeneralCommandID_AckAccessoryAuthenticationInfo = 0x16, /* 1.01, from dev, acc-auth-info.h */ + IAPGeneralCommandID_GetAccessoryAuthenticationSignature = 0x17, /* 1.01, from dev, acc-auth-sig.h */ + IAPGeneralCommandID_RetAccessoryAuthenticationSignature = 0x18, /* 1.01, from acc, acc-auth-sig.h */ + IAPGeneralCommandID_AckAccessoryAuthenticationStatus = 0x19, /* 1.01, from dev, acc-auth-sig.h*/ + IAPGeneralCommandID_GetIPodAuthenticationInfo = 0x1A, /* 1.01, from acc, no payload */ + IAPGeneralCommandID_RetIPodAuthenticationInfo = 0x1B, /* 1.01, from dev, ipod-auth-info.h */ + IAPGeneralCommandID_AckIPodAuthenticationInfo = 0x1C, /* 1.01, from acc, ipod-auth-info.h */ + IAPGeneralCommandID_GetIPodAuthenticationSignature = 0x1D, /* 1.01, from acc, ipod-auth-sig.h */ + IAPGeneralCommandID_RetIPodAuthenticationSignature = 0x1E, /* 1.01, from dev, ipod-auth-sig.h */ + IAPGeneralCommandID_AckIPodAuthenticationStatus = 0x1F, /* 1.01, from acc, ipod-auth-sig.h */ + IAPGeneralCommandID_NotifyIPodStateChange = 0x23, /* 1.02, from dev, notify-ipod-state-change.h */ + IAPGeneralCommandID_GetIPodOptions = 0x24, /* 1.05, from acc, no payload */ + IAPGeneralCommandID_RetIPodOptions = 0x25, /* 1.05, from dev, ipod-options.h */ + IAPGeneralCommandID_GetAccessoryInfo = 0x27, /* 1.04, from dev, acc-info.h */ + IAPGeneralCommandID_RetAccessoryInfo = 0x28, /* 1.04, from acc, acc-info.h */ + IAPGeneralCommandID_GetIPodPreferences = 0x29, /* 1.05, from acc, ipod-preferences.h */ + IAPGeneralCommandID_RetIPodPreferences = 0x2A, /* 1.05, from dev, ipod-preferences.h */ + IAPGeneralCommandID_SetIPodPreferences = 0x2B, /* 1.05, from acc, ipod-preferences.h */ + IAPGeneralCommandID_GetUIMode = 0x35, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetUIMode = 0x36, /* 1.09, from dev, ui-mode.h */ + IAPGeneralCommandID_SetUIMode = 0x37, /* 1.09, from acc, ui-mode.h */ + IAPGeneralCommandID_StartIDPS = 0x38, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_SetFIDTokenValues = 0x39, /* 1.09, from acc, fid-token-values.h */ + IAPGeneralCommandID_AckFIDTokenValues = 0x3A, /* 1.09, from dev, fid-token-values.h */ + IAPGeneralCommandID_EndIDPS = 0x3B, /* 1.09, from acc, end-idps.h */ + IAPGeneralCommandID_IDPSStatus = 0x3C, /* 1.09, from dev, end-idps.h */ + IAPGeneralCommandID_OpenDataSessionForProtocol = 0x3F, /* 1.09, from dev, data-session.h */ + IAPGeneralCommandID_CloseDataSession = 0x40, /* 1.09, from dev, data-session.h */ + IAPGeneralCommandID_AccessoryAck = 0x41, /* 1.09, from acc, acc-ack.h */ + IAPGeneralCommandID_AccessoryDataTransfer = 0x42, /* 1.09, from acc, data-transfer.h */ + IAPGeneralCommandID_IPodDataTransfer = 0x43, /* 1.09, from dev, data-transfer.h */ + IAPGeneralCommandID_SetAccessoryStatusNotification = 0x46, /* 1.09, from dev, acc-status-notification.h */ + IAPGeneralCommandID_RetAccessoryStatusNotification = 0x47, /* 1.09, from acc, acc-status-notification.h */ + IAPGeneralCommandID_AccessoryStatusNotification = 0x48, /* 1.09, from acc, acc-status-notification.h */ + IAPGeneralCommandID_SetEventNotification = 0x49, /* 1.09, from acc, event-notification.h */ + IAPGeneralCommandID_IPodNotification = 0x4A, /* 1.09, from dev, ipod-notifiction.h */ + IAPGeneralCommandID_GetIPodOptionsForLingo = 0x4B, /* 1.09, from acc, ipod-options-for-lingo.h */ + IAPGeneralCommandID_RetIPodOptionsForLingo = 0x4C, /* 1.09, from dev, ipod-options-for-lingo.h */ + IAPGeneralCommandID_GetEventNotification = 0x4D, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetEventNotification = 0x4E, /* 1.09, from dev, event-notification.h */ + IAPGeneralCommandID_GetSupportedEventNotification = 0x4F, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_CancelCommand = 0x50, /* 1.09, from acc, cancel-command.h */ + IAPGeneralCommandID_RetSupportedEventNotification = 0x51, /* 1.09, from dev, event-notification.h */ + IAPGeneralCommandID_SetAvailableCurrent = 0x54, /* 1.09, from acc, set-available-current.h */ + IAPGeneralCommandID_SetInternalBatteryChargingState = 0x56, /* 1.09, from acc, set-internal-battery-charging-state.h */ + IAPGeneralCommandID_RequestApplicationLaunch = 0x64, /* 1.09, from acc, request-application-launch.h */ + IAPGeneralCommandID_GetNowPlayingApplicationBundleName = 0x65, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_RetNowPlayingApplicationBundleName = 0x66, /* 1.09, from dev, now-playing-app-bundle-name.h */ + IAPGeneralCommandID_GetLocalizationInfo = 0x67, /* 1.09, from acc, localization-info.h */ + IAPGeneralCommandID_RetLocalizationInfo = 0x68, /* 1.09, from dev, localization-info.h */ + IAPGeneralCommandID_RequestWiFiConnectionInfo = 0x69, /* 1.09, from acc, no payload */ + IAPGeneralCommandID_WiFiConnectionInfo = 0x6A, /* 1.09, from dev, wifi-connection-info.h */ +}; + +#include "general/acc-ack.h" +#include "general/acc-auth-info.h" +#include "general/acc-auth-sig.h" +#include "general/acc-info.h" +#include "general/acc-status-notification.h" +#include "general/cancel-command.h" +#include "general/data-session.h" +#include "general/data-transfer.h" +#include "general/end-idps.h" +#include "general/event-notification.h" +#include "general/extended-interface-mode.h" +#include "general/fid-token-values.h" +#include "general/identify-device-lingoes.h" +#include "general/identify.h" +#include "general/ipod-ack.h" +#include "general/ipod-auth-info.h" +#include "general/ipod-auth-sig.h" +#include "general/ipod-name.h" +#include "general/ipod-notification.h" +#include "general/ipod-options-for-lingo.h" +#include "general/ipod-options.h" +#include "general/ipod-preferences.h" +#include "general/ipod-software-version.h" +#include "general/lingo-protocol-version.h" +#include "general/localization-info.h" +#include "general/now-playing-app-bundle-name.h" +#include "general/request-application-launch.h" +#include "general/set-available-current.h" +#include "general/transport-max-payload-size.h" +#include "general/ui-mode.h" +#include "general/wifi-connection-info.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h new file mode 100644 index 0000000000..3d9a4d91ef --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-ack.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPAccAckPayload { + uint8_t status; /* = IAPAckStatus_{Success,EBadParameter} */ + uint8_t id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h new file mode 100644 index 0000000000..2dd764e93e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-info.h @@ -0,0 +1,27 @@ +#pragma once +#include + +struct IAPRetAccAuthInfoPayload { + uint8_t protocol_major; + uint8_t protocol_minor; +} __attribute__((packed)); + +struct IAPRetAccAuthInfoPayload2p0 { + uint8_t protocol_major; /* = 0x02 */ + uint8_t protocol_minor; /* = 0x00 */ + uint8_t cert_current_section_index; + uint8_t cert_max_section_index; + uint8_t cert_data[]; +} __attribute__((packed)); + +enum IAPAckAccAuthInfoStatus { + IAPAckAccAuthInfoStatus_Supported = 0x00, + IAPAckAccAuthInfoStatus_CertTooLong = 0x04, + IAPAckAccAuthInfoStatus_Unsupported = 0x08, + IAPAckAccAuthInfoStatus_InvalidCert = 0x0A, + IAPAckAccAuthInfoStatus_InvalidCertPerm = 0x0B, +}; + +struct IAPAckAccAuthInfoPayload { + uint8_t status; /* IAPAckAccAuthInfoStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h new file mode 100644 index 0000000000..9fd22d2598 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-auth-sig.h @@ -0,0 +1,22 @@ +#pragma once +#include + +struct IAPGetAccAuthSigPayload1p0 { + uint8_t challenge[16]; + uint8_t retry; +} __attribute__((packed)); + +struct IAPGetAccAuthSigPayload2p0 { + uint8_t challenge[20]; + uint8_t retry; +} __attribute__((packed)); + +/* +struct IAPRetAccAuthSigPayload { + uint8_t sig[]; +} __attribute__((packed)); +*/ + +struct IAPAckAccAuthSigPayload { + uint8_t status; /* IAPAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h new file mode 100644 index 0000000000..6f91bee72e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-info.h @@ -0,0 +1,83 @@ +#pragma once +#include + +enum IAPAccInfoType { + IAPAccInfoType_AccInfoCaps = 0x00, /* G1 -> R2 */ + IAPAccInfoType_AccName = 0x01, /* G1 -> R1 */ + IAPAccInfoType_MinDeviceFirmwareVersion = 0x02, /* G2 -> R3 */ + IAPAccInfoType_MinLingoVersion = 0x03, /* G3 -> R4 */ + IAPAccInfoType_FirmwareVersion = 0x04, /* G1 -> R5 */ + IAPAccInfoType_HardwareVersion = 0x05, /* G1 -> R5 */ + IAPAccInfoType_Manufacture = 0x06, /* G1 -> R2 */ + IAPAccInfoType_ModelNumber = 0x07, /* G1 -> R2 */ + IAPAccInfoType_SerialNumber = 0x08, /* G1 -> R2 */ + IAPAccInfoType_MaxPayloadSize = 0x09, /* G1 -> R6 */ + IAPAccInfoType_SupportedStatusTypes = 0x0B, /* G1 -> R7 */ +}; + +/* G1 */ +struct IAPGetAccInfoPayload { + uint8_t type; /* IAPAccInfoType */ +} __attribute__((packed)); + +/* G2 */ +struct IAPGetAccInfoMinDeviceFirmwareVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinDeviceFirmwareVersion */ + uint32_t model_id; +} __attribute__((packed)); + +/* G3 */ +struct IAPGetAccInfoMinLingoVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinLingoVersion */ + uint8_t lingo_id; +}; + +/* R1 */ +struct IAPRetAccInfoPayload { + uint8_t type; /* IAPAccInfoType */ + char value[]; +}; + +/* R2 */ +struct IAPRetAccInfoCapsPayload { + uint8_t type; /* = IAPAccInfoType_AccInfoCaps */ + uint32_t caps; +} __attribute__((packed)); + +/* R3 */ +struct IAPRetAccInfoMinDeviceFirmwareVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinDeviceFirmwareVersion */ +}; + +/* R4 */ +struct IAPRetAccInfoMinLingoVersionPayload { + uint8_t type; /* = IAPAccInfoType_MinLingoVersion */ + uint8_t lingo_id; + uint8_t major; + uint8_t minor; +}; + +/* R5 */ +struct IAPRetAccInfoFirmHardVersionPayload { + uint8_t type; /* = IAPAccInfoType_{Firmware,Hardware}Version */ + uint8_t major; + uint8_t minor; + uint8_t revision; +} __attribute__((packed)); + +/* R6 */ +struct IAPRetAccInfoMaxPayloadSizePayload { + uint8_t type; /* = IAPAccInfoType_MaxPayloadSize */ + uint16_t max_payload_size; +} __attribute__((packed)); + +enum IAPAccInfoStatusTypes { + IAPAccInfoStatusTypes_Bluetooth = 0b0010, + IAPAccInfoStatusTypes_FaultCondition = 0b0100, +}; + +/* R7 */ +struct IAPRetAccInfoSupportedStatusTypes { + uint8_t type; /* = IAPAccInfoType_SupportedStatusTypes */ + uint32_t status_types; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h new file mode 100644 index 0000000000..501d6d2ce6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/acc-status-notification.h @@ -0,0 +1,18 @@ +#pragma once +#include + +struct IAPSetAccStatusNotificationPayload { + uint32_t mask; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); + +struct IAPRetAccStatusNotificationPayload { + uint32_t mask; /* IAPAccInfoStatusTypes */ +} __attribute__((packed)); + +struct IAPAccStatusNotificationPayload { + uint32_t type; /* IAPAccInfoStatusTypes */ + uint8_t params[]; +} __attribute__((packed)); + +/* [1] P.181 Table 3-108 AccessoryStatusNotification parameters */ +/* TODO: add definitions */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h new file mode 100644 index 0000000000..af10443935 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/cancel-command.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPCancelCommandPayload { + uint8_t lingo_id; + uint16_t command_id; + uint16_t transaction_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h new file mode 100644 index 0000000000..1367987cd6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-session.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPOpenDataSessionForProtocolPayload { + uint16_t session_id; + uint8_t protocol_index; +} __attribute__((packed)); + +struct IAPCloseDataSessionPayload { + uint16_t session_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h new file mode 100644 index 0000000000..73cfa522a1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/data-transfer.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPAccDataTransferPayload { + uint16_t session_id; + uint8_t data[]; +} __attribute__((packed)); + +struct IAPIPodDataTransferPayload { + uint16_t session_id; + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h new file mode 100644 index 0000000000..c9c7fe41e1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/end-idps.h @@ -0,0 +1,33 @@ +#pragma once +#include + +enum IAPEndIDPSStatus { + IAPEndIDPSStatus_Success = 0x00, + IAPEndIDPSStatus_Reset = 0x01, + IAPEndIDPSStatus_Abort = 0x02, + IAPEndIDPSStatus_AnotherTransport = 0x03, +}; + +struct IAPEndIDPSPayload { + uint8_t status; /* IAPEndIDPSStatus */ +}; + +enum IAPIDPSStatus { + /* IAPEndIDPSStatus_Success */ + IAPIDPSStatus_Success = 0x00, + IAPIDPSStatus_RequiredTokenRejected = 0x01, + IAPIDPSStatus_RequiredTokenMissing = 0x02, + IAPIDPSStatus_RequiredTokenRejectedMissing = 0x03, + /* IAPEndIDPSStatus_Reset */ + IAPIDPSStatus_NotTimedOut = 0x04, + IAPIDPSStatus_TimedOut = 0x05, + /* IAPEndIDPSStatus_Abort */ + IAPIDPSStatus_Aborted = 0x06, + /* IAPEndIDPSStatus_AnotherTransport */ + IAPIDPSStatus_Failed = 0x07, + +}; + +struct IAPIDPSStatusPayload { + uint8_t status; /* IAPIDPSStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h new file mode 100644 index 0000000000..d6ac7f1505 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/event-notification.h @@ -0,0 +1,31 @@ +#pragma once +#include + +/* [1] P.184 Table 3-112 Notification bitmask bits */ + +enum IAPEventNotificationEvents { + IAPSetEventNotificationEvents_FlowControl = 1 << 2, + IAPSetEventNotificationEvents_RadioTagging = 1 << 3, + IAPSetEventNotificationEvents_Camera = 1 << 4, + IAPSetEventNotificationEvents_ChargingInfo = 1 << 5, + IAPSetEventNotificationEvents_DatabaseChanged = 1 << 9, + IAPSetEventNotificationEvents_AppBundleName = 1 << 10, + IAPSetEventNotificationEvents_SessionSpaceAvail = 1 << 11, + IAPSetEventNotificationEvents_CommandComplete = 1 << 13, + IAPSetEventNotificationEvents_IPodOutMode = 1 << 15, + IAPSetEventNotificationEvents_BluetoothConnection = 1 << 17, + IAPSetEventNotificationEvents_AppDisplayName = 1 << 19, + IAPSetEventNotificationEvents_AssistiveTouch = 1 << 20, +}; + +struct IAPSetEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); + +struct IAPRetEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); + +struct IAPRetSupportedEventNotificationPayload { + uint64_t mask; /* IAPEventNotificationEvents */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h new file mode 100644 index 0000000000..5a19804e26 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/extended-interface-mode.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.127 3.3.4 Command 0x04: ReturnExtendedInterfaceMode */ + +struct IAPReturnExtendedInterfaceModePayload { + uint8_t is_ext_mode; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h new file mode 100644 index 0000000000..01bbf6ffbd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values.h @@ -0,0 +1,58 @@ +#pragma once +#include + +/* [1] P.161 Table 3-67 FIDTokenValues tokens */ +/* = 0x{type}{subtype} */ +enum IAPFIDTokenTypes { + IAPFIDTokenTypes_Identify = 0x0000, + IAPFIDTokenTypes_AccCaps = 0x0001, + IAPFIDTokenTypes_AccInfo = 0x0002, + IAPFIDTokenTypes_IPodPreference = 0x0003, + IAPFIDTokenTypes_EAProtocol = 0x0004, + IAPFIDTokenTypes_BundleSeedIDPref = 0x0005, + IAPFIDTokenTypes_ScreenInfo = 0x0007, + IAPFIDTokenTypes_EAProtocolMetadata = 0x0008, + IAPFIDTokenTypes_AccDigitalAudioSampleRates = 0x000E, + IAPFIDTokenTypes_AccDigitalAudioVideoDelay = 0x000F, + IAPFIDTokenTypes_MicrophoneCaps = 0x0100, +}; + +/* [1] P.161 Table 3-66 FIDTokenValues field format */ +struct IAPFIDTokenValuesToken { + uint8_t length; + uint8_t type; + uint8_t subtype; + uint8_t data[]; +} __attribute__((packed)); + +/* [1] P.170 Table 3-86 Acknowledgment status codes */ +enum IAPFIDTokenValuesAckStatus { + IAPFIDTokenValuesIdentifyAckStatus_Accepted = 0x00, + IAPFIDTokenValuesIdentifyAckStatus_RequiredFailed = 0x01, + IAPFIDTokenValuesIdentifyAckStatus_OptionalFailed = 0x02, + IAPFIDTokenValuesIdentifyAckStatus_NotSupported = 0x03, + IAPFIDTokenValuesIdentifyAckStatus_LingoBusy = 0x04, + IAPFIDTokenValuesIdentifyAckStatus_MaxConnections = 0x05, +}; + +#include "fid-token-values/acc-caps.h" +#include "fid-token-values/acc-digital-audio-sample-rates.h" +#include "fid-token-values/acc-digital-audio-video-delay.h" +#include "fid-token-values/acc-info.h" +#include "fid-token-values/bundle-seed-id-pref.h" +#include "fid-token-values/ea-protocol-metadata.h" +#include "fid-token-values/ea-protocol.h" +#include "fid-token-values/identify.h" +#include "fid-token-values/ipod-preference.h" +#include "fid-token-values/microphone-caps.h" +#include "fid-token-values/screen-info.h" + +struct IAPSetFIDTokenValuesPayload { + uint8_t num_token_values; + uint8_t data[]; /* packed token values */ +}; + +struct IAPAckFIDTokenValuesPayload { + uint8_t num_token_value_acks; + uint8_t data[]; /* packed token value acks */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h new file mode 100644 index 0000000000..3a86700594 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-caps.h @@ -0,0 +1,31 @@ +#pragma once +#include + +/* [1] P.164 Table 3-71 Accessory capabilities bit values */ +enum IAPFIDTokenValuesAccCapsBits { + IAPFIDTokenValuesAccCapsBits_LineOut = 1 << 0, + IAPFIDTokenValuesAccCapsBits_LineIn = 1 << 1, + IAPFIDTokenValuesAccCapsBits_VideoOut = 1 << 2, + IAPFIDTokenValuesAccCapsBits_USBAudioOut = 1 << 4, + IAPFIDTokenValuesAccCapsBits_AppsCommunication = 1 << 9, + IAPFIDTokenValuesAccCapsBits_CheckVolume = 1 << 11, + IAPFIDTokenValuesAccCapsBits_VoiceOver = 1 << 17, + IAPFIDTokenValuesAccCapsBits_AsyncPlaybackChange = 1 << 18, + IAPFIDTokenValuesAccCapsBits_MixedResponse = 1 << 19, + IAPFIDTokenValuesAccCapsBits_AudioRoutingSwitch = 1 << 21, + IAPFIDTokenValuesAccCapsBits_AssistiveTouch = 1 << 23, +}; + +struct IAPFIDTokenValuesAccCapsToken { + uint8_t length; /* = 0x0A */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x01 */ + uint64_t caps_bits; /* IAPFIDTokenValuesAccCapsBits */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccCapsAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x01 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h new file mode 100644 index 0000000000..e9ee239aaa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-sample-rates.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesAccDigitalAudioSampleRatesToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0E */ + uint32_t sample_rates[]; +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccDigitalAudioSampleRatesAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0E */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h new file mode 100644 index 0000000000..b658d55c5f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-digital-audio-video-delay.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesAccDigitalAudioVideoDelayToken { + uint8_t length; /* = 0x06 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0F */ + uint32_t delay; +} __attribute__((packed)); + +struct IAPFIDTokenValuesAccDigitalAudioVideoDelayAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x0F */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h new file mode 100644 index 0000000000..94926ae7d9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/acc-info.h @@ -0,0 +1,33 @@ +#pragma once +#include + +/* [1] P.165 Table 3-73 Accessory Info Type values */ +enum IAPFIDTokenValuesAccInfoTypes { + IAPFIDTokenValuesAccInfoTypes_AccName = 0x01, + IAPFIDTokenValuesAccInfoTypes_FirmwareVersion = 0x04, + IAPFIDTokenValuesAccInfoTypes_HardwareVersion = 0x05, + IAPFIDTokenValuesAccInfoTypes_Manufacture = 0x06, + IAPFIDTokenValuesAccInfoTypes_ModelNumber = 0x07, + IAPFIDTokenValuesAccInfoTypes_SerialNumber = 0x08, + IAPFIDTokenValuesAccInfoTypes_MaxPayloadSize = 0x09, + IAPFIDTokenValuesAccInfoTypes_AccStatus = 0x0B, + IAPFIDTokenValuesAccInfoTypes_RFCerts = 0x0C, +}; + +struct IAPFIDTokenValuesAccInfoToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x02 */ + uint8_t info_type; /* IAPFIDTokenValuesAccInfoTypes */ + uint8_t info[]; +} __attribute__((packed)); + +/* TODO: add info definitions */ + +struct IAPFIDTokenValuesAccInfoAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x02 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t info_type; /* IAPFIDTokenValuesAccInfoTypes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h new file mode 100644 index 0000000000..144ebc3ac8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/bundle-seed-id-pref.h @@ -0,0 +1,16 @@ +#pragma once +#include + +struct IAPFIDTokenValuesBundleSeedIDPrefToken { + uint8_t length; /* = 0x0D */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x05 */ + char bundle_seed_id_string[11]; +} __attribute__((packed)); + +struct IAPFIDTokenValuesBundleSeedIDPrefAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x05 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h new file mode 100644 index 0000000000..7ec88e97b7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol-metadata.h @@ -0,0 +1,23 @@ +#pragma once +#include + +enum IAPFIDTokenValuesEAProtocolMetadataTypes { + IAPFIDTokenValuesEAProtocolMetadataTypes_DontFindApp = 0x00, + IAPFIDTokenValuesEAProtocolMetadataTypes_FindApp = 0x01, + IAPFIDTokenValuesEAProtocolMetadataTypes_DontFindButDisplayMessage = 0x03, +}; + +struct IAPFIDTokenValuesEAProtocolMetadataToken { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x08 */ + uint8_t protocol_index; + uint8_t metadata_type; /* IAPFIDTokenValuesEAProtocolMetadataTypes */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesEAProtocolMetadataAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x08 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h new file mode 100644 index 0000000000..20558ea7d3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ea-protocol.h @@ -0,0 +1,20 @@ +#pragma once +#include + +/* [1] P.167 Table 3-76 EAProtocolToken format */ +struct IAPFIDTokenValuesEAProtocolToken { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x04 */ + uint8_t protocol_index; + char protocol_string[]; +} __attribute__((packed)); + +/* [1] P.171 Table 3-90 Acknowledgment format for EAProtocolToken */ +struct IAPFIDTokenValuesEAProtocolAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x04 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t protocol_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h new file mode 100644 index 0000000000..8779774b28 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/identify.h @@ -0,0 +1,28 @@ +#pragma once +#include + +struct IAPFIDTokenValuesIdentifyTokenHead { + uint8_t length; + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x00 */ + uint8_t num_lingoes; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyTokenTail { + uint32_t device_option; /* = 0b10 */ + uint32_t device_id; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyToken { + struct IAPFIDTokenValuesIdentifyTokenHead head; + /*uint8_t lingoes[num_lingoes]; */ + struct IAPFIDTokenValuesIdentifyTokenHead tail; +} __attribute__((packed)); + +struct IAPFIDTokenValuesIdentifyAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x00 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t lingo_ids[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h new file mode 100644 index 0000000000..722cc1db11 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/ipod-preference.h @@ -0,0 +1,21 @@ +#pragma once +#include + +/* [1] P.166 Table 3-75 iPodPreferenceToken format */ +struct IAPFIDTokenValuesIPodPreferenceToken { + uint8_t length; /* = 0x05 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x03 */ + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; + uint8_t restore_on_exit; /* = 0x01 */ +} __attribute__((packed)); + +/* [1] P.171 Table 3-89 Acknowledgment format for ipodPreferenceToken */ +struct IAPFIDTokenValuesIPodPreferenceAck { + uint8_t length; /* = 0x04 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x03 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ + uint8_t class_id; /* IAPIPodPereferenceClassID */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h new file mode 100644 index 0000000000..947f7852b6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/microphone-caps.h @@ -0,0 +1,24 @@ +#pragma once +#include + +enum IAPFIDTokenValuesMicrophoneCaps { + IAPFIDTokenValuesMicrophoneCaps_StereoInput = 1 << 0, + IAPFIDTokenValuesMicrophoneCaps_StereoMonoInputSwitch = 1 << 1, + IAPFIDTokenValuesMicrophoneCaps_VariableRecordLevel = 1 << 2, + IAPFIDTokenValuesMicrophoneCaps_RecordLevelLimit = 1 << 3, + IAPFIDTokenValuesMicrophoneCaps_DuplexAudio = 1 << 4, +}; + +struct IAPFIDTokenValuesMicrophoneCapsToken { + uint8_t length; /* = 0x06 */ + uint8_t type; /* = 0x01 */ + uint8_t subtype; /* = 0x00 */ + uint32_t caps_bits; /* IAPFIDTokenValuesMicrophoneCaps */ +} __attribute__((packed)); + +struct IAPFIDTokenValuesMicrophoneCapsAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x01 */ + uint8_t subtype; /* = 0x00 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h new file mode 100644 index 0000000000..a05c8b0faf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/fid-token-values/screen-info.h @@ -0,0 +1,27 @@ +#pragma once +#include + +enum IAPFIDTokenValuesScreenInfoFeatureBits { + IAPFIDTokenValuesScreenInfoFeatureBits_ColorDisplay = 1 << 0, +}; + +struct IAPFIDTokenValuesScreenInfoToken { + uint8_t length; /* = 0x10 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x07 */ + uint16_t total_screen_width_inches; + uint16_t total_screen_height_inches; + uint16_t total_screen_width_pixels; + uint16_t total_screen_height_pixels; + uint16_t ipod_out_screen_width_pixels; + uint16_t ipod_out_screen_height_pixels; + uint8_t screen_feature_mask; /* IAPFIDTokenValuesScreenInfoFeatureBits */ + uint8_t screen_gamma_value; +} __attribute__((packed)); + +struct IAPFIDTokenValuesScreenInfoAck { + uint8_t length; /* = 0x03 */ + uint8_t type; /* = 0x00 */ + uint8_t subtype; /* = 0x07 */ + uint8_t status; /* IAPFIDTokenValuesAckStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h new file mode 100644 index 0000000000..d2c36955e3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify-device-lingoes.h @@ -0,0 +1,30 @@ +#pragma once +#include + +/* [1] P.134 Table 3-24 Device Lingoes Spoken bits */ +enum IAPIdentifyDeviceLingoesLingoBits { + IAPIdentifyDeviceLingoesLingoBits_General = 1 << 0, + IAPIdentifyDeviceLingoesLingoBits_SimpleRemote = 1 << 2, + IAPIdentifyDeviceLingoesLingoBits_DisplayRemote = 1 << 3, + IAPIdentifyDeviceLingoesLingoBits_ExtendedInterface = 1 << 4, + IAPIdentifyDeviceLingoesLingoBits_AccessoryPower = 1 << 5, + IAPIdentifyDeviceLingoesLingoBits_USBHostMode = 1 << 6, + IAPIdentifyDeviceLingoesLingoBits_RFTuner = 1 << 7, + IAPIdentifyDeviceLingoesLingoBits_AccessoryEqualizer = 1 << 8, + IAPIdentifyDeviceLingoesLingoBits_Sports = 1 << 9, + IAPIdentifyDeviceLingoesLingoBits_DigitalAudio = 1 << 10, + IAPIdentifyDeviceLingoesLingoBits_Storage = 1 << 12, +}; + +/* [1] P.135 Table 3-25 IdentifyDeviceLingoes Options bits */ +enum IAPIdentifyDeviceLingoesOptions { + IAPIdentifyDeviceLingoesOptions_NoAuth = 0b00, + IAPIdentifyDeviceLingoesOptions_DeferAuth = 0b01, + IAPIdentifyDeviceLingoesOptions_ImmediateAuth = 0b10, +}; + +struct IAPIdentifyDeviceLingoesPayload { + uint32_t lingoes_bits; /* IAPIdentifyDeviceLingoesLingoBits */ + uint32_t options; /* IAPIdentifyDeviceLingoesOptions */ + uint32_t device_id; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h new file mode 100644 index 0000000000..ef469fffa6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/identify.h @@ -0,0 +1,15 @@ +#pragma once +#include + +/* [1] P.531 Table C-8 Identify packet */ +struct IAPIdentifyPayload { + uint8_t supported_lingo; +}; + +/* [1] P.531 Table C-9 Identify packet for high-power accessories */ +struct IAPIdentifyHighPowerPayload { + uint8_t supported_lingo; /* = IAPLingoID_AccessoryPower */ + uint8_t reserved; /* = 0x00 */ + uint8_t num_valid_bits; /* = 0x02 */ + uint8_t option_flags; /* TODO: add bitfield definition */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h new file mode 100644 index 0000000000..3dfe657a64 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-ack.h @@ -0,0 +1,57 @@ +#pragma once +#include + +/* [1] P.124 3.3.2 Command 0x02: iPodAck */ + +enum IAPAckStatus { + IAPAckStatus_Success = 0x00, + IAPAckStatus_EUnknownDatabaseOrSessionID = 0x01, + IAPAckStatus_ECommandFailed = 0x02, + IAPAckStatus_EOutOfResource = 0x03, + IAPAckStatus_EBadParameter = 0x04, + IAPAckStatus_EUnknownID = 0x05, + IAPAckStatus_CommandPending = 0x06, + IAPAckStatus_ENotAuthenticated = 0x07, + IAPAckStatus_EBadAuthVersion = 0x08, + IAPAckStatus_EAccPowerModeRequestFailed = 0x09, + IAPAckStatus_EInvalidCert = 0x0A, + IAPAckStatus_EInvalidCertPerm = 0x0B, + IAPAckStatus_EFileInUse = 0x0C, + IAPAckStatus_EInvalidFileHandle = 0x0D, + IAPAckStatus_EDirectoryNotEmpty = 0x0E, + IAPAckStatus_EOperationTimedOut = 0x0F, + IAPAckStatus_ECommandUnavailable = 0x10, + IAPAckStatus_EBadWire = 0x11, + IAPAckStatus_ESelectionNotGenius = 0x12, + IAPAckStatus_MultiDataRecvSuccess = 0x13, + IAPAckStatus_ELingoBusy = 0x14, + IAPAckStatus_EMaxNumAcc = 0x15, + IAPAckStatus_HIDIndexInUse = 0x16, + IAPAckStatus_EDropped = 0x17, + IAPAckStatus_EInvalidVideoSettings = 0x18, +}; + +struct IAPIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; +} __attribute__((packed)); + +struct IAPIPodAckCommandPendingPayload { + uint8_t status; /* = IAPAckStatus_CommandPending */ + uint8_t id; + uint32_t time_ms; +} __attribute__((packed)); + +/* [1] P.389 Table 4-292 Multisection iPodAck packet */ +struct IAPIPodAckMultiSectPayload { + uint8_t status; /* IAPAckStatus_IAPAckStatus_MultiDataRecvSuccess */ + uint8_t id; + uint16_t sect_cur; +}; + +struct IAPIPodAckEDroppedPayload { + uint8_t status; /* = IAPAckStatus_EDropped */ + uint8_t id; /* = IAPCommandIDGeneral_AccessoryDataTransfer */ + uint16_t session_id; + uint32_t num_dropped_bytes; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h new file mode 100644 index 0000000000..de03274418 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-info.h @@ -0,0 +1,14 @@ +#pragma once +#include + +struct IAPRetIPodAuthInfoPayload2p0 { + uint8_t protocol_major; /* = 0x02 */ + uint8_t protocol_minor; /* = 0x00 */ + uint8_t cert_current_section_index; + uint8_t cert_max_section_index; + uint8_t cert_data[]; +} __attribute__((packed)); + +struct IAPAckIPodAuthInfoPayload { + uint8_t status; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h new file mode 100644 index 0000000000..977b299790 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-auth-sig.h @@ -0,0 +1,17 @@ +#pragma once +#include + +struct IAPGetIPodAuthSigPayload2p0 { + uint8_t challenge[20]; + uint8_t retry; +} __attribute__((packed)); + +/* +struct IAPRetIPodAuthSigPayload { + uint8_t sig[]; +} __attribute__((packed)); +*/ + +struct IAPAckIPodAuthSigPayload { + uint8_t status; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h new file mode 100644 index 0000000000..7cedcedeff --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-name.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPReturnIPodNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h new file mode 100644 index 0000000000..2674c4867e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-notification.h @@ -0,0 +1,120 @@ +#pragma once +#include + +enum IAPIPodNotificationType { + IAPIPodNotificationType_FlowControl = 2, + IAPIPodNotificationType_RadioTagging = 3, + IAPIPodNotificationType_Camera = 4, + IAPIPodNotificationType_ChargingInfo = 5, + IAPIPodNotificationType_DatabaseChanged = 9, + IAPIPodNotificationType_AppBundleName = 10, + IAPIPodNotificationType_SessionSpaceAvail = 11, + IAPIPodNotificationType_CommandComplete = 13, + IAPIPodNotificationType_IPodOutMode = 15, + IAPIPodNotificationType_BluetoothConnection = 17, + IAPIPodNotificationType_AppDisplayName = 19, + IAPIPodNotificationType_AssistiveTouch = 20, +}; + +struct IAPIPodNotificationPayload { + uint8_t type; /* IAPIPodNotificationType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPIPodNotificationFlowControlPayload { + uint8_t type; /* = IAPIPodNotificationType_FlowControl */ + uint32_t wait_time_ms; + uint16_t overflow_transaction_id; +} __attribute__((packed)); + +enum IAPIPodNotificationRadioTaggingStatus { + IAPIPodNotificationRadioTaggingStatus_Success = 0x00, + IAPIPodNotificationRadioTaggingStatus_Failed = 0x01, + IAPIPodNotificationRadioTaggingStatus_InfoAvail = 0x02, + IAPIPodNotificationRadioTaggingStatus_InfoNotAvail = 0x03, +}; + +struct IAPIPodNotificationRadioTaggingPayload { + uint8_t type; /* = IAPIPodNotificationType_RadioTagging */ + uint8_t status; /* IAPIPodNotificationRadioTaggingStatus */ +} __attribute__((packed)); + +enum IAPIPodNotificationCameraStatus { + IAPIPodNotificationCameraStatus_AppOff = 0x00, + IAPIPodNotificationCameraStatus_Preview = 0x03, + IAPIPodNotificationCameraStatus_Recording = 0x04, +}; + +struct IAPIPodNotificationCameraPayload { + uint8_t type; /* = IAPIPodNotificationType_Camera */ + uint8_t status; /* IAPIPodNotificationCameraStatus */ +} __attribute__((packed)); + +enum IAPIPodNotificationChargingInfoType { + IAPIPodNotificationChargingInfoType_AvailCurrent = 0x00, +}; + +struct IAPIPodNotificationChargingInfoPayload { + uint8_t type; /* = IAPIPodNotificationType_ChargingInfo */ + uint8_t info_type; /* IAPIPodNotificationChargingInfoType */ + uint16_t info_value; +} __attribute__((packed)); + +struct IAPIPodNotificationDatabaseChangedPayload { + uint8_t type; /* = IAPIPodNotificationType_DatabaseChanged */ +} __attribute__((packed)); + +struct IAPIPodNotificationAppBundleNamePayload { + uint8_t type; /* = IAPIPodNotificationType_AppBundleName */ + char name[]; +} __attribute__((packed)); + +struct IAPIPodNotificationSessionSpaceAvailPayload { + uint8_t type; /* = IAPIPodNotificationType_SessionSpaceAvail */ + uint16_t session_id; +} __attribute__((packed)); + +enum IAPIPodNotificationCommandCompleteStatus { + IAPIPodNotificationCommandCompleteStatus_Success = 0x00, + IAPIPodNotificationCommandCompleteStatus_Failed = 0x01, + IAPIPodNotificationCommandCompleteStatus_Cancelled = 0x02, +}; + +struct IAPIPodNotificationCommandCompletePayload { + uint8_t type; /* = IAPIPodNotificationType_CommandComplete */ + uint8_t lingo_id; + uint16_t command_id; + uint8_t status; /* IAPIPodNotificationCommandCompleteStatus */ +} __attribute__((packed)); + +struct IAPIPodNotificationIPodOutModePayload { + uint8_t type; /* = IAPIPodNotificationType_IPodOutMode */ + uint8_t is_active; +} __attribute__((packed)); + +enum IAPIPodNotificationBluetoothConnectionStatusBits { + IAPIPodNotificationBluetoothConnectionStatusBits_HFP = 1 << 0, + IAPIPodNotificationBluetoothConnectionStatusBits_PBAP = 1 << 1, + IAPIPodNotificationBluetoothConnectionStatusBits_AVRCP = 1 << 3, + IAPIPodNotificationBluetoothConnectionStatusBits_A2DP = 1 << 4, + IAPIPodNotificationBluetoothConnectionStatusBits_HID = 1 << 5, + IAPIPodNotificationBluetoothConnectionStatusBits_IAP = 1 << 7, + IAPIPodNotificationBluetoothConnectionStatusBits_PAN_NAP = 1 << 8, + IAPIPodNotificationBluetoothConnectionStatusBits_PAN_U = 1 << 12, +}; + +struct IAPIPodNotificationBluetoothConnectionPayload { + uint8_t type; /* = IAPIPodNotificationType_BluetoothConnection */ + uint8_t mac_addr[6]; + uint64_t status; /* IAPIPodNotificationBluetoothConnectionStatusBits */ +} __attribute__((packed)); + +struct IAPIPodNotificationAppDisplayNamePayload { + uint8_t type; /* = IAPIPodNotificationType_AppDisplayName */ + char name[]; +} __attribute__((packed)); + +struct IAPIPodNotificationAssistiveTouchPayload { + uint8_t type; /* = IAPIPodNotificationType_AssistiveTouch */ + uint8_t is_on; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h new file mode 100644 index 0000000000..aeeca1a793 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options-for-lingo.h @@ -0,0 +1,114 @@ +#pragma once +#include + +struct IAPGetIPodOptionsForLingoPayload { + uint8_t lingo_id; +} __attribute__((packed)); + +/* [1] P.192 Table 3-132 RetiPodOptionsForLingo option bits */ + +enum IAPRetIPodOptionsForLingoGeneralBits { + IAPRetIPodOptionsForLingoGeneralBits_LineOutUsage = 1 << 0, + IAPRetIPodOptionsForLingoGeneralBits_VideOutput = 1 << 1, + IAPRetIPodOptionsForLingoGeneralBits_VideoNTSCFormat = 1 << 2, + IAPRetIPodOptionsForLingoGeneralBits_VideoPALFormat = 1 << 3, + IAPRetIPodOptionsForLingoGeneralBits_VideoCompositeConnection = 1 << 4, + IAPRetIPodOptionsForLingoGeneralBits_VideoSVideoConnection = 1 << 5, + IAPRetIPodOptionsForLingoGeneralBits_VideoComponentConnection = 1 << 6, + IAPRetIPodOptionsForLingoGeneralBits_VideoClosedCaptioning = 1 << 7, + IAPRetIPodOptionsForLingoGeneralBits_VideoAspectRatio4x3 = 1 << 8, + IAPRetIPodOptionsForLingoGeneralBits_VideoAspectRatio16x9 = 1 << 9, + IAPRetIPodOptionsForLingoGeneralBits_VideoSubtitle = 1 << 10, + IAPRetIPodOptionsForLingoGeneralBits_VideoAltAudioChannel = 1 << 11, + IAPRetIPodOptionsForLingoGeneralBits_AppCommnunication = 1 << 13, + IAPRetIPodOptionsForLingoGeneralBits_DeviceNotification = 1 << 14, + IAPRetIPodOptionsForLingoGeneralBits_PauseOnRemovalControl = 1 << 19, + IAPRetIPodOptionsForLingoGeneralBits_RestrictedIDPS = 1 << 23, + IAPRetIPodOptionsForLingoGeneralBits_AppLaunchRequest = 1 << 24, + IAPRetIPodOptionsForLingoGeneralBits_AudioRouteSwitch = 1 << 26, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeHardwareInvoke = 1 << 27, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeAudioOutput = 1 << 28, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeAudioInput = 1 << 29, + IAPRetIPodOptionsForLingoGeneralBits_USBHostModeNoMaxCurrent = 1 << 30, +}; + +enum IAPRetIPodOptionsForLingoSimpleRemoteBits { + IAPRetIPodOptionsForLingoSimpleRemoteBits_ContextSpecificControls = 1 << 0, + IAPRetIPodOptionsForLingoSimpleRemoteBits_AudioMediaControls = 1 << 1, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VideoMediaControls = 1 << 2, + IAPRetIPodOptionsForLingoSimpleRemoteBits_ImageMediaControls = 1 << 3, + IAPRetIPodOptionsForLingoSimpleRemoteBits_SportsMediaControls = 1 << 4, + IAPRetIPodOptionsForLingoSimpleRemoteBits_CameraMediaControls = 1 << 8, + IAPRetIPodOptionsForLingoSimpleRemoteBits_USBHIDCommands = 1 << 9, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VoiceOverControls = 1 << 10, + IAPRetIPodOptionsForLingoSimpleRemoteBits_VoiceOverPreferences = 1 << 11, + IAPRetIPodOptionsForLingoSimpleRemoteBits_AssistiveTouchCursor = 1 << 12, +}; + +enum IAPRetIPodOptionsForLingoDisplayRemoteBits { + IAPRetIPodOptionsForLingoDisplayRemoteBits_UIVolumeControl = 1 << 0, + IAPRetIPodOptionsForLingoDisplayRemoteBits_AbsoluteVolumeControl = 1 << 1, + IAPRetIPodOptionsForLingoDisplayRemoteBits_GeniusPlaylistCreation = 1 << 2, +}; + +enum IAPRetIPodOptionsForLingoExtendedInterfaceBits { + IAPRetIPodOptionsForLingoExtendedInterfaceBits_VideoBrowsing = 1 << 0, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_InfoCommands = 1 << 1, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_NestedPlaylists = 1 << 2, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_GeniusPlaylistCreationAndRefresh = 1 << 3, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_DisplayImages = 1 << 4, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_CategoryListAccess = 1 << 5, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_PlayControl = 1 << 6, + IAPRetIPodOptionsForLingoExtendedInterfaceBits_UIDBasedCommands = 1 << 7, +}; + +/* +enum IAPRetIPodOptionsForLingoAccessoryPowerBits { +}; +*/ + +enum IAPRetIPodOptionsForLingoUSBHostModeBits { + IAPRetIPodOptionsForLingoUSBHostModeBits_USBHostModeHardwareInvoke = 1 << 0, /* deprecated */ + IAPRetIPodOptionsForLingoUSBHostModeBits_USBHostModeFirmwareInvoke = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoRFTunerBits { + IAPRetIPodOptionsForLingoRFTunerBits_RDSRawMode = 1 << 0, + IAPRetIPodOptionsForLingoRFTunerBits_HDRadioTuning = 1 << 1, + IAPRetIPodOptionsForLingoRFTunerBits_AMRadioTuning = 1 << 2, +}; + +/* +enum IAPRetIPodOptionsForLingoAccessoryEqualizerBits { +}; +*/ + +enum IAPRetIPodOptionsForLingoSportsBits { + IAPRetIPodOptionsForLingoSportsBits_NikePlusEquipment = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoDigitalAudioBits { + IAPRetIPodOptionsForLingoDigitalAudioBits_SetVideoDelayEnabled = 1 << 0, + IAPRetIPodOptionsForLingoDigitalAudioBits_SampleRateCapsFIDToken = 1 << 2, + IAPRetIPodOptionsForLingoDigitalAudioBits_VideoDelayFIDToken = 1 << 3, +}; + +enum IAPRetIPodOptionsForLingoStorageBits { + IAPRetIPodOptionsForLingoStorageBits_ITunesTagging = 1 << 0, + IAPRetIPodOptionsForLingoStorageBits_NikePlusEquipment = 1 << 1, +}; + +enum IAPRetIPodOptionsForLingoIPodOutBits { + IAPRetIPodOptionsForLingoIPodOutBits_WheelUIModeAvail = 1 << 0, + IAPRetIPodOptionsForLingoIPodOutBits_PALVideoEnabled = 1 << 2, +}; + +enum IAPRetIPodOptionsForLingoLocationBits { + IAPRetIPodOptionsForLingoLocationBits_AcceptNMEAData = 1 << 0, + IAPRetIPodOptionsForLingoLocationBits_SendLocationAssistanceData = 1 << 1, +}; + +struct IAPRetIPodOptionsForLingoPayload { + uint8_t lingo_id; + uint64_t bits; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h new file mode 100644 index 0000000000..14377c04d9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-options.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPRetIPodOptionsState { + IAPRetIPodOptionsState_VideoOutputSupport = 1 << 0, + IAPRetIPodOptionsState_SetiPodPreferencesSupport = 1 << 1, +}; + +struct IAPRetIPodOptionsPayload { + uint64_t state; /* IAPRetIPodOptionsState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h new file mode 100644 index 0000000000..ec6d168440 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-preferences.h @@ -0,0 +1,95 @@ +#pragma once +#include + +/* [1] P.152 Table 3-57 Apple device preference class and setting IDs */ +enum IAPIPodPereferenceClassID { + IAPIPodPereferenceClassID_VideoOut = 0x00, + IAPIPodPereferenceClassID_Screen = 0x01, + IAPIPodPereferenceClassID_VideoFormat = 0x02, + IAPIPodPereferenceClassID_LineOutUsage = 0x03, + IAPIPodPereferenceClassID_VideoOutConnection = 0x08, + IAPIPodPereferenceClassID_ClosedCaptioning = 0x09, + IAPIPodPereferenceClassID_VideoAspectRatio = 0x0A, + IAPIPodPereferenceClassID_Subtitle = 0x0C, + IAPIPodPereferenceClassID_VideoAltAudioChannel = 0x0D, + IAPIPodPereferenceClassID_PauseOnPowerRemoval = 0x0F, + IAPIPodPereferenceClassID_VoiceOver = 0x14, + IAPIPodPereferenceClassID_AssistiveTouch = 0x16, +}; + +enum IAPIPodPreferenceVideoOutSettingID { + IAPIPodPreferenceVideoOutSettingID_Off = 0x00, + IAPIPodPreferenceVideoOutSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceScreenSettingID { + IAPIPodPreferenceScreenSettingID_FillScreen = 0x00, + IAPIPodPreferenceScreenSettingID_FitToEdge = 0x01, +}; + +enum IAPIPodPreferenceVideoFormatSettingID { + IAPIPodPreferenceVideoFormatSettingID_NTSC = 0x00, + IAPIPodPreferenceVideoFormatSettingID_PAL = 0x01, +}; + +enum IAPIPodPreferenceLineOutUsageSettingID { + IAPIPodPreferenceLineOutUsageSettingID_NotUsed = 0x00, + IAPIPodPreferenceLineOutUsageSettingID_Used = 0x01, +}; + +enum IAPIPodPreferenceVideoOutConnectionSettingID { + IAPIPodPreferenceVideoOutConnectionSettingID_Composite = 0x01, + IAPIPodPreferenceVideoOutConnectionSettingID_SVideo = 0x02, + IAPIPodPreferenceVideoOutConnectionSettingID_Component = 0x03, +}; + +enum IAPIPodPreferenceClosedCaptioningSettingID { + IAPIPodPreferenceClosedCaptioningSettingID_Off = 0x00, + IAPIPodPreferenceClosedCaptioningSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceVideoAspectRatioSettingID { + IAPIPodPreferenceVideoAspectRatioSettingID_4x3 = 0x00, + IAPIPodPreferenceVideoAspectRatioSettingID_16x9 = 0x01, +}; + +enum IAPIPodPreferenceSubtitleSettingID { + IAPIPodPreferenceSubtitleSettingID_Off = 0x00, + IAPIPodPreferenceSubtitleSettingID_On = 0x01, + +}; + +enum IAPIPodPreferenceVideoAltAudioChannelSettingID { + IAPIPodPreferenceVideoAltAudioChannelSettingID_Off = 0x00, + IAPIPodPreferenceVideoAltAudioChannelSettingID_On = 0x01, +}; + +enum IAPIPodPreferencePauseOnPowerRemovalSettingID { + IAPIPodPreferencePauseOnPowerRemovalSettingID_Off = 0x00, + IAPIPodPreferencePauseOnPowerRemovalSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceVoiceOverSettingID { + IAPIPodPreferenceVoiceOverSettingID_Off = 0x00, + IAPIPodPreferenceVoiceOverSettingID_On = 0x01, +}; + +enum IAPIPodPreferenceAssistiveTouchSettingID { + IAPIPodPreferenceAssistiveTouchSettingID_Off = 0x00, + IAPIPodPreferenceAssistiveTouchSettingID_On = 0x01, +}; + +struct IAPGetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ +} __attribute__((packed)); + +struct IAPRetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; +} __attribute__((packed)); + +struct IAPSetIPodPreferencesPayload { + uint8_t class_id; /* IAPIPodPereferenceClassID */ + uint8_t setting_id; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h new file mode 100644 index 0000000000..3285007403 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-serial-num.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnIPodSerialNumPayload { + char serial[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h new file mode 100644 index 0000000000..bb036dbbc9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ipod-software-version.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPReturnIPodSoftwareVersionPayload { + uint8_t major; + uint8_t minor; + uint8_t revision; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h new file mode 100644 index 0000000000..44569c7c12 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/lingo-protocol-version.h @@ -0,0 +1,12 @@ +#pragma once +#include + +struct IAPRequestLingoProtocolVersionPayload { + uint8_t lingo; +} __attribute__((packed)); + +struct IAPReturnLingoProtocolVersionPayload { + uint8_t lingo; + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h new file mode 100644 index 0000000000..f9a4235834 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/localization-info.h @@ -0,0 +1,16 @@ +#pragma once +#include + +enum IAPLocalicationInfoType { + IAPLocalicationInfoType_Language = 0x00, + IAPLocalicationInfoType_Region = 0x01, +}; + +struct IAPGetLocalizationInfoPayload { + uint8_t type; /* IAPLocalicationInfoType */ +} __attribute__((packed)); + +struct IAPRetLocalizationInfoPayload { + uint8_t type; /* IAPLocalicationInfoType */ + char value[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h new file mode 100644 index 0000000000..22b187832a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/notify-ipod-state-change.h @@ -0,0 +1,13 @@ +#pragma once +#include + +enum IAPNotifyIPodStateChangeState { + IAPNotifyIPodStateChangeState_LostContextHibernate = 0x01, + IAPNotifyIPodStateChangeState_SaveContextHibernate = 0x02, + IAPNotifyIPodStateChangeState_Sleep = 0x03, + IAPNotifyIPodStateChangeState_PowerOn = 0x04, +}; + +struct IAPNotifyIPodStateChangePayload { + uint8_t state; /* IAPNotifyIPodStateChangeState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h new file mode 100644 index 0000000000..b6401d555d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/now-playing-app-bundle-name.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* +struct IAPRetNowPlayingApplicationBundleNamePayload { + char name[]; +} __attribute__((packed)); +*/ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h new file mode 100644 index 0000000000..8a6ecc3cd7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/request-application-launch.h @@ -0,0 +1,9 @@ +#pragma once +#include + +struct IAPRequestApplicationLaunchPayload { + uint8_t reserved1; + uint8_t flag; + uint8_t reserved2; + char app_id[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h new file mode 100644 index 0000000000..ed9cf86f30 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-available-current.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPSetAvailableCurrentPayload { + uint16_t current_limit_ma; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h new file mode 100644 index 0000000000..b5804cc4cb --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/set-internal-battery-charging-state.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPInternalBatteryChargingState { + IAPInternalBatteryChargingState_DontCharge = 0x00, + IAPInternalBatteryChargingState_MayCharge = 0x01, +}; + +struct IAPInternalBatteryChargingStatePayload { + uint8_t state; /* IAPInternalBatteryChargingState */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h new file mode 100644 index 0000000000..bf83edd35c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/transport-max-payload-size.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPReturnTransportMaxPayloadSizePayload { + uint16_t max_payload_size; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h new file mode 100644 index 0000000000..ed0410458a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/ui-mode.h @@ -0,0 +1,13 @@ +#pragma once +#include + +enum IAPUIMode { + IAPUIMode_Standard = 0x00, + IAPUIMode_Extended = 0x01, + IAPUIMode_IPodOutFullscreen = 0x02, + IAPUIMode_IPodOutActionSafe = 0x03, +}; + +struct IAPSetUIModePayload { + uint8_t ui_mode; /* IAPUIMode */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h b/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h new file mode 100644 index 0000000000..701bbe5fa0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/general/wifi-connection-info.h @@ -0,0 +1,24 @@ +#pragma once +#include + +enum IAPWiFiConnectionInfoStatus { + IAPWiFiConnectionInfoStatus_Success = 0x00, + IAPWiFiConnectionInfoStatus_Unavailable = 0x01, + IAPWiFiConnectionInfoStatus_Declined = 0x02, + IAPWiFiConnectionInfoStatus_Failed = 0x03, +}; + +enum IAPWiFiConnectionInfoSecurityType { + IAPWiFiConnectionInfoSecurityType_Unsecured = 0x00, + IAPWiFiConnectionInfoSecurityType_WEP = 0x01, + IAPWiFiConnectionInfoSecurityType_WPA = 0x02, + IAPWiFiConnectionInfoSecurityType_WPA2 = 0x03, + IAPWiFiConnectionInfoSecurityType_MixedWPAWPA2 = 0x04, +}; + +struct IAPWiFiConnectionInfoPayload { + uint8_t status; /* IAPWiFiConnectionInfoStatus */ + uint8_t security_type; /* IAPWiFiConnectionInfoSecurityType */ + char ssid[32]; + char passphrase[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h new file mode 100644 index 0000000000..cbf6d0b329 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.366 Table 4-258 iPod Out lingo command summary */ +enum IAPIPodOutCommandID { + IAPIPodOutCommandID_IPodAck = 0x00, /* from dev, general/ipod-ack.h */ + IAPIPodOutCommandID_GetIPodOutOptions = 0x01, /* from acc, ipod-out-options.h */ + IAPIPodOutCommandID_RetIPodOutOptions = 0x02, /* from dev, ipod-out-options.h */ + IAPIPodOutCommandID_SetIPodOutOptions = 0x03, /* from acc, ipod-out-options.h */ + IAPIPodOutCommandID_AccessoryStateChangeEvent = 0x04, /* from acc, */ +}; + +#include "ipod-out/accessory-state-change-event.h" +#include "ipod-out/ipod-out-options.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h new file mode 100644 index 0000000000..4863e89554 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/accessory-state-change-event.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.368 4.12.7 Command 0x04: AccessoryStateChangeEvent */ + +enum IAPAccessoryStateChangeEvents { + IAPAccessoryStateChangeEvents_DisplaySwitchAwayFromIPodOut = 0x00, + IAPAccessoryStateChangeEvents_DisplaySwitchBackToIPodOut = 0x01, + IAPAccessoryStateChangeEvents_AudioSwitchAwayFromIPodOut = 0x02, + IAPAccessoryStateChangeEvents_AudioSwitchBackToIPodOut = 0x03, + IAPAccessoryStateChangeEvents_EnterDaytimeMode = 0x04, + IAPAccessoryStateChangeEvents_EnterNighttimeMode = 0x05, +}; + +struct AccessoryStateChangeEventPayload { + uint8_t state_change; /* IAPAccessoryStateChangeEvents */ +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h new file mode 100644 index 0000000000..0af8b99ce2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/ipod-out/ipod-out-options.h @@ -0,0 +1,29 @@ +#pragma once +#include + +/* [1] P.366 4.12.4 Command 0x01: GetiPodOutOptions */ + +struct IAPGetIPodOutOptionsPayload { + uint8_t report_current; +} __attribute__((packed)); + +enum IAPIPodOutOptionBits { + IAPIPodOutOptionBits_DisplayAudio = 1 << 0, + IAPIPodOutOptionBits_DisplayPhoneCall = 1 << 1, + IAPIPodOutOptionBits_DisplaySMS = 1 << 2, + IAPIPodOutOptionBits_DisplayVoicemail = 1 << 4, + IAPIPodOutOptionBits_DisplayPushNotifications = 1 << 5, + IAPIPodOutOptionBits_DisplayClockAlarmNotifications = 1 << 6, + IAPIPodOutOptionBits_DisplayTestPattern = 1 << 8, + IAPIPodOutOptionBits_ShowMinimalIPodOut = 1 << 9, + IAPIPodOutOptionBits_ShowFullIPodOud = 1 << 10, +}; + +struct IAPRetIPodOutOptionsPayload { + uint8_t report_current; + uint32_t options; /* IAPIPodOutOptionBits */ +} __attribute__((packed)); + +struct IAPSetIPodOutOptionsPayload { + uint32_t options; /* IAPIPodOutOptionBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/location.h b/firmware/usbstack/iap/libiap/spec/lingoes/location.h new file mode 100644 index 0000000000..8e2a4aabae --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/location.h @@ -0,0 +1,19 @@ +#pragma once +#include + +/* [1] P.371 Table 4-266 Location lingo commands */ +enum IAPLocationCommandID { + IAPLocationCommandID_AccessoryAck = 0x00, /* from acc */ + IAPLocationCommandID_GetAccessoryCaps = 0x01, /* from dev */ + IAPLocationCommandID_AccessoryCaps = 0x02, /* from acc */ + IAPLocationCommandID_GetAccessoryControl = 0x03, /* from dev */ + IAPLocationCommandID_RetAccessoryControl = 0x04, /* from acc */ + IAPLocationCommandID_SetAccessoryControl = 0x05, /* from dev */ + IAPLocationCommandID_GetAccessoryData = 0x06, /* from dev */ + IAPLocationCommandID_RetAccessoryData = 0x07, /* from acc */ + IAPLocationCommandID_SetAccessoryData = 0x08, /* from dev */ + IAPLocationCommandID_AsyncAccessoryData = 0x09, /* from acc */ + IAPLocationCommandID_IPodAck = 0x80, /* from dev */ +}; + +/* TODO: add payload definitions */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h new file mode 100644 index 0000000000..3c90b3800c --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.534 Table C-12 Microphone lingo command summary */ +enum IAPMicrophoneCommandID { + IAPMicrophoneCommandID_BeginRecord = 0x00, /* deprecated */ + IAPMicrophoneCommandID_EndRecord = 0x01, /* deprecated */ + IAPMicrophoneCommandID_BeginPlayback = 0x02, /* deprecated */ + IAPMicrophoneCommandID_EndPlayback = 0x03, /* deprecated */ + IAPMicrophoneCommandID_AccessoryAck = 0x04, /* from acc, general/acc-ack.h */ + IAPMicrophoneCommandID_GetAccessoryAck = 0x05, /* from dev, no payload */ + IAPMicrophoneCommandID_IPodModeChange = 0x06, /* from dev, ipod-mode-change.h */ + IAPMicrophoneCommandID_GetAccessoryCaps = 0x07, /* from dev, no payload */ + IAPMicrophoneCommandID_RetAccessoryCaps = 0x08, /* from acc, acc-caps.h */ + IAPMicrophoneCommandID_GetAccessoryCtrl = 0x09, /* from dev, acc-ctrl.h */ + IAPMicrophoneCommandID_RetAccessoryCtrl = 0x0A, /* from acc, acc-ctrl.h */ + IAPMicrophoneCommandID_SetAccessoryCtrl = 0x0B, /* from dev, acc-ctrl.h */ +}; + +#include "microphone/acc-caps.h" +#include "microphone/acc-ctrl.h" +#include "microphone/ipod-mode-change.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h new file mode 100644 index 0000000000..3a98aadb32 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-caps.h @@ -0,0 +1,14 @@ +#pragma once +#include + +enum IAPAccCapsBits { + IAPAccCapsBits_StereoInput = 1 << 0, + IAPAccCapsBits_StereoMonoInputSwitch = 1 << 1, + IAPAccCapsBits_VariableRecordLevel = 1 << 2, + IAPAccCapsBits_RecordLevelLimit = 1 << 3, + IAPAccCapsBits_DuplexAudio = 1 << 4, +}; + +struct IAPRetAccCapsPayload { + uint8_t bits; /* IAPAccCapsBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h new file mode 100644 index 0000000000..fe3506aad9 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/acc-ctrl.h @@ -0,0 +1,22 @@ +#pragma once +#include + +enum IAPAccCtrlType { + IAPAccCtrlType_LineInput = 0x01, + IAPAccCtrlType_RecordingLevelControl = 0x02, + IAPAccCtrlType_RecordingLevelLimiterControl = 0x03, +}; + +struct IAPGetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ +} __attribute__((packed)); + +struct IAPRetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ + uint8_t value; +} __attribute__((packed)); + +struct IAPSetAccCtrlPayload { + uint8_t type; /* IAPAccCtrlType */ + uint8_t value; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h new file mode 100644 index 0000000000..55a49be67a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/microphone/ipod-mode-change.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.537 Table C-18 Mode values */ +enum IAPIPodModeChangeModes { + IAPIPodModeChangeModes_BeginAudioRecord = 0x00, + IAPIPodModeChangeModes_EndAudioRecord = 0x01, + IAPIPodModeChangeModes_BeginAudioPlayback = 0x02, + IAPIPodModeChangeModes_EndAudioPlayback = 0x03, +}; + +struct IAPIPodModeChangePayload { + uint8_t mode; /* IAPIPodModeChangeModes */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h new file mode 100644 index 0000000000..11a540b6f6 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner.h @@ -0,0 +1,69 @@ +#pragma once +#include + +/* [1] P.288 Table 4-111 RF Tuner lingo command summary */ +enum IAPRFTunerCommandID { + IAPRFTunerCommandID_AccessoryAck = 0x00, /* 1.00, from acc, acc-ack.h */ + IAPRFTunerCommandID_GetTunerCaps = 0x01, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerCaps = 0x02, /* 1.00, from acc, tuner-caps.h */ + IAPRFTunerCommandID_GetTunerCtrl = 0x03, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerCtrl = 0x04, /* 1.00, from acc, tuner-ctrl.h */ + IAPRFTunerCommandID_SetTunerCtrl = 0x05, /* 1.00, from dev, tuner-ctlr.h */ + IAPRFTunerCommandID_GetTunerBand = 0x06, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerBand = 0x07, /* 1.00, from acc, tuner-band.h */ + IAPRFTunerCommandID_SetTunerBand = 0x08, /* 1.00, from dev, tuner-band.h*/ + IAPRFTunerCommandID_GetTunerFreq = 0x09, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerFreq = 0x0A, /* 1.00, from acc, tuner-freq.h */ + IAPRFTunerCommandID_SetTunerFreq = 0x0B, /* 1.00, from dev, tuner-freq.h */ + IAPRFTunerCommandID_GetTunerMode = 0x0C, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerMode = 0x0D, /* 1.00, from acc, tuner-mode.h */ + IAPRFTunerCommandID_SetTunerMode = 0x0E, /* 1.00, from dev, tuner-mode.h */ + IAPRFTunerCommandID_GetTunerSeekRssi = 0x0F, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerSeekRssi = 0x10, /* 1.00, from acc, tuner-seek-rssi.h */ + IAPRFTunerCommandID_SetTunerSeekRssi = 0x11, /* 1.00, from dev, tuner-seek-rssi.h */ + IAPRFTunerCommandID_TunerSeekStart = 0x12, /* 1.00, from dev, tuner-seek.h */ + IAPRFTunerCommandID_TunerSeekDone = 0x13, /* 1.00, from acc, tuner-seek.h */ + IAPRFTunerCommandID_GetTunerStatus = 0x14, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetTunerStatus = 0x15, /* 1.00, from acc, tuner-status.h */ + IAPRFTunerCommandID_GetStatusNotifyMask = 0x16, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetStatusNotifyMask = 0x17, /* 1.00, from acc, status-notification.h */ + IAPRFTunerCommandID_SetStatusNotifyMask = 0x18, /* 1.00, from dev, status-notification.h */ + IAPRFTunerCommandID_StatusChangeNotify = 0x19, /* 1.00, from acc, status-notification.h */ + IAPRFTunerCommandID_GetRdsReadyStatus = 0x1A, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetRdsReadyStatus = 0x1B, /* 1.00, from acc, rds-ready-status.h */ + IAPRFTunerCommandID_GetRdsData = 0x1C, /* 1.00, from dev, rds-data.h */ + IAPRFTunerCommandID_RetRdsData = 0x1D, /* 1.00, from acc, rds-data.h */ + IAPRFTunerCommandID_GetRdsNotifyMask = 0x1E, /* 1.00, from dev, no payload */ + IAPRFTunerCommandID_RetRdsNotifyMask = 0x1F, /* 1.00, from acc, rds-notify-mask.h */ + IAPRFTunerCommandID_SetRdsNotifyMask = 0x20, /* 1.00, from dev, rds-notify-mask.h */ + IAPRFTunerCommandID_RdsReadyNotify = 0x21, /* 1.00, from acc, rds-data.h */ + IAPRFTunerCommandID_GetHDProgramServiceCount = 0x25, /* 1.01, from dev, no paylaod */ + IAPRFTunerCommandID_RetHDProgramServiceCount = 0x26, /* 1.01, from acc, hd-program.h */ + IAPRFTunerCommandID_GetHDProgramService = 0x27, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDProgramService = 0x28, /* 1.01, from acc, hd-program.h */ + IAPRFTunerCommandID_SetHDProgramService = 0x29, /* 1.01, from dev, hd-program.h */ + IAPRFTunerCommandID_GetHDDataReadyStatus = 0x2A, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDDataReadyStatus = 0x2B, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_GetHDData = 0x2C, /* 1.01, from dev, hd-data.h */ + IAPRFTunerCommandID_RetHDData = 0x2D, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_GetHDDataNotifyMask = 0x2E, /* 1.01, from dev, no payload */ + IAPRFTunerCommandID_RetHDDataNotifyMask = 0x2F, /* 1.01, from acc, hd-data.h */ + IAPRFTunerCommandID_SetHDDataNotifyMask = 0x30, /* 1.01, from dev, hd-data.h */ + IAPRFTunerCommandID_HDDataReadyNotify = 0x31, /* 1.01, from acc, hd-data.h */ +}; + +#include "rf-tuner/acc-ack.h" +#include "rf-tuner/hd-data.h" +#include "rf-tuner/hd-program.h" +#include "rf-tuner/rds-data.h" +#include "rf-tuner/rds-notify-mask.h" +#include "rf-tuner/rds-ready-status.h" +#include "rf-tuner/status-notification.h" +#include "rf-tuner/tuner-band.h" +#include "rf-tuner/tuner-caps.h" +#include "rf-tuner/tuner-ctrl.h" +#include "rf-tuner/tuner-freq.h" +#include "rf-tuner/tuner-mode.h" +#include "rf-tuner/tuner-seek-rssi.h" +#include "rf-tuner/tuner-seek.h" +#include "rf-tuner/tuner-status.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h new file mode 100644 index 0000000000..e16d3eab7d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/acc-ack.h @@ -0,0 +1,13 @@ +#pragma once +#include + +struct IAPRFTunerAccAckPayload { + uint8_t status; /* = IAPAckStatus */ + uint8_t id; +} __attribute__((packed)); + +struct IAPRFTunerAccAckPendingPayload { + uint8_t status; /* = IAPAckStatus_CommandPending */ + uint8_t id; + uint32_t pending_time_ms; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h new file mode 100644 index 0000000000..0e95dc46b2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-data.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.319 4.7.45 Command 0x2B: RetHDDataReadyStatus */ + +enum IAPHDDataType { + IAPHDDataType_PSD = 0x00, + IAPHDDataType_SISStationIDNumber = 0x02, + IAPHDDataType_SISStationNameShort = 0x03, + IAPHDDataType_SISStationNameLong = 0x04, + IAPHDDataType_SISALFN = 0x05, + IAPHDDataType_SISStationLocation = 0x06, + IAPHDDataType_SISStationMessage = 0x07, + IAPHDDataType_SISSlogan = 0x08, + IAPHDDataType_SISParameterMessage = 0x09, +}; + +struct IAPRetHDDataReadyStatusPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPGetHDDataPayload { + uint8_t type; /* IAPHDDataType */ +} __attribute__((packed)); + +struct IAPRetHDDataPayload { + uint8_t type; /* IAPHDDataType */ + uint8_t data[]; /* TODO: add difinitions */ +} __attribute__((packed)); + +struct IAPRetHDDataNotifyMaskPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPSetHDDataNotifyMaskPayload { + uint32_t status; /* 1 << IAPHDDataType | ... */ +} __attribute__((packed)); + +struct IAPHDDataReadyNotifyPayload { + uint8_t type; /* IAPHDDataType */ + uint8_t data[]; /* TODO: add difinitions */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h new file mode 100644 index 0000000000..a02d8ba5d3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/hd-program.h @@ -0,0 +1,17 @@ +#pragma once +#include + +/* [1] P.317 4.7.40 Command 0x26: RetHDProgramServiceCount */ + +struct IAPRetHDProgramServiceCountPayload { + uint8_t count; + uint8_t analog_program_exists; +} __attribute__((packed)); + +struct IAPRetHDProgramServicePayload { + uint8_t index; +} __attribute__((packed)); + +struct IAPSetHDProgramServicePayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h new file mode 100644 index 0000000000..eebb783ea7 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-data.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.311 4.7.33 Command 0x1C: GetRdsData */ + +struct IAPGetRdsDataPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ +}; + +struct IAPRetRdsDataPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ + uint8_t data[]; /* TODO: add definitions */ +}; + +struct IAPReadyNotifyPayload { + uint8_t type; /* IAPRdsData{Parsed,Raw}Type */ + uint8_t data[]; /* TODO: add definitions */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h new file mode 100644 index 0000000000..d3d67434e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-notify-mask.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/* [1] P.314 4.7.36 Command 0x1F: RetRdsNotifyMask */ + +struct IAPRetRdsNotifyMaskPayload { + uint32_t mask; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); + +struct IAPSetRdsNotifyMaskPayload { + uint32_t mask; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h new file mode 100644 index 0000000000..713ebec3bf --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/rds-ready-status.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.310 4.7.32 Command 0x1B: RetRdsReadyStatus */ + +enum IAPRdsDataParsedType { + IAPRdsDataParsedType_RadioText = 0x04, + IAPRdsDataParsedType_ProgramServiceName = 0x1E, +}; + +enum IAPRdsDataRawType { + IAPRdsDataRawType_RdsGroupData = 0x05, +}; + +struct IAPRetRdsReadyStatusPayload { + uint32_t status; /* 1 << IAPRdsData{Parsed,Raw}Type | ... */ +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h new file mode 100644 index 0000000000..4d304fb27f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/status-notification.h @@ -0,0 +1,16 @@ +#pragma once +#include + +/* [1] P.307 4.7.28 Command 0x17: RetStatusNotifyMask */ + +struct IAPRetStatusNotifyMaskPayload { + uint8_t mask; /* IAPTunerStatusBits */ +} __attribute__((packed)); + +struct IAPSetStatusNotifyMaskPayload { + uint8_t mask; /* IAPTunerStatusBits */ +} __attribute__((packed)); + +struct IAPStatusChangeNotifyPayload { + uint8_t mask; /* IAPTunerStatusBits */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h new file mode 100644 index 0000000000..5ec03a950d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-band.h @@ -0,0 +1,19 @@ +#pragma once +#include + +/* [1] P.297 4.7.12 Command 0x07: RetTunerBand */ + +enum IAPTunerBandStateBits { + AMBandWorldwide = 0x00, + FMBandEuropeUS = 0x01, + FMBandJapan = 0x02, + FNBandWide = 0x03, +}; + +struct IAPRetTunerBandPayload { + uint8_t state; /* IAPTunerBandStateBits */ +} __attribute__((packed)); + +struct IAPSetTunerBandPayload { + uint8_t state; /* IAPTunerBandStateBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h new file mode 100644 index 0000000000..0b233f89fd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-caps.h @@ -0,0 +1,42 @@ +#pragma once +#include + +/* [1] P.292 4.7.7 Command 0x02: RetTunerCaps */ + +enum IAPFMTunerResolutionID { + _200kHz = 0b00, + _100kHz = 0b01, + _50kHz = 0b10, +}; + +enum IAPAMTunerResolutionID { + _10kHz = 0b0, + _9kHz = 0b1, +}; + +enum IAPTunerCapsBits { + IAPTunerCapsBits_AMWorldwide = 1 << 0, + IAPTunerCapsBits_FMEuropeUS = 1 << 1, + IAPTunerCapsBits_FMJapan = 1 << 2, + IAPTunerCapsBits_FMWide = 1 << 3, + IAPTunerCapsBits_HDRadio = 1 << 4, + IAPTunerCapsBits_PowerOnOff = 1 << 8, + IAPTunerCapsBits_StatusChangeNotification = 1 << 9, + IAPTunerCapsBits_MinFMResolution = 3 << 16, /* IAPFMTunerResolutionID */ + IAPTunerCapsBits_TunerSeekUpDown = 1 << 18, + IAPTunerCapsBits_TunerSeekRSSIThreshold = 1 << 19, + IAPTunerCapsBits_ForcemonophonicMode = 1 << 20, + IAPTunerCapsBits_StereoBlend = 1 << 21, + IAPTunerCapsBits_FMTunerDeemphasisSelect = 1 << 22, + IAPTunerCapsBits_AMTuner9kHzResoluton = 1 << 23, + IAPTunerCapsBits_RDSData = 1 << 24, + IAPTunerCapsBits_TunerChannelRSSIIndication = 1 << 25, + IAPTunerCapsBits_StereoSourceIndicator = 1 << 26, + IAPTunerCapsBits_RDSRawMode = 1 << 27, +}; + +struct IAPRetTunerCapsPayload { + uint32_t caps; /* IAPTunerCapsBits */ + uint8_t reserved1; /* = 0x01 */ + uint8_t reserved2; /* = 0x00 */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h new file mode 100644 index 0000000000..c09fd2465d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-ctrl.h @@ -0,0 +1,18 @@ +#pragma once +#include + +/* [1] P.294 4.7.9 Command 0x04: RetTunerCtrl */ + +enum IAPTunerCtrlStateBits { + PowerDraw = 1 << 0, + StateChangeNotification = 1 << 1, + RDSRawMode = 1 << 3, +}; + +struct IAPRetTunerCtrlPayload { + uint8_t state; /* IAPTunerCtrlStateBits */ +} __attribute__((packed)); + +struct IAPSetTunerCtrlPayload { + uint8_t state; /* IAPTunerCtrlStateBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h new file mode 100644 index 0000000000..ed70a8f0aa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-freq.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.298 4.7.15 Command 0x0A: RetTunerFreq */ + +struct IAPRetTunerFreqPayload { + uint32_t freq_khz; + uint8_t rssi_level; +} __attribute__((packed)); + +struct IAPSetTunerFreqPayload { + uint32_t freq_khz; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h new file mode 100644 index 0000000000..9ed455875f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-mode.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.300 4.7.18 Command 0x0D: RetTunerMode */ + +enum IAPTunerModeStatusBits { + FMTunerResolution = 3 << 0, /* IAPFMTunerResolutionID */ + TunerSeekingUp = 1 << 2, + TunerSeekingWithMinRSSIThreshold = 1 << 3, + ForceMonophonic = 1 << 4, + StereoBlend = 1 << 5, + FMTunerDeemphasis50usec = 1 << 6, + AMTunerResolution = 1 << 7, /* IAPAMTunerResolutionID */ +}; + +struct IAPRetTunerModePayload { + uint8_t status; /* IAPTunerModeStatusBits */ +} __attribute__((packed)); + +struct IAPSetTunerModePayload { + uint8_t status; /* IAPTunerModeStatusBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h new file mode 100644 index 0000000000..e3cf8fca8e --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek-rssi.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/* [1] P.302 4.7.21 Command 0x10: RetTunerSeekRssi */ + +struct IAPRetTunerSeekRssiPayload { + uint8_t rssi_threshold; +} __attribute__((packed)); + +struct IAPSetTunerSeekRssiPayload { + uint8_t rssi_threshold; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h new file mode 100644 index 0000000000..ff56682887 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-seek.h @@ -0,0 +1,33 @@ +#pragma once +#include + +/* [1] P.302 4.7.23 Command 0x12: TunerSeekStart */ + +enum IAPTunerSeekOperationID { + IAPTunerSeekOperationID_NoSeek = 0x00, + IAPTunerSeekOperationID_SeekUpFromBeginning = 0x01, + IAPTunerSeekOperationID_SeekDownFromEnd = 0x02, + IAPTunerSeekOperationID_SeekUpFromCurrent = 0x03, + IAPTunerSeekOperationID_SeekDownFromCurrent = 0x04, + IAPTunerSeekOperationID_SeekUpFromBeginningWithRssiThreshold = 0x05, + IAPTunerSeekOperationID_SeekDownFromEndWithRssiThreshold = 0x06, + IAPTunerSeekOperationID_SeekUpFromCurrentWithRssiThreshold = 0x07, + IAPTunerSeekOperationID_SeekDownFromCurrentWithRssiThreshold = 0x08, + IAPTunerSeekOperationID_SeekUpFromBeginningForHDSignal = 0x09, + IAPTunerSeekOperationID_SeekDownFromEndForHDSignal = 0x0A, + IAPTunerSeekOperationID_SeekUpFromCurrentForHDSignal = 0x0B, + IAPTunerSeekOperationID_SeekDownFromCurrentForHDSignal = 0x0C, + IAPTunerSeekOperationID_SeekUpFromBeginningForHDSignalWithRssiThreshold = 0x0D, + IAPTunerSeekOperationID_SeekDownFromEndForHDSignalWithRssiThreshold = 0x0E, + IAPTunerSeekOperationID_SeekUpFromCurrentForHDSignalWithRssiThreshold = 0x0F, + IAPTunerSeekOperationID_SeekDownFromCurrentForHDSignalWithRssiThreshold = 0x10, +}; + +struct IAPTunerSeekStartPayload { + uint8_t operation_id; /* IAPTunerSeekOperationID */ +} __attribute__((packed)); + +struct IAPTunerSeekDonePayload { + uint32_t freq_khz; + uint8_t signal_strength; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h new file mode 100644 index 0000000000..70ba8b01e4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/rf-tuner/tuner-status.h @@ -0,0 +1,17 @@ +#pragma once +#include + +/* [1] P.306 4.7.26 Command 0x15: RetTunerStatus */ + +enum IAPTunerStatusBits { + IAPTunerStatusBits_DataReceived = 1 << 0, + IAPTunerStatusBits_RSSILevelChanged = 1 << 1, + IAPTunerStatusBits_StereoSourceIndicator = 1 << 2, + IAPTunerStatusBits_HDSignalPresent = 1 << 3, + IAPTunerStatusBits_HDDigitalAudioPresent = 1 << 4, + IAPTunerStatusBits_HDDataReceived = 1 << 5, +}; + +struct IAPRetTunerStatusPayload { + uint8_t status; /* IAPTunerStatusBits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h new file mode 100644 index 0000000000..e96a25bde3 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote.h @@ -0,0 +1,39 @@ +#pragma once +#include + +/* [1] P.213 Table 4-3 Simple remote lingo command summary */ +enum IAPSimpleRemoteCommandID { + IAPSimpleRemoteCommandID_ContextButtonStatus = 0x00, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_IPodAck = 0x01, /* from dev, general/ipod-ack.h */ + IAPSimpleRemoteCommandID_ImageButtonStatus = 0x02, /* from acc, button-status.h, deprecated */ + IAPSimpleRemoteCommandID_VideoButtonStatus = 0x03, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_AudioButtonStatus = 0x04, /* from acc, button-status.h */ + IAPSimpleRemoteCommandID_IPodOutButtonStatus = 0x0B, /* from acc, ipod-out-button-status.h */ + IAPSimpleRemoteCommandID_RotationInputStatus = 0x0C, /* from acc, rotation-input-status.h */ + IAPSimpleRemoteCommandID_RadioButtonStatus = 0x0D, /* from acc, radio-button-status.h */ + IAPSimpleRemoteCommandID_CameraButtonStatus = 0x0E, /* from acc, camera-button-status.h */ + IAPSimpleRemoteCommandID_RegisterDescriptor = 0x0F, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_IPodHIDReport = 0x10, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_AccessoryHIDReport = 0x11, /* from dev, hid.h */ + IAPSimpleRemoteCommandID_UnregisterDescriptor = 0x12, /* from acc, hid.h */ + IAPSimpleRemoteCommandID_VoiceOverEvent = 0x13, /* from acc, voice-over-event.h */ + IAPSimpleRemoteCommandID_GetVoiceOverParameter = 0x14, /* from acc, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_RetVoiceOverParameter = 0x15, /* from dev, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_SetVoiceOverParameter = 0x16, /* from acc, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_GetCurrentVoiceOverItemProperty = 0x17, /* from acc, current-voice-over-item-property.h */ + IAPSimpleRemoteCommandID_RetCurrentVoiceOverItemProperty = 0x18, /* from dev, current-voice-over-item-property.h */ + IAPSimpleRemoteCommandID_SetVoiceOverContext = 0x19, /* from acc, set-voice-over-context.h */ + IAPSimpleRemoteCommandID_VoiceOverParameterChanged = 0x2A, /* from dev, voice-over-parameter.h */ + IAPSimpleRemoteCommandID_AccessoryAck = 0x81, /* from acc, general/acc-ack.h */ +}; + +#include "simple-remote/button-status.h" +#include "simple-remote/camera-button-status.h" +#include "simple-remote/current-voice-over-item-property.h" +#include "simple-remote/hid.h" +#include "simple-remote/ipod-out-button-status.h" +#include "simple-remote/radio-button-status.h" +#include "simple-remote/rotation-input-status.h" +#include "simple-remote/set-voice-over-context.h" +#include "simple-remote/voice-over-event.h" +#include "simple-remote/voice-over-parameter.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h new file mode 100644 index 0000000000..779c2f540b --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/button-status.h @@ -0,0 +1,98 @@ +#pragma once +#include + +enum IAPContextButtonBits { + /* bits1 */ + IAPContextButtonStatusButtons_PlayPause = 1 << 0, + IAPContextButtonStatusButtons_VolumeUp = 1 << 1, + IAPContextButtonStatusButtons_VolumeDown = 1 << 2, + IAPContextButtonStatusButtons_NextTrack = 1 << 3, + IAPContextButtonStatusButtons_PrevTrack = 1 << 4, + IAPContextButtonStatusButtons_NextAlbum = 1 << 5, + IAPContextButtonStatusButtons_PrevAlbum = 1 << 6, + IAPContextButtonStatusButtons_Stop = 1 << 7, + /* bits2 */ + IAPContextButtonStatusButtons_PlayResume = 1 << 0, + IAPContextButtonStatusButtons_Pause = 1 << 1, + IAPContextButtonStatusButtons_MuteToggle = 1 << 2, + IAPContextButtonStatusButtons_NextChapter = 1 << 3, + IAPContextButtonStatusButtons_PrevChapter = 1 << 4, + IAPContextButtonStatusButtons_NextPlaylist = 1 << 5, + IAPContextButtonStatusButtons_PrevPlaylist = 1 << 6, + IAPContextButtonStatusButtons_ShuffleSettingAdvance = 1 << 7, + /* bits3 */ + IAPContextButtonStatusButtons_RepeatSettingAdvance = 1 << 0, + IAPContextButtonStatusButtons_PowerOn = 1 << 1, + IAPContextButtonStatusButtons_PowerOff = 1 << 2, + IAPContextButtonStatusButtons_Backlight30Seconds = 1 << 3, + IAPContextButtonStatusButtons_BeginFastForward = 1 << 4, + IAPContextButtonStatusButtons_BeginRewind = 1 << 5, + IAPContextButtonStatusButtons_Menu = 1 << 6, + IAPContextButtonStatusButtons_Select = 1 << 7, + /* bits4 */ + IAPContextButtonStatusButtons_UpArrow = 1 << 0, + IAPContextButtonStatusButtons_DownArrow = 1 << 1, + IAPContextButtonStatusButtons_BacklightOff = 1 << 2, +}; + +enum IAPImageButtonBits { + /* bits1 */ + IAPImageButtonBits_PlayPause = 1 << 0, + IAPImageButtonBits_NextImage = 1 << 1, + IAPImageButtonBits_PrevImage = 1 << 2, + IAPImageButtonBits_Stop = 1 << 3, + IAPImageButtonBits_PlayResume = 1 << 4, + IAPImageButtonBits_Pause = 1 << 5, + IAPImageButtonBits_ShuffleAdvance = 1 << 6, + IAPImageButtonBits_RepeatAdvance = 1 << 7, +}; + +enum IAPVideoButtonBits { + /* bits1 */ + IAPVideoButtonBits_PlayPause = 1 << 0, + IAPVideoButtonBits_NextVideo = 1 << 1, + IAPVideoButtonBits_PrevVideo = 1 << 2, + IAPVideoButtonBits_Stop = 1 << 3, + IAPVideoButtonBits_PlayResume = 1 << 4, + IAPVideoButtonBits_Pause = 1 << 5, + IAPVideoButtonBits_BeginFF = 1 << 6, + IAPVideoButtonBits_BeginREW = 1 << 7, + /* bits1 */ + IAPVideoButtonBitsNextChapter = 1 << 0, + IAPVideoButtonBitsPrevChapter = 1 << 1, + IAPVideoButtonBitsNextFrame = 1 << 2, + IAPVideoButtonBitsPrevFrame = 1 << 3, +}; + +enum IAPAudioButtonBits { + /* bits1 */ + IAPAudioButtonBits_PlayPause = 1 << 0, + IAPAudioButtonBits_VolumeUp = 1 << 1, + IAPAudioButtonBits_VolumeDown = 1 << 2, + IAPAudioButtonBits_NextTrack = 1 << 3, + IAPAudioButtonBits_PrevTrack = 1 << 4, + IAPAudioButtonBits_NextAlbum = 1 << 5, + IAPAudioButtonBits_PrevAlbum = 1 << 6, + IAPAudioButtonBits_Stop = 1 << 7, + /* bits2 */ + IAPAudioButtonBits_PlayResume = 1 << 0, + IAPAudioButtonBits_Pause = 1 << 1, + IAPAudioButtonBits_MuteToggle = 1 << 2, + IAPAudioButtonBits_NextChapter = 1 << 3, + IAPAudioButtonBits_PrevChapter = 1 << 4, + IAPAudioButtonBits_NextPlaylist = 1 << 5, + IAPAudioButtonBits_PrevPlaylist = 1 << 6, + IAPAudioButtonBits_ShuffleSettingAdvance = 1 << 7, + /* bits3 */ + IAPAudioButtonBits_RepeatSettingAdvance = 1 << 0, + IAPAudioButtonBits_BeginFF = 1 << 1, + IAPAudioButtonBits_BeginREW = 1 << 2, + IAPAudioButtonBits_Record = 1 << 3, +}; + +struct IAPButtonStatusPayload { + uint8_t bits1; /* IAP{Context,Image,Video,Audio}ButtonBits(bits1) */ + uint8_t bits2; /* IAP{Context,Image,Video,Audio}ButtonBits(bits2), optional */ + uint8_t bits3; /* IAP{Context,Image,Video,Audio}ButtonBits(bits3), optional */ + uint8_t bits4; /* IAP{Context,Image,Video,Audio}ButtonBits(bits4), optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h new file mode 100644 index 0000000000..7e5fd17b8b --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/camera-button-status.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPCameraButtonStatus { + IAPCameraButtonStatus_Up = 0x00, + IAPCameraButtonStatus_Down = 0x01, +}; + +struct IAPCameraButtonStatusPayload { + uint8_t status; /* IAPCameraButtonStatus */ +}; diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h new file mode 100644 index 0000000000..fbbf85fe79 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/current-voice-over-item-property.h @@ -0,0 +1,72 @@ +#pragma once +#include + +enum IAPCurrentVoiceOverItemPropertyType { + IAPCurrentVoiceOverItemPropertyType_Label, + IAPCurrentVoiceOverItemPropertyType_Value, + IAPCurrentVoiceOverItemPropertyType_Hint, + IAPCurrentVoiceOverItemPropertyType_Frame, + IAPCurrentVoiceOverItemPropertyType_Traits, +}; + +struct IAPGetCurrentVoiceOverItemPropertyPayload { + uint8_t type; /* IAPCurrentVoiceOverItemPropertyType */ +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyPayload { + uint8_t type; /* IAPCurrentVoiceOverItemPropertyType */ + uint8_t value[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyLabelPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Label */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyVolumePayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Volume */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyHintPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Hint */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +struct IAPRetCurrentVoiceOverItemPropertyFramePayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Frame */ + uint16_t top_x; + uint16_t top_y; + uint16_t bottom_x; + uint16_t bottom_y; +} __attribute__((packed)); + +enum IAPRetCurrentVoiceOverItemPropertyTraits { + IAPRetCurrentVoiceOverItemPropertyTraits_Button = 0x0000, + IAPRetCurrentVoiceOverItemPropertyTraits_Link = 0x0001, + IAPRetCurrentVoiceOverItemPropertyTraits_SearchField = 0x0002, + IAPRetCurrentVoiceOverItemPropertyTraits_Image = 0x0003, + IAPRetCurrentVoiceOverItemPropertyTraits_Selected = 0x0004, + IAPRetCurrentVoiceOverItemPropertyTraits_Sound = 0x0005, + IAPRetCurrentVoiceOverItemPropertyTraits_KeyboardKey = 0x0006, + IAPRetCurrentVoiceOverItemPropertyTraits_StaticText = 0x0007, + IAPRetCurrentVoiceOverItemPropertyTraits_SummaryElement = 0x0008, + IAPRetCurrentVoiceOverItemPropertyTraits_NotEnabled = 0x0009, + IAPRetCurrentVoiceOverItemPropertyTraits_UpdatesFrequently = 0x000A, + IAPRetCurrentVoiceOverItemPropertyTraits_StartsMediaSession = 0x000B, + IAPRetCurrentVoiceOverItemPropertyTraits_Adjustable = 0x000C, + IAPRetCurrentVoiceOverItemPropertyTraits_BackButton = 0x000D, + IAPRetCurrentVoiceOverItemPropertyTraits_Map = 0x000E, + IAPRetCurrentVoiceOverItemPropertyTraits_DeleteKey = 0x000F, +}; + +struct IAPRetCurrentVoiceOverItemPropertyTraitsPayload { + uint8_t type; /* = IAPCurrentVoiceOverItemPropertyType_Traits */ + uint16_t traits[]; /* IAPRetCurrentVoiceOverItemPropertyTraits */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h new file mode 100644 index 0000000000..70b42be1fd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/hid.h @@ -0,0 +1,75 @@ +#pragma once +#include + +enum IAPRegisterDescriptorCountryCodes { + IAPRegisterDescriptorCountryCodes_Undefined = 0x00, + IAPRegisterDescriptorCountryCodes_Arabic = 0x01, + IAPRegisterDescriptorCountryCodes_Belgian = 0x02, + IAPRegisterDescriptorCountryCodes_CanadianBilingual = 0x03, + IAPRegisterDescriptorCountryCodes_CanadianFrench = 0x04, + IAPRegisterDescriptorCountryCodes_CzechRepublic = 0x05, + IAPRegisterDescriptorCountryCodes_Danish = 0x06, + IAPRegisterDescriptorCountryCodes_Finnish = 0x07, + IAPRegisterDescriptorCountryCodes_French = 0x08, + IAPRegisterDescriptorCountryCodes_German = 0x09, + IAPRegisterDescriptorCountryCodes_Greek = 0x0A, + IAPRegisterDescriptorCountryCodes_Hebrew = 0x0B, + IAPRegisterDescriptorCountryCodes_Hungarian = 0x0C, + IAPRegisterDescriptorCountryCodes_International = 0x0D, + IAPRegisterDescriptorCountryCodes_Italian = 0x0E, + IAPRegisterDescriptorCountryCodes_Japan = 0x0F, + IAPRegisterDescriptorCountryCodes_Korean = 0x10, + IAPRegisterDescriptorCountryCodes_LatinAmerican = 0x11, + IAPRegisterDescriptorCountryCodes_Netherlands = 0x12, + IAPRegisterDescriptorCountryCodes_Norwegian = 0x13, + IAPRegisterDescriptorCountryCodes_Persian = 0x14, + IAPRegisterDescriptorCountryCodes_Poland = 0x15, + IAPRegisterDescriptorCountryCodes_Portuguese = 0x16, + IAPRegisterDescriptorCountryCodes_Russian = 0x17, + IAPRegisterDescriptorCountryCodes_Slovakia = 0x18, + IAPRegisterDescriptorCountryCodes_Spanish = 0x19, + IAPRegisterDescriptorCountryCodes_Swedish = 0x1A, + IAPRegisterDescriptorCountryCodes_SwissFrench = 0x1B, + IAPRegisterDescriptorCountryCodes_SwissGerman = 0x1C, + IAPRegisterDescriptorCountryCodes_Switzerland = 0x1D, + IAPRegisterDescriptorCountryCodes_Taiwan = 0x1E, + IAPRegisterDescriptorCountryCodes_TurkishQ = 0x1F, + IAPRegisterDescriptorCountryCodes_UK = 0x20, + IAPRegisterDescriptorCountryCodes_US = 0x21, + IAPRegisterDescriptorCountryCodes_Croatian = 0x22, + IAPRegisterDescriptorCountryCodes_TurkishF = 0x23, + IAPRegisterDescriptorCountryCodes_Thai = 0xFA, + IAPRegisterDescriptorCountryCodes_Flemish = 0xFB, + IAPRegisterDescriptorCountryCodes_Romanian = 0xFC, + IAPRegisterDescriptorCountryCodes_Bulgarian = 0xFD, + IAPRegisterDescriptorCountryCodes_Chinese = 0xFE, +}; + +struct IAPRegisterDescriptorPayload { + uint8_t index; + uint16_t vid; + uint16_t pid; + uint8_t country_code; /* IAPRegisterDescriptorCountryCodes */ + uint8_t descriptor[]; +} __attribute__((packed)); + +enum IAPHIDReportReportType { + IAPHIDReportReportType_Input = 0x00, + IAPHIDReportReportType_Output = 0x01, +}; + +struct IAPIPodHIDReportPayload { + uint8_t index; + uint8_t report_type; /* = IAPHIDReportReportType_Input */ + uint8_t report[]; +} __attribute__((packed)); + +struct IAPAccessoryHIDReportPayload { + uint8_t index; + uint8_t report_type; /* = IAPHIDReportReportType_Output */ + uint8_t report[]; +} __attribute__((packed)); + +struct IAPUnregisterDescriptorPayload { + uint8_t index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h new file mode 100644 index 0000000000..a81839a9c4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/ipod-out-button-status.h @@ -0,0 +1,26 @@ +#pragma once +#include + +enum IAPIPodOutButtonStatusSource { + IAPIPodOutButtonStatusSource_CarCenterConsole = 0x00, + IAPIPodOutButtonStatusSource_SteeringWheel = 0x01, + IAPIPodOutButtonStatusSource_CarDashboard = 0x02, +}; + +enum IAPIPodOutButtonBits { + /* bits1 */ + IAPIPodOutButtonBits_Select = 1 << 0, + IAPIPodOutButtonBits_Left = 1 << 1, + IAPIPodOutButtonBits_Rigth = 1 << 2, + IAPIPodOutButtonBits_Up = 1 << 3, + IAPIPodOutButtonBits_Down = 1 << 4, + IAPIPodOutButtonBits_Menu = 1 << 5, +}; + +struct IAPIPodOutButtonStatusPayload { + uint8_t source; /* IAPIPodOutButtonStatusSource */ + uint8_t bits1; /* IAPIPodOutButtonBits(bits1) */ + uint8_t bits2; /* IAPIPodOutButtonBits(bits2), optional */ + uint8_t bits3; /* IAPIPodOutButtonBits(bits3), optional */ + uint8_t bits4; /* IAPIPodOutButtonBits(bits4), optional */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h new file mode 100644 index 0000000000..71b4a7abc5 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/radio-button-status.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPRadioButtonStatus { + IAPRadioButtonStatus_Released = 0x00, + IAPRadioButtonStatus_Pushed = 0x02, +}; + +struct IAPRadioButtonStatusPayload { + uint8_t status; /* IAPRadioButtonStatus */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h new file mode 100644 index 0000000000..05ca73726d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/rotation-input-status.h @@ -0,0 +1,33 @@ +#pragma once +#include + +enum IAPRotationInputStatusControllerType { + IAPRotationInputStatusControllerType_FreeWheel = 0x00, +}; + +enum IAPRotationInputStatusRotationDirection { + IAPRotationInputStatusRotationDirection_Left = 0x00, /* CCW */ + IAPRotationInputStatusRotationDirection_Right = 0x01, /* CW */ +}; + +enum IAPRotationInputStatusRotationAction { + IAPRotationInputStatusRotationAction_Completed = 0x00, + IAPRotationInputStatusRotationAction_InProgress = 0x01, + IAPRotationInputStatusRotationAction_Repeat = 0x02, +}; + +enum IAPRotationInputStatusRotationType { + IAPRotationInputStatusRotationType_Detent = 0x00, + IAPRotationInputStatusRotationType_Degree = 0x01, +}; + +struct IAPRotationInputStatusPayload { + uint32_t user_action_duration_ms; + uint8_t source; /* IAPIPodOutButtonStatusSource */ + uint8_t controller_type; /* IAPRotationInputStatusControllerType */ + uint8_t rotation_direction; /* IAPRotationInputStatusRotationDirection */ + uint8_t rotation_action; /* IAPRotationInputStatusRotationAction */ + uint8_t rotation_type; /* IAPRotationInputStatusRotationType */ + uint16_t detents_or_degrees_moved; + uint16_t max_detents_or_degrees_moved; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h new file mode 100644 index 0000000000..05945067f0 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/set-voice-over-context.h @@ -0,0 +1,17 @@ +#pragma once +#include + +enum IAPSetVoiceOverContextType { + IAPSetVoiceOverContextType_None = 0x00, + IAPSetVoiceOverContextType_Header = 0x01, + IAPSetVoiceOverContextType_Link = 0x02, + IAPSetVoiceOverContextType_Form = 0x03, + IAPSetVoiceOverContextType_Cursor = 0x04, + IAPSetVoiceOverContextType_VerticalNavigation = 0x05, + IAPSetVoiceOverContextType_ValueAdjustment = 0x06, + IAPSetVoiceOverContextType_ZoomAdjustment = 0x07, +}; + +struct IAPSetVoiceOverContextPayload { + uint8_t type; /* IAPSetVoiceOverContextType */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h new file mode 100644 index 0000000000..f09290cee4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-event.h @@ -0,0 +1,85 @@ +#pragma once +#include + +enum IAPVoiceOverEventType { + IAPVoiceOverEventType_MovePointer = 0x00, + IAPVoiceOverEventType_MoveToFirst = 0x01, + IAPVoiceOverEventType_MoveToLast = 0x02, + IAPVoiceOverEventType_MoveToNext = 0x03, + IAPVoiceOverEventType_MoveToPrev = 0x04, + IAPVoiceOverEventType_ScrollLeftPage = 0x05, + IAPVoiceOverEventType_ScrollRightPage = 0x06, + IAPVoiceOverEventType_ScrollUpPage = 0x07, + IAPVoiceOverEventType_ScrollDownPage = 0x08, + IAPVoiceOverEventType_ScrollToPoint = 0x09, + IAPVoiceOverEventType_SendTextToInput = 0x0A, + IAPVoiceOverEventType_Cut = 0x0B, + IAPVoiceOverEventType_Copy = 0x0C, + IAPVoiceOverEventType_Paste = 0x0D, + IAPVoiceOverEventType_Home = 0x0E, + IAPVoiceOverEventType_Touch = 0x0F, + IAPVoiceOverEventType_DisplayScale = 0x10, + IAPVoiceOverEventType_CenterDisplay = 0x11, + IAPVoiceOverEventType_PauseSpeak = 0x12, + IAPVoiceOverEventType_ResumeSpeak = 0x13, + IAPVoiceOverEventType_ReadTextFromCurrent = 0x14, + IAPVoiceOverEventType_ReadTextFromTop = 0x15, + IAPVoiceOverEventType_SendTextToSpeech = 0x16, + IAPVoiceOverEventType_Escape = 0x17, +}; + +struct IAPVoiceOverEventPayload { + uint8_t type; /* IAPVoiceOverEventType */ + uint8_t data[]; +} __attribute__((packed)); + +struct IAPVoiceOverEventMovePointerPayload { + uint8_t type; /* = IAPVoiceOverEventType_MovePointer */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventScrollToPointPayload { + uint8_t type; /* = IAPVoiceOverEventType_ScrollToPoint */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventSendTextToInputPayload { + uint8_t type; /* = IAPVoiceOverEventType_SendTextToInput */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); + +enum IAPVoiceOverEventTouchEventType { + IAPVoiceOverEventTouchEventType_Begin = 0x00, + IAPVoiceOverEventTouchEventType_Moved = 0x01, + IAPVoiceOverEventTouchEventType_Stationary = 0x02, + IAPVoiceOverEventTouchEventType_Ended = 0x03, + IAPVoiceOverEventTouchEventType_Canceled = 0x04, +}; + +struct IAPVoiceOverEventTouchPayload { + uint8_t type; /* = IAPVoiceOverEventType_Touch */ + uint8_t reserved[4]; + uint8_t touch_event_type; /* IAPVoiceOverEventTouchEventType */ +} __attribute__((packed)); + +struct IAPVoiceOverEventDisplayScalePayload { + uint8_t type; /* = IAPVoiceOverEventType_DisplayScale */ + uint16_t scale_factor; +} __attribute__((packed)); + +struct IAPVoiceOverEventCenterDisplayPayload { + uint8_t type; /* = IAPVoiceOverEventType_CenterDisplay */ + uint16_t x; + uint16_t y; +} __attribute__((packed)); + +struct IAPVoiceOverEventSendTextToSpeechPayload { + uint8_t type; /* = IAPVoiceOverEventType_SendTextToSpeech */ + uint16_t current_section_index; + uint16_t last_section_index; + char text[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h new file mode 100644 index 0000000000..0d409294b8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/simple-remote/voice-over-parameter.h @@ -0,0 +1,28 @@ +#pragma once +#include + +enum IAPGetVoiceOverParameterType { + IAPGetVoiceOverParameterType_VoiceOverVolume = 0x00, + IAPGetVoiceOverParameterType_SpeakingRate = 0x01, + IAPGetVoiceOverParameterType_VoiceOverEnabled = 0x02, +}; + +struct IAPGetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ +} __attribute__((packed)); + +struct IAPRetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); + +struct IAPSetVoiceOverParameterPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); + + +struct IAPVoiceOverParameterChangedPayload { + uint8_t type; /* IAPGetVoiceOverParameterType */ + uint8_t value; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h b/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h new file mode 100644 index 0000000000..a33e179f3a --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sound-check-state.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPRetSoundCheckStatePayload { + uint8_t sound_check_status; +} __attribute__((packed)); + +struct IAPSetSoundCheckStatePayload { + uint8_t sound_check_status; + uint8_t restore_on_exit; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports.h new file mode 100644 index 0000000000..7969516dc8 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports.h @@ -0,0 +1,25 @@ +#pragma once +#include + +/* [1] P.336 Table 4-212 Sports lingo command summary */ +enum IAPSportsCommandID { + IAPSportsCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPSportsCommandID_GetAccessoryVersion = 0x01, /* from dev, no payload */ + IAPSportsCommandID_RetAccessoryVersion = 0x02, /* from acc, accessory-version.h */ + IAPSportsCommandID_GetAccessoryCaps = 0x03, /* from dev, no payload */ + IAPSportsCommandID_RetAccessoryCaps = 0x04, /* from acc, accessory-caps.h */ + IAPSportsCommandID_IPodAck = 0x80, /* from dev, general/ipod-ack.h */ + IAPSportsCommandID_GetIPodCaps = 0x83, /* from acc, no payload */ + IAPSportsCommandID_RetIPodCaps = 0x84, /* from dev, ipod-caps.h */ + IAPSportsCommandID_GetUserIndex = 0x85, /* from acc, no payload */ + IAPSportsCommandID_RetUserIndex = 0x86, /* from dev, user-index.h */ + IAPSportsCommandID_GetUserData = 0x88, /* from acc, user-data.h */ + IAPSportsCommandID_RetUserData = 0x89, /* from dev, user-data.h */ + IAPSportsCommandID_SetUserData = 0x8A, /* from acc, user-data.h */ +}; + +#include "sports/accessory-caps.h" +#include "sports/accessory-version.h" +#include "sports/ipod-caps.h" +#include "sports/user-data.h" +#include "sports/user-index.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h new file mode 100644 index 0000000000..ddbdb779b4 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-caps.h @@ -0,0 +1,11 @@ +#pragma once +#include + +enum IAPSportsAccessoryCapBits { + IAPAccessoryCapBits_LaterCommands = 1 << 9, +}; + +struct IAPRetAccessoryCapsPayload { + uint16_t mask; /* IAPSportsAccessoryCapBits */ + uint8_t reserved; /* = 0x00 */ +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h new file mode 100644 index 0000000000..6a6be98427 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/accessory-version.h @@ -0,0 +1,7 @@ +#pragma once +#include + +struct IAPRetAccessoryVersionPayload { + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h new file mode 100644 index 0000000000..a203552a0f --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/ipod-caps.h @@ -0,0 +1,12 @@ +#pragma once +#include + +enum IAPSportsIPodCapBits { + Cardio = 1 << 0, + UserData = 1 << 1, +}; + +struct IAPRetIPodCapsPayload { + uint16_t caps; /* IAPSportsIPodCapBits */ + uint8_t user_count; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h new file mode 100644 index 0000000000..5044e64efa --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-data.h @@ -0,0 +1,73 @@ +#pragma once +#include + +/* [1] P.341 4.9.13 Command 0x88: GetUserData */ + +enum IAPSportsUserDataType { + IAPSportsUserDataType_UnitSystem = 0x00, + IAPSportsUserDataType_Name = 0x01, + IAPSportsUserDataType_Gender = 0x02, + IAPSportsUserDataType_Weight = 0x03, + IAPSportsUserDataType_Age = 0x04, + IAPSportsUserDataType_WorkoutRecording = 0x05, +}; + +struct IAPGetUserDataPayload { + uint8_t type; /* IAPSportsUserDataType */ +} __attribute__((packed)); + +struct IAPRetUserDataPayload { + uint8_t type; /* IAPSportsUserDataType */ + uint8_t data[]; +} __attribute__((packed)); + +enum IAPSportsUnitSystem { + IAPSportsUnitSystem_None = 0x00, + IAPSportsUnitSystem_Imperial = 0x01, + IAPSportsUnitSystem_Metric = 0x02, +}; + +struct IAPRetUserDataUnitSystemPayload { + uint8_t type; /* = IAPSportsUserDataType_UnitSystem */ + uint8_t preferred_unit_system; /* IAPSportsUnitSystem */ +} __attribute__((packed)); + +struct IAPRetUserDataNamePayload { + uint8_t type; /* = IAPSportsUserDataType_Name */ + char name[]; +} __attribute__((packed)); + +enum IAPSportsGender { + IAPSportsGender_None = 0x00, + IAPSportsGender_Female = 0x01, + IAPSportsGender_Male = 0x02, +}; + +struct IAPRetUserDataGenderPayload { + uint8_t type; /* = IAPSportsUserDataType_Gender */ + uint8_t gender; /* IAPSportsGender */ +} __attribute__((packed)); + +struct IAPRetUserDataWeightPayload { + uint8_t type; /* = IAPSportsUserDataType_Weight */ + uint16_t weight; +} __attribute__((packed)); + +struct IAPRetUserDataAgePayload { + uint8_t type; /* = IAPSportsUserDataType_Age */ + uint8_t age; +} __attribute__((packed)); + +enum IAPSportsWorkoutRecordingPreferences { + IAPSportsWorkoutRecordingPreferences_None = 0x00, + IAPSportsWorkoutRecordingPreferences_Never = 0x01, + IAPSportsWorkoutRecordingPreferences_Ask = 0x02, + IAPSportsWorkoutRecordingPreferences_Always = 0x03, +}; + +struct IAPRetUserDataWorkoutRecordingPayload { + uint8_t type; /* = IAPSportsUserDataType_WorkoutRecording */ + uint8_t preference; /* IAPSportsWorkoutRecordingPreferences */ +} __attribute__((packed)); + +/* IAPSetUserDataPayload = IAPRetUserDataPayload */ diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h new file mode 100644 index 0000000000..2fce3aa328 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/sports/user-index.h @@ -0,0 +1,6 @@ +#pragma once +#include + +struct IAPRetUserIndexPayload { + uint8_t user_index; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage.h new file mode 100644 index 0000000000..cfce9291b1 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage.h @@ -0,0 +1,27 @@ +#pragma once +#include + +/* [1] P.357 Table 4-242 Storage lingo commands */ + +enum IAPStorageCommandID { + IAPStorageCommandID_IPodAck = 0x00, /* from dev, ipod-ack.h */ + IAPStorageCommandID_GetIPodCaps = 0x01, /* from acc, no payload */ + IAPStorageCommandID_RetIPodCaps = 0x02, /* from dev, ipod-caps.h */ + IAPStorageCommandID_RetIPodFileHandle = 0x04, /* from dev, ipod-file-handle.h */ + IAPStorageCommandID_WriteIPodFileData = 0x07, /* from acc, write-ipod-file-data.h */ + IAPStorageCommandID_CloseIPodFile = 0x08, /* from acc, ipod-file-handle.h */ + IAPStorageCommandID_GetIPodFreeSpace = 0x10, /* from acc, no payload */ + IAPStorageCommandID_RetIPodFreeSpace = 0x11, /* from dev, ipod-free-space.h */ + IAPStorageCommandID_OpenIPodFeatureFile = 0x12, /* from acc, ipod-file-handle.h */ + IAPStorageCommandID_AccessoryAck = 0x80, /* from acc, acc-ack.h */ + IAPStorageCommandID_GetAccessoryCaps = 0x81, /* from dev, no payload */ + IAPStorageCommandID_RetAccessoryCaps = 0x82, /* from acc, */ +}; + +#include "storage/acc-ack.h" +#include "storage/accessory-caps.h" +#include "storage/ipod-ack.h" +#include "storage/ipod-caps.h" +#include "storage/ipod-file-handle.h" +#include "storage/ipod-free-space.h" +#include "storage/write-ipod-file-data.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h new file mode 100644 index 0000000000..1980237af2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/acc-ack.h @@ -0,0 +1,8 @@ +#pragma once +#include + +struct IAPStorageAccessoryAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; + uint8_t handle; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h new file mode 100644 index 0000000000..7a7c26deed --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/accessory-caps.h @@ -0,0 +1,11 @@ +#pragma once +#include + +/* [1] P.365 4.11.14 Command 0x82: RetAccessoryCaps */ + +struct IAPStorageRetAccessoryCapsPayload { + uint8_t reserved1; /* = 0x00 */ + uint8_t reserved2; /* = 0xFF */ + uint8_t major; + uint8_t minor; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h new file mode 100644 index 0000000000..05b9c012e2 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-ack.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.359 4.11.3 Command 0x00: iPodAck */ + +struct IAPStorageIPodAckPayload { + uint8_t status; /* IAPAckStatus */ + uint8_t id; + uint8_t handle; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h new file mode 100644 index 0000000000..2a4d04be46 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-caps.h @@ -0,0 +1,13 @@ +#pragma once +#include + +/* [1] P.359 4.11.5 Command 0x02: RetiPodCaps */ + +struct IAPIPodRetIPodCapsPayload { + uint64_t total_space; + uint32_t max_file_size; + uint16_t max_write_size; + uint8_t reserved[6]; + uint8_t major_version; + uint8_t minor_version; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h new file mode 100644 index 0000000000..edaeefc7cd --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-file-handle.h @@ -0,0 +1,30 @@ +#pragma once +#include + +/* [1] P.360 4.11.6 Command 0x04: RetiPodFileHandle */ + +struct IAPRetIPodFileHandlePayload { + uint8_t handle; +} __attribute__((packed)); + +struct IAPCloseIPodFilePayload { + uint8_t handle; +} __attribute__((packed)); + +enum IAPOpenIPodFeatureFileFeatureType { + IAPOpenIPodFeatureFileFeatureType_TadioTagging = 0x01, + IAPOpenIPodFeatureFileFeatureType_CardioEquipmentWorkout = 0x02, +}; + +enum IAPOpenIPodFeatureFileOptionsMask { + IAPOpenIPodFeatureFileOptionsMask_AppendFileData = 1 << 0, + IAPOpenIPodFeatureFileOptionsMask_AppendIPodInfo = 1 << 1, + IAPOpenIPodFeatureFileOptionsMask_InsertSignature = 1 << 3, +}; + +struct IAPOpenIPodFeatureFilePayload { + uint8_t feature_type; /* IAPOpenIPodFeatureFileFeatureType */ + uint32_t options_mask; /* IAPOpenIPodFeatureFileOptionsMask */ + uint8_t file_data[]; +} __attribute__((packed)); + diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h new file mode 100644 index 0000000000..8f81ec8059 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/ipod-free-space.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* [1] P.362 4.11.10 Command 0x11: RetiPodFreeSpace */ + +struct IAPRetIPodFreeSpacePayload { + uint64_t free_space; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h b/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h new file mode 100644 index 0000000000..1c6fe7ce42 --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/storage/write-ipod-file-data.h @@ -0,0 +1,10 @@ +#pragma once +#include + +/* [1] P.360 4.11.7 Command 0x07: WriteiPodFileData */ + +struct IAPWriteIPodFileDataPayload { + uint32_t offset; + uint8_t handle; + uint8_t data[]; +} __attribute__((packed)); diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h new file mode 100644 index 0000000000..cf4594bdec --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode.h @@ -0,0 +1,14 @@ +#pragma once +#include + +/* [1] P.283 Table 4-101 USB Host mode commands */ +enum IAPUSBHostModeCommandID { + IAPUSBHostModeCommandID_AccessoryAck = 0x00, /* from acc, general/acc-ack.h */ + IAPUSBHostModeCommandID_NotifyUSBMode = 0x04, /* from dev, usb-mode.h */ + IAPUSBHostModeCommandID_IPodAck = 0x80, /* from dev, general/ipod-ack.h */ + IAPUSBHostModeCommandID_GetIPodUSBMode = 0x81, /* from acc, no payload */ + IAPUSBHostModeCommandID_RetIPodUSBMode = 0x82, /* from dev, usb-mode.h */ + IAPUSBHostModeCommandID_SetIPodUSBMode = 0x83, /* from acc, usb-mode.h */ +}; + +#include "usb-host-mode/usb-mode.h" diff --git a/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h new file mode 100644 index 0000000000..fdc0f6e58d --- /dev/null +++ b/firmware/usbstack/iap/libiap/spec/lingoes/usb-host-mode/usb-mode.h @@ -0,0 +1,22 @@ +#pragma once +#include + +/* [1] P.285 4.6.3 Command 0x04: NotifyUSBMode */ + +enum IAPUSBHostModeStatusCodes { + Disabled = 0x00, + DeviceMode = 0x01, + HostMode = 0x02, +}; + +struct IAPNotifyUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; + +struct IAPRetIPodUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; + +struct IAPSetIPodUSBModePayload { + uint8_t mode; /* IAPUSBHostModeStatusCodes */ +}; diff --git a/firmware/usbstack/iap/libiap/time.h b/firmware/usbstack/iap/libiap/time.h new file mode 100644 index 0000000000..304bb4f04d --- /dev/null +++ b/firmware/usbstack/iap/libiap/time.h @@ -0,0 +1,11 @@ +#pragma once +#include + +struct IAPPlatformTime { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t seconds; +}; diff --git a/firmware/usbstack/iap/libiap/unaligned.h b/firmware/usbstack/iap/libiap/unaligned.h new file mode 100644 index 0000000000..864cac0a34 --- /dev/null +++ b/firmware/usbstack/iap/libiap/unaligned.h @@ -0,0 +1,7 @@ +#pragma once +#include + +typedef __attribute__((aligned(1))) uint8_t uu8; +typedef __attribute__((aligned(1))) uint16_t uu16; +typedef __attribute__((aligned(1))) uint32_t uu32; +typedef __attribute__((aligned(1))) uint64_t uu64; diff --git a/firmware/usbstack/iap/macros.h b/firmware/usbstack/iap/macros.h new file mode 100644 index 0000000000..f741398560 --- /dev/null +++ b/firmware/usbstack/iap/macros.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 + +#include "debug.h" + +#if DEBUG_ENABLE_INFO == 1 +#define LOG(fmt, ...) logf("%lu %s:%d " fmt, iap_debug_timestamp(), __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); +#else +#define LOG(...) +#endif + +#if DEBUG_ENABLE_ERROR == 1 +#define ERROR(fmt, ...) logf("%lu %s:%d " fmt, iap_debug_timestamp(), __func__, __LINE__ __VA_OPT__(, __VA_ARGS__)); +#else +#define ERROR(...) +#endif + +#define check_act(cond, act, ...) \ + if(!(cond)) { \ + ERROR("assertion failed" __VA_OPT__(":" __VA_ARGS__)); \ + act; \ + } + +#define AS_PACKET_SIZE 192 +#define AS_EP_IN usb_iap_ep_allocs[0].ep +#define HID_EP_IN usb_iap_ep_allocs[1].ep diff --git a/firmware/usbstack/iap/notification.c b/firmware/usbstack/iap/notification.c new file mode 100644 index 0000000000..95444f077c --- /dev/null +++ b/firmware/usbstack/iap/notification.c @@ -0,0 +1,111 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "playback.h" + +#include "iap-usb.h" +#include "libiap/iap.h" +#include "platform.h" + +#include "macros.h" + +extern bool iap_initialized; + +void iap_on_track_time_position(uint32_t pos_ms) { + if(!iap_initialized) { + return; + } + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_track_time_position(ctx, pos_ms); +} + +void iap_on_track_playback_index(uint32_t index, bool track_ready) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + + if(track_ready) { + /* called from from audio_finish_load_track() */ + goto notify; + } + + /* called from audio_playlist_track_change() */ + struct Platform* plt = ctx->platform; + if(plt->aa_slot < 0) { + goto notify; + } + if(playback_current_aa_hid(plt->aa_slot) >= 0) { + /* artwork ready, maybe preloaded track */ + goto notify; + } else { + /* artwork not ready, maybe after a playlist jump. + * in this case, we will be called again from audio_finish_load_track(), + * with track_ready == true. */ + return; + } +notify: + iap_notify_track_playback_index(ctx, index); +} + +void iap_on_tracks_count(uint32_t count) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_tracks_count(ctx, count); +} + +void iap_on_play_status(int status) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_play_status(ctx, _iap_convert_play_status(status)); +} + +void iap_on_volume(int volume) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_volume(ctx, _iap_convert_volume(volume), iap_false); +} + +void iap_on_shuffle_state(bool state) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_shuffle_state(ctx, _iap_convert_shuffle_state(state)); +} + +void iap_on_repeat_state(int state) { + if(!iap_initialized) { + return; + } + + struct IAPContext* ctx = _iap_acquire_ctx(false); + iap_notify_repeat_state(ctx, _iap_convert_repeat_state(state)); +} diff --git a/firmware/usbstack/iap/platform-macros.h b/firmware/usbstack/iap/platform-macros.h new file mode 100644 index 0000000000..6b1eb8a5ab --- /dev/null +++ b/firmware/usbstack/iap/platform-macros.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include + +#include "debug.h" + +#if DEBUG_ENABLE_INFO == 1 +#define IAP_LOGF(fmt, ...) logf("%lu " fmt, iap_debug_timestamp() __VA_OPT__(, __VA_ARGS__)) +#endif + +#if DEBUG_ENABLE_ERROR == 1 +#define IAP_ERRORF(fmt, ...) logf("%lu " fmt, iap_debug_timestamp() __VA_OPT__(, __VA_ARGS__)) +#endif + +#define IAP_ARTWORK_WIDTH 128 +#define IAP_ARTWORK_HEIGHT 128 +#define IAP_COLOR_ARTWORK iap_true diff --git a/firmware/usbstack/iap/platform.c b/firmware/usbstack/iap/platform.c new file mode 100644 index 0000000000..1e5ce22a9e --- /dev/null +++ b/firmware/usbstack/iap/platform.c @@ -0,0 +1,482 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "audio.h" +#include "buffering.h" +#include "core_alloc.h" +#include "metadata.h" +#include "misc.h" +#include "pcm_mixer.h" +#include "pcm_sink.h" +#include "playback.h" +#include "playlist.h" +#include "powermgmt.h" +#include "settings.h" +#include "sound.h" +#include "usb_drv.h" + +#include "../usb_iap.h" +#include "debug.h" +#include "libiap/iap.h" +#include "macros.h" +#include "platform.h" + +void* iap_platform_malloc(struct IAPContext* iap_ctx, size_t size, int flags) { + struct Platform* plt = iap_ctx->platform; + for(size_t i = 0; i < ARRAYLEN(plt->malloc_results); i += 1) { + if(plt->malloc_results[i].ptr != NULL) { + continue; + } + struct IAPAllocResult result; + if(flags & IAPPlatformMallocFlags_Uncached) { + check_act(iap_alloc_usb_send_buffer(size, &result), return NULL); + } else { + check_act(iap_alloc_buffer(size, &result), return NULL); + } + plt->malloc_results[i] = result; + return result.ptr; + } + ERROR("no free malloc slot"); + return NULL; +} + +void iap_platform_free(struct IAPContext* iap_ctx, void* ptr) { + struct Platform* plt = iap_ctx->platform; + for(size_t i = 0; i < ARRAYLEN(plt->malloc_results); i += 1) { + if(plt->malloc_results[i].ptr == ptr) { + core_free(plt->malloc_results[i].handle); + plt->malloc_results[i].ptr = NULL; + return; + } + } + ERROR("no matching malloc slot"); +} + +int iap_platform_send_hid_report(struct IAPContext* iap_ctx, const void* ptr, size_t size) { + (void)iap_ctx; +#if DEBUG_DUMP_TX == 1 + logf("==== dev ==== %p %u > %d", ptr, size, HID_EP_IN); + iap_platform_dump_hex(ptr, MIN(size, 48)); +#endif + const int ret = usb_drv_send_nonblocking(HID_EP_IN, (void*)ptr, size); + return ret == 0 ? (int)size : ret; +} + +IAPBool iap_platform_get_ipod_serial_num(struct IAPContext* iap_ctx, struct IAPSpan* serial) { + (void)iap_ctx; + static const char* serial_num = "000000000000"; + return iap_span_append(serial, serial_num, strlen(serial_num) + 1); +} + +IAPBool iap_platform_get_play_status(struct IAPContext* iap_ctx, struct IAPPlatformPlayStatus* status) { + struct Platform* plt = iap_ctx->platform; + + status->state = _iap_convert_play_status(audio_status()); + if(status->state == IAPIPodStatePlayStatus_PlaybackStopped) { + return iap_true; + } + + struct mp3entry* id3 = audio_current_track(); + check_act(id3 != NULL, return iap_false); + status->track_total_ms = id3->length; + status->track_pos_ms = id3->elapsed; + status->track_index = playlist_get_display_index() - 1; + status->track_count = playlist_amount(); + status->track_caps = IAPIPodStateTrackCapBits_HasReleaseDate; + if(plt->aa_slot >= 0 && playback_current_aa_hid(plt->aa_slot) >= 0) { + status->track_caps |= IAPIPodStateTrackCapBits_HasAlbumArts; + } + + return iap_true; +} + +void iap_platform_control(struct IAPContext* iap_ctx, enum IAPPlatformControl control, struct IAPPlatformPendingControl pending) { + struct Platform* plt = iap_ctx->platform; + + long button = BUTTON_NONE; + switch(control) { + case IAPPlatformControl_TogglePlayPause: + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + break; + case IAPPlatformControl_Play: + if(audio_status() != AUDIO_STATUS_PLAY) { + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + } + break; + case IAPPlatformControl_Pause: + if(audio_status() == AUDIO_STATUS_PLAY) { + button = BUTTON_MULTIMEDIA_PLAYPAUSE; + } + break; + case IAPPlatformControl_Stop: + button = BUTTON_MULTIMEDIA_STOP; + break; + case IAPPlatformControl_Next: + button = BUTTON_MULTIMEDIA_NEXT; + break; + case IAPPlatformControl_Prev: + button = BUTTON_MULTIMEDIA_PREV; + break; + case IAPPlatformControl_VolumeUp: + adjust_volume(1); + break; + case IAPPlatformControl_VolumeDown: + adjust_volume(-1); + break; + case IAPPlatformControl_ToggleMute: + /* rockbox has no mute/unmute */ + break; + } + logf("control=%d button=0x%04lX", control, button); + + IAPBool ret = iap_true; + if(button == BUTTON_NONE) { + goto exit; + } + check_act(button_queue_try_post(button, 0), ret = false); + if(ret && button == BUTTON_MULTIMEDIA_PLAYPAUSE && audio_status() == 0) { + /* we are transitioning to stopped to playing, which may take time. + * to maintain synchronization with accessories, do not send an ack until playback actually begins. */ + plt->control_pending = true; + plt->pending_control = pending; + return; + } +exit: + check_act(iap_control_response(iap_ctx, pending, ret), ); +} + +static uint8_t normalize_8(int val, int min, int max) { + return 0xFF * (val - min) / (max - min); +} + +IAPBool iap_platform_get_volume(struct IAPContext* iap_ctx, struct IAPPlatformVolumeStatus* status) { + (void)iap_ctx; + + status->volume = _iap_convert_volume(global_status.volume); + status->muted = iap_false; + return iap_true; +} + +IAPBool iap_platform_get_power_status(struct IAPContext* iap_ctx, struct IAPPlatformPowerStatus* status) { + (void)iap_ctx; + status->battery_level = _iap_convert_battery_level(battery_level()); + status->state = _iap_convert_charge_status(charge_state); + return iap_true; +} + +IAPBool iap_platform_get_shuffle_setting(struct IAPContext* iap_ctx, uint8_t* status) { + (void)iap_ctx; + *status = _iap_convert_shuffle_state(global_settings.playlist_shuffle); + return iap_true; +} + +IAPBool iap_platform_set_shuffle_setting(struct IAPContext* iap_ctx, uint8_t status) { + (void)iap_ctx; + + if(status == IAPIPodStateShuffleSettingState_Tracks && !global_settings.playlist_shuffle) { + global_settings.playlist_shuffle = true; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + playlist_randomise(NULL, current_tick, true); + } + } else if(status == IAPIPodStateShuffleSettingState_Off && global_settings.playlist_shuffle) { + global_settings.playlist_shuffle = false; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + playlist_sort(NULL, true); + } + } + return iap_true; +} + +IAPBool iap_platform_get_repeat_setting(struct IAPContext* iap_ctx, uint8_t* status) { + (void)iap_ctx; + *status = _iap_convert_repeat_state(global_settings.repeat_mode); + return iap_true; +} + +IAPBool iap_platform_set_repeat_setting(struct IAPContext* iap_ctx, uint8_t status) { + (void)iap_ctx; + + /* there are more repeat options in Rockbox rather than iAP. + * e.g. RB Repeat One and AB are both mapped to iAP Repeat One. + * so we should not accept iAP Repeat One as RB Repeat One, if we have + * RB Repeat AB set. */ + if(_iap_convert_repeat_state(global_settings.repeat_mode) == status) { + /* already the same (or equievalent) mode set */ + return iap_true; + } + + static const uint8_t table[] = { + [IAPIPodStateRepeatSettingState_Off] = REPEAT_OFF, + [IAPIPodStateRepeatSettingState_One] = REPEAT_ONE, + [IAPIPodStateRepeatSettingState_All] = REPEAT_ALL, + }; + check_act(status < ARRAY_SIZE(table), return iap_false); + global_settings.repeat_mode = table[status]; + settings_save(); + if(audio_status() & AUDIO_STATUS_PLAY) { + audio_flush_and_reload_tracks(); + } + return iap_true; +} + +IAPBool iap_platform_get_date_time(struct IAPContext* iap_ctx, struct IAPDateTime* time) { + (void)iap_ctx; + _iap_convert_datetime(get_time(), time); + return iap_true; +} + +IAPBool iap_platform_get_backlight_level(struct IAPContext* iap_ctx, uint8_t* level) { + (void)iap_ctx; + +#ifdef HAVE_BACKLIGHT_BRIGHTNESS + *level = normalize_8(global_settings.brightness, MIN_BRIGHTNESS_SETTING, MAX_BRIGHTNESS_SETTING); +#else + *level = 0xFF; +#endif + return iap_true; +} + +IAPBool iap_platform_get_hold_switch_state(struct IAPContext* iap_ctx, IAPBool* state) { + (void)iap_ctx; + +#ifdef HAS_BUTTON_HOLD + *state = button_hold(); +#else + *state = iap_false; +#endif + return iap_true; +} + +/* taken from iap-core.c */ +static void get_trackinfo(const unsigned int track, struct mp3entry* id3) { + int tracknum = track; + tracknum += playlist_get_first_index(NULL); + if(tracknum >= playlist_amount()) { + tracknum -= playlist_amount(); + } + + if(playlist_next(0) != tracknum) { + struct playlist_track_info info; + playlist_get_track_info(NULL, tracknum, &info); + get_metadata(id3, -1, info.filename); + } else { + memcpy(id3, audio_current_track(), sizeof(*id3)); + } +} + +IAPBool iap_platform_get_indexed_track_info(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformTrackInfo* info) { + struct Platform* plt = iap_ctx->platform; + + struct playlist_track_info track; + struct mp3entry id3; + check_act(playlist_get_track_info(NULL, index, &track) == 0, return iap_false); + get_trackinfo(index, &id3); + + if(info->total_ms != NULL) { + *info->total_ms = id3.length; + } + if(info->caps != NULL) { + *info->caps = IAPIPodStateTrackCapBits_HasReleaseDate; + /* FIXME: respect index */ + if(plt->aa_slot >= 0 && playback_current_aa_hid(plt->aa_slot) >= 0) { + *info->caps |= IAPIPodStateTrackCapBits_HasAlbumArts; + } + } + if(info->release_date != NULL) { + info->release_date->year = id3.year; + info->release_date->month = 0; + info->release_date->day = 0; + info->release_date->hour = 0; + info->release_date->minute = 0; + info->release_date->seconds = 0; + } + if(info->artist != NULL) { + check_act(iap_span_append(info->artist, id3.artist, strlen(id3.artist) + 1), return iap_false); + } + if(info->composer != NULL) { + check_act(iap_span_append(info->composer, id3.composer, strlen(id3.composer) + 1), return iap_false); + } + if(info->album != NULL) { + check_act(iap_span_append(info->album, id3.album, strlen(id3.album) + 1), return iap_false); + } + if(info->title != NULL) { + check_act(iap_span_append(info->title, id3.title, strlen(id3.title) + 1), return iap_false); + } + return iap_true; +} + +IAPBool iap_platform_set_playing_track(struct IAPContext* iap_ctx, uint32_t index) { + (void)iap_ctx; + audio_skip((int)index - playlist_next(0)); + return iap_true; +} + +IAPBool iap_platform_open_artwork(struct IAPContext* iap_ctx, uint32_t index, struct IAPPlatformArtwork* artwork) { + struct Platform* plt = iap_ctx->platform; + /* only aa for currently playing track is available */ + check_act((int)index == playlist_get_display_index() - 1, return iap_false); + const int hid = playback_current_aa_hid(plt->aa_slot); + check_act(hid >= 0, return iap_false, "%d %d", plt->aa_slot, hid); + struct bitmap* bmp; + check_act(bufgetdata(hid, 0, (void*)&bmp) > 0, return iap_false); + artwork->color = iap_true; + artwork->width = bmp->width; + artwork->height = bmp->height; + artwork->opaque = hid; + return iap_true; +} + +IAPBool iap_platform_get_artwork_ptr(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork, struct IAPSpan* span) { + struct Platform* plt = iap_ctx->platform; + + /* check the albumart has not reloaded */ + /* FIXME: not a correct check due to possibility of hid confliction */ + const int hid = playback_current_aa_hid(plt->aa_slot); + check_act(hid == (int)artwork->opaque, return iap_false); + + struct bitmap* bmp; + /* more checks */ + check_act(bufgetdata(hid, 0, (void*)&bmp) > 0, return iap_false); + check_act(bmp->width == artwork->width && bmp->height == artwork->height, return iap_false); + + span->ptr = bmp->data; + span->size = bmp->width * bmp->height * 2; + return iap_true; +} + +IAPBool iap_platform_close_artwork(struct IAPContext* iap_ctx, struct IAPPlatformArtwork* artwork) { + (void)iap_ctx; + (void)artwork; + return iap_true; +} + +void iap_platform_dump_hex(const void* ptr, size_t size) { + if(ptr == NULL) { + logf("(null)"); + return; + } + +#if DEBUG_HEXDUMP_NOLIMIT != 1 + size = MIN(size, 32); +#endif + + static const char chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + char line[4 * (8 + 1) + 1]; + for(size_t l = 0; l * 16 < size; l += 1) { + char* c = line; + for(size_t b = 0; b < 4; b += 1) { + for(size_t i = 0; i < 4; i += 1) { + size_t index = l * 16 + b * 4 + i; + if(index >= size) { + break; + } + *c++ = chars[((uint8_t*)ptr)[index] >> 4]; + *c++ = chars[((uint8_t*)ptr)[index] & 0xF]; + } + *c++ = ' '; + } + *c++ = '\0'; + logf("%04X: %s", l * 16, line); + } +} + +IAPBool iap_platform_on_acc_samprs_received(struct IAPContext* iap_ctx, struct IAPSpan* samprs) { + (void)iap_ctx; + + bool has_44k = false; + bool has_48k = false; + while(samprs->size > 0) { + uint32_t sample_rate; + check_act(iap_span_read_32(samprs, &sample_rate), return iap_false); + has_44k |= sample_rate == SAMPR_44; + has_48k |= sample_rate == SAMPR_48; + } + check_act(has_44k && has_48k, return iap_false, "accessory lacks mandatory freq support: 44k=%d 48k=%d", has_44k, has_48k); + check_act(mixer_switch_sink(PCM_SINK_IAP), return false); + return iap_true; +} + +uint8_t _iap_convert_play_status(int rb_audio_status) { + if(rb_audio_status & AUDIO_STATUS_PAUSE) { + return IAPIPodStatePlayStatus_PlaybackPaused; + } else if(rb_audio_status & AUDIO_STATUS_PLAY) { + return IAPIPodStatePlayStatus_Playing; + } else { + return IAPIPodStatePlayStatus_PlaybackStopped; + } +} + +uint8_t _iap_convert_volume(int rb_volume) { + return normalize_8(rb_volume, sound_min(SOUND_VOLUME), sound_max(SOUND_VOLUME)); +} + +uint8_t _iap_convert_shuffle_state(bool rb_state) { + return rb_state ? IAPIPodStateShuffleSettingState_Tracks : IAPIPodStateShuffleSettingState_Off; +} + +uint8_t _iap_convert_repeat_state(int rb_state) { + static const uint8_t table[] = { + [REPEAT_OFF] = IAPIPodStateRepeatSettingState_Off, + [REPEAT_ALL] = IAPIPodStateRepeatSettingState_All, + [REPEAT_ONE] = IAPIPodStateRepeatSettingState_One, + [REPEAT_SHUFFLE] = IAPIPodStateRepeatSettingState_All, +#ifdef AB_REPEAT_ENABLE + [REPEAT_AB] = IAPIPodStateRepeatSettingState_One, +#endif + }; + + if(rb_state == REPEAT_OFF && global_settings.next_folder) { + /* when repeat is off and next folder is enabled, report repeat all. + * without this hack, the accessory will likely set repeat to all, + * resulting in trapping us in the current directory. */ + return IAPIPodStateRepeatSettingState_All; + } else { + check_act(rb_state < ARRAY_SIZE(table), return iap_false); + return table[rb_state]; + } +} + +uint8_t _iap_convert_battery_level(int rb_battery_level) { + return 0xFF * rb_battery_level / 100; +} + +uint8_t _iap_convert_charge_status(enum charge_state_type rb_charge_state) { + switch(rb_charge_state) { + case CHARGING: + return IAPIPodStatePowerState_ExternalCharging; + case TOPOFF: + case TRICKLE: + return IAPIPodStatePowerState_ExternalCharged; + break; + default: + return IAPIPodStatePowerState_Internal; + } +} + +void _iap_convert_datetime(struct tm* rb_time, struct IAPDateTime* time) { + time->year = rb_time->tm_year + 1900; + time->month = rb_time->tm_mon + 1; + time->day = rb_time->tm_mday; + time->hour = rb_time->tm_hour; + time->minute = rb_time->tm_min; + time->seconds = rb_time->tm_sec; +} diff --git a/firmware/usbstack/iap/platform.h b/firmware/usbstack/iap/platform.h new file mode 100644 index 0000000000..2707722b04 --- /dev/null +++ b/firmware/usbstack/iap/platform.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include + +#include "libiap/datetime.h" +#include "libiap/platform.h" +#include "powermgmt.h" +#include "time.h" + +#include "buffer.h" + +/* usb_iap.c */ +struct IAPContext* _iap_acquire_ctx(bool lock); +void _iap_release_ctx(void); + +struct Platform { + struct IAPAllocResult malloc_results[4]; /* allow up to 4 mallocs */ + struct IAPPlatformPendingControl pending_control; + bool control_pending; + + int aa_slot; +}; + +/* helper functions */ +uint8_t _iap_convert_play_status(int rb_audio_status); +uint8_t _iap_convert_volume(int rb_volume); +uint8_t _iap_convert_shuffle_state(bool rb_state); +uint8_t _iap_convert_repeat_state(int rb_state); +uint8_t _iap_convert_battery_level(int rb_battery_level); +uint8_t _iap_convert_charge_status(enum charge_state_type rb_charge_state); +void _iap_convert_datetime(struct tm* rb_time, struct IAPDateTime* time); diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 9b90fbd5ad..ba4bd82145 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -53,6 +53,10 @@ #include "usb_audio_def.h" // DEBUG #endif +#ifdef USB_ENABLE_IAP +#include "usb_iap.h" +#endif + /* TODO: Move target-specific stuff somewhere else (serial number reading) */ #if defined(IPOD_ARCH) && defined(CPU_PP) @@ -72,7 +76,11 @@ #define USB_MAX_CURRENT 500 #endif +#ifdef USB_ENABLE_IAP +#define NUM_CONFIGS 2 +#else #define NUM_CONFIGS 1 +#endif /*-------------------------------------------------------------------------*/ /* USB protocol descriptors: */ @@ -107,7 +115,7 @@ static struct usb_config_descriptor __attribute__((aligned(2))) .bDescriptorType = USB_DT_CONFIG, .wTotalLength = 0, /* will be filled in later */ .bNumInterfaces = 1, - .bConfigurationValue = 1, + .bConfigurationValue = 0, /* will be filled in later */ .iConfiguration = 0, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = (USB_MAX_CURRENT + 1) / 2, /* In 2mA units */ @@ -303,6 +311,30 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] = .get_interface = usb_audio_get_interface, }, #endif +#ifdef USB_ENABLE_IAP + [USB_DRIVER_IAP] = { + .enabled = false, + .needs_exclusive_storage = false, + .config = 2, + .first_interface = 0, + .last_interface = 0, + .ep_allocs_size = ARRAYLEN(usb_iap_ep_allocs), + .ep_allocs = usb_iap_ep_allocs, + .set_first_interface = usb_iap_set_first_interface, + .get_config_descriptor = usb_iap_get_config_descriptor, + .init_connection = usb_iap_init_connection, + .init = usb_iap_init, + .disconnect = usb_iap_disconnect, + .transfer_complete = usb_iap_transfer_complete, + .fast_transfer_complete = usb_iap_fast_transfer_complete, + .control_request = usb_iap_control_request, +#ifdef HAVE_HOTSWAP + .notify_hotswap = NULL, +#endif + .set_interface = usb_iap_set_interface, + .get_interface = usb_iap_get_interface, + }, +#endif }; #ifdef USB_LEGACY_CONTROL_API diff --git a/firmware/usbstack/usb_iap.c b/firmware/usbstack/usb_iap.c new file mode 100644 index 0000000000..3126ec6bc3 --- /dev/null +++ b/firmware/usbstack/usb_iap.c @@ -0,0 +1,679 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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 "audio.h" +#include "pcm_mixer.h" +#include "pcm_sink.h" +#include "playback.h" +#include "powermgmt.h" +#include "timefuncs.h" +#include "usb_drv.h" + +#include "iap/audio.h" +#include "iap/libiap/iap.h" +#include "iap/libiap/platform.h" +#include "iap/macros.h" +#include "iap/platform-macros.h" +#include "iap/platform.h" +#include "usb_audio_def.h" +#include "usb_class_driver.h" +#include "usb_hid_def.h" +#include "usb_iap.h" + +struct usb_class_driver_ep_allocation usb_iap_ep_allocs[2] = { + /* uac input */ + {.type = USB_ENDPOINT_XFER_ISOC, .dir = DIR_IN, .optional = false}, + /* hid input */ + {.type = USB_ENDPOINT_XFER_INT, .dir = DIR_IN, .optional = false}, +}; + +/* interface 0 (audio control) */ +static struct usb_interface_descriptor ipod_audio_control_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, +}; + +static struct usb_ac_header ipod_audio_control_uac_header = { + .bLength = USB_AC_SIZEOF_HEADER(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_HEADER, + .bcdADC = 0x0100, /* 1.00 */ + .wTotalLength = -1, /* dynamic */ + .bInCollection = 1, + .baInterfaceNr = {-1}, /* dynamic */ +}; + +static struct usb_ac_input_terminal ipod_audio_control_uac_input_terminal = { + .bLength = sizeof(struct usb_ac_input_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_INPUT_TERMINAL, + .bTerminalId = 1, + .wTerminalType = USB_AC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 2, /* ipod_audio_control_uac_output_terminal */ + .bNrChannels = 2, + .wChannelConfig = USB_AC_CHANNELS_LEFT_RIGHT_FRONT, +}; + +static struct usb_ac_output_terminal ipod_audio_control_uac_output_terminal = { + .bLength = sizeof(struct usb_ac_output_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_OUTPUT_TERMINAL, + .bTerminalId = 2, + .wTerminalType = USB_AC_TERMINAL_STREAMING, + .bAssocTerminal = 1, /* ipod_audio_control_uac_input_terminal */ + .bSourceId = 1, +}; + +/* interface 1 (audio stream) */ +/* interface 1 alt 0 */ +static struct usb_interface_descriptor ipod_audio_stream_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, +}; + +/* interface 1 alt 1 */ +static struct usb_interface_descriptor ipod_audio_stream_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, +}; + +static struct usb_as_interface ipod_audio_stream_1_uac_header = { + .bLength = sizeof(struct usb_as_interface), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_GENERAL, + .bTerminalLink = 2, /* ipod_audio_control_uac_output_terminal */ + .bDelay = 1, + .wFormatTag = USB_AS_FORMAT_TYPE_I_PCM, +}; + +/* TODO: remove unsupported freqs */ +static struct usb_as_format_type_i_discrete ipod_audio_stream_1_uac_discrete = { + .bLength = USB_AS_SIZEOF_FORMAT_TYPE_I_DISCRETE(9), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_FORMAT_TYPE, + .bFormatType = USB_AS_FORMAT_TYPE_I, + .bNrChannels = 2, + .bSubframeSize = 2, /* bBitResolution / 8 */ + .bBitResolution = 16, + .bSamFreqType = 9, + .tSamFreq = { + {0x40, 0x1F, 0x00}, /* 8000 */ + {0x11, 0x2B, 0x00}, /* 11025 */ + {0xE0, 0x2E, 0x00}, /* 12000 */ + {0x80, 0x3E, 0x00}, /* 16000 */ + {0x22, 0x56, 0x00}, /* 22050 */ + {0xC0, 0x5D, 0x00}, /* 24000 */ + {0x00, 0x7D, 0x00}, /* 32000 */ + {0x44, 0xAC, 0x00}, /* 44100 */ + {0x80, 0xBB, 0x00}, /* 48000 */ + }, +}; + +/* interface 1 endpoint 0 */ +static struct usb_as_iso_audio_endpoint ipod_audio_stream_1_endpoint = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = -1, /* dynamic */ + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = AS_PACKET_SIZE, + .bInterval = -1, /* dynamic */ + .bRefresh = 0, + .bSynchAddress = 0, +}; + +static struct usb_as_iso_ctrldata_endpoint ipod_audio_stream_1_endpoint_uac = { + .bLength = sizeof(struct usb_as_iso_ctrldata_endpoint), + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_AS_EP_GENERAL, + .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +/* interface 2 (hid) */ +static struct usb_interface_descriptor ipod_hid_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = -1, /* dynamic */ + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, +}; + +#define INPUT_REPORT(id, count) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x95, count, /* Report Count */ \ + 0x82, 0x02, 0x01 /* Input 0x0102 (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes) */ + +#define OUTPUT_REPORT(id, count) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x95, count, /* Report Count */ \ + 0x92, 0x02, 0x01 /* Output 0x0102 (...) */ + +#define INPUT_REPORT2(id, count1, count2) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x96, count1, count2, /* Report Count */ \ + 0x82, 0x02, 0x01 /* Input 0x0102 (...) */ + +#define OUTPUT_REPORT2(id, count1, count2) 0x09, 0x01, /* Usage 0x01 */ \ + 0x85, id, /* Report ID */ \ + 0x96, count1, count2, /* Report Count */ \ + 0x92, 0x02, 0x01 /* Output 0x0102 (...) */ + +// clang-format off +static const uint8_t ipod_hid_report_fs[] = { + 0x06, 0x00, 0xFF, /* Usage Page 0xFF00 (Vendor-defined) */ + 0x09, 0x01, /* Usage 0x01 */ + 0xA1, 0x01, /* Collection 0x01 (Application) */ + 0x75, 0x08, /* Report Size 0x08 */ + 0x26, 0x80, 0x00, /* Logical Maximum 0x0081 (128) */ + 0x15, 0x00, /* Logical Minumum 0x0000 (0) */ + + INPUT_REPORT(0x01, 0x0C), + INPUT_REPORT(0x02, 0x0E), + INPUT_REPORT(0x03, 0x14), + INPUT_REPORT(0x04, 0x3F), + + OUTPUT_REPORT(0x05, 0x08), + OUTPUT_REPORT(0x06, 0x0A), + OUTPUT_REPORT(0x07, 0x0E), + OUTPUT_REPORT(0x08, 0x14), + OUTPUT_REPORT(0x09, 0x3F), + + 0xC0, /* End Collection */ +}; + +static const uint8_t ipod_hid_report_hs[] = { + 0x06, 0x00, 0xFF, /* Usage Page 0xFF00 (Vendor-defined) */ + 0x09, 0x01, /* Usage 0x01 */ + 0xA1, 0x01, /* Collection 0x01 (Application) */ + 0x75, 0x08, /* Report Size 0x08 */ + 0x26, 0x80, 0x00, /* Logical Maximum 0x0081 (128) */ + 0x15, 0x00, /* Logical Minumum 0x0000 (0) */ + + INPUT_REPORT(0x01, 0x05), + INPUT_REPORT(0x02, 0x09), + INPUT_REPORT(0x03, 0x0D), + INPUT_REPORT(0x04, 0x11), + INPUT_REPORT(0x05, 0x19), + INPUT_REPORT(0x06, 0x31), + INPUT_REPORT(0x07, 0x5F), + INPUT_REPORT(0x08, 0xC1), + INPUT_REPORT2(0x09, 0x01, 0x01), + INPUT_REPORT2(0x0A, 0x81, 0x01), + INPUT_REPORT2(0x0B, 0x01, 0x02), + INPUT_REPORT2(0x0C, 0xFF, 0x02), + + OUTPUT_REPORT(0x0D, 0x05), + OUTPUT_REPORT(0x0E, 0x09), + OUTPUT_REPORT(0x1F, 0x0D), + OUTPUT_REPORT(0x10, 0x11), + OUTPUT_REPORT(0x11, 0x19), + OUTPUT_REPORT(0x12, 0x31), + OUTPUT_REPORT(0x13, 0x5F), + OUTPUT_REPORT(0x14, 0xC1), + OUTPUT_REPORT(0x15, 0xFF), + + 0xC0, /* End Collection */ +}; +// clang-format on + +static struct usb_hid_descriptor ipod_hid_hid_desc = { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_DT_HID, + .wBcdHID = 0x0111, /* 1.11 */ + .bCountryCode = 0, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DT_REPORT, + .wDescriptorLength0 = -1, /* dynamic */ +}; + +/* interface 2 endpoint 0 */ +static struct usb_endpoint_descriptor ipod_hid_endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = -1, /* dynamic */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = -1, /* dynamic */ + .bInterval = 1, +}; + +static struct { + int interface; +} ctrl; + +static struct { + uint32_t sample_rate; + int interface; + int8_t alt; +} stream; + +static struct { + int interface; +} hid; + +static struct mutex iap_ctx_mutex; +static struct Platform platform; +static struct timeout tick_tmo; +static bool iap_ctx_mutex_initialized = false; + +struct IAPContext* _iap_acquire_ctx(bool lock) { + static struct IAPContext ctx; + if(lock) { + mutex_lock(&iap_ctx_mutex); + } + return &ctx; +} + +void _iap_release_ctx() { + mutex_unlock(&iap_ctx_mutex); +} + +bool iap_initialized; + +/* those notifications need pollling */ +static enum charge_state_type last_charge_state; +static uint8_t last_battery_level; +static int8_t last_minute; +static int8_t last_hold_switch_state; + +enum Notify { + Notify_Tick, +}; + +static int tick_callback(struct timeout* tmo) { + (void)tmo; + usb_signal_class_notify(USB_DRIVER_IAP, Notify_Tick); + return HZ / 10; +} + +int usb_iap_set_first_interface(int interface) { + ctrl.interface = interface + 0; + stream.interface = interface + 1; + hid.interface = interface + 2; + return interface + 3; +} + +#define PACK_DESC(desc) pack_data(&dest, &desc, ((struct usb_descriptor_header*)&desc)->bLength) + +int usb_iap_get_config_descriptor(unsigned char* dest, int max_packet_size) { + (void)max_packet_size; + + unsigned char* orig_dest = dest; + + ipod_audio_control_desc.bInterfaceNumber = ctrl.interface; + PACK_DESC(ipod_audio_control_desc); + ipod_audio_control_uac_header.baInterfaceNr[0] = stream.interface; + ipod_audio_control_uac_header.wTotalLength = + sizeof(ipod_audio_control_uac_header) + + sizeof(ipod_audio_control_uac_input_terminal) + + sizeof(ipod_audio_control_uac_output_terminal); + PACK_DESC(ipod_audio_control_uac_header); + PACK_DESC(ipod_audio_control_uac_input_terminal); + PACK_DESC(ipod_audio_control_uac_output_terminal); + + ipod_audio_stream_0_desc.bInterfaceNumber = stream.interface; + ipod_audio_stream_1_desc.bInterfaceNumber = stream.interface; + ipod_audio_stream_1_endpoint.bEndpointAddress = AS_EP_IN; + ipod_audio_stream_1_endpoint.bInterval = usb_drv_port_speed() ? 4 : 1; + PACK_DESC(ipod_audio_stream_0_desc); + PACK_DESC(ipod_audio_stream_1_desc); + PACK_DESC(ipod_audio_stream_1_uac_header); + PACK_DESC(ipod_audio_stream_1_uac_discrete); + PACK_DESC(ipod_audio_stream_1_endpoint); + PACK_DESC(ipod_audio_stream_1_endpoint_uac); + + ipod_hid_desc.bInterfaceNumber = hid.interface; + ipod_hid_endpoint.bEndpointAddress = HID_EP_IN; + if(usb_drv_port_speed()) { + ipod_hid_hid_desc.wDescriptorLength0 = sizeof(ipod_hid_report_hs); + ipod_hid_endpoint.wMaxPacketSize = 64; + } else { + ipod_hid_hid_desc.wDescriptorLength0 = sizeof(ipod_hid_report_fs); + ipod_hid_endpoint.wMaxPacketSize = 64; + } + PACK_DESC(ipod_hid_desc); + PACK_DESC(ipod_hid_hid_desc); + PACK_DESC(ipod_hid_endpoint); + + return dest - orig_dest; +} + +void usb_iap_init_connection(void) { + stream.sample_rate = 48000; + last_charge_state = -1; + last_minute = -1; + last_hold_switch_state = -1; + + iap_debug_reset_timestamp(); + + /* TODO: disable iap on error */ + + /* init audio sink */ + check_act(iap_audio_init(), return); + + /* init libiap */ + if(!iap_ctx_mutex_initialized) { + iap_ctx_mutex_initialized = true; + mutex_init(&iap_ctx_mutex); + } + + struct IAPContext* ctx = _iap_acquire_ctx(true); + + const struct IAPOpts opts = { + .usb_highspeed = usb_drv_port_speed(), + .ignore_hid_report_id = iap_true, + .artwork_single_report = iap_false, + .enable_packet_dump = iap_false, + }; + check_act(iap_init_ctx(ctx, opts, &platform), goto cleanup_audio); + _iap_release_ctx(); + + /* prepare artwork */ + struct dim dim = {IAP_ARTWORK_WIDTH, IAP_ARTWORK_HEIGHT}; + platform.aa_slot = playback_claim_aa_slot(&dim); + if(platform.aa_slot < 0) { + ERROR("failed to claim albumart slot"); + } + platform.control_pending = false; + + /* register timer */ + timeout_register(&tick_tmo, tick_callback, HZ / 10, 0); + + iap_initialized = true; + LOG("initialized"); + return; + +cleanup_audio: + _iap_release_ctx(); + iap_audio_deinit(); +} + +int usb_iap_set_interface(int intf, int alt) { + LOG("set interface interface=%d alt=%d", intf, alt); + check_act(intf == stream.interface, return -1); + if(alt == 0) { + check_act(iap_audio_disable(), return -1); + } else if(alt == 1) { + check_act(iap_audio_enable(), return -1); + } else { + ERROR("invalid alt %d", alt); + return -1; + } + return 0; +} + +int usb_iap_get_interface(int intf) { + LOG("get interface interface=%d", intf); + check_act(intf == stream.interface, return -1); + return stream.alt; +} + +void usb_iap_init(void) { + LOG("init"); +} + +void usb_iap_disconnect(void) { + iap_initialized = false; + audio_pause(); + mixer_switch_sink(PCM_SINK_BUILTIN); + timeout_cancel(&tick_tmo); + if(platform.aa_slot >= 0) { + playback_release_aa_slot(platform.aa_slot); + } + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_deinit_ctx(ctx), ); + _iap_release_ctx(); + check_act(iap_audio_deinit(), ); + LOG("disconnected"); +} + +void usb_iap_transfer_complete(int ep, int dir, int status, int length) { + (void)length; + + if((ep | dir) == HID_EP_IN) { + check_act(status == 0, return); +#if DEBUG_DUMP_TX + LOG("ep=%d dir=%d state=%d length=%d", ep, dir, status, length); +#endif + struct IAPContext* ctx = _iap_acquire_ctx(true); + check_act(iap_notify_send_complete(ctx), ); + _iap_release_ctx(); + } +} + +bool usb_iap_fast_transfer_complete(int ep, int dir, int status, int length) { + (void)status; + (void)length; + return (ep | dir) == AS_EP_IN; +} + +static unsigned char ctrl_buf[256] USB_DEVBSS_ATTR; + +static void respond_zero(struct usb_ctrlrequest* req) { + if(req->wLength > sizeof(ctrl_buf)) { + ERROR("required data too long %u > %u", req->wLength, sizeof(ctrl_buf)); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + } else { + memset(ctrl_buf, 0, req->wLength); + usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength); + } +} + +/* returns true when ctrl_buf has received data */ +static bool receive_data(struct usb_ctrlrequest* req, void* reqdata) { + if(reqdata == NULL) { + /* setup */ + if(req->wLength > sizeof(ctrl_buf)) { + ERROR("parameter too long"); + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + } else { + usb_drv_control_response(USB_CONTROL_RECEIVE, ctrl_buf, req->wLength); + } + return false; + } else { + /* data */ + return true; + } +} + +static bool control_request_if_std(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)reqdata; + + unsigned char* const orig_dest = dest; + switch(req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: { + const uint8_t desc_type = req->wValue >> 8; + const uint8_t desc_index = req->wValue & 0xff; + LOG("descriptor request type=%x index=%x", desc_type, desc_index); + (void)desc_index; + switch(desc_type) { + case USB_DT_HID: + PACK_DATA(&dest, ipod_hid_hid_desc); + break; + case USB_DT_REPORT: + if(usb_drv_port_speed()) { + PACK_DATA(&dest, ipod_hid_report_hs); + } else { + PACK_DATA(&dest, ipod_hid_report_fs); + } + break; + } + if(dest != orig_dest) { + usb_drv_control_response(USB_CONTROL_ACK, orig_dest, MIN(dest - orig_dest, req->wLength)); + return true; + } + } break; + } + return false; +} + +static bool control_request_if_class(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)dest; + + const uint8_t recip_interface = req->wIndex & 0xff; + if(recip_interface == hid.interface) { + switch(req->bRequest) { + case USB_HID_GET_REPORT: + respond_zero(req); + return true; + case USB_HID_SET_REPORT: { + if(!receive_data(req, reqdata)) { + return true; + } +#if DEBUG_DUMP_RX == 1 + logf("==== acc: %u bytes ====", req->wLength); + iap_platform_dump_hex(reqdata, req->wLength); +#endif + + struct IAPContext* ctx = _iap_acquire_ctx(true); + const bool ret = iap_feed_hid_report(ctx, reqdata, req->wLength); + _iap_release_ctx(); + + check_act(ret, return false); + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + } + case USB_HID_SET_IDLE: + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + } + } + return false; +} + +static bool control_request_if_endpoint(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + (void)dest; + + LOG("ctrl to endpoint %x (stream=%x, hid=%x)", req->wIndex, AS_EP_IN, HID_EP_IN); + if(req->wIndex == AS_EP_IN) { + const uint8_t recip_entity = req->wIndex >> 8; + const uint8_t control_selector = req->wValue >> 8; + (void)recip_entity; + switch(req->bRequest) { + case USB_AC_SET_CUR: + if(!receive_data(req, reqdata)) { + return true; + } + LOG("audio ctrl to stream endpoint entity=0x%02X request=0x%02X length=%u", recip_entity, req->bRequest, req->wLength); + switch(control_selector) { + case USB_AS_EP_CS_SAMPLING_FREQ_CTL: + check_act(req->wLength == 3, goto stall); + stream.sample_rate = ctrl_buf[0] | (ctrl_buf[1] << 8) | (ctrl_buf[2] << 16); + LOG("audio stream sampling rate %lu", stream.sample_rate); + check_act(iap_audio_set_sampr(stream.sample_rate), goto stall); + break; + } + usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); + return true; + case USB_AC_GET_CUR: + switch(control_selector) { + case USB_AS_EP_CS_SAMPLING_FREQ_CTL: + check_act(req->wLength == 3, goto stall); + ctrl_buf[2] = (stream.sample_rate >> 16) & 0xff; + ctrl_buf[1] = (stream.sample_rate >> 8) & 0xff; + ctrl_buf[0] = (stream.sample_rate & 0xff); + usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength); + return true; + } + /* fallthrough */ + case USB_AC_GET_MIN: + case USB_AC_GET_MAX: + case USB_AC_GET_RES: + respond_zero(req); + return true; + stall: + usb_drv_control_response(USB_CONTROL_STALL, NULL, 0); + return true; + } + } + return false; +} + +bool usb_iap_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) { + const uint8_t req_recipient = req->bRequestType & USB_RECIP_MASK; + const uint8_t req_type = req->bRequestType & USB_TYPE_MASK; +#if 0 + LOG("bRequestType=%x, bRequest=%x, wValue=%x, wIndex=%x, wLength=%x", req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength); + LOG("recip=%x type=%x", req_recipient, req_type); +#endif + if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_STANDARD) { + return control_request_if_std(req, reqdata, dest); + } else if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_CLASS) { + return control_request_if_class(req, reqdata, dest); + } else if(req_recipient == USB_RECIP_ENDPOINT && req_type == USB_TYPE_CLASS) { + return control_request_if_endpoint(req, reqdata, dest); + } + return false; +} + +void usb_iap_notify_event(intptr_t data) { + switch(data) { + case Notify_Tick: { + struct IAPContext* ctx = _iap_acquire_ctx(true); + struct Platform* plt = ctx->platform; + if(plt->control_pending) { + /* waiting for playback begins */ + _iap_release_ctx(); + return; + } + + if(last_charge_state < 0 || last_charge_state != charge_state || last_battery_level != battery_level()) { + last_charge_state = charge_state; + last_battery_level = battery_level(); + iap_notify_power_state(ctx, _iap_convert_charge_status(last_charge_state), _iap_convert_battery_level(last_battery_level)); + } + + struct tm* tm = get_time(); + if(last_minute == -1 || last_minute != tm->tm_min) { + last_minute = tm->tm_min; + struct IAPDateTime time; + _iap_convert_datetime(get_time(), &time); + iap_notify_time_setting(ctx, &time); + } + +#ifdef HAS_BUTTON_HOLD + int8_t hold = button_hold() ? 1 : 0; + if(hold != last_hold_switch_state) { + last_hold_switch_state = hold; + iap_notify_hold_switch_state(ctx, last_hold_switch_state); + } +#endif + + check_act(iap_periodic_tick(ctx), ); + _iap_release_ctx(); + } break; + } +} diff --git a/firmware/usbstack/usb_iap.h b/firmware/usbstack/usb_iap.h new file mode 100644 index 0000000000..8825d56514 --- /dev/null +++ b/firmware/usbstack/usb_iap.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 by Sho Tanimoto + * + * 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. + * + ****************************************************************************/ +#pragma once +#include "usb_core.h" +#include "usb_class_driver.h" + +extern struct usb_class_driver_ep_allocation usb_iap_ep_allocs[2]; + +int usb_iap_request_endpoints(struct usb_class_driver*); +int usb_iap_set_first_interface(int interface); +int usb_iap_get_config_descriptor(unsigned char* dest, int max_packet_size); +void usb_iap_init_connection(void); +bool usb_iap_set_alt_interface(int interface, int alt); +int usb_iap_get_alt_interface(int interface); +void usb_iap_init(void); +void usb_iap_disconnect(void); +void usb_iap_transfer_complete(int ep, int dir, int state, int length); +bool usb_iap_fast_transfer_complete(int ep, int dir, int status, int length); +bool usb_iap_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest); +int usb_iap_set_interface(int intf, int alt); +int usb_iap_get_interface(int intf); +void usb_iap_notify_event(intptr_t data);