开源鸿蒙内核配置与驱动三条路径—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】

框架的位置清楚了。今天说加一颗外设能走哪三条路,以及选错路时为什么编译成功、板上却有时有节点、有时没有。

现象很具体:你 menuconfig 里把某个驱动勾成 *,编完、刷完,dmesg 里没有 probe。或者 insmod r8188eu.koKey was rejected by service。或者超声波节点 /dev/hcsr04 好好的,你又在 HCS 里给同一条 I2C 登记了一个 HDF 设备,两边开始互相 NACK。

原因不在芯片。开源鸿蒙这块 RK3568 上,一个外设能走三条路进系统,配置却分散在 defconfig、build_kernel.sh、设备树、HCS 四处。改错地方,编译照样 exit 0。

产品目录按教程代号 rk3568_evb


现象:menuconfig 勾了,重启又没了

bash 复制代码
cd out/kernel/src_tmp/linux-5.10
make LLVM=1 ARCH=arm64 menuconfig

CONFIG_FOO=y 当时在 .config 里。下一次 ./build.sh --product-name rk3568_evb --ccache 跑完,打开 out/kernel/OBJ/linux-5.10/.configFOO 又是 is not set

原因

板级 build_kernel.sh 每次都会用碎片 defconfig 重新合成 .config。menuconfig 改的是当前 OBJ 里那一份,不是权威源。权威源有三处,缺一处,下一次合成就丢:

  1. 源码树里的 defconfig
  2. 本次构建用的 out/kernel/.../rockchip_linux_defconfig(只对「此刻不重建、先测一把」有用)
  3. device/board/rk/rk3568_evb/kernel/build_kernel.sh 第 13 段的幂等 sed / append

GN 的内核 action 还不跟踪 device/board/。你只改了 build_kernel.sh,ninja 可能判定「内核没变」,连合成都不跑。

代码

bash 复制代码
# device/board/rk/rk3568_evb/kernel/build_kernel.sh  §13 MERGED_CFG
CFG="$KERNEL_SRC/arch/arm64/configs/rockchip_linux_defconfig"

set_y() {
    local k="$1"
    if grep -q "^${k}=y" "$CFG"; then
        return 0
    fi
    if grep -q "^# ${k} is not set" "$CFG"; then
        sed -i "s/^# ${k} is not set/${k}=y/" "$CFG"
    else
        echo "${k}=y" >> "$CFG"
    fi
}

set_y CONFIG_EVB_MISC
set_y CONFIG_CFI_PERMISSIVE
# R8188EU 不能 set_y

编完对产物,不要信 exit 0:

bash 复制代码
grep '^CONFIG_EVB_MISC' out/kernel/OBJ/linux-5.10/.config
strings out/kernel/OBJ/linux-5.10/vmlinux | grep evb_misc | head

.config=yvmlinux 里没有符号:删 out/kernel/checkpoint 再编。


现象:staging 驱动一 insmod,整板复位

原因

内核开了 CONFIG_CFI_CLANG=y 加 ThinLTO。间接调用核对函数指针类型,签名稍不对就 panic。staging 里 callback 类型不严是常态。bring-up 把 CFI 从「杀」降成「告警继续跑」:CONFIG_CFI_PERMISSIVE=y

代码

写进 §13,不要只在 menuconfig 里勾:

bash 复制代码
set_y CONFIG_CFI_PERMISSIVE
rm -rf out/kernel/checkpoint
./build.sh --product-name rk3568_evb --ccache --build-target kernel --fast-rebuild --disable-package-image
bash 复制代码
grep CFI_PERMISSIVE out/kernel/OBJ/linux-5.10/.config
hdc shell "dmesg | grep -i cfi"

现象:insmod 报 Key was rejected

bash 复制代码
Loading of module with unavailable key is rejected
insmod: failed to load /data/r8188eu.ko: Key was rejected by service

脚本把失败打印成「跳过(已加载)」------2>/dev/null && ... || log 跳过 把错误吞了。先 lsmod

原因

CONFIG_MODULE_SIG_FORCE=y。未签名、或公钥和当前内核嵌的那把对不上,一律拒绝。重编内核若重新生成 certs/signing_key.pem,旧 .ko 全废。钥匙必须固定在板级目录,每次构建拷进内核树。

代码

