2 * $FreeBSD: src/sys/dev/usb/ums.c,v 1.64 2003/11/09 09:17:22 tanimura Exp $
3 * $DragonFly: src/sys/dev/usbmisc/ums/ums.c,v 1.24 2007/06/28 13:55:13 hasso Exp $
7 * Copyright (c) 1998 The NetBSD Foundation, Inc.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Lennart Augustsson (lennart@augustsson.net) at
12 * Carlstedt Research & Technology.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the NetBSD
25 * Foundation, Inc. and its contributors.
26 * 4. Neither the name of The NetBSD Foundation nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
44 * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/module.h>
53 #include <sys/ioccom.h>
57 #include <sys/select.h>
58 #include <sys/vnode.h>
60 #include <sys/sysctl.h>
61 #include <sys/thread2.h>
63 #include <bus/usb/usb.h>
64 #include <bus/usb/usbhid.h>
66 #include <bus/usb/usbdi.h>
67 #include <bus/usb/usbdi_util.h>
68 #include <bus/usb/usbdevs.h>
69 #include <bus/usb/usb_quirks.h>
70 #include <bus/usb/hid.h>
72 #include <machine/mouse.h>
75 #define DPRINTF(x) if (umsdebug) kprintf x
76 #define DPRINTFN(n,x) if (umsdebug>(n)) kprintf x
78 SYSCTL_NODE(_hw_usb
, OID_AUTO
, ums
, CTLFLAG_RW
, 0, "USB ums");
79 SYSCTL_INT(_hw_usb_ums
, OID_AUTO
, debug
, CTLFLAG_RW
,
80 &umsdebug
, 0, "ums debug level");
86 #define UMSUNIT(s) (minor(s)&0x1f)
88 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
90 #define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */
93 device_t sc_dev
; /* base device */
94 usbd_interface_handle sc_iface
; /* interface */
95 usbd_pipe_handle sc_intrpipe
; /* interrupt pipe */
101 struct hid_location sc_loc_x
, sc_loc_y
, sc_loc_z
;
102 struct hid_location
*sc_loc_btn
;
104 struct callout sc_timeout
; /* for spurious button ups */
107 int sc_disconnected
; /* device is gone */
109 int flags
; /* device configuration */
110 #define UMS_Z 0x01 /* z direction available */
111 #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
113 #define MAX_BUTTONS 31 /* must not exceed size of sc_buttons */
115 u_char qbuf
[QUEUE_BUFSIZE
]; /* must be divisable by 3&4 */
116 u_char dummy
[100]; /* XXX just for safety and for now */
117 int qcount
, qhead
, qtail
;
120 mousestatus_t status
;
123 # define UMS_ASLEEP 0x01 /* readFromDevice is waiting */
124 # define UMS_SELECT 0x02 /* select is waiting */
125 struct selinfo rsel
; /* process waiting in select */
128 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
129 #define MOUSE_FLAGS (HIO_RELATIVE)
131 static void ums_intr(usbd_xfer_handle xfer
,
132 usbd_private_handle priv
, usbd_status status
);
134 static void ums_add_to_queue(struct ums_softc
*sc
,
135 int dx
, int dy
, int dz
, int buttons
);
136 static void ums_add_to_queue_timeout(void *priv
);
138 static int ums_enable(void *);
139 static void ums_disable(void *);
141 static d_open_t ums_open
;
142 static d_close_t ums_close
;
143 static d_read_t ums_read
;
144 static d_ioctl_t ums_ioctl
;
145 static d_poll_t ums_poll
;
147 #define UMS_CDEV_MAJOR 111
149 static struct dev_ops ums_ops
= {
150 { "ums", UMS_CDEV_MAJOR
, 0 },
152 .d_close
= ums_close
,
154 .d_ioctl
= ums_ioctl
,
158 USB_DECLARE_DRIVER(ums
);
162 USB_MATCH_START(ums
, uaa
);
163 usb_interface_descriptor_t
*id
;
169 return (UMATCH_NONE
);
170 id
= usbd_get_interface_descriptor(uaa
->iface
);
171 if (!id
|| id
->bInterfaceClass
!= UICLASS_HID
)
172 return (UMATCH_NONE
);
174 err
= usbd_read_report_desc(uaa
->iface
, &desc
, &size
, M_TEMP
);
176 return (UMATCH_NONE
);
178 if (hid_is_collection(desc
, size
,
179 HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_MOUSE
)))
180 ret
= UMATCH_IFACECLASS
;
190 USB_ATTACH_START(ums
, sc
, uaa
);
191 usbd_interface_handle iface
= uaa
->iface
;
192 usb_interface_descriptor_t
*id
;
193 usb_endpoint_descriptor_t
*ed
;
200 struct hid_location loc_btn
;
202 sc
->sc_disconnected
= 1;
203 sc
->sc_iface
= iface
;
204 id
= usbd_get_interface_descriptor(iface
);
205 usbd_devinfo(uaa
->device
, 0, devinfo
);
207 kprintf("%s: %s, iclass %d/%d\n", device_get_nameunit(sc
->sc_dev
),
208 devinfo
, id
->bInterfaceClass
, id
->bInterfaceSubClass
);
209 ed
= usbd_interface2endpoint_descriptor(iface
, 0);
211 kprintf("%s: could not read endpoint descriptor\n",
212 device_get_nameunit(sc
->sc_dev
));
213 USB_ATTACH_ERROR_RETURN
;
216 DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
217 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
219 ed
->bLength
, ed
->bDescriptorType
,
220 UE_GET_ADDR(ed
->bEndpointAddress
),
221 UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_IN
? "in":"out",
222 UE_GET_XFERTYPE(ed
->bmAttributes
),
223 UGETW(ed
->wMaxPacketSize
), ed
->bInterval
));
225 if (UE_GET_DIR(ed
->bEndpointAddress
) != UE_DIR_IN
||
226 UE_GET_XFERTYPE(ed
->bmAttributes
) != UE_INTERRUPT
) {
227 kprintf("%s: unexpected endpoint\n",
228 device_get_nameunit(sc
->sc_dev
));
229 USB_ATTACH_ERROR_RETURN
;
232 err
= usbd_read_report_desc(uaa
->iface
, &desc
, &size
, M_TEMP
);
234 USB_ATTACH_ERROR_RETURN
;
236 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_X
),
237 hid_input
, &sc
->sc_loc_x
, &flags
)) {
238 kprintf("%s: mouse has no X report\n", device_get_nameunit(sc
->sc_dev
));
239 USB_ATTACH_ERROR_RETURN
;
241 if ((flags
& MOUSE_FLAGS_MASK
) != MOUSE_FLAGS
) {
242 kprintf("%s: X report 0x%04x not supported\n",
243 device_get_nameunit(sc
->sc_dev
), flags
);
244 USB_ATTACH_ERROR_RETURN
;
247 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_Y
),
248 hid_input
, &sc
->sc_loc_y
, &flags
)) {
249 kprintf("%s: mouse has no Y report\n", device_get_nameunit(sc
->sc_dev
));
250 USB_ATTACH_ERROR_RETURN
;
252 if ((flags
& MOUSE_FLAGS_MASK
) != MOUSE_FLAGS
) {
253 kprintf("%s: Y report 0x%04x not supported\n",
254 device_get_nameunit(sc
->sc_dev
), flags
);
255 USB_ATTACH_ERROR_RETURN
;
258 /* try to guess the Z activator: first check Z, then WHEEL */
259 if (hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_Z
),
260 hid_input
, &sc
->sc_loc_z
, &flags
) ||
261 hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_WHEEL
),
262 hid_input
, &sc
->sc_loc_z
, &flags
)) {
263 if ((flags
& MOUSE_FLAGS_MASK
) != MOUSE_FLAGS
) {
264 sc
->sc_loc_z
.size
= 0; /* Bad Z coord, ignore it */
270 /* figure out the number of buttons */
271 for (i
= 1; i
<= MAX_BUTTONS
; i
++)
272 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_BUTTON
, i
),
273 hid_input
, &loc_btn
, 0))
275 sc
->nbuttons
= i
- 1;
276 sc
->sc_loc_btn
= kmalloc(sizeof(struct hid_location
)*sc
->nbuttons
,
277 M_USBDEV
, M_INTWAIT
);
279 kprintf("%s: %d buttons%s\n", device_get_nameunit(sc
->sc_dev
),
280 sc
->nbuttons
, sc
->flags
& UMS_Z
? " and Z dir." : "");
282 for (i
= 1; i
<= sc
->nbuttons
; i
++)
283 hid_locate(desc
, size
, HID_USAGE2(HUP_BUTTON
, i
),
284 hid_input
, &sc
->sc_loc_btn
[i
-1], 0);
286 sc
->sc_isize
= hid_report_size(desc
, size
, hid_input
, &sc
->sc_iid
);
287 sc
->sc_ibuf
= kmalloc(sc
->sc_isize
, M_USB
, M_INTWAIT
);
288 sc
->sc_ep_addr
= ed
->bEndpointAddress
;
289 sc
->sc_disconnected
= 0;
293 DPRINTF(("ums_attach: sc=%p\n", sc
));
294 DPRINTF(("ums_attach: X\t%d/%d\n",
295 sc
->sc_loc_x
.pos
, sc
->sc_loc_x
.size
));
296 DPRINTF(("ums_attach: Y\t%d/%d\n",
297 sc
->sc_loc_y
.pos
, sc
->sc_loc_y
.size
));
298 if (sc
->flags
& UMS_Z
)
299 DPRINTF(("ums_attach: Z\t%d/%d\n",
300 sc
->sc_loc_z
.pos
, sc
->sc_loc_z
.size
));
301 for (i
= 1; i
<= sc
->nbuttons
; i
++) {
302 DPRINTF(("ums_attach: B%d\t%d/%d\n",
303 i
, sc
->sc_loc_btn
[i
-1].pos
,sc
->sc_loc_btn
[i
-1].size
));
305 DPRINTF(("ums_attach: size=%d, id=%d\n", sc
->sc_isize
, sc
->sc_iid
));
308 if (sc
->nbuttons
> MOUSE_MSC_MAXBUTTON
)
309 sc
->hw
.buttons
= MOUSE_MSC_MAXBUTTON
;
311 sc
->hw
.buttons
= sc
->nbuttons
;
312 sc
->hw
.iftype
= MOUSE_IF_USB
;
313 sc
->hw
.type
= MOUSE_MOUSE
;
314 sc
->hw
.model
= MOUSE_MODEL_GENERIC
;
316 sc
->mode
.protocol
= MOUSE_PROTO_MSC
;
318 sc
->mode
.resolution
= MOUSE_RES_UNKNOWN
;
319 sc
->mode
.accelfactor
= 0;
321 sc
->mode
.packetsize
= MOUSE_MSC_PACKETSIZE
;
322 sc
->mode
.syncmask
[0] = MOUSE_MSC_SYNCMASK
;
323 sc
->mode
.syncmask
[1] = MOUSE_MSC_SYNC
;
325 sc
->status
.flags
= 0;
326 sc
->status
.button
= sc
->status
.obutton
= 0;
327 sc
->status
.dx
= sc
->status
.dy
= sc
->status
.dz
= 0;
329 dev_ops_add(&ums_ops
, -1, device_get_unit(self
));
330 make_dev(&ums_ops
, device_get_unit(self
),
331 UID_ROOT
, GID_OPERATOR
,
332 0644, "ums%d", device_get_unit(self
));
334 if (usbd_get_quirks(uaa
->device
)->uq_flags
& UQ_SPUR_BUT_UP
) {
335 DPRINTF(("%s: Spurious button up events\n",
336 device_get_nameunit(sc
->sc_dev
)));
337 sc
->flags
|= UMS_SPUR_BUT_UP
;
340 USB_ATTACH_SUCCESS_RETURN
;
345 ums_detach(device_t self
)
347 struct ums_softc
*sc
= device_get_softc(self
);
352 DPRINTF(("%s: disconnected\n", device_get_nameunit(self
)));
354 kfree(sc
->sc_loc_btn
, M_USB
);
355 kfree(sc
->sc_ibuf
, M_USB
);
357 /* someone waiting for data */
359 * XXX If we wakeup the process here, the device will be gone by
360 * the time the process gets a chance to notice. *_close and friends
361 * should be fixed to handle this case.
362 * Or we should do a delayed detach for this.
363 * Does this delay now force tsleep to exit with an error?
365 if (sc
->state
& UMS_ASLEEP
) {
366 sc
->state
&= ~UMS_ASLEEP
;
369 if (sc
->state
& UMS_SELECT
) {
370 sc
->state
&= ~UMS_SELECT
;
371 selwakeuppri(&sc
->rsel
, 0);
373 dev_ops_remove(&ums_ops
, -1, device_get_unit(self
));
379 ums_intr(usbd_xfer_handle xfer
, usbd_private_handle addr
,
382 struct ums_softc
*sc
= addr
;
388 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
390 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc
, status
));
391 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
392 sc
->sc_ibuf
[0], sc
->sc_ibuf
[1], sc
->sc_ibuf
[2]));
394 if (status
== USBD_CANCELLED
)
397 if (status
!= USBD_NORMAL_COMPLETION
) {
398 DPRINTF(("ums_intr: status=%d\n", status
));
399 if (status
== USBD_STALLED
)
400 usbd_clear_endpoint_stall_async(sc
->sc_intrpipe
);
406 if (*ibuf
++ != sc
->sc_iid
)
410 dx
= hid_get_data(ibuf
, &sc
->sc_loc_x
);
411 dy
= -hid_get_data(ibuf
, &sc
->sc_loc_y
);
412 dz
= -hid_get_data(ibuf
, &sc
->sc_loc_z
);
413 for (i
= 0; i
< sc
->nbuttons
; i
++)
414 if (hid_get_data(ibuf
, &sc
->sc_loc_btn
[i
]))
415 buttons
|= (1 << UMS_BUT(i
));
417 if (dx
|| dy
|| dz
|| (sc
->flags
& UMS_Z
)
418 || buttons
!= sc
->status
.button
) {
419 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
420 dx
, dy
, dz
, buttons
));
422 sc
->status
.button
= buttons
;
427 /* Discard data in case of full buffer */
428 if (sc
->qcount
== sizeof(sc
->qbuf
)) {
429 DPRINTF(("Buffer full, discarded packet"));
434 * The Qtronix keyboard has a built in PS/2 port for a mouse.
435 * The firmware once in a while posts a spurious button up
436 * event. This event we ignore by doing a timeout for 50 msecs.
437 * If we receive dx=dy=dz=buttons=0 before we add the event to
439 * In any other case we delete the timeout event.
441 if (sc
->flags
& UMS_SPUR_BUT_UP
&&
442 dx
== 0 && dy
== 0 && dz
== 0 && buttons
== 0) {
443 callout_reset(&sc
->sc_timeout
, MS_TO_TICKS(50),
444 ums_add_to_queue_timeout
, (void *) sc
);
446 callout_stop(&sc
->sc_timeout
);
447 ums_add_to_queue(sc
, dx
, dy
, dz
, buttons
);
453 ums_add_to_queue_timeout(void *priv
)
455 struct ums_softc
*sc
= priv
;
458 ums_add_to_queue(sc
, 0, 0, 0, 0);
463 ums_add_to_queue(struct ums_softc
*sc
, int dx
, int dy
, int dz
, int buttons
)
465 /* Discard data in case of full buffer */
466 if (sc
->qhead
+sc
->mode
.packetsize
> sizeof(sc
->qbuf
)) {
467 DPRINTF(("Buffer full, discarded packet"));
471 if (dx
> 254) dx
= 254;
472 if (dx
< -256) dx
= -256;
473 if (dy
> 254) dy
= 254;
474 if (dy
< -256) dy
= -256;
475 if (dz
> 126) dz
= 126;
476 if (dz
< -128) dz
= -128;
478 sc
->qbuf
[sc
->qhead
] = sc
->mode
.syncmask
[1];
479 sc
->qbuf
[sc
->qhead
] |= ~buttons
& MOUSE_MSC_BUTTONS
;
480 sc
->qbuf
[sc
->qhead
+1] = dx
>> 1;
481 sc
->qbuf
[sc
->qhead
+2] = dy
>> 1;
482 sc
->qbuf
[sc
->qhead
+3] = dx
- (dx
>> 1);
483 sc
->qbuf
[sc
->qhead
+4] = dy
- (dy
>> 1);
485 if (sc
->mode
.level
== 1) {
486 sc
->qbuf
[sc
->qhead
+5] = dz
>> 1;
487 sc
->qbuf
[sc
->qhead
+6] = dz
- (dz
>> 1);
488 sc
->qbuf
[sc
->qhead
+7] = ((~buttons
>> 3)
489 & MOUSE_SYS_EXTBUTTONS
);
492 sc
->qhead
+= sc
->mode
.packetsize
;
493 sc
->qcount
+= sc
->mode
.packetsize
;
494 /* wrap round at end of buffer */
495 if (sc
->qhead
>= sizeof(sc
->qbuf
))
498 /* someone waiting for data */
499 if (sc
->state
& UMS_ASLEEP
) {
500 sc
->state
&= ~UMS_ASLEEP
;
503 if (sc
->state
& UMS_SELECT
) {
504 sc
->state
&= ~UMS_SELECT
;
505 selwakeuppri(&sc
->rsel
, 0);
512 struct ums_softc
*sc
= v
;
521 sc
->qhead
= sc
->qtail
= 0;
522 sc
->status
.flags
= 0;
523 sc
->status
.button
= sc
->status
.obutton
= 0;
524 sc
->status
.dx
= sc
->status
.dy
= sc
->status
.dz
= 0;
526 callout_init(&sc
->sc_timeout
);
528 /* Set up interrupt pipe. */
529 err
= usbd_open_pipe_intr(sc
->sc_iface
, sc
->sc_ep_addr
,
530 USBD_SHORT_XFER_OK
, &sc
->sc_intrpipe
, sc
,
531 sc
->sc_ibuf
, sc
->sc_isize
, ums_intr
,
532 USBD_DEFAULT_INTERVAL
);
534 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
543 ums_disable(void *priv
)
545 struct ums_softc
*sc
= priv
;
547 callout_stop(&sc
->sc_timeout
);
549 /* Disable interrupts. */
550 usbd_abort_pipe(sc
->sc_intrpipe
);
551 usbd_close_pipe(sc
->sc_intrpipe
);
556 DPRINTF(("Discarded %d bytes in queue\n", sc
->qcount
));
560 ums_open(struct dev_open_args
*ap
)
562 cdev_t dev
= ap
->a_head
.a_dev
;
563 struct ums_softc
*sc
;
565 USB_GET_SC_OPEN(ums
, UMSUNIT(dev
), sc
);
567 return ums_enable(sc
);
571 ums_close(struct dev_close_args
*ap
)
573 cdev_t dev
= ap
->a_head
.a_dev
;
574 struct ums_softc
*sc
;
576 USB_GET_SC(ums
, UMSUNIT(dev
), sc
);
588 ums_read(struct dev_read_args
*ap
)
590 cdev_t dev
= ap
->a_head
.a_dev
;
591 struct uio
*uio
= ap
->a_uio
;
592 struct ums_softc
*sc
;
593 char buf
[sizeof(sc
->qbuf
)];
597 USB_GET_SC(ums
, UMSUNIT(dev
), sc
);
605 while (sc
->qcount
== 0 ) {
606 if (ap
->a_ioflag
& IO_NDELAY
) { /* non-blocking I/O */
611 sc
->state
|= UMS_ASLEEP
; /* blocking I/O */
612 error
= tsleep(sc
, PCATCH
, "umsrea", 0);
616 } else if (!sc
->sc_enabled
) {
620 /* check whether the device is still there */
622 sc
= devclass_get_softc(ums_devclass
, UMSUNIT(dev
));
630 * The writer process only extends qcount and qtail. We could copy
631 * them and use the copies to do the copying out of the queue.
634 while ((sc
->qcount
> 0) && (uio
->uio_resid
> 0)) {
635 l
= (sc
->qcount
< uio
->uio_resid
? sc
->qcount
:uio
->uio_resid
);
638 if (l
> sizeof(sc
->qbuf
) - sc
->qtail
) /* transfer till end of buf */
639 l
= sizeof(sc
->qbuf
) - sc
->qtail
;
642 uiomove(&sc
->qbuf
[sc
->qtail
], l
, uio
);
645 if ( sc
->qcount
- l
< 0 ) {
646 DPRINTF(("qcount below 0, count=%d l=%d\n", sc
->qcount
, l
));
649 sc
->qcount
-= l
; /* remove the bytes from the buffer */
650 sc
->qtail
= (sc
->qtail
+ l
) % sizeof(sc
->qbuf
);
658 ums_poll(struct dev_poll_args
*ap
)
660 cdev_t dev
= ap
->a_head
.a_dev
;
661 struct ums_softc
*sc
;
664 USB_GET_SC(ums
, UMSUNIT(dev
), sc
);
672 if (ap
->a_events
& (POLLIN
| POLLRDNORM
)) {
674 revents
= ap
->a_events
& (POLLIN
| POLLRDNORM
);
676 sc
->state
|= UMS_SELECT
;
677 selrecord(curthread
, &sc
->rsel
);
681 ap
->a_events
= revents
;
686 ums_ioctl(struct dev_ioctl_args
*ap
)
688 cdev_t dev
= ap
->a_head
.a_dev
;
689 struct ums_softc
*sc
;
693 USB_GET_SC(ums
, UMSUNIT(dev
), sc
);
699 case MOUSE_GETHWINFO
:
700 *(mousehw_t
*)ap
->a_data
= sc
->hw
;
703 *(mousemode_t
*)ap
->a_data
= sc
->mode
;
706 mode
= *(mousemode_t
*)ap
->a_data
;
708 if (mode
.level
== -1)
709 /* don't change the current setting */
711 else if ((mode
.level
< 0) || (mode
.level
> 1))
715 sc
->mode
.level
= mode
.level
;
717 if (sc
->mode
.level
== 0) {
718 if (sc
->nbuttons
> MOUSE_MSC_MAXBUTTON
)
719 sc
->hw
.buttons
= MOUSE_MSC_MAXBUTTON
;
721 sc
->hw
.buttons
= sc
->nbuttons
;
722 sc
->mode
.protocol
= MOUSE_PROTO_MSC
;
723 sc
->mode
.packetsize
= MOUSE_MSC_PACKETSIZE
;
724 sc
->mode
.syncmask
[0] = MOUSE_MSC_SYNCMASK
;
725 sc
->mode
.syncmask
[1] = MOUSE_MSC_SYNC
;
726 } else if (sc
->mode
.level
== 1) {
727 if (sc
->nbuttons
> MOUSE_SYS_MAXBUTTON
)
728 sc
->hw
.buttons
= MOUSE_SYS_MAXBUTTON
;
730 sc
->hw
.buttons
= sc
->nbuttons
;
731 sc
->mode
.protocol
= MOUSE_PROTO_SYSMOUSE
;
732 sc
->mode
.packetsize
= MOUSE_SYS_PACKETSIZE
;
733 sc
->mode
.syncmask
[0] = MOUSE_SYS_SYNCMASK
;
734 sc
->mode
.syncmask
[1] = MOUSE_SYS_SYNC
;
737 bzero(sc
->qbuf
, sizeof(sc
->qbuf
));
738 sc
->qhead
= sc
->qtail
= sc
->qcount
= 0;
743 *(int *)ap
->a_data
= sc
->mode
.level
;
746 if (*(int *)ap
->a_data
< 0 || *(int *)ap
->a_data
> 1)
750 sc
->mode
.level
= *(int *)ap
->a_data
;
752 if (sc
->mode
.level
== 0) {
753 if (sc
->nbuttons
> MOUSE_MSC_MAXBUTTON
)
754 sc
->hw
.buttons
= MOUSE_MSC_MAXBUTTON
;
756 sc
->hw
.buttons
= sc
->nbuttons
;
757 sc
->mode
.protocol
= MOUSE_PROTO_MSC
;
758 sc
->mode
.packetsize
= MOUSE_MSC_PACKETSIZE
;
759 sc
->mode
.syncmask
[0] = MOUSE_MSC_SYNCMASK
;
760 sc
->mode
.syncmask
[1] = MOUSE_MSC_SYNC
;
761 } else if (sc
->mode
.level
== 1) {
762 if (sc
->nbuttons
> MOUSE_SYS_MAXBUTTON
)
763 sc
->hw
.buttons
= MOUSE_SYS_MAXBUTTON
;
765 sc
->hw
.buttons
= sc
->nbuttons
;
766 sc
->mode
.protocol
= MOUSE_PROTO_SYSMOUSE
;
767 sc
->mode
.packetsize
= MOUSE_SYS_PACKETSIZE
;
768 sc
->mode
.syncmask
[0] = MOUSE_SYS_SYNCMASK
;
769 sc
->mode
.syncmask
[1] = MOUSE_SYS_SYNC
;
772 bzero(sc
->qbuf
, sizeof(sc
->qbuf
));
773 sc
->qhead
= sc
->qtail
= sc
->qcount
= 0;
777 case MOUSE_GETSTATUS
: {
778 mousestatus_t
*status
= (mousestatus_t
*) ap
->a_data
;
781 *status
= sc
->status
;
782 sc
->status
.obutton
= sc
->status
.button
;
783 sc
->status
.button
= 0;
784 sc
->status
.dx
= sc
->status
.dy
= sc
->status
.dz
= 0;
787 if (status
->dx
|| status
->dy
|| status
->dz
)
788 status
->flags
|= MOUSE_POSCHANGED
;
789 if (status
->button
!= status
->obutton
)
790 status
->flags
|= MOUSE_BUTTONSCHANGED
;
800 DRIVER_MODULE(ums
, uhub
, ums_driver
, ums_devclass
, usbd_driver_load
, 0);