悠笙の开发日记 - 开发
type: channel
link: https://www.iyoroy.cn/category/Develop/
items: 为Typecho配置简易多语言方案
items: 跨平台服务编写日记 Ep.2 进程间通讯（IPC）
items: 跨平台服务编写日记 Ep.1 统一的日志管理
items: C++使用WINAPI查询DNS解析记录
items: 小米845系列机器类原生调用红外摄像头进行人脸识别
items: 给Android 4.9内核添加KernelSU支持
items: 记一次为Android 4.9内核的ROM启用erofs支持

为Typecho配置简易多语言方案
type: item
date: 2025-11-19T14:10:00+00:00
description: 想着给博客做一下国际化支持，为每篇文章和每个页面单独配置一个英文版本，但是上网查了一圈发现Typecho对国际化的支持并不好，最终自己设计了一套方案，写这篇文章记录下来 本文默认你对PHP、Nginx、Typecho的基础逻辑有部分了解 分析 需求 需要为每个文章和页面都配置中英双语 需要配置一个语言选择器，使得前端可以快速切换语言 需要搜索引擎可以正确识别并收录多语言版本的文章 大致方案 中英文文章的区分大致有这两种方案： 作为一个单独的参数，如访问文章时用/?lang=zh-CN和/?lang=en-US来区分，但是这种方案实现起来比较困难，同时对搜索引擎收录也不太好； 通过URL路径区分，如访问https://<host>/article是中文页面，访问https://<host>/en/article是英文页面，这种配置起来比较简单，直接分成两个Typecho实例即可，并且对搜索引擎友好。问题是评论和浏览量统计需要手动同步。 总结下来，我选择了第二种方案，并且打算直接通过在/en/新建一个Typecho实例实现多语言支持。 具体实现 首先将博客实例复制两份，一份中文一份英文，然后分别翻译成英文； 修改前端代码，实现语言选择器； 要保证两边文章URL只有一个/en之差，就得保证中文站点和英文站点文章的cid相同，而这个cid是根据创建文章和附件的顺序来的，因此打算编写一个同步插件，中文端发文章的时候自动在英文数据库中插入一条对应cid的文章； 改写SiteMap插件，因为sitemap不能既包含页面链接又包含其他sitemap的引用，因此主站需要创建两个sitemap，一个是收录中文站页面的主sitemap，另一个是索引，负责索引中文和英文的sitemap； 在 中添加hreflang属性来告知搜索引擎如何处理多语言； 将英文端的访问量和点赞量链接到中文的数据库； 将英文端的评论链接到中文数据库。 开整 创建英文实例 将整个网站复制一份，放在原先的网站根目录的/en/文件夹下，同时将数据库也复制一份，我这里命名成typecho_en。 接着，分别为两个实例配置伪静态： location /en/ { if (!-e $request_filename) { rewrite ^(.*)$ /en/index.php$1 last; } } location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php$1 last; } } 关于为什么要将中文的主实例也用location括起来，是因为实际测试的时候发现如果不括起来就会将英文实例也当作中文实例的一部分解析，造成404。 同时，修改<webroot>/en/config.inc.php里的数据库配置，指向英文实例的数据库。 这个时候，访问<host>/en/应该能看到一个和中文主站一模一样的站点。 修改Typecho语言 这一步其实感觉可以不要，因为基本上前端页面的语言都是由主题决定的，将Typecho本身语言设置为英文没什么大用，但是为了统一，还是选择改了一下（这样可以一眼看出来是哪个实例的后台（笑）） 直接参考GitHub上typecho官方的多语言支持即可，从Release里下载语言包，然后解压到<webroot>/en/usr/langs/下，然后进入https://<host>/en/admin/options-general.php，就能看见语言设置选项，改为English即可。 修改主题语言 最繁琐的一步，我使用的是Joe，进入<webroot>/en/usr/themes/Joe，将所有你能看到的显示相关的中文都翻译成英文，这里没什么非常方便的方法，用翻译工具的话翻出来会很别扭，我还是选择了手动翻译。 需要注意的是，有一部分的前端配置是在js中的，不止是PHP源文件，这些都需要翻译。 翻译文章 这一步没啥好说的，把/en/下的文章一篇篇翻译成英文，保存 配置文章同步发布 这一步是为了保持两边文章的cid同步，因为cid和访问url有关，保持cid同步可以使下文配置语言选择器一步更加简单，只要在host后加个/en或者去掉就行了。 cid是数据库中typecho_contents表的一个自增字段，同时也是主键，他的分配和typecho的文章、附件等有关。因为上传的文件也会占用cid，而我打算将所有附件都上传到中文站，因此如果不做特殊处理两边的cid容易匹配不上，增加后续工作量。 因此我选择的方案是用AI编写一个插件，当中文站发布文章的时候自动触发，读取中文站为其分配的cid并写入到英文站的数据库中。 创建文件<webroot>/usr/plugins/SyncToEnglish/Plugin…

跨平台服务编写日记 Ep.2 进程间通讯（IPC）
type: item
date: 2025-05-19T03:54:00+00:00
description: 前情提要 上一篇文章实现了统一的日志管理，这篇文章来实现进程间消息通讯，即IPC。 分析 Windows 在Windows下，主要通过管道（Pipe）实现进程间通讯。管道又分为命名管道（Named Pipe）和匿名管道（Anonymous Pipe）。其中，匿名管道是单向的，通常在父进程和子进程之间通讯[2]。而命名管道则可以是单向管道或双工管道，并且支持一对多通讯[3]。顾名思义，识别命名管道的唯一方式是它的名称，因此两个进程只要都连接到同一个名字的命名管道即可实现通信。 我们需要实现进程间的双向通讯，因此采用命名管道。大致思路就是：进程作为伺服模式，也就是接收端启动时创建一个线程，创建一个命名管道并监听管道内消息。当管道被连接时从中读取管道内数据；当进程作为发送端启动时尝试连接到同一个名称的管道，并写入消息内容。 Linux 在Linux下通常使用socket进行进程间通讯。不过不同于监听端口，进程间通讯一般会选择监听一个sock文件[5]，常见的服务类应用如docker daemon、mysql都是通过这种方式。 因此，大致思路如下：作为伺服模式启动的进程创建一个socket监听，并等待从中接收消息；发送端连接到socket套接字并发送消息。和上文命名管道的名称类似，socket套接字会映射一个唯一的.sock文件，发送方只要打开这个文件即可发送消息。（实际上打开方式不是常规的打开文件，而是用socket专用的打开方式[5]） 代码实现 初始化 为了实现共用一套主代码，我使用了和上一篇文章中一样的通过宏定义区分系统类型的方案，将Windows和Linux的代码分别写在service-windows.h和service-linux.h两个头文件中： #ifdef _WIN32 #include "service-windows.h" #elif defined(__linux__) #include "service-linux.h" #endif 当接收端进程启动时，创建一个线程处理收信息（使用std::thread作为多线程库）： thread_bind = std::thread(bind_thread_main); 监听部分 Windows 在Windows下，只要尝试从指定名称的命名管道读取数据即可。其中，因为设置了管道为等待模式（即下文中CreateNamedPipe的第三个参数DWORD dwPipeMode中设置了PIPE_WAIT），ConnectNamedPipe会是阻塞模式，因此不用担心不断循环造成的性能损失。 void bind_thread_main() { while (!exit_requested.load()) { HANDLE hPipe = CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, // Output buffer size 1024, // Input buffer size 0, // Default timeout NULL); if (hPipe == INVALID_HANDLE_VALUE) { service_log.push(LEVEL_WARN, "Failed to create pipe: %d", GetLastError()); continue; } if (ConnectNamedPipe(hPipe, NULL) || GetLastError() == ERROR_PIPE_CONNECTED) { char buffer[1024]; DWORD bytesRead; if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) { buffer[bytesRead] = '\0'; m_queueMsg.push(buffer); service_log.push(LEVEL_VERBOSE, "Message received: %s", buffer); } FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); CloseHandle(hPipe); } else { CloseHandle(hPipe); } } } Linux 为了防止创建失败，在创建前会先尝试删除没有清理干净的sock文件，即代码中unlink(SOCKET_PATH)。SOCKET_PATH为全局变量，定义了套接字文件的路径…

