内核空间中RS232串口通信

4
我正在为Linux v3.2编写内核模块,用于控制外部激光器,但我在通过机器上的RS232串行端口进行信号传输时遇到了问题。 似乎已经在我的内核中编译了一个串行驱动程序,拥有我想要访问的ioport地址的所有权:
# cat /proc/ioports | grep serial
  02e8-02ef : serial
  02f8-02ff : serial
  03f8-03ff : serial

这是有道理的,因为Linux允许用户空间程序使用/dev/ttyS*设备节点通过串行端口进行通信。例如,以下是我为LCD面板设置设备的方法:

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

...

    /* Initialization and configuration */
    const char *const lcd_dev = "/dev/ttyS1";        
    int lcd_dev_fd = open(lcd_dev, O_RDWR | O_NOCTTY | O_NDELAY);
    fcntl(lcd_dev_fd, F_SETFL, 0);

    struct termios options;
    tcgetattr(lcd_dev_fd, &options);

    cfsetispeed(&options, B19200);
    cfsetospeed(&options, B19200);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    options.c_oflag &= ~OPOST;

    tcsetattr(lcd_dev_fd, TCSANOW, &options);

    ...

    /* sending bytes */
    const unsigned char scls[] = {0xFE, 'X', 0xFE, 75, 0xFE, 84, 0xFE, 'H'};
    write(lcd_dev_fd, scls, sizeof(scls);

然而,这是一个用户空间接口,因此与我的内核空间模块 不兼容。我需要一种方法来在内核空间中产生相同的效果(RS232串行I/O)。
虽然我可以卸载默认的Linux 串行驱动程序并用自己的自定义RS232驱动程序替换它,但我不想重复造轮子 - 默认的Linux 串行 驱动程序似乎支持我所需的功能。
是否有一种简单的方式可以通过内核空间使用RS232串行端口(也许是通过默认的Linux 串行驱动程序),还是我只能编写自己的自定义RS232驱动程序?

3
为什么不能在用户空间编写所有应用程序?为什么需要设备驱动程序? - eepp
2
我认为这个链接可以给你一个很好的Linux内核串行驱动程序架构概述:http://free-electrons.com/doc/serial-drivers.pdf - Mali
1
@eepp 驱动程序通过多个ioport地址控制激光,并执行非常硬件特定的操作。串行I/O必须在代码的各个部分发生,并且整体代码将与串行I/O紧密相连。换句话说,现有的代码和设计不适合用户空间。 - Vilhelm Gray
2
@VilhelmGray - eepp关于转移到用户空间的想法是有道理的,但如果您觉得自己无法做到,您可能需要查看内核中使用串口的事物,例如串行控制台、KGDB等。实际上,访问特定串行硬件并不那么复杂,因此,如果您找不到使用现有抽象的优雅解决方案,复制实际的UART接口代码可能并不是世界末日,只要您预期的硬件范围较小即可。 - Chris Stratton
2
减少内核内容确实是个好主意。制作一个迷你驱动程序,通过 ioctl() 公开您的 ioport 操作,然后其余部分可以存在于用户空间中。 - Peter
显示剩余6条评论
1个回答

7
请看线路规程。你可以使用它将tty设备附加到内核空间中定义的某些读写例程上,从而使用串行线编写驱动程序。用户空间会将线路规程附加到其中,内核将完成其余所有工作。
一个很好的例子是slcan(drivers/net/can/slcan.c)。处理这个问题的libc调用示例可以在can-utils(https://gitorious.org/linux-can/can-utils)中找到。如果你喜欢按章节工作,Linux设备驱动程序的第18章将是一个不错的起点:http://www.makelinux.net/ldd3/chp-18,尽管这本书已经过时了。一些细节也可以在http://www.linusakesson.net/programming/tty/上找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接