~siretart/lcd4linux/debian

« back to all changes in this revision

Viewing changes to plugin_huawei.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: plugin_huawei.c 870 2008-04-10 14:55:23Z michux $
 
2
 * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/plugin_huawei.c $
 
3
 *
 
4
 * plugin plugin_huawei. Copyright (C) 2010 Jar <jar@pcuf.fi>
 
5
 *
 
6
 * Huawei E220 3G modem statistics via /dev/ttyUSB* user interface. It may (or may not) work well
 
7
 * with other Huawei 3G USB modems too (which uses PPP interface between modem and computer), like
 
8
 * E160, E169, E226, E272, E230 etc. Since they all seem to use same kind of set AT commands and
 
9
 * responses. Tested with Huawei E220, E160E  and Vodafone Huawei K3565.
 
10
 *
 
11
 * Thanks to Mikko <vmj@linuxbox.fi> for contributing the scan_uint() function and some other code.
 
12
 *
 
13
 * Based on sample plugin which is
 
14
 * Copyright (C) 2003 Michael Reinelt <michael@reinelt.co.at>
 
15
 * Copyright (C) 2004, 2005, 2006, 2007, 2008 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 
16
 *
 
17
 *
 
18
 * This file is part of LCD4Linux.
 
19
 *
 
20
 * LCD4Linux is free software; you can redistribute it and/or modify
 
21
 * it under the terms of the GNU General Public License as published by
 
22
 * the Free Software Foundation; either version 2, or (at your option)
 
23
 * any later version.
 
24
 *
 
25
 * LCD4Linux is distributed in the hope that it will be useful,
 
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
28
 * GNU General Public License for more details.
 
29
 *
 
30
 * You should have received a copy of the GNU General Public License
 
31
 * along with this program; if not, write to the Free Software
 
32
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
33
 *
 
34
 */
 
35
 
 
36
 
 
37
/* define the include files you need */
 
38
#include "config.h"
 
39
 
 
40
#include <stdio.h>              /* standard buffered input/output */
 
41
#include <stdlib.h>             /* standard library definitions */
 
42
#include <stdarg.h>             /* for va_start et al */
 
43
#include <string.h>             /* string operations */
 
44
#include <unistd.h>             /* read(), close() */
 
45
#include <fcntl.h>              /* open() */
 
46
#include <termios.h>            /* tcflush() */
 
47
#include <sys/types.h>          /* data types */
 
48
#include <sys/stat.h>           /* stat structure */
 
49
#include <sys/time.h>           /* timeval structure */
 
50
#include <errno.h>              /* system error numbers */
 
51
 
 
52
 
 
53
/* these should always be included */
 
54
#include "debug.h"
 
55
#include "plugin.h"
 
56
 
 
57
 
 
58
#ifdef WITH_DMALLOC
 
59
#include <dmalloc.h>
 
60
#endif
 
61
 
 
62
 
 
63
#define PORT "/dev/ttyUSB1"     /* default port */
 
64
#define BAUDRATE B38400         /* default baudrate */
 
65
#define SEND_BUFFER_SIZE 128    /* send buffer size */
 
66
#define RECV_BUFFER_SIZE 256    /* receive buffer size */
 
67
#define MIN_INTERVAL 100        /* minimum query interval 100 ms */
 
68
 
 
69
 
 
70
#define INIT_STRING     "ATE1 ^CURC=0;^DSFLOWCLR"       /* disable unsolicited report codes and reset DS traffic
 
71
                                                         * with local echo enabled 
 
72
                                                         */
 
73
#define QUALITY         "ATE1 +CSQ"     /* signal quality query with local echo enabled */
 
74
#define SYSINFO         "ATE1 ^SYSINFO" /* network access query with local echo enabled */
 
75
#define MANUF           "ATE1 +GMI"     /* manufacturer query with local echo enabled */
 
76
#define MODEL           "ATE1 +GMM"     /* model query with local echo enabled */
 
77
#define FWVER           "ATE1 +CGMR"    /* firmware version query with local echo enabled */
 
78
#define FLOWREPORT      "ATE1 ^DSFLOWQRY"       /* DS traffic query with local echo enabled */
 
79
#define OPERATOR        "ATE1 +COPS=3,0;+COPS?" /* gsm/umts operator query (3=set format only, 0=long alphanum. string)
 
80
                                                 * with local echo enabled
 
81
                                                 */
 
82
 
 
83
static char name[] = "plugin_huawei.c";
 
84
 
 
85
static char *sub_system_mode[] = {
 
86
    "NO CONN",                  /* no service */
 
87
    "GSM",                      /* 2G/GSM */
 
88
    "GPRS",                     /* 2.5G/GPRS */
 
89
    "EDGE",                     /* 2.75G/EDGE */
 
90
    "WCDMA",                    /* 3G/UMTS */
 
91
    "HSDPA",                    /* 3.5G/UMTS */
 
92
    "HSUPA",                    /* 3.5G/UMTS */
 
93
    "HSPA",                     /* HSDPA+HSUPA */
 
94
    "UNKNOWN"
 
95
};
 
96
 
 
97
static int fd = -2;             /* serial fd */
 
98
static char *port = NULL;       /* serial device */
 
99
 
 
100
/* signal strength query */
 
101
static unsigned int rssi = 0;   /* relative rssi 0...31 */
 
102
static unsigned int ber = 0;    /* ber (bit error rate, not supported) */
 
103
 
 
104
/* manufacturer query */
 
105
static char manuf[32] = "";     /* manufacturer */
 
106
 
 
107
/* model query */
 
108
static char model[32] = "";     /* model */
 
109
 
 
110
/* fw version query */
 
111
static char fwver[32] = "";     /* firmware version */
 
112
 
 
113
/* gsm/umts operator query */
 
114
static char operator[64] = "";  /* current gsm/umts network operator */
 
115
 
 
116
/* DS traffic report query */
 
117
static unsigned long int last_ds_time = 0;      /* last DS connection time [s] */
 
