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

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

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

产品目录按教程代号 rk3568_evb。 


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

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 可能判定「内核没变」,连合成都不跑。

代码

# 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:

grep '^CONFIG_EVB_MISC' out/kernel/OBJ/linux-5.10/.config
strings out/kernel/OBJ/linux-5.10/vmlinux | grep evb_misc | head

.config 是 =y 而 vmlinux 里没有符号:删 out/kernel/checkpoint 再编。


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

原因

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

代码

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

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
grep CFI_PERMISSIVE out/kernel/OBJ/linux-5.10/.config
hdc shell "dmesg | grep -i cfi"

现象:insmod 报 Key was rejected

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 全废。钥匙必须固定在板级目录,每次构建拷进内核树。

代码

device/board/rk/rk3568_evb/kernel/certs/signing_key.pem
device/board/rk/rk3568_evb/kernel/certs/signing_key.x509
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/"
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 必须一样。

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/Kconfig 有 depends on m=y 会被降回模块。必须当 .ko 打进镜像,开机 insmod

代码

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 只能放合法命令):

{
  "name": "net_wifi_r8188eu",
  "path": "/vendor/bin/wifi-r8188eu-up.sh",
  "once": 1
}

脚本先 lsmod 再 insmod,失败留 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

#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/

// 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:

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:

/ {
    evb_misc: evb-misc {
        compatible = "rk3568,evb-misc";
        status = "okay";
    };
};

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

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 再申请会抢。

代码

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 就够:

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

代码

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

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

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 仍是 =mKconfig `depends on m`保持 =m,开机 insmod
insmod 整板复位CFI`CFI_PERMISSIVE=y`
有节点无 `/dev`驱动没编进来或 compatible 不对grep .config,对 of_match
用户态 so 段错误用 aarch64-linux-gnu 编了armv7 + musl
HDF 和内核抢 I2C两边都 okayDTS disabled 或 HCS 不要登记

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