~ubuntu-branches/ubuntu/quantal/wvdial/quantal

« back to all changes in this revision

Viewing changes to wvmodemscan.cc

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-10-06 11:05:06 UTC
  • mfrom: (0.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20041006110506-138x19fe4q0iu46e
Tags: 1.54.0-1ubuntu1
postinst: Disable command-line configuration to not disturb installation;
the script just exits early, so the postinst code is not completely lost
(Warty bug #2069)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Worldvisions Weaver Software:
 
3
 *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
 
4
 *
 
5
 * Intelligent serial port scanner: try to find a port (or ports)
 
6
 * with a working modem, guess a basic startup init string, and find
 
7
 * the maximum baud rate.
 
8
 */
 
9
#include "wvmodemscan.h"
 
10
#include "wvmodem.h"
 
11
#include "strutils.h"
 
12
#include <time.h>
 
13
#include <assert.h>
 
14
#include <dirent.h>
 
15
#include <ctype.h>
 
16
#include <sys/stat.h>
 
17
 
 
18
WvString isdn_init;
 
19
bool     default_asyncmap = false;
 
20
 
 
21
// startup at atz atq0 atv1 ate1 ats0 carrier dtr fastdial
 
22
// baudstep reinit done
 
23
static char *commands[WvModemScan::NUM_STAGES] = {
 
24
    NULL, "Q0 V1 E1", "Z", "S0=0",
 
25
    "&C1", "&D2", "+FCLASS=0", NULL,
 
26
    NULL, "", NULL
 
27
};
 
28
 
 
29
static int baudcheck[6] = {
 
30
        2400,
 
31
        9600,
 
32
        115200,
 
33
        0
 
34
};
 
35
 
 
36
static int default_baud =   baudcheck[0];
 
37
static int isdn_speed   = 115200;
 
38
 
 
39
WvModemScan::WvModemScan(const char *devname, bool is_modem_link)
 
40
        : debug(devname, WvLog::Debug)
 
41
{
 
42
    stage = Startup;
 
43
    memset(status, 0, sizeof(status));
 
44
 
 
45
    if (devname[0] == '/')
 
46
        file = devname;
 
47
    else
 
48
        file = WvString("/dev/%s", devname);
 
49
    
 
50
    use_modem_link = is_modem_link;
 
51
    baud = default_baud;
 
52
    modem = NULL;
 
53
    tries = 0;
 
54
    broken = false;
 
55
}
 
56
 
 
57
 
 
58
WvModemScan::~WvModemScan()
 
59
{
 
60
    if (isok() && isdone())
 
61
        debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());
 
62
    
 
63
    if (modem)
 
64
        delete modem;
 
65
}
 
66
 
 
67
 
 
68
bool WvModemScan::use_default_asyncmap() const
 
69
{
 
70
    return default_asyncmap;
 
71
}
 
72
 
 
73
bool WvModemScan::isok() const
 
74
{
 
75
    return !broken;
 
76
}
 
77
 
 
78
 
 
79
 
 
80
WvString WvModemScan::initstr() const
 
81
{
 
82
    char s[200];
 
83
 
 
84
    if (isdn_init)
 
85
        return (isdn_init);
 
86
 
 
87
    strcpy(s, "AT");
 
88
    
 
89
    for (int i = 0; i < NUM_STAGES; i++)
 
90
    {
 
91
        if (status[i] != Worked && status[i] != Test)
 
92
            continue;
 
93
        if (!commands[i] || !commands[i][0])
 
94
            continue;
 
95
        if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
 
96
            continue;
 
97
        
 
98
        strcat(s, commands[i]);
 
99
        strcat(s, " ");
 
100
    }
 
101
    
 
102
    return WvString(trim_string(s));
 
103
}
 
104
 
 
105
 
 
106
void WvModemScan::execute()
 
107
{
 
108
    if (isdone() || !isok()) return;
 
109
 
 
110
    switch ((Stage)stage)
 
111
    {
 
112
    case Startup:
 
113
        assert(!modem);
 
114
        modem = new WvModem(file, baud);
 
115
        modem->die_fast = true;
 
116
        if (!modem->isok())
 
117
        {
 
118
            if (modem->geterr()
 
119
                && modem->geterr() != EIO
 
120
                && modem->geterr() != ENOENT
 
121
                && modem->geterr() != ENODEV)
 
122
            {
 
123
                debug(WvLog::Info, "%s\n", modem->errstr());
 
124
            }
 
125
            broken = true;
 
126
        }
 
127
        else
 
128
            stage++;
 
129
        break;
 
130
        
 
131
    case AT:
 
132
    case ATZ:
 
133
    case ATS0:
 
134
    case Carrier:
 
135
    case DTR:
 
136
    case FCLASS:
 
137
    case Reinit:
 
138
        assert(modem);
 
139
        status[stage] = Test;
 
140
        if (!strncmp(file, "/dev/ircomm", 11)) {
 
141
            while (baudcheck[tries+1] <= 9600 && baudcheck[tries+1] != 0) {
 
142
                tries++;
 
143
            }
 
144
            if (baudcheck[tries] > 19200 || baudcheck[tries] == 0) {
 
145
                broken = true;
 
146
                debug("failed at 9600 and 19200 baud.\n");
 
147
                return;
 
148
            }
 
149
            baud = modem->speed(baudcheck[tries]);
 
150
        }
 
151
        if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
 
152
            || ((stage <= AT || stage == Reinit) && status[stage]==Fail) )
 
153
        {
 
154
            int old_baud = baud;
 
155
            tries++;
 
156
            //modem->drain();
 
157
            //modem->speed(baud*2);
 
158
            //baud = modem->speed(baud);
 
159
            if (baudcheck[tries] == 0) {
 
160
                broken = true;
 
161
                debug("and failed too at %s, giving up.\n",
 
162
                        WvString(isdn_speed));
 
163
                // Go back to default_baud:
 
164
                modem->speed(default_baud);
 
165
                baud = modem->getspeed();
 
166
            } else
 
167
                if (strncmp(file, "/dev/ircomm", 11))
 
168
                debug("failed with %s baud, next try: %s baud\n",
 
169
                        old_baud,
 
170
                        baud = modem->speed(baudcheck[tries]));
 
171
                        //baud = modem->speed(baud*2));
 
172
#if 0       
 
173
            if (tries >= 4)
 
174
            {
 
175
                if (baud == default_baud) {
 
176
                        debug("nothing at %s baud,\n", WvString(default_baud));
 
177
                        // Ok, then let's try ISDN speed for ISDN TAs:
 
178
                        modem->speed(isdn_speed);
 
179
                        baud = modem->getspeed();
 
180
                        tries = 0;
 
181
                } else {
 
182
                        // Ok, we tried default_baud and ISDN speed, give up:
 
183
                        broken = true;
 
184
                        debug("nor at %s.\n", WvString(isdn_speed));
 
185
                        // Go back to default_baud:
 
186
                        modem->speed(default_baud);
 
187
                        baud = modem->getspeed();
 
188
                }
 
189
            }
 
190
#endif
 
191
            
 
192
            // else try again shortly
 
193
        }
 
194
        else
 
195
        {
 
196
            tries = 0;
 
197
            stage++;
 
198
        }
 
199
        break;
 
200
        
 
201
    case GetIdent:
 
202
        assert(modem);
 
203
        status[stage] = Test;
 
204
        debug("Modem Identifier: ");
 
205
        if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
 
206
        {
 
207
            tries++;
 
208
 
 
209
            if (tries >= 3)
 
210
                debug("nothing.\n");
 
211
 
 
212
            // else try again shortly
 
213
        }
 
214
        else
 
215
        {
 
216
            if (is_isdn())
 
217
                debug("Looks like an ISDN modem.\n");
 
218
 
 
219
            if (!strncmp(identifier, "Hagenuk", 7)) {
 
220
                status[stage] = Test;
 
221
                if (doresult(WvString("ATI1\r"), 500)) 
 
222
                    if (!strncmp(identifier, "Speed Dragon", 12)
 
223
                    ||  !strncmp(identifier, "Power Dragon", 12)) {
 
224
                        isdn_init = "ATB8";
 
225
                        modem_name = WvString("Hagenuk %s", identifier);
 
226
                    }
 
227
                status[stage] = Worked;
 
228
            } else if (!strncmp(identifier, "346900", 6)) {
 
229
                status[stage] = Test;
 
230
                if (doresult(WvString("ATI3\r"), 500))
 
231
                    if (!strncmp(identifier, "3Com U.S. Robotics ISDN",23)) {
 
232
                        isdn_init = "AT*PPP=1";
 
233
                        modem_name = identifier;
 
234
                    }
 
235
                status[stage] = Worked;
 
236
            } else if (!strncmp(identifier, "SP ISDN", 7)) {
 
237
                status[stage] = Test;
 
238
                if (doresult(WvString("ATI4\r"), 500))
 
239
                    if (!strncmp(identifier, "Sportster ISDN TA", 17)) {
 
240
                        isdn_init = "ATB3";
 
241
                        modem_name = identifier;
 
242
                    }
 
243
                status[stage] = Worked;
 
244
            } else if (!strncmp(identifier, "\"Version", 8)) {
 
245
                status[stage] = Test;
 
246
                if (doresult(WvString("ATI6\r"), 500))
 
247
                        modem_name = identifier;
 
248
                status[stage] = Worked;
 
249
            } else if (!strncmp(identifier, "644", 3)) {
 
250
                status[stage] = Test;
 
251
                if (doresult(WvString("ATI6\r"), 500))
 
252
                        if (!strncmp(identifier, "ELSA MicroLink ISDN", 19)) {
 
253
                                isdn_init = "AT$IBP=HDLCP";
 
254
                                modem_name = identifier;
 
255
                                default_asyncmap = true;
 
256
                        }
 
257
                status[stage] = Worked;
 
258
            } else if (!strncmp(identifier, "643", 3)) {
 
259
                status[stage] = Test;
 
260
                if (doresult(WvString("ATI6\r"), 500))
 
261
                        if (!strncmp(identifier, "MicroLink ISDN/TLV.34", 21)) {
 
262
                                isdn_init = "AT\\N10%P1";
 
263
                                modem_name = identifier;
 
264
                        }
 
265
                status[stage] = Worked;
 
266
            } else if (!strncmp(identifier, "ISDN TA", 6)) {
 
267
                status[stage] = Test;
 
268
                if (doresult(WvString("ATI5\r"), 500))
 
269
                        if (strstr(identifier, ";ASU")) {
 
270
                                isdn_init = "ATB40";
 
271
                                modem_name = "ASUSCOM ISDNLink TA";
 
272
                        }
 
273
                status[stage] = Worked;
 
274
            } else if (!strncmp(identifier, "128000", 6)) {
 
275
                status[stage] = Test;
 
276
                if (doresult(WvString("ATI3\r"), 500))
 
277
                        if (!strncmp(identifier, "Lasat Speed", 11)) {
 
278
                                isdn_init = "AT\\P1&B2X3";
 
279
                                modem_name = identifier;
 
280
                        }
 
281
                status[stage] = Worked;
 
282
            } else if (!strncmp(identifier, "28642", 5) // Elite 2864I
 
283
                ||     !strncmp(identifier, "1281", 4)  // Omni TA128 USA
 
284
                ||     !strncmp(identifier, "1282", 4)  // Omni TA128 DSS1
 
285
                ||     !strncmp(identifier, "1283", 4)  // Omni TA128 1TR6
 
286
                ||     !strncmp(identifier, "1291", 4)  // Omni.Net USA
 
287
                ||     !strncmp(identifier, "1292", 4)  // Omni.Net DSS1
 
288
                ||     !strncmp(identifier, "1293", 4)  // Omni.Net 1TR6
 
289
                ) {
 
290
                status[stage] = Test;
 
291
                if (doresult(WvString("ATI1\r"), 500))
 
292
                    if (!strncmp(identifier, "Elite 2864I", 11)
 
293
                    ||  !strncmp(identifier, "ZyXEL omni", 10)) {
 
294
                        isdn_init = "AT&O2B40";
 
295
                        if (strncmp(identifier, "ZyXEL", 5))
 
296
                            modem_name = WvString("ZyXEL %s", identifier);
 
297
                        else
 
298
                            modem_name = identifier;
 
299
                    }
 
300
                status[stage] = Worked;
 
301
            }
 
302
 
 
303
            tries = 0;
 
304
            stage++;
 
305
        }
 
306
        
 
307
    case BaudStep:
 
308
        assert(modem);
 
309
        modem->drain();
 
310
        modem->speed(baud*2);
 
311
 
 
312
        // if we try 2*baud three times without success, or setting 2*baud
 
313
        // results in a lower setting than 1*baud, we have reached the
 
314
        // top speed of the modem or the serial port, respectively.
 
315
        if (tries >= 3 || modem->getspeed() <= baud)
 
316
        {
 
317
            // using the absolute maximum baud rate confuses many slower modems
 
318
            // in obscure ways; step down one.
 
319
            baud = modem->speed(baud);
 
320
            debug("Max speed is %s; that should be safe.\n", baud);
 
321
            
 
322
            stage++;
 
323
            status[stage] = Worked;
 
324
            break;
 
325
        }
 
326
        
 
327
        debug("Speed %s: ", modem->getspeed());
 
328
        
 
329
        if (!doresult("AT\r", 500) || status[stage] == Fail)
 
330
        {
 
331
            tries++;
 
332
        }
 
333
        else // got a response
 
334
        {
 
335
            baud = modem->getspeed();
 
336
            tries = 0;
 
337
            // next time through we try a faster speed
 
338
        }
 
339
        break;
 
340
        
 
341
    case Done:
 
342
    case NUM_STAGES:
 
343
        assert(0);
 
344
        break; // should never happen
 
345
    }
 
346
    
 
347
    if (stage == Done) // we just incremented stage number to Done
 
348
    {
 
349
        if (modem)
 
350
            delete modem;
 
351
        modem = NULL;
 
352
    }
 
353
}
 
