]> rtime.felk.cvut.cz Git - sojka/can-utils.git/blobdiff - slcanpty.c
can-utils: AOSP build clean up
[sojka/can-utils.git] / slcanpty.c
index ada6f3bc68b679be62796fd1062f8aaf7402583b..2c1cabea262f2b80f10a40bd9cf912a7c03a2a7b 100644 (file)
@@ -1,7 +1,3 @@
-/*
- *  $Id$
- */
-
 /*
  * slcanpty.c -  creates a pty for applications using the slcan ASCII protocol
  * and converts the ASCII data to a CAN network interface (and vice versa)
@@ -26,6 +22,9 @@
  *
  */
 
+/* To get ptsname grantpt and unlockpt definitions from stdlib.h */
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +33,7 @@
 #include <termios.h>
 
 #include <net/if.h>
+#include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -43,6 +43,7 @@
 
 /* maximum rx buffer len: extended CAN frame with timestamp */
 #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
+#define DEVICE_NAME_PTMX "/dev/ptmx"
 
 #define DEBUG
 
@@ -63,7 +64,7 @@ static int asc2nibble(char c)
 
 /* read data from pty, send CAN frames to CAN socket and answer commands */
 int pty2can(int pty, int socket, struct can_filter *fi,
-              int *is_open, int *tstamp)
+           int *is_open, int *tstamp)
 {
        int nbytes;
        char cmd;
@@ -72,13 +73,21 @@ int pty2can(int pty, int socket, struct can_filter *fi,
        int ptr;
        struct can_frame frame;
        int tmp, i;
+       static int rxoffset = 0; /* points to the end of an received incomplete SLCAN message */
+
+       nbytes = read(pty, &buf[rxoffset], sizeof(buf)-rxoffset-1);
+       if (nbytes <= 0) {
+               /* nbytes == 0 : no error but pty decriptor has been closed */
+               if (nbytes < 0)
+                       perror("read pty");
 
-       nbytes = read(pty, &buf, sizeof(buf)-1);
-       if (nbytes < 0) {
-               perror("read pty");
                return 1;
        }
 
+       /* reset incomplete message offset */
+       nbytes += rxoffset;
+       rxoffset = 0;
+
 rx_restart:
        /* remove trailing '\r' characters to be robust against some apps */
        while (buf[0] == '\r' && nbytes > 0) {
@@ -90,6 +99,21 @@ rx_restart:
        if (!nbytes)
                return 0;
 
+       /* check if we can detect a complete SLCAN message including '\r' */
+       for (tmp = 0; tmp < nbytes; tmp++) {
+               if (buf[tmp] == '\r')
+                       break;
+       }
+
+       /* no '\r' found in the message buffer? */
+       if (tmp == nbytes) {
+               /* save incomplete message */
+               rxoffset = nbytes;
+
+               /* leave here and read from pty again */
+               return 0;
+       }
+
        cmd = buf[0];
        buf[nbytes] = 0;
 
@@ -273,8 +297,8 @@ rx_restart:
                        ptr--;
        }
 
-       nbytes = write(socket, &frame, sizeof(frame));
-       if (nbytes != sizeof(frame)) {
+       tmp = write(socket, &frame, sizeof(frame));
+       if (tmp != sizeof(frame)) {
                perror("write socket");
                return 1;
        }
@@ -362,6 +386,27 @@ int can2pty(int pty, int socket, int *tstamp)
        return 0;
 }
 
+int check_select_stdin(void)
+{
+       fd_set rdfs;
+       struct timeval timeout;
+       int ret;
+
+       FD_ZERO(&rdfs);
+       FD_SET(0, &rdfs);
+       timeout.tv_sec = 0;
+       timeout.tv_usec = 0;
+
+       ret = select(1, &rdfs, NULL, NULL, &timeout);
+
+       if (ret < 0)
+               return 0; /* not selectable */
+
+       if (ret > 0 && getchar() == EOF)
+               return 0; /* EOF, eg. /dev/null */
+
+       return 1;
+}
 
 int main(int argc, char **argv)
 {
@@ -371,6 +416,7 @@ int main(int argc, char **argv)
        struct sockaddr_can addr;
        struct termios topts;
        struct ifreq ifr;
+       int select_stdin = 0;
        int running = 1;
        int tstamp = 0;
        int is_open = 0;
@@ -386,10 +432,14 @@ int main(int argc, char **argv)
                fprintf(stderr, "Usage: %s <pty> <can interface>\n", argv[0]);
                fprintf(stderr, "e.g. '%s /dev/ptyc0 can0' creates"
                        " /dev/ttyc0 for the slcan application\n", argv[0]);
+               fprintf(stderr, "e.g. for pseudo-terminal '%s %s can0' creates"
+                       " /dev/pts/N\n", argv[0], DEVICE_NAME_PTMX);
                fprintf(stderr, "\n");
                return 1;
        }
 
+       select_stdin = check_select_stdin();
+
        /* open pty */
        p = open(argv[1], O_RDWR);
        if (p < 0) {
@@ -404,9 +454,34 @@ int main(int argc, char **argv)
 
        /* disable local echo which would cause double frames */
        topts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK |
-                          ECHONL | ECHOPRT | ECHOKE | ICRNL);
+                          ECHONL | ECHOPRT | ECHOKE);
+       topts.c_iflag &= ~(ICRNL);
+       topts.c_iflag |= INLCR;
        tcsetattr(p, TCSANOW, &topts);
 
+       /* Support for the Unix 98 pseudo-terminal interface /dev/ptmx /dev/pts/N */
+       if  (strcmp(argv[1], DEVICE_NAME_PTMX) == 0) {
+
+               char *name_pts = NULL;  /* slave pseudo-terminal device name */
+
+               if (grantpt(p) < 0) {
+                       perror("grantpt");
+                       return 1;
+               }
+
+               if (unlockpt(p) < 0) {
+                       perror("unlockpt");
+                       return 1;
+               }
+
+               name_pts = ptsname(p);
+               if (name_pts == NULL) {
+                       perror("ptsname");
+                       return 1;
+               }
+               printf("open: %s: slave pseudo-terminal is %s\n", argv[1], name_pts);
+       }
+
        /* open socket */
        s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        if (s < 0) {
@@ -438,7 +513,10 @@ int main(int argc, char **argv)
        while (running) {
 
                FD_ZERO(&rdfs);
-               FD_SET(0, &rdfs);
+
+               if (select_stdin)
+                       FD_SET(0, &rdfs);
+
                FD_SET(p, &rdfs);
                FD_SET(s, &rdfs);