68 Commits

Author SHA1 Message Date
Konsta
f419728867 mkimg: adjust flashable image size
* It's been reported that 14848MiB = 15569256448 bytes is still too
  large to fit some 16GB storage devices.
* 15360000000 bytes is dividable by 16384, 4096, 2048, 1024, 512, ...
* Fixes 60497874a8.
2025-06-08 12:28:42 +03:00
Konsta
971d5b6c09 mkimg: detach loop device 2025-06-08 12:28:34 +03:00
Konsta
a567111d10 wrimg: add more block devices to search 2025-06-08 12:28:26 +03:00
Konsta
30ed6bc9d9 increase system partition size
* Make room for unspecified add-ons.
2025-05-08 15:55:37 +03:00
Konsta
0a545bee4d camera: remove unneeded media profiles property
* This hasn't been needed in years.
  72ad8cb0ae
2025-05-08 15:55:02 +03:00
Konsta
641342d40d wrimg: add script to write partition images
* Can be used to write partition images to a storage device that has the
  correct partition structure after flashing an image created by mkimg.sh.
* Poor man's alternative to
  fastboot flash boot boot.img
  fastboot flash system system.img
  fastboot flash vendor vendor.img
  fastboot erase userdata
2025-04-04 15:31:34 +03:00
Konsta
f73605e466 mkimg: improve error message 2025-04-04 15:31:34 +03:00
Konsta
ad8d8e119f wifi: overlay: enable background scan 2025-04-04 15:31:34 +03:00
Konsta
981d1a86ed wifi: overlay: set default name for hotspot 2025-04-04 15:31:34 +03:00
Konsta
374fd0a10f bluetooth: set default device name
* Set default device name for bluetooth.
* I still prefer this displayed as 'Raspberry Pi 5' after commit
  acfacd0969.
2025-04-04 15:31:34 +03:00
Konsta
a10139112e overlay: set default device name
* Set default device name displayed in settings menu.
* I still prefer this displayed as 'Raspberry Pi 5' after commit
  acfacd0969.
2025-04-04 15:31:34 +03:00
Konsta
24261912e5 ramdisk: remove ion 2025-04-04 15:31:34 +03:00
Konsta
2d13fe7084 audio: auto probe pcm card for jack & dac
* When 'jack' is selected using the property, first PCM card that has name
  'Headphones' is used.
  When 'dac' is selected, first PCM card that is not named 'Headphones',
  'vc4hdmi0', or 'vc4hdmi1' is used.
2025-04-04 15:31:34 +03:00
Konsta
503bafe5fc audio: cache pcm card and device
* It's always been intended that changing audio output devices requires
  a reboot. Get the PCM card and device once when the HAL is initialized.
  Might save a few ms on start_output_stream.
2025-04-04 15:31:34 +03:00
Konsta
ca32aa9f6a hdmi audio: cache alsa device
* It's always been intended that changing audio output devices requires
  a reboot. Get the ALSA device once when the HAL is initialized.
  Might save a few ms on start_output_stream.
2025-04-04 15:31:34 +03:00
Konsta
f194ef56dc boot: remove start_x
* On Pi 5 firmware is self-contained in the bootloader EEPROM. There's no
  extended firmware files.
2025-04-04 15:31:34 +03:00
Konsta
e42d7c2c85 add chipset properties
* This became needed for CTS some years ago.
  faa63a37c0
2025-04-04 15:31:34 +03:00
Konsta
336b3acbd3 sepolicy: various fixes for graphics 2025-04-04 15:31:34 +03:00
Konsta
f1b945544c sepolicy: add more drm devices
* Pi 5 has independent cards for DSI-0, DSI-1, DPI, and VEC.
  In theory we could have two DSI displays so add two more entries.

avc:  denied  { ioctl } for  path="/dev/dri/card2" dev="tmpfs" ino=749 ioctlcmd=0x64bc scontext=u:r:hal_graphics_composer_default:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=1
2025-04-04 15:31:34 +03:00
Konsta
803dff31a4 bluetooth: remove unused BDROID_BUILDCFG header
* Doesn't do anything if BOARD_HAVE_BLUETOOTH_BCM is not set.
* Bluetooth device name is parsed from 'ro.product.model' if no default
  is provided using 'bluetooth.device.default_name' property.
2025-04-04 15:31:34 +03:00
Konsta
ea4a9de2a1 set TARGET_BOOTLOADER_BOARD_NAME
* Populate 'ro.product.board' as well.
* Fixes 1bd1c53659.
2025-04-04 15:31:34 +03:00
Konsta
85f0c2e7df Revert "car: Disabled BT MAP causing cyclic BT reconnect"
* This should be enabled on automotive targets and I can't reprocude any
  connection issues described in https://github.com/raspberry-vanilla/android_local_manifest/issues/33.
* Partially fixes https://github.com/raspberry-vanilla/android_local_manifest/issues/110.

This reverts commit c724e60c78.
2025-04-04 15:31:34 +03:00
Konsta
1d0fbde1c2 car: device path has car directory
* Fixes c2277d28e8.
2025-04-04 15:31:34 +03:00
Konsta
adcc7decfb cleanup device identifiers
* Drop Raspberry from model as some hardware information applications
  display it after the manufacturer/brand and we'll end up with duplicate
  Raspberry in the device name.
* PRODUCT_RELEASE_NAME is long gone.
2025-04-04 15:31:34 +03:00
Konsta
fb4ea86709 set hardware platform name
* Some hardware information applications use this so set actual hardware
  platform name.
* Historic reason why this was set to rpi is that this allowed to use same
  HALs on rpi3/rpi4 when they were commonized. 'ro.board.platform' this sets
  is used in the legacy module load order.
  https://android.googlesource.com/platform/hardware/libhardware/+/refs/tags/android-15.0.0_r20/modules/README.android
  This has no use with modern HIDL/AIDL/APEX HALs. For legacy HALs name can
  be implicitly set using system properties (e.g. 'ro.hardware.audio.primary'
  that's still relevant).
2025-04-04 15:31:34 +03:00
Konsta
c90767f8ad reorganize board config 2025-04-04 15:31:34 +03:00
Konsta
489b0dd7e1 treble: remove unneeded flags
* PRODUCT_FULL_TREBLE gets set on PRODUCT_SHIPPING_API_LEVEL >= 26.
  There's no need to override it.
  2b32469c47
* BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED gets set with PRODUCT_FULL_TREBLE.
  c227ce7c25
2025-04-04 15:31:34 +03:00
Konsta
72c361529a sepolicy: remove libglapi
* Fixes 63fb691e8f.
2025-04-04 15:31:34 +03:00
Konsta
c197b6e434 Revert "overlay: disable screen dimming by default"
* In practice, doesn't disable dimming.

This reverts commit 9d51fc04b6.
2025-04-04 15:31:34 +03:00
Konsta
f59051b83c suspend_blocker: move service to init fragment 2025-04-04 15:31:34 +03:00
Konsta
60ce02b873 memory: remove lmkd properties
* These were added years ago when userspace low memory killer daemon
  was introduced. For most part these match the defaults and ones that
  dont't, shouldn't be set on Android R and above.
  https://android.googlesource.com/platform/system/memory/lmkd/+/refs/tags/android-15.0.0_r20/README.md
  cc5d2cca25
2025-04-04 15:31:34 +03:00
Konsta
3529558f7e virtualization: report vm as supported
* 8c352a4c5c
* Fixes dc1fc3a6e3.
2025-04-04 15:31:34 +03:00
Konsta
800892454f add touchscreen permission 2025-04-04 15:31:34 +03:00
Konsta
179295783b Revert "car: BT AVRCP target enabled"
* AVRCP target should be enabled with A2DP source and
  AVRCP controller should be enabled with A2DP sink.

This reverts commit cf4022a694.
2025-03-03 17:08:14 +02:00
Konsta
63fb691e8f graphics: remove libglapi from mesa packages
* No longer exists on Mesa 25.0.
2025-03-03 17:07:30 +02:00
Konsta
6ccded2c27 graphics: disable hwc config groups
* 613a9440a0.
2025-03-03 17:07:21 +02:00
Konsta
b2699d4cda audio: proprietary -> vendor 2025-03-03 17:07:09 +02:00
Konsta
84a9f581c0 suspend_blocker: proprietary -> vendor 2025-03-03 17:06:52 +02:00
Konsta
e995a55705 overlay: proprietary -> vendor 2025-03-03 17:06:38 +02:00
Konsta
934d780093 overlay: tv: remove minimum screenoff timeout
* Fixes screen saver on Android TV.
* IIRC this is an old hack when Android TV didn't respect stay awake
  option. Probably hasn't been needed in years.
2025-03-03 17:06:14 +02:00
Konsta
9d51fc04b6 overlay: disable screen dimming by default 2025-03-03 17:06:07 +02:00
Konsta
6be4c4397c health: fake more battery stats
Change-Id: Ic511604c9ed5e5972af34ef0afb120affacd0586
2025-03-03 17:05:57 +02:00
Konsta
8908ca4289 cec: add rpi hal implementation
* TODO: Convert to AIDL.
2025-03-03 17:05:41 +02:00
Konsta
d4bc9c6804 cec: remove mock hal 2025-03-03 17:05:13 +02:00
Konsta
72fbf40c67 cec: copy hidl hal
* Copy from hardware/interfaces/tv/cec/1.0/default
  at b04e2f3df5ebbbeea46f555d0965357f05aa1457.
2025-03-03 17:05:06 +02:00
Konsta
67ee38657f cec: remove legacy hal
This reverts commit c0a8378d8b and more.
2025-03-03 17:04:46 +02:00
Konsta
60497874a8 mkimg: increase image size to fit 16GB storage device
* This will create a flashable image that is 15569256448 bytes.
* I checked several 16GB sdcards and USB storage devices and the
  actual byte size ranged from 15646851072 to 16008609792.
2025-03-03 17:03:01 +02:00
Konsta
b9cf3a20dc mkimg: use fallocate to create the image
* This is much faster than creating a file by filling it with zeroes.
* Remove unnecessary quotes for consistency.
2025-03-03 17:01:39 +02:00
Konsta
96ef319127 mkimg: set image owner 2025-01-30 20:06:19 +02:00
Konsta
40fa313664 mkimg: minor cleanups 2025-01-30 20:06:19 +02:00
Konsta
b654f2f634 mkimg: check partition images exist 2025-01-30 18:51:19 +02:00
Konsta
4353b21505 mkimg: strip aosp_ from build target 2025-01-30 18:51:19 +02:00
MinnieTheMoocher
c8cff6403c mkimg: use existing env variables instead of hardcoded strings
* this requires to run "lunch" before, the script checks for that
2025-01-30 18:51:19 +02:00
Konsta
971e4f5a25 graphics: add libgbm_mesa to mesa packages
* This gets built as a dependency to minigbm gbm_mesa_driver's
  libgbm_mesa_wrapper but add it here for completeness.
* Move Mesa gbm libraries under separate section.
2024-12-05 19:56:03 +02:00
Konsta
72705008ca sepolicy: update mesa libraries
* Adapt to changes in Mesa 24.2/24.3.
2024-12-05 19:56:03 +02:00
Konsta
1f09347814 usb: convert gadget hal to aidl
* Based on hardware/interfaces/usb/gadget/aidl/default.
2024-11-22 15:49:19 +02:00
Konsta
9da660bd44 usb: add gadget lib
* Copy from hardware/interfaces/usb/gadget/1.2/default.
2024-11-22 15:49:19 +02:00
Konsta
4d4fba6638 graphics: add dri_gbm to mesa packages
* Needed with Mesa 24.3.
  15bea329d7
  514df444eb
2024-11-22 15:43:22 +02:00
Konsta
03beac99ad Revert "seccomp_policy: mediaswcodec: allow mkdirat"
* No longer needed with 8a5dd4d00a.

This reverts commit 32db92738a.
2024-11-22 13:08:02 +02:00
Oleg Lyovin
e715baa749 CEC: fix use-after-free in hdmicec_close
Fields of 'ctx' are accessed just after it is freed.

Test: manual
Change-Id: I7c3786db9ae618d84149874c72662e4d105765b4
Signed-off-by: Oleg Lyovin <ovlevin@salutedevices.com>
2024-11-22 13:07:48 +02:00
Oleg Lyovin
63d3f1ddb8 CEC: do not join NULL thread
'hdmicec_close' may be called after exit_fd initialized,
but before ctx->thread created.

This patch checks it for NULL before join.

Test: manual
Change-Id: I1c57be56f2cd9956044d9a48fea914b1c1fd0132
Signed-off-by: Oleg Lyovin <ovlevin@salutedevices.com>
2024-11-22 13:07:48 +02:00
Konsta
a32b0dc3d1 audio: minor code readability and formatting fixes
* Fixes 0205059ddb.
2024-11-22 13:07:48 +02:00
Konsta
be24cefd65 car: show instrument cluster on secondary display
* Fixes https://github.com/raspberry-vanilla/android_local_manifest/issues/43
2024-09-07 13:29:47 +03:00
Konsta
35895e0978 car: build can bus hal and debug tools 2024-09-07 13:29:47 +03:00
Konsta
0ab313fa7a car: build occupant awareness hal 2024-09-07 13:29:47 +03:00
Konsta
a00cc82199 car: build audio control hal 2024-09-07 13:29:47 +03:00
Konsta
e07c42997a car: switch to aidl vehicle hal 2024-09-07 13:29:47 +03:00
Konsta
ef50160a6c car: move automotive related configs to separate directory 2024-09-07 13:29:47 +03:00
63 changed files with 2153 additions and 962 deletions

View File

@@ -6,12 +6,7 @@
DEVICE_PATH := device/brcm/rpi5
# Platform
TARGET_NO_BOOTLOADER := true
TARGET_NO_RECOVERY := true
TARGET_BOARD_PLATFORM := rpi
# Architecture
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_ABI := arm64-v8a
@@ -25,9 +20,11 @@ TARGET_2ND_CPU_ABI2 := armeabi
TARGET_2ND_CPU_VARIANT := generic
# Bluetooth
BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := $(DEVICE_PATH)/bluetooth
BOARD_HAVE_BLUETOOTH := true
# Bootloader
TARGET_NO_BOOTLOADER := true
# Camera
BOARD_LIBCAMERA_IPAS := rpi/pisp
BOARD_LIBCAMERA_PIPELINES := rpi/pisp
@@ -55,26 +52,34 @@ DEVICE_MATRIX_FILE := $(DEVICE_PATH)/compatibility_matrix.xml
# Partition sizes
BOARD_FLASH_BLOCK_SIZE := 4096
BOARD_BOOTIMAGE_PARTITION_SIZE := 134217728 # 128M
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2147483648 # 2048M
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 2684354560 # 2560M
BOARD_USERDATAIMAGE_PARTITION_SIZE := 134217728 # 128M
BOARD_VENDORIMAGE_PARTITION_SIZE := 268435456 # 256M
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true
TARGET_USERIMAGES_USE_EXT4 := true
# Platform
TARGET_BOARD_PLATFORM := bcm2712
TARGET_BOOTLOADER_BOARD_NAME := bcm2712
# Properties
TARGET_VENDOR_PROP += $(DEVICE_PATH)/vendor.prop
# Recovery
TARGET_NO_RECOVERY := true
# SELinux
BOARD_SEPOLICY_DIRS += device/brcm/rpi5/sepolicy
BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
# Treble
BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED := true
BOARD_VNDK_VERSION := current
PRODUCT_FULL_TREBLE_OVERRIDE := true
TARGET_COPY_OUT_VENDOR := vendor
# Virtualization
BOARD_KERNEL_CMDLINE += androidboot.hypervisor.vm.supported=1
# Wifi
BOARD_WLAN_DEVICE := bcmdhd
BOARD_HOSTAPD_DRIVER := NL80211

View File

@@ -28,6 +28,5 @@ PRODUCT_COPY_FILES += \
PRODUCT_DEVICE := rpi5
PRODUCT_NAME := aosp_rpi5
PRODUCT_BRAND := Raspberry
PRODUCT_MODEL := Raspberry Pi 5
PRODUCT_MODEL := Pi 5
PRODUCT_MANUFACTURER := Raspberry
PRODUCT_RELEASE_NAME := Raspberry Pi 5

View File

@@ -7,17 +7,27 @@
# Inherit device configuration
$(call inherit-product, device/brcm/rpi5/device.mk)
DEVICE_PATH_CAR := device/brcm/rpi5/car
PRODUCT_AAPT_CONFIG := normal mdpi hdpi
PRODUCT_AAPT_PREF_CONFIG := hdpi
PRODUCT_CHARACTERISTICS := automotive,nosdcard
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk)
$(call inherit-product, packages/services/Car/car_product/build/car.mk)
# Audio
PRODUCT_PACKAGES += \
android.hardware.automotive.audiocontrol-service.example
PRODUCT_COPY_FILES += \
$(DEVICE_PATH_CAR)/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml
# Bluetooth
PRODUCT_VENDOR_PROPERTIES += \
bluetooth.device.class_of_device=38,4,8 \
bluetooth.profile.a2dp.source.enabled=false \
bluetooth.profile.asha.central.enabled=false \
bluetooth.profile.avrcp.target.enabled=false \
bluetooth.profile.bap.broadcast.assist.enabled=false \
bluetooth.profile.bap.unicast.client.enabled=false \
bluetooth.profile.bas.client.enabled=false \
@@ -27,7 +37,6 @@ PRODUCT_VENDOR_PROPERTIES += \
bluetooth.profile.hfp.ag.enabled=false \
bluetooth.profile.hid.device.enabled=false \
bluetooth.profile.hid.host.enabled=false \
bluetooth.profile.map.client.enabled=false \
bluetooth.profile.map.server.enabled=false \
bluetooth.profile.mcp.server.enabled=false \
bluetooth.profile.opp.enabled=false \
@@ -45,6 +54,19 @@ PRODUCT_COPY_FILES += \
# Camera
ENABLE_CAMERA_SERVICE := true
# CAN
PRODUCT_PACKAGES += \
android.hardware.automotive.can-service
PRODUCT_PACKAGES += \
canhalctrl \
canhaldump \
canhalsend
# Display
PRODUCT_COPY_FILES += \
$(DEVICE_PATH_CAR)/display_settings.xml:$(TARGET_COPY_OUT_VENDOR)/etc/display_settings.xml
# EVS
ENABLE_CAREVSSERVICE_SAMPLE := true
ENABLE_EVS_SAMPLE := true
@@ -52,7 +74,13 @@ ENABLE_EVS_SERVICE := true
ENABLE_REAR_VIEW_CAMERA_SAMPLE := true
PRODUCT_COPY_FILES += \
device/brcm/rpi5/camera/evs_config_override.json:${TARGET_COPY_OUT_VENDOR}/etc/automotive/evs/config_override.json
$(DEVICE_PATH_CAR)/evs_config_override.json:${TARGET_COPY_OUT_VENDOR}/etc/automotive/evs/config_override.json
# Occupant awareness
PRODUCT_PACKAGES += \
android.hardware.automotive.occupant_awareness@1.0-service
include packages/services/Car/car_product/occupant_awareness/OccupantAwareness.mk
# Overlays
PRODUCT_PACKAGES += \
@@ -68,12 +96,11 @@ PRODUCT_COPY_FILES += \
# Vehicle
PRODUCT_PACKAGES += \
android.hardware.automotive.vehicle@2.0-default-service
android.hardware.automotive.vehicle@V3-default-service
# Device identifier. This must come after all inclusions.
PRODUCT_DEVICE := rpi5
PRODUCT_NAME := aosp_rpi5_car
PRODUCT_BRAND := Raspberry
PRODUCT_MODEL := Raspberry Pi 5
PRODUCT_MODEL := Pi 5
PRODUCT_MANUFACTURER := Raspberry
PRODUCT_RELEASE_NAME := Raspberry Pi 5

