summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--freebsd/sys/dev/usb/input/atp.c2636
-rw-r--r--freebsd/sys/dev/usb/input/uep.c446
-rw-r--r--freebsd/sys/dev/usb/input/uhid.c883
-rw-r--r--freebsd/sys/dev/usb/input/ukbd.c2309
-rw-r--r--freebsd/sys/dev/usb/input/ums.c1231
-rw-r--r--freebsd/sys/dev/usb/input/usb_rdesc.h276
-rw-r--r--freebsd/sys/dev/usb/input/wsp.c1405
-rw-r--r--freebsd/sys/sys/mouse.h395
8 files changed, 9581 insertions, 0 deletions
diff --git a/freebsd/sys/dev/usb/input/atp.c b/freebsd/sys/dev/usb/input/atp.c
new file mode 100644
index 00000000..461d4fff
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/atp.c
@@ -0,0 +1,2636 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2014 Rohit Grover
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Some tables, structures, definitions and constant values for the
+ * touchpad protocol has been copied from Linux's
+ * "drivers/input/mouse/bcm5974.c" which has the following copyright
+ * holders under GPLv2. All device specific code in this driver has
+ * been written from scratch. The decoding algorithm is based on
+ * output from FreeBSD's usbdump.
+ *
+ * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
+ * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ */
+
+/*
+ * Author's note: 'atp' supports two distinct families of Apple trackpad
+ * products: the older Fountain/Geyser and the latest Wellspring trackpads.
+ * The first version made its appearance with FreeBSD 8 and worked only with
+ * the Fountain/Geyser hardware. A fork of this driver for Wellspring was
+ * contributed by Huang Wen Hui. This driver unifies the Wellspring effort
+ * and also improves upon the original work.
+ *
+ * I'm grateful to Stephan Scheunig, Angela Naegele, and Nokia IT-support
+ * for helping me with access to hardware. Thanks also go to Nokia for
+ * giving me an opportunity to do this work.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR atp_debug
+#include <dev/usb/usb_debug.h>
+
+#include <sys/mouse.h>
+
+#define ATP_DRIVER_NAME "atp"
+
+/*
+ * Driver specific options: the following options may be set by
+ * `options' statements in the kernel configuration file.
+ */
+
+/* The divisor used to translate sensor reported positions to mickeys. */
+#ifndef ATP_SCALE_FACTOR
+#define ATP_SCALE_FACTOR 16
+#endif
+
+/* Threshold for small movement noise (in mickeys) */
+#ifndef ATP_SMALL_MOVEMENT_THRESHOLD
+#define ATP_SMALL_MOVEMENT_THRESHOLD 30
+#endif
+
+/* Threshold of instantaneous deltas beyond which movement is considered fast.*/
+#ifndef ATP_FAST_MOVEMENT_TRESHOLD
+#define ATP_FAST_MOVEMENT_TRESHOLD 150
+#endif
+
+/*
+ * This is the age in microseconds beyond which a touch is considered
+ * to be a slide; and therefore a tap event isn't registered.
+ */
+#ifndef ATP_TOUCH_TIMEOUT
+#define ATP_TOUCH_TIMEOUT 125000
+#endif
+
+#ifndef ATP_IDLENESS_THRESHOLD
+#define ATP_IDLENESS_THRESHOLD 10
+#endif
+
+#ifndef FG_SENSOR_NOISE_THRESHOLD
+#define FG_SENSOR_NOISE_THRESHOLD 2
+#endif
+
+/*
+ * A double-tap followed by a single-finger slide is treated as a
+ * special gesture. The driver responds to this gesture by assuming a
+ * virtual button-press for the lifetime of the slide. The following
+ * threshold is the maximum time gap (in microseconds) between the two
+ * tap events preceding the slide for such a gesture.
+ */
+#ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD
+#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000
+#endif
+
+/*
+ * The wait duration in ticks after losing a touch contact before
+ * zombied strokes are reaped and turned into button events.
+ */
+#define ATP_ZOMBIE_STROKE_REAP_INTERVAL (hz / 20) /* 50 ms */
+
+/* The multiplier used to translate sensor reported positions to mickeys. */
+#define FG_SCALE_FACTOR 380
+
+/*
+ * The movement threshold for a stroke; this is the maximum difference
+ * in position which will be resolved as a continuation of a stroke
+ * component.
+ */
+#define FG_MAX_DELTA_MICKEYS ((3 * (FG_SCALE_FACTOR)) >> 1)
+
+/* Distance-squared threshold for matching a finger with a known stroke */
+#ifndef WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ
+#define WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 1000000
+#endif
+
+/* Ignore pressure spans with cumulative press. below this value. */
+#define FG_PSPAN_MIN_CUM_PRESSURE 10
+
+/* Maximum allowed width for pressure-spans.*/
+#define FG_PSPAN_MAX_WIDTH 4
+
+/* end of driver specific options */
+
+/* Tunables */
+static SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB ATP");
+
+#ifdef USB_DEBUG
+enum atp_log_level {
+ ATP_LLEVEL_DISABLED = 0,
+ ATP_LLEVEL_ERROR,
+ ATP_LLEVEL_DEBUG, /* for troubleshooting */
+ ATP_LLEVEL_INFO, /* for diagnostics */
+};
+static int atp_debug = ATP_LLEVEL_ERROR; /* the default is to only log errors */
+SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level");
+#endif /* USB_DEBUG */
+
+static u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RWTUN,
+ &atp_touch_timeout, 125000, "age threshold in microseconds for a touch");
+
+static u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RWTUN,
+ &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD,
+ "maximum time in microseconds to allow association between a double-tap and "
+ "drag gesture");
+
+static u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR;
+static int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS);
+SYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RWTUN,
+ &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor),
+ atp_sysctl_scale_factor_handler, "IU", "movement scale factor");
+
+static u_int atp_small_movement_threshold = ATP_SMALL_MOVEMENT_THRESHOLD;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RWTUN,
+ &atp_small_movement_threshold, ATP_SMALL_MOVEMENT_THRESHOLD,
+ "the small movement black-hole for filtering noise");
+
+static u_int atp_tap_minimum = 1;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RWTUN,
+ &atp_tap_minimum, 1, "Minimum number of taps before detection");
+
+/*
+ * Strokes which accumulate at least this amount of absolute movement
+ * from the aggregate of their components are considered as
+ * slides. Unit: mickeys.
+ */
+static u_int atp_slide_min_movement = 2 * ATP_SMALL_MOVEMENT_THRESHOLD;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RWTUN,
+ &atp_slide_min_movement, 2 * ATP_SMALL_MOVEMENT_THRESHOLD,
+ "strokes with at least this amt. of movement are considered slides");
+
+/*
+ * The minimum age of a stroke for it to be considered mature; this
+ * helps filter movements (noise) from immature strokes. Units: interrupts.
+ */
+static u_int atp_stroke_maturity_threshold = 4;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RWTUN,
+ &atp_stroke_maturity_threshold, 4,
+ "the minimum age of a stroke for it to be considered mature");
+
+typedef enum atp_trackpad_family {
+ TRACKPAD_FAMILY_FOUNTAIN_GEYSER,
+ TRACKPAD_FAMILY_WELLSPRING,
+ TRACKPAD_FAMILY_MAX /* keep this at the tail end of the enumeration */
+} trackpad_family_t;
+
+enum fountain_geyser_product {
+ FOUNTAIN,
+ GEYSER1,
+ GEYSER1_17inch,
+ GEYSER2,
+ GEYSER3,
+ GEYSER4,
+ FOUNTAIN_GEYSER_PRODUCT_MAX /* keep this at the end */
+};
+
+enum wellspring_product {
+ WELLSPRING1,
+ WELLSPRING2,
+ WELLSPRING3,
+ WELLSPRING4,
+ WELLSPRING4A,
+ WELLSPRING5,
+ WELLSPRING6A,
+ WELLSPRING6,
+ WELLSPRING5A,
+ WELLSPRING7,
+ WELLSPRING7A,
+ WELLSPRING8,
+ WELLSPRING_PRODUCT_MAX /* keep this at the end of the enumeration */
+};
+
+/* trackpad header types */
+enum fountain_geyser_trackpad_type {
+ FG_TRACKPAD_TYPE_GEYSER1,
+ FG_TRACKPAD_TYPE_GEYSER2,
+ FG_TRACKPAD_TYPE_GEYSER3,
+ FG_TRACKPAD_TYPE_GEYSER4,
+};
+enum wellspring_trackpad_type {
+ WSP_TRACKPAD_TYPE1, /* plain trackpad */
+ WSP_TRACKPAD_TYPE2, /* button integrated in trackpad */
+ WSP_TRACKPAD_TYPE3 /* additional header fields since June 2013 */
+};
+
+/*
+ * Trackpad family and product and family are encoded together in the
+ * driver_info value associated with a trackpad product.
+ */
+#define N_PROD_BITS 8 /* Number of bits used to encode product */
+#define ENCODE_DRIVER_INFO(FAMILY, PROD) \
+ (((FAMILY) << N_PROD_BITS) | (PROD))
+#define DECODE_FAMILY_FROM_DRIVER_INFO(INFO) ((INFO) >> N_PROD_BITS)
+#define DECODE_PRODUCT_FROM_DRIVER_INFO(INFO) \
+ ((INFO) & ((1 << N_PROD_BITS) - 1))
+
+#define FG_DRIVER_INFO(PRODUCT) \
+ ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_FOUNTAIN_GEYSER, PRODUCT)
+#define WELLSPRING_DRIVER_INFO(PRODUCT) \
+ ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_WELLSPRING, PRODUCT)
+
+/*
+ * The following structure captures the state of a pressure span along
+ * an axis. Each contact with the touchpad results in separate
+ * pressure spans along the two axes.
+ */
+typedef struct fg_pspan {
+ u_int width; /* in units of sensors */
+ u_int cum; /* cumulative compression (from all sensors) */
+ u_int cog; /* center of gravity */
+ u_int loc; /* location (scaled using the mickeys factor) */
+ boolean_t matched; /* to track pspans as they match against strokes. */
+} fg_pspan;
+
+#define FG_MAX_PSPANS_PER_AXIS 3
+#define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS)
+
+#define WELLSPRING_INTERFACE_INDEX 1
+
+/* trackpad finger data offsets, le16-aligned */
+#define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2)
+#define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2)
+#define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2)
+
+/* trackpad button data offsets */
+#define WSP_TYPE2_BUTTON_DATA_OFFSET 15
+#define WSP_TYPE3_BUTTON_DATA_OFFSET 23
+
+/* list of device capability bits */
+#define HAS_INTEGRATED_BUTTON 1
+
+/* trackpad finger structure - little endian */
+struct wsp_finger_sensor_data {
+ int16_t origin; /* zero when switching track finger */
+ int16_t abs_x; /* absolute x coordinate */
+ int16_t abs_y; /* absolute y coordinate */
+ int16_t rel_x; /* relative x coordinate */
+ int16_t rel_y; /* relative y coordinate */
+ int16_t tool_major; /* tool area, major axis */
+ int16_t tool_minor; /* tool area, minor axis */
+ int16_t orientation; /* 16384 when point, else 15 bit angle */
+ int16_t touch_major; /* touch area, major axis */
+ int16_t touch_minor; /* touch area, minor axis */
+ int16_t unused[3]; /* zeros */
+ int16_t multi; /* one finger: varies, more fingers: constant */
+} __packed;
+
+typedef struct wsp_finger {
+ /* to track fingers as they match against strokes. */
+ boolean_t matched;
+
+ /* location (scaled using the mickeys factor) */
+ int x;
+ int y;
+} wsp_finger_t;
+
+#define WSP_MAX_FINGERS 16
+#define WSP_SIZEOF_FINGER_SENSOR_DATA sizeof(struct wsp_finger_sensor_data)
+#define WSP_SIZEOF_ALL_FINGER_DATA (WSP_MAX_FINGERS * \
+ WSP_SIZEOF_FINGER_SENSOR_DATA)
+#define WSP_MAX_FINGER_ORIENTATION 16384
+
+#define ATP_SENSOR_DATA_BUF_MAX 1024
+#if (ATP_SENSOR_DATA_BUF_MAX < ((WSP_MAX_FINGERS * 14 * 2) + \
+ WSP_TYPE3_FINGER_DATA_OFFSET))
+/* note: 14 * 2 in the above is based on sizeof(struct wsp_finger_sensor_data)*/
+#error "ATP_SENSOR_DATA_BUF_MAX is too small"
+#endif
+
+#define ATP_MAX_STROKES MAX(WSP_MAX_FINGERS, FG_MAX_STROKES)
+
+#define FG_MAX_XSENSORS 26
+#define FG_MAX_YSENSORS 16
+
+/* device-specific configuration */
+struct fg_dev_params {
+ u_int data_len; /* for sensor data */
+ u_int n_xsensors;
+ u_int n_ysensors;
+ enum fountain_geyser_trackpad_type prot;
+};
+struct wsp_dev_params {
+ uint8_t caps; /* device capability bitmask */
+ uint8_t tp_type; /* type of trackpad interface */
+ uint8_t finger_data_offset; /* offset to trackpad finger data */
+};
+
+static const struct fg_dev_params fg_dev_params[FOUNTAIN_GEYSER_PRODUCT_MAX] = {
+ [FOUNTAIN] = {
+ .data_len = 81,
+ .n_xsensors = 16,
+ .n_ysensors = 16,
+ .prot = FG_TRACKPAD_TYPE_GEYSER1
+ },
+ [GEYSER1] = {
+ .data_len = 81,
+ .n_xsensors = 16,
+ .n_ysensors = 16,
+ .prot = FG_TRACKPAD_TYPE_GEYSER1
+ },
+ [GEYSER1_17inch] = {
+ .data_len = 81,
+ .n_xsensors = 26,
+ .n_ysensors = 16,
+ .prot = FG_TRACKPAD_TYPE_GEYSER1
+ },
+ [GEYSER2] = {
+ .data_len = 64,
+ .n_xsensors = 15,
+ .n_ysensors = 9,
+ .prot = FG_TRACKPAD_TYPE_GEYSER2
+ },
+ [GEYSER3] = {
+ .data_len = 64,
+ .n_xsensors = 20,
+ .n_ysensors = 10,
+ .prot = FG_TRACKPAD_TYPE_GEYSER3
+ },
+ [GEYSER4] = {
+ .data_len = 64,
+ .n_xsensors = 20,
+ .n_ysensors = 10,
+ .prot = FG_TRACKPAD_TYPE_GEYSER4
+ }
+};
+
+static const STRUCT_USB_HOST_ID fg_devs[] = {
+ /* PowerBooks Feb 2005, iBooks G4 */
+ { USB_VPI(USB_VENDOR_APPLE, 0x020e, FG_DRIVER_INFO(FOUNTAIN)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x020f, FG_DRIVER_INFO(FOUNTAIN)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x0210, FG_DRIVER_INFO(FOUNTAIN)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x030a, FG_DRIVER_INFO(FOUNTAIN)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x030b, FG_DRIVER_INFO(GEYSER1)) },
+
+ /* PowerBooks Oct 2005 */
+ { USB_VPI(USB_VENDOR_APPLE, 0x0214, FG_DRIVER_INFO(GEYSER2)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x0215, FG_DRIVER_INFO(GEYSER2)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x0216, FG_DRIVER_INFO(GEYSER2)) },
+
+ /* Core Duo MacBook & MacBook Pro */
+ { USB_VPI(USB_VENDOR_APPLE, 0x0217, FG_DRIVER_INFO(GEYSER3)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x0218, FG_DRIVER_INFO(GEYSER3)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x0219, FG_DRIVER_INFO(GEYSER3)) },
+
+ /* Core2 Duo MacBook & MacBook Pro */
+ { USB_VPI(USB_VENDOR_APPLE, 0x021a, FG_DRIVER_INFO(GEYSER4)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x021b, FG_DRIVER_INFO(GEYSER4)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x021c, FG_DRIVER_INFO(GEYSER4)) },
+
+ /* Core2 Duo MacBook3,1 */
+ { USB_VPI(USB_VENDOR_APPLE, 0x0229, FG_DRIVER_INFO(GEYSER4)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x022a, FG_DRIVER_INFO(GEYSER4)) },
+ { USB_VPI(USB_VENDOR_APPLE, 0x022b, FG_DRIVER_INFO(GEYSER4)) },
+
+ /* 17 inch PowerBook */
+ { USB_VPI(USB_VENDOR_APPLE, 0x020d, FG_DRIVER_INFO(GEYSER1_17inch)) },
+};
+
+static const struct wsp_dev_params wsp_dev_params[WELLSPRING_PRODUCT_MAX] = {
+ [WELLSPRING1] = {
+ .caps = 0,
+ .tp_type = WSP_TRACKPAD_TYPE1,
+ .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING2] = {
+ .caps = 0,
+ .tp_type = WSP_TRACKPAD_TYPE1,
+ .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING3] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING4] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING4A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING5] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING6] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING5A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING6A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING7] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING7A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE2,
+ .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET,
+ },
+ [WELLSPRING8] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = WSP_TRACKPAD_TYPE3,
+ .finger_data_offset = WSP_TYPE3_FINGER_DATA_OFFSET,
+ },
+};
+
+#define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+
+/* TODO: STRUCT_USB_HOST_ID */
+static const struct usb_device_id wsp_devs[] = {
+ /* MacbookAir1.1 */
+ ATP_DEV(APPLE, WELLSPRING_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING1)),
+ ATP_DEV(APPLE, WELLSPRING_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING1)),
+ ATP_DEV(APPLE, WELLSPRING_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING1)),
+
+ /* MacbookProPenryn, aka wellspring2 */
+ ATP_DEV(APPLE, WELLSPRING2_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING2)),
+ ATP_DEV(APPLE, WELLSPRING2_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING2)),
+ ATP_DEV(APPLE, WELLSPRING2_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING2)),
+
+ /* Macbook5,1 (unibody), aka wellspring3 */
+ ATP_DEV(APPLE, WELLSPRING3_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING3)),
+ ATP_DEV(APPLE, WELLSPRING3_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING3)),
+ ATP_DEV(APPLE, WELLSPRING3_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING3)),
+
+ /* MacbookAir3,2 (unibody), aka wellspring4 */
+ ATP_DEV(APPLE, WELLSPRING4_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4)),
+ ATP_DEV(APPLE, WELLSPRING4_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4)),
+ ATP_DEV(APPLE, WELLSPRING4_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4)),
+
+ /* MacbookAir3,1 (unibody), aka wellspring4 */
+ ATP_DEV(APPLE, WELLSPRING4A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4A)),
+ ATP_DEV(APPLE, WELLSPRING4A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4A)),
+ ATP_DEV(APPLE, WELLSPRING4A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4A)),
+
+ /* Macbook8 (unibody, March 2011) */
+ ATP_DEV(APPLE, WELLSPRING5_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5)),
+ ATP_DEV(APPLE, WELLSPRING5_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5)),
+ ATP_DEV(APPLE, WELLSPRING5_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5)),
+
+ /* MacbookAir4,1 (unibody, July 2011) */
+ ATP_DEV(APPLE, WELLSPRING6A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6A)),
+ ATP_DEV(APPLE, WELLSPRING6A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6A)),
+ ATP_DEV(APPLE, WELLSPRING6A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6A)),
+
+ /* MacbookAir4,2 (unibody, July 2011) */
+ ATP_DEV(APPLE, WELLSPRING6_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6)),
+ ATP_DEV(APPLE, WELLSPRING6_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6)),
+ ATP_DEV(APPLE, WELLSPRING6_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6)),
+
+ /* Macbook8,2 (unibody) */
+ ATP_DEV(APPLE, WELLSPRING5A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5A)),
+ ATP_DEV(APPLE, WELLSPRING5A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5A)),
+ ATP_DEV(APPLE, WELLSPRING5A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5A)),
+
+ /* MacbookPro10,1 (unibody, June 2012) */
+ /* MacbookPro11,? (unibody, June 2013) */
+ ATP_DEV(APPLE, WELLSPRING7_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7)),
+ ATP_DEV(APPLE, WELLSPRING7_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7)),
+ ATP_DEV(APPLE, WELLSPRING7_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7)),
+
+ /* MacbookPro10,2 (unibody, October 2012) */
+ ATP_DEV(APPLE, WELLSPRING7A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7A)),
+ ATP_DEV(APPLE, WELLSPRING7A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7A)),
+ ATP_DEV(APPLE, WELLSPRING7A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7A)),
+
+ /* MacbookAir6,2 (unibody, June 2013) */
+ ATP_DEV(APPLE, WELLSPRING8_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING8)),
+ ATP_DEV(APPLE, WELLSPRING8_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING8)),
+ ATP_DEV(APPLE, WELLSPRING8_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING8)),
+};
+
+typedef enum atp_stroke_type {
+ ATP_STROKE_TOUCH,
+ ATP_STROKE_SLIDE,
+} atp_stroke_type;
+
+typedef enum atp_axis {
+ X = 0,
+ Y = 1,
+ NUM_AXES
+} atp_axis;
+
+#define ATP_FIFO_BUF_SIZE 8 /* bytes */
+#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */
+
+enum {
+ ATP_INTR_DT,
+ ATP_RESET,
+ ATP_N_TRANSFER,
+};
+
+typedef struct fg_stroke_component {
+ /* Fields encapsulating the pressure-span. */
+ u_int loc; /* location (scaled) */
+ u_int cum_pressure; /* cumulative compression */
+ u_int max_cum_pressure; /* max cumulative compression */
+ boolean_t matched; /*to track components as they match against pspans.*/
+
+ int delta_mickeys; /* change in location (un-smoothened movement)*/
+} fg_stroke_component_t;
+
+/*
+ * The following structure captures a finger contact with the
+ * touchpad. A stroke comprises two p-span components and some state.
+ */
+typedef struct atp_stroke {
+ TAILQ_ENTRY(atp_stroke) entry;
+
+ atp_stroke_type type;
+ uint32_t flags; /* the state of this stroke */
+#define ATSF_ZOMBIE 0x1
+ boolean_t matched; /* to track match against fingers.*/
+
+ struct timeval ctime; /* create time; for coincident siblings. */
+
+ /*
+ * Unit: interrupts; we maintain this value in
+ * addition to 'ctime' in order to avoid the
+ * expensive call to microtime() at every
+ * interrupt.
+ */
+ uint32_t age;
+
+ /* Location */
+ int x;
+ int y;
+
+ /* Fields containing information about movement. */
+ int instantaneous_dx; /* curr. change in X location (un-smoothened) */
+ int instantaneous_dy; /* curr. change in Y location (un-smoothened) */
+ int pending_dx; /* cum. of pending short movements */
+ int pending_dy; /* cum. of pending short movements */
+ int movement_dx; /* interpreted smoothened movement */
+ int movement_dy; /* interpreted smoothened movement */
+ int cum_movement_x; /* cum. horizontal movement */
+ int cum_movement_y; /* cum. vertical movement */
+
+ /*
+ * The following member is relevant only for fountain-geyser trackpads.
+ * For these, there is the need to track pressure-spans and cumulative
+ * pressures for stroke components.
+ */
+ fg_stroke_component_t components[NUM_AXES];
+} atp_stroke_t;
+
+struct atp_softc; /* forward declaration */
+typedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len);
+
+struct atp_softc {
+ device_t sc_dev;
+ struct usb_device *sc_usb_device;
+ struct mtx sc_mutex; /* for synchronization */
+ struct usb_fifo_sc sc_fifo;
+
+#define MODE_LENGTH 8
+ char sc_mode_bytes[MODE_LENGTH]; /* device mode */
+
+ trackpad_family_t sc_family;
+ const void *sc_params; /* device configuration */
+ sensor_data_interpreter_t sensor_data_interpreter;
+
+ mousehw_t sc_hw;
+ mousemode_t sc_mode;
+ mousestatus_t sc_status;
+
+ u_int sc_state;
+#define ATP_ENABLED 0x01
+#define ATP_ZOMBIES_EXIST 0x02
+#define ATP_DOUBLE_TAP_DRAG 0x04
+#define ATP_VALID 0x08
+
+ struct usb_xfer *sc_xfer[ATP_N_TRANSFER];
+
+ u_int sc_pollrate;
+ int sc_fflags;
+
+ atp_stroke_t sc_strokes_data[ATP_MAX_STROKES];
+ TAILQ_HEAD(,atp_stroke) sc_stroke_free;
+ TAILQ_HEAD(,atp_stroke) sc_stroke_used;
+ u_int sc_n_strokes;
+
+ struct callout sc_callout;
+
+ /*
+ * button status. Set to non-zero if the mouse-button is physically
+ * pressed. This state variable is exposed through softc to allow
+ * reap_sibling_zombies to avoid registering taps while the trackpad
+ * button is pressed.
+ */
+ uint8_t sc_ibtn;
+
+ /*
+ * Time when touch zombies were last reaped; useful for detecting
+ * double-touch-n-drag.
+ */
+ struct timeval sc_touch_reap_time;
+
+ u_int sc_idlecount;
+
+ /* Regarding the data transferred from t-pad in USB INTR packets. */
+ u_int sc_expected_sensor_data_len;
+ uint8_t sc_sensor_data[ATP_SENSOR_DATA_BUF_MAX] __aligned(4);
+
+ int sc_cur_x[FG_MAX_XSENSORS]; /* current sensor readings */
+ int sc_cur_y[FG_MAX_YSENSORS];
+ int sc_base_x[FG_MAX_XSENSORS]; /* base sensor readings */
+ int sc_base_y[FG_MAX_YSENSORS];
+ int sc_pressure_x[FG_MAX_XSENSORS]; /* computed pressures */
+ int sc_pressure_y[FG_MAX_YSENSORS];
+ fg_pspan sc_pspans_x[FG_MAX_PSPANS_PER_AXIS];
+ fg_pspan sc_pspans_y[FG_MAX_PSPANS_PER_AXIS];
+};
+
+/*
+ * The last byte of the fountain-geyser sensor data contains status bits; the
+ * following values define the meanings of these bits.
+ * (only Geyser 3/4)
+ */
+enum geyser34_status_bits {
+ FG_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */
+ FG_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/
+};
+
+typedef enum interface_mode {
+ RAW_SENSOR_MODE = (uint8_t)0x01,
+ HID_MODE = (uint8_t)0x08
+} interface_mode;
+
+
+/*
+ * function prototypes
+ */
+static usb_fifo_cmd_t atp_start_read;
+static usb_fifo_cmd_t atp_stop_read;
+static usb_fifo_open_t atp_open;
+static usb_fifo_close_t atp_close;
+static usb_fifo_ioctl_t atp_ioctl;
+
+static struct usb_fifo_methods atp_fifo_methods = {
+ .f_open = &atp_open,
+ .f_close = &atp_close,
+ .f_ioctl = &atp_ioctl,
+ .f_start_read = &atp_start_read,
+ .f_stop_read = &atp_stop_read,
+ .basename[0] = ATP_DRIVER_NAME,
+};
+
+/* device initialization and shutdown */
+static usb_error_t atp_set_device_mode(struct atp_softc *, interface_mode);
+static void atp_reset_callback(struct usb_xfer *, usb_error_t);
+static int atp_enable(struct atp_softc *);
+static void atp_disable(struct atp_softc *);
+
+/* sensor interpretation */
+static void fg_interpret_sensor_data(struct atp_softc *, u_int);
+static void fg_extract_sensor_data(const int8_t *, u_int, atp_axis,
+ int *, enum fountain_geyser_trackpad_type);
+static void fg_get_pressures(int *, const int *, const int *, int);
+static void fg_detect_pspans(int *, u_int, u_int, fg_pspan *, u_int *);
+static void wsp_interpret_sensor_data(struct atp_softc *, u_int);
+
+/* movement detection */
+static boolean_t fg_match_stroke_component(fg_stroke_component_t *,
+ const fg_pspan *, atp_stroke_type);
+static void fg_match_strokes_against_pspans(struct atp_softc *,
+ atp_axis, fg_pspan *, u_int, u_int);
+static boolean_t wsp_match_strokes_against_fingers(struct atp_softc *,
+ wsp_finger_t *, u_int);
+static boolean_t fg_update_strokes(struct atp_softc *, fg_pspan *, u_int,
+ fg_pspan *, u_int);
+static boolean_t wsp_update_strokes(struct atp_softc *,
+ wsp_finger_t [WSP_MAX_FINGERS], u_int);
+static void fg_add_stroke(struct atp_softc *, const fg_pspan *, const fg_pspan *);
+static void fg_add_new_strokes(struct atp_softc *, fg_pspan *,
+ u_int, fg_pspan *, u_int);
+static void wsp_add_stroke(struct atp_softc *, const wsp_finger_t *);
+static void atp_advance_stroke_state(struct atp_softc *,
+ atp_stroke_t *, boolean_t *);
+static boolean_t atp_stroke_has_small_movement(const atp_stroke_t *);
+static void atp_update_pending_mickeys(atp_stroke_t *);
+static boolean_t atp_compute_stroke_movement(atp_stroke_t *);
+static void atp_terminate_stroke(struct atp_softc *, atp_stroke_t *);
+
+/* tap detection */
+static boolean_t atp_is_horizontal_scroll(const atp_stroke_t *);
+static boolean_t atp_is_vertical_scroll(const atp_stroke_t *);
+static void atp_reap_sibling_zombies(void *);
+static void atp_convert_to_slide(struct atp_softc *, atp_stroke_t *);
+
+/* updating fifo */
+static void atp_reset_buf(struct atp_softc *);
+static void atp_add_to_queue(struct atp_softc *, int, int, int, uint32_t);
+
+/* Device methods. */
+static device_probe_t atp_probe;
+static device_attach_t atp_attach;
+static device_detach_t atp_detach;
+static usb_callback_t atp_intr;
+
+static const struct usb_config atp_xfer_config[ATP_N_TRANSFER] = {
+ [ATP_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {
+ .pipe_bof = 1, /* block pipe on failure */
+ .short_xfer_ok = 1,
+ },
+ .bufsize = ATP_SENSOR_DATA_BUF_MAX,
+ .callback = &atp_intr,
+ },
+ [ATP_RESET] = {
+ .type = UE_CONTROL,
+ .endpoint = 0, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .bufsize = sizeof(struct usb_device_request) + MODE_LENGTH,
+ .callback = &atp_reset_callback,
+ .interval = 0, /* no pre-delay */
+ },
+};
+
+static atp_stroke_t *
+atp_alloc_stroke(struct atp_softc *sc)
+{
+ atp_stroke_t *pstroke;
+
+ pstroke = TAILQ_FIRST(&sc->sc_stroke_free);
+ if (pstroke == NULL)
+ goto done;
+
+ TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry);
+ memset(pstroke, 0, sizeof(*pstroke));
+ TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry);
+
+ sc->sc_n_strokes++;
+done:
+ return (pstroke);
+}
+
+static void
+atp_free_stroke(struct atp_softc *sc, atp_stroke_t *pstroke)
+{
+ if (pstroke == NULL)
+ return;
+
+ sc->sc_n_strokes--;
+
+ TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry);
+ TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry);
+}
+
+static void
+atp_init_stroke_pool(struct atp_softc *sc)
+{
+ u_int x;
+
+ TAILQ_INIT(&sc->sc_stroke_free);
+ TAILQ_INIT(&sc->sc_stroke_used);
+
+ sc->sc_n_strokes = 0;
+
+ memset(&sc->sc_strokes_data, 0, sizeof(sc->sc_strokes_data));
+
+ for (x = 0; x != ATP_MAX_STROKES; x++) {
+ TAILQ_INSERT_TAIL(&sc->sc_stroke_free, &sc->sc_strokes_data[x],
+ entry);
+ }
+}
+
+static usb_error_t
+atp_set_device_mode(struct atp_softc *sc, interface_mode newMode)
+{
+ uint8_t mode_value;
+ usb_error_t err;
+
+ if ((newMode != RAW_SENSOR_MODE) && (newMode != HID_MODE))
+ return (USB_ERR_INVAL);
+
+ if ((newMode == RAW_SENSOR_MODE) &&
+ (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER))
+ mode_value = (uint8_t)0x04;
+ else
+ mode_value = newMode;
+
+ err = usbd_req_get_report(sc->sc_usb_device, NULL /* mutex */,
+ sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */,
+ 0x03 /* type */, 0x00 /* id */);
+ if (err != USB_ERR_NORMAL_COMPLETION) {
+ DPRINTF("Failed to read device mode (%d)\n", err);
+ return (err);
+ }
+
+ if (sc->sc_mode_bytes[0] == mode_value)
+ return (err);
+
+ /*
+ * XXX Need to wait at least 250ms for hardware to get
+ * ready. The device mode handling appears to be handled
+ * asynchronously and we should not issue these commands too
+ * quickly.
+ */
+ pause("WHW", hz / 4);
+
+ sc->sc_mode_bytes[0] = mode_value;
+ return (usbd_req_set_report(sc->sc_usb_device, NULL /* mutex */,
+ sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */,
+ 0x03 /* type */, 0x00 /* id */));
+}
+
+static void
+atp_reset_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ usb_device_request_t req;
+ struct usb_page_cache *pc;
+ struct atp_softc *sc = usbd_xfer_softc(xfer);
+
+ uint8_t mode_value;
+ if (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER)
+ mode_value = 0x04;
+ else
+ mode_value = RAW_SENSOR_MODE;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ sc->sc_mode_bytes[0] = mode_value;
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue,
+ (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, MODE_LENGTH);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ pc = usbd_xfer_get_frame(xfer, 1);
+ usbd_copy_in(pc, 0, sc->sc_mode_bytes, MODE_LENGTH);
+
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 1, MODE_LENGTH);
+ usbd_xfer_set_frames(xfer, 2);
+ usbd_transfer_submit(xfer);
+ break;
+
+ case USB_ST_TRANSFERRED:
+ default:
+ break;
+ }
+}
+
+static int
+atp_enable(struct atp_softc *sc)
+{
+ if (sc->sc_state & ATP_ENABLED)
+ return (0);
+
+ /* reset status */
+ memset(&sc->sc_status, 0, sizeof(sc->sc_status));
+
+ atp_init_stroke_pool(sc);
+
+ sc->sc_state |= ATP_ENABLED;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n");
+ return (0);
+}
+
+static void
+atp_disable(struct atp_softc *sc)
+{
+ sc->sc_state &= ~(ATP_ENABLED | ATP_VALID);
+ DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n");
+}
+
+static void
+fg_interpret_sensor_data(struct atp_softc *sc, u_int data_len)
+{
+ u_int n_xpspans = 0;
+ u_int n_ypspans = 0;
+ uint8_t status_bits;
+
+ const struct fg_dev_params *params =
+ (const struct fg_dev_params *)sc->sc_params;
+
+ fg_extract_sensor_data(sc->sc_sensor_data, params->n_xsensors, X,
+ sc->sc_cur_x, params->prot);
+ fg_extract_sensor_data(sc->sc_sensor_data, params->n_ysensors, Y,
+ sc->sc_cur_y, params->prot);
+
+ /*
+ * If this is the initial update (from an untouched
+ * pad), we should set the base values for the sensor
+ * data; deltas with respect to these base values can
+ * be used as pressure readings subsequently.
+ */
+ status_bits = sc->sc_sensor_data[params->data_len - 1];
+ if (((params->prot == FG_TRACKPAD_TYPE_GEYSER3) ||
+ (params->prot == FG_TRACKPAD_TYPE_GEYSER4)) &&
+ ((sc->sc_state & ATP_VALID) == 0)) {
+ if (status_bits & FG_STATUS_BASE_UPDATE) {
+ memcpy(sc->sc_base_x, sc->sc_cur_x,
+ params->n_xsensors * sizeof(*sc->sc_base_x));
+ memcpy(sc->sc_base_y, sc->sc_cur_y,
+ params->n_ysensors * sizeof(*sc->sc_base_y));
+ sc->sc_state |= ATP_VALID;
+ return;
+ }
+ }
+
+ /* Get pressure readings and detect p-spans for both axes. */
+ fg_get_pressures(sc->sc_pressure_x, sc->sc_cur_x, sc->sc_base_x,
+ params->n_xsensors);
+ fg_detect_pspans(sc->sc_pressure_x, params->n_xsensors,
+ FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_x, &n_xpspans);
+ fg_get_pressures(sc->sc_pressure_y, sc->sc_cur_y, sc->sc_base_y,
+ params->n_ysensors);
+ fg_detect_pspans(sc->sc_pressure_y, params->n_ysensors,
+ FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_y, &n_ypspans);
+
+ /* Update strokes with new pspans to detect movements. */
+ if (fg_update_strokes(sc, sc->sc_pspans_x, n_xpspans, sc->sc_pspans_y, n_ypspans))
+ sc->sc_status.flags |= MOUSE_POSCHANGED;
+
+ sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0;
+ sc->sc_status.button = sc->sc_ibtn;
+
+ /*
+ * The Fountain/Geyser device continues to trigger interrupts
+ * at a fast rate even after touchpad activity has
+ * stopped. Upon detecting that the device has remained idle
+ * beyond a threshold, we reinitialize it to silence the
+ * interrupts.
+ */
+ if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) {
+ sc->sc_idlecount++;
+ if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
+ /*
+ * Use the last frame before we go idle for
+ * calibration on pads which do not send
+ * calibration frames.
+ */
+ const struct fg_dev_params *params =
+ (const struct fg_dev_params *)sc->sc_params;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
+
+ if (params->prot < FG_TRACKPAD_TYPE_GEYSER3) {
+ memcpy(sc->sc_base_x, sc->sc_cur_x,
+ params->n_xsensors * sizeof(*(sc->sc_base_x)));
+ memcpy(sc->sc_base_y, sc->sc_cur_y,
+ params->n_ysensors * sizeof(*(sc->sc_base_y)));
+ }
+
+ sc->sc_idlecount = 0;
+ usbd_transfer_start(sc->sc_xfer[ATP_RESET]);
+ }
+ } else {
+ sc->sc_idlecount = 0;
+ }
+}
+
+/*
+ * Interpret the data from the X and Y pressure sensors. This function
+ * is called separately for the X and Y sensor arrays. The data in the
+ * USB packet is laid out in the following manner:
+ *
+ * sensor_data:
+ * --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4
+ * indices: 0 1 2 3 4 5 6 7 8 ... 15 ... 20 21 22 23 24
+ *
+ * '--' (in the above) indicates that the value is unimportant.
+ *
+ * Information about the above layout was obtained from the
+ * implementation of the AppleTouch driver in Linux.
+ *
+ * parameters:
+ * sensor_data
+ * raw sensor data from the USB packet.
+ * num
+ * The number of elements in the array 'arr'.
+ * axis
+ * Axis of data to fetch
+ * arr
+ * The array to be initialized with the readings.
+ * prot
+ * The protocol to use to interpret the data
+ */
+static void
+fg_extract_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
+ int *arr, enum fountain_geyser_trackpad_type prot)
+{
+ u_int i;
+ u_int di; /* index into sensor data */
+
+ switch (prot) {
+ case FG_TRACKPAD_TYPE_GEYSER1:
+ /*
+ * For Geyser 1, the sensors are laid out in pairs
+ * every 5 bytes.
+ */
+ for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) {
+ arr[i] = sensor_data[di];
+ arr[i+8] = sensor_data[di+2];
+ if ((axis == X) && (num > 16))
+ arr[i+16] = sensor_data[di+40];
+ }
+
+ break;
+ case FG_TRACKPAD_TYPE_GEYSER2:
+ for (i = 0, di = (axis == Y) ? 1 : 19; i < num; /* empty */ ) {
+ arr[i++] = sensor_data[di++];
+ arr[i++] = sensor_data[di++];
+ di++;
+ }
+ break;
+ case FG_TRACKPAD_TYPE_GEYSER3:
+ case FG_TRACKPAD_TYPE_GEYSER4:
+ for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) {
+ arr[i++] = sensor_data[di++];
+ arr[i++] = sensor_data[di++];
+ di++;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fg_get_pressures(int *p, const int *cur, const int *base, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ p[i] = cur[i] - base[i];
+ if (p[i] > 127)
+ p[i] -= 256;
+ if (p[i] < -127)
+ p[i] += 256;
+ if (p[i] < 0)
+ p[i] = 0;
+
+ /*
+ * Shave off pressures below the noise-pressure
+ * threshold; this will reduce the contribution from
+ * lower pressure readings.
+ */
+ if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD)
+ p[i] = 0; /* filter away noise */
+ else
+ p[i] -= FG_SENSOR_NOISE_THRESHOLD;
+ }
+}
+
+static void
+fg_detect_pspans(int *p, u_int num_sensors,
+ u_int max_spans, /* max # of pspans permitted */
+ fg_pspan *spans, /* finger spans */
+ u_int *nspans_p) /* num spans detected */
+{
+ u_int i;
+ int maxp; /* max pressure seen within a span */
+ u_int num_spans = 0;
+
+ enum fg_pspan_state {
+ ATP_PSPAN_INACTIVE,
+ ATP_PSPAN_INCREASING,
+ ATP_PSPAN_DECREASING,
+ } state; /* state of the pressure span */
+
+ /*
+ * The following is a simple state machine to track
+ * the phase of the pressure span.
+ */
+ memset(spans, 0, max_spans * sizeof(fg_pspan));
+ maxp = 0;
+ state = ATP_PSPAN_INACTIVE;
+ for (i = 0; i < num_sensors; i++) {
+ if (num_spans >= max_spans)
+ break;
+
+ if (p[i] == 0) {
+ if (state == ATP_PSPAN_INACTIVE) {
+ /*
+ * There is no pressure information for this
+ * sensor, and we aren't tracking a finger.
+ */
+ continue;
+ } else {
+ state = ATP_PSPAN_INACTIVE;
+ maxp = 0;
+ num_spans++;
+ }
+ } else {
+ switch (state) {
+ case ATP_PSPAN_INACTIVE:
+ state = ATP_PSPAN_INCREASING;
+ maxp = p[i];
+ break;
+
+ case ATP_PSPAN_INCREASING:
+ if (p[i] > maxp)
+ maxp = p[i];
+ else if (p[i] <= (maxp >> 1))
+ state = ATP_PSPAN_DECREASING;
+ break;
+
+ case ATP_PSPAN_DECREASING:
+ if (p[i] > p[i - 1]) {
+ /*
+ * This is the beginning of
+ * another span; change state
+ * to give the appearance that
+ * we're starting from an
+ * inactive span, and then
+ * re-process this reading in
+ * the next iteration.
+ */
+ num_spans++;
+ state = ATP_PSPAN_INACTIVE;
+ maxp = 0;
+ i--;
+ continue;
+ }
+ break;
+ }
+
+ /* Update the finger span with this reading. */
+ spans[num_spans].width++;
+ spans[num_spans].cum += p[i];
+ spans[num_spans].cog += p[i] * (i + 1);
+ }
+ }
+ if (state != ATP_PSPAN_INACTIVE)
+ num_spans++; /* close the last finger span */
+
+ /* post-process the spans */
+ for (i = 0; i < num_spans; i++) {
+ /* filter away unwanted pressure spans */
+ if ((spans[i].cum < FG_PSPAN_MIN_CUM_PRESSURE) ||
+ (spans[i].width > FG_PSPAN_MAX_WIDTH)) {
+ if ((i + 1) < num_spans) {
+ memcpy(&spans[i], &spans[i + 1],
+ (num_spans - i - 1) * sizeof(fg_pspan));
+ i--;
+ }
+ num_spans--;
+ continue;
+ }
+
+ /* compute this span's representative location */
+ spans[i].loc = spans[i].cog * FG_SCALE_FACTOR /
+ spans[i].cum;
+
+ spans[i].matched = false; /* not yet matched against a stroke */
+ }
+
+ *nspans_p = num_spans;
+}
+
+static void
+wsp_interpret_sensor_data(struct atp_softc *sc, u_int data_len)
+{
+ const struct wsp_dev_params *params = sc->sc_params;
+ wsp_finger_t fingers[WSP_MAX_FINGERS];
+ struct wsp_finger_sensor_data *source_fingerp;
+ u_int n_source_fingers;
+ u_int n_fingers;
+ u_int i;
+
+ /* validate sensor data length */
+ if ((data_len < params->finger_data_offset) ||
+ ((data_len - params->finger_data_offset) %
+ WSP_SIZEOF_FINGER_SENSOR_DATA) != 0)
+ return;
+
+ /* compute number of source fingers */
+ n_source_fingers = (data_len - params->finger_data_offset) /
+ WSP_SIZEOF_FINGER_SENSOR_DATA;
+
+ if (n_source_fingers > WSP_MAX_FINGERS)
+ n_source_fingers = WSP_MAX_FINGERS;
+
+ /* iterate over the source data collecting useful fingers */
+ n_fingers = 0;
+ source_fingerp = (struct wsp_finger_sensor_data *)(sc->sc_sensor_data +
+ params->finger_data_offset);
+
+ for (i = 0; i < n_source_fingers; i++, source_fingerp++) {
+ /* swap endianness, if any */
+ if (le16toh(0x1234) != 0x1234) {
+ source_fingerp->origin = le16toh((uint16_t)source_fingerp->origin);
+ source_fingerp->abs_x = le16toh((uint16_t)source_fingerp->abs_x);
+ source_fingerp->abs_y = le16toh((uint16_t)source_fingerp->abs_y);
+ source_fingerp->rel_x = le16toh((uint16_t)source_fingerp->rel_x);
+ source_fingerp->rel_y = le16toh((uint16_t)source_fingerp->rel_y);
+ source_fingerp->tool_major = le16toh((uint16_t)source_fingerp->tool_major);
+ source_fingerp->tool_minor = le16toh((uint16_t)source_fingerp->tool_minor);
+ source_fingerp->orientation = le16toh((uint16_t)source_fingerp->orientation);
+ source_fingerp->touch_major = le16toh((uint16_t)source_fingerp->touch_major);
+ source_fingerp->touch_minor = le16toh((uint16_t)source_fingerp->touch_minor);
+ source_fingerp->multi = le16toh((uint16_t)source_fingerp->multi);
+ }
+
+ /* check for minium threshold */
+ if (source_fingerp->touch_major == 0)
+ continue;
+
+ fingers[n_fingers].matched = false;
+ fingers[n_fingers].x = source_fingerp->abs_x;
+ fingers[n_fingers].y = -source_fingerp->abs_y;
+
+ n_fingers++;
+ }
+
+ if ((sc->sc_n_strokes == 0) && (n_fingers == 0))
+ return;
+
+ if (wsp_update_strokes(sc, fingers, n_fingers))
+ sc->sc_status.flags |= MOUSE_POSCHANGED;
+
+ switch(params->tp_type) {
+ case WSP_TRACKPAD_TYPE2:
+ sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE2_BUTTON_DATA_OFFSET];
+ break;
+ case WSP_TRACKPAD_TYPE3:
+ sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE3_BUTTON_DATA_OFFSET];
+ break;
+ default:
+ break;
+ }
+ sc->sc_status.button = sc->sc_ibtn ? MOUSE_BUTTON1DOWN : 0;
+}
+
+/*
+ * Match a pressure-span against a stroke-component. If there is a
+ * match, update the component's state and return true.
+ */
+static boolean_t
+fg_match_stroke_component(fg_stroke_component_t *component,
+ const fg_pspan *pspan, atp_stroke_type stroke_type)
+{
+ int delta_mickeys;
+ u_int min_pressure;
+
+ delta_mickeys = pspan->loc - component->loc;
+
+ if (abs(delta_mickeys) > (int)FG_MAX_DELTA_MICKEYS)
+ return (false); /* the finger span is too far out; no match */
+
+ component->loc = pspan->loc;
+
+ /*
+ * A sudden and significant increase in a pspan's cumulative
+ * pressure indicates the incidence of a new finger
+ * contact. This usually revises the pspan's
+ * centre-of-gravity, and hence the location of any/all
+ * matching stroke component(s). But such a change should
+ * *not* be interpreted as a movement.
+ */
+ if (pspan->cum > ((3 * component->cum_pressure) >> 1))
+ delta_mickeys = 0;
+
+ component->cum_pressure = pspan->cum;
+ if (pspan->cum > component->max_cum_pressure)
+ component->max_cum_pressure = pspan->cum;
+
+ /*
+ * Disregard the component's movement if its cumulative
+ * pressure drops below a fraction of the maximum; this
+ * fraction is determined based on the stroke's type.
+ */
+ if (stroke_type == ATP_STROKE_TOUCH)
+ min_pressure = (3 * component->max_cum_pressure) >> 2;
+ else
+ min_pressure = component->max_cum_pressure >> 2;
+ if (component->cum_pressure < min_pressure)
+ delta_mickeys = 0;
+
+ component->delta_mickeys = delta_mickeys;
+ return (true);
+}
+
+static void
+fg_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
+ fg_pspan *pspans, u_int n_pspans, u_int repeat_count)
+{
+ atp_stroke_t *strokep;
+ u_int repeat_index = 0;
+ u_int i;
+
+ /* Determine the index of the multi-span. */
+ if (repeat_count) {
+ for (i = 0; i < n_pspans; i++) {
+ if (pspans[i].cum > pspans[repeat_index].cum)
+ repeat_index = i;
+ }
+ }
+
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ if (strokep->components[axis].matched)
+ continue; /* skip matched components */
+
+ for (i = 0; i < n_pspans; i++) {
+ if (pspans[i].matched)
+ continue; /* skip matched pspans */
+
+ if (fg_match_stroke_component(
+ &strokep->components[axis], &pspans[i],
+ strokep->type)) {
+
+ /* There is a match. */
+ strokep->components[axis].matched = true;
+
+ /* Take care to repeat at the multi-span. */
+ if ((repeat_count > 0) && (i == repeat_index))
+ repeat_count--;
+ else
+ pspans[i].matched = true;
+
+ break; /* skip to the next strokep */
+ }
+ } /* loop over pspans */
+ } /* loop over strokes */
+}
+
+static boolean_t
+wsp_match_strokes_against_fingers(struct atp_softc *sc,
+ wsp_finger_t *fingers, u_int n_fingers)
+{
+ boolean_t movement = false;
+ atp_stroke_t *strokep;
+ u_int i;
+
+ /* reset the matched status for all strokes */
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry)
+ strokep->matched = false;
+
+ for (i = 0; i != n_fingers; i++) {
+ u_int least_distance_sq = WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ;
+ atp_stroke_t *strokep_best = NULL;
+
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ int instantaneous_dx;
+ int instantaneous_dy;
+ u_int d_squared;
+
+ if (strokep->matched)
+ continue;
+
+ instantaneous_dx = fingers[i].x - strokep->x;
+ instantaneous_dy = fingers[i].y - strokep->y;
+
+ /* skip strokes which are far away */
+ d_squared =
+ (instantaneous_dx * instantaneous_dx) +
+ (instantaneous_dy * instantaneous_dy);
+
+ if (d_squared < least_distance_sq) {
+ least_distance_sq = d_squared;
+ strokep_best = strokep;
+ }
+ }
+
+ strokep = strokep_best;
+
+ if (strokep != NULL) {
+ fingers[i].matched = true;
+
+ strokep->matched = true;
+ strokep->instantaneous_dx = fingers[i].x - strokep->x;
+ strokep->instantaneous_dy = fingers[i].y - strokep->y;
+ strokep->x = fingers[i].x;
+ strokep->y = fingers[i].y;
+
+ atp_advance_stroke_state(sc, strokep, &movement);
+ }
+ }
+ return (movement);
+}
+
+/*
+ * Update strokes by matching against current pressure-spans.
+ * Return true if any movement is detected.
+ */
+static boolean_t
+fg_update_strokes(struct atp_softc *sc, fg_pspan *pspans_x,
+ u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans)
+{
+ atp_stroke_t *strokep;
+ atp_stroke_t *strokep_next;
+ boolean_t movement = false;
+ u_int repeat_count = 0;
+ u_int i;
+ u_int j;
+
+ /* Reset X and Y components of all strokes as unmatched. */
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ strokep->components[X].matched = false;
+ strokep->components[Y].matched = false;
+ }
+
+ /*
+ * Usually, the X and Y pspans come in pairs (the common case
+ * being a single pair). It is possible, however, that
+ * multiple contacts resolve to a single pspan along an
+ * axis, as illustrated in the following:
+ *
+ * F = finger-contact
+ *
+ * pspan pspan
+ * +-----------------------+
+ * | . . |
+ * | . . |
+ * | . . |
+ * | . . |
+ * pspan |.........F......F |
+ * | |
+ * | |
+ * | |
+ * +-----------------------+
+ *
+ *
+ * The above case can be detected by a difference in the
+ * number of X and Y pspans. When this happens, X and Y pspans
+ * aren't easy to pair or match against strokes.
+ *
+ * When X and Y pspans differ in number, the axis with the
+ * smaller number of pspans is regarded as having a repeating
+ * pspan (or a multi-pspan)--in the above illustration, the
+ * Y-axis has a repeating pspan. Our approach is to try to
+ * match the multi-pspan repeatedly against strokes. The
+ * difference between the number of X and Y pspans gives us a
+ * crude repeat_count for matching multi-pspans--i.e. the
+ * multi-pspan along the Y axis (above) has a repeat_count of 1.
+ */
+ repeat_count = abs(n_xpspans - n_ypspans);
+
+ fg_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans,
+ (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ?
+ repeat_count : 0));
+ fg_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans,
+ (((repeat_count != 0) && (n_ypspans < n_xpspans)) ?
+ repeat_count : 0));
+
+ /* Update the state of strokes based on the above pspan matches. */
+ TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) {
+
+ if (strokep->components[X].matched &&
+ strokep->components[Y].matched) {
+ strokep->matched = true;
+ strokep->instantaneous_dx =
+ strokep->components[X].delta_mickeys;
+ strokep->instantaneous_dy =
+ strokep->components[Y].delta_mickeys;
+ atp_advance_stroke_state(sc, strokep, &movement);
+ } else {
+ /*
+ * At least one component of this stroke
+ * didn't match against current pspans;
+ * terminate it.
+ */
+ atp_terminate_stroke(sc, strokep);
+ }
+ }
+
+ /* Add new strokes for pairs of unmatched pspans */
+ for (i = 0; i < n_xpspans; i++) {
+ if (pspans_x[i].matched == false) break;
+ }
+ for (j = 0; j < n_ypspans; j++) {
+ if (pspans_y[j].matched == false) break;
+ }
+ if ((i < n_xpspans) && (j < n_ypspans)) {
+#ifdef USB_DEBUG
+ if (atp_debug >= ATP_LLEVEL_INFO) {
+ printf("unmatched pspans:");
+ for (; i < n_xpspans; i++) {
+ if (pspans_x[i].matched)
+ continue;
+ printf(" X:[loc:%u,cum:%u]",
+ pspans_x[i].loc, pspans_x[i].cum);
+ }
+ for (; j < n_ypspans; j++) {
+ if (pspans_y[j].matched)
+ continue;
+ printf(" Y:[loc:%u,cum:%u]",
+ pspans_y[j].loc, pspans_y[j].cum);
+ }
+ printf("\n");
+ }
+#endif /* USB_DEBUG */
+ if ((n_xpspans == 1) && (n_ypspans == 1))
+ /* The common case of a single pair of new pspans. */
+ fg_add_stroke(sc, &pspans_x[0], &pspans_y[0]);
+ else
+ fg_add_new_strokes(sc, pspans_x, n_xpspans,
+ pspans_y, n_ypspans);
+ }
+
+#ifdef USB_DEBUG
+ if (atp_debug >= ATP_LLEVEL_INFO) {
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ printf(" %s%clc:%u,dm:%d,cum:%d,max:%d,%c"
+ ",%clc:%u,dm:%d,cum:%d,max:%d,%c",
+ (strokep->flags & ATSF_ZOMBIE) ? "zomb:" : "",
+ (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<',
+ strokep->components[X].loc,
+ strokep->components[X].delta_mickeys,
+ strokep->components[X].cum_pressure,
+ strokep->components[X].max_cum_pressure,
+ (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>',
+ (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<',
+ strokep->components[Y].loc,
+ strokep->components[Y].delta_mickeys,
+ strokep->components[Y].cum_pressure,
+ strokep->components[Y].max_cum_pressure,
+ (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>');
+ }
+ if (TAILQ_FIRST(&sc->sc_stroke_used) != NULL)
+ printf("\n");
+ }
+#endif /* USB_DEBUG */
+ return (movement);
+}
+
+/*
+ * Update strokes by matching against current pressure-spans.
+ * Return true if any movement is detected.
+ */
+static boolean_t
+wsp_update_strokes(struct atp_softc *sc, wsp_finger_t *fingers, u_int n_fingers)
+{
+ boolean_t movement = false;
+ atp_stroke_t *strokep_next;
+ atp_stroke_t *strokep;
+ u_int i;
+
+ if (sc->sc_n_strokes > 0) {
+ movement = wsp_match_strokes_against_fingers(
+ sc, fingers, n_fingers);
+
+ /* handle zombie strokes */
+ TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) {
+ if (strokep->matched)
+ continue;
+ atp_terminate_stroke(sc, strokep);
+ }
+ }
+
+ /* initialize unmatched fingers as strokes */
+ for (i = 0; i != n_fingers; i++) {
+ if (fingers[i].matched)
+ continue;
+
+ wsp_add_stroke(sc, fingers + i);
+ }
+ return (movement);
+}
+
+/* Initialize a stroke using a pressure-span. */
+static void
+fg_add_stroke(struct atp_softc *sc, const fg_pspan *pspan_x,
+ const fg_pspan *pspan_y)
+{
+ atp_stroke_t *strokep;
+
+ strokep = atp_alloc_stroke(sc);
+ if (strokep == NULL)
+ return;
+
+ /*
+ * Strokes begin as potential touches. If a stroke survives
+ * longer than a threshold, or if it records significant
+ * cumulative movement, then it is considered a 'slide'.
+ */
+ strokep->type = ATP_STROKE_TOUCH;
+ strokep->matched = false;
+ microtime(&strokep->ctime);
+ strokep->age = 1; /* number of interrupts */
+ strokep->x = pspan_x->loc;
+ strokep->y = pspan_y->loc;
+
+ strokep->components[X].loc = pspan_x->loc;
+ strokep->components[X].cum_pressure = pspan_x->cum;
+ strokep->components[X].max_cum_pressure = pspan_x->cum;
+ strokep->components[X].matched = true;
+
+ strokep->components[Y].loc = pspan_y->loc;
+ strokep->components[Y].cum_pressure = pspan_y->cum;
+ strokep->components[Y].max_cum_pressure = pspan_y->cum;
+ strokep->components[Y].matched = true;
+
+ if (sc->sc_n_strokes > 1) {
+ /* Reset double-tap-n-drag if we have more than one strokes. */
+ sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
+ }
+
+ DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n",
+ strokep->components[X].loc,
+ strokep->components[Y].loc,
+ (u_int)strokep->ctime.tv_sec,
+ (unsigned long int)strokep->ctime.tv_usec);
+}
+
+static void
+fg_add_new_strokes(struct atp_softc *sc, fg_pspan *pspans_x,
+ u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans)
+{
+ fg_pspan spans[2][FG_MAX_PSPANS_PER_AXIS];
+ u_int nspans[2];
+ u_int i;
+ u_int j;
+
+ /* Copy unmatched pspans into the local arrays. */
+ for (i = 0, nspans[X] = 0; i < n_xpspans; i++) {
+ if (pspans_x[i].matched == false) {
+ spans[X][nspans[X]] = pspans_x[i];
+ nspans[X]++;
+ }
+ }
+ for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) {
+ if (pspans_y[j].matched == false) {
+ spans[Y][nspans[Y]] = pspans_y[j];
+ nspans[Y]++;
+ }
+ }
+
+ if (nspans[X] == nspans[Y]) {
+ /* Create new strokes from pairs of unmatched pspans */
+ for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++)
+ fg_add_stroke(sc, &spans[X][i], &spans[Y][j]);
+ } else {
+ u_int cum = 0;
+ atp_axis repeat_axis; /* axis with multi-pspans */
+ u_int repeat_count; /* repeat count for the multi-pspan*/
+ u_int repeat_index = 0; /* index of the multi-span */
+
+ repeat_axis = (nspans[X] > nspans[Y]) ? Y : X;
+ repeat_count = abs(nspans[X] - nspans[Y]);
+ for (i = 0; i < nspans[repeat_axis]; i++) {
+ if (spans[repeat_axis][i].cum > cum) {
+ repeat_index = i;
+ cum = spans[repeat_axis][i].cum;
+ }
+ }
+
+ /* Create new strokes from pairs of unmatched pspans */
+ i = 0, j = 0;
+ for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
+ fg_add_stroke(sc, &spans[X][i], &spans[Y][j]);
+
+ /* Take care to repeat at the multi-pspan. */
+ if (repeat_count > 0) {
+ if ((repeat_axis == X) &&
+ (repeat_index == i)) {
+ i--; /* counter loop increment */
+ repeat_count--;
+ } else if ((repeat_axis == Y) &&
+ (repeat_index == j)) {
+ j--; /* counter loop increment */
+ repeat_count--;
+ }
+ }
+ }
+ }
+}
+
+/* Initialize a stroke from an unmatched finger. */
+static void
+wsp_add_stroke(struct atp_softc *sc, const wsp_finger_t *fingerp)
+{
+ atp_stroke_t *strokep;
+
+ strokep = atp_alloc_stroke(sc);
+ if (strokep == NULL)
+ return;
+
+ /*
+ * Strokes begin as potential touches. If a stroke survives
+ * longer than a threshold, or if it records significant
+ * cumulative movement, then it is considered a 'slide'.
+ */
+ strokep->type = ATP_STROKE_TOUCH;
+ strokep->matched = true;
+ microtime(&strokep->ctime);
+ strokep->age = 1; /* number of interrupts */
+ strokep->x = fingerp->x;
+ strokep->y = fingerp->y;
+
+ /* Reset double-tap-n-drag if we have more than one strokes. */
+ if (sc->sc_n_strokes > 1)
+ sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y);
+}
+
+static void
+atp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep,
+ boolean_t *movementp)
+{
+ /* Revitalize stroke if it had previously been marked as a zombie. */
+ if (strokep->flags & ATSF_ZOMBIE)
+ strokep->flags &= ~ATSF_ZOMBIE;
+
+ strokep->age++;
+ if (strokep->age <= atp_stroke_maturity_threshold) {
+ /* Avoid noise from immature strokes. */
+ strokep->instantaneous_dx = 0;
+ strokep->instantaneous_dy = 0;
+ }
+
+ if (atp_compute_stroke_movement(strokep))
+ *movementp = true;
+
+ if (strokep->type != ATP_STROKE_TOUCH)
+ return;
+
+ /* Convert touch strokes to slides upon detecting movement or age. */
+ if ((abs(strokep->cum_movement_x) > atp_slide_min_movement) ||
+ (abs(strokep->cum_movement_y) > atp_slide_min_movement))
+ atp_convert_to_slide(sc, strokep);
+ else {
+ /* Compute the stroke's age. */
+ struct timeval tdiff;
+ getmicrotime(&tdiff);
+ if (timevalcmp(&tdiff, &strokep->ctime, >)) {
+ timevalsub(&tdiff, &strokep->ctime);
+
+ if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
+ ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
+ (tdiff.tv_usec >= (atp_touch_timeout % 1000000))))
+ atp_convert_to_slide(sc, strokep);
+ }
+ }
+}
+
+static boolean_t
+atp_stroke_has_small_movement(const atp_stroke_t *strokep)
+{
+ return (((u_int)abs(strokep->instantaneous_dx) <=
+ atp_small_movement_threshold) &&
+ ((u_int)abs(strokep->instantaneous_dy) <=
+ atp_small_movement_threshold));
+}
+
+/*
+ * Accumulate instantaneous changes into the stroke's 'pending' bucket; if
+ * the aggregate exceeds the small_movement_threshold, then retain
+ * instantaneous changes for later.
+ */
+static void
+atp_update_pending_mickeys(atp_stroke_t *strokep)
+{
+ /* accumulate instantaneous movement */
+ strokep->pending_dx += strokep->instantaneous_dx;
+ strokep->pending_dy += strokep->instantaneous_dy;
+
+#define UPDATE_INSTANTANEOUS_AND_PENDING(I, P) \
+ if (abs((P)) <= atp_small_movement_threshold) \
+ (I) = 0; /* clobber small movement */ \
+ else { \
+ if ((I) > 0) { \
+ /* \
+ * Round up instantaneous movement to the nearest \
+ * ceiling. This helps preserve small mickey \
+ * movements from being lost in following scaling \
+ * operation. \
+ */ \
+ (I) = (((I) + (atp_mickeys_scale_factor - 1)) / \
+ atp_mickeys_scale_factor) * \
+ atp_mickeys_scale_factor; \
+ \
+ /* \
+ * Deduct the rounded mickeys from pending mickeys. \
+ * Note: we multiply by 2 to offset the previous \
+ * accumulation of instantaneous movement into \
+ * pending. \
+ */ \
+ (P) -= ((I) << 1); \
+ \
+ /* truncate pending to 0 if it becomes negative. */ \
+ (P) = imax((P), 0); \
+ } else { \
+ /* \
+ * Round down instantaneous movement to the nearest \
+ * ceiling. This helps preserve small mickey \
+ * movements from being lost in following scaling \
+ * operation. \
+ */ \
+ (I) = (((I) - (atp_mickeys_scale_factor - 1)) / \
+ atp_mickeys_scale_factor) * \
+ atp_mickeys_scale_factor; \
+ \
+ /* \
+ * Deduct the rounded mickeys from pending mickeys. \
+ * Note: we multiply by 2 to offset the previous \
+ * accumulation of instantaneous movement into \
+ * pending. \
+ */ \
+ (P) -= ((I) << 1); \
+ \
+ /* truncate pending to 0 if it becomes positive. */ \
+ (P) = imin((P), 0); \
+ } \
+ }
+
+ UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dx,
+ strokep->pending_dx);
+ UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dy,
+ strokep->pending_dy);
+}
+
+/*
+ * Compute a smoothened value for the stroke's movement from
+ * instantaneous changes in the X and Y components.
+ */
+static boolean_t
+atp_compute_stroke_movement(atp_stroke_t *strokep)
+{
+ /*
+ * Short movements are added first to the 'pending' bucket,
+ * and then acted upon only when their aggregate exceeds a
+ * threshold. This has the effect of filtering away movement
+ * noise.
+ */
+ if (atp_stroke_has_small_movement(strokep))
+ atp_update_pending_mickeys(strokep);
+ else { /* large movement */
+ /* clear away any pending mickeys if there are large movements*/
+ strokep->pending_dx = 0;
+ strokep->pending_dy = 0;
+ }
+
+ /* scale movement */
+ strokep->movement_dx = (strokep->instantaneous_dx) /
+ (int)atp_mickeys_scale_factor;
+ strokep->movement_dy = (strokep->instantaneous_dy) /
+ (int)atp_mickeys_scale_factor;
+
+ if ((abs(strokep->instantaneous_dx) >= ATP_FAST_MOVEMENT_TRESHOLD) ||
+ (abs(strokep->instantaneous_dy) >= ATP_FAST_MOVEMENT_TRESHOLD)) {
+ strokep->movement_dx <<= 1;
+ strokep->movement_dy <<= 1;
+ }
+
+ strokep->cum_movement_x += strokep->movement_dx;
+ strokep->cum_movement_y += strokep->movement_dy;
+
+ return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0));
+}
+
+/*
+ * Terminate a stroke. Aside from immature strokes, a slide or touch is
+ * retained as a zombies so as to reap all their termination siblings
+ * together; this helps establish the number of fingers involved at the
+ * end of a multi-touch gesture.
+ */
+static void
+atp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep)
+{
+ if (strokep->flags & ATSF_ZOMBIE)
+ return;
+
+ /* Drop immature strokes rightaway. */
+ if (strokep->age <= atp_stroke_maturity_threshold) {
+ atp_free_stroke(sc, strokep);
+ return;
+ }
+
+ strokep->flags |= ATSF_ZOMBIE;
+ sc->sc_state |= ATP_ZOMBIES_EXIST;
+
+ callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL,
+ atp_reap_sibling_zombies, sc);
+
+ /*
+ * Reset the double-click-n-drag at the termination of any
+ * slide stroke.
+ */
+ if (strokep->type == ATP_STROKE_SLIDE)
+ sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
+}
+
+static boolean_t
+atp_is_horizontal_scroll(const atp_stroke_t *strokep)
+{
+ if (abs(strokep->cum_movement_x) < atp_slide_min_movement)
+ return (false);
+ if (strokep->cum_movement_y == 0)
+ return (true);
+ return (abs(strokep->cum_movement_x / strokep->cum_movement_y) >= 4);
+}
+
+static boolean_t
+atp_is_vertical_scroll(const atp_stroke_t *strokep)
+{
+ if (abs(strokep->cum_movement_y) < atp_slide_min_movement)
+ return (false);
+ if (strokep->cum_movement_x == 0)
+ return (true);
+ return (abs(strokep->cum_movement_y / strokep->cum_movement_x) >= 4);
+}
+
+static void
+atp_reap_sibling_zombies(void *arg)
+{
+ struct atp_softc *sc = (struct atp_softc *)arg;
+ u_int8_t n_touches_reaped = 0;
+ u_int8_t n_slides_reaped = 0;
+ u_int8_t n_horizontal_scrolls = 0;
+ u_int8_t n_vertical_scrolls = 0;
+ int horizontal_scroll = 0;
+ int vertical_scroll = 0;
+ atp_stroke_t *strokep;
+ atp_stroke_t *strokep_next;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "\n");
+
+ TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) {
+ if ((strokep->flags & ATSF_ZOMBIE) == 0)
+ continue;
+
+ if (strokep->type == ATP_STROKE_TOUCH) {
+ n_touches_reaped++;
+ } else {
+ n_slides_reaped++;
+
+ if (atp_is_horizontal_scroll(strokep)) {
+ n_horizontal_scrolls++;
+ horizontal_scroll += strokep->cum_movement_x;
+ } else if (atp_is_vertical_scroll(strokep)) {
+ n_vertical_scrolls++;
+ vertical_scroll += strokep->cum_movement_y;
+ }
+ }
+
+ atp_free_stroke(sc, strokep);
+ }
+
+ DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n",
+ n_touches_reaped + n_slides_reaped);
+ sc->sc_state &= ~ATP_ZOMBIES_EXIST;
+
+ /* No further processing necessary if physical button is depressed. */
+ if (sc->sc_ibtn != 0)
+ return;
+
+ if ((n_touches_reaped == 0) && (n_slides_reaped == 0))
+ return;
+
+ /* Add a pair of virtual button events (button-down and button-up) if
+ * the physical button isn't pressed. */
+ if (n_touches_reaped != 0) {
+ if (n_touches_reaped < atp_tap_minimum)
+ return;
+
+ switch (n_touches_reaped) {
+ case 1:
+ atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN);
+ microtime(&sc->sc_touch_reap_time); /* remember this time */
+ break;
+ case 2:
+ atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN);
+ break;
+ case 3:
+ atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN);
+ break;
+ default:
+ /* we handle taps of only up to 3 fingers */
+ return;
+ }
+ atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */
+
+ } else if ((n_slides_reaped == 2) && (n_horizontal_scrolls == 2)) {
+ if (horizontal_scroll < 0)
+ atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON4DOWN);
+ else
+ atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON5DOWN);
+ atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */
+ }
+}
+
+/* Switch a given touch stroke to being a slide. */
+static void
+atp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep)
+{
+ strokep->type = ATP_STROKE_SLIDE;
+
+ /* Are we at the beginning of a double-click-n-drag? */
+ if ((sc->sc_n_strokes == 1) &&
+ ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
+ timevalcmp(&strokep->ctime, &sc->sc_touch_reap_time, >)) {
+ struct timeval delta;
+ struct timeval window = {
+ atp_double_tap_threshold / 1000000,
+ atp_double_tap_threshold % 1000000
+ };
+
+ delta = strokep->ctime;
+ timevalsub(&delta, &sc->sc_touch_reap_time);
+ if (timevalcmp(&delta, &window, <=))
+ sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
+ }
+}
+
+static void
+atp_reset_buf(struct atp_softc *sc)
+{
+ /* reset read queue */
+ usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
+}
+
+static void
+atp_add_to_queue(struct atp_softc *sc, int dx, int dy, int dz,
+ uint32_t buttons_in)
+{
+ uint32_t buttons_out;
+ uint8_t buf[8];
+
+ dx = imin(dx, 254); dx = imax(dx, -256);
+ dy = imin(dy, 254); dy = imax(dy, -256);
+ dz = imin(dz, 126); dz = imax(dz, -128);
+
+ buttons_out = MOUSE_MSC_BUTTONS;
+ if (buttons_in & MOUSE_BUTTON1DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON1UP;
+ else if (buttons_in & MOUSE_BUTTON2DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON2UP;
+ else if (buttons_in & MOUSE_BUTTON3DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON3UP;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n",
+ dx, dy, buttons_out);
+
+ /* Encode the mouse data in standard format; refer to mouse(4) */
+ buf[0] = sc->sc_mode.syncmask[1];
+ buf[0] |= buttons_out;
+ buf[1] = dx >> 1;
+ buf[2] = dy >> 1;
+ buf[3] = dx - (dx >> 1);
+ buf[4] = dy - (dy >> 1);
+ /* Encode extra bytes for level 1 */
+ if (sc->sc_mode.level == 1) {
+ buf[5] = dz >> 1;
+ buf[6] = dz - (dz >> 1);
+ buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS);
+ }
+
+ usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
+ sc->sc_mode.packetsize, 1);
+}
+
+static int
+atp_probe(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ if (uaa->info.bInterfaceClass != UICLASS_HID)
+ return (ENXIO);
+ /*
+ * Note: for some reason, the check
+ * (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) doesn't hold true
+ * for wellspring trackpads, so we've removed it from the common path.
+ */
+
+ if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0)
+ return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ?
+ 0 : ENXIO);
+
+ if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0)
+ if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX)
+ return (0);
+
+ return (ENXIO);
+}
+
+static int
+atp_attach(device_t dev)
+{
+ struct atp_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_error_t err;
+ void *descriptor_ptr = NULL;
+ uint16_t descriptor_len;
+ unsigned long di;
+
+ DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc);
+
+ sc->sc_dev = dev;
+ sc->sc_usb_device = uaa->device;
+
+ /* Get HID descriptor */
+ if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr,
+ &descriptor_len, M_TEMP, uaa->info.bIfaceIndex) !=
+ USB_ERR_NORMAL_COMPLETION)
+ return (ENXIO);
+
+ /* Get HID report descriptor length */
+ sc->sc_expected_sensor_data_len = hid_report_size(descriptor_ptr,
+ descriptor_len, hid_input, NULL);
+ free(descriptor_ptr, M_TEMP);
+
+ if ((sc->sc_expected_sensor_data_len <= 0) ||
+ (sc->sc_expected_sensor_data_len > ATP_SENSOR_DATA_BUF_MAX)) {
+ DPRINTF("atp_attach: datalength invalid or too large: %d\n",
+ sc->sc_expected_sensor_data_len);
+ return (ENXIO);
+ }
+
+ /*
+ * By default the touchpad behaves like an HID device, sending
+ * packets with reportID = 2. Such reports contain only
+ * limited information--they encode movement deltas and button
+ * events,--but do not include data from the pressure
+ * sensors. The device input mode can be switched from HID
+ * reports to raw sensor data using vendor-specific USB
+ * control commands.
+ */
+ if ((err = atp_set_device_mode(sc, RAW_SENSOR_MODE)) != 0) {
+ DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err);
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE);
+
+ di = USB_GET_DRIVER_INFO(uaa);
+
+ sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di);
+
+ switch(sc->sc_family) {
+ case TRACKPAD_FAMILY_FOUNTAIN_GEYSER:
+ sc->sc_params =
+ &fg_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)];
+ sc->sensor_data_interpreter = fg_interpret_sensor_data;
+ break;
+ case TRACKPAD_FAMILY_WELLSPRING:
+ sc->sc_params =
+ &wsp_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)];
+ sc->sensor_data_interpreter = wsp_interpret_sensor_data;
+ break;
+ default:
+ goto detach;
+ }
+
+ err = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, atp_xfer_config,
+ ATP_N_TRANSFER, sc, &sc->sc_mutex);
+ if (err) {
+ DPRINTF("error=%s\n", usbd_errstr(err));
+ goto detach;
+ }
+
+ if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex,
+ &atp_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), -1, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644)) {
+ goto detach;
+ }
+
+ device_set_usb_desc(dev);
+
+ sc->sc_hw.buttons = 3;
+ sc->sc_hw.iftype = MOUSE_IF_USB;
+ sc->sc_hw.type = MOUSE_PAD;
+ sc->sc_hw.model = MOUSE_MODEL_GENERIC;
+ sc->sc_hw.hwid = 0;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.rate = -1;
+ sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ sc->sc_mode.accelfactor = 0;
+ sc->sc_mode.level = 0;
+
+ sc->sc_state = 0;
+ sc->sc_ibtn = 0;
+
+ callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0);
+
+ return (0);
+
+detach:
+ atp_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+atp_detach(device_t dev)
+{
+ struct atp_softc *sc;
+
+ sc = device_get_softc(dev);
+ atp_set_device_mode(sc, HID_MODE);
+
+ mtx_lock(&sc->sc_mutex);
+ callout_drain(&sc->sc_callout);
+ if (sc->sc_state & ATP_ENABLED)
+ atp_disable(sc);
+ mtx_unlock(&sc->sc_mutex);
+
+ usb_fifo_detach(&sc->sc_fifo);
+
+ usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER);
+
+ mtx_destroy(&sc->sc_mutex);
+
+ return (0);
+}
+
+static void
+atp_intr(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct atp_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_page_cache *pc;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, sc->sc_sensor_data, len);
+ if (len < sc->sc_expected_sensor_data_len) {
+ /* make sure we don't process old data */
+ memset(sc->sc_sensor_data + len, 0,
+ sc->sc_expected_sensor_data_len - len);
+ }
+
+ sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED |
+ MOUSE_POSCHANGED);
+ sc->sc_status.obutton = sc->sc_status.button;
+
+ (sc->sensor_data_interpreter)(sc, len);
+
+ if (sc->sc_status.button != 0) {
+ /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */
+ sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
+ } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) {
+ /* Assume a button-press with DOUBLE_TAP_N_DRAG. */
+ sc->sc_status.button = MOUSE_BUTTON1DOWN;
+ }
+
+ sc->sc_status.flags |=
+ sc->sc_status.button ^ sc->sc_status.obutton;
+ if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) {
+ DPRINTFN(ATP_LLEVEL_INFO, "button %s\n",
+ ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ?
+ "pressed" : "released"));
+ }
+
+ if (sc->sc_status.flags & (MOUSE_POSCHANGED |
+ MOUSE_STDBUTTONSCHANGED)) {
+
+ atp_stroke_t *strokep;
+ u_int8_t n_movements = 0;
+ int dx = 0;
+ int dy = 0;
+ int dz = 0;
+
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ if (strokep->flags & ATSF_ZOMBIE)
+ continue;
+
+ dx += strokep->movement_dx;
+ dy += strokep->movement_dy;
+ if (strokep->movement_dx ||
+ strokep->movement_dy)
+ n_movements++;
+ }
+
+ /* average movement if multiple strokes record motion.*/
+ if (n_movements > 1) {
+ dx /= (int)n_movements;
+ dy /= (int)n_movements;
+ }
+
+ /* detect multi-finger vertical scrolls */
+ if (n_movements >= 2) {
+ boolean_t all_vertical_scrolls = true;
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ if (strokep->flags & ATSF_ZOMBIE)
+ continue;
+
+ if (!atp_is_vertical_scroll(strokep))
+ all_vertical_scrolls = false;
+ }
+ if (all_vertical_scrolls) {
+ dz = dy;
+ dy = dx = 0;
+ }
+ }
+
+ sc->sc_status.dx += dx;
+ sc->sc_status.dy += dy;
+ sc->sc_status.dz += dz;
+ atp_add_to_queue(sc, dx, -dy, -dz, sc->sc_status.button);
+ }
+
+ case USB_ST_SETUP:
+ tr_setup:
+ /* check if we can put more data into the FIFO */
+ if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
+ usbd_xfer_set_frame_len(xfer, 0,
+ sc->sc_expected_sensor_data_len);
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+atp_start_read(struct usb_fifo *fifo)
+{
+ struct atp_softc *sc = usb_fifo_softc(fifo);
+ int rate;
+
+ /* Check if we should override the default polling interval */
+ rate = sc->sc_pollrate;
+ /* Range check rate */
+ if (rate > 1000)
+ rate = 1000;
+ /* Check for set rate */
+ if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) {
+ /* Stop current transfer, if any */
+ usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
+ /* Set new interval */
+ usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate);
+ /* Only set pollrate once */
+ sc->sc_pollrate = 0;
+ }
+
+ usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]);
+}
+
+static void
+atp_stop_read(struct usb_fifo *fifo)
+{
+ struct atp_softc *sc = usb_fifo_softc(fifo);
+ usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
+}
+
+static int
+atp_open(struct usb_fifo *fifo, int fflags)
+{
+ struct atp_softc *sc = usb_fifo_softc(fifo);
+
+ /* check for duplicate open, should not happen */
+ if (sc->sc_fflags & fflags)
+ return (EBUSY);
+
+ /* check for first open */
+ if (sc->sc_fflags == 0) {
+ int rc;
+ if ((rc = atp_enable(sc)) != 0)
+ return (rc);
+ }
+
+ if (fflags & FREAD) {
+ if (usb_fifo_alloc_buffer(fifo,
+ ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+
+ sc->sc_fflags |= (fflags & (FREAD | FWRITE));
+ return (0);
+}
+
+static void
+atp_close(struct usb_fifo *fifo, int fflags)
+{
+ struct atp_softc *sc = usb_fifo_softc(fifo);
+ if (fflags & FREAD)
+ usb_fifo_free_buffer(fifo);
+
+ sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+ if (sc->sc_fflags == 0) {
+ atp_disable(sc);
+ }
+}
+
+static int
+atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
+{
+ struct atp_softc *sc = usb_fifo_softc(fifo);
+ mousemode_t mode;
+ int error = 0;
+
+ mtx_lock(&sc->sc_mutex);
+
+ switch(cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->sc_hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->sc_mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1)
+ /* Don't change the current setting */
+ ;
+ else if ((mode.level < 0) || (mode.level > 1)) {
+ error = EINVAL;
+ break;
+ }
+ sc->sc_mode.level = mode.level;
+ sc->sc_pollrate = mode.rate;
+ sc->sc_hw.buttons = 3;
+
+ if (sc->sc_mode.level == 0) {
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ atp_reset_buf(sc);
+ break;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->sc_mode.level;
+ break;
+ case MOUSE_SETLEVEL:
+ if ((*(int *)addr < 0) || (*(int *)addr > 1)) {
+ error = EINVAL;
+ break;
+ }
+ sc->sc_mode.level = *(int *)addr;
+ sc->sc_hw.buttons = 3;
+
+ if (sc->sc_mode.level == 0) {
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ atp_reset_buf(sc);
+ break;
+ case MOUSE_GETSTATUS: {
+ mousestatus_t *status = (mousestatus_t *)addr;
+
+ *status = sc->sc_status;
+ sc->sc_status.obutton = sc->sc_status.button;
+ sc->sc_status.button = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+
+ if (status->dx || status->dy || status->dz)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ break;
+ }
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ mtx_unlock(&sc->sc_mutex);
+ return (error);
+}
+
+static int
+atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ u_int tmp;
+
+ tmp = atp_mickeys_scale_factor;
+ error = sysctl_handle_int(oidp, &tmp, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (tmp == atp_mickeys_scale_factor)
+ return (0); /* no change */
+ if ((tmp == 0) || (tmp > (10 * ATP_SCALE_FACTOR)))
+ return (EINVAL);
+
+ atp_mickeys_scale_factor = tmp;
+ DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n",
+ ATP_DRIVER_NAME, tmp);
+
+ return (0);
+}
+
+static devclass_t atp_devclass;
+
+static device_method_t atp_methods[] = {
+ DEVMETHOD(device_probe, atp_probe),
+ DEVMETHOD(device_attach, atp_attach),
+ DEVMETHOD(device_detach, atp_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t atp_driver = {
+ .name = ATP_DRIVER_NAME,
+ .methods = atp_methods,
+ .size = sizeof(struct atp_softc)
+};
+
+DRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0);
+MODULE_DEPEND(atp, usb, 1, 1, 1);
+MODULE_VERSION(atp, 1);
+USB_PNP_HOST_INFO(fg_devs);
+USB_PNP_HOST_INFO(wsp_devs);
diff --git a/freebsd/sys/dev/usb/input/uep.c b/freebsd/sys/dev/usb/input/uep.c
new file mode 100644
index 00000000..93441974
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/uep.c
@@ -0,0 +1,446 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright 2010, Gleb Smirnoff <glebius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <sys/ioccom.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+
+#define USB_DEBUG_VAR uep_debug
+#include <dev/usb/usb_debug.h>
+
+#ifdef USB_DEBUG
+static int uep_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep");
+SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &uep_debug, 0, "Debug level");
+#endif
+
+#define UEP_MAX_X 2047
+#define UEP_MAX_Y 2047
+
+#define UEP_DOWN 0x01
+#define UEP_PACKET_LEN_MAX 16
+#define UEP_PACKET_LEN_REPORT 5
+#define UEP_PACKET_LEN_REPORT2 6
+#define UEP_PACKET_DIAG 0x0a
+#define UEP_PACKET_REPORT_MASK 0xe0
+#define UEP_PACKET_REPORT 0x80
+#define UEP_PACKET_REPORT_PRESSURE 0xc0
+#define UEP_PACKET_REPORT_PLAYER 0xa0
+#define UEP_PACKET_LEN_MASK
+
+#define UEP_FIFO_BUF_SIZE 8 /* bytes */
+#define UEP_FIFO_QUEUE_MAXLEN 50 /* units */
+
+enum {
+ UEP_INTR_DT,
+ UEP_N_TRANSFER,
+};
+
+struct uep_softc {
+ struct mtx mtx;
+
+ struct usb_xfer *xfer[UEP_N_TRANSFER];
+ struct usb_fifo_sc fifo;
+
+ u_int pollrate;
+ u_int state;
+#define UEP_ENABLED 0x01
+
+ /* Reassembling buffer. */
+ u_char buf[UEP_PACKET_LEN_MAX];
+ uint8_t buf_len;
+};
+
+static usb_callback_t uep_intr_callback;
+
+static device_probe_t uep_probe;
+static device_attach_t uep_attach;
+static device_detach_t uep_detach;
+
+static usb_fifo_cmd_t uep_start_read;
+static usb_fifo_cmd_t uep_stop_read;
+static usb_fifo_open_t uep_open;
+static usb_fifo_close_t uep_close;
+
+static void uep_put_queue(struct uep_softc *, u_char *);
+
+static struct usb_fifo_methods uep_fifo_methods = {
+ .f_open = &uep_open,
+ .f_close = &uep_close,
+ .f_start_read = &uep_start_read,
+ .f_stop_read = &uep_stop_read,
+ .basename[0] = "uep",
+};
+
+static int
+get_pkt_len(u_char *buf)
+{
+ if (buf[0] == UEP_PACKET_DIAG) {
+ int len;
+
+ len = buf[1] + 2;
+ if (len > UEP_PACKET_LEN_MAX) {
+ DPRINTF("bad packet len %u\n", len);
+ return (UEP_PACKET_LEN_MAX);
+ }
+
+ return (len);
+ }
+
+ switch (buf[0] & UEP_PACKET_REPORT_MASK) {
+ case UEP_PACKET_REPORT:
+ return (UEP_PACKET_LEN_REPORT);
+ case UEP_PACKET_REPORT_PRESSURE:
+ case UEP_PACKET_REPORT_PLAYER:
+ case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER:
+ return (UEP_PACKET_LEN_REPORT2);
+ default:
+ DPRINTF("bad packet len 0\n");
+ return (0);
+ }
+}
+
+static void
+uep_process_pkt(struct uep_softc *sc, u_char *buf)
+{
+ int32_t x, y;
+
+ if ((buf[0] & 0xFE) != 0x80) {
+ DPRINTF("bad input packet format 0x%.2x\n", buf[0]);
+ return;
+ }
+
+ /*
+ * Packet format is 5 bytes:
+ *
+ * 1000000T
+ * 0000AAAA
+ * 0AAAAAAA
+ * 0000BBBB
+ * 0BBBBBBB
+ *
+ * T: 1=touched 0=not touched
+ * A: bits of axis A position, MSB to LSB
+ * B: bits of axis B position, MSB to LSB
+ *
+ * For the unit I have, which is CTF1020-S from CarTFT.com,
+ * A = X and B = Y. But in NetBSD uep(4) it is other way round :)
+ *
+ * The controller sends a stream of T=1 events while the
+ * panel is touched, followed by a single T=0 event.
+ *
+ */
+
+ x = (buf[1] << 7) | buf[2];
+ y = (buf[3] << 7) | buf[4];
+
+ DPRINTFN(2, "x %u y %u\n", x, y);
+
+ uep_put_queue(sc, buf);
+}
+
+static void
+uep_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uep_softc *sc = usbd_xfer_softc(xfer);
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ {
+ struct usb_page_cache *pc;
+ u_char buf[17], *p;
+ int pkt_len;
+
+ if (len > (int)sizeof(buf)) {
+ DPRINTF("bad input length %d\n", len);
+ goto tr_setup;
+ }
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, buf, len);
+
+ /*
+ * The below code mimics Linux a lot. I don't know
+ * why NetBSD reads complete packets, but we need
+ * to reassamble 'em like Linux does (tries?).
+ */
+ if (sc->buf_len > 0) {
+ int res;
+
+ if (sc->buf_len == 1)
+ sc->buf[1] = buf[0];
+
+ if ((pkt_len = get_pkt_len(sc->buf)) == 0)
+ goto tr_setup;
+
+ res = pkt_len - sc->buf_len;
+ memcpy(sc->buf + sc->buf_len, buf, res);
+ uep_process_pkt(sc, sc->buf);
+ sc->buf_len = 0;
+
+ p = buf + res;
+ len -= res;
+ } else
+ p = buf;
+
+ if (len == 1) {
+ sc->buf[0] = buf[0];
+ sc->buf_len = 1;
+
+ goto tr_setup;
+ }
+
+ while (len > 0) {
+ if ((pkt_len = get_pkt_len(p)) == 0)
+ goto tr_setup;
+
+ /* full packet: process */
+ if (pkt_len <= len) {
+ uep_process_pkt(sc, p);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(sc->buf, p, len);
+ sc->buf_len = len;
+ }
+ p += pkt_len;
+ len -= pkt_len;
+ }
+ }
+ case USB_ST_SETUP:
+ tr_setup:
+ /* check if we can put more data into the FIFO */
+ if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) {
+ usbd_xfer_set_frame_len(xfer, 0,
+ usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ /* try clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static const struct usb_config uep_config[UEP_N_TRANSFER] = {
+ [UEP_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .callback = &uep_intr_callback,
+ },
+};
+
+static const STRUCT_USB_HOST_ID uep_devs[] = {
+ {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)},
+ {USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)},
+ {USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)},
+};
+
+static int
+uep_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != 0)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa));
+}
+
+static int
+uep_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct uep_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF);
+
+ error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
+ sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx);
+
+ if (error) {
+ DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods,
+ &sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ if (error) {
+ DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ sc->buf_len = 0;
+
+ return (0);
+
+detach:
+ uep_detach(dev);
+
+ return (ENOMEM); /* XXX */
+}
+
+static int
+uep_detach(device_t dev)
+{
+ struct uep_softc *sc = device_get_softc(dev);
+
+ usb_fifo_detach(&sc->fifo);
+
+ usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER);
+
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static void
+uep_start_read(struct usb_fifo *fifo)
+{
+ struct uep_softc *sc = usb_fifo_softc(fifo);
+ u_int rate;
+
+ if ((rate = sc->pollrate) > 1000)
+ rate = 1000;
+
+ if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) {
+ usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
+ usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate);
+ sc->pollrate = 0;
+ }
+
+ usbd_transfer_start(sc->xfer[UEP_INTR_DT]);
+}
+
+static void
+uep_stop_read(struct usb_fifo *fifo)
+{
+ struct uep_softc *sc = usb_fifo_softc(fifo);
+
+ usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
+}
+
+static void
+uep_put_queue(struct uep_softc *sc, u_char *buf)
+{
+ usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf,
+ UEP_PACKET_LEN_REPORT, 1);
+}
+
+static int
+uep_open(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & FREAD) {
+ struct uep_softc *sc = usb_fifo_softc(fifo);
+
+ if (sc->state & UEP_ENABLED)
+ return (EBUSY);
+ if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE,
+ UEP_FIFO_QUEUE_MAXLEN))
+ return (ENOMEM);
+
+ sc->state |= UEP_ENABLED;
+ }
+
+ return (0);
+}
+
+static void
+uep_close(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & FREAD) {
+ struct uep_softc *sc = usb_fifo_softc(fifo);
+
+ sc->state &= ~(UEP_ENABLED);
+ usb_fifo_free_buffer(fifo);
+ }
+}
+
+static devclass_t uep_devclass;
+
+static device_method_t uep_methods[] = {
+ DEVMETHOD(device_probe, uep_probe),
+ DEVMETHOD(device_attach, uep_attach),
+ DEVMETHOD(device_detach, uep_detach),
+ { 0, 0 },
+};
+
+static driver_t uep_driver = {
+ .name = "uep",
+ .methods = uep_methods,
+ .size = sizeof(struct uep_softc),
+};
+
+DRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL);
+MODULE_DEPEND(uep, usb, 1, 1, 1);
+MODULE_VERSION(uep, 1);
+USB_PNP_HOST_INFO(uep_devs);
diff --git a/freebsd/sys/dev/usb/input/uhid.c b/freebsd/sys/dev/usb/input/uhid.c
new file mode 100644
index 00000000..d3812d0b
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/uhid.c
@@ -0,0 +1,883 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+
+#include <rtems/bsd/local/usbdevs.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uhid_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/input/usb_rdesc.h>
+#include <dev/usb/quirk/usb_quirk.h>
+
+#ifdef USB_DEBUG
+static int uhid_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
+SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &uhid_debug, 0, "Debug level");
+#endif
+
+#define UHID_BSIZE 1024 /* bytes, buffer size */
+#define UHID_FRAME_NUM 50 /* bytes, frame number */
+
+enum {
+ UHID_INTR_DT_WR,
+ UHID_INTR_DT_RD,
+ UHID_CTRL_DT_WR,
+ UHID_CTRL_DT_RD,
+ UHID_N_TRANSFER,
+};
+
+struct uhid_softc {
+ struct usb_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb_xfer *sc_xfer[UHID_N_TRANSFER];
+ struct usb_device *sc_udev;
+ void *sc_repdesc_ptr;
+
+ uint32_t sc_isize;
+ uint32_t sc_osize;
+ uint32_t sc_fsize;
+
+ uint16_t sc_repdesc_size;
+
+ uint8_t sc_iface_no;
+ uint8_t sc_iface_index;
+ uint8_t sc_iid;
+ uint8_t sc_oid;
+ uint8_t sc_fid;
+ uint8_t sc_flags;
+#define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */
+#define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are
+ * static */
+};
+
+static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()};
+static const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()};
+static const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()};
+
+/* prototypes */
+
+static device_probe_t uhid_probe;
+static device_attach_t uhid_attach;
+static device_detach_t uhid_detach;
+
+static usb_callback_t uhid_intr_write_callback;
+static usb_callback_t uhid_intr_read_callback;
+static usb_callback_t uhid_write_callback;
+static usb_callback_t uhid_read_callback;
+
+static usb_fifo_cmd_t uhid_start_read;
+static usb_fifo_cmd_t uhid_stop_read;
+static usb_fifo_cmd_t uhid_start_write;
+static usb_fifo_cmd_t uhid_stop_write;
+static usb_fifo_open_t uhid_open;
+static usb_fifo_close_t uhid_close;
+static usb_fifo_ioctl_t uhid_ioctl;
+
+static struct usb_fifo_methods uhid_fifo_methods = {
+ .f_open = &uhid_open,
+ .f_close = &uhid_close,
+ .f_ioctl = &uhid_ioctl,
+ .f_start_read = &uhid_start_read,
+ .f_stop_read = &uhid_stop_read,
+ .f_start_write = &uhid_start_write,
+ .f_stop_write = &uhid_stop_write,
+ .basename[0] = "uhid",
+};
+
+static void
+uhid_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uhid_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_page_cache *pc;
+ int actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc,
+ 0, usbd_xfer_max_len(xfer), &actlen, 0)) {
+ usbd_xfer_set_frame_len(xfer, 0, actlen);
+ usbd_transfer_submit(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uhid_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uhid_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("transferred!\n");
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ /*
+ * If the ID byte is non zero we allow descriptors
+ * having multiple sizes:
+ */
+ if ((actlen >= (int)sc->sc_isize) ||
+ ((actlen > 0) && (sc->sc_iid != 0))) {
+ /* limit report length to the maximum */
+ if (actlen > (int)sc->sc_isize)
+ actlen = sc->sc_isize;
+ usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc,
+ 0, actlen, 1);
+ } else {
+ /* ignore it */
+ DPRINTF("ignored transfer, %d bytes\n", actlen);
+ }
+
+ case USB_ST_SETUP:
+re_submit:
+ if (usb_fifo_put_bytes_max(
+ sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
+ usbd_xfer_set_frame_len(xfer, 0, sc->sc_isize);
+ usbd_transfer_submit(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto re_submit;
+ }
+ return;
+ }
+}
+
+static void
+uhid_fill_set_report(struct usb_device_request *req, uint8_t iface_no,
+ uint8_t type, uint8_t id, uint16_t size)
+{
+ req->bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req->bRequest = UR_SET_REPORT;
+ USETW2(req->wValue, type, id);
+ req->wIndex[0] = iface_no;
+ req->wIndex[1] = 0;
+ USETW(req->wLength, size);
+}
+
+static void
+uhid_fill_get_report(struct usb_device_request *req, uint8_t iface_no,
+ uint8_t type, uint8_t id, uint16_t size)
+{
+ req->bmRequestType = UT_READ_CLASS_INTERFACE;
+ req->bRequest = UR_GET_REPORT;
+ USETW2(req->wValue, type, id);
+ req->wIndex[0] = iface_no;
+ req->wIndex[1] = 0;
+ USETW(req->wLength, size);
+}
+
+static void
+uhid_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uhid_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_device_request req;
+ struct usb_page_cache *pc;
+ uint32_t size = sc->sc_osize;
+ uint32_t actlen;
+ uint8_t id;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ /* try to extract the ID byte */
+ if (sc->sc_oid) {
+ pc = usbd_xfer_get_frame(xfer, 0);
+ if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc,
+ 0, 1, &actlen, 0)) {
+ if (actlen != 1) {
+ goto tr_error;
+ }
+ usbd_copy_out(pc, 0, &id, 1);
+
+ } else {
+ return;
+ }
+ if (size) {
+ size--;
+ }
+ } else {
+ id = 0;
+ }
+
+ pc = usbd_xfer_get_frame(xfer, 1);
+ if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc,
+ 0, UHID_BSIZE, &actlen, 1)) {
+ if (actlen != size) {
+ goto tr_error;
+ }
+ uhid_fill_set_report
+ (&req, sc->sc_iface_no,
+ UHID_OUTPUT_REPORT, id, size);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 1, size);
+ usbd_xfer_set_frames(xfer, size ? 2 : 1);
+ usbd_transfer_submit(xfer);
+ }
+ return;
+
+ default:
+tr_error:
+ /* bomb out */
+ usb_fifo_get_data_error(sc->sc_fifo.fp[USB_FIFO_TX]);
+ return;
+ }
+}
+
+static void
+uhid_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uhid_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_device_request req;
+ struct usb_page_cache *pc;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc, sizeof(req),
+ sc->sc_isize, 1);
+ return;
+
+ case USB_ST_SETUP:
+
+ if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) > 0) {
+
+ uhid_fill_get_report
+ (&req, sc->sc_iface_no, UHID_INPUT_REPORT,
+ sc->sc_iid, sc->sc_isize);
+
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 1, sc->sc_isize);
+ usbd_xfer_set_frames(xfer, sc->sc_isize ? 2 : 1);
+ usbd_transfer_submit(xfer);
+ }
+ return;
+
+ default: /* Error */
+ /* bomb out */
+ usb_fifo_put_data_error(sc->sc_fifo.fp[USB_FIFO_RX]);
+ return;
+ }
+}
+
+static const struct usb_config uhid_config[UHID_N_TRANSFER] = {
+
+ [UHID_INTR_DT_WR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .flags = {.pipe_bof = 1,.no_pipe_ok = 1, },
+ .bufsize = UHID_BSIZE,
+ .callback = &uhid_intr_write_callback,
+ },
+
+ [UHID_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = UHID_BSIZE,
+ .callback = &uhid_intr_read_callback,
+ },
+
+ [UHID_CTRL_DT_WR] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE,
+ .callback = &uhid_write_callback,
+ .timeout = 1000, /* 1 second */
+ },
+
+ [UHID_CTRL_DT_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE,
+ .callback = &uhid_read_callback,
+ .timeout = 1000, /* 1 second */
+ },
+};
+
+static void
+uhid_start_read(struct usb_fifo *fifo)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+
+ if (sc->sc_flags & UHID_FLAG_IMMED) {
+ usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_RD]);
+ } else {
+ usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]);
+ }
+}
+
+static void
+uhid_stop_read(struct usb_fifo *fifo)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+
+ usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]);
+ usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]);
+}
+
+static void
+uhid_start_write(struct usb_fifo *fifo)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+
+ if ((sc->sc_flags & UHID_FLAG_IMMED) ||
+ sc->sc_xfer[UHID_INTR_DT_WR] == NULL) {
+ usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]);
+ } else {
+ usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_WR]);
+ }
+}
+
+static void
+uhid_stop_write(struct usb_fifo *fifo)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+
+ usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_WR]);
+}
+
+static int
+uhid_get_report(struct uhid_softc *sc, uint8_t type,
+ uint8_t id, void *kern_data, void *user_data,
+ uint16_t len)
+{
+ int err;
+ uint8_t free_data = 0;
+
+ if (kern_data == NULL) {
+ kern_data = malloc(len, M_USBDEV, M_WAITOK);
+ if (kern_data == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ free_data = 1;
+ }
+ err = usbd_req_get_report(sc->sc_udev, NULL, kern_data,
+ len, sc->sc_iface_index, type, id);
+ if (err) {
+ err = ENXIO;
+ goto done;
+ }
+ if (user_data) {
+ /* dummy buffer */
+ err = copyout(kern_data, user_data, len);
+ if (err) {
+ goto done;
+ }
+ }
+done:
+ if (free_data) {
+ free(kern_data, M_USBDEV);
+ }
+ return (err);
+}
+
+static int
+uhid_set_report(struct uhid_softc *sc, uint8_t type,
+ uint8_t id, void *kern_data, void *user_data,
+ uint16_t len)
+{
+ int err;
+ uint8_t free_data = 0;
+
+ if (kern_data == NULL) {
+ kern_data = malloc(len, M_USBDEV, M_WAITOK);
+ if (kern_data == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ free_data = 1;
+ err = copyin(user_data, kern_data, len);
+ if (err) {
+ goto done;
+ }
+ }
+ err = usbd_req_set_report(sc->sc_udev, NULL, kern_data,
+ len, sc->sc_iface_index, type, id);
+ if (err) {
+ err = ENXIO;
+ goto done;
+ }
+done:
+ if (free_data) {
+ free(kern_data, M_USBDEV);
+ }
+ return (err);
+}
+
+static int
+uhid_open(struct usb_fifo *fifo, int fflags)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+
+ /*
+ * The buffers are one byte larger than maximum so that one
+ * can detect too large read/writes and short transfers:
+ */
+ if (fflags & FREAD) {
+ /* reset flags */
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags &= ~UHID_FLAG_IMMED;
+ mtx_unlock(&sc->sc_mtx);
+
+ if (usb_fifo_alloc_buffer(fifo,
+ sc->sc_isize + 1, UHID_FRAME_NUM)) {
+ return (ENOMEM);
+ }
+ }
+ if (fflags & FWRITE) {
+ if (usb_fifo_alloc_buffer(fifo,
+ sc->sc_osize + 1, UHID_FRAME_NUM)) {
+ return (ENOMEM);
+ }
+ }
+ return (0);
+}
+
+static void
+uhid_close(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & (FREAD | FWRITE)) {
+ usb_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+uhid_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr,
+ int fflags)
+{
+ struct uhid_softc *sc = usb_fifo_softc(fifo);
+ struct usb_gen_descriptor *ugd;
+ uint32_t size;
+ int error = 0;
+ uint8_t id;
+
+ switch (cmd) {
+ case USB_GET_REPORT_DESC:
+ ugd = addr;
+ if (sc->sc_repdesc_size > ugd->ugd_maxlen) {
+ size = ugd->ugd_maxlen;
+ } else {
+ size = sc->sc_repdesc_size;
+ }
+ ugd->ugd_actlen = size;
+ if (ugd->ugd_data == NULL)
+ break; /* descriptor length only */
+ error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size);
+ break;
+
+ case USB_SET_IMMED:
+ if (!(fflags & FREAD)) {
+ error = EPERM;
+ break;
+ }
+ if (*(int *)addr) {
+
+ /* do a test read */
+
+ error = uhid_get_report(sc, UHID_INPUT_REPORT,
+ sc->sc_iid, NULL, NULL, sc->sc_isize);
+ if (error) {
+ break;
+ }
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags |= UHID_FLAG_IMMED;
+ mtx_unlock(&sc->sc_mtx);
+ } else {
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags &= ~UHID_FLAG_IMMED;
+ mtx_unlock(&sc->sc_mtx);
+ }
+ break;
+
+ case USB_GET_REPORT:
+ if (!(fflags & FREAD)) {
+ error = EPERM;
+ break;
+ }
+ ugd = addr;
+ switch (ugd->ugd_report_type) {
+ case UHID_INPUT_REPORT:
+ size = sc->sc_isize;
+ id = sc->sc_iid;
+ break;
+ case UHID_OUTPUT_REPORT:
+ size = sc->sc_osize;
+ id = sc->sc_oid;
+ break;
+ case UHID_FEATURE_REPORT:
+ size = sc->sc_fsize;
+ id = sc->sc_fid;
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (id != 0)
+ copyin(ugd->ugd_data, &id, 1);
+ error = uhid_get_report(sc, ugd->ugd_report_type, id,
+ NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
+ break;
+
+ case USB_SET_REPORT:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ ugd = addr;
+ switch (ugd->ugd_report_type) {
+ case UHID_INPUT_REPORT:
+ size = sc->sc_isize;
+ id = sc->sc_iid;
+ break;
+ case UHID_OUTPUT_REPORT:
+ size = sc->sc_osize;
+ id = sc->sc_oid;
+ break;
+ case UHID_FEATURE_REPORT:
+ size = sc->sc_fsize;
+ id = sc->sc_fid;
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (id != 0)
+ copyin(ugd->ugd_data, &id, 1);
+ error = uhid_set_report(sc, ugd->ugd_report_type, id,
+ NULL, ugd->ugd_data, imin(ugd->ugd_maxlen, size));
+ break;
+
+ case USB_GET_REPORT_ID:
+ *(int *)addr = 0; /* XXX: we only support reportid 0? */
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static const STRUCT_USB_HOST_ID uhid_devs[] = {
+ /* generic HID class */
+ {USB_IFACE_CLASS(UICLASS_HID),},
+ /* the Xbox 360 gamepad doesn't use the HID class */
+ {USB_IFACE_CLASS(UICLASS_VENDOR),
+ USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER),
+ USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),},
+};
+
+static int
+uhid_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ error = usbd_lookup_id_by_uaa(uhid_devs, sizeof(uhid_devs), uaa);
+ if (error)
+ return (error);
+
+ if (usb_test_quirk(uaa, UQ_HID_IGNORE))
+ return (ENXIO);
+
+ /*
+ * Don't attach to mouse and keyboard devices, hence then no
+ * "nomatch" event is generated and then ums and ukbd won't
+ * attach properly when loaded.
+ */
+ if ((uaa->info.bInterfaceClass == UICLASS_HID) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
+ (((uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD) &&
+ !usb_test_quirk(uaa, UQ_KBD_IGNORE)) ||
+ ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) &&
+ !usb_test_quirk(uaa, UQ_UMS_IGNORE))))
+ return (ENXIO);
+
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+uhid_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct uhid_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int error = 0;
+
+ DPRINTFN(10, "sc=%p\n", sc);
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ sc->sc_udev = uaa->device;
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+
+ error = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config,
+ UHID_N_TRANSFER, sc, &sc->sc_mtx);
+
+ if (error) {
+ DPRINTF("error=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+ if (uaa->info.idVendor == USB_VENDOR_WACOM) {
+
+ /* the report descriptor for the Wacom Graphire is broken */
+
+ if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) {
+
+ sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr);
+ sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire_report_descr);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+
+ } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {
+
+ static uint8_t reportbuf[] = {2, 2, 2};
+
+ /*
+ * The Graphire3 needs 0x0202 to be written to
+ * feature report ID 2 before it'll start
+ * returning digitizer data.
+ */
+ error = usbd_req_set_report(uaa->device, NULL,
+ reportbuf, sizeof(reportbuf),
+ uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2);
+
+ if (error) {
+ DPRINTF("set report failed, error=%s (ignored)\n",
+ usbd_errstr(error));
+ }
+ sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr);
+ sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire3_4x5_report_descr);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+ }
+ } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) &&
+ (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {
+ static const uint8_t reportbuf[3] = {1, 3, 0};
+ /*
+ * Turn off the four LEDs on the gamepad which
+ * are blinking by default:
+ */
+ error = usbd_req_set_report(uaa->device, NULL,
+ __DECONST(void *, reportbuf), sizeof(reportbuf),
+ uaa->info.bIfaceIndex, UHID_OUTPUT_REPORT, 0);
+ if (error) {
+ DPRINTF("set output report failed, error=%s (ignored)\n",
+ usbd_errstr(error));
+ }
+ /* the Xbox 360 gamepad has no report descriptor */
+ sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr);
+ sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb360gp_report_descr);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+ }
+ if (sc->sc_repdesc_ptr == NULL) {
+
+ error = usbd_req_get_hid_desc(uaa->device, NULL,
+ &sc->sc_repdesc_ptr, &sc->sc_repdesc_size,
+ M_USBDEV, uaa->info.bIfaceIndex);
+
+ if (error) {
+ device_printf(dev, "no report descriptor\n");
+ goto detach;
+ }
+ }
+ error = usbd_req_set_idle(uaa->device, NULL,
+ uaa->info.bIfaceIndex, 0, 0);
+
+ if (error) {
+ DPRINTF("set idle failed, error=%s (ignored)\n",
+ usbd_errstr(error));
+ }
+ sc->sc_isize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid);
+
+ sc->sc_osize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid);
+
+ sc->sc_fsize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid);
+
+ if (sc->sc_isize > UHID_BSIZE) {
+ DPRINTF("input size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_isize);
+ sc->sc_isize = UHID_BSIZE;
+ }
+ if (sc->sc_osize > UHID_BSIZE) {
+ DPRINTF("output size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_osize);
+ sc->sc_osize = UHID_BSIZE;
+ }
+ if (sc->sc_fsize > UHID_BSIZE) {
+ DPRINTF("feature size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_fsize);
+ sc->sc_fsize = UHID_BSIZE;
+ }
+
+ error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &uhid_fifo_methods, &sc->sc_fifo,
+ unit, -1, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uhid_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+uhid_detach(device_t dev)
+{
+ struct uhid_softc *sc = device_get_softc(dev);
+
+ usb_fifo_detach(&sc->sc_fifo);
+
+ usbd_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER);
+
+ if (sc->sc_repdesc_ptr) {
+ if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) {
+ free(sc->sc_repdesc_ptr, M_USBDEV);
+ }
+ }
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static devclass_t uhid_devclass;
+
+static device_method_t uhid_methods[] = {
+ DEVMETHOD(device_probe, uhid_probe),
+ DEVMETHOD(device_attach, uhid_attach),
+ DEVMETHOD(device_detach, uhid_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t uhid_driver = {
+ .name = "uhid",
+ .methods = uhid_methods,
+ .size = sizeof(struct uhid_softc),
+};
+
+DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, NULL, 0);
+MODULE_DEPEND(uhid, usb, 1, 1, 1);
+MODULE_VERSION(uhid, 1);
+USB_PNP_HOST_INFO(uhid_devs);
diff --git a/freebsd/sys/dev/usb/input/ukbd.c b/freebsd/sys/dev/usb/input/ukbd.c
new file mode 100644
index 00000000..5c6f5583
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/ukbd.c
@@ -0,0 +1,2309 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <rtems/bsd/local/opt_compat.h>
+#include <rtems/bsd/local/opt_kbd.h>
+#include <rtems/bsd/local/opt_ukbd.h>
+#include <rtems/bsd/local/opt_evdev.h>
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR ukbd_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#endif
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/kbio.h>
+
+#include <dev/kbd/kbdreg.h>
+
+/* the initial key map, accent map and fkey strings */
+#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
+#define KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+
+/* the following file must be included after "ukbdmap.h" */
+#include <dev/kbd/kbdtables.h>
+
+#ifdef USB_DEBUG
+static int ukbd_debug = 0;
+static int ukbd_no_leds = 0;
+static int ukbd_pollrate = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB keyboard");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &ukbd_debug, 0, "Debug level");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
+ &ukbd_no_leds, 0, "Disables setting of keyboard leds");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
+ &ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
+#endif
+
+#define UKBD_EMULATE_ATSCANCODE 1
+#define UKBD_DRIVER_NAME "ukbd"
+#define UKBD_NMOD 8 /* units */
+#define UKBD_NKEYCODE 6 /* units */
+#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
+#define UKBD_IN_BUF_FULL ((UKBD_IN_BUF_SIZE / 2) - 1) /* bytes */
+#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
+#define UKBD_BUFFER_SIZE 64 /* bytes */
+
+struct ukbd_data {
+ uint16_t modifiers;
+#define MOD_CONTROL_L 0x01
+#define MOD_CONTROL_R 0x10
+#define MOD_SHIFT_L 0x02
+#define MOD_SHIFT_R 0x20
+#define MOD_ALT_L 0x04
+#define MOD_ALT_R 0x40
+#define MOD_WIN_L 0x08
+#define MOD_WIN_R 0x80
+/* internal */
+#define MOD_EJECT 0x0100
+#define MOD_FN 0x0200
+ uint8_t keycode[UKBD_NKEYCODE];
+};
+
+enum {
+ UKBD_INTR_DT_0,
+ UKBD_INTR_DT_1,
+ UKBD_CTRL_LED,
+ UKBD_N_TRANSFER,
+};
+
+struct ukbd_softc {
+ keyboard_t sc_kbd;
+ keymap_t sc_keymap;
+ accentmap_t sc_accmap;
+ fkeytab_t sc_fkeymap[UKBD_NFKEY];
+ struct hid_location sc_loc_apple_eject;
+ struct hid_location sc_loc_apple_fn;
+ struct hid_location sc_loc_ctrl_l;
+ struct hid_location sc_loc_ctrl_r;
+ struct hid_location sc_loc_shift_l;
+ struct hid_location sc_loc_shift_r;
+ struct hid_location sc_loc_alt_l;
+ struct hid_location sc_loc_alt_r;
+ struct hid_location sc_loc_win_l;
+ struct hid_location sc_loc_win_r;
+ struct hid_location sc_loc_events;
+ struct hid_location sc_loc_numlock;
+ struct hid_location sc_loc_capslock;
+ struct hid_location sc_loc_scrolllock;
+ struct usb_callout sc_callout;
+ struct ukbd_data sc_ndata;
+ struct ukbd_data sc_odata;
+
+ struct thread *sc_poll_thread;
+ struct usb_device *sc_udev;
+ struct usb_interface *sc_iface;
+ struct usb_xfer *sc_xfer[UKBD_N_TRANSFER];
+#ifdef EVDEV_SUPPORT
+ struct evdev_dev *sc_evdev;
+#endif
+
+ sbintime_t sc_co_basetime;
+ int sc_delay;
+ uint32_t sc_ntime[UKBD_NKEYCODE];
+ uint32_t sc_otime[UKBD_NKEYCODE];
+ uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
+ uint32_t sc_time_ms;
+ uint32_t sc_composed_char; /* composed char code, if non-zero */
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t sc_buffered_char[2];
+#endif
+ uint32_t sc_flags; /* flags */
+#define UKBD_FLAG_COMPOSE 0x00000001
+#define UKBD_FLAG_POLLING 0x00000002
+#define UKBD_FLAG_SET_LEDS 0x00000004
+#define UKBD_FLAG_ATTACHED 0x00000010
+#define UKBD_FLAG_GONE 0x00000020
+
+#define UKBD_FLAG_HID_MASK 0x003fffc0
+#define UKBD_FLAG_APPLE_EJECT 0x00000040
+#define UKBD_FLAG_APPLE_FN 0x00000080
+#define UKBD_FLAG_APPLE_SWAP 0x00000100
+#define UKBD_FLAG_CTRL_L 0x00000400
+#define UKBD_FLAG_CTRL_R 0x00000800
+#define UKBD_FLAG_SHIFT_L 0x00001000
+#define UKBD_FLAG_SHIFT_R 0x00002000
+#define UKBD_FLAG_ALT_L 0x00004000
+#define UKBD_FLAG_ALT_R 0x00008000
+#define UKBD_FLAG_WIN_L 0x00010000
+#define UKBD_FLAG_WIN_R 0x00020000
+#define UKBD_FLAG_EVENTS 0x00040000
+#define UKBD_FLAG_NUMLOCK 0x00080000
+#define UKBD_FLAG_CAPSLOCK 0x00100000
+#define UKBD_FLAG_SCROLLLOCK 0x00200000
+
+ int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int sc_state; /* shift/lock key state */
+ int sc_accents; /* accent key index (> 0) */
+ int sc_polling; /* polling recursion count */
+ int sc_led_size;
+ int sc_kbd_size;
+
+ uint16_t sc_inputs;
+ uint16_t sc_inputhead;
+ uint16_t sc_inputtail;
+ uint16_t sc_modifiers;
+
+ uint8_t sc_leds; /* store for async led requests */
+ uint8_t sc_iface_index;
+ uint8_t sc_iface_no;
+ uint8_t sc_id_apple_eject;
+ uint8_t sc_id_apple_fn;
+ uint8_t sc_id_ctrl_l;
+ uint8_t sc_id_ctrl_r;
+ uint8_t sc_id_shift_l;
+ uint8_t sc_id_shift_r;
+ uint8_t sc_id_alt_l;
+ uint8_t sc_id_alt_r;
+ uint8_t sc_id_win_l;
+ uint8_t sc_id_win_r;
+ uint8_t sc_id_event;
+ uint8_t sc_id_numlock;
+ uint8_t sc_id_capslock;
+ uint8_t sc_id_scrolllock;
+ uint8_t sc_id_events;
+ uint8_t sc_kbd_id;
+
+ uint8_t sc_buffer[UKBD_BUFFER_SIZE];
+};
+
+#define KEY_ERROR 0x01
+
+#define KEY_PRESS 0
+#define KEY_RELEASE 0x400
+#define KEY_INDEX(c) ((c) & 0xFF)
+
+#define SCAN_PRESS 0
+#define SCAN_RELEASE 0x80
+#define SCAN_PREFIX_E0 0x100
+#define SCAN_PREFIX_E1 0x200
+#define SCAN_PREFIX_CTL 0x400
+#define SCAN_PREFIX_SHIFT 0x800
+#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
+ SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
+#define SCAN_CHAR(c) ((c) & 0x7f)
+
+#define UKBD_LOCK() USB_MTX_LOCK(&Giant)
+#define UKBD_UNLOCK() USB_MTX_UNLOCK(&Giant)
+#define UKBD_LOCK_ASSERT() USB_MTX_ASSERT(&Giant, MA_OWNED)
+
+struct ukbd_mods {
+ uint32_t mask, key;
+};
+
+static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = {
+ {MOD_CONTROL_L, 0xe0},
+ {MOD_CONTROL_R, 0xe4},
+ {MOD_SHIFT_L, 0xe1},
+ {MOD_SHIFT_R, 0xe5},
+ {MOD_ALT_L, 0xe2},
+ {MOD_ALT_R, 0xe6},
+ {MOD_WIN_L, 0xe3},
+ {MOD_WIN_R, 0xe7},
+};
+
+#define NN 0 /* no translation */
+/*
+ * Translate USB keycodes to AT keyboard scancodes.
+ */
+/*
+ * FIXME: Mac USB keyboard generates:
+ * 0x53: keypad NumLock/Clear
+ * 0x66: Power
+ * 0x67: keypad =
+ * 0x68: F13
+ * 0x69: F14
+ * 0x6a: F15
+ *
+ * USB Apple Keyboard JIS generates:
+ * 0x90: Kana
+ * 0x91: Eisu
+ */
+static const uint8_t ukbd_trtab[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
+ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
+ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
+ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
+ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
+ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
+ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
+ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
+ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
+ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
+ 97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */
+ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
+ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
+ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
+ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
+ 121, 120, NN, NN, NN, NN, NN, 123, /* 80 - 87 */
+ 124, 125, 126, 127, 128, NN, NN, NN, /* 88 - 8F */
+ 129, 130, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
+ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
+};
+
+static const uint8_t ukbd_boot_desc[] = {
+ 0x05, 0x01, 0x09, 0x06, 0xa1,
+ 0x01, 0x05, 0x07, 0x19, 0xe0,
+ 0x29, 0xe7, 0x15, 0x00, 0x25,
+ 0x01, 0x75, 0x01, 0x95, 0x08,
+ 0x81, 0x02, 0x95, 0x01, 0x75,
+ 0x08, 0x81, 0x01, 0x95, 0x03,
+ 0x75, 0x01, 0x05, 0x08, 0x19,
+ 0x01, 0x29, 0x03, 0x91, 0x02,
+ 0x95, 0x05, 0x75, 0x01, 0x91,
+ 0x01, 0x95, 0x06, 0x75, 0x08,
+ 0x15, 0x00, 0x26, 0xff, 0x00,
+ 0x05, 0x07, 0x19, 0x00, 0x2a,
+ 0xff, 0x00, 0x81, 0x00, 0xc0
+};
+
+/* prototypes */
+static void ukbd_timeout(void *);
+static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
+static int ukbd_set_typematic(keyboard_t *, int);
+#ifdef UKBD_EMULATE_ATSCANCODE
+static uint32_t ukbd_atkeycode(int, int);
+static int ukbd_key2scan(struct ukbd_softc *, int, int, int);
+#endif
+static uint32_t ukbd_read_char(keyboard_t *, int);
+static void ukbd_clear_state(keyboard_t *);
+static int ukbd_ioctl(keyboard_t *, u_long, caddr_t);
+static int ukbd_enable(keyboard_t *);
+static int ukbd_disable(keyboard_t *);
+static void ukbd_interrupt(struct ukbd_softc *);
+static void ukbd_event_keyinput(struct ukbd_softc *);
+
+static device_probe_t ukbd_probe;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+static device_resume_t ukbd_resume;
+
+#ifdef EVDEV_SUPPORT
+static const struct evdev_methods ukbd_evdev_methods = {
+ .ev_event = evdev_ev_kbd_event,
+};
+#endif
+
+static uint8_t
+ukbd_any_key_pressed(struct ukbd_softc *sc)
+{
+ uint8_t i;
+ uint8_t j;
+
+ for (j = i = 0; i < UKBD_NKEYCODE; i++)
+ j |= sc->sc_odata.keycode[i];
+
+ return (j ? 1 : 0);
+}
+
+static void
+ukbd_start_timer(struct ukbd_softc *sc)
+{
+ sbintime_t delay, prec;
+
+ delay = SBT_1MS * sc->sc_delay;
+ sc->sc_co_basetime += delay;
+ /* This is rarely called, so prefer precision to efficiency. */
+ prec = qmin(delay >> 7, SBT_1MS * 10);
+ usb_callout_reset_sbt(&sc->sc_callout, sc->sc_co_basetime, prec,
+ ukbd_timeout, sc, C_ABSOLUTE);
+}
+
+static void
+ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
+{
+
+ UKBD_LOCK_ASSERT();
+
+ DPRINTF("0x%02x (%d) %s\n", key, key,
+ (key & KEY_RELEASE) ? "released" : "pressed");
+
+#ifdef EVDEV_SUPPORT
+ if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL) {
+ evdev_push_event(sc->sc_evdev, EV_KEY,
+ evdev_hid2key(KEY_INDEX(key)), !(key & KEY_RELEASE));
+ evdev_sync(sc->sc_evdev);
+ }
+#endif
+
+ if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
+ sc->sc_input[sc->sc_inputtail] = key;
+ ++(sc->sc_inputs);
+ ++(sc->sc_inputtail);
+ if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
+ sc->sc_inputtail = 0;
+ }
+ } else {
+ DPRINTF("input buffer is full\n");
+ }
+}
+
+static void
+ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait)
+{
+
+ UKBD_LOCK_ASSERT();
+ KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0,
+ ("ukbd_do_poll called when not polling\n"));
+ DPRINTFN(2, "polling\n");
+
+ if (USB_IN_POLLING_MODE_FUNC() == 0) {
+ /*
+ * In this context the kernel is polling for input,
+ * but the USB subsystem works in normal interrupt-driven
+ * mode, so we just wait on the USB threads to do the job.
+ * Note that we currently hold the Giant, but it's also used
+ * as the transfer mtx, so we must release it while waiting.
+ */
+ while (sc->sc_inputs == 0) {
+ /*
+ * Give USB threads a chance to run. Note that
+ * kern_yield performs DROP_GIANT + PICKUP_GIANT.
+ */
+ kern_yield(PRI_UNCHANGED);
+ if (!wait)
+ break;
+ }
+ return;
+ }
+
+ while (sc->sc_inputs == 0) {
+
+ usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER);
+
+ /* Delay-optimised support for repetition of keys */
+ if (ukbd_any_key_pressed(sc)) {
+ /* a key is pressed - need timekeeping */
+ DELAY(1000);
+
+ /* 1 millisecond has passed */
+ sc->sc_time_ms += 1;
+ }
+
+ ukbd_interrupt(sc);
+
+ if (!wait)
+ break;
+ }
+}
+
+static int32_t
+ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
+{
+ int32_t c;
+
+ UKBD_LOCK_ASSERT();
+ KASSERT((USB_IN_POLLING_MODE_FUNC() == 0) ||
+ (sc->sc_flags & UKBD_FLAG_POLLING) != 0,
+ ("not polling in kdb or panic\n"));
+
+ if (sc->sc_inputs == 0 &&
+ (sc->sc_flags & UKBD_FLAG_GONE) == 0) {
+ /* start transfer, if not already started */
+ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
+ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
+ }
+
+ if (sc->sc_flags & UKBD_FLAG_POLLING)
+ ukbd_do_poll(sc, wait);
+
+ if (sc->sc_inputs == 0) {
+ c = -1;
+ } else {
+ c = sc->sc_input[sc->sc_inputhead];
+ --(sc->sc_inputs);
+ ++(sc->sc_inputhead);
+ if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
+ sc->sc_inputhead = 0;
+ }
+ }
+ return (c);
+}
+
+static void
+ukbd_interrupt(struct ukbd_softc *sc)
+{
+ struct timeval ctv;
+ uint32_t n_mod;
+ uint32_t o_mod;
+ uint32_t now = sc->sc_time_ms;
+ int32_t dtime;
+ uint8_t key;
+ uint8_t i;
+ uint8_t j;
+
+ UKBD_LOCK_ASSERT();
+
+ if (sc->sc_ndata.keycode[0] == KEY_ERROR)
+ return;
+
+ n_mod = sc->sc_ndata.modifiers;
+ o_mod = sc->sc_odata.modifiers;
+ if (n_mod != o_mod) {
+ for (i = 0; i < UKBD_NMOD; i++) {
+ if ((n_mod & ukbd_mods[i].mask) !=
+ (o_mod & ukbd_mods[i].mask)) {
+ ukbd_put_key(sc, ukbd_mods[i].key |
+ ((n_mod & ukbd_mods[i].mask) ?
+ KEY_PRESS : KEY_RELEASE));
+ }
+ }
+ }
+ /* Check for released keys. */
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ key = sc->sc_odata.keycode[i];
+ if (key == 0) {
+ continue;
+ }
+ for (j = 0; j < UKBD_NKEYCODE; j++) {
+ if (sc->sc_ndata.keycode[j] == 0) {
+ continue;
+ }
+ if (key == sc->sc_ndata.keycode[j]) {
+ goto rfound;
+ }
+ }
+ ukbd_put_key(sc, key | KEY_RELEASE);
+rfound: ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ key = sc->sc_ndata.keycode[i];
+ if (key == 0) {
+ continue;
+ }
+ sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
+ for (j = 0; j < UKBD_NKEYCODE; j++) {
+ if (sc->sc_odata.keycode[j] == 0) {
+ continue;
+ }
+ if (key == sc->sc_odata.keycode[j]) {
+
+ /* key is still pressed */
+
+ sc->sc_ntime[i] = sc->sc_otime[j];
+ dtime = (sc->sc_otime[j] - now);
+
+ if (dtime > 0) {
+ /* time has not elapsed */
+ goto pfound;
+ }
+ sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
+ break;
+ }
+ }
+ if (j < UKBD_NKEYCODE) {
+ /* Old key repeating. */
+ sc->sc_delay = sc->sc_kbd.kb_delay2;
+ } else {
+ /* New key. */
+ microuptime(&ctv);
+ sc->sc_co_basetime = tvtosbt(ctv);
+ sc->sc_delay = sc->sc_kbd.kb_delay1;
+ }
+ ukbd_put_key(sc, key | KEY_PRESS);
+
+ /*
+ * If any other key is presently down, force its repeat to be
+ * well in the future (100s). This makes the last key to be
+ * pressed do the autorepeat.
+ */
+ for (j = 0; j != UKBD_NKEYCODE; j++) {
+ if (j != i)
+ sc->sc_ntime[j] = now + (100 * 1000);
+ }
+pfound: ;
+ }
+
+ sc->sc_odata = sc->sc_ndata;
+
+ memcpy(sc->sc_otime, sc->sc_ntime, sizeof(sc->sc_otime));
+
+ ukbd_event_keyinput(sc);
+}
+
+static void
+ukbd_event_keyinput(struct ukbd_softc *sc)
+{
+ int c;
+
+ UKBD_LOCK_ASSERT();
+
+ if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0)
+ return;
+
+ if (sc->sc_inputs == 0)
+ return;
+
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
+ KBD_IS_BUSY(&sc->sc_kbd)) {
+ /* let the callback function process the input */
+ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
+ sc->sc_kbd.kb_callback.kc_arg);
+ } else {
+ /* read and discard the input, no one is waiting for it */
+ do {
+ c = ukbd_read_char(&sc->sc_kbd, 0);
+ } while (c != NOKEY);
+ }
+}
+
+static void
+ukbd_timeout(void *arg)
+{
+ struct ukbd_softc *sc = arg;
+
+ UKBD_LOCK_ASSERT();
+
+ sc->sc_time_ms += sc->sc_delay;
+ sc->sc_delay = 0;
+
+ ukbd_interrupt(sc);
+
+ /* Make sure any leftover key events gets read out */
+ ukbd_event_keyinput(sc);
+
+ if (ukbd_any_key_pressed(sc) || (sc->sc_inputs != 0)) {
+ ukbd_start_timer(sc);
+ }
+}
+
+static uint8_t
+ukbd_apple_fn(uint8_t keycode) {
+ switch (keycode) {
+ case 0x28: return 0x49; /* RETURN -> INSERT */
+ case 0x2a: return 0x4c; /* BACKSPACE -> DEL */
+ case 0x50: return 0x4a; /* LEFT ARROW -> HOME */
+ case 0x4f: return 0x4d; /* RIGHT ARROW -> END */
+ case 0x52: return 0x4b; /* UP ARROW -> PGUP */
+ case 0x51: return 0x4e; /* DOWN ARROW -> PGDN */
+ default: return keycode;
+ }
+}
+
+static uint8_t
+ukbd_apple_swap(uint8_t keycode) {
+ switch (keycode) {
+ case 0x35: return 0x64;
+ case 0x64: return 0x35;
+ default: return keycode;
+ }
+}
+
+static void
+ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ukbd_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_page_cache *pc;
+ uint8_t i;
+ uint8_t offset;
+ uint8_t id;
+ int len;
+
+ UKBD_LOCK_ASSERT();
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("actlen=%d bytes\n", len);
+
+ if (len == 0) {
+ DPRINTF("zero length data\n");
+ goto tr_setup;
+ }
+
+ if (sc->sc_kbd_id != 0) {
+ /* check and remove HID ID byte */
+ usbd_copy_out(pc, 0, &id, 1);
+ offset = 1;
+ len--;
+ if (len == 0) {
+ DPRINTF("zero length data\n");
+ goto tr_setup;
+ }
+ } else {
+ offset = 0;
+ id = 0;
+ }
+
+ if (len > UKBD_BUFFER_SIZE)
+ len = UKBD_BUFFER_SIZE;
+
+ /* get data */
+ usbd_copy_out(pc, offset, sc->sc_buffer, len);
+
+ /* clear temporary storage */
+ memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
+
+ /* scan through HID data */
+ if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
+ (id == sc->sc_id_apple_eject)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_eject))
+ sc->sc_modifiers |= MOD_EJECT;
+ else
+ sc->sc_modifiers &= ~MOD_EJECT;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
+ (id == sc->sc_id_apple_fn)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_fn))
+ sc->sc_modifiers |= MOD_FN;
+ else
+ sc->sc_modifiers &= ~MOD_FN;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_CTRL_L) &&
+ (id == sc->sc_id_ctrl_l)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_l))
+ sc-> sc_modifiers |= MOD_CONTROL_L;
+ else
+ sc-> sc_modifiers &= ~MOD_CONTROL_L;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_CTRL_R) &&
+ (id == sc->sc_id_ctrl_r)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_r))
+ sc->sc_modifiers |= MOD_CONTROL_R;
+ else
+ sc->sc_modifiers &= ~MOD_CONTROL_R;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_SHIFT_L) &&
+ (id == sc->sc_id_shift_l)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_l))
+ sc->sc_modifiers |= MOD_SHIFT_L;
+ else
+ sc->sc_modifiers &= ~MOD_SHIFT_L;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_SHIFT_R) &&
+ (id == sc->sc_id_shift_r)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_r))
+ sc->sc_modifiers |= MOD_SHIFT_R;
+ else
+ sc->sc_modifiers &= ~MOD_SHIFT_R;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_ALT_L) &&
+ (id == sc->sc_id_alt_l)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_l))
+ sc->sc_modifiers |= MOD_ALT_L;
+ else
+ sc->sc_modifiers &= ~MOD_ALT_L;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_ALT_R) &&
+ (id == sc->sc_id_alt_r)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_r))
+ sc->sc_modifiers |= MOD_ALT_R;
+ else
+ sc->sc_modifiers &= ~MOD_ALT_R;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_WIN_L) &&
+ (id == sc->sc_id_win_l)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_l))
+ sc->sc_modifiers |= MOD_WIN_L;
+ else
+ sc->sc_modifiers &= ~MOD_WIN_L;
+ }
+ if ((sc->sc_flags & UKBD_FLAG_WIN_R) &&
+ (id == sc->sc_id_win_r)) {
+ if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_r))
+ sc->sc_modifiers |= MOD_WIN_R;
+ else
+ sc->sc_modifiers &= ~MOD_WIN_R;
+ }
+
+ sc->sc_ndata.modifiers = sc->sc_modifiers;
+
+ if ((sc->sc_flags & UKBD_FLAG_EVENTS) &&
+ (id == sc->sc_id_events)) {
+ i = sc->sc_loc_events.count;
+ if (i > UKBD_NKEYCODE)
+ i = UKBD_NKEYCODE;
+ if (i > len)
+ i = len;
+ while (i--) {
+ sc->sc_ndata.keycode[i] =
+ hid_get_data(sc->sc_buffer + i, len - i,
+ &sc->sc_loc_events);
+ }
+ }
+
+#ifdef USB_DEBUG
+ DPRINTF("modifiers = 0x%04x\n", (int)sc->sc_modifiers);
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ if (sc->sc_ndata.keycode[i]) {
+ DPRINTF("[%d] = 0x%02x\n",
+ (int)i, (int)sc->sc_ndata.keycode[i]);
+ }
+ }
+#endif
+ if (sc->sc_modifiers & MOD_FN) {
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ sc->sc_ndata.keycode[i] =
+ ukbd_apple_fn(sc->sc_ndata.keycode[i]);
+ }
+ }
+
+ if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP) {
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ sc->sc_ndata.keycode[i] =
+ ukbd_apple_swap(sc->sc_ndata.keycode[i]);
+ }
+ }
+
+ ukbd_interrupt(sc);
+
+ if (ukbd_any_key_pressed(sc) != 0) {
+ ukbd_start_timer(sc);
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ } else {
+ DPRINTF("input queue is full!\n");
+ }
+ break;
+
+ default: /* Error */
+ DPRINTF("error=%s\n", usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ukbd_set_leds_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ukbd_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_device_request req;
+ struct usb_page_cache *pc;
+ uint8_t id;
+ uint8_t any;
+ int len;
+
+ UKBD_LOCK_ASSERT();
+
+#ifdef USB_DEBUG
+ if (ukbd_no_leds)
+ return;
+#endif
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ if (!(sc->sc_flags & UKBD_FLAG_SET_LEDS))
+ break;
+ sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ req.wLength[1] = 0;
+
+ memset(sc->sc_buffer, 0, UKBD_BUFFER_SIZE);
+
+ id = 0;
+ any = 0;
+
+ /* Assumption: All led bits must be in the same ID. */
+
+ if (sc->sc_flags & UKBD_FLAG_NUMLOCK) {
+ if (sc->sc_leds & NLKED) {
+ hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
+ &sc->sc_loc_numlock, 1);
+ }
+ id = sc->sc_id_numlock;
+ any = 1;
+ }
+
+ if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK) {
+ if (sc->sc_leds & SLKED) {
+ hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
+ &sc->sc_loc_scrolllock, 1);
+ }
+ id = sc->sc_id_scrolllock;
+ any = 1;
+ }
+
+ if (sc->sc_flags & UKBD_FLAG_CAPSLOCK) {
+ if (sc->sc_leds & CLKED) {
+ hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
+ &sc->sc_loc_capslock, 1);
+ }
+ id = sc->sc_id_capslock;
+ any = 1;
+ }
+
+ /* if no leds, nothing to do */
+ if (!any)
+ break;
+
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_evdev != NULL)
+ evdev_push_leds(sc->sc_evdev, sc->sc_leds);
+#endif
+
+ /* range check output report length */
+ len = sc->sc_led_size;
+ if (len > (UKBD_BUFFER_SIZE - 1))
+ len = (UKBD_BUFFER_SIZE - 1);
+
+ /* check if we need to prefix an ID byte */
+ sc->sc_buffer[0] = id;
+
+ pc = usbd_xfer_get_frame(xfer, 1);
+ if (id != 0) {
+ len++;
+ usbd_copy_in(pc, 0, sc->sc_buffer, len);
+ } else {
+ usbd_copy_in(pc, 0, sc->sc_buffer + 1, len);
+ }
+ req.wLength[0] = len;
+ usbd_xfer_set_frame_len(xfer, 1, len);
+
+ DPRINTF("len=%d, id=%d\n", len, id);
+
+ /* setup control request last */
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+
+ /* start data transfer */
+ usbd_xfer_set_frames(xfer, 2);
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTFN(1, "error=%s\n", usbd_errstr(error));
+ break;
+ }
+}
+
+static const struct usb_config ukbd_config[UKBD_N_TRANSFER] = {
+
+ [UKBD_INTR_DT_0] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .callback = &ukbd_intr_callback,
+ },
+
+ [UKBD_INTR_DT_1] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .callback = &ukbd_intr_callback,
+ },
+
+ [UKBD_CTRL_LED] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .bufsize = sizeof(struct usb_device_request) + UKBD_BUFFER_SIZE,
+ .callback = &ukbd_set_leds_callback,
+ .timeout = 1000, /* 1 second */
+ },
+};
+
+/* A match on these entries will load ukbd */
+static const STRUCT_USB_HOST_ID __used ukbd_devs[] = {
+ {USB_IFACE_CLASS(UICLASS_HID),
+ USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
+ USB_IFACE_PROTOCOL(UIPROTO_BOOT_KEYBOARD),},
+};
+
+static int
+ukbd_probe(device_t dev)
+{
+ keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ void *d_ptr;
+ int error;
+ uint16_t d_len;
+
+ UKBD_LOCK_ASSERT();
+ DPRINTFN(11, "\n");
+
+ if (sw == NULL) {
+ return (ENXIO);
+ }
+ if (uaa->usb_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+
+ if (uaa->info.bInterfaceClass != UICLASS_HID)
+ return (ENXIO);
+
+ if (usb_test_quirk(uaa, UQ_KBD_IGNORE))
+ return (ENXIO);
+
+ if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
+ (uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD))
+ return (BUS_PROBE_DEFAULT);
+
+ error = usbd_req_get_hid_desc(uaa->device, NULL,
+ &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (error)
+ return (ENXIO);
+
+ if (hid_is_keyboard(d_ptr, d_len)) {
+ if (hid_is_mouse(d_ptr, d_len)) {
+ /*
+ * NOTE: We currently don't support USB mouse
+ * and USB keyboard on the same USB endpoint.
+ * Let "ums" driver win.
+ */
+ error = ENXIO;
+ } else {
+ error = BUS_PROBE_DEFAULT;
+ }
+ } else {
+ error = ENXIO;
+ }
+ free(d_ptr, M_TEMP);
+ return (error);
+}
+
+static void
+ukbd_parse_hid(struct ukbd_softc *sc, const uint8_t *ptr, uint32_t len)
+{
+ uint32_t flags;
+
+ /* reset detected bits */
+ sc->sc_flags &= ~UKBD_FLAG_HID_MASK;
+
+ /* check if there is an ID byte */
+ sc->sc_kbd_size = hid_report_size(ptr, len,
+ hid_input, &sc->sc_kbd_id);
+
+ /* investigate if this is an Apple Keyboard */
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_CONSUMER, HUG_APPLE_EJECT),
+ hid_input, 0, &sc->sc_loc_apple_eject, &flags,
+ &sc->sc_id_apple_eject)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_APPLE_EJECT |
+ UKBD_FLAG_APPLE_SWAP;
+ DPRINTFN(1, "Found Apple eject-key\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(0xFFFF, 0x0003),
+ hid_input, 0, &sc->sc_loc_apple_fn, &flags,
+ &sc->sc_id_apple_fn)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_APPLE_FN;
+ DPRINTFN(1, "Found Apple FN-key\n");
+ }
+ /* figure out some keys */
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE0),
+ hid_input, 0, &sc->sc_loc_ctrl_l, &flags,
+ &sc->sc_id_ctrl_l)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_CTRL_L;
+ DPRINTFN(1, "Found left control\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE4),
+ hid_input, 0, &sc->sc_loc_ctrl_r, &flags,
+ &sc->sc_id_ctrl_r)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_CTRL_R;
+ DPRINTFN(1, "Found right control\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE1),
+ hid_input, 0, &sc->sc_loc_shift_l, &flags,
+ &sc->sc_id_shift_l)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_SHIFT_L;
+ DPRINTFN(1, "Found left shift\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE5),
+ hid_input, 0, &sc->sc_loc_shift_r, &flags,
+ &sc->sc_id_shift_r)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_SHIFT_R;
+ DPRINTFN(1, "Found right shift\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE2),
+ hid_input, 0, &sc->sc_loc_alt_l, &flags,
+ &sc->sc_id_alt_l)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_ALT_L;
+ DPRINTFN(1, "Found left alt\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE6),
+ hid_input, 0, &sc->sc_loc_alt_r, &flags,
+ &sc->sc_id_alt_r)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_ALT_R;
+ DPRINTFN(1, "Found right alt\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE3),
+ hid_input, 0, &sc->sc_loc_win_l, &flags,
+ &sc->sc_id_win_l)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_WIN_L;
+ DPRINTFN(1, "Found left GUI\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0xE7),
+ hid_input, 0, &sc->sc_loc_win_r, &flags,
+ &sc->sc_id_win_r)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_WIN_R;
+ DPRINTFN(1, "Found right GUI\n");
+ }
+ /* figure out event buffer */
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_KEYBOARD, 0x00),
+ hid_input, 0, &sc->sc_loc_events, &flags,
+ &sc->sc_id_events)) {
+ if (flags & HIO_VARIABLE) {
+ DPRINTFN(1, "Ignoring keyboard event control\n");
+ } else {
+ sc->sc_flags |= UKBD_FLAG_EVENTS;
+ DPRINTFN(1, "Found keyboard event array\n");
+ }
+ }
+
+ /* figure out leds on keyboard */
+ sc->sc_led_size = hid_report_size(ptr, len,
+ hid_output, NULL);
+
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_LEDS, 0x01),
+ hid_output, 0, &sc->sc_loc_numlock, &flags,
+ &sc->sc_id_numlock)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_NUMLOCK;
+ DPRINTFN(1, "Found keyboard numlock\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_LEDS, 0x02),
+ hid_output, 0, &sc->sc_loc_capslock, &flags,
+ &sc->sc_id_capslock)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_CAPSLOCK;
+ DPRINTFN(1, "Found keyboard capslock\n");
+ }
+ if (hid_locate(ptr, len,
+ HID_USAGE2(HUP_LEDS, 0x03),
+ hid_output, 0, &sc->sc_loc_scrolllock, &flags,
+ &sc->sc_id_scrolllock)) {
+ if (flags & HIO_VARIABLE)
+ sc->sc_flags |= UKBD_FLAG_SCROLLLOCK;
+ DPRINTFN(1, "Found keyboard scrolllock\n");
+ }
+}
+
+static int
+ukbd_attach(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ int unit = device_get_unit(dev);
+ keyboard_t *kbd = &sc->sc_kbd;
+ void *hid_ptr = NULL;
+ usb_error_t err;
+ uint16_t n;
+ uint16_t hid_len;
+#ifdef EVDEV_SUPPORT
+ struct evdev_dev *evdev;
+ int i;
+#endif
+#ifdef USB_DEBUG
+ int rate;
+#endif
+ UKBD_LOCK_ASSERT();
+
+ kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
+
+ kbd->kb_data = (void *)sc;
+
+ device_set_usb_desc(dev);
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface = uaa->iface;
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_mode = K_XLATE;
+
+ usb_callout_init_mtx(&sc->sc_callout, &Giant, 0);
+
+#ifdef UKBD_NO_POLLING
+ err = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
+ UKBD_N_TRANSFER, sc, &Giant);
+#else
+ /*
+ * Setup the UKBD USB transfers one by one, so they are memory
+ * independent which allows for handling panics triggered by
+ * the keyboard driver itself, typically via CTRL+ALT+ESC
+ * sequences. Or if the USB keyboard driver was processing a
+ * key at the moment of panic.
+ */
+ for (n = 0; n != UKBD_N_TRANSFER; n++) {
+ err = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer + n, ukbd_config + n,
+ 1, sc, &Giant);
+ if (err)
+ break;
+ }
+#endif
+
+ if (err) {
+ DPRINTF("error=%s\n", usbd_errstr(err));
+ goto detach;
+ }
+ /* setup default keyboard maps */
+
+ sc->sc_keymap = key_map;
+ sc->sc_accmap = accent_map;
+ for (n = 0; n < UKBD_NFKEY; n++) {
+ sc->sc_fkeymap[n] = fkey_tab[n];
+ }
+
+ kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
+ sc->sc_fkeymap, UKBD_NFKEY);
+
+ KBD_FOUND_DEVICE(kbd);
+
+ ukbd_clear_state(kbd);
+
+ /*
+ * FIXME: set the initial value for lock keys in "sc_state"
+ * according to the BIOS data?
+ */
+ KBD_PROBE_DONE(kbd);
+
+ /* get HID descriptor */
+ err = usbd_req_get_hid_desc(uaa->device, NULL, &hid_ptr,
+ &hid_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (err == 0) {
+ DPRINTF("Parsing HID descriptor of %d bytes\n",
+ (int)hid_len);
+
+ ukbd_parse_hid(sc, hid_ptr, hid_len);
+
+ free(hid_ptr, M_TEMP);
+ }
+
+ /* check if we should use the boot protocol */
+ if (usb_test_quirk(uaa, UQ_KBD_BOOTPROTO) ||
+ (err != 0) || (!(sc->sc_flags & UKBD_FLAG_EVENTS))) {
+
+ DPRINTF("Forcing boot protocol\n");
+
+ err = usbd_req_set_protocol(sc->sc_udev, NULL,
+ sc->sc_iface_index, 0);
+
+ if (err != 0) {
+ DPRINTF("Set protocol error=%s (ignored)\n",
+ usbd_errstr(err));
+ }
+
+ ukbd_parse_hid(sc, ukbd_boot_desc, sizeof(ukbd_boot_desc));
+ }
+
+ /* ignore if SETIDLE fails, hence it is not crucial */
+ usbd_req_set_idle(sc->sc_udev, NULL, sc->sc_iface_index, 0, 0);
+
+ ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
+
+ KBD_INIT_DONE(kbd);
+
+ if (kbd_register(kbd) < 0) {
+ goto detach;
+ }
+ KBD_CONFIG_DONE(kbd);
+
+ ukbd_enable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd)) {
+ goto detach;
+ }
+#endif
+
+#ifdef EVDEV_SUPPORT
+ evdev = evdev_alloc();
+ evdev_set_name(evdev, device_get_desc(dev));
+ evdev_set_phys(evdev, device_get_nameunit(dev));
+ evdev_set_id(evdev, BUS_USB, uaa->info.idVendor,
+ uaa->info.idProduct, 0);
+ evdev_set_serial(evdev, usb_get_serial(uaa->device));
+ evdev_set_methods(evdev, kbd, &ukbd_evdev_methods);
+ evdev_support_event(evdev, EV_SYN);
+ evdev_support_event(evdev, EV_KEY);
+ if (sc->sc_flags & (UKBD_FLAG_NUMLOCK | UKBD_FLAG_CAPSLOCK |
+ UKBD_FLAG_SCROLLLOCK))
+ evdev_support_event(evdev, EV_LED);
+ evdev_support_event(evdev, EV_REP);
+
+ for (i = 0x00; i <= 0xFF; i++)
+ evdev_support_key(evdev, evdev_hid2key(i));
+ if (sc->sc_flags & UKBD_FLAG_NUMLOCK)
+ evdev_support_led(evdev, LED_NUML);
+ if (sc->sc_flags & UKBD_FLAG_CAPSLOCK)
+ evdev_support_led(evdev, LED_CAPSL);
+ if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK)
+ evdev_support_led(evdev, LED_SCROLLL);
+
+ if (evdev_register(evdev))
+ evdev_free(evdev);
+ else
+ sc->sc_evdev = evdev;
+#endif
+
+ sc->sc_flags |= UKBD_FLAG_ATTACHED;
+
+ if (bootverbose) {
+ genkbd_diag(kbd, bootverbose);
+ }
+
+#ifdef USB_DEBUG
+ /* check for polling rate override */
+ rate = ukbd_pollrate;
+ if (rate > 0) {
+ if (rate > 1000)
+ rate = 1;
+ else
+ rate = 1000 / rate;
+
+ /* set new polling interval in ms */
+ usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_0], rate);
+ usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_1], rate);
+ }
+#endif
+ /* start the keyboard */
+ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
+ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
+
+ return (0); /* success */
+
+detach:
+ ukbd_detach(dev);
+ return (ENXIO); /* error */
+}
+
+static int
+ukbd_detach(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+ int error;
+
+ UKBD_LOCK_ASSERT();
+
+ DPRINTF("\n");
+
+ sc->sc_flags |= UKBD_FLAG_GONE;
+
+ usb_callout_stop(&sc->sc_callout);
+
+ /* kill any stuck keys */
+ if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
+ /* stop receiving events from the USB keyboard */
+ usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_0]);
+ usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_1]);
+
+ /* release all leftover keys, if any */
+ memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
+
+ /* process releasing of all keys */
+ ukbd_interrupt(sc);
+ }
+
+ ukbd_disable(&sc->sc_kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
+ error = kbd_detach(&sc->sc_kbd);
+ if (error) {
+ /* usb attach cannot return an error */
+ device_printf(dev, "WARNING: kbd_detach() "
+ "returned non-zero! (ignored)\n");
+ }
+ }
+#endif
+
+#ifdef EVDEV_SUPPORT
+ evdev_free(sc->sc_evdev);
+#endif
+
+ if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
+ error = kbd_unregister(&sc->sc_kbd);
+ if (error) {
+ /* usb attach cannot return an error */
+ device_printf(dev, "WARNING: kbd_unregister() "
+ "returned non-zero! (ignored)\n");
+ }
+ }
+ sc->sc_kbd.kb_flags = 0;
+
+ usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
+
+ usb_callout_drain(&sc->sc_callout);
+
+ DPRINTF("%s: disconnected\n",
+ device_get_nameunit(dev));
+
+ return (0);
+}
+
+static int
+ukbd_resume(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+
+ UKBD_LOCK_ASSERT();
+
+ ukbd_clear_state(&sc->sc_kbd);
+
+ return (0);
+}
+
+/* early keyboard probe, not supported */
+static int
+ukbd_configure(int flags)
+{
+ return (0);
+}
+
+/* detect a keyboard, not used */
+static int
+ukbd__probe(int unit, void *arg, int flags)
+{
+ return (ENXIO);
+}
+
+/* reset and initialize the device, not used */
+static int
+ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ return (ENXIO);
+}
+
+/* test the interface to the device, not used */
+static int
+ukbd_test_if(keyboard_t *kbd)
+{
+ return (0);
+}
+
+/* finish using this keyboard, not used */
+static int
+ukbd_term(keyboard_t *kbd)
+{
+ return (ENXIO);
+}
+
+/* keyboard interrupt routine, not used */
+static int
+ukbd_intr(keyboard_t *kbd, void *arg)
+{
+ return (0);
+}
+
+/* lock the access to the keyboard, not used */
+static int
+ukbd_lock(keyboard_t *kbd, int lock)
+{
+ return (1);
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+ukbd_enable(keyboard_t *kbd)
+{
+
+ UKBD_LOCK();
+ KBD_ACTIVATE(kbd);
+ UKBD_UNLOCK();
+
+ return (0);
+}
+
+/* disallow the access to the device */
+static int
+ukbd_disable(keyboard_t *kbd)
+{
+
+ UKBD_LOCK();
+ KBD_DEACTIVATE(kbd);
+ UKBD_UNLOCK();
+
+ return (0);
+}
+
+/* check if data is waiting */
+/* Currently unused. */
+static int
+ukbd_check(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ UKBD_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (0);
+
+ if (sc->sc_flags & UKBD_FLAG_POLLING)
+ ukbd_do_poll(sc, 0);
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (sc->sc_buffered_char[0]) {
+ return (1);
+ }
+#endif
+ if (sc->sc_inputs > 0) {
+ return (1);
+ }
+ return (0);
+}
+
+/* check if char is waiting */
+static int
+ukbd_check_char_locked(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ UKBD_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (0);
+
+ if ((sc->sc_composed_char > 0) &&
+ (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
+ return (1);
+ }
+ return (ukbd_check(kbd));
+}
+
+static int
+ukbd_check_char(keyboard_t *kbd)
+{
+ int result;
+
+ UKBD_LOCK();
+ result = ukbd_check_char_locked(kbd);
+ UKBD_UNLOCK();
+
+ return (result);
+}
+
+/* read one byte from the keyboard if it's allowed */
+/* Currently unused. */
+static int
+ukbd_read(keyboard_t *kbd, int wait)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+ int32_t usbcode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t keycode;
+ uint32_t scancode;
+
+#endif
+
+ UKBD_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (-1);
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (sc->sc_buffered_char[0]) {
+ scancode = sc->sc_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
+ sc->sc_buffered_char[1] = 0;
+ return (scancode);
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* XXX */
+ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
+ if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
+ return (-1);
+
+ ++(kbd->kb_count);
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ keycode = ukbd_atkeycode(usbcode, sc->sc_ndata.modifiers);
+ if (keycode == NN) {
+ return -1;
+ }
+ return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
+ (usbcode & KEY_RELEASE)));
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ return (usbcode);
+#endif /* UKBD_EMULATE_ATSCANCODE */
+}
+
+/* read char from the keyboard */
+static uint32_t
+ukbd_read_char_locked(keyboard_t *kbd, int wait)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+ uint32_t action;
+ uint32_t keycode;
+ int32_t usbcode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t scancode;
+#endif
+
+ UKBD_LOCK_ASSERT();
+
+ if (!KBD_IS_ACTIVE(kbd))
+ return (NOKEY);
+
+next_code:
+
+ /* do we have a composed char to return ? */
+
+ if ((sc->sc_composed_char > 0) &&
+ (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
+
+ action = sc->sc_composed_char;
+ sc->sc_composed_char = 0;
+
+ if (action > 0xFF) {
+ goto errkey;
+ }
+ goto done;
+ }
+#ifdef UKBD_EMULATE_ATSCANCODE
+
+ /* do we have a pending raw scan code? */
+
+ if (sc->sc_mode == K_RAW) {
+ scancode = sc->sc_buffered_char[0];
+ if (scancode) {
+ if (scancode & SCAN_PREFIX) {
+ sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
+ sc->sc_buffered_char[1] = 0;
+ return (scancode);
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* see if there is something in the keyboard port */
+ /* XXX */
+ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
+ if (usbcode == -1) {
+ return (NOKEY);
+ }
+ ++kbd->kb_count;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* USB key index -> key code -> AT scan code */
+ keycode = ukbd_atkeycode(usbcode, sc->sc_ndata.modifiers);
+ if (keycode == NN) {
+ return (NOKEY);
+ }
+ /* return an AT scan code for the K_RAW mode */
+ if (sc->sc_mode == K_RAW) {
+ return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
+ (usbcode & KEY_RELEASE)));
+ }
+#else /* !UKBD_EMULATE_ATSCANCODE */
+
+ /* return the byte as is for the K_RAW mode */
+ if (sc->sc_mode == K_RAW) {
+ return (usbcode);
+ }
+ /* USB key index -> key code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN) {
+ return (NOKEY);
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ switch (keycode) {
+ case 0x38: /* left alt (compose key) */
+ if (usbcode & KEY_RELEASE) {
+ if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
+ sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
+
+ if (sc->sc_composed_char > 0xFF) {
+ sc->sc_composed_char = 0;
+ }
+ }
+ } else {
+ if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
+ sc->sc_flags |= UKBD_FLAG_COMPOSE;
+ sc->sc_composed_char = 0;
+ }
+ }
+ break;
+ }
+
+ /* return the key code in the K_CODE mode */
+ if (usbcode & KEY_RELEASE) {
+ keycode |= SCAN_RELEASE;
+ }
+ if (sc->sc_mode == K_CODE) {
+ return (keycode);
+ }
+ /* compose a character code */
+ if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
+ switch (keycode) {
+ /* key pressed, process it */
+ case 0x47:
+ case 0x48:
+ case 0x49: /* keypad 7,8,9 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x40;
+ goto check_composed;
+
+ case 0x4B:
+ case 0x4C:
+ case 0x4D: /* keypad 4,5,6 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x47;
+ goto check_composed;
+
+ case 0x4F:
+ case 0x50:
+ case 0x51: /* keypad 1,2,3 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x4E;
+ goto check_composed;
+
+ case 0x52: /* keypad 0 */
+ sc->sc_composed_char *= 10;
+ goto check_composed;
+
+ /* key released, no interest here */
+ case SCAN_RELEASE | 0x47:
+ case SCAN_RELEASE | 0x48:
+ case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
+ case SCAN_RELEASE | 0x4B:
+ case SCAN_RELEASE | 0x4C:
+ case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
+ case SCAN_RELEASE | 0x4F:
+ case SCAN_RELEASE | 0x50:
+ case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
+ case SCAN_RELEASE | 0x52: /* keypad 0 */
+ goto next_code;
+
+ case 0x38: /* left alt key */
+ break;
+
+ default:
+ if (sc->sc_composed_char > 0) {
+ sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
+ sc->sc_composed_char = 0;
+ goto errkey;
+ }
+ break;
+ }
+ }
+ /* keycode to key action */
+ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
+ (keycode & SCAN_RELEASE),
+ &sc->sc_state, &sc->sc_accents);
+ if (action == NOKEY) {
+ goto next_code;
+ }
+done:
+ return (action);
+
+check_composed:
+ if (sc->sc_composed_char <= 0xFF) {
+ goto next_code;
+ }
+errkey:
+ return (ERRKEY);
+}
+
+/* Currently wait is always false. */
+static uint32_t
+ukbd_read_char(keyboard_t *kbd, int wait)
+{
+ uint32_t keycode;
+
+ UKBD_LOCK();
+ keycode = ukbd_read_char_locked(kbd, wait);
+ UKBD_UNLOCK();
+
+ return (keycode);
+}
+
+/* some useful control functions */
+static int
+ukbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+ int i;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ int ival;
+
+#endif
+
+ UKBD_LOCK_ASSERT();
+
+ switch (cmd) {
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = sc->sc_mode;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 7):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)arg) {
+ if ((sc->sc_flags & UKBD_FLAG_POLLING) == 0)
+ ukbd_clear_state(kbd);
+ sc->sc_mode = *(int *)arg;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 66):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in "sc_state" won't be changed */
+ if (*(int *)arg & ~LOCK_MASK)
+ return (EINVAL);
+
+ i = *(int *)arg;
+
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (sc->sc_mode == K_XLATE &&
+ kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd))
+ ukbd_set_leds(sc, i);
+
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = sc->sc_state & LOCK_MASK;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 20):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ return (EINVAL);
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)arg;
+
+ /* set LEDs and quit */
+ return (ukbd_ioctl(kbd, KDSETLED, arg));
+
+ case KDSETREPEAT: /* set keyboard repeat rate (new
+ * interface) */
+ if (!KBD_HAS_DEVICE(kbd)) {
+ return (0);
+ }
+ /*
+ * Convert negative, zero and tiny args to the same limits
+ * as atkbd. We could support delays of 1 msec, but
+ * anything much shorter than the shortest atkbd value
+ * of 250.34 is almost unusable as well as incompatible.
+ */
+ kbd->kb_delay1 = imax(((int *)arg)[0], 250);
+ kbd->kb_delay2 = imax(((int *)arg)[1], 34);
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_evdev != NULL)
+ evdev_push_repeats(sc->sc_evdev, kbd);
+#endif
+ return (0);
+
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 67):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETRAD: /* set keyboard repeat rate (old
+ * interface) */
+ return (ukbd_set_typematic(kbd, *(int *)arg));
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ case OPIO_KEYMAP: /* set keyboard translation table
+ * (compat) */
+ case PIO_KEYMAPENT: /* set keyboard translation table
+ * entry */
+ case PIO_DEADKEYMAP: /* set accent key translation table */
+ sc->sc_accents = 0;
+ /* FALLTHROUGH */
+ default:
+ return (genkbd_commonioctl(kbd, cmd, arg));
+ }
+
+ return (0);
+}
+
+static int
+ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ int result;
+
+ /*
+ * XXX Check if someone is calling us from a critical section:
+ */
+ if (curthread->td_critnest != 0)
+ return (EDEADLK);
+
+ /*
+ * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
+ * context where printf(9) can be called, which among other things
+ * includes interrupt filters and threads with any kinds of locks
+ * already held. For this reason it would be dangerous to acquire
+ * the Giant here unconditionally. On the other hand we have to
+ * have it to handle the ioctl.
+ * So we make our best effort to auto-detect whether we can grab
+ * the Giant or not. Blame syscons(4) for this.
+ */
+ switch (cmd) {
+ case KDGKBSTATE:
+ case KDSKBSTATE:
+ case KDSETLED:
+ if (!mtx_owned(&Giant) && !USB_IN_POLLING_MODE_FUNC())
+ return (EDEADLK); /* best I could come up with */
+ /* FALLTHROUGH */
+ default:
+ UKBD_LOCK();
+ result = ukbd_ioctl_locked(kbd, cmd, arg);
+ UKBD_UNLOCK();
+ return (result);
+ }
+}
+
+
+/* clear the internal state of the keyboard */
+static void
+ukbd_clear_state(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ UKBD_LOCK_ASSERT();
+
+ sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
+ sc->sc_state &= LOCK_MASK; /* preserve locking key state */
+ sc->sc_accents = 0;
+ sc->sc_composed_char = 0;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ sc->sc_buffered_char[0] = 0;
+ sc->sc_buffered_char[1] = 0;
+#endif
+ memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
+ memset(&sc->sc_odata, 0, sizeof(sc->sc_odata));
+ memset(&sc->sc_ntime, 0, sizeof(sc->sc_ntime));
+ memset(&sc->sc_otime, 0, sizeof(sc->sc_otime));
+}
+
+/* save the internal state, not used */
+static int
+ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (len == 0) ? 1 : -1;
+}
+
+/* set the internal state, not used */
+static int
+ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ return (EINVAL);
+}
+
+static int
+ukbd_poll(keyboard_t *kbd, int on)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ UKBD_LOCK();
+ /*
+ * Keep a reference count on polling to allow recursive
+ * cngrab() during a panic for example.
+ */
+ if (on)
+ sc->sc_polling++;
+ else if (sc->sc_polling > 0)
+ sc->sc_polling--;
+
+ if (sc->sc_polling != 0) {
+ sc->sc_flags |= UKBD_FLAG_POLLING;
+ sc->sc_poll_thread = curthread;
+ } else {
+ sc->sc_flags &= ~UKBD_FLAG_POLLING;
+ sc->sc_delay = 0;
+ }
+ UKBD_UNLOCK();
+
+ return (0);
+}
+
+/* local functions */
+
+static void
+ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
+{
+
+ UKBD_LOCK_ASSERT();
+ DPRINTF("leds=0x%02x\n", leds);
+
+ sc->sc_leds = leds;
+ sc->sc_flags |= UKBD_FLAG_SET_LEDS;
+
+ /* start transfer, if not already started */
+
+ usbd_transfer_start(sc->sc_xfer[UKBD_CTRL_LED]);
+}
+
+static int
+ukbd_set_typematic(keyboard_t *kbd, int code)
+{
+#ifdef EVDEV_SUPPORT
+ struct ukbd_softc *sc = kbd->kb_data;
+#endif
+ static const int delays[] = {250, 500, 750, 1000};
+ static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504};
+
+ if (code & ~0x7f) {
+ return (EINVAL);
+ }
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_evdev != NULL)
+ evdev_push_repeats(sc->sc_evdev, kbd);
+#endif
+ return (0);
+}
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+static uint32_t
+ukbd_atkeycode(int usbcode, int shift)
+{
+ uint32_t keycode;
+
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ /*
+ * Translate Alt-PrintScreen to SysRq.
+ *
+ * Some or all AT keyboards connected through USB have already
+ * mapped Alted PrintScreens to an unusual usbcode (0x8a).
+ * ukbd_trtab translates this to 0x7e, and key2scan() would
+ * translate that to 0x79 (Intl' 4). Assume that if we have
+ * an Alted 0x7e here then it actually is an Alted PrintScreen.
+ *
+ * The usual usbcode for all PrintScreens is 0x46. ukbd_trtab
+ * translates this to 0x5c, so the Alt check to classify 0x5c
+ * is routine.
+ */
+ if ((keycode == 0x5c || keycode == 0x7e) &&
+ shift & (MOD_ALT_L | MOD_ALT_R))
+ return (0x54);
+ return (keycode);
+}
+
+static int
+ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up)
+{
+ static const int scan[] = {
+ /* 89 */
+ 0x11c, /* Enter */
+ /* 90-99 */
+ 0x11d, /* Ctrl-R */
+ 0x135, /* Divide */
+ 0x137, /* PrintScreen */
+ 0x138, /* Alt-R */
+ 0x147, /* Home */
+ 0x148, /* Up */
+ 0x149, /* PageUp */
+ 0x14b, /* Left */
+ 0x14d, /* Right */
+ 0x14f, /* End */
+ /* 100-109 */
+ 0x150, /* Down */
+ 0x151, /* PageDown */
+ 0x152, /* Insert */
+ 0x153, /* Delete */
+ 0x146, /* Pause/Break */
+ 0x15b, /* Win_L(Super_L) */
+ 0x15c, /* Win_R(Super_R) */
+ 0x15d, /* Application(Menu) */
+
+ /* SUN TYPE 6 USB KEYBOARD */
+ 0x168, /* Sun Type 6 Help */
+ 0x15e, /* Sun Type 6 Stop */
+ /* 110 - 119 */
+ 0x15f, /* Sun Type 6 Again */
+ 0x160, /* Sun Type 6 Props */
+ 0x161, /* Sun Type 6 Undo */
+ 0x162, /* Sun Type 6 Front */
+ 0x163, /* Sun Type 6 Copy */
+ 0x164, /* Sun Type 6 Open */
+ 0x165, /* Sun Type 6 Paste */
+ 0x166, /* Sun Type 6 Find */
+ 0x167, /* Sun Type 6 Cut */
+ 0x125, /* Sun Type 6 Mute */
+ /* 120 - 130 */
+ 0x11f, /* Sun Type 6 VolumeDown */
+ 0x11e, /* Sun Type 6 VolumeUp */
+ 0x120, /* Sun Type 6 PowerDown */
+
+ /* Japanese 106/109 keyboard */
+ 0x73, /* Keyboard Intl' 1 (backslash / underscore) */
+ 0x70, /* Keyboard Intl' 2 (Katakana / Hiragana) */
+ 0x7d, /* Keyboard Intl' 3 (Yen sign) (Not using in jp106/109) */
+ 0x79, /* Keyboard Intl' 4 (Henkan) */
+ 0x7b, /* Keyboard Intl' 5 (Muhenkan) */
+ 0x5c, /* Keyboard Intl' 6 (Keypad ,) (For PC-9821 layout) */
+ 0x71, /* Apple Keyboard JIS (Kana) */
+ 0x72, /* Apple Keyboard JIS (Eisu) */
+ };
+
+ if ((code >= 89) && (code < (int)(89 + nitems(scan)))) {
+ code = scan[code - 89];
+ }
+ /* PrintScreen */
+ if (code == 0x137 && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R |
+ MOD_SHIFT_L | MOD_SHIFT_R)))) {
+ code |= SCAN_PREFIX_SHIFT;
+ }
+ /* Pause/Break */
+ if ((code == 0x146) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) {
+ code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
+ }
+ code |= (up ? SCAN_RELEASE : SCAN_PRESS);
+
+ if (code & SCAN_PREFIX) {
+ if (code & SCAN_PREFIX_CTL) {
+ /* Ctrl */
+ sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
+ sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
+ } else if (code & SCAN_PREFIX_SHIFT) {
+ /* Shift */
+ sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
+ sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
+ } else {
+ sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
+ sc->sc_buffered_char[1] = 0;
+ }
+ return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return (code);
+
+}
+
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+static keyboard_switch_t ukbdsw = {
+ .probe = &ukbd__probe,
+ .init = &ukbd_init,
+ .term = &ukbd_term,
+ .intr = &ukbd_intr,
+ .test_if = &ukbd_test_if,
+ .enable = &ukbd_enable,
+ .disable = &ukbd_disable,
+ .read = &ukbd_read,
+ .check = &ukbd_check,
+ .read_char = &ukbd_read_char,
+ .check_char = &ukbd_check_char,
+ .ioctl = &ukbd_ioctl,
+ .lock = &ukbd_lock,
+ .clear_state = &ukbd_clear_state,
+ .get_state = &ukbd_get_state,
+ .set_state = &ukbd_set_state,
+ .get_fkeystr = &genkbd_get_fkeystr,
+ .poll = &ukbd_poll,
+ .diag = &genkbd_diag,
+};
+
+KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
+
+static int
+ukbd_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ kbd_add_driver(&ukbd_kbd_driver);
+ break;
+ case MOD_UNLOAD:
+ kbd_delete_driver(&ukbd_kbd_driver);
+ break;
+ }
+ return (0);
+}
+
+static devclass_t ukbd_devclass;
+
+static device_method_t ukbd_methods[] = {
+ DEVMETHOD(device_probe, ukbd_probe),
+ DEVMETHOD(device_attach, ukbd_attach),
+ DEVMETHOD(device_detach, ukbd_detach),
+ DEVMETHOD(device_resume, ukbd_resume),
+
+ DEVMETHOD_END
+};
+
+static driver_t ukbd_driver = {
+ .name = "ukbd",
+ .methods = ukbd_methods,
+ .size = sizeof(struct ukbd_softc),
+};
+
+DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
+MODULE_DEPEND(ukbd, usb, 1, 1, 1);
+#ifdef EVDEV_SUPPORT
+MODULE_DEPEND(ukbd, evdev, 1, 1, 1);
+#endif
+MODULE_VERSION(ukbd, 1);
+USB_PNP_HOST_INFO(ukbd_devs);
diff --git a/freebsd/sys/dev/usb/input/ums.c b/freebsd/sys/dev/usb/input/ums.c
new file mode 100644
index 00000000..1af6d63e
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/ums.c
@@ -0,0 +1,1231 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include <rtems/bsd/local/opt_evdev.h>
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/sbuf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR ums_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#endif
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/mouse.h>
+
+#ifdef USB_DEBUG
+static int ums_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
+SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &ums_debug, 0, "Debug level");
+#endif
+
+#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
+#define MOUSE_FLAGS (HIO_RELATIVE)
+
+#define UMS_BUF_SIZE 8 /* bytes */
+#define UMS_IFQ_MAXLEN 50 /* units */
+#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */
+#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
+#define UMS_INFO_MAX 2 /* maximum number of HID sets */
+
+enum {
+ UMS_INTR_DT,
+ UMS_N_TRANSFER,
+};
+
+struct ums_info {
+ struct hid_location sc_loc_w;
+ struct hid_location sc_loc_x;
+ struct hid_location sc_loc_y;
+ struct hid_location sc_loc_z;
+ struct hid_location sc_loc_t;
+ struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
+
+ uint32_t sc_flags;
+#define UMS_FLAG_X_AXIS 0x0001
+#define UMS_FLAG_Y_AXIS 0x0002
+#define UMS_FLAG_Z_AXIS 0x0004
+#define UMS_FLAG_T_AXIS 0x0008
+#define UMS_FLAG_SBU 0x0010 /* spurious button up events */
+#define UMS_FLAG_REVZ 0x0020 /* Z-axis is reversed */
+#define UMS_FLAG_W_AXIS 0x0040
+
+ uint8_t sc_iid_w;
+ uint8_t sc_iid_x;
+ uint8_t sc_iid_y;
+ uint8_t sc_iid_z;
+ uint8_t sc_iid_t;
+ uint8_t sc_iid_btn[UMS_BUTTON_MAX];
+ uint8_t sc_buttons;
+};
+
+struct ums_softc {
+ struct usb_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+ struct usb_callout sc_callout;
+ struct ums_info sc_info[UMS_INFO_MAX];
+
+ mousehw_t sc_hw;
+ mousemode_t sc_mode;
+ mousestatus_t sc_status;
+
+ struct usb_xfer *sc_xfer[UMS_N_TRANSFER];
+
+ int sc_pollrate;
+ int sc_fflags;
+#ifdef EVDEV_SUPPORT
+ int sc_evflags;
+#define UMS_EVDEV_OPENED 1
+#endif
+
+ uint8_t sc_buttons;
+ uint8_t sc_iid;
+ uint8_t sc_temp[64];
+
+#ifdef EVDEV_SUPPORT
+ struct evdev_dev *sc_evdev;
+#endif
+};
+
+static void ums_put_queue_timeout(void *__sc);
+
+static usb_callback_t ums_intr_callback;
+
+static device_probe_t ums_probe;
+static device_attach_t ums_attach;
+static device_detach_t ums_detach;
+
+static usb_fifo_cmd_t ums_fifo_start_read;
+static usb_fifo_cmd_t ums_fifo_stop_read;
+static usb_fifo_open_t ums_fifo_open;
+static usb_fifo_close_t ums_fifo_close;
+static usb_fifo_ioctl_t ums_fifo_ioctl;
+
+#ifdef EVDEV_SUPPORT
+static evdev_open_t ums_ev_open;
+static evdev_close_t ums_ev_close;
+static void ums_evdev_push(struct ums_softc *, int32_t, int32_t,
+ int32_t, int32_t, int32_t);
+#endif
+
+static void ums_start_rx(struct ums_softc *);
+static void ums_stop_rx(struct ums_softc *);
+static void ums_put_queue(struct ums_softc *, int32_t, int32_t,
+ int32_t, int32_t, int32_t);
+static int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS);
+
+static struct usb_fifo_methods ums_fifo_methods = {
+ .f_open = &ums_fifo_open,
+ .f_close = &ums_fifo_close,
+ .f_ioctl = &ums_fifo_ioctl,
+ .f_start_read = &ums_fifo_start_read,
+ .f_stop_read = &ums_fifo_stop_read,
+ .basename[0] = "ums",
+};
+
+#ifdef EVDEV_SUPPORT
+static const struct evdev_methods ums_evdev_methods = {
+ .ev_open = &ums_ev_open,
+ .ev_close = &ums_ev_close,
+};
+#endif
+
+static void
+ums_put_queue_timeout(void *__sc)
+{
+ struct ums_softc *sc = __sc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ ums_put_queue(sc, 0, 0, 0, 0, 0);
+#ifdef EVDEV_SUPPORT
+ ums_evdev_push(sc, 0, 0, 0, 0, 0);
+#endif
+}
+
+static void
+ums_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ums_softc *sc = usbd_xfer_softc(xfer);
+ struct ums_info *info = &sc->sc_info[0];
+ struct usb_page_cache *pc;
+ uint8_t *buf = sc->sc_temp;
+ int32_t buttons = 0;
+ int32_t buttons_found = 0;
+#ifdef EVDEV_SUPPORT
+ int32_t buttons_reported = 0;
+#endif
+ int32_t dw = 0;
+ int32_t dx = 0;
+ int32_t dy = 0;
+ int32_t dz = 0;
+ int32_t dt = 0;
+ uint8_t i;
+ uint8_t id;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
+
+ if (len > (int)sizeof(sc->sc_temp)) {
+ DPRINTFN(6, "truncating large packet to %zu bytes\n",
+ sizeof(sc->sc_temp));
+ len = sizeof(sc->sc_temp);
+ }
+ if (len == 0)
+ goto tr_setup;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, buf, len);
+
+ DPRINTFN(6, "data = %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n",
+ (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
+ (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
+ (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
+ (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
+
+ if (sc->sc_iid) {
+ id = *buf;
+
+ len--;
+ buf++;
+
+ } else {
+ id = 0;
+ if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) {
+ if ((*buf == 0x14) || (*buf == 0x15)) {
+ goto tr_setup;
+ }
+ }
+ }
+
+ repeat:
+ if ((info->sc_flags & UMS_FLAG_W_AXIS) &&
+ (id == info->sc_iid_w))
+ dw += hid_get_data(buf, len, &info->sc_loc_w);
+
+ if ((info->sc_flags & UMS_FLAG_X_AXIS) &&
+ (id == info->sc_iid_x))
+ dx += hid_get_data(buf, len, &info->sc_loc_x);
+
+ if ((info->sc_flags & UMS_FLAG_Y_AXIS) &&
+ (id == info->sc_iid_y))
+ dy -= hid_get_data(buf, len, &info->sc_loc_y);
+
+ if ((info->sc_flags & UMS_FLAG_Z_AXIS) &&
+ (id == info->sc_iid_z)) {
+ int32_t temp;
+ temp = hid_get_data(buf, len, &info->sc_loc_z);
+ if (info->sc_flags & UMS_FLAG_REVZ)
+ temp = -temp;
+ dz -= temp;
+ }
+
+ if ((info->sc_flags & UMS_FLAG_T_AXIS) &&
+ (id == info->sc_iid_t)) {
+ dt -= hid_get_data(buf, len, &info->sc_loc_t);
+ /* T-axis is translated into button presses */
+ buttons_found |= (1UL << 5) | (1UL << 6);
+ }
+
+ for (i = 0; i < info->sc_buttons; i++) {
+ uint32_t mask;
+ mask = 1UL << UMS_BUT(i);
+ /* check for correct button ID */
+ if (id != info->sc_iid_btn[i])
+ continue;
+ /* check for button pressed */
+ if (hid_get_data(buf, len, &info->sc_loc_btn[i]))
+ buttons |= mask;
+ /* register button mask */
+ buttons_found |= mask;
+ }
+
+ if (++info != &sc->sc_info[UMS_INFO_MAX])
+ goto repeat;
+
+#ifdef EVDEV_SUPPORT
+ buttons_reported = buttons;
+#endif
+ /* keep old button value(s) for non-detected buttons */
+ buttons |= sc->sc_status.button & ~buttons_found;
+
+ if (dx || dy || dz || dt || dw ||
+ (buttons != sc->sc_status.button)) {
+
+ DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
+ dx, dy, dz, dt, dw, buttons);
+
+ /* translate T-axis into button presses until further */
+ if (dt > 0) {
+ ums_put_queue(sc, 0, 0, 0, 0, buttons);
+ buttons |= 1UL << 5;
+ } else if (dt < 0) {
+ ums_put_queue(sc, 0, 0, 0, 0, buttons);
+ buttons |= 1UL << 6;
+ }
+
+ sc->sc_status.button = buttons;
+ sc->sc_status.dx += dx;
+ sc->sc_status.dy += dy;
+ sc->sc_status.dz += dz;
+ /*
+ * sc->sc_status.dt += dt;
+ * no way to export this yet
+ */
+
+ /*
+ * The Qtronix keyboard has a built in PS/2
+ * port for a mouse. The firmware once in a
+ * while posts a spurious button up
+ * event. This event we ignore by doing a
+ * timeout for 50 msecs. If we receive
+ * dx=dy=dz=buttons=0 before we add the event
+ * to the queue. In any other case we delete
+ * the timeout event.
+ */
+ if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) &&
+ (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
+ (dw == 0) && (buttons == 0)) {
+
+ usb_callout_reset(&sc->sc_callout, hz / 20,
+ &ums_put_queue_timeout, sc);
+ } else {
+
+ usb_callout_stop(&sc->sc_callout);
+
+ ums_put_queue(sc, dx, dy, dz, dt, buttons);
+#ifdef EVDEV_SUPPORT
+ ums_evdev_push(sc, dx, dy, dz, dt,
+ buttons_reported);
+#endif
+
+ }
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ /* check if we can put more data into the FIFO */
+ if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) == 0) {
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_evflags == 0)
+ break;
+#else
+ break;
+#endif
+ }
+
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static const struct usb_config ums_config[UMS_N_TRANSFER] = {
+
+ [UMS_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .callback = &ums_intr_callback,
+ },
+};
+
+/* A match on these entries will load ums */
+static const STRUCT_USB_HOST_ID __used ums_devs[] = {
+ {USB_IFACE_CLASS(UICLASS_HID),
+ USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
+ USB_IFACE_PROTOCOL(UIPROTO_MOUSE),},
+};
+
+static int
+ums_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ void *d_ptr;
+ int error;
+ uint16_t d_len;
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ if (uaa->info.bInterfaceClass != UICLASS_HID)
+ return (ENXIO);
+
+ if (usb_test_quirk(uaa, UQ_UMS_IGNORE))
+ return (ENXIO);
+
+ if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
+ (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE))
+ return (BUS_PROBE_DEFAULT);
+
+ error = usbd_req_get_hid_desc(uaa->device, NULL,
+ &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (error)
+ return (ENXIO);
+
+ if (hid_is_mouse(d_ptr, d_len))
+ error = BUS_PROBE_DEFAULT;
+ else
+ error = ENXIO;
+
+ free(d_ptr, M_TEMP);
+ return (error);
+}
+
+static void
+ums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf,
+ uint16_t len, uint8_t index)
+{
+ struct ums_info *info = &sc->sc_info[index];
+ uint32_t flags;
+ uint8_t i;
+ uint8_t j;
+
+ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_X_AXIS;
+ }
+ }
+ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_Y_AXIS;
+ }
+ }
+ /* Try the wheel first as the Z activator since it's tradition. */
+ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags,
+ &info->sc_iid_z) ||
+ hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags,
+ &info->sc_iid_z)) {
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_Z_AXIS;
+ }
+ /*
+ * We might have both a wheel and Z direction, if so put
+ * put the Z on the W coordinate.
+ */
+ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z), hid_input, index, &info->sc_loc_w, &flags,
+ &info->sc_iid_w)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_W_AXIS;
+ }
+ }
+ } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z), hid_input, index, &info->sc_loc_z, &flags,
+ &info->sc_iid_z)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_Z_AXIS;
+ }
+ }
+ /*
+ * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
+ * using 0x0048, which is HUG_TWHEEL, and seems to expect you
+ * to know that the byte after the wheel is the tilt axis.
+ * There are no other HID axis descriptors other than X,Y and
+ * TWHEEL
+ */
+ if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_TWHEEL), hid_input, index, &info->sc_loc_t,
+ &flags, &info->sc_iid_t)) {
+
+ info->sc_loc_t.pos += 8;
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ info->sc_flags |= UMS_FLAG_T_AXIS;
+ }
+ } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER,
+ HUC_AC_PAN), hid_input, index, &info->sc_loc_t,
+ &flags, &info->sc_iid_t)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS)
+ info->sc_flags |= UMS_FLAG_T_AXIS;
+ }
+ /* figure out the number of buttons */
+
+ for (i = 0; i < UMS_BUTTON_MAX; i++) {
+ if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)),
+ hid_input, index, &info->sc_loc_btn[i], NULL,
+ &info->sc_iid_btn[i])) {
+ break;
+ }
+ }
+
+ /* detect other buttons */
+
+ for (j = 0; (i < UMS_BUTTON_MAX) && (j < 2); i++, j++) {
+ if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)),
+ hid_input, index, &info->sc_loc_btn[i], NULL,
+ &info->sc_iid_btn[i])) {
+ break;
+ }
+ }
+
+ info->sc_buttons = i;
+
+ if (i > sc->sc_buttons)
+ sc->sc_buttons = i;
+
+ if (info->sc_flags == 0)
+ return;
+
+ /* announce information about the mouse */
+ device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n",
+ (info->sc_buttons),
+ (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
+ (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
+ (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
+ (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
+ (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "",
+ info->sc_iid_x);
+}
+
+static int
+ums_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct ums_softc *sc = device_get_softc(dev);
+ struct ums_info *info;
+ void *d_ptr = NULL;
+ int isize;
+ int err;
+ uint16_t d_len;
+ uint8_t i;
+#ifdef USB_DEBUG
+ uint8_t j;
+#endif
+
+ DPRINTFN(11, "sc=%p\n", sc);
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
+
+ /*
+ * Force the report (non-boot) protocol.
+ *
+ * Mice without boot protocol support may choose not to implement
+ * Set_Protocol at all; Ignore any error.
+ */
+ err = usbd_req_set_protocol(uaa->device, NULL,
+ uaa->info.bIfaceIndex, 1);
+
+ err = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
+ UMS_N_TRANSFER, sc, &sc->sc_mtx);
+
+ if (err) {
+ DPRINTF("error=%s\n", usbd_errstr(err));
+ goto detach;
+ }
+
+ /* Get HID descriptor */
+ err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
+ &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (err) {
+ device_printf(dev, "error reading report description\n");
+ goto detach;
+ }
+
+ isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
+
+ /*
+ * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
+ * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
+ * all of its other button positions are all off. It also reports that
+ * it has two additional buttons and a tilt wheel.
+ */
+ if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
+
+ sc->sc_iid = 0;
+
+ info = &sc->sc_info[0];
+ info->sc_flags = (UMS_FLAG_X_AXIS |
+ UMS_FLAG_Y_AXIS |
+ UMS_FLAG_Z_AXIS |
+ UMS_FLAG_SBU);
+ info->sc_buttons = 3;
+ isize = 5;
+ /* 1st byte of descriptor report contains garbage */
+ info->sc_loc_x.pos = 16;
+ info->sc_loc_x.size = 8;
+ info->sc_loc_y.pos = 24;
+ info->sc_loc_y.size = 8;
+ info->sc_loc_z.pos = 32;
+ info->sc_loc_z.size = 8;
+ info->sc_loc_btn[0].pos = 8;
+ info->sc_loc_btn[0].size = 1;
+ info->sc_loc_btn[1].pos = 9;
+ info->sc_loc_btn[1].size = 1;
+ info->sc_loc_btn[2].pos = 10;
+ info->sc_loc_btn[2].size = 1;
+
+ /* Announce device */
+ device_printf(dev, "3 buttons and [XYZ] "
+ "coordinates ID=0\n");
+
+ } else {
+ /* Search the HID descriptor and announce device */
+ for (i = 0; i < UMS_INFO_MAX; i++) {
+ ums_hid_parse(sc, dev, d_ptr, d_len, i);
+ }
+ }
+
+ if (usb_test_quirk(uaa, UQ_MS_REVZ)) {
+ info = &sc->sc_info[0];
+ /* Some wheels need the Z axis reversed. */
+ info->sc_flags |= UMS_FLAG_REVZ;
+ }
+ if (isize > (int)usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) {
+ DPRINTF("WARNING: report size, %d bytes, is larger "
+ "than interrupt size, %d bytes!\n", isize,
+ usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT]));
+ }
+ free(d_ptr, M_TEMP);
+ d_ptr = NULL;
+
+#ifdef USB_DEBUG
+ for (j = 0; j < UMS_INFO_MAX; j++) {
+ info = &sc->sc_info[j];
+
+ DPRINTF("sc=%p, index=%d\n", sc, j);
+ DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos,
+ info->sc_loc_x.size, info->sc_iid_x);
+ DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos,
+ info->sc_loc_y.size, info->sc_iid_y);
+ DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos,
+ info->sc_loc_z.size, info->sc_iid_z);
+ DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos,
+ info->sc_loc_t.size, info->sc_iid_t);
+ DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos,
+ info->sc_loc_w.size, info->sc_iid_w);
+
+ for (i = 0; i < info->sc_buttons; i++) {
+ DPRINTF("B%d\t%d/%d id=%d\n",
+ i + 1, info->sc_loc_btn[i].pos,
+ info->sc_loc_btn[i].size, info->sc_iid_btn[i]);
+ }
+ }
+ DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
+#endif
+
+ err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ums_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), -1, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+ if (err)
+ goto detach;
+
+#ifdef EVDEV_SUPPORT
+ sc->sc_evdev = evdev_alloc();
+ evdev_set_name(sc->sc_evdev, device_get_desc(dev));
+ evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
+ evdev_set_id(sc->sc_evdev, BUS_USB, uaa->info.idVendor,
+ uaa->info.idProduct, 0);
+ evdev_set_serial(sc->sc_evdev, usb_get_serial(uaa->device));
+ evdev_set_methods(sc->sc_evdev, sc, &ums_evdev_methods);
+ evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
+ evdev_support_event(sc->sc_evdev, EV_SYN);
+ evdev_support_event(sc->sc_evdev, EV_REL);
+ evdev_support_event(sc->sc_evdev, EV_KEY);
+
+ info = &sc->sc_info[0];
+
+ if (info->sc_flags & UMS_FLAG_X_AXIS)
+ evdev_support_rel(sc->sc_evdev, REL_X);
+
+ if (info->sc_flags & UMS_FLAG_Y_AXIS)
+ evdev_support_rel(sc->sc_evdev, REL_Y);
+
+ if (info->sc_flags & UMS_FLAG_Z_AXIS)
+ evdev_support_rel(sc->sc_evdev, REL_WHEEL);
+
+ if (info->sc_flags & UMS_FLAG_T_AXIS)
+ evdev_support_rel(sc->sc_evdev, REL_HWHEEL);
+
+ for (i = 0; i < info->sc_buttons; i++)
+ evdev_support_key(sc->sc_evdev, BTN_MOUSE + i);
+
+ err = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
+ if (err)
+ goto detach;
+#endif
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+ OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD,
+ sc, 0, ums_sysctl_handler_parseinfo,
+ "", "Dump of parsed HID report descriptor");
+
+ return (0);
+
+detach:
+ if (d_ptr) {
+ free(d_ptr, M_TEMP);
+ }
+ ums_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+ums_detach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb_fifo_detach(&sc->sc_fifo);
+
+#ifdef EVDEV_SUPPORT
+ evdev_free(sc->sc_evdev);
+#endif
+
+ usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
+
+ usb_callout_drain(&sc->sc_callout);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ums_reset(struct ums_softc *sc)
+{
+
+ /* reset all USB mouse parameters */
+
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+
+ sc->sc_hw.iftype = MOUSE_IF_USB;
+ sc->sc_hw.type = MOUSE_MOUSE;
+ sc->sc_hw.model = MOUSE_MODEL_GENERIC;
+ sc->sc_hw.hwid = 0;
+
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.rate = -1;
+ sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->sc_mode.accelfactor = 0;
+ sc->sc_mode.level = 0;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+
+ /* reset status */
+
+ sc->sc_status.flags = 0;
+ sc->sc_status.button = 0;
+ sc->sc_status.obutton = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+ /* sc->sc_status.dt = 0; */
+}
+
+static void
+ums_start_rx(struct ums_softc *sc)
+{
+ int rate;
+
+ /* Check if we should override the default polling interval */
+ rate = sc->sc_pollrate;
+ /* Range check rate */
+ if (rate > 1000)
+ rate = 1000;
+ /* Check for set rate */
+ if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) {
+ DPRINTF("Setting pollrate = %d\n", rate);
+ /* Stop current transfer, if any */
+ usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
+ /* Set new interval */
+ usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate);
+ /* Only set pollrate once */
+ sc->sc_pollrate = 0;
+ }
+
+ usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
+}
+
+static void
+ums_stop_rx(struct ums_softc *sc)
+{
+ usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
+ usb_callout_stop(&sc->sc_callout);
+}
+
+static void
+ums_fifo_start_read(struct usb_fifo *fifo)
+{
+ struct ums_softc *sc = usb_fifo_softc(fifo);
+
+ ums_start_rx(sc);
+}
+
+static void
+ums_fifo_stop_read(struct usb_fifo *fifo)
+{
+ struct ums_softc *sc = usb_fifo_softc(fifo);
+
+ ums_stop_rx(sc);
+}
+
+
+#if ((MOUSE_SYS_PACKETSIZE != 8) || \
+ (MOUSE_MSC_PACKETSIZE != 5))
+#error "Software assumptions are not met. Please update code."
+#endif
+
+static void
+ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
+ int32_t dz, int32_t dt, int32_t buttons)
+{
+ uint8_t buf[8];
+
+ if (1) {
+
+ if (dx > 254)
+ dx = 254;
+ if (dx < -256)
+ dx = -256;
+ if (dy > 254)
+ dy = 254;
+ if (dy < -256)
+ dy = -256;
+ if (dz > 126)
+ dz = 126;
+ if (dz < -128)
+ dz = -128;
+ if (dt > 126)
+ dt = 126;
+ if (dt < -128)
+ dt = -128;
+
+ buf[0] = sc->sc_mode.syncmask[1];
+ buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
+ buf[1] = dx >> 1;
+ buf[2] = dy >> 1;
+ buf[3] = dx - (dx >> 1);
+ buf[4] = dy - (dy >> 1);
+
+ if (sc->sc_mode.level == 1) {
+ buf[5] = dz >> 1;
+ buf[6] = dz - (dz >> 1);
+ buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
+ }
+ usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
+ sc->sc_mode.packetsize, 1);
+ } else {
+ DPRINTF("Buffer full, discarded packet\n");
+ }
+}
+
+#ifdef EVDEV_SUPPORT
+static void
+ums_evdev_push(struct ums_softc *sc, int32_t dx, int32_t dy,
+ int32_t dz, int32_t dt, int32_t buttons)
+{
+ if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) {
+ /* Push evdev event */
+ evdev_push_rel(sc->sc_evdev, REL_X, dx);
+ evdev_push_rel(sc->sc_evdev, REL_Y, -dy);
+ evdev_push_rel(sc->sc_evdev, REL_WHEEL, -dz);
+ evdev_push_rel(sc->sc_evdev, REL_HWHEEL, dt);
+ evdev_push_mouse_btn(sc->sc_evdev,
+ (buttons & ~MOUSE_STDBUTTONS) |
+ (buttons & (1 << 2) ? MOUSE_BUTTON1DOWN : 0) |
+ (buttons & (1 << 1) ? MOUSE_BUTTON2DOWN : 0) |
+ (buttons & (1 << 0) ? MOUSE_BUTTON3DOWN : 0));
+ evdev_sync(sc->sc_evdev);
+ }
+}
+#endif
+
+static void
+ums_reset_buf(struct ums_softc *sc)
+{
+ /* reset read queue, must be called locked */
+ usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
+}
+
+#ifdef EVDEV_SUPPORT
+static int
+ums_ev_open(struct evdev_dev *evdev, void *ev_softc)
+{
+ struct ums_softc *sc = (struct ums_softc *)ev_softc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ sc->sc_evflags = UMS_EVDEV_OPENED;
+
+ if (sc->sc_fflags == 0) {
+ ums_reset(sc);
+ ums_start_rx(sc);
+ }
+
+ return (0);
+}
+
+static void
+ums_ev_close(struct evdev_dev *evdev, void *ev_softc)
+{
+ struct ums_softc *sc = (struct ums_softc *)ev_softc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ sc->sc_evflags = 0;
+
+ if (sc->sc_fflags == 0)
+ ums_stop_rx(sc);
+}
+#endif
+
+static int
+ums_fifo_open(struct usb_fifo *fifo, int fflags)
+{
+ struct ums_softc *sc = usb_fifo_softc(fifo);
+
+ DPRINTFN(2, "\n");
+
+ /* check for duplicate open, should not happen */
+ if (sc->sc_fflags & fflags)
+ return (EBUSY);
+
+ /* check for first open */
+#ifdef EVDEV_SUPPORT
+ if (sc->sc_fflags == 0 && sc->sc_evflags == 0)
+ ums_reset(sc);
+#else
+ if (sc->sc_fflags == 0)
+ ums_reset(sc);
+#endif
+
+ if (fflags & FREAD) {
+ /* allocate RX buffer */
+ if (usb_fifo_alloc_buffer(fifo,
+ UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+
+ sc->sc_fflags |= fflags & (FREAD | FWRITE);
+ return (0);
+}
+
+static void
+ums_fifo_close(struct usb_fifo *fifo, int fflags)
+{
+ struct ums_softc *sc = usb_fifo_softc(fifo);
+
+ DPRINTFN(2, "\n");
+
+ if (fflags & FREAD)
+ usb_fifo_free_buffer(fifo);
+
+ sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+}
+
+static int
+ums_fifo_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
+{
+ struct ums_softc *sc = usb_fifo_softc(fifo);
+ mousemode_t mode;
+ int error = 0;
+
+ DPRINTFN(2, "\n");
+
+ mtx_lock(&sc->sc_mtx);
+
+ switch (cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->sc_hw;
+ break;
+
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->sc_mode;
+ break;
+
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1) {
+ /* don't change the current setting */
+ } else if ((mode.level < 0) || (mode.level > 1)) {
+ error = EINVAL;
+ break;
+ } else {
+ sc->sc_mode.level = mode.level;
+ }
+
+ /* store polling rate */
+ sc->sc_pollrate = mode.rate;
+
+ if (sc->sc_mode.level == 0) {
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ ums_reset_buf(sc);
+ break;
+
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->sc_mode.level;
+ break;
+
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr < 0 || *(int *)addr > 1) {
+ error = EINVAL;
+ break;
+ }
+ sc->sc_mode.level = *(int *)addr;
+
+ if (sc->sc_mode.level == 0) {
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ ums_reset_buf(sc);
+ break;
+
+ case MOUSE_GETSTATUS:{
+ mousestatus_t *status = (mousestatus_t *)addr;
+
+ *status = sc->sc_status;
+ sc->sc_status.obutton = sc->sc_status.button;
+ sc->sc_status.button = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+ /* sc->sc_status.dt = 0; */
+
+ if (status->dx || status->dy || status->dz /* || status->dt */ ) {
+ status->flags |= MOUSE_POSCHANGED;
+ }
+ if (status->button != status->obutton) {
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ }
+ break;
+ }
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ return (error);
+}
+
+static int
+ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS)
+{
+ struct ums_softc *sc = arg1;
+ struct ums_info *info;
+ struct sbuf *sb;
+ int i, j, err, had_output;
+
+ sb = sbuf_new_auto();
+ for (i = 0, had_output = 0; i < UMS_INFO_MAX; i++) {
+ info = &sc->sc_info[i];
+
+ /* Don't emit empty info */
+ if ((info->sc_flags &
+ (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS |
+ UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 &&
+ info->sc_buttons == 0)
+ continue;
+
+ if (had_output)
+ sbuf_printf(sb, "\n");
+ had_output = 1;
+ sbuf_printf(sb, "i%d:", i + 1);
+ if (info->sc_flags & UMS_FLAG_X_AXIS)
+ sbuf_printf(sb, " X:r%d, p%d, s%d;",
+ (int)info->sc_iid_x,
+ (int)info->sc_loc_x.pos,
+ (int)info->sc_loc_x.size);
+ if (info->sc_flags & UMS_FLAG_Y_AXIS)
+ sbuf_printf(sb, " Y:r%d, p%d, s%d;",
+ (int)info->sc_iid_y,
+ (int)info->sc_loc_y.pos,
+ (int)info->sc_loc_y.size);
+ if (info->sc_flags & UMS_FLAG_Z_AXIS)
+ sbuf_printf(sb, " Z:r%d, p%d, s%d;",
+ (int)info->sc_iid_z,
+ (int)info->sc_loc_z.pos,
+ (int)info->sc_loc_z.size);
+ if (info->sc_flags & UMS_FLAG_T_AXIS)
+ sbuf_printf(sb, " T:r%d, p%d, s%d;",
+ (int)info->sc_iid_t,
+ (int)info->sc_loc_t.pos,
+ (int)info->sc_loc_t.size);
+ if (info->sc_flags & UMS_FLAG_W_AXIS)
+ sbuf_printf(sb, " W:r%d, p%d, s%d;",
+ (int)info->sc_iid_w,
+ (int)info->sc_loc_w.pos,
+ (int)info->sc_loc_w.size);
+
+ for (j = 0; j < info->sc_buttons; j++) {
+ sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1,
+ (int)info->sc_iid_btn[j],
+ (int)info->sc_loc_btn[j].pos,
+ (int)info->sc_loc_btn[j].size);
+ }
+ }
+ sbuf_finish(sb);
+ err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
+ sbuf_delete(sb);
+
+ return (err);
+}
+
+static devclass_t ums_devclass;
+
+static device_method_t ums_methods[] = {
+ DEVMETHOD(device_probe, ums_probe),
+ DEVMETHOD(device_attach, ums_attach),
+ DEVMETHOD(device_detach, ums_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ums_driver = {
+ .name = "ums",
+ .methods = ums_methods,
+ .size = sizeof(struct ums_softc),
+};
+
+DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
+MODULE_DEPEND(ums, usb, 1, 1, 1);
+#ifdef EVDEV_SUPPORT
+MODULE_DEPEND(ums, evdev, 1, 1, 1);
+#endif
+MODULE_VERSION(ums, 1);
+USB_PNP_HOST_INFO(ums_devs);
diff --git a/freebsd/sys/dev/usb/input/usb_rdesc.h b/freebsd/sys/dev/usb/input/usb_rdesc.h
new file mode 100644
index 00000000..23ea620b
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/usb_rdesc.h
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 2000 Nick Hibma <n_hibma@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Copyright (c) 2005 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * This file contains replacements for broken HID report descriptors.
+ */
+
+#define UHID_GRAPHIRE_REPORT_DESCR(...) \
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x09, 0x33, /* USAGE (Touch) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */\
+ 0x09, 0x3c, /* USAGE (Invert) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x38, /* USAGE (Transducer Index) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x32, /* USAGE (In Range) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x30, /* USAGE (Tip Pressure) */\
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x03, /* REPORT_ID (3) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+
+#define UHID_GRAPHIRE3_4X5_REPORT_DESCR(...) \
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x02, /* USAGE (Mouse) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ 0x85, 0x01, /* REPORT_ID (1) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x05, 0x09, /* USAGE_PAGE (Button) */\
+ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */\
+ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x05, /* REPORT_SIZE (5) */\
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x09, 0x38, /* USAGE (Wheel) */\
+ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */\
+ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */\
+ 0x75, 0x08, /* REPORT_SIZE (8) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x06, /* INPUT (Data,Var,Rel) */\
+ 0xc0, /* END_COLLECTION */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x01, /* COLLECTION (Applicaption) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x09, 0x33, /* USAGE (Touch) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\
+ 0x09, 0x3c, /* USAGE (Invert) */\
+ 0x09, 0x38, /* USAGE (Transducer Index) */\
+ 0x09, 0x32, /* USAGE (In Range) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x30, /* USAGE (Tip Pressure) */\
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x03, /* REPORT_ID (3) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0xc0 /* END_COLLECTION */\
+
+/*
+ * The descriptor has no output report format, thus preventing you from
+ * controlling the LEDs and the built-in rumblers.
+ */
+#define UHID_XB360GP_REPORT_DESCR(...) \
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x05, /* USAGE (Gamepad) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ /* Unused */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* Byte count */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x3b, /* USAGE (Byte Count) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* D-Pad */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x90, /* USAGE (D-Pad Up) */\
+ 0x09, 0x91, /* USAGE (D-Pad Down) */\
+ 0x09, 0x93, /* USAGE (D-Pad Left) */\
+ 0x09, 0x92, /* USAGE (D-Pad Right) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ 0xc0, /* END COLLECTION */\
+ /* Buttons 5-11 */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x07, /* REPORT COUNT (7) */\
+ 0x05, 0x09, /* USAGE PAGE (Button) */\
+ 0x09, 0x08, /* USAGE (Button 8) */\
+ 0x09, 0x07, /* USAGE (Button 7) */\
+ 0x09, 0x09, /* USAGE (Button 9) */\
+ 0x09, 0x0a, /* USAGE (Button 10) */\
+ 0x09, 0x05, /* USAGE (Button 5) */\
+ 0x09, 0x06, /* USAGE (Button 6) */\
+ 0x09, 0x0b, /* USAGE (Button 11) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Unused */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* Buttons 1-4 */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x09, /* USAGE PAGE (Button) */\
+ 0x19, 0x01, /* USAGE MINIMUM (Button 1) */\
+ 0x29, 0x04, /* USAGE MAXIMUM (Button 4) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Triggers */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x46, 0xff, 0x00, /* PHYSICAL MAXIMUM (255) */\
+ 0x95, 0x02, /* REPORT SIZE (2) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x32, /* USAGE (Z) */\
+ 0x09, 0x35, /* USAGE (Rz) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Sticks */\
+ 0x75, 0x10, /* REPORT SIZE (16) */\
+ 0x16, 0x00, 0x80, /* LOGICAL MINIMUM (-32768) */\
+ 0x26, 0xff, 0x7f, /* LOGICAL MAXIMUM (32767) */\
+ 0x36, 0x00, 0x80, /* PHYSICAL MINIMUM (-32768) */\
+ 0x46, 0xff, 0x7f, /* PHYSICAL MAXIMUM (32767) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x09, 0x33, /* USAGE (Rx) */\
+ 0x09, 0x34, /* USAGE (Ry) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Unused */\
+ 0x75, 0x30, /* REPORT SIZE (48) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ 0xc0 /* END COLLECTION */\
+
diff --git a/freebsd/sys/dev/usb/input/wsp.c b/freebsd/sys/dev/usb/input/wsp.c
new file mode 100644
index 00000000..cff29723
--- /dev/null
+++ b/freebsd/sys/dev/usb/input/wsp.c
@@ -0,0 +1,1405 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2012 Huang Wen Hui
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/sysctl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR wsp_debug
+#include <dev/usb/usb_debug.h>
+
+#include <sys/mouse.h>
+
+#define WSP_DRIVER_NAME "wsp"
+#define WSP_BUFFER_MAX 1024
+
+#define WSP_CLAMP(x,low,high) do { \
+ if ((x) < (low)) \
+ (x) = (low); \
+ else if ((x) > (high)) \
+ (x) = (high); \
+} while (0)
+
+/* Tunables */
+static SYSCTL_NODE(_hw_usb, OID_AUTO, wsp, CTLFLAG_RW, 0, "USB wsp");
+
+#ifdef USB_DEBUG
+enum wsp_log_level {
+ WSP_LLEVEL_DISABLED = 0,
+ WSP_LLEVEL_ERROR,
+ WSP_LLEVEL_DEBUG, /* for troubleshooting */
+ WSP_LLEVEL_INFO, /* for diagnostics */
+};
+static int wsp_debug = WSP_LLEVEL_ERROR;/* the default is to only log errors */
+
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &wsp_debug, WSP_LLEVEL_ERROR, "WSP debug level");
+#endif /* USB_DEBUG */
+
+static struct wsp_tuning {
+ int scale_factor;
+ int z_factor;
+ int pressure_touch_threshold;
+ int pressure_untouch_threshold;
+ int pressure_tap_threshold;
+ int scr_hor_threshold;
+ int enable_single_tap_clicks;
+}
+ wsp_tuning =
+{
+ .scale_factor = 12,
+ .z_factor = 5,
+ .pressure_touch_threshold = 50,
+ .pressure_untouch_threshold = 10,
+ .pressure_tap_threshold = 120,
+ .scr_hor_threshold = 20,
+ .enable_single_tap_clicks = 1,
+};
+
+static void
+wsp_runing_rangecheck(struct wsp_tuning *ptun)
+{
+ WSP_CLAMP(ptun->scale_factor, 1, 63);
+ WSP_CLAMP(ptun->z_factor, 1, 63);
+ WSP_CLAMP(ptun->pressure_touch_threshold, 1, 255);
+ WSP_CLAMP(ptun->pressure_untouch_threshold, 1, 255);
+ WSP_CLAMP(ptun->pressure_tap_threshold, 1, 255);
+ WSP_CLAMP(ptun->scr_hor_threshold, 1, 255);
+ WSP_CLAMP(ptun->enable_single_tap_clicks, 0, 1);
+}
+
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, scale_factor, CTLFLAG_RWTUN,
+ &wsp_tuning.scale_factor, 0, "movement scale factor");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, z_factor, CTLFLAG_RWTUN,
+ &wsp_tuning.z_factor, 0, "Z-axis scale factor");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_touch_threshold, CTLFLAG_RWTUN,
+ &wsp_tuning.pressure_touch_threshold, 0, "touch pressure threshold");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_untouch_threshold, CTLFLAG_RWTUN,
+ &wsp_tuning.pressure_untouch_threshold, 0, "untouch pressure threshold");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, pressure_tap_threshold, CTLFLAG_RWTUN,
+ &wsp_tuning.pressure_tap_threshold, 0, "tap pressure threshold");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, scr_hor_threshold, CTLFLAG_RWTUN,
+ &wsp_tuning.scr_hor_threshold, 0, "horizontal scrolling threshold");
+SYSCTL_INT(_hw_usb_wsp, OID_AUTO, enable_single_tap_clicks, CTLFLAG_RWTUN,
+ &wsp_tuning.enable_single_tap_clicks, 0, "enable single tap clicks");
+
+/*
+ * Some tables, structures, definitions and constant values for the
+ * touchpad protocol has been copied from Linux's
+ * "drivers/input/mouse/bcm5974.c" which has the following copyright
+ * holders under GPLv2. All device specific code in this driver has
+ * been written from scratch. The decoding algorithm is based on
+ * output from FreeBSD's usbdump.
+ *
+ * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
+ * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ */
+
+/* button data structure */
+struct bt_data {
+ uint8_t unknown1; /* constant */
+ uint8_t button; /* left button */
+ uint8_t rel_x; /* relative x coordinate */
+ uint8_t rel_y; /* relative y coordinate */
+} __packed;
+
+/* trackpad header types */
+enum tp_type {
+ TYPE1, /* plain trackpad */
+ TYPE2, /* button integrated in trackpad */
+ TYPE3, /* additional header fields since June 2013 */
+ TYPE4 /* additional header field for pressure data */
+};
+
+/* trackpad finger data offsets, le16-aligned */
+#define FINGER_TYPE1 (13 * 2)
+#define FINGER_TYPE2 (15 * 2)
+#define FINGER_TYPE3 (19 * 2)
+#define FINGER_TYPE4 (23 * 2)
+
+/* trackpad button data offsets */
+#define BUTTON_TYPE2 15
+#define BUTTON_TYPE3 23
+#define BUTTON_TYPE4 31
+
+/* list of device capability bits */
+#define HAS_INTEGRATED_BUTTON 1
+
+/* trackpad finger data block size */
+#define FSIZE_TYPE1 (14 * 2)
+#define FSIZE_TYPE2 (14 * 2)
+#define FSIZE_TYPE3 (14 * 2)
+#define FSIZE_TYPE4 (15 * 2)
+
+/* trackpad finger header - little endian */
+struct tp_header {
+ uint8_t flag;
+ uint8_t sn0;
+ uint16_t wFixed0;
+ uint32_t dwSn1;
+ uint32_t dwFixed1;
+ uint16_t wLength;
+ uint8_t nfinger;
+ uint8_t ibt;
+ int16_t wUnknown[6];
+ uint8_t q1;
+ uint8_t q2;
+} __packed;
+
+/* trackpad finger structure - little endian */
+struct tp_finger {
+ int16_t origin; /* zero when switching track finger */
+ int16_t abs_x; /* absolute x coodinate */
+ int16_t abs_y; /* absolute y coodinate */
+ int16_t rel_x; /* relative x coodinate */
+ int16_t rel_y; /* relative y coodinate */
+ int16_t tool_major; /* tool area, major axis */
+ int16_t tool_minor; /* tool area, minor axis */
+ int16_t orientation; /* 16384 when point, else 15 bit angle */
+ int16_t touch_major; /* touch area, major axis */
+ int16_t touch_minor; /* touch area, minor axis */
+ int16_t unused[2]; /* zeros */
+ int16_t pressure; /* pressure on forcetouch touchpad */
+ int16_t multi; /* one finger: varies, more fingers:
+ * constant */
+} __packed;
+
+/* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS 16
+#define SIZEOF_FINGER sizeof(struct tp_finger)
+#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER)
+
+#if (WSP_BUFFER_MAX < ((MAX_FINGERS * FSIZE_TYPE4) + FINGER_TYPE4))
+#error "WSP_BUFFER_MAX is too small"
+#endif
+
+enum {
+ WSP_FLAG_WELLSPRING1,
+ WSP_FLAG_WELLSPRING2,
+ WSP_FLAG_WELLSPRING3,
+ WSP_FLAG_WELLSPRING4,
+ WSP_FLAG_WELLSPRING4A,
+ WSP_FLAG_WELLSPRING5,
+ WSP_FLAG_WELLSPRING6A,
+ WSP_FLAG_WELLSPRING6,
+ WSP_FLAG_WELLSPRING5A,
+ WSP_FLAG_WELLSPRING7,
+ WSP_FLAG_WELLSPRING7A,
+ WSP_FLAG_WELLSPRING8,
+ WSP_FLAG_WELLSPRING9,
+ WSP_FLAG_MAX,
+};
+
+/* device-specific configuration */
+struct wsp_dev_params {
+ uint8_t caps; /* device capability bitmask */
+ uint8_t tp_type; /* type of trackpad interface */
+ uint8_t tp_button; /* offset to button data */
+ uint8_t tp_offset; /* offset to trackpad finger data */
+ uint8_t tp_fsize; /* bytes in single finger block */
+ uint8_t tp_delta; /* offset from header to finger struct */
+ uint8_t iface_index;
+ uint8_t um_size; /* usb control message length */
+ uint8_t um_req_val; /* usb control message value */
+ uint8_t um_req_idx; /* usb control message index */
+ uint8_t um_switch_idx; /* usb control message mode switch index */
+ uint8_t um_switch_on; /* usb control message mode switch on */
+ uint8_t um_switch_off; /* usb control message mode switch off */
+};
+
+static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = {
+ [WSP_FLAG_WELLSPRING1] = {
+ .caps = 0,
+ .tp_type = TYPE1,
+ .tp_button = 0,
+ .tp_offset = FINGER_TYPE1,
+ .tp_fsize = FSIZE_TYPE1,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING2] = {
+ .caps = 0,
+ .tp_type = TYPE1,
+ .tp_button = 0,
+ .tp_offset = FINGER_TYPE1,
+ .tp_fsize = FSIZE_TYPE1,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING3] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING4] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING4A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING5] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING6] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING5A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING6A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING7] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING7A] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE2,
+ .tp_button = BUTTON_TYPE2,
+ .tp_offset = FINGER_TYPE2,
+ .tp_fsize = FSIZE_TYPE2,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING8] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE3,
+ .tp_button = BUTTON_TYPE3,
+ .tp_offset = FINGER_TYPE3,
+ .tp_fsize = FSIZE_TYPE3,
+ .tp_delta = 0,
+ .iface_index = 0,
+ .um_size = 8,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x00,
+ .um_switch_idx = 0,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x08,
+ },
+ [WSP_FLAG_WELLSPRING9] = {
+ .caps = HAS_INTEGRATED_BUTTON,
+ .tp_type = TYPE4,
+ .tp_button = BUTTON_TYPE4,
+ .tp_offset = FINGER_TYPE4,
+ .tp_fsize = FSIZE_TYPE4,
+ .tp_delta = 2,
+ .iface_index = 2,
+ .um_size = 2,
+ .um_req_val = 0x03,
+ .um_req_idx = 0x02,
+ .um_switch_idx = 1,
+ .um_switch_on = 0x01,
+ .um_switch_off = 0x00,
+ },
+};
+
+#define WSP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+
+static const STRUCT_USB_HOST_ID wsp_devs[] = {
+ /* MacbookAir1.1 */
+ WSP_DEV(APPLE, WELLSPRING_ANSI, WSP_FLAG_WELLSPRING1),
+ WSP_DEV(APPLE, WELLSPRING_ISO, WSP_FLAG_WELLSPRING1),
+ WSP_DEV(APPLE, WELLSPRING_JIS, WSP_FLAG_WELLSPRING1),
+
+ /* MacbookProPenryn, aka wellspring2 */
+ WSP_DEV(APPLE, WELLSPRING2_ANSI, WSP_FLAG_WELLSPRING2),
+ WSP_DEV(APPLE, WELLSPRING2_ISO, WSP_FLAG_WELLSPRING2),
+ WSP_DEV(APPLE, WELLSPRING2_JIS, WSP_FLAG_WELLSPRING2),
+
+ /* Macbook5,1 (unibody), aka wellspring3 */
+ WSP_DEV(APPLE, WELLSPRING3_ANSI, WSP_FLAG_WELLSPRING3),
+ WSP_DEV(APPLE, WELLSPRING3_ISO, WSP_FLAG_WELLSPRING3),
+ WSP_DEV(APPLE, WELLSPRING3_JIS, WSP_FLAG_WELLSPRING3),
+
+ /* MacbookAir3,2 (unibody), aka wellspring4 */
+ WSP_DEV(APPLE, WELLSPRING4_ANSI, WSP_FLAG_WELLSPRING4),
+ WSP_DEV(APPLE, WELLSPRING4_ISO, WSP_FLAG_WELLSPRING4),
+ WSP_DEV(APPLE, WELLSPRING4_JIS, WSP_FLAG_WELLSPRING4),
+
+ /* MacbookAir3,1 (unibody), aka wellspring4 */
+ WSP_DEV(APPLE, WELLSPRING4A_ANSI, WSP_FLAG_WELLSPRING4A),
+ WSP_DEV(APPLE, WELLSPRING4A_ISO, WSP_FLAG_WELLSPRING4A),
+ WSP_DEV(APPLE, WELLSPRING4A_JIS, WSP_FLAG_WELLSPRING4A),
+
+ /* Macbook8 (unibody, March 2011) */
+ WSP_DEV(APPLE, WELLSPRING5_ANSI, WSP_FLAG_WELLSPRING5),
+ WSP_DEV(APPLE, WELLSPRING5_ISO, WSP_FLAG_WELLSPRING5),
+ WSP_DEV(APPLE, WELLSPRING5_JIS, WSP_FLAG_WELLSPRING5),
+
+ /* MacbookAir4,1 (unibody, July 2011) */
+ WSP_DEV(APPLE, WELLSPRING6A_ANSI, WSP_FLAG_WELLSPRING6A),
+ WSP_DEV(APPLE, WELLSPRING6A_ISO, WSP_FLAG_WELLSPRING6A),
+ WSP_DEV(APPLE, WELLSPRING6A_JIS, WSP_FLAG_WELLSPRING6A),
+
+ /* MacbookAir4,2 (unibody, July 2011) */
+ WSP_DEV(APPLE, WELLSPRING6_ANSI, WSP_FLAG_WELLSPRING6),
+ WSP_DEV(APPLE, WELLSPRING6_ISO, WSP_FLAG_WELLSPRING6),
+ WSP_DEV(APPLE, WELLSPRING6_JIS, WSP_FLAG_WELLSPRING6),
+
+ /* Macbook8,2 (unibody) */
+ WSP_DEV(APPLE, WELLSPRING5A_ANSI, WSP_FLAG_WELLSPRING5A),
+ WSP_DEV(APPLE, WELLSPRING5A_ISO, WSP_FLAG_WELLSPRING5A),
+ WSP_DEV(APPLE, WELLSPRING5A_JIS, WSP_FLAG_WELLSPRING5A),
+
+ /* MacbookPro10,1 (unibody, June 2012) */
+ /* MacbookPro11,1-3 (unibody, June 2013) */
+ WSP_DEV(APPLE, WELLSPRING7_ANSI, WSP_FLAG_WELLSPRING7),
+ WSP_DEV(APPLE, WELLSPRING7_ISO, WSP_FLAG_WELLSPRING7),
+ WSP_DEV(APPLE, WELLSPRING7_JIS, WSP_FLAG_WELLSPRING7),
+
+ /* MacbookPro10,2 (unibody, October 2012) */
+ WSP_DEV(APPLE, WELLSPRING7A_ANSI, WSP_FLAG_WELLSPRING7A),
+ WSP_DEV(APPLE, WELLSPRING7A_ISO, WSP_FLAG_WELLSPRING7A),
+ WSP_DEV(APPLE, WELLSPRING7A_JIS, WSP_FLAG_WELLSPRING7A),
+
+ /* MacbookAir6,2 (unibody, June 2013) */
+ WSP_DEV(APPLE, WELLSPRING8_ANSI, WSP_FLAG_WELLSPRING8),
+ WSP_DEV(APPLE, WELLSPRING8_ISO, WSP_FLAG_WELLSPRING8),
+ WSP_DEV(APPLE, WELLSPRING8_JIS, WSP_FLAG_WELLSPRING8),
+
+ /* MacbookPro12,1 MacbookPro11,4 */
+ WSP_DEV(APPLE, WELLSPRING9_ANSI, WSP_FLAG_WELLSPRING9),
+ WSP_DEV(APPLE, WELLSPRING9_ISO, WSP_FLAG_WELLSPRING9),
+ WSP_DEV(APPLE, WELLSPRING9_JIS, WSP_FLAG_WELLSPRING9),
+};
+
+#define WSP_FIFO_BUF_SIZE 8 /* bytes */
+#define WSP_FIFO_QUEUE_MAXLEN 50 /* units */
+
+enum {
+ WSP_INTR_DT,
+ WSP_N_TRANSFER,
+};
+
+struct wsp_softc {
+ struct usb_device *sc_usb_device;
+ struct mtx sc_mutex; /* for synchronization */
+ struct usb_xfer *sc_xfer[WSP_N_TRANSFER];
+ struct usb_fifo_sc sc_fifo;
+
+ const struct wsp_dev_params *sc_params; /* device configuration */
+
+ mousehw_t sc_hw;
+ mousemode_t sc_mode;
+ u_int sc_pollrate;
+ mousestatus_t sc_status;
+ u_int sc_state;
+#define WSP_ENABLED 0x01
+
+ struct tp_finger *index[MAX_FINGERS]; /* finger index data */
+ int16_t pos_x[MAX_FINGERS]; /* position array */
+ int16_t pos_y[MAX_FINGERS]; /* position array */
+ u_int sc_touch; /* touch status */
+#define WSP_UNTOUCH 0x00
+#define WSP_FIRST_TOUCH 0x01
+#define WSP_SECOND_TOUCH 0x02
+#define WSP_TOUCHING 0x04
+ int16_t pre_pos_x; /* previous position array */
+ int16_t pre_pos_y; /* previous position array */
+ int dx_sum; /* x axis cumulative movement */
+ int dy_sum; /* y axis cumulative movement */
+ int dz_sum; /* z axis cumulative movement */
+ int dz_count;
+#define WSP_DZ_MAX_COUNT 32
+ int dt_sum; /* T-axis cumulative movement */
+ int rdx; /* x axis remainder of divide by scale_factor */
+ int rdy; /* y axis remainder of divide by scale_factor */
+ int rdz; /* z axis remainder of divide by scale_factor */
+ int tp_datalen;
+ uint8_t o_ntouch; /* old touch finger status */
+ uint8_t finger; /* 0 or 1 *, check which finger moving */
+ uint16_t intr_count;
+#define WSP_TAP_THRESHOLD 3
+#define WSP_TAP_MAX_COUNT 20
+ int distance; /* the distance of 2 fingers */
+#define MAX_DISTANCE 2500 /* the max allowed distance */
+ uint8_t ibtn; /* button status in tapping */
+ uint8_t ntaps; /* finger status in tapping */
+ uint8_t scr_mode; /* scroll status in movement */
+#define WSP_SCR_NONE 0
+#define WSP_SCR_VER 1
+#define WSP_SCR_HOR 2
+ uint8_t tp_data[WSP_BUFFER_MAX] __aligned(4); /* trackpad transferred data */
+};
+
+/*
+ * function prototypes
+ */
+static usb_fifo_cmd_t wsp_start_read;
+static usb_fifo_cmd_t wsp_stop_read;
+static usb_fifo_open_t wsp_open;
+static usb_fifo_close_t wsp_close;
+static usb_fifo_ioctl_t wsp_ioctl;
+
+static struct usb_fifo_methods wsp_fifo_methods = {
+ .f_open = &wsp_open,
+ .f_close = &wsp_close,
+ .f_ioctl = &wsp_ioctl,
+ .f_start_read = &wsp_start_read,
+ .f_stop_read = &wsp_stop_read,
+ .basename[0] = WSP_DRIVER_NAME,
+};
+
+/* device initialization and shutdown */
+static int wsp_enable(struct wsp_softc *sc);
+static void wsp_disable(struct wsp_softc *sc);
+
+/* updating fifo */
+static void wsp_reset_buf(struct wsp_softc *sc);
+static void wsp_add_to_queue(struct wsp_softc *, int, int, int, uint32_t);
+
+/* Device methods. */
+static device_probe_t wsp_probe;
+static device_attach_t wsp_attach;
+static device_detach_t wsp_detach;
+static usb_callback_t wsp_intr_callback;
+
+static const struct usb_config wsp_config[WSP_N_TRANSFER] = {
+ [WSP_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {
+ .pipe_bof = 0,
+ .short_xfer_ok = 1,
+ },
+ .bufsize = WSP_BUFFER_MAX,
+ .callback = &wsp_intr_callback,
+ },
+};
+
+static usb_error_t
+wsp_set_device_mode(struct wsp_softc *sc, uint8_t on)
+{
+ const struct wsp_dev_params *params = sc->sc_params;
+ uint8_t mode_bytes[8];
+ usb_error_t err;
+
+ /* Type 3 does not require a mode switch */
+ if (params->tp_type == TYPE3)
+ return 0;
+
+ err = usbd_req_get_report(sc->sc_usb_device, NULL,
+ mode_bytes, params->um_size, params->iface_index,
+ params->um_req_val, params->um_req_idx);
+
+ if (err != USB_ERR_NORMAL_COMPLETION) {
+ DPRINTF("Failed to read device mode (%d)\n", err);
+ return (err);
+ }
+
+ /*
+ * XXX Need to wait at least 250ms for hardware to get
+ * ready. The device mode handling appears to be handled
+ * asynchronously and we should not issue these commands too
+ * quickly.
+ */
+ pause("WHW", hz / 4);
+
+ mode_bytes[params->um_switch_idx] =
+ on ? params->um_switch_on : params->um_switch_off;
+
+ return (usbd_req_set_report(sc->sc_usb_device, NULL,
+ mode_bytes, params->um_size, params->iface_index,
+ params->um_req_val, params->um_req_idx));
+}
+
+static int
+wsp_enable(struct wsp_softc *sc)
+{
+ /* reset status */
+ memset(&sc->sc_status, 0, sizeof(sc->sc_status));
+ sc->sc_state |= WSP_ENABLED;
+
+ DPRINTFN(WSP_LLEVEL_INFO, "enabled wsp\n");
+ return (0);
+}
+
+static void
+wsp_disable(struct wsp_softc *sc)
+{
+ sc->sc_state &= ~WSP_ENABLED;
+ DPRINTFN(WSP_LLEVEL_INFO, "disabled wsp\n");
+}
+
+static int
+wsp_probe(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct usb_interface_descriptor *id;
+ struct usb_interface *iface;
+ uint8_t i;
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ /* figure out first interface matching */
+ for (i = 1;; i++) {
+ iface = usbd_get_iface(uaa->device, i);
+ if (iface == NULL || i == 3)
+ return (ENXIO);
+ id = iface->idesc;
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_HID) ||
+ (id->bInterfaceProtocol != 0 &&
+ id->bInterfaceProtocol != UIPROTO_MOUSE))
+ continue;
+ break;
+ }
+ /* check if we are attaching to the first match */
+ if (uaa->info.bIfaceIndex != i)
+ return (ENXIO);
+ return (usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa));
+}
+
+static int
+wsp_attach(device_t dev)
+{
+ struct wsp_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_error_t err;
+ void *d_ptr = NULL;
+ uint16_t d_len;
+
+ DPRINTFN(WSP_LLEVEL_INFO, "sc=%p\n", sc);
+
+ /* Get HID descriptor */
+ err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
+ &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (err == USB_ERR_NORMAL_COMPLETION) {
+ /* Get HID report descriptor length */
+ sc->tp_datalen = hid_report_size(d_ptr, d_len, hid_input, NULL);
+ free(d_ptr, M_TEMP);
+
+ if (sc->tp_datalen <= 0 || sc->tp_datalen > WSP_BUFFER_MAX) {
+ DPRINTF("Invalid datalength or too big "
+ "datalength: %d\n", sc->tp_datalen);
+ return (ENXIO);
+ }
+ } else {
+ return (ENXIO);
+ }
+
+ sc->sc_usb_device = uaa->device;
+
+ /* get device specific configuration */
+ sc->sc_params = wsp_dev_params + USB_GET_DRIVER_INFO(uaa);
+
+ /*
+ * By default the touchpad behaves like a HID device, sending
+ * packets with reportID = 8. Such reports contain only
+ * limited information. They encode movement deltas and button
+ * events, but do not include data from the pressure
+ * sensors. The device input mode can be switched from HID
+ * reports to raw sensor data using vendor-specific USB
+ * control commands:
+ */
+
+ /*
+ * During re-enumeration of the device we need to force the
+ * device back into HID mode before switching it to RAW
+ * mode. Else the device does not work like expected.
+ */
+ err = wsp_set_device_mode(sc, 0);
+ if (err != USB_ERR_NORMAL_COMPLETION) {
+ DPRINTF("Failed to set mode to HID MODE (%d)\n", err);
+ return (ENXIO);
+ }
+
+ err = wsp_set_device_mode(sc, 1);
+ if (err != USB_ERR_NORMAL_COMPLETION) {
+ DPRINTF("failed to set mode to RAW MODE (%d)\n", err);
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mutex, "wspmtx", NULL, MTX_DEF | MTX_RECURSE);
+
+ err = usbd_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, wsp_config,
+ WSP_N_TRANSFER, sc, &sc->sc_mutex);
+ if (err) {
+ DPRINTF("error=%s\n", usbd_errstr(err));
+ goto detach;
+ }
+ if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex,
+ &wsp_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), -1, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644)) {
+ goto detach;
+ }
+ device_set_usb_desc(dev);
+
+ sc->sc_hw.buttons = 3;
+ sc->sc_hw.iftype = MOUSE_IF_USB;
+ sc->sc_hw.type = MOUSE_PAD;
+ sc->sc_hw.model = MOUSE_MODEL_GENERIC;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.rate = -1;
+ sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+
+ sc->sc_touch = WSP_UNTOUCH;
+ sc->scr_mode = WSP_SCR_NONE;
+
+ return (0);
+
+detach:
+ wsp_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+wsp_detach(device_t dev)
+{
+ struct wsp_softc *sc = device_get_softc(dev);
+
+ (void) wsp_set_device_mode(sc, 0);
+
+ mtx_lock(&sc->sc_mutex);
+ if (sc->sc_state & WSP_ENABLED)
+ wsp_disable(sc);
+ mtx_unlock(&sc->sc_mutex);
+
+ usb_fifo_detach(&sc->sc_fifo);
+
+ usbd_transfer_unsetup(sc->sc_xfer, WSP_N_TRANSFER);
+
+ mtx_destroy(&sc->sc_mutex);
+
+ return (0);
+}
+
+static void
+wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct wsp_softc *sc = usbd_xfer_softc(xfer);
+ const struct wsp_dev_params *params = sc->sc_params;
+ struct usb_page_cache *pc;
+ struct tp_finger *f;
+ struct tp_header *h;
+ struct wsp_tuning tun = wsp_tuning;
+ int ntouch = 0; /* the finger number in touch */
+ int ibt = 0; /* button status */
+ int dx = 0;
+ int dy = 0;
+ int dz = 0;
+ int rdx = 0;
+ int rdy = 0;
+ int rdz = 0;
+ int len;
+ int i;
+
+ wsp_runing_rangecheck(&tun);
+
+ if (sc->dz_count == 0)
+ sc->dz_count = WSP_DZ_MAX_COUNT;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ /* copy out received data */
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, sc->tp_data, len);
+
+ if ((len < params->tp_offset + params->tp_fsize) ||
+ ((len - params->tp_offset) % params->tp_fsize) != 0) {
+ DPRINTFN(WSP_LLEVEL_INFO, "Invalid length: %d, %x, %x\n",
+ len, sc->tp_data[0], sc->tp_data[1]);
+ goto tr_setup;
+ }
+
+ if (len < sc->tp_datalen) {
+ /* make sure we don't process old data */
+ memset(sc->tp_data + len, 0, sc->tp_datalen - len);
+ }
+
+ h = (struct tp_header *)(sc->tp_data);
+
+ if (params->tp_type >= TYPE2) {
+ ibt = sc->tp_data[params->tp_button];
+ ntouch = sc->tp_data[params->tp_button - 1];
+ }
+ /* range check */
+ if (ntouch < 0)
+ ntouch = 0;
+ else if (ntouch > MAX_FINGERS)
+ ntouch = MAX_FINGERS;
+
+ for (i = 0; i != ntouch; i++) {
+ f = (struct tp_finger *)(sc->tp_data + params->tp_offset + params->tp_delta + i * params->tp_fsize);
+ /* swap endianness, if any */
+ if (le16toh(0x1234) != 0x1234) {
+ f->origin = le16toh((uint16_t)f->origin);
+ f->abs_x = le16toh((uint16_t)f->abs_x);
+ f->abs_y = le16toh((uint16_t)f->abs_y);
+ f->rel_x = le16toh((uint16_t)f->rel_x);
+ f->rel_y = le16toh((uint16_t)f->rel_y);
+ f->tool_major = le16toh((uint16_t)f->tool_major);
+ f->tool_minor = le16toh((uint16_t)f->tool_minor);
+ f->orientation = le16toh((uint16_t)f->orientation);
+ f->touch_major = le16toh((uint16_t)f->touch_major);
+ f->touch_minor = le16toh((uint16_t)f->touch_minor);
+ f->pressure = le16toh((uint16_t)f->pressure);
+ f->multi = le16toh((uint16_t)f->multi);
+ }
+ DPRINTFN(WSP_LLEVEL_INFO,
+ "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, "
+ "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, "
+ "tchmaj=%4d, tchmin=%4d, presure=%4d, m=%4x\n",
+ i, ibt, ntouch, f->origin, f->abs_x, f->abs_y,
+ f->rel_x, f->rel_y, f->tool_major, f->tool_minor, f->orientation,
+ f->touch_major, f->touch_minor, f->pressure, f->multi);
+ sc->pos_x[i] = f->abs_x;
+ sc->pos_y[i] = -f->abs_y;
+ sc->index[i] = f;
+ }
+
+ sc->sc_status.flags &= ~MOUSE_POSCHANGED;
+ sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
+ sc->sc_status.obutton = sc->sc_status.button;
+ sc->sc_status.button = 0;
+
+ if (ibt != 0) {
+ sc->sc_status.button |= MOUSE_BUTTON1DOWN;
+ sc->ibtn = 1;
+ }
+ sc->intr_count++;
+
+ if (sc->ntaps < ntouch) {
+ switch (ntouch) {
+ case 1:
+ if (sc->index[0]->touch_major > tun.pressure_tap_threshold &&
+ sc->index[0]->tool_major <= 1200)
+ sc->ntaps = 1;
+ break;
+ case 2:
+ if (sc->index[0]->touch_major > tun.pressure_tap_threshold-30 &&
+ sc->index[1]->touch_major > tun.pressure_tap_threshold-30)
+ sc->ntaps = 2;
+ break;
+ case 3:
+ if (sc->index[0]->touch_major > tun.pressure_tap_threshold-40 &&
+ sc->index[1]->touch_major > tun.pressure_tap_threshold-40 &&
+ sc->index[2]->touch_major > tun.pressure_tap_threshold-40)
+ sc->ntaps = 3;
+ break;
+ default:
+ break;
+ }
+ }
+ if (ntouch == 2) {
+ sc->distance = max(sc->distance, max(
+ abs(sc->pos_x[0] - sc->pos_x[1]),
+ abs(sc->pos_y[0] - sc->pos_y[1])));
+ }
+ if (sc->index[0]->touch_major < tun.pressure_untouch_threshold &&
+ sc->sc_status.button == 0) {
+ sc->sc_touch = WSP_UNTOUCH;
+ if (sc->intr_count < WSP_TAP_MAX_COUNT &&
+ sc->intr_count > WSP_TAP_THRESHOLD &&
+ sc->ntaps && sc->ibtn == 0) {
+ /*
+ * Add a pair of events (button-down and
+ * button-up).
+ */
+ switch (sc->ntaps) {
+ case 1:
+ if (!(params->caps & HAS_INTEGRATED_BUTTON) || tun.enable_single_tap_clicks) {
+ wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN);
+ DPRINTFN(WSP_LLEVEL_INFO, "LEFT CLICK!\n");
+ }
+ break;
+ case 2:
+ DPRINTFN(WSP_LLEVEL_INFO, "sum_x=%5d, sum_y=%5d\n",
+ sc->dx_sum, sc->dy_sum);
+ if (sc->distance < MAX_DISTANCE && abs(sc->dx_sum) < 5 &&
+ abs(sc->dy_sum) < 5) {
+ wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN);
+ DPRINTFN(WSP_LLEVEL_INFO, "RIGHT CLICK!\n");
+ }
+ break;
+ case 3:
+ wsp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN);
+ break;
+ default:
+ /* we don't handle taps of more than three fingers */
+ break;
+ }
+ wsp_add_to_queue(sc, 0, 0, 0, 0); /* button release */
+ }
+ if ((sc->dt_sum / tun.scr_hor_threshold) != 0 &&
+ sc->ntaps == 2 && sc->scr_mode == WSP_SCR_HOR) {
+
+ /*
+ * translate T-axis into button presses
+ * until further
+ */
+ if (sc->dt_sum > 0)
+ wsp_add_to_queue(sc, 0, 0, 0, 1UL << 3);
+ else if (sc->dt_sum < 0)
+ wsp_add_to_queue(sc, 0, 0, 0, 1UL << 4);
+ }
+ sc->dz_count = WSP_DZ_MAX_COUNT;
+ sc->dz_sum = 0;
+ sc->intr_count = 0;
+ sc->ibtn = 0;
+ sc->ntaps = 0;
+ sc->finger = 0;
+ sc->distance = 0;
+ sc->dt_sum = 0;
+ sc->dx_sum = 0;
+ sc->dy_sum = 0;
+ sc->rdx = 0;
+ sc->rdy = 0;
+ sc->rdz = 0;
+ sc->scr_mode = WSP_SCR_NONE;
+ } else if (sc->index[0]->touch_major >= tun.pressure_touch_threshold &&
+ sc->sc_touch == WSP_UNTOUCH) { /* ignore first touch */
+ sc->sc_touch = WSP_FIRST_TOUCH;
+ } else if (sc->index[0]->touch_major >= tun.pressure_touch_threshold &&
+ sc->sc_touch == WSP_FIRST_TOUCH) { /* ignore second touch */
+ sc->sc_touch = WSP_SECOND_TOUCH;
+ DPRINTFN(WSP_LLEVEL_INFO, "Fist pre_x=%5d, pre_y=%5d\n",
+ sc->pre_pos_x, sc->pre_pos_y);
+ } else {
+ if (sc->sc_touch == WSP_SECOND_TOUCH)
+ sc->sc_touch = WSP_TOUCHING;
+
+ if (ntouch != 0 &&
+ sc->index[0]->touch_major >= tun.pressure_touch_threshold) {
+ dx = sc->pos_x[0] - sc->pre_pos_x;
+ dy = sc->pos_y[0] - sc->pre_pos_y;
+
+ /* Ignore movement during button is releasing */
+ if (sc->ibtn != 0 && sc->sc_status.button == 0)
+ dx = dy = 0;
+
+ /* Ignore movement if ntouch changed */
+ if (sc->o_ntouch != ntouch)
+ dx = dy = 0;
+
+ /* Ignore unexpeted movement when typing */
+ if (ntouch == 1 && sc->index[0]->tool_major > 1200)
+ dx = dy = 0;
+
+ if (sc->ibtn != 0 && ntouch == 1 &&
+ sc->intr_count < WSP_TAP_MAX_COUNT &&
+ abs(sc->dx_sum) < 1 && abs(sc->dy_sum) < 1 )
+ dx = dy = 0;
+
+ if (ntouch == 2 && sc->sc_status.button != 0) {
+ dx = sc->pos_x[sc->finger] - sc->pre_pos_x;
+ dy = sc->pos_y[sc->finger] - sc->pre_pos_y;
+
+ /*
+ * Ignore movement of switch finger or
+ * movement from ibt=0 to ibt=1
+ */
+ if (sc->index[0]->origin == 0 || sc->index[1]->origin == 0 ||
+ sc->sc_status.obutton != sc->sc_status.button) {
+ dx = dy = 0;
+ sc->finger = 0;
+ }
+ if ((abs(sc->index[0]->rel_x) + abs(sc->index[0]->rel_y)) <
+ (abs(sc->index[1]->rel_x) + abs(sc->index[1]->rel_y)) &&
+ sc->finger == 0) {
+ sc->sc_touch = WSP_SECOND_TOUCH;
+ dx = dy = 0;
+ sc->finger = 1;
+ }
+ if ((abs(sc->index[0]->rel_x) + abs(sc->index[0]->rel_y)) >=
+ (abs(sc->index[1]->rel_x) + abs(sc->index[1]->rel_y)) &&
+ sc->finger == 1) {
+ sc->sc_touch = WSP_SECOND_TOUCH;
+ dx = dy = 0;
+ sc->finger = 0;
+ }
+ DPRINTFN(WSP_LLEVEL_INFO, "dx=%5d, dy=%5d, mov=%5d\n",
+ dx, dy, sc->finger);
+ }
+ if (sc->dz_count--) {
+ rdz = (dy + sc->rdz) % tun.scale_factor;
+ sc->dz_sum -= (dy + sc->rdz) / tun.scale_factor;
+ sc->rdz = rdz;
+ }
+ if ((sc->dz_sum / tun.z_factor) != 0)
+ sc->dz_count = 0;
+ }
+ rdx = (dx + sc->rdx) % tun.scale_factor;
+ dx = (dx + sc->rdx) / tun.scale_factor;
+ sc->rdx = rdx;
+
+ rdy = (dy + sc->rdy) % tun.scale_factor;
+ dy = (dy + sc->rdy) / tun.scale_factor;
+ sc->rdy = rdy;
+
+ sc->dx_sum += dx;
+ sc->dy_sum += dy;
+
+ if (ntouch == 2 && sc->sc_status.button == 0) {
+ if (sc->scr_mode == WSP_SCR_NONE &&
+ abs(sc->dx_sum) + abs(sc->dy_sum) > tun.scr_hor_threshold)
+ sc->scr_mode = abs(sc->dx_sum) >
+ abs(sc->dy_sum) * 2 ? WSP_SCR_HOR : WSP_SCR_VER;
+ DPRINTFN(WSP_LLEVEL_INFO, "scr_mode=%5d, count=%d, dx_sum=%d, dy_sum=%d\n",
+ sc->scr_mode, sc->intr_count, sc->dx_sum, sc->dy_sum);
+ if (sc->scr_mode == WSP_SCR_HOR)
+ sc->dt_sum += dx;
+ else
+ sc->dt_sum = 0;
+
+ dx = dy = 0;
+ if (sc->dz_count == 0)
+ dz = sc->dz_sum / tun.z_factor;
+ if (sc->scr_mode == WSP_SCR_HOR ||
+ abs(sc->pos_x[0] - sc->pos_x[1]) > MAX_DISTANCE ||
+ abs(sc->pos_y[0] - sc->pos_y[1]) > MAX_DISTANCE)
+ dz = 0;
+ }
+ if (ntouch == 3)
+ dx = dy = dz = 0;
+ if (sc->intr_count < WSP_TAP_MAX_COUNT &&
+ abs(dx) < 3 && abs(dy) < 3 && abs(dz) < 3)
+ dx = dy = dz = 0;
+ else
+ sc->intr_count = WSP_TAP_MAX_COUNT;
+ if (dx || dy || dz)
+ sc->sc_status.flags |= MOUSE_POSCHANGED;
+ DPRINTFN(WSP_LLEVEL_INFO, "dx=%5d, dy=%5d, dz=%5d, sc_touch=%x, btn=%x\n",
+ dx, dy, dz, sc->sc_touch, sc->sc_status.button);
+ sc->sc_status.dx += dx;
+ sc->sc_status.dy += dy;
+ sc->sc_status.dz += dz;
+
+ wsp_add_to_queue(sc, dx, -dy, dz, sc->sc_status.button);
+ if (sc->dz_count == 0) {
+ sc->dz_sum = 0;
+ sc->rdz = 0;
+ }
+
+ }
+ sc->pre_pos_x = sc->pos_x[0];
+ sc->pre_pos_y = sc->pos_y[0];
+
+ if (ntouch == 2 && sc->sc_status.button != 0) {
+ sc->pre_pos_x = sc->pos_x[sc->finger];
+ sc->pre_pos_y = sc->pos_y[sc->finger];
+ }
+ sc->o_ntouch = ntouch;
+
+ case USB_ST_SETUP:
+tr_setup:
+ /* check if we can put more data into the FIFO */
+ if (usb_fifo_put_bytes_max(
+ sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
+ usbd_xfer_set_frame_len(xfer, 0,
+ sc->tp_datalen);
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+wsp_add_to_queue(struct wsp_softc *sc, int dx, int dy, int dz,
+ uint32_t buttons_in)
+{
+ uint32_t buttons_out;
+ uint8_t buf[8];
+
+ dx = imin(dx, 254);
+ dx = imax(dx, -256);
+ dy = imin(dy, 254);
+ dy = imax(dy, -256);
+ dz = imin(dz, 126);
+ dz = imax(dz, -128);
+
+ buttons_out = MOUSE_MSC_BUTTONS;
+ if (buttons_in & MOUSE_BUTTON1DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON1UP;
+ else if (buttons_in & MOUSE_BUTTON2DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON2UP;
+ else if (buttons_in & MOUSE_BUTTON3DOWN)
+ buttons_out &= ~MOUSE_MSC_BUTTON3UP;
+
+ /* Encode the mouse data in standard format; refer to mouse(4) */
+ buf[0] = sc->sc_mode.syncmask[1];
+ buf[0] |= buttons_out;
+ buf[1] = dx >> 1;
+ buf[2] = dy >> 1;
+ buf[3] = dx - (dx >> 1);
+ buf[4] = dy - (dy >> 1);
+ /* Encode extra bytes for level 1 */
+ if (sc->sc_mode.level == 1) {
+ buf[5] = dz >> 1; /* dz / 2 */
+ buf[6] = dz - (dz >> 1);/* dz - (dz / 2) */
+ buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS);
+ }
+ usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
+ sc->sc_mode.packetsize, 1);
+}
+
+static void
+wsp_reset_buf(struct wsp_softc *sc)
+{
+ /* reset read queue */
+ usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
+}
+
+static void
+wsp_start_read(struct usb_fifo *fifo)
+{
+ struct wsp_softc *sc = usb_fifo_softc(fifo);
+ int rate;
+
+ /* Check if we should override the default polling interval */
+ rate = sc->sc_pollrate;
+ /* Range check rate */
+ if (rate > 1000)
+ rate = 1000;
+ /* Check for set rate */
+ if ((rate > 0) && (sc->sc_xfer[WSP_INTR_DT] != NULL)) {
+ /* Stop current transfer, if any */
+ usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]);
+ /* Set new interval */
+ usbd_xfer_set_interval(sc->sc_xfer[WSP_INTR_DT], 1000 / rate);
+ /* Only set pollrate once */
+ sc->sc_pollrate = 0;
+ }
+ usbd_transfer_start(sc->sc_xfer[WSP_INTR_DT]);
+}
+
+static void
+wsp_stop_read(struct usb_fifo *fifo)
+{
+ struct wsp_softc *sc = usb_fifo_softc(fifo);
+
+ usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]);
+}
+
+
+static int
+wsp_open(struct usb_fifo *fifo, int fflags)
+{
+ DPRINTFN(WSP_LLEVEL_INFO, "\n");
+
+ if (fflags & FREAD) {
+ struct wsp_softc *sc = usb_fifo_softc(fifo);
+ int rc;
+
+ if (sc->sc_state & WSP_ENABLED)
+ return (EBUSY);
+
+ if (usb_fifo_alloc_buffer(fifo,
+ WSP_FIFO_BUF_SIZE, WSP_FIFO_QUEUE_MAXLEN)) {
+ return (ENOMEM);
+ }
+ rc = wsp_enable(sc);
+ if (rc != 0) {
+ usb_fifo_free_buffer(fifo);
+ return (rc);
+ }
+ }
+ return (0);
+}
+
+static void
+wsp_close(struct usb_fifo *fifo, int fflags)
+{
+ if (fflags & FREAD) {
+ struct wsp_softc *sc = usb_fifo_softc(fifo);
+
+ wsp_disable(sc);
+ usb_fifo_free_buffer(fifo);
+ }
+}
+
+int
+wsp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
+{
+ struct wsp_softc *sc = usb_fifo_softc(fifo);
+ mousemode_t mode;
+ int error = 0;
+
+ mtx_lock(&sc->sc_mutex);
+
+ switch (cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->sc_hw;
+ break;
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->sc_mode;
+ break;
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1)
+ /* Don't change the current setting */
+ ;
+ else if ((mode.level < 0) || (mode.level > 1)) {
+ error = EINVAL;
+ goto done;
+ }
+ sc->sc_mode.level = mode.level;
+ sc->sc_pollrate = mode.rate;
+ sc->sc_hw.buttons = 3;
+
+ if (sc->sc_mode.level == 0) {
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ wsp_reset_buf(sc);
+ break;
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->sc_mode.level;
+ break;
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr < 0 || *(int *)addr > 1) {
+ error = EINVAL;
+ goto done;
+ }
+ sc->sc_mode.level = *(int *)addr;
+ sc->sc_hw.buttons = 3;
+
+ if (sc->sc_mode.level == 0) {
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ wsp_reset_buf(sc);
+ break;
+ case MOUSE_GETSTATUS:{
+ mousestatus_t *status = (mousestatus_t *)addr;
+
+ *status = sc->sc_status;
+ sc->sc_status.obutton = sc->sc_status.button;
+ sc->sc_status.button = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+
+ if (status->dx || status->dy || status->dz)
+ status->flags |= MOUSE_POSCHANGED;
+ if (status->button != status->obutton)
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ break;
+ }
+ default:
+ error = ENOTTY;
+ }
+
+done:
+ mtx_unlock(&sc->sc_mutex);
+ return (error);
+}
+
+static device_method_t wsp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, wsp_probe),
+ DEVMETHOD(device_attach, wsp_attach),
+ DEVMETHOD(device_detach, wsp_detach),
+ DEVMETHOD_END
+};
+
+static driver_t wsp_driver = {
+ .name = WSP_DRIVER_NAME,
+ .methods = wsp_methods,
+ .size = sizeof(struct wsp_softc)
+};
+
+static devclass_t wsp_devclass;
+
+DRIVER_MODULE(wsp, uhub, wsp_driver, wsp_devclass, NULL, 0);
+MODULE_DEPEND(wsp, usb, 1, 1, 1);
+MODULE_VERSION(wsp, 1);
+USB_PNP_HOST_INFO(wsp_devs);
diff --git a/freebsd/sys/sys/mouse.h b/freebsd/sys/sys/mouse.h
new file mode 100644
index 00000000..9fd1d6d8
--- /dev/null
+++ b/freebsd/sys/sys/mouse.h
@@ -0,0 +1,395 @@
+/*-
+ * Copyright (c) 1992, 1993 Erik Forsberg.
+ * Copyright (c) 1996, 1997 Kazutaka YOKOTA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_MOUSE_H_
+#define _SYS_MOUSE_H_
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+
+/* ioctls */
+#define MOUSE_GETSTATUS _IOR('M', 0, mousestatus_t)
+#define MOUSE_GETHWINFO _IOR('M', 1, mousehw_t)
+#define MOUSE_GETMODE _IOR('M', 2, mousemode_t)
+#define MOUSE_SETMODE _IOW('M', 3, mousemode_t)
+#define MOUSE_GETLEVEL _IOR('M', 4, int)
+#define MOUSE_SETLEVEL _IOW('M', 5, int)
+#define MOUSE_GETVARS _IOR('M', 6, mousevar_t)
+#define MOUSE_SETVARS _IOW('M', 7, mousevar_t)
+#define MOUSE_READSTATE _IOWR('M', 8, mousedata_t)
+#define MOUSE_READDATA _IOWR('M', 9, mousedata_t)
+
+#ifdef notyet
+#define MOUSE_SETRESOLUTION _IOW('M', 10, int)
+#define MOUSE_SETSCALING _IOW('M', 11, int)
+#define MOUSE_SETRATE _IOW('M', 12, int)
+#define MOUSE_GETHWID _IOR('M', 13, int)
+#endif
+
+#define MOUSE_SYN_GETHWINFO _IOR('M', 100, synapticshw_t)
+
+/* mouse status block */
+typedef struct mousestatus {
+ int flags; /* state change flags */
+ int button; /* button status */
+ int obutton; /* previous button status */
+ int dx; /* x movement */
+ int dy; /* y movement */
+ int dz; /* z movement */
+} mousestatus_t;
+
+/* button */
+#define MOUSE_BUTTON1DOWN 0x0001 /* left */
+#define MOUSE_BUTTON2DOWN 0x0002 /* middle */
+#define MOUSE_BUTTON3DOWN 0x0004 /* right */
+#define MOUSE_BUTTON4DOWN 0x0008
+#define MOUSE_BUTTON5DOWN 0x0010
+#define MOUSE_BUTTON6DOWN 0x0020
+#define MOUSE_BUTTON7DOWN 0x0040
+#define MOUSE_BUTTON8DOWN 0x0080
+#define MOUSE_MAXBUTTON 31
+#define MOUSE_STDBUTTONS 0x0007 /* buttons 1-3 */
+#define MOUSE_EXTBUTTONS 0x7ffffff8 /* the others (28 of them!) */
+#define MOUSE_BUTTONS (MOUSE_STDBUTTONS | MOUSE_EXTBUTTONS)
+
+/* flags */
+#define MOUSE_STDBUTTONSCHANGED MOUSE_STDBUTTONS
+#define MOUSE_EXTBUTTONSCHANGED MOUSE_EXTBUTTONS
+#define MOUSE_BUTTONSCHANGED MOUSE_BUTTONS
+#define MOUSE_POSCHANGED 0x80000000
+
+typedef struct mousehw {
+ int buttons; /* -1 if unknown */
+ int iftype; /* MOUSE_IF_XXX */
+ int type; /* mouse/track ball/pad... */
+ int model; /* I/F dependent model ID: MOUSE_MODEL_XXX */
+ int hwid; /* I/F dependent hardware ID
+ * for the PS/2 mouse, it will be PSM_XXX_ID
+ */
+} mousehw_t;
+
+typedef struct synapticshw {
+ int infoMajor;
+ int infoMinor;
+ int infoRot180;
+ int infoPortrait;
+ int infoSensor;
+ int infoHardware;
+ int infoNewAbs;
+ int capPen;
+ int infoSimplC;
+ int infoGeometry;
+ int capExtended;
+ int capSleep;
+ int capFourButtons;
+ int capMultiFinger;
+ int capPalmDetect;
+ int capPassthrough;
+ int capMiddle;
+ int capLowPower;
+ int capMultiFingerReport;
+ int capBallistics;
+ int nExtendedButtons;
+ int nExtendedQueries;
+ int capClickPad;
+ int capDeluxeLEDs;
+ int noAbsoluteFilter;
+ int capReportsV;
+ int capUniformClickPad;
+ int capReportsMin;
+ int capInterTouch;
+ int capReportsMax;
+ int capClearPad;
+ int capAdvancedGestures;
+ int multiFingerMode;
+ int capCoveredPad;
+ int verticalScroll;
+ int horizontalScroll;
+ int verticalWheel;
+ int capEWmode;
+ int minimumXCoord;
+ int minimumYCoord;
+ int maximumXCoord;
+ int maximumYCoord;
+ int infoXupmm;
+ int infoYupmm;
+} synapticshw_t;
+
+/* iftype */
+#define MOUSE_IF_UNKNOWN (-1)
+#define MOUSE_IF_SERIAL 0
+#define MOUSE_IF_BUS 1
+#define MOUSE_IF_INPORT 2
+#define MOUSE_IF_PS2 3
+#define MOUSE_IF_SYSMOUSE 4
+#define MOUSE_IF_USB 5
+
+/* type */
+#define MOUSE_UNKNOWN (-1) /* should be treated as a mouse */
+#define MOUSE_MOUSE 0
+#define MOUSE_TRACKBALL 1
+#define MOUSE_STICK 2
+#define MOUSE_PAD 3
+
+/* model */
+#define MOUSE_MODEL_UNKNOWN (-1)
+#define MOUSE_MODEL_GENERIC 0
+#define MOUSE_MODEL_GLIDEPOINT 1
+#define MOUSE_MODEL_NETSCROLL 2
+#define MOUSE_MODEL_NET 3
+#define MOUSE_MODEL_INTELLI 4
+#define MOUSE_MODEL_THINK 5
+#define MOUSE_MODEL_EASYSCROLL 6
+#define MOUSE_MODEL_MOUSEMANPLUS 7
+#define MOUSE_MODEL_KIDSPAD 8
+#define MOUSE_MODEL_VERSAPAD 9
+#define MOUSE_MODEL_EXPLORER 10
+#define MOUSE_MODEL_4D 11
+#define MOUSE_MODEL_4DPLUS 12
+#define MOUSE_MODEL_SYNAPTICS 13
+#define MOUSE_MODEL_TRACKPOINT 14
+#define MOUSE_MODEL_ELANTECH 15
+
+typedef struct mousemode {
+ int protocol; /* MOUSE_PROTO_XXX */
+ int rate; /* report rate (per sec), -1 if unknown */
+ int resolution; /* MOUSE_RES_XXX, -1 if unknown */
+ int accelfactor; /* accelation factor (must be 1 or greater) */
+ int level; /* driver operation level */
+ int packetsize; /* the length of the data packet */
+ unsigned char syncmask[2]; /* sync. data bits in the header byte */
+} mousemode_t;
+
+/* protocol */
+/*
+ * Serial protocols:
+ * Microsoft, MouseSystems, Logitech, MM series, MouseMan, Hitachi Tablet,
+ * GlidePoint, IntelliMouse, Thinking Mouse, MouseRemote, Kidspad,
+ * VersaPad
+ * Bus mouse protocols:
+ * bus, InPort
+ * PS/2 mouse protocol:
+ * PS/2
+ */
+#define MOUSE_PROTO_UNKNOWN (-1)
+#define MOUSE_PROTO_MS 0 /* Microsoft Serial, 3 bytes */
+#define MOUSE_PROTO_MSC 1 /* Mouse Systems, 5 bytes */
+#define MOUSE_PROTO_LOGI 2 /* Logitech, 3 bytes */
+#define MOUSE_PROTO_MM 3 /* MM series, 3 bytes */
+#define MOUSE_PROTO_LOGIMOUSEMAN 4 /* Logitech MouseMan 3/4 bytes */
+#define MOUSE_PROTO_BUS 5 /* MS/Logitech bus mouse */
+#define MOUSE_PROTO_INPORT 6 /* MS/ATI InPort mouse */
+#define MOUSE_PROTO_PS2 7 /* PS/2 mouse, 3 bytes */
+#define MOUSE_PROTO_HITTAB 8 /* Hitachi Tablet 3 bytes */
+#define MOUSE_PROTO_GLIDEPOINT 9 /* ALPS GlidePoint, 3/4 bytes */
+#define MOUSE_PROTO_INTELLI 10 /* MS IntelliMouse, 4 bytes */
+#define MOUSE_PROTO_THINK 11 /* Kensington Thinking Mouse, 3/4 bytes */
+#define MOUSE_PROTO_SYSMOUSE 12 /* /dev/sysmouse */
+#define MOUSE_PROTO_X10MOUSEREM 13 /* X10 MouseRemote, 3 bytes */
+#define MOUSE_PROTO_KIDSPAD 14 /* Genius Kidspad */
+#define MOUSE_PROTO_VERSAPAD 15 /* Interlink VersaPad, 6 bytes */
+#define MOUSE_PROTO_JOGDIAL 16 /* Vaio's JogDial */
+#define MOUSE_PROTO_GTCO_DIGIPAD 17
+
+#define MOUSE_RES_UNKNOWN (-1)
+#define MOUSE_RES_DEFAULT 0
+#define MOUSE_RES_LOW (-2)
+#define MOUSE_RES_MEDIUMLOW (-3)
+#define MOUSE_RES_MEDIUMHIGH (-4)
+#define MOUSE_RES_HIGH (-5)
+
+typedef struct mousedata {
+ int len; /* # of data in the buffer */
+ int buf[16]; /* data buffer */
+} mousedata_t;
+
+#if (defined(MOUSE_GETVARS))
+
+typedef struct mousevar {
+ int var[16];
+} mousevar_t;
+
+/* magic numbers in var[0] */
+#define MOUSE_VARS_PS2_SIG 0x00325350 /* 'PS2' */
+#define MOUSE_VARS_BUS_SIG 0x00535542 /* 'BUS' */
+#define MOUSE_VARS_INPORT_SIG 0x00504e49 /* 'INP' */
+
+#endif /* MOUSE_GETVARS */
+
+/* Synaptics Touchpad */
+#define MOUSE_SYNAPTICS_PACKETSIZE 6 /* '3' works better */
+
+/* Elantech Touchpad */
+#define MOUSE_ELANTECH_PACKETSIZE 6
+
+/* Microsoft Serial mouse data packet */
+#define MOUSE_MSS_PACKETSIZE 3
+#define MOUSE_MSS_SYNCMASK 0x40
+#define MOUSE_MSS_SYNC 0x40
+#define MOUSE_MSS_BUTTONS 0x30
+#define MOUSE_MSS_BUTTON1DOWN 0x20 /* left */
+#define MOUSE_MSS_BUTTON2DOWN 0x00 /* no middle button */
+#define MOUSE_MSS_BUTTON3DOWN 0x10 /* right */
+
+/* Logitech MouseMan data packet (M+ protocol) */
+#define MOUSE_LMAN_BUTTON2DOWN 0x20 /* middle button, the 4th byte */
+
+/* ALPS GlidePoint extension (variant of M+ protocol) */
+#define MOUSE_ALPS_BUTTON2DOWN 0x20 /* middle button, the 4th byte */
+#define MOUSE_ALPS_TAP 0x10 /* `tapping' action, the 4th byte */
+
+/* Kinsington Thinking Mouse extension (variant of M+ protocol) */
+#define MOUSE_THINK_BUTTON2DOWN 0x20 /* lower-left button, the 4th byte */
+#define MOUSE_THINK_BUTTON4DOWN 0x10 /* lower-right button, the 4th byte */
+
+/* MS IntelliMouse (variant of MS Serial) */
+#define MOUSE_INTELLI_PACKETSIZE 4
+#define MOUSE_INTELLI_BUTTON2DOWN 0x10 /* middle button in the 4th byte */
+
+/* Mouse Systems Corp. mouse data packet */
+#define MOUSE_MSC_PACKETSIZE 5
+#define MOUSE_MSC_SYNCMASK 0xf8
+#define MOUSE_MSC_SYNC 0x80
+#define MOUSE_MSC_BUTTONS 0x07
+#define MOUSE_MSC_BUTTON1UP 0x04 /* left */
+#define MOUSE_MSC_BUTTON2UP 0x02 /* middle */
+#define MOUSE_MSC_BUTTON3UP 0x01 /* right */
+#define MOUSE_MSC_MAXBUTTON 3
+
+/* MM series mouse data packet */
+#define MOUSE_MM_PACKETSIZE 3
+#define MOUSE_MM_SYNCMASK 0xe0
+#define MOUSE_MM_SYNC 0x80
+#define MOUSE_MM_BUTTONS 0x07
+#define MOUSE_MM_BUTTON1DOWN 0x04 /* left */
+#define MOUSE_MM_BUTTON2DOWN 0x02 /* middle */
+#define MOUSE_MM_BUTTON3DOWN 0x01 /* right */
+#define MOUSE_MM_XPOSITIVE 0x10
+#define MOUSE_MM_YPOSITIVE 0x08
+
+/* PS/2 mouse data packet */
+#define MOUSE_PS2_PACKETSIZE 3
+#define MOUSE_PS2_SYNCMASK 0xc8
+#define MOUSE_PS2_SYNC 0x08
+#define MOUSE_PS2_BUTTONS 0x07 /* 0x03 for 2 button mouse */
+#define MOUSE_PS2_BUTTON1DOWN 0x01 /* left */
+#define MOUSE_PS2_BUTTON2DOWN 0x04 /* middle */
+#define MOUSE_PS2_BUTTON3DOWN 0x02 /* right */
+#define MOUSE_PS2_TAP MOUSE_PS2_SYNC /* GlidePoint (PS/2) `tapping'
+ * Yes! this is the same bit
+ * as SYNC!
+ */
+
+#define MOUSE_PS2_XNEG 0x10
+#define MOUSE_PS2_YNEG 0x20
+#define MOUSE_PS2_XOVERFLOW 0x40
+#define MOUSE_PS2_YOVERFLOW 0x80
+
+/* Logitech MouseMan+ (PS/2) data packet (PS/2++ protocol) */
+#define MOUSE_PS2PLUS_SYNCMASK 0x48
+#define MOUSE_PS2PLUS_SYNC 0x48
+#define MOUSE_PS2PLUS_ZNEG 0x08 /* sign bit */
+#define MOUSE_PS2PLUS_BUTTON4DOWN 0x10 /* 4th button on MouseMan+ */
+#define MOUSE_PS2PLUS_BUTTON5DOWN 0x20
+
+/* IBM ScrollPoint (PS/2) also uses PS/2++ protocol */
+#define MOUSE_SPOINT_ZNEG 0x80 /* sign bits */
+#define MOUSE_SPOINT_WNEG 0x08
+
+/* MS IntelliMouse (PS/2) data packet */
+#define MOUSE_PS2INTELLI_PACKETSIZE 4
+/* some compatible mice have additional buttons */
+#define MOUSE_PS2INTELLI_BUTTON4DOWN 0x40
+#define MOUSE_PS2INTELLI_BUTTON5DOWN 0x80
+
+/* MS IntelliMouse Explorer (PS/2) data packet (variation of IntelliMouse) */
+#define MOUSE_EXPLORER_ZNEG 0x08 /* sign bit */
+/* IntelliMouse Explorer has additional button data in the fourth byte */
+#define MOUSE_EXPLORER_BUTTON4DOWN 0x10
+#define MOUSE_EXPLORER_BUTTON5DOWN 0x20
+
+/* Interlink VersaPad (serial I/F) data packet */
+#define MOUSE_VERSA_PACKETSIZE 6
+#define MOUSE_VERSA_IN_USE 0x04
+#define MOUSE_VERSA_SYNCMASK 0xc3
+#define MOUSE_VERSA_SYNC 0xc0
+#define MOUSE_VERSA_BUTTONS 0x30
+#define MOUSE_VERSA_BUTTON1DOWN 0x20 /* left */
+#define MOUSE_VERSA_BUTTON2DOWN 0x00 /* middle */
+#define MOUSE_VERSA_BUTTON3DOWN 0x10 /* right */
+#define MOUSE_VERSA_TAP 0x08
+
+/* Interlink VersaPad (PS/2 I/F) data packet */
+#define MOUSE_PS2VERSA_PACKETSIZE 6
+#define MOUSE_PS2VERSA_IN_USE 0x10
+#define MOUSE_PS2VERSA_SYNCMASK 0xe8
+#define MOUSE_PS2VERSA_SYNC 0xc8
+#define MOUSE_PS2VERSA_BUTTONS 0x05
+#define MOUSE_PS2VERSA_BUTTON1DOWN 0x04 /* left */
+#define MOUSE_PS2VERSA_BUTTON2DOWN 0x00 /* middle */
+#define MOUSE_PS2VERSA_BUTTON3DOWN 0x01 /* right */
+#define MOUSE_PS2VERSA_TAP 0x02
+
+/* A4 Tech 4D Mouse (PS/2) data packet */
+#define MOUSE_4D_PACKETSIZE 3
+#define MOUSE_4D_WHEELBITS 0xf0
+
+/* A4 Tech 4D+ Mouse (PS/2) data packet */
+#define MOUSE_4DPLUS_PACKETSIZE 3
+#define MOUSE_4DPLUS_ZNEG 0x04 /* sign bit */
+#define MOUSE_4DPLUS_BUTTON4DOWN 0x08
+
+/* sysmouse extended data packet */
+/*
+ * /dev/sysmouse sends data in two formats, depending on the protocol
+ * level. At the level 0, format is exactly the same as MousSystems'
+ * five byte packet. At the level 1, the first five bytes are the same
+ * as at the level 0. There are additional three bytes which shows
+ * `dz' and the states of additional buttons. `dz' is expressed as the
+ * sum of the byte 5 and 6 which contain signed seven bit values.
+ * The states of the button 4 though 10 are in the bit 0 though 6 in
+ * the byte 7 respectively: 1 indicates the button is up.
+ */
+#define MOUSE_SYS_PACKETSIZE 8
+#define MOUSE_SYS_SYNCMASK 0xf8
+#define MOUSE_SYS_SYNC 0x80
+#define MOUSE_SYS_BUTTON1UP 0x04 /* left, 1st byte */
+#define MOUSE_SYS_BUTTON2UP 0x02 /* middle, 1st byte */
+#define MOUSE_SYS_BUTTON3UP 0x01 /* right, 1st byte */
+#define MOUSE_SYS_BUTTON4UP 0x0001 /* 7th byte */
+#define MOUSE_SYS_BUTTON5UP 0x0002
+#define MOUSE_SYS_BUTTON6UP 0x0004
+#define MOUSE_SYS_BUTTON7UP 0x0008
+#define MOUSE_SYS_BUTTON8UP 0x0010
+#define MOUSE_SYS_BUTTON9UP 0x0020
+#define MOUSE_SYS_BUTTON10UP 0x0040
+#define MOUSE_SYS_MAXBUTTON 10
+#define MOUSE_SYS_STDBUTTONS 0x07
+#define MOUSE_SYS_EXTBUTTONS 0x7f /* the others */
+
+/* Mouse remote socket */
+#define _PATH_MOUSEREMOTE "/var/run/MouseRemote"
+
+#endif /* _SYS_MOUSE_H_ */