summaryrefslogtreecommitdiffstats
path: root/sw/programmer/linux/src/serial.c
blob: 42f021e546272df7fe3c92e9a41a7f9fa87ae638 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include "serial.h"

static int rate_to_constant(int baudrate) {
#define B(x) case x: return B##x
    switch(baudrate) {
        B(50);     B(75);     B(110);    B(134);    B(150);
        B(200);    B(300);    B(600);    B(1200);   B(1800);
        B(2400);   B(4800);   B(9600);   B(19200);  B(38400);
        B(57600);  B(115200); B(230400); B(460800); B(500000); 
        B(576000); B(921600); B(1000000);B(1152000);B(1500000); 
    default: return 0;
    }
#undef B
}

int serial_open(const char *devpath, unsigned long baudrate)
{
    int fd, speed;
    struct termios tty;
    struct serial_struct serinfo;
    // struct termios tty_old;
    
    // open device
    if ((fd = open(devpath, O_RDWR | O_NOCTTY)) < 0) {
        return -1;
    }

    speed = rate_to_constant(baudrate);
    if (speed == 0) {
        /* custom divisor */
        serinfo.reserved_char[0] = 0;
        if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
            return -1;
        serinfo.flags &= ~ASYNC_SPD_MASK;
        serinfo.flags |= ASYNC_SPD_CUST;
        serinfo.custom_divisor = (serinfo.baud_base + (baudrate / 2)) / baudrate;

        if (serinfo.custom_divisor < 1) 
            serinfo.custom_divisor = 1;
        if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
            return -1;
        if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
            return -1;
        if (serinfo.custom_divisor * baudrate != serinfo.baud_base) {
            warnx("actual baudrate is %d / %d = %f",
                  serinfo.baud_base, serinfo.custom_divisor,
                  (float)serinfo.baud_base / serinfo.custom_divisor);
        }
    }

    // set custom baudrate
    ioctl(fd, TIOCGSERIAL, &serinfo);
    serinfo.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY; 
    serinfo.custom_divisor = serinfo.baud_base / baudrate; 
    ioctl(fd, TIOCSSERIAL, &serinfo);

    // set parameters
    if (tcgetattr(fd, &tty) != 0) {
        // perror("failed tcgetattr");
        return -1;
    }

    cfsetispeed(&tty, speed ?: B38400);
    cfsetospeed(&tty, speed ?: B38400);
    cfmakeraw(&tty);

    tty.c_cflag &= ~PARENB;  // no parity
    tty.c_cflag &= ~CSTOPB;  // no stop bit
    tty.c_cflag |= CS8;      // 8 bit data
    tty.c_cflag &= ~CRTSCTS; // no flow control

    tty.c_lflag     =  0;  // no signaling chars, no echo, no canonical processing
    tty.c_oflag     =  0;  // no remapping, no delays
    tty.c_cc[VMIN]  =  0;  // no block read
    tty.c_cc[VTIME] =  5;  // .5 seconds read timeout

    tty.c_cflag |= CREAD | CLOCAL;      // turn on read and ignore ctrl lines
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
    tty.c_oflag &= ~OPOST; // make raw

    tcflush(fd , TCIFLUSH); // ?

    if (tcsetattr (fd, TCSANOW, &tty) != 0) {
        // perror("failed tcsetattr to set serial port.\n");
        return -1;
    }

    return fd;
}