View File

@@ -37,6 +37,5 @@ PRODUCT_PACKAGES += \
PRODUCT_DEVICE := rpi5
PRODUCT_NAME := aosp_rpi5_tv
PRODUCT_BRAND := Raspberry
PRODUCT_MODEL := Raspberry Pi 5
PRODUCT_MODEL := Pi 5
PRODUCT_MANUFACTURER := Raspberry
PRODUCT_RELEASE_NAME := Raspberry Pi 5

View File

@@ -6,7 +6,7 @@
cc_library_shared {
name: "audio.primary.rpi",
relative_install_path: "hw",
proprietary: true,
vendor: true,
srcs: ["audio_hw.c"],
include_dirs: [
"external/expat/lib",
@@ -26,7 +26,7 @@ cc_library_shared {
cc_library_shared {
name: "audio.primary.rpi_hdmi",
relative_install_path: "hw",
proprietary: true,
vendor: true,
srcs: ["audio_hw_hdmi.c"],
include_dirs: [
"external/expat/lib",

View File

@@ -57,6 +57,9 @@
#define CHANNEL_STEREO 2
#define MIN_WRITE_SLEEP_US 5000
int pcm_card;
int pcm_device;
struct stub_stream_in {
struct audio_stream_in stream;
};
@@ -86,14 +89,14 @@ struct alsa_stream_out {
static int probe_pcm_out_card() {
FILE *fp;
char card_node[] = "/proc/asound/card0/id";
char card_node[32];
char card_id[16];
char card_prop[PROPERTY_VALUE_MAX];
property_get("persist.audio.device", card_prop, "");
for (int i = 0; i < 5; i++) {
card_node[17] = i + '0';
snprintf(card_node, sizeof(card_node), "/proc/asound/card%d/id", i);
if ((fp = fopen(card_node, "r")) != NULL) {
fgets(card_id, sizeof(card_id), fp);
ALOGV("%s: %s", card_node, card_id);
@@ -148,7 +151,7 @@ static int start_output_stream(struct alsa_stream_out *out)
out->config.start_threshold = PLAYBACK_PERIOD_START_THRESHOLD * PERIOD_SIZE;
out->config.avail_min = PERIOD_SIZE;
out->pcm = pcm_open(get_pcm_card(), get_pcm_device(), PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC, &out->config);
out->pcm = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC, &out->config);
if (!pcm_is_ready(out->pcm)) {
ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm));
@@ -346,17 +349,17 @@ static int out_get_presentation_position(const struct audio_stream_out *stream,
struct alsa_stream_out *out = (struct alsa_stream_out *)stream;
int ret = -1;
if (out->pcm) {
unsigned int avail;
if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
int64_t signed_frames = out->written - kernel_buffer_size + avail;
if (signed_frames >= 0) {
*frames = signed_frames;
ret = 0;
}
if (out->pcm) {
unsigned int avail;
if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
int64_t signed_frames = out->written - kernel_buffer_size + avail;
if (signed_frames >= 0) {
*frames = signed_frames;
ret = 0;
}
}
}
return ret;
}
@@ -484,7 +487,7 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
struct pcm_params *params;
int ret = 0;
params = pcm_params_get(get_pcm_card(), get_pcm_device(), PCM_OUT);
params = pcm_params_get(pcm_card, pcm_device, PCM_OUT);
if (!params)
return -ENOSYS;
@@ -690,6 +693,10 @@ static int adev_open(const hw_module_t* module, const char* name,
ALOGV("adev_open: %s", name);
pcm_card = get_pcm_card();
pcm_device = get_pcm_device();
ALOGI("adev_open: pcm_card %d, pcm_device %d", pcm_card, pcm_device);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;

View File

@@ -55,6 +55,8 @@
#define CHANNEL_STEREO 2
#define MIN_WRITE_SLEEP_US 5000
char device_name[PROPERTY_VALUE_MAX];
struct stub_stream_in {
struct audio_stream_in stream;
};
@@ -101,8 +103,6 @@ static int start_output_stream(struct alsa_stream_out *out)
if (out->unavailable)
return -ENODEV;
char device_name[PROPERTY_VALUE_MAX];
get_alsa_device_name(device_name);
ALOGI("start_output_stream: %s", device_name);
int r;
@@ -717,6 +717,9 @@ static int adev_open(const hw_module_t* module, const char* name,
ALOGV("adev_open: %s", name);
get_alsa_device_name(device_name);
ALOGI("adev_open: %s", device_name);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;

View File

@@ -1,12 +0,0 @@
/*
* Copyright (C) 2021-2022 KonstaKANG
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BDROID_BUILDCFG_H
#define _BDROID_BUILDCFG_H
#define BTM_DEF_LOCAL_NAME "Raspberry Pi 5"
#endif

View File

@@ -6,7 +6,6 @@ initramfs ramdisk.img followkernel
# Camera
camera_auto_detect=1
start_x=1
# Display
disable_overscan=1

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Defines the audio configuration in a car, including
- Audio zones
- Zone configurations (in each audio zone)
- Volume groups (in each zone configuration)
- Context to audio bus mappings (in each volume group)
in the car environment.
-->
<carAudioConfiguration version="3">
<zones>
<zone name="Primary zone" isPrimary="true" occupantZoneId="0">
<zoneConfigs>
<zoneConfig name="Config 0" isDefault="true">
<volumeGroups>
<group>
<device address="Speaker">
<context context="music"/>
<context context="navigation"/>
<context context="voice_command"/>
<context context="call_ring"/>
<context context="call"/>
<context context="alarm"/>
<context context="notification"/>
<context context="system_sound"/>
<context context="emergency"/>
<context context="safety"/>
<context context="vehicle_status"/>
<context context="announcement"/>
</device>
</group>
</volumeGroups>
</zoneConfig>
</zoneConfigs>
</zone>
</zones>
</carAudioConfiguration>

8
car/display_settings.xml Normal file
View File

@@ -0,0 +1,8 @@
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<!-- Use physical port number instead of local id -->
<config identifier="1" />
<!-- Display settings for cluster -->
<display name="port:1" dontMoveToTop="true" />
</display-settings>

View File

@@ -1,17 +1,26 @@
// Copyright (C) 2019 The Android Open-Source Project
// Copyright (C) 2021-2022 KonstaKANG
// Copyright (C) 2021 The Android Open Source Project
// Copyright (C) 2025 KonstaKANG
//
// SPDX-License-Identifier: Apache-2.0
cc_library_shared {
name: "hdmi_cec.rpi",
cc_binary {
name: "android.hardware.tv.cec@1.0-service.rpi",
relative_install_path: "hw",
proprietary: true,
srcs: ["hdmi_cec.c"],
cflags: ["-Werror"],
init_rc: ["android.hardware.tv.cec@1.0-service.rpi.rc"],
vintf_fragments: ["android.hardware.tv.cec@1.0-service.rpi.xml"],
vendor: true,
srcs: [
"HdmiCec.cpp",
"HdmiCecPort.cpp",
"service.cpp",
],
shared_libs: [
"liblog",
"android.hardware.tv.cec@1.0",
"libbase",
"libcutils",
"libhardware",
"libhidlbase",
"liblog",
"libutils",
],
}

504
cec/HdmiCec.cpp Normal file
View File

@@ -0,0 +1,504 @@
/*
* Copyright (C) 2021 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "android.hardware.tv.cec@1.0-service.rpi"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <cutils/properties.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <poll.h>
#include "HdmiCec.h"
#define PROPERTY_CEC_DEVICE "persist.hdmi.cec_device"
#define PROPERTY_CEC_VERSION "ro.hdmi.cec_version"
#define PROPERTY_VENDOR_ID "ro.hdmi.vendor_id"
namespace android {
namespace hardware {
namespace tv {
namespace cec {
namespace V1_0 {
namespace implementation {
using android::base::GetProperty;
using std::string;
HdmiCec::HdmiCec() {
mCecEnabled = false;
mWakeupEnabled = false;
mCecControlEnabled = false;
mCallback = nullptr;
Result result = init();
if (result != Result::SUCCESS) {
LOG(ERROR) << "Failed to init HDMI-CEC HAL";
}
}
HdmiCec::~HdmiCec() {
release();
}
// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
Return<Result> HdmiCec::addLogicalAddress(CecLogicalAddress addr) {
if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) {
LOG(ERROR) << "Add logical address failed, Invalid address";
return Result::FAILURE_INVALID_ARGS;
}
cec_log_addrs cecLogAddrs;
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
if (ret) {
LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
return Result::FAILURE_BUSY;
}
cecLogAddrs.cec_version = getCecVersion();
cecLogAddrs.vendor_id = getVendorId();
unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED;
unsigned int allDevTypes = 0;
unsigned int primDevType = 0xff;
switch (addr) {
case CecLogicalAddress::TV:
primDevType = CEC_OP_PRIM_DEVTYPE_TV;
logAddrType = CEC_LOG_ADDR_TYPE_TV;
allDevTypes = CEC_OP_ALL_DEVTYPE_TV;
break;
case CecLogicalAddress::RECORDER_1:
case CecLogicalAddress::RECORDER_2:
case CecLogicalAddress::RECORDER_3:
primDevType = CEC_OP_PRIM_DEVTYPE_RECORD;
logAddrType = CEC_LOG_ADDR_TYPE_RECORD;
allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD;
break;
case CecLogicalAddress::TUNER_1:
case CecLogicalAddress::TUNER_2:
case CecLogicalAddress::TUNER_3:
case CecLogicalAddress::TUNER_4:
primDevType = CEC_OP_PRIM_DEVTYPE_TUNER;
logAddrType = CEC_LOG_ADDR_TYPE_TUNER;
allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER;
break;
case CecLogicalAddress::PLAYBACK_1:
case CecLogicalAddress::PLAYBACK_2:
case CecLogicalAddress::PLAYBACK_3:
primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK;
allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK;
cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
break;
case CecLogicalAddress::AUDIO_SYSTEM:
primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
break;
case CecLogicalAddress::FREE_USE:
primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC;
allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH;
break;
case CecLogicalAddress::UNREGISTERED:
cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
break;
}
int logAddrIndex = cecLogAddrs.num_log_addrs;
cecLogAddrs.num_log_addrs += 1;
cecLogAddrs.log_addr[logAddrIndex] = static_cast<cec_logical_address_t>(addr);
cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType;
cecLogAddrs.primary_device_type[logAddrIndex] = primDevType;
cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes;
cecLogAddrs.features[logAddrIndex][0] = 0;
cecLogAddrs.features[logAddrIndex][1] = 0;
ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
if (ret) {
LOG(ERROR) << "Add logical address failed for port " << mHdmiCecPorts[0]->mPortId
<< ", Error = " << strerror(errno);
return Result::FAILURE_BUSY;
}
return Result::SUCCESS;
}
Return<void> HdmiCec::clearLogicalAddress() {
cec_log_addrs cecLogAddrs;
memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
if (ret) {
LOG(ERROR) << "Clear logical Address failed for port " << mHdmiCecPorts[0]->mPortId
<< ", Error = " << strerror(errno);
}
return Void();
}
Return<void> HdmiCec::getPhysicalAddress(getPhysicalAddress_cb callback) {
uint16_t addr;
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
callback(Result::FAILURE_INVALID_STATE, addr);
return Void();
}
callback(Result::SUCCESS, addr);
return Void();
}
Return<SendMessageResult> HdmiCec::sendMessage(const CecMessage& message) {
if (!mCecEnabled) {
return SendMessageResult::FAIL;
}
cec_msg cecMsg;
memset(&cecMsg, 0, sizeof(cec_msg));
int initiator = static_cast<cec_logical_address_t>(message.initiator);
int destination = static_cast<cec_logical_address_t>(message.destination);
cecMsg.msg[0] = (initiator << 4) | destination;
for (size_t i = 0; i < message.body.size(); ++i) {
cecMsg.msg[i + 1] = message.body[i];
}
cecMsg.len = message.body.size() + 1;
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_TRANSMIT, &cecMsg);
if (ret) {
LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
return SendMessageResult::FAIL;
}
if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
}
return getSendMessageResult(cecMsg.tx_status);
}
Return<void> HdmiCec::setCallback(const sp<IHdmiCecCallback>& callback) {
if (mCallback != nullptr) {
mCallback->unlinkToDeath(this);
mCallback = nullptr;
}
if (callback != nullptr) {
mCallback = callback;
mCallback->linkToDeath(this, 0 /*cookie*/);
}
return Void();
}
Return<int32_t> HdmiCec::getCecVersion() {
return property_get_int32(PROPERTY_CEC_VERSION, CEC_OP_CEC_VERSION_1_4);
}
Return<uint32_t> HdmiCec::getVendorId() {
return property_get_int32(PROPERTY_VENDOR_ID, 0x000c03 /* HDMI LLC vendor ID */);
}
Return<void> HdmiCec::getPortInfo(getPortInfo_cb callback) {
uint16_t addr = CEC_PHYS_ADDR_INVALID;
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Get port info failed for port : " << mHdmiCecPorts[0]->mPortId
<< ", Error = " << strerror(errno);
}
hidl_vec<HdmiPortInfo> portInfos {
{.type = HdmiPortType::OUTPUT,
.portId = mHdmiCecPorts[0]->mPortId,
.cecSupported = true,
.arcSupported = false,
.physicalAddress = addr}
};
callback(portInfos);
return Void();
}
Return<void> HdmiCec::setOption(OptionKey key, bool value) {
switch (key) {
case OptionKey::ENABLE_CEC:
LOG(DEBUG) << "setOption: Enable CEC: " << value;
mCecEnabled = value;
break;
case OptionKey::WAKEUP:
LOG(DEBUG) << "setOption: WAKEUP: " << value;
mWakeupEnabled = value;
break;
case OptionKey::SYSTEM_CEC_CONTROL:
LOG(DEBUG) << "setOption: SYSTEM_CEC_CONTROL: " << value;
mCecControlEnabled = value;
break;
}
return Void();
}
Return<void> HdmiCec::setLanguage(const hidl_string& language __unused) {
return Void();
}
Return<void> HdmiCec::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
return Void();
}
Return<bool> HdmiCec::isConnected(int32_t portId __unused) {
uint16_t addr = CEC_PHYS_ADDR_INVALID;
int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
return false;
}
if (addr == CEC_PHYS_ADDR_INVALID) {
return false;
}
return true;
}
// Initialise the cec file descriptor
Return<Result> HdmiCec::init() {
string cecDevice = GetProperty(PROPERTY_CEC_DEVICE, "cec0");
if (cecDevice != "cec0" && cecDevice != "cec1") {
LOG(ERROR) << "Invalid CEC device " << cecDevice;
return Result::FAILURE_NOT_SUPPORTED;
}
string devicePath = "/dev/" + cecDevice;
int portId = stoi(cecDevice.substr(3));
shared_ptr<HdmiCecPort> hdmiCecPort(new HdmiCecPort(portId));
Result result = hdmiCecPort->init(devicePath.c_str());
if (result != Result::SUCCESS) {
return Result::FAILURE_NOT_SUPPORTED;
}
thread eventThread(&HdmiCec::event_thread, this, hdmiCecPort.get());
mEventThreads.push_back(std::move(eventThread));
mHdmiCecPorts.push_back(std::move(hdmiCecPort));
LOG(INFO) << "Using CEC device " << devicePath;
mCecEnabled = true;
mWakeupEnabled = true;
mCecControlEnabled = true;
return Result::SUCCESS;
}
Return<void> HdmiCec::release() {
mCecEnabled = false;
mWakeupEnabled = false;
mCecControlEnabled = false;
for (thread& eventThread : mEventThreads) {
if (eventThread.joinable()) {
eventThread.join();
}
}
setCallback(nullptr);
mHdmiCecPorts.clear();
mEventThreads.clear();
return Void();
}
void HdmiCec::event_thread(HdmiCecPort* hdmiCecPort) {
struct pollfd ufds[3] = {
{hdmiCecPort->mCecFd, POLLIN, 0},
{hdmiCecPort->mCecFd, POLLERR, 0},
{hdmiCecPort->mExitFd, POLLIN, 0},
};
while (1) {
ufds[0].revents = 0;
ufds[1].revents = 0;
ufds[2].revents = 0;
int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1);
if (ret <= 0) {
continue;
}
if (ufds[2].revents == POLLIN) { /* Exit */
break;
}
if (ufds[1].revents == POLLERR) { /* CEC Event */
cec_event ev;
ret = ioctl(hdmiCecPort->mCecFd, CEC_DQEVENT, &ev);
if (ret) {
LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
continue;
}
if (!mCecEnabled) {
continue;
}
if (ev.event == CEC_EVENT_STATE_CHANGE) {
if (mCallback != nullptr) {
HotplugEvent hotplugEvent{
.connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
.portId = hdmiCecPort->mPortId};
mCallback->onHotplugEvent(hotplugEvent);
} else {
LOG(ERROR) << "No event callback for hotplug";
}
}
}
if (ufds[0].revents == POLLIN) { /* CEC Driver */
cec_msg msg = {};
ret = ioctl(hdmiCecPort->mCecFd, CEC_RECEIVE, &msg);
if (ret) {
LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
continue;
}
if (msg.rx_status != CEC_RX_STATUS_OK) {
LOG(ERROR) << "msg rx_status = " << msg.rx_status;
continue;
}
if (!mCecEnabled) {
continue;
}
if (!mWakeupEnabled && isWakeupMessage(msg)) {
LOG(DEBUG) << "Filter wakeup message";
continue;
}
if (!mCecControlEnabled && !isTransferableInSleep(msg)) {
LOG(DEBUG) << "Filter message in standby mode";
continue;
}
if (mCallback != nullptr) {
size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
CecMessage cecMessage{
.initiator = static_cast<CecLogicalAddress>(msg.msg[0] >> 4),
.destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
};
cecMessage.body.resize(length);
for (size_t i = 0; i < length; ++i) {
cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
}
mCallback->onCecMessage(cecMessage);
} else {
LOG(ERROR) << "no event callback for message";
}
}
}
}
int HdmiCec::getOpcode(cec_msg message) {
return static_cast<uint8_t>(message.msg[1]);
}
bool HdmiCec::isWakeupMessage(cec_msg message) {
int opcode = getOpcode(message);
switch (opcode) {
case CEC_MESSAGE_TEXT_VIEW_ON:
case CEC_MESSAGE_IMAGE_VIEW_ON:
return true;
default:
return false;
}
}
bool HdmiCec::isTransferableInSleep(cec_msg message) {
int opcode = getOpcode(message);
switch (opcode) {
case CEC_MESSAGE_ABORT:
case CEC_MESSAGE_DEVICE_VENDOR_ID:
case CEC_MESSAGE_GET_CEC_VERSION:
case CEC_MESSAGE_GET_MENU_LANGUAGE:
case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
case CEC_MESSAGE_GIVE_OSD_NAME:
case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
case CEC_MESSAGE_REPORT_POWER_STATUS:
case CEC_MESSAGE_SET_OSD_NAME:
case CEC_MESSAGE_DECK_CONTROL:
case CEC_MESSAGE_PLAY:
case CEC_MESSAGE_IMAGE_VIEW_ON:
case CEC_MESSAGE_TEXT_VIEW_ON:
case CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
return true;
case CEC_MESSAGE_USER_CONTROL_PRESSED:
return isPowerUICommand(message);
default:
return false;
}
}
int HdmiCec::getFirstParam(cec_msg message) {
return static_cast<uint8_t>(message.msg[2]);
}
bool HdmiCec::isPowerUICommand(cec_msg message) {
int uiCommand = getFirstParam(message);
switch (uiCommand) {
case CEC_OP_UI_CMD_POWER:
case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
return true;
default:
return false;
}
}
Return<SendMessageResult> HdmiCec::getSendMessageResult(int tx_status) {
switch (tx_status) {
case CEC_TX_STATUS_OK:
return SendMessageResult::SUCCESS;
case CEC_TX_STATUS_ARB_LOST:
return SendMessageResult::BUSY;
case CEC_TX_STATUS_NACK:
return SendMessageResult::NACK;
default:
return SendMessageResult::FAIL;
}
}
} // namespace implementation
} // namespace V1_0
} // namespace cec
} // namespace tv
} // namespace hardware
} // namespace android

