i2c_register_board_info 符号未定义

i2c_register_board_info symbol is undefined

我正在尝试为 BeagleBone Black 编写一个内核模块,它将与我的自定义 I2C 从设备进行通信。我尝试遵循几个内核模块开发教程,它们在某些时候似乎都不完整,或者假设我知道一些我显然不知道的东西......我现在的问题是 Makefile 没有看到 i2c_register_board_info 符号。我把这个驱动程序写成一个单独的模块,它没有在内核编译期间编译。此外,我在使用 buildroot 构建时启用了 I2C 工具。使用 I2C 工具,我能够检测并连接我的设备。我的 Makefile 如下所示:

MODULE_NAME = PowerManagerDriver

PWD := $(shell pwd)
SRC_DIR = user_files
BUILD_DIR = build
BUILD_EXT = *.o .*.cmd *.ko *.mod.c *.order *.symvers *.dwo

SRCS = $(SRC_DIR)/main.c $(SRC_DIR)/pmd_i2c.c 
OBJS = $(SRCS:.c=.o)

obj-m += $(MODULE_NAME).o 
$(MODULE_NAME)-y = $(OBJS)

KERNELDIR ?= /home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92

all: default

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    mv $(SRC_DIR)/*.o $(BUILD_DIR)/
    mv $(BUILD_EXT) $(BUILD_DIR)/ &> /dev/null

clean:
    rm -rf $(BUILD_DIR)/*
    rm -rf $(BUILD_EXT)

make 输出:

13:45:33 **** Incremental Build of configuration Default for project PowerManagerDriver ****
make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-uclibcgnueabihf- -j6 all 
make -C /home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92 M=/home/lukasz/eclipse-workspace/PowerManagerDriver modules
make[1]: Entering directory '/home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92'
  CC [M]  /home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/main.o
  CC [M]  /home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/pmd_i2c.o
/home/lukasz/eclipse-workspace/PowerManagerDriver/user_files/pmd_i2c.c:99:26: warning: ‘pdm_i2cClient’ defined but not used [-Wunused-variable]
 static struct i2c_client pdm_i2cClient = { 0 };
                          ^
  LD [M]  /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: "i2c_register_board_info" [/home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.ko] undefined!
  CC      /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.mod.o
  LD [M]  /home/lukasz/eclipse-workspace/PowerManagerDriver/PowerManagerDriver.ko
make[1]: Leaving directory '/home/lukasz/brl/Machine/beaglebone/build/linux-a75d8e93056181d512f6c818e8627bd4554aaf92'
mv user_files/*.o build/
mv *.o .*.cmd *.ko *.mod.c *.order *.symvers *.dwo build/ &> /dev/null

13:45:35 Build Finished. 0 errors, 1 warnings. (took 1s.394ms)

到目前为止,我只有几个 I2C 接口函数:

static int pdm_i2cProbe(struct i2c_client* client,
                        const struct i2c_device_id* id)
{
    PMD_ASSERT(client);
    PMD_ASSERT(id);

    return 0;
}

static struct i2c_driver pdm_i2cDriver =
{
    .driver =
    {
        .name = "pdm-driver",
        .owner = THIS_MODULE,
    },

    .probe = pdm_i2cProbe,
};

static struct i2c_board_info pdm_i2cBoardInfo[] =
{
    {
        I2C_BOARD_INFO("pdm-driver", 0x30),
        .irq = 69,
    },
};

/**
 * @brief   Initializes the I2C module.
 * @param   busNr: The I2C peripheral number on which the device is connected.
 * @return  \ref e_pdmStatus_OK on succesfull init.
 */
pmdStatus_t pdm_i2cInit(const unsigned int busNr)
{
    if (i2c_register_board_info((int)busNr, pdm_i2cBoardInfo,
            ARRAY_SIZE(pdm_i2cBoardInfo)))
        return e_pmdStatus_BADPARAM;

    if (i2c_add_driver(&pdm_i2cDriver))
        return e_pmdStatus_EXE;

    return e_pmdStatus_OK;
}

在这一点上,即使我没有探测任何东西,因为探测函数是空的,我希望至少能正确加载模块。我什至没有进入测试阶段,因为找不到 i2c_register_board_info。构建内核时我的 .config 文件包含这些行:

# I2C support
#
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_CHARDEV=y
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y

我以前找到过这个主题i2c registering macro not found?,但我什么都不懂。

当尝试按要求使用 insmod 加载模块时:

# uname -a
Linux buildroot 4.9.59 #1 SMP Fri Oct 5 11:55:54 CEST 2018 armv7l GNU/Linux
# insmod PowerManagerDriver.ko
[   39.438108] PowerManagerDriver: loading out-of-tree module taints kernel.
[   39.445800] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
[   39.455743] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
insmod: can't insert 'PowerManagerDriver.ko': unknown symbol in module, or unknown parameter
#

好的,这听起来像是 Build Linux Kernel module with warning i2c_register_board_info undefined 的副本。

问题:

# insmod PowerManagerDriver.ko
[   39.438108] PowerManagerDriver: loading out-of-tree module taints kernel.
[   39.445800] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
[   39.455743] PowerManagerDriver: Unknown symbol i2c_register_board_info (err 0)
insmod: can't insert 'PowerManagerDriver.ko': unknown symbol in module, or unknown parameter

原因:

Build Linux Kernel module with warning i2c_register_board_info undefined

Function i2c_register_board_info isn't exported (with EXPORT_SYMBOL) for kernel modules. Only code compiled into the kernel may use such functions. As far as I understand from its description, the function is intended to use by boards developers

Your problem is you are mixing two things in one driver, i.e. driver itself which can be a module and platform (legacy!) code, which can't. You have to drop platform code for ACPI or device tree resource provider, or as a last resort to split it out to another compile unit.

解决方法:

I removed i2c_register_board_info then I can build driver to module (.ko) without warning and can insmod by adding these function in init function called by module_init:

... 
adapter = i2c_get_adapter(CONFIG_I2C_BUS); 
... 
client = i2c_new_device(adapter, &i2c_pn535_sample_devs); 
... 

not sure if it is good or bad. because when i remove driver (rmmod), driver does not release device and can not insmod again, I have to reboot device to insmod again

如果您找到不同的或更好的解决方案,请随时添加(并接受!)您自己的回复。

抱歉,我无法提供更多帮助:(

如果您的硬件 'guaranteed' 在那里,则无需强制定义设备。您可以使用设备树 (dtb) 在特定总线上的特定地址声明您的从站的存在,为您的驱动程序提供参数等。因此您的模块永远不需要做注册板信息的事情。

这在文档中有解释:https://www.kernel.org/doc/Documentation/i2c/instantiating-devices

换句话说,您应该能够仅使用开放固件 (of) 挂钩和已填充的设备为您的从属设备编写驱动程序作为内核模块树 (dtb).