118
static unsigned long long int last_tx_flow = 0; /* last DS transmiting trafic [Bytes] */
 
119
static unsigned long long int last_rx_flow = 0; /* last DS receiving trafic [Bytes] */
 
120
static unsigned long int total_ds_time = 0;     /* total DS connection time [s] */
 
121
static unsigned long long int total_tx_flow = 0;        /* total transmitting DS traffic [Bytes] */
 
122
static unsigned long long int total_rx_flow = 0;        /* total receiving DS traffic [Bytes] */
 
123
 
 
124
static double calc_tx_rate = 0; /* calculated tx rate */
 
125
static double calc_rx_rate = 0; /* calculated rx rate */
 
126
 
 
127
/* system information query */
 
128
static unsigned int status = 0; /* system service state */
 
129
static unsigned int domain = 0; /* system service domain */
 
130
static unsigned int roaming_status = 0; /* roaming status */
 
131
static unsigned int mode = 0;   /* system mode */
 
132
static unsigned int sim_state = 0;      /* SIM card state */
 
133
static unsigned int reserved = 0;       /* reserved, E618 used it to indicate the simlock state */
 
134
static unsigned int sub_mode = 0;       /* system sub mode */
 
135
 
 
136
static int debug = 0;           /* enable debug messages */
 
137
 
 
138
 
 
139
static int age_diff(struct timeval prev_age)
 
140
{
 
141
    int diff;
 
142
    struct timeval now;
 
143
 
 
144
    gettimeofday(&now, NULL);
 
145
    diff = (now.tv_sec - prev_age.tv_sec) * 1000 + (now.tv_usec - prev_age.tv_usec) / 1000;
 
146
 
 
147
    return diff;
 
148
}
 
149
 
 
150
static int scan_uint(const char *str, unsigned int nargs, ...)
 
151
{
 
152
    unsigned int wall = 0;
 
153
    unsigned int i = 0;         /* number of parsed numbers */
 
154
    unsigned int *a = NULL;     /* pointer to currently parsed number (type
 
155
                                   must be that of the actual arguments) */
 
156
 
 
157
    /* initialize the argument list */
 
158
    va_list argv;
 
159
    va_start(argv, nargs);
 
160
 
 
161
    if (nargs > 0) {
 
162
        /* initialize to first argument */
 
163
        a = va_arg(argv, unsigned int *);
 
164
        /* initialize it to zero */
 
165
        *a = 0;
 
166
    }
 
167
 
 
168
    /* loop through the argument list and the string */
 
169
    while (str && *str && wall < 1024) {
 
170
        switch (*str) {
 
171
        case '1':
 
172
        case '2':
 
173
        case '3':
 
174
        case '4':
 
175
        case '5':
 
176
        case '6':
 
177
        case '7':
 
178
        case '8':
 
179
        case '9':
 
180
        case '0':
 
181
            /* intent: multiply current value of 'a' with 10 and add
 
182
               numeric value of character str) */
 
183
            if (i < nargs) {
 
184
                *a = ((*a) * 10) + ((*str) - '0');
 
185
            }
 
186
            break;
 
187
 
 
188
        case ',':
 
189
            if (++i < nargs) {
 
190
                /* proceed to next argument */
 
191
                a = va_arg(argv, unsigned int *);
 
192
                /* initialize it to zero */
 
193
                *a = 0;
 
194
            } else {
 
195
                /* this was the last number to parse */
 
196
                /* (comma indicates that there are more) */
 
197
            }
 
198
            break;
 
199
 
 
200
        default:
 
201
            /* ignore any unknown characters */
 
202
            break;
 
203
        }
 
204
 
 
205
        /* proceed to next character */
 
206
        str++;
 
207
        wall++;
 
208
    }
 
209
 
 
210
    /* fill any extraneous arguments with zero */
 
211
    if (str && !(*str) && i < nargs - 1) {
 
212
        unsigned int j = i;
 
213
        for (j++; j < nargs; j++) {
 
214
            a = va_arg(argv, unsigned int *);
 
215
            *a = 0;
 
216
        }
 
217
    }
 
218
 
 
219
    /* finalize the argument list */
 
220
    va_end(argv);
 
221
 
 
222
    /* return the number of parsed numbers */
 
223
    return (int) (i > 0) ? i + 1 : i;
 
224
}
 
225
 
 
226
static int huawei_port_exists(const char *device)
 
227
{
 
228
    int ret;
 
229
    struct stat stat_ptr;
 
230
 
 
231
    /* check the device is present */
 
232
    ret = stat(device, &stat_ptr);
 
233
    if (ret < 0)
 
234
        return 0;
 
235
 
 
236
    /* check the device is character device */
 
237
    return (S_ISCHR(stat_ptr.st_mode));
 
238
}
 
239
 
 
240
static void huawei_read_port(void)
 
241
{
 
242
    char *test;
 
243
 
 
244
    /* port can be defined via env variable */
 
245
    if ((test = getenv("HUAWEI_PORT"))) {
 
246
        if (port != test) {
 
247
            port = test;
 
248
            info("%s: Using HUAWEI_PORT=%s from env variable for user interface device", name, port);
 
249
        }
 
250
    } else
 
251
        port = PORT;
 
252
 
 
253
    return;
 
254
}
 
255
 
 
256
static int huawei_configure_port(void)
 