95
cec/HdmiCec.h Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2021 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <hardware/hdmi_cec.h>
#include <linux/cec.h>
#include <thread>
#include <vector>
#include "HdmiCecPort.h"
namespace android {
namespace hardware {
namespace tv {
namespace cec {
namespace V1_0 {
namespace implementation {
using std::shared_ptr;
using std::thread;
using std::vector;
class HdmiCec : public IHdmiCec, public hidl_death_recipient {
public:
HdmiCec();
~HdmiCec();
// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
Return<Result> addLogicalAddress(CecLogicalAddress addr) override;
Return<void> clearLogicalAddress() override;
Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
Return<SendMessageResult> sendMessage(const CecMessage& message) override;
Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
Return<int32_t> getCecVersion() override;
Return<uint32_t> getVendorId() override;
Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
Return<void> setOption(OptionKey key, bool value) override;
Return<void> setLanguage(const hidl_string& language) override;
Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
Return<bool> isConnected(int32_t portId) override;
virtual void serviceDied(uint64_t, const wp<::android::hidl::base::V1_0::IBase>&) {
setCallback(nullptr);
}
Return<Result> init();
Return<void> release();
private:
void event_thread(HdmiCecPort* hdmiCecPort);
static int getOpcode(cec_msg message);
static int getFirstParam(cec_msg message);
static bool isWakeupMessage(cec_msg message);
static bool isTransferableInSleep(cec_msg message);
static bool isPowerUICommand(cec_msg message);
static Return<SendMessageResult> getSendMessageResult(int tx_status);
vector<thread> mEventThreads;
vector<shared_ptr<HdmiCecPort>> mHdmiCecPorts;
// When set to false, all the CEC commands are discarded. True by default after initialization.
bool mCecEnabled;
/*
* When set to false, HAL does not wake up the system upon receiving <Image View On> or
* <Text View On>. True by default after initialization.
*/
bool mWakeupEnabled;
/*
* Updated when system goes into or comes out of standby mode.
* When set to true, Android system is handling CEC commands.
* When set to false, microprocessor is handling CEC commands.
* True by default after initialization.
*/
bool mCecControlEnabled;
sp<IHdmiCecCallback> mCallback;
};
} // namespace implementation
} // namespace V1_0
} // namespace cec
} // namespace tv
} // namespace hardware
} // namespace android

104
cec/HdmiCecPort.cpp Normal file
View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2021 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "android.hardware.tv.cec@1.0-service.rpi"
#include <android-base/logging.h>
#include <errno.h>
#include <linux/cec.h>
#include <linux/ioctl.h>
#include <sys/eventfd.h>
#include <algorithm>
#include "HdmiCecPort.h"
namespace android {
namespace hardware {
namespace tv {
namespace cec {
namespace V1_0 {
namespace implementation {
HdmiCecPort::HdmiCecPort(unsigned int portId) {
mPortId = portId;
mCecFd = -1;
mExitFd = -1;
}
HdmiCecPort::~HdmiCecPort() {
release();
}
// Initialise the cec file descriptor
Return<Result> HdmiCecPort::init(const char* path) {
mCecFd = open(path, O_RDWR);
if (mCecFd < 0) {
LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
return Result::FAILURE_NOT_SUPPORTED;
}
mExitFd = eventfd(0, EFD_NONBLOCK);
if (mExitFd < 0) {
LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
release();
return Result::FAILURE_NOT_SUPPORTED;
}
// Ensure the CEC device supports required capabilities
struct cec_caps caps = {};
int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
if (ret) {
LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
release();
return Result::FAILURE_NOT_SUPPORTED;
}
if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
release();
return Result::FAILURE_NOT_SUPPORTED;
}
uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
ret = ioctl(mCecFd, CEC_S_MODE, &mode);
if (ret) {
LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
release();
return Result::FAILURE_NOT_SUPPORTED;
}
return Result::SUCCESS;
}
Return<void> HdmiCecPort::release() {
if (mExitFd > 0) {
uint64_t tmp = 1;
write(mExitFd, &tmp, sizeof(tmp));
}
if (mExitFd > 0) {
close(mExitFd);
}
if (mCecFd > 0) {
close(mCecFd);
}
return Void();
}
} // namespace implementation
} // namespace V1_0
} // namespace cec
} // namespace tv
} // namespace hardware
} // namespace android