跨平台服务编写日记 Ep.1 统一的日志管理
type: item
date: 2025-04-18T16:19:00+00:00
description: 前阵子心血来潮，想为在使用的一个跨平台的控制台服务类应用程序编写一个自己的管理程序，加一些功能。因而设计了一套简单的服务运行流程。 {alert type="warning"} 本系列文章中的观点和方案为我根据自己已有的知识储备结合DeepSeek的帮助所归纳设计，并未经过严格测试，并不保证在生产环境中使用的可行性和稳定性 {/alert} 大致思路 大致分为个线程，分别用作： 日志记录 目标应用实例管理，可能不止一个线程 监听IPC消息 处理IPC收到的消息（主进程） 本文着重讨论的是日志记录部分。 编写思路 为什么要给日志记录单开一个线程，个人考虑是因为本身就是多线程的架构，需要编写一个统一的日志记录模块。如果每个线程单独打印，则很有可能出现两个线程同时写入文件或者同时输出到控制台，造成日志混乱。 因此，日志记录大致思路就是： 定义一个队列，存储日志内容和等级 创建一个线程，不断地从线程中取出元素，根据设定的日志等级决定是否打印到控制台或者输出到文件 外部push日志内容到队列中 一些细节上的内容 保证可移植性，尽量使用STL库编写，如使用std::thread而不是pthread 保证线程安全，需要使用互斥锁之类的保护相应变量 让日志队列为空时线程等待，想到编写一个类似于Java下BlockingQueue的阻塞队列 指定一个日志等级，超过这个等级的日志才会被保存或者打印 通过va_list实现不定参数，使日志记录有sprintf的的使用体验 开始编写 有了上述思路，整体编写就很简单了。 BlockingQueue 偷懒了，这部分直接让DeepSeek写的 为了实现一个多线程安全的阻塞队列，当队列为空时调用front()会阻塞直到其他线程添加元素，我们可以结合互斥锁（std::mutex）和条件变量（std::condition_variable）来同步线程操作。 代码实现 互斥锁（std::mutex） 所有对队列的操作（push、front、pop、empty）都需要先获取锁，确保同一时间只有一个线程能修改队列，避免数据竞争。 条件变量（std::condition_variable） 当调用front()时，如果队列为空，线程会通过cv_.wait()释放锁并阻塞，直到其他线程调用push()添加元素后，通过cv_.notify_one()唤醒一个等待线程。 cv_.wait()需配合std::unique_lock，并在等待时自动释放锁，避免死锁。 使用谓词检查（[this] { return !queue_.empty(); }）防止虚假唤醒。 元素获取与移除 front()返回队首元素的拷贝（而非引用），确保调用者获得数据时队列的锁已释放，避免悬空引用。 pop()需显式调用以移除元素，确保队列状态可控。 #include <queue> // 队列 #include <mutex> // 互斥锁 #include <condition_variable> // 条件变量 template<typename T> class BlockingQueue { public: // 向队列中添加元素 void push(const T& item) { std::lock_guard<std::mutex> lock(mtx_); queue_.push(item); cv_.notify_one(); // 通知一个等待的线程 } // 获取队首元素（阻塞直到队列非空） T front() { std::unique_lock<std::mutex> lock(mtx_); cv_.wait(lock, [this] { return !queue_.empty(); }); // 阻塞直到队列非空 return queue_.front(); } // 获取队首元素并移除 T take() { std::unique_lock<std::mutex> lock(mtx_); cv_.wait(lock, [this] { return !queue_.empty(); }); T item = std::move(queue_.front()); // 移动语义避免拷贝 queue_.pop(); return item; } // 移除队首元素（需外部调用，非阻塞） void pop() { std::lock_guard<std::mutex> lock(mtx_); if (!queue_.empty()) { queue_.pop(); } } // 检查队列是否为空 bool empty() const { std::lock_guard<std::mutex> lock(mtx_); return…

