C 如何从内核模块打开/写入/读取uart设备?

C 如何从内核模块打开/写入/读取uart设备?,c,linux,module,kernel,uart,C,Linux,Module,Kernel,Uart,这个设备是扫描仪。我知道uart5是在dtsi文件中设置的,在用户空间中它列在/dev/ttymxc4下。从用户空间,我知道我可以通过 fd = open("/dev/ttymxc5", O_RDWR|O_NOCTTY|O_NONBLOCK); if (fd < 0) { fprintf (stderr,"Open error on %s: %s\n", SCANNER_UART, strerror(errno)); return nullptr; } 我用gpio线路打

这个设备是扫描仪。我知道uart5是在dtsi文件中设置的,在用户空间中它列在/dev/ttymxc4下。从用户空间,我知道我可以通过

fd = open("/dev/ttymxc5", O_RDWR|O_NOCTTY|O_NONBLOCK);
if (fd < 0)
{
    fprintf (stderr,"Open error on %s: %s\n", SCANNER_UART, strerror(errno));
    return nullptr;
}
我用gpio线路打开系统和其他一些东西。然而,在示意图中,我没有看到这些东西的gpio线

UART5_CTS_HOST_SCAN_3_3V
UART5_RTS_HOST_SCAN_3_3V
UART5_RxD_HOST_SCAN_3_3V
UART5_TxD_HOST_SCAN_3_3V
我只是不知道如何打开/写入/读取设备中的数据。我知道sys_open和类似的调用,但是,我知道它们不是这样做的“正确”方式;我不想通过用户空间