44
cec/HdmiCecPort.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
namespace android {
namespace hardware {
namespace tv {
namespace cec {
namespace V1_0 {
namespace implementation {
class HdmiCecPort {
public:
HdmiCecPort(unsigned int portId);
~HdmiCecPort();
Return<Result> init(const char* path);
Return<void> release();
unsigned int mPortId;
int mCecFd;
int mExitFd;
};
} // namespace implementation
} // namespace V1_0
} // namespace cec
} // namespace tv
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,5 @@
service vendor.cec-hal-1-0-rpi /vendor/bin/hw/android.hardware.tv.cec@1.0-service.rpi
interface android.hardware.tv.cec@1.0::IHdmiCec default
class hal
user system
group system

View File

@@ -0,0 +1,11 @@
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.tv.cec</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View File

@@ -1,618 +0,0 @@
/*
* Copyright (C) 2019 BayLibre, SAS.
* Copyright (C) 2021-2022 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "hdmi_cec"
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <poll.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/cec.h>
#include <sys/eventfd.h>
#include <log/log.h>
#include <cutils/properties.h>
#include <hardware/hdmi_cec.h>
typedef struct hdmicec_context
{
hdmi_cec_device_t device; /* must be first */
int cec_fd;
unsigned int vendor_id;
unsigned int type;
unsigned int version;
struct hdmi_port_info port_info;
event_callback_t p_event_cb;
void *cb_arg;
pthread_t thread;
int exit_fd;
pthread_mutex_t options_lock;
bool cec_enabled;
bool cec_control_enabled;
} hdmicec_context_t;
static int hdmicec_add_logical_address(const struct hdmi_cec_device *dev, cec_logical_address_t addr)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
unsigned int la_type = CEC_LOG_ADDR_TYPE_UNREGISTERED;
unsigned int all_dev_types = 0;
unsigned int prim_type = 0xff;
struct cec_log_addrs laddrs;
int ret;
ALOGD("%s: addr:%x\n", __func__, addr);
if (addr >= CEC_ADDR_BROADCAST)
return -1;
ret = ioctl(ctx->cec_fd, CEC_ADAP_G_LOG_ADDRS, &laddrs);
if (ret)
return ret;
memset(&laddrs, 0, sizeof(laddrs));
laddrs.cec_version = ctx->version;
laddrs.vendor_id = ctx->vendor_id;
switch (addr) {
case CEC_LOG_ADDR_TV:
prim_type = CEC_OP_PRIM_DEVTYPE_TV;
la_type = CEC_LOG_ADDR_TYPE_TV;
all_dev_types = CEC_OP_ALL_DEVTYPE_TV;
break;
case CEC_LOG_ADDR_RECORD_1:
case CEC_LOG_ADDR_RECORD_2:
case CEC_LOG_ADDR_RECORD_3:
prim_type = CEC_OP_PRIM_DEVTYPE_RECORD;
la_type = CEC_LOG_ADDR_TYPE_RECORD;
all_dev_types = CEC_OP_ALL_DEVTYPE_RECORD;
break;
case CEC_LOG_ADDR_TUNER_1:
case CEC_LOG_ADDR_TUNER_2:
case CEC_LOG_ADDR_TUNER_3:
case CEC_LOG_ADDR_TUNER_4:
prim_type = CEC_OP_PRIM_DEVTYPE_TUNER;
la_type = CEC_LOG_ADDR_TYPE_TUNER;
all_dev_types = CEC_OP_ALL_DEVTYPE_TUNER;
break;
case CEC_LOG_ADDR_PLAYBACK_1:
case CEC_LOG_ADDR_PLAYBACK_2:
case CEC_LOG_ADDR_PLAYBACK_3:
prim_type = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
la_type = CEC_LOG_ADDR_TYPE_PLAYBACK;
all_dev_types = CEC_OP_ALL_DEVTYPE_PLAYBACK;
laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
break;
case CEC_LOG_ADDR_AUDIOSYSTEM:
prim_type = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
la_type = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
all_dev_types = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
break;
case CEC_LOG_ADDR_SPECIFIC:
prim_type = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
la_type = CEC_LOG_ADDR_TYPE_SPECIFIC;
all_dev_types = CEC_OP_ALL_DEVTYPE_SWITCH;
break;
case CEC_ADDR_RESERVED_1:
case CEC_ADDR_RESERVED_2:
case CEC_ADDR_UNREGISTERED:
laddrs.flags = CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
break;
}
laddrs.num_log_addrs = 1;
laddrs.log_addr[0] = addr;
laddrs.log_addr_type[0] = la_type;
laddrs.primary_device_type[0] = prim_type;
laddrs.all_device_types[0] = all_dev_types;
laddrs.features[0][0] = 0;
laddrs.features[0][1] = 0;
ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
if (ret) {
ALOGD("%s: %m\n", __func__);
return ret;
}
ALOGD("%s: log_addr_mask=%x\n", __func__, laddrs.log_addr_mask);
return 0;
}
static void hdmicec_clear_logical_address(const struct hdmi_cec_device *dev)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
struct cec_log_addrs laddrs;
int ret;
memset(&laddrs, 0, sizeof(laddrs));
ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
if (ret)
ALOGD("%s: %m\n", __func__);
}
static int hdmicec_get_physical_address(const struct hdmi_cec_device *dev, uint16_t *addr)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
int ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, addr);
if (ret)
ALOGD("%s: %m\n", __func__);
return ret;
}
static int hdmicec_send_message(const struct hdmi_cec_device *dev, const cec_message_t *msg)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
struct cec_msg cec_msg;
int ret;
pthread_mutex_lock(&ctx->options_lock);
bool cec_enabled = ctx->cec_enabled;
pthread_mutex_unlock(&ctx->options_lock);
if (!cec_enabled) {
return HDMI_RESULT_FAIL;
}
ALOGD("%s: len=%u\n", __func__, (unsigned int)msg->length);
memset(&cec_msg, 0, sizeof(cec_msg));
cec_msg.msg[0] = (msg->initiator << 4) | msg->destination;
memcpy(&cec_msg.msg[1], msg->body, msg->length);
cec_msg.len = msg->length + 1;
ret = ioctl(ctx->cec_fd, CEC_TRANSMIT, &cec_msg);
if (ret) {
ALOGD("%s: %m\n", __func__);
return HDMI_RESULT_FAIL;
}
if (cec_msg.tx_status != CEC_TX_STATUS_OK)
ALOGD("%s: tx_status=%d\n", __func__, cec_msg.tx_status);
switch (cec_msg.tx_status) {
case CEC_TX_STATUS_OK:
return HDMI_RESULT_SUCCESS;
case CEC_TX_STATUS_ARB_LOST:
return HDMI_RESULT_BUSY;
case CEC_TX_STATUS_NACK:
return HDMI_RESULT_NACK;
default:
if (cec_msg.tx_status & CEC_TX_STATUS_NACK)
return HDMI_RESULT_NACK;
return HDMI_RESULT_FAIL;
}
}
static void hdmicec_register_event_callback(const struct hdmi_cec_device *dev,
event_callback_t callback, void *arg)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
ctx->p_event_cb = callback;
ctx->cb_arg = arg;
}
static void hdmicec_get_version(const struct hdmi_cec_device *dev, int *version)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
*version = ctx->version;
}
static void hdmicec_get_vendor_id(const struct hdmi_cec_device *dev, uint32_t *vendor_id)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
*vendor_id = ctx->vendor_id;
}
static void hdmicec_get_port_info(const struct hdmi_cec_device *dev,
struct hdmi_port_info *list[], int *total)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
int ret;
ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR, &ctx->port_info.physical_address);
if (ret)
ALOGD("%s: %m\n", __func__);
ALOGD("type:%s, id:%d, cec support:%d, arc support:%d, physical address:%x",
ctx->port_info.type ? "output" : "input",
ctx->port_info.port_id,
ctx->port_info.cec_supported,
ctx->port_info.arc_supported,
ctx->port_info.physical_address);
if (ctx->port_info.physical_address != CEC_PHYS_ADDR_INVALID) {
*list = &ctx->port_info;
*total = 1;
}
}
static void hdmicec_set_option(const struct hdmi_cec_device *dev, int flag, int value)
{
struct hdmicec_context* ctx = (struct hdmicec_context*)dev;
ALOGD("%s: flag=%d, value=%d", __func__, flag, value);
switch (flag) {
case HDMI_OPTION_ENABLE_CEC:
pthread_mutex_lock(&ctx->options_lock);
ctx->cec_enabled = (value == 1 ? true : false);
pthread_mutex_unlock(&ctx->options_lock);
break;
case HDMI_OPTION_WAKEUP:
// Not valid for playback devices
break;
case HDMI_OPTION_SYSTEM_CEC_CONTROL:
pthread_mutex_lock(&ctx->options_lock);
ctx->cec_control_enabled = (value == 1 ? true : false);
pthread_mutex_unlock(&ctx->options_lock);
break;
}
}
static int hdmicec_is_connected(const struct hdmi_cec_device *dev, int port_id)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
int ret;
(void)port_id;
ret = ioctl(ctx->cec_fd, CEC_ADAP_G_PHYS_ADDR,
&ctx->port_info.physical_address);
if (ret) {
ALOGD("%s: %m\n", __func__);
return ret;
}
if (ctx->port_info.physical_address == CEC_PHYS_ADDR_INVALID)
return false;
return true;
}
static int get_opcode(struct cec_msg* message) {
return (((uint8_t)message->msg[1]) & 0xff);
}
static int get_first_param(struct cec_msg* message) {
return (((uint8_t)message->msg[2]) & 0xff);
}
static bool is_power_ui_command(struct cec_msg* message) {
int ui_command = get_first_param(message);
switch (ui_command) {
case CEC_OP_UI_CMD_POWER:
case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
return true;
default:
return false;
}
}
static bool is_transferable_in_sleep(struct cec_msg* message) {
int opcode = get_opcode(message);
switch (opcode) {
case CEC_MESSAGE_ABORT:
case CEC_MESSAGE_DEVICE_VENDOR_ID:
case CEC_MESSAGE_GET_CEC_VERSION:
case CEC_MESSAGE_GET_MENU_LANGUAGE:
case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
case CEC_MESSAGE_GIVE_OSD_NAME:
case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
case CEC_MESSAGE_REPORT_POWER_STATUS:
case CEC_MESSAGE_SET_OSD_NAME:
case CEC_MESSAGE_DECK_CONTROL:
case CEC_MESSAGE_PLAY:
return true;
case CEC_MESSAGE_USER_CONTROL_PRESSED:
return is_power_ui_command(message);
default:
return false;
}
}
static void *event_thread(void *arg)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)arg;
int ret;
struct pollfd ufds[3] = {
{ ctx->cec_fd, POLLIN, 0 },
{ ctx->cec_fd, POLLERR, 0 },
{ ctx->exit_fd, POLLIN, 0 },
};
ALOGI("%s start!", __func__);
while (1) {
ufds[0].revents = 0;
ufds[1].revents = 0;
ufds[2].revents = 0;
ret = poll(ufds, 3, -1);
if (ret <= 0)
continue;
if (ufds[2].revents == POLLIN) /* Exit */
break;
if (ufds[1].revents == POLLERR) { /* CEC Event */
hdmi_event_t event = { };
struct cec_event ev;
ret = ioctl(ctx->cec_fd, CEC_DQEVENT, &ev);
if (ret)
continue;
pthread_mutex_lock(&ctx->options_lock);
bool cec_enabled = ctx->cec_enabled;
pthread_mutex_unlock(&ctx->options_lock);
if (!cec_enabled) {
continue;
}
if (ev.event == CEC_EVENT_STATE_CHANGE) {
event.type = HDMI_EVENT_HOT_PLUG;
event.dev = &ctx->device;
event.hotplug.port_id = 1;
if (ev.state_change.phys_addr == CEC_PHYS_ADDR_INVALID)
event.hotplug.connected = false;
else
event.hotplug.connected = true;
if (ctx->p_event_cb != NULL) {
ctx->p_event_cb(&event, ctx->cb_arg);
} else {
ALOGE("no event callback for hotplug\n");
}
}
}
if (ufds[0].revents == POLLIN) { /* CEC Driver */
struct cec_msg msg = { };
hdmi_event_t event = { };
ret = ioctl(ctx->cec_fd, CEC_RECEIVE, &msg);
if (ret) {
ALOGE("%s: CEC_RECEIVE error (%m)\n", __func__);
continue;
}
if (msg.rx_status != CEC_RX_STATUS_OK) {
ALOGD("%s: rx_status=%d\n", __func__, msg.rx_status);
continue;
}
pthread_mutex_lock(&ctx->options_lock);
bool cec_enabled = ctx->cec_enabled;
pthread_mutex_unlock(&ctx->options_lock);
if (!cec_enabled) {
continue;
}
pthread_mutex_lock(&ctx->options_lock);
bool cec_control_enabled = ctx->cec_control_enabled;
pthread_mutex_unlock(&ctx->options_lock);
if (!cec_control_enabled && !is_transferable_in_sleep(&msg)) {
ALOGD("%s: filter message in standby mode\n", __func__);
continue;
}
if (ctx->p_event_cb != NULL) {
event.type = HDMI_EVENT_CEC_MESSAGE;
event.dev = &ctx->device;
event.cec.initiator = msg.msg[0] >> 4;
event.cec.destination = msg.msg[0] & 0xf;
event.cec.length = msg.len - 1;
memcpy(event.cec.body, &msg.msg[1], msg.len - 1);
ctx->p_event_cb(&event, ctx->cb_arg);
} else {
ALOGE("no event callback for msg\n");
}
}
}
ALOGI("%s exit!", __func__);
return NULL;
}
static void hdmicec_set_arc(const struct hdmi_cec_device *dev, int port_id, int flag)
{
(void)dev;
(void)port_id;
(void)flag;
/* Not supported */
}
static int hdmicec_close(struct hdmi_cec_device *dev)
{
struct hdmicec_context *ctx = (struct hdmicec_context *)dev;
uint64_t tmp = 1;
ALOGD("%s\n", __func__);
if (ctx->exit_fd > 0) {
write(ctx->exit_fd, &tmp, sizeof(tmp));
pthread_join(ctx->thread, NULL);
}
if (ctx->cec_fd > 0)
close(ctx->cec_fd);
if (ctx->exit_fd > 0)
close(ctx->exit_fd);
free(ctx);
ctx->cec_enabled = false;
ctx->cec_control_enabled = false;
return 0;
}
static int cec_init(struct hdmicec_context *ctx)
{
struct cec_log_addrs laddrs = {};
struct cec_caps caps = {};
uint32_t mode;
int ret;
// Ensure the CEC device supports required capabilities
ret = ioctl(ctx->cec_fd, CEC_ADAP_G_CAPS, &caps);
if (ret)
return ret;
if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS |
CEC_CAP_TRANSMIT |
CEC_CAP_PASSTHROUGH))) {
ALOGE("%s: wrong cec adapter capabilities %x\n",
__func__, caps.capabilities);
return -1;
}
// This is an exclusive follower, in addition put the CEC device into passthrough mode
mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
ret = ioctl(ctx->cec_fd, CEC_S_MODE, &mode);
if (ret)
return ret;
ctx->type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
ctx->vendor_id = property_get_int32("ro.hdmi.vendor_id",
0x000c03 /* HDMI LLC vendor ID */);
ctx->version = property_get_bool("ro.hdmi.cec_version",
CEC_OP_CEC_VERSION_1_4);
ctx->port_info.type = ctx->type == CEC_DEVICE_TV ? HDMI_INPUT : HDMI_OUTPUT;
ctx->port_info.port_id = 1;
ctx->port_info.cec_supported = 1;
ctx->port_info.arc_supported = 0;
ALOGD("%s: type=%d\n", __func__, ctx->type);
ALOGD("%s: vendor_id=%04x\n", __func__, ctx->vendor_id);
ALOGD("%s: version=%d\n", __func__, ctx->version);
memset(&laddrs, 0, sizeof(laddrs));
ret = ioctl(ctx->cec_fd, CEC_ADAP_S_LOG_ADDRS, &laddrs);
if (ret)
return ret;
pthread_mutex_init(&ctx->options_lock, NULL);
ALOGD("%s: initialized CEC controller\n", __func__);
return ret;
}
static int open_hdmi_cec(const struct hw_module_t *module, const char *id,
struct hw_device_t **device)
{
char path[32];
char prop[PROPERTY_VALUE_MAX];
hdmicec_context_t *ctx;
int ret;
ALOGD("%s: id=%s\n", __func__, id);
ctx = malloc(sizeof(struct hdmicec_context));
if (!ctx)
return -ENOMEM;
memset(ctx, 0, sizeof(*ctx));
property_get("ro.hdmi.cec_device", prop, "cec0");
snprintf(path, sizeof(path), "/dev/%s", prop);
ctx->cec_fd = open(path, O_RDWR);
if (ctx->cec_fd < 0) {
ALOGE("faild to open %s, ret=%s\n", path, strerror(errno));
goto fail;
}
ctx->exit_fd = eventfd(0, EFD_NONBLOCK);
if (ctx->exit_fd < 0) {
ALOGE("faild to open eventfd, ret = %d\n", errno);
goto fail;
}
ctx->device.common.tag = HARDWARE_DEVICE_TAG;
ctx->device.common.version = HDMI_CEC_DEVICE_API_VERSION_1_0;
ctx->device.common.module = (struct hw_module_t *)module;
ctx->device.common.close = (int (*)(struct hw_device_t* device))hdmicec_close;
ctx->device.add_logical_address = hdmicec_add_logical_address;
ctx->device.clear_logical_address = hdmicec_clear_logical_address;
ctx->device.get_physical_address = hdmicec_get_physical_address;
ctx->device.send_message = hdmicec_send_message;
ctx->device.register_event_callback = hdmicec_register_event_callback;
ctx->device.get_version = hdmicec_get_version;
ctx->device.get_vendor_id = hdmicec_get_vendor_id;
ctx->device.get_port_info = hdmicec_get_port_info;
ctx->device.set_option = hdmicec_set_option;
ctx->device.set_audio_return_channel = hdmicec_set_arc;
ctx->device.is_connected = hdmicec_is_connected;
/* init status */
ret = cec_init(ctx);
if (ret)
goto fail;
*device = &ctx->device.common;
/* thread loop for receiving cec msg */
if (pthread_create(&ctx->thread, NULL, event_thread, ctx)) {
ALOGE("Can't create event thread: %s\n", strerror(errno));
goto fail;
}
ctx->cec_enabled = true;
ctx->cec_control_enabled = true;
return 0;
fail:
hdmicec_close((struct hdmi_cec_device *)ctx);
return -errno;
}
/* module method */
static struct hw_module_methods_t hdmi_cec_module_methods = {
.open = open_hdmi_cec,
};
/* hdmi_cec module */
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = HDMI_CEC_HARDWARE_MODULE_ID,
.name = "Raspberry Pi HDMI CEC HAL",
.author = "The Android Open Source Project",
.methods = &hdmi_cec_module_methods,
};

49
cec/service.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "android.hardware.tv.cec@1.0-service-rpi"
#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hidl/LegacySupport.h>
#include "HdmiCec.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::tv::cec::V1_0::IHdmiCec;
using android::hardware::tv::cec::V1_0::implementation::HdmiCec;
using android::OK;
using android::status_t;
int main() {
configureRpcThreadpool(1, true /* callerWillJoin */);
android::sp<IHdmiCec> service = new HdmiCec();
status_t status = service->registerAsService();
if (status != OK) {
ALOGE("Cannot register HDMI-CEC HAL service.");
return 1;
}
ALOGI("HDMI-CEC HAL ready.");
joinRpcThreadpool();
// Under normal cases, execution will not reach this line.
ALOGE("HDMI-CEC HAL failed to join thread pool.");
return 1;
}

