4
votes

I'm writing a kernel module for Linux v3.2 to control an external laser, but I'm having trouble communicating signals through the RS232 serial port on the machine.

There seems to be a serial driver already compiled in my kernel holding ownership to the ioport addresses I want to access:

# cat /proc/ioports | grep serial
  02e8-02ef : serial
  02f8-02ff : serial
  03f8-03ff : serial

This makes sense since Linux allows userspace programs to use the /dev/ttyS* device nodes to communicate via the serial ports. For example, here's how I setup the device for an LCD panel:

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

However, this is a userspace interface and thus incompatible with my kernelspace module. I need a way to produce the same effect (RS232 serial I/O) but from within kernelspace.

Although I can unload the default Linux serial driver and replace it with my own custom RS232 driver, I don't want to reinvent the wheel here -- the default Linux serial driver appears to support the functionality I require.

Is there a simple way to communicate via the RS232 serial ports in kernelspace (perhaps through this default Linux serial driver), or do I just have to program my own custom RS232 driver?

1
Why can't you write all your application in userspace? Why do you need a device driver?eepp
I think this link can give you a good overview of the Linux kernel serial driver architecture : free-electrons.com/doc/serial-drivers.pdfMali
@eepp The driver controls the laser over several ioport addresses, and performs very hardware specific operations. The serial I/O must occur in various sections of code, and the overall code will be very intertwined with the serial I/O. In other words, the existing code and design is not suited for userspace.Vilhelm Gray
@VilhelmGray - eepp's thought about moving to userspace is valid, but if you feel you cannot you may want to look at things in the kernel which do use the serial port, for example the serial console, KGDB, etc. As a pragmatic matter, accessing specific serial hardware is not that complicated, so if you can't find an elegant solution using an existing abstraction, duplicating the actual UART interface code may not be the end of the world as long as your envisioned range of hardware is small.Chris Stratton
Minimizing the kernel content really is a good idea. Make a mini-driver that exposes your ioport operations via an ioctl(), then the rest can live in userland.Peter

1 Answers

6
votes

Take a look at line disciplines. You can use it to attach a tty device to some read and write routines defined in kernelspace and thus write a driver using a serial line. The line discipline gets attached to it from user space and the kernel will do all the rest.

A good example is slcan (drivers/net/can/slcan.c). Examples for the libc calls handling this can be found in can-utils (https://gitorious.org/linux-can/can-utils). If you prefer working by book chapter 18 of Linux Device Drivers will give a good first step: http://www.makelinux.net/ldd3/chp-18, though the book is not very up to date. Some details may also be found at http://www.linusakesson.net/programming/tty/.