summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2014-02-25 07:40:37 +0000
committerdim <dim@FreeBSD.org>2014-02-25 07:40:37 +0000
commitb6b1de44bac83664c1e0c7328bbaf6a9916d1e81 (patch)
treee70ea06f2cc047626d0114a399c6830d35427390
parent4d6ab5a180da9145c9f83969f100917669704d88 (diff)
parent2dffd7c97edf7096030837706fb17e59b1b63cf4 (diff)
downloadFreeBSD-src-b6b1de44bac83664c1e0c7328bbaf6a9916d1e81.zip
FreeBSD-src-b6b1de44bac83664c1e0c7328bbaf6a9916d1e81.tar.gz
Merge from head up to r262472.
-rw-r--r--bin/sh/sh.16
-rw-r--r--lib/libc/iconv/citrus_prop.c5
-rw-r--r--lib/libc/iconv/citrus_prop.h4
-rw-r--r--lib/libiconv_modules/BIG5/citrus_big5.c11
-rw-r--r--lib/libiconv_modules/HZ/citrus_hz.c14
-rw-r--r--lib/libiconv_modules/VIQR/citrus_viqr.c7
-rw-r--r--share/man/man4/atp.466
-rw-r--r--share/man/man7/hier.73
-rw-r--r--sys/arm/arm/cpufunc.c16
-rw-r--r--sys/arm/arm/cpufunc_asm_armv4.S6
-rw-r--r--sys/arm/arm/cpufunc_asm_armv6.S6
-rw-r--r--sys/arm/arm/cpufunc_asm_armv7.S38
-rw-r--r--sys/arm/arm/mp_machdep.c6
-rw-r--r--sys/arm/conf/IMX61
-rw-r--r--sys/arm/conf/QUARTZ26
-rw-r--r--sys/arm/freescale/imx/files.imx61
-rw-r--r--sys/arm/freescale/imx/imx6_mp.c166
-rw-r--r--sys/arm/freescale/imx/std.imx63
-rw-r--r--sys/arm/include/cpufunc.h11
-rw-r--r--sys/boot/common/Makefile.inc2
-rw-r--r--sys/boot/fdt/dts/am335x.dtsi20
-rw-r--r--sys/boot/fdt/dts/beaglebone-black.dts6
-rw-r--r--sys/boot/fdt/dts/vybrid-quartz.dts65
-rw-r--r--sys/boot/ficl/Makefile2
-rw-r--r--sys/cddl/dev/systrace/systrace.c6
-rw-r--r--sys/conf/files1
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch.c30
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8327.c481
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_8327.h91
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_vlans.c16
-rw-r--r--sys/dev/etherswitch/arswitch/arswitch_vlans.h10
-rw-r--r--sys/dev/etherswitch/arswitch/arswitchvar.h25
-rw-r--r--sys/dev/iwn/if_iwn.c27
-rw-r--r--sys/dev/iwn/if_iwn_debug.h1
-rw-r--r--sys/dev/iwn/if_iwn_ioctl.h25
-rw-r--r--sys/dev/iwn/if_iwnvar.h16
-rw-r--r--sys/dev/usb/input/atp.c2671
-rw-r--r--sys/dev/usb/wlan/if_run.c9
-rw-r--r--sys/dev/usb/wlan/if_runvar.h5
-rw-r--r--sys/mips/conf/DB120.hints42
-rw-r--r--sys/sparc64/pci/fire.c2
-rw-r--r--sys/sparc64/sparc64/spitfire.c2
-rw-r--r--tools/tools/iwn/Makefile5
-rw-r--r--tools/tools/iwn/iwnstats/Makefile22
-rw-r--r--tools/tools/iwn/iwnstats/iwn_ioctl.c91
-rw-r--r--tools/tools/iwn/iwnstats/iwn_ioctl.h38
-rw-r--r--tools/tools/iwn/iwnstats/iwnstats.h40
-rw-r--r--tools/tools/iwn/iwnstats/main.c282
-rw-r--r--usr.sbin/pkg/pkg.c2
-rw-r--r--usr.sbin/pmcstat/Makefile3
-rw-r--r--usr.sbin/pmcstat/pmcpl_annotate_cg.c127
-rw-r--r--usr.sbin/pmcstat/pmcpl_annotate_cg.h42
-rw-r--r--usr.sbin/pmcstat/pmcstat.c12
-rw-r--r--usr.sbin/pmcstat/pmcstat.h1
-rw-r--r--usr.sbin/pmcstat/pmcstat_log.c6
55 files changed, 3404 insertions, 1218 deletions
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index fb8328c..1c3f8fb 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -40,14 +40,14 @@
.Nd command interpreter (shell)
.Sh SYNOPSIS
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Oo
.Ar script
.Op Ar arg ...
.Oc
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Fl c Ar string
.Oo
@@ -55,7 +55,7 @@
.Op Ar arg ...
.Oc
.Nm
-.Op Fl /+abCEefIimnPpTuVvx
+.Op Fl /+abCEefhIimnPpTuVvx
.Op Fl /+o Ar longname
.Fl s
.Op Ar arg ...
diff --git a/lib/libc/iconv/citrus_prop.c b/lib/libc/iconv/citrus_prop.c
index c2d4829..0e6d34a 100644
--- a/lib/libc/iconv/citrus_prop.c
+++ b/lib/libc/iconv/citrus_prop.c
@@ -339,7 +339,7 @@ name_found:
static int
_citrus_prop_parse_element(struct _memstream * __restrict ms,
- const _citrus_prop_hint_t * __restrict hints, void ** __restrict context)
+ const _citrus_prop_hint_t * __restrict hints, void * __restrict context)
{
int ch, errnum;
#define _CITRUS_PROP_HINT_NAME_LEN_MAX 255
@@ -435,8 +435,7 @@ _citrus_prop_parse_variable(const _citrus_prop_hint_t * __restrict hints,
if (ch == EOF || ch == '\0')
break;
_memstream_ungetc(&ms, ch);
- errnum = _citrus_prop_parse_element(
- &ms, hints, (void ** __restrict)context);
+ errnum = _citrus_prop_parse_element(&ms, hints, context);
if (errnum != 0)
return (errnum);
}
diff --git a/lib/libc/iconv/citrus_prop.h b/lib/libc/iconv/citrus_prop.h
index db087a4..2962154 100644
--- a/lib/libc/iconv/citrus_prop.h
+++ b/lib/libc/iconv/citrus_prop.h
@@ -42,7 +42,7 @@ typedef struct _citrus_prop_hint_t _citrus_prop_hint_t;
#define _CITRUS_PROP_CB0_T(_func_, _type_) \
typedef int (*_citrus_prop_##_func_##_cb_func_t) \
- (void ** __restrict, const char *, _type_); \
+ (void * __restrict, const char *, _type_); \
typedef struct { \
_citrus_prop_##_func_##_cb_func_t func; \
} _citrus_prop_##_func_##_cb_t;
@@ -52,7 +52,7 @@ _CITRUS_PROP_CB0_T(str, const char *)
#define _CITRUS_PROP_CB1_T(_func_, _type_) \
typedef int (*_citrus_prop_##_func_##_cb_func_t) \
- (void ** __restrict, const char *, _type_, _type_); \
+ (void * __restrict, const char *, _type_, _type_); \
typedef struct { \
_citrus_prop_##_func_##_cb_func_t func; \
} _citrus_prop_##_func_##_cb_t;
diff --git a/lib/libiconv_modules/BIG5/citrus_big5.c b/lib/libiconv_modules/BIG5/citrus_big5.c
index e79b162..d0e7047 100644
--- a/lib/libiconv_modules/BIG5/citrus_big5.c
+++ b/lib/libiconv_modules/BIG5/citrus_big5.c
@@ -172,7 +172,7 @@ _citrus_BIG5_check_excludes(_BIG5EncodingInfo *ei, wint_t c)
}
static int
-_citrus_BIG5_fill_rowcol(void ** __restrict ctx, const char * __restrict s,
+_citrus_BIG5_fill_rowcol(void * __restrict ctx, const char * __restrict s,
uint64_t start, uint64_t end)
{
_BIG5EncodingInfo *ei;
@@ -191,7 +191,7 @@ _citrus_BIG5_fill_rowcol(void ** __restrict ctx, const char * __restrict s,
static int
/*ARGSUSED*/
-_citrus_BIG5_fill_excludes(void ** __restrict ctx,
+_citrus_BIG5_fill_excludes(void * __restrict ctx,
const char * __restrict s __unused, uint64_t start, uint64_t end)
{
_BIG5EncodingInfo *ei;
@@ -237,7 +237,6 @@ static int
_citrus_BIG5_encoding_module_init(_BIG5EncodingInfo * __restrict ei,
const void * __restrict var, size_t lenvar)
{
- void *ctx = (void *)ei;
const char *s;
int err;
@@ -259,9 +258,9 @@ _citrus_BIG5_encoding_module_init(_BIG5EncodingInfo * __restrict ei,
}
/* fallback Big5-1984, for backward compatibility. */
- _citrus_BIG5_fill_rowcol((void **)&ctx, "row", 0xA1, 0xFE);
- _citrus_BIG5_fill_rowcol((void **)&ctx, "col", 0x40, 0x7E);
- _citrus_BIG5_fill_rowcol((void **)&ctx, "col", 0xA1, 0xFE);
+ _citrus_BIG5_fill_rowcol(ei, "row", 0xA1, 0xFE);
+ _citrus_BIG5_fill_rowcol(ei, "col", 0x40, 0x7E);
+ _citrus_BIG5_fill_rowcol(ei, "col", 0xA1, 0xFE);
return (0);
}
diff --git a/lib/libiconv_modules/HZ/citrus_hz.c b/lib/libiconv_modules/HZ/citrus_hz.c
index 6d0b6d8..f9eb006 100644
--- a/lib/libiconv_modules/HZ/citrus_hz.c
+++ b/lib/libiconv_modules/HZ/citrus_hz.c
@@ -65,8 +65,8 @@ typedef enum {
} charset_t;
typedef struct {
- int end;
int start;
+ int end;
int width;
} range_t;
@@ -505,12 +505,12 @@ _citrus_HZ_encoding_module_uninit(_HZEncodingInfo *ei)
}
static int
-_citrus_HZ_parse_char(void **context, const char *name __unused, const char *s)
+_citrus_HZ_parse_char(void *context, const char *name __unused, const char *s)
{
escape_t *escape;
void **p;
- p = (void **)*context;
+ p = (void **)context;
escape = (escape_t *)p[0];
if (escape->ch != '\0')
return (EINVAL);
@@ -522,14 +522,14 @@ _citrus_HZ_parse_char(void **context, const char *name __unused, const char *s)
}
static int
-_citrus_HZ_parse_graphic(void **context, const char *name, const char *s)
+_citrus_HZ_parse_graphic(void *context, const char *name, const char *s)
{
_HZEncodingInfo *ei;
escape_t *escape;
graphic_t *graphic;
void **p;
- p = (void **)*context;
+ p = (void **)context;
escape = (escape_t *)p[0];
ei = (_HZEncodingInfo *)p[1];
graphic = malloc(sizeof(*graphic));
@@ -591,13 +591,13 @@ _CITRUS_PROP_HINT_END
};
static int
-_citrus_HZ_parse_escape(void **context, const char *name, const char *s)
+_citrus_HZ_parse_escape(void *context, const char *name, const char *s)
{
_HZEncodingInfo *ei;
escape_t *escape;
void *p[2];
- ei = (_HZEncodingInfo *)*context;
+ ei = (_HZEncodingInfo *)context;
escape = malloc(sizeof(*escape));
if (escape == NULL)
return (EINVAL);
diff --git a/lib/libiconv_modules/VIQR/citrus_viqr.c b/lib/libiconv_modules/VIQR/citrus_viqr.c
index b8a881d..4bcba87 100644
--- a/lib/libiconv_modules/VIQR/citrus_viqr.c
+++ b/lib/libiconv_modules/VIQR/citrus_viqr.c
@@ -433,7 +433,6 @@ static int
_citrus_VIQR_encoding_module_init(_VIQREncodingInfo * __restrict ei,
const void * __restrict var __unused, size_t lenvar __unused)
{
- const mnemonic_def_t *p;
const char *s;
size_t i, n;
int errnum;
@@ -457,7 +456,10 @@ _citrus_VIQR_encoding_module_init(_VIQREncodingInfo * __restrict ei,
return (errnum);
}
}
- for (i = 0;; ++i) {
+#if mnemonic_ext_size > 0
+ for (i = 0; i < mnemonic_ext_size; ++i) {
+ const mnemonic_def_t *p;
+
p = &mnemonic_ext[i];
n = strlen(p->name);
if (ei->mb_cur_max < n)
@@ -469,6 +471,7 @@ _citrus_VIQR_encoding_module_init(_VIQREncodingInfo * __restrict ei,
return (errnum);
}
}
+#endif
return (0);
}
diff --git a/share/man/man4/atp.4 b/share/man/man4/atp.4
index 27d6397..f782798 100644
--- a/share/man/man4/atp.4
+++ b/share/man/man4/atp.4
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2009 Rohit Grover <rgrover1 at gmail dot com>.
+.\" Copyright (c) 2014 Rohit Grover <rgrover1 at gmail dot com>.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 7, 2014
+.Dd February 24, 2014
.Dt ATP 4
.Os
.Sh NAME
@@ -41,8 +41,7 @@ your kernel configuration file:
.Cd "device usb"
.Ed
.Pp
-Alternatively, to load the driver as a
-module at boot time, place the following line in
+Alternatively, to load the driver as a module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
atp_load="YES"
@@ -50,24 +49,21 @@ atp_load="YES"
.Sh DESCRIPTION
The
.Nm
-driver provides support for the Apple Internal Trackpad
-device found in many Apple laptops.
+driver provides support for the Apple Internal Trackpad device found in many
+Apple laptops. Older (Fountain/Geyser) and the newer (Wellspring) trackpad
+families are all supported through a unified driver.
+.Pp
+The driver simulates a three\-button mouse using multi\-finger tap detection.
+Single finger tap generates a left\-button click; two\-finger tap maps to the
+middle button; whereas a three\-finger tap gets treated as a right button
+click.
+.Pp
+There is support for 2\-finger horizontal scrolling, which translates to
+page\-back/forward events; vertical multi\-finger scrolling emulates the mouse
+wheel.
.Pp
-The driver simulates a three\-button mouse using multi\-finger tap
-detection.
-.
-A single\-finger tap generates a left button click;
-two\-finger tap maps to the middle button; whereas a three\-finger tap
-gets treated as a right button click.
-.
A double\-tap followed by a drag is treated as a selection gesture; a
virtual left\-button click is assumed for the lifespan of the drag.
-.
-.Nm
-attempts to filter away activity at the horizontal edges of the
-trackpad\-\-this is to keep unintentional palm movement from being
-considered as user input.
-.
.Pp
.Nm
supports dynamic reconfiguration using
@@ -76,6 +72,28 @@ through nodes under
.Nm hw.usb.atp .
Pointer sensitivity can be controlled using the sysctl tunable
.Nm hw.usb.atp.scale_factor .
+Smaller values of
+.Fa scale_factor
+result in faster movement.
+.
+A simple high-pass filter is used to reduce contributions
+from small movements; the threshold for this filter may be controlled by
+.Nm hw.usb.atp.small_movement .
+.
+The maximum tolerable duration of a touch gesture is controlled by
+.Nm hw.usb.atp.touch_timeout
+(in microseconds); beyond this period, touches are considered to be slides.
+(This conversion also happens when a finger stroke accumulates at least
+.Nm hw.usb.atp.slide_min_movement
+movement (in mickeys).
+.
+The maximum time (in microseconds) to allow an association between a double-
+tap and drag gesture may be controlled by
+.Nm hw.usb.atp.double_tap_threshold .
+.
+Should one want to disable tap detection and rely only upon physical button
+presses, set the following sysctl to a value of 2
+.Nm hw.usb.atp.tap_minimum .
.
.Sh HARDWARE
The
@@ -84,6 +102,8 @@ driver provides support for the following Product IDs:
.Pp
.Bl -bullet -compact
.It
+PowerBooks, iBooks (IDs: 0x020e, 0x020f, 0x0210, 0x0214, 0x0215, 0x0216)
+.It
Core Duo MacBook & MacBook Pro (IDs: 0x0217, 0x0218, 0x0219)
.It
Core2 Duo MacBook & MacBook Pro (IDs: 0x021a, 0x021b, 0x021c)
@@ -95,6 +115,14 @@ Core2 Duo MacBook3,1 (IDs: 0x0229, 0x022a, 0x022b)
15 inch PowerBook (IDs: 0x020e, 0x020f, 0x0215)
.It
17 inch PowerBook (ID: 0x020d)
+.It
+Almost all recent Macbook-Pros and Airs (IDs: 0x0223, 0x0223, 0x0224, 0x0224,
+0x0225, 0x0225, 0x0230, 0x0230, 0x0231, 0x0231, 0x0232, 0x0232, 0x0236,
+0x0236, 0x0237, 0x0237, 0x0238, 0x0238, 0x023f, 0x023f, 0x0240, 0x0241,
+0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 0x0249, 0x024a, 0x024b,
+0x024c, 0x024d, 0x024e, 0x0252, 0x0252, 0x0253, 0x0253, 0x0254, 0x0254,
+0x0259, 0x025a, 0x025b, 0x0262, 0x0262, 0x0263, 0x0264, 0x0290, 0x0291,
+0x0292)
.El
.Pp
To discover the product\-id of a touchpad, search for 'Trackpad' in the
diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7
index ef70196..920583d 100644
--- a/share/man/man7/hier.7
+++ b/share/man/man7/hier.7
@@ -383,6 +383,9 @@ a.out backward compatibility libraries
DTrace library scripts
.It Pa engines/
OpenSSL (Cryptography/SSL toolkit) dynamically loadable engines
+.It Pa private/
+Private system libraries not for use by third-party programs.
+ABI and API stability are not guaranteed.
.El
.Pp
.It Pa libdata/
diff --git a/sys/arm/arm/cpufunc.c b/sys/arm/arm/cpufunc.c
index dc0a478..11365aa 100644
--- a/sys/arm/arm/cpufunc.c
+++ b/sys/arm/arm/cpufunc.c
@@ -146,6 +146,7 @@ struct cpu_functions arm7tdmi_cpufuncs = {
(void *)arm7tdmi_cache_flushID, /* dcache_inv_range */
(void *)cpufunc_nullop, /* dcache_wb_range */
+ cpufunc_nullop, /* idcache_inv_all */
arm7tdmi_cache_flushID, /* idcache_wbinv_all */
(void *)arm7tdmi_cache_flushID, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -208,6 +209,7 @@ struct cpu_functions arm8_cpufuncs = {
/*XXX*/ (void *)arm8_cache_purgeID, /* dcache_inv_range */
(void *)arm8_cache_cleanID, /* dcache_wb_range */
+ cpufunc_nullop, /* idcache_inv_all */
arm8_cache_purgeID, /* idcache_wbinv_all */
(void *)arm8_cache_purgeID, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -269,6 +271,7 @@ struct cpu_functions arm9_cpufuncs = {
arm9_dcache_inv_range, /* dcache_inv_range */
arm9_dcache_wb_range, /* dcache_wb_range */
+ armv4_idcache_inv_all, /* idcache_inv_all */
arm9_idcache_wbinv_all, /* idcache_wbinv_all */
arm9_idcache_wbinv_range, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -331,6 +334,7 @@ struct cpu_functions armv5_ec_cpufuncs = {
armv5_ec_dcache_inv_range, /* dcache_inv_range */
armv5_ec_dcache_wb_range, /* dcache_wb_range */
+ armv4_idcache_inv_all, /* idcache_inv_all */
armv5_ec_idcache_wbinv_all, /* idcache_wbinv_all */
armv5_ec_idcache_wbinv_range, /* idcache_wbinv_range */
@@ -392,6 +396,7 @@ struct cpu_functions sheeva_cpufuncs = {
sheeva_dcache_inv_range, /* dcache_inv_range */
sheeva_dcache_wb_range, /* dcache_wb_range */
+ armv4_idcache_inv_all, /* idcache_inv_all */
armv5_ec_idcache_wbinv_all, /* idcache_wbinv_all */
sheeva_idcache_wbinv_range, /* idcache_wbinv_all */
@@ -454,6 +459,7 @@ struct cpu_functions arm10_cpufuncs = {
arm10_dcache_inv_range, /* dcache_inv_range */
arm10_dcache_wb_range, /* dcache_wb_range */
+ armv4_idcache_inv_all, /* idcache_inv_all */
arm10_idcache_wbinv_all, /* idcache_wbinv_all */
arm10_idcache_wbinv_range, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -515,6 +521,7 @@ struct cpu_functions pj4bv7_cpufuncs = {
armv7_dcache_inv_range, /* dcache_inv_range */
armv7_dcache_wb_range, /* dcache_wb_range */
+ armv7_idcache_inv_all, /* idcache_inv_all */
armv7_idcache_wbinv_all, /* idcache_wbinv_all */
armv7_idcache_wbinv_range, /* idcache_wbinv_all */
@@ -577,6 +584,7 @@ struct cpu_functions sa110_cpufuncs = {
/*XXX*/ sa1_cache_purgeD_rng, /* dcache_inv_range */
sa1_cache_cleanD_rng, /* dcache_wb_range */
+ sa1_cache_flushID, /* idcache_inv_all */
sa1_cache_purgeID, /* idcache_wbinv_all */
sa1_cache_purgeID_rng, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -638,6 +646,7 @@ struct cpu_functions sa11x0_cpufuncs = {
/*XXX*/ sa1_cache_purgeD_rng, /* dcache_inv_range */
sa1_cache_cleanD_rng, /* dcache_wb_range */
+ sa1_cache_flushID, /* idcache_inv_all */
sa1_cache_purgeID, /* idcache_wbinv_all */
sa1_cache_purgeID_rng, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -699,6 +708,7 @@ struct cpu_functions ixp12x0_cpufuncs = {
/*XXX*/ sa1_cache_purgeD_rng, /* dcache_inv_range */
sa1_cache_cleanD_rng, /* dcache_wb_range */
+ sa1_cache_flushID, /* idcache_inv_all */
sa1_cache_purgeID, /* idcache_wbinv_all */
sa1_cache_purgeID_rng, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -763,6 +773,7 @@ struct cpu_functions xscale_cpufuncs = {
xscale_cache_flushD_rng, /* dcache_inv_range */
xscale_cache_cleanD_rng, /* dcache_wb_range */
+ xscale_cache_flushID, /* idcache_inv_all */
xscale_cache_purgeID, /* idcache_wbinv_all */
xscale_cache_purgeID_rng, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -826,6 +837,7 @@ struct cpu_functions xscalec3_cpufuncs = {
xscale_cache_flushD_rng, /* dcache_inv_range */
xscalec3_cache_cleanD_rng, /* dcache_wb_range */
+ xscale_cache_flushID, /* idcache_inv_all */
xscalec3_cache_purgeID, /* idcache_wbinv_all */
xscalec3_cache_purgeID_rng, /* idcache_wbinv_range */
xscalec3_l2cache_purge, /* l2cache_wbinv_all */
@@ -888,6 +900,7 @@ struct cpu_functions fa526_cpufuncs = {
fa526_dcache_inv_range, /* dcache_inv_range */
fa526_dcache_wb_range, /* dcache_wb_range */
+ armv4_idcache_inv_all, /* idcache_inv_all */
fa526_idcache_wbinv_all, /* idcache_wbinv_all */
fa526_idcache_wbinv_range, /* idcache_wbinv_range */
cpufunc_nullop, /* l2cache_wbinv_all */
@@ -949,6 +962,7 @@ struct cpu_functions arm1136_cpufuncs = {
armv6_dcache_inv_range, /* dcache_inv_range */
armv6_dcache_wb_range, /* dcache_wb_range */
+ armv6_idcache_inv_all, /* idcache_inv_all */
arm11x6_idcache_wbinv_all, /* idcache_wbinv_all */
arm11x6_idcache_wbinv_range, /* idcache_wbinv_range */
@@ -1010,6 +1024,7 @@ struct cpu_functions arm1176_cpufuncs = {
armv6_dcache_inv_range, /* dcache_inv_range */
armv6_dcache_wb_range, /* dcache_wb_range */
+ armv6_idcache_inv_all, /* idcache_inv_all */
arm11x6_idcache_wbinv_all, /* idcache_wbinv_all */
arm11x6_idcache_wbinv_range, /* idcache_wbinv_range */
@@ -1072,6 +1087,7 @@ struct cpu_functions cortexa_cpufuncs = {
armv7_dcache_inv_range, /* dcache_inv_range */
armv7_dcache_wb_range, /* dcache_wb_range */
+ armv7_idcache_inv_all, /* idcache_inv_all */
armv7_idcache_wbinv_all, /* idcache_wbinv_all */
armv7_idcache_wbinv_range, /* idcache_wbinv_range */
diff --git a/sys/arm/arm/cpufunc_asm_armv4.S b/sys/arm/arm/cpufunc_asm_armv4.S
index 1123e4a..a61a3dc 100644
--- a/sys/arm/arm/cpufunc_asm_armv4.S
+++ b/sys/arm/arm/cpufunc_asm_armv4.S
@@ -71,3 +71,9 @@ ENTRY(armv4_drain_writebuf)
RET
END(armv4_drain_writebuf)
+ENTRY(armv4_idcache_inv_all)
+ mov r0, #0
+ mcr p15, 0, r0, c7, c7, 0 /* invalidate all I+D cache */
+ RET
+END(armv4_drain_writebuf)
+
diff --git a/sys/arm/arm/cpufunc_asm_armv6.S b/sys/arm/arm/cpufunc_asm_armv6.S
index b8a2d9c..5ba0076 100644
--- a/sys/arm/arm/cpufunc_asm_armv6.S
+++ b/sys/arm/arm/cpufunc_asm_armv6.S
@@ -148,3 +148,9 @@ ENTRY(armv6_dcache_wbinv_all)
END(armv6_idcache_wbinv_all)
END(armv6_dcache_wbinv_all)
+ENTRY(armv6_idcache_inv_all)
+ mov r0, #0
+ mcr p15, 0, r0, c7, c7, 0 /* invalidate all I+D cache */
+ RET
+END(armv6_idcache_inv_all)
+
diff --git a/sys/arm/arm/cpufunc_asm_armv7.S b/sys/arm/arm/cpufunc_asm_armv7.S
index bc28f82..74933eb 100644
--- a/sys/arm/arm/cpufunc_asm_armv7.S
+++ b/sys/arm/arm/cpufunc_asm_armv7.S
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2010 Per Odlund <per.odlund@armagedon.se>
* Copyright (C) 2011 MARVELL INTERNATIONAL LTD.
* All rights reserved.
*
@@ -305,3 +306,40 @@ ENTRY(armv7_auxctrl)
RET
END(armv7_auxctrl)
+ENTRY(armv7_idcache_inv_all)
+ mov r0, #0
+ mcr p15, 2, r0, c0, c0, 0 @ set cache level to L1
+ mrc p15, 1, r0, c0, c0, 0 @ read CCSIDR
+
+ ubfx r2, r0, #13, #15 @ get num sets - 1 from CCSIDR
+ ubfx r3, r0, #3, #10 @ get numways - 1 from CCSIDR
+ clz r1, r3 @ number of bits to MSB of way
+ lsl r3, r3, r1 @ shift into position
+ mov ip, #1 @
+ lsl ip, ip, r1 @ ip now contains the way decr
+
+ ubfx r0, r0, #0, #3 @ get linesize from CCSIDR
+ add r0, r0, #4 @ apply bias
+ lsl r2, r2, r0 @ shift sets by log2(linesize)
+ add r3, r3, r2 @ merge numsets - 1 with numways - 1
+ sub ip, ip, r2 @ subtract numsets - 1 from way decr
+ mov r1, #1
+ lsl r1, r1, r0 @ r1 now contains the set decr
+ mov r2, ip @ r2 now contains set way decr
+
+ /* r3 = ways/sets, r2 = way decr, r1 = set decr, r0 and ip are free */
+1: mcr p15, 0, r3, c7, c6, 2 @ invalidate line
+ movs r0, r3 @ get current way/set
+ beq 2f @ at 0 means we are done.
+ movs r0, r0, lsl #10 @ clear way bits leaving only set bits
+ subne r3, r3, r1 @ non-zero?, decrement set #
+ subeq r3, r3, r2 @ zero?, decrement way # and restore set count
+ b 1b
+
+2: dsb @ wait for stores to finish
+ mov r0, #0 @ and ...
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate instruction+branch cache
+ isb @ instruction sync barrier
+ bx lr @ return
+END(armv7_l1cache_inv_all)
+
diff --git a/sys/arm/arm/mp_machdep.c b/sys/arm/arm/mp_machdep.c
index 0c70aca..c9561c1 100644
--- a/sys/arm/arm/mp_machdep.c
+++ b/sys/arm/arm/mp_machdep.c
@@ -128,10 +128,10 @@ cpu_mp_start(void)
bzero((void *)temp_pagetable_va, L1_TABLE_SIZE);
for (addr = arm_physmem_kernaddr; addr <= addr_end; addr += L1_S_SIZE) {
((int *)(temp_pagetable_va))[addr >> L1_S_SHIFT] =
- L1_TYPE_S|L1_SHARED|L1_S_C|L1_S_AP(AP_KRW)|L1_S_DOM(PMAP_DOMAIN_KERNEL)|addr;
+ L1_TYPE_S|L1_SHARED|L1_S_C|L1_S_B|L1_S_AP(AP_KRW)|L1_S_DOM(PMAP_DOMAIN_KERNEL)|addr;
((int *)(temp_pagetable_va))[(addr -
arm_physmem_kernaddr + KERNVIRTADDR) >> L1_S_SHIFT] =
- L1_TYPE_S|L1_SHARED|L1_S_C|L1_S_AP(AP_KRW)|L1_S_DOM(PMAP_DOMAIN_KERNEL)|addr;
+ L1_TYPE_S|L1_SHARED|L1_S_C|L1_S_B|L1_S_AP(AP_KRW)|L1_S_DOM(PMAP_DOMAIN_KERNEL)|addr;
}
#if defined(CPU_MV_PJ4B)
@@ -173,6 +173,8 @@ init_secondary(int cpu)
uint32_t loop_counter;
int start = 0, end = 0;
+ cpu_idcache_inv_all();
+
cpu_setup(NULL);
setttb(pmap_pa);
cpu_tlb_flushID();
diff --git a/sys/arm/conf/IMX6 b/sys/arm/conf/IMX6
index 7883f3e..8d418e5 100644
--- a/sys/arm/conf/IMX6
+++ b/sys/arm/conf/IMX6
@@ -17,6 +17,7 @@
#
# $FreeBSD$
+ident IMX6
include "../freescale/imx/std.imx6"
options HZ=250 # Scheduling quantum is 4 milliseconds.
diff --git a/sys/arm/conf/QUARTZ b/sys/arm/conf/QUARTZ
new file mode 100644
index 0000000..4a31667
--- /dev/null
+++ b/sys/arm/conf/QUARTZ
@@ -0,0 +1,26 @@
+# Kernel configuration for Device Solutions Quartz Module.
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+include "VYBRID.common"
+ident QUARTZ
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=vybrid-quartz.dts
diff --git a/sys/arm/freescale/imx/files.imx6 b/sys/arm/freescale/imx/files.imx6
index 41c2594..1512b7c 100644
--- a/sys/arm/freescale/imx/files.imx6
+++ b/sys/arm/freescale/imx/files.imx6
@@ -22,6 +22,7 @@ arm/freescale/imx/common.c standard
arm/freescale/imx/imx6_anatop.c standard
arm/freescale/imx/imx6_ccm.c standard
arm/freescale/imx/imx6_machdep.c standard
+arm/freescale/imx/imx6_mp.c optional smp
arm/freescale/imx/imx6_pl310.c standard
arm/freescale/imx/imx_machdep.c standard
arm/freescale/imx/imx_gpt.c standard
diff --git a/sys/arm/freescale/imx/imx6_mp.c b/sys/arm/freescale/imx/imx6_mp.c
new file mode 100644
index 0000000..e121832
--- /dev/null
+++ b/sys/arm/freescale/imx/imx6_mp.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2014 Juergen Weiss <weiss@uni-mainz.de>
+ * Copyright (c) 2014 Ian Lepore <ian@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 ``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 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+
+#define SCU_PHYSBASE 0x00a00000
+#define SCU_SIZE 0x00001000
+
+#define SCU_CONTROL_REG 0x00
+#define SCU_CONTROL_ENABLE (1 << 0)
+#define SCU_CONFIG_REG 0x04
+#define SCU_CONFIG_REG_NCPU_MASK 0x03
+#define SCU_CPUPOWER_REG 0x08
+#define SCU_INV_TAGS_REG 0x0c
+#define SCU_DIAG_CONTROL 0x30
+#define SCU_DIAG_DISABLE_MIGBIT (1 << 0)
+#define SCU_FILTER_START_REG 0x40
+#define SCU_FILTER_END_REG 0x44
+#define SCU_SECURE_ACCESS_REG 0x50
+#define SCU_NONSECURE_ACCESS_REG 0x54
+
+#define SRC_PHYSBASE 0x020d8000
+#define SRC_SIZE 0x4000
+#define SRC_CONTROL_REG 0x00
+#define SRC_CONTROL_C1ENA_SHIFT 22 /* Bit for Core 1 enable */
+#define SRC_CONTROL_C1RST_SHIFT 14 /* Bit for Core 1 reset */
+#define SRC_GPR0_C1FUNC 0x20 /* Register for Core 1 entry func */
+#define SRC_GPR1_C1ARG 0x24 /* Register for Core 1 entry arg */
+
+void
+platform_mp_init_secondary(void)
+{
+
+ gic_init_secondary();
+}
+
+void
+platform_mp_setmaxid(void)
+{
+ bus_space_handle_t scu;
+ uint32_t val;
+
+ /* If we've already set the global vars don't bother to do it again. */
+ if (mp_ncpus != 0)
+ return;
+
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE, SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_CONFIG_REG);
+ bus_space_unmap(fdtbus_bs_tag, scu, SCU_SIZE);
+
+ mp_maxid = (val & SCU_CONFIG_REG_NCPU_MASK);
+ mp_ncpus = mp_maxid + 1;
+}
+
+int
+platform_mp_probe(void)
+{
+
+ /* I think platform_mp_setmaxid must get called first, but be safe. */
+ if (mp_ncpus == 0)
+ platform_mp_setmaxid();
+
+ return (mp_ncpus > 1);
+}
+
+void
+platform_mp_start_ap(void)
+{
+ bus_space_handle_t scu;
+ bus_space_handle_t src;
+
+ uint32_t val;
+ int i;
+
+ if (bus_space_map(fdtbus_bs_tag, SCU_PHYSBASE, SCU_SIZE, 0, &scu) != 0)
+ panic("Couldn't map the SCU\n");
+ if (bus_space_map(fdtbus_bs_tag, SRC_PHYSBASE, SRC_SIZE, 0, &src) != 0)
+ panic("Couldn't map the system reset controller (SRC)\n");
+
+ /*
+ * Invalidate SCU cache tags. The 0x0000fff0 constant invalidates all
+ * ways on all cores 1-3 (leaving core 0 alone). Per the ARM docs, it's
+ * harmless to write to the bits for cores that are not present.
+ */
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_INV_TAGS_REG, 0x0000fff0);
+
+ /*
+ * Erratum ARM/MP: 764369 (problems with cache maintenance).
+ * Setting the "disable-migratory bit" in the undocumented SCU
+ * Diagnostic Control Register helps work around the problem.
+ */
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_DIAG_CONTROL,
+ val | SCU_DIAG_DISABLE_MIGBIT);
+
+ /* Enable the SCU. */
+ val = bus_space_read_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG);
+ bus_space_write_4(fdtbus_bs_tag, scu, SCU_CONTROL_REG,
+ val | SCU_CONTROL_ENABLE);
+
+ cpu_idcache_wbinv_all();
+ cpu_l2cache_wbinv_all();
+
+ /*
+ * For each AP core, set the entry point address and argument registers,
+ * and set the core-enable and core-reset bits in the control register.
+ */
+ val = bus_space_read_4(fdtbus_bs_tag, src, SRC_CONTROL_REG);
+ for (i=1; i < mp_ncpus; i++) {
+ bus_space_write_4(fdtbus_bs_tag, src, SRC_GPR0_C1FUNC + 8*i,
+ pmap_kextract((vm_offset_t)mpentry));
+ bus_space_write_4(fdtbus_bs_tag, src, SRC_GPR1_C1ARG + 8*i, 0);
+
+ val |= ((1 << (SRC_CONTROL_C1ENA_SHIFT - 1 + i )) |
+ ( 1 << (SRC_CONTROL_C1RST_SHIFT - 1 + i)));
+
+ }
+ bus_space_write_4(fdtbus_bs_tag, src, 0, val);
+
+ armv7_sev();
+
+ bus_space_unmap(fdtbus_bs_tag, scu, SCU_SIZE);
+ bus_space_unmap(fdtbus_bs_tag, src, SRC_SIZE);
+}
+
+void
+platform_ipi_send(cpuset_t cpus, u_int ipi)
+{
+
+ pic_ipi_send(cpus, ipi);
+}
diff --git a/sys/arm/freescale/imx/std.imx6 b/sys/arm/freescale/imx/std.imx6
index c328b22..51f82a8 100644
--- a/sys/arm/freescale/imx/std.imx6
+++ b/sys/arm/freescale/imx/std.imx6
@@ -10,5 +10,8 @@ options KERNPHYSADDR = 0x12000000
makeoptions KERNPHYSADDR = 0x12000000
options PHYSADDR = 0x10000000
+options IPI_IRQ_START=0
+options IPI_IRQ_END=15
+
files "../freescale/imx/files.imx6"
diff --git a/sys/arm/include/cpufunc.h b/sys/arm/include/cpufunc.h
index 0b53906..f38f9c1 100644
--- a/sys/arm/include/cpufunc.h
+++ b/sys/arm/include/cpufunc.h
@@ -104,6 +104,12 @@ struct cpu_functions {
*
* There are some rules that must be followed:
*
+ * ID-cache Invalidate All:
+ * Unlike other functions, this one must never write back.
+ * It is used to intialize the MMU when it is in an unknown
+ * state (such as when it may have lines tagged as valid
+ * that belong to a previous set of mappings).
+ *
* I-cache Synch (all or range):
* The goal is to synchronize the instruction stream,
* so you may beed to write-back dirty D-cache blocks
@@ -138,6 +144,7 @@ struct cpu_functions {
void (*cf_dcache_inv_range) (vm_offset_t, vm_size_t);
void (*cf_dcache_wb_range) (vm_offset_t, vm_size_t);
+ void (*cf_idcache_inv_all) (void);
void (*cf_idcache_wbinv_all) (void);
void (*cf_idcache_wbinv_range) (vm_offset_t, vm_size_t);
void (*cf_l2cache_wbinv_all) (void);
@@ -238,6 +245,7 @@ void tlb_broadcast(int);
#define cpu_dcache_inv_range(a, s) cpufuncs.cf_dcache_inv_range((a), (s))
#define cpu_dcache_wb_range(a, s) cpufuncs.cf_dcache_wb_range((a), (s))
+#define cpu_idcache_inv_all() cpufuncs.cf_idcache_inv_all()
#define cpu_idcache_wbinv_all() cpufuncs.cf_idcache_wbinv_all()
#define cpu_idcache_wbinv_range(a, s) cpufuncs.cf_idcache_wbinv_range((a), (s))
#define cpu_l2cache_wbinv_all() cpufuncs.cf_l2cache_wbinv_all()
@@ -495,6 +503,7 @@ void armv6_dcache_wbinv_range (vm_offset_t, vm_size_t);
void armv6_dcache_inv_range (vm_offset_t, vm_size_t);
void armv6_dcache_wb_range (vm_offset_t, vm_size_t);
+void armv6_idcache_inv_all (void);
void armv6_idcache_wbinv_all (void);
void armv6_idcache_wbinv_range (vm_offset_t, vm_size_t);
@@ -503,6 +512,7 @@ void armv7_tlb_flushID (void);
void armv7_tlb_flushID_SE (u_int);
void armv7_icache_sync_range (vm_offset_t, vm_size_t);
void armv7_idcache_wbinv_range (vm_offset_t, vm_size_t);
+void armv7_idcache_inv_all (void);
void armv7_dcache_wbinv_all (void);
void armv7_idcache_wbinv_all (void);
void armv7_dcache_wbinv_range (vm_offset_t, vm_size_t);
@@ -587,6 +597,7 @@ void armv4_tlb_flushD (void);
void armv4_tlb_flushD_SE (u_int va);
void armv4_drain_writebuf (void);
+void armv4_idcache_inv_all (void);
#endif
#if defined(CPU_IXP12X0)
diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc
index a2e5f34..2a4d561 100644
--- a/sys/boot/common/Makefile.inc
+++ b/sys/boot/common/Makefile.inc
@@ -18,7 +18,7 @@ SRCS+= load_elf32.c reloc_elf32.c
SRCS+= load_elf64.c reloc_elf64.c
.elif ${MACHINE_CPUARCH} == "sparc64"
SRCS+= load_elf64.c reloc_elf64.c
-.elif ${MACHINE_ARCH} == "mips64"
+.elif ${MACHINE_ARCH} == "mips64" || ${MACHINE_ARCH} == "mips64el"
SRCS+= load_elf64.c reloc_elf64.c
.endif
diff --git a/sys/boot/fdt/dts/am335x.dtsi b/sys/boot/fdt/dts/am335x.dtsi
index c16f039..35cecf2 100644
--- a/sys/boot/fdt/dts/am335x.dtsi
+++ b/sys/boot/fdt/dts/am335x.dtsi
@@ -210,6 +210,26 @@
i2c-device-id = <0>;
};
+ i2c1: i2c@4802a000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,i2c";
+ reg =< 0x4802a000 0x1000 >;
+ interrupts = <71>;
+ interrupt-parent = <&AINTC>;
+ i2c-device-id = <1>;
+ };
+
+ i2c2: i2c@4819c000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "ti,i2c";
+ reg =< 0x4819c000 0x1000 >;
+ interrupts = <30>;
+ interrupt-parent = <&AINTC>;
+ i2c-device-id = <2>;
+ };
+
pwm@48300000 {
compatible = "ti,am335x-pwm";
#address-cells = <1>;
diff --git a/sys/boot/fdt/dts/beaglebone-black.dts b/sys/boot/fdt/dts/beaglebone-black.dts
index 7c38f29..5da633f 100644
--- a/sys/boot/fdt/dts/beaglebone-black.dts
+++ b/sys/boot/fdt/dts/beaglebone-black.dts
@@ -52,6 +52,12 @@
/* I2C0 */
"I2C0_SDA", "I2C0_SDA","i2c",
"I2C0_SCL", "I2C0_SCL","i2c",
+ /* I2C1 */
+ "SPI0_D1", "I2C1_SDA", "i2c",
+ "SPI0_CS0", "I2C1_SCL", "i2c",
+ /* I2C2 */
+ "UART1_CTSn", "I2C2_SDA", "i2c",
+ "UART1_RTSn", "I2C2_SCL", "i2c",
/* Ethernet */
"MII1_RX_ER", "gmii1_rxerr", "input_pulldown",
"MII1_TX_EN", "gmii1_txen", "output",
diff --git a/sys/boot/fdt/dts/vybrid-quartz.dts b/sys/boot/fdt/dts/vybrid-quartz.dts
new file mode 100644
index 0000000..3a8882c
--- /dev/null
+++ b/sys/boot/fdt/dts/vybrid-quartz.dts
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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$
+ */
+
+/dts-v1/;
+
+/include/ "vybrid.dtsi"
+
+/ {
+ model = "Device Solutions Quartz Module";
+
+ memory {
+ device_type = "memory";
+ reg = < 0x80000000 0x10000000 >; /* 256MB RAM */
+ };
+
+ SOC: vybrid {
+ serial0: serial@40027000 {
+ status = "okay";
+ };
+
+ fec1: ethernet@400D1000 {
+ status = "okay";
+ iomux_config = < 54 0x1 55 0x1
+ 56 0x1 57 0x1
+ 58 0x1 59 0x1
+ 60 0x1 61 0x1
+ 62 0x1 0 0x2 >;
+ };
+
+ edma1: edma@40098000 {
+ status = "okay";
+ };
+ };
+
+ chosen {
+ bootargs = "-v";
+ stdin = "serial0";
+ stdout = "serial0";
+ };
+};
diff --git a/sys/boot/ficl/Makefile b/sys/boot/ficl/Makefile
index 08656f3..684df5b 100644
--- a/sys/boot/ficl/Makefile
+++ b/sys/boot/ficl/Makefile
@@ -5,7 +5,7 @@ FICLDIR?= ${.CURDIR}
.if !defined(FICL64)
.PATH: ${FICLDIR}/${MACHINE_CPUARCH:S/amd64/i386/}
-.elif ${MACHINE_ARCH} == "mips64"
+.elif ${MACHINE_ARCH} == "mips64" || ${MACHINE_ARCH} == "mips64el"
.PATH: ${FICLDIR}/${MACHINE_ARCH}
.else
.PATH: ${FICLDIR}/${MACHINE_CPUARCH}
diff --git a/sys/cddl/dev/systrace/systrace.c b/sys/cddl/dev/systrace/systrace.c
index ff63129..83f0793 100644
--- a/sys/cddl/dev/systrace/systrace.c
+++ b/sys/cddl/dev/systrace/systrace.c
@@ -168,6 +168,9 @@ static dtrace_pops_t systrace_pops = {
static struct cdev *systrace_cdev;
static dtrace_provider_id_t systrace_id;
+typedef void (*systrace_dtrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
#if !defined(LINUX_SYSTRACE)
/*
* Probe callback function.
@@ -211,7 +214,8 @@ systrace_probe(u_int32_t id, int sysnum, struct sysent *sysent, void *params,
}
/* Process the probe using the converted argments. */
- dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]);
+ ((systrace_dtrace_probe)(dtrace_probe))(id, uargs[0], uargs[1],
+ uargs[2], uargs[3], uargs[4], uargs[5], uargs[6], uargs[7]);
}
#endif
diff --git a/sys/conf/files b/sys/conf/files
index c610302..3e3a073 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1343,6 +1343,7 @@ dev/etherswitch/arswitch/arswitch_phy.c optional arswitch
dev/etherswitch/arswitch/arswitch_8216.c optional arswitch
dev/etherswitch/arswitch/arswitch_8226.c optional arswitch
dev/etherswitch/arswitch/arswitch_8316.c optional arswitch
+dev/etherswitch/arswitch/arswitch_8327.c optional arswitch
dev/etherswitch/arswitch/arswitch_7240.c optional arswitch
dev/etherswitch/arswitch/arswitch_9340.c optional arswitch
dev/etherswitch/arswitch/arswitch_vlans.c optional arswitch
diff --git a/sys/dev/etherswitch/arswitch/arswitch.c b/sys/dev/etherswitch/arswitch/arswitch.c
index d8fea9e..838fe28 100644
--- a/sys/dev/etherswitch/arswitch/arswitch.c
+++ b/sys/dev/etherswitch/arswitch/arswitch.c
@@ -66,6 +66,7 @@
#include <dev/etherswitch/arswitch/arswitch_8216.h>
#include <dev/etherswitch/arswitch/arswitch_8226.h>
#include <dev/etherswitch/arswitch/arswitch_8316.h>
+#include <dev/etherswitch/arswitch/arswitch_8327.h>
#include <dev/etherswitch/arswitch/arswitch_9340.h>
#include "mdio_if.h"
@@ -222,7 +223,7 @@ arswitch_set_vlan_mode(struct arswitch_softc *sc, uint32_t mode)
};
/* Reset VLANs. */
- arswitch_reset_vlans(sc);
+ sc->hal.arswitch_vlan_init_hw(sc);
return (0);
}
@@ -274,6 +275,11 @@ arswitch_attach(device_t dev)
sc->hal.arswitch_port_init = ar8xxx_port_init;
sc->hal.arswitch_port_vlan_setup = ar8xxx_port_vlan_setup;
sc->hal.arswitch_port_vlan_get = ar8xxx_port_vlan_get;
+ sc->hal.arswitch_vlan_init_hw = ar8xxx_reset_vlans;
+ sc->hal.arswitch_vlan_getvgroup = ar8xxx_getvgroup;
+ sc->hal.arswitch_vlan_setvgroup = ar8xxx_setvgroup;
+ sc->hal.arswitch_vlan_get_pvid = ar8xxx_get_pvid;
+ sc->hal.arswitch_vlan_set_pvid = ar8xxx_set_pvid;
/*
* Attach switch related functions
@@ -288,6 +294,8 @@ arswitch_attach(device_t dev)
ar8226_attach(sc);
else if (AR8X16_IS_SWITCH(sc, AR8316))
ar8316_attach(sc);
+ else if (AR8X16_IS_SWITCH(sc, AR8327))
+ ar8327_attach(sc);
else
return (ENXIO);
@@ -538,7 +546,7 @@ ar8xxx_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
ARSWITCH_LOCK(sc);
/* Retrieve the PVID. */
- arswitch_get_pvid(sc, p->es_port, &p->es_pvid);
+ sc->hal.arswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
/* Port flags. */
reg = arswitch_readreg(sc->sc_dev, AR8X16_REG_PORT_CTRL(p->es_port));
@@ -602,7 +610,7 @@ ar8xxx_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
/* Set the PVID. */
if (p->es_pvid != 0)
- arswitch_set_pvid(sc, p->es_port, p->es_pvid);
+ sc->hal.arswitch_vlan_set_pvid(sc, p->es_port, p->es_pvid);
/* Mutually exclusive. */
if (p->es_flags & ETHERSWITCH_PORT_ADDTAG &&
@@ -730,6 +738,22 @@ arswitch_setconf(device_t dev, etherswitch_conf_t *conf)
return (0);
}
+static int
+arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
+{
+ struct arswitch_softc *sc = device_get_softc(dev);
+
+ return (sc->hal.arswitch_vlan_getvgroup(sc, e));
+}
+
+static int
+arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
+{
+ struct arswitch_softc *sc = device_get_softc(dev);
+
+ return (sc->hal.arswitch_vlan_setvgroup(sc, e));
+}
+
static device_method_t arswitch_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, arswitch_probe),
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8327.c b/sys/dev/etherswitch/arswitch/arswitch_8327.c
new file mode 100644
index 0000000..262fd62
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8327.c
@@ -0,0 +1,481 @@
+/*-
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2014 Adrian Chadd.
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <machine/bus.h>
+#include <dev/iicbus/iic.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/etherswitch/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+
+#include <dev/etherswitch/arswitch/arswitchreg.h>
+#include <dev/etherswitch/arswitch/arswitchvar.h>
+#include <dev/etherswitch/arswitch/arswitch_reg.h>
+#include <dev/etherswitch/arswitch/arswitch_8327.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+static void
+ar8327_phy_fixup(struct arswitch_softc *sc, int phy)
+{
+
+ switch (sc->chip_rev) {
+ case 1:
+ /* For 100M waveform */
+ arswitch_writedbg(sc->sc_dev, phy, 0, 0x02ea);
+ /* Turn on Gigabit clock */
+ arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x68a0);
+ break;
+
+ case 2:
+ arswitch_writemmd(sc->sc_dev, phy, 0x7, 0x3c);
+ arswitch_writemmd(sc->sc_dev, phy, 0x4007, 0x0);
+ /* fallthrough */
+ case 4:
+ arswitch_writemmd(sc->sc_dev, phy, 0x3, 0x800d);
+ arswitch_writemmd(sc->sc_dev, phy, 0x4003, 0x803f);
+
+ arswitch_writedbg(sc->sc_dev, phy, 0x3d, 0x6860);
+ arswitch_writedbg(sc->sc_dev, phy, 0x5, 0x2c46);
+ arswitch_writedbg(sc->sc_dev, phy, 0x3c, 0x6000);
+ break;
+ }
+}
+
+static uint32_t
+ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
+{
+ uint32_t t;
+
+ if (!cfg)
+ return (0);
+
+ t = 0;
+ switch (cfg->mode) {
+ case AR8327_PAD_NC:
+ break;
+
+ case AR8327_PAD_MAC2MAC_MII:
+ t = AR8327_PAD_MAC_MII_EN;
+ if (cfg->rxclk_sel)
+ t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
+ if (cfg->txclk_sel)
+ t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
+ break;
+
+ case AR8327_PAD_MAC2MAC_GMII:
+ t = AR8327_PAD_MAC_GMII_EN;
+ if (cfg->rxclk_sel)
+ t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
+ if (cfg->txclk_sel)
+ t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
+ break;
+
+ case AR8327_PAD_MAC_SGMII:
+ t = AR8327_PAD_SGMII_EN;
+
+ /*
+ * WAR for the Qualcomm Atheros AP136 board.
+ * It seems that RGMII TX/RX delay settings needs to be
+ * applied for SGMII mode as well, The ethernet is not
+ * reliable without this.
+ */
+ t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+ t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+ if (cfg->rxclk_delay_en)
+ t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+ if (cfg->txclk_delay_en)
+ t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+
+ if (cfg->sgmii_delay_en)
+ t |= AR8327_PAD_SGMII_DELAY_EN;
+
+ break;
+
+ case AR8327_PAD_MAC2PHY_MII:
+ t = AR8327_PAD_PHY_MII_EN;
+ if (cfg->rxclk_sel)
+ t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
+ if (cfg->txclk_sel)
+ t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
+ break;
+
+ case AR8327_PAD_MAC2PHY_GMII:
+ t = AR8327_PAD_PHY_GMII_EN;
+ if (cfg->pipe_rxclk_sel)
+ t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
+ if (cfg->rxclk_sel)
+ t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
+ if (cfg->txclk_sel)
+ t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
+ break;
+
+ case AR8327_PAD_MAC_RGMII:
+ t = AR8327_PAD_RGMII_EN;
+ t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+ t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+ if (cfg->rxclk_delay_en)
+ t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+ if (cfg->txclk_delay_en)
+ t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+ break;
+
+ case AR8327_PAD_PHY_GMII:
+ t = AR8327_PAD_PHYX_GMII_EN;
+ break;
+
+ case AR8327_PAD_PHY_RGMII:
+ t = AR8327_PAD_PHYX_RGMII_EN;
+ break;
+
+ case AR8327_PAD_PHY_MII:
+ t = AR8327_PAD_PHYX_MII_EN;
+ break;
+ }
+
+ return (t);
+}
+
+/*
+ * Map the hard-coded port config from the switch setup to
+ * the chipset port config (status, duplex, flow, etc.)
+ */
+static uint32_t
+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
+{
+ uint32_t t;
+
+ if (!cfg->force_link)
+ return (AR8X16_PORT_STS_LINK_AUTO);
+
+ t = AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC;
+ t |= cfg->duplex ? AR8X16_PORT_STS_DUPLEX : 0;
+ t |= cfg->rxpause ? AR8X16_PORT_STS_RXFLOW : 0;
+ t |= cfg->txpause ? AR8X16_PORT_STS_TXFLOW : 0;
+
+ switch (cfg->speed) {
+ case AR8327_PORT_SPEED_10:
+ t |= AR8X16_PORT_STS_SPEED_10;
+ break;
+ case AR8327_PORT_SPEED_100:
+ t |= AR8X16_PORT_STS_SPEED_100;
+ break;
+ case AR8327_PORT_SPEED_1000:
+ t |= AR8X16_PORT_STS_SPEED_1000;
+ break;
+ }
+
+ return (t);
+}
+
+/*
+ * Initialise the ar8327 specific hardware features from
+ * the hints provided in the boot environment.
+ */
+static int
+ar8327_init_pdata(struct arswitch_softc *sc)
+{
+ struct ar8327_pad_cfg pc;
+ struct ar8327_port_cfg port_cfg;
+ uint32_t t;
+
+ /* XXX hard-coded DB120 defaults for now! */
+
+ /* Port 0 - rgmii; 1000/full */
+ bzero(&port_cfg, sizeof(port_cfg));
+ port_cfg.speed = AR8327_PORT_SPEED_1000;
+ port_cfg.duplex = 1;
+ port_cfg.rxpause = 1;
+ port_cfg.txpause = 1;
+ port_cfg.force_link = 1;
+ sc->ar8327.port0_status = ar8327_get_port_init_status(&port_cfg);
+
+ /* Port 6 - ignore */
+ bzero(&port_cfg, sizeof(port_cfg));
+ sc->ar8327.port6_status = ar8327_get_port_init_status(&port_cfg);
+
+ /* Pad 0 */
+ bzero(&pc, sizeof(pc));
+ pc.mode = AR8327_PAD_MAC_RGMII,
+ pc.txclk_delay_en = true,
+ pc.rxclk_delay_en = true,
+ pc.txclk_delay_sel = AR8327_CLK_DELAY_SEL1,
+ pc.rxclk_delay_sel = AR8327_CLK_DELAY_SEL2,
+
+ t = ar8327_get_pad_cfg(&pc);
+#if 0
+ if (AR8X16_IS_SWITCH(sc, AR8337))
+ t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+#endif
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PAD0_MODE, t);
+
+ /* Pad 5 */
+ bzero(&pc, sizeof(pc));
+ t = ar8327_get_pad_cfg(&pc);
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PAD5_MODE, t);
+
+ /* Pad 6 */
+ bzero(&pc, sizeof(pc));
+ t = ar8327_get_pad_cfg(&pc);
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PAD6_MODE, t);
+
+ /* XXX LED config */
+
+ /* XXX SGMII config */
+
+ return (0);
+}
+
+static int
+ar8327_hw_setup(struct arswitch_softc *sc)
+{
+ int i;
+ int err;
+
+ /* pdata fetch and setup */
+ err = ar8327_init_pdata(sc);
+ if (err != 0)
+ return (err);
+
+ /* XXX init leds */
+
+ for (i = 0; i < AR8327_NUM_PHYS; i++) {
+ /* phy fixup */
+ ar8327_phy_fixup(sc, i);
+
+ /* start PHY autonegotiation? */
+ /* XXX is this done as part of the normal PHY setup? */
+
+ };
+
+ /* Let things settle */
+ DELAY(1000);
+
+ return (0);
+}
+
+/*
+ * Initialise other global values, for the AR8327.
+ */
+static int
+ar8327_hw_global_setup(struct arswitch_softc *sc)
+{
+ uint32_t t;
+
+ /* enable CPU port and disable mirror port */
+ t = AR8327_FWD_CTRL0_CPU_PORT_EN |
+ AR8327_FWD_CTRL0_MIRROR_PORT;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL0, t);
+
+ /* forward multicast and broadcast frames to CPU */
+ t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
+ (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
+ (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
+ arswitch_writereg(sc->sc_dev, AR8327_REG_FWD_CTRL1, t);
+
+ /* enable jumbo frames */
+ /* XXX need to macro-shift the value! */
+ arswitch_modifyreg(sc->sc_dev, AR8327_REG_MAX_FRAME_SIZE,
+ AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
+
+ /* Enable MIB counters */
+ arswitch_modifyreg(sc->sc_dev, AR8327_REG_MODULE_EN,
+ AR8327_MODULE_EN_MIB, AR8327_MODULE_EN_MIB);
+
+ return (0);
+}
+
+/*
+ * Port setup.
+ */
+static void
+ar8327_port_init(struct arswitch_softc *sc, int port)
+{
+ uint32_t t;
+
+ if (port == AR8X16_PORT_CPU)
+ t = sc->ar8327.port0_status;
+ else if (port == 6)
+ t = sc->ar8327.port6_status;
+ else
+#if 0
+ /* XXX DB120 - hard-code port0 to 1000/full */
+ if (port == 0) {
+ t = AR8X16_PORT_STS_SPEED_1000;
+ t |= AR8X16_PORT_STS_TXMAC | AR8X16_PORT_STS_RXMAC;
+ t |= AR8X16_PORT_STS_DUPLEX;
+ t |= AR8X16_PORT_STS_RXFLOW;
+ t |= AR8X16_PORT_STS_TXFLOW;
+ } else
+#endif
+ t = AR8X16_PORT_STS_LINK_AUTO;
+
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_STATUS(port), t);
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_HEADER(port), 0);
+
+ t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
+ t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(port), t);
+
+ t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(port), t);
+
+ t = AR8327_PORT_LOOKUP_LEARN;
+ t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static int
+ar8327_port_vlan_setup(struct arswitch_softc *sc, etherswitch_port_t *p)
+{
+
+ /* XXX stub for now */
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+static int
+ar8327_port_vlan_get(struct arswitch_softc *sc, etherswitch_port_t *p)
+{
+
+ /* XXX stub for now */
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+static void
+ar8327_reset_vlans(struct arswitch_softc *sc)
+{
+ int i;
+ uint32_t mode, t;
+
+ /*
+ * For now, let's default to one portgroup, just so traffic
+ * flows. All ports can see other ports.
+ */
+ for (i = 0; i < AR8327_NUM_PORTS; i++) {
+ /* set pvid = i */
+ t = i << AR8327_PORT_VLAN0_DEF_SVID_S;
+ t |= i << AR8327_PORT_VLAN0_DEF_CVID_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN0(i), t);
+
+ /* set egress == out_keep */
+ mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+
+ t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
+ t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_VLAN1(i), t);
+
+ /* Set ingress = out_keep; members = 0x3f for all ports */
+
+ t = 0x3f; /* all ports */
+ t |= AR8327_PORT_LOOKUP_LEARN;
+
+ /* in_port_only, forward */
+ t |= AR8X16_PORT_VLAN_MODE_PORT_ONLY << AR8327_PORT_LOOKUP_IN_MODE_S;
+ t |= AR8X16_PORT_CTRL_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+ arswitch_writereg(sc->sc_dev, AR8327_REG_PORT_LOOKUP(i), t);
+ }
+}
+
+static int
+ar8327_vlan_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
+{
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+static int
+ar8327_vlan_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
+{
+
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+static int
+ar8327_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
+{
+
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+static int
+ar8327_set_pvid(struct arswitch_softc *sc, int port, int pvid)
+{
+
+ device_printf(sc->sc_dev, "%s: called\n", __func__);
+ return (0);
+}
+
+void
+ar8327_attach(struct arswitch_softc *sc)
+{
+
+ sc->hal.arswitch_hw_setup = ar8327_hw_setup;
+ sc->hal.arswitch_hw_global_setup = ar8327_hw_global_setup;
+
+ sc->hal.arswitch_port_init = ar8327_port_init;
+ sc->hal.arswitch_port_vlan_setup = ar8327_port_vlan_setup;
+ sc->hal.arswitch_port_vlan_get = ar8327_port_vlan_get;
+
+ sc->hal.arswitch_vlan_init_hw = ar8327_reset_vlans;
+ sc->hal.arswitch_vlan_getvgroup = ar8327_vlan_getvgroup;
+ sc->hal.arswitch_vlan_setvgroup = ar8327_vlan_setvgroup;
+ sc->hal.arswitch_vlan_get_pvid = ar8327_get_pvid;
+ sc->hal.arswitch_vlan_set_pvid = ar8327_set_pvid;
+
+ /* Set the switch vlan capabilities. */
+ sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q |
+ ETHERSWITCH_VLAN_PORT | ETHERSWITCH_VLAN_DOUBLE_TAG;
+ sc->info.es_nvlangroups = AR8X16_MAX_VLANS;
+}
diff --git a/sys/dev/etherswitch/arswitch/arswitch_8327.h b/sys/dev/etherswitch/arswitch/arswitch_8327.h
new file mode 100644
index 0000000..1f35d96
--- /dev/null
+++ b/sys/dev/etherswitch/arswitch/arswitch_8327.h
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@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$
+ */
+#ifndef __ARSWITCH_8327_H__
+#define __ARSWITCH_8327_H__
+
+enum ar8327_pad_mode {
+ AR8327_PAD_NC = 0,
+ AR8327_PAD_MAC2MAC_MII,
+ AR8327_PAD_MAC2MAC_GMII,
+ AR8327_PAD_MAC_SGMII,
+ AR8327_PAD_MAC2PHY_MII,
+ AR8327_PAD_MAC2PHY_GMII,
+ AR8327_PAD_MAC_RGMII,
+ AR8327_PAD_PHY_GMII,
+ AR8327_PAD_PHY_RGMII,
+ AR8327_PAD_PHY_MII,
+};
+
+enum ar8327_clk_delay_sel {
+ AR8327_CLK_DELAY_SEL0 = 0,
+ AR8327_CLK_DELAY_SEL1,
+ AR8327_CLK_DELAY_SEL2,
+ AR8327_CLK_DELAY_SEL3,
+};
+
+/* XXX update the field types */
+struct ar8327_pad_cfg {
+ uint32_t mode;
+ uint32_t rxclk_sel;
+ uint32_t txclk_sel;
+ uint32_t txclk_delay_sel;
+ uint32_t rxclk_delay_sel;
+ uint32_t txclk_delay_en;
+ uint32_t rxclk_delay_en;
+ uint32_t sgmii_delay_en;
+ uint32_t pipe_rxclk_sel;
+};
+
+struct ar8327_sgmii_cfg {
+ uint32_t sgmii_ctrl;
+ uint32_t serdes_aen;
+};
+
+struct ar8327_led_cfg {
+ uint32_t led_ctrl0;
+ uint32_t led_ctrl1;
+ uint32_t led_ctrl2;
+ uint32_t led_ctrl3;
+ uint32_t open_drain;
+};
+
+struct ar8327_port_cfg {
+#define AR8327_PORT_SPEED_10 1
+#define AR8327_PORT_SPEED_100 2
+#define AR8327_PORT_SPEED_1000 3
+ uint32_t speed;
+ uint32_t force_link;
+ uint32_t duplex;
+ uint32_t txpause;
+ uint32_t rxpause;
+};
+
+extern void ar8327_attach(struct arswitch_softc *sc);
+
+#endif /* __ARSWITCH_8327_H__ */
+
diff --git a/sys/dev/etherswitch/arswitch/arswitch_vlans.c b/sys/dev/etherswitch/arswitch/arswitch_vlans.c
index f981614..5e24e57 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_vlans.c
+++ b/sys/dev/etherswitch/arswitch/arswitch_vlans.c
@@ -171,7 +171,7 @@ arswitch_set_port_vlan(struct arswitch_softc *sc, uint32_t ports, int vid)
* Reset vlans to default state.
*/
void
-arswitch_reset_vlans(struct arswitch_softc *sc)
+ar8xxx_reset_vlans(struct arswitch_softc *sc)
{
uint32_t ports;
int i, j;
@@ -220,7 +220,7 @@ arswitch_reset_vlans(struct arswitch_softc *sc)
sc->vid[0] = 1;
/* Set PVID for everyone. */
for (i = 0; i <= sc->numphys; i++)
- arswitch_set_pvid(sc, i, sc->vid[0]);
+ sc->hal.arswitch_vlan_set_pvid(sc, i, sc->vid[0]);
ports = 0;
for (i = 0; i <= sc->numphys; i++)
ports |= (1 << i);
@@ -259,12 +259,10 @@ arswitch_reset_vlans(struct arswitch_softc *sc)
}
int
-arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+ar8xxx_getvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
- struct arswitch_softc *sc;
int err;
- sc = device_get_softc(dev);
ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
if (vg->es_vlangroup > sc->info.es_nvlangroups)
@@ -305,12 +303,10 @@ arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
}
int
-arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+ar8xxx_setvgroup(struct arswitch_softc *sc, etherswitch_vlangroup_t *vg)
{
- struct arswitch_softc *sc;
int err, vid;
- sc = device_get_softc(dev);
ARSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
/* Check VLAN mode. */
@@ -362,7 +358,7 @@ arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
}
int
-arswitch_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
+ar8xxx_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
{
uint32_t reg;
@@ -373,7 +369,7 @@ arswitch_get_pvid(struct arswitch_softc *sc, int port, int *pvid)
}
int
-arswitch_set_pvid(struct arswitch_softc *sc, int port, int pvid)
+ar8xxx_set_pvid(struct arswitch_softc *sc, int port, int pvid)
{
ARSWITCH_LOCK_ASSERT(sc, MA_OWNED);
diff --git a/sys/dev/etherswitch/arswitch/arswitch_vlans.h b/sys/dev/etherswitch/arswitch/arswitch_vlans.h
index e671e22..0cd1af8 100644
--- a/sys/dev/etherswitch/arswitch/arswitch_vlans.h
+++ b/sys/dev/etherswitch/arswitch/arswitch_vlans.h
@@ -29,10 +29,10 @@
#ifndef __ARSWITCH_VLANS_H__
#define __ARSWITCH_VLANS_H__
-void arswitch_reset_vlans(struct arswitch_softc *);
-int arswitch_getvgroup(device_t, etherswitch_vlangroup_t *);
-int arswitch_setvgroup(device_t, etherswitch_vlangroup_t *);
-int arswitch_get_pvid(struct arswitch_softc *, int, int *);
-int arswitch_set_pvid(struct arswitch_softc *, int, int);
+void ar8xxx_reset_vlans(struct arswitch_softc *);
+int ar8xxx_getvgroup(struct arswitch_softc *, etherswitch_vlangroup_t *);
+int ar8xxx_setvgroup(struct arswitch_softc *, etherswitch_vlangroup_t *);
+int ar8xxx_get_pvid(struct arswitch_softc *, int, int *);
+int ar8xxx_set_pvid(struct arswitch_softc *, int, int);
#endif /* __ARSWITCH_VLANS_H__ */
diff --git a/sys/dev/etherswitch/arswitch/arswitchvar.h b/sys/dev/etherswitch/arswitch/arswitchvar.h
index f79185b..414fd82 100644
--- a/sys/dev/etherswitch/arswitch/arswitchvar.h
+++ b/sys/dev/etherswitch/arswitch/arswitchvar.h
@@ -45,6 +45,9 @@ typedef enum {
#define AR8X16_IS_SWITCH(_sc, _type) \
(!!((_sc)->sc_switchtype == AR8X16_SWITCH_ ## _type))
+#define ARSWITCH_NUM_PORTS MAX(AR8327_NUM_PORTS, AR8X16_NUM_PORTS)
+#define ARSWITCH_NUM_PHYS MAX(AR8327_NUM_PHYS, AR8X16_NUM_PHYS)
+
struct arswitch_softc {
struct mtx sc_mtx; /* serialize access to softc */
device_t sc_dev;
@@ -59,9 +62,10 @@ struct arswitch_softc {
int chip_rev;
int mii_lo_first;
ar8x16_switch_type sc_switchtype;
- char *ifname[AR8X16_NUM_PHYS];
- device_t miibus[AR8X16_NUM_PHYS];
- struct ifnet *ifp[AR8X16_NUM_PHYS];
+ /* should be the max of both pre-AR8327 and AR8327 ports */
+ char *ifname[ARSWITCH_NUM_PHYS];
+ device_t miibus[ARSWITCH_NUM_PHYS];
+ struct ifnet *ifp[ARSWITCH_NUM_PHYS];
struct callout callout_tick;
etherswitch_info_t info;
@@ -82,7 +86,22 @@ struct arswitch_softc {
etherswitch_port_t *);
int (* arswitch_port_vlan_get) (struct arswitch_softc *,
etherswitch_port_t *);
+ void (* arswitch_vlan_init_hw) (struct arswitch_softc *);
+ int (* arswitch_vlan_getvgroup) (struct arswitch_softc *,
+ etherswitch_vlangroup_t *);
+ int (* arswitch_vlan_setvgroup) (struct arswitch_softc *,
+ etherswitch_vlangroup_t *);
+ int (* arswitch_vlan_get_pvid) (struct arswitch_softc *, int,
+ int *);
+ int (* arswitch_vlan_set_pvid) (struct arswitch_softc *, int,
+ int);
} hal;
+
+ struct {
+ uint32_t port0_status;
+ uint32_t port5_status;
+ uint32_t port6_status;
+ } ar8327;
};
#define ARSWITCH_LOCK(_sc) \
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c
index b2a6e99..361e4a3 100644
--- a/sys/dev/iwn/if_iwn.c
+++ b/sys/dev/iwn/if_iwn.c
@@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <dev/iwn/if_iwn_devid.h>
#include <dev/iwn/if_iwn_chip_cfg.h>
#include <dev/iwn/if_iwn_debug.h>
+#include <dev/iwn/if_iwn_ioctl.h>
struct iwn_ident {
uint16_t vendor;
@@ -3140,6 +3141,16 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc,
memcpy(sc->calibcmd[idx].buf, calib, len);
}
+static void
+iwn_stats_update(struct iwn_softc *sc, struct iwn_calib_state *calib,
+ struct iwn_stats *stats)
+{
+
+ /* XXX lock assert */
+ memcpy(&sc->last_stat, stats, sizeof(struct iwn_stats));
+ sc->last_stat_valid = 1;
+}
+
/*
* Process an RX_STATISTICS or BEACON_STATISTICS firmware notification.
* The latter is sent by the firmware after each received beacon.
@@ -3172,6 +3183,9 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
__func__, desc->type);
sc->calib_cnt = 0; /* Reset TX power calibration timeout. */
+ /* Collect/track general statistics for reporting */
+ iwn_stats_update(sc, calib, stats);
+
/* Test if temperature has changed. */
if (stats->general.temp != sc->rawtemp) {
/* Convert "raw" temperature to degC. */
@@ -4712,6 +4726,19 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
case SIOCGIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
break;
+ case SIOCGIWNSTATS:
+ IWN_LOCK(sc);
+ /* XXX validate permissions/memory/etc? */
+ error = copyout(&sc->last_stat, ifr->ifr_data,
+ sizeof(struct iwn_stats));
+ IWN_UNLOCK(sc);
+ break;
+ case SIOCZIWNSTATS:
+ IWN_LOCK(sc);
+ memset(&sc->last_stat, 0, sizeof(struct iwn_stats));
+ IWN_UNLOCK(sc);
+ error = 0;
+ break;
default:
error = EINVAL;
break;
diff --git a/sys/dev/iwn/if_iwn_debug.h b/sys/dev/iwn/if_iwn_debug.h
index 253c76d..2932c7e 100644
--- a/sys/dev/iwn/if_iwn_debug.h
+++ b/sys/dev/iwn/if_iwn_debug.h
@@ -43,6 +43,7 @@ enum {
IWN_DEBUG_TXRATE = 0x00002000, /* TX rate debugging */
IWN_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */
IWN_DEBUG_SCAN = 0x00008000, /* Scan related operations */
+ IWN_DEBUG_STATS = 0x00010000, /* Statistics updates */
IWN_DEBUG_REGISTER = 0x20000000, /* print chipset register */
IWN_DEBUG_TRACE = 0x40000000, /* Print begin and start driver function */
IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */
diff --git a/sys/dev/iwn/if_iwn_ioctl.h b/sys/dev/iwn/if_iwn_ioctl.h
new file mode 100644
index 0000000..1acf464
--- /dev/null
+++ b/sys/dev/iwn/if_iwn_ioctl.h
@@ -0,0 +1,25 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IF_IWN_IOCTL_H__
+#define __IF_IWN_IOCTL_H__
+
+/* XXX how should I pick appropriate ioctl numbers? */
+#define SIOCGIWNSTATS _IOWR('i', 145, struct ifreq)
+#define SIOCZIWNSTATS _IOWR('i', 146, struct ifreq)
+
+#endif /* __IF_IWN_IOCTL_H__ */
diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h
index f146feb..bcc01b2 100644
--- a/sys/dev/iwn/if_iwnvar.h
+++ b/sys/dev/iwn/if_iwnvar.h
@@ -328,6 +328,22 @@ struct iwn_softc {
int ctx;
struct ieee80211vap *ivap[IWN_NUM_RXON_CTX];
+ /* General statistics */
+ /*
+ * The statistics are reset after each channel
+ * change. So it may be zeroed after things like
+ * a background scan.
+ *
+ * So for now, this is just a cheap hack to
+ * expose the last received statistics dump
+ * via an ioctl(). Later versions of this
+ * could expose the last 'n' messages, or just
+ * provide a pipeline for the firmware responses
+ * via something like BPF.
+ */
+ struct iwn_stats last_stat;
+ int last_stat_valid;
+
uint8_t uc_scan_progress;
uint32_t rawtemp;
int temp;
diff --git a/sys/dev/usb/input/atp.c b/sys/dev/usb/input/atp.c
index 8a55e32..832c1e6 100644
--- a/sys/dev/usb/input/atp.c
+++ b/sys/dev/usb/input/atp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2009 Rohit Grover
+ * Copyright (c) 2014 Rohit Grover
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,29 +24,64 @@
* 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 <sys/param.h>
+#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
-#include <sys/malloc.h>
+#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
-#include <sys/bus.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 <sys/sysctl.h>
-#include <sys/uio.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
@@ -61,17 +96,35 @@ __FBSDID("$FreeBSD$");
* `options' statements in the kernel configuration file.
*/
-/* The multiplier used to translate sensor reported positions to mickeys. */
+/* The divisor used to translate sensor reported positions to mickeys. */
#ifndef ATP_SCALE_FACTOR
-#define ATP_SCALE_FACTOR 48
+#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.
+ * 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
+#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
/*
@@ -82,39 +135,40 @@ __FBSDID("$FreeBSD$");
* tap events preceding the slide for such a gesture.
*/
#ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD
-#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000
+#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000
#endif
/*
- * The device provides us only with pressure readings from an array of
- * X and Y sensors; for our algorithms, we need to interpret groups
- * (typically pairs) of X and Y readings as being related to a single
- * finger stroke. We can relate X and Y readings based on their times
- * of incidence. The coincidence window should be at least 10000us
- * since it is used against values from getmicrotime(), which has a
- * precision of around 10ms.
+ * The wait duration in ticks after losing a touch contact before
+ * zombied strokes are reaped and turned into button events.
*/
-#ifndef ATP_COINCIDENCE_THRESHOLD
-#define ATP_COINCIDENCE_THRESHOLD 40000 /* unit: microseconds */
-#if ATP_COINCIDENCE_THRESHOLD > 100000
-#error "ATP_COINCIDENCE_THRESHOLD too large"
-#endif
-#endif /* #ifndef ATP_COINCIDENCE_THRESHOLD */
+#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 wait duration (in microseconds) after losing a touch contact
- * before zombied strokes are reaped and turned into button events.
+ * 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 ATP_ZOMBIE_STROKE_REAP_WINDOW 50000
-#if ATP_ZOMBIE_STROKE_REAP_WINDOW > 100000
-#error "ATP_ZOMBIE_STROKE_REAP_WINDOW too large"
+#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
-/* end of driver specific options */
+/* 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");
+static SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB ATP");
#ifdef USB_DEBUG
enum atp_log_level {
@@ -130,12 +184,13 @@ SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RW,
static u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT;
SYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW,
- &atp_touch_timeout, 125000, "age threshold (in micros) for a touch");
+ &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_RW,
&atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD,
- "maximum time (in micros) between a double-tap");
+ "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);
@@ -143,264 +198,538 @@ SYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RW,
&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_SCALE_FACTOR >> 3;
+static u_int atp_small_movement_threshold = ATP_SMALL_MOVEMENT_THRESHOLD;
SYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RW,
- &atp_small_movement_threshold, ATP_SCALE_FACTOR >> 3,
+ &atp_small_movement_threshold, ATP_SMALL_MOVEMENT_THRESHOLD,
"the small movement black-hole for filtering noise");
-/*
- * The movement threshold for a stroke; this is the maximum difference
- * in position which will be resolved as a continuation of a stroke
- * component.
- */
-static u_int atp_max_delta_mickeys = ((3 * ATP_SCALE_FACTOR) >> 1);
-SYSCTL_UINT(_hw_usb_atp, OID_AUTO, max_delta_mickeys, CTLFLAG_RW,
- &atp_max_delta_mickeys, ((3 * ATP_SCALE_FACTOR) >> 1),
- "max. mickeys-delta which will match against an existing stroke");
+
+static u_int atp_tap_minimum = 1;
+SYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RW,
+ &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 = (ATP_SCALE_FACTOR >> 3);
+static u_int atp_slide_min_movement = 2 * ATP_SMALL_MOVEMENT_THRESHOLD;
SYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RW,
- &atp_slide_min_movement, (ATP_SCALE_FACTOR >> 3),
+ &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 = 2;
+static u_int atp_stroke_maturity_threshold = 4;
SYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RW,
- &atp_stroke_maturity_threshold, 2,
+ &atp_stroke_maturity_threshold, 4,
"the minimum age of a stroke for it to be considered mature");
-/* Accept pressure readings from sensors only if above this value. */
-static u_int atp_sensor_noise_threshold = 2;
-SYSCTL_UINT(_hw_usb_atp, OID_AUTO, sensor_noise_threshold, CTLFLAG_RW,
- &atp_sensor_noise_threshold, 2,
- "accept pressure readings from sensors only if above this value");
+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 */
+};
-/* Ignore pressure spans with cumulative press. below this value. */
-static u_int atp_pspan_min_cum_pressure = 10;
-SYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_min_cum_pressure, CTLFLAG_RW,
- &atp_pspan_min_cum_pressure, 10,
- "ignore pressure spans with cumulative press. below this value");
+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 */
+};
-/* Maximum allowed width for pressure-spans.*/
-static u_int atp_pspan_max_width = 4;
-SYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_max_width, CTLFLAG_RW,
- &atp_pspan_max_width, 4,
- "maximum allowed width (in sensors) for pressure-spans");
-
-/* We support three payload protocols */
-typedef enum {
- ATP_PROT_GEYSER1,
- ATP_PROT_GEYSER2,
- ATP_PROT_GEYSER3,
-} atp_protocol;
-
-/* Define the various flavours of devices supported by this driver. */
-enum {
- ATP_DEV_PARAMS_0,
- ATP_DEV_PARAMS_PBOOK,
- ATP_DEV_PARAMS_PBOOK_15A,
- ATP_DEV_PARAMS_PBOOK_17,
- ATP_N_DEV_PARAMS
+/* trackpad header types */
+enum fountain_geyser_trackpad_type {
+ FG_TRACKPAD_TYPE_GEYSER1,
+ FG_TRACKPAD_TYPE_GEYSER2,
+ FG_TRACKPAD_TYPE_GEYSER3,
+ FG_TRACKPAD_TYPE_GEYSER4,
};
-struct atp_dev_params {
- u_int data_len; /* for sensor data */
- u_int n_xsensors;
- u_int n_ysensors;
- atp_protocol prot;
-} atp_dev_params[ATP_N_DEV_PARAMS] = {
- [ATP_DEV_PARAMS_0] = {
- .data_len = 64,
- .n_xsensors = 20,
- .n_ysensors = 10,
- .prot = ATP_PROT_GEYSER3
+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
},
- [ATP_DEV_PARAMS_PBOOK] = {
+ [GEYSER1] = {
.data_len = 81,
.n_xsensors = 16,
.n_ysensors = 16,
- .prot = ATP_PROT_GEYSER1
+ .prot = FG_TRACKPAD_TYPE_GEYSER1
},
- [ATP_DEV_PARAMS_PBOOK_15A] = {
+ [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 = ATP_PROT_GEYSER2
+ .prot = FG_TRACKPAD_TYPE_GEYSER2
},
- [ATP_DEV_PARAMS_PBOOK_17] = {
- .data_len = 81,
- .n_xsensors = 26,
- .n_ysensors = 16,
- .prot = ATP_PROT_GEYSER1
+ [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 atp_devs[] = {
+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, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x0218, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x0219, ATP_DEV_PARAMS_0) },
+ { 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, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x021b, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x021c, ATP_DEV_PARAMS_0) },
+ { 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, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x022a, ATP_DEV_PARAMS_0) },
- { USB_VPI(USB_VENDOR_APPLE, 0x022b, ATP_DEV_PARAMS_0) },
-
- /* 12 inch PowerBook and iBook */
- { USB_VPI(USB_VENDOR_APPLE, 0x030a, ATP_DEV_PARAMS_PBOOK) },
- { USB_VPI(USB_VENDOR_APPLE, 0x030b, ATP_DEV_PARAMS_PBOOK) },
-
- /* 15 inch PowerBook */
- { USB_VPI(USB_VENDOR_APPLE, 0x020e, ATP_DEV_PARAMS_PBOOK) },
- { USB_VPI(USB_VENDOR_APPLE, 0x020f, ATP_DEV_PARAMS_PBOOK) },
- { USB_VPI(USB_VENDOR_APPLE, 0x0215, ATP_DEV_PARAMS_PBOOK_15A) },
+ { 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, ATP_DEV_PARAMS_PBOOK_17) },
+ { 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,
+ },
};
-/*
- * 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 atp_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. */
-} atp_pspan;
+#define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+
+static const STRUCT_USB_HOST_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;
-#define ATP_MAX_PSPANS_PER_AXIS 3
+typedef enum atp_axis {
+ X = 0,
+ Y = 1,
+ NUM_AXES
+} atp_axis;
-typedef struct atp_stroke_component {
+#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.*/
- /* Fields containing information about movement. */
int delta_mickeys; /* change in location (un-smoothened movement)*/
- int pending; /* cum. of pending short movements */
- int movement; /* current smoothened movement */
-} atp_stroke_component;
-
-typedef enum atp_axis {
- X = 0,
- Y = 1
-} atp_axis;
-
-#define ATP_MAX_STROKES (2 * ATP_MAX_PSPANS_PER_AXIS)
+} 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 {
- atp_stroke_type type;
- struct timeval ctime; /* create time; for coincident siblings. */
- u_int age; /*
- * Unit: interrupts; we maintain
- * this value in addition to
- * 'ctime' in order to avoid the
- * expensive call to microtime()
- * at every interrupt.
- */
-
- atp_stroke_component components[2];
- u_int velocity_squared; /*
- * Average magnitude (squared)
- * of recent velocity.
- */
- u_int cum_movement; /* cum. absolute movement so far */
-
- uint32_t flags; /* the state of this stroke */
-#define ATSF_ZOMBIE 0x1
-} atp_stroke;
+ TAILQ_ENTRY(atp_stroke) entry;
-#define ATP_FIFO_BUF_SIZE 8 /* bytes */
-#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */
+ atp_stroke_type type;
+ uint32_t flags; /* the state of this stroke */
+#define ATSF_ZOMBIE 0x1
+ boolean_t matched; /* to track match against fingers.*/
-enum {
- ATP_INTR_DT,
- ATP_RESET,
- ATP_N_TRANSFER,
-};
+ 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;
-#define MODE_LENGTH 8
- char sc_mode_bytes[MODE_LENGTH]; /* device mode */
- struct mtx sc_mutex; /* for synchronization */
- struct usb_xfer *sc_xfer[ATP_N_TRANSFER];
- struct usb_fifo_sc sc_fifo;
-
- struct atp_dev_params *sc_params;
-
- mousehw_t sc_hw;
- mousemode_t sc_mode;
- u_int sc_pollrate;
- 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
-
- u_int sc_left_margin;
- u_int sc_right_margin;
-
- atp_stroke sc_strokes[ATP_MAX_STROKES];
- u_int sc_n_strokes;
-
- int8_t *sensor_data; /* from interrupt packet */
- int *base_x; /* base sensor readings */
- int *base_y;
- int *cur_x; /* current sensor readings */
- int *cur_y;
- int *pressure_x; /* computed pressures */
- int *pressure_y;
-
- u_int sc_idlecount; /* preceding idle interrupts */
-#define ATP_IDLENESS_THRESHOLD 10
-
- struct timeval sc_reap_time;
- struct timeval sc_reap_ctime; /*ctime of siblings to be reaped*/
+ 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 sensor data contains status bits; the
+ * 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 atp_status_bits {
- ATP_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */
- ATP_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/
+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)0x04,
+ RAW_SENSOR_MODE = (uint8_t)0x01,
HID_MODE = (uint8_t)0x08
} interface_mode;
+
/*
* function prototypes
*/
@@ -420,100 +749,183 @@ static struct usb_fifo_methods atp_fifo_methods = {
};
/* device initialization and shutdown */
-static usb_error_t atp_req_get_report(struct usb_device *udev, void *data);
-static int atp_set_device_mode(device_t dev, interface_mode mode);
-static void atp_reset_callback(struct usb_xfer *, usb_error_t);
-static int atp_enable(struct atp_softc *sc);
-static void atp_disable(struct atp_softc *sc);
-static int atp_softc_populate(struct atp_softc *);
-static void atp_softc_unpopulate(struct atp_softc *);
+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 __inline void atp_interpret_sensor_data(const int8_t *, u_int, atp_axis,
- int *, atp_protocol);
-static __inline void atp_get_pressures(int *, const int *, const int *, int);
-static void atp_detect_pspans(int *, u_int, u_int, atp_pspan *,
- u_int *);
+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 atp_match_stroke_component(atp_stroke_component *,
- const atp_pspan *, atp_stroke_type);
-static void atp_match_strokes_against_pspans(struct atp_softc *,
- atp_axis, atp_pspan *, u_int, u_int);
-static boolean_t atp_update_strokes(struct atp_softc *,
- atp_pspan *, u_int, atp_pspan *, u_int);
-static __inline void atp_add_stroke(struct atp_softc *, const atp_pspan *,
- const atp_pspan *);
-static void atp_add_new_strokes(struct atp_softc *, atp_pspan *,
- u_int, atp_pspan *, u_int);
-static void atp_advance_stroke_state(struct atp_softc *,
- atp_stroke *, boolean_t *);
-static void atp_terminate_stroke(struct atp_softc *, u_int);
-static __inline boolean_t atp_stroke_has_small_movement(const atp_stroke *);
-static __inline void atp_update_pending_mickeys(atp_stroke_component *);
-static void atp_compute_smoothening_scale_ratio(atp_stroke *, int *,
- int *);
-static boolean_t atp_compute_stroke_movement(atp_stroke *);
+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 __inline void atp_setup_reap_time(struct atp_softc *, struct timeval *);
-static void atp_reap_zombies(struct atp_softc *, u_int *, u_int *);
-static void atp_convert_to_slide(struct atp_softc *, atp_stroke *);
+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 *sc);
-static void atp_add_to_queue(struct atp_softc *, int, int, uint32_t);
+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;
-usb_error_t
-atp_req_get_report(struct usb_device *udev, void *data)
+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)
{
- struct usb_device_request req;
+ atp_stroke_t *pstroke;
+
+ pstroke = TAILQ_FIRST(&sc->sc_stroke_free);
+ if (pstroke == NULL)
+ goto done;
- req.bmRequestType = UT_READ_CLASS_INTERFACE;
- req.bRequest = UR_GET_REPORT;
- USETW2(req.wValue, (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
- USETW(req.wIndex, 0);
- USETW(req.wLength, MODE_LENGTH);
+ TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry);
+ memset(pstroke, 0, sizeof(*pstroke));
+ TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry);
- return (usbd_do_request(udev, NULL /* mutex */, &req, data));
+ sc->sc_n_strokes++;
+done:
+ return (pstroke);
}
-static int
-atp_set_device_mode(device_t dev, interface_mode mode)
+static void
+atp_free_stroke(struct atp_softc *sc, atp_stroke_t *pstroke)
{
- struct atp_softc *sc;
- usb_device_request_t req;
- usb_error_t err;
+ if (pstroke == NULL)
+ return;
- if ((mode != RAW_SENSOR_MODE) && (mode != HID_MODE))
- return (ENXIO);
+ sc->sc_n_strokes--;
- sc = device_get_softc(dev);
+ TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry);
+ TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry);
+}
- sc->sc_mode_bytes[0] = mode;
- 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);
- err = usbd_do_request(sc->sc_usb_device, NULL, &req, sc->sc_mode_bytes);
- if (err != USB_ERR_NORMAL_COMPLETION)
- return (ENXIO);
+static void
+atp_init_stroke_pool(struct atp_softc *sc)
+{
+ u_int x;
- return (0);
+ 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 */));
}
-void
+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] = RAW_SENSOR_MODE;
+ sc->sc_mode_bytes[0] = mode_value;
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
req.bRequest = UR_SET_REPORT;
USETW2(req.wValue,
@@ -541,17 +953,14 @@ atp_reset_callback(struct usb_xfer *xfer, usb_error_t error)
static int
atp_enable(struct atp_softc *sc)
{
- /* Allocate the dynamic buffers */
- if (atp_softc_populate(sc) != 0) {
- atp_softc_unpopulate(sc);
- return (ENOMEM);
- }
+ if (sc->sc_state & ATP_ENABLED)
+ return (0);
/* reset status */
- memset(sc->sc_strokes, 0, sizeof(sc->sc_strokes));
- sc->sc_n_strokes = 0;
memset(&sc->sc_status, 0, sizeof(sc->sc_status));
- sc->sc_idlecount = 0;
+
+ atp_init_stroke_pool(sc);
+
sc->sc_state |= ATP_ENABLED;
DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n");
@@ -561,126 +970,94 @@ atp_enable(struct atp_softc *sc)
static void
atp_disable(struct atp_softc *sc)
{
- atp_softc_unpopulate(sc);
-
sc->sc_state &= ~(ATP_ENABLED | ATP_VALID);
DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n");
}
-/* Allocate dynamic memory for some fields in softc. */
-static int
-atp_softc_populate(struct atp_softc *sc)
+static void
+fg_interpret_sensor_data(struct atp_softc *sc, u_int data_len)
{
- const struct atp_dev_params *params = sc->sc_params;
-
- if (params == NULL) {
- DPRINTF("params uninitialized!\n");
- return (ENXIO);
- }
- if (params->data_len) {
- sc->sensor_data = malloc(params->data_len * sizeof(int8_t),
- M_USB, M_WAITOK);
- if (sc->sensor_data == NULL) {
- DPRINTF("mem for sensor_data\n");
- return (ENXIO);
- }
- }
-
- if (params->n_xsensors != 0) {
- sc->base_x = malloc(params->n_xsensors * sizeof(*(sc->base_x)),
- M_USB, M_WAITOK);
- if (sc->base_x == NULL) {
- DPRINTF("mem for sc->base_x\n");
- return (ENXIO);
- }
-
- sc->cur_x = malloc(params->n_xsensors * sizeof(*(sc->cur_x)),
- M_USB, M_WAITOK);
- if (sc->cur_x == NULL) {
- DPRINTF("mem for sc->cur_x\n");
- return (ENXIO);
- }
+ u_int n_xpspans = 0;
+ u_int n_ypspans = 0;
+ uint8_t status_bits;
- sc->pressure_x =
- malloc(params->n_xsensors * sizeof(*(sc->pressure_x)),
- M_USB, M_WAITOK);
- if (sc->pressure_x == NULL) {
- DPRINTF("mem. for pressure_x\n");
- return (ENXIO);
- }
- }
-
- if (params->n_ysensors != 0) {
- sc->base_y = malloc(params->n_ysensors * sizeof(*(sc->base_y)),
- M_USB, M_WAITOK);
- if (sc->base_y == NULL) {
- DPRINTF("mem for base_y\n");
- return (ENXIO);
- }
+ const struct fg_dev_params *params =
+ (const struct fg_dev_params *)sc->sc_params;
- sc->cur_y = malloc(params->n_ysensors * sizeof(*(sc->cur_y)),
- M_USB, M_WAITOK);
- if (sc->cur_y == NULL) {
- DPRINTF("mem for cur_y\n");
- return (ENXIO);
- }
+ 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);
- sc->pressure_y =
- malloc(params->n_ysensors * sizeof(*(sc->pressure_y)),
- M_USB, M_WAITOK);
- if (sc->pressure_y == NULL) {
- DPRINTF("mem. for pressure_y\n");
- return (ENXIO);
+ /*
+ * 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;
}
}
- return (0);
-}
+ /* 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);
-/* Free dynamic memory allocated for some fields in softc. */
-static void
-atp_softc_unpopulate(struct atp_softc *sc)
-{
- const struct atp_dev_params *params = sc->sc_params;
+ /* 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;
- if (params == NULL) {
- return;
- }
- if (params->n_xsensors != 0) {
- if (sc->base_x != NULL) {
- free(sc->base_x, M_USB);
- sc->base_x = NULL;
- }
+ sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0;
+ sc->sc_status.button = sc->sc_ibtn;
- if (sc->cur_x != NULL) {
- free(sc->cur_x, M_USB);
- sc->cur_x = NULL;
- }
+ /*
+ * 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;
- if (sc->pressure_x != NULL) {
- free(sc->pressure_x, M_USB);
- sc->pressure_x = NULL;
- }
- }
- if (params->n_ysensors != 0) {
- if (sc->base_y != NULL) {
- free(sc->base_y, M_USB);
- sc->base_y = NULL;
- }
+ DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
- if (sc->cur_y != NULL) {
- free(sc->cur_y, M_USB);
- sc->cur_y = NULL;
- }
+ 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)));
+ }
- if (sc->pressure_y != NULL) {
- free(sc->pressure_y, M_USB);
- sc->pressure_y = NULL;
+ sc->sc_idlecount = 0;
+ usbd_transfer_start(sc->sc_xfer[ATP_RESET]);
}
- }
- if (sc->sensor_data != NULL) {
- free(sc->sensor_data, M_USB);
- sc->sensor_data = NULL;
+ } else {
+ sc->sc_idlecount = 0;
}
}
@@ -710,15 +1087,15 @@ atp_softc_unpopulate(struct atp_softc *sc)
* prot
* The protocol to use to interpret the data
*/
-static __inline void
-atp_interpret_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
- int *arr, atp_protocol prot)
+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 ATP_PROT_GEYSER1:
+ case FG_TRACKPAD_TYPE_GEYSER1:
/*
* For Geyser 1, the sensors are laid out in pairs
* every 5 bytes.
@@ -726,24 +1103,33 @@ atp_interpret_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
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)
+ if ((axis == X) && (num > 16))
arr[i+16] = sensor_data[di+40];
}
break;
- case ATP_PROT_GEYSER2:
- case ATP_PROT_GEYSER3:
+ 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 __inline void
-atp_get_pressures(int *p, const int *cur, const int *base, int n)
+static void
+fg_get_pressures(int *p, const int *cur, const int *base, int n)
{
int i;
@@ -761,24 +1147,24 @@ atp_get_pressures(int *p, const int *cur, const int *base, int n)
* threshold; this will reduce the contribution from
* lower pressure readings.
*/
- if ((u_int)p[i] <= atp_sensor_noise_threshold)
+ if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD)
p[i] = 0; /* filter away noise */
else
- p[i] -= atp_sensor_noise_threshold;
+ p[i] -= FG_SENSOR_NOISE_THRESHOLD;
}
}
static void
-atp_detect_pspans(int *p, u_int num_sensors,
- u_int max_spans, /* max # of pspans permitted */
- atp_pspan *spans, /* finger spans */
- u_int *nspans_p) /* num spans detected */
+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 atp_pspan_state {
+ enum fg_pspan_state {
ATP_PSPAN_INACTIVE,
ATP_PSPAN_INCREASING,
ATP_PSPAN_DECREASING,
@@ -788,7 +1174,7 @@ atp_detect_pspans(int *p, u_int num_sensors,
* The following is a simple state machine to track
* the phase of the pressure span.
*/
- memset(spans, 0, max_spans * sizeof(atp_pspan));
+ memset(spans, 0, max_spans * sizeof(fg_pspan));
maxp = 0;
state = ATP_PSPAN_INACTIVE;
for (i = 0; i < num_sensors; i++) {
@@ -853,11 +1239,11 @@ atp_detect_pspans(int *p, u_int num_sensors,
/* post-process the spans */
for (i = 0; i < num_spans; i++) {
/* filter away unwanted pressure spans */
- if ((spans[i].cum < atp_pspan_min_cum_pressure) ||
- (spans[i].width > atp_pspan_max_width)) {
+ 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(atp_pspan));
+ (num_spans - i - 1) * sizeof(fg_pspan));
i--;
}
num_spans--;
@@ -865,32 +1251,106 @@ atp_detect_pspans(int *p, u_int num_sensors,
}
/* compute this span's representative location */
- spans[i].loc = spans[i].cog * atp_mickeys_scale_factor /
+ spans[i].loc = spans[i].cog * FG_SCALE_FACTOR /
spans[i].cum;
- spans[i].matched = FALSE; /* not yet matched against a stroke */
+ 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.
+ * match, update the component's state and return true.
*/
static boolean_t
-atp_match_stroke_component(atp_stroke_component *component,
- const atp_pspan *pspan, atp_stroke_type stroke_type)
+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 ((u_int)abs(delta_mickeys) > atp_max_delta_mickeys)
- return (FALSE); /* the finger span is too far out; no match */
+ if (abs(delta_mickeys) > (int)FG_MAX_DELTA_MICKEYS)
+ return (false); /* the finger span is too far out; no match */
- component->loc = pspan->loc;
+ component->loc = pspan->loc;
/*
* A sudden and significant increase in a pspan's cumulative
@@ -900,7 +1360,7 @@ atp_match_stroke_component(atp_stroke_component *component,
* matching stroke component(s). But such a change should
* *not* be interpreted as a movement.
*/
- if (pspan->cum > ((3 * component->cum_pressure) >> 1))
+ if (pspan->cum > ((3 * component->cum_pressure) >> 1))
delta_mickeys = 0;
component->cum_pressure = pspan->cum;
@@ -920,72 +1380,126 @@ atp_match_stroke_component(atp_stroke_component *component,
delta_mickeys = 0;
component->delta_mickeys = delta_mickeys;
- return (TRUE);
+ return (true);
}
static void
-atp_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
- atp_pspan *pspans, u_int n_pspans, u_int repeat_count)
+fg_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
+ fg_pspan *pspans, u_int n_pspans, u_int repeat_count)
{
- u_int i, j;
+ atp_stroke_t *strokep;
u_int repeat_index = 0;
+ u_int i;
/* Determine the index of the multi-span. */
if (repeat_count) {
- u_int cum = 0;
for (i = 0; i < n_pspans; i++) {
- if (pspans[i].cum > cum) {
+ if (pspans[i].cum > pspans[repeat_index].cum)
repeat_index = i;
- cum = pspans[i].cum;
- }
}
}
- for (i = 0; i < sc->sc_n_strokes; i++) {
- atp_stroke *stroke = &sc->sc_strokes[i];
- if (stroke->components[axis].matched)
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ if (strokep->components[axis].matched)
continue; /* skip matched components */
- for (j = 0; j < n_pspans; j++) {
- if (pspans[j].matched)
+ for (i = 0; i < n_pspans; i++) {
+ if (pspans[i].matched)
continue; /* skip matched pspans */
- if (atp_match_stroke_component(
- &stroke->components[axis], &pspans[j],
- stroke->type)) {
+ if (fg_match_stroke_component(
+ &strokep->components[axis], &pspans[i],
+ strokep->type)) {
+
/* There is a match. */
- stroke->components[axis].matched = TRUE;
+ strokep->components[axis].matched = true;
/* Take care to repeat at the multi-span. */
- if ((repeat_count > 0) && (j == repeat_index))
+ if ((repeat_count > 0) && (i == repeat_index))
repeat_count--;
else
- pspans[j].matched = TRUE;
+ pspans[i].matched = true;
- break; /* skip to the next stroke */
+ 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.
+ * Return true if any movement is detected.
*/
static boolean_t
-atp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
- u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
+fg_update_strokes(struct atp_softc *sc, fg_pspan *pspans_x,
+ u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans)
{
- u_int i, j;
- atp_stroke *stroke;
- boolean_t movement = FALSE;
- u_int repeat_count = 0;
+ 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. */
- for (i = 0; i < sc->sc_n_strokes; i++) {
- stroke = &sc->sc_strokes[i];
- stroke->components[X].matched = FALSE;
- stroke->components[Y].matched = FALSE;
+ TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) {
+ strokep->components[X].matched = false;
+ strokep->components[Y].matched = false;
}
/*
@@ -1024,35 +1538,40 @@ atp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
*/
repeat_count = abs(n_xpspans - n_ypspans);
- atp_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans,
+ fg_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans,
(((repeat_count != 0) && ((n_xpspans < n_ypspans))) ?
repeat_count : 0));
- atp_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans,
+ 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. */
- for (i = 0; i < sc->sc_n_strokes; i++) {
- stroke = &sc->sc_strokes[i];
- if (stroke->components[X].matched &&
- stroke->components[Y].matched) {
- atp_advance_stroke_state(sc, stroke, &movement);
+ 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, i);
+ 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;
+ if (pspans_x[i].matched == false) break;
}
for (j = 0; j < n_ypspans; j++) {
- if (pspans_y[j].matched == FALSE) break;
+ if (pspans_y[j].matched == false) break;
}
if ((i < n_xpspans) && (j < n_ypspans)) {
#ifdef USB_DEBUG
@@ -1075,109 +1594,135 @@ atp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
#endif /* USB_DEBUG */
if ((n_xpspans == 1) && (n_ypspans == 1))
/* The common case of a single pair of new pspans. */
- atp_add_stroke(sc, &pspans_x[0], &pspans_y[0]);
+ fg_add_stroke(sc, &pspans_x[0], &pspans_y[0]);
else
- atp_add_new_strokes(sc,
- pspans_x, n_xpspans,
+ fg_add_new_strokes(sc, pspans_x, n_xpspans,
pspans_y, n_ypspans);
}
#ifdef USB_DEBUG
if (atp_debug >= ATP_LLEVEL_INFO) {
- for (i = 0; i < sc->sc_n_strokes; i++) {
- atp_stroke *stroke = &sc->sc_strokes[i];
-
- printf(" %s%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c"
- ",%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c",
- (stroke->flags & ATSF_ZOMBIE) ? "zomb:" : "",
- (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
- stroke->components[X].loc,
- stroke->components[X].delta_mickeys,
- stroke->components[X].pending,
- stroke->components[X].cum_pressure,
- stroke->components[X].max_cum_pressure,
- stroke->components[X].movement,
- (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>',
- (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
- stroke->components[Y].loc,
- stroke->components[Y].delta_mickeys,
- stroke->components[Y].pending,
- stroke->components[Y].cum_pressure,
- stroke->components[Y].max_cum_pressure,
- stroke->components[Y].movement,
- (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>');
+ 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 (sc->sc_n_strokes)
+ 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 __inline void
-atp_add_stroke(struct atp_softc *sc, const atp_pspan *pspan_x,
- const atp_pspan *pspan_y)
+static void
+fg_add_stroke(struct atp_softc *sc, const fg_pspan *pspan_x,
+ const fg_pspan *pspan_y)
{
- atp_stroke *stroke;
+ atp_stroke_t *strokep;
- if (sc->sc_n_strokes >= ATP_MAX_STROKES)
+ strokep = atp_alloc_stroke(sc);
+ if (strokep == NULL)
return;
- stroke = &sc->sc_strokes[sc->sc_n_strokes];
-
- memset(stroke, 0, sizeof(atp_stroke));
/*
* 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'.
*/
- stroke->type = ATP_STROKE_TOUCH;
- microtime(&stroke->ctime);
- stroke->age = 1; /* Unit: interrupts */
+ 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;
- stroke->components[X].loc = pspan_x->loc;
- stroke->components[X].cum_pressure = pspan_x->cum;
- stroke->components[X].max_cum_pressure = pspan_x->cum;
- stroke->components[X].matched = TRUE;
-
- stroke->components[Y].loc = pspan_y->loc;
- stroke->components[Y].cum_pressure = pspan_y->cum;
- stroke->components[Y].max_cum_pressure = pspan_y->cum;
- stroke->components[Y].matched = TRUE;
-
- sc->sc_n_strokes++;
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",
- stroke->components[X].loc,
- stroke->components[Y].loc,
- (unsigned int)stroke->ctime.tv_sec,
- (unsigned long int)stroke->ctime.tv_usec);
+ strokep->components[X].loc,
+ strokep->components[Y].loc,
+ (u_int)strokep->ctime.tv_sec,
+ (unsigned long int)strokep->ctime.tv_usec);
}
static void
-atp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
- u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
+fg_add_new_strokes(struct atp_softc *sc, fg_pspan *pspans_x,
+ u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans)
{
- atp_pspan spans[2][ATP_MAX_PSPANS_PER_AXIS];
+ 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) {
+ 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) {
+ if (pspans_y[j].matched == false) {
spans[Y][nspans[Y]] = pspans_y[j];
nspans[Y]++;
}
@@ -1186,7 +1731,7 @@ atp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
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++)
- atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
+ fg_add_stroke(sc, &spans[X][i], &spans[Y][j]);
} else {
u_int cum = 0;
atp_axis repeat_axis; /* axis with multi-pspans */
@@ -1205,7 +1750,7 @@ atp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
/* Create new strokes from pairs of unmatched pspans */
i = 0, j = 0;
for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
- atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
+ fg_add_stroke(sc, &spans[X][i], &spans[Y][j]);
/* Take care to repeat at the multi-pspan. */
if (repeat_count > 0) {
@@ -1223,413 +1768,390 @@ atp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
}
}
-/*
- * Advance the state of this stroke--and update the out-parameter
- * 'movement' as a side-effect.
- */
-void
-atp_advance_stroke_state(struct atp_softc *sc, atp_stroke *stroke,
- boolean_t *movement)
+/* Initialize a stroke from an unmatched finger. */
+static void
+wsp_add_stroke(struct atp_softc *sc, const wsp_finger_t *fingerp)
{
- stroke->age++;
- if (stroke->age <= atp_stroke_maturity_threshold) {
- /* Avoid noise from immature strokes. */
- stroke->components[X].delta_mickeys = 0;
- stroke->components[Y].delta_mickeys = 0;
- }
+ atp_stroke_t *strokep;
- /* Revitalize stroke if it had previously been marked as a zombie. */
- if (stroke->flags & ATSF_ZOMBIE)
- stroke->flags &= ~ATSF_ZOMBIE;
-
- if (atp_compute_stroke_movement(stroke))
- *movement = TRUE;
-
- if (stroke->type != ATP_STROKE_TOUCH)
+ strokep = atp_alloc_stroke(sc);
+ if (strokep == NULL)
return;
- /* Convert touch strokes to slides upon detecting movement or age. */
- if (stroke->cum_movement >= atp_slide_min_movement) {
- atp_convert_to_slide(sc, stroke);
- } else {
- /* If a touch stroke is found to be older than the
- * touch-timeout threshold, it should be converted to
- * a slide; except if there is a co-incident sibling
- * with a later creation time.
- *
- * When multiple fingers make contact with the
- * touchpad, they are likely to be separated in their
- * times of incidence. During a multi-finger tap,
- * therefore, the last finger to make
- * contact--i.e. the one with the latest
- * 'ctime'--should be used to determine how the
- * touch-siblings get treated; otherwise older
- * siblings may lapse the touch-timeout and get
- * converted into slides prematurely. The following
- * loop determines if there exists another touch
- * stroke with a larger 'ctime' than the current
- * stroke (NOTE: zombies with a larger 'ctime' are
- * also considered) .
- */
-
- u_int i;
- for (i = 0; i < sc->sc_n_strokes; i++) {
- if ((&sc->sc_strokes[i] == stroke) ||
- (sc->sc_strokes[i].type != ATP_STROKE_TOUCH))
- continue;
-
- if (timevalcmp(&sc->sc_strokes[i].ctime,
- &stroke->ctime, >))
- break;
- }
- if (i == sc->sc_n_strokes) {
- /* Found no other touch stroke with a larger 'ctime'. */
- struct timeval tdiff;
-
- /* Compute the stroke's age. */
- getmicrotime(&tdiff);
- if (timevalcmp(&tdiff, &stroke->ctime, >))
- timevalsub(&tdiff, &stroke->ctime);
- else {
- /*
- * If we are here, it is because getmicrotime
- * reported the current time as being behind
- * the stroke's start time; getmicrotime can
- * be imprecise.
- */
- tdiff.tv_sec = 0;
- tdiff.tv_usec = 0;
- }
+ /*
+ * 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;
- 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, stroke);
- }
- }
+ DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y);
}
-/* Switch a given touch stroke to being a slide. */
-void
-atp_convert_to_slide(struct atp_softc *sc, atp_stroke *stroke)
+static void
+atp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep,
+ boolean_t *movementp)
{
- stroke->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(&stroke->ctime, &sc->sc_reap_time, >)) {
- struct timeval delta;
- struct timeval window = {
- atp_double_tap_threshold / 1000000,
- atp_double_tap_threshold % 1000000
- };
+ /* Revitalize stroke if it had previously been marked as a zombie. */
+ if (strokep->flags & ATSF_ZOMBIE)
+ strokep->flags &= ~ATSF_ZOMBIE;
- delta = stroke->ctime;
- timevalsub(&delta, &sc->sc_reap_time);
- if (timevalcmp(&delta, &window, <=))
- sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
+ strokep->age++;
+ if (strokep->age <= atp_stroke_maturity_threshold) {
+ /* Avoid noise from immature strokes. */
+ strokep->instantaneous_dx = 0;
+ strokep->instantaneous_dy = 0;
}
-}
-/*
- * Terminate a stroke. While SLIDE strokes are dropped, TOUCH strokes
- * are retained as zombies so as to reap all their siblings together;
- * this helps establish the number of fingers involved in the tap.
- */
-static void
-atp_terminate_stroke(struct atp_softc *sc,
- u_int index) /* index of the stroke to be terminated */
-{
- atp_stroke *s = &sc->sc_strokes[index];
+ if (atp_compute_stroke_movement(strokep))
+ *movementp = true;
- if (s->flags & ATSF_ZOMBIE) {
+ if (strokep->type != ATP_STROKE_TOUCH)
return;
- }
- if ((s->type == ATP_STROKE_TOUCH) &&
- (s->age > atp_stroke_maturity_threshold)) {
- s->flags |= ATSF_ZOMBIE;
+ /* 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 no zombies exist, then prepare to reap zombies later. */
- if ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) {
- atp_setup_reap_time(sc, &s->ctime);
- sc->sc_state |= ATP_ZOMBIES_EXIST;
+ 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);
}
- } else {
- /* Drop this stroke. */
- memcpy(&sc->sc_strokes[index], &sc->sc_strokes[index + 1],
- (sc->sc_n_strokes - index - 1) * sizeof(atp_stroke));
- sc->sc_n_strokes--;
-
- /*
- * Reset the double-click-n-drag at the termination of
- * any slide stroke.
- */
- sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
}
}
-static __inline boolean_t
-atp_stroke_has_small_movement(const atp_stroke *stroke)
+static boolean_t
+atp_stroke_has_small_movement(const atp_stroke_t *strokep)
{
- return (((u_int)abs(stroke->components[X].delta_mickeys) <=
- atp_small_movement_threshold) &&
- ((u_int)abs(stroke->components[Y].delta_mickeys) <=
- atp_small_movement_threshold));
+ return (((u_int)abs(strokep->instantaneous_dx) <=
+ atp_small_movement_threshold) &&
+ ((u_int)abs(strokep->instantaneous_dy) <=
+ atp_small_movement_threshold));
}
/*
- * Accumulate delta_mickeys into the component's 'pending' bucket; if
+ * Accumulate instantaneous changes into the stroke's 'pending' bucket; if
* the aggregate exceeds the small_movement_threshold, then retain
- * delta_mickeys for later.
+ * instantaneous changes for later.
*/
-static __inline void
-atp_update_pending_mickeys(atp_stroke_component *component)
-{
- component->pending += component->delta_mickeys;
- if ((u_int)abs(component->pending) <= atp_small_movement_threshold)
- component->delta_mickeys = 0;
- else {
- /*
- * Penalise pending mickeys for having accumulated
- * over short deltas. This operation has the effect of
- * scaling down the cumulative contribution of short
- * movements.
- */
- component->pending -= (component->delta_mickeys << 1);
- }
-}
-
-
static void
-atp_compute_smoothening_scale_ratio(atp_stroke *stroke, int *numerator,
- int *denominator)
+atp_update_pending_mickeys(atp_stroke_t *strokep)
{
- int dxdt;
- int dydt;
- u_int vel_squared; /* Square of the velocity vector's magnitude. */
- u_int vel_squared_smooth;
-
- /* Table holding (10 * sqrt(x)) for x between 1 and 256. */
- static uint8_t sqrt_table[256] = {
- 10, 14, 17, 20, 22, 24, 26, 28,
- 30, 31, 33, 34, 36, 37, 38, 40,
- 41, 42, 43, 44, 45, 46, 47, 48,
- 50, 50, 51, 52, 53, 54, 55, 56,
- 57, 58, 59, 60, 60, 61, 62, 63,
- 64, 64, 65, 66, 67, 67, 68, 69,
- 70, 70, 71, 72, 72, 73, 74, 74,
- 75, 76, 76, 77, 78, 78, 79, 80,
- 80, 81, 81, 82, 83, 83, 84, 84,
- 85, 86, 86, 87, 87, 88, 88, 89,
- 90, 90, 91, 91, 92, 92, 93, 93,
- 94, 94, 95, 95, 96, 96, 97, 97,
- 98, 98, 99, 100, 100, 100, 101, 101,
- 102, 102, 103, 103, 104, 104, 105, 105,
- 106, 106, 107, 107, 108, 108, 109, 109,
- 110, 110, 110, 111, 111, 112, 112, 113,
- 113, 114, 114, 114, 115, 115, 116, 116,
- 117, 117, 117, 118, 118, 119, 119, 120,
- 120, 120, 121, 121, 122, 122, 122, 123,
- 123, 124, 124, 124, 125, 125, 126, 126,
- 126, 127, 127, 128, 128, 128, 129, 129,
- 130, 130, 130, 131, 131, 131, 132, 132,
- 133, 133, 133, 134, 134, 134, 135, 135,
- 136, 136, 136, 137, 137, 137, 138, 138,
- 138, 139, 139, 140, 140, 140, 141, 141,
- 141, 142, 142, 142, 143, 143, 143, 144,
- 144, 144, 145, 145, 145, 146, 146, 146,
- 147, 147, 147, 148, 148, 148, 149, 149,
- 150, 150, 150, 150, 151, 151, 151, 152,
- 152, 152, 153, 153, 153, 154, 154, 154,
- 155, 155, 155, 156, 156, 156, 157, 157,
- 157, 158, 158, 158, 159, 159, 159, 160
- };
- const u_int N = sizeof(sqrt_table) / sizeof(sqrt_table[0]);
-
- dxdt = stroke->components[X].delta_mickeys;
- dydt = stroke->components[Y].delta_mickeys;
-
- *numerator = 0, *denominator = 0; /* default values. */
-
- /* Compute a smoothened magnitude_squared of the stroke's velocity. */
- vel_squared = dxdt * dxdt + dydt * dydt;
- vel_squared_smooth = (3 * stroke->velocity_squared + vel_squared) >> 2;
- stroke->velocity_squared = vel_squared_smooth; /* retained as history */
- if ((vel_squared == 0) || (vel_squared_smooth == 0))
- return; /* returning (numerator == 0) will imply zero movement*/
-
- /*
- * In order to determine the overall movement scale factor,
- * we're actually interested in the effect of smoothening upon
- * the *magnitude* of velocity; i.e. we need to compute the
- * square-root of (vel_squared_smooth / vel_squared) in the
- * form of a numerator and denominator.
- */
-
- /* Keep within the bounds of the square-root table. */
- while ((vel_squared > N) || (vel_squared_smooth > N)) {
- /* Dividing uniformly by 2 won't disturb the final ratio. */
- vel_squared >>= 1;
- vel_squared_smooth >>= 1;
+ /* 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); \
+ } \
}
- *numerator = sqrt_table[vel_squared_smooth - 1];
- *denominator = sqrt_table[vel_squared - 1];
+ 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
- * delta_mickeys in the X and Y components.
+ * instantaneous changes in the X and Y components.
*/
static boolean_t
-atp_compute_stroke_movement(atp_stroke *stroke)
+atp_compute_stroke_movement(atp_stroke_t *strokep)
{
- int num; /* numerator of scale ratio */
- int denom; /* denominator of scale ratio */
-
/*
* 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(stroke)) {
- atp_update_pending_mickeys(&stroke->components[X]);
- atp_update_pending_mickeys(&stroke->components[Y]);
- } else { /* large movement */
+ 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*/
- stroke->components[X].pending = 0;
- stroke->components[Y].pending = 0;
+ strokep->pending_dx = 0;
+ strokep->pending_dy = 0;
}
- /* Get the scale ratio and smoothen movement. */
- atp_compute_smoothening_scale_ratio(stroke, &num, &denom);
- if ((num == 0) || (denom == 0)) {
- stroke->components[X].movement = 0;
- stroke->components[Y].movement = 0;
- stroke->velocity_squared >>= 1; /* Erode velocity_squared. */
- } else {
- stroke->components[X].movement =
- (stroke->components[X].delta_mickeys * num) / denom;
- stroke->components[Y].movement =
- (stroke->components[Y].delta_mickeys * num) / denom;
-
- stroke->cum_movement +=
- abs(stroke->components[X].movement) +
- abs(stroke->components[Y].movement);
+ /* 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;
}
- return ((stroke->components[X].movement != 0) ||
- (stroke->components[Y].movement != 0));
+ strokep->cum_movement_x += strokep->movement_dx;
+ strokep->cum_movement_y += strokep->movement_dy;
+
+ return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0));
}
-static __inline void
-atp_setup_reap_time(struct atp_softc *sc, struct timeval *tvp)
+/*
+ * 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)
{
- struct timeval reap_window = {
- ATP_ZOMBIE_STROKE_REAP_WINDOW / 1000000,
- ATP_ZOMBIE_STROKE_REAP_WINDOW % 1000000
- };
+ 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;
- microtime(&sc->sc_reap_time);
- timevaladd(&sc->sc_reap_time, &reap_window);
+ callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL,
+ atp_reap_sibling_zombies, sc);
- sc->sc_reap_ctime = *tvp; /* ctime to reap */
+ /*
+ * 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 void
-atp_reap_zombies(struct atp_softc *sc, u_int *n_reaped, u_int *reaped_xlocs)
+static boolean_t
+atp_is_horizontal_scroll(const atp_stroke_t *strokep)
{
- u_int i;
- atp_stroke *stroke;
+ 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);
+}
- *n_reaped = 0;
- for (i = 0; i < sc->sc_n_strokes; i++) {
- struct timeval tdiff;
+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;
- stroke = &sc->sc_strokes[i];
+ DPRINTFN(ATP_LLEVEL_INFO, "\n");
- if ((stroke->flags & ATSF_ZOMBIE) == 0)
+ TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) {
+ if ((strokep->flags & ATSF_ZOMBIE) == 0)
continue;
- /* Compare this stroke's ctime with the ctime being reaped. */
- if (timevalcmp(&stroke->ctime, &sc->sc_reap_ctime, >=)) {
- tdiff = stroke->ctime;
- timevalsub(&tdiff, &sc->sc_reap_ctime);
+ if (strokep->type == ATP_STROKE_TOUCH) {
+ n_touches_reaped++;
} else {
- tdiff = sc->sc_reap_ctime;
- timevalsub(&tdiff, &stroke->ctime);
- }
-
- if ((tdiff.tv_sec > (ATP_COINCIDENCE_THRESHOLD / 1000000)) ||
- ((tdiff.tv_sec == (ATP_COINCIDENCE_THRESHOLD / 1000000)) &&
- (tdiff.tv_usec > (ATP_COINCIDENCE_THRESHOLD % 1000000)))) {
- continue; /* Skip non-siblings. */
+ 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;
+ }
}
- /*
- * Reap this sibling zombie stroke.
- */
+ atp_free_stroke(sc, strokep);
+ }
- if (reaped_xlocs != NULL)
- reaped_xlocs[*n_reaped] = stroke->components[X].loc;
+ DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n",
+ n_touches_reaped + n_slides_reaped);
+ sc->sc_state &= ~ATP_ZOMBIES_EXIST;
- /* Erase the stroke from the sc. */
- memcpy(&stroke[i], &stroke[i + 1],
- (sc->sc_n_strokes - i - 1) * sizeof(atp_stroke));
- sc->sc_n_strokes--;
+ /* No further processing necessary if physical button is depressed. */
+ if (sc->sc_ibtn != 0)
+ return;
- *n_reaped += 1;
- --i; /* Decr. i to keep it unchanged for the next iteration */
- }
+ if ((n_touches_reaped == 0) && (n_slides_reaped == 0))
+ return;
- DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", *n_reaped);
+ /* 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;
- /* There could still be zombies remaining in the system. */
- for (i = 0; i < sc->sc_n_strokes; i++) {
- stroke = &sc->sc_strokes[i];
- if (stroke->flags & ATSF_ZOMBIE) {
- DPRINTFN(ATP_LLEVEL_INFO, "zombies remain!\n");
- atp_setup_reap_time(sc, &stroke->ctime);
+ 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 */
}
+}
- /* If we reach here, then no more zombies remain. */
- sc->sc_state &= ~ATP_ZOMBIES_EXIST;
+/* 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]);
+}
-/* 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 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];
-static const struct usb_config atp_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 = 0, /* use wMaxPacketSize */
- .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 */
- },
-};
+ 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)
@@ -1639,25 +2161,58 @@ atp_probe(device_t self)
if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
- if ((uaa->info.bInterfaceClass != UICLASS_HID) ||
- (uaa->info.bInterfaceProtocol != UIPROTO_MOUSE))
+ 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);
- return (usbd_lookup_id_by_uaa(atp_devs, sizeof(atp_devs), uaa));
+ 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 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
@@ -1665,41 +2220,51 @@ atp_attach(device_t dev)
* 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; but first the mode must be read.
+ * control commands.
*/
- err = atp_req_get_report(sc->sc_usb_device, sc->sc_mode_bytes);
- if (err != USB_ERR_NORMAL_COMPLETION) {
- DPRINTF("failed to read device mode (%d)\n", err);
- return (ENXIO);
- }
-
- if (atp_set_device_mode(dev, RAW_SENSOR_MODE) != 0) {
+ 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_config,
+ &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)) {
+ &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_params = &atp_dev_params[uaa->driver_info];
-
sc->sc_hw.buttons = 3;
sc->sc_hw.iftype = MOUSE_IF_USB;
sc->sc_hw.type = MOUSE_PAD;
@@ -1708,17 +2273,16 @@ atp_attach(device_t dev)
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;
+ sc->sc_mode.accelfactor = 0;
+ sc->sc_mode.level = 0;
sc->sc_state = 0;
+ sc->sc_ibtn = 0;
- sc->sc_left_margin = atp_mickeys_scale_factor;
- sc->sc_right_margin = (sc->sc_params->n_xsensors - 1) *
- atp_mickeys_scale_factor;
+ callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0);
return (0);
@@ -1733,11 +2297,13 @@ atp_detach(device_t dev)
struct atp_softc *sc;
sc = device_get_softc(dev);
- if (sc->sc_state & ATP_ENABLED) {
- mtx_lock(&sc->sc_mutex);
+ 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);
- }
+ mtx_unlock(&sc->sc_mutex);
usb_fifo_detach(&sc->sc_fifo);
@@ -1752,92 +2318,27 @@ static void
atp_intr(struct usb_xfer *xfer, usb_error_t error)
{
struct atp_softc *sc = usbd_xfer_softc(xfer);
- int len;
struct usb_page_cache *pc;
- uint8_t status_bits;
- atp_pspan pspans_x[ATP_MAX_PSPANS_PER_AXIS];
- atp_pspan pspans_y[ATP_MAX_PSPANS_PER_AXIS];
- u_int n_xpspans = 0, n_ypspans = 0;
- u_int reaped_xlocs[ATP_MAX_STROKES];
- u_int tap_fingers = 0;
+ int len;
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
- if (len > (int)sc->sc_params->data_len) {
- DPRINTFN(ATP_LLEVEL_ERROR,
- "truncating large packet from %u to %u bytes\n",
- len, sc->sc_params->data_len);
- len = sc->sc_params->data_len;
- }
- if (len < (int)sc->sc_params->data_len)
- goto tr_setup;
-
pc = usbd_xfer_get_frame(xfer, 0);
- usbd_copy_out(pc, 0, sc->sensor_data, sc->sc_params->data_len);
-
- /* Interpret sensor data */
- atp_interpret_sensor_data(sc->sensor_data,
- sc->sc_params->n_xsensors, X, sc->cur_x,
- sc->sc_params->prot);
- atp_interpret_sensor_data(sc->sensor_data,
- sc->sc_params->n_ysensors, Y, sc->cur_y,
- sc->sc_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->sensor_data[sc->sc_params->data_len - 1];
- if ((sc->sc_params->prot == ATP_PROT_GEYSER3 &&
- (status_bits & ATP_STATUS_BASE_UPDATE)) ||
- !(sc->sc_state & ATP_VALID)) {
- memcpy(sc->base_x, sc->cur_x,
- sc->sc_params->n_xsensors * sizeof(*(sc->base_x)));
- memcpy(sc->base_y, sc->cur_y,
- sc->sc_params->n_ysensors * sizeof(*(sc->base_y)));
- sc->sc_state |= ATP_VALID;
- goto tr_setup;
+ 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);
}
- /* Get pressure readings and detect p-spans for both axes. */
- atp_get_pressures(sc->pressure_x, sc->cur_x, sc->base_x,
- sc->sc_params->n_xsensors);
- atp_detect_pspans(sc->pressure_x, sc->sc_params->n_xsensors,
- ATP_MAX_PSPANS_PER_AXIS,
- pspans_x, &n_xpspans);
- atp_get_pressures(sc->pressure_y, sc->cur_y, sc->base_y,
- sc->sc_params->n_ysensors);
- atp_detect_pspans(sc->pressure_y, sc->sc_params->n_ysensors,
- ATP_MAX_PSPANS_PER_AXIS,
- pspans_y, &n_ypspans);
-
- /* Update strokes with new pspans to detect movements. */
- sc->sc_status.flags &= ~MOUSE_POSCHANGED;
- if (atp_update_strokes(sc,
- pspans_x, n_xpspans,
- pspans_y, n_ypspans))
- sc->sc_status.flags |= MOUSE_POSCHANGED;
-
- /* Reap zombies if it is time. */
- if (sc->sc_state & ATP_ZOMBIES_EXIST) {
- struct timeval now;
-
- getmicrotime(&now);
- if (timevalcmp(&now, &sc->sc_reap_time, >=))
- atp_reap_zombies(sc, &tap_fingers,
- reaped_xlocs);
- }
-
- sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
+ sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED |
+ MOUSE_POSCHANGED);
sc->sc_status.obutton = sc->sc_status.button;
- /* Get the state of the physical buttton. */
- sc->sc_status.button = (status_bits & ATP_STATUS_BUTTON) ?
- MOUSE_BUTTON1DOWN : 0;
+ (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;
@@ -1847,108 +2348,67 @@ atp_intr(struct usb_xfer *xfer, usb_error_t error)
}
sc->sc_status.flags |=
- sc->sc_status.button ^ sc->sc_status.obutton;
+ 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"));
- } else if ((sc->sc_status.obutton == 0) &&
- (sc->sc_status.button == 0) &&
- (tap_fingers != 0)) {
- /* Ignore single-finger taps at the edges. */
- if ((tap_fingers == 1) &&
- ((reaped_xlocs[0] <= sc->sc_left_margin) ||
- (reaped_xlocs[0] > sc->sc_right_margin))) {
- tap_fingers = 0;
- }
- DPRINTFN(ATP_LLEVEL_INFO,
- "tap_fingers: %u\n", tap_fingers);
+ DPRINTFN(ATP_LLEVEL_INFO, "button %s\n",
+ ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ?
+ "pressed" : "released"));
}
- if (sc->sc_status.flags &
- (MOUSE_POSCHANGED | MOUSE_STDBUTTONSCHANGED)) {
- int dx, dy;
- u_int n_movements;
+ if (sc->sc_status.flags & (MOUSE_POSCHANGED |
+ MOUSE_STDBUTTONSCHANGED)) {
- dx = 0, dy = 0, n_movements = 0;
- for (u_int i = 0; i < sc->sc_n_strokes; i++) {
- atp_stroke *stroke = &sc->sc_strokes[i];
+ 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;
- if ((stroke->components[X].movement) ||
- (stroke->components[Y].movement)) {
- dx += stroke->components[X].movement;
- dy += stroke->components[Y].movement;
+ dx += strokep->movement_dx;
+ dy += strokep->movement_dy;
+ if (strokep->movement_dx ||
+ strokep->movement_dy)
n_movements++;
- }
}
- /*
- * Disregard movement if multiple
- * strokes record motion.
- */
- if (n_movements != 1)
- dx = 0, dy = 0;
-
- sc->sc_status.dx += dx;
- sc->sc_status.dy += dy;
- atp_add_to_queue(sc, dx, -dy, sc->sc_status.button);
- }
- if (tap_fingers != 0) {
- /* Add a pair of events (button-down and button-up). */
- switch (tap_fingers) {
- case 1: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON1DOWN);
- break;
- case 2: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON2DOWN);
- break;
- case 3: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON3DOWN);
- break;
- default: break;/* handle taps of only up to 3 fingers */
+ /* average movement if multiple strokes record motion.*/
+ if (n_movements > 1) {
+ dx /= (int)n_movements;
+ dy /= (int)n_movements;
}
- atp_add_to_queue(sc, 0, 0, 0); /* button release */
- }
- /*
- * The 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_status.button == 0)) {
- sc->sc_idlecount++;
- if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
- DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
+ /* 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;
- /*
- * Use the last frame before we go idle for
- * calibration on pads which do not send
- * calibration frames.
- */
- if (sc->sc_params->prot < ATP_PROT_GEYSER3) {
- memcpy(sc->base_x, sc->cur_x,
- sc->sc_params->n_xsensors *
- sizeof(*(sc->base_x)));
- memcpy(sc->base_y, sc->cur_y,
- sc->sc_params->n_ysensors *
- sizeof(*(sc->base_y)));
+ if (!atp_is_vertical_scroll(strokep))
+ all_vertical_scrolls = false;
+ }
+ if (all_vertical_scrolls) {
+ dz = dy;
+ dy = dx = 0;
}
-
- sc->sc_idlecount = 0;
- usbd_transfer_start(sc->sc_xfer[ATP_RESET]);
}
- } else {
- sc->sc_idlecount = 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) {
+ if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
usbd_xfer_set_frame_len(xfer, 0,
- sc->sc_params->data_len);
+ sc->sc_expected_sensor_data_len);
usbd_transfer_submit(xfer);
}
break;
@@ -1961,53 +2421,6 @@ atp_intr(struct usb_xfer *xfer, usb_error_t error)
}
break;
}
-
- return;
-}
-
-static void
-atp_add_to_queue(struct atp_softc *sc, int dx, int dy, 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);
-
- 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] = 0; /* dz */
- buf[6] = 0; /* dz - (dz / 2) */
- buf[7] = MOUSE_SYS_EXTBUTTONS; /* Extra buttons all up. */
- }
-
- usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
- sc->sc_mode.packetsize, 1);
-}
-
-static void
-atp_reset_buf(struct atp_softc *sc)
-{
- /* reset read queue */
- usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
}
static void
@@ -2038,50 +2451,50 @@ 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)
{
- DPRINTFN(ATP_LLEVEL_INFO, "\n");
+ struct atp_softc *sc = usb_fifo_softc(fifo);
- if (fflags & FREAD) {
- struct atp_softc *sc = usb_fifo_softc(fifo);
- int rc;
+ /* check for duplicate open, should not happen */
+ if (sc->sc_fflags & fflags)
+ return (EBUSY);
- if (sc->sc_state & ATP_ENABLED)
- 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)) {
+ ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) {
return (ENOMEM);
}
-
- rc = atp_enable(sc);
- if (rc != 0) {
- usb_fifo_free_buffer(fifo);
- return (rc);
- }
}
+ sc->sc_fflags |= (fflags & (FREAD | FWRITE));
return (0);
}
static void
atp_close(struct usb_fifo *fifo, int fflags)
{
- if (fflags & FREAD) {
- struct atp_softc *sc = usb_fifo_softc(fifo);
+ 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);
- usb_fifo_free_buffer(fifo);
}
}
-int
+static int
atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
{
struct atp_softc *sc = usb_fifo_softc(fifo);
@@ -2105,20 +2518,20 @@ atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
;
else if ((mode.level < 0) || (mode.level > 1)) {
error = EINVAL;
- goto done;
+ 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.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.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;
}
@@ -2128,21 +2541,21 @@ atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
*(int *)addr = sc->sc_mode.level;
break;
case MOUSE_SETLEVEL:
- if (*(int *)addr < 0 || *(int *)addr > 1) {
+ if ((*(int *)addr < 0) || (*(int *)addr > 1)) {
error = EINVAL;
- goto done;
+ 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.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.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;
}
@@ -2154,9 +2567,9 @@ atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
*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.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
if (status->dx || status->dy || status->dz)
status->flags |= MOUSE_POSCHANGED;
@@ -2164,11 +2577,12 @@ atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
status->flags |= MOUSE_BUTTONSCHANGED;
break;
}
+
default:
error = ENOTTY;
+ break;
}
-done:
mtx_unlock(&sc->sc_mutex);
return (error);
}
@@ -2178,49 +2592,40 @@ atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS)
{
int error;
u_int tmp;
- u_int prev_mickeys_scale_factor;
-
- prev_mickeys_scale_factor = atp_mickeys_scale_factor;
tmp = atp_mickeys_scale_factor;
error = sysctl_handle_int(oidp, &tmp, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
- if (tmp == prev_mickeys_scale_factor)
+ 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);
- /* Update dependent thresholds. */
- if (atp_small_movement_threshold == (prev_mickeys_scale_factor >> 3))
- atp_small_movement_threshold = atp_mickeys_scale_factor >> 3;
- if (atp_max_delta_mickeys == ((3 * prev_mickeys_scale_factor) >> 1))
- atp_max_delta_mickeys = ((3 * atp_mickeys_scale_factor) >>1);
- if (atp_slide_min_movement == (prev_mickeys_scale_factor >> 3))
- atp_slide_min_movement = atp_mickeys_scale_factor >> 3;
-
return (0);
}
+static devclass_t atp_devclass;
+
static device_method_t atp_methods[] = {
- /* Device interface */
DEVMETHOD(device_probe, atp_probe),
DEVMETHOD(device_attach, atp_attach),
DEVMETHOD(device_detach, atp_detach),
- { 0, 0 }
+
+ DEVMETHOD_END
};
static driver_t atp_driver = {
- .name = ATP_DRIVER_NAME,
+ .name = ATP_DRIVER_NAME,
.methods = atp_methods,
- .size = sizeof(struct atp_softc)
+ .size = sizeof(struct atp_softc)
};
-static devclass_t atp_devclass;
-
DRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0);
MODULE_DEPEND(atp, usb, 1, 1, 1);
MODULE_VERSION(atp, 1);
diff --git a/sys/dev/usb/wlan/if_run.c b/sys/dev/usb/wlan/if_run.c
index 69a525c..55cea59 100644
--- a/sys/dev/usb/wlan/if_run.c
+++ b/sys/dev/usb/wlan/if_run.c
@@ -100,7 +100,8 @@ SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RW, &run_debug, 0,
static const STRUCT_USB_HOST_ID run_devs[] = {
#define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
#define RUN_DEV_EJECT(v,p) \
- { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, 0) }
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) }
+#define RUN_EJECT 1
RUN_DEV(ABOCOM, RT2770),
RUN_DEV(ABOCOM, RT2870),
RUN_DEV(ABOCOM, RT3070),
@@ -315,7 +316,7 @@ static const STRUCT_USB_HOST_ID run_devs[] = {
RUN_DEV(ZINWELL, RT3072_2),
RUN_DEV(ZYXEL, RT2870_1),
RUN_DEV(ZYXEL, RT2870_2),
- RUN_DEV(ZYXEL, NWD2705),
+ RUN_DEV_EJECT(ZYXEL, NWD2705),
RUN_DEV_EJECT(RALINK, RT_STOR),
#undef RUN_DEV_EJECT
#undef RUN_DEV
@@ -707,6 +708,8 @@ run_attach(device_t self)
device_set_usb_desc(self);
sc->sc_udev = uaa->device;
sc->sc_dev = self;
+ if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT)
+ sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED;
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
MTX_NETWORK_LOCK, MTX_DEF);
@@ -1151,7 +1154,7 @@ run_load_microcode(struct run_softc *sc)
}
/* write microcode image */
- if (sc->mac_ver != 0x3593) {
+ if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) {
run_write_region_1(sc, RT2870_FW_BASE, base, 4096);
run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff);
run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff);
diff --git a/sys/dev/usb/wlan/if_runvar.h b/sys/dev/usb/wlan/if_runvar.h
index 63d9422..ad0fc30 100644
--- a/sys/dev/usb/wlan/if_runvar.h
+++ b/sys/dev/usb/wlan/if_runvar.h
@@ -154,6 +154,11 @@ struct run_softc {
device_t sc_dev;
struct usb_device *sc_udev;
struct ifnet *sc_ifp;
+ int sc_need_fwload;
+
+ int sc_flags;
+#define RUN_FLAG_FWLOAD_NEEDED 0x01
+
uint16_t wcid_stats[RT2870_WCID_MAX + 1][3];
#define RUN_TXCNT 0
#define RUN_SUCCESS 1
diff --git a/sys/mips/conf/DB120.hints b/sys/mips/conf/DB120.hints
index 4250a29..8831f2b 100644
--- a/sys/mips/conf/DB120.hints
+++ b/sys/mips/conf/DB120.hints
@@ -14,13 +14,37 @@ hint.argemdio.0.order=0
hint.ar934x_gmac.0.gmac_cfg=0x41
# GMAC0 here - connected to an AR8327
-#hint.arswitch.0.at="mdio0"
-#hint.arswitch.0.is_7240=0
-#hint.arswitch.0.is_9340=0 # not the internal switch!
-#hint.arswitch.0.numphys=5
-#hint.arswitch.0.phy4cpu=0
-#hint.arswitch.0.is_rgmii=1
-#hint.arswitch.0.is_gmii=0
+hint.arswitch.0.at="mdio0"
+hint.arswitch.0.is_7240=0
+hint.arswitch.0.is_9340=0 # not the internal switch!
+hint.arswitch.0.numphys=5
+hint.arswitch.0.phy4cpu=0
+hint.arswitch.0.is_rgmii=1
+hint.arswitch.0.is_gmii=0
+# XXX other AR8327 configuration parameters
+
+# pad0 cfg:
+# .mode = AR8327_PAD_MAC_RGMII,
+# .txclk_delay_en = true,
+# .rxclk_delay_en = true,
+# .txclk_delay_sel = AR8327_CLK_DELAY_SEL1,
+# .rxclk_delay_sel = AR8327_CLK_DELAY_SEL2,
+
+# .led_ctrl0 = 0x00000000,
+# .led_ctrl1 = 0xc737c737,
+# .led_ctrl2 = 0x00000000,
+# .led_ctrl3 = 0x00c30c00,
+# .open_drain = true,
+
+# .port0_cfg = {
+# .force_link = 1,
+# .speed = AR8327_PORT_SPEED_1000,
+# .duplex = 1,
+# .txpause = 1,
+# .rxpause = 1,
+# },
+
+# port6 cfg?
# XXX OpenWRT DB120 BSP doesn't have media/duplex set?
hint.arge.0.phymask=0x0
@@ -36,7 +60,9 @@ hint.argemdio.1.msize=0x1000
hint.argemdio.1.order=0
# Embedded switch on the AR9344
-hint.arswitch.1.at="mdio1"
+# mdio1 is actually created as the AR8327 internal bus; so
+# this pops up as mdio2.
+hint.arswitch.1.at="mdio2"
hint.arswitch.1.is_7240=0
hint.arswitch.1.is_9340=1
hint.arswitch.1.numphys=4
diff --git a/sys/sparc64/pci/fire.c b/sys/sparc64/pci/fire.c
index ff47487..2755260 100644
--- a/sys/sparc64/pci/fire.c
+++ b/sys/sparc64/pci/fire.c
@@ -1688,7 +1688,7 @@ static int
fire_alloc_msix(device_t dev, device_t child, int *irq)
{
struct fire_softc *sc;
- u_int i, msiq;
+ int i, msiq;
sc = device_get_softc(dev);
if ((sc->sc_flags & FIRE_MSIX) == 0)
diff --git a/sys/sparc64/sparc64/spitfire.c b/sys/sparc64/sparc64/spitfire.c
index 7e51f2d..7b57e9d 100644
--- a/sys/sparc64/sparc64/spitfire.c
+++ b/sys/sparc64/sparc64/spitfire.c
@@ -130,7 +130,7 @@ spitfire_icache_page_inval(vm_paddr_t pa)
: "=r" (tag) : "r" (addr), "n" (ASI_ICACHE_TAG));
if (((tag >> IC_VALID_SHIFT) & IC_VALID_MASK) == 0)
continue;
- tag &= IC_TAG_MASK << IC_TAG_SHIFT;
+ tag &= (u_long)IC_TAG_MASK << IC_TAG_SHIFT;
if (tag == target) {
PMAP_STATS_INC(spitfire_icache_npage_inval_match);
stxa_sync(addr, ASI_ICACHE_TAG, tag);
diff --git a/tools/tools/iwn/Makefile b/tools/tools/iwn/Makefile
new file mode 100644
index 0000000..1f9e93a
--- /dev/null
+++ b/tools/tools/iwn/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= iwnstats
+
+.include <bsd.subdir.mk>
diff --git a/tools/tools/iwn/iwnstats/Makefile b/tools/tools/iwn/iwnstats/Makefile
new file mode 100644
index 0000000..06f92ac
--- /dev/null
+++ b/tools/tools/iwn/iwnstats/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+NO_MAN=1
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../../sys/dev/iwn/
+
+CFLAGS+=-I${.CURDIR}/../../../../sys/dev/iwn/
+CFLAGS+=-I${.CURDIR}/../../../../sys/
+
+PROG= iwnstats
+
+# Because of a clang preprocessor parser limitation causing this
+# to not compile, use gcc for now.
+#CC= gcc
+
+SRCS= main.c iwn_ioctl.c
+
+# CFLAGS.clang+= -fbracket-depth=512
+
+.include <bsd.prog.mk>
diff --git a/tools/tools/iwn/iwnstats/iwn_ioctl.c b/tools/tools/iwn/iwnstats/iwn_ioctl.c
new file mode 100644
index 0000000..13c6153
--- /dev/null
+++ b/tools/tools/iwn/iwnstats/iwn_ioctl.c
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * iwn ioctl API.
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_var.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+
+#include "net80211/ieee80211_ioctl.h"
+#include "net80211/ieee80211_radiotap.h"
+
+#include "if_iwn_ioctl.h"
+
+/*
+ * This contains the register definitions for iwn; including
+ * the statistics definitions.
+ */
+#include "if_iwnreg.h"
+
+#include "iwnstats.h"
+
+#include "iwn_ioctl.h"
+
+void
+iwn_setifname(struct iwnstats *is, const char *ifname)
+{
+
+ strncpy(is->ifr.ifr_name, ifname, sizeof (is->ifr.ifr_name));
+}
+
+void
+iwn_zerostats(struct iwnstats *is)
+{
+
+ if (ioctl(is->s, SIOCZIWNSTATS, &is->ifr) < 0)
+ err(-1, "ioctl: %s", is->ifr.ifr_name);
+}
+
+int
+iwn_collect(struct iwnstats *is)
+{
+ int err;
+
+ is->ifr.ifr_data = (caddr_t) &is->st;
+ err = ioctl(is->s, SIOCGIWNSTATS, &is->ifr);
+ if (err < 0)
+ warn("ioctl: %s", is->ifr.ifr_name);
+ return (err);
+}
diff --git a/tools/tools/iwn/iwnstats/iwn_ioctl.h b/tools/tools/iwn/iwnstats/iwn_ioctl.h
new file mode 100644
index 0000000..b07ddb5
--- /dev/null
+++ b/tools/tools/iwn/iwnstats/iwn_ioctl.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IWN_IOCTL_H__
+#define __IWN_IOCTL_H__
+
+extern void iwn_setifname(struct iwnstats *is, const char *ifname);
+extern void iwn_zerostats(struct iwnstats *is);
+extern int iwn_collect(struct iwnstats *is);
+
+#endif /* __IWN_IOCTL_H__ */
diff --git a/tools/tools/iwn/iwnstats/iwnstats.h b/tools/tools/iwn/iwnstats/iwnstats.h
new file mode 100644
index 0000000..6443ca1
--- /dev/null
+++ b/tools/tools/iwn/iwnstats/iwnstats.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+#ifndef __IWNSTATS_H__
+#define __IWNSTATS_H__
+
+struct iwnstats {
+ int s;
+ struct ifreq ifr;
+ struct iwn_stats st;
+};
+
+#endif /* __IWNSTATS_H__ */
diff --git a/tools/tools/iwn/iwnstats/main.c b/tools/tools/iwn/iwnstats/main.c
new file mode 100644
index 0000000..7b14cd9
--- /dev/null
+++ b/tools/tools/iwn/iwnstats/main.c
@@ -0,0 +1,282 @@
+/*-
+ * Copyright (c) 2014 Adrian Chadd <adrian@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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+#include <net/if.h>
+#include <sys/endian.h>
+
+#include "net80211/ieee80211_ioctl.h"
+#include "net80211/ieee80211_radiotap.h"
+
+#include "if_iwn_ioctl.h"
+#include "if_iwnreg.h"
+#include "iwnstats.h"
+#include "iwn_ioctl.h"
+
+#define IWN_DEFAULT_IF "iwn0"
+
+struct iwnstats *
+iwnstats_new(const char *ifname)
+{
+ struct iwnstats *is;
+
+ is = calloc(1, sizeof(struct iwnstats));
+ if (is == NULL)
+ return (NULL);
+
+ is->s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (is->s < 0)
+ err(1, "socket");
+
+ iwn_setifname(is, ifname);
+ return (is);
+}
+
+static void
+iwn_stats_phy_print(struct iwnstats *is, struct iwn_rx_phy_stats *rxphy,
+ const char *prefix)
+{
+
+ printf("%s: %s: ina=%d, fina=%d, bad_plcp=%d, bad_crc32=%d, overrun=%d, eoverrun=%d\n",
+ __func__,
+ prefix,
+ le32toh(rxphy->ina),
+ le32toh(rxphy->fina),
+ le32toh(rxphy->bad_plcp),
+ le32toh(rxphy->bad_crc32),
+ le32toh(rxphy->overrun),
+ le32toh(rxphy->eoverrun));
+
+ printf("%s: %s: fa=%d, bad_fina_sync=%d, sfd_timeout=%d, fina_timeout=%d, no_rts_ack=%d\n",
+ __func__,
+ prefix,
+ le32toh(rxphy->fa),
+ le32toh(rxphy->bad_fina_sync),
+ le32toh(rxphy->sfd_timeout),
+ le32toh(rxphy->fina_timeout),
+ le32toh(rxphy->no_rts_ack));
+
+ printf("%s: %s: rxe_limit=%d, ack=%d, cts=%d, ba_resp=%d, dsp_kill=%d, bad_mh=%d, rssi_sum=%d\n",
+ __func__,
+ prefix,
+ le32toh(rxphy->rxe_limit),
+ le32toh(rxphy->ack),
+ le32toh(rxphy->cts),
+ le32toh(rxphy->ba_resp),
+ le32toh(rxphy->dsp_kill),
+ le32toh(rxphy->bad_mh),
+ le32toh(rxphy->rssi_sum));
+}
+
+static void
+iwn_stats_rx_general_print(struct iwnstats *is, struct iwn_rx_general_stats *g)
+{
+
+ printf("%s: bad_cts=%d, bad_ack=%d, not_bss=%d, filtered=%d, bad_chan=%d, beacons=%d\n",
+ __func__,
+ le32toh(g->bad_cts),
+ le32toh(g->bad_ack),
+ le32toh(g->not_bss),
+ le32toh(g->filtered),
+ le32toh(g->bad_chan),
+ le32toh(g->beacons));
+
+ /* XXX it'd be nice to have adc/ina saturated as a % of time */
+ printf("%s: missed_beacons=%d, adc_saturated=%d, ina_searched=%d\n",
+ __func__,
+ le32toh(g->missed_beacons),
+ le32toh(g->adc_saturated),
+ le32toh(g->ina_searched));
+
+ printf("%s: noise=[%d, %d, %d] flags=0x%08x, load=%d, fa=%d\n",
+ __func__,
+ le32toh(g->noise[0]),
+ le32toh(g->noise[1]),
+ le32toh(g->noise[2]),
+ le32toh(g->flags),
+ le32toh(g->load),
+ le32toh(g->fa));
+
+ printf("%s: rssi=[%d, %d, %d] energy=[%d %d %d]\n",
+ __func__,
+ le32toh(g->rssi[0]),
+ le32toh(g->rssi[1]),
+ le32toh(g->rssi[2]),
+ le32toh(g->energy[0]),
+ le32toh(g->energy[1]),
+ le32toh(g->energy[2]));
+}
+
+static void
+iwn_stats_tx_print(struct iwnstats *is, struct iwn_tx_stats *tx)
+{
+
+ printf("%s: preamble=%d, rx_detected=%d, bt_defer=%d, bt_kill=%d, short_len=%d\n",
+ __func__,
+ le32toh(tx->preamble),
+ le32toh(tx->rx_detected),
+ le32toh(tx->bt_defer),
+ le32toh(tx->bt_kill),
+ le32toh(tx->short_len));
+
+ printf("%s: cts_timeout=%d, ack_timeout=%d, exp_ack=%d, ack=%d, msdu=%d\n",
+ __func__,
+ le32toh(tx->cts_timeout),
+ le32toh(tx->ack_timeout),
+ le32toh(tx->exp_ack),
+ le32toh(tx->ack),
+ le32toh(tx->msdu));
+
+ printf("%s: burst_err1=%d, burst_err2=%d, cts_collision=%d, ack_collision=%d\n",
+ __func__,
+ le32toh(tx->burst_err1),
+ le32toh(tx->burst_err2),
+ le32toh(tx->cts_collision),
+ le32toh(tx->ack_collision));
+
+ printf("%s: ba_timeout=%d, ba_resched=%d, query_ampdu=%d, query=%d, query_ampdu_frag=%d\n",
+ __func__,
+ le32toh(tx->ba_timeout),
+ le32toh(tx->ba_resched),
+ le32toh(tx->query_ampdu),
+ le32toh(tx->query),
+ le32toh(tx->query_ampdu_frag));
+
+ printf("%s: query_mismatch=%d, not_ready=%d, underrun=%d, bt_ht_kill=%d, rx_ba_resp=%d\n",
+ __func__,
+ le32toh(tx->query_mismatch),
+ le32toh(tx->not_ready),
+ le32toh(tx->underrun),
+ le32toh(tx->bt_ht_kill),
+ le32toh(tx->rx_ba_resp));
+}
+
+static void
+iwn_stats_ht_phy_print(struct iwnstats *is, struct iwn_rx_ht_phy_stats *ht)
+{
+
+ printf("%s: bad_plcp=%d, overrun=%d, eoverrun=%d, good_crc32=%d, bad_crc32=%d\n",
+ __func__,
+ le32toh(ht->bad_plcp),
+ le32toh(ht->overrun),
+ le32toh(ht->eoverrun),
+ le32toh(ht->good_crc32),
+ le32toh(ht->bad_crc32));
+
+ printf("%s: bad_mh=%d, good_ampdu_crc32=%d, ampdu=%d, fragment=%d\n",
+ __func__,
+ le32toh(ht->bad_plcp),
+ le32toh(ht->good_ampdu_crc32),
+ le32toh(ht->ampdu),
+ le32toh(ht->fragment));
+}
+
+
+static void
+iwn_stats_general_print(struct iwnstats *is, struct iwn_stats *stats)
+{
+
+ /* General */
+ printf("%s: temp=%d, temp_m=%d, burst_check=%d, burst=%d, sleep=%d, slot_out=%d, slot_idle=%d\n",
+ __func__,
+ le32toh(stats->general.temp),
+ le32toh(stats->general.temp_m),
+ le32toh(stats->general.burst_check),
+ le32toh(stats->general.burst),
+ le32toh(stats->general.sleep),
+ le32toh(stats->general.slot_out),
+ le32toh(stats->general.slot_idle));
+ printf("%s: slot_out=%d, ttl_tstamp=0x%08x, tx_ant_a=%d, tx_ant_b=%d, exec=%d, probe=%d\n",
+ __func__,
+ le32toh(stats->general.slot_out),
+ le32toh(stats->general.ttl_tstamp),
+ le32toh(stats->general.tx_ant_a),
+ le32toh(stats->general.tx_ant_b),
+ le32toh(stats->general.exec),
+ le32toh(stats->general.probe));
+ printf("%s: rx_enabled=%d\n",
+ __func__,
+ le32toh(stats->general.rx_enabled));
+}
+
+static void
+iwn_print(struct iwnstats *is)
+{
+ struct iwn_stats *s;
+
+ s = &is->st;
+
+ iwn_stats_general_print(is, s);
+
+ /* RX */
+ iwn_stats_phy_print(is, &s->rx.ofdm, "ofdm");
+ iwn_stats_phy_print(is, &s->rx.cck, "cck");
+ iwn_stats_ht_phy_print(is, &s->rx.ht);
+ iwn_stats_rx_general_print(is, &s->rx.general);
+
+ /* TX */
+ iwn_stats_tx_print(is, &s->tx);
+ printf("--\n");
+}
+
+int
+main(int argc, const char *argv[])
+{
+ struct iwnstats *is;
+
+ is = iwnstats_new(IWN_DEFAULT_IF);
+
+ if (is == NULL) {
+ fprintf(stderr, "%s: couldn't allocate new stats structure\n",
+ argv[0]);
+ exit(127);
+ }
+
+ /* begin fetching data */
+ while (1) {
+ if (iwn_collect(is) != 0) {
+ fprintf(stderr, "%s: fetch failed\n", argv[0]);
+ goto next;
+ }
+
+ iwn_print(is);
+
+ next:
+ usleep(100 * 1000);
+ }
+
+ exit(0);
+}
diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c
index 6fae571..545d6dc 100644
--- a/usr.sbin/pkg/pkg.c
+++ b/usr.sbin/pkg/pkg.c
@@ -299,7 +299,7 @@ parse_fingerprint(ucl_object_t *obj)
fct = HASH_SHA256;
if (fct == HASH_UNKNOWN) {
- warnx("Unsupported hashing function: %s\n", function);
+ warnx("Unsupported hashing function: %s", function);
return (NULL);
}
diff --git a/usr.sbin/pmcstat/Makefile b/usr.sbin/pmcstat/Makefile
index c27e56d..b8c8081 100644
--- a/usr.sbin/pmcstat/Makefile
+++ b/usr.sbin/pmcstat/Makefile
@@ -9,6 +9,7 @@ DPADD= ${LIBELF} ${LIBKVM} ${LIBPMC} ${LIBM} ${LIBNCURSES}
LDADD= -lelf -lkvm -lpmc -lm -lncurses
SRCS= pmcstat.c pmcstat.h pmcstat_log.c \
-pmcpl_callgraph.c pmcpl_gprof.c pmcpl_annotate.c pmcpl_calltree.c
+pmcpl_callgraph.c pmcpl_gprof.c pmcpl_annotate.c \
+pmcpl_annotate_cg.c pmcpl_calltree.c
.include <bsd.prog.mk>
diff --git a/usr.sbin/pmcstat/pmcpl_annotate_cg.c b/usr.sbin/pmcstat/pmcpl_annotate_cg.c
new file mode 100644
index 0000000..e90bda1
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcpl_annotate_cg.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2005-2007, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * Copyright (c) 2014, Adrian Chadd, Netflix Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Transform a hwpmc(4) log into human readable form, and into
+ * gprof(1) compatible profiles.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/gmon.h>
+#include <sys/imgact_aout.h>
+#include <sys/imgact_elf.h>
+#include <sys/mman.h>
+#include <sys/pmc.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libgen.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <sysexits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pmcstat.h"
+#include "pmcstat_log.h"
+#include "pmcpl_annotate_cg.h"
+
+/*
+ * Record a callchain.
+ */
+
+void
+pmcpl_annotate_cg_process(struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr,
+ uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu)
+{
+ struct pmcstat_pcmap *map;
+ struct pmcstat_symbol *sym;
+ uintfptr_t newpc;
+ struct pmcstat_image *image;
+ int i;
+ char filename[PATH_MAX], funcname[PATH_MAX];
+ unsigned sline;
+
+ (void) pmcr; (void) nsamples; (void) usermode; (void) cpu;
+
+ for (i = 0; i < (int) nsamples; i++) {
+ map = NULL;
+ sym = NULL;
+ image = NULL;
+ filename[0] = '\0';
+ funcname[0] = '\0';
+ sline = 0;
+
+ map = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, cc[i]);
+ if (map != NULL) {
+ assert(cc[i] >= map->ppm_lowpc && cc[i] < map->ppm_highpc);
+ image = map->ppm_image;
+ newpc = cc[i] - (map->ppm_lowpc +
+ (image->pi_vaddr - image->pi_start));
+ sym = pmcstat_symbol_search(image, newpc);
+ }
+
+ if (map != NULL && image != NULL && sym != NULL) {
+ (void) pmcstat_image_addr2line(image, cc[i],
+ filename, sizeof(filename), &sline, funcname, sizeof(funcname));
+ }
+
+ if (map != NULL && sym != NULL) {
+ fprintf(args.pa_graphfile, "%p %s %s:%d\n",
+ (void *)cc[i],
+ funcname,
+ filename,
+ sline);
+ } else {
+ fprintf(args.pa_graphfile, "%p <unknown> ??:0\n",
+ (void *) cc[i]);
+ }
+ }
+ fprintf(args.pa_graphfile, "--\n");
+}
diff --git a/usr.sbin/pmcstat/pmcpl_annotate_cg.h b/usr.sbin/pmcstat/pmcpl_annotate_cg.h
new file mode 100644
index 0000000..bd655f7
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcpl_annotate_cg.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2005-2007, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * Copyright (c) 2014, Adrian Chadd, Netflix Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * 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$
+ */
+
+#ifndef _PMCSTAT_PL_ANNOTATE_CG_H_
+#define _PMCSTAT_PL_ANNOTATE_CG_H_
+
+/* Function prototypes */
+void pmcpl_annotate_cg_process(
+ struct pmcstat_process *pp, struct pmcstat_pmcrecord *pmcr,
+ uint32_t nsamples, uintfptr_t *cc, int usermode, uint32_t cpu);
+
+#endif /* _PMCSTAT_PL_ANNOTATE_CG_H_ */
diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c
index cc43b82..3edba3f 100644
--- a/usr.sbin/pmcstat/pmcstat.c
+++ b/usr.sbin/pmcstat/pmcstat.c
@@ -503,6 +503,7 @@ pmcstat_show_usage(void)
"\t -S spec\t allocate a system-wide sampling PMC\n"
"\t -T\t\t start in top mode\n"
"\t -W\t\t (toggle) show counts per context switch\n"
+ "\t -a <file>\t print sampled PCs and callgraph to \"file\"\n"
"\t -c cpu-list\t set cpus for subsequent system-wide PMCs\n"
"\t -d\t\t (toggle) track descendants\n"
"\t -f spec\t pass \"spec\" to as plugin option\n"
@@ -617,8 +618,14 @@ main(int argc, char **argv)
CPU_SET(hcpu, &cpumask);
while ((option = getopt(argc, argv,
- "CD:EF:G:M:NO:P:R:S:TWc:df:gk:m:n:o:p:qr:s:t:vw:z:")) != -1)
+ "CD:EF:G:M:NO:P:R:S:TWa:c:df:gk:m:n:o:p:qr:s:t:vw:z:")) != -1)
switch (option) {
+ case 'a': /* Annotate + callgraph */
+ args.pa_flags |= FLAG_DO_ANNOTATE;
+ args.pa_plugin = PMCSTAT_PL_ANNOTATE_CG;
+ graphfilename = optarg;
+ break;
+
case 'C': /* cumulative values */
use_cumulative_counts = !use_cumulative_counts;
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
@@ -917,7 +924,8 @@ main(int argc, char **argv)
/* -m option is allowed with -R only. */
if (args.pa_flags & FLAG_DO_ANNOTATE && args.pa_inputpath == NULL)
- errx(EX_USAGE, "ERROR: option -m requires an input file");
+ errx(EX_USAGE, "ERROR: option %s requires an input file",
+ args.pa_plugin == PMCSTAT_PL_ANNOTATE ? "-m" : "-a");
/* -m option is not allowed combined with -g or -G. */
if (args.pa_flags & FLAG_DO_ANNOTATE &&
diff --git a/usr.sbin/pmcstat/pmcstat.h b/usr.sbin/pmcstat/pmcstat.h
index 6b64b76..c8ec14d 100644
--- a/usr.sbin/pmcstat/pmcstat.h
+++ b/usr.sbin/pmcstat/pmcstat.h
@@ -91,6 +91,7 @@
#define PMCSTAT_PL_GPROF 2
#define PMCSTAT_PL_ANNOTATE 3
#define PMCSTAT_PL_CALLTREE 4
+#define PMCSTAT_PL_ANNOTATE_CG 5
#define PMCSTAT_TOP_DELTA 0
#define PMCSTAT_TOP_ACCUM 1
diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c
index fdcf9c4..f0e4939 100644
--- a/usr.sbin/pmcstat/pmcstat_log.c
+++ b/usr.sbin/pmcstat/pmcstat_log.c
@@ -149,6 +149,7 @@ struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */
#include "pmcpl_gprof.h"
#include "pmcpl_callgraph.h"
#include "pmcpl_annotate.h"
+#include "pmcpl_annotate_cg.h"
#include "pmcpl_calltree.h"
static struct pmc_plugins {
@@ -214,6 +215,11 @@ static struct pmc_plugins {
.pl_topdisplay = pmcpl_ct_topdisplay
},
{
+ .pl_name = "annotate_cg",
+ .pl_process = pmcpl_annotate_cg_process
+ },
+
+ {
.pl_name = NULL
}
};
OpenPOWER on IntegriCloud