View File

@@ -135,9 +135,7 @@ PRODUCT_COPY_FILES += \
# CEC
PRODUCT_PACKAGES += \
android.hardware.tv.cec@1.0-impl \
android.hardware.tv.cec@1.0-service \
hdmi_cec.rpi
android.hardware.tv.cec@1.0-service.rpi
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.hardware.hdmi.cec.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.hdmi.cec.xml
@@ -179,8 +177,11 @@ PRODUCT_PACKAGES += \
libEGL_mesa \
libGLESv1_CM_mesa \
libGLESv2_mesa \
libgallium_dri \
libglapi
libgallium_dri
PRODUCT_PACKAGES += \
dri_gbm \
libgbm_mesa
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.software.opengles.deqp.level-2023-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.opengles.deqp.level.xml
@@ -248,10 +249,14 @@ PRODUCT_PACKAGES += \
PRODUCT_PACKAGES += \
com.android.hardware.thermal
# Touchscreen
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml
# USB
PRODUCT_PACKAGES += \
android.hardware.usb-service.example \
android.hardware.usb.gadget@1.2-service.rpi
android.hardware.usb.gadget-service.rpi
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.hardware.usb.accessory.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.accessory.xml \

View File

@@ -25,13 +25,57 @@ namespace aidl::android::hardware::health {
void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) {
health_info->chargerAcOnline = true;
health_info->batteryLevel = 100;
health_info->batteryStatus = BatteryStatus::CHARGING;
health_info->chargerUsbOnline = true;
health_info->chargerWirelessOnline = false;
health_info->chargerDockOnline = false;
health_info->maxChargingCurrentMicroamps = 500000;
health_info->maxChargingVoltageMicrovolts = 5000000;
health_info->batteryStatus = BatteryStatus::FULL;
health_info->batteryHealth = BatteryHealth::GOOD;
health_info->batteryPresent = true;
health_info->batteryLevel = 100;
health_info->batteryVoltageMillivolts = 5000;
health_info->batteryTemperatureTenthsCelsius = 250;
health_info->batteryCurrentMicroamps = 500000;
health_info->batteryCycleCount = 25;
health_info->batteryFullChargeUah = 5000000;
health_info->batteryChargeCounterUah = 5000000;
health_info->batteryTechnology = "Li-ion";
health_info->batteryCapacityLevel = BatteryCapacityLevel::FULL;
health_info->batteryFullChargeDesignCapacityUah = 5000000;
}
ndk::ScopedAStatus HealthImpl::getChargeCounterUah(int32_t* out) {
*out = 5000000;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus HealthImpl::getCurrentNowMicroamps(int32_t* out) {
*out = 500000;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus HealthImpl::getCurrentAverageMicroamps(int32_t*) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus HealthImpl::getCapacity(int32_t* out) {
*out = 100;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus HealthImpl::getChargeStatus(BatteryStatus* out) {
*out = BatteryStatus::CHARGING;
*out = BatteryStatus::FULL;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus HealthImpl::getBatteryHealthData(BatteryHealthData* out) {
out->batteryManufacturingDateSeconds = 1231006505;
out->batteryFirstUsageSeconds = 1231469665;
out->batteryStateOfHealth = 99;
out->batterySerialNumber =
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
out->batteryPartStatus = BatteryPartStatus::ORIGINAL;
return ndk::ScopedAStatus::ok();
}

View File

@@ -28,7 +28,12 @@ public:
using Health::Health;
virtual ~HealthImpl() {}
ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override;
ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* out) override;
ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override;
ndk::ScopedAStatus getCapacity(int32_t* out) override;
ndk::ScopedAStatus getChargeStatus(BatteryStatus* out) override;
ndk::ScopedAStatus getBatteryHealthData(BatteryHealthData* out) override;
protected:
void UpdateHealthInfo(HealthInfo* health_info) override;

View File

@@ -52,13 +52,4 @@
<instance>default</instance>
</interface>
</hal>
<hal format="hidl">
<name>android.hardware.tv.cec</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View File

@@ -6,14 +6,37 @@
# SPDX-License-Identifier: Apache-2.0
#
exit_with_error() {
echo $@
exit 1
}
if [ -z ${TARGET_PRODUCT} ]; then
exit_with_error "TARGET_PRODUCT environment variable is not set. Run lunch first."
fi
if [ -z ${ANDROID_PRODUCT_OUT} ]; then
exit_with_error "ANDROID_PRODUCT_OUT environment variable is not set. Run lunch first."
fi
for PARTITION in "boot" "system" "vendor"; do
if [ ! -f ${ANDROID_PRODUCT_OUT}/${PARTITION}.img ]; then
exit_with_error "Partition image not found. Run 'make ${PARTITION}image' first."
fi
done
VERSION=RaspberryVanillaAOSP14
DATE=$(date +%Y%m%d)
IMGNAME=${VERSION}-${DATE}-rpi5.img
IMGSIZE=7
OUTDIR=$(pwd | sed 's/\/device\/brcm\/rpi5$//')/out/target/product/rpi5
TARGET=$(echo ${TARGET_PRODUCT} | sed 's/^aosp_//')
IMGNAME=${VERSION}-${DATE}-${TARGET}.img
IMGSIZE=15360000000
echo "Creating image file ${OUTDIR}/${IMGNAME}..."
sudo dd if=/dev/zero of="${OUTDIR}/${IMGNAME}" bs=1M count=$(echo "${IMGSIZE}*1024" | bc)
if [ -f ${ANDROID_PRODUCT_OUT}/${IMGNAME} ]; then
exit_with_error "${ANDROID_PRODUCT_OUT}/${IMGNAME} already exists!"
fi
echo "Creating image file ${ANDROID_PRODUCT_OUT}/${IMGNAME}..."
sudo fallocate -l ${IMGSIZE} ${ANDROID_PRODUCT_OUT}/${IMGNAME}
sync
echo "Creating partitions..."
@@ -28,7 +51,7 @@ echo n
echo p
echo 2
echo
echo +2048M
echo +2560M
echo n
echo p
echo 3
@@ -44,28 +67,29 @@ echo c
echo a
echo 1
echo w
) | sudo fdisk "${OUTDIR}/${IMGNAME}"
) | sudo fdisk ${ANDROID_PRODUCT_OUT}/${IMGNAME}
sync
LOOPDEV=$(sudo kpartx -av "${OUTDIR}/${IMGNAME}" | awk 'NR==1{ sub(/p[0-9]$/, "", $3); print $3 }')
LOOPDEV=$(sudo kpartx -av ${ANDROID_PRODUCT_OUT}/${IMGNAME} | awk 'NR==1{ sub(/p[0-9]$/, "", $3); print $3 }')
if [ -z ${LOOPDEV} ]; then
echo "Unable to find loop device!"
exit 1
exit_with_error "Unable to find loop device!"
fi
echo "Image mounted as /dev/${LOOPDEV}"
sleep 1
echo "Copying boot..."
sudo dd if=${OUTDIR}/boot.img of=/dev/mapper/${LOOPDEV}p1 bs=1M
sudo dd if=${ANDROID_PRODUCT_OUT}/boot.img of=/dev/mapper/${LOOPDEV}p1 bs=1M
echo "Copying system..."
sudo dd if=${OUTDIR}/system.img of=/dev/mapper/${LOOPDEV}p2 bs=1M
sudo dd if=${ANDROID_PRODUCT_OUT}/system.img of=/dev/mapper/${LOOPDEV}p2 bs=1M
echo "Copying vendor..."
sudo dd if=${OUTDIR}/vendor.img of=/dev/mapper/${LOOPDEV}p3 bs=1M
sudo dd if=${ANDROID_PRODUCT_OUT}/vendor.img of=/dev/mapper/${LOOPDEV}p3 bs=1M
echo "Creating userdata..."
sudo mkfs.ext4 /dev/mapper/${LOOPDEV}p4 -I 512 -L userdata
sync
sudo kpartx -d "/dev/${LOOPDEV}"
echo "Done, created ${OUTDIR}/${IMGNAME}!"
sudo losetup -d "/dev/${LOOPDEV}"
sudo chown ${USER}:${USER} ${ANDROID_PRODUCT_OUT}/${IMGNAME}
echo "Done, created ${ANDROID_PRODUCT_OUT}/${IMGNAME}!"
exit 0

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "AndroidRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "AndroidTvRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -17,17 +17,6 @@
-->
<resources>
<!-- User activity timeout: Minimum screen off timeout in milliseconds.
Sets a lower bound for the {@link Settings.System#SCREEN_OFF_TIMEOUT} setting
which determines how soon the device will go to sleep when there is no
user activity.
This value must be greater than zero, otherwise the device will immediately
fall asleep again as soon as it is awoken.
-->
<integer name="config_minimumScreenOffTimeout">86400000</integer>
<!-- Default screen brightness setting.
Must be in the range specified by minimum and maximum. -->
<integer name="config_screenBrightnessSettingDefault">128</integer>

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "CarServiceRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -18,6 +18,39 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!--
Specifies configuration of displays in system telling its usage / type and assigned
occupant. DEFAULT_DISPLAY, if assigned here, should be always assigned to the DRIVER zone.
Some examples are:
<item>displayPort=0,displayType=MAIN,occupantZoneId=0,inputTypes=DPAD_KEYS|
NAVIGATE_KEYS|ROTARY_NAVIGATION</item>
<item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0,
inputTypes=DPAD_KEYS</item>
<item>displayPort=2,displayType=MAIN,occupantZoneId=1,
inputTypes=TOUCH_SCREEN</item>
<item>displayPort=3,displayType=MAIN,occupantZoneId=2,
inputTypes=TOUCH_SCREEN</item>
<item>displayUniqueId=virtual:com.example:MainD,displayType=MAIN,occupantZoneId=3,
inputTypes=TOUCH_SCREEN</item>
NOTE: each item should have displayPort or displayUniqueId, if it has both, displayPort
will be used.
displayPort: Unique Port id for the physical display.
displayUniqueId: Unique Id for the display.
The unique id of the virtual display will be the form of 'virtual:<PACKAGE>:<ID>'.
displayType: Display type for the display. Use * part from
CarOccupantZoneManager.DISPLAY_TYPE_* like MAIN, INSTRUMENT_CLUSTER and
etc.
occupantZoneId: occupantZoneId specified from config_occupant_zones.
inputTypes: supported input types for the corresponding display.
-->
<string-array translatable="false" name="config_occupant_display_mapping">
<item>displayPort=0,displayType=MAIN,occupantZoneId=0,inputTypes=TOUCH_SCREEN|DPAD_KEYS|NAVIGATE_KEYS|ROTARY_NAVIGATION</item>
<item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0,inputTypes=DPAD_KEYS</item>
</string-array>
<!-- Specifies notice UI that will be launched when user starts a car or do user
switching. It is recommended to use dialog with at least TYPE_APPLICATION_OVERLAY window
type to show the UI regardless of activity launches. Target package will be auto-granted

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "SettingsProviderRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -18,6 +18,9 @@
<resources>
<!-- Default for Settings.Global.DEVICE_NAME $1=MODEL -->
<string name="def_device_name_simple">Raspberry Pi 5</string>
<!-- Default screen brightness -->
<integer name="def_screen_brightness">128</integer>

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "SettingsProviderTvRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -18,6 +18,9 @@
<resources>
<!-- Default for Settings.Global.DEVICE_NAME $1=MODEL -->
<string name="def_device_name_simple">Raspberry Pi 5</string>
<!-- Default screen brightness -->
<integer name="def_screen_brightness">128</integer>

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "SettingsRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "SystemUIRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -6,5 +6,5 @@ runtime_resource_overlay {
name: "WifiRpiOverlay",
resource_dirs: ["res"],
sdk_version: "current",
proprietary: true
vendor: true,
}

View File

@@ -24,4 +24,13 @@
is no longer indicative, and a separate config now exists for each band -->
<bool translatable="false" name ="config_wifi5ghzSupport">true</bool>
<!-- Boolean indicating whether the wifi chipset supports background scanning mechanism.
This mechanism allows the host to remain in suspend state and the dongle to actively
scan and wake the host when a configured SSID is detected by the dongle. This chipset
capability can provide power savings when wifi needs to be always kept on. -->
<bool translatable="false" name="config_wifi_background_scan_support">true</bool>
<!-- Do not translate. Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default" translatable="false">Raspberry Pi 5</string>
</resources>

View File

@@ -11,12 +11,3 @@ on post-fs-data
mkdir /data/vendor/wifi 0770 wifi wifi
mkdir /data/vendor/wifi/wpa 0770 wifi wifi
mkdir /data/vendor/wifi/wpa/sockets 0770 wifi wifi
on property:sys.boot_completed=1
# Reinit lmkd to reconfigure lmkd properties
setprop lmkd.reinit 1
service suspend_blocker_rpi /vendor/bin/suspend_blocker_rpi
class early_hal # Start together with system_suspend HAL
group system
user root

View File

@@ -17,8 +17,5 @@
/dev/media2 0660 media media
/dev/video19 0660 media media
# ION
/dev/ion 0664 system system
# USB
/sys/class/udc/1000480000.usb current_speed 0664 system system

View File

@@ -1,4 +1,3 @@
# device specific syscalls
mkdirat: 1
sched_getaffinity: 1
sysinfo: 1

View File

@@ -4,6 +4,7 @@
# CEC
/dev/cec0 u:object_r:cec_device:s0
/dev/cec1 u:object_r:cec_device:s0
/vendor/bin/hw/android\.hardware\.tv\.cec@1\.0-service\.rpi u:object_r:hal_tv_cec_default_exec:s0
# DRM
/vendor/bin/hw/android\.hardware\.drm-service\.clearkey u:object_r:hal_drm_clearkey_exec:s0
@@ -16,18 +17,15 @@
/vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service\.software u:object_r:hal_gatekeeper_default_exec:s0
# Graphics
/dev/dri u:object_r:gpu_device:s0
/dev/dri/card0 u:object_r:gpu_device:s0
/dev/dri/card1 u:object_r:gpu_device:s0
/dev/dri/renderD128 u:object_r:gpu_device:s0
/dev/dri(/.*)? u:object_r:gpu_device:s0
/vendor/bin/hw/android\.hardware\.graphics\.allocator@4\.0-service\.minigbm_gbm_mesa u:object_r:hal_graphics_allocator_default_exec:s0
/vendor/lib(64)?/dri/libgallium_dri\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/android\.hardware\.graphics.mapper@4\.0-impl\.minigbm_gbm_mesa\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/hw/vulkan\.broadcom\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/dri_gbm\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libdrm\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libgallium_dri\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libgbm_mesa\.so u:object_r:same_process_hal_file:s0
/vendor/lib{64}?/libgbm_mesa_wrapper\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libglapi\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libgbm_mesa_wrapper\.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libminigbm_gralloc_gbm_mesa\.so u:object_r:same_process_hal_file:s0
# Health
@@ -48,4 +46,4 @@
/vendor/bin/suspend_blocker_rpi u:object_r:suspend_blocker_exec:s0
# USB
/vendor/bin/hw/android\.hardware\.usb\.gadget@1\.2-service\.rpi u:object_r:hal_usb_gadget_default_exec:s0
/vendor/bin/hw/android\.hardware\.usb\.gadget-service\.rpi u:object_r:hal_usb_gadget_default_exec:s0

View File

@@ -1,3 +1,6 @@
genfscon sysfs /devices/platform/v3dbus/fec00000.v3d/uevent u:object_r:sysfs_gpu:s0
genfscon sysfs /devices/platform/gpu/uevent u:object_r:sysfs_gpu:s0
genfscon sysfs /firmware/devicetree/base/serial-number u:object_r:sysfs_dt_firmware_android:s0
# Graphics
genfscon sysfs /devices/platform/axi/1002000000.v3d u:object_r:sysfs_gpu:s0
genfscon sysfs /devices/platform/axi/axi:gpu u:object_r:sysfs_gpu:s0
# Serial number
genfscon sysfs /firmware/devicetree/base/serial-number u:object_r:sysfs_dt_firmware_android:s0

View File

@@ -8,7 +8,5 @@ allow cameraserver device:dir r_dir_perms;
allow cameraserver video_device:dir r_dir_perms;
allow cameraserver video_device:chr_file rw_file_perms;
allow hal_camera_default gpu_device:dir { open read search };
allow hal_camera_default gpu_device:chr_file { open read write ioctl map getattr };
allow cameraserver gpu_device:dir { open read write search getattr };
allow cameraserver gpu_device:chr_file { open read write ioctl map getattr };
gpu_access(hal_camera_default)
gpu_access(cameraserver)

View File

@@ -1 +0,0 @@
gpu_access(surfaceflinger)

View File

@@ -1,2 +1 @@
gpu_access(mediaswcodec)
allow mediaswcodec gpu_device:chr_file { getattr ioctl map open read write };

View File

@@ -2,7 +2,8 @@
# gpu_access(client_domain)
# Allow client_domain to communicate with the GPU
define(`gpu_access', `
allow $1 gpu_device:dir { open read search getattr };
allow $1 gpu_device:chr_file { open read getattr ioctl map write };
allow $1 sysfs_gpu:file { getattr open read };
allow $1 gpu_device:dir r_dir_perms;
allow $1 gpu_device:chr_file rw_file_perms;
allow $1 sysfs_gpu:dir r_dir_perms;
allow $1 sysfs_gpu:file r_file_perms;
')

View File

@@ -5,7 +5,8 @@
cc_binary {
name: "suspend_blocker_rpi",
init_rc: ["suspend_blocker_rpi.rc"],
vendor: true,
srcs: ["suspend_blocker_rpi.cpp"],
proprietary: true,
shared_libs: ["libpower"],
}

View File

@@ -0,0 +1,4 @@
service suspend_blocker_rpi /vendor/bin/suspend_blocker_rpi
class early_hal # Start together with system_suspend HAL
group system
user root

View File

@@ -1,23 +1,22 @@
// Copyright (C) 2020 The Android Open Source Project
// Copyright (C) 2023 KonstaKANG
// Copyright (C) 2024 KonstaKANG
//
// SPDX-License-Identifier: Apache-2.0
cc_binary {
name: "android.hardware.usb.gadget@1.2-service.rpi",
name: "android.hardware.usb.gadget-service.rpi",
defaults: ["hidl_defaults"],
relative_install_path: "hw",
init_rc: ["android.hardware.usb.gadget@1.2-service.rpi.rc"],
vintf_fragments: ["android.hardware.usb.gadget@1.2-service.rpi.xml"],
init_rc: ["android.hardware.usb.gadget-service.rpi.rc"],
vintf_fragments: ["android.hardware.usb.gadget-service.rpi.xml"],
vendor: true,
srcs: [
"service.cpp",
"UsbGadget.cpp",
],
shared_libs: [
"android.hardware.usb.gadget@1.0",
"android.hardware.usb.gadget@1.1",
"android.hardware.usb.gadget@1.2",
"android.hardware.usb.gadget-V1-ndk",
"libbinder_ndk",
"libbase",
"libcutils",
"libhardware",
@@ -25,5 +24,5 @@ cc_binary {
"liblog",
"libutils",
],
static_libs: ["libusbconfigfs-2"],
static_libs: ["libusbconfigfs-rpi"],
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2023 KonstaKANG
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
* limitations under the License.
*/
#define LOG_TAG "android.hardware.usb.gadget@1.2-service.rpi"
#define LOG_TAG "android.hardware.usb.gadget-service.rpi"
#include "UsbGadget.h"
#include <dirent.h>
@@ -27,12 +27,11 @@
#include <sys/types.h>
#include <unistd.h>
namespace aidl {
namespace android {
namespace hardware {
namespace usb {
namespace gadget {
namespace V1_2 {
namespace implementation {
UsbGadget::UsbGadget() {
if (access(OS_DESC_PATH, R_OK) != 0) {
@@ -46,16 +45,24 @@ void currentFunctionsAppliedCallback(bool functionsApplied, void* payload) {
gadget->mCurrentUsbFunctionsApplied = functionsApplied;
}
Return<void> UsbGadget::getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) {
Return<void> ret = callback->getCurrentUsbFunctionsCb(
mCurrentUsbFunctions, mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED
: Status::FUNCTIONS_NOT_APPLIED);
if (!ret.isOk()) ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.description().c_str());
ScopedAStatus UsbGadget::getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback>& callback,
int64_t in_transactionId) {
if (callback == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
}
return Void();
ScopedAStatus ret = callback->getCurrentUsbFunctionsCb(
mCurrentUsbFunctions,
mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED : Status::FUNCTIONS_NOT_APPLIED,
in_transactionId);
if (!ret.isOk())
ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.getDescription().c_str());
return ScopedAStatus::ok();
}
Return<void> UsbGadget::getUsbSpeed(const sp<V1_2::IUsbGadgetCallback>& callback) {
ScopedAStatus UsbGadget::getUsbSpeed(const shared_ptr<IUsbGadgetCallback> &callback,
int64_t in_transactionId) {
std::string current_speed;
if (ReadFileToString(SPEED_PATH, &current_speed)) {
current_speed = Trim(current_speed);
@@ -72,144 +79,150 @@ Return<void> UsbGadget::getUsbSpeed(const sp<V1_2::IUsbGadgetCallback>& callback
mUsbSpeed = UsbSpeed::SUPERSPEED_10Gb;
else if (current_speed == "UNKNOWN")
mUsbSpeed = UsbSpeed::UNKNOWN;
else {
/**
* This part is used for USB4 or reserved speed.
*
* If reserved speed is detected, it needs to convert to other speeds.
* For example:
* If the bandwidth of new speed is 7G, adding new if
* statement and set mUsbSpeed to SUPERSPEED.
* If the bandwidth of new speed is 80G, adding new if
* statement and set mUsbSpeed to USB4_GEN3_40Gb.
*/
mUsbSpeed = UsbSpeed::RESERVED_SPEED;
}
else
mUsbSpeed = UsbSpeed::UNKNOWN;
} else {
ALOGE("Fail to read current speed");
mUsbSpeed = UsbSpeed::UNKNOWN;
}
if (callback) {
Return<void> ret = callback->getUsbSpeedCb(mUsbSpeed);
ScopedAStatus ret = callback->getUsbSpeedCb(mUsbSpeed, in_transactionId);
if (!ret.isOk()) ALOGE("Call to getUsbSpeedCb failed %s", ret.description().c_str());
if (!ret.isOk())
ALOGE("Call to getUsbSpeedCb failed %s", ret.getDescription().c_str());
}
return Void();
return ScopedAStatus::ok();
}
V1_0::Status UsbGadget::tearDownGadget() {
if (resetGadget() != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
Status UsbGadget::tearDownGadget() {
if (resetGadget() != Status::SUCCESS) return Status::ERROR;
if (monitorFfs.isMonitorRunning()) {
monitorFfs.reset();
} else {
ALOGI("mMonitor not running");
}
return V1_0::Status::SUCCESS;
return Status::SUCCESS;
}
Return<Status> UsbGadget::reset() {
ScopedAStatus UsbGadget::reset(const shared_ptr<IUsbGadgetCallback> &callback,
int64_t in_transactionId) {
if (!WriteStringToFile("none", PULLUP_PATH)) {
ALOGI("Gadget cannot be pulled down");
return Status::ERROR;
if (callback)
callback->resetCb(Status::ERROR, in_transactionId);
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Error while calling resetCb");
}
usleep(kDisconnectWaitUs);
if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) {
ALOGI("Gadget cannot be pulled up");
return Status::ERROR;
if (callback)
callback->resetCb(Status::ERROR, in_transactionId);
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Error while calling resetCb");
}
return Status::SUCCESS;
if (callback)
callback->resetCb(Status::SUCCESS, in_transactionId);
return ScopedAStatus::ok();
}
static V1_0::Status validateAndSetVidPid(uint64_t functions) {
V1_0::Status ret = V1_0::Status::SUCCESS;
static Status validateAndSetVidPid(uint64_t functions) {
Status ret = Status::SUCCESS;
switch (functions) {
case static_cast<uint64_t>(V1_2::GadgetFunction::MTP):
case GadgetFunction::MTP:
ret = setVidPid("0x18d1", "0x4ee1");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::MTP:
case GadgetFunction::ADB | GadgetFunction::MTP:
ret = setVidPid("0x18d1", "0x4ee2");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::RNDIS):
case GadgetFunction::RNDIS:
ret = setVidPid("0x18d1", "0x4ee3");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::RNDIS:
case GadgetFunction::ADB | GadgetFunction::RNDIS:
ret = setVidPid("0x18d1", "0x4ee4");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::PTP):
case GadgetFunction::PTP:
ret = setVidPid("0x18d1", "0x4ee5");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::PTP:
case GadgetFunction::ADB | GadgetFunction::PTP:
ret = setVidPid("0x18d1", "0x4ee6");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::ADB):
case GadgetFunction::ADB:
ret = setVidPid("0x18d1", "0x4ee7");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::MIDI):
case GadgetFunction::MIDI:
ret = setVidPid("0x18d1", "0x4ee8");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::MIDI:
case GadgetFunction::ADB | GadgetFunction::MIDI:
ret = setVidPid("0x18d1", "0x4ee9");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::NCM):
case GadgetFunction::NCM:
ret = setVidPid("0x18d1", "0x4eeb");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::NCM:
case GadgetFunction::ADB | GadgetFunction::NCM:
ret = setVidPid("0x18d1", "0x4eec");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::ACCESSORY):
case GadgetFunction::ACCESSORY:
ret = setVidPid("0x18d1", "0x2d00");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::ACCESSORY:
case GadgetFunction::ADB | GadgetFunction::ACCESSORY:
ret = setVidPid("0x18d1", "0x2d01");
break;
case static_cast<uint64_t>(V1_2::GadgetFunction::AUDIO_SOURCE):
case GadgetFunction::AUDIO_SOURCE:
ret = setVidPid("0x18d1", "0x2d02");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::AUDIO_SOURCE:
case GadgetFunction::ADB | GadgetFunction::AUDIO_SOURCE:
ret = setVidPid("0x18d1", "0x2d03");
break;
case V1_2::GadgetFunction::ACCESSORY | V1_2::GadgetFunction::AUDIO_SOURCE:
case GadgetFunction::ACCESSORY | GadgetFunction::AUDIO_SOURCE:
ret = setVidPid("0x18d1", "0x2d04");
break;
case V1_2::GadgetFunction::ADB | V1_2::GadgetFunction::ACCESSORY |
V1_2::GadgetFunction::AUDIO_SOURCE:
case GadgetFunction::ADB | GadgetFunction::ACCESSORY |
GadgetFunction::AUDIO_SOURCE:
ret = setVidPid("0x18d1", "0x2d05");
break;
default:
ALOGE("Combination not supported");
ret = V1_0::Status::CONFIGURATION_NOT_SUPPORTED;
ret = Status::CONFIGURATION_NOT_SUPPORTED;
}
return ret;
}
V1_0::Status UsbGadget::setupFunctions(uint64_t functions,
const sp<V1_0::IUsbGadgetCallback>& callback,
uint64_t timeout) {
Status UsbGadget::setupFunctions(long functions,
const shared_ptr<IUsbGadgetCallback> &callback, uint64_t timeout,
int64_t in_transactionId) {
bool ffsEnabled = false;
int i = 0;
if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) !=
V1_0::Status::SUCCESS)
return V1_0::Status::ERROR;
Status::SUCCESS)
return Status::ERROR;
if ((functions & V1_2::GadgetFunction::ADB) != 0) {
if ((functions & GadgetFunction::ADB) != 0) {
ffsEnabled = true;
if (addAdb(&monitorFfs, &i) != V1_0::Status::SUCCESS) return V1_0::Status::ERROR;
if (addAdb(&monitorFfs, &i) != Status::SUCCESS) return Status::ERROR;
}
// Pull up the gadget right away when there are no ffs functions.
if (!ffsEnabled) {
if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) return V1_0::Status::ERROR;
if (!WriteStringToFile(kGadgetName, PULLUP_PATH))
return Status::ERROR;
mCurrentUsbFunctionsApplied = true;
if (callback) callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
return V1_0::Status::SUCCESS;
if (callback)
callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId);
return Status::SUCCESS;
}
monitorFfs.registerFunctionsAppliedCallback(&currentFunctionsAppliedCallback, this);
@@ -222,25 +235,28 @@ V1_0::Status UsbGadget::setupFunctions(uint64_t functions,
if (callback) {
bool pullup = monitorFfs.waitForPullUp(timeout);
Return<void> ret = callback->setCurrentUsbFunctionsCb(
functions, pullup ? V1_0::Status::SUCCESS : V1_0::Status::ERROR);
if (!ret.isOk()) ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str());
ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions,
pullup ? Status::SUCCESS : Status::ERROR,
in_transactionId);
if (!ret.isOk())
ALOGE("setCurrentUsbFunctionsCb error %s", ret.getMessage());
}
return V1_0::Status::SUCCESS;
return Status::SUCCESS;
}
Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,
const sp<V1_0::IUsbGadgetCallback>& callback,
uint64_t timeout) {
ScopedAStatus UsbGadget::setCurrentUsbFunctions(int64_t functions,
const shared_ptr<IUsbGadgetCallback> &callback,
int64_t timeoutMs,
int64_t in_transactionId) {
std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);
mCurrentUsbFunctions = functions;
mCurrentUsbFunctionsApplied = false;
// Unlink the gadget and stop the monitor if running.
V1_0::Status status = tearDownGadget();
if (status != V1_0::Status::SUCCESS) {
Status status = tearDownGadget();
if (status != Status::SUCCESS) {
goto error;
}
@@ -249,39 +265,45 @@ Return<void> UsbGadget::setCurrentUsbFunctions(uint64_t functions,
// Leave the gadget pulled down to give time for the host to sense disconnect.
usleep(kDisconnectWaitUs);
if (functions == static_cast<uint64_t>(V1_2::GadgetFunction::NONE)) {
if (callback == NULL) return Void();
Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, V1_0::Status::SUCCESS);
if (functions == GadgetFunction::NONE) {
if (callback == NULL)
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "callback == NULL");
ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId);
if (!ret.isOk())
ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
return Void();
ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str());
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Error while calling setCurrentUsbFunctionsCb");
}
status = validateAndSetVidPid(functions);
if (status != V1_0::Status::SUCCESS) {
if (status != Status::SUCCESS) {
goto error;
}
status = setupFunctions(functions, callback, timeout);
if (status != V1_0::Status::SUCCESS) {
status = setupFunctions(functions, callback, timeoutMs, in_transactionId);
if (status != Status::SUCCESS) {
goto error;
}
ALOGI("Usb Gadget setcurrent functions called successfully");
return Void();
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Usb Gadget setcurrent functions called successfully");
error:
ALOGI("Usb Gadget setcurrent functions failed");
if (callback == NULL) return Void();
Return<void> ret = callback->setCurrentUsbFunctionsCb(functions, status);
if (callback == NULL)
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Usb Gadget setcurrent functions failed");
ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, status, in_transactionId);
if (!ret.isOk())
ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str());
return Void();
ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str());
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Error while calling setCurrentUsbFunctionsCb");
}
} // namespace implementation
} // namespace V1_2
} // namespace gadget
} // namespace usb
} // namespace hardware
} // namespace android
} // aidl

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2023 KonstaKANG
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,18 @@
* limitations under the License.
*/
#ifndef ANDROID_HARDWARE_USB_GADGET_V1_2_USBGADGET_H
#define ANDROID_HARDWARE_USB_GADGET_V1_2_USBGADGET_H
#pragma once
#include <UsbGadgetCommon.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/hardware/usb/gadget/1.2/IUsbGadget.h>
#include <android/hardware/usb/gadget/1.2/types.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <android-base/strings.h>
#include <aidl/android/hardware/usb/gadget/BnUsbGadget.h>
#include <aidl/android/hardware/usb/gadget/BnUsbGadgetCallback.h>
#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>
#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>
#include <aidl/android/hardware/usb/gadget/IUsbGadgetCallback.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <utils/Log.h>
@@ -36,36 +36,25 @@
#include <string>
#include <thread>
namespace aidl {
namespace android {
namespace hardware {
namespace usb {
namespace gadget {
namespace V1_2 {
namespace implementation {
using ::android::sp;
using ::aidl::android::hardware::usb::gadget::GadgetFunction;
using ::aidl::android::hardware::usb::gadget::IUsbGadgetCallback;
using ::aidl::android::hardware::usb::gadget::IUsbGadget;
using ::aidl::android::hardware::usb::gadget::Status;
using ::aidl::android::hardware::usb::gadget::UsbSpeed;
using ::android::base::GetProperty;
using ::android::base::ReadFileToString;
using ::android::base::SetProperty;
using ::android::base::Trim;
using ::android::base::unique_fd;
using ::android::base::ReadFileToString;
using ::android::base::Trim;
using ::android::base::WriteStringToFile;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::usb::gadget::addAdb;
using ::android::hardware::usb::gadget::addEpollFd;
using ::android::hardware::usb::gadget::getVendorFunctions;
using ::android::hardware::usb::gadget::kDebug;
using ::android::hardware::usb::gadget::kDisconnectWaitUs;
using ::android::hardware::usb::gadget::linkFunction;
using ::android::hardware::usb::gadget::MonitorFfs;
using ::android::hardware::usb::gadget::resetGadget;
using ::android::hardware::usb::gadget::setVidPid;
using ::android::hardware::usb::gadget::unlinkFunctions;
using ::ndk::ScopedAStatus;
using ::std::shared_ptr;
using ::std::string;
constexpr char kGadgetName[] = "1000480000.usb";
@@ -74,36 +63,36 @@ static MonitorFfs monitorFfs(kGadgetName);
#define UDC_PATH "/sys/class/udc/1000480000.usb/"
#define SPEED_PATH UDC_PATH "current_speed"
struct UsbGadget : public IUsbGadget {
struct UsbGadget : public BnUsbGadget {
UsbGadget();
// Makes sure that only one request is processed at a time.
std::mutex mLockSetCurrentFunction;
uint64_t mCurrentUsbFunctions;
long mCurrentUsbFunctions;
bool mCurrentUsbFunctionsApplied;
UsbSpeed mUsbSpeed;
Return<void> setCurrentUsbFunctions(uint64_t functions,
const sp<V1_0::IUsbGadgetCallback>& callback,
uint64_t timeout) override;
ScopedAStatus setCurrentUsbFunctions(int64_t functions,
const shared_ptr<IUsbGadgetCallback> &callback,
int64_t timeoutMs, int64_t in_transactionId) override;
Return<void> getCurrentUsbFunctions(const sp<V1_0::IUsbGadgetCallback>& callback) override;
ScopedAStatus getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback> &callback,
int64_t in_transactionId) override;
Return<Status> reset() override;
ScopedAStatus reset(const shared_ptr<IUsbGadgetCallback> &callback,
int64_t in_transactionId) override;
Return<void> getUsbSpeed(const sp<V1_2::IUsbGadgetCallback>& callback) override;
ScopedAStatus getUsbSpeed(const shared_ptr<IUsbGadgetCallback> &callback,
int64_t in_transactionId) override;
private:
V1_0::Status tearDownGadget();
V1_0::Status setupFunctions(uint64_t functions, const sp<V1_0::IUsbGadgetCallback>& callback,
uint64_t timeout);
Status tearDownGadget();
Status setupFunctions(long functions, const shared_ptr<IUsbGadgetCallback> &callback,
uint64_t timeout, int64_t in_transactionId);
};
} // namespace implementation
} // namespace V1_2
} // namespace gadget
} // namespace usb
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_USB_V1_2_USBGADGET_H
} // aidl

View File

@@ -0,0 +1,4 @@
service vendor.usb_gadget-default /vendor/bin/hw/android.hardware.usb.gadget-service.rpi
class hal
user system
group system shell mtp

View File

@@ -1,8 +1,7 @@
<manifest version="1.0" type="device">
<hal format="hidl">
<hal format="aidl">
<name>android.hardware.usb.gadget</name>
<transport>hwbinder</transport>
<version>1.2</version>
<version>1</version>
<interface>
<name>IUsbGadget</name>
<instance>default</instance>

View File

@@ -1,7 +0,0 @@
service vendor.usb-gadget-hal-1-2 /vendor/bin/hw/android.hardware.usb.gadget@1.2-service.rpi
interface android.hardware.usb.gadget@1.0::IUsbGadget default
interface android.hardware.usb.gadget@1.1::IUsbGadget default
interface android.hardware.usb.gadget@1.2::IUsbGadget default
class hal
user system
group system shell mtp

28
usb/lib/Android.bp Normal file
View File

@@ -0,0 +1,28 @@
// Copyright (C) 2020 The Android Open Source Project
// Copyright (C) 2024 KonstaKANG
//
// SPDX-License-Identifier: Apache-2.0
cc_library_static {
name: "libusbconfigfs-rpi",
vendor_available: true,
export_include_dirs: ["include"],
srcs: [
"UsbGadgetUtils.cpp",
"MonitorFfs.cpp",
],
cflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"android.hardware.usb.gadget-V1-ndk",
"libbase",
"libcutils",
"libhidlbase",
"libutils",
],
}

272
usb/lib/MonitorFfs.cpp Normal file
View File

@@ -0,0 +1,272 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "libusbconfigfs"
#include "include/UsbGadgetCommon.h"
namespace aidl {
namespace android {
namespace hardware {
namespace usb {
namespace gadget {
static volatile bool gadgetPullup;
MonitorFfs::MonitorFfs(const char* const gadget)
: mWatchFd(),
mEndpointList(),
mLock(),
mCv(),
mLockFd(),
mCurrentUsbFunctionsApplied(false),
mMonitor(),
mCallback(NULL),
mPayload(NULL),
mGadgetName(gadget),
mMonitorRunning(false) {
unique_fd eventFd(eventfd(0, 0));
if (eventFd == -1) {
ALOGE("mEventFd failed to create %d", errno);
abort();
}
unique_fd epollFd(epoll_create(2));
if (epollFd == -1) {
ALOGE("mEpollFd failed to create %d", errno);
abort();
}
unique_fd inotifyFd(inotify_init());
if (inotifyFd < 0) {
ALOGE("inotify init failed");
abort();
}
if (addEpollFd(epollFd, inotifyFd) == -1) abort();
if (addEpollFd(epollFd, eventFd) == -1) abort();
mEpollFd = std::move(epollFd);
mInotifyFd = std::move(inotifyFd);
mEventFd = std::move(eventFd);
gadgetPullup = false;
}
static void displayInotifyEvent(struct inotify_event* i) {
ALOGE(" wd =%2d; ", i->wd);
if (i->cookie > 0) ALOGE("cookie =%4d; ", i->cookie);
ALOGE("mask = ");
if (i->mask & IN_ACCESS) ALOGE("IN_ACCESS ");
if (i->mask & IN_ATTRIB) ALOGE("IN_ATTRIB ");
if (i->mask & IN_CLOSE_NOWRITE) ALOGE("IN_CLOSE_NOWRITE ");
if (i->mask & IN_CLOSE_WRITE) ALOGE("IN_CLOSE_WRITE ");
if (i->mask & IN_CREATE) ALOGE("IN_CREATE ");
if (i->mask & IN_DELETE) ALOGE("IN_DELETE ");
if (i->mask & IN_DELETE_SELF) ALOGE("IN_DELETE_SELF ");
if (i->mask & IN_IGNORED) ALOGE("IN_IGNORED ");
if (i->mask & IN_ISDIR) ALOGE("IN_ISDIR ");
if (i->mask & IN_MODIFY) ALOGE("IN_MODIFY ");
if (i->mask & IN_MOVE_SELF) ALOGE("IN_MOVE_SELF ");
if (i->mask & IN_MOVED_FROM) ALOGE("IN_MOVED_FROM ");
if (i->mask & IN_MOVED_TO) ALOGE("IN_MOVED_TO ");
if (i->mask & IN_OPEN) ALOGE("IN_OPEN ");
if (i->mask & IN_Q_OVERFLOW) ALOGE("IN_Q_OVERFLOW ");
if (i->mask & IN_UNMOUNT) ALOGE("IN_UNMOUNT ");
ALOGE("\n");
if (i->len > 0) ALOGE(" name = %s\n", i->name);
}
void* MonitorFfs::startMonitorFd(void* param) {
MonitorFfs* monitorFfs = (MonitorFfs*)param;
char buf[kBufferSize];
bool writeUdc = true, stopMonitor = false;
struct epoll_event events[kEpollEvents];
steady_clock::time_point disconnect;
bool descriptorWritten = true;
for (int i = 0; i < static_cast<int>(monitorFfs->mEndpointList.size()); i++) {
if (access(monitorFfs->mEndpointList.at(i).c_str(), R_OK)) {
descriptorWritten = false;
break;
}
}
// notify here if the endpoints are already present.
if (descriptorWritten) {
usleep(kPullUpDelay);
if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
lock_guard<mutex> lock(monitorFfs->mLock);
monitorFfs->mCurrentUsbFunctionsApplied = true;
monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, monitorFfs->mPayload);
gadgetPullup = true;
writeUdc = false;
ALOGI("GADGET pulled up");
monitorFfs->mCv.notify_all();
}
}
while (!stopMonitor) {
int nrEvents = epoll_wait(monitorFfs->mEpollFd, events, kEpollEvents, -1);
if (nrEvents <= 0) {
ALOGE("epoll wait did not return descriptor number");
continue;
}
for (int i = 0; i < nrEvents; i++) {
ALOGI("event=%u on fd=%d\n", events[i].events, events[i].data.fd);
if (events[i].data.fd == monitorFfs->mInotifyFd) {
// Process all of the events in buffer returned by read().
int numRead = read(monitorFfs->mInotifyFd, buf, kBufferSize);
for (char* p = buf; p < buf + numRead;) {
struct inotify_event* event = (struct inotify_event*)p;
if (kDebug) displayInotifyEvent(event);
p += sizeof(struct inotify_event) + event->len;
bool descriptorPresent = true;
for (int j = 0; j < static_cast<int>(monitorFfs->mEndpointList.size()); j++) {
if (access(monitorFfs->mEndpointList.at(j).c_str(), R_OK)) {
if (kDebug) ALOGI("%s absent", monitorFfs->mEndpointList.at(j).c_str());
descriptorPresent = false;
break;
}
}
if (!descriptorPresent && !writeUdc) {
if (kDebug) ALOGI("endpoints not up");
writeUdc = true;
disconnect = std::chrono::steady_clock::now();
} else if (descriptorPresent && writeUdc) {
steady_clock::time_point temp = steady_clock::now();
if (std::chrono::duration_cast<microseconds>(temp - disconnect).count() <
kPullUpDelay)
usleep(kPullUpDelay);
if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) {
lock_guard<mutex> lock(monitorFfs->mLock);
monitorFfs->mCurrentUsbFunctionsApplied = true;
monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied,
monitorFfs->mPayload);
ALOGI("GADGET pulled up");
writeUdc = false;
gadgetPullup = true;
// notify the main thread to signal userspace.
monitorFfs->mCv.notify_all();
}
}
}
} else {
uint64_t flag;
read(monitorFfs->mEventFd, &flag, sizeof(flag));
if (flag == 100) {
stopMonitor = true;
break;
}
}
}
}
return NULL;
}
void MonitorFfs::reset() {
lock_guard<mutex> lock(mLockFd);
uint64_t flag = 100;
unsigned long ret;
if (mMonitorRunning) {
// Stop the monitor thread by writing into signal fd.
ret = TEMP_FAILURE_RETRY(write(mEventFd, &flag, sizeof(flag)));
if (ret < 0) ALOGE("Error writing eventfd errno=%d", errno);
ALOGI("mMonitor signalled to exit");
mMonitor->join();
ALOGI("mMonitor destroyed");
mMonitorRunning = false;
}
for (std::vector<int>::size_type i = 0; i != mWatchFd.size(); i++)
inotify_rm_watch(mInotifyFd, mWatchFd[i]);
mEndpointList.clear();
gadgetPullup = false;
mCallback = NULL;
mPayload = NULL;
}
bool MonitorFfs::startMonitor() {
mMonitor = unique_ptr<thread>(new thread(this->startMonitorFd, this));
mMonitorRunning = true;
return true;
}
bool MonitorFfs::isMonitorRunning() {
return mMonitorRunning;
}
bool MonitorFfs::waitForPullUp(int timeout_ms) {
std::unique_lock<std::mutex> lk(mLock);
if (gadgetPullup) return true;
if (mCv.wait_for(lk, timeout_ms * 1ms, [] { return gadgetPullup; })) {
ALOGI("monitorFfs signalled true");
return true;
} else {
ALOGI("monitorFfs signalled error");
// continue monitoring as the descriptors might be written at a later
// point.
return false;
}
}
bool MonitorFfs::addInotifyFd(string fd) {
lock_guard<mutex> lock(mLockFd);
int wfd;
wfd = inotify_add_watch(mInotifyFd, fd.c_str(), IN_ALL_EVENTS);
if (wfd == -1)
return false;
else
mWatchFd.push_back(wfd);
return true;
}
void MonitorFfs::addEndPoint(string ep) {
lock_guard<mutex> lock(mLockFd);
mEndpointList.push_back(ep);
}
void MonitorFfs::registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied,
void* payload),
void* payload) {
mCallback = callback;
mPayload = payload;
}
} // namespace gadget
} // namespace usb
} // namespace hardware
} // namespace android
} // namespace aidl

