This commit was manufactured by cvs2svn to create tag
[opensuse:hwinfo.git] / src / hd / mouse.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 "mouse.h"
16
17 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18  * mouse info
19  *
20  * TODO: reset serial lines to old values (cf. modem.c)
21  *
22  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
23  */
24
25 #ifndef LIBHD_TINY
26
27 static unsigned read_data(hd_data_t *hd_data, int fd, unsigned char *buf, unsigned buf_size);
28 static void get_ps2_mouse(hd_data_t *hd_data);
29 static void test_ps2_open(void *arg);
30 #if 0
31 static void test_serial_open(void *arg);
32 #endif
33
34 static void get_serial_mouse(hd_data_t* hd_data);
35 static int _setspeed(int fd, int old, int new, int needtowrite, unsigned short flags);
36 static void setspeed(int fd, int new, int needtowrite, unsigned short flags);
37 static unsigned chk4id(ser_device_t *mi);
38 static ser_device_t *add_ser_mouse_entry(ser_device_t **sm, ser_device_t *new_sm);
39 static void dump_ser_mouse_data(hd_data_t *hd_data);
40 static void get_sunmouse(hd_data_t *hd_data);
41
42 void hd_scan_mouse(hd_data_t *hd_data)
43 {
44   ser_device_t *sm, *sm_next;
45
46   if(!hd_probe_feature(hd_data, pr_mouse)) return;
47
48   hd_data->module = mod_mouse;
49
50   /* some clean-up */
51   remove_hd_entries(hd_data);
52   hd_data->ser_mouse = NULL;
53
54   PROGRESS(1, 0, "ps/2");
55
56   get_ps2_mouse(hd_data);
57
58   PROGRESS(2, 0, "serial");
59
60   get_serial_mouse(hd_data);
61   if((hd_data->debug & HD_DEB_MOUSE)) dump_ser_mouse_data(hd_data);
62
63   for(sm = hd_data->ser_mouse; sm; sm = sm_next) {
64     sm_next = sm->next;
65
66     free_mem(sm->dev_name);
67     free_mem(sm);
68   }
69   hd_data->ser_mouse = NULL;
70
71   PROGRESS(3, 0, "sunmouse");
72
73   get_sunmouse(hd_data);
74 }
75
76
77 unsigned read_data(hd_data_t *hd_data, int fd, unsigned char *buf, unsigned buf_size)
78 {
79   int k, len = 0;
80   unsigned char *bp;
81
82   while(
83     len < buf_size &&
84     (k = read(fd, buf + len, buf_size - len)) >= 0
85   ) len += k;
86
87   bp = buf;
88   if(len && (*bp == 0xfe || *bp == 0xfa)) { bp++; len--; }
89
90   for(k = 0; k < len; k++) buf[k] = bp[k];
91
92   if((hd_data->debug & HD_DEB_MOUSE)) {
93     ADD2LOG("ps/2[%d]: ", len);
94     hexdump(&hd_data->log, 1, len, buf);
95     ADD2LOG("\n");
96   }
97
98   return len;
99 }
100
101
102 /*
103  * How it works:
104  *
105  * 1. There must exist a PS/2 controller entry (-> there is a PS/2 port).
106  * 2. If there are PS/2 mouse irq events, assume a PS/2 mouse is attached.
107  * 3. Otherwise:
108  *      - open /dev/psaux
109  *      - write the "get mouse info" command (0xe9)
110  *      - read back the response, which should be either 0xfe "resend data"
111  *        or, e.g. (0xfa) 0x20 0x02 0x3c (0xfa = "ACK" (should be swallowed
112  *        by the psaux driver, but isn't), the rest are settings)
113  *      - ignore the first byte if it is 0xfa or 0xfe
114  *      - if there are at least 2 bytes left, assume a mouse is attached.
115  *
116  * Note1: we could use the command 0xfe "get mouse ID" instead. But that turned
117  *        out to be less reliable, as this command returns only one byte, which
118  *        is even 0.
119  * Note2: step 2 is mainly relevant if the mouse is already in use. In that
120  *        case we would have problems reading back the respose of our command.
121  *        (Typically the mouse driver will get it (and choke on it).)
122  */
123
124 static void get_ps2_mouse(hd_data_t *hd_data)
125 {
126   hd_t *hd, *hd1;
127   hd_res_t *res;
128   int fd;
129   fd_set set;
130   struct timeval tv;
131   unsigned char cmd_mouse_info = 0xe9;  /* read mouse info (3 bytes) */
132   unsigned char cmd_mouse_id = 0xf2;    /* read mouse id (1 byte) */
133   unsigned char buf[100];
134   unsigned mouse_id = -1;
135   static unsigned char intelli_init[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
136   int buf_len = 0;
137 #ifdef __PPC__
138   int always_ps2_mouse = 0;
139 #endif
140
141   for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
142     /* look for a PS/2 controller entry... */
143     if(hd1->base_class.id == bc_ps2) {
144       /* ...and see if there were irq events... */
145       for(res = hd1->res; res; res = res->next) {
146         if(res->irq.type == res_irq && res->irq.triggered) break;
147       }
148
149 #ifdef __PPC__
150       /*
151        * On PReP & CHRP, assume a PS/2 mouse to be attached.
152        * There seems to be no way to actually *detect* it.
153        */
154       if(!res) {
155         hd_t *hd;
156         sys_info_t *st;
157
158         if((hd = hd_list(hd_data, hw_sys, 0, NULL))) {
159           if(
160             hd->detail &&
161             hd->detail->type == hd_detail_sys &&
162             (st = hd->detail->sys.data) &&
163             (
164               !strcmp(st->system_type, "PReP") ||
165               strstr(st->system_type, "CHRP") == st->system_type        /* CHRP && CHRP64 */
166             )
167           ) {
168             always_ps2_mouse = 1;
169           }
170         }
171       }
172 #endif
173
174       PROGRESS(1, 1, "ps/2");
175
176       /* open the mouse device... */
177       if(hd_timeout(test_ps2_open, NULL, 2) > 0) {
178         ADD2LOG("ps/2: open(%s) timed out\n", DEV_PSAUX);
179         fd = -2;
180       }
181       else {
182         fd = open(DEV_PSAUX, O_RDWR | O_NONBLOCK);
183       }
184
185       PROGRESS(1, 2, "ps/2");
186
187       if(fd >= 0) {
188         /* ...write the id command... */
189
190         PROGRESS(1, 3, "ps/2");
191
192         write(fd, intelli_init, sizeof intelli_init);
193         usleep(25000);
194         read_data(hd_data, fd, buf, sizeof buf);
195
196         if(write(fd, &cmd_mouse_id, 1) == 1) {
197
198           PROGRESS(1, 4, "ps/2");
199           usleep(50000);        /* ...give it a chance to react... */
200
201           /* ...read the response... */
202           buf_len = read_data(hd_data, fd, buf, sizeof buf);
203
204           if(buf_len >= 1) mouse_id = buf[buf_len - 1];
205
206           /* assume the user just can't keep his hands still... */
207           if(buf_len >= 5) mouse_id = buf[0];
208
209           // if we didn't get any response, try this
210           if(buf_len == 0 || (hd_data->debug & HD_DEB_MOUSE)) {
211             PROGRESS(1, 5, "ps/2");
212             if(write(fd, &cmd_mouse_info, 1) == 1) {
213               usleep(50000);
214               buf_len = read_data(hd_data, fd, buf, sizeof buf);
215               /*
216                * Assume a mouse to be attached if at least 2 bytes are
217                * returned.
218                */
219               if(mouse_id == -1 && buf_len >= 2) mouse_id = 0;
220             }
221           }
222
223           PROGRESS(1, 6, "ps/2");
224         }
225         close(fd);
226
227         PROGRESS(1, 7, "ps/2");
228
229         /*
230          * The following code is apparently necessary on some board/mouse
231          * combinations. Otherwise the PS/2 mouse won't work.
232          */
233         if((fd = open(DEV_PSAUX, O_RDONLY | O_NONBLOCK)) >= 0) {
234           PROGRESS(1, 8, "ps/2");
235
236           FD_ZERO(&set);
237           FD_SET(fd, &set);
238           tv.tv_sec = 0; tv.tv_usec = 1;
239           if(select(fd + 1, &set, NULL, NULL, &tv) == 1) {
240             PROGRESS(1, 9, "ps/2");
241
242             read(fd, buf, sizeof buf);
243
244             PROGRESS(1, 10, "ps/2");
245           }
246           PROGRESS(1, 11, "ps/2");
247
248           close(fd);
249
250           PROGRESS(1, 12, "ps/2");
251         }
252       }
253       else {
254         ADD2LOG("open(" DEV_PSAUX "): %s\n", fd == -1 ? strerror(errno) : "timeout");
255       }
256
257       if(mouse_id == -1) {
258
259         /*
260          * Assume a PS/2 mouse is attached if the ps/2 controller has
261          * genetrated some events.
262          */
263
264         if(
265           res
266 #ifdef __PPC__
267           || always_ps2_mouse
268 #endif
269         ) {
270           PROGRESS(1, 13, "ps/2");
271           mouse_id = 0;
272         }
273       }
274
275       if(mouse_id != -1) {
276         PROGRESS(1, 14, "ps/2");
277
278         hd = add_hd_entry(hd_data, __LINE__, 0);
279         hd->base_class.id = bc_mouse;
280         hd->sub_class.id = sc_mou_ps2;
281         hd->bus.id = bus_ps2;
282         hd->unix_dev_name = new_str(DEV_PSAUX);
283         hd->attached_to = hd1->idx;
284
285         hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
286         switch(mouse_id) {
287           case 3:               /* 3 buttons + wheel */
288             hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0004);
289             break;
290
291           case 4:               /* 5 buttons + wheel */
292             hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0005);
293             break;
294
295           default:      /* 0 */
296             hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0002);
297         }
298       }
299
300       /* there can only be one... */
301       break;
302     }
303   }
304 }
305
306 void test_ps2_open(void *arg)
307 {
308   open(DEV_PSAUX, O_RDWR | O_NONBLOCK);
309 }
310
311 static void get_sunmouse(hd_data_t *hd_data)
312 {
313   hd_t *hd;
314   int fd;
315   int found;
316
317   found = 0;
318
319   /* Only search for Sun mouse if we have a Sun keyboard */
320   for(hd = hd_data->hd; hd; hd = hd->next)
321     {
322       if(hd->base_class.id == bc_keyboard &&
323          hd->sub_class.id == sc_keyboard_kbd &&
324          ID_TAG(hd->vendor.id) == TAG_SPECIAL && ID_VALUE(hd->vendor.id) == 0x0202)
325         found = 1;
326     }
327
328   if (found)
329     {
330       if ((fd = open(DEV_SUNMOUSE, O_RDONLY)) != -1)
331         {
332           /* FIXME: Should probably talk to the mouse to see
333              if the connector is not empty. */
334           close (fd);
335
336           PROGRESS(1, 1, "Sun Mouse");
337
338           hd = add_hd_entry (hd_data, __LINE__, 0);
339           hd->base_class.id = bc_mouse;
340           hd->sub_class.id = sc_mou_sun;
341           hd->bus.id = bus_serial;
342           hd->unix_dev_name = new_str(DEV_SUNMOUSE);
343
344           hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0202);
345           hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0000);
346         }
347     }
348 }
349
350 #if 0
351 void test_serial_open(void *arg)
352 {
353   open((char *) arg, O_RDWR | O_NONBLOCK);
354 }
355 #endif
356
357 void get_serial_mouse(hd_data_t *hd_data)
358 {
359   hd_t *hd;
360   int j, fd, fd_max = 0, sel, max_len;
361   unsigned modem_info;
362   fd_set set, set0;
363   struct timeval to;
364   char buf[4];
365   ser_device_t *sm;
366   struct termios tio;
367
368   FD_ZERO(&set);
369
370   for(hd = hd_data->hd; hd; hd = hd->next) {
371     if(
372       hd->base_class.id == bc_comm &&
373       hd->sub_class.id == sc_com_ser &&
374       hd->unix_dev_name &&
375       !hd->tag.ser_skip &&
376       !has_something_attached(hd_data, hd)
377     ) {
378 #if 0
379       if(hd_timeout(test_serial_open, hd->unix_dev_name, 3) > 0) {
380         ADD2LOG("serial: open(%s) timed out\n", hd->unix_dev_name);
381       }
382       else
383 #endif
384       if((fd = open(hd->unix_dev_name, O_RDWR | O_NONBLOCK)) >= 0) {
385         if(tcgetattr(fd, &tio)) continue;
386         sm = add_ser_mouse_entry(&hd_data->ser_mouse, new_mem(sizeof *sm));
387         sm->dev_name = new_str(hd->unix_dev_name);
388         sm->fd = fd;
389         sm->tio = tio;
390         sm->hd_idx = hd->idx;
391         if(fd > fd_max) fd_max = fd;
392         FD_SET(fd, &set);
393
394         /*
395          * PnP COM spec black magic...
396          */
397         setspeed(fd, 1200, 1, CS7);
398         modem_info = TIOCM_DTR | TIOCM_RTS;
399         ioctl(fd, TIOCMBIC, &modem_info);
400       }
401     }
402   }
403
404   if(!hd_data->ser_mouse) return;
405
406   /*
407    * 200 ms seems to be too fast for some mice...
408    */
409   usleep(300000);               /* PnP protocol */
410
411   for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
412     modem_info = TIOCM_DTR | TIOCM_RTS;
413     ioctl(sm->fd, TIOCMBIS, &modem_info);
414   }
415
416   /* smaller buffer size, otherwise we might wait really long... */
417   max_len = sizeof sm->buf < 128 ? sizeof sm->buf : 128;
418
419   to.tv_sec = 0; to.tv_usec = 300000;
420
421   set0 = set;
422   for(;;) {
423    to.tv_sec = 0; to.tv_usec = 300000;
424     set = set0;
425     if((sel = select(fd_max + 1, &set, NULL, NULL, &to)) > 0) {
426       for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
427         if(FD_ISSET(sm->fd, &set)) {
428           if((j = read(sm->fd, sm->buf + sm->buf_len, max_len - sm->buf_len)) > 0)
429             sm->buf_len += j;
430           if(j <= 0) FD_CLR(sm->fd, &set0);     // #####
431         }
432       }
433     }
434     else {
435       break;
436     }
437   }
438
439   for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
440     chk4id(sm);
441     /* reset serial lines */
442     tcflush(sm->fd, TCIOFLUSH);
443     tcsetattr(sm->fd, TCSAFLUSH, &sm->tio);
444     close(sm->fd);
445
446     if(sm->is_mouse) {
447       hd = add_hd_entry(hd_data, __LINE__, 0);
448       hd->base_class.id = bc_mouse;
449       hd->sub_class.id = sc_mou_ser;
450       hd->bus.id = bus_serial;
451       hd->unix_dev_name = new_str(sm->dev_name);
452       hd->attached_to = sm->hd_idx;
453       if(*sm->pnp_id) {
454         strncpy(buf, sm->pnp_id, 3);
455         buf[3] = 0;
456         hd->vendor.id = name2eisa_id(buf);
457         hd->device.id = MAKE_ID(TAG_EISA, strtol(sm->pnp_id + 3, NULL, 16));
458
459         hd->serial = new_str(sm->serial);
460         if(sm->user_name) hd->device.name = new_str(sm->user_name);
461         if(sm->vend) hd->vendor.name = new_str(sm->vend);
462
463         if(sm->dev_id && strlen(sm->dev_id) >= 7) {
464           char buf[5], *s;
465           unsigned u1, u2;
466
467           u1 = name2eisa_id(sm->dev_id);
468           if(u1) {
469             strncpy(buf, sm->dev_id + 3, 4);
470             buf[4] = 0;
471             u2 = strtol(sm->dev_id + 3, &s, 16);
472             if(!*s) {
473               hd->compat_vendor.id = u1;
474               hd->compat_device.id = MAKE_ID(TAG_EISA, u2);
475             }
476           }
477         }
478       }
479       else {
480         hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
481         hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0003);
482       }
483     }
484   }
485 }
486
487
488 /*
489  * Baud setting magic taken from gpm.
490  */
491
492 int _setspeed(int fd, int old, int new, int needtowrite, unsigned short flags)
493 {
494   struct termios tty;
495   char *c;
496   int err = 0;
497
498   flags |= CREAD | CLOCAL | HUPCL;
499
500   if(tcgetattr(fd, &tty)) return errno;
501
502   tty.c_iflag = IGNBRK | IGNPAR;
503   tty.c_oflag = 0;
504   tty.c_lflag = 0;
505   tty.c_line = 0;
506   tty.c_cc[VTIME] = 0;
507   tty.c_cc[VMIN] = 1;
508
509   switch (old)
510     {
511     case 9600:  tty.c_cflag = flags | B9600; break;
512     case 4800:  tty.c_cflag = flags | B4800; break;
513     case 2400:  tty.c_cflag = flags | B2400; break;
514     case 1200:
515     default:    tty.c_cflag = flags | B1200; break;
516     }
517
518   if(tcsetattr(fd, TCSAFLUSH, &tty)) return errno;
519
520   switch (new)
521     {
522     case 9600:  c = "*q";  tty.c_cflag = flags | B9600; break;
523     case 4800:  c = "*p";  tty.c_cflag = flags | B4800; break;
524     case 2400:  c = "*o";  tty.c_cflag = flags | B2400; break;
525     case 1200:
526     default:    c = "*n";  tty.c_cflag = flags | B1200; break;
527     }
528
529   if(needtowrite) {
530     err = 2 - write(fd, c, 2);
531   }
532
533   usleep(100000);
534
535   if(tcsetattr(fd, TCSAFLUSH, &tty)) return errno;
536
537   return err;
538 }
539
540
541 void setspeed(int fd, int new, int needtowrite, unsigned short flags)
542 {
543   int i, err;
544
545   for(i = 9600; i >= 1200; i >>= 1) {
546     err = _setspeed(fd, i, new, needtowrite, flags);
547 #if 0
548     if(err) {
549       fprintf(stderr, "%d, %d ", i, err);
550       perror("");
551     }
552 #endif
553   }
554 }
555
556
557 #if 0
558 /*
559  * Check for a PnP info field starting at ofs;
560  * returns either the length of the field or 0 if none was found.
561  *
562  * the minfo_t struct is updated with the PnP data
563  */
564 int is_pnpinfo(ser_device_t *mi, int ofs)
565 {
566   int i;
567   unsigned char *s = mi->buf + ofs;
568   int len = mi->buf_len - ofs;
569
570   if(len <= 0) return 0;
571
572   switch(*s) {
573     case 0x08:
574       mi->bits = 6; break;
575     case 0x28:
576       mi->bits = 7; break;
577     default:
578       return 0;
579   }
580
581   if(len < 11) return 0;
582
583   /* six bit values */
584   if((s[1] & ~0x3f) || (s[2] & ~0x3f)) return 0;
585   mi->pnp_rev = (s[1] << 6) + s[2];
586
587   /* the eisa id */
588   for(i = 0; i < 7; i++) {
589     mi->pnp_id[i] = s[i + 3];
590     if(mi->bits == 6) mi->pnp_id[i] += 0x20;
591   }
592   mi->pnp_id[7] = 0;
593
594   /* now check the id */
595   for(i = 0; i < 3; i++) {
596     if(
597       (mi->pnp_id[i] < 'A' || mi->pnp_id[i] > 'Z') &&
598       mi->pnp_id[i] != '_'
599     ) return 0;
600   }
601
602   for(i = 3; i < 7; i++) {
603     if(
604       (mi->pnp_id[i] < '0' || mi->pnp_id[i] > '9') &&
605       (mi->pnp_id[i] < 'A' || mi->pnp_id[i] > 'F')
606     ) return 0;
607   }
608
609   if(
610     (mi->bits == 6 && s[10] == 0x09) ||
611     (mi->bits == 7 && s[10] == 0x29)
612   ) {
613     return 11;
614   }
615
616   if(
617     (mi->bits != 6 || s[10] != 0x3c) &&
618     (mi->bits != 7 || s[10] != 0x5c)
619   ) {
620     return 0;
621   }
622
623   /* skip extended info */
624   for(i = 11; i < len; i++) {
625     if(
626       (mi->bits == 6 && s[i] == 0x09) ||
627       (mi->bits == 7 && s[i] == 0x29)
628     ) {
629       return i + 1;
630     }
631   }
632
633   /*
634    * some mice have problems providing the extended info -> return ok in
635    * these cases too
636    */
637   if(
638     (mi->bits == 6 && s[10] == 0x3c) ||
639     (mi->bits == 7 && s[10] == 0x5c)
640   ) {
641     return 11;
642   }
643
644   /* no end token... */
645
646   return 0;
647 }
648 #endif
649
650 unsigned chk4id(ser_device_t *mi)
651 {
652   int i;
653
654   if(!mi->buf_len) return 0;
655
656   for(i = 0; i < mi->buf_len; i++) {
657     if((mi->pnp = is_pnpinfo(mi, i))) break;
658   }
659   if(i == mi->buf_len) {
660     /* non PnP, but MS compatible */
661     if(*mi->buf == 'M')
662       mi->non_pnp = mi->buf_len - 1;
663     else
664       return 0;
665   }
666
667   mi->garbage = i;
668
669   for(i = 0; i < mi->garbage; i++) {
670     if(mi->buf[i] == 'M') {
671       mi->non_pnp = mi->garbage - i;
672       mi->garbage = i;
673       break;
674     }
675   }
676
677   if(mi->non_pnp || mi->bits == 6) mi->is_mouse = 1;
678
679   return mi->is_mouse;
680 }
681
682 ser_device_t *add_ser_mouse_entry(ser_device_t **sm, ser_device_t *new_sm)
683 {
684   while(*sm) sm = &(*sm)->next;
685   return *sm = new_sm;
686 }
687
688
689 void dump_ser_mouse_data(hd_data_t *hd_data)
690 {
691   int j;
692   ser_device_t *sm;
693
694   if(!(sm = hd_data->ser_mouse)) return;
695
696   ADD2LOG("----- serial mice -----\n");
697
698   for(; sm; sm = sm->next) {
699     ADD2LOG("%s\n", sm->dev_name);
700     if(sm->serial) ADD2LOG("serial: \"%s\"\n", sm->serial);
701     if(sm->class_name) ADD2LOG("class_name: \"%s\"\n", sm->class_name);
702     if(sm->dev_id) ADD2LOG("dev_id: \"%s\"\n", sm->dev_id);
703     if(sm->user_name) ADD2LOG("user_name: \"%s\"\n", sm->user_name);
704
705     if(sm->garbage) {
706       ADD2LOG("  garbage[%u]: ", sm->garbage);
707       hexdump(&hd_data->log, 1, sm->garbage, sm->buf);
708       ADD2LOG("\n");
709     }
710
711     if(sm->non_pnp) {
712       ADD2LOG("  non-pnp[%u]: ", sm->non_pnp);
713       hexdump(&hd_data->log, 1, sm->non_pnp, sm->buf + sm->garbage);
714       ADD2LOG("\n");
715     }
716
717     if(sm->pnp) {
718       ADD2LOG("  pnp[%u]: ", sm->pnp);
719       hexdump(&hd_data->log, 1, sm->pnp, sm->buf + sm->garbage + sm->non_pnp);
720       ADD2LOG("\n");
721     }
722
723     if((j = sm->buf_len - (sm->garbage + sm->non_pnp + sm->pnp))) {
724       ADD2LOG("  moves[%u]: ", j);
725       hexdump(&hd_data->log, 1, j, sm->buf + sm->garbage + sm->non_pnp + sm->pnp);
726       ADD2LOG("\n");
727     }
728
729     if(sm->is_mouse) ADD2LOG("  is mouse\n");
730
731     if(sm->pnp) {
732       ADD2LOG("  bits: %u\n", sm->bits);
733       ADD2LOG("  PnP Rev: %u.%02u\n", sm->pnp_rev / 100, sm->pnp_rev % 100);
734       ADD2LOG("  PnP ID: \"%s\"\n", sm->pnp_id);
735     }
736
737     if(sm->next) ADD2LOG("\n");
738   }
739
740   ADD2LOG("----- serial mice end -----\n");
741 }
742
743 #endif          /* !defined(LIBHD_TINY) */