C++使用WINAPI查询DNS解析记录
type: item
date: 2025-03-20T09:33:00+00:00
description: 这篇文章是古早时期的博客上迁移过来的（什么赛博秽土转生）， 如有错误欢迎指正 时间线 {timeline} {timeline-item color="#50BFFF"} 2022年7月5日：于旧博客上发布，可在archive.org找到 {/timeline-item} {timeline-item color="#50BFFF"} 2025年3月20日：迁移至当前博客 {/timeline-item} {timeline-item color="#4F9E28"} 2026年2月21日：添加头文件和静态库文件链接提醒 {/timeline-item} {/timeline} 背景 为API配置了多条访问线路，以应对部分地区无法访问导致服务不可用的情况。开始是想到在网站里新建一个文件保存节点信息，发现行不通，联不通的地区根本无法获知其他线路；于是想到用DNS解析记录，用一个TXT解析记录来保存相应的节点数据，以备查询 实战 查询资料 一番Bing下来，发现大部分现有的文章都是使用socket来直接发送查询数据包，获取到的也都是A和CNAME类型的记录，方案不可行。最后，把目光锁定到了MSDN上的DnsQuery函数上，同时找到一篇样例：使用DnsQuery解析主机名 函数分析 从官方文档上得知，函数所需头文件为windns.h，需要包含引入库文件Ws2_32.lib和Dnsapi.lib，函数的参数如下： DNS_STATUS DnsQuery_A( [in] PCSTR pszName, [in] WORD wType, [in] DWORD Options, [in, out, optional] PVOID pExtra, [out, optional] PDNS_RECORD *ppQueryResults, [out, optional] PVOID *pReserved ); 参数解释： pszName是要查询的主机名； wType是查询类型，如A记录，CNAME，TXT等，具体的MSDN官方给出了文档：DNS Constants Options字面意思上理解是查询方式，我直接使用DNS_QUERY_STANDARD，具体也可以查询文档：DNS Constants pExtra和pReserved直接给NULL就行 ppQueryResults传入查询结果的指针，类型是PDNS_RECORD，这个需要传入变量值的引用。 返回值，DNS_STATUS类型，如果查询失败会返回Winerror.h中的相应错误码 使用 明白了使用方法，那就简单了。 首先引入库： #include <windns.h> #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Dnsapi.lib") 我的TXT记录域名为test.iyoroy.cn，查询部分代码如下： PDNS_RECORD pDnsRecord; DNS_STATUS QueryRet = DnsQuery(L"test.iyoroy.cn", DNS_TYPE_TEXT, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL); if (QueryRet) { MessageBox(GhWnd, L"DNS查询失败！\r\n将使用默认节点", L"Warning", MB_ICONERROR | MB_OK); } std::wstring strQueryRes = *pDnsRecord->Data.TXT.pStringArray;//这个就是查询结果 成功实现功能 后记 TXT记录会自动过滤掉空格、换行，因此我选择记录base64编码，使用时再解码并使用stringstream拆分空格。Base64解码我借鉴了这篇文章：C++进行base64编码和解码，以下是我用宽字节重写的解码函数 {collapse} {collapse-item label="展开代码"} //Function:Base64解密 static const std::wstring base64_chars = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ" L"abcdefghijklmnopqrstuvwxyz" L"0123456789+/"; static inline bool is_base64(wchar_t c) { return (isalnum(c) || (c == '+') || (c == '/')); } std::wstring base64_decode(std::wstring const& encoded_string) { int in_len = …