210
usb/lib/UsbGadgetUtils.cpp Normal file
View File

@@ -0,0 +1,210 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "libusbconfigfs"
#include "include/UsbGadgetCommon.h"
namespace aidl {
namespace android {
namespace hardware {
namespace usb {
namespace gadget {
int unlinkFunctions(const char* path) {
DIR* config = opendir(path);
struct dirent* function;
char filepath[kMaxFilePathLength];
int ret = 0;
if (config == NULL) return -1;
// d_type does not seems to be supported in /config
// so filtering by name.
while (((function = readdir(config)) != NULL)) {
if ((strstr(function->d_name, FUNCTION_NAME) == NULL)) continue;
// build the path for each file in the folder.
sprintf(filepath, "%s/%s", path, function->d_name);
ret = remove(filepath);
if (ret) {
ALOGE("Unable remove file %s errno:%d", filepath, errno);
break;
}
}
closedir(config);
return ret;
}
int addEpollFd(const unique_fd& epfd, const unique_fd& fd) {
struct epoll_event event;
int ret;
event.data.fd = fd;
event.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
if (ret) ALOGE("epoll_ctl error %d", errno);
return ret;
}
int linkFunction(const char* function, int index) {
char functionPath[kMaxFilePathLength];
char link[kMaxFilePathLength];
sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function);
sprintf(link, "%s%d", FUNCTION_PATH, index);
if (symlink(functionPath, link)) {
ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno);
return -1;
}
return 0;
}
Status setVidPid(const char* vid, const char* pid) {
if (!WriteStringToFile(vid, VENDOR_ID_PATH)) return Status::ERROR;
if (!WriteStringToFile(pid, PRODUCT_ID_PATH)) return Status::ERROR;
return Status::SUCCESS;
}
std::string getVendorFunctions() {
if (GetProperty(kBuildType, "") == "user") return "user";
std::string bootMode = GetProperty(PERSISTENT_BOOT_MODE, "");
std::string persistVendorFunctions = GetProperty(kPersistentVendorConfig, "");
std::string vendorFunctions = GetProperty(kVendorConfig, "");
std::string ret = "";
if (vendorFunctions != "") {
ret = vendorFunctions;
} else if (bootMode == "usbradio" || bootMode == "factory" || bootMode == "ffbm-00" ||
bootMode == "ffbm-01") {
if (persistVendorFunctions != "")
ret = persistVendorFunctions;
else
ret = "diag";
// vendor.usb.config will reflect the current configured functions
SetProperty(kVendorConfig, ret);
}
return ret;
}
Status resetGadget() {
ALOGI("setCurrentUsbFunctions None");
if (!WriteStringToFile("none", PULLUP_PATH)) ALOGI("Gadget cannot be pulled down");
if (!WriteStringToFile("0", DEVICE_CLASS_PATH)) return Status::ERROR;
if (!WriteStringToFile("0", DEVICE_SUB_CLASS_PATH)) return Status::ERROR;
if (!WriteStringToFile("0", DEVICE_PROTOCOL_PATH)) return Status::ERROR;
if (!WriteStringToFile("0", DESC_USE_PATH)) return Status::ERROR;
if (unlinkFunctions(CONFIG_PATH)) return Status::ERROR;
return Status::SUCCESS;
}
Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
int* functionCount) {
if (((functions & GadgetFunction::MTP) != 0)) {
*ffsEnabled = true;
ALOGI("setCurrentUsbFunctions mtp");
if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR;
if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR;
// Add endpoints to be monitored.
monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1");
monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2");
monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3");
} else if (((functions & GadgetFunction::PTP) != 0)) {
*ffsEnabled = true;
ALOGI("setCurrentUsbFunctions ptp");
if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR;
if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR;
if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR;
// Add endpoints to be monitored.
monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1");
monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2");
monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3");
}
if ((functions & GadgetFunction::MIDI) != 0) {
ALOGI("setCurrentUsbFunctions MIDI");
if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR;
}
if ((functions & GadgetFunction::ACCESSORY) != 0) {
ALOGI("setCurrentUsbFunctions Accessory");
if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR;
}
if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) {
ALOGI("setCurrentUsbFunctions Audio Source");
if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR;
}
if ((functions & GadgetFunction::RNDIS) != 0) {
ALOGI("setCurrentUsbFunctions rndis");
std::string rndisFunction = GetProperty(kVendorRndisConfig, "");
if (rndisFunction != "") {
if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR;
} else {
// link gsi.rndis for older pixel projects
if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR;
}
}
if ((functions & GadgetFunction::NCM) != 0) {
ALOGI("setCurrentUsbFunctions ncm");
if (linkFunction("ncm.gs6", (*functionCount)++)) return Status::ERROR;
}
return Status::SUCCESS;
}
Status addAdb(MonitorFfs* monitorFfs, int* functionCount) {
ALOGI("setCurrentUsbFunctions Adb");
if (!WriteStringToFile("1", DESC_USE_PATH))
return Status::ERROR;
if (!monitorFfs->addInotifyFd("/dev/usb-ffs/adb/")) return Status::ERROR;
if (linkFunction("ffs.adb", (*functionCount)++)) return Status::ERROR;
monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep1");
monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep2");
ALOGI("Service started");
return Status::SUCCESS;
}
} // namespace gadget
} // namespace usb
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,182 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef HARDWARE_USB_USBGADGETCOMMON_H
#define HARDWARE_USB_USBGADGETCOMMON_H
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>
#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/inotify.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/Log.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
namespace aidl {
namespace android {
namespace hardware {
namespace usb {
namespace gadget {
constexpr int kBufferSize = 512;
constexpr int kMaxFilePathLength = 256;
constexpr int kEpollEvents = 10;
constexpr bool kDebug = false;
constexpr int kDisconnectWaitUs = 100000;
constexpr int kPullUpDelay = 500000;
constexpr int kShutdownMonitor = 100;
constexpr char kBuildType[] = "ro.build.type";
constexpr char kPersistentVendorConfig[] = "persist.vendor.usb.usbradio.config";
constexpr char kVendorConfig[] = "vendor.usb.config";
constexpr char kVendorRndisConfig[] = "vendor.usb.rndis.config";
#define GADGET_PATH "/config/usb_gadget/g1/"
#define PULLUP_PATH GADGET_PATH "UDC"
#define PERSISTENT_BOOT_MODE "ro.bootmode"
#define VENDOR_ID_PATH GADGET_PATH "idVendor"
#define PRODUCT_ID_PATH GADGET_PATH "idProduct"
#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass"
#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass"
#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol"
#define DESC_USE_PATH GADGET_PATH "os_desc/use"
#define OS_DESC_PATH GADGET_PATH "os_desc/b.1"
#define CONFIG_PATH GADGET_PATH "configs/b.1/"
#define FUNCTIONS_PATH GADGET_PATH "functions/"
#define FUNCTION_NAME "function"
#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME
#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis"
using ::android::base::GetProperty;
using ::android::base::SetProperty;
using ::android::base::unique_fd;
using ::android::base::WriteStringToFile;
using ::aidl::android::hardware::usb::gadget::GadgetFunction;
using ::aidl::android::hardware::usb::gadget::Status;
using ::std::lock_guard;
using ::std::move;
using ::std::mutex;
using ::std::string;
using ::std::thread;
using ::std::unique_ptr;
using ::std::vector;
using ::std::chrono::microseconds;
using ::std::chrono::steady_clock;
using ::std::literals::chrono_literals::operator""ms;
// MonitorFfs automously manages gadget pullup by monitoring
// the ep file status. Restarts the usb gadget when the ep
// owner restarts.
class MonitorFfs {
private:
// Monitors the endpoints Inotify events.
unique_fd mInotifyFd;
// Control pipe for shutting down the mMonitor thread.
// mMonitor exits when SHUTDOWN_MONITOR is written into
// mEventFd/
unique_fd mEventFd;
// Pools on mInotifyFd and mEventFd.
unique_fd mEpollFd;
vector<int> mWatchFd;
// Maintains the list of Endpoints.
vector<string> mEndpointList;
// protects the CV.
std::mutex mLock;
std::condition_variable mCv;
// protects mInotifyFd, mEpollFd.
std::mutex mLockFd;
// Flag to maintain the current status of gadget pullup.
bool mCurrentUsbFunctionsApplied;
// Thread object that executes the ep monitoring logic.
unique_ptr<thread> mMonitor;
// Callback to be invoked when gadget is pulled up.
void (*mCallback)(bool functionsApplied, void* payload);
void* mPayload;
// Name of the USB gadget. Used for pullup.
const char* const mGadgetName;
// Monitor State
bool mMonitorRunning;
public:
MonitorFfs(const char* const gadget);
// Inits all the UniqueFds.
void reset();
// Starts monitoring endpoints and pullup the gadget when
// the descriptors are written.
bool startMonitor();
// Waits for timeout_ms for gadget pull up to happen.
// Returns immediately if the gadget is already pulled up.
bool waitForPullUp(int timeout_ms);
// Adds the given fd to the watch list.
bool addInotifyFd(string fd);
// Adds the given endpoint to the watch list.
void addEndPoint(string ep);
// Registers the async callback from the caller to notify the caller
// when the gadget pull up happens.
void registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied, void*(payload)),
void* payload);
bool isMonitorRunning();
// Ep monitoring and the gadget pull up logic.
static void* startMonitorFd(void* param);
};
//**************** Helper functions ************************//
// Adds the given fd to the epollfd(epfd).
int addEpollFd(const unique_fd& epfd, const unique_fd& fd);
// Removes all the usb functions link in the specified path.
int unlinkFunctions(const char* path);
// Craetes a configfs link for the function.
int linkFunction(const char* function, int index);
// Sets the USB VID and PID.
Status setVidPid(const char* vid, const char* pid);
// Extracts vendor functions from the vendor init properties.
std::string getVendorFunctions();
// Adds Adb to the usb configuration.
Status addAdb(MonitorFfs* monitorFfs, int* functionCount);
// Adds all applicable generic android usb functions other than ADB.
Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled,
int* functionCount);
// Pulls down USB gadget.
Status resetGadget();
} // namespace gadget
} // namespace usb
} // namespace hardware
} // namespace android
} // namespace aidl
#endif

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2023 KonstaKANG
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2024 KonstaKANG
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,39 +15,19 @@
* limitations under the License.
*/
#define LOG_TAG "android.hardware.usb.gadget@1.2-service.rpi"
#include <hidl/HidlTransportSupport.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "UsbGadget.h"
using android::sp;
// libhwbinder:
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
// Generated HIDL files
using android::hardware::usb::gadget::V1_2::IUsbGadget;
using android::hardware::usb::gadget::V1_2::implementation::UsbGadget;
using android::OK;
using android::status_t;
using ::aidl::android::hardware::usb::gadget::UsbGadget;
int main() {
configureRpcThreadpool(1, true /*callerWillJoin*/);
android::sp<IUsbGadget> service = new UsbGadget();
status_t status = service->registerAsService();
if (status != OK) {
ALOGE("Cannot register USB Gadget HAL service");
return 1;
}
ALOGI("USB Gadget HAL Ready.");
joinRpcThreadpool();
// Under noraml cases, execution will not reach this line.
ALOGI("USB Gadget HAL failed to join thread pool.");
return 1;
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<UsbGadget> usbgadget = ndk::SharedRefBase::make<UsbGadget>();
const std::string instance = std::string() + UsbGadget::descriptor + "/default";
binder_status_t status = AServiceManager_addService(usbgadget->asBinder().get(), instance.c_str());
CHECK(status == STATUS_OK);
ABinderProcess_joinThreadPool();
return -1;
}