354
 
 
355
 
 
356
bool WvModemScan::doresult(WvStringParm _s, int msec)
 
357
{
 
358
    char buf[1024], *cptr;
 
359
    size_t len;
 
360
    WvString s(_s);
 
361
    
 
362
    modem->drain();
 
363
    usleep(50 * 1000); // delay a bit after emptying the buffer
 
364
    modem->write(s);
 
365
 
 
366
    debug("%s -- ", trim_string(s.edit()));
 
367
    
 
368
    len = coagulate(buf, sizeof(buf), msec);
 
369
 
 
370
    if (!len)
 
371
    {
 
372
        // debug("(no answer yet)\n");
 
373
        return false;
 
374
    }
 
375
    
 
376
    buf[len] = 0;
 
377
    
 
378
    cptr = trim_string(buf);
 
379
    while (strchr(cptr, '\r'))
 
380
    {
 
381
        cptr = trim_string(strchr(cptr, '\r'));
 
382
        if (stage == GetIdent && status[stage] == Test)
 
383
        {
 
384
            char *p = strpbrk(cptr, "\n\r");
 
385
            if (p) *p=0;
 
386
            identifier = cptr;
 
387
            status[stage] = Worked;
 
388
            debug("%s\n", identifier);
 
389
            return true;
 
390
        }
 
391
    }
 
392
    while (strchr(cptr, '\n'))
 
393
        cptr = trim_string(strchr(cptr, '\n'));
 
394
    
 
395
    debug("%s\n", cptr);
 
396
 
 
397
    if (!strncmp(cptr, "OK", 2))
 
398
        status[stage] = Worked;
 
399
    else
 
400
        status[stage] = Fail;
 
401
    
 
402
    return true;
 
403
}
 
