2
* Worldvisions Weaver Software:
3
* Copyright (C) 1997-2002 Net Integration Technologies, Inc.
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.
9
#include "wvmodemscan.h"
19
bool default_asyncmap = false;
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,
29
static int baudcheck[6] = {
36
static int default_baud = baudcheck[0];
37
static int isdn_speed = 115200;
39
WvModemScan::WvModemScan(const char *devname, bool is_modem_link)
40
: debug(devname, WvLog::Debug)
43
memset(status, 0, sizeof(status));
45
if (devname[0] == '/')
48
file = WvString("/dev/%s", devname);
50
use_modem_link = is_modem_link;
58
WvModemScan::~WvModemScan()
60
if (isok() && isdone())
61
debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());
68
bool WvModemScan::use_default_asyncmap() const
70
return default_asyncmap;
73
bool WvModemScan::isok() const
80
WvString WvModemScan::initstr() const
89
for (int i = 0; i < NUM_STAGES; i++)
91
if (status[i] != Worked && status[i] != Test)
93
if (!commands[i] || !commands[i][0])
95
if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
98
strcat(s, commands[i]);
102
return WvString(trim_string(s));
106
void WvModemScan::execute()
108
if (isdone() || !isok()) return;
110
switch ((Stage)stage)
114
modem = new WvModem(file, baud);
115
modem->die_fast = true;
119
&& modem->geterr() != EIO
120
&& modem->geterr() != ENOENT
121
&& modem->geterr() != ENODEV)
123
debug(WvLog::Info, "%s\n", modem->errstr());
139
status[stage] = Test;
140
if (!strncmp(file, "/dev/ircomm", 11)) {
141
while (baudcheck[tries+1] <= 9600 && baudcheck[tries+1] != 0) {
144
if (baudcheck[tries] > 19200 || baudcheck[tries] == 0) {
146
debug("failed at 9600 and 19200 baud.\n");
149
baud = modem->speed(baudcheck[tries]);
151
if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
152
|| ((stage <= AT || stage == Reinit) && status[stage]==Fail) )
157
//modem->speed(baud*2);
158
//baud = modem->speed(baud);
159
if (baudcheck[tries] == 0) {
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();
167
if (strncmp(file, "/dev/ircomm", 11))
168
debug("failed with %s baud, next try: %s baud\n",
170
baud = modem->speed(baudcheck[tries]));
171
//baud = modem->speed(baud*2));
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();
182
// Ok, we tried default_baud and ISDN speed, give up:
184
debug("nor at %s.\n", WvString(isdn_speed));
185
// Go back to default_baud:
186
modem->speed(default_baud);
187
baud = modem->getspeed();
192
// else try again shortly
203
status[stage] = Test;
204
debug("Modem Identifier: ");
205
if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
212
// else try again shortly
217
debug("Looks like an ISDN modem.\n");
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)) {
225
modem_name = WvString("Hagenuk %s", identifier);
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;
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)) {
241
modem_name = identifier;
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;
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;
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")) {
271
modem_name = "ASUSCOM ISDNLink TA";
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;
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
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);
298
modem_name = identifier;
300
status[stage] = Worked;
310
modem->speed(baud*2);
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)
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);
323
status[stage] = Worked;
327
debug("Speed %s: ", modem->getspeed());
329
if (!doresult("AT\r", 500) || status[stage] == Fail)
333
else // got a response
335
baud = modem->getspeed();
337
// next time through we try a faster speed
344
break; // should never happen
347
if (stage == Done) // we just incremented stage number to Done
356
bool WvModemScan::doresult(WvStringParm _s, int msec)
358
char buf[1024], *cptr;
363
usleep(50 * 1000); // delay a bit after emptying the buffer
366
debug("%s -- ", trim_string(s.edit()));
368
len = coagulate(buf, sizeof(buf), msec);
372
// debug("(no answer yet)\n");
378
cptr = trim_string(buf);
379
while (strchr(cptr, '\r'))
381
cptr = trim_string(strchr(cptr, '\r'));
382
if (stage == GetIdent && status[stage] == Test)
384
char *p = strpbrk(cptr, "\n\r");
387
status[stage] = Worked;
388
debug("%s\n", identifier);
392
while (strchr(cptr, '\n'))
393
cptr = trim_string(strchr(cptr, '\n'));
397
if (!strncmp(cptr, "OK", 2))
398
status[stage] = Worked;
400
status[stage] = Fail;
406
size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
419
while (modem->select(msec, true, false))
421
amt = modem->read(cptr, size-1);
428
if (strstr(buf, "OK") || strstr(buf, "ERROR"))
436
const char *WvModemScan::is_isdn() const
444
if (identifier == "3C882") // 3Com Impact IQ
446
if (identifier == "346800") // USR ISDN TA
449
#if 0 // this isn't nearly unique enough...
450
if (identifier == "960") // Motorola BitSurfr
458
static int fileselect(const struct dirent *e)
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
467
// (no internal ISDN support) || !strncmp(e->d_name, "ttyI", 4);
471
static int filesort(const void *_e1, const void *_e2)
473
dirent const * const *e1 = (dirent const * const *)_e1;
474
dirent const * const *e2 = (dirent const * const *)_e2;
478
for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
480
if (!isdigit(*p1) || !isdigit(*p2))
482
// Scan i (ircomm*) after t (tty*):
483
if (*p1 == 'i' && *p2 == 't')
485
// Scan A (ttyACM*) after S (ttyS*):
486
if (*p1 == 'A' && *p2 == 'S')
488
if (*p1 == 'S' && *p2 == 'A')
491
if (diff) return diff;
493
else // both are digits
495
return atoi(p1) - atoi(p2);
503
WvModemScanList::WvModemScanList() : log("Port Scan", WvLog::Debug)
505
struct dirent **namelist;
506
struct stat mouse, modem;
507
int num, count, mousestat, modemstat;
512
mousestat = stat("/dev/mouse", &mouse);
513
modemstat = stat("/dev/modem", &modem);
514
num = scandir("/dev", &namelist, fileselect, filesort);
519
for (count = 0; count < num; count++)
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
526
if (mousestat==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
528
log("\nIgnoring %s because /dev/mouse is a link to it.\n",
529
namelist[count]->d_name);
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),
543
append(new WvModemScan(WvString("%s", namelist[count]->d_name), false),
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.
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.
563
void WvModemScanList::execute()
567
WvModemScanList::Iter i(*this);
569
for (i.rewind(); i.next(); )
570
if (!i().isdone()) break;
572
if (!i.cur()) return;
580
WvStringParm f = s.filename();
581
const char *cptr = strrchr(f, '/');
587
if (!strncmp(cptr, "tty", 3))
613
bool WvModemScanList::isdone()
615
WvModemScanList::Iter i(*this);
617
for (i.rewind(); i.next(); )
618
if (!i().isdone()) return false;