257
{
 
258
    int ret;
 
259
    struct termios options;
 
260
 
 
261
    /* open port */
 
262
    fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
 
263
    if (fd < 0) {
 
264
        error("%s: ERROR: Problem opening %s. Try using the HUAWEI_PORT env variable (export HUAWEI_PORT=/dev/ttyUSB*)",
 
265
              name, port);
 
266
        return -1;
 
267
    }
 
268
 
 
269
    /* enable blocking behavior with timeout */
 
270
    ret = fcntl(fd, F_SETFL, 0);
 
271
    if (ret < 0) {
 
272
        error("%s: ERROR: Problem setting file descriptor status flags %i", name, fd);
 
273
        close(fd);
 
274
        fd = -2;
 
275
        return -1;
 
276
    }
 
277
 
 
278
    /* get the current options */
 
279
    ret = tcgetattr(fd, &options);
 
280
    if (ret < 0) {
 
281
        error("%s: ERROR: Problem getting terminal attributes %s", name, port);
 
282
        close(fd);
 
283
        fd = -2;
 
284
        return -1;
 
285
    }
 
286
 
 
287
    /* set raw input */
 
288
    options.c_cflag |= (CS8 | CLOCAL | CREAD);
 
289
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
 
290
    options.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | IGNCR | INLCR | ICRNL |
 
291
                         IUCLC | IXANY | IXON | IXOFF | INPCK | ISTRIP);
 
292
    options.c_oflag = 0;        /* raw output */
 
293
    options.c_cc[VMIN] = 0;
 
294
    options.c_cc[VTIME] = 1;    /* 0.1 second timeout */
 
295
 
 
296
    /* set the input baud rate to BAUDRATE */
 
297
    ret = cfsetispeed(&options, BAUDRATE);
 
298
    if (ret < 0) {
 
299
        error("%s: ERROR: Problem setting input baud rate %s", name, port);
 
300
        close(fd);
 
301
        fd = -2;
 
302
        return -1;
 
303
    }
 
304
 
 
305
    /* set the output baud rate to BAUDRATE */
 
306
    ret = cfsetospeed(&options, BAUDRATE);
 
307
    if (ret < 0) {
 
308
        error("%s: ERROR: Problem setting output baud rate %s", name, port);
 
309
        close(fd);
 
310
        fd = -2;
 
311
        return -1;
 
312
    }
 
313
 
 
314
    /* set the options */
 
315
    ret = tcsetattr(fd, TCSANOW, &options);
 
316
    if (ret < 0) {
 
317
        error("%s: ERROR: Problem setting terminal attributes %s", name, port);
 
318
        close(fd);
 
319
        fd = -2;
 
320
        return -1;
 
321
    }
 
322
 
 
323
    /* flush input and output buffers */
 
324
    ret = tcflush(fd, TCIFLUSH);
 
325
    if (ret < 0) {
 
326
        error("%s: ERROR: Problem flushing input and output buffers %s", name, port);
 
327
        close(fd);
 
328
        fd = -2;
 
329
        return -1;
 
330
    }
 
331
 
 
332
    return fd;
 
333
}
 
334
 
 
335
static int huawei_send(const char *cmd)
 
336
{
 
337
    int len, bytes;
 
338
    char buf[SEND_BUFFER_SIZE];
 
339
 
 
340
    sprintf(buf, "%s\r", cmd);
 
341
    len = strlen(buf);
 
342
 
 
343
    /* write */
 
344
    bytes = write(fd, buf, len);
 
345
 
 
346
    /* force null termination */
 
347
    if (bytes > 0)
 
348
        buf[bytes] = '\0';
 
349
    else
 
350
        buf[0] = '\0';
 
351
 
 
352
    if (bytes < 0 && errno != EAGAIN) {
 
353
        error("%s: ERROR: Writing to device %s failed: %s", name, port, strerror(errno));
 
354
        return -1;
 
355
    } else if (bytes != len) {
 
356
        error("%s: ERROR: Partial write when writing to device %s", name, port);
 
357
        return -1;
 
358
    }
 
359
 
 
360
    /* remove trailing CR */
 
361
    while (bytes > 0 && buf[bytes - 1] == '\r') {
 
362
        buf[--bytes] = '\0';
 
363
    }
 
364
 
 
365
    if (debug)
 
366
        debug("DEBUG: <-Send bytes=%i, send buf=%s", bytes, buf);
 
367
 
 
368
    return bytes;
 
369
}
 
370
 
 
371
static char *huawei_receive(void)
 
372
{
 
373
    int run, count, bytes;
 
374
    char c[1], *p;
 
375
    static char buf[RECV_BUFFER_SIZE];
 
376
 
 
377
    /* skip the plain NL's and CR's (<CR><LF><CR><LF> makes max. 4 pcs. of them) and re-read */
 
378
    for (run = 0; run < 4; run++) {
 
379
 
 
380
        /* pointer to the current position in the buffer */
 
381
        p = buf;
 
382
 
 
383
        /* set count to buffer size */
 
384
        count = RECV_BUFFER_SIZE;
 
385
 
 
386
        /* Read until a NL or a CR is encountered. If 'buf' fills up before newline is seen,
 
387
         * exit the loop when there's still room for zero termination (count == 1).
 
388
         */
 
389
        while (count > 1) {
 
390
            bytes = read(fd, c, 1);
 
391
 
 
392
            if (*c == '\n' || *c == '\r' || (bytes < 0 && errno != EAGAIN))
 
393
                break;
 
394
 
 
395
            *p = *c;
 
396
            p++;
 
397
            count--;
 
398
        }
 
399
 
 
400
        /* force null termination */
 
401
        *p = '\0';
 
402
 
 
403
        /* now we have data (when buf len > 0), exit from for-loop and return the data to caller */
 
404
        if (strlen(buf) > 0 || (bytes < 0 && errno != EAGAIN))
 
405
            break;
 
406
    }
 
407
 
 
408
    if (bytes < 0 && errno != EAGAIN) {
 
409
        error("%s: ERROR: Reading from device %s failed: %s", name, port, strerror(errno));
 
410
        return ("");
 
411
    }
 
412
 
 
413
    /* print bytes read and buffer data when debuging enabled */
 
414
    if (debug)
 
415
        debug("DEBUG: ->Received bytes=%i, receive buf=%s", (int) strlen(buf), buf);
 
416
 
 
417
    return (buf);
 
418
}
 
419
 
 
420
static int huawei_recv_error(const char *msg)
 