总之,我该怎么做

  • “选择”模块中的/dev/ttymxc4设备,然后
  • 打开、设置波特率,并向设备读/写数据

  • 谢谢!请帮忙!对于uart来说,一切都是新事物,我在过去处理过i2c,它似乎不那么复杂。

    以下是我所做的。我从内核访问uart文件,而不是使用任何内核本机方法。这是欺骗,但它是有效的。所以

    #define SCANNER_UART "/dev/ttymxc4"
    ...
    static int scanner_open(struct inode *inode, struct file *file)
        struct termios term;
    ...
        scanner_file = filp_open(SCANNER_UART, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
    ...
        if (serial_tty_ioctl(scanner_file, TCGETS, (unsigned long)&term) < 0)
        {
            pr_err("%s: Failed to get termios\n", __FUNCTION__);
            return -1;
        }
    
        term.c_cflag  = B9600 | CLOCAL | CREAD; // 115200 if change, must configure scanner
    
        /* No parity (8N1) */
        term.c_cflag &= ~PARENB;
        term.c_cflag &= ~CSTOPB;
        term.c_cflag &= ~CSIZE;
        term.c_cflag |= CS8;
    
        term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        term.c_oflag &= ~OPOST;
    
        term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    
        term.c_cc[VTIME] = 5; // 0.5 seconds read timeout
        term.c_cc[VMIN] = 0;  // read does not block
    
        if (serial_tty_ioctl(scanner_file, TCSETS, (unsigned long)&term) < 0)
        {
            pr_err("%s: Failed to set termios\n", __FUNCTION__);
            return -1;
        }
    ...
    static const struct file_operations scanner_fops = {
        .owner          = THIS_MODULE,
        .write          = scanner_write,
        .read           = scanner_read,
        .open           = scanner_open,
        .release        = scanner_close,
        .llseek         = no_llseek,
    };
    
    struct miscdevice scanner_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "scanner",
        .fops = &scanner_fops,
    };
    
    ...
        ret = misc_register(&scanner_device);
        if (ret) {
            pr_err("can't misc_register :(\n");
            return ret;
        }
    
    #定义扫描器UART”/dev/ttymxc4
    ...
    静态整型扫描程序\u打开(结构索引节点*索引节点,结构文件*文件)
    结构术语;
    ...
    scanner_file=filp_open(scanner_UART,O_RDWR | O_NOCTTY | O_NONBLOCK,0);
    ...
    if(串行扫描文件、TCGETS(无符号长期)和长期)小于0
    {
    pr_err(“%s:无法获取termios\n”,_函数);
    返回-1;
    }
    term.c|cflag=B9600 | CLOCAL | CREAD;//115200如果更改,必须配置扫描仪
    /*无奇偶校验(8N1)*/
    term.c_cflag&=~PARENB;
    term.c_cflag&=~CSTOPB;
    term.c_cflag&=~CSIZE;
    术语c|cflag |=CS8;
    术语c|lflag&=~(ICANON | ECHO | ECHO | ISIG);
    术语c_of lag&=~OPOST;
    术语c|u iflag&=~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    term.c_cc[VTIME]=5;//0.5秒读取超时
    term.c_cc[VMIN]=0;//读不阻塞
    if(串行扫描文件、TCSETS(无符号长期)和长期)小于0
    {
    pr_err(“%s:设置termios\n失败”,函数);
    返回-1;
    }
    ...
    静态常量结构文件\u操作扫描程序\u fops={
    .owner=此_模块,
    .write=扫描器\写入,
    .read=扫描仪读取,
    .open=扫描仪\u打开,
    .release=scanner\u close,
    .llseek=nollseek,
    };
    结构MISC设备扫描仪\u设备={
    .minor=杂项动力minor,
    .name=“扫描仪”,
    .fops=&scanner\u fops,
    };
    ...
    ret=杂项寄存器(和扫描仪设备);
    如果(ret){
    pr_err(“无法杂项注册:(\n”);
    返回ret;
    }
    

    然后,我使用Sysfs向用户提供功能。这是正确的方法吗?可能不是,但它符合我的目的。它本质上是将实现的用户空间方式移动到内核。

    正确的方法是通过“serdev”设备接口(
    #include
    ),特别是对于基于设备树的系统。DT绑定记录在
    文档/devicetree/bindings/serial/slave device.txt
    中。示例驱动程序包括
    驱动程序/mfd/rave-sp.c
    驱动程序/net/ethernet/qualcomm/qca_uart.c
    和一些蓝牙驱动程序(grep用于
    serdevu设备
    )。该产品是Motorala(Zebra)SE4500。想通过sysfs将其添加到Android中。内核是4.9,似乎不包括serdev。但是我正在尝试添加它,因为它是非常模块化的(到目前为止,构建正在编译)。你认为这是最好的解决方案还是有4.9“正确”的解决方案?谢谢。我不知道是否有其他解决方案4.9的“正确”解决方案,因为serdev接口自4.11以来是新的,之前没有任何等效的接口。后端口似乎是一个合理的选择。它是相当独立的,但不是完全独立的,因为需要更改“tty_port”代码来处理端口客户机功能和挂接“serdev”"内容。请参阅提交c3485ee0d560、c1c98dadb2de、cd6484e1830b、BED3C6DFA6A、8ee3fde04758作为初学者。后端口是否需要更改tty_port.c?
    tty
    似乎内置于我的4.4-127 El Repo LT内核中。有没有更简单的方法?我只需要控制RTS pin…我尝试将
    serdev
    编译为4.4.127下的模块。Unfo当然,它很快就变得复杂,并且触及了内置驱动程序的源代码。因此,看起来不像
    serdev
    将是一个从内核模块控制UART RTS引脚的解决方案……您不应该从内核空间打开字符设备。相反,您可以使用类似于serdev的东西。有一些蓝牙驱动程序作为示例e、 请参阅对原始问题的评论。Serdev是声明的答案,但所使用的内核没有Serdev。这不是一个选项。
    #define SCANNER_UART "/dev/ttymxc4"
    ...
    static int scanner_open(struct inode *inode, struct file *file)
        struct termios term;
    ...
        scanner_file = filp_open(SCANNER_UART, O_RDWR|O_NOCTTY|O_NONBLOCK, 0);
    ...
        if (serial_tty_ioctl(scanner_file, TCGETS, (unsigned long)&term) < 0)
        {
            pr_err("%s: Failed to get termios\n", __FUNCTION__);
            return -1;
        }
    
        term.c_cflag  = B9600 | CLOCAL | CREAD; // 115200 if change, must configure scanner
    
        /* No parity (8N1) */
        term.c_cflag &= ~PARENB;
        term.c_cflag &= ~CSTOPB;
        term.c_cflag &= ~CSIZE;
        term.c_cflag |= CS8;
    
        term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        term.c_oflag &= ~OPOST;
    
        term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    
        term.c_cc[VTIME] = 5; // 0.5 seconds read timeout
        term.c_cc[VMIN] = 0;  // read does not block
    
        if (serial_tty_ioctl(scanner_file, TCSETS, (unsigned long)&term) < 0)
        {
            pr_err("%s: Failed to set termios\n", __FUNCTION__);
            return -1;
        }
    ...
    static const struct file_operations scanner_fops = {
        .owner          = THIS_MODULE,
        .write          = scanner_write,
        .read           = scanner_read,
        .open           = scanner_open,
        .release        = scanner_close,
        .llseek         = no_llseek,
    };
    
    struct miscdevice scanner_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "scanner",
        .fops = &scanner_fops,
    };
    
    ...
        ret = misc_register(&scanner_device);
        if (ret) {
            pr_err("can't misc_register :(\n");
            return ret;
        }