3

The device is a scanner. I know uart5 is setup in the dtsi file and in userspace it is listed under /dev/ttymxc4. From userspace, I understand that I can manipulate the device by

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;
}

And use termios to set all the settings like baudrate, write data using the write call etc.

I am wanting to abstract a lot of the commands under sysfs. I've setup a "uart driver" like this:

result = uart_register_driver(&scanner_reg);
if (result)
    return result;

result = uart_add_one_port(&scanner_reg, &scanner_port);
if (result)
    uart_unregister_driver(&scanner_reg);

And I am using gpio lines to turn on the system and a few other things. However, in the schematic, I do not see the gpio lines for these things.

UART5_CTS_HOST_SCAN_3_3V
UART5_RTS_HOST_SCAN_3_3V
UART5_RxD_HOST_SCAN_3_3V
UART5_TxD_HOST_SCAN_3_3V

I am just not sure how to open/write/read data from the device. I know about sys_open and similar calls, however, I know they are not the "right" way to do this; I don't want to have to go through userspace.

So, in summary how do I

  1. "choose" the /dev/ttymxc4 device in my module and
  2. open, set baud rate, and read/write data to the device?

Thanks! Please help! New to everything uart, I've dealt with i2c in the past and it seemed less complicated.

Ryan
  • 436
  • 5
  • 15
  • 3
    The "right" way would be to go through the "serdev" device interface (`#include `), especially for a device-tree based system. The DT bindings are documented in `Documentation/devicetree/bindings/serial/slave-device.txt`. Example drivers include `drivers/mfd/rave-sp.c`, `drivers/net/ethernet/qualcomm/qca_uart.c` and a few bluetooth drivers (grep for `serdev_device`). – Ian Abbott Jun 26 '18 at 15:25
  • The product is the Motorala (Zebra) SE4500. Want to add it to Android through sysfs. The kernel is 4.9 which doesn't seem to include serdev. However I am trying to add it in since it is very modular (so far the build is compiling). Do you think this is the best solution or is there is a 4.9 "right" solution? Thanks. – Ryan Jun 26 '18 at 21:00
  • 1
    I don't know if there is a "right" solution for 4.9 since the serdev interface is new since 4.11 and there was nothing equivalent before it. Backporting seems like a reasonable choice. It is fairly self-contained, but not quite, as changes are needed to the "tty_port" code to handle port client functions and to hook in the "serdev" stuff. See commits c3485ee0d560 , c1c98dadb2de, cd6484e1830b, bed35c6dfa6a, 8ee3fde04758 for starters. – Ian Abbott Jun 27 '18 at 14:39
  • Does backporting require changing tty_port.c? `tty` seems to be built into my 4.4-127 El Repo LT kernel. Is there an easier way? I just need to control RTS pin... https://stackoverflow.com/questions/56954882/control-usart-rts-pin-from-driver-on-embedded-board?noredirect=1#comment100543754_56954882 – Danny Jul 15 '19 at 05:42
  • 1
    I tried compiling `serdev` as a module under 4.4.127. Unfortunately, it quickly got complicated and touched source for built-in drivers. So doesn't look like `serdev` will be a solution for controlling UART RTS pin from a kernel module... – Danny Jul 15 '19 at 07:25

1 Answers1

1

Here's what I did. I am accessing the uart file from the kernel instead of using any kernel native method. This is cheating, but it works. So,

#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;
    }

I then use Sysfs to provide functionality to the user. Is this the right way to do it? Probably not, but it is working for my purposes. It is essentially moving the user-space way of implementing to the kernel.

Ryan
  • 436
  • 5
  • 15
  • You should not open a character device from kernel space. Instead of that you could use something like a serdev. There are some Bluetooth drivers as an example. – Gonzo Gonzales May 29 '21 at 10:54
  • See the comments on the original question. Serdev was the stated answer, but the kernel being used didn't have serdev. It was not an option. – Ryan Jun 04 '21 at 17:49