421
{
 
422
    if (strstr(msg, "ERROR") != NULL || strncmp(msg, "NO CARRIER", 10) == 0 ||
 
423
        strncmp(msg, "COMMAND NOT SUPPORT", 19) == 0 || strncmp(msg, "TOO MANY PARAMETERS", 19) == 0)
 
424
        return 1;
 
425
 
 
426
    return 0;
 
427
}
 
428
 
 
429
static char *huawei_send_receive(const char *cmd)
 
430
{
 
431
    int run, bytes_send, recv_seq;
 
432
    char *tmp;
 
433
    static char reply[RECV_BUFFER_SIZE];
 
434
 
 
435
    /* send command to modem */
 
436
    bytes_send = huawei_send(cmd);
 
437
 
 
438
    /* start with an empty reply */
 
439
    strcpy(reply, "");
 
440
 
 
441
    if (bytes_send > 0) {
 
442
 
 
443
        recv_seq = 0;
 
444
 
 
445
        /* receive modem responses (currently handle only 3 lines of responses:
 
446
         * request->reply->ok|error)
 
447
         */
 
448
        for (run = 0; run < 3; run++) {
 
449
 
 
450
            tmp = huawei_receive();
 
451
 
 
452
            /* init cmd */
 
453
            if (strncmp(cmd, INIT_STRING, strlen(INIT_STRING)) == 0) {
 
454
 
 
455
                /* waiting local echo back after the init string has been sent */
 
456
                if (recv_seq == 0 && strncmp(tmp, INIT_STRING, strlen(INIT_STRING)) == 0) {
 
457
                    recv_seq = 1;
 
458
                }
 
459
 
 
460
                /* waiting "OK" after local echo has been received */
 
461
                else if (recv_seq == 1 && strncmp(tmp, "OK", 2) == 0) {
 
462
                    strncpy(reply, tmp, sizeof(reply) - 1);
 
463
                    break;
 
464
                }
 
465
 
 
466
                /* waiting possible "ERROR" after local echo has been received */
 
467
                else if (recv_seq == 1 && huawei_recv_error(tmp) == 1) {
 
468
                    strncpy(reply, tmp, sizeof(reply) - 1);
 
469
                    break;
 
470
                }
 
471
 
 
472
                /* data query cmd */
 
473
            } else {
 
474
 
 
475
                /* waiting local echo back after the cmd has been sent */
 
476
                if (recv_seq == 0 && strncmp(tmp, cmd, strlen(cmd)) == 0) {
 
477
                    recv_seq = 1;
 
478
                }
 
479
 
 
480
                /* waiting data (but not "OK" or "ERROR") after local echo has been received */
 
481
                else if (recv_seq == 1 && strlen(tmp) > 0 && huawei_recv_error(tmp) == 0) {
 
482
                    recv_seq = 2;
 
483
                    strncpy(reply, tmp, sizeof(reply) - 1);
 
484
                }
 
485
 
 
486
                /* waiting "OK" after data has been received */
 
487
                else if (recv_seq == 2 && strncmp(tmp, "OK", 2) == 0) {
 
488
                    break;
 
489
                }
 
490
 
 
491
                /* waiting possible "ERROR" after local echo has been received */
 
492
                else if (recv_seq == 1 && huawei_recv_error(tmp) == 1) {
 
493
                    strncpy(reply, tmp, sizeof(reply) - 1);
 
494
                    break;
 
495
                }
 
496
            }
 
497
        }
 
498
    }
 
499
 
 
500
    return (reply);
 
501
}
 
502
 
 
503
static int huawei_configured(void)
 
504
{
 
505
    int port_exists, ret, bytes;
 
506
    static int connected = -1, configured = 0;
 
507
    char *buf;
 
508
 
 
509
    /* re-read port because device name may change during plugin execution */
 
510
    huawei_read_port();
 
511
 
 
512
    /* check the device exists before reading on it */
 
513
    port_exists = huawei_port_exists(port);
 
514
 
 
515
    /* modem removed event */
 
516
    if (port_exists < 1 && (connected == -1 || connected == 1)) {
 
517
        info("%s: Modem doesn't exists or has been removed, device %s is to be closed", name, port);
 
518
        if (fd > 0) {
 
519
            ret = close(fd);
 
520
            if (ret < 0) {
 
521
                error("%s: ERROR: Error when closing device %s after modem removal, ret=%i", name, port, ret);
 
522
            }
 
523
            fd = -2;
 
524
        }
 
525
        connected = 0;
 
526
    }
 
527
 
 
528
    /* modem inserted event */
 
529
    if (port_exists > 0 && (connected == -1 || connected == 0)) {
 
530
        info("%s: Modem has been inserted, device %s will be opened", name, port);
 
531
        if (fd < 0) {
 
532
            ret = huawei_configure_port();
 
533
            if (ret > 0) {
 
534
                connected = 1;
 
535
                if (debug)
 
536
                    debug("DEBUG: Device %s configured successfully, fd=%i", port, ret);
 
537
            } else {
 
538
                error("%s: ERROR: Device %s configuring failure->retrying..., ret=%i", name, port, ret);
 
539
            }
 
540
        }
 
541
    }
 
542
 
 
543
    /* init variables to zero (except tx/rx total flows) when device disconnected */
 
544
    if (connected == 0) {
 
545
        rssi = 0;
 
546
        sub_mode = 0;
 
547
        last_ds_time = 0;
 
548
        total_ds_time = 0;
 
549
        last_tx_flow = 0;
 
550
        last_rx_flow = 0;
 
551
        calc_tx_rate = 0;
 
552
        calc_rx_rate = 0;
 
553
        strcpy(manuf, "");
 
554
        strcpy(model, "");
 
555
        strcpy(fwver, "");
 
556
        strcpy(operator, "");
 
557
        configured = 0;
 
558
    }
 
559
 
 
560
    /* modem initialization */
 
561
    if (connected == 1 && configured != 1) {
 
562
        buf = huawei_send_receive(INIT_STRING);
 
563
        bytes = strlen(buf);
 
564
 
 
565
        if (strncmp(buf, "OK", 2) == 0) {
 
566
            configured = 1;
 
567
            info("%s: Modem user inerface succesfully initialized to: \'%s\'", name, INIT_STRING);
 
568
        } else {
 
569
            configured = 0;
 
570
            error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, INIT_STRING);
 
571
        }
 
572
    }
 
