]>
Commit | Line | Data |
---|---|---|
a6316ce4 MT |
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 */ |