404
 
 
405
 
 
406
size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
 
407
{
 
408
    size_t len = 0, amt;
 
409
    char *cptr = buf;
 
410
 
 
411
    assert(modem);
 
412
    
 
413
    if (!modem->isok())
 
414
    {
 
415
        broken = true;
 
416
        return 0;
 
417
    }
 
418
    
 
419
    while (modem->select(msec, true, false))
 
420
    {
 
421
        amt = modem->read(cptr, size-1);
 
422
        cptr[amt] = 0;
 
423
 
 
424
        len += amt;
 
425
        size -= amt;
 
426
        cptr += amt;
 
427
 
 
428
        if (strstr(buf, "OK") || strstr(buf, "ERROR"))
 
429
            break;
 
430
    }
 
431
    
 
432
    return len;
 
433
}
 
434
 
 
435
 
 
436
const char *WvModemScan::is_isdn() const
 
437
{
 
438
    if (isdn_init)
 
439
        return isdn_init;
 
440
 
 
441
    if (!identifier)
 
442
        return NULL;
 
443
 
 
444
    if (identifier == "3C882")          // 3Com Impact IQ
 
445
        return identifier;
 
446
    if (identifier == "346800")         // USR ISDN TA
 
447
        return identifier;
 
448
 
 
449
#if 0 // this isn't nearly unique enough...
 
450
    if (identifier == "960")            // Motorola BitSurfr
 
451
        return identifier;
 
452
#endif
 
453
    
 
454
    return NULL;
 
455
}       
 