573
 
 
574
    return configured;
 
575
}
 
576
 
 
577
static void huawei_read_quality(const char *cmd)
 
578
{
 
579
    int bytes;
 
580
    char *buf;
 
581
 
 
582
    buf = huawei_send_receive(cmd);
 
583
    bytes = strlen(buf);
 
584
 
 
585
    if (strncmp(buf, "+CSQ: ", 6) == 0) {
 
586
 
 
587
        /* Returns relative signal strength (RSSI) and ber (not supported by modems).
 
588
         *
 
589
         * +CSQ: 14,99
 
590
         *     rssi,ber
 
591
         *
 
592
         *  0 <= -113 dBm
 
593
         *  1 -111 dBm
 
594
         *  2 to 30 -109 dBm to -53 dBm
 
595
         *  31 >= -51 dBm
 
596
         *  99 unknown or unmeasurable
 
597
         */
 
598
 
 
599
        if (scan_uint(buf + 6, 2, &rssi, &ber) != 2)
 
600
            error("%s: ERROR: Cannot parse all +CSQ: data fields, some data may be wrong or missing", name);
 
601
 
 
602
        if (debug)
 
603
            debug("DEBUG: Relative rssi value: %u", rssi);
 
604
    } else
 
605
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
606
 
 
607
    return;
 
608
}
 
609
 
 
610
static void huawei_read_sysinfo(const char *cmd)
 
611
{
 
612
    int bytes;
 
613
    char *buf;
 
614
 
 
615
    buf = huawei_send_receive(cmd);
 
616
    bytes = strlen(buf);
 
617
 
 
618
    if (strncmp(buf, "^SYSINFO:", 9) == 0) {
 
619
 
 
620
        /* Returns system information.
 
621
         *
 
622
         * ^SYSINFO:2,3,0,5,1,,4
 
623
         *     status,domain,roaming_status,mode,SIM state,reserved,sub_mode
 
624
         */
 
625
 
 
626
        if (scan_uint(buf + 9, 7, &status, &domain, &roaming_status, &mode, &sim_state, &reserved, &sub_mode) != 7)
 
627
            error("%s: ERROR: Cannot parse all ^SYSINFO: data fields, some data may be wrong or missing", name);
 
628
 
 
629
        if (debug)
 
630
            debug("DEBUG: Sub mode value: %u", sub_mode);
 
631
    } else
 
632
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
633
 
 
634
    return;
 
635
}
 
636
 
 
637
static void huawei_read_manuf(const char *cmd)
 
638
{
 
639
    int bytes;
 
640
    char *buf;
 
641
 
 
642
    buf = huawei_send_receive(cmd);
 
643
    bytes = strlen(buf);
 
644
 
 
645
    /* accept all but "ERROR" */
 
646
    if (bytes > 0 && huawei_recv_error(buf) == 0) {
 
647
 
 
648
        /* Returns manufacturer string.
 
649
         * 
 
650
         * huawei
 
651
         */
 
652
        strncpy(manuf, buf, sizeof(manuf) - 1);
 
653
 
 
654
        if (debug)
 
655
            debug("DEBUG: Manufacturer string: %s", manuf);
 
656
    } else
 
657
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
658
 
 
659
    return;
 
660
}
 
661
 
 
662
static void huawei_read_model(const char *cmd)
 
663
{
 
664
    int bytes;
 
665
    char *buf;
 
666
 
 
667
    buf = huawei_send_receive(cmd);
 
668
    bytes = strlen(buf);
 
669
 
 
670
    /* accept all but "ERROR" */
 
671
    if (bytes > 0 && huawei_recv_error(buf) == 0) {
 
672
 
 
673
        /* Returns model string.
 
674
         * 
 
675
         * E220
 
676
         */
 
677
        strncpy(model, buf, sizeof(model) - 1);
 
678
 
 
679
        if (debug)
 
680
            debug("DEBUG: Model string: %s", model);
 
681
    } else
 
682
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
683
 
 
684
    return;
 
685
}
 
686
 
 
687
static void huawei_read_fwver(const char *cmd)
 
688
{
 
689
    int bytes;
 
690
    char *buf;
 
691
 
 
692
    buf = huawei_send_receive(cmd);
 
693
    bytes = strlen(buf);
 
694
 
 
695
    /* accept all but "ERROR" */
 
696
    if (bytes > 0 && huawei_recv_error(buf) == 0) {
 
697
 
 
698
        /* Returns firmware version string.
 
699
         * 
 
700
         * 11.110.03.00.00
 
701
         */
 
702
        strncpy(fwver, buf, sizeof(fwver) - 1);
 
703
 
 
704
        if (debug)
 
705
            debug("DEBUG: Firmware version string: %s", fwver);
 
706
    } else
 
707
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
708
 
 
709
    return;
 
710
}
 
711
 
 
712
static void huawei_read_operator(const char *cmd)
 
713
{
 
714
    int bytes, i, pos = 0, copy = 0;
 
715
    char *buf;
 
716
 
 
717
    buf = huawei_send_receive(cmd);
 
718
    bytes = strlen(buf);
 
719
 
 
720
    if (strncmp(buf, "+COPS:", 6) == 0) {
 
721
 
 
722
        /* Returns operator string.
 
723
         * 
 
724
         * +COPS: 0,0,"vodafone ES",2
 
725
         * reg_mode,format,operator string (based on format),network access type
 
726
         */
 
727
 
 
728
        /* parse "operator string" */
 
729
        for (i = 6; i <= bytes; i++) {
 
730
            if (buf[i] == '\"' && copy == 0) {
 
731
                i++;
 
732
                copy = 1;
 
733
            } else if ((buf[i] == '\"') && copy == 1)
 
734
                copy = 0;
 
735
 
 
736
            if (copy == 1 && strlen(operator) < sizeof(operator) - 1) {
 
737
                operator[pos] = buf[i];
 
738
                pos++;
 
739
            }
 
740
        }
 
741
 
 
742
        if (debug)
 
743
            debug("DEBUG: Operator version string: \'%s\'", operator);
 
744
    } else
 
745
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
746
 
 
747
    return;
 
748
}
 
