~siretart/lcd4linux/debian

« back to all changes in this revision

Viewing changes to drv_generic_serial.c

  • Committer: Reinhard Tartler
  • Date: 2011-04-27 17:24:15 UTC
  • mto: This revision was merged to the branch mainline in revision 750.
  • Revision ID: siretart@tauware.de-20110427172415-6n4aptmvmz0eztvm
Tags: upstream-0.11.0~svn1143
ImportĀ upstreamĀ versionĀ 0.11.0~svn1143

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: drv_generic_serial.c 890 2008-08-31 18:27:53Z sonic74 $
 
2
 * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_generic_serial.c $
 
3
 *
 
4
 * generic driver helper for serial and usbserial displays
 
5
 *
 
6
 * Copyright (C) 1999, 2000 Michael Reinelt <michael@reinelt.co.at>
 
7
 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 
8
 *
 
9
 * This file is part of LCD4Linux.
 
10
 *
 
11
 * LCD4Linux is free software; you can redistribute it and/or modify
 
12
 * it under the terms of the GNU General Public License as published by
 
13
 * the Free Software Foundation; either version 2, or (at your option)
 
14
 * any later version.
 
15
 *
 
16
 * LCD4Linux is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 * GNU General Public License for more details.
 
20
 *
 
21
 * You should have received a copy of the GNU General Public License
 
22
 * along with this program; if not, write to the Free Software
 
23
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
24
 *
 
25
 */
 
26
 
 
27
/*
 
28
 *
 
29
 * exported fuctions:
 
30
 *
 
31
 * int drv_generic_serial_open (char *section, char *driver, unsigned int flags)
 
32
 *   opens the serial port
 
33
 *
 
34
 * int drv_generic_serial_poll (char *string, int len)
 
35
 *   reads from the serial or USB port
 
36
 *   without retry
 
37
 *
 
38
 * int drv_generic_serial_read (char *string, int len);
 
39
 *   reads from the serial or USB port
 
40
 *   with retry
 
41
 *
 
42
 * void drv_generic_serial_write (char *string, int len);
 
43
 *   writes to the serial or USB port
 
44
 *
 
45
 * int drv_generic_serial_close (void);
 
46
 *   closes the serial port
 
47
 *
 
48
 */
 
49
 
 
50
#include "config.h"
 
51
 
 
52
#include <stdlib.h>
 
53
#include <stdio.h>
 
54
#include <string.h>
 
55
#include <errno.h>
 
56
#include <unistd.h>
 
57
#include <termios.h>
 
58
#include <fcntl.h>
 
59
#include <time.h>
 
60
#include <signal.h>
 
61
#include <sys/types.h>
 
62
#include <sys/stat.h>
 
63
 
 
64
#include "debug.h"
 
65
#include "qprintf.h"
 
66
#include "cfg.h"
 
67
#include "drv_generic_serial.h"
 
68
 
 
69
 
 
70
extern int got_signal;
 
71
 
 
72
static char *Section;
 
73
static char *Driver;
 
74
static char *Port;
 
75
static speed_t Speed;
 
76
static int Device = -1;
 
77
 
 
78
 
 
79
#define LOCK "/var/lock/LCK..%s"
 
80
 
 
81
 
 
82
/****************************************/
 
83
/*** generic serial/USB communication ***/
 
84
/****************************************/
 
85
 
 
86
static pid_t drv_generic_serial_lock_port(const char *Port)
 
87
{
 
88
    char lockfile[256];
 
89
    char tempfile[256];
 
90
    char buffer[16];
 
91
    char *port, *p;
 
92
    int fd, len, pid;
 
93
 
 
94
    if (strncmp(Port, "/dev/", 5) == 0) {
 
95
        port = strdup(Port + 5);
 
96
    } else {
 
97
        port = strdup(Port);
 
98
    }
 
99
 
 
100
    while ((p = strchr(port, '/')) != NULL) {
 
101
        *p = '_';
 
102
    }
 
103
 
 
104
    qprintf(lockfile, sizeof(lockfile), LOCK, port);
 
105
    qprintf(tempfile, sizeof(tempfile), LOCK, "TMP.XXXXXX");
 
106
 
 
107
    free(port);
 
108
 
 
109
    if ((fd = mkstemp(tempfile)) == -1) {
 
110
        error("mkstemp(%s) failed: %s", tempfile, strerror(errno));
 
111
        return -1;
 
112
    }
 
113
 
 
114
    if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
 
115
        error("fchmod(%s) failed: %s", tempfile, strerror(errno));
 
116
        close(fd);
 
117
        unlink(tempfile);
 
118
        return -1;
 
119
    }
 
120
 
 
121
    snprintf(buffer, sizeof(buffer), "%10d\n", (int) getpid());
 