bash 复制代码
device/board/rk/rk3568_evb/kernel/certs/signing_key.pem
device/board/rk/rk3568_evb/kernel/certs/signing_key.x509
bash 复制代码
CERT_SRC="$BOARD_KERNEL/certs"
CERT_DST="$KERNEL_SRC/certs"
mkdir -p "$CERT_DST"
cp -f "$CERT_SRC/signing_key.pem"  "$CERT_DST/"
cp -f "$CERT_SRC/signing_key.x509" "$CERT_DST/"
bash 复制代码
ROOT=/path/to/OpenHarmony
export PATH=$ROOT/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
export KBUILD_OUTPUT=$ROOT/out/kernel/OBJ/linux-5.10
cd $ROOT/out/kernel/src_tmp/linux-5.10

make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
     M=drivers/staging/rtl8188eu modules -j$(nproc)

$KBUILD_OUTPUT/scripts/sign-file sha1 \
    $KBUILD_OUTPUT/certs/signing_key.pem \
    $KBUILD_OUTPUT/certs/signing_key.x509 \
    drivers/staging/rtl8188eu/r8188eu.ko

md5sum device/board/rk/rk3568_evb/kernel/certs/signing_key.x509 \
       out/kernel/OBJ/linux-5.10/certs/signing_key.x509

两份 x509 md5 必须一样。

bash 复制代码
hdc file send r8188eu.ko /data/r8188eu.ko
hdc shell "lsmod | grep r8188eu || insmod /data/r8188eu.ko"
hdc shell "dmesg | tail -20"

现象:R8188EU 写成 =y,.config 里仍是 =m

原因

drivers/staging/rtl8188eu/Kconfigdepends on m=y 会被降回模块。必须当 .ko 打进镜像,开机 insmod

代码

bash 复制代码
if grep -q '^CONFIG_R8188EU=y' "$CFG"; then
    sed -i 's/^CONFIG_R8188EU=y/CONFIG_R8188EU=m/' "$CFG"
fi
if ! grep -q '^CONFIG_R8188EU=m' "$CFG"; then
    echo 'CONFIG_R8188EU=m' >> "$CFG"
fi

init(init.rk30board.cfg 的 cmds 只能放合法命令):

bash 复制代码
{
  "name": "net_wifi_r8188eu",
  "path": "/vendor/bin/wifi-r8188eu-up.sh",
  "once": 1
}

脚本先 lsmodinsmod,失败留 stderr。


三条路怎么选

现象:同一个超声波,你写了 HDF Sensor 又写了内核 hcsr04.c,节点有时在、有时不在。

原因:Linux 原生驱动和 HDF 并存。一个硬件只能被一边 probe。选路看上层要什么

  • 只要 /dev 或 sysfs,外设测试应用 open路径 A,内核原生
  • 要用 HDF 的 GPIO/I2C/PWM API,但还不是标准 Kit → 路径 B,HDF 平台
  • 要进桌面 / ADM / composer / 相机 Kit → 路径 C,HDF 外设模型

触摸、音频、显示走 C。超声波、RFID、电机、LED、矩阵键盘走 A。选一条,不要双边 okay


路径 A:misc 字符设备接到内核

目标:DTS 一个节点,misc_register/dev/evb_misc,用户态能 read、能 ioctl

代码:evb_misc.h

bash 复制代码
#ifndef EVB_MISC_H
#define EVB_MISC_H
#include <linux/ioctl.h>
#define EVB_MISC_IOC_MAGIC  'E'
struct evb_misc_msg {
    unsigned int len;
    char text[120];
};
#define EVB_MISC_IOC_GET _IOR(EVB_MISC_IOC_MAGIC, 1, struct evb_misc_msg)
#define EVB_MISC_IOC_SET _IOW(EVB_MISC_IOC_MAGIC, 2, struct evb_misc_msg)
#endif

用户态把 <linux/ioctl.h> 换成 <sys/ioctl.h>。本产品用户态 32 位 musl,.ko 仍是 aarch64。ioctl 参数不要塞指针,上面已经给 compat_ioctl 留了同一函数。

代码:evb_misc.c

放到 device/board/rk/rk3568_evb/kernel/drivers/,由 build_kernel.sh 拷进 drivers/misc/

bash 复制代码
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include "evb_misc.h"