749
 
 
750
static void huawei_read_flowreport(const char *cmd)
 
751
{
 
752
    char *buf;
 
753
    int bytes;
 
754
    static unsigned long long int prev_tx_flow = 0, prev_rx_flow = 0;
 
755
    static unsigned long int prev_ds_time = 0;
 
756
 
 
757
    buf = huawei_send_receive(cmd);
 
758
    bytes = strlen(buf);
 
759
 
 
760
    if (strncmp(buf, "^DSFLOWQRY:", 11) == 0) {
 
761
 
 
762
        /* Returns flow report.
 
763
         *
 
764
         * ^DSFLOWQRY:00000E8E,0000000000333ACC,000000000A93E9AD,007B3514,000000007C0E5F69,00000007AD1AE9C6
 
765
         *        last_ds_time,last_rx_flow    ,last_tx_flow    ,total_ds_time,total_tx_flow,total_rx_flow
 
766
         */
 
767
 
 
768
        if (sscanf(buf + 11, "%lX,%LX,%LX,%lX,%LX,%LX", &last_ds_time, &last_tx_flow, &last_rx_flow, &total_ds_time,
 
769
                   &total_tx_flow, &total_rx_flow) != 6)
 
770
            error("%s: ERROR: Cannot parse all ^DSFLOWQRY: data fields, some data may be wrong or missing", name);
 
771
 
 
772
        /* ^DSFLOWQRY: lacks tx_rate and rx_rate values (^DSFLOWRPT has them), we try to calculate them here. 
 
773
         * Values comes at 2s interval.
 
774
         */
 
775
 
 
776
        /* tx_rate calculation */
 
777
        if (last_ds_time >= prev_ds_time + 2 && last_tx_flow > prev_tx_flow)
 
778
            calc_tx_rate = ((double) (last_tx_flow - prev_tx_flow)) / ((double) (last_ds_time - prev_ds_time));
 
779
        else if (last_ds_time >= prev_ds_time + 2 && last_tx_flow == prev_tx_flow)
 
780
            calc_tx_rate = 0;
 
781
 
 
782
        /* rx_rate calculation */
 
783
        if (last_ds_time >= prev_ds_time + 2 && last_rx_flow > prev_rx_flow)
 
784
            calc_rx_rate = ((double) (last_rx_flow - prev_rx_flow)) / ((double) (last_ds_time - prev_ds_time));
 
785
        else if (last_ds_time >= prev_ds_time + 2 && last_rx_flow == prev_rx_flow)
 
786
            calc_rx_rate = 0;
 
787
 
 
788
        /* previous values */
 
789
        prev_tx_flow = last_tx_flow;
 
790
        prev_rx_flow = last_rx_flow;
 
791
        prev_ds_time = last_ds_time;
 
792
 
 
793
        if (debug) {
 
794
            debug("DEBUG: Last DS connection time [s]: %lu", last_ds_time);
 
795
            debug("DEBUG: Last DS transmiting traffic [bytes]: %llu", last_tx_flow);
 
796
            debug("DEBUG: Last DS receiving traffic [bytes]: %llu", last_rx_flow);
 
797
            debug("DEBUG: Total DS connection time [s]: %lu", total_ds_time);
 
798
            debug("DEBUG: Total DS transmiting trafic [bytes]: %llu", total_tx_flow);
 
799
            debug("DEBUG: Total DS receiving trafic [bytes]: %llu", total_rx_flow);
 
800
            debug("DEBUG: Calculated tx rate [bytes/s]: %lf", calc_tx_rate);
 
801
            debug("DEBUG: Calculated rx rate [bytes/s]: %lf", calc_rx_rate);
 
802
        }
 
803
 
 
804
    } else
 
805
        error("%s: ERROR: Invalid or empty response: \'%s\' received for: \'%s\'", name, buf, cmd);
 
806
 
 
807
    return;
 
808
}
 
809
 
 
810
static void my_quality(RESULT * result, RESULT * arg1)
 
811
{
 
812
    int age;
 
813
    static struct timeval prev_age;
 
814
    static double value;
 
815
 
 
816
    age = age_diff(prev_age);
 
817
 
 
818
    if (age < 0 || age >= MIN_INTERVAL) {
 
819
        gettimeofday(&prev_age, NULL);
 
820
 
 
821
        if (huawei_configured() == 1)
 
822
            huawei_read_quality(QUALITY);
 
823
    }
 
824
 
 
825
    /* Note: R2S stands for 'Result to String' */
 
826
    if (strncmp(R2S(arg1), "%", 1) == 0) {
 
827
        /* scale rssi 0...31 to 0..100% value */
 
828
        if (rssi > 0 && rssi < 32)
 
829
            value = (double) rssi *100 / 31;
 
830
        else if (rssi == 0)
 
831
            value = 0;
 
832
 
 
833
    } else if (strncmp(R2S(arg1), "dbm", 3) == 0) {
 
834
        /* scale rssi 0...31 to -113 dBm...-51 dBm value */
 
835
        if (rssi > 0 && rssi < 32)
 
836
            value = ((double) rssi * 2) - 113;
 
837
        else if (rssi == 0)
 
838
            value = -113;
 
839
 
 
840
    } else if (strncmp(R2S(arg1), "rssi", 4) == 0) {
 
841
        /* pass through relative rssi 0...31 value */
 
842
        if (rssi > 0 && rssi < 32)
 
843
            value = (double) rssi;
 
844
        else if (rssi == 0)
 
845
            value = 0;
 
846
 
 
847
    } else {
 
848
        error("%s: ERROR: Argument for huawei::quality() is missing, give: '%%'|'dbm'|'rssi'", name);
 
849
        value = 0;
 
850
    }
 
851
 
 
852
    /* store result */
 
853
    SetResult(&result, R_NUMBER, &value);
 
854
 
 
855
    return;
 
856
}
 
