]> git.ipfire.org Git - ipfire-2.x.git/blob - src/hwinfo/src/hd/modem.c
Kleiner netter neuer Versuch.
[ipfire-2.x.git] / src / hwinfo / src / hd / modem.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <termios.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/ioctl.h>
12
13 #include "hd.h"
14 #include "hd_int.h"
15 #include "hddb.h"
16 #include "modem.h"
17
18 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 * modem info
20 *
21 *
22 * Note: what about modem speed?
23 *
24 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
25 */
26
27 #ifndef LIBHD_TINY
28
29 static struct speeds_s {
30 unsigned baud;
31 speed_t mask;
32 } speeds[] = {
33 { 1200, B1200 },
34 { 1800, B1800 },
35 { 2400, B2400 },
36 { 4800, B4800 },
37 { 9600, B9600 },
38 { 19200, B19200 },
39 { 38400, B38400 },
40 { 57600, B57600 },
41 { 115200, B115200 }
42 #if !defined(__sparc__)
43 ,{ 230400, B230400 }
44 ,{ 460800, B460800 }
45 ,{ 500000, B500000 }
46 ,{ 1000000, B1000000 }
47 ,{ 2000000, B2000000 }
48 ,{ 4000000, B4000000 }
49 #endif
50 };
51
52 #define MAX_SPEED (sizeof speeds / sizeof *speeds)
53
54 static char *init_strings[] = {
55 "Q0 V1 E1",
56 "S0=0",
57 "&C1",
58 "&D2",
59 "+FCLASS=0"
60 };
61
62 #define MAX_INIT_STRING (sizeof init_strings / sizeof *init_strings)
63
64 static void get_serial_modem(hd_data_t* hd_data);
65 static void add_serial_modem(hd_data_t* hd_data);
66 static int dev_name_duplicate(hd_data_t *hd_data, char *dev_name);
67 static void guess_modem_name(hd_data_t *hd_data, ser_device_t *sm);
68 static void at_cmd(hd_data_t *hd_data, char *at, int raw, int log_it);
69 static void write_modem(hd_data_t *hd_data, char *msg);
70 static void read_modem(hd_data_t *hd_data);
71 static ser_device_t *add_ser_modem_entry(ser_device_t **sm, ser_device_t *new_sm);
72 static int set_modem_speed(ser_device_t *sm, unsigned baud);
73 static int init_modem(ser_device_t *mi);
74 static unsigned chk4id(ser_device_t *mi);
75 static void dump_ser_modem_data(hd_data_t *hd_data);
76
77 void hd_scan_modem(hd_data_t *hd_data)
78 {
79 ser_device_t *sm, *sm_next;
80
81 if(!hd_probe_feature(hd_data, pr_modem)) return;
82
83 hd_data->module = mod_modem;
84
85 /* some clean-up */
86 remove_hd_entries(hd_data);
87 hd_data->ser_modem = NULL;
88
89 PROGRESS(1, 0, "serial");
90
91 hd_fork(hd_data, 15, 120);
92
93 if(hd_data->flags.forked) {
94 get_serial_modem(hd_data);
95 hd_move_to_shm(hd_data);
96 if((hd_data->debug & HD_DEB_MODEM)) dump_ser_modem_data(hd_data);
97 }
98 else {
99 /* take data from shm */
100 hd_data->ser_modem = ((hd_data_t *) (hd_data->shm.data))->ser_modem;
101 if((hd_data->debug & HD_DEB_MODEM)) dump_ser_modem_data(hd_data);
102 }
103
104 hd_fork_done(hd_data);
105
106 add_serial_modem(hd_data);
107
108 hd_shm_clean(hd_data);
109
110 for(sm = hd_data->ser_modem; sm; sm = sm_next) {
111 sm_next = sm->next;
112
113 free_str_list(sm->at_resp);
114
115 free_mem(sm->dev_name);
116 free_mem(sm->serial);
117 free_mem(sm->class_name);
118 free_mem(sm->dev_id);
119 free_mem(sm->user_name);
120 free_mem(sm->vend);
121 free_mem(sm->init_string1);
122 free_mem(sm->init_string2);
123
124 free_mem(sm);
125 }
126 hd_data->ser_modem = NULL;
127
128 }
129
130 int check_for_responce(str_list_t *str_list, char *str, int len)
131 {
132 for(; str_list != NULL; str_list = str_list->next) {
133 if(!strncmp(str_list->str, str, len)) return 1;
134 }
135
136 return 0;
137 }
138
139 str_list_t *str_list_dup(str_list_t *orig)
140 {
141 str_list_t *dup = NULL;
142
143 for(; orig != NULL; orig = orig->next) {
144 add_str_list(&dup, orig->str);
145 }
146
147 return dup;
148 }
149
150 void get_serial_modem(hd_data_t *hd_data)
151 {
152 hd_t *hd;
153 int i, j, fd;
154 unsigned modem_info, baud;
155 char *command;
156 ser_device_t *sm;
157 int chk_usb = hd_probe_feature(hd_data, pr_modem_usb);
158
159 /* serial modems & usb modems */
160 for(hd = hd_data->hd; hd; hd = hd->next) {
161 if(
162 (
163 (
164 hd->base_class.id == bc_comm &&
165 hd->sub_class.id == sc_com_ser &&
166 !hd->tag.ser_skip &&
167 hd->tag.ser_device != 2 && /* cf. serial.c */
168 !has_something_attached(hd_data, hd)
169 ) ||
170 (
171 chk_usb &&
172 hd->bus.id == bus_usb &&
173 hd->base_class.id == bc_modem
174 )
175 ) && hd->unix_dev_name
176 ) {
177 if(dev_name_duplicate(hd_data, hd->unix_dev_name)) continue;
178 if((fd = open(hd->unix_dev_name, O_RDWR | O_NONBLOCK)) >= 0) {
179 sm = add_ser_modem_entry(&hd_data->ser_modem, new_mem(sizeof *sm));
180 sm->dev_name = new_str(hd->unix_dev_name);
181 sm->fd = fd;
182 sm->hd_idx = hd->idx;
183 sm->do_io = 1;
184 init_modem(sm);
185 }
186 }
187 }
188
189 if(!hd_data->ser_modem) return;
190
191 PROGRESS(2, 0, "init");
192
193 usleep(300000); /* PnP protocol; 200ms seems to be too fast */
194
195 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
196 modem_info = TIOCM_DTR | TIOCM_RTS;
197 ioctl(sm->fd, TIOCMBIS, &modem_info);
198 ioctl(sm->fd, TIOCMGET, &modem_info);
199 if(!(modem_info & (TIOCM_DSR | TIOCM_CD))) {
200 sm->do_io = 0;
201 }
202 }
203
204 /* just a quick test if we get a response to an AT command */
205
206 for(i = 0; i < 4; i++) {
207 PROGRESS(3, i + 1, "at test");
208
209 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
210 if(!sm->is_modem)
211 set_modem_speed(sm, i == 0 ? 115200 : i == 1 ? 38400 : i == 2 ? 9600 : 1200);
212 }
213
214 at_cmd(hd_data, "AT\r", 1, 1);
215
216 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
217 if(strstr(sm->buf, "OK") || strstr(sm->buf, "0")) {
218 sm->is_modem = 1;
219 sm->do_io = 0;
220 }
221 sm->buf_len = 0; /* clear buffer */
222 }
223 }
224
225 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
226 if((sm->do_io = sm->is_modem)) {
227 sm->max_baud = sm->cur_baud;
228 }
229 }
230
231 /* check for init string */
232 PROGRESS(4, 0, "init string");
233
234 command = NULL;
235 for(i = 0; (unsigned) i < MAX_INIT_STRING; i++) {
236 str_printf(&command, 0, "AT %s\r", init_strings[i]);
237 at_cmd(hd_data, command, 1, 1);
238
239 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
240 if(strstr(sm->buf, "OK") || strstr(sm->buf, "0")) {
241 str_printf(&sm->init_string2, -1,
242 "%s %s", sm->init_string2 ? "" : "AT", init_strings[i]
243 );
244 }
245 }
246 }
247 command = free_mem(command);
248
249 for(sm = hd_data->ser_modem; sm; sm = sm->next)
250 if(sm->is_modem)
251 str_printf(&sm->init_string1, -1, "ATZ");
252
253 {
254 int cmds[] = { 1, 3, 4, 5, 6 };
255 char at[10];
256 int i, j, ModemsCount = 0;
257 str_list_t **responces = NULL;
258 for(sm = hd_data->ser_modem; sm; sm = sm->next)
259 if(sm->is_modem)
260 ModemsCount++;
261 responces = new_mem(ModemsCount * sizeof *responces);
262
263 at_cmd(hd_data, "ATI\r", 0, 1);
264 for(j = 0, sm = hd_data->ser_modem; sm; sm = sm->next) {
265 if(sm->is_modem)
266 responces[j++] = str_list_dup(sm->at_resp);
267 }
268
269 for(i = 0; (unsigned) i < sizeof cmds / sizeof *cmds; i++) {
270 int atx = cmds[i];
271 sprintf(at, "ATI%d\r", atx);
272 at_cmd(hd_data, at, 0, 1);
273 for(j = 0, sm = hd_data->ser_modem; sm; sm = sm->next) {
274 if(sm->is_modem) {
275 if(atx == 1 && check_for_responce(responces[j], "Hagenuk", 7) &&
276 (check_for_responce(sm->at_resp, "Speed Dragon", 12) ||
277 check_for_responce(sm->at_resp, "Power Dragon", 12))) {
278 free_mem(sm->init_string1);
279 free_mem(sm->init_string2);
280 sm->init_string1 = new_str("AT&F");
281 sm->init_string2 = new_str("ATB8");
282 }
283 if(atx == 3 && check_for_responce(responces[j], "346900", 6) &&
284 check_for_responce(sm->at_resp, "3Com U.S. Robotics ISDN", 23)) {
285 free_mem(sm->init_string1);
286 free_mem(sm->init_string2);
287 sm->init_string1 = new_str("AT&F");
288 sm->init_string2 = new_str("AT*PPP=1");
289 }
290 if(atx == 4 && check_for_responce(responces[j], "SP ISDN", 7) &&
291 check_for_responce(sm->at_resp, "Sportster ISDN TA", 17)) {
292 free_mem(sm->init_string1);
293 free_mem(sm->init_string2);
294 sm->init_string1 = new_str("AT&F");
295 sm->init_string2 = new_str("ATB3");
296 }
297 if(atx == 6 && check_for_responce(responces[j], "644", 3) &&
298 check_for_responce(sm->at_resp, "ELSA MicroLink ISDN", 19)) {
299 free_mem(sm->init_string1);
300 free_mem(sm->init_string2);
301 sm->init_string1 = new_str("AT&F");
302 sm->init_string2 = new_str("AT$IBP=HDLCP");
303 free_mem(sm->pppd_option);
304 sm->pppd_option = new_str("default-asyncmap");
305 }
306 if(atx == 6 && check_for_responce(responces[j], "643", 3) &&
307 check_for_responce(sm->at_resp, "MicroLink ISDN/TLV.34", 21)) {
308 free_mem(sm->init_string1);
309 free_mem(sm->init_string2);
310 sm->init_string1 = new_str("AT&F");
311 sm->init_string2 = new_str("AT\\N10%P1");
312 }
313 if(atx == 5 && check_for_responce(responces[j], "ISDN TA", 6) &&
314 check_for_responce(sm->at_resp, "ISDN TA;ASU", 4)) {
315 free_mem(sm->vend);
316 sm->vend = new_str("ASUS");
317 free_mem(sm->user_name);
318 sm->user_name = new_str("ISDNLink TA");
319 free_mem(sm->init_string1);
320 free_mem(sm->init_string2);
321 sm->init_string1 = new_str("AT&F");
322 sm->init_string2 = new_str("ATB40");
323 }
324 if(atx==3 && check_for_responce(responces[j], "128000", 6) &&
325 check_for_responce(sm->at_resp, "Lasat Speed", 11)) {
326 free_mem(sm->init_string1);
327 free_mem(sm->init_string2);
328 sm->init_string1 = new_str("AT&F");
329 sm->init_string2 = new_str("AT\\P1&B2X3");
330 }
331 if(atx == 1 &&
332 (check_for_responce(responces[j], "28642", 5) ||
333 check_for_responce(responces[j], "1281", 4) ||
334 check_for_responce(responces[j], "1282", 4) ||
335 check_for_responce(responces[j], "1283", 4) ||
336 check_for_responce(responces[j], "1291", 4) ||
337 check_for_responce(responces[j], "1292", 4) ||
338 check_for_responce(responces[j], "1293", 4)) &&
339 (check_for_responce(sm->at_resp, "Elite 2864I", 11) ||
340 check_for_responce(sm->at_resp, "ZyXEL omni", 10))) {
341 free_mem(sm->init_string1);
342 free_mem(sm->init_string2);
343 sm->init_string1 = new_str("AT&F");
344 sm->init_string2 = new_str("AT&O2B40");
345 }
346 j++;
347 }
348 }
349 }
350
351 for(i = 0; i < ModemsCount; i++) free_str_list(responces[i]);
352 free_mem(responces);
353 }
354
355 /* now, go for the maximum speed... */
356 PROGRESS(5, 0, "speed");
357
358 for(i = MAX_SPEED - 1; i >= 0; i--) {
359 baud = speeds[i].baud;
360 for(j = 0, sm = hd_data->ser_modem; sm; sm = sm->next) {
361 if(sm->is_modem) {
362 if(baud > sm->max_baud) {
363 sm->do_io = set_modem_speed(sm, baud) ? 0 : 1;
364 if(sm->do_io) j++;
365 }
366 }
367 }
368
369 /* no modems */
370 if(!j) continue;
371
372 at_cmd(hd_data, "AT\r", 1, 0);
373
374 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
375 if(strstr(sm->buf, "OK") || strstr(sm->buf, "0")) {
376 sm->max_baud = sm->cur_baud;
377 }
378 else {
379 sm->do_io = 0;
380 }
381 sm->buf_len = 0; /* clear buffer */
382 }
383 }
384
385 /* now, fix it all up... */
386 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
387 if(sm->is_modem) {
388 set_modem_speed(sm, sm->max_baud);
389 sm->do_io = 1;
390 }
391 }
392
393 #if 0
394 /* just for testing */
395 if((hd_data->debug & HD_DEB_MODEM)) {
396 int i;
397 int cmds[] = { 0, 1, 2, 3, 6 };
398 char at[10];
399
400 PROGRESS(8, 0, "testing");
401
402 at_cmd(hd_data, "ATI\r", 0, 1);
403 for(i = 0; (unsigned) i < sizeof cmds / sizeof *cmds; i++) {
404 sprintf(at, "ATI%d\r", cmds[i]);
405 at_cmd(hd_data, at, 0, 1);
406 }
407 at_cmd(hd_data, "AT\r", 0, 1);
408 }
409 #endif
410
411 PROGRESS(5, 0, "pnp id");
412
413 at_cmd(hd_data, "ATI9\r", 1, 1);
414
415 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
416 if(sm->is_modem) {
417 chk4id(sm);
418
419 if(!sm->user_name) guess_modem_name(hd_data, sm);
420 }
421
422 /* reset serial lines */
423 tcflush(sm->fd, TCIOFLUSH);
424 tcsetattr(sm->fd, TCSAFLUSH, &sm->tio);
425 close(sm->fd);
426 }
427 }
428
429
430 void add_serial_modem(hd_data_t *hd_data)
431 {
432 hd_t *hd;
433 char buf[4];
434 ser_device_t *sm;
435 hd_res_t *res;
436
437 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
438 if(!sm->is_modem) continue;
439
440 hd = hd_get_device_by_idx(hd_data, sm->hd_idx);
441 if(hd && hd->base_class.id == bc_modem) {
442 /* just *add* info */
443
444 }
445 else {
446 hd = add_hd_entry(hd_data, __LINE__, 0);
447 hd->base_class.id = bc_modem;
448 hd->bus.id = bus_serial;
449 hd->unix_dev_name = new_str(sm->dev_name);
450 hd->attached_to = sm->hd_idx;
451 res = add_res_entry(&hd->res, new_mem(sizeof *res));
452 res->baud.type = res_baud;
453 res->baud.speed = sm->max_baud;
454 if(sm->pppd_option) {
455 res = add_res_entry(&hd->res, new_mem(sizeof *res));
456 res->pppd_option.type = res_pppd_option;
457 res->pppd_option.option = new_str(sm->pppd_option);
458 }
459 if(*sm->pnp_id) {
460 strncpy(buf, sm->pnp_id, 3);
461 buf[3] = 0;
462 hd->vendor.id = name2eisa_id(buf);
463 hd->device.id = MAKE_ID(TAG_EISA, strtol(sm->pnp_id + 3, NULL, 16));
464 }
465 hd->serial = new_str(sm->serial);
466 if(sm->user_name) hd->device.name = new_str(sm->user_name);
467 if(sm->vend) hd->vendor.name = new_str(sm->vend);
468
469 if(sm->dev_id && strlen(sm->dev_id) >= 7) {
470 char buf[5], *s;
471 unsigned u1, u2;
472
473 u1 = name2eisa_id(sm->dev_id);
474 if(u1) {
475 strncpy(buf, sm->dev_id + 3, 4);
476 buf[4] = 0;
477 u2 = strtol(sm->dev_id + 3, &s, 16);
478 if(!*s) {
479 hd->compat_vendor.id = u1;
480 hd->compat_device.id = MAKE_ID(TAG_EISA, u2);
481 }
482 }
483 }
484
485 if(!(hd->device.id || hd->device.name || hd->vendor.id || hd->vendor.name)) {
486 hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x2000);
487 hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
488 }
489 }
490 res = add_res_entry(&hd->res, new_mem(sizeof *res));
491 res->init_strings.type = res_init_strings;
492 res->init_strings.init1 = new_str(sm->init_string1);
493 res->init_strings.init2 = new_str(sm->init_string2);
494 }
495 }
496
497
498 int dev_name_duplicate(hd_data_t *hd_data, char *dev_name)
499 {
500 ser_device_t *sm;
501
502 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
503 if(!strcmp(sm->dev_name, dev_name)) return 1;
504 }
505
506 return 0;
507 }
508
509 void guess_modem_name(hd_data_t *hd_data, ser_device_t *modem)
510 {
511 ser_device_t *sm;
512 str_list_t *sl;
513 char *s;
514 #ifdef __PPC__
515 char *s1, *s2;
516 unsigned u;
517 #endif
518
519 for(sm = hd_data->ser_modem; sm; sm = sm->next) sm->do_io = 0;
520
521 (sm = modem)->do_io = 1;
522
523 #ifdef __PPC__
524 at_cmd(hd_data, "ATI0\r", 0, 1);
525 sl = sm->at_resp;
526 if(sl && !strcmp(sl->str, "ATI0")) sl = sl->next; /* skip AT cmd echo */
527
528 s1 = NULL;
529 if(sl) {
530 if(strstr(sl->str, "PowerBook")) {
531 sm->vend = new_str("Apple");
532 sm->user_name = new_str(sl->str);
533
534 return;
535 }
536 s1 = new_str(sl->str);
537 }
538
539 at_cmd(hd_data, "ATI1\r", 0, 1);
540 sl = sm->at_resp;
541 if(sl && !strcmp(sl->str, "ATI1")) sl = sl->next; /* skip AT cmd echo */
542
543 if(sl) {
544 if(strstr(sl->str, "APPLE")) {
545 sm->vend = new_str("Apple");
546 str_printf(&sm->user_name, 0, "AT Modem");
547 if(s1) {
548 u = strtoul(s1, &s2, 10);
549 if(u && !*s2 && !(u % 1000)) {
550 str_printf(&sm->user_name, 0, "%uk AT Modem", u / 1000);
551 }
552 }
553 s1 = free_mem(s1);
554
555 return;
556 }
557 }
558 s1 = free_mem(s1);
559
560 #endif
561
562 /* ATI3 command */
563 at_cmd(hd_data, "ATI3\r", 0, 1);
564 sl = sm->at_resp;
565 if(sl && !strcmp(sl->str, "ATI3")) sl = sl->next; /* skip AT cmd echo */
566
567 if(sl) {
568 if(*sl->str == 'U' && strstr(sl->str, "Robotics ")) {
569 /* looks like an U.S. Robotics... */
570
571 sm->vend = new_str("U.S. Robotics, Inc.");
572 /* strip revision code */
573 if((s = strstr(sl->str, " Rev. "))) *s = 0;
574 sm->user_name = canon_str(sl->str, strlen(sl->str));
575
576 return;
577 }
578
579 if(strstr(sl->str, "3Com U.S. Robotics ") == sl->str) {
580 /* looks like an 3Com U.S. Robotics... */
581
582 sm->vend = new_str("3Com U.S. Robotics, Inc.");
583 sm->user_name = canon_str(sl->str, strlen(sl->str));
584
585 return;
586 }
587
588 if(strstr(sl->str, "-V34_DS -d Z201 2836")) {
589 /* looks like a Zoom V34X */
590
591 sm->vend = new_str("Zoom Telephonics, Inc.");
592 sm->user_name = new_str("Zoom FaxModem V.34X Plus Model 2836");
593
594 return;
595 }
596
597 if(strstr(sl->str, "FM560 VER 3.01 V.90")) {
598 /* looks like a Microcom DeskPorte 56K Voice ... */
599
600 sm->vend = new_str("Microcom");
601 sm->user_name = new_str("TravelCard 56K");
602
603 return;
604 }
605
606 if(strstr(sl->str, "Compaq Microcom 550 56K Modem")) {
607 /* looks like a Microcom DeskPorte Pocket ... */
608
609 sm->vend = new_str("Compaq");
610 sm->user_name = new_str("Microcom 550 56K Modem");
611
612 return;
613 }
614 }
615
616 /* ATI0 command */
617 at_cmd(hd_data, "ATI0\r", 0, 1);
618 sl = sm->at_resp;
619 if(sl && !strcmp(sl->str, "ATI0")) sl = sl->next; /* skip AT cmd echo */
620
621 if(sl) {
622 if(strstr(sl->str, "DP Pocket")) {
623 /* looks like a Microcom DeskPorte Pocket ... */
624
625 sm->vend = new_str("Microcom");
626 sm->user_name = new_str("DeskPorte Pocket");
627
628 return;
629 }
630 }
631
632 /* ATI6 command */
633 at_cmd(hd_data, "ATI6\r", 0, 1);
634 sl = sm->at_resp;
635 if(sl && !strcmp(sl->str, "ATI6")) sl = sl->next; /* skip AT cmd echo */
636
637 if(sl) {
638 if(strstr(sl->str, "RCV56DPF-PLL L8571A")) {
639 /* looks like a Microcom DeskPorte 56K Voice ... */
640
641 sm->vend = new_str("Microcom");
642 sm->user_name = new_str("DeskPorte 56K Voice");
643
644 return;
645 }
646 }
647
648 /* ATI2 command */
649 at_cmd(hd_data, "ATI2\r", 0, 1);
650 sl = sm->at_resp;
651 if(sl && !strcmp(sl->str, "ATI2")) sl = sl->next; /* skip AT cmd echo */
652
653 if(sl) {
654 if(strstr(sl->str, "ZyXEL ")) {
655 /* looks like a ZyXEL... */
656
657 sm->vend = new_str("ZyXEL");
658
659 at_cmd(hd_data, "ATI1\r", 0, 1);
660 sl = sm->at_resp;
661 if(sl && !strcmp(sl->str, "ATI1")) sl = sl->next;
662
663 if(sl && sl->next) {
664 sl = sl->next;
665 if((s = strstr(sl->str, " V "))) *s = 0;
666 sm->user_name = canon_str(sl->str, strlen(sl->str));
667 }
668
669 return;
670 }
671 }
672
673 }
674
675 void at_cmd(hd_data_t *hd_data, char *at, int raw, int log_it)
676 {
677 static unsigned u = 1;
678 char *s, *s0;
679 ser_device_t *sm;
680 str_list_t *sl;
681 int modems = 0;
682
683 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
684 if(sm->do_io) {
685 sm->buf_len = 0;
686 modems++;
687 }
688 }
689
690 if(modems == 0) return;
691
692 PROGRESS(9, u, "write at cmd");
693 write_modem(hd_data, at);
694 PROGRESS(9, u, "read at resp");
695 usleep (200000);
696 read_modem(hd_data);
697 PROGRESS(9, u, "read ok");
698 u++;
699
700 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
701 if(sm->do_io) {
702 sm->at_resp = free_str_list(sm->at_resp);
703 if(sm->buf_len == 0 || raw) continue;
704 s0 = sm->buf;
705 while((s = strsep(&s0, "\r\n"))) {
706 if(*s) add_str_list(&sm->at_resp, s);
707 }
708 }
709 }
710
711 if(!(hd_data->debug & HD_DEB_MODEM) || !log_it) return;
712
713 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
714 if(sm->do_io) {
715 ADD2LOG("%s@%u: %s\n", sm->dev_name, sm->cur_baud, at);
716 if(raw) {
717 ADD2LOG(" ");
718 hexdump(&hd_data->log, 1, sm->buf_len, sm->buf);
719 ADD2LOG("\n");
720 }
721 else {
722 for(sl = sm->at_resp; sl; sl = sl->next) ADD2LOG(" %s\n", sl->str);
723 }
724 }
725 }
726 }
727
728
729 void write_modem(hd_data_t *hd_data, char *msg)
730 {
731 ser_device_t *sm;
732 int i, len = strlen(msg);
733
734 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
735 if(sm->do_io) {
736 i = write(sm->fd, msg, len);
737 if(i != len) {
738 ADD2LOG("%s write oops: %d/%d (\"%s\")\n", sm->dev_name, i, len, msg);
739 }
740 }
741 }
742 }
743
744 void read_modem(hd_data_t *hd_data)
745 {
746 int i, sel, fd_max = -1;
747 fd_set set, set0;
748 struct timeval to;
749 ser_device_t *sm;
750
751 FD_ZERO(&set0);
752
753 for(i = 0, sm = hd_data->ser_modem; sm; sm = sm->next) {
754 if(sm->do_io) {
755 FD_SET(sm->fd, &set0);
756 if(sm->fd > fd_max) fd_max = sm->fd;
757 i++;
758 }
759 }
760
761 if(!i) return; /* nothing selected */
762
763 for(;;) {
764 to.tv_sec = 0; to.tv_usec = 1000000;
765 set = set0;
766 if((sel = select(fd_max + 1, &set, NULL, NULL, &to)) > 0) {
767 // fprintf(stderr, "sel: %d\n", sel);
768 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
769 if(FD_ISSET(sm->fd, &set)) {
770 if((i = read(sm->fd, sm->buf + sm->buf_len, sizeof sm->buf - sm->buf_len)) > 0)
771 sm->buf_len += i;
772 // fprintf(stderr, "%s: got %d\n", sm->dev_name, i);
773 if(i <= 0) FD_CLR(sm->fd, &set0);
774 }
775 }
776 }
777 else {
778 break;
779 }
780 }
781
782 /* make the strings \000 terminated */
783 for(sm = hd_data->ser_modem; sm; sm = sm->next) {
784 if(sm->buf_len == sizeof sm->buf) sm->buf_len--;
785 sm->buf[sm->buf_len] = 0;
786 }
787 }
788
789 int set_modem_speed(ser_device_t *sm, unsigned baud)
790 {
791 int i;
792 speed_t st;
793 struct termios tio;
794
795 for(i = 0; (unsigned) i < MAX_SPEED; i++) if(speeds[i].baud == baud) break;
796
797 if(i == MAX_SPEED) return 1;
798
799 if(tcgetattr(sm->fd, &tio)) return errno;
800
801 cfsetospeed(&tio, speeds[i].mask);
802 cfsetispeed(&tio, speeds[i].mask);
803
804 if(tcsetattr(sm->fd, TCSAFLUSH, &tio)) return errno;
805
806 /* tcsetattr() returns ok even if it couldn't set the speed... */
807
808 if(tcgetattr(sm->fd, &tio)) return errno;
809
810 st = cfgetospeed(&tio);
811
812 for(i = 0; (unsigned) i < MAX_SPEED; i++) if(speeds[i].mask == st) break;
813
814 if(i == MAX_SPEED) return 2;
815
816 sm->cur_baud = speeds[i].baud;
817
818 return baud == speeds[i].baud ? 0 : 3;
819 }
820
821
822 int init_modem(ser_device_t *sm)
823 {
824 struct termios tio;
825
826 if(tcgetattr(sm->fd, &tio)) return errno;
827
828 sm->tio = tio;
829
830 tio.c_iflag = IGNBRK | IGNPAR;
831 tio.c_oflag = 0;
832 tio.c_lflag = 0;
833 tio.c_line = 0;
834 tio.c_cc[VTIME] = 0;
835 tio.c_cc[VMIN] = 1;
836
837 tio.c_cflag = CREAD | CLOCAL | HUPCL | B1200 | CS8;
838
839 if(tcsetattr(sm->fd, TCSAFLUSH, &tio)) return errno;
840
841 return 0;
842 }
843
844
845 /*
846 * Check for a PnP info field starting at ofs;
847 * returns either the length of the field or 0 if none was found.
848 *
849 * the minfo_t struct is updated with the PnP data
850 */
851 int is_pnpinfo(ser_device_t *mi, int ofs)
852 {
853 int i, j, k, l;
854 unsigned char c, *s = mi->buf + ofs, *t;
855 int len = mi->buf_len - ofs;
856 unsigned serial, class_name, dev_id, user_name;
857
858 if(len <= 0) return 0;
859
860 switch(*s) {
861 case 0x08:
862 mi->bits = 6; break;
863 case 0x28:
864 mi->bits = 7; break;
865 default:
866 return 0;
867 }
868
869 if(len < 11) return 0;
870
871 i = 1;
872
873 /* six bit values */
874 if((s[i] & ~0x3f) || (s[i + 1] & ~0x3f)) return 0;
875 mi->pnp_rev = (s[i] << 6) + s[i + 1];
876
877 /* pnp_rev may *optionally* be given as a string!!! (e.g. "1.0")*/
878 if(mi->bits == 7) {
879 j = 0;
880 if(s[i + 2] < 'A') {
881 j++;
882 if(s[i + 3] < 'A') j++;
883 }
884 if(j) {
885 if(s[i] < '0' || s[i] > '9') return 0;
886 if(s[i + 1] != '.') return 0;
887 for(k = 0; k < j; k++)
888 if(s[i + 2 + k] < '0' || s[i + 2 + k] > '9') return 0;
889 mi->pnp_rev = (s[i] - '0') * 100;
890 mi->pnp_rev += s[i + 2] * 10;
891 if(j == 2) mi->pnp_rev += s[i + 3];
892 i += j;
893 }
894 }
895
896 i += 2;
897
898 /* the eisa id */
899 for(j = 0; j < 7; j++) {
900 mi->pnp_id[j] = s[i + j];
901 if(mi->bits == 6) mi->pnp_id[j] += 0x20;
902 }
903 mi->pnp_id[7] = 0;
904
905 i += 7;
906
907 /* now check the id */
908 for(j = 0; j < 3; j++) {
909 if(
910 /* numbers are not really allowed, but... */
911 (mi->pnp_id[j] < '0' || mi->pnp_id[j] > '9') &&
912 (mi->pnp_id[j] < 'A' || mi->pnp_id[j] > 'Z') &&
913 mi->pnp_id[j] != '_'
914 ) return 0;
915 }
916
917 for(j = 3; j < 7; j++) {
918 if(
919 (mi->pnp_id[j] < '0' || mi->pnp_id[j] > '9') &&
920 (mi->pnp_id[j] < 'A' || mi->pnp_id[j] > 'F')
921 ) return 0;
922 }
923
924
925 if((mi->bits == 6 && s[i] == 0x09) || (mi->bits == 7 && s[i] == 0x29)) {
926 return i + 1;
927 }
928
929 if((mi->bits != 6 || s[i] != 0x3c) && (mi->bits != 7 || s[i] != 0x5c)) {
930 return 0;
931 }
932
933 /* parse extended info */
934 serial = class_name = dev_id = user_name = 0;
935 for(j = 0; i < len; i++) {
936 if((mi->bits == 6 && s[i] == 0x09) || (mi->bits == 7 && s[i] == 0x29)) {
937
938 if(serial) for(k = serial; k < len; k++) {
939 c = s[k];
940 if(mi->bits == 6) c += 0x20;
941 if(c == '\\') break;
942 str_printf(&mi->serial, -1, "%c", c);
943 }
944
945 if(class_name) for(k = class_name; k < len; k++) {
946 c = s[k];
947 if(mi->bits == 6) c += 0x20;
948 if(c == '\\') break;
949 str_printf(&mi->class_name, -1, "%c", c);
950 }
951
952 if(dev_id) for(k = dev_id; k < len; k++) {
953 c = s[k];
954 if(mi->bits == 6) c += 0x20;
955 if(c == '\\') break;
956 str_printf(&mi->dev_id, -1, "%c", c);
957 }
958
959 if(user_name) {
960 for(k = user_name; k < len; k++) {
961 c = s[k];
962 if(mi->bits == 6) c += 0x20;
963 if(c == '\\' || c == ')') break;
964 str_printf(&mi->user_name, -1, "%c", c);
965 }
966 if(mi->user_name && (l = strlen(mi->user_name)) >= 2) {
967 /* skip *optional*(!!!) 2 char checksum */
968 t = mi->user_name + l - 2;
969 if(
970 ((t[0] >= '0' && t[0] <= '9') || (t[0] >= 'A' && t[0] <= 'F')) &&
971 ((t[1] >= '0' && t[1] <= '9') || (t[1] >= 'A' && t[1] <= 'F'))
972 ) {
973 /* OK, *might* be a hex number... */
974 mi->user_name[l - 2] = 0;
975
976 /*
977 * A better check would be to look for the complete name string
978 * in the output from another AT command, e.g AT3, AT6 or AT11.
979 * If it's there -> no checksum field.
980 */
981 }
982 }
983 }
984
985 return i + 1;
986 }
987
988 if(((mi->bits == 6 && s[i] == 0x3c) || (mi->bits == 7 && s[i] == 0x5c)) && i < len - 1) {
989 switch(j) {
990 case 0:
991 serial = i + 1; j++; break;
992 case 1:
993 class_name = i + 1; j++; break;
994 case 2:
995 dev_id = i + 1; j++; break;
996 case 3:
997 user_name = i + 1; j++; break;
998 default:
999 fprintf(stderr, "PnP-ID oops\n");
1000 }
1001 }
1002 }
1003
1004 /* no end token... */
1005
1006 return 0;
1007 }
1008
1009
1010 unsigned chk4id(ser_device_t *mi)
1011 {
1012 int i;
1013
1014 if(!mi->buf_len) return 0;
1015
1016 for(i = 0; i < mi->buf_len; i++) {
1017 if((mi->pnp = is_pnpinfo(mi, i))) break;
1018 }
1019 if(i == mi->buf_len) return 0;
1020
1021 mi->garbage = i;
1022
1023 return 1;
1024 }
1025
1026 ser_device_t *add_ser_modem_entry(ser_device_t **sm, ser_device_t *new_sm)
1027 {
1028 while(*sm) sm = &(*sm)->next;
1029 return *sm = new_sm;
1030 }
1031
1032 void dump_ser_modem_data(hd_data_t *hd_data)
1033 {
1034 int j;
1035 ser_device_t *sm;
1036
1037 if(!(sm = hd_data->ser_modem)) return;
1038
1039 ADD2LOG("----- serial modems -----\n");
1040
1041 for(; sm; sm = sm->next) {
1042 ADD2LOG("%s\n", sm->dev_name);
1043 if(sm->serial) ADD2LOG("serial: \"%s\"\n", sm->serial);
1044 if(sm->class_name) ADD2LOG("class_name: \"%s\"\n", sm->class_name);
1045 if(sm->dev_id) ADD2LOG("dev_id: \"%s\"\n", sm->dev_id);
1046 if(sm->user_name) ADD2LOG("user_name: \"%s\"\n", sm->user_name);
1047
1048 if(sm->garbage) {
1049 ADD2LOG(" pre_garbage[%u]: ", sm->garbage);
1050 hexdump(&hd_data->log, 1, sm->garbage, sm->buf);
1051 ADD2LOG("\n");
1052 }
1053
1054 if(sm->pnp) {
1055 ADD2LOG(" pnp[%u]: ", sm->pnp);
1056 hexdump(&hd_data->log, 1, sm->pnp, sm->buf + sm->garbage);
1057 ADD2LOG("\n");
1058 }
1059
1060 if((j = sm->buf_len - (sm->garbage + sm->pnp))) {
1061 ADD2LOG(" post_garbage[%u]: ", j);
1062 hexdump(&hd_data->log, 1, j, sm->buf + sm->garbage + sm->pnp);
1063 ADD2LOG("\n");
1064 }
1065
1066 if(sm->is_modem)
1067 ADD2LOG(" is modem\n");
1068 else
1069 ADD2LOG(" not a modem\n");
1070
1071 if(sm->pnp) {
1072 ADD2LOG(" bits: %u\n", sm->bits);
1073 ADD2LOG(" PnP Rev: %u.%02u\n", sm->pnp_rev / 100, sm->pnp_rev % 100);
1074 ADD2LOG(" PnP ID: \"%s\"\n", sm->pnp_id);
1075 }
1076
1077 if(sm->next) ADD2LOG("\n");
1078 }
1079
1080 ADD2LOG("----- serial modems end -----\n");
1081 }
1082
1083 #endif /* ifndef LIBHD_TINY */