dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pios_serial.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  *
30  * Additional note on redistribution: The copyright and license notices above
31  * must be maintained in each individual source file that is a derivative work
32  * of this source file; otherwise redistribution is prohibited.
33  */
34 
35 /* Project Includes */
36 #include "pios.h"
37 
38 #include <pios_serial_priv.h>
39 #include "pios_thread.h"
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #ifdef PIOS_INCLUDE_SERIAL
46 #include <termios.h>
47 #include <linux/serial.h>
48 #include <sys/ioctl.h>
49 #endif
50 
51 /* Provide a COM driver */
52 #ifdef PIOS_INCLUDE_SERIAL
53 /* PIOS_INCLUDE_SERIAL now just refers to the stuff relating to outright
54  * serial ports, not just the stuff we do to wrap FDs.
55  */
56 static void PIOS_SERIAL_ChangeBaud(uintptr_t serial_id, uint32_t baud);
57 #endif
58 static void PIOS_SERIAL_RegisterRxCallback(uintptr_t udp_id, pios_com_callback rx_in_cb, uintptr_t context);
59 static void PIOS_SERIAL_RegisterTxCallback(uintptr_t udp_id, pios_com_callback tx_out_cb, uintptr_t context);
60 static void PIOS_SERIAL_TxStart(uintptr_t udp_id, uint16_t tx_bytes_avail);
61 static void PIOS_SERIAL_RxStart(uintptr_t udp_id, uint16_t rx_bytes_avail);
62 
63 typedef struct {
64  int readfd, writefd;
65 
66  // TODO: Think about the volatility of callbacks, etc.
67  // (Not unique to this module)
69  uintptr_t tx_out_context;
71  uintptr_t rx_in_context;
72 
73  uint8_t rx_buffer[PIOS_SERIAL_RX_BUFFER_SIZE];
74  uint8_t tx_buffer[PIOS_SERIAL_RX_BUFFER_SIZE];
75 
78 } pios_ser_dev;
79 
81 #if defined(PIOS_INCLUDE_SERIAL)
82  .set_baud = PIOS_SERIAL_ChangeBaud,
83 #endif
84  .tx_start = PIOS_SERIAL_TxStart,
85  .rx_start = PIOS_SERIAL_RxStart,
86  .bind_tx_cb = PIOS_SERIAL_RegisterTxCallback,
87  .bind_rx_cb = PIOS_SERIAL_RegisterRxCallback,
88 };
89 
90 static pios_ser_dev * find_ser_dev_by_id(uintptr_t serial)
91 {
92  return (pios_ser_dev *) serial;
93 }
94 
95 static void rx_do_cb(pios_ser_dev *ser_dev, uint8_t *incoming_buffer,
96  int len) {
97 
98  if (ser_dev->rx_in_cb) {
99  bool rx_need_yield = false;
100 
101  ser_dev->rx_in_cb(ser_dev->rx_in_context, incoming_buffer,
102  len, NULL, &rx_need_yield);
103 
104  return;
105  }
106 }
107 
111 static void PIOS_SERIAL_RxTask(void *ser_dev_n)
112 {
113  pios_ser_dev *ser_dev = (pios_ser_dev*)ser_dev_n;
114 
115  const int INCOMING_BUFFER_SIZE = 16;
116  uint8_t incoming_buffer[INCOMING_BUFFER_SIZE];
117 
118  while (1) {
119  int result = read(ser_dev->readfd, incoming_buffer,
120  INCOMING_BUFFER_SIZE);
121 
122  if (result > 0) {
123  rx_do_cb(ser_dev, incoming_buffer, result);
124  }
125 
126  if (result == 0) { /* EOF */
127  if (ser_dev->dont_touch_line) {
128  /* In any case we don't expect a device
129  * to go away. For true serial devices,
130  * it probably means USB or something and
131  * keeping the rest of the tasks going
132  * seems best. If it's stdio-ish, then
133  * we really want to take the process
134  * down.
135  */
136  exit(1);
137  }
138 
139  break;
140  }
141 
142  if (result == -1) {
144  }
145  }
146 }
147 
151 int32_t PIOS_SERIAL_InitFromFd(uintptr_t *serial_id, int readfd,
152  int writefd, bool dont_touch_line)
153 {
154  pios_ser_dev *ser_dev = PIOS_malloc(sizeof(pios_ser_dev));
155 
156  memset(ser_dev, 0, sizeof(*ser_dev));
157 
158  /* initialize */
159  ser_dev->rx_in_cb = NULL;
160  ser_dev->tx_out_cb = NULL;
161 
162  ser_dev->dont_touch_line = dont_touch_line;
163 
164  ser_dev->readfd = readfd;
165  ser_dev->writefd = writefd;
166 
167  PIOS_Thread_Create(PIOS_SERIAL_RxTask, "pios_serial_rx",
169 
170  printf("serial dev %p - fd %i/%i opened\n", ser_dev,
171  ser_dev->readfd, ser_dev->writefd);
172 
173  *serial_id = (uintptr_t) ser_dev;
174 
175 #if defined(PIOS_INCLUDE_SERIAL)
176  PIOS_SERIAL_ChangeBaud(*serial_id, 9600);
177 #endif
178 
179  return 0;
180 }
181 
182 int32_t PIOS_SERIAL_Init(uintptr_t *serial_id, const char *path)
183 {
184 #if defined(PIOS_INCLUDE_SERIAL)
185  int fd = open(path, O_RDWR | O_NOCTTY);
186 
187  if (fd < 0) {
188  perror("serial-open");
189  return -1;
190  }
191 
192  int ret = PIOS_SERIAL_InitFromFd(serial_id, fd, fd, false);
193 
194  if (ret < 0) {
195  close(fd);
196 
197  return ret;
198  }
199 
200  printf("serial dev %p - (path %s)\n", (void *) (*serial_id), path);
201 
202  return ret;
203 #else
204  return -1;
205 #endif
206 }
207 
208 
209 #if defined(PIOS_INCLUDE_SERIAL)
210 void PIOS_SERIAL_ChangeBaud(uintptr_t serial_id, uint32_t baud)
211 {
212  if (!baud) {
213  return;
214  }
215 
216  pios_ser_dev *ser_dev = find_ser_dev_by_id(serial_id);
217 
218  PIOS_Assert(ser_dev);
219 
220  if (ser_dev->dont_touch_line) {
221  return;
222  }
223 
224  struct termios options;
225 
226  memset(&options, 0, sizeof(options));
227 
228  options.c_cflag = CLOCAL | CREAD | CS8;
229 
230  switch (baud) {
231  case 1200:
232  printf("Setting Serial ID %p to 1200 bps\n",
233  ser_dev);
234  cfsetispeed(&options, B1200);
235  cfsetospeed(&options, B1200);
236  break;
237  case 2400:
238  printf("Setting Serial ID %p to 2400 bps\n",
239  ser_dev);
240  cfsetispeed(&options, B2400);
241  cfsetospeed(&options, B2400);
242  break;
243  case 4800:
244  printf("Setting Serial ID %p to 4800 bps\n",
245  ser_dev);
246  cfsetispeed(&options, B4800);
247  cfsetospeed(&options, B4800);
248  break;
249  case 9600:
250  printf("Setting Serial ID %p to 9600 bps\n",
251  ser_dev);
252  cfsetispeed(&options, B9600);
253  cfsetospeed(&options, B9600);
254  break;
255  case 19200:
256  printf("Setting Serial ID %p to 19200 bps\n",
257  ser_dev);
258  cfsetispeed(&options, B19200);
259  cfsetospeed(&options, B19200);
260  break;
261  case 57600:
262  printf("Setting Serial ID %p to 57600 bps\n",
263  ser_dev);
264  cfsetispeed(&options, B57600);
265  cfsetospeed(&options, B57600);
266  break;
267  case 115200:
268  printf("Setting Serial ID %p to 115200 bps\n",
269  ser_dev);
270  cfsetispeed(&options, B115200);
271  cfsetospeed(&options, B115200);
272  break;
273  case 230400:
274  printf("Setting Serial ID %p to 230400 bps\n",
275  ser_dev);
276  cfsetispeed(&options, B230400);
277  cfsetospeed(&options, B230400);
278  break;
279  case 38400:
280  default:
281  /*
282  * This serinfo magic for baud rate is deprecated in
283  * favor of BOTHER on Linux. However, that requires
284  * termios2 and there's various conflict that makes
285  * it hard to do presently.
286  *
287  * Only consequence of using deprecated mechanism
288  * is an angry kprintf.
289  *
290  * Right now, only use it for rates that there isn't
291  * a proper define for, to preserve interoperability
292  * with simple USB drivers.
293  */
294 
295  if (ser_dev->ever_used_custom_rate ||
296  (baud != 38400)) {
297  ser_dev->ever_used_custom_rate = true;
298 
299  struct serial_struct serinfo;
300 
301  if (ioctl(ser_dev->readfd, TIOCGSERIAL, &serinfo)) {
302  perror("ioctl-TIOCGSERIAL");
303  }
304 
305  serinfo.flags =
306  (serinfo.flags & ~ASYNC_SPD_MASK) |
307  ASYNC_SPD_CUST;
308  serinfo.custom_divisor = (serinfo.baud_base + (baud / 2)) / baud;
309  uint32_t closest_speed = serinfo.baud_base / serinfo.custom_divisor;
310 
311  if ((closest_speed < baud * 99 / 100) ||
312  (closest_speed > baud * 101 / 100)) {
313  printf("Can't attain serial rate %d; using %d\n",
314  baud, closest_speed);
315  }
316 
317  if (ioctl(ser_dev->readfd, TIOCSSERIAL, &serinfo)) {
318  perror("ioctl-TIOCSSERIAL");
319  }
320  }
321 
322  printf("Setting Serial ID %p to %d bps\n",
323  ser_dev, baud);
324  cfsetispeed(&options, B38400);
325  cfsetospeed(&options, B38400);
326  break;
327  }
328 
329  /* 1 character is enough to wake us. Leave VTIME at 0, so we will
330  * block arbitrarily long.
331  */
332  options.c_cc[VMIN] = 1;
333 
334  if (tcsetattr(ser_dev->readfd, TCSANOW, &options)) {
335  perror("tcsetattr");
336  }
337 }
338 #endif /* PIOS_INCLUDE_SERIAL */
339 
340 static void PIOS_SERIAL_RxStart(uintptr_t tp_id, uint16_t rx_bytes_avail)
341 {
342 }
343 
344 static void PIOS_SERIAL_TxStart(uintptr_t serial_id, uint16_t tx_bytes_avail)
345 {
346  pios_ser_dev *ser_dev = find_ser_dev_by_id(serial_id);
347 
348  PIOS_Assert(ser_dev);
349 
350  int32_t length,rem;
351 
355  if (ser_dev->tx_out_cb) {
356  while (tx_bytes_avail > 0) {
357  bool tx_need_yield = false;
358  length = (ser_dev->tx_out_cb)(ser_dev->tx_out_context, ser_dev->tx_buffer, PIOS_SERIAL_RX_BUFFER_SIZE, NULL, &tx_need_yield);
359  rem = length;
360  while (rem > 0) {
361  ssize_t len = 0;
362  len = write(ser_dev->writefd, ser_dev->tx_buffer,
363  length);
364  if (len <= 0) {
365  rem = 0;
366  } else {
367  rem -= len;
368  }
369  }
370  tx_bytes_avail -= length;
371  }
372  }
373 }
374 
375 static void PIOS_SERIAL_RegisterRxCallback(uintptr_t serial_id, pios_com_callback rx_in_cb, uintptr_t context)
376 {
377  pios_ser_dev *ser_dev = find_ser_dev_by_id(serial_id);
378 
379  PIOS_Assert(ser_dev);
380 
381  ser_dev->rx_in_context = context;
382  ser_dev->rx_in_cb = rx_in_cb;
383 }
384 
385 static void PIOS_SERIAL_RegisterTxCallback(uintptr_t serial_id, pios_com_callback tx_out_cb, uintptr_t context)
386 {
387  pios_ser_dev *ser_dev = find_ser_dev_by_id(serial_id);
388 
389  PIOS_Assert(ser_dev);
390 
391  ser_dev->tx_out_context = context;
392  ser_dev->tx_out_cb = tx_out_cb;
393 }
394 
static pios_ser_dev * find_ser_dev_by_id(uintptr_t serial)
Definition: pios_serial.c:90
static void PIOS_SERIAL_RegisterRxCallback(uintptr_t udp_id, pios_com_callback rx_in_cb, uintptr_t context)
Definition: pios_serial.c:375
pios_com_callback rx_in_cb
Definition: pios_serial.c:70
const struct pios_com_driver pios_serial_com_driver
Definition: pios_serial.c:80
Main PiOS header to include all the compiled in PiOS options.
uintptr_t rx_in_context
Definition: pios_serial.c:71
static void PIOS_SERIAL_TxStart(uintptr_t udp_id, uint16_t tx_bytes_avail)
Definition: pios_serial.c:344
#define PIOS_SERIAL_RX_BUFFER_SIZE
Definition: pios_board.h:54
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
bool dont_touch_line
Definition: pios_serial.c:77
static void PIOS_SERIAL_RegisterTxCallback(uintptr_t udp_id, pios_com_callback tx_out_cb, uintptr_t context)
Definition: pios_serial.c:385
static void PIOS_SERIAL_RxStart(uintptr_t udp_id, uint16_t rx_bytes_avail)
Definition: pios_serial.c:340
uint8_t length
#define PIOS_THREAD_STACK_SIZE_MIN
Definition: pios_thread.h:55
static void PIOS_SERIAL_RxTask(void *ser_dev_n)
Definition: pios_serial.c:111
bool ever_used_custom_rate
Definition: pios_serial.c:76
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
uintptr_t tx_out_context
Definition: pios_serial.c:69
SERIAL private definitions.
int32_t PIOS_SERIAL_InitFromFd(uintptr_t *serial_id, int readfd, int writefd, bool dont_touch_line)
Definition: pios_serial.c:151
static void rx_do_cb(pios_ser_dev *ser_dev, uint8_t *incoming_buffer, int len)
Definition: pios_serial.c:95
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
int printf(const char *format,...)
pios_com_callback tx_out_cb
Definition: pios_serial.c:68
uint8_t tx_buffer[PIOS_SERIAL_RX_BUFFER_SIZE]
Definition: pios_serial.c:74
#define PIOS_Assert(test)
Definition: pios_debug.h:52
int32_t PIOS_SERIAL_Init(uintptr_t *serial_id, const char *path)
Definition: pios_serial.c:182
uint16_t(* pios_com_callback)(uintptr_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *task_woken)
Definition: pios_com.h:41
void(* set_baud)(uintptr_t id, uint32_t baud)
Definition: pios_com.h:44