857
 
 
858
static void my_mode(RESULT * result, RESULT * arg1)
 
859
{
 
860
    int age;
 
861
    static struct timeval prev_age;
 
862
    double value_num;
 
863
    char *value_str, *mode_str;
 
864
 
 
865
    age = age_diff(prev_age);
 
866
 
 
867
    if (age < 0 || age >= MIN_INTERVAL) {
 
868
        gettimeofday(&prev_age, NULL);
 
869
 
 
870
        if (huawei_configured() == 1)
 
871
            huawei_read_sysinfo(SYSINFO);
 
872
    }
 
873
 
 
874
    if (strncmp(R2S(arg1), "text", 4) == 0) {
 
875
        /* sub modes 8 and 9 are unknown */
 
876
        if (sub_mode > 7)
 
877
            sub_mode = 8;
 
878
 
 
879
        /* start with an empty string */
 
880
        value_str = strdup("");
 
881
 
 
882
        /* mode string */
 
883
        mode_str = sub_system_mode[sub_mode];
 
884
 
 
885
        /* allocate memory for value */
 
886
        value_str = realloc(value_str, strlen(mode_str) + 1);
 
887
 
 
888
        /* write mode string to value */
 
889
        strcat(value_str, mode_str);
 
890
 
 
891
        /* store result */
 
892
        SetResult(&result, R_STRING, value_str);
 
893
 
 
894
        /* free local string */
 
895
        free(value_str);
 
896
 
 
897
    } else if (strncmp(R2S(arg1), "number", 6) == 0) {
 
898
        value_num = (double) mode;
 
899
 
 
900
        /* store result */
 
901
        SetResult(&result, R_NUMBER, &value_num);
 
902
 
 
903
    } else {
 
904
        error("%s: ERROR: Argument for huawei::mode() is missing, give: 'text'|'number'", name);
 
905
 
 
906
        value_num = 0;
 
907
 
 
908
        /* store result */
 
909
        SetResult(&result, R_NUMBER, &value_num);
 
910
    }
 
911
 
 
912
    return;
 
913
}
 
914
 
 
915
static void my_manuf(RESULT * result)
 
916
{
 
917
    int age;
 
918
    static struct timeval prev_age;
 
919
    char *value_str;
 
920
 
 
921
    age = age_diff(prev_age);
 
922
 
 
923
    if (age < 0 || age >= MIN_INTERVAL) {
 
924
        gettimeofday(&prev_age, NULL);
 
925
 
 
926
        if (huawei_configured() == 1)
 
927
            huawei_read_manuf(MANUF);
 
928
    }
 
929
 
 
930
    /* start with an empty string */
 
931
    value_str = strdup("");
 
932
 
 
933
    /* allocate memory for value */
 
934
    value_str = realloc(value_str, strlen(manuf) + 1);
 
935
 
 
936
    /* write mode string to value */
 
937
    strcat(value_str, manuf);
 
938
 
 
939
    /* store result */
 
940
    SetResult(&result, R_STRING, value_str);
 
941
 
 
942
    /* free local string */
 
943
    free(value_str);
 
944
 
 
945
    return;
 
946
}
 
947
 
 
948
static void my_model(RESULT * result)
 
949
{
 
950
    int age;
 
951
    static struct timeval prev_age;
 
952
    char *value_str;
 
953
 
 
954
    age = age_diff(prev_age);
 
955
 
 
956
    if (age < 0 || age >= MIN_INTERVAL) {
 
957
        gettimeofday(&prev_age, NULL);
 
958
 
 
959
        if (huawei_configured() == 1)
 
960
            huawei_read_model(MODEL);
 
961
    }
 
962
 
 
963
    /* start with an empty string */
 
964
    value_str = strdup("");
 
965
 
 
966
    /* allocate memory for value */
 
967
    value_str = realloc(value_str, strlen(model) + 1);
 
968
 
 
969
    /* write mode string to value */
 
970
    strcat(value_str, model);
 
971
 
 
972
    /* store result */
 
973
    SetResult(&result, R_STRING, value_str);
 
974
 
 
975
    /* free local string */
 
976
    free(value_str);
 
977
 
 
978
    return;
 
979
}
 
980
 
 
981
static void my_fwver(RESULT * result)
 
982
{
 
983
    int age;
 
984
    static struct timeval prev_age;
 
985
    char *value_str;
 
986
 
 
987
    age = age_diff(prev_age);
 
988
 
 
989
    if (age < 0 || age >= MIN_INTERVAL) {
 
990
        gettimeofday(&prev_age, NULL);
 
991
 
 
992
        if (huawei_configured() == 1)
 
993
            huawei_read_fwver(FWVER);
 
994
    }
 
995
 
 
996
    /* start with an empty string */
 
997
    value_str = strdup("");
 
998
 
 
999
    /* allocate memory for value */
 
1000
    value_str = realloc(value_str, strlen(fwver) + 1);
 
1001
 
 
1002
    /* write mode string to value */
 
1003
    strcat(value_str, fwver);
 
1004
 
 
1005
    /* store result */
 
1006
    SetResult(&result, R_STRING, value_str);
 
1007
 
 
1008
    /* free local string */
 
1009
    free(value_str);
 
1010
 
 
1011
    return;
 
1012
}
 
1013
 
 
1014
static void my_operator(RESULT * result)
 
