~ubuntu-branches/ubuntu/edgy/hwinfo/edgy

« back to all changes in this revision

Viewing changes to src/hd/modem.c

  • Committer: Bazaar Package Importer
  • Author(s): James Vega
  • Date: 2006-09-28 20:56:06 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20060928205606-bgxl69hts04xbx51
Tags: 13.4-1
* New upstream version.
* Switch from dbs to quilt
  - Revamp debian/rules
  - Add quilt and remove dbs from Build-Depends in debian/control
* Remove reference to hwscan(8) from manpage. (closes: #388245)
* Re-wrote manpage from scratch.  Drop docbook-to-man from Build-Depends.
* Remove NEWS.Debian since it is no longer applicable.

Show diffs side-by-side

added added

removed removed

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