456
 
 
457
 
 
458
static int fileselect(const struct dirent *e)
 
459
{
 
460
    return !strncmp(e->d_name, "ttyS", 4)       // serial
 
461
       || !strncmp(e->d_name, "ttyLT", 5)       // Lucent WinModem
 
462
       || !strncmp(e->d_name, "ttyACM", 6)      // USB acm Modems
 
463
       || !strncmp(e->d_name, "ttyUSB", 6)      // Modems on USB RS232
 
464
       || !strncmp(e->d_name, "ircomm", 6)      // Handys over IrDA
 
465
       || !strncmp(e->d_name, "ttySL", 5);      // SmartLink WinModem
 
466
 
 
467
        // (no internal ISDN support)   || !strncmp(e->d_name, "ttyI", 4);
 
468
}
 
469
 
 
470
 
 
471
static int filesort(const void *_e1, const void *_e2)
 
472
{
 
473
    dirent const * const *e1 = (dirent const * const *)_e1;
 
474
    dirent const * const *e2 = (dirent const * const *)_e2;
 
475
    const char *p1, *p2;
 
476
    int diff;
 
477
    
 
478
    for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
 
479
    {
 
480
        if (!isdigit(*p1) || !isdigit(*p2))
 
481
        {
 
482
            // Scan i (ircomm*) after t (tty*):
 
483
            if (*p1 == 'i' && *p2 == 't')
 
484
                return(1);
 
485
            // Scan A (ttyACM*) after S (ttyS*):
 
486
            if (*p1 == 'A' && *p2 == 'S')
 
487
                return(1);
 
488
            if (*p1 == 'S' && *p2 == 'A')
 
489
                return(-1);
 
490
            diff = *p1 - *p2;
 
491
            if (diff) return diff;
 
492
        }
 
493
        else // both are digits
 
494
        {
 
495
            return atoi(p1) - atoi(p2);
 
496
        }
 
497
    }
 
498
    
 
499
    return 0;
 
500
}
 