122
    len = strlen(buffer);
 
123
    if (write(fd, buffer, len) != len) {
 
124
        error("write(%s) failed: %s", tempfile, strerror(errno));
 
125
        close(fd);
 
126
        unlink(tempfile);
 
127
        return -1;
 
128
    }
 
129
    close(fd);
 
130
 
 
131
 
 
132
    while (link(tempfile, lockfile) == -1) {
 
133
 
 
134
        if (errno != EEXIST) {
 
135
            error("link(%s, %s) failed: %s", tempfile, lockfile, strerror(errno));
 
136
            unlink(tempfile);
 
137
            return -1;
 
138
        }
 
139
 
 
140
        if ((fd = open(lockfile, O_RDONLY)) == -1) {
 
141
            if (errno == ENOENT)
 
142
                continue;       /* lockfile disappared */
 
143
            error("open(%s) failed: %s", lockfile, strerror(errno));
 
144
            unlink(tempfile);
 
145
            return -1;
 
146
        }
 
147
 
 
148
        len = read(fd, buffer, sizeof(buffer) - 1);
 
149
        if (len < 0) {
 
150
            error("read(%s) failed: %s", lockfile, strerror(errno));
 
151
            unlink(tempfile);
 
152
            return -1;
 
153
        }
 
154
 
 
155
        buffer[len] = '\0';
 
156
        if (sscanf(buffer, "%d", &pid) != 1 || pid == 0) {
 
157
            error("scan(%s) failed.", lockfile);
 
158
            unlink(tempfile);
 
159
            return -1;
 
160
        }
 
161
 
 
162
        if (pid == getpid()) {
 
163
            error("%s already locked by us. uh-oh...", lockfile);
 
164
            unlink(tempfile);
 
165
            return 0;
 
166
        }
 
167
 
 
168
        if ((kill(pid, 0) == -1) && errno == ESRCH) {
 
169
            error("removing stale lockfile %s", lockfile);
 
170
            if (unlink(lockfile) == -1 && errno != ENOENT) {
 
171
                error("unlink(%s) failed: %s", lockfile, strerror(errno));
 
172
                unlink(tempfile);
 
173
                return pid;
 
174
            }
 
175
            continue;
 
176
        }
 
177
        unlink(tempfile);
 
178
        return pid;
 
179
    }
 
180
 
 
181
    unlink(tempfile);
 
182
    return 0;
 
183
}
 
184
 
 
185
 
 
186
static pid_t drv_generic_serial_unlock_port(const char *Port)
 
187
{
 
188
    char lockfile[256];
 
189
    char *port, *p;
 
190
 
 
191
    if (strncmp(Port, "/dev/", 5) == 0) {
 
192
        port = strdup(Port + 5);
 
193
    } else {
 
194
        port = strdup(Port);
 
195
    }
 
196
 
 
197
    while ((p = strchr(port, '/')) != NULL) {
 
198
        *p = '_';
 
199
    }
 
200
 
 
201
    qprintf(lockfile, sizeof(lockfile), LOCK, port);
 
202
    unlink(lockfile);
 
203
    free(port);
 
204
 
 
205
    return 0;
 
206
}
 
207
 
 
208
 
 
209
int drv_generic_serial_open(const char *section, const char *driver, const unsigned int flags)
 
