2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2003 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(WvStringParm 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());
67 bool WvModemScan::use_default_asyncmap() const
69 return default_asyncmap
;
72 bool WvModemScan::isok() const
79 WvString
WvModemScan::initstr() const
88 for (int i
= 0; i
< NUM_STAGES
; i
++)
90 if (status
[i
] != Worked
&& status
[i
] != Test
)
92 if (!commands
[i
] || !commands
[i
][0])
94 if ((commands
[i
][0]=='Z' || commands
[i
][0]=='I') && status
[i
] != Test
)
97 strcat(s
, commands
[i
]);
101 return WvString(trim_string(s
));
105 void WvModemScan::execute()
107 if (isdone() || !isok()) return;
109 switch ((Stage
)stage
)
113 modem
= new WvModem(file
, baud
);
114 modem
->die_fast
= true;
118 && modem
->geterr() != EIO
119 && modem
->geterr() != ENOENT
120 && modem
->geterr() != ENODEV
)
122 debug(WvLog::Info
, "%s\n", modem
->errstr());
138 status
[stage
] = Test
;
139 if (!strncmp(file
, "/dev/ircomm", 11))
141 while (baudcheck
[tries
+1] <= 9600 && baudcheck
[tries
+1] != 0)
144 if (baudcheck
[tries
] > 19200 || baudcheck
[tries
] == 0)
147 debug("failed at 9600 and 19200 baud.\n");
150 baud
= modem
->speed(baudcheck
[tries
]);
152 if (!doresult(WvString("%s\r", initstr()), stage
==ATZ
? 3000 : 500)
153 || ((stage
<= AT
|| stage
== Reinit
) && status
[stage
]==Fail
))
158 //modem->speed(baud*2);
159 //baud = modem->speed(baud);
160 if (baudcheck
[tries
] == 0)
163 debug("and failed too at %s, giving up.\n",
164 WvString(isdn_speed
));
165 // Go back to default_baud:
166 modem
->speed(default_baud
);
167 baud
= modem
->getspeed();
169 else if (strncmp(file
, "/dev/ircomm", 11))
170 debug("failed with %s baud, next try: %s baud\n",
172 baud
= modem
->speed(baudcheck
[tries
]));
173 //baud = modem->speed(baud*2));
177 if (baud
== default_baud
)
179 debug("nothing at %s baud,\n", WvString(default_baud
));
180 // Ok, then let's try ISDN speed for ISDN TAs:
181 modem
->speed(isdn_speed
);
182 baud
= modem
->getspeed();
187 // Ok, we tried default_baud and ISDN speed, give up:
189 debug("nor at %s.\n", WvString(isdn_speed
));
190 // Go back to default_baud:
191 modem
->speed(default_baud
);
192 baud
= modem
->getspeed();
197 // else try again shortly
208 status
[stage
] = Test
;
209 debug("Modem Identifier: ");
210 if (!doresult(WvString("ATI\r"), 500) || (status
[stage
]==Fail
))
217 // else try again shortly
222 debug("Looks like an ISDN modem.\n");
224 if (!strncmp(identifier
, "Hagenuk", 7))
226 status
[stage
] = Test
;
227 if (doresult(WvString("ATI1\r"), 500))
228 if (!strncmp(identifier
, "Speed Dragon", 12)
229 || !strncmp(identifier
, "Power Dragon", 12))
232 modem_name
= WvString("Hagenuk %s", identifier
);
234 status
[stage
] = Worked
;
236 else if (!strncmp(identifier
, "346900", 6))
238 status
[stage
] = Test
;
239 if (doresult(WvString("ATI3\r"), 500))
240 if (!strncmp(identifier
, "3Com U.S. Robotics ISDN",23))
242 isdn_init
= "AT*PPP=1";
243 modem_name
= identifier
;
245 status
[stage
] = Worked
;
247 else if (!strncmp(identifier
, "SP ISDN", 7))
249 status
[stage
] = Test
;
250 if (doresult(WvString("ATI4\r"), 500))
251 if (!strncmp(identifier
, "Sportster ISDN TA", 17))
254 modem_name
= identifier
;
256 status
[stage
] = Worked
;
258 else if (!strncmp(identifier
, "\"Version", 8))
260 status
[stage
] = Test
;
261 if (doresult(WvString("ATI6\r"), 500))
262 modem_name
= identifier
;
263 status
[stage
] = Worked
;
265 else if (!strncmp(identifier
, "644", 3))
267 status
[stage
] = Test
;
268 if (doresult(WvString("ATI6\r"), 500))
269 if (!strncmp(identifier
, "ELSA MicroLink ISDN", 19))
271 isdn_init
= "AT$IBP=HDLCP";
272 modem_name
= identifier
;
273 default_asyncmap
= true;
275 status
[stage
] = Worked
;
277 else if (!strncmp(identifier
, "643", 3))
279 status
[stage
] = Test
;
280 if (doresult(WvString("ATI6\r"), 500))
281 if (!strncmp(identifier
, "MicroLink ISDN/TLV.34", 21))
283 isdn_init
= "AT\\N10%P1";
284 modem_name
= identifier
;
286 status
[stage
] = Worked
;
288 else if (!strncmp(identifier
, "ISDN TA", 6))
290 status
[stage
] = Test
;
291 if (doresult(WvString("ATI5\r"), 500))
292 if (strstr(identifier
, ";ASU"))
295 modem_name
= "ASUSCOM ISDNLink TA";
297 status
[stage
] = Worked
;
299 else if (!strncmp(identifier
, "128000", 6))
301 status
[stage
] = Test
;
302 if (doresult(WvString("ATI3\r"), 500))
303 if (!strncmp(identifier
, "Lasat Speed", 11))
305 isdn_init
= "AT\\P1&B2X3";
306 modem_name
= identifier
;
308 status
[stage
] = Worked
;
310 else if (!strncmp(identifier
, "28642", 5) // Elite 2864I
311 || !strncmp(identifier
, "1281", 4) // Omni TA128 USA
312 || !strncmp(identifier
, "1282", 4) // Omni TA128 DSS1
313 || !strncmp(identifier
, "1283", 4) // Omni TA128 1TR6
314 || !strncmp(identifier
, "1291", 4) // Omni.Net USA
315 || !strncmp(identifier
, "1292", 4) // Omni.Net DSS1
316 || !strncmp(identifier
, "1293", 4) // Omni.Net 1TR6
319 status
[stage
] = Test
;
320 if (doresult(WvString("ATI1\r"), 500))
321 if (!strncmp(identifier
, "Elite 2864I", 11)
322 || !strncmp(identifier
, "ZyXEL omni", 10))
324 isdn_init
= "AT&O2B40";
325 if (strncmp(identifier
, "ZyXEL", 5))
326 modem_name
= WvString("ZyXEL %s", identifier
);
328 modem_name
= identifier
;
330 status
[stage
] = Worked
;
340 modem
->speed(baud
*2);
342 // if we try 2*baud three times without success, or setting 2*baud
343 // results in a lower setting than 1*baud, we have reached the
344 // top speed of the modem or the serial port, respectively.
345 if (tries
>= 3 || modem
->getspeed() <= baud
)
347 // using the absolute maximum baud rate confuses many slower modems
348 // in obscure ways; step down one.
349 baud
= modem
->speed(baud
);
350 debug("Max speed is %s; that should be safe.\n", baud
);
353 status
[stage
] = Worked
;
357 debug("Speed %s: ", modem
->getspeed());
359 if (!doresult("AT\r", 500) || status
[stage
] == Fail
)
363 else // got a response
365 baud
= modem
->getspeed();
367 // next time through we try a faster speed
374 break; // should never happen
377 if (stage
== Done
) // we just incremented stage number to Done
384 bool WvModemScan::doresult(WvStringParm _s
, int msec
)
386 char buf
[1024], *cptr
;
391 usleep(50 * 1000); // delay a bit after emptying the buffer
394 debug("%s -- ", trim_string(s
.edit()));
396 len
= coagulate(buf
, sizeof(buf
), msec
);
400 // debug("(no answer yet)\n");
406 cptr
= trim_string(buf
);
407 while (strchr(cptr
, '\r'))
409 cptr
= trim_string(strchr(cptr
, '\r'));
410 if (stage
== GetIdent
&& status
[stage
] == Test
)
412 char *p
= strpbrk(cptr
, "\n\r");
415 status
[stage
] = Worked
;
416 debug("%s\n", identifier
);
420 while (strchr(cptr
, '\n'))
421 cptr
= trim_string(strchr(cptr
, '\n'));
425 if (!strncmp(cptr
, "OK", 2))
426 status
[stage
] = Worked
;
428 status
[stage
] = Fail
;
434 size_t WvModemScan::coagulate(char *buf
, size_t size
, int msec
)
447 while (modem
->select(msec
, true, false))
449 amt
= modem
->read(cptr
, size
-1);
456 if (strstr(buf
, "OK") || strstr(buf
, "ERROR"))
464 const char *WvModemScan::is_isdn() const
472 if (identifier
== "3C882") // 3Com Impact IQ
474 if (identifier
== "346800") // USR ISDN TA
477 #if 0 // this isn't nearly unique enough...
478 if (identifier
== "960") // Motorola BitSurfr
486 static int fileselect(const struct dirent
*e
)
488 return !strncmp(e
->d_name
, "ttyS", 4) // serial
489 || !strncmp(e
->d_name
, "ttyLT", 5) // Lucent WinModem
490 || !strncmp(e
->d_name
, "ttyACM", 6) // USB acm Modems
491 || !strncmp(e
->d_name
, "ttyUSB", 6) // Modems on USB RS232
492 || !strncmp(e
->d_name
, "ircomm", 6) // Handys over IrDA
493 || !strncmp(e
->d_name
, "ttySL", 5); // SmartLink WinModem
495 // (no internal ISDN support) || !strncmp(e->d_name, "ttyI", 4);
499 static int filesort(const void *_e1
, const void *_e2
)
501 dirent
const * const *e1
= (dirent
const * const *)_e1
;
502 dirent
const * const *e2
= (dirent
const * const *)_e2
;
506 for (p1
=(*e1
)->d_name
, p2
=(*e2
)->d_name
; *p1
|| *p2
; p1
++, p2
++)
508 if (!isdigit(*p1
) || !isdigit(*p2
))
510 // Scan i (ircomm*) after t (tty*):
511 if (*p1
== 'i' && *p2
== 't')
513 // Scan A (ttyACM*) after S (ttyS*):
514 if (*p1
== 'A' && *p2
== 'S')
516 if (*p1
== 'S' && *p2
== 'A')
519 if (diff
) return diff
;
521 else // both are digits
523 return atoi(p1
) - atoi(p2
);
531 WvModemScanList::WvModemScanList(WvStringParm _exception
)
532 : log("Modem Port Scan", WvLog::Debug
)
534 struct dirent
**namelist
;
535 struct stat mouse
, modem
;
536 int num
, count
, mousestat
, modemstat
;
542 mousestat
= stat("/dev/mouse", &mouse
);
543 modemstat
= stat("/dev/modem", &modem
);
544 num
= scandir("/dev", &namelist
, fileselect
, filesort
);
549 // there shouldn't be a /dev/
551 exception
= strrchr(_exception
, '/') + 1;
553 for (count
= 0; count
< num
; count
++)
555 // never search the device assigned to /dev/mouse; most mouse-using
556 // programs neglect to lock the device, so we could mess up the
557 // mouse response! (We are careful to put things back when done,
558 // but X seems to still get confused.) Anyway the mouse is seldom
560 if (mousestat
==0 && mouse
.st_ino
== (ino_t
)namelist
[count
]->d_ino
)
562 log("\nIgnoring %s because /dev/mouse is a link to it.\n",
563 namelist
[count
]->d_name
);
567 if (!!exception
&& !strcmp(exception
, namelist
[count
]->d_name
))
569 log("\nIgnoring %s because I've been told to ignore it.\n",
570 namelist
[count
]->d_name
);
574 // bump /dev/modem to the top of the list, if it exists
575 // and also use /dev/modem as the device name which will be used later
576 // so PCMCIA can change it where it has detected a serial port and
577 // wvdial will follow without the need for another wvdialconf call.
578 if (modemstat
==0 && modem
.st_ino
== (ino_t
)namelist
[count
]->d_ino
)
580 log("\nScanning %s first, /dev/modem is a link to it.\n",
581 namelist
[count
]->d_name
);
582 prepend(new WvModemScan(WvString("%s", namelist
[count
]->d_name
), true),
586 append(new WvModemScan(WvString("%s", namelist
[count
]->d_name
), false),
596 // we used to try to scan all ports simultaneously; unfortunately, this
597 // caused problems when people had "noncritical" IRQ conflicts (ie. two
598 // serial ports with the same IRQ, but they work as long as only one port
599 // is used at a time). Also, the log messages looked really confused.
601 // So now we do the scanning sequentially, which is slower. The port
602 // being scanned is at the head of the list. If the probe fails, we
603 // unlink the element. If it succeeds, we have found a modem -- so,
604 // isdone() knows we are done when the _first_ element is done.
606 void WvModemScanList::execute()
610 WvModemScanList::Iter
i(*this);
612 for (i
.rewind(); i
.next(); )
613 if (!i().isdone()) break;
615 if (!i
.cur()) return;
623 WvStringParm f
= s
.filename();
624 const char *cptr
= strrchr(f
, '/');
630 if (!strncmp(cptr
, "tty", 3))
656 bool WvModemScanList::isdone()
658 WvModemScanList::Iter
i(*this);
660 for (i
.rewind(); i
.next(); )
661 if (!i().isdone()) return false;