1015
{
 
1016
    int age;
 
1017
    static struct timeval prev_age;
 
1018
    char *value_str;
 
1019
 
 
1020
    age = age_diff(prev_age);
 
1021
 
 
1022
    if (age < 0 || age >= MIN_INTERVAL) {
 
1023
        gettimeofday(&prev_age, NULL);
 
1024
 
 
1025
        if (huawei_configured() == 1)
 
1026
            huawei_read_operator(OPERATOR);
 
1027
    }
 
1028
 
 
1029
    /* start with an empty string */
 
1030
    value_str = strdup("");
 
1031
 
 
1032
    /* allocate memory for value */
 
1033
    value_str = realloc(value_str, strlen(operator) + 1);
 
1034
 
 
1035
    /* write mode string to value */
 
1036
    strcat(value_str, operator);
 
1037
 
 
1038
    /* store result */
 
1039
    SetResult(&result, R_STRING, value_str);
 
1040
 
 
1041
    /* free local string */
 
1042
    free(value_str);
 
1043
 
 
1044
    return;
 
1045
}
 
1046
 
 
1047
static void my_flowreport(RESULT * result, RESULT * arg1)
 
1048
{
 
1049
    int age;
 
1050
    unsigned int days, hours, mins, secs;
 
1051
    double value_num;
 
1052
    char value_str[32];
 
1053
    static struct timeval prev_age;
 
1054
 
 
1055
    age = age_diff(prev_age);
 
1056
 
 
1057
    if (age < 0 || age >= MIN_INTERVAL) {
 
1058
        gettimeofday(&prev_age, NULL);
 
1059
 
 
1060
        if (huawei_configured() == 1)
 
1061
            huawei_read_flowreport(FLOWREPORT);
 
1062
    }
 
1063
 
 
1064
    if (strncmp(R2S(arg1), "uptime", 6) == 0) {
 
1065
 
 
1066
        days = last_ds_time / 86400;
 
1067
        hours = (last_ds_time / 3600) - (days * 24);
 
1068
        mins = (last_ds_time / 60) - (days * 1440) - (hours * 60);
 
1069
        secs = last_ds_time % 60;
 
1070
 
 
1071
        if (days > 0)
 
1072
            sprintf(value_str, "%u days %02u:%02u:%02u", days, hours, mins, secs);
 
1073
        else
 
1074
            sprintf(value_str, "%02u:%02u:%02u", hours, mins, secs);
 
1075
 
 
1076
        /* store result: days, hours, mins, secs */
 
1077
        SetResult(&result, R_STRING, value_str);
 
1078
 
 
1079
        return;
 
1080
    }
 
1081
 
 
1082
    if (strncmp(R2S(arg1), "uptime_seconds", 14) == 0) {
 
1083
        /* uptime in seconds */
 
1084
        value_num = (double) last_ds_time;
 
1085
 
 
1086
        /* store result: seconds */
 
1087
        SetResult(&result, R_NUMBER, &value_num);
 
1088
 
 
1089
        return;
 
1090
    }
 
1091
 
 
1092
    if (strncmp(R2S(arg1), "tx_rate", 7) == 0) {
 
1093
        /* tx data in Bytes/s */
 
1094
        value_num = calc_tx_rate;
 
1095
 
 
1096
        /* store result */
 
1097
        SetResult(&result, R_NUMBER, &value_num);
 
1098
 
 
1099
        return;
 
1100
    }
 
1101
 
 
1102
    if (strncmp(R2S(arg1), "rx_rate", 7) == 0) {
 
1103
        /* rx data in Bytes/s */
 
1104
        value_num = calc_rx_rate;
 
1105
 
 
1106
        /* store result */
 
1107
        SetResult(&result, R_NUMBER, &value_num);
 
1108
 
 
1109
        return;
 
1110
    }
 
1111
 
 
1112
    if (strncmp(R2S(arg1), "total_tx", 8) == 0) {
 
1113
        /* total rx data in Bytes */
 
1114
        value_num = (double) total_tx_flow;
 
1115
 
 
1116
        /* store result */
 
1117
        SetResult(&result, R_NUMBER, &value_num);
 
1118
 
 
1119
        return;
 
1120
    }
 
1121
 
 
1122
    if (strncmp(R2S(arg1), "total_rx", 8) == 0) {
 
1123
        /* total rx data in Bytes */
 
1124
        value_num = (double) total_rx_flow;
 
1125
 
 
1126
        /* store result */
 
1127
        SetResult(&result, R_NUMBER, &value_num);
 
1128
 
 
1129
        return;
 
1130
    }
 
1131
 
 
1132
    error
 
1133
        ("%s: ERROR: Argument for huawei::flowreport() is missing, give: 'uptime'|'uptime_seconds'|'tx_rate'|'rx_rate'|'total_tx'|'total_rx')",
 
1134
         name);
 
1135
    value_num = 0;
 
1136
 
 
1137
    /* store result */
 
1138
    SetResult(&result, R_NUMBER, &value_num);
 
1139
 
 
1140
    return;
 
1141
}
 
1142
 
 
1143
/* plugin initialization. MUST NOT be declared 'static'! */
 
1144
int plugin_init_huawei(void)
 
1145
{
 
1146
    /* register our functions */
 
1147
    AddFunction("huawei::quality", 1, my_quality);
 
1148
    AddFunction("huawei::mode", 1, my_mode);
 
1149
    AddFunction("huawei::model", 0, my_model);
 
1150
    AddFunction("huawei::manuf", 0, my_manuf);
 
1151
    AddFunction("huawei::fwver", 0, my_fwver);
 
1152
    AddFunction("huawei::operator", 0, my_operator);
 
1153
    AddFunction("huawei::flowreport", 1, my_flowreport);
 
1154
 
 
1155
    return 0;
 
1156
}
 
1157
 
 
1158
void plugin_exit_huawei(void)
 
1159
{
 
1160
    int ret;
 
1161
 
 
1162
    /* close file descriptor */
 
1163
    if (fd > 0) {
 
1164
        ret = close(fd);
 
1165
        if (ret < 0)
 
1166
            error("%s: ERROR: Device %s closing failure on plugin_exit, %i", name, port, ret);
 
1167
    }
 
1168
    fd = -2;
 
1169
}