小米845系列机器类原生调用红外摄像头进行人脸识别
type: item
date: 2025-03-06T14:57:00+00:00
description: 背景 小米845系列的小米8、小米8屏幕指纹版有一颗前置红外摄像头用于面部识别，这样即使在无光的环境下仍可以使用面部解锁。然而如果使用LineageOS的设备树不加修改编译出来的类原生无法使用红外摄像头，只能使用普通的摄像头，而PixelExperience的设备树是可以的，故探究其原因。 原因 CameraID 首先是需要指定人脸识别模块使用红外摄像头。翻阅PixelExperience的相关设备树源码可以发现：在overlay中设置了调用CameraID为5的摄像头用作人脸识别。 https://github.com/PixelExperience-Devices/device_xiaomi_dipper/blob/fourteen/overlay/packages/apps/FaceUnlockService/app/src/main/res/values/config.xml <?xml version="1.0" encoding="utf-8"?> <resources> <integer name="override_front_cam_id">5</integer> <bool name="use_alternative_vendor_impl">true</bool> </resources> PixelExperience使用的是motorola的人脸解锁方案，而现在很多类原生使用的是AOSPA的ParanoidSense，因此上述设置的overlay并不适用。 查阅ParanoidSense源码可以看到：使用了Properties属性ro.face.sense_service.camera_id标识使用的摄像头。 https://github.com/AOSPA/android_packages_apps_ParanoidSense/blob/uvite/src/co/aospa/sense/camera/CameraUtil.kt#L32 val cameraIdProp = SystemProperties.get("ro.face.sense_service.camera_id") 因此理论上只要设置ro.face.sense_service.camera_id属性为5即可使其调用红外人脸摄像头。 vendor.camera.aux.packagelist属性 如果仅仅设置上述CameraID相关属性，那么人脸模块调用时会直接报错。翻阅Frameworks部分的代码可以得知：系统默认隐藏了除主前摄和主后摄之外的其他辅助摄像头（即AUXCamera），而红外摄像头也属于AUXCamera之一。 取LineageOS的Framework代码作为例子： https://github.com/LineageOS/android_frameworks_base/blob/lineage-21.0/core/java/android/hardware/Camera.java#L295-L301 /** * Returns the number of physical cameras available on this device. * The return value of this method might change dynamically if the device * supports external cameras and an external camera is connected or * disconnected. * * If there is a * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA * logical multi-camera} in the system, to maintain app backward compatibility, this method will * only expose one camera per facing for all logical camera and physical camera groups. * Use camera2 API to see all cameras. * * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. */ public static…

给Android 4.9内核添加KernelSU支持
type: item
date: 2024-12-26T16:18:00+00:00
description: 前言 KernelSU出来也有一段时间了，官方只支持GKI内核但是给出了非GKI内核的集成方案。咱使用的小米8屏幕指纹版是基于Android4.9内核的，实测直接使用KernelSU的脚本集成会出问题，故记录下折腾过程 特别感谢：Tomsec大佬提供的4.9修复方案 准备材料 Android Kernel源码，此处使用的是LineageOS/android_kernel_xiaomi_sdm845 能访问GitHub的网络环境 AnyKernel3源码，可参考如下仓库：https://github.com/YoriInstitute/AnyKernel3-equuleus 交叉编译器，可参考如下仓库：https://github.com/YoriInstitute/AnyKernel3-equuleus/blob/13/.github/workflows/build.yml#L21-L23 编译服务器 开整 编译环境配置这里就不叙述了，可以放在ROM编译里制作包的时候直接集成在boot.img里也可以单独编译成AnyKernel3可刷入包，这里使用的是AnyKernel3的方案。 下载内核源码、交叉编译器 git clone https://github.com/KaguraiYoRoy/android_kernel_xiaomi_sdm845 -b lineage-20 sdm845 #内核源码 git clone https://android.googlesource.com/platform/prebuilts/clang/host/linux-x86 --depth=1 -b android-13.0.0_r43 clang #Clang git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9 -b android-10.0.0_r32 --depth=1 #aarch64的GCC git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9 -b android-10.0.0_r32 --depth=1 #arm的GCC 不同内核需要的交叉编译器可能不同，请根据你的实际情况修改 {alert type="info"} 此处下载内核时没加参数--depth=1是为了后期便于cherry-pick，若你选择手动修改内核源码而不是pick则可以加上此参数，可以显著减少下载时的时间 {/alert} 修改内核源码 参考如下两个commit： https://github.com/OnlyTomInSecond/android_kernel_xiaomi_sdm845/commit/7862fd2c14b69a1a346b7c699f8370e2de423eef commit b5853fb7cefdc8a2e160cb73512dc6b51569fa66 Author: OnlyTomInSecond <q2781273965@gmail.com> Date: Wed Apr 5 11:05:12 2023 +0800 kernelsu: allow init exec ksud under nosuid Change-Id: I8aa6e6d3cbee1addd5da9bb48b4c08ce91f9db81 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4abba0e1674d..693f2ac03352 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2320,7 +2320,10 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, { int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); int nosuid = !mnt_may_suid(bprm->file->f_path.mnt); - int rc; + int rc, error; + static u32 ksu_sid; + char *secdata; + u32 seclen; if (!nnp && !nosuid) ret…