501
 
 
502
 
 
503
WvModemScanList::WvModemScanList() : log("Port Scan", WvLog::Debug)
 
504
{
 
505
    struct dirent **namelist;
 
506
    struct stat mouse, modem;
 
507
    int num, count, mousestat, modemstat;
 
508
    
 
509
    thisline = -1;
 
510
    printed = false;
 
511
    
 
512
    mousestat = stat("/dev/mouse", &mouse);
 
513
    modemstat = stat("/dev/modem", &modem);
 
514
    num = scandir("/dev", &namelist, fileselect, filesort);
 
515
    
 
516
    if (num < 0)
 
517
        return;
 
518
    
 
519
    for (count = 0; count < num; count++)
 
520
    {
 
521
        // never search the device assigned to /dev/mouse; most mouse-using
 
522
        // programs neglect to lock the device, so we could mess up the
 
523
        // mouse response!  (We are careful to put things back when done,
 
524
        // but X seems to still get confused.)  Anyway the mouse is seldom
 
525
        // a modem.
 
526
        if (mousestat==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
 
527
        {
 
528
            log("\nIgnoring %s because /dev/mouse is a link to it.\n",
 
529
                       namelist[count]->d_name);
 
530
            continue;
 
531
        }
 
532
        
 
533
        // bump /dev/modem to the top of the list, if it exists
 
534
        // and also use /dev/modem as the device name which will be used later
 
535
        // so PCMCIA can change it where it has detected a serial port and
 
536
        // wvdial will follow without the need for another wvdialconf call.
 
537
        if (modemstat==0 && modem.st_ino == (ino_t)namelist[count]->d_ino) {
 
538
            log("\nScanning %s first, /dev/modem is a link to it.\n",
 
539
                       namelist[count]->d_name);
 
540
            prepend(new WvModemScan(WvString("%s", namelist[count]->d_name), true),
 
541
                   true);
 
542
        } else
 
543
            append(new WvModemScan(WvString("%s", namelist[count]->d_name), false),
 
544
                   true);
 
545
    }
 
546
    
 
547
    while (--num >= 0)
 
548
        free(namelist[num]);
 
549
    free(namelist);
 
550
}
 
551
 
 
552
 
 
553
// we used to try to scan all ports simultaneously; unfortunately, this
 
554
// caused problems when people had "noncritical" IRQ conflicts (ie. two
 
555
// serial ports with the same IRQ, but they work as long as only one port
 
556
// is used at a time).  Also, the log messages looked really confused.
 
557
//
 
558
// So now we do the scanning sequentially, which is slower.  The port
 
559
// being scanned is at the head of the list.  If the probe fails, we
 
560
// unlink the element.  If it succeeds, we have found a modem -- so,
 
561
// isdone() knows we are done when the _first_ element is done.
 
562
//
 
563
void WvModemScanList::execute()
 
564
{
 
565
    assert (!isdone());
 
566
 
 
567
    WvModemScanList::Iter i(*this);
 
568
 
 
569
    for (i.rewind(); i.next(); )
 
570
        if (!i().isdone()) break;
 
571
 
 
572
    if (!i.cur()) return; 
 
573
    
 
574
    WvModemScan &s(*i);
 
575
    
 
576
    if (!s.isok())
 
577
    {
 
578
        if (!printed)
 
579
        {
 
580
            WvStringParm f = s.filename();
 
581
            const char *cptr = strrchr(f, '/');
 
582
            if (cptr)
 
583
                cptr++;
 
584
            else
 
585
                cptr = f;
 
586
 
 
587
            if (!strncmp(cptr, "tty", 3))
 
588
                cptr += 3;
 
589
            
 
590
            ++thisline %= 8;
 
591
            if (!thisline)
 
592
                log("\n");
 
593
 
 
594
            log("%-4s ", cptr);
 
595
        }
 
596
 
 
597
        i.unlink();
 
598
        printed = false;
 
599
    }
 
600
    else
 
601
    {
 
602
        s.execute();
 
603
        
 
604
        if (s.isok())
 
605
            printed = true;
 
606
    }
 
607
            
 
608
    if (isdone()) 
 
609
        log("\n");
 
610
}
 
611
 
 
612
 
 
613
bool WvModemScanList::isdone()
 
614
{
 
615
    WvModemScanList::Iter i(*this);
 
616
    
 
617
    for (i.rewind(); i.next(); )
 
618
        if (!i().isdone()) return false;
 
619
    
 
620
    return true;
 
621
}