View File

@@ -1,14 +1,14 @@
# Audio
persist.audio.device=dac
persist.audio.hdmi.device=vc4hdmi0
persist.audio.pcm.card=0
persist.audio.pcm.device=0
persist.audio.pcm.card.auto=true
ro.config.media_vol_default=20
ro.config.media_vol_steps=25
ro.hardware.audio.primary=rpi_hdmi
# Bluetooth
bluetooth.device.class_of_device?=90,2,12
bluetooth.device.default_name=Raspberry Pi 5
bluetooth.profile.a2dp.source.enabled?=true
bluetooth.profile.asha.central.enabled?=true
bluetooth.profile.avrcp.target.enabled?=true
@@ -33,13 +33,16 @@ bluetooth.profile.vcp.controller.enabled?=true
persist.bluetooth.a2dp_aac.vbr_supported=true
# Camera
media.settings.xml=/vendor/etc/media_profiles_V1_0.xml
ro.hardware.camera=libcamera
# CEC
ro.hdmi.cec_device=cec0
persist.hdmi.cec_device=cec0
ro.hdmi.device_type=4
# Chipset
ro.soc.manufacturer=Broadcom
ro.soc.model=BCM2712
# Display
debug.drm.mode.force=1920x1080
@@ -53,21 +56,10 @@ ro.hardware.egl=mesa
ro.hardware.hwcomposer=drm
ro.hardware.vulkan=broadcom
ro.opengles.version=196609
ro.vendor.hwc.drm.use_config_groups=0
vendor.hwc.backend_override=rpi5
vendor.hwc.drm.ctm=DRM_OR_IGNORE
# LMKD
ro.lmk.critical=0
ro.lmk.critical_upgrade=false
ro.lmk.downgrade_pressure=100
ro.lmk.kill_heaviest_task=true
ro.lmk.kill_timeout_ms=100
ro.lmk.log_stats=true
ro.lmk.low=1001
ro.lmk.medium=800
ro.lmk.upgrade_pressure=100
ro.lmk.use_minfree_levels=true
# Lockscreen
ro.lockscreen.disable.default=true
@@ -85,4 +77,3 @@ wifi.interface=wlan0
# Window extensions
persist.settings.large_screen_opt.enabled=true