记一次为Android 4.9内核的ROM启用erofs支持
type: item
date: 2024-12-25T15:26:00+00:00
description: 前言 众所周知，咱是老牌小米8屏幕指纹版钉子户了 咱一直想给这个机子上个erofs（关于erofs是什么请自行搜索 但是经过查阅得知erofs是在5.10被加入到Linux内核中的，也就是说咱4.9得手动集成 准备材料 Android ROM源码：这里使用的是LineageOS 22.0 Android Kernel源码：这里咱用的是LineageOS官方小米845内核：LineageOS/android_kernel_xiaomi_sdm845 设备树：这里使用的是LineageOS官方设备树，包含sdm845-common部分（LineageOS/android_device_xiaomi_sdm845-common）和device-specific部分（LineageOS/android_device_xiaomi_equuleus） 编译服务器 {alert type="info"} 注：因为咱主要是玩编译的比较多，因此这篇主要阐述的是编译ROM过程中的折腾记录 {/alert} 开整 部署Android ROM编译环境就不说了，网上一搜一大片，把ROM源码、设备树、内核源码下载下来放到对应位置 为内核添加erofs支持 此处代码pick from ReallySnow的msm-4.9内核（谢谢ReallySnow大佬的支持！），可参考如下分支： https://github.com/ReallySnow/msm-4.9/tree/erofs https://github.com/YoriInstitute/android_kernel_xiaomi_sdm845/tree/lineage-22.0-erofs pick之前要先处理lz4压缩部分代码的冲突。详细Relation chain可参考： https://review.lineageos.org/c/LineageOS/android_kernel_xiaomi_sdm845/+/412614 咱尝试将改造提交给LineageOS官方但是很显然没被合并x pick完更改后在内核的defconfig文件中开启erofs支持： 打开$KERNEL_SOURCE/arch/arm64/configs/vendor/xiaomi/mi845_defconfig并添加：CONFIG_EROFS_FS=y： commit 7ae5caab2c15982b3d3821c97e2f2e6126ca1281 (HEAD -> erofs, refs/published/erofs) Author: KaguraiYoRoy <neko@yori.moe> Date: Tue Dec 24 22:49:07 2024 +0800 arch: arm64: configs: mi845: Enable CONFIG_EROFS_FS Change-Id: Ie387b03bde54700d73ccf19f0baf4db62526cc8f diff --git a/arch/arm64/configs/vendor/xiaomi/mi845_defconfig b/arch/arm64/configs/vendor/xiaomi/mi845_defconfig index 7684be360d98..69ea580fdb11 100644 --- a/arch/arm64/configs/vendor/xiaomi/mi845_defconfig +++ b/arch/arm64/configs/vendor/xiaomi/mi845_defconfig @@ -651,3 +651,4 @@ CONFIG_SOUNDWIRE_WCD_CTRL=y CONFIG_WCD9XXX_CODEC_CORE=y CONFIG_WCD_DSP_GLINK=y CONFIG_WCD_SPI_AC=y +CONFIG_EROFS_FS=y \ No newline at end of file 修改分区文件系统和fstab 打开设备树中板级配置文件把要改为erofs的分区的文件系统改为erofs，示例中的配置文件在$WORKING_DIR/device/xiaomi/sdm845-common/BoardConfigCommon.mk： commit 0fce86fb40ca637a8563bef6192afaea1658c881 (HEAD -> erofs, refs/published/erofs) Author: KaguraiYoRoy <neko@yori.moe> Date: Tue Dec 24 23:37:50 2024 +0800 sdm845-common: Switch t…