struct evb_misc_dev {
    struct miscdevice misc;
    struct mutex lock;
    char buf[128];
    size_t len;
};

static ssize_t evb_misc_read(struct file *file, char __user *ubuf,
                             size_t count, loff_t *ppos)
{
    struct evb_misc_dev *edev = file->private_data;
    ssize_t n;

    mutex_lock(&edev->lock);
    n = simple_read_from_buffer(ubuf, count, ppos, edev->buf, edev->len);
    mutex_unlock(&edev->lock);
    return n;
}

static long evb_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct evb_misc_dev *edev = file->private_data;
    struct evb_misc_msg msg;
    int ret = 0;

    if (_IOC_TYPE(cmd) != EVB_MISC_IOC_MAGIC)
        return -ENOTTY;

    mutex_lock(&edev->lock);
    switch (cmd) {
    case EVB_MISC_IOC_GET:
        memset(&msg, 0, sizeof(msg));
        memcpy(msg.text, edev->buf, min(sizeof(msg.text) - 1, edev->len));
        msg.len = edev->len;
        if (copy_to_user((void __user *)arg, &msg, sizeof(msg)))
            ret = -EFAULT;
        break;
    case EVB_MISC_IOC_SET:
        if (copy_from_user(&msg, (void __user *)arg, sizeof(msg))) {
            ret = -EFAULT;
            break;
        }
        if (msg.len >= sizeof(edev->buf)) {
            ret = -EINVAL;
            break;
        }
        memcpy(edev->buf, msg.text, msg.len);
        edev->buf[msg.len] = '\0';
        edev->len = msg.len;
        break;
    default:
        ret = -ENOTTY;
        break;
    }
    mutex_unlock(&edev->lock);
    return ret;
}

static int evb_misc_open(struct inode *inode, struct file *file)
{
    struct evb_misc_dev *edev = container_of(file->private_data,
                                             struct evb_misc_dev, misc);
    file->private_data = edev;
    return 0;
}

static const struct file_operations evb_misc_fops = {
    .owner          = THIS_MODULE,
    .open           = evb_misc_open,
    .read           = evb_misc_read,
    .unlocked_ioctl = evb_misc_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = evb_misc_ioctl,
#endif
};

static int evb_misc_probe(struct platform_device *pdev)
{
    struct evb_misc_dev *edev;
    int ret;

    edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL);
    if (!edev)
        return -ENOMEM;

    mutex_init(&edev->lock);
    strscpy(edev->buf, "evb_misc ready\n", sizeof(edev->buf));
    edev->len = strlen(edev->buf);

    edev->misc.minor = MISC_DYNAMIC_MINOR;
    edev->misc.name  = "evb_misc";
    edev->misc.fops  = &evb_misc_fops;
    edev->misc.mode  = 0666;

    ret = misc_register(&edev->misc);
    if (ret)
        return ret;
    platform_set_drvdata(pdev, edev);
    dev_info(&pdev->dev, "/dev/evb_misc minor=%d\n", edev->misc.minor);
    return 0;
}

static int evb_misc_remove(struct platform_device *pdev)
{
    struct evb_misc_dev *edev = platform_get_drvdata(pdev);
    misc_deregister(&edev->misc);
    return 0;
}

