简体   繁体   English

用于 ST7580 PLC 调制解调器的自定义 Linux serdev 驱动程序

[英]Custom Linux serdev driver for ST7580 PLC modem

I have a ST7580 Power line communication modem connected to a BeagleBone Black device running Debian Linux, kernel v4.19.我有一个 ST7580 电力线通信调制解调器,它连接到运行 Debian Linux、kernel v4.19 的 BeagleBone Black 设备。 I am trying to communicate with the modem using a custom serial driver (using the SERDEV model) which is based on some samples.我正在尝试使用基于某些示例的自定义串行驱动程序(使用 SERDEV 模型)与调制解调器通信。 I have compiled it as a kernel module named st7580km.ko and used modprobe to insert it into the kernel. Since I want to use a DeviceTree overlay to load the driver for this peripheral device, I have copied and modified the BONE-UART1.dts and named it as BONE-ST7580.dts with the following content:我将其编译为一个名为st7580km.ko的 kernel 模块,并使用modprobe将其插入到 kernel 中。由于我想使用 DeviceTree 覆盖加载此外围设备的驱动程序,因此我复制并修改了BONE-UART1.dts并将其命名为BONE-ST7580.dts ,内容如下:

/dts-v1/;
/plugin/;
#include <dt-bindings/pinctrl/am33xx.h>

&{/chosen} {
    overlays {
        BONE-ST7580 = __TIMESTAMP__;
    };
};

&ocp {
    P9_24_pinmux { pinctrl-0 = <&P9_24_uart_pin>; };    /* UART TX*/
    P9_26_pinmux { pinctrl-0 = <&P9_26_uart_pin>; };    /* UART RX*/
};

&bone_uart1 {
    compatible = "st7580, st7580km";
    status = "okay";
};

I have compiled the dts file to dtbo and placed the dtbo in /lib/firmware.我已经将 dts 文件编译为 dtbo 并将 dtbo 放在 /lib/firmware 中。

The kernel module source: kernel模块源码:

#include <asm/unaligned.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/serdev.h>

#define ST7580_DRIVER_NAME "st7580"
#define ST7580_DRIVER_VERSION  "0.1.0"
#define ST7580_DEFAULT_BAUD_RATE 57600
#define ST7580_CHECKSUM_LENGTH 2
#define ST7580_MAX_DATA_LENGTH (3 + 255 + ST7580_CHECKSUM_LENGTH )
#define ST7580_TX_ACK_TIMEOUT msecs_to_jiffies(40)

static void st7580_write_wakeup(struct serdev_device *serdev)
{
    dev_info(&serdev->dev, "\nst7580_write_wakeup: pull down RTS\n");
    /* Pull down RTS */
    serdev_device_set_tiocm(serdev, TIOCMBIS, TIOCM_RTS);
}

static int st7580_receive_buf(struct serdev_device *serdev, 
       const unsigned char *buf, size_t size)
{
    dev_info(&serdev->dev, "\nst7580_receive_buf: Received %u bytes\n", size);
    return serdev_controller_receive_buf(serdev, buf, size);
}

static const struct serdev_device_ops st7580_serdev_ops = {
    .receive_buf = st7580_receive_buf, /* default */
    .write_wakeup = st7580_write_wakeup,
};

static int st7580_probe(struct serdev_device *serdev)
{
    int uart_flags;
    int ret;

    dev_info(&serdev->dev, "\nst7580_probe started...");
    serdev_device_set_client_ops(serdev, &st7580_serdev_ops);
    ret = devm_serdev_device_open(&serdev->dev, serdev);
    if (ret) {
        _dev_err(&serdev->dev, "unable to open the device\n");
        return ret;
    }
    serdev_device_set_baudrate(serdev, ST7580_DEFAULT_BAUD_RATE);
    serdev_device_set_flow_control(serdev, false);
    ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
    uart_flags = 0;
    uart_flags &= ~PARENB;
    uart_flags &= ~CSTOPB;
    uart_flags &= ~CSIZE;
    uart_flags |=  CS8;
    uart_flags &= ~CRTSCTS;
    uart_flags |= CREAD | CLOCAL;
    //uart_settings.c_iflag &= ~(IXON | IXOFF | IXANY);
    //uart_settings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    //uart_settings.c_iflag &= ~IGNBRK;
    serdev_device_set_tiocm(serdev, TIOCMSET, uart_flags);
    serdev_device_set_rts(serdev, 0);
    serdev_device_set_tiocm(serdev, TIOCMSET, TIOCM_RTS);
    dev_info(&serdev->dev, "\nDriver name is %s, loaded successsfully\n", ST7580_DRIVER_NAME);
    return ret;
}

static const struct of_device_id st7580_of_match[] = {
    { .compatible = "stm7580,st7580" },
    { .compatible = "stm7580km,st7580km" },
    { .compatible = "st7580-fsk-psk,st7580" },
    { }
};
MODULE_DEVICE_TABLE(of, st7580_of_match);

static struct serdev_device_driver st7580_driver = {
    .driver = {
        .name = ST7580_DRIVER_NAME,
        .of_match_table = st7580_of_match,
    },
    .probe = st7580_probe,
};
module_serdev_device_driver(st7580_driver);

I have also modified the /boot/uEnv.txt and added this line to load the DeviceTree overlay.我还修改了 /boot/uEnv.txt 并添加了这一行来加载 DeviceTree 覆盖。

optargs=quiet drm.debug=7 capemgr.enable_partno=BB-ST7580

When I reboot I expect that my driver will be loaded using which I can communicate to the modem from a userspace app.当我重新启动时,我希望我的驱动程序将被加载,我可以使用它从用户空间应用程序与调制解调器通信。 I have placed some debug messages in the code which I expect to see in the dmesg output like below:我在代码中放置了一些调试消息,我希望在 dmesg output 中看到这些消息,如下所示:

dev_info(&serdev->dev, "\nDriver name is %s, loaded successsfully\n", ST7580_DRIVER_NAME);

So far I do dont see any of the messages and am not sure if the kernel is using my driver for this peripheral.到目前为止,我没有看到任何消息,也不确定 kernel 是否正在使用我的驱动程序来处理这个外围设备。 Can any one tell me what I have missed or any errors?任何人都可以告诉我我遗漏了什么或有任何错误吗?

Step one is to hook up to the debug UART on the Beaglebone .第一步是连接到 Beaglebone 上的调试 UART It gives you the necessary low level information to figure out what is happening during the U-Boot phase and early Kernel boot.它为您提供必要的低级信息,以了解在 U-Boot 阶段和早期 Kernel 引导期间发生的情况。

For embedded (Linux) work there is no excuse from hooking up to a UART.对于嵌入式 (Linux) 工作,没有理由不连接到 UART。 It's an essential tool.这是一个必不可少的工具。

Things to check for in this case is if you have more than one U-Boot in play (eMMC vs SD) and if you're working with the right one.在这种情况下要检查的事情是您是否有多个 U-Boot 正在运行(eMMC 与 SD)以及您是否正在使用正确的 U-Boot。 Also if there are any error messages from U-Boot, etc. It should be quite easy to tell from the UART output.此外,如果 U-Boot 等有任何错误消息。从 UART output 应该很容易判断。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM