source: trunk/src/ifd/serial.c @ 950

Revision 950, 9.6 KB checked in by aj, 5 years ago (diff)

work on lots of warnings, mostly things once declared with static and later without.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * I/O routines for serial devices
3 *
4 * Copyright (C) 2003 Olaf Kirch <okir@lst.de>
5 */
6
7#include "internal.h"
8#include <sys/types.h>
9#include <sys/select.h>
10#include <sys/poll.h>
11#include <sys/ioctl.h>
12#include <unistd.h>
13#include <fcntl.h>
14#include <termios.h>
15#include <errno.h>
16#include <string.h>
17
18static unsigned int termios_to_speed(unsigned int bits);
19static unsigned int speed_to_termios(unsigned int speed);
20
21/*
22 * Reset the device
23 */
24static int ifd_serial_reset(ifd_device_t * dev)
25{
26        ifd_device_params_t params, orig_params;
27        int rc;
28
29        if ((rc = ifd_device_get_parameters(dev, &orig_params)) < 0)
30                return rc;
31
32        /* Drop DTR */
33        params = orig_params;
34        params.serial.speed = 0;
35        params.serial.dtr = 0;
36        if ((rc = ifd_device_set_parameters(dev, &params)) < 0)
37                return rc;
38        usleep(500000);
39
40        /* Change back to original config */
41        if ((rc = ifd_device_set_parameters(dev, &orig_params)) < 0)
42                return rc;
43        return 0;
44}
45
46/*
47 * Get the current configuration
48 */
49static int ifd_serial_get_params(ifd_device_t * dev,
50                                 ifd_device_params_t * params)
51{
52        int control;
53        unsigned int bits;
54        struct termios t;
55
56        memset(params, 0, sizeof(*params));
57
58        if (tcgetattr(dev->fd, &t) < 0) {
59                ct_error("%s: tcgetattr: %m", dev->name);
60                return -1;
61        }
62
63        switch (t.c_cflag & CSIZE) {
64        case CS5:
65                bits = 5;
66                break;
67        case CS6:
68                bits = 6;
69                break;
70        case CS7:
71                bits = 7;
72                break;
73        case CS8:
74                bits = 8;
75                break;
76        default:
77                bits = 8;       /* hmmm */
78        }
79
80        params->serial.speed = termios_to_speed(cfgetospeed(&t));
81        params->serial.bits = bits;
82        params->serial.stopbits = (t.c_cflag & CSTOPB) ? 2 : 1;
83        if (!(t.c_cflag & PARENB))
84                params->serial.parity = IFD_SERIAL_PARITY_NONE;
85        else if (t.c_cflag & PARODD)
86                params->serial.parity = IFD_SERIAL_PARITY_ODD;
87        else
88                params->serial.parity = IFD_SERIAL_PARITY_EVEN;
89
90        if ((t.c_iflag & (INPCK | PARMRK)) == (INPCK | PARMRK))
91                params->serial.check_parity = 1;
92
93        if (ioctl(dev->fd, TIOCMGET, &control) < 0) {
94                ct_error("%s: TIOCMGET: %m", dev->name);
95                return -1;
96        }
97        if (control & TIOCM_RTS)
98                params->serial.rts = 1;
99        if (control & TIOCM_DTR)
100                params->serial.dtr = 1;
101
102        dev->settings = *params;
103        return 0;
104}
105
106/*
107 * Set serial line params
108 */
109static int ifd_serial_set_params(ifd_device_t * dev,
110                                 const ifd_device_params_t * params)
111{
112        unsigned int speed;
113        int control, ocontrol;
114        struct termios t;
115
116        if (tcgetattr(dev->fd, &t) < 0) {
117                ct_error("%s: tcgetattr: %m", dev->name);
118                return -1;
119        }
120
121        if (ct_config.debug) {
122                char parity = 'N';
123
124                if (params->serial.parity == IFD_SERIAL_PARITY_EVEN)
125                        parity = 'E';
126                else if (params->serial.parity == IFD_SERIAL_PARITY_ODD)
127                        parity = 'O';
128                ifd_debug(1, "setting serial line to %u, %u%c%u, "
129                          "dtr=%d, rts=%d",
130                          params->serial.speed,
131                          params->serial.bits,
132                          parity,
133                          params->serial.stopbits,
134                          params->serial.dtr, params->serial.rts);
135        }
136
137        cfsetospeed(&t, speed_to_termios(params->serial.speed));
138        cfsetispeed(&t, speed_to_termios(params->serial.speed));
139
140        t.c_cflag &= ~CSIZE;
141        switch (params->serial.bits) {
142        case 5:
143                t.c_cflag |= CS5;
144                break;
145        case 6:
146                t.c_cflag |= CS6;
147                break;
148        case 7:
149                t.c_cflag |= CS7;
150                break;
151        default:
152                t.c_cflag |= CS8;
153                break;
154        }
155
156        t.c_cflag &= ~(PARENB | PARODD);
157        switch (params->serial.parity) {
158        case IFD_SERIAL_PARITY_EVEN:
159                t.c_cflag |= PARENB;
160                break;
161        case IFD_SERIAL_PARITY_ODD:
162                t.c_cflag |= PARENB | PARODD;
163                break;
164        }
165
166        t.c_cflag &= ~CSTOPB;
167        if (params->serial.stopbits > 1)
168                t.c_cflag |= CSTOPB;
169
170        t.c_iflag = IGNBRK;
171        if (params->serial.check_parity)
172                t.c_iflag = INPCK | PARMRK;
173        else
174                t.c_iflag |= IGNPAR;
175
176#ifdef CRTSCTS
177        t.c_cflag &= ~CRTSCTS;
178#endif
179        t.c_cflag |= HUPCL | CREAD | CLOCAL;
180        t.c_oflag = 0;
181        t.c_lflag = 0;
182
183        if (tcsetattr(dev->fd, TCSANOW, &t) < 0) {
184                ct_error("%s: tcsetattr: %m", dev->name);
185                return -1;
186        }
187
188        if ((speed = termios_to_speed(cfgetospeed(&t))) != 0)
189                dev->etu = 1000000 / speed;
190
191        if (ioctl(dev->fd, TIOCMGET, &ocontrol) < 0) {
192                ct_error("%s: TIOCMGET: %m", dev->name);
193                return -1;
194        }
195        control = ocontrol & ~(TIOCM_DTR | TIOCM_RTS);
196        if (params->serial.rts)
197                control |= TIOCM_RTS;
198        if (params->serial.dtr)
199                control |= TIOCM_DTR;
200        if (((control ^ ocontrol) & (TIOCM_DTR | TIOCM_RTS))
201            && ioctl(dev->fd, TIOCMSET, &control) < 0) {
202                ct_error("%s: TIOCMGET: %m", dev->name);
203                return -1;
204        }
205
206        dev->settings = *params;
207        return 0;
208}
209
210/*
211 * Flush pending input
212 */
213static void ifd_serial_flush(ifd_device_t * dev)
214{
215        tcflush(dev->fd, TCIFLUSH);
216}
217
218/*
219 * Send a BREAK command
220 */
221void ifd_serial_send_break(ifd_device_t * dev, unsigned int usec)
222{
223        ioctl(dev->fd, TIOCSBRK);
224        usleep(usec);
225        ioctl(dev->fd, TIOCCBRK);
226}
227
228/*
229 * Input/output routines
230 */
231static int ifd_serial_send(ifd_device_t * dev, const unsigned char *buffer,
232                           size_t len)
233{
234        size_t total = len;
235        int n;
236
237        while (len) {
238                n = write(dev->fd, buffer, len);
239                if (n < 0) {
240                        ct_error("Error writing to %s: %m", dev->name);
241                        return -1;
242                }
243                buffer += n;
244                len -= n;
245        }
246
247        return total;
248}
249
250static int ifd_serial_recv(ifd_device_t * dev, unsigned char *buffer,
251                           size_t len, long timeout)
252{
253        size_t total = len, to_read;
254        struct timeval begin;
255        int n, last_ff = 0;
256
257        gettimeofday(&begin, NULL);
258
259        while (len) {
260                struct pollfd pfd;
261                long wait;
262
263                if ((wait = timeout - ifd_time_elapsed(&begin)) < 0)
264                        goto timeout;
265
266                pfd.fd = dev->fd;
267                pfd.events = POLLIN;
268                n = poll(&pfd, 1, wait);
269                if (n < 0) {
270                        ct_error("%s: error while waiting for input: %m",
271                                 dev->name);
272                        return -1;
273                }
274                if (n == 0)
275                        continue;
276
277                to_read = len;
278                if (dev->settings.serial.check_parity)
279                        to_read = 1;
280
281                n = read(dev->fd, buffer, to_read);
282                if (n < 0) {
283                        ct_error("%s: failed to read from device: %m",
284                                 dev->name);
285                        return -1;
286                }
287                if (ct_config.debug >= 9)
288                        ifd_debug(9, "serial recv:%s", ct_hexdump(buffer, n));
289                /* Check for parity errors and 0xFF */
290                if (dev->settings.serial.check_parity) {
291                        if (last_ff) {
292                                if (buffer[0] == 0x00) {
293                                        ct_error("%s: parity error on input",
294                                                 dev->name);
295                                        return -1;
296                                }
297                                if (buffer[0] != 0xFF) {
298                                        ifd_debug(1,
299                                                  "%s: unexpected character pair FF %02x",
300                                                  dev->name, buffer[0]);
301                                }
302                                last_ff = 0;
303                        } else if (buffer[0] == 0xFF) {
304                                last_ff = 1;
305                                continue;
306                        }
307                }
308                buffer += n;
309                len -= n;
310        }
311
312        return total;
313
314      timeout:                  /* Timeouts are a little special; they may happen e.g.
315                                 * when trying to obtain the ATR */
316        if (!ct_config.suppress_errors)
317                ct_error("%s: timed out while waiting for input", dev->name);
318        ifd_debug(9, "(%u bytes received so far)", total - len);
319        return IFD_ERROR_TIMEOUT;
320}
321
322/*
323 * Get status of modem lines
324 */
325int ifd_serial_get_dtr(ifd_device_t * dev)
326{
327        int status;
328
329        if (ioctl(dev->fd, TIOCMGET, &status) < 0) {
330                ct_error("%s: ioctl(TIOCMGET) failed: %m", dev->name);
331                return -1;
332        }
333        return (status & TIOCM_DTR) ? 1 : 0;
334}
335
336int ifd_serial_get_dsr(ifd_device_t * dev)
337{
338        int status;
339
340        if (ioctl(dev->fd, TIOCMGET, &status) < 0) {
341                ct_error("%s: ioctl(TIOCMGET) failed: %m", dev->name);
342                return -1;
343        }
344        return (status & TIOCM_DSR) ? 1 : 0;
345}
346
347int ifd_serial_get_cts(ifd_device_t * dev)
348{
349        int status;
350
351        if (ioctl(dev->fd, TIOCMGET, &status) < 0) {
352                ct_error("%s: ioctl(TIOCMGET) failed: %m", dev->name);
353                return -1;
354        }
355        return (status & TIOCM_CTS) ? 1 : 0;
356}
357
358/*
359 * Close the device
360 */
361static void ifd_serial_close(ifd_device_t * dev)
362{
363        if (dev->fd >= 0)
364                close(dev->fd);
365        dev->fd = -1;
366}
367
368static struct ifd_device_ops ifd_serial_ops;
369
370/*
371 * Open serial device
372 */
373ifd_device_t *ifd_open_serial(const char *name)
374{
375        ifd_device_params_t params;
376        ifd_device_t *dev;
377        int fd;
378
379        if ((fd = open(name, O_RDWR | O_NDELAY)) < 0) {
380                ct_error("Unable to open %s: %m", name);
381                return NULL;
382        }
383
384        /* Clear the NDELAY flag */
385        fcntl(fd, F_SETFL, 0);
386
387        ifd_serial_ops.reset = ifd_serial_reset;
388        ifd_serial_ops.set_params = ifd_serial_set_params;
389        ifd_serial_ops.get_params = ifd_serial_get_params;
390        ifd_serial_ops.flush = ifd_serial_flush;
391        ifd_serial_ops.send_break = ifd_serial_send_break;
392        ifd_serial_ops.send = ifd_serial_send;
393        ifd_serial_ops.recv = ifd_serial_recv;
394        ifd_serial_ops.close = ifd_serial_close;
395
396        dev = ifd_device_new(name, &ifd_serial_ops, sizeof(*dev));
397        dev->timeout = 1000;    /* acceptable? */
398        dev->type = IFD_DEVICE_TYPE_SERIAL;
399        dev->fd = fd;
400
401        memset(&params, 0, sizeof(params));
402        params.serial.speed = 9600;
403        params.serial.bits = 8;
404        params.serial.parity = IFD_SERIAL_PARITY_NONE;
405        params.serial.stopbits = 1;
406        params.serial.rts = 1;
407        params.serial.dtr = 1;
408
409        ifd_serial_set_params(dev, &params);
410
411        return dev;
412}
413
414/*
415 * Map termios speed flags to speed
416 */
417static struct {
418        unsigned int bits, speed;
419} termios_speed[] = {
420#ifdef B0
421        { B0, 0 },
422#endif
423#ifdef B50
424        { B50, 50 },
425#endif
426#ifdef B75
427        { B75, 75 },
428#endif
429#ifdef B110
430        { B110, 110 },
431#endif
432#ifdef B134
433        { B134, 134 },
434#endif
435#ifdef B150
436        { B150, 150 },
437#endif
438#ifdef B200
439        { B200, 200 },
440#endif
441#ifdef B300
442        { B300, 300 },
443#endif
444#ifdef B600
445        { B600, 600 },
446#endif
447#ifdef B1200
448        { B1200, 1200 },
449#endif
450#ifdef B1800
451        { B1800, 1800 },
452#endif
453#ifdef B2400
454        { B2400, 2400 },
455#endif
456#ifdef B4800
457        { B4800, 4800 },
458#endif
459#ifdef B9600
460        { B9600, 9600 },
461#endif
462#ifdef B19200
463        {
464        B19200, 19200 },
465#endif
466#ifdef B38400
467        {
468        B38400, 38400 },
469#endif
470#ifdef B57600
471        { B57600, 57600 },
472#endif
473#ifdef B115200
474        { B115200, 115200 },
475#endif
476#ifdef B230400
477        { B230400, 230400 },
478#endif
479        { -1, -1 }
480};
481
482static unsigned int speed_to_termios(unsigned int speed)
483{
484        unsigned int n;
485
486        for (n = 0; termios_speed[n].bits >= 0; n++) {
487                if (termios_speed[n].speed >= speed) {
488                        return termios_speed[n].bits;
489                }
490        }
491        return B9600;
492}
493
494static unsigned int termios_to_speed(unsigned int bits)
495{
496        unsigned int n;
497
498        for (n = 0; termios_speed[n].bits >= 0; n++) {
499                if (termios_speed[n].bits == bits) {
500                        return termios_speed[n].speed;
501                }
502        }
503
504        return 0;
505}
Note: See TracBrowser for help on using the repository browser.