diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index fb8328ca9a4b..1c3f8fb8b4e1 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 c2d48291c7ca..0e6d34ac2d8a 100644 --- a/lib/libc/iconv/citrus_prop.c +++ b/lib/libc/iconv/citrus_prop.c @@ -339,7 +339,7 @@ _citrus_prop_read_symbol(struct _memstream * __restrict ms, 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 db087a4ceeda..29621540e69c 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 e79b162ad5e3..d0e7047ea253 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 6d0b6d8ef70e..f9eb006af8dc 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 b8a881defc20..4bcba8722a3b 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 27d6397c3aa5..f7827988ca30 100644 --- a/share/man/man4/atp.4 +++ b/share/man/man4/atp.4 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2009 Rohit Grover . +.\" Copyright (c) 2014 Rohit Grover . .\" 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 ef7019658418..920583d921cd 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 dc0a478ed7e2..11365aabc1ee 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 1123e4a51baa..a61a3dc2a306 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 b8a2d9c199e4..5ba0076284b0 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 bc28f82e4a30..74933eb27e32 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 * 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 0c70aca4efda..c9561c147184 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 7883f3e286da..8d418e57efdd 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 000000000000..4a3166780bd0 --- /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 41c2594b54f7..1512b7c3e0c1 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 000000000000..e1218329ad50 --- /dev/null +++ b/sys/arm/freescale/imx/imx6_mp.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 2014 Juergen Weiss + * Copyright (c) 2014 Ian Lepore + * 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 +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 c328b22d3531..51f82a8e2f90 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 0b53906a4978..f38f9c1610af 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 a2e5f34d81ce..2a4d561d444b 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 c16f039be9fa..35cecf2f7f72 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 7c38f29e016c..5da633f71308 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 000000000000..3a8882c998a0 --- /dev/null +++ b/sys/boot/fdt/dts/vybrid-quartz.dts @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * 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 08656f3d3e19..684df5b81c55 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 ff63129feef9..83f079373124 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 c61030225e84..3e3a0731f4ab 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 d8fea9e26ab6..838fe288e829 100644 --- a/sys/dev/etherswitch/arswitch/arswitch.c +++ b/sys/dev/etherswitch/arswitch/arswitch.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #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 000000000000..262fd62c6258 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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 000000000000..1f35d96639d1 --- /dev/null +++ b/sys/dev/etherswitch/arswitch/arswitch_8327.h @@ -0,0 +1,91 @@ +/*- + * 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$ + */ +#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 f981614077aa..5e24e57fa13f 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 e671e225ea1e..0cd1af832007 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 f79185b84942..414fd8297b14 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 b2a6e992f97c..361e4a3e6f0c 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include 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 253c76df45f4..2932c7e4abdc 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 000000000000..1acf4649e257 --- /dev/null +++ b/sys/dev/iwn/if_iwn_ioctl.h @@ -0,0 +1,25 @@ +/*- + * Copyright (c) 2014 Adrian Chadd + * + * 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 f146feb8558d..bcc01b2d10e0 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 8a55e32d1525..832c1e62aaf4 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 __FBSDID("$FreeBSD$"); +#include +#include #include +#include #include #include -#include +#include #include #include #include -#include +#include +#include #include #include #include #include #include -#include -#include #include #include #include #include + #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 +/* 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,199 +198,391 @@ 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; -/* 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"); - -/* 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 -}; -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 - }, - [ATP_DEV_PARAMS_PBOOK] = { - .data_len = 81, - .n_xsensors = 16, - .n_ysensors = 16, - .prot = ATP_PROT_GEYSER1 - }, - [ATP_DEV_PARAMS_PBOOK_15A] = { - .data_len = 64, - .n_xsensors = 15, - .n_ysensors = 9, - .prot = ATP_PROT_GEYSER2 - }, - [ATP_DEV_PARAMS_PBOOK_17] = { - .data_len = 81, - .n_xsensors = 26, - .n_ysensors = 16, - .prot = ATP_PROT_GEYSER1 - }, +enum fountain_geyser_product { + FOUNTAIN, + GEYSER1, + GEYSER1_17inch, + GEYSER2, + GEYSER3, + GEYSER4, + FOUNTAIN_GEYSER_PRODUCT_MAX /* keep this at the end */ }; -static const STRUCT_USB_HOST_ID atp_devs[] = { - /* 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) }, - - /* 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) }, - - /* 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) }, - - /* 17 inch PowerBook */ - { USB_VPI(USB_VENDOR_APPLE, 0x020d, ATP_DEV_PARAMS_PBOOK_17) }, - +enum wellspring_product { + WELLSPRING1, + WELLSPRING2, + WELLSPRING3, + WELLSPRING4, + WELLSPRING4A, + WELLSPRING5, + WELLSPRING6A, + WELLSPRING6, + WELLSPRING5A, + WELLSPRING7, + WELLSPRING7A, + WELLSPRING8, + WELLSPRING_PRODUCT_MAX /* keep this at the end of the enumeration */ }; +/* trackpad header types */ +enum fountain_geyser_trackpad_type { + FG_TRACKPAD_TYPE_GEYSER1, + FG_TRACKPAD_TYPE_GEYSER2, + FG_TRACKPAD_TYPE_GEYSER3, + FG_TRACKPAD_TYPE_GEYSER4, +}; +enum wellspring_trackpad_type { + WSP_TRACKPAD_TYPE1, /* plain trackpad */ + WSP_TRACKPAD_TYPE2, /* button integrated in trackpad */ + WSP_TRACKPAD_TYPE3 /* additional header fields since June 2013 */ +}; + +/* + * Trackpad family and product and family are encoded together in the + * driver_info value associated with a trackpad product. + */ +#define N_PROD_BITS 8 /* Number of bits used to encode product */ +#define ENCODE_DRIVER_INFO(FAMILY, PROD) \ + (((FAMILY) << N_PROD_BITS) | (PROD)) +#define DECODE_FAMILY_FROM_DRIVER_INFO(INFO) ((INFO) >> N_PROD_BITS) +#define DECODE_PRODUCT_FROM_DRIVER_INFO(INFO) \ + ((INFO) & ((1 << N_PROD_BITS) - 1)) + +#define FG_DRIVER_INFO(PRODUCT) \ + ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_FOUNTAIN_GEYSER, PRODUCT) +#define WELLSPRING_DRIVER_INFO(PRODUCT) \ + ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_WELLSPRING, PRODUCT) + /* * The following structure captures the state of a pressure span along * an axis. Each contact with the touchpad results in separate * pressure spans along the two axes. */ -typedef struct 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) */ +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. */ -} atp_pspan; +} fg_pspan; + +#define FG_MAX_PSPANS_PER_AXIS 3 +#define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS) + +#define WELLSPRING_INTERFACE_INDEX 1 + +/* trackpad finger data offsets, le16-aligned */ +#define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2) +#define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2) +#define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2) + +/* trackpad button data offsets */ +#define WSP_TYPE2_BUTTON_DATA_OFFSET 15 +#define WSP_TYPE3_BUTTON_DATA_OFFSET 23 + +/* list of device capability bits */ +#define HAS_INTEGRATED_BUTTON 1 + +/* trackpad finger structure - little endian */ +struct wsp_finger_sensor_data { + int16_t origin; /* zero when switching track finger */ + int16_t abs_x; /* absolute x coordinate */ + int16_t abs_y; /* absolute y coordinate */ + int16_t rel_x; /* relative x coordinate */ + int16_t rel_y; /* relative y coordinate */ + int16_t tool_major; /* tool area, major axis */ + int16_t tool_minor; /* tool area, minor axis */ + int16_t orientation; /* 16384 when point, else 15 bit angle */ + int16_t touch_major; /* touch area, major axis */ + int16_t touch_minor; /* touch area, minor axis */ + int16_t unused[3]; /* zeros */ + int16_t multi; /* one finger: varies, more fingers: constant */ +} __packed; + +typedef struct wsp_finger { + /* to track fingers as they match against strokes. */ + boolean_t matched; + + /* location (scaled using the mickeys factor) */ + int x; + int y; +} wsp_finger_t; + +#define WSP_MAX_FINGERS 16 +#define WSP_SIZEOF_FINGER_SENSOR_DATA sizeof(struct wsp_finger_sensor_data) +#define WSP_SIZEOF_ALL_FINGER_DATA (WSP_MAX_FINGERS * \ + WSP_SIZEOF_FINGER_SENSOR_DATA) +#define WSP_MAX_FINGER_ORIENTATION 16384 + +#define ATP_SENSOR_DATA_BUF_MAX 1024 +#if (ATP_SENSOR_DATA_BUF_MAX < ((WSP_MAX_FINGERS * 14 * 2) + \ + WSP_TYPE3_FINGER_DATA_OFFSET)) +/* note: 14 * 2 in the above is based on sizeof(struct wsp_finger_sensor_data)*/ +#error "ATP_SENSOR_DATA_BUF_MAX is too small" +#endif + +#define ATP_MAX_STROKES MAX(WSP_MAX_FINGERS, FG_MAX_STROKES) + +#define FG_MAX_XSENSORS 26 +#define FG_MAX_YSENSORS 16 + +/* device-specific configuration */ +struct fg_dev_params { + u_int data_len; /* for sensor data */ + u_int n_xsensors; + u_int n_ysensors; + enum fountain_geyser_trackpad_type prot; +}; +struct wsp_dev_params { + uint8_t caps; /* device capability bitmask */ + uint8_t tp_type; /* type of trackpad interface */ + uint8_t finger_data_offset; /* offset to trackpad finger data */ +}; + +static const struct fg_dev_params fg_dev_params[FOUNTAIN_GEYSER_PRODUCT_MAX] = { + [FOUNTAIN] = { + .data_len = 81, + .n_xsensors = 16, + .n_ysensors = 16, + .prot = FG_TRACKPAD_TYPE_GEYSER1 + }, + [GEYSER1] = { + .data_len = 81, + .n_xsensors = 16, + .n_ysensors = 16, + .prot = FG_TRACKPAD_TYPE_GEYSER1 + }, + [GEYSER1_17inch] = { + .data_len = 81, + .n_xsensors = 26, + .n_ysensors = 16, + .prot = FG_TRACKPAD_TYPE_GEYSER1 + }, + [GEYSER2] = { + .data_len = 64, + .n_xsensors = 15, + .n_ysensors = 9, + .prot = FG_TRACKPAD_TYPE_GEYSER2 + }, + [GEYSER3] = { + .data_len = 64, + .n_xsensors = 20, + .n_ysensors = 10, + .prot = FG_TRACKPAD_TYPE_GEYSER3 + }, + [GEYSER4] = { + .data_len = 64, + .n_xsensors = 20, + .n_ysensors = 10, + .prot = FG_TRACKPAD_TYPE_GEYSER4 + } +}; + +static const STRUCT_USB_HOST_ID fg_devs[] = { + /* PowerBooks Feb 2005, iBooks G4 */ + { USB_VPI(USB_VENDOR_APPLE, 0x020e, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x020f, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0210, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x030a, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x030b, FG_DRIVER_INFO(GEYSER1)) }, + + /* PowerBooks Oct 2005 */ + { USB_VPI(USB_VENDOR_APPLE, 0x0214, FG_DRIVER_INFO(GEYSER2)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0215, FG_DRIVER_INFO(GEYSER2)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0216, FG_DRIVER_INFO(GEYSER2)) }, + + /* Core Duo MacBook & MacBook Pro */ + { USB_VPI(USB_VENDOR_APPLE, 0x0217, FG_DRIVER_INFO(GEYSER3)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0218, FG_DRIVER_INFO(GEYSER3)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0219, FG_DRIVER_INFO(GEYSER3)) }, + + /* Core2 Duo MacBook & MacBook Pro */ + { USB_VPI(USB_VENDOR_APPLE, 0x021a, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x021b, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x021c, FG_DRIVER_INFO(GEYSER4)) }, + + /* Core2 Duo MacBook3,1 */ + { USB_VPI(USB_VENDOR_APPLE, 0x0229, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x022a, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x022b, FG_DRIVER_INFO(GEYSER4)) }, + + /* 17 inch PowerBook */ + { USB_VPI(USB_VENDOR_APPLE, 0x020d, FG_DRIVER_INFO(GEYSER1_17inch)) }, +}; + +static const struct wsp_dev_params wsp_dev_params[WELLSPRING_PRODUCT_MAX] = { + [WELLSPRING1] = { + .caps = 0, + .tp_type = WSP_TRACKPAD_TYPE1, + .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, + }, + [WELLSPRING2] = { + .caps = 0, + .tp_type = WSP_TRACKPAD_TYPE1, + .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, + }, + [WELLSPRING3] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING4] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING4A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING5] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING6] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING5A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING6A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING7] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING7A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING8] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE3, + .finger_data_offset = WSP_TYPE3_FINGER_DATA_OFFSET, + }, +}; + +#define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + +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 struct atp_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 + Y = 1, + NUM_AXES } atp_axis; -#define ATP_MAX_STROKES (2 * ATP_MAX_PSPANS_PER_AXIS) - -/* - * 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; - #define ATP_FIFO_BUF_SIZE 8 /* bytes */ #define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ @@ -345,62 +592,144 @@ enum { ATP_N_TRANSFER, }; +typedef struct fg_stroke_component { + /* Fields encapsulating the pressure-span. */ + u_int loc; /* location (scaled) */ + u_int cum_pressure; /* cumulative compression */ + u_int max_cum_pressure; /* max cumulative compression */ + boolean_t matched; /*to track components as they match against pspans.*/ + + int delta_mickeys; /* change in location (un-smoothened movement)*/ +} fg_stroke_component_t; + +/* + * The following structure captures a finger contact with the + * touchpad. A stroke comprises two p-span components and some state. + */ +typedef struct atp_stroke { + TAILQ_ENTRY(atp_stroke) entry; + + atp_stroke_type type; + uint32_t flags; /* the state of this stroke */ +#define ATSF_ZOMBIE 0x1 + boolean_t matched; /* to track match against fingers.*/ + + struct timeval ctime; /* create time; for coincident siblings. */ + + /* + * Unit: interrupts; we maintain this value in + * addition to 'ctime' in order to avoid the + * expensive call to microtime() at every + * interrupt. + */ + uint32_t age; + + /* Location */ + int x; + int y; + + /* Fields containing information about movement. */ + int instantaneous_dx; /* curr. change in X location (un-smoothened) */ + int instantaneous_dy; /* curr. change in Y location (un-smoothened) */ + int pending_dx; /* cum. of pending short movements */ + int pending_dy; /* cum. of pending short movements */ + int movement_dx; /* interpreted smoothened movement */ + int movement_dy; /* interpreted smoothened movement */ + int cum_movement_x; /* cum. horizontal movement */ + int cum_movement_y; /* cum. vertical movement */ + + /* + * The following member is relevant only for fountain-geyser trackpads. + * For these, there is the need to track pressure-spans and cumulative + * pressures for stroke components. + */ + fg_stroke_component_t components[NUM_AXES]; +} atp_stroke_t; + +struct atp_softc; /* forward declaration */ +typedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len); + struct atp_softc { - device_t sc_dev; - struct usb_device *sc_usb_device; -#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; + device_t sc_dev; + struct usb_device *sc_usb_device; + struct mtx sc_mutex; /* for synchronization */ + struct usb_fifo_sc sc_fifo; - struct atp_dev_params *sc_params; +#define MODE_LENGTH 8 + char sc_mode_bytes[MODE_LENGTH]; /* device mode */ - 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 + trackpad_family_t sc_family; + const void *sc_params; /* device configuration */ + sensor_data_interpreter_t sensor_data_interpreter; - u_int sc_left_margin; - u_int sc_right_margin; + mousehw_t sc_hw; + mousemode_t sc_mode; + mousestatus_t sc_status; - atp_stroke sc_strokes[ATP_MAX_STROKES]; - u_int sc_n_strokes; + u_int sc_state; +#define ATP_ENABLED 0x01 +#define ATP_ZOMBIES_EXIST 0x02 +#define ATP_DOUBLE_TAP_DRAG 0x04 +#define ATP_VALID 0x08 - 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; + struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; - u_int sc_idlecount; /* preceding idle interrupts */ -#define ATP_IDLENESS_THRESHOLD 10 + u_int sc_pollrate; + int sc_fflags; - struct timeval sc_reap_time; - struct timeval sc_reap_ctime; /*ctime of siblings to be reaped*/ + 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; - 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); + pstroke = TAILQ_FIRST(&sc->sc_stroke_free); + if (pstroke == NULL) + goto done; - return (usbd_do_request(udev, NULL /* mutex */, &req, data)); + TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry); + memset(pstroke, 0, sizeof(*pstroke)); + TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry); + + sc->sc_n_strokes++; +done: + return (pstroke); } -static 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); - - 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); - - return (0); + TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry); + TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry); } -void +static void +atp_init_stroke_pool(struct atp_softc *sc) +{ + u_int x; + + TAILQ_INIT(&sc->sc_stroke_free); + TAILQ_INIT(&sc->sc_stroke_used); + + sc->sc_n_strokes = 0; + + memset(&sc->sc_strokes_data, 0, sizeof(sc->sc_strokes_data)); + + for (x = 0; x != ATP_MAX_STROKES; x++) { + TAILQ_INSERT_TAIL(&sc->sc_stroke_free, &sc->sc_strokes_data[x], + entry); + } +} + +static usb_error_t +atp_set_device_mode(struct atp_softc *sc, interface_mode newMode) +{ + uint8_t mode_value; + usb_error_t err; + + if ((newMode != RAW_SENSOR_MODE) && (newMode != HID_MODE)) + return (USB_ERR_INVAL); + + if ((newMode == RAW_SENSOR_MODE) && + (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER)) + mode_value = (uint8_t)0x04; + else + mode_value = newMode; + + err = usbd_req_get_report(sc->sc_usb_device, NULL /* mutex */, + sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, + 0x03 /* type */, 0x00 /* id */); + if (err != USB_ERR_NORMAL_COMPLETION) { + DPRINTF("Failed to read device mode (%d)\n", err); + return (err); + } + + if (sc->sc_mode_bytes[0] == mode_value) + return (err); + + /* + * XXX Need to wait at least 250ms for hardware to get + * ready. The device mode handling appears to be handled + * asynchronously and we should not issue these commands too + * quickly. + */ + pause("WHW", hz / 4); + + sc->sc_mode_bytes[0] = mode_value; + return (usbd_req_set_report(sc->sc_usb_device, NULL /* mutex */, + sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, + 0x03 /* type */, 0x00 /* id */)); +} + +static void atp_reset_callback(struct usb_xfer *xfer, usb_error_t error) { usb_device_request_t req; struct usb_page_cache *pc; struct atp_softc *sc = usbd_xfer_softc(xfer); + uint8_t mode_value; + if (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER) + mode_value = 0x04; + else + mode_value = RAW_SENSOR_MODE; + switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: - sc->sc_mode_bytes[0] = 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) -{ - 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); - } - - 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); - } - - 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); - } - - 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); - } - } - - return (0); -} - -/* Free dynamic memory allocated for some fields in softc. */ static void -atp_softc_unpopulate(struct atp_softc *sc) +fg_interpret_sensor_data(struct atp_softc *sc, u_int data_len) { - const struct atp_dev_params *params = sc->sc_params; + u_int n_xpspans = 0; + u_int n_ypspans = 0; + uint8_t status_bits; - if (params == NULL) { - return; - } - if (params->n_xsensors != 0) { - if (sc->base_x != NULL) { - free(sc->base_x, M_USB); - sc->base_x = NULL; - } + const struct fg_dev_params *params = + (const struct fg_dev_params *)sc->sc_params; - if (sc->cur_x != NULL) { - free(sc->cur_x, M_USB); - sc->cur_x = NULL; - } + fg_extract_sensor_data(sc->sc_sensor_data, params->n_xsensors, X, + sc->sc_cur_x, params->prot); + fg_extract_sensor_data(sc->sc_sensor_data, params->n_ysensors, Y, + sc->sc_cur_y, params->prot); - if (sc->pressure_x != NULL) { - free(sc->pressure_x, M_USB); - sc->pressure_x = NULL; + /* + * 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; } } - if (params->n_ysensors != 0) { - if (sc->base_y != NULL) { - free(sc->base_y, M_USB); - sc->base_y = NULL; - } - if (sc->cur_y != NULL) { - free(sc->cur_y, M_USB); - sc->cur_y = NULL; - } + /* 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); - if (sc->pressure_y != NULL) { - free(sc->pressure_y, M_USB); - sc->pressure_y = NULL; + /* Update strokes with new pspans to detect movements. */ + if (fg_update_strokes(sc, sc->sc_pspans_x, n_xpspans, sc->sc_pspans_y, n_ypspans)) + sc->sc_status.flags |= MOUSE_POSCHANGED; + + sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0; + sc->sc_status.button = sc->sc_ibtn; + + /* + * The Fountain/Geyser device continues to trigger interrupts + * at a fast rate even after touchpad activity has + * stopped. Upon detecting that the device has remained idle + * beyond a threshold, we reinitialize it to silence the + * interrupts. + */ + if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) { + sc->sc_idlecount++; + if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) { + /* + * Use the last frame before we go idle for + * calibration on pads which do not send + * calibration frames. + */ + const struct fg_dev_params *params = + (const struct fg_dev_params *)sc->sc_params; + + DPRINTFN(ATP_LLEVEL_INFO, "idle\n"); + + if (params->prot < FG_TRACKPAD_TYPE_GEYSER3) { + memcpy(sc->sc_base_x, sc->sc_cur_x, + params->n_xsensors * sizeof(*(sc->sc_base_x))); + memcpy(sc->sc_base_y, sc->sc_cur_y, + params->n_ysensors * sizeof(*(sc->sc_base_y))); + } + + sc->sc_idlecount = 0; + usbd_transfer_start(sc->sc_xfer[ATP_RESET]); } - } - 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; - 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; + 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; - 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; + 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; - 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,756 +1768,361 @@ 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) { + atp_stroke_t *strokep; + + strokep = atp_alloc_stroke(sc); + if (strokep == NULL) + return; + + /* + * Strokes begin as potential touches. If a stroke survives + * longer than a threshold, or if it records significant + * cumulative movement, then it is considered a 'slide'. + */ + strokep->type = ATP_STROKE_TOUCH; + strokep->matched = true; + microtime(&strokep->ctime); + strokep->age = 1; /* number of interrupts */ + strokep->x = fingerp->x; + strokep->y = fingerp->y; + + /* Reset double-tap-n-drag if we have more than one strokes. */ + if (sc->sc_n_strokes > 1) + sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; + + DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y); +} + +static void +atp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep, + boolean_t *movementp) +{ + /* Revitalize stroke if it had previously been marked as a zombie. */ + if (strokep->flags & ATSF_ZOMBIE) + strokep->flags &= ~ATSF_ZOMBIE; + + strokep->age++; + if (strokep->age <= atp_stroke_maturity_threshold) { /* Avoid noise from immature strokes. */ - stroke->components[X].delta_mickeys = 0; - stroke->components[Y].delta_mickeys = 0; + strokep->instantaneous_dx = 0; + strokep->instantaneous_dy = 0; } - /* 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(strokep)) + *movementp = true; - if (atp_compute_stroke_movement(stroke)) - *movement = TRUE; - - if (stroke->type != ATP_STROKE_TOUCH) + if (strokep->type != ATP_STROKE_TOUCH) 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; - } + if ((abs(strokep->cum_movement_x) > atp_slide_min_movement) || + (abs(strokep->cum_movement_y) > atp_slide_min_movement)) + atp_convert_to_slide(sc, strokep); + else { + /* Compute the stroke's age. */ + struct timeval tdiff; + getmicrotime(&tdiff); + if (timevalcmp(&tdiff, &strokep->ctime, >)) { + timevalsub(&tdiff, &strokep->ctime); if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) || ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) && - (tdiff.tv_usec >= - (atp_touch_timeout % 1000000)))) - atp_convert_to_slide(sc, stroke); + (tdiff.tv_usec >= (atp_touch_timeout % 1000000)))) + atp_convert_to_slide(sc, strokep); } } } -/* Switch a given touch stroke to being a slide. */ -void -atp_convert_to_slide(struct atp_softc *sc, atp_stroke *stroke) +static boolean_t +atp_stroke_has_small_movement(const atp_stroke_t *strokep) { - 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 - }; - - delta = stroke->ctime; - timevalsub(&delta, &sc->sc_reap_time); - if (timevalcmp(&delta, &window, <=)) - sc->sc_state |= ATP_DOUBLE_TAP_DRAG; - } + return (((u_int)abs(strokep->instantaneous_dx) <= + atp_small_movement_threshold) && + ((u_int)abs(strokep->instantaneous_dy) <= + atp_small_movement_threshold)); } /* - * 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 (s->flags & ATSF_ZOMBIE) { - return; - } - - if ((s->type == ATP_STROKE_TOUCH) && - (s->age > atp_stroke_maturity_threshold)) { - s->flags |= ATSF_ZOMBIE; - - /* 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; - } - } 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) -{ - 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)); -} - -/* - * 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; + /* accumulate instantaneous movement */ + strokep->pending_dx += strokep->instantaneous_dx; + strokep->pending_dy += strokep->instantaneous_dy; - /* 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; +#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; + /* scale movement */ + strokep->movement_dx = (strokep->instantaneous_dx) / + (int)atp_mickeys_scale_factor; + strokep->movement_dy = (strokep->instantaneous_dy) / + (int)atp_mickeys_scale_factor; - stroke->cum_movement += - abs(stroke->components[X].movement) + - abs(stroke->components[Y].movement); + 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)); -} - -static __inline void -atp_setup_reap_time(struct atp_softc *sc, struct timeval *tvp) -{ - struct timeval reap_window = { - ATP_ZOMBIE_STROKE_REAP_WINDOW / 1000000, - ATP_ZOMBIE_STROKE_REAP_WINDOW % 1000000 - }; - - microtime(&sc->sc_reap_time); - timevaladd(&sc->sc_reap_time, &reap_window); - - sc->sc_reap_ctime = *tvp; /* ctime to reap */ + strokep->cum_movement_x += strokep->movement_dx; + strokep->cum_movement_y += strokep->movement_dy; + + return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0)); } +/* + * Terminate a stroke. Aside from immature strokes, a slide or touch is + * retained as a zombies so as to reap all their termination siblings + * together; this helps establish the number of fingers involved at the + * end of a multi-touch gesture. + */ static void -atp_reap_zombies(struct atp_softc *sc, u_int *n_reaped, u_int *reaped_xlocs) +atp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep) { - u_int i; - atp_stroke *stroke; + if (strokep->flags & ATSF_ZOMBIE) + return; - *n_reaped = 0; - for (i = 0; i < sc->sc_n_strokes; i++) { - struct timeval tdiff; - - stroke = &sc->sc_strokes[i]; - - if ((stroke->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); - } 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. */ - } - - /* - * Reap this sibling zombie stroke. - */ - - if (reaped_xlocs != NULL) - reaped_xlocs[*n_reaped] = stroke->components[X].loc; - - /* 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--; - - *n_reaped += 1; - --i; /* Decr. i to keep it unchanged for the next iteration */ + /* Drop immature strokes rightaway. */ + if (strokep->age <= atp_stroke_maturity_threshold) { + atp_free_stroke(sc, strokep); + return; } - DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", *n_reaped); + strokep->flags |= ATSF_ZOMBIE; + sc->sc_state |= ATP_ZOMBIES_EXIST; - /* 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); - return; - } - } - - /* If we reach here, then no more zombies remain. */ - sc->sc_state &= ~ATP_ZOMBIES_EXIST; -} - - -/* Device methods. */ -static device_probe_t atp_probe; -static device_attach_t atp_attach; -static device_detach_t atp_detach; -static usb_callback_t atp_intr; - -static const struct usb_config atp_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 */ - }, -}; - -static int -atp_probe(device_t self) -{ - struct usb_attach_arg *uaa = device_get_ivars(self); - - if (uaa->usb_mode != USB_MODE_HOST) - return (ENXIO); - - if ((uaa->info.bInterfaceClass != UICLASS_HID) || - (uaa->info.bInterfaceProtocol != UIPROTO_MOUSE)) - return (ENXIO); - - return (usbd_lookup_id_by_uaa(atp_devs, sizeof(atp_devs), uaa)); -} - -static int -atp_attach(device_t dev) -{ - struct atp_softc *sc = device_get_softc(dev); - struct usb_attach_arg *uaa = device_get_ivars(dev); - usb_error_t err; - - DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc); - - sc->sc_dev = dev; - sc->sc_usb_device = uaa->device; + callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL, + atp_reap_sibling_zombies, sc); /* - * By default the touchpad behaves like an HID device, sending - * packets with reportID = 2. Such reports contain only - * limited information--they encode movement deltas and button - * events,--but do not include data from the pressure - * sensors. The device input mode can be switched from HID - * reports to raw sensor data using vendor-specific USB - * control commands; but first the mode must be read. + * Reset the double-click-n-drag at the termination of any + * slide stroke. */ - 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) { - DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err); - return (ENXIO); - } - - mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE); - - err = usbd_transfer_setup(uaa->device, - &uaa->info.bIfaceIndex, sc->sc_xfer, atp_config, - ATP_N_TRANSFER, sc, &sc->sc_mutex); - - if (err) { - DPRINTF("error=%s\n", usbd_errstr(err)); - goto detach; - } - - if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, - &atp_fifo_methods, &sc->sc_fifo, - device_get_unit(dev), -1, uaa->info.bIfaceIndex, - UID_ROOT, GID_OPERATOR, 0644)) { - goto detach; - } - - device_set_usb_desc(dev); - - sc->sc_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; - sc->sc_hw.model = MOUSE_MODEL_GENERIC; - sc->sc_hw.hwid = 0; - sc->sc_mode.protocol = MOUSE_PROTO_MSC; - sc->sc_mode.rate = -1; - sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; - sc->sc_mode.accelfactor = 0; - sc->sc_mode.level = 0; - sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; - sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; - sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; - - sc->sc_state = 0; - - sc->sc_left_margin = atp_mickeys_scale_factor; - sc->sc_right_margin = (sc->sc_params->n_xsensors - 1) * - atp_mickeys_scale_factor; - - return (0); - -detach: - atp_detach(dev); - return (ENOMEM); + if (strokep->type == ATP_STROKE_SLIDE) + sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; } -static int -atp_detach(device_t dev) +static boolean_t +atp_is_horizontal_scroll(const atp_stroke_t *strokep) { - struct atp_softc *sc; + 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); +} - sc = device_get_softc(dev); - if (sc->sc_state & ATP_ENABLED) { - mtx_lock(&sc->sc_mutex); - atp_disable(sc); - mtx_unlock(&sc->sc_mutex); - } - - usb_fifo_detach(&sc->sc_fifo); - - usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER); - - mtx_destroy(&sc->sc_mutex); - - return (0); +static 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_intr(struct usb_xfer *xfer, usb_error_t error) +atp_reap_sibling_zombies(void *arg) { - 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; + 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; - usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + DPRINTFN(ATP_LLEVEL_INFO, "\n"); - 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; + TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { + if ((strokep->flags & ATSF_ZOMBIE) == 0) + continue; - 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; - } - - /* 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.obutton = sc->sc_status.button; - - /* Get the state of the physical buttton. */ - sc->sc_status.button = (status_bits & ATP_STATUS_BUTTON) ? - MOUSE_BUTTON1DOWN : 0; - if (sc->sc_status.button != 0) { - /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */ - sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; - } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) { - /* Assume a button-press with DOUBLE_TAP_N_DRAG. */ - sc->sc_status.button = MOUSE_BUTTON1DOWN; - } - - sc->sc_status.flags |= - sc->sc_status.button ^ sc->sc_status.obutton; - if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) { - DPRINTFN(ATP_LLEVEL_INFO, "button %s\n", - ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ? - "pressed" : "released")); - } 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); - } - - if (sc->sc_status.flags & - (MOUSE_POSCHANGED | MOUSE_STDBUTTONSCHANGED)) { - int dx, dy; - u_int n_movements; - - 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]; - - if ((stroke->components[X].movement) || - (stroke->components[Y].movement)) { - dx += stroke->components[X].movement; - dy += stroke->components[Y].movement; - 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 */ - } - 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"); - - /* - * 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))); - } - - sc->sc_idlecount = 0; - usbd_transfer_start(sc->sc_xfer[ATP_RESET]); - } + if (strokep->type == ATP_STROKE_TOUCH) { + n_touches_reaped++; } else { - sc->sc_idlecount = 0; + 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; + } } - case USB_ST_SETUP: - tr_setup: - /* check if we can put more data into the FIFO */ - if (usb_fifo_put_bytes_max( - sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { - usbd_xfer_set_frame_len(xfer, 0, - sc->sc_params->data_len); - usbd_transfer_submit(xfer); - } - break; - - default: /* Error */ - if (error != USB_ERR_CANCELLED) { - /* try clear stall first */ - usbd_xfer_set_stall(xfer); - goto tr_setup; - } - break; + atp_free_stroke(sc, strokep); } - return; + DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", + n_touches_reaped + n_slides_reaped); + sc->sc_state &= ~ATP_ZOMBIES_EXIST; + + /* No further processing necessary if physical button is depressed. */ + if (sc->sc_ibtn != 0) + return; + + if ((n_touches_reaped == 0) && (n_slides_reaped == 0)) + return; + + /* Add a pair of virtual button events (button-down and button-up) if + * the physical button isn't pressed. */ + if (n_touches_reaped != 0) { + if (n_touches_reaped < atp_tap_minimum) + return; + + switch (n_touches_reaped) { + case 1: + atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); + microtime(&sc->sc_touch_reap_time); /* remember this time */ + break; + case 2: + atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN); + break; + case 3: + atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN); + break; + default: + /* we handle taps of only up to 3 fingers */ + return; + } + atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ + + } else if ((n_slides_reaped == 2) && (n_horizontal_scrolls == 2)) { + if (horizontal_scroll < 0) + atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON4DOWN); + else + atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON5DOWN); + atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ + } +} + +/* Switch a given touch stroke to being a slide. */ +static void +atp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep) +{ + strokep->type = ATP_STROKE_SLIDE; + + /* Are we at the beginning of a double-click-n-drag? */ + if ((sc->sc_n_strokes == 1) && + ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) && + timevalcmp(&strokep->ctime, &sc->sc_touch_reap_time, >)) { + struct timeval delta; + struct timeval window = { + atp_double_tap_threshold / 1000000, + atp_double_tap_threshold % 1000000 + }; + + delta = strokep->ctime; + timevalsub(&delta, &sc->sc_touch_reap_time); + if (timevalcmp(&delta, &window, <=)) + sc->sc_state |= ATP_DOUBLE_TAP_DRAG; + } } static void -atp_add_to_queue(struct atp_softc *sc, int dx, int dy, uint32_t buttons_in) +atp_reset_buf(struct atp_softc *sc) +{ + /* reset read queue */ + usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); +} + +static void +atp_add_to_queue(struct atp_softc *sc, int dx, int dy, int dz, + uint32_t buttons_in) { uint32_t buttons_out; uint8_t buf[8]; dx = imin(dx, 254); dx = imax(dx, -256); dy = imin(dy, 254); dy = imax(dy, -256); + dz = imin(dz, 126); dz = imax(dz, -128); buttons_out = MOUSE_MSC_BUTTONS; if (buttons_in & MOUSE_BUTTON1DOWN) @@ -1994,20 +2144,283 @@ atp_add_to_queue(struct atp_softc *sc, int dx, int dy, uint32_t buttons_in) 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. */ + 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 void -atp_reset_buf(struct atp_softc *sc) +static int +atp_probe(device_t self) { - /* reset read queue */ - usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + + if (uaa->info.bInterfaceClass != UICLASS_HID) + return (ENXIO); + /* + * Note: for some reason, the check + * (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) doesn't hold true + * for wellspring trackpads, so we've removed it from the common path. + */ + + if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0) + return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ? + 0 : ENXIO); + + if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0) + if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX) + return (0); + + return (ENXIO); +} + +static int +atp_attach(device_t dev) +{ + struct atp_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + usb_error_t err; + void *descriptor_ptr = NULL; + uint16_t descriptor_len; + unsigned long di; + + DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc); + + sc->sc_dev = dev; + sc->sc_usb_device = uaa->device; + + /* Get HID descriptor */ + if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr, + &descriptor_len, M_TEMP, uaa->info.bIfaceIndex) != + USB_ERR_NORMAL_COMPLETION) + return (ENXIO); + + /* Get HID report descriptor length */ + sc->sc_expected_sensor_data_len = hid_report_size(descriptor_ptr, + descriptor_len, hid_input, NULL); + free(descriptor_ptr, M_TEMP); + + if ((sc->sc_expected_sensor_data_len <= 0) || + (sc->sc_expected_sensor_data_len > ATP_SENSOR_DATA_BUF_MAX)) { + DPRINTF("atp_attach: datalength invalid or too large: %d\n", + sc->sc_expected_sensor_data_len); + return (ENXIO); + } + + /* + * By default the touchpad behaves like an HID device, sending + * packets with reportID = 2. Such reports contain only + * limited information--they encode movement deltas and button + * events,--but do not include data from the pressure + * sensors. The device input mode can be switched from HID + * reports to raw sensor data using vendor-specific USB + * control commands. + */ + if ((err = atp_set_device_mode(sc, RAW_SENSOR_MODE)) != 0) { + DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err); + return (ENXIO); + } + + mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE); + + di = USB_GET_DRIVER_INFO(uaa); + + sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di); + + switch(sc->sc_family) { + case TRACKPAD_FAMILY_FOUNTAIN_GEYSER: + sc->sc_params = + &fg_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; + sc->sensor_data_interpreter = fg_interpret_sensor_data; + break; + case TRACKPAD_FAMILY_WELLSPRING: + sc->sc_params = + &wsp_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; + sc->sensor_data_interpreter = wsp_interpret_sensor_data; + break; + default: + goto detach; + } + + err = usbd_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, atp_xfer_config, + ATP_N_TRANSFER, sc, &sc->sc_mutex); + if (err) { + DPRINTF("error=%s\n", usbd_errstr(err)); + goto detach; + } + + if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, + &atp_fifo_methods, &sc->sc_fifo, + device_get_unit(dev), -1, uaa->info.bIfaceIndex, + UID_ROOT, GID_OPERATOR, 0644)) { + goto detach; + } + + device_set_usb_desc(dev); + + sc->sc_hw.buttons = 3; + sc->sc_hw.iftype = MOUSE_IF_USB; + sc->sc_hw.type = MOUSE_PAD; + sc->sc_hw.model = MOUSE_MODEL_GENERIC; + sc->sc_hw.hwid = 0; + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.rate = -1; + sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + sc->sc_mode.accelfactor = 0; + sc->sc_mode.level = 0; + + sc->sc_state = 0; + sc->sc_ibtn = 0; + + callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0); + + return (0); + +detach: + atp_detach(dev); + return (ENOMEM); +} + +static int +atp_detach(device_t dev) +{ + struct atp_softc *sc; + + sc = device_get_softc(dev); + atp_set_device_mode(sc, HID_MODE); + + mtx_lock(&sc->sc_mutex); + callout_drain(&sc->sc_callout); + if (sc->sc_state & ATP_ENABLED) + atp_disable(sc); + mtx_unlock(&sc->sc_mutex); + + usb_fifo_detach(&sc->sc_fifo); + + usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER); + + mtx_destroy(&sc->sc_mutex); + + return (0); +} + +static void +atp_intr(struct usb_xfer *xfer, usb_error_t error) +{ + struct atp_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, sc->sc_sensor_data, len); + if (len < sc->sc_expected_sensor_data_len) { + /* make sure we don't process old data */ + memset(sc->sc_sensor_data + len, 0, + sc->sc_expected_sensor_data_len - len); + } + + sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED | + MOUSE_POSCHANGED); + sc->sc_status.obutton = sc->sc_status.button; + + (sc->sensor_data_interpreter)(sc, len); + + if (sc->sc_status.button != 0) { + /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */ + sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; + } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) { + /* Assume a button-press with DOUBLE_TAP_N_DRAG. */ + sc->sc_status.button = MOUSE_BUTTON1DOWN; + } + + sc->sc_status.flags |= + sc->sc_status.button ^ sc->sc_status.obutton; + if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) { + DPRINTFN(ATP_LLEVEL_INFO, "button %s\n", + ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ? + "pressed" : "released")); + } + + if (sc->sc_status.flags & (MOUSE_POSCHANGED | + MOUSE_STDBUTTONSCHANGED)) { + + atp_stroke_t *strokep; + u_int8_t n_movements = 0; + int dx = 0; + int dy = 0; + int dz = 0; + + TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { + if (strokep->flags & ATSF_ZOMBIE) + continue; + + dx += strokep->movement_dx; + dy += strokep->movement_dy; + if (strokep->movement_dx || + strokep->movement_dy) + n_movements++; + } + + /* average movement if multiple strokes record motion.*/ + if (n_movements > 1) { + dx /= (int)n_movements; + dy /= (int)n_movements; + } + + /* detect multi-finger vertical scrolls */ + if (n_movements >= 2) { + boolean_t all_vertical_scrolls = true; + TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { + if (strokep->flags & ATSF_ZOMBIE) + continue; + + if (!atp_is_vertical_scroll(strokep)) + all_vertical_scrolls = false; + } + if (all_vertical_scrolls) { + dz = dy; + dy = dx = 0; + } + } + + sc->sc_status.dx += dx; + sc->sc_status.dy += dy; + sc->sc_status.dz += dz; + atp_add_to_queue(sc, dx, -dy, -dz, sc->sc_status.button); + } + + case USB_ST_SETUP: + tr_setup: + /* check if we can put more data into the FIFO */ + if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { + usbd_xfer_set_frame_len(xfer, 0, + sc->sc_expected_sensor_data_len); + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } } static void @@ -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); + + /* check for duplicate open, should not happen */ + if (sc->sc_fflags & fflags) + return (EBUSY); + + /* check for first open */ + if (sc->sc_fflags == 0) { + int rc; + if ((rc = atp_enable(sc)) != 0) + return (rc); + } if (fflags & FREAD) { - struct atp_softc *sc = usb_fifo_softc(fifo); - int rc; - - if (sc->sc_state & ATP_ENABLED) - return (EBUSY); - 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); - - atp_disable(sc); + 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); } } -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 69a525c42fe6..55cea59a9546 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 63d9422c8c7c..ad0fc30fe599 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 4250a29c87da..8831f2b25f1d 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 ff47487c55a8..275526015254 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 7e51f2dca0c6..7b57e9df6746 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 000000000000..1f9e93a34fbe --- /dev/null +++ b/tools/tools/iwn/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= iwnstats + +.include diff --git a/tools/tools/iwn/iwnstats/Makefile b/tools/tools/iwn/iwnstats/Makefile new file mode 100644 index 000000000000..06f92acc8a91 --- /dev/null +++ b/tools/tools/iwn/iwnstats/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ + +NO_MAN=1 + +.include + +.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 diff --git a/tools/tools/iwn/iwnstats/iwn_ioctl.c b/tools/tools/iwn/iwnstats/iwn_ioctl.c new file mode 100644 index 000000000000..13c615397a0c --- /dev/null +++ b/tools/tools/iwn/iwnstats/iwn_ioctl.c @@ -0,0 +1,91 @@ +/*- + * 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, + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#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 000000000000..b07ddb54478d --- /dev/null +++ b/tools/tools/iwn/iwnstats/iwn_ioctl.h @@ -0,0 +1,38 @@ +/*- + * 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, + * 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 000000000000..6443ca19c8e9 --- /dev/null +++ b/tools/tools/iwn/iwnstats/iwnstats.h @@ -0,0 +1,40 @@ +/*- + * 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, + * 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 000000000000..7b14cd9c9435 --- /dev/null +++ b/tools/tools/iwn/iwnstats/main.c @@ -0,0 +1,282 @@ +/*- + * 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, + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 6fae5713dae1..545d6dc0523b 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 c27e56d8d13b..b8c8081b6f61 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 diff --git a/usr.sbin/pmcstat/pmcpl_annotate_cg.c b/usr.sbin/pmcstat/pmcpl_annotate_cg.c new file mode 100644 index 000000000000..e90bda1ac522 --- /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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ??: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 000000000000..bd655f7c9e01 --- /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 cc43b820690d..3edba3f20db4 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 \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 6b64b76074fb..c8ec14de19c9 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 fdcf9c403702..f0e493959acb 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 { @@ -213,6 +214,11 @@ static struct pmc_plugins { .pl_topkeypress = pmcpl_ct_topkeypress, .pl_topdisplay = pmcpl_ct_topdisplay }, + { + .pl_name = "annotate_cg", + .pl_process = pmcpl_annotate_cg_process + }, + { .pl_name = NULL }