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