bf5d46998b19f10dbf321fae72458f13e38df07a
[fw/altos] / libaltos / libaltos_posix.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "libaltos_private.h"
20 #include "libaltos_posix.h"
21
22 void
23 altos_set_last_posix_error(void)
24 {
25         altos_set_last_error(errno, strerror(errno));
26 }
27
28 PUBLIC struct altos_file *
29 altos_open(struct altos_device *device)
30 {
31         struct altos_file_posix *file = calloc (sizeof (struct altos_file_posix), 1);
32         int                     ret;
33         struct termios          term;
34
35         if (!file) {
36                 altos_set_last_posix_error();
37                 return NULL;
38         }
39
40 //      altos_set_last_error(12, "yeah yeah, failed again");
41 //      free(file);
42 //      return NULL;
43
44         file->fd = open(device->path, O_RDWR | O_NOCTTY);
45         if (file->fd < 0) {
46                 altos_set_last_posix_error();
47                 free(file);
48                 return NULL;
49         }
50 #ifdef USE_POLL
51         pipe(file->pipe);
52 #else
53         file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
54         if (file->out_fd < 0) {
55                 altos_set_last_posix_error();
56                 close(file->fd);
57                 free(file);
58                 return NULL;
59         }
60 #endif
61         ret = tcgetattr(file->fd, &term);
62         if (ret < 0) {
63                 altos_set_last_posix_error();
64                 close(file->fd);
65 #ifndef USE_POLL
66                 close(file->out_fd);
67 #endif
68                 free(file);
69                 return NULL;
70         }
71         cfmakeraw(&term);
72         cfsetospeed(&term, B9600);
73         cfsetispeed(&term, B9600);
74 #ifdef USE_POLL
75         term.c_cc[VMIN] = 1;
76         term.c_cc[VTIME] = 0;
77 #else
78         term.c_cc[VMIN] = 0;
79         term.c_cc[VTIME] = 1;
80 #endif
81         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
82         if (ret < 0) {
83                 altos_set_last_posix_error();
84                 close(file->fd);
85 #ifndef USE_POLL
86                 close(file->out_fd);
87 #endif
88                 free(file);
89                 return NULL;
90         }
91         return &file->file;
92 }
93
94 PUBLIC void
95 altos_close(struct altos_file *file_common)
96 {
97         struct altos_file_posix *file = (struct altos_file_posix *) file_common;
98
99         if (file->fd != -1) {
100                 int     fd = file->fd;
101                 file->fd = -1;
102 #ifdef USE_POLL
103                 write(file->pipe[1], "\r", 1);
104 #else
105                 close(file->out_fd);
106                 file->out_fd = -1;
107 #endif
108                 close(fd);
109         }
110 }
111
112 PUBLIC int
113 altos_flush(struct altos_file *file_common)
114 {
115         struct altos_file_posix *file = (struct altos_file_posix *) file_common;
116
117         if (file->file.out_used && 0) {
118                 printf ("flush \"");
119                 fwrite(file->file.out_data, 1, file->file.out_used, stdout);
120                 printf ("\"\n");
121         }
122         while (file->file.out_used) {
123                 int     ret;
124
125                 if (file->fd < 0)
126                         return -EBADF;
127 #ifdef USE_POLL
128                 ret = write (file->fd, file->file.out_data, file->file.out_used);
129 #else
130                 ret = write (file->out_fd, file->file.out_data, file->file.out_used);
131 #endif
132                 if (ret < 0) {
133                         altos_set_last_posix_error();
134                         return -altos_last_error.code;
135                 }
136                 if (ret) {
137                         memmove(file->file.out_data, file->file.out_data + ret,
138                                 file->file.out_used - ret);
139                         file->file.out_used -= ret;
140                 }
141         }
142         return 0;
143 }
144
145
146 #ifdef USE_POLL
147 #include <poll.h>
148 #endif
149
150 int
151 altos_fill(struct altos_file *file_common, int timeout)
152 {
153         struct altos_file_posix *file = (struct altos_file_posix *) file_common;
154
155         int             ret;
156 #ifdef USE_POLL
157         struct pollfd   fd[2];
158 #endif
159         if (timeout == 0)
160                 timeout = -1;
161         while (file->file.in_read == file->file.in_used) {
162                 if (file->fd < 0)
163                         return LIBALTOS_ERROR;
164 #ifdef USE_POLL
165                 fd[0].fd = file->fd;
166                 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
167                 fd[1].fd = file->pipe[0];
168                 fd[1].events = POLLIN;
169                 ret = poll(fd, 2, timeout);
170                 if (ret < 0) {
171                         altos_set_last_posix_error();
172                         return LIBALTOS_ERROR;
173                 }
174                 if (ret == 0)
175                         return LIBALTOS_TIMEOUT;
176
177                 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
178                         return LIBALTOS_ERROR;
179                 if (fd[0].revents & POLLIN)
180 #endif
181                 {
182                         ret = read(file->fd, file->file.in_data, USB_BUF_SIZE);
183                         if (ret < 0) {
184                                 altos_set_last_posix_error();
185                                 return LIBALTOS_ERROR;
186                         }
187                         file->file.in_read = 0;
188                         file->file.in_used = ret;
189 #ifndef USE_POLL
190                         if (ret == 0 && timeout > 0)
191                                 return LIBALTOS_TIMEOUT;
192 #endif
193                 }
194         }
195         if (file->file.in_used && 0) {
196                 printf ("fill \"");
197                 fwrite(file->file.in_data, 1, file->file.in_used, stdout);
198                 printf ("\"\n");
199         }
200         return 0;
201 }
202