Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iicbus/iichid.c
- This file was added.
/*- | |||||
* Copyright (c) 2018-2019 Marc Priggemeyer <marc.priggemeyer@gmail.com> | |||||
* Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org> | |||||
* | |||||
* 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. | |||||
*/ | |||||
/* | |||||
* I2C HID transport backend. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include "opt_hid.h" | |||||
#include <sys/param.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/callout.h> | |||||
#include <sys/endian.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/rman.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/taskqueue.h> | |||||
#include <machine/resource.h> | |||||
#include <contrib/dev/acpica/include/acpi.h> | |||||
#include <contrib/dev/acpica/include/accommon.h> | |||||
#include <dev/acpica/acpivar.h> | |||||
#include <dev/evdev/input.h> | |||||
#include <dev/hid/hid.h> | |||||
#include <dev/hid/hidquirk.h> | |||||
#include <dev/iicbus/iic.h> | |||||
#include <dev/iicbus/iicbus.h> | |||||
#include <dev/iicbus/iiconf.h> | |||||
#include "hid_if.h" | |||||
#ifdef IICHID_DEBUG | |||||
static int iichid_debug = 0; | |||||
static SYSCTL_NODE(_hw, OID_AUTO, iichid, CTLFLAG_RW, 0, "I2C HID"); | |||||
SYSCTL_INT(_hw_iichid, OID_AUTO, debug, CTLFLAG_RWTUN, | |||||
&iichid_debug, 1, "Debug level"); | |||||
#define DPRINTFN(sc, n, ...) do { \ | |||||
if (iichid_debug >= (n)) \ | |||||
device_printf((sc)->dev, __VA_ARGS__); \ | |||||
} while (0) | |||||
#define DPRINTF(sc, ...) DPRINTFN(sc, 1, __VA_ARGS__) | |||||
#else | |||||
hselasky: You can change:
DPRINTFN((sc), 1, __VA_ARGS__)
Into:
DPRINTFN(sc, 1, __VA_ARGS__)
At this… | |||||
Done Inline ActionsDone wulf: Done | |||||
#define DPRINTFN(...) do {} while (0) | |||||
#define DPRINTF(...) do {} while (0) | |||||
Done Inline Actionsplease use do {} while (0) for these! hselasky: please use do {} while (0) for these! | |||||
Done Inline ActionsDone wulf: Done | |||||
#endif | |||||
typedef hid_size_t iichid_size_t; | |||||
#define IICHID_SIZE_MAX (UINT16_MAX - 2) | |||||
/* 7.2 */ | |||||
enum { | |||||
I2C_HID_CMD_DESCR = 0x0, | |||||
I2C_HID_CMD_RESET = 0x1, | |||||
I2C_HID_CMD_GET_REPORT = 0x2, | |||||
I2C_HID_CMD_SET_REPORT = 0x3, | |||||
I2C_HID_CMD_GET_IDLE = 0x4, | |||||
I2C_HID_CMD_SET_IDLE = 0x5, | |||||
I2C_HID_CMD_GET_PROTO = 0x6, | |||||
I2C_HID_CMD_SET_PROTO = 0x7, | |||||
I2C_HID_CMD_SET_POWER = 0x8, | |||||
}; | |||||
#define I2C_HID_POWER_ON 0x0 | |||||
#define I2C_HID_POWER_OFF 0x1 | |||||
/* | |||||
* Since interrupt resource acquisition is not always possible (in case of GPIO | |||||
* interrupts) iichid now supports a sampling_mode. | |||||
* Set dev.iichid.<unit>.sampling_rate_slow to a value greater then 0 | |||||
* to activate sampling. A value of 0 is possible but will not reset the | |||||
* callout and, thereby, disable further report requests. Do not set the | |||||
* sampling_rate_fast value too high as it may result in periodical lags of | |||||
* cursor motion. | |||||
*/ | |||||
#define IICHID_SAMPLING_RATE_FAST 60 | |||||
#define IICHID_SAMPLING_RATE_SLOW 10 | |||||
#define IICHID_SAMPLING_HYSTERESIS 1 | |||||
/* 5.1.1 - HID Descriptor Format */ | |||||
struct i2c_hid_desc { | |||||
uint16_t wHIDDescLength; | |||||
uint16_t bcdVersion; | |||||
uint16_t wReportDescLength; | |||||
uint16_t wReportDescRegister; | |||||
uint16_t wInputRegister; | |||||
uint16_t wMaxInputLength; | |||||
uint16_t wOutputRegister; | |||||
uint16_t wMaxOutputLength; | |||||
uint16_t wCommandRegister; | |||||
uint16_t wDataRegister; | |||||
uint16_t wVendorID; | |||||
uint16_t wProductID; | |||||
uint16_t wVersionID; | |||||
uint32_t reserved; | |||||
} __packed; | |||||
static char *iichid_ids[] = { | |||||
"PNP0C50", | |||||
"ACPI0C50", | |||||
NULL | |||||
}; | |||||
enum iichid_powerstate_how { | |||||
IICHID_PS_NULL, | |||||
IICHID_PS_ON, | |||||
IICHID_PS_OFF, | |||||
}; | |||||
/* | |||||
* Locking: no internal locks are used. To serialize access to shared members, | |||||
* external iicbus lock should be taken. That allows to make locking greatly | |||||
* simple at the cost of running front interrupt handlers with locked bus. | |||||
*/ | |||||
struct iichid_softc { | |||||
device_t dev; | |||||
bool probe_done; | |||||
int probe_result; | |||||
struct hid_device_info hw; | |||||
uint16_t addr; /* Shifted left by 1 */ | |||||
struct i2c_hid_desc desc; | |||||
hid_intr_t *intr_handler; | |||||
void *intr_ctx; | |||||
uint8_t *intr_buf; | |||||
iichid_size_t intr_bufsize; | |||||
int irq_rid; | |||||
struct resource *irq_res; | |||||
void *irq_cookie; | |||||
#ifdef IICHID_SAMPLING | |||||
int sampling_rate_slow; /* iicbus lock */ | |||||
int sampling_rate_fast; | |||||
int sampling_hysteresis; | |||||
int missing_samples; /* iicbus lock */ | |||||
struct timeout_task periodic_task; /* iicbus lock */ | |||||
bool callout_setup; /* iicbus lock */ | |||||
struct taskqueue *taskqueue; | |||||
struct task event_task; | |||||
#endif | |||||
bool open; /* iicbus lock */ | |||||
bool suspend; /* iicbus lock */ | |||||
bool power_on; /* iicbus lock */ | |||||
}; | |||||
static device_probe_t iichid_probe; | |||||
static device_attach_t iichid_attach; | |||||
static device_detach_t iichid_detach; | |||||
static device_resume_t iichid_resume; | |||||
static device_suspend_t iichid_suspend; | |||||
#ifdef IICHID_SAMPLING | |||||
static int iichid_setup_callout(struct iichid_softc *); | |||||
static int iichid_reset_callout(struct iichid_softc *); | |||||
static void iichid_teardown_callout(struct iichid_softc *); | |||||
#endif | |||||
static __inline bool | |||||
acpi_is_iichid(ACPI_HANDLE handle) | |||||
{ | |||||
char **ids; | |||||
UINT32 sta; | |||||
for (ids = iichid_ids; *ids != NULL; ids++) { | |||||
if (acpi_MatchHid(handle, *ids)) | |||||
break; | |||||
} | |||||
if (*ids == NULL) | |||||
return (false); | |||||
/* | |||||
* If no _STA method or if it failed, then assume that | |||||
* the device is present. | |||||
*/ | |||||
if (ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) || | |||||
ACPI_DEVICE_PRESENT(sta)) | |||||
return (true); | |||||
return (false); | |||||
} | |||||
static ACPI_STATUS | |||||
iichid_get_config_reg(ACPI_HANDLE handle, uint16_t *config_reg) | |||||
{ | |||||
ACPI_OBJECT *result; | |||||
ACPI_BUFFER acpi_buf; | |||||
ACPI_STATUS status; | |||||
/* | |||||
* function (_DSM) to be evaluated to retrieve the address of | |||||
* the configuration register of the HID device. | |||||
*/ | |||||
/* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ | |||||
static uint8_t dsm_guid[ACPI_UUID_LENGTH] = { | |||||
0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, | |||||
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, | |||||
}; | |||||
status = acpi_EvaluateDSMTyped(handle, dsm_guid, 1, 1, NULL, &acpi_buf, | |||||
ACPI_TYPE_INTEGER); | |||||
if (ACPI_FAILURE(status)) { | |||||
printf("%s: error evaluating _DSM\n", __func__); | |||||
return (status); | |||||
} | |||||
result = (ACPI_OBJECT *) acpi_buf.Pointer; | |||||
*config_reg = result->Integer.Value & 0xFFFF; | |||||
AcpiOsFree(result); | |||||
return (status); | |||||
} | |||||
static int | |||||
iichid_cmd_read(struct iichid_softc* sc, void *buf, iichid_size_t maxlen, | |||||
iichid_size_t *actual_len) | |||||
{ | |||||
/* | |||||
* 6.1.3 - Retrieval of Input Reports | |||||
* DEVICE returns the length (2 Bytes) and the entire Input Report. | |||||
*/ | |||||
uint8_t actbuf[2] = { 0, 0 }; | |||||
/* Read actual input report length. */ | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_RD | IIC_M_NOSTOP, sizeof(actbuf), actbuf }, | |||||
}; | |||||
uint16_t actlen; | |||||
int error; | |||||
error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); | |||||
if (error != 0) | |||||
return (error); | |||||
actlen = actbuf[0] | actbuf[1] << 8; | |||||
if (actlen <= 2 || actlen == 0xFFFF || maxlen == 0) { | |||||
/* Read and discard 1 byte to send I2C STOP condition. */ | |||||
msgs[0] = (struct iic_msg) | |||||
{ sc->addr, IIC_M_RD | IIC_M_NOSTART, 1, actbuf }; | |||||
actlen = 0; | |||||
} else { | |||||
actlen -= 2; | |||||
if (actlen > maxlen) { | |||||
DPRINTF(sc, "input report too big. requested=%d " | |||||
"received=%d\n", maxlen, actlen); | |||||
actlen = maxlen; | |||||
} | |||||
/* Read input report itself. */ | |||||
msgs[0] = (struct iic_msg) | |||||
{ sc->addr, IIC_M_RD | IIC_M_NOSTART, actlen, buf }; | |||||
} | |||||
error = iicbus_transfer(sc->dev, msgs, 1); | |||||
if (error == 0 && actual_len != NULL) | |||||
*actual_len = actlen; | |||||
DPRINTFN(sc, 5, | |||||
"%*D - %*D\n", 2, actbuf, " ", msgs[0].len, msgs[0].buf, " "); | |||||
return (error); | |||||
} | |||||
static int | |||||
iichid_cmd_write(struct iichid_softc *sc, const void *buf, iichid_size_t len) | |||||
{ | |||||
/* 6.2.3 - Sending Output Reports. */ | |||||
uint8_t *cmdreg = (uint8_t *)&sc->desc.wOutputRegister; | |||||
uint16_t replen = 2 + len; | |||||
uint8_t cmd[4] = { cmdreg[0], cmdreg[1], replen & 0xFF, replen >> 8 }; | |||||
struct iic_msg msgs[] = { | |||||
{sc->addr, IIC_M_WR | IIC_M_NOSTOP, sizeof(cmd), cmd}, | |||||
{sc->addr, IIC_M_WR | IIC_M_NOSTART, len, __DECONST(void *, buf)}, | |||||
}; | |||||
if (le16toh(sc->desc.wMaxOutputLength) == 0) | |||||
return (IIC_ENOTSUPP); | |||||
if (len < 2) | |||||
return (IIC_ENOTSUPP); | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_WRITE (len %d): " | |||||
"%*D\n", len, len, buf, " "); | |||||
return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); | |||||
} | |||||
static int | |||||
iichid_cmd_get_hid_desc(struct iichid_softc *sc, uint16_t config_reg, | |||||
struct i2c_hid_desc *hid_desc) | |||||
{ | |||||
/* | |||||
* 5.2.2 - HID Descriptor Retrieval | |||||
* register is passed from the controller. | |||||
*/ | |||||
uint16_t cmd = htole16(config_reg); | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_WR | IIC_M_NOSTOP, 2, (uint8_t *)&cmd }, | |||||
{ sc->addr, IIC_M_RD, sizeof(*hid_desc), (uint8_t *)hid_desc }, | |||||
}; | |||||
int error; | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_DESCR at 0x%x\n", config_reg); | |||||
error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); | |||||
if (error != 0) | |||||
return (error); | |||||
DPRINTF(sc, "HID descriptor: %*D\n", | |||||
(int)sizeof(struct i2c_hid_desc), hid_desc, " "); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_set_power(struct iichid_softc *sc, uint8_t param) | |||||
{ | |||||
uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; | |||||
uint8_t cmd[] = { cmdreg[0], cmdreg[1], param, I2C_HID_CMD_SET_POWER }; | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_WR, sizeof(cmd), cmd }, | |||||
}; | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_SET_POWER(%d)\n", param); | |||||
return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); | |||||
} | |||||
static int | |||||
iichid_reset(struct iichid_softc *sc) | |||||
{ | |||||
uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; | |||||
uint8_t cmd[] = { cmdreg[0], cmdreg[1], 0, I2C_HID_CMD_RESET }; | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_WR, sizeof(cmd), cmd }, | |||||
}; | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_RESET\n"); | |||||
return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); | |||||
} | |||||
static int | |||||
iichid_cmd_get_report_desc(struct iichid_softc* sc, void *buf, | |||||
iichid_size_t len) | |||||
{ | |||||
uint16_t cmd = sc->desc.wReportDescRegister; | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_WR | IIC_M_NOSTOP, 2, (uint8_t *)&cmd }, | |||||
{ sc->addr, IIC_M_RD, len, buf }, | |||||
}; | |||||
int error; | |||||
DPRINTF(sc, "HID command I2C_HID_REPORT_DESCR at 0x%x with size %d\n", | |||||
le16toh(cmd), len); | |||||
error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); | |||||
if (error != 0) | |||||
return (error); | |||||
DPRINTF(sc, "HID report descriptor: %*D\n", len, buf, " "); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_cmd_get_report(struct iichid_softc* sc, void *buf, iichid_size_t maxlen, | |||||
iichid_size_t *actual_len, uint8_t type, uint8_t id) | |||||
{ | |||||
/* | |||||
* 7.2.2.4 - "The protocol is optimized for Report < 15. If a | |||||
* report ID >= 15 is necessary, then the Report ID in the Low Byte | |||||
* must be set to 1111 and a Third Byte is appended to the protocol. | |||||
* This Third Byte contains the entire/actual report ID." | |||||
*/ | |||||
uint8_t *dtareg = (uint8_t *)&sc->desc.wDataRegister; | |||||
uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; | |||||
uint8_t cmd[] = { /*________|______id>=15_____|______id<15______*/ | |||||
cmdreg[0] , | |||||
cmdreg[1] , | |||||
(id >= 15 ? 15 | (type << 4): id | (type << 4)), | |||||
I2C_HID_CMD_GET_REPORT , | |||||
(id >= 15 ? id : dtareg[0] ), | |||||
(id >= 15 ? dtareg[0] : dtareg[1] ), | |||||
(id >= 15 ? dtareg[1] : 0 ), | |||||
}; | |||||
int cmdlen = (id >= 15 ? 7 : 6 ); | |||||
uint8_t actbuf[2] = { 0, 0 }; | |||||
uint16_t actlen; | |||||
int d, error; | |||||
struct iic_msg msgs[] = { | |||||
{ sc->addr, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd }, | |||||
{ sc->addr, IIC_M_RD | IIC_M_NOSTOP, 2, actbuf }, | |||||
{ sc->addr, IIC_M_RD | IIC_M_NOSTART, maxlen, buf }, | |||||
}; | |||||
if (maxlen == 0) | |||||
return (EINVAL); | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_GET_REPORT %d " | |||||
"(type %d, len %d)\n", id, type, maxlen); | |||||
/* | |||||
* 7.2.2.2 - Response will be a 2-byte length value, the report | |||||
* id (1 byte, if defined in Report Descriptor), and then the report. | |||||
*/ | |||||
error = iicbus_transfer(sc->dev, msgs, nitems(msgs)); | |||||
if (error != 0) | |||||
return (error); | |||||
actlen = actbuf[0] | actbuf[1] << 8; | |||||
if (actlen != maxlen + 2) | |||||
DPRINTF(sc, "response size %d != expected length %d\n", | |||||
actlen, maxlen + 2); | |||||
if (actlen <= 2 || actlen == 0xFFFF) | |||||
return (ENOMSG); | |||||
d = id != 0 ? *(uint8_t *)buf : 0; | |||||
if (d != id) { | |||||
DPRINTF(sc, "response report id %d != %d\n", d, id); | |||||
return (EBADMSG); | |||||
} | |||||
actlen -= 2; | |||||
if (actlen > maxlen) | |||||
actlen = maxlen; | |||||
if (actual_len != NULL) | |||||
*actual_len = actlen; | |||||
DPRINTF(sc, "response: %*D %*D\n", 2, actbuf, " ", actlen, buf, " "); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_cmd_set_report(struct iichid_softc* sc, const void *buf, | |||||
iichid_size_t len, uint8_t type, uint8_t id) | |||||
{ | |||||
/* | |||||
* 7.2.2.4 - "The protocol is optimized for Report < 15. If a | |||||
* report ID >= 15 is necessary, then the Report ID in the Low Byte | |||||
* must be set to 1111 and a Third Byte is appended to the protocol. | |||||
* This Third Byte contains the entire/actual report ID." | |||||
*/ | |||||
uint8_t *dtareg = (uint8_t *)&sc->desc.wDataRegister; | |||||
uint8_t *cmdreg = (uint8_t *)&sc->desc.wCommandRegister; | |||||
uint16_t replen = 2 + len; | |||||
uint8_t cmd[] = { /*________|______id>=15_____|______id<15______*/ | |||||
cmdreg[0] , | |||||
cmdreg[1] , | |||||
(id >= 15 ? 15 | (type << 4): id | (type << 4)), | |||||
I2C_HID_CMD_SET_REPORT , | |||||
(id >= 15 ? id : dtareg[0] ), | |||||
(id >= 15 ? dtareg[0] : dtareg[1] ), | |||||
(id >= 15 ? dtareg[1] : replen & 0xff ), | |||||
(id >= 15 ? replen & 0xff : replen >> 8 ), | |||||
(id >= 15 ? replen >> 8 : 0 ), | |||||
}; | |||||
int cmdlen = (id >= 15 ? 9 : 8 ); | |||||
struct iic_msg msgs[] = { | |||||
{sc->addr, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd}, | |||||
{sc->addr, IIC_M_WR | IIC_M_NOSTART, len, __DECONST(void *, buf)}, | |||||
}; | |||||
DPRINTF(sc, "HID command I2C_HID_CMD_SET_REPORT %d (type %d, len %d): " | |||||
"%*D\n", id, type, len, len, buf, " "); | |||||
return (iicbus_transfer(sc->dev, msgs, nitems(msgs))); | |||||
} | |||||
#ifdef IICHID_SAMPLING | |||||
static void | |||||
iichid_event_task(void *context, int pending) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
device_t parent; | |||||
iichid_size_t actual; | |||||
bool bus_requested; | |||||
int error; | |||||
sc = context; | |||||
parent = device_get_parent(sc->dev); | |||||
bus_requested = false; | |||||
if (iicbus_request_bus(parent, sc->dev, IIC_WAIT) != 0) | |||||
goto rearm; | |||||
bus_requested = true; | |||||
if (!sc->power_on) | |||||
goto out; | |||||
error = iichid_cmd_read(sc, sc->intr_buf, sc->intr_bufsize, &actual); | |||||
if (error == 0) { | |||||
if (actual > 0) { | |||||
sc->intr_handler(sc->intr_ctx, sc->intr_buf, actual); | |||||
sc->missing_samples = 0; | |||||
} else | |||||
++sc->missing_samples; | |||||
} else | |||||
DPRINTF(sc, "read error occured: %d\n", error); | |||||
rearm: | |||||
if (sc->callout_setup && sc->sampling_rate_slow > 0) { | |||||
if (sc->missing_samples == sc->sampling_hysteresis) | |||||
sc->intr_handler(sc->intr_ctx, sc->intr_buf, 0); | |||||
taskqueue_enqueue_timeout(sc->taskqueue, &sc->periodic_task, | |||||
hz / MAX(sc->missing_samples >= sc->sampling_hysteresis ? | |||||
sc->sampling_rate_slow : sc->sampling_rate_fast, 1)); | |||||
} | |||||
out: | |||||
if (bus_requested) | |||||
iicbus_release_bus(parent, sc->dev); | |||||
} | |||||
#endif /* IICHID_SAMPLING */ | |||||
static void | |||||
iichid_intr(void *context) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
device_t parent; | |||||
iichid_size_t maxlen, actual; | |||||
int error; | |||||
sc = context; | |||||
parent = device_get_parent(sc->dev); | |||||
/* | |||||
* Designware(IG4) driver-specific hack. | |||||
* Requesting of an I2C bus with IIC_DONTWAIT parameter enables polled | |||||
* mode in the driver, making possible iicbus_transfer execution from | |||||
* interrupt handlers and callouts. | |||||
*/ | |||||
if (iicbus_request_bus(parent, sc->dev, IIC_DONTWAIT) != 0) | |||||
return; | |||||
/* | |||||
* Reading of input reports of I2C devices residing in SLEEP state is | |||||
* not allowed and often returns a garbage. If a HOST needs to | |||||
* communicate with the DEVICE it MUST issue a SET POWER command | |||||
* (to ON) before any other command. As some hardware requires reads to | |||||
* acknoledge interrupts we fetch only length header and discard it. | |||||
*/ | |||||
maxlen = sc->power_on ? sc->intr_bufsize : 0; | |||||
error = iichid_cmd_read(sc, sc->intr_buf, maxlen, &actual); | |||||
if (error == 0) { | |||||
if (sc->power_on) { | |||||
if (actual != 0) | |||||
sc->intr_handler(sc->intr_ctx, sc->intr_buf, | |||||
actual); | |||||
else | |||||
DPRINTF(sc, "no data received\n"); | |||||
} | |||||
} else | |||||
DPRINTF(sc, "read error occured: %d\n", error); | |||||
iicbus_release_bus(parent, sc->dev); | |||||
} | |||||
static int | |||||
iichid_set_power_state(struct iichid_softc *sc, | |||||
enum iichid_powerstate_how how_open, | |||||
enum iichid_powerstate_how how_suspend) | |||||
{ | |||||
device_t parent; | |||||
int error; | |||||
int how_request; | |||||
bool power_on; | |||||
/* | |||||
* Request iicbus early as sc->suspend and sc->power_on | |||||
* are protected by iicbus internal lock. | |||||
*/ | |||||
parent = device_get_parent(sc->dev); | |||||
/* Allow to interrupt open()/close() handlers by SIGINT */ | |||||
how_request = how_open == IICHID_PS_NULL ? IIC_WAIT : IIC_INTRWAIT; | |||||
error = iicbus_request_bus(parent, sc->dev, how_request); | |||||
if (error != 0) | |||||
return (error); | |||||
switch (how_open) { | |||||
case IICHID_PS_ON: | |||||
sc->open = true; | |||||
break; | |||||
case IICHID_PS_OFF: | |||||
sc->open = false; | |||||
break; | |||||
case IICHID_PS_NULL: | |||||
default: | |||||
break; | |||||
} | |||||
switch (how_suspend) { | |||||
case IICHID_PS_ON: | |||||
sc->suspend = false; | |||||
break; | |||||
case IICHID_PS_OFF: | |||||
sc->suspend = true; | |||||
break; | |||||
case IICHID_PS_NULL: | |||||
default: | |||||
break; | |||||
} | |||||
power_on = sc->open & !sc->suspend; | |||||
if (power_on != sc->power_on) { | |||||
error = iichid_set_power(sc, | |||||
power_on ? I2C_HID_POWER_ON : I2C_HID_POWER_OFF); | |||||
sc->power_on = power_on; | |||||
#ifdef IICHID_SAMPLING | |||||
if (sc->sampling_rate_slow >= 0 && sc->intr_handler != NULL) { | |||||
if (power_on) { | |||||
iichid_setup_callout(sc); | |||||
iichid_reset_callout(sc); | |||||
} else | |||||
iichid_teardown_callout(sc); | |||||
} | |||||
#endif | |||||
} | |||||
iicbus_release_bus(parent, sc->dev); | |||||
return (error); | |||||
} | |||||
static int | |||||
iichid_setup_interrupt(struct iichid_softc *sc) | |||||
{ | |||||
sc->irq_cookie = 0; | |||||
int error = bus_setup_intr(sc->dev, sc->irq_res, | |||||
INTR_TYPE_TTY|INTR_MPSAFE, NULL, iichid_intr, sc, &sc->irq_cookie); | |||||
if (error != 0) | |||||
DPRINTF(sc, "Could not setup interrupt handler\n"); | |||||
else | |||||
DPRINTF(sc, "successfully setup interrupt\n"); | |||||
return (error); | |||||
} | |||||
static void | |||||
iichid_teardown_interrupt(struct iichid_softc *sc) | |||||
{ | |||||
if (sc->irq_cookie) | |||||
bus_teardown_intr(sc->dev, sc->irq_res, sc->irq_cookie); | |||||
sc->irq_cookie = 0; | |||||
} | |||||
#ifdef IICHID_SAMPLING | |||||
static int | |||||
iichid_setup_callout(struct iichid_softc *sc) | |||||
{ | |||||
if (sc->sampling_rate_slow < 0) { | |||||
DPRINTF(sc, "sampling_rate is below 0, can't setup callout\n"); | |||||
return (EINVAL); | |||||
} | |||||
sc->callout_setup = true; | |||||
DPRINTF(sc, "successfully setup callout\n"); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_reset_callout(struct iichid_softc *sc) | |||||
{ | |||||
if (sc->sampling_rate_slow <= 0) { | |||||
DPRINTF(sc, "sampling_rate is below or equal to 0, " | |||||
"can't reset callout\n"); | |||||
return (EINVAL); | |||||
} | |||||
if (!sc->callout_setup) | |||||
return (EINVAL); | |||||
/* Start with slow sampling. */ | |||||
sc->missing_samples = sc->sampling_hysteresis; | |||||
taskqueue_enqueue(sc->taskqueue, &sc->event_task); | |||||
return (0); | |||||
} | |||||
static void | |||||
iichid_teardown_callout(struct iichid_softc *sc) | |||||
{ | |||||
sc->callout_setup = false; | |||||
taskqueue_cancel_timeout(sc->taskqueue, &sc->periodic_task, NULL); | |||||
DPRINTF(sc, "tore callout down\n"); | |||||
} | |||||
static int | |||||
iichid_sysctl_sampling_rate_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
device_t parent; | |||||
int error, oldval, value; | |||||
sc = arg1; | |||||
value = sc->sampling_rate_slow; | |||||
error = sysctl_handle_int(oidp, &value, 0, req); | |||||
if (error != 0 || req->newptr == NULL || | |||||
value == sc->sampling_rate_slow) | |||||
return (error); | |||||
/* Can't switch to interrupt mode if it is not supported. */ | |||||
if (sc->irq_res == NULL && value < 0) | |||||
return (EINVAL); | |||||
parent = device_get_parent(sc->dev); | |||||
error = iicbus_request_bus(parent, sc->dev, IIC_WAIT); | |||||
if (error != 0) | |||||
return (iic2errno(error)); | |||||
oldval = sc->sampling_rate_slow; | |||||
sc->sampling_rate_slow = value; | |||||
if (oldval < 0 && value >= 0) { | |||||
iichid_teardown_interrupt(sc); | |||||
if (sc->power_on) | |||||
iichid_setup_callout(sc); | |||||
} else if (oldval >= 0 && value < 0) { | |||||
if (sc->power_on) | |||||
iichid_teardown_callout(sc); | |||||
iichid_setup_interrupt(sc); | |||||
} | |||||
if (sc->power_on && value > 0) | |||||
iichid_reset_callout(sc); | |||||
iicbus_release_bus(parent, sc->dev); | |||||
DPRINTF(sc, "new sampling_rate value: %d\n", value); | |||||
return (0); | |||||
} | |||||
#endif /* IICHID_SAMPLING */ | |||||
static void | |||||
iichid_intr_setup(device_t dev, hid_intr_t intr, void *context, | |||||
struct hid_rdesc_info *rdesc) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
/* | |||||
* Do not rely on wMaxInputLength, as some devices may set it to | |||||
* a wrong length. Find the longest input report in report descriptor. | |||||
*/ | |||||
rdesc->rdsize = rdesc->isize; | |||||
/* Write and get/set_report sizes are limited by I2C-HID protocol. */ | |||||
rdesc->grsize = rdesc->srsize = IICHID_SIZE_MAX; | |||||
rdesc->wrsize = IICHID_SIZE_MAX; | |||||
sc->intr_handler = intr; | |||||
sc->intr_ctx = context; | |||||
sc->intr_buf = malloc(rdesc->rdsize, M_DEVBUF, M_WAITOK | M_ZERO); | |||||
sc->intr_bufsize = rdesc->rdsize; | |||||
#ifdef IICHID_SAMPLING | |||||
taskqueue_start_threads(&sc->taskqueue, 1, PI_TTY, | |||||
"%s taskq", device_get_nameunit(sc->dev)); | |||||
#endif | |||||
} | |||||
static void | |||||
iichid_intr_unsetup(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
#ifdef IICHID_SAMPLING | |||||
taskqueue_drain_all(sc->taskqueue); | |||||
#endif | |||||
free(sc->intr_buf, M_DEVBUF); | |||||
} | |||||
static int | |||||
iichid_intr_start(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
DPRINTF(sc, "iichid device open\n"); | |||||
iichid_set_power_state(sc, IICHID_PS_ON, IICHID_PS_NULL); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_intr_stop(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
DPRINTF(sc, "iichid device close\n"); | |||||
/* | |||||
* 8.2 - The HOST determines that there are no active applications | |||||
* that are currently using the specific HID DEVICE. The HOST | |||||
* is recommended to issue a HIPO command to the DEVICE to force | |||||
* the DEVICE in to a lower power state. | |||||
*/ | |||||
iichid_set_power_state(sc, IICHID_PS_OFF, IICHID_PS_NULL); | |||||
return (0); | |||||
} | |||||
static void | |||||
iichid_intr_poll(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
iichid_size_t actual; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = iichid_cmd_read(sc, sc->intr_buf, sc->intr_bufsize, &actual); | |||||
if (error == 0 && actual != 0) | |||||
sc->intr_handler(sc->intr_ctx, sc->intr_buf, actual); | |||||
} | |||||
/* | |||||
* HID interface | |||||
*/ | |||||
static int | |||||
iichid_get_rdesc(device_t dev, void *buf, hid_size_t len) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = iichid_cmd_get_report_desc(sc, buf, len); | |||||
if (error) | |||||
DPRINTF(sc, "failed to fetch report descriptor: %d\n", error); | |||||
return (iic2errno(error)); | |||||
} | |||||
static int | |||||
iichid_read(device_t dev, void *buf, hid_size_t maxlen, hid_size_t *actlen) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
device_t parent; | |||||
int error; | |||||
if (maxlen > IICHID_SIZE_MAX) | |||||
return (EMSGSIZE); | |||||
sc = device_get_softc(dev); | |||||
parent = device_get_parent(sc->dev); | |||||
error = iicbus_request_bus(parent, sc->dev, IIC_WAIT); | |||||
if (error == 0) { | |||||
error = iichid_cmd_read(sc, buf, maxlen, actlen); | |||||
iicbus_release_bus(parent, sc->dev); | |||||
} | |||||
return (iic2errno(error)); | |||||
} | |||||
static int | |||||
iichid_write(device_t dev, const void *buf, hid_size_t len) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
if (len > IICHID_SIZE_MAX) | |||||
return (EMSGSIZE); | |||||
sc = device_get_softc(dev); | |||||
return (iic2errno(iichid_cmd_write(sc, buf, len))); | |||||
} | |||||
static int | |||||
iichid_get_report(device_t dev, void *buf, hid_size_t maxlen, | |||||
hid_size_t *actlen, uint8_t type, uint8_t id) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
if (maxlen > IICHID_SIZE_MAX) | |||||
return (EMSGSIZE); | |||||
sc = device_get_softc(dev); | |||||
return (iic2errno( | |||||
iichid_cmd_get_report(sc, buf, maxlen, actlen, type, id))); | |||||
} | |||||
static int | |||||
iichid_set_report(device_t dev, const void *buf, hid_size_t len, uint8_t type, | |||||
uint8_t id) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
if (len > IICHID_SIZE_MAX) | |||||
return (EMSGSIZE); | |||||
sc = device_get_softc(dev); | |||||
return (iic2errno(iichid_cmd_set_report(sc, buf, len, type, id))); | |||||
} | |||||
static int | |||||
iichid_set_idle(device_t dev, uint16_t duration, uint8_t id) | |||||
{ | |||||
return (ENOTSUP); | |||||
} | |||||
static int | |||||
iichid_set_protocol(device_t dev, uint16_t protocol) | |||||
{ | |||||
return (ENOTSUP); | |||||
} | |||||
static int | |||||
iichid_fill_device_info(struct i2c_hid_desc *desc, ACPI_HANDLE handle, | |||||
struct hid_device_info *hw) | |||||
{ | |||||
ACPI_DEVICE_INFO *device_info; | |||||
hw->idBus = BUS_I2C; | |||||
hw->idVendor = le16toh(desc->wVendorID); | |||||
hw->idProduct = le16toh(desc->wProductID); | |||||
hw->idVersion = le16toh(desc->wVersionID); | |||||
/* get ACPI HID. It is a base part of the device name. */ | |||||
if (ACPI_FAILURE(AcpiGetObjectInfo(handle, &device_info))) | |||||
return (ENXIO); | |||||
if (device_info->Valid & ACPI_VALID_HID) | |||||
strlcpy(hw->idPnP, device_info->HardwareId.String, | |||||
HID_PNP_ID_SIZE); | |||||
snprintf(hw->name, sizeof(hw->name), "%s:%02lX %04X:%04X", | |||||
(device_info->Valid & ACPI_VALID_HID) ? | |||||
device_info->HardwareId.String : "Unknown", | |||||
(device_info->Valid & ACPI_VALID_UID) ? | |||||
strtoul(device_info->UniqueId.String, NULL, 10) : 0UL, | |||||
le16toh(desc->wVendorID), le16toh(desc->wProductID)); | |||||
AcpiOsFree(device_info); | |||||
strlcpy(hw->serial, "", sizeof(hw->serial)); | |||||
hw->rdescsize = le16toh(desc->wReportDescLength); | |||||
if (desc->wOutputRegister == 0 || desc->wMaxOutputLength == 0) | |||||
hid_add_dynamic_quirk(hw, HQ_NOWRITE); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_probe(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
ACPI_HANDLE handle; | |||||
char buf[80]; | |||||
uint16_t config_reg; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
sc->dev = dev; | |||||
if (sc->probe_done) | |||||
goto done; | |||||
sc->probe_done = true; | |||||
sc->probe_result = ENXIO; | |||||
if (acpi_disabled("iichid")) | |||||
return (ENXIO); | |||||
sc->addr = iicbus_get_addr(dev) << 1; | |||||
if (sc->addr == 0) | |||||
return (ENXIO); | |||||
handle = acpi_get_handle(dev); | |||||
if (handle == NULL) | |||||
return (ENXIO); | |||||
if (!acpi_is_iichid(handle)) | |||||
return (ENXIO); | |||||
if (ACPI_FAILURE(iichid_get_config_reg(handle, &config_reg))) | |||||
return (ENXIO); | |||||
DPRINTF(sc, " IICbus addr : 0x%02X\n", sc->addr >> 1); | |||||
DPRINTF(sc, " HID descriptor reg: 0x%02X\n", config_reg); | |||||
error = iichid_cmd_get_hid_desc(sc, config_reg, &sc->desc); | |||||
if (error) { | |||||
DPRINTF(sc, "could not retrieve HID descriptor from the " | |||||
"device: %d\n", error); | |||||
return (ENXIO); | |||||
} | |||||
if (le16toh(sc->desc.wHIDDescLength) != 30 || | |||||
le16toh(sc->desc.bcdVersion) != 0x100) { | |||||
DPRINTF(sc, "HID descriptor is broken\n"); | |||||
return (ENXIO); | |||||
} | |||||
/* Setup hid_device_info so we can figure out quirks for the device. */ | |||||
if (iichid_fill_device_info(&sc->desc, handle, &sc->hw) != 0) { | |||||
DPRINTF(sc, "error evaluating AcpiGetObjectInfo\n"); | |||||
return (ENXIO); | |||||
} | |||||
if (hid_test_quirk(&sc->hw, HQ_HID_IGNORE)) | |||||
return (ENXIO); | |||||
sc->probe_result = BUS_PROBE_DEFAULT; | |||||
done: | |||||
if (sc->probe_result <= BUS_PROBE_SPECIFIC) { | |||||
snprintf(buf, sizeof(buf), "%s I2C HID device", sc->hw.name); | |||||
device_set_desc_copy(dev, buf); | |||||
} | |||||
return (sc->probe_result); | |||||
} | |||||
static int | |||||
iichid_attach(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
device_t child; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = iichid_set_power(sc, I2C_HID_POWER_ON); | |||||
if (error) { | |||||
device_printf(dev, "failed to power on: %d\n", error); | |||||
return (ENXIO); | |||||
} | |||||
/* | |||||
* Windows driver sleeps for 1ms between the SET_POWER and RESET | |||||
* commands. So we too as some devices may depend on this. | |||||
*/ | |||||
pause("iichid", (hz + 999) / 1000); | |||||
error = iichid_reset(sc); | |||||
if (error) { | |||||
device_printf(dev, "failed to reset hardware: %d\n", error); | |||||
return (ENXIO); | |||||
} | |||||
sc->power_on = false; | |||||
#ifdef IICHID_SAMPLING | |||||
TASK_INIT(&sc->event_task, 0, iichid_event_task, sc); | |||||
/* taskqueue_create can't fail with M_WAITOK mflag passed. */ | |||||
sc->taskqueue = taskqueue_create("hmt_tq", M_WAITOK | M_ZERO, | |||||
taskqueue_thread_enqueue, &sc->taskqueue); | |||||
TIMEOUT_TASK_INIT(sc->taskqueue, &sc->periodic_task, 0, | |||||
iichid_event_task, sc); | |||||
sc->sampling_rate_slow = -1; | |||||
sc->sampling_rate_fast = IICHID_SAMPLING_RATE_FAST; | |||||
sc->sampling_hysteresis = IICHID_SAMPLING_HYSTERESIS; | |||||
#endif | |||||
sc->irq_rid = 0; | |||||
sc->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, | |||||
&sc->irq_rid, RF_ACTIVE); | |||||
if (sc->irq_res != NULL) { | |||||
DPRINTF(sc, "allocated irq at %p and rid %d\n", | |||||
sc->irq_res, sc->irq_rid); | |||||
error = iichid_setup_interrupt(sc); | |||||
} | |||||
if (sc->irq_res == NULL || error != 0) { | |||||
#ifdef IICHID_SAMPLING | |||||
device_printf(sc->dev, | |||||
"Interrupt setup failed. Fallback to sampling\n"); | |||||
sc->sampling_rate_slow = IICHID_SAMPLING_RATE_SLOW; | |||||
#else | |||||
device_printf(sc->dev, "Interrupt setup failed\n"); | |||||
if (sc->irq_res != NULL) | |||||
bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, | |||||
sc->irq_res); | |||||
error = ENXIO; | |||||
goto done; | |||||
#endif | |||||
} | |||||
#ifdef IICHID_SAMPLING | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), | |||||
OID_AUTO, "sampling_rate_slow", CTLTYPE_INT | CTLFLAG_RWTUN, | |||||
sc, 0, iichid_sysctl_sampling_rate_handler, "I", | |||||
"idle sampling rate in num/second"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), | |||||
OID_AUTO, "sampling_rate_fast", CTLTYPE_INT | CTLFLAG_RWTUN, | |||||
&sc->sampling_rate_fast, 0, | |||||
"active sampling rate in num/second"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), | |||||
OID_AUTO, "sampling_hysteresis", CTLTYPE_INT | CTLFLAG_RWTUN, | |||||
&sc->sampling_hysteresis, 0, | |||||
"number of missing samples before enabling of slow mode"); | |||||
hid_add_dynamic_quirk(&sc->hw, HQ_IICHID_SAMPLING); | |||||
#endif /* IICHID_SAMPLING */ | |||||
child = device_add_child(dev, "hidbus", -1); | |||||
if (child == NULL) { | |||||
device_printf(sc->dev, "Could not add I2C device\n"); | |||||
iichid_detach(dev); | |||||
error = ENOMEM; | |||||
goto done; | |||||
} | |||||
device_set_ivars(child, &sc->hw); | |||||
error = bus_generic_attach(dev); | |||||
if (error) { | |||||
device_printf(dev, "failed to attach child: error %d\n", error); | |||||
iichid_detach(dev); | |||||
} | |||||
done: | |||||
(void)iichid_set_power(sc, I2C_HID_POWER_OFF); | |||||
return (error); | |||||
} | |||||
static int | |||||
iichid_detach(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
error = device_delete_children(dev); | |||||
if (error) | |||||
return (error); | |||||
iichid_teardown_interrupt(sc); | |||||
if (sc->irq_res != NULL) | |||||
bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, | |||||
sc->irq_res); | |||||
#ifdef IICHID_SAMPLING | |||||
if (sc->taskqueue != NULL) | |||||
taskqueue_free(sc->taskqueue); | |||||
sc->taskqueue = NULL; | |||||
#endif | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_suspend(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
DPRINTF(sc, "Suspend called, setting device to power_state 1\n"); | |||||
(void)bus_generic_suspend(dev); | |||||
/* | |||||
* 8.2 - The HOST is going into a deep power optimized state and wishes | |||||
* to put all the devices into a low power state also. The HOST | |||||
* is recommended to issue a HIPO command to the DEVICE to force | |||||
* the DEVICE in to a lower power state. | |||||
*/ | |||||
error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_OFF); | |||||
if (error != 0) | |||||
DPRINTF(sc, "Could not set power_state, error: %d\n", error); | |||||
else | |||||
DPRINTF(sc, "Successfully set power_state\n"); | |||||
return (0); | |||||
} | |||||
static int | |||||
iichid_resume(device_t dev) | |||||
{ | |||||
struct iichid_softc *sc; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
DPRINTF(sc, "Resume called, setting device to power_state 0\n"); | |||||
error = iichid_set_power_state(sc, IICHID_PS_NULL, IICHID_PS_ON); | |||||
if (error != 0) | |||||
DPRINTF(sc, "Could not set power_state, error: %d\n", error); | |||||
else | |||||
DPRINTF(sc, "Successfully set power_state\n"); | |||||
(void)bus_generic_resume(dev); | |||||
return (0); | |||||
} | |||||
static devclass_t iichid_devclass; | |||||
static device_method_t iichid_methods[] = { | |||||
DEVMETHOD(device_probe, iichid_probe), | |||||
DEVMETHOD(device_attach, iichid_attach), | |||||
DEVMETHOD(device_detach, iichid_detach), | |||||
DEVMETHOD(device_suspend, iichid_suspend), | |||||
DEVMETHOD(device_resume, iichid_resume), | |||||
DEVMETHOD(hid_intr_setup, iichid_intr_setup), | |||||
DEVMETHOD(hid_intr_unsetup, iichid_intr_unsetup), | |||||
DEVMETHOD(hid_intr_start, iichid_intr_start), | |||||
DEVMETHOD(hid_intr_stop, iichid_intr_stop), | |||||
DEVMETHOD(hid_intr_poll, iichid_intr_poll), | |||||
/* HID interface */ | |||||
DEVMETHOD(hid_get_rdesc, iichid_get_rdesc), | |||||
DEVMETHOD(hid_read, iichid_read), | |||||
DEVMETHOD(hid_write, iichid_write), | |||||
DEVMETHOD(hid_get_report, iichid_get_report), | |||||
DEVMETHOD(hid_set_report, iichid_set_report), | |||||
DEVMETHOD(hid_set_idle, iichid_set_idle), | |||||
DEVMETHOD(hid_set_protocol, iichid_set_protocol), | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t iichid_driver = { | |||||
.name = "iichid", | |||||
.methods = iichid_methods, | |||||
.size = sizeof(struct iichid_softc), | |||||
}; | |||||
DRIVER_MODULE(iichid, iicbus, iichid_driver, iichid_devclass, NULL, 0); | |||||
MODULE_DEPEND(iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); | |||||
MODULE_DEPEND(iichid, acpi, 1, 1, 1); | |||||
MODULE_DEPEND(iichid, hid, 1, 1, 1); | |||||
MODULE_DEPEND(iichid, hidbus, 1, 1, 1); | |||||
MODULE_VERSION(iichid, 1); | |||||
IICBUS_ACPI_PNP_INFO(iichid_ids); |
You can change:
DPRINTFN((sc), 1, VA_ARGS)
Into:
DPRINTFN(sc, 1, VA_ARGS)
At this level, the macro arguments are already comma separated.