1b1f1b07fSVladimir Kondratyev /*- 2b1f1b07fSVladimir Kondratyev * Copyright (c) 2018-2019 Marc Priggemeyer <marc.priggemeyer@gmail.com> 3b1f1b07fSVladimir Kondratyev * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org> 4b1f1b07fSVladimir Kondratyev * 5b1f1b07fSVladimir Kondratyev * Redistribution and use in source and binary forms, with or without 6b1f1b07fSVladimir Kondratyev * modification, are permitted provided that the following conditions 7b1f1b07fSVladimir Kondratyev * are met: 8b1f1b07fSVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright 9b1f1b07fSVladimir Kondratyev * notice, this list of conditions and the following disclaimer. 10b1f1b07fSVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright 11b1f1b07fSVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the 12b1f1b07fSVladimir Kondratyev * documentation and/or other materials provided with the distribution. 13b1f1b07fSVladimir Kondratyev * 14b1f1b07fSVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15b1f1b07fSVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16b1f1b07fSVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17b1f1b07fSVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18b1f1b07fSVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19b1f1b07fSVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20b1f1b07fSVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21b1f1b07fSVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22b1f1b07fSVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23b1f1b07fSVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24b1f1b07fSVladimir Kondratyev * SUCH DAMAGE. 25b1f1b07fSVladimir Kondratyev */ 26b1f1b07fSVladimir Kondratyev 27b1f1b07fSVladimir Kondratyev /* 28b1f1b07fSVladimir Kondratyev * I2C HID transport backend. 29b1f1b07fSVladimir Kondratyev */ 30b1f1b07fSVladimir Kondratyev 31b1f1b07fSVladimir Kondratyev #include <sys/cdefs.h> 32b1f1b07fSVladimir Kondratyev #include "opt_hid.h" 33b1f1b07fSVladimir Kondratyev 34b1f1b07fSVladimir Kondratyev #include <sys/param.h> 35b1f1b07fSVladimir Kondratyev #include <sys/bus.h> 36b1f1b07fSVladimir Kondratyev #include <sys/callout.h> 37b1f1b07fSVladimir Kondratyev #include <sys/endian.h> 38b1f1b07fSVladimir Kondratyev #include <sys/kernel.h> 39b1f1b07fSVladimir Kondratyev #include <sys/lock.h> 40b1f1b07fSVladimir Kondratyev #include <sys/malloc.h> 41b1f1b07fSVladimir Kondratyev #include <sys/module.h> 42b1f1b07fSVladimir Kondratyev #include <sys/rman.h> 43b1f1b07fSVladimir Kondratyev #include <sys/sysctl.h> 44b1f1b07fSVladimir Kondratyev #include <sys/systm.h> 45b1f1b07fSVladimir Kondratyev #include <sys/taskqueue.h> 46b1f1b07fSVladimir Kondratyev 47b1f1b07fSVladimir Kondratyev #include <machine/resource.h> 48b1f1b07fSVladimir Kondratyev 49b1f1b07fSVladimir Kondratyev #include <contrib/dev/acpica/include/acpi.h> 50b1f1b07fSVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h> 51b1f1b07fSVladimir Kondratyev #include <dev/acpica/acpivar.h> 52b1f1b07fSVladimir Kondratyev 53b1f1b07fSVladimir Kondratyev #include <dev/evdev/input.h> 54b1f1b07fSVladimir Kondratyev 55b1f1b07fSVladimir Kondratyev #include <dev/hid/hid.h> 56b1f1b07fSVladimir Kondratyev #include <dev/hid/hidquirk.h> 57b1f1b07fSVladimir Kondratyev 58b1f1b07fSVladimir Kondratyev #include <dev/iicbus/iic.h> 59b1f1b07fSVladimir Kondratyev #include <dev/iicbus/iicbus.h> 60b1f1b07fSVladimir Kondratyev #include <dev/iicbus/iiconf.h> 61b1f1b07fSVladimir Kondratyev 62b1f1b07fSVladimir Kondratyev #include "hid_if.h" 63b1f1b07fSVladimir Kondratyev 64b1f1b07fSVladimir Kondratyev #ifdef IICHID_DEBUG 65b1f1b07fSVladimir Kondratyev static int iichid_debug = 0; 66b1f1b07fSVladimir Kondratyev 67b1f1b07fSVladimir Kondratyev static SYSCTL_NODE(_hw, OID_AUTO, iichid, CTLFLAG_RW, 0, "I2C HID"); 68b1f1b07fSVladimir Kondratyev SYSCTL_INT(_hw_iichid, OID_AUTO, debug, CTLFLAG_RWTUN, 69b1f1b07fSVladimir Kondratyev &iichid_debug, 1, "Debug level"); 70b1f1b07fSVladimir Kondratyev 71b1f1b07fSVladimir Kondratyev #define DPRINTFN(sc, n, ...) do { \ 72b1f1b07fSVladimir Kondratyev if (iichid_debug >= (n)) \ 73b1f1b07fSVladimir Kondratyev device_printf((sc)->dev, __VA_ARGS__); \ 74b1f1b07fSVladimir Kondratyev } while (0) 75b1f1b07fSVladimir Kondratyev #define DPRINTF(sc, ...) DPRINTFN(sc, 1, __VA_ARGS__) 76b1f1b07fSVladimir Kondratyev #else 77b1f1b07fSVladimir Kondratyev #define DPRINTFN(...) do {} while (0) 78b1f1b07fSVladimir Kondratyev #define DPRINTF(...) do {} while (0) 79b1f1b07fSVladimir Kondratyev #endif 80b1f1b07fSVladimir Kondratyev 81b1f1b07fSVladimir Kondratyev typedef hid_size_t iichid_size_t; 82b1f1b07fSVladimir Kondratyev #define IICHID_SIZE_MAX (UINT16_MAX - 2) 83b1f1b07fSVladimir Kondratyev 84b1f1b07fSVladimir Kondratyev /* 7.2 */ 85b1f1b07fSVladimir Kondratyev enum { 86b1f1b07fSVladimir Kondratyev I2C_HID_CMD_DESCR = 0x0, 87b1f1b07fSVladimir Kondratyev I2C_HID_CMD_RESET = 0x1, 88b1f1b07fSVladimir Kondratyev I2C_HID_CMD_GET_REPORT = 0x2, 89b1f1b07fSVladimir Kondratyev I2C_HID_CMD_SET_REPORT = 0x3, 90b1f1b07fSVladimir Kondratyev I2C_HID_CMD_GET_IDLE = 0x4, 91b1f1b07fSVladimir Kondratyev I2C_HID_CMD_SET_IDLE = 0x5, 92b1f1b07fSVladimir Kondratyev I2C_HID_CMD_GET_PROTO = 0x6, 93b1f1b07fSVladimir Kondratyev I2C_HID_CMD_SET_PROTO = 0x7, 94b1f1b07fSVladimir Kondratyev I2C_HID_CMD_SET_POWER = 0x8, 95b1f1b07fSVladimir Kondratyev }; 96b1f1b07fSVladimir Kondratyev 97b1f1b07fSVladimir Kondratyev #define I2C_HID_POWER_ON 0x0 98b1f1b07fSVladimir Kondratyev #define I2C_HID_POWER_OFF 0x1 99b1f1b07fSVladimir Kondratyev 100b1f1b07fSVladimir Kondratyev /* 101b1f1b07fSVladimir Kondratyev * Since interrupt resource acquisition is not always possible (in case of GPIO 102b1f1b07fSVladimir Kondratyev * interrupts) iichid now supports a sampling_mode. 103b1f1b07fSVladimir Kondratyev * Set dev.iichid.<unit>.sampling_rate_slow to a value greater then 0 104b1f1b07fSVladimir Kondratyev * to activate sampling. A value of 0 is possible but will not reset the 105b1f1b07fSVladimir Kondratyev * callout and, thereby, disable further report requests. Do not set the 106b1f1b07fSVladimir Kondratyev * sampling_rate_fast value too high as it may result in periodical lags of 107b1f1b07fSVladimir Kondratyev * cursor motion. 108b1f1b07fSVladimir Kondratyev */ 10968e457dfSAlexander Motin #define IICHID_SAMPLING_RATE_FAST 80 110b1f1b07fSVladimir Kondratyev #define IICHID_SAMPLING_RATE_SLOW 10 11168e457dfSAlexander Motin #define IICHID_SAMPLING_HYSTERESIS 16 /* ~ 2x fast / slow */ 112b1f1b07fSVladimir Kondratyev 113b1f1b07fSVladimir Kondratyev /* 5.1.1 - HID Descriptor Format */ 114b1f1b07fSVladimir Kondratyev struct i2c_hid_desc { 115b1f1b07fSVladimir Kondratyev uint16_t wHIDDescLength; 116b1f1b07fSVladimir Kondratyev uint16_t bcdVersion; 117b1f1b07fSVladimir Kondratyev uint16_t wReportDescLength; 118b1f1b07fSVladimir Kondratyev uint16_t wReportDescRegister; 119b1f1b07fSVladimir Kondratyev uint16_t wInputRegister; 120b1f1b07fSVladimir Kondratyev uint16_t wMaxInputLength; 121b1f1b07fSVladimir Kondratyev uint16_t wOutputRegister; 122b1f1b07fSVladimir Kondratyev uint16_t wMaxOutputLength; 123b1f1b07fSVladimir Kondratyev uint16_t wCommandRegister; 124b1f1b07fSVladimir Kondratyev uint16_t wDataRegister; 125b1f1b07fSVladimir Kondratyev uint16_t wVendorID; 126b1f1b07fSVladimir Kondratyev uint16_t wProductID; 127b1f1b07fSVladimir Kondratyev uint16_t wVersionID; 128b1f1b07fSVladimir Kondratyev uint32_t reserved; 129b1f1b07fSVladimir Kondratyev } __packed; 130b1f1b07fSVladimir Kondratyev 13134e051c4SVladimir Kondratyev #define IICHID_REG_NONE -1 13234e051c4SVladimir Kondratyev #define IICHID_REG_ACPI (UINT16_MAX + 1) 13334e051c4SVladimir Kondratyev #define IICHID_REG_ELAN 0x0001 13434e051c4SVladimir Kondratyev 13534e051c4SVladimir Kondratyev static const struct iichid_id { 13634e051c4SVladimir Kondratyev char *id; 13734e051c4SVladimir Kondratyev int reg; 13834e051c4SVladimir Kondratyev } iichid_ids[] = { 13934e051c4SVladimir Kondratyev { "ELAN0000", IICHID_REG_ELAN }, 14034e051c4SVladimir Kondratyev { "PNP0C50", IICHID_REG_ACPI }, 14134e051c4SVladimir Kondratyev { "ACPI0C50", IICHID_REG_ACPI }, 14234e051c4SVladimir Kondratyev { NULL, 0 }, 143b1f1b07fSVladimir Kondratyev }; 144b1f1b07fSVladimir Kondratyev 145b1f1b07fSVladimir Kondratyev enum iichid_powerstate_how { 146b1f1b07fSVladimir Kondratyev IICHID_PS_NULL, 147b1f1b07fSVladimir Kondratyev IICHID_PS_ON, 148b1f1b07fSVladimir Kondratyev IICHID_PS_OFF, 149b1f1b07fSVladimir Kondratyev }; 150b1f1b07fSVladimir Kondratyev 151b1f1b07fSVladimir Kondratyev /* 152b1f1b07fSVladimir Kondratyev * Locking: no internal locks are used. To serialize access to shared members, 153b1f1b07fSVladimir Kondratyev * external iicbus lock should be taken. That allows to make locking greatly 154b1f1b07fSVladimir Kondratyev * simple at the cost of running front interrupt handlers with locked bus. 155b1f1b07fSVladimir Kondratyev */ 156b1f1b07fSVladimir Kondratyev struct iichid_softc { 157b1f1b07fSVladimir Kondratyev device_t dev; 158b1f1b07fSVladimir Kondratyev 159b1f1b07fSVladimir Kondratyev bool probe_done; 160b1f1b07fSVladimir Kondratyev int probe_result; 161b1f1b07fSVladimir Kondratyev 162b1f1b07fSVladimir Kondratyev struct hid_device_info hw; 163b1f1b07fSVladimir Kondratyev uint16_t addr; /* Shifted left by 1 */ 164b1f1b07fSVladimir Kondratyev struct i2c_hid_desc desc; 165b1f1b07fSVladimir Kondratyev 166b1f1b07fSVladimir Kondratyev hid_intr_t *intr_handler; 167b1f1b07fSVladimir Kondratyev void *intr_ctx; 168b1f1b07fSVladimir Kondratyev uint8_t *intr_buf; 169b1f1b07fSVladimir Kondratyev iichid_size_t intr_bufsize; 170b1f1b07fSVladimir Kondratyev 171b1f1b07fSVladimir Kondratyev int irq_rid; 172b1f1b07fSVladimir Kondratyev struct resource *irq_res; 173b1f1b07fSVladimir Kondratyev void *irq_cookie; 174b1f1b07fSVladimir Kondratyev 175b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 176b1f1b07fSVladimir Kondratyev int sampling_rate_slow; /* iicbus lock */ 177b1f1b07fSVladimir Kondratyev int sampling_rate_fast; 178b1f1b07fSVladimir Kondratyev int sampling_hysteresis; 179b1f1b07fSVladimir Kondratyev int missing_samples; /* iicbus lock */ 1808c86b981SAlexander Motin int dup_samples; /* iicbus lock */ 1818c86b981SAlexander Motin iichid_size_t dup_size; /* iicbus lock */ 182b1f1b07fSVladimir Kondratyev bool callout_setup; /* iicbus lock */ 1838c86b981SAlexander Motin uint8_t *dup_buf; 184b1f1b07fSVladimir Kondratyev struct taskqueue *taskqueue; 185a8f80c0cSAlexander Motin struct timeout_task sampling_task; /* iicbus lock */ 186b1f1b07fSVladimir Kondratyev #endif 187b1f1b07fSVladimir Kondratyev 18882626fefSVladimir Kondratyev struct task suspend_task; 189b1f1b07fSVladimir Kondratyev bool open; /* iicbus lock */ 190b1f1b07fSVladimir Kondratyev bool suspend; /* iicbus lock */ 191b1f1b07fSVladimir Kondratyev bool power_on; /* iicbus lock */ 192b1f1b07fSVladimir Kondratyev }; 193b1f1b07fSVladimir Kondratyev 194b1f1b07fSVladimir Kondratyev static device_probe_t iichid_probe; 195b1f1b07fSVladimir Kondratyev static device_attach_t iichid_attach; 196b1f1b07fSVladimir Kondratyev static device_detach_t iichid_detach; 197b1f1b07fSVladimir Kondratyev static device_resume_t iichid_resume; 198b1f1b07fSVladimir Kondratyev static device_suspend_t iichid_suspend; 199b1f1b07fSVladimir Kondratyev 20082626fefSVladimir Kondratyev static void iichid_suspend_task(void *, int); 20182626fefSVladimir Kondratyev 202b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 203b1f1b07fSVladimir Kondratyev static int iichid_setup_callout(struct iichid_softc *); 204b1f1b07fSVladimir Kondratyev static int iichid_reset_callout(struct iichid_softc *); 205b1f1b07fSVladimir Kondratyev static void iichid_teardown_callout(struct iichid_softc *); 206b1f1b07fSVladimir Kondratyev #endif 207b1f1b07fSVladimir Kondratyev 20834e051c4SVladimir Kondratyev static inline int 209b1f1b07fSVladimir Kondratyev acpi_is_iichid(ACPI_HANDLE handle) 210b1f1b07fSVladimir Kondratyev { 21134e051c4SVladimir Kondratyev const struct iichid_id *ids; 212b1f1b07fSVladimir Kondratyev UINT32 sta; 21334e051c4SVladimir Kondratyev int reg; 214b1f1b07fSVladimir Kondratyev 21534e051c4SVladimir Kondratyev for (ids = iichid_ids; ids->id != NULL; ids++) { 21634e051c4SVladimir Kondratyev if (acpi_MatchHid(handle, ids->id)) { 21734e051c4SVladimir Kondratyev reg = ids->reg; 218b1f1b07fSVladimir Kondratyev break; 219b1f1b07fSVladimir Kondratyev } 22034e051c4SVladimir Kondratyev } 22134e051c4SVladimir Kondratyev if (ids->id == NULL) 22234e051c4SVladimir Kondratyev return (IICHID_REG_NONE); 223b1f1b07fSVladimir Kondratyev 224b1f1b07fSVladimir Kondratyev /* 225b1f1b07fSVladimir Kondratyev * If no _STA method or if it failed, then assume that 226b1f1b07fSVladimir Kondratyev * the device is present. 227b1f1b07fSVladimir Kondratyev */ 228b1f1b07fSVladimir Kondratyev if (ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) || 229b1f1b07fSVladimir Kondratyev ACPI_DEVICE_PRESENT(sta)) 23034e051c4SVladimir Kondratyev return (reg); 231b1f1b07fSVladimir Kondratyev 23234e051c4SVladimir Kondratyev return (IICHID_REG_NONE); 233b1f1b07fSVladimir Kondratyev } 234b1f1b07fSVladimir Kondratyev 235b1f1b07fSVladimir Kondratyev static ACPI_STATUS 236b1f1b07fSVladimir Kondratyev iichid_get_config_reg(ACPI_HANDLE handle, uint16_t *config_reg) 237b1f1b07fSVladimir Kondratyev { 238b1f1b07fSVladimir Kondratyev ACPI_OBJECT *result; 239b1f1b07fSVladimir Kondratyev ACPI_BUFFER acpi_buf; 240b1f1b07fSVladimir Kondratyev ACPI_STATUS status; 241b1f1b07fSVladimir Kondratyev 242b1f1b07fSVladimir Kondratyev /* 243b1f1b07fSVladimir Kondratyev * function (_DSM) to be evaluated to retrieve the address of 244b1f1b07fSVladimir Kondratyev * the configuration register of the HID device. 245b1f1b07fSVladimir Kondratyev */ 246b1f1b07fSVladimir Kondratyev /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ 247b1f1b07fSVladimir Kondratyev static uint8_t dsm_guid[ACPI_UUID_LENGTH] = { 248b1f1b07fSVladimir Kondratyev 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, 249b1f1b07fSVladimir Kondratyev 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, 250b1f1b07fSVladimir Kondratyev }; 251b1f1b07fSVladimir Kondratyev 252b1f1b07fSVladimir Kondratyev status = acpi_EvaluateDSMTyped(handle, dsm_guid, 1, 1, NULL, &acpi_buf, 253b1f1b07fSVladimir Kondratyev ACPI_TYPE_INTEGER); 254b1f1b07fSVladimir Kondratyev if (ACPI_FAILURE(status)) { 255b1f1b07fSVladimir Kondratyev printf("%s: error evaluating _DSM\n", __func__); 256b1f1b07fSVladimir Kondratyev return (status); 257b1f1b07fSVladimir Kondratyev } 258b1f1b07fSVladimir Kondratyev result = (ACPI_OBJECT *) acpi_buf.Pointer; 259b1f1b07fSVladimir Kondratyev *config_reg = result->Integer.Value & 0xFFFF; 260b1f1b07fSVladimir Kondratyev 261b1f1b07fSVladimir Kondratyev AcpiOsFree(result); 262b1f1b07fSVladimir Kondratyev return (status); 263b1f1b07fSVladimir Kondratyev } 264b1f1b07fSVladimir Kondratyev 265b1f1b07fSVladimir Kondratyev static int 266b1f1b07fSVladimir Kondratyev iichid_cmd_read(struct iichid_softc* sc, void *buf, iichid_size_t maxlen, 267b1f1b07fSVladimir Kondratyev iichid_size_t *actual_len) 268b1f1b07fSVladimir Kondratyev { 269b1f1b07fSVladimir Kondratyev /* 270b1f1b07fSVladimir Kondratyev * 6.1.3 - Retrieval of Input Reports 271b1f1b07fSVladimir Kondratyev * DEVICE returns the length (2 Bytes) and the entire Input Report. 272b1f1b07fSVladimir Kondratyev */ 273b1f1b07fSVladimir Kondratyev uint8_t actbuf[2] = { 0, 0 }; 274b1f1b07fSVladimir Kondratyev /* Read actual input report length. */ 275b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 276b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD | IIC_M_NOSTOP, sizeof(actbuf), actbuf }, 277b1f1b07fSVladimir Kondratyev }; 278b1f1b07fSVladimir Kondratyev uint16_t actlen; 279b1f1b07fSVladimir Kondratyev int error; 280b1f1b07fSVladimir Kondratyev 281b1f1b07fSVladimir Kondratyev error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); 282b1f1b07fSVladimir Kondratyev if (error != 0) 283b1f1b07fSVladimir Kondratyev return (error); 284b1f1b07fSVladimir Kondratyev 285b1f1b07fSVladimir Kondratyev actlen = actbuf[0] | actbuf[1] << 8; 286b1f1b07fSVladimir Kondratyev if (actlen <= 2 || actlen == 0xFFFF || maxlen == 0) { 287b1f1b07fSVladimir Kondratyev /* Read and discard 1 byte to send I2C STOP condition. */ 288b1f1b07fSVladimir Kondratyev msgs[0] = (struct iic_msg) 289b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD | IIC_M_NOSTART, 1, actbuf }; 290b1f1b07fSVladimir Kondratyev actlen = 0; 291b1f1b07fSVladimir Kondratyev } else { 292b1f1b07fSVladimir Kondratyev actlen -= 2; 293b1f1b07fSVladimir Kondratyev if (actlen > maxlen) { 294b1f1b07fSVladimir Kondratyev DPRINTF(sc, "input report too big. requested=%d " 295b1f1b07fSVladimir Kondratyev "received=%d\n", maxlen, actlen); 296b1f1b07fSVladimir Kondratyev actlen = maxlen; 297b1f1b07fSVladimir Kondratyev } 298b1f1b07fSVladimir Kondratyev /* Read input report itself. */ 299b1f1b07fSVladimir Kondratyev msgs[0] = (struct iic_msg) 300b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD | IIC_M_NOSTART, actlen, buf }; 301b1f1b07fSVladimir Kondratyev } 302b1f1b07fSVladimir Kondratyev 303b1f1b07fSVladimir Kondratyev error = iicbus_transfer(sc->dev, msgs, 1); 304b1f1b07fSVladimir Kondratyev if (error == 0 && actual_len != NULL) 305b1f1b07fSVladimir Kondratyev *actual_len = actlen; 306b1f1b07fSVladimir Kondratyev 307b1f1b07fSVladimir Kondratyev DPRINTFN(sc, 5, 308b1f1b07fSVladimir Kondratyev "%*D - %*D\n", 2, actbuf, " ", msgs[0].len, msgs[0].buf, " "); 309b1f1b07fSVladimir Kondratyev 310b1f1b07fSVladimir Kondratyev return (error); 311b1f1b07fSVladimir Kondratyev } 312b1f1b07fSVladimir Kondratyev 313b1f1b07fSVladimir Kondratyev static int 314b1f1b07fSVladimir Kondratyev iichid_cmd_write(struct iichid_softc *sc, const void *buf, iichid_size_t len) 315b1f1b07fSVladimir Kondratyev { 316b1f1b07fSVladimir Kondratyev /* 6.2.3 - Sending Output Reports. */ 317b1f1b07fSVladimir Kondratyev uint8_t *cmdreg = (uint8_t *)&sc->desc.wOutputRegister; 318b1f1b07fSVladimir Kondratyev uint16_t replen = 2 + len; 319b1f1b07fSVladimir Kondratyev uint8_t cmd[4] = { cmdreg[0], cmdreg[1], replen & 0xFF, replen >> 8 }; 320b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 321b1f1b07fSVladimir Kondratyev {sc->addr, IIC_M_WR | IIC_M_NOSTOP, sizeof(cmd), cmd}, 322b1f1b07fSVladimir Kondratyev {sc->addr, IIC_M_WR | IIC_M_NOSTART, len, __DECONST(void *, buf)}, 323b1f1b07fSVladimir Kondratyev }; 324b1f1b07fSVladimir Kondratyev 325b1f1b07fSVladimir Kondratyev if (le16toh(sc->desc.wMaxOutputLength) == 0) 326b1f1b07fSVladimir Kondratyev return (IIC_ENOTSUPP); 327b1f1b07fSVladimir Kondratyev if (len < 2) 328b1f1b07fSVladimir Kondratyev return (IIC_ENOTSUPP); 329b1f1b07fSVladimir Kondratyev 330b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_WRITE (len %d): " 331b1f1b07fSVladimir Kondratyev "%*D\n", len, len, buf, " "); 332b1f1b07fSVladimir Kondratyev 333b1f1b07fSVladimir Kondratyev return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); 334b1f1b07fSVladimir Kondratyev } 335b1f1b07fSVladimir Kondratyev 336b1f1b07fSVladimir Kondratyev static int 337b1f1b07fSVladimir Kondratyev iichid_cmd_get_hid_desc(struct iichid_softc *sc, uint16_t config_reg, 338b1f1b07fSVladimir Kondratyev struct i2c_hid_desc *hid_desc) 339b1f1b07fSVladimir Kondratyev { 340b1f1b07fSVladimir Kondratyev /* 341b1f1b07fSVladimir Kondratyev * 5.2.2 - HID Descriptor Retrieval 342b1f1b07fSVladimir Kondratyev * register is passed from the controller. 343b1f1b07fSVladimir Kondratyev */ 344b1f1b07fSVladimir Kondratyev uint16_t cmd = htole16(config_reg); 345b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 346b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_WR | IIC_M_NOSTOP, 2, (uint8_t *)&cmd }, 347b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD, sizeof(*hid_desc), (uint8_t *)hid_desc }, 348b1f1b07fSVladimir Kondratyev }; 349b1f1b07fSVladimir Kondratyev int error; 350b1f1b07fSVladimir Kondratyev 351b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_DESCR at 0x%x\n", config_reg); 352b1f1b07fSVladimir Kondratyev 353b1f1b07fSVladimir Kondratyev error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); 354b1f1b07fSVladimir Kondratyev if (error != 0) 355b1f1b07fSVladimir Kondratyev return (error); 356b1f1b07fSVladimir Kondratyev 357b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID descriptor: %*D\n", 358b1f1b07fSVladimir Kondratyev (int)sizeof(struct i2c_hid_desc), hid_desc, " "); 359b1f1b07fSVladimir Kondratyev 360b1f1b07fSVladimir Kondratyev return (0); 361b1f1b07fSVladimir Kondratyev } 362b1f1b07fSVladimir Kondratyev 363b1f1b07fSVladimir Kondratyev static int 364b1f1b07fSVladimir Kondratyev iichid_set_power(struct iichid_softc *sc, uint8_t param) 365b1f1b07fSVladimir Kondratyev { 366b1f1b07fSVladimir Kondratyev uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; 367b1f1b07fSVladimir Kondratyev uint8_t cmd[] = { cmdreg[0], cmdreg[1], param, I2C_HID_CMD_SET_POWER }; 368b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 369b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_WR, sizeof(cmd), cmd }, 370b1f1b07fSVladimir Kondratyev }; 371b1f1b07fSVladimir Kondratyev 372b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_SET_POWER(%d)\n", param); 373b1f1b07fSVladimir Kondratyev 374b1f1b07fSVladimir Kondratyev return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); 375b1f1b07fSVladimir Kondratyev } 376b1f1b07fSVladimir Kondratyev 377b1f1b07fSVladimir Kondratyev static int 378b1f1b07fSVladimir Kondratyev iichid_reset(struct iichid_softc *sc) 379b1f1b07fSVladimir Kondratyev { 380b1f1b07fSVladimir Kondratyev uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; 381b1f1b07fSVladimir Kondratyev uint8_t cmd[] = { cmdreg[0], cmdreg[1], 0, I2C_HID_CMD_RESET }; 382b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 383b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_WR, sizeof(cmd), cmd }, 384b1f1b07fSVladimir Kondratyev }; 385b1f1b07fSVladimir Kondratyev 386b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_RESET\n"); 387b1f1b07fSVladimir Kondratyev 388b1f1b07fSVladimir Kondratyev return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); 389b1f1b07fSVladimir Kondratyev } 390b1f1b07fSVladimir Kondratyev 391b1f1b07fSVladimir Kondratyev static int 392b1f1b07fSVladimir Kondratyev iichid_cmd_get_report_desc(struct iichid_softc* sc, void *buf, 393b1f1b07fSVladimir Kondratyev iichid_size_t len) 394b1f1b07fSVladimir Kondratyev { 395b1f1b07fSVladimir Kondratyev uint16_t cmd = sc->desc.wReportDescRegister; 396b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 397b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_WR | IIC_M_NOSTOP, 2, (uint8_t *)&cmd }, 398b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD, len, buf }, 399b1f1b07fSVladimir Kondratyev }; 400b1f1b07fSVladimir Kondratyev int error; 401b1f1b07fSVladimir Kondratyev 402b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_REPORT_DESCR at 0x%x with size %d\n", 403b1f1b07fSVladimir Kondratyev le16toh(cmd), len); 404b1f1b07fSVladimir Kondratyev 405b1f1b07fSVladimir Kondratyev error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); 406b1f1b07fSVladimir Kondratyev if (error != 0) 407b1f1b07fSVladimir Kondratyev return (error); 408b1f1b07fSVladimir Kondratyev 409b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID report descriptor: %*D\n", len, buf, " "); 410b1f1b07fSVladimir Kondratyev 411b1f1b07fSVladimir Kondratyev return (0); 412b1f1b07fSVladimir Kondratyev } 413b1f1b07fSVladimir Kondratyev 414b1f1b07fSVladimir Kondratyev static int 415b1f1b07fSVladimir Kondratyev iichid_cmd_get_report(struct iichid_softc* sc, void *buf, iichid_size_t maxlen, 416b1f1b07fSVladimir Kondratyev iichid_size_t *actual_len, uint8_t type, uint8_t id) 417b1f1b07fSVladimir Kondratyev { 418b1f1b07fSVladimir Kondratyev /* 419b1f1b07fSVladimir Kondratyev * 7.2.2.4 - "The protocol is optimized for Report < 15. If a 420b1f1b07fSVladimir Kondratyev * report ID >= 15 is necessary, then the Report ID in the Low Byte 421b1f1b07fSVladimir Kondratyev * must be set to 1111 and a Third Byte is appended to the protocol. 422b1f1b07fSVladimir Kondratyev * This Third Byte contains the entire/actual report ID." 423b1f1b07fSVladimir Kondratyev */ 424b1f1b07fSVladimir Kondratyev uint8_t *dtareg = (uint8_t *)&sc->desc.wDataRegister; 425b1f1b07fSVladimir Kondratyev uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; 426b1f1b07fSVladimir Kondratyev uint8_t cmd[] = { /*________|______id>=15_____|______id<15______*/ 427b1f1b07fSVladimir Kondratyev cmdreg[0] , 428b1f1b07fSVladimir Kondratyev cmdreg[1] , 429b1f1b07fSVladimir Kondratyev (id >= 15 ? 15 | (type << 4): id | (type << 4)), 430b1f1b07fSVladimir Kondratyev I2C_HID_CMD_GET_REPORT , 431b1f1b07fSVladimir Kondratyev (id >= 15 ? id : dtareg[0] ), 432b1f1b07fSVladimir Kondratyev (id >= 15 ? dtareg[0] : dtareg[1] ), 433b1f1b07fSVladimir Kondratyev (id >= 15 ? dtareg[1] : 0 ), 434b1f1b07fSVladimir Kondratyev }; 435b1f1b07fSVladimir Kondratyev int cmdlen = (id >= 15 ? 7 : 6 ); 436b1f1b07fSVladimir Kondratyev uint8_t actbuf[2] = { 0, 0 }; 437b1f1b07fSVladimir Kondratyev uint16_t actlen; 438b1f1b07fSVladimir Kondratyev int d, error; 439b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 440b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd }, 441b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD | IIC_M_NOSTOP, 2, actbuf }, 442b1f1b07fSVladimir Kondratyev { sc->addr, IIC_M_RD | IIC_M_NOSTART, maxlen, buf }, 443b1f1b07fSVladimir Kondratyev }; 444b1f1b07fSVladimir Kondratyev 445b1f1b07fSVladimir Kondratyev if (maxlen == 0) 446b1f1b07fSVladimir Kondratyev return (EINVAL); 447b1f1b07fSVladimir Kondratyev 448b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_GET_REPORT %d " 449b1f1b07fSVladimir Kondratyev "(type %d, len %d)\n", id, type, maxlen); 450b1f1b07fSVladimir Kondratyev 451b1f1b07fSVladimir Kondratyev /* 452b1f1b07fSVladimir Kondratyev * 7.2.2.2 - Response will be a 2-byte length value, the report 453b1f1b07fSVladimir Kondratyev * id (1 byte, if defined in Report Descriptor), and then the report. 454b1f1b07fSVladimir Kondratyev */ 455b1f1b07fSVladimir Kondratyev error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); 456b1f1b07fSVladimir Kondratyev if (error != 0) 457b1f1b07fSVladimir Kondratyev return (error); 458b1f1b07fSVladimir Kondratyev 459b1f1b07fSVladimir Kondratyev actlen = actbuf[0] | actbuf[1] << 8; 460b1f1b07fSVladimir Kondratyev if (actlen != maxlen + 2) 461b1f1b07fSVladimir Kondratyev DPRINTF(sc, "response size %d != expected length %d\n", 462b1f1b07fSVladimir Kondratyev actlen, maxlen + 2); 463b1f1b07fSVladimir Kondratyev 464b1f1b07fSVladimir Kondratyev if (actlen <= 2 || actlen == 0xFFFF) 465b1f1b07fSVladimir Kondratyev return (ENOMSG); 466b1f1b07fSVladimir Kondratyev 467b1f1b07fSVladimir Kondratyev d = id != 0 ? *(uint8_t *)buf : 0; 468b1f1b07fSVladimir Kondratyev if (d != id) { 469b1f1b07fSVladimir Kondratyev DPRINTF(sc, "response report id %d != %d\n", d, id); 470b1f1b07fSVladimir Kondratyev return (EBADMSG); 471b1f1b07fSVladimir Kondratyev } 472b1f1b07fSVladimir Kondratyev 473b1f1b07fSVladimir Kondratyev actlen -= 2; 474b1f1b07fSVladimir Kondratyev if (actlen > maxlen) 475b1f1b07fSVladimir Kondratyev actlen = maxlen; 476b1f1b07fSVladimir Kondratyev if (actual_len != NULL) 477b1f1b07fSVladimir Kondratyev *actual_len = actlen; 478b1f1b07fSVladimir Kondratyev 479b1f1b07fSVladimir Kondratyev DPRINTF(sc, "response: %*D %*D\n", 2, actbuf, " ", actlen, buf, " "); 480b1f1b07fSVladimir Kondratyev 481b1f1b07fSVladimir Kondratyev return (0); 482b1f1b07fSVladimir Kondratyev } 483b1f1b07fSVladimir Kondratyev 484b1f1b07fSVladimir Kondratyev static int 485b1f1b07fSVladimir Kondratyev iichid_cmd_set_report(struct iichid_softc* sc, const void *buf, 486b1f1b07fSVladimir Kondratyev iichid_size_t len, uint8_t type, uint8_t id) 487b1f1b07fSVladimir Kondratyev { 488b1f1b07fSVladimir Kondratyev /* 489b1f1b07fSVladimir Kondratyev * 7.2.2.4 - "The protocol is optimized for Report < 15. If a 490b1f1b07fSVladimir Kondratyev * report ID >= 15 is necessary, then the Report ID in the Low Byte 491b1f1b07fSVladimir Kondratyev * must be set to 1111 and a Third Byte is appended to the protocol. 492b1f1b07fSVladimir Kondratyev * This Third Byte contains the entire/actual report ID." 493b1f1b07fSVladimir Kondratyev */ 494b1f1b07fSVladimir Kondratyev uint8_t *dtareg = (uint8_t *)&sc->desc.wDataRegister; 495b1f1b07fSVladimir Kondratyev uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; 496b1f1b07fSVladimir Kondratyev uint16_t replen = 2 + len; 497b1f1b07fSVladimir Kondratyev uint8_t cmd[] = { /*________|______id>=15_____|______id<15______*/ 498b1f1b07fSVladimir Kondratyev cmdreg[0] , 499b1f1b07fSVladimir Kondratyev cmdreg[1] , 500b1f1b07fSVladimir Kondratyev (id >= 15 ? 15 | (type << 4): id | (type << 4)), 501b1f1b07fSVladimir Kondratyev I2C_HID_CMD_SET_REPORT , 502b1f1b07fSVladimir Kondratyev (id >= 15 ? id : dtareg[0] ), 503b1f1b07fSVladimir Kondratyev (id >= 15 ? dtareg[0] : dtareg[1] ), 504b1f1b07fSVladimir Kondratyev (id >= 15 ? dtareg[1] : replen & 0xff ), 505b1f1b07fSVladimir Kondratyev (id >= 15 ? replen & 0xff : replen >> 8 ), 506b1f1b07fSVladimir Kondratyev (id >= 15 ? replen >> 8 : 0 ), 507b1f1b07fSVladimir Kondratyev }; 508b1f1b07fSVladimir Kondratyev int cmdlen = (id >= 15 ? 9 : 8 ); 509b1f1b07fSVladimir Kondratyev struct iic_msg msgs[] = { 510b1f1b07fSVladimir Kondratyev {sc->addr, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd}, 511b1f1b07fSVladimir Kondratyev {sc->addr, IIC_M_WR | IIC_M_NOSTART, len, __DECONST(void *, buf)}, 512b1f1b07fSVladimir Kondratyev }; 513b1f1b07fSVladimir Kondratyev 514b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID command I2C_HID_CMD_SET_REPORT %d (type %d, len %d): " 515b1f1b07fSVladimir Kondratyev "%*D\n", id, type, len, len, buf, " "); 516b1f1b07fSVladimir Kondratyev 517b1f1b07fSVladimir Kondratyev return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); 518b1f1b07fSVladimir Kondratyev } 519b1f1b07fSVladimir Kondratyev 520b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 521b1f1b07fSVladimir Kondratyev static void 522a8f80c0cSAlexander Motin iichid_sampling_task(void *context, int pending) 523b1f1b07fSVladimir Kondratyev { 524b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 525b1f1b07fSVladimir Kondratyev device_t parent; 526b1f1b07fSVladimir Kondratyev iichid_size_t actual; 527b1f1b07fSVladimir Kondratyev bool bus_requested; 5288c86b981SAlexander Motin int error, rate; 529b1f1b07fSVladimir Kondratyev 530b1f1b07fSVladimir Kondratyev sc = context; 531b1f1b07fSVladimir Kondratyev parent = device_get_parent(sc->dev); 532b1f1b07fSVladimir Kondratyev 533b1f1b07fSVladimir Kondratyev bus_requested = false; 534b1f1b07fSVladimir Kondratyev if (iicbus_request_bus(parent, sc->dev, IIC_WAIT) != 0) 535b1f1b07fSVladimir Kondratyev goto rearm; 536b1f1b07fSVladimir Kondratyev bus_requested = true; 537b1f1b07fSVladimir Kondratyev 538b1f1b07fSVladimir Kondratyev if (!sc->power_on) 539b1f1b07fSVladimir Kondratyev goto out; 540b1f1b07fSVladimir Kondratyev 541b1f1b07fSVladimir Kondratyev error = iichid_cmd_read(sc, sc->intr_buf, sc->intr_bufsize, &actual); 542b1f1b07fSVladimir Kondratyev if (error == 0) { 543b1f1b07fSVladimir Kondratyev if (actual > 0) { 544b1f1b07fSVladimir Kondratyev sc->intr_handler(sc->intr_ctx, sc->intr_buf, actual); 545b1f1b07fSVladimir Kondratyev sc->missing_samples = 0; 5468c86b981SAlexander Motin if (sc->dup_size != actual || 5478c86b981SAlexander Motin memcmp(sc->dup_buf, sc->intr_buf, actual) != 0) { 5488c86b981SAlexander Motin sc->dup_size = actual; 5498c86b981SAlexander Motin memcpy(sc->dup_buf, sc->intr_buf, actual); 5508c86b981SAlexander Motin sc->dup_samples = 0; 551b1f1b07fSVladimir Kondratyev } else 5528c86b981SAlexander Motin ++sc->dup_samples; 5538c86b981SAlexander Motin } else { 5548c86b981SAlexander Motin if (++sc->missing_samples == 1) 5558c86b981SAlexander Motin sc->intr_handler(sc->intr_ctx, sc->intr_buf, 0); 5568c86b981SAlexander Motin sc->dup_samples = 0; 5578c86b981SAlexander Motin } 558b1f1b07fSVladimir Kondratyev } else 5597fad3ed8SGordon Bergling DPRINTF(sc, "read error occurred: %d\n", error); 560b1f1b07fSVladimir Kondratyev 561b1f1b07fSVladimir Kondratyev rearm: 562b1f1b07fSVladimir Kondratyev if (sc->callout_setup && sc->sampling_rate_slow > 0) { 5638c86b981SAlexander Motin if (sc->missing_samples >= sc->sampling_hysteresis || 5648c86b981SAlexander Motin sc->dup_samples >= sc->sampling_hysteresis) 5658c86b981SAlexander Motin rate = sc->sampling_rate_slow; 5668c86b981SAlexander Motin else 5678c86b981SAlexander Motin rate = sc->sampling_rate_fast; 568a8f80c0cSAlexander Motin taskqueue_enqueue_timeout_sbt(sc->taskqueue, &sc->sampling_task, 56968e457dfSAlexander Motin SBT_1S / MAX(rate, 1), 0, C_PREL(2)); 570b1f1b07fSVladimir Kondratyev } 571b1f1b07fSVladimir Kondratyev out: 572b1f1b07fSVladimir Kondratyev if (bus_requested) 573b1f1b07fSVladimir Kondratyev iicbus_release_bus(parent, sc->dev); 574b1f1b07fSVladimir Kondratyev } 575b1f1b07fSVladimir Kondratyev #endif /* IICHID_SAMPLING */ 576b1f1b07fSVladimir Kondratyev 577b1f1b07fSVladimir Kondratyev static void 578b1f1b07fSVladimir Kondratyev iichid_intr(void *context) 579b1f1b07fSVladimir Kondratyev { 580b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 581b1f1b07fSVladimir Kondratyev device_t parent; 582b1f1b07fSVladimir Kondratyev iichid_size_t maxlen, actual; 583b1f1b07fSVladimir Kondratyev int error; 584b1f1b07fSVladimir Kondratyev 585b1f1b07fSVladimir Kondratyev sc = context; 586b1f1b07fSVladimir Kondratyev parent = device_get_parent(sc->dev); 587b1f1b07fSVladimir Kondratyev 588b1f1b07fSVladimir Kondratyev /* 589b1f1b07fSVladimir Kondratyev * Designware(IG4) driver-specific hack. 590b1f1b07fSVladimir Kondratyev * Requesting of an I2C bus with IIC_DONTWAIT parameter enables polled 591b1f1b07fSVladimir Kondratyev * mode in the driver, making possible iicbus_transfer execution from 592b1f1b07fSVladimir Kondratyev * interrupt handlers and callouts. 593b1f1b07fSVladimir Kondratyev */ 594b1f1b07fSVladimir Kondratyev if (iicbus_request_bus(parent, sc->dev, IIC_DONTWAIT) != 0) 595b1f1b07fSVladimir Kondratyev return; 596b1f1b07fSVladimir Kondratyev 597b1f1b07fSVladimir Kondratyev /* 598b1f1b07fSVladimir Kondratyev * Reading of input reports of I2C devices residing in SLEEP state is 599b1f1b07fSVladimir Kondratyev * not allowed and often returns a garbage. If a HOST needs to 600b1f1b07fSVladimir Kondratyev * communicate with the DEVICE it MUST issue a SET POWER command 601b1f1b07fSVladimir Kondratyev * (to ON) before any other command. As some hardware requires reads to 6025236888dSJ.R. Oldroyd * acknowledge interrupts we fetch only length header and discard it. 603b1f1b07fSVladimir Kondratyev */ 604b1f1b07fSVladimir Kondratyev maxlen = sc->power_on ? sc->intr_bufsize : 0; 605b1f1b07fSVladimir Kondratyev error = iichid_cmd_read(sc, sc->intr_buf, maxlen, &actual); 606b1f1b07fSVladimir Kondratyev if (error == 0) { 607b1f1b07fSVladimir Kondratyev if (sc->power_on) { 608b1f1b07fSVladimir Kondratyev if (actual != 0) 609b1f1b07fSVladimir Kondratyev sc->intr_handler(sc->intr_ctx, sc->intr_buf, 610b1f1b07fSVladimir Kondratyev actual); 611b1f1b07fSVladimir Kondratyev else 612b1f1b07fSVladimir Kondratyev DPRINTF(sc, "no data received\n"); 613b1f1b07fSVladimir Kondratyev } 614b1f1b07fSVladimir Kondratyev } else 6157fad3ed8SGordon Bergling DPRINTF(sc, "read error occurred: %d\n", error); 616b1f1b07fSVladimir Kondratyev 617b1f1b07fSVladimir Kondratyev iicbus_release_bus(parent, sc->dev); 618b1f1b07fSVladimir Kondratyev } 619b1f1b07fSVladimir Kondratyev 620b1f1b07fSVladimir Kondratyev static int 621b1f1b07fSVladimir Kondratyev iichid_set_power_state(struct iichid_softc *sc, 622b1f1b07fSVladimir Kondratyev enum iichid_powerstate_how how_open, 623b1f1b07fSVladimir Kondratyev enum iichid_powerstate_how how_suspend) 624b1f1b07fSVladimir Kondratyev { 625b1f1b07fSVladimir Kondratyev device_t parent; 626b1f1b07fSVladimir Kondratyev int error; 627b1f1b07fSVladimir Kondratyev int how_request; 628b1f1b07fSVladimir Kondratyev bool power_on; 629b1f1b07fSVladimir Kondratyev 630b1f1b07fSVladimir Kondratyev /* 631b1f1b07fSVladimir Kondratyev * Request iicbus early as sc->suspend and sc->power_on 632b1f1b07fSVladimir Kondratyev * are protected by iicbus internal lock. 633b1f1b07fSVladimir Kondratyev */ 634b1f1b07fSVladimir Kondratyev parent = device_get_parent(sc->dev); 635b1f1b07fSVladimir Kondratyev /* Allow to interrupt open()/close() handlers by SIGINT */ 636b1f1b07fSVladimir Kondratyev how_request = how_open == IICHID_PS_NULL ? IIC_WAIT : IIC_INTRWAIT; 637b1f1b07fSVladimir Kondratyev error = iicbus_request_bus(parent, sc->dev, how_request); 638b1f1b07fSVladimir Kondratyev if (error != 0) 639b1f1b07fSVladimir Kondratyev return (error); 640b1f1b07fSVladimir Kondratyev 641b1f1b07fSVladimir Kondratyev switch (how_open) { 642b1f1b07fSVladimir Kondratyev case IICHID_PS_ON: 643b1f1b07fSVladimir Kondratyev sc->open = true; 644b1f1b07fSVladimir Kondratyev break; 645b1f1b07fSVladimir Kondratyev case IICHID_PS_OFF: 646b1f1b07fSVladimir Kondratyev sc->open = false; 647b1f1b07fSVladimir Kondratyev break; 648b1f1b07fSVladimir Kondratyev case IICHID_PS_NULL: 649b1f1b07fSVladimir Kondratyev default: 650b1f1b07fSVladimir Kondratyev break; 651b1f1b07fSVladimir Kondratyev } 652b1f1b07fSVladimir Kondratyev 653b1f1b07fSVladimir Kondratyev switch (how_suspend) { 654b1f1b07fSVladimir Kondratyev case IICHID_PS_ON: 655b1f1b07fSVladimir Kondratyev sc->suspend = false; 656b1f1b07fSVladimir Kondratyev break; 657b1f1b07fSVladimir Kondratyev case IICHID_PS_OFF: 658b1f1b07fSVladimir Kondratyev sc->suspend = true; 659b1f1b07fSVladimir Kondratyev break; 660b1f1b07fSVladimir Kondratyev case IICHID_PS_NULL: 661b1f1b07fSVladimir Kondratyev default: 662b1f1b07fSVladimir Kondratyev break; 663b1f1b07fSVladimir Kondratyev } 664b1f1b07fSVladimir Kondratyev 665b1f1b07fSVladimir Kondratyev power_on = sc->open & !sc->suspend; 666b1f1b07fSVladimir Kondratyev 667b1f1b07fSVladimir Kondratyev if (power_on != sc->power_on) { 668b1f1b07fSVladimir Kondratyev error = iichid_set_power(sc, 669b1f1b07fSVladimir Kondratyev power_on ? I2C_HID_POWER_ON : I2C_HID_POWER_OFF); 670b1f1b07fSVladimir Kondratyev 671b1f1b07fSVladimir Kondratyev sc->power_on = power_on; 672b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 673b1f1b07fSVladimir Kondratyev if (sc->sampling_rate_slow >= 0 && sc->intr_handler != NULL) { 674b1f1b07fSVladimir Kondratyev if (power_on) { 675b1f1b07fSVladimir Kondratyev iichid_setup_callout(sc); 676b1f1b07fSVladimir Kondratyev iichid_reset_callout(sc); 677b1f1b07fSVladimir Kondratyev } else 678b1f1b07fSVladimir Kondratyev iichid_teardown_callout(sc); 679b1f1b07fSVladimir Kondratyev } 680b1f1b07fSVladimir Kondratyev #endif 681b1f1b07fSVladimir Kondratyev } 682b1f1b07fSVladimir Kondratyev 683b1f1b07fSVladimir Kondratyev iicbus_release_bus(parent, sc->dev); 684b1f1b07fSVladimir Kondratyev 685b1f1b07fSVladimir Kondratyev return (error); 686b1f1b07fSVladimir Kondratyev } 687b1f1b07fSVladimir Kondratyev 688b1f1b07fSVladimir Kondratyev static int 689b1f1b07fSVladimir Kondratyev iichid_setup_interrupt(struct iichid_softc *sc) 690b1f1b07fSVladimir Kondratyev { 691b1f1b07fSVladimir Kondratyev sc->irq_cookie = 0; 692b1f1b07fSVladimir Kondratyev 693b1f1b07fSVladimir Kondratyev int error = bus_setup_intr(sc->dev, sc->irq_res, 694b1f1b07fSVladimir Kondratyev INTR_TYPE_TTY|INTR_MPSAFE, NULL, iichid_intr, sc, &sc->irq_cookie); 695b1f1b07fSVladimir Kondratyev if (error != 0) 696b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Could not setup interrupt handler\n"); 697b1f1b07fSVladimir Kondratyev else 698b1f1b07fSVladimir Kondratyev DPRINTF(sc, "successfully setup interrupt\n"); 699b1f1b07fSVladimir Kondratyev 700b1f1b07fSVladimir Kondratyev return (error); 701b1f1b07fSVladimir Kondratyev } 702b1f1b07fSVladimir Kondratyev 703b1f1b07fSVladimir Kondratyev static void 704b1f1b07fSVladimir Kondratyev iichid_teardown_interrupt(struct iichid_softc *sc) 705b1f1b07fSVladimir Kondratyev { 706b1f1b07fSVladimir Kondratyev if (sc->irq_cookie) 707b1f1b07fSVladimir Kondratyev bus_teardown_intr(sc->dev, sc->irq_res, sc->irq_cookie); 708b1f1b07fSVladimir Kondratyev 709b1f1b07fSVladimir Kondratyev sc->irq_cookie = 0; 710b1f1b07fSVladimir Kondratyev } 711b1f1b07fSVladimir Kondratyev 712b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 713b1f1b07fSVladimir Kondratyev static int 714b1f1b07fSVladimir Kondratyev iichid_setup_callout(struct iichid_softc *sc) 715b1f1b07fSVladimir Kondratyev { 716b1f1b07fSVladimir Kondratyev 717b1f1b07fSVladimir Kondratyev if (sc->sampling_rate_slow < 0) { 718b1f1b07fSVladimir Kondratyev DPRINTF(sc, "sampling_rate is below 0, can't setup callout\n"); 719b1f1b07fSVladimir Kondratyev return (EINVAL); 720b1f1b07fSVladimir Kondratyev } 721b1f1b07fSVladimir Kondratyev 722b1f1b07fSVladimir Kondratyev sc->callout_setup = true; 723b1f1b07fSVladimir Kondratyev DPRINTF(sc, "successfully setup callout\n"); 724b1f1b07fSVladimir Kondratyev return (0); 725b1f1b07fSVladimir Kondratyev } 726b1f1b07fSVladimir Kondratyev 727b1f1b07fSVladimir Kondratyev static int 728b1f1b07fSVladimir Kondratyev iichid_reset_callout(struct iichid_softc *sc) 729b1f1b07fSVladimir Kondratyev { 730b1f1b07fSVladimir Kondratyev 731b1f1b07fSVladimir Kondratyev if (sc->sampling_rate_slow <= 0) { 732b1f1b07fSVladimir Kondratyev DPRINTF(sc, "sampling_rate is below or equal to 0, " 733b1f1b07fSVladimir Kondratyev "can't reset callout\n"); 734b1f1b07fSVladimir Kondratyev return (EINVAL); 735b1f1b07fSVladimir Kondratyev } 736b1f1b07fSVladimir Kondratyev 737b1f1b07fSVladimir Kondratyev if (!sc->callout_setup) 738b1f1b07fSVladimir Kondratyev return (EINVAL); 739b1f1b07fSVladimir Kondratyev 740b1f1b07fSVladimir Kondratyev /* Start with slow sampling. */ 741b1f1b07fSVladimir Kondratyev sc->missing_samples = sc->sampling_hysteresis; 7428c86b981SAlexander Motin sc->dup_samples = 0; 7438c86b981SAlexander Motin sc->dup_size = 0; 744a8f80c0cSAlexander Motin taskqueue_enqueue_timeout(sc->taskqueue, &sc->sampling_task, 0); 745b1f1b07fSVladimir Kondratyev 746b1f1b07fSVladimir Kondratyev return (0); 747b1f1b07fSVladimir Kondratyev } 748b1f1b07fSVladimir Kondratyev 749b1f1b07fSVladimir Kondratyev static void 750b1f1b07fSVladimir Kondratyev iichid_teardown_callout(struct iichid_softc *sc) 751b1f1b07fSVladimir Kondratyev { 752b1f1b07fSVladimir Kondratyev 753b1f1b07fSVladimir Kondratyev sc->callout_setup = false; 754a8f80c0cSAlexander Motin taskqueue_cancel_timeout(sc->taskqueue, &sc->sampling_task, NULL); 755b1f1b07fSVladimir Kondratyev DPRINTF(sc, "tore callout down\n"); 756b1f1b07fSVladimir Kondratyev } 757b1f1b07fSVladimir Kondratyev 758b1f1b07fSVladimir Kondratyev static int 759b1f1b07fSVladimir Kondratyev iichid_sysctl_sampling_rate_handler(SYSCTL_HANDLER_ARGS) 760b1f1b07fSVladimir Kondratyev { 761b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 762b1f1b07fSVladimir Kondratyev device_t parent; 763b1f1b07fSVladimir Kondratyev int error, oldval, value; 764b1f1b07fSVladimir Kondratyev 765b1f1b07fSVladimir Kondratyev sc = arg1; 766b1f1b07fSVladimir Kondratyev 767b1f1b07fSVladimir Kondratyev value = sc->sampling_rate_slow; 768b1f1b07fSVladimir Kondratyev error = sysctl_handle_int(oidp, &value, 0, req); 769b1f1b07fSVladimir Kondratyev 770b1f1b07fSVladimir Kondratyev if (error != 0 || req->newptr == NULL || 771b1f1b07fSVladimir Kondratyev value == sc->sampling_rate_slow) 772b1f1b07fSVladimir Kondratyev return (error); 773b1f1b07fSVladimir Kondratyev 774b1f1b07fSVladimir Kondratyev /* Can't switch to interrupt mode if it is not supported. */ 775b1f1b07fSVladimir Kondratyev if (sc->irq_res == NULL && value < 0) 776b1f1b07fSVladimir Kondratyev return (EINVAL); 777b1f1b07fSVladimir Kondratyev 778b1f1b07fSVladimir Kondratyev parent = device_get_parent(sc->dev); 779b1f1b07fSVladimir Kondratyev error = iicbus_request_bus(parent, sc->dev, IIC_WAIT); 780b1f1b07fSVladimir Kondratyev if (error != 0) 781b1f1b07fSVladimir Kondratyev return (iic2errno(error)); 782b1f1b07fSVladimir Kondratyev 783b1f1b07fSVladimir Kondratyev oldval = sc->sampling_rate_slow; 784b1f1b07fSVladimir Kondratyev sc->sampling_rate_slow = value; 785b1f1b07fSVladimir Kondratyev 786b1f1b07fSVladimir Kondratyev if (oldval < 0 && value >= 0) { 787b1f1b07fSVladimir Kondratyev iichid_teardown_interrupt(sc); 788b1f1b07fSVladimir Kondratyev if (sc->power_on) 789b1f1b07fSVladimir Kondratyev iichid_setup_callout(sc); 790b1f1b07fSVladimir Kondratyev } else if (oldval >= 0 && value < 0) { 791b1f1b07fSVladimir Kondratyev if (sc->power_on) 792b1f1b07fSVladimir Kondratyev iichid_teardown_callout(sc); 793b1f1b07fSVladimir Kondratyev iichid_setup_interrupt(sc); 794b1f1b07fSVladimir Kondratyev } 795b1f1b07fSVladimir Kondratyev 796b1f1b07fSVladimir Kondratyev if (sc->power_on && value > 0) 797b1f1b07fSVladimir Kondratyev iichid_reset_callout(sc); 798b1f1b07fSVladimir Kondratyev 799b1f1b07fSVladimir Kondratyev iicbus_release_bus(parent, sc->dev); 800b1f1b07fSVladimir Kondratyev 801b1f1b07fSVladimir Kondratyev DPRINTF(sc, "new sampling_rate value: %d\n", value); 802b1f1b07fSVladimir Kondratyev 803b1f1b07fSVladimir Kondratyev return (0); 804b1f1b07fSVladimir Kondratyev } 805b1f1b07fSVladimir Kondratyev #endif /* IICHID_SAMPLING */ 806b1f1b07fSVladimir Kondratyev 807b1f1b07fSVladimir Kondratyev static void 8084b171281SVladimir Kondratyev iichid_intr_setup(device_t dev, device_t child __unused, hid_intr_t intr, 8094b171281SVladimir Kondratyev void *context, struct hid_rdesc_info *rdesc) 810b1f1b07fSVladimir Kondratyev { 811b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 812b1f1b07fSVladimir Kondratyev 8139aa0e5afSVladimir Kondratyev if (intr == NULL) 8149aa0e5afSVladimir Kondratyev return; 8159aa0e5afSVladimir Kondratyev 816b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 817b1f1b07fSVladimir Kondratyev /* 818b1f1b07fSVladimir Kondratyev * Do not rely on wMaxInputLength, as some devices may set it to 819b1f1b07fSVladimir Kondratyev * a wrong length. Find the longest input report in report descriptor. 820b1f1b07fSVladimir Kondratyev */ 821b1f1b07fSVladimir Kondratyev rdesc->rdsize = rdesc->isize; 822b1f1b07fSVladimir Kondratyev /* Write and get/set_report sizes are limited by I2C-HID protocol. */ 823b1f1b07fSVladimir Kondratyev rdesc->grsize = rdesc->srsize = IICHID_SIZE_MAX; 824b1f1b07fSVladimir Kondratyev rdesc->wrsize = IICHID_SIZE_MAX; 825b1f1b07fSVladimir Kondratyev 826b1f1b07fSVladimir Kondratyev sc->intr_handler = intr; 827b1f1b07fSVladimir Kondratyev sc->intr_ctx = context; 828b1f1b07fSVladimir Kondratyev sc->intr_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO); 829b1f1b07fSVladimir Kondratyev sc->intr_bufsize = rdesc->rdsize; 830b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 8318c86b981SAlexander Motin sc->dup_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO); 832b1f1b07fSVladimir Kondratyev taskqueue_start_threads(&sc->taskqueue, 1, PI_TTY, 833b1f1b07fSVladimir Kondratyev "%s taskq", device_get_nameunit(sc->dev)); 834b1f1b07fSVladimir Kondratyev #endif 835b1f1b07fSVladimir Kondratyev } 836b1f1b07fSVladimir Kondratyev 837b1f1b07fSVladimir Kondratyev static void 8384b171281SVladimir Kondratyev iichid_intr_unsetup(device_t dev, device_t child __unused) 839b1f1b07fSVladimir Kondratyev { 840b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 841b1f1b07fSVladimir Kondratyev 842b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 843b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 844b1f1b07fSVladimir Kondratyev taskqueue_drain_all(sc->taskqueue); 8458c86b981SAlexander Motin free(sc->dup_buf, M_DEVBUF); 846b1f1b07fSVladimir Kondratyev #endif 847b1f1b07fSVladimir Kondratyev free(sc->intr_buf, M_DEVBUF); 848b1f1b07fSVladimir Kondratyev } 849b1f1b07fSVladimir Kondratyev 850b1f1b07fSVladimir Kondratyev static int 8514b171281SVladimir Kondratyev iichid_intr_start(device_t dev, device_t child __unused) 852b1f1b07fSVladimir Kondratyev { 853b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 854b1f1b07fSVladimir Kondratyev 855b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 856b1f1b07fSVladimir Kondratyev DPRINTF(sc, "iichid device open\n"); 857b1f1b07fSVladimir Kondratyev iichid_set_power_state(sc, IICHID_PS_ON, IICHID_PS_NULL); 858b1f1b07fSVladimir Kondratyev 859b1f1b07fSVladimir Kondratyev return (0); 860b1f1b07fSVladimir Kondratyev } 861b1f1b07fSVladimir Kondratyev 862b1f1b07fSVladimir Kondratyev static int 8634b171281SVladimir Kondratyev iichid_intr_stop(device_t dev, device_t child __unused) 864b1f1b07fSVladimir Kondratyev { 865b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 866b1f1b07fSVladimir Kondratyev 867b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 868b1f1b07fSVladimir Kondratyev DPRINTF(sc, "iichid device close\n"); 869b1f1b07fSVladimir Kondratyev /* 870b1f1b07fSVladimir Kondratyev * 8.2 - The HOST determines that there are no active applications 871b1f1b07fSVladimir Kondratyev * that are currently using the specific HID DEVICE. The HOST 872b1f1b07fSVladimir Kondratyev * is recommended to issue a HIPO command to the DEVICE to force 873b1f1b07fSVladimir Kondratyev * the DEVICE in to a lower power state. 874b1f1b07fSVladimir Kondratyev */ 875b1f1b07fSVladimir Kondratyev iichid_set_power_state(sc, IICHID_PS_OFF, IICHID_PS_NULL); 876b1f1b07fSVladimir Kondratyev 877b1f1b07fSVladimir Kondratyev return (0); 878b1f1b07fSVladimir Kondratyev } 879b1f1b07fSVladimir Kondratyev 880b1f1b07fSVladimir Kondratyev static void 8814b171281SVladimir Kondratyev iichid_intr_poll(device_t dev, device_t child __unused) 882b1f1b07fSVladimir Kondratyev { 883b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 884b1f1b07fSVladimir Kondratyev iichid_size_t actual; 885b1f1b07fSVladimir Kondratyev int error; 886b1f1b07fSVladimir Kondratyev 887b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 888b1f1b07fSVladimir Kondratyev error = iichid_cmd_read(sc, sc->intr_buf, sc->intr_bufsize, &actual); 889b1f1b07fSVladimir Kondratyev if (error == 0 && actual != 0) 890b1f1b07fSVladimir Kondratyev sc->intr_handler(sc->intr_ctx, sc->intr_buf, actual); 891b1f1b07fSVladimir Kondratyev } 892b1f1b07fSVladimir Kondratyev 893b1f1b07fSVladimir Kondratyev /* 894b1f1b07fSVladimir Kondratyev * HID interface 895b1f1b07fSVladimir Kondratyev */ 896b1f1b07fSVladimir Kondratyev static int 8974b171281SVladimir Kondratyev iichid_get_rdesc(device_t dev, device_t child __unused, void *buf, 8984b171281SVladimir Kondratyev hid_size_t len) 899b1f1b07fSVladimir Kondratyev { 900b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 901b1f1b07fSVladimir Kondratyev int error; 902b1f1b07fSVladimir Kondratyev 903b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 904b1f1b07fSVladimir Kondratyev error = iichid_cmd_get_report_desc(sc, buf, len); 905b1f1b07fSVladimir Kondratyev if (error) 906b1f1b07fSVladimir Kondratyev DPRINTF(sc, "failed to fetch report descriptor: %d\n", error); 907b1f1b07fSVladimir Kondratyev 908b1f1b07fSVladimir Kondratyev return (iic2errno(error)); 909b1f1b07fSVladimir Kondratyev } 910b1f1b07fSVladimir Kondratyev 911b1f1b07fSVladimir Kondratyev static int 9124b171281SVladimir Kondratyev iichid_read(device_t dev, device_t child __unused, void *buf, 9134b171281SVladimir Kondratyev hid_size_t maxlen, hid_size_t *actlen) 914b1f1b07fSVladimir Kondratyev { 915b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 916b1f1b07fSVladimir Kondratyev device_t parent; 917b1f1b07fSVladimir Kondratyev int error; 918b1f1b07fSVladimir Kondratyev 919b1f1b07fSVladimir Kondratyev if (maxlen > IICHID_SIZE_MAX) 920b1f1b07fSVladimir Kondratyev return (EMSGSIZE); 921b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 922b1f1b07fSVladimir Kondratyev parent = device_get_parent(sc->dev); 923b1f1b07fSVladimir Kondratyev error = iicbus_request_bus(parent, sc->dev, IIC_WAIT); 924b1f1b07fSVladimir Kondratyev if (error == 0) { 925b1f1b07fSVladimir Kondratyev error = iichid_cmd_read(sc, buf, maxlen, actlen); 926b1f1b07fSVladimir Kondratyev iicbus_release_bus(parent, sc->dev); 927b1f1b07fSVladimir Kondratyev } 928b1f1b07fSVladimir Kondratyev return (iic2errno(error)); 929b1f1b07fSVladimir Kondratyev } 930b1f1b07fSVladimir Kondratyev 931b1f1b07fSVladimir Kondratyev static int 9324b171281SVladimir Kondratyev iichid_write(device_t dev, device_t child __unused, const void *buf, 9334b171281SVladimir Kondratyev hid_size_t len) 934b1f1b07fSVladimir Kondratyev { 935b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 936b1f1b07fSVladimir Kondratyev 937b1f1b07fSVladimir Kondratyev if (len > IICHID_SIZE_MAX) 938b1f1b07fSVladimir Kondratyev return (EMSGSIZE); 939b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 940b1f1b07fSVladimir Kondratyev return (iic2errno(iichid_cmd_write(sc, buf, len))); 941b1f1b07fSVladimir Kondratyev } 942b1f1b07fSVladimir Kondratyev 943b1f1b07fSVladimir Kondratyev static int 9444b171281SVladimir Kondratyev iichid_get_report(device_t dev, device_t child __unused, void *buf, 9454b171281SVladimir Kondratyev hid_size_t maxlen, hid_size_t *actlen, uint8_t type, uint8_t id) 946b1f1b07fSVladimir Kondratyev { 947b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 948b1f1b07fSVladimir Kondratyev 949b1f1b07fSVladimir Kondratyev if (maxlen > IICHID_SIZE_MAX) 950b1f1b07fSVladimir Kondratyev return (EMSGSIZE); 951b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 952b1f1b07fSVladimir Kondratyev return (iic2errno( 953b1f1b07fSVladimir Kondratyev iichid_cmd_get_report(sc, buf, maxlen, actlen, type, id))); 954b1f1b07fSVladimir Kondratyev } 955b1f1b07fSVladimir Kondratyev 956b1f1b07fSVladimir Kondratyev static int 9574b171281SVladimir Kondratyev iichid_set_report(device_t dev, device_t child __unused, const void *buf, 9584b171281SVladimir Kondratyev hid_size_t len, uint8_t type, uint8_t id) 959b1f1b07fSVladimir Kondratyev { 960b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 961b1f1b07fSVladimir Kondratyev 962b1f1b07fSVladimir Kondratyev if (len > IICHID_SIZE_MAX) 963b1f1b07fSVladimir Kondratyev return (EMSGSIZE); 964b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 965b1f1b07fSVladimir Kondratyev return (iic2errno(iichid_cmd_set_report(sc, buf, len, type, id))); 966b1f1b07fSVladimir Kondratyev } 967b1f1b07fSVladimir Kondratyev 968b1f1b07fSVladimir Kondratyev static int 9694b171281SVladimir Kondratyev iichid_set_idle(device_t dev, device_t child __unused, 9704b171281SVladimir Kondratyev uint16_t duration, uint8_t id) 971b1f1b07fSVladimir Kondratyev { 972b1f1b07fSVladimir Kondratyev return (ENOTSUP); 973b1f1b07fSVladimir Kondratyev } 974b1f1b07fSVladimir Kondratyev 975b1f1b07fSVladimir Kondratyev static int 9764b171281SVladimir Kondratyev iichid_set_protocol(device_t dev, device_t child __unused, uint16_t protocol) 977b1f1b07fSVladimir Kondratyev { 978b1f1b07fSVladimir Kondratyev return (ENOTSUP); 979b1f1b07fSVladimir Kondratyev } 980b1f1b07fSVladimir Kondratyev 981b1f1b07fSVladimir Kondratyev static int 9824b171281SVladimir Kondratyev iichid_ioctl(device_t dev, device_t child __unused, unsigned long cmd, 9834b171281SVladimir Kondratyev uintptr_t data) 9846b1da3d2SVladimir Kondratyev { 9856b1da3d2SVladimir Kondratyev int error; 9866b1da3d2SVladimir Kondratyev 9876b1da3d2SVladimir Kondratyev switch (cmd) { 9886b1da3d2SVladimir Kondratyev case I2CRDWR: 9896b1da3d2SVladimir Kondratyev error = iic2errno(iicbus_transfer(dev, 9906b1da3d2SVladimir Kondratyev ((struct iic_rdwr_data *)data)->msgs, 9916b1da3d2SVladimir Kondratyev ((struct iic_rdwr_data *)data)->nmsgs)); 9926b1da3d2SVladimir Kondratyev break; 9936b1da3d2SVladimir Kondratyev default: 9946b1da3d2SVladimir Kondratyev error = EINVAL; 9956b1da3d2SVladimir Kondratyev } 9966b1da3d2SVladimir Kondratyev 9976b1da3d2SVladimir Kondratyev return (error); 9986b1da3d2SVladimir Kondratyev } 9996b1da3d2SVladimir Kondratyev 10006b1da3d2SVladimir Kondratyev static int 1001b1f1b07fSVladimir Kondratyev iichid_fill_device_info(struct i2c_hid_desc *desc, ACPI_HANDLE handle, 1002b1f1b07fSVladimir Kondratyev struct hid_device_info *hw) 1003b1f1b07fSVladimir Kondratyev { 1004b1f1b07fSVladimir Kondratyev ACPI_DEVICE_INFO *device_info; 1005b1f1b07fSVladimir Kondratyev 1006b1f1b07fSVladimir Kondratyev hw->idBus = BUS_I2C; 1007b1f1b07fSVladimir Kondratyev hw->idVendor = le16toh(desc->wVendorID); 1008b1f1b07fSVladimir Kondratyev hw->idProduct = le16toh(desc->wProductID); 1009b1f1b07fSVladimir Kondratyev hw->idVersion = le16toh(desc->wVersionID); 1010b1f1b07fSVladimir Kondratyev 1011b1f1b07fSVladimir Kondratyev /* get ACPI HID. It is a base part of the device name. */ 1012b1f1b07fSVladimir Kondratyev if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &device_info))) 1013b1f1b07fSVladimir Kondratyev return (ENXIO); 1014b1f1b07fSVladimir Kondratyev 1015b1f1b07fSVladimir Kondratyev if (device_info->Valid & ACPI_VALID_HID) 1016b1f1b07fSVladimir Kondratyev strlcpy(hw->idPnP, device_info->HardwareId.String, 1017b1f1b07fSVladimir Kondratyev HID_PNP_ID_SIZE); 1018b1f1b07fSVladimir Kondratyev snprintf(hw->name, sizeof(hw->name), "%s:%02lX %04X:%04X", 1019b1f1b07fSVladimir Kondratyev (device_info->Valid & ACPI_VALID_HID) ? 1020b1f1b07fSVladimir Kondratyev device_info->HardwareId.String : "Unknown", 1021b1f1b07fSVladimir Kondratyev (device_info->Valid & ACPI_VALID_UID) ? 1022b1f1b07fSVladimir Kondratyev strtoul(device_info->UniqueId.String, NULL, 10) : 0UL, 1023b1f1b07fSVladimir Kondratyev le16toh(desc->wVendorID), le16toh(desc->wProductID)); 1024b1f1b07fSVladimir Kondratyev 1025b1f1b07fSVladimir Kondratyev AcpiOsFree(device_info); 1026b1f1b07fSVladimir Kondratyev 1027b1f1b07fSVladimir Kondratyev strlcpy(hw->serial, "", sizeof(hw->serial)); 1028b1f1b07fSVladimir Kondratyev hw->rdescsize = le16toh(desc->wReportDescLength); 1029b1f1b07fSVladimir Kondratyev if (desc->wOutputRegister == 0 || desc->wMaxOutputLength == 0) 1030b1f1b07fSVladimir Kondratyev hid_add_dynamic_quirk(hw, HQ_NOWRITE); 1031b1f1b07fSVladimir Kondratyev 1032b1f1b07fSVladimir Kondratyev return (0); 1033b1f1b07fSVladimir Kondratyev } 1034b1f1b07fSVladimir Kondratyev 1035b1f1b07fSVladimir Kondratyev static int 1036b1f1b07fSVladimir Kondratyev iichid_probe(device_t dev) 1037b1f1b07fSVladimir Kondratyev { 1038b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 1039b1f1b07fSVladimir Kondratyev ACPI_HANDLE handle; 1040b1f1b07fSVladimir Kondratyev uint16_t config_reg; 104134e051c4SVladimir Kondratyev int error, reg; 1042b1f1b07fSVladimir Kondratyev 1043b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 1044b1f1b07fSVladimir Kondratyev sc->dev = dev; 1045b1f1b07fSVladimir Kondratyev if (sc->probe_done) 1046b1f1b07fSVladimir Kondratyev goto done; 1047b1f1b07fSVladimir Kondratyev 1048b1f1b07fSVladimir Kondratyev sc->probe_done = true; 1049b1f1b07fSVladimir Kondratyev sc->probe_result = ENXIO; 1050b1f1b07fSVladimir Kondratyev 1051b1f1b07fSVladimir Kondratyev if (acpi_disabled("iichid")) 1052b1f1b07fSVladimir Kondratyev return (ENXIO); 1053b1f1b07fSVladimir Kondratyev 1054b1f1b07fSVladimir Kondratyev sc->addr = iicbus_get_addr(dev) << 1; 1055b1f1b07fSVladimir Kondratyev if (sc->addr == 0) 1056b1f1b07fSVladimir Kondratyev return (ENXIO); 1057b1f1b07fSVladimir Kondratyev 1058b1f1b07fSVladimir Kondratyev handle = acpi_get_handle(dev); 1059b1f1b07fSVladimir Kondratyev if (handle == NULL) 1060b1f1b07fSVladimir Kondratyev return (ENXIO); 1061b1f1b07fSVladimir Kondratyev 106234e051c4SVladimir Kondratyev reg = acpi_is_iichid(handle); 106334e051c4SVladimir Kondratyev if (reg == IICHID_REG_NONE) 1064b1f1b07fSVladimir Kondratyev return (ENXIO); 1065b1f1b07fSVladimir Kondratyev 106634e051c4SVladimir Kondratyev if (reg == IICHID_REG_ACPI) { 1067b1f1b07fSVladimir Kondratyev if (ACPI_FAILURE(iichid_get_config_reg(handle, &config_reg))) 1068b1f1b07fSVladimir Kondratyev return (ENXIO); 106934e051c4SVladimir Kondratyev } else 107034e051c4SVladimir Kondratyev config_reg = (uint16_t)reg; 1071b1f1b07fSVladimir Kondratyev 1072b1f1b07fSVladimir Kondratyev DPRINTF(sc, " IICbus addr : 0x%02X\n", sc->addr >> 1); 1073b1f1b07fSVladimir Kondratyev DPRINTF(sc, " HID descriptor reg: 0x%02X\n", config_reg); 1074b1f1b07fSVladimir Kondratyev 1075b1f1b07fSVladimir Kondratyev error = iichid_cmd_get_hid_desc(sc, config_reg, &sc->desc); 1076b1f1b07fSVladimir Kondratyev if (error) { 1077b1f1b07fSVladimir Kondratyev DPRINTF(sc, "could not retrieve HID descriptor from the " 1078b1f1b07fSVladimir Kondratyev "device: %d\n", error); 1079b1f1b07fSVladimir Kondratyev return (ENXIO); 1080b1f1b07fSVladimir Kondratyev } 1081b1f1b07fSVladimir Kondratyev 1082b1f1b07fSVladimir Kondratyev if (le16toh(sc->desc.wHIDDescLength) != 30 || 1083b1f1b07fSVladimir Kondratyev le16toh(sc->desc.bcdVersion) != 0x100) { 1084b1f1b07fSVladimir Kondratyev DPRINTF(sc, "HID descriptor is broken\n"); 1085b1f1b07fSVladimir Kondratyev return (ENXIO); 1086b1f1b07fSVladimir Kondratyev } 1087b1f1b07fSVladimir Kondratyev 1088b1f1b07fSVladimir Kondratyev /* Setup hid_device_info so we can figure out quirks for the device. */ 1089b1f1b07fSVladimir Kondratyev if (iichid_fill_device_info(&sc->desc, handle, &sc->hw) != 0) { 1090b1f1b07fSVladimir Kondratyev DPRINTF(sc, "error evaluating AcpiGetObjectInfo\n"); 1091b1f1b07fSVladimir Kondratyev return (ENXIO); 1092b1f1b07fSVladimir Kondratyev } 1093b1f1b07fSVladimir Kondratyev 1094b1f1b07fSVladimir Kondratyev if (hid_test_quirk(&sc->hw, HQ_HID_IGNORE)) 1095b1f1b07fSVladimir Kondratyev return (ENXIO); 1096b1f1b07fSVladimir Kondratyev 1097b1f1b07fSVladimir Kondratyev sc->probe_result = BUS_PROBE_DEFAULT; 1098b1f1b07fSVladimir Kondratyev done: 109948f5a429SMark Johnston if (sc->probe_result <= BUS_PROBE_SPECIFIC) 110048f5a429SMark Johnston device_set_descf(dev, "%s I2C HID device", sc->hw.name); 1101b1f1b07fSVladimir Kondratyev return (sc->probe_result); 1102b1f1b07fSVladimir Kondratyev } 1103b1f1b07fSVladimir Kondratyev 1104b1f1b07fSVladimir Kondratyev static int 1105b1f1b07fSVladimir Kondratyev iichid_attach(device_t dev) 1106b1f1b07fSVladimir Kondratyev { 1107b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 1108b1f1b07fSVladimir Kondratyev device_t child; 1109b1f1b07fSVladimir Kondratyev int error; 1110b1f1b07fSVladimir Kondratyev 1111b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 1112b1f1b07fSVladimir Kondratyev error = iichid_set_power(sc, I2C_HID_POWER_ON); 1113b1f1b07fSVladimir Kondratyev if (error) { 1114b1f1b07fSVladimir Kondratyev device_printf(dev, "failed to power on: %d\n", error); 1115b1f1b07fSVladimir Kondratyev return (ENXIO); 1116b1f1b07fSVladimir Kondratyev } 1117b1f1b07fSVladimir Kondratyev /* 1118b1f1b07fSVladimir Kondratyev * Windows driver sleeps for 1ms between the SET_POWER and RESET 1119b1f1b07fSVladimir Kondratyev * commands. So we too as some devices may depend on this. 1120b1f1b07fSVladimir Kondratyev */ 1121b1f1b07fSVladimir Kondratyev pause("iichid", (hz + 999) / 1000); 1122b1f1b07fSVladimir Kondratyev 1123b1f1b07fSVladimir Kondratyev error = iichid_reset(sc); 1124b1f1b07fSVladimir Kondratyev if (error) { 1125b1f1b07fSVladimir Kondratyev device_printf(dev, "failed to reset hardware: %d\n", error); 11265236888dSJ.R. Oldroyd error = ENXIO; 11275236888dSJ.R. Oldroyd goto done; 1128b1f1b07fSVladimir Kondratyev } 1129b1f1b07fSVladimir Kondratyev 11305236888dSJ.R. Oldroyd sc->power_on = true; 11315236888dSJ.R. Oldroyd 113282626fefSVladimir Kondratyev TASK_INIT(&sc->suspend_task, 0, iichid_suspend_task, sc); 1133b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 1134358453ceSAlexander Motin sc->taskqueue = taskqueue_create_fast("iichid_tq", M_WAITOK | M_ZERO, 1135b1f1b07fSVladimir Kondratyev taskqueue_thread_enqueue, &sc->taskqueue); 1136a8f80c0cSAlexander Motin TIMEOUT_TASK_INIT(sc->taskqueue, &sc->sampling_task, 0, 1137a8f80c0cSAlexander Motin iichid_sampling_task, sc); 1138b1f1b07fSVladimir Kondratyev 1139b1f1b07fSVladimir Kondratyev sc->sampling_rate_slow = -1; 1140b1f1b07fSVladimir Kondratyev sc->sampling_rate_fast = IICHID_SAMPLING_RATE_FAST; 1141b1f1b07fSVladimir Kondratyev sc->sampling_hysteresis = IICHID_SAMPLING_HYSTERESIS; 1142b1f1b07fSVladimir Kondratyev #endif 1143b1f1b07fSVladimir Kondratyev 1144b1f1b07fSVladimir Kondratyev sc->irq_rid = 0; 1145b1f1b07fSVladimir Kondratyev sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, 1146b1f1b07fSVladimir Kondratyev &sc->irq_rid, RF_ACTIVE); 1147b1f1b07fSVladimir Kondratyev 1148b1f1b07fSVladimir Kondratyev if (sc->irq_res != NULL) { 1149b1f1b07fSVladimir Kondratyev DPRINTF(sc, "allocated irq at %p and rid %d\n", 1150b1f1b07fSVladimir Kondratyev sc->irq_res, sc->irq_rid); 1151b1f1b07fSVladimir Kondratyev error = iichid_setup_interrupt(sc); 1152b1f1b07fSVladimir Kondratyev } 1153b1f1b07fSVladimir Kondratyev 1154b1f1b07fSVladimir Kondratyev if (sc->irq_res == NULL || error != 0) { 1155b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 1156b1f1b07fSVladimir Kondratyev device_printf(sc->dev, 11570ac6cc3fSEd Maste "Using sampling mode\n"); 1158b1f1b07fSVladimir Kondratyev sc->sampling_rate_slow = IICHID_SAMPLING_RATE_SLOW; 1159b1f1b07fSVladimir Kondratyev #else 1160b1f1b07fSVladimir Kondratyev device_printf(sc->dev, "Interrupt setup failed\n"); 1161b1f1b07fSVladimir Kondratyev if (sc->irq_res != NULL) 1162b1f1b07fSVladimir Kondratyev bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, 1163b1f1b07fSVladimir Kondratyev sc->irq_res); 1164b1f1b07fSVladimir Kondratyev error = ENXIO; 1165b1f1b07fSVladimir Kondratyev goto done; 1166b1f1b07fSVladimir Kondratyev #endif 1167b1f1b07fSVladimir Kondratyev } 1168b1f1b07fSVladimir Kondratyev 1169b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 1170b1f1b07fSVladimir Kondratyev SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), 1171b1f1b07fSVladimir Kondratyev SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 1172b1f1b07fSVladimir Kondratyev OID_AUTO, "sampling_rate_slow", CTLTYPE_INT | CTLFLAG_RWTUN, 1173b1f1b07fSVladimir Kondratyev sc, 0, iichid_sysctl_sampling_rate_handler, "I", 1174b1f1b07fSVladimir Kondratyev "idle sampling rate in num/second"); 1175b1f1b07fSVladimir Kondratyev SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), 1176b1f1b07fSVladimir Kondratyev SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 11778ffcde25SVladimir Kondratyev OID_AUTO, "sampling_rate_fast", CTLFLAG_RWTUN, 1178b1f1b07fSVladimir Kondratyev &sc->sampling_rate_fast, 0, 1179b1f1b07fSVladimir Kondratyev "active sampling rate in num/second"); 1180b1f1b07fSVladimir Kondratyev SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), 1181b1f1b07fSVladimir Kondratyev SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), 11828ffcde25SVladimir Kondratyev OID_AUTO, "sampling_hysteresis", CTLFLAG_RWTUN, 1183b1f1b07fSVladimir Kondratyev &sc->sampling_hysteresis, 0, 1184b1f1b07fSVladimir Kondratyev "number of missing samples before enabling of slow mode"); 1185b1f1b07fSVladimir Kondratyev hid_add_dynamic_quirk(&sc->hw, HQ_IICHID_SAMPLING); 1186c508b081SVladimir Kondratyev 1187c508b081SVladimir Kondratyev if (sc->sampling_rate_slow >= 0) { 1188c508b081SVladimir Kondratyev pause("iichid", (hz + 999) / 1000); 1189c508b081SVladimir Kondratyev (void)iichid_cmd_read(sc, NULL, 0, NULL); 1190c508b081SVladimir Kondratyev } 1191b1f1b07fSVladimir Kondratyev #endif /* IICHID_SAMPLING */ 1192b1f1b07fSVladimir Kondratyev 11935b56413dSWarner Losh child = device_add_child(dev, "hidbus", DEVICE_UNIT_ANY); 1194b1f1b07fSVladimir Kondratyev if (child == NULL) { 1195b1f1b07fSVladimir Kondratyev device_printf(sc->dev, "Could not add I2C device\n"); 1196b1f1b07fSVladimir Kondratyev iichid_detach(dev); 1197b1f1b07fSVladimir Kondratyev error = ENOMEM; 1198b1f1b07fSVladimir Kondratyev goto done; 1199b1f1b07fSVladimir Kondratyev } 1200b1f1b07fSVladimir Kondratyev 1201b1f1b07fSVladimir Kondratyev device_set_ivars(child, &sc->hw); 120218250ec6SJohn Baldwin bus_attach_children(dev); 120318250ec6SJohn Baldwin error = 0; 1204b1f1b07fSVladimir Kondratyev done: 1205018cb11cSVladimir Kondratyev iicbus_request_bus(device_get_parent(dev), dev, IIC_WAIT); 1206018cb11cSVladimir Kondratyev if (!sc->open) { 1207b1f1b07fSVladimir Kondratyev (void)iichid_set_power(sc, I2C_HID_POWER_OFF); 12085236888dSJ.R. Oldroyd sc->power_on = false; 1209018cb11cSVladimir Kondratyev } 1210018cb11cSVladimir Kondratyev iicbus_release_bus(device_get_parent(dev), dev); 1211b1f1b07fSVladimir Kondratyev return (error); 1212b1f1b07fSVladimir Kondratyev } 1213b1f1b07fSVladimir Kondratyev 1214b1f1b07fSVladimir Kondratyev static int 1215b1f1b07fSVladimir Kondratyev iichid_detach(device_t dev) 1216b1f1b07fSVladimir Kondratyev { 1217b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 1218b1f1b07fSVladimir Kondratyev int error; 1219b1f1b07fSVladimir Kondratyev 1220b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 1221*3ddaf820SJohn Baldwin error = bus_generic_detach(dev); 1222b1f1b07fSVladimir Kondratyev if (error) 1223b1f1b07fSVladimir Kondratyev return (error); 1224b1f1b07fSVladimir Kondratyev iichid_teardown_interrupt(sc); 1225b1f1b07fSVladimir Kondratyev if (sc->irq_res != NULL) 1226b1f1b07fSVladimir Kondratyev bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, 1227b1f1b07fSVladimir Kondratyev sc->irq_res); 1228b1f1b07fSVladimir Kondratyev #ifdef IICHID_SAMPLING 1229b1f1b07fSVladimir Kondratyev if (sc->taskqueue != NULL) 1230b1f1b07fSVladimir Kondratyev taskqueue_free(sc->taskqueue); 1231b1f1b07fSVladimir Kondratyev sc->taskqueue = NULL; 1232b1f1b07fSVladimir Kondratyev #endif 1233b1f1b07fSVladimir Kondratyev return (0); 1234b1f1b07fSVladimir Kondratyev } 1235b1f1b07fSVladimir Kondratyev 123682626fefSVladimir Kondratyev static void 123782626fefSVladimir Kondratyev iichid_suspend_task(void *context, int pending) 123882626fefSVladimir Kondratyev { 123982626fefSVladimir Kondratyev struct iichid_softc *sc = context; 124082626fefSVladimir Kondratyev 124182626fefSVladimir Kondratyev iichid_teardown_interrupt(sc); 124282626fefSVladimir Kondratyev } 124382626fefSVladimir Kondratyev 1244b1f1b07fSVladimir Kondratyev static int 1245b1f1b07fSVladimir Kondratyev iichid_suspend(device_t dev) 1246b1f1b07fSVladimir Kondratyev { 1247b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 1248b1f1b07fSVladimir Kondratyev int error; 1249b1f1b07fSVladimir Kondratyev 1250b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 1251b1f1b07fSVladimir Kondratyev (void)bus_generic_suspend(dev); 1252b1f1b07fSVladimir Kondratyev /* 1253b1f1b07fSVladimir Kondratyev * 8.2 - The HOST is going into a deep power optimized state and wishes 1254b1f1b07fSVladimir Kondratyev * to put all the devices into a low power state also. The HOST 1255b1f1b07fSVladimir Kondratyev * is recommended to issue a HIPO command to the DEVICE to force 1256b1f1b07fSVladimir Kondratyev * the DEVICE in to a lower power state. 1257b1f1b07fSVladimir Kondratyev */ 12585236888dSJ.R. Oldroyd DPRINTF(sc, "Suspend called, setting device to power_state 1\n"); 1259b1f1b07fSVladimir Kondratyev error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_OFF); 1260b1f1b07fSVladimir Kondratyev if (error != 0) 1261b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Could not set power_state, error: %d\n", error); 1262b1f1b07fSVladimir Kondratyev else 1263b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Successfully set power_state\n"); 1264b1f1b07fSVladimir Kondratyev 126582626fefSVladimir Kondratyev #ifdef IICHID_SAMPLING 126682626fefSVladimir Kondratyev if (sc->sampling_rate_slow < 0) 126782626fefSVladimir Kondratyev #endif 126882626fefSVladimir Kondratyev { 126982626fefSVladimir Kondratyev /* 127082626fefSVladimir Kondratyev * bus_teardown_intr can not be executed right here as it wants 127182626fefSVladimir Kondratyev * to run on certain CPU to interacts with LAPIC while suspend 127282626fefSVladimir Kondratyev * thread is bound to CPU0. So run it from taskqueue context. 127382626fefSVladimir Kondratyev */ 127482626fefSVladimir Kondratyev #ifdef IICHID_SAMPLING 127582626fefSVladimir Kondratyev #define suspend_thread sc->taskqueue 127682626fefSVladimir Kondratyev #else 127782626fefSVladimir Kondratyev #define suspend_thread taskqueue_thread 127882626fefSVladimir Kondratyev #endif 127982626fefSVladimir Kondratyev taskqueue_enqueue(suspend_thread, &sc->suspend_task); 128082626fefSVladimir Kondratyev taskqueue_drain(suspend_thread, &sc->suspend_task); 128182626fefSVladimir Kondratyev } 128282626fefSVladimir Kondratyev 1283b1f1b07fSVladimir Kondratyev return (0); 1284b1f1b07fSVladimir Kondratyev } 1285b1f1b07fSVladimir Kondratyev 1286b1f1b07fSVladimir Kondratyev static int 1287b1f1b07fSVladimir Kondratyev iichid_resume(device_t dev) 1288b1f1b07fSVladimir Kondratyev { 1289b1f1b07fSVladimir Kondratyev struct iichid_softc *sc; 1290b1f1b07fSVladimir Kondratyev int error; 1291b1f1b07fSVladimir Kondratyev 1292b1f1b07fSVladimir Kondratyev sc = device_get_softc(dev); 129382626fefSVladimir Kondratyev #ifdef IICHID_SAMPLING 129482626fefSVladimir Kondratyev if (sc->sampling_rate_slow < 0) 129582626fefSVladimir Kondratyev #endif 129682626fefSVladimir Kondratyev iichid_setup_interrupt(sc); 129782626fefSVladimir Kondratyev 1298b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Resume called, setting device to power_state 0\n"); 1299b1f1b07fSVladimir Kondratyev error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_ON); 1300b1f1b07fSVladimir Kondratyev if (error != 0) 1301b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Could not set power_state, error: %d\n", error); 1302b1f1b07fSVladimir Kondratyev else 1303b1f1b07fSVladimir Kondratyev DPRINTF(sc, "Successfully set power_state\n"); 1304b1f1b07fSVladimir Kondratyev (void)bus_generic_resume(dev); 1305b1f1b07fSVladimir Kondratyev 1306b1f1b07fSVladimir Kondratyev return (0); 1307b1f1b07fSVladimir Kondratyev } 1308b1f1b07fSVladimir Kondratyev 1309b1f1b07fSVladimir Kondratyev static device_method_t iichid_methods[] = { 1310b1f1b07fSVladimir Kondratyev DEVMETHOD(device_probe, iichid_probe), 1311b1f1b07fSVladimir Kondratyev DEVMETHOD(device_attach, iichid_attach), 1312b1f1b07fSVladimir Kondratyev DEVMETHOD(device_detach, iichid_detach), 1313b1f1b07fSVladimir Kondratyev DEVMETHOD(device_suspend, iichid_suspend), 1314b1f1b07fSVladimir Kondratyev DEVMETHOD(device_resume, iichid_resume), 1315b1f1b07fSVladimir Kondratyev 1316b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_intr_setup, iichid_intr_setup), 1317b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_intr_unsetup, iichid_intr_unsetup), 1318b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_intr_start, iichid_intr_start), 1319b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_intr_stop, iichid_intr_stop), 1320b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_intr_poll, iichid_intr_poll), 1321b1f1b07fSVladimir Kondratyev 1322b1f1b07fSVladimir Kondratyev /* HID interface */ 1323b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_get_rdesc, iichid_get_rdesc), 1324b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_read, iichid_read), 1325b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_write, iichid_write), 1326b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_get_report, iichid_get_report), 1327b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_set_report, iichid_set_report), 1328b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_set_idle, iichid_set_idle), 1329b1f1b07fSVladimir Kondratyev DEVMETHOD(hid_set_protocol, iichid_set_protocol), 13306b1da3d2SVladimir Kondratyev DEVMETHOD(hid_ioctl, iichid_ioctl), 1331b1f1b07fSVladimir Kondratyev 1332b1f1b07fSVladimir Kondratyev DEVMETHOD_END 1333b1f1b07fSVladimir Kondratyev }; 1334b1f1b07fSVladimir Kondratyev 1335b1f1b07fSVladimir Kondratyev static driver_t iichid_driver = { 1336b1f1b07fSVladimir Kondratyev .name = "iichid", 1337b1f1b07fSVladimir Kondratyev .methods = iichid_methods, 1338b1f1b07fSVladimir Kondratyev .size = sizeof(struct iichid_softc), 1339b1f1b07fSVladimir Kondratyev }; 1340b1f1b07fSVladimir Kondratyev 13413a866152SJohn Baldwin DRIVER_MODULE(iichid, iicbus, iichid_driver, NULL, NULL); 1342b1f1b07fSVladimir Kondratyev MODULE_DEPEND(iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 1343b1f1b07fSVladimir Kondratyev MODULE_DEPEND(iichid, acpi, 1, 1, 1); 1344b1f1b07fSVladimir Kondratyev MODULE_DEPEND(iichid, hid, 1, 1, 1); 1345b1f1b07fSVladimir Kondratyev MODULE_DEPEND(iichid, hidbus, 1, 1, 1); 1346b1f1b07fSVladimir Kondratyev MODULE_VERSION(iichid, 1); 1347b1f1b07fSVladimir Kondratyev IICBUS_ACPI_PNP_INFO(iichid_ids); 1348