210
{
 
211
    int i, fd;
 
212
    pid_t pid;
 
213
    struct termios portset;
 
214
 
 
215
    Section = (char *) section;
 
216
    Driver = (char *) driver;
 
217
 
 
218
    Port = cfg_get(section, "Port", NULL);
 
219
    if (Port == NULL || *Port == '\0') {
 
220
        error("%s: no '%s.Port' entry from %s", Driver, section, cfg_source());
 
221
        return -1;
 
222
    }
 
223
 
 
224
    if (cfg_number(section, "Speed", 19200, 1200, 230400, &i) < 0)
 
225
        return -1;
 
226
    switch (i) {
 
227
    case 1200:
 
228
        Speed = B1200;
 
229
        break;
 
230
    case 2400:
 
231
        Speed = B2400;
 
232
        break;
 
233
    case 4800:
 
234
        Speed = B4800;
 
235
        break;
 
236
    case 9600:
 
237
        Speed = B9600;
 
238
        break;
 
239
    case 19200:
 
240
        Speed = B19200;
 
241
        break;
 
242
    case 38400:
 
243
        Speed = B38400;
 
244
        break;
 
245
    case 57600:
 
246
        Speed = B57600;
 
247
        break;
 
248
    case 115200:
 
249
        Speed = B115200;
 
250
        break;
 
251
#ifdef B230400
 
252
    case 230400:
 
253
        Speed = B230400;
 
254
        break;
 
255
#endif
 
256
    default:
 
257
        error("%s: unsupported speed '%d' from %s", Driver, i, cfg_source());
 
258
        return -1;
 
259
    }
 
260
 
 
261
    info("%s: using port '%s' at %d baud", Driver, Port, i);
 
262
 
 
263
    if ((pid = drv_generic_serial_lock_port(Port)) != 0) {
 
264
        if (pid == -1)
 
265
            error("%s: port %s could not be locked", Driver, Port);
 
266
        else
 
267
            error("%s: port %s is locked by process %d", Driver, Port, pid);
 
268
        return -1;
 
269
    }
 
270
 
 
271
    fd = open(Port, O_RDWR | O_NOCTTY | O_NDELAY);
 
272
    if (fd == -1) {
 
273
        error("%s: open(%s) failed: %s", Driver, Port, strerror(errno));
 
274
        drv_generic_serial_unlock_port(Port);
 
275
        return -1;
 
276
    }
 
277
 
 
278
    if (tcgetattr(fd, &portset) == -1) {
 
279
        error("%s: tcgetattr(%s) failed: %s", Driver, Port, strerror(errno));
 
280
        drv_generic_serial_unlock_port(Port);
 
281
        return -1;
 
282
    }
 
283
 
 
284
    cfmakeraw(&portset);
 
285
    portset.c_cflag |= flags;
 
286
    cfsetispeed(&portset, Speed);
 
287
    cfsetospeed(&portset, Speed);
 
288
    if (tcsetattr(fd, TCSANOW, &portset) == -1) {
 
289
        error("%s: tcsetattr(%s) failed: %s", Driver, Port, strerror(errno));
 
290
        drv_generic_serial_unlock_port(Port);
 
291
        return -1;
 
292
    }
 
293
 
 
294
    Device = fd;
 
295
    return Device;
 
296
}
 
297
 
 
298
 
 
299
int drv_generic_serial_poll(char *string, const int len)
 
300
{
 
301
    int ret;
 
302
    if (Device == -1)
 
303
        return -1;
 
304
    ret = read(Device, string, len);
 
305
    if (ret < 0 && errno != EAGAIN) {
 
306
        error("%s: read(%s) failed: %s", Driver, Port, strerror(errno));
 
307
    }
 
308
    return ret;
 
309
}
 
310
 
 
311
 
 
312
int drv_generic_serial_read(char *string, const int len)
 
313
{
 
314
    int count, run, ret;
 
315
 
 
316
    count = len < 0 ? -len : len;
 
317
 
 
318
    for (run = 0; run < 10; run++) {
 
319
        ret = drv_generic_serial_poll(string, count);
 
320
        if (ret >= 0 || errno != EAGAIN)
 
321
            break;
 
322
        info("%s: read(%s): EAGAIN", Driver, Port);
 
323
        usleep(1000);
 
324
    }
 
325
 
 
326
    if (ret > 0 && ret != count && len > 0) {
 
327
        error("%s: partial read(%s): len=%d ret=%d", Driver, Port, len, ret);
 
328
    }
 
329
 
 
330
    return ret;
 
331
}
 
332
 
 
333
 
 
334
void drv_generic_serial_write(const char *string, const int len)
 
335
{
 
336
    static int error = 0;
 
337
    int run, ret;
 
338
 
 
339
    if (Device == -1) {
 
340
        error("%s: write to closed port %s failed!", Driver, Port);
 
341
        return;
 
342
    }
 
343
 
 
344
    for (run = 0; run < 10; run++) {
 
345
        ret = write(Device, string, len);
 
346
        if (ret >= 0 || errno != EAGAIN) {
 
347
            break;
 
348
        }
 
349
        /* EAGAIN: retry 10 times, emit message after 2nd retry */
 
350
        if (run > 0) {
 
351
            info("%s: write(%s): EAGAIN #%d", Driver, Port, run);
 
352
        }
 
353
        usleep(1000);
 
354
    }
 
355
 
 
356
    if (ret < 0) {
 
357
        error("%s: write(%s) failed: %s", Driver, Port, strerror(errno));
 
358
        if (++error > 10) {
 
359
            error("%s: too much errors, giving up", Driver);
 
360
            got_signal = -1;
 
361
        }
 
362
    } else if (ret != len) {
 
363
        error("%s: partial write(%s): len=%d ret=%d", Driver, Port, len, ret);
 
364
    } else {
 
365
        error = 0;
 
366
    }
 
367
    return;
 
368
}
 
369
 
 
370
 
 
371
int drv_generic_serial_close(void)
 
372
{
 
373
    info("%s: closing port %s", Driver, Port);
 
374
    close(Device);
 
375
    drv_generic_serial_unlock_port(Port);
 
376
    free(Port);
 
377
    return 0;
 
378
}