#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <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/hid/hid.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbhid.h>
#include "usbdevs.h"
#define USB_DEBUG_VAR atp_debug
#include <dev/usb/usb_debug.h>
#include <sys/mouse.h>
#define ATP_DRIVER_NAME "atp"
#ifndef ATP_SCALE_FACTOR
#define ATP_SCALE_FACTOR 16
#endif
#ifndef ATP_SMALL_MOVEMENT_THRESHOLD
#define ATP_SMALL_MOVEMENT_THRESHOLD 30
#endif
#ifndef ATP_FAST_MOVEMENT_TRESHOLD
#define ATP_FAST_MOVEMENT_TRESHOLD 150
#endif
#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
#ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD
#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000
#endif
#define ATP_ZOMBIE_STROKE_REAP_INTERVAL (hz / 20)
#define FG_SCALE_FACTOR 380
#define FG_MAX_DELTA_MICKEYS ((3 * (FG_SCALE_FACTOR)) >> 1)
#ifndef WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ
#define WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 1000000
#endif
#define FG_PSPAN_MIN_CUM_PRESSURE 10
#define FG_PSPAN_MAX_WIDTH 4
static SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"USB ATP");
#ifdef USB_DEBUG
enum atp_log_level {
ATP_LLEVEL_DISABLED = 0,
ATP_LLEVEL_ERROR,
ATP_LLEVEL_DEBUG,
ATP_LLEVEL_INFO,
};
static int atp_debug = ATP_LLEVEL_ERROR;
SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RWTUN,
&atp_debug, ATP_LLEVEL_ERROR, "ATP debug level");
#endif
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 | CTLFLAG_MPSAFE,
&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");
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");
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
} trackpad_family_t;
enum fountain_geyser_product {
FOUNTAIN,
GEYSER1,
GEYSER1_17inch,
GEYSER2,
GEYSER3,
GEYSER4,
FOUNTAIN_GEYSER_PRODUCT_MAX
};
enum wellspring_product {
WELLSPRING1,
WELLSPRING2,
WELLSPRING3,
WELLSPRING4,
WELLSPRING4A,
WELLSPRING5,
WELLSPRING6A,
WELLSPRING6,
WELLSPRING5A,
WELLSPRING7,
WELLSPRING7A,
WELLSPRING8,
WELLSPRING_PRODUCT_MAX
};
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,
WSP_TRACKPAD_TYPE2,
WSP_TRACKPAD_TYPE3
};
#define N_PROD_BITS 8
#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)
typedef struct fg_pspan {
u_int width;
u_int cum;
u_int cog;
u_int loc;
boolean_t matched;
} fg_pspan;
#define FG_MAX_PSPANS_PER_AXIS 3
#define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS)
#define WELLSPRING_INTERFACE_INDEX 1
#define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2)
#define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2)
#define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2)
#define WSP_TYPE2_BUTTON_DATA_OFFSET 15
#define WSP_TYPE3_BUTTON_DATA_OFFSET 23
#define HAS_INTEGRATED_BUTTON 1
struct wsp_finger_sensor_data {
int16_t origin;
int16_t abs_x;
int16_t abs_y;
int16_t rel_x;
int16_t rel_y;
int16_t tool_major;
int16_t tool_minor;
int16_t orientation;
int16_t touch_major;
int16_t touch_minor;
int16_t unused[3];
int16_t multi;
} __packed;
typedef struct wsp_finger {
boolean_t matched;
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))
#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
struct fg_dev_params {
u_int data_len;
u_int n_xsensors;
u_int n_ysensors;
enum fountain_geyser_trackpad_type prot;
};
struct wsp_dev_params {
uint8_t caps;
uint8_t tp_type;
uint8_t finger_data_offset;
};
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[] = {
{ 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)) },
{ 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)) },
{ 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)) },
{ 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)) },
{ 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)) },
{ 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) }
static const struct usb_device_id wsp_devs[] = {
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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
#define ATP_FIFO_QUEUE_MAXLEN 50
enum {
ATP_INTR_DT,
ATP_RESET,
ATP_N_TRANSFER,
};
typedef struct fg_stroke_component {
u_int loc;
u_int cum_pressure;
u_int max_cum_pressure;
boolean_t matched;
int delta_mickeys;
} fg_stroke_component_t;
typedef struct atp_stroke {
TAILQ_ENTRY(atp_stroke) entry;
atp_stroke_type type;
uint32_t flags;
#define ATSF_ZOMBIE 0x1
boolean_t matched;
struct timeval ctime;
uint32_t age;
int x;
int y;
int instantaneous_dx;
int instantaneous_dy;
int pending_dx;
int pending_dy;
int movement_dx;
int movement_dy;
int cum_movement_x;
int cum_movement_y;
fg_stroke_component_t components[NUM_AXES];
} atp_stroke_t;
struct atp_softc;
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;
struct usb_fifo_sc sc_fifo;
#define MODE_LENGTH 8
char sc_mode_bytes[MODE_LENGTH];
trackpad_family_t sc_family;
const void *sc_params;
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;
uint8_t sc_ibtn;
struct timeval sc_touch_reap_time;
u_int sc_idlecount;
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];
int sc_cur_y[FG_MAX_YSENSORS];
int sc_base_x[FG_MAX_XSENSORS];
int sc_base_y[FG_MAX_YSENSORS];
int sc_pressure_x[FG_MAX_XSENSORS];
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];
};
enum geyser34_status_bits {
FG_STATUS_BUTTON = (uint8_t)0x01,
FG_STATUS_BASE_UPDATE = (uint8_t)0x04,
};
typedef enum interface_mode {
RAW_SENSOR_MODE = (uint8_t)0x01,
HID_MODE = (uint8_t)0x08
} interface_mode;
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,
};
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 *);
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);
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 *);
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 *);
static void atp_reset_buf(struct atp_softc *);
static void atp_add_to_queue(struct atp_softc *, int, int, int, uint32_t);
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,
.short_xfer_ok = 1,
},
.bufsize = ATP_SENSOR_DATA_BUF_MAX,
.callback = &atp_intr,
},
[ATP_RESET] = {
.type = UE_CONTROL,
.endpoint = 0,
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request) + MODE_LENGTH,
.callback = &atp_reset_callback,
.interval = 0,
},
};
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 ,
sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 ,
0x03 , 0x00 );
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);
pause("WHW", hz / 4);
sc->sc_mode_bytes[0] = mode_value;
return (usbd_req_set_report(sc->sc_usb_device, NULL ,
sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 ,
0x03 , 0x00 ));
}
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 , (uint8_t)0x00 );
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);
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);
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;
}
}
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);
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;
if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) {
sc->sc_idlecount++;
if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
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;
}
}
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;
switch (prot) {
case FG_TRACKPAD_TYPE_GEYSER1:
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; ) {
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; ) {
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;
if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD)
p[i] = 0;
else
p[i] -= FG_SENSOR_NOISE_THRESHOLD;
}
}
static void
fg_detect_pspans(int *p, u_int num_sensors,
u_int max_spans,
fg_pspan *spans,
u_int *nspans_p)
{
u_int i;
int maxp;
u_int num_spans = 0;
enum fg_pspan_state {
ATP_PSPAN_INACTIVE,
ATP_PSPAN_INCREASING,
ATP_PSPAN_DECREASING,
} state;
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) {
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]) {
num_spans++;
state = ATP_PSPAN_INACTIVE;
maxp = 0;
i--;
continue;
}
break;
}
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++;
for (i = 0; i < num_spans; i++) {
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;
}
spans[i].loc = spans[i].cog * FG_SCALE_FACTOR /
spans[i].cum;
spans[i].matched = false;
}
*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;
if ((data_len < params->finger_data_offset) ||
((data_len - params->finger_data_offset) %
WSP_SIZEOF_FINGER_SENSOR_DATA) != 0)
return;
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;
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++) {
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);
}
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;
}
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);
component->loc = pspan->loc;
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;
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;
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;
for (i = 0; i < n_pspans; i++) {
if (pspans[i].matched)
continue;
if (fg_match_stroke_component(
&strokep->components[axis], &pspans[i],
strokep->type)) {
strokep->components[axis].matched = true;
if ((repeat_count > 0) && (i == repeat_index))
repeat_count--;
else
pspans[i].matched = true;
break;
}
}
}
}
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;
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;
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);
}
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;
TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
strokep->components[X].matched = false;
strokep->components[Y].matched = false;
}
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));
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 {
atp_terminate_stroke(sc, strokep);
}
}
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
if ((n_xpspans == 1) && (n_ypspans == 1))
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
return (movement);
}
static boolean_t
wsp_update_strokes(struct atp_softc *sc, wsp_finger_t fingers[WSP_MAX_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);
TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) {
if (strokep->matched)
continue;
atp_terminate_stroke(sc, strokep);
}
}
for (i = 0; i != n_fingers; i++) {
if (fingers[i].matched)
continue;
wsp_add_stroke(sc, fingers + i);
}
return (movement);
}
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;
strokep->type = ATP_STROKE_TOUCH;
strokep->matched = false;
microtime(&strokep->ctime);
strokep->age = 1;
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) {
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;
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]) {
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;
u_int repeat_count;
u_int repeat_index = 0;
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;
}
}
i = 0, j = 0;
for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
fg_add_stroke(sc, &spans[X][i], &spans[Y][j]);
if (repeat_count > 0) {
if ((repeat_axis == X) &&
(repeat_index == i)) {
i--;
repeat_count--;
} else if ((repeat_axis == Y) &&
(repeat_index == j)) {
j--;
repeat_count--;
}
}
}
}
}
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;
strokep->type = ATP_STROKE_TOUCH;
strokep->matched = true;
microtime(&strokep->ctime);
strokep->age = 1;
strokep->x = fingerp->x;
strokep->y = fingerp->y;
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)
{
if (strokep->flags & ATSF_ZOMBIE)
strokep->flags &= ~ATSF_ZOMBIE;
strokep->age++;
if (strokep->age <= atp_stroke_maturity_threshold) {
strokep->instantaneous_dx = 0;
strokep->instantaneous_dy = 0;
}
if (atp_compute_stroke_movement(strokep))
*movementp = true;
if (strokep->type != ATP_STROKE_TOUCH)
return;
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 {
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));
}
static void
atp_update_pending_mickeys(atp_stroke_t *strokep)
{
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; \
else { \
if ((I) > 0) { \
\
(I) = (((I) + (atp_mickeys_scale_factor - 1)) / \
atp_mickeys_scale_factor) * \
atp_mickeys_scale_factor; \
\
\
(P) -= ((I) << 1); \
\
\
(P) = imax((P), 0); \
} else { \
\
(I) = (((I) - (atp_mickeys_scale_factor - 1)) / \
atp_mickeys_scale_factor) * \
atp_mickeys_scale_factor; \
\
\
(P) -= ((I) << 1); \
\
\
(P) = imin((P), 0); \
} \
}
UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dx,
strokep->pending_dx);
UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dy,
strokep->pending_dy);
}
static boolean_t
atp_compute_stroke_movement(atp_stroke_t *strokep)
{
if (atp_stroke_has_small_movement(strokep))
atp_update_pending_mickeys(strokep);
else {
strokep->pending_dx = 0;
strokep->pending_dy = 0;
}
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));
}
static void
atp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep)
{
if (strokep->flags & ATSF_ZOMBIE)
return;
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);
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;
int horizontal_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;
}
}
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;
if (sc->sc_ibtn != 0)
return;
if ((n_touches_reaped == 0) && (n_slides_reaped == 0))
return;
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);
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:
return;
}
atp_add_to_queue(sc, 0, 0, 0, 0);
} 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);
}
}
static void
atp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep)
{
strokep->type = ATP_STROKE_SLIDE;
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)
{
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);
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);
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);
if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0)
return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ?
BUS_PROBE_DEFAULT : ENXIO);
if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0)
if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX)
return (BUS_PROBE_DEFAULT);
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;
if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr,
&descriptor_len, M_TEMP, uaa->info.bIfaceIndex) !=
USB_ERR_NORMAL_COMPLETION)
return (ENXIO);
sc->sc_expected_sensor_data_len = hid_report_size_max(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);
}
di = USB_GET_DRIVER_INFO(uaa);
sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di);
if ((sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER) &&
(DECODE_PRODUCT_FROM_DRIVER_INFO(di) == FOUNTAIN))
DPRINTF("device mode switch skipped: Fountain device\n");
else 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);
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) {
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) {
sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
} else if (sc->sc_state & ATP_DOUBLE_TAP_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++;
}
if (n_movements > 1) {
dx /= (int)n_movements;
dy /= (int)n_movements;
}
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:
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:
if (error != USB_ERR_CANCELLED) {
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;
rate = sc->sc_pollrate;
if (rate > 1000)
rate = 1000;
if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) {
usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate);
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);
if (sc->sc_fflags & fflags)
return (EBUSY);
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)
;
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);
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 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, NULL, NULL);
MODULE_DEPEND(atp, usb, 1, 1, 1);
MODULE_DEPEND(atp, hid, 1, 1, 1);
MODULE_VERSION(atp, 1);
USB_PNP_HOST_INFO(fg_devs);
USB_PNP_HOST_INFO(wsp_devs);