static const struct of_device_id evb_misc_of_match[] = {
    { .compatible = "rk3568,evb-misc" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, evb_misc_of_match);

static struct platform_driver evb_misc_driver = {
    .probe  = evb_misc_probe,
    .remove = evb_misc_remove,
    .driver = {
        .name           = "evb-misc",
        .of_match_table = evb_misc_of_match,
    },
};
module_platform_driver(evb_misc_driver);
MODULE_LICENSE("GPL");

Kconfig:

bash 复制代码
config EVB_MISC
    tristate "rk3568_evb misc char skeleton"
    default n

Makefile:obj-$(CONFIG_EVB_MISC) += evb_misc.o

build_kernel.sh §13 拷源并 set_y CONFIG_EVB_MISC

DTS:

bash 复制代码
/ {
    evb_misc: evb-misc {
        compatible = "rk3568,evb-misc";
        status = "okay";
    };
};

compatible 必须和 of_device_id 逐字符相同。开发期当 .ko

bash 复制代码
make LLVM=1 LLVM_IAS=1 CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 \
     M=drivers/misc evb_misc.ko -j$(nproc)
$KBUILD_OUTPUT/scripts/sign-file sha1 \
    $KBUILD_OUTPUT/certs/signing_key.pem \
    $KBUILD_OUTPUT/certs/signing_key.x509 \
    drivers/misc/evb_misc.ko
hdc file send evb_misc.ko /data/evb_misc.ko
hdc shell "insmod /data/evb_misc.ko; cat /dev/evb_misc"

路径 B:HDF 平台

现象:PWM 在 /sys/class/pwm 能转,HDF 程序 GpioRead 失败。

原因:要在 device_info.hcs 登记,policy = 2 用户态才 Bind 得到。Linux 已经 export 的脚,HDF 再申请会抢。

代码

bash 复制代码
device_pwm :: device {
    device0 :: deviceNode {
        policy = 2;
        priority = 70;
        permission = 0644;
        moduleName = "HDF_PLATFORM_PWM";
        serviceName = "HDF_PLATFORM_PWM_0";
        deviceMatchAttr = "rockchip_rk3568_pwm_0";
    }
}

改 khdf 刷 boot_linux,改 uhdf 刷 vendor。改子 hcs 必须删 hcb。风扇演示走 sysfs 就够:

bash 复制代码
hdc shell "echo 0 > /sys/class/pwm/pwmchip0/export"
hdc shell "echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period"
hdc shell "echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle"
hdc shell "echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable"

路径 C:HDF 外设模型

现象:GT911 内核 goodix probe 成功,桌面划不动;或者坐标按 720×1280 缩。

原因:桌面走 Input 模型 + HCS 的 solutionX/Y。DTS 里 goodix 必须 disabled

代码

bash 复制代码
&i2c2 {
    status = "okay";
    clock-frequency = <100000>;
    gt911_lvds: gt911@5d {
        compatible = "goodix,gt911";
        reg = <0x5d>;
        status = "disabled";
    };
};
bash 复制代码
inputAttr {
    solutionX = 1024;
    solutionY = 600;
}
busConfig { busType = 0; busNum = 2; }

device_info.hcsmoduleName = "HDF_TOUCH_GT911"policy = 2preload = 0

bash 复制代码
hdc shell "cat /proc/bus/input/devices"
hdc shell "ls /sys/bus/i2c/devices/2-005d/driver"
hdc shell "i2cdetect -y -r 2"

现象对照

现象 原因 先做什么
menuconfig 勾了下次丢 合成从 defconfig+§13 来 写进 §13,三处同步
`.config` 有、vmlinux 无符号 ninja 没重跑内核 删 checkpoint
insmod Key rejected 没签或钥匙换了 固定 certs,sign-file
R8188EU 写 =y 仍是 =m Kconfig `depends on m` 保持 =m,开机 insmod
insmod 整板复位 CFI `CFI_PERMISSIVE=y`
有节点无 `/dev` 驱动没编进来或 compatible 不对 grep .config,对 of_match
用户态 so 段错误 用 aarch64-linux-gnu 编了 armv7 + musl
HDF 和内核抢 I2C 两边都 okay DTS disabled 或 HCS 不要登记

系列第 14 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

相关推荐
明月_清风1 小时前
递归算法:从原理到实战,一次讲透
后端·算法·go
用户252632753471 小时前
如何实现MySQL多表联查
前端
掘金者阿豪1 小时前
ChatGPT Plus、Pro 5x、Pro 20x 到底有多少额度?聊聊 Codex 那个让人看不懂的“周限额”
后端
今天AI了吗1 小时前
【AI】一文讲清 RAG:从大模型局限到企业级知识库落地流程
前端·javascript·人工智能·python·深度学习·机器学习·easyui
hunterandroid1 小时前
[鸿蒙从零到一] HarmonyOS 冷启动瀑布图分析与关键路径裁剪实战
前端·华为
用户608186527901 小时前
Avalonia 控件模板实战:从 WPF 迁移自定义 Button 样式的完整指南
后端
郭邯1 小时前
用 Canvas 实现纯前端图片格式互转:PNG/JPG/WebP 一键切换,还能控制质量与尺寸
前端
工边页字1 小时前
Electron应用的8种防护方式
前端·javascript·electron