136
wrimg.sh Executable file
View File

@@ -0,0 +1,136 @@
#!/bin/bash
#
# Copyright (C) 2025 KonstaKANG
#
# SPDX-License-Identifier: Apache-2.0
#
exit_with_error() {
echo $@
exit 1
}
check_device() {
for PARTITION in "1" "2" "3" "4"; do
if [ ! -b /dev/${1}${PARTITION} ]; then
return 1
fi
done
PARTITION1=$(lsblk -o LABEL,SIZE -b /dev/${1}1 | tail -n 1)
PARTITION2=$(lsblk -o LABEL,SIZE -b /dev/${1}2 | tail -n 1)
PARTITION3=$(lsblk -o LABEL,SIZE -b /dev/${1}3 | tail -n 1)
PARTITION4=$(lsblk -o LABEL,SIZE -b /dev/${1}4 | tail -n 1)
if [ $(echo ${PARTITION1} | awk {'print $1'}) != "boot" ] || [ $(echo ${PARTITION1} | awk {'print $2'}) != "134217728" ]; then
return 1
fi
if [ $(echo ${PARTITION2} | awk {'print $1'}) != "/" ] || [ $(echo ${PARTITION2} | awk {'print $2'}) != "2684354560" ]; then
return 1
fi
if [ $(echo ${PARTITION3} | awk {'print $1'}) != "vendor" ] || [ $(echo ${PARTITION3} | awk {'print $2'}) != "268435456" ]; then
return 1
fi
if [ $(echo ${PARTITION4} | awk {'print $1'}) != "userdata" ]; then
return 1
fi
DEVICE=${1}
return 0
}
find_device() {
for SDX in "sda" "sdb" "sdc" "sdd" "sde" "sdf"; do
check_device ${SDX}
if [ $? == "0" ]; then
break
fi
done
if [ -z ${DEVICE} ]; then
exit_with_error "Unable to find suitable block device!"
fi
}
confirm() {
echo "Build target ${1}..."
if [ "${2}" == "wipe" ]; then
echo "Wiping userdata partition..."
else
echo "Writing ${2} image..."
fi
echo "Writing to device /dev/${DEVICE}..."
lsblk -o NAME,LABEL,SIZE /dev/${DEVICE}
read -p "Continue (y/n)? " -n 1 -r RESPONSE
echo ""
if [[ ! ${RESPONSE} =~ ^[Yy]$ ]]; then
exit_with_error "Exiting!"
fi
}
write_partition() {
if [ ! -f ${ANDROID_PRODUCT_OUT}/${1}.img ]; then
exit_with_error "Partition image not found. Run 'make ${1}image' first."
fi
echo "Copying ${1}..."
sudo umount /dev/${DEVICE}${2}
sudo dd if=${ANDROID_PRODUCT_OUT}/${1}.img of=/dev/${DEVICE}${2} bs=1M
}
wipe_userdata() {
echo "Creating userdata..."
sudo umount /dev/${DEVICE}4
sudo wipefs -a /dev/${DEVICE}4
sudo mkfs.ext4 /dev/${DEVICE}4 -I 512 -L userdata
}
finish() {
sync
echo "Done!"
exit 0
}
if [ -z ${TARGET_PRODUCT} ]; then
exit_with_error "TARGET_PRODUCT environment variable is not set. Run lunch first."
fi
if [ -z ${ANDROID_PRODUCT_OUT} ]; then
exit_with_error "ANDROID_PRODUCT_OUT environment variable is not set. Run lunch first."
fi
TARGET=$(echo ${TARGET_PRODUCT} | sed 's/^aosp_//')
DEVICE=
if [ -z $1 ]; then
find_device
confirm ${TARGET} "boot, system, and vendor"
write_partition boot 1
write_partition system 2
write_partition vendor 3
finish
elif [ ! -z $1 ] && [ $1 == "boot" ]; then
find_device
confirm ${TARGET} "boot"
write_partition boot 1
finish
elif [ ! -z $1 ] && [ $1 == "system" ]; then
find_device
confirm ${TARGET} "system"
write_partition system 2
finish
elif [ ! -z $1 ] && [ $1 == "vendor" ]; then
find_device
confirm ${TARGET} "vendor"
write_partition vendor 3
finish
elif [ ! -z $1 ] && [ $1 == "wipe" ]; then
find_device
confirm ${TARGET} "wipe"
wipe_userdata
finish
else
exit_with_error "Usage: $0 [boot|system|vendor|wipe]"
fi