]>
git.ipfire.org Git - people/stevee/ipfire-2.x.git/blob - src/hwinfo/src/hd/mouse.c
11 #include <sys/ioctl.h>
17 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
20 * TODO: reset serial lines to old values (cf. modem.c)
22 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
28 static unsigned read_data(hd_data_t
*hd_data
, int fd
, unsigned char *buf
, unsigned buf_size
);
29 static void get_ps2_mouse(hd_data_t
*hd_data
);
30 static void test_ps2_open(void *arg
);
33 static void get_serial_mouse(hd_data_t
* hd_data
);
34 static void add_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
);
41 static void get_sunmouse(hd_data_t
*hd_data
);
44 void hd_scan_mouse(hd_data_t
*hd_data
)
46 ser_device_t
*sm
, *sm_next
;
48 if(!hd_probe_feature(hd_data
, pr_mouse
)) return;
50 hd_data
->module
= mod_mouse
;
53 remove_hd_entries(hd_data
);
54 hd_data
->ser_mouse
= NULL
;
57 PROGRESS(1, 0, "ps/2");
59 get_ps2_mouse(hd_data
);
62 PROGRESS(2, 0, "serial");
64 hd_fork(hd_data
, 20, 20);
66 if(hd_data
->flags
.forked
) {
67 get_serial_mouse(hd_data
);
68 hd_move_to_shm(hd_data
);
69 if((hd_data
->debug
& HD_DEB_MOUSE
)) dump_ser_mouse_data(hd_data
);
72 /* take data from shm */
73 hd_data
->ser_mouse
= ((hd_data_t
*) (hd_data
->shm
.data
))->ser_mouse
;
74 if((hd_data
->debug
& HD_DEB_MOUSE
)) dump_ser_mouse_data(hd_data
);
77 hd_fork_done(hd_data
);
79 add_serial_mouse(hd_data
);
81 hd_shm_clean(hd_data
);
83 for(sm
= hd_data
->ser_mouse
; sm
; sm
= sm_next
) {
86 free_mem(sm
->dev_name
);
89 hd_data
->ser_mouse
= NULL
;
92 PROGRESS(3, 0, "sunmouse");
94 get_sunmouse(hd_data
);
100 unsigned read_data(hd_data_t
*hd_data
, int fd
, unsigned char *buf
, unsigned buf_size
)
106 (unsigned) len
< buf_size
&&
107 (k
= read(fd
, buf
+ len
, buf_size
- len
)) >= 0
111 if(len
&& (*bp
== 0xfe || *bp
== 0xfa)) { bp
++; len
--; }
113 for(k
= 0; k
< len
; k
++) buf
[k
] = bp
[k
];
115 if((hd_data
->debug
& HD_DEB_MOUSE
)) {
116 ADD2LOG("ps/2[%d]: ", len
);
117 hexdump(&hd_data
->log
, 1, len
, buf
);
128 * 1. There must exist a PS/2 controller entry (-> there is a PS/2 port).
129 * 2. If there are PS/2 mouse irq events, assume a PS/2 mouse is attached.
132 * - write the "get mouse info" command (0xe9)
133 * - read back the response, which should be either 0xfe "resend data"
134 * or, e.g. (0xfa) 0x20 0x02 0x3c (0xfa = "ACK" (should be swallowed
135 * by the psaux driver, but isn't), the rest are settings)
136 * - ignore the first byte if it is 0xfa or 0xfe
137 * - if there are at least 2 bytes left, assume a mouse is attached.
139 * Note1: we could use the command 0xfe "get mouse ID" instead. But that turned
140 * out to be less reliable, as this command returns only one byte, which
142 * Note2: step 2 is mainly relevant if the mouse is already in use. In that
143 * case we would have problems reading back the respose of our command.
144 * (Typically the mouse driver will get it (and choke on it).)
147 static void get_ps2_mouse(hd_data_t
*hd_data
)
154 unsigned char cmd_mouse_info
= 0xe9; /* read mouse info (3 bytes) */
155 unsigned char cmd_mouse_id
= 0xf2; /* read mouse id (1 byte) */
156 unsigned char buf
[100];
157 unsigned mouse_id
= -1;
158 static unsigned char intelli_init
[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
161 int always_ps2_mouse
= 0;
164 for(hd1
= hd_data
->hd
; hd1
; hd1
= hd1
->next
) {
165 /* look for a PS/2 controller entry... */
166 if(hd1
->base_class
.id
== bc_ps2
) {
167 /* ...and see if there were irq events... */
168 for(res
= hd1
->res
; res
; res
= res
->next
) {
169 if(res
->irq
.type
== res_irq
&& res
->irq
.triggered
) break;
174 * On PReP & CHRP, assume a PS/2 mouse to be attached.
175 * There seems to be no way to actually *detect* it.
181 if((hd
= hd_list(hd_data
, hw_sys
, 0, NULL
))) {
184 hd
->detail
->type
== hd_detail_sys
&&
185 (st
= hd
->detail
->sys
.data
) &&
187 !strcmp(st
->system_type
, "PReP") ||
188 strstr(st
->system_type
, "CHRP") == st
->system_type
/* CHRP && CHRP64 */
191 always_ps2_mouse
= 1;
197 PROGRESS(1, 1, "ps/2");
199 /* open the mouse device... */
200 if(hd_timeout(test_ps2_open
, NULL
, 2) > 0) {
201 ADD2LOG("ps/2: open(%s) timed out\n", DEV_PSAUX
);
205 fd
= open(DEV_PSAUX
, O_RDWR
| O_NONBLOCK
);
208 PROGRESS(1, 2, "ps/2");
211 /* ...write the id command... */
213 PROGRESS(1, 3, "ps/2");
215 write(fd
, intelli_init
, sizeof intelli_init
);
217 read_data(hd_data
, fd
, buf
, sizeof buf
);
219 if(write(fd
, &cmd_mouse_id
, 1) == 1) {
221 PROGRESS(1, 4, "ps/2");
222 usleep(50000); /* ...give it a chance to react... */
224 /* ...read the response... */
225 buf_len
= read_data(hd_data
, fd
, buf
, sizeof buf
);
227 if(buf_len
>= 1) mouse_id
= buf
[buf_len
- 1];
229 // if we didn't get any response, try this
230 if(buf_len
== 0 || (hd_data
->debug
& HD_DEB_MOUSE
)) {
231 PROGRESS(1, 5, "ps/2");
232 if(write(fd
, &cmd_mouse_info
, 1) == 1) {
234 buf_len
= read_data(hd_data
, fd
, buf
, sizeof buf
);
236 * Assume a mouse to be attached if at least 2 bytes are
239 if(mouse_id
== -1u && buf_len
>= 2) mouse_id
= 0;
243 PROGRESS(1, 6, "ps/2");
247 PROGRESS(1, 7, "ps/2");
250 * The following code is apparently necessary on some board/mouse
251 * combinations. Otherwise the PS/2 mouse won't work.
253 if((fd
= open(DEV_PSAUX
, O_RDONLY
| O_NONBLOCK
)) >= 0) {
254 PROGRESS(1, 8, "ps/2");
258 tv
.tv_sec
= 0; tv
.tv_usec
= 1;
259 if(select(fd
+ 1, &set
, NULL
, NULL
, &tv
) == 1) {
260 PROGRESS(1, 9, "ps/2");
262 read(fd
, buf
, sizeof buf
);
264 PROGRESS(1, 10, "ps/2");
266 PROGRESS(1, 11, "ps/2");
270 PROGRESS(1, 12, "ps/2");
274 ADD2LOG("open(" DEV_PSAUX
"): %s\n", fd
== -1 ? strerror(errno
) : "timeout");
277 if(mouse_id
== -1u) {
280 * Assume a PS/2 mouse is attached if the ps/2 controller has
281 * genetrated some events.
290 PROGRESS(1, 13, "ps/2");
295 if(mouse_id
!= -1u) {
296 PROGRESS(1, 14, "ps/2");
298 hd
= add_hd_entry(hd_data
, __LINE__
, 0);
299 hd
->base_class
.id
= bc_mouse
;
300 hd
->sub_class
.id
= sc_mou_ps2
;
301 hd
->bus
.id
= bus_ps2
;
302 hd
->unix_dev_name
= new_str(DEV_MICE
);
303 hd
->attached_to
= hd1
->idx
;
305 hd
->vendor
.id
= MAKE_ID(TAG_SPECIAL
, 0x0200);
307 case 3: /* 3 buttons + wheel */
308 hd
->device
.id
= MAKE_ID(TAG_SPECIAL
, 0x0004);
311 case 4: /* 5 buttons + wheel */
312 hd
->device
.id
= MAKE_ID(TAG_SPECIAL
, 0x0005);
316 hd
->device
.id
= MAKE_ID(TAG_SPECIAL
, 0x0002);
320 /* there can only be one... */
326 void test_ps2_open(void *arg
)
328 open(DEV_PSAUX
, O_RDWR
| O_NONBLOCK
);
333 static void get_sunmouse(hd_data_t
*hd_data
)
341 /* Only search for Sun mouse if we have a Sun keyboard */
342 for(hd
= hd_data
->hd
; hd
; hd
= hd
->next
)
344 if(hd
->base_class
.id
== bc_keyboard
&&
345 hd
->sub_class
.id
== sc_keyboard_kbd
&&
346 ID_TAG(hd
->vendor
.id
) == TAG_SPECIAL
&& ID_VALUE(hd
->vendor
.id
) == 0x0202)
352 if ((fd
= open(DEV_SUNMOUSE
, O_RDONLY
)) != -1)
354 /* FIXME: Should probably talk to the mouse to see
355 if the connector is not empty. */
358 PROGRESS(1, 1, "Sun Mouse");
360 hd
= add_hd_entry (hd_data
, __LINE__
, 0);
361 hd
->base_class
.id
= bc_mouse
;
362 hd
->sub_class
.id
= sc_mou_sun
;
363 hd
->bus
.id
= bus_serial
;
364 hd
->unix_dev_name
= new_str(DEV_SUNMOUSE
);
366 hd
->vendor
.id
= MAKE_ID(TAG_SPECIAL
, 0x0202);
367 hd
->device
.id
= MAKE_ID(TAG_SPECIAL
, 0x0000);
374 * Gather serial mouse data and put it into hd_data->ser_mouse.
376 void get_serial_mouse(hd_data_t
*hd_data
)
379 int j
, fd
, fd_max
= 0, sel
, max_len
;
388 for(hd
= hd_data
->hd
; hd
; hd
= hd
->next
) {
390 hd
->base_class
.id
== bc_comm
&&
391 hd
->sub_class
.id
== sc_com_ser
&&
394 !has_something_attached(hd_data
, hd
)
396 if((fd
= open(hd
->unix_dev_name
, O_RDWR
| O_NONBLOCK
)) >= 0) {
397 if(tcgetattr(fd
, &tio
)) continue;
398 sm
= add_ser_mouse_entry(&hd_data
->ser_mouse
, new_mem(sizeof *sm
));
399 sm
->dev_name
= new_str(hd
->unix_dev_name
);
402 sm
->hd_idx
= hd
->idx
;
403 if(fd
> fd_max
) fd_max
= fd
;
407 * PnP COM spec black magic...
409 setspeed(fd
, 1200, 1, CS7
);
410 modem_info
= TIOCM_DTR
| TIOCM_RTS
;
411 ioctl(fd
, TIOCMBIC
, &modem_info
);
416 if(!hd_data
->ser_mouse
) return;
419 * 200 ms seems to be too fast for some mice...
421 usleep(300000); /* PnP protocol */
423 for(sm
= hd_data
->ser_mouse
; sm
; sm
= sm
->next
) {
424 modem_info
= TIOCM_DTR
| TIOCM_RTS
;
425 ioctl(sm
->fd
, TIOCMBIS
, &modem_info
);
428 /* smaller buffer size, otherwise we might wait really long... */
429 max_len
= sizeof sm
->buf
< 128 ? sizeof sm
->buf
: 128;
431 to
.tv_sec
= 0; to
.tv_usec
= 300000;
435 to
.tv_sec
= 0; to
.tv_usec
= 300000;
437 if((sel
= select(fd_max
+ 1, &set
, NULL
, NULL
, &to
)) > 0) {
438 for(sm
= hd_data
->ser_mouse
; sm
; sm
= sm
->next
) {
439 if(FD_ISSET(sm
->fd
, &set
)) {
440 if((j
= read(sm
->fd
, sm
->buf
+ sm
->buf_len
, max_len
- sm
->buf_len
)) > 0)
442 if(j
<= 0) FD_CLR(sm
->fd
, &set0
); // #####
451 for(sm
= hd_data
->ser_mouse
; sm
; sm
= sm
->next
) {
453 /* reset serial lines */
454 tcflush(sm
->fd
, TCIOFLUSH
);
455 tcsetattr(sm
->fd
, TCSAFLUSH
, &sm
->tio
);
462 * Go through serial mouse data and add hd entries.
464 void add_serial_mouse(hd_data_t
*hd_data
)
470 for(sm
= hd_data
->ser_mouse
; sm
; sm
= sm
->next
) {
472 hd
= add_hd_entry(hd_data
, __LINE__
, 0);
473 hd
->base_class
.id
= bc_mouse
;
474 hd
->sub_class
.id
= sc_mou_ser
;
475 hd
->bus
.id
= bus_serial
;
476 hd
->unix_dev_name
= new_str(sm
->dev_name
);
477 hd
->attached_to
= sm
->hd_idx
;
479 strncpy(buf
, sm
->pnp_id
, 3);
481 hd
->vendor
.id
= name2eisa_id(buf
);
482 if(!hd
->vendor
.id
) { /* in case it's a really strange one... */
483 hd
->vendor
.name
= new_str(buf
);
485 hd
->device
.id
= MAKE_ID(TAG_EISA
, strtol(sm
->pnp_id
+ 3, NULL
, 16));
487 hd
->serial
= new_str(sm
->serial
);
488 if(sm
->user_name
) hd
->device
.name
= new_str(sm
->user_name
);
490 free_mem(hd
->vendor
.name
);
491 hd
->vendor
.name
= new_str(sm
->vend
);
494 if(sm
->dev_id
&& strlen(sm
->dev_id
) >= 7) {
498 u1
= name2eisa_id(sm
->dev_id
);
500 strncpy(buf
, sm
->dev_id
+ 3, 4);
502 u2
= strtol(sm
->dev_id
+ 3, &s
, 16);
504 hd
->compat_vendor
.id
= u1
;
505 hd
->compat_device
.id
= MAKE_ID(TAG_EISA
, u2
);
511 hd
->vendor
.id
= MAKE_ID(TAG_SPECIAL
, 0x0200);
512 hd
->device
.id
= MAKE_ID(TAG_SPECIAL
, 0x0003);
520 * Baud setting magic taken from gpm.
523 int _setspeed(int fd
, int old
, int new, int needtowrite
, unsigned short flags
)
529 flags
|= CREAD
| CLOCAL
| HUPCL
;
531 if(tcgetattr(fd
, &tty
)) return errno
;
533 tty
.c_iflag
= IGNBRK
| IGNPAR
;
542 case 9600: tty
.c_cflag
= flags
| B9600
; break;
543 case 4800: tty
.c_cflag
= flags
| B4800
; break;
544 case 2400: tty
.c_cflag
= flags
| B2400
; break;
546 default: tty
.c_cflag
= flags
| B1200
; break;
549 if(tcsetattr(fd
, TCSAFLUSH
, &tty
)) return errno
;
553 case 9600: c
= "*q"; tty
.c_cflag
= flags
| B9600
; break;
554 case 4800: c
= "*p"; tty
.c_cflag
= flags
| B4800
; break;
555 case 2400: c
= "*o"; tty
.c_cflag
= flags
| B2400
; break;
557 default: c
= "*n"; tty
.c_cflag
= flags
| B1200
; break;
561 err
= 2 - write(fd
, c
, 2);
566 if(tcsetattr(fd
, TCSAFLUSH
, &tty
)) return errno
;
572 void setspeed(int fd
, int new, int needtowrite
, unsigned short flags
)
576 for(i
= 9600; i
>= 1200; i
>>= 1) {
577 err
= _setspeed(fd
, i
, new, needtowrite
, flags
);
580 fprintf(stderr
, "%d, %d ", i
, err
);
590 * Check for a PnP info field starting at ofs;
591 * returns either the length of the field or 0 if none was found.
593 * the minfo_t struct is updated with the PnP data
595 int is_pnpinfo(ser_device_t
*mi
, int ofs
)
598 unsigned char *s
= mi
->buf
+ ofs
;
599 int len
= mi
->buf_len
- ofs
;
601 if(len
<= 0) return 0;
612 if(len
< 11) return 0;
615 if((s
[1] & ~0x3f) || (s
[2] & ~0x3f)) return 0;
616 mi
->pnp_rev
= (s
[1] << 6) + s
[2];
619 for(i
= 0; i
< 7; i
++) {
620 mi
->pnp_id
[i
] = s
[i
+ 3];
621 if(mi
->bits
== 6) mi
->pnp_id
[i
] += 0x20;
625 /* now check the id */
626 for(i
= 0; i
< 3; i
++) {
628 (mi
->pnp_id
[i
] < 'A' || mi
->pnp_id
[i
] > 'Z') &&
633 for(i
= 3; i
< 7; i
++) {
635 (mi
->pnp_id
[i
] < '0' || mi
->pnp_id
[i
] > '9') &&
636 (mi
->pnp_id
[i
] < 'A' || mi
->pnp_id
[i
] > 'F')
641 (mi
->bits
== 6 && s
[10] == 0x09) ||
642 (mi
->bits
== 7 && s
[10] == 0x29)
648 (mi
->bits
!= 6 || s
[10] != 0x3c) &&
649 (mi
->bits
!= 7 || s
[10] != 0x5c)
654 /* skip extended info */
655 for(i
= 11; i
< len
; i
++) {
657 (mi
->bits
== 6 && s
[i
] == 0x09) ||
658 (mi
->bits
== 7 && s
[i
] == 0x29)
665 * some mice have problems providing the extended info -> return ok in
669 (mi
->bits
== 6 && s
[10] == 0x3c) ||
670 (mi
->bits
== 7 && s
[10] == 0x5c)
675 /* no end token... */
681 unsigned chk4id(ser_device_t
*mi
)
686 unsigned char fake
[] =
691 mi
->buf_len
= sizeof fake
;
692 memcpy(mi
->buf
, fake
, mi
->buf_len
);
693 // for(i = 0; i < mi->buf_len; i++) mi->buf[i] += ' ';
696 if(!mi
->buf_len
) return 0;
698 for(i
= 0; i
< mi
->buf_len
; i
++) {
699 if((mi
->pnp
= is_pnpinfo(mi
, i
))) break;
701 if(i
== mi
->buf_len
) {
702 /* non PnP, but MS compatible */
704 mi
->non_pnp
= mi
->buf_len
- 1;
711 for(i
= 0; i
< mi
->garbage
; i
++) {
712 if(mi
->buf
[i
] == 'M') {
713 mi
->non_pnp
= mi
->garbage
- i
;
719 if(mi
->non_pnp
|| mi
->bits
== 6) mi
->is_mouse
= 1;
724 ser_device_t
*add_ser_mouse_entry(ser_device_t
**sm
, ser_device_t
*new_sm
)
726 while(*sm
) sm
= &(*sm
)->next
;
731 void dump_ser_mouse_data(hd_data_t
*hd_data
)
736 if(!(sm
= hd_data
->ser_mouse
)) return;
738 ADD2LOG("----- serial mice -----\n");
740 for(; sm
; sm
= sm
->next
) {
741 ADD2LOG("%s\n", sm
->dev_name
);
742 if(sm
->serial
) ADD2LOG("serial: \"%s\"\n", sm
->serial
);
743 if(sm
->class_name
) ADD2LOG("class_name: \"%s\"\n", sm
->class_name
);
744 if(sm
->dev_id
) ADD2LOG("dev_id: \"%s\"\n", sm
->dev_id
);
745 if(sm
->user_name
) ADD2LOG("user_name: \"%s\"\n", sm
->user_name
);
748 ADD2LOG(" garbage[%u]: ", sm
->garbage
);
749 hexdump(&hd_data
->log
, 1, sm
->garbage
, sm
->buf
);
754 ADD2LOG(" non-pnp[%u]: ", sm
->non_pnp
);
755 hexdump(&hd_data
->log
, 1, sm
->non_pnp
, sm
->buf
+ sm
->garbage
);
760 ADD2LOG(" pnp[%u]: ", sm
->pnp
);
761 hexdump(&hd_data
->log
, 1, sm
->pnp
, sm
->buf
+ sm
->garbage
+ sm
->non_pnp
);
765 if((j
= sm
->buf_len
- (sm
->garbage
+ sm
->non_pnp
+ sm
->pnp
))) {
766 ADD2LOG(" moves[%u]: ", j
);
767 hexdump(&hd_data
->log
, 1, j
, sm
->buf
+ sm
->garbage
+ sm
->non_pnp
+ sm
->pnp
);
771 if(sm
->is_mouse
) ADD2LOG(" is mouse\n");
774 ADD2LOG(" bits: %u\n", sm
->bits
);
775 ADD2LOG(" PnP Rev: %u.%02u\n", sm
->pnp_rev
/ 100, sm
->pnp_rev
% 100);
776 ADD2LOG(" PnP ID: \"%s\"\n", sm
->pnp_id
);
779 if(sm
->next
) ADD2LOG("\n");
782 ADD2LOG("----- serial mice end -----\n");
785 #endif /* !defined(LIBHD_TINY) */