Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1853,6 +1853,7 @@ dev/iicbus/iicbb_if.m optional iicbb dev/iicbus/iicbus.c optional iicbus dev/iicbus/iicbus_if.m optional iicbus +dev/iicubs/iichid.c optional iichid acpi iicbus dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" Index: sys/dev/iicbus/input/acpi_iichid.c =================================================================== --- /dev/null +++ sys/dev/iicbus/input/acpi_iichid.c @@ -0,0 +1,549 @@ +/*- + * Copyright (c) 2019 Marc Priggemeyer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +static device_probe_t acpi_iichid_probe; +static device_attach_t acpi_iichid_attach; +static device_detach_t acpi_iichid_detach; + +static devclass_t acpi_iichid_devclass; + +static device_method_t acpi_iichid_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, acpi_iichid_probe), + DEVMETHOD(device_attach, acpi_iichid_attach), + DEVMETHOD(device_detach, acpi_iichid_detach), + + DEVMETHOD_END +}; + +static driver_t acpi_iichid_driver = { + .name = "acpi_iichid", + .methods = acpi_iichid_methods, + .size = sizeof(struct acpi_iichid_softc), +}; + +static char *acpi_iichid_ids[] = { + "PNP0C50", + "ACPI0C50", + NULL +}; + +static ACPI_STATUS +acpi_iichid_walk_handler(ACPI_RESOURCE *res, void *context) +{ + struct acpi_iichid_softc *sc; + + sc = context; + + switch(res->Type) { + case ACPI_RESOURCE_TYPE_SERIAL_BUS: + //device_printf(sc->dev, " - serial bus: "); + if (res->Data.CommonSerialBus.Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { + device_printf(sc->dev, "wrong bus type, should be %d is %d\n", ACPI_RESOURCE_SERIAL_TYPE_I2C, res->Data.CommonSerialBus.Type); + return (AE_TYPE); + } else { + //device_printf(sc->dev, "0x%x on %s\n", le16toh(res->Data.I2cSerialBus.SlaveAddress), res->Data.CommonSerialBus.ResourceSource.StringPtr); + sc->hw.device_addr = res->Data.I2cSerialBus.SlaveAddress; + } + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + if (res->Data.ExtendedIrq.InterruptCount > 0) { + device_printf(sc->dev, " - irq: %d\n", (int)res->Data.ExtendedIrq.Interrupts[0]); + sc->irq = res->Data.ExtendedIrq.Interrupts[0]; + } + + break; + case ACPI_RESOURCE_TYPE_END_TAG: + //device_printf(sc->dev, " - end parsing\n"); + break; + + default: + device_printf(sc->dev, "unexpected type %d while parsing Current Resource Settings (_CSR)\n", res->Type); + break; + } + + return AE_OK; +} + +static int +acpi_iichid_probe(device_t dev) +{ + if (acpi_disabled("iichid") || ACPI_ID_PROBE(device_get_parent(dev), dev, acpi_iichid_ids) == NULL) + return (ENXIO); + + device_set_desc(dev, "HID over I2C (ACPI)"); + + return (BUS_PROBE_VENDOR); +} + +static void +periodic_or_intr(void *context) +{ + struct acpi_iichid_softc *sc; + + sc = context; + taskqueue_enqueue(sc->taskqueue, &sc->event_task); +} + +static void +event_task(void *context, int pending) +{ + struct acpi_iichid_softc *sc; + + sc = context; + + mtx_lock(&sc->lock); + + struct iichid_softc *dsc; + dsc = sc->iichid_sc; + + mtx_unlock(&sc->lock); + + dsc->event_handler(dsc, pending); + + mtx_lock(&sc->lock); + if (sc->callout_setup && sc->sampling_rate > 0 && !callout_pending(&sc->periodic_callout) ) + callout_reset(&sc->periodic_callout, hz / sc->sampling_rate, periodic_or_intr, sc); + mtx_unlock(&sc->lock); +} + +static int +acpi_iichid_setup_callout(device_t dev, struct acpi_iichid_softc *sc) +{ + if (sc->sampling_rate < 0) + { + device_printf(dev, "sampling_rate is below 0, can't setup callout\n"); + return (EINVAL); + } + + callout_init(&sc->periodic_callout, 1); + sc->callout_setup=true; + device_printf(dev, "successfully setup callout"); + return (0); +} + +static int +acpi_iichid_reset_callout(device_t dev, struct acpi_iichid_softc *sc) +{ + if (sc->sampling_rate <= 0) + { + device_printf(dev, "sampling_rate is below or equal to 0, can't reset callout\n"); + return (EINVAL); + } + + if (sc->callout_setup) + callout_reset(&sc->periodic_callout, hz / sc->sampling_rate, periodic_or_intr, sc); + else + return (EINVAL); + return (0); +} + +static void +acpi_iichid_teardown_callout(device_t dev, struct acpi_iichid_softc *sc) +{ + callout_drain(&sc->periodic_callout); + sc->callout_setup=false; + device_printf(dev, "tore callout down\n"); +} + +static int +acpi_iichid_setup_interrupt(device_t dev, struct acpi_iichid_softc *sc) +{ + sc->irq_rid = 0; + sc->irq_cookie = 0; + sc->irq_res = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE); + + if( sc->irq_res != NULL ) + { + device_printf(dev, "allocated irq at 0x%lx and rid %d\n", (uint64_t)sc->irq_res, sc->irq_rid); + int error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY | INTR_MPSAFE, NULL, periodic_or_intr, sc, &sc->irq_cookie); + if (error != 0) + { + device_printf(dev, "Could not setup interrupt handler\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); + return error; + } else + device_printf(dev, "successfully setup interrupt\n"); + + } else { + device_printf(dev, "could not allocate IRQ resource\n"); + } + + return (0); +} + +static void +acpi_iichid_teardown_interrupt(device_t dev, struct acpi_iichid_softc *sc) +{ + if (sc->irq_cookie) + { + bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); + } + + if (sc->irq_res) + { + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); + } + sc->irq_rid = 0; + sc->irq_cookie = 0; + sc->irq_res = 0; +} + +static int +sysctl_sampling_rate_handler(SYSCTL_HANDLER_ARGS) +{ + int err, value, oldval; + struct acpi_iichid_softc *sc; + + sc = arg1; + + mtx_lock(&sc->lock); + + value = sc->sampling_rate; + oldval = sc->sampling_rate; + err = sysctl_handle_int(oidp, &value, 0, req); + + if (err != 0 || req->newptr == NULL || value == sc->sampling_rate) + { + mtx_unlock(&sc->lock); + return (err); + } + + sc->sampling_rate = value; + + if( oldval < 0 && value >= 0 ) + { + acpi_iichid_teardown_interrupt(sc->dev, sc); + acpi_iichid_setup_callout(sc->dev, sc); + } else if ( oldval >=0 && value < 0) + { + acpi_iichid_teardown_callout(sc->dev, sc); + acpi_iichid_setup_interrupt(sc->dev, sc); + } + + if( value > 0 ) + acpi_iichid_reset_callout(sc->dev, sc); + + device_printf(sc->dev, "new sampling_rate value: %d\n", value); + + mtx_unlock(&sc->lock); + + return (0); +} + +static int +acpi_iichid_attach(device_t dev) +{ + struct acpi_iichid_softc *sc; + sc = device_get_softc(dev); + + mtx_init(&sc->lock, "HID over I2C (ACPI) lock", NULL, MTX_DEF); + + sc->dev = dev; + + sc->irq = 0; + sc->irq_rid = 0; + sc->irq_res = 0; + sc->irq_cookie = 0; + sc->sampling_rate = -1; + sc->taskqueue = 0; + sc->iichid_sc = 0; + sc->callout_setup = false; + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "sampling_rate", CTLTYPE_INT | CTLFLAG_RWTUN, + sc, 0, + sysctl_sampling_rate_handler, "I", "sampling rate in num/second"); + + //get ACPI handles for current device and its parent + ACPI_HANDLE ahnd = acpi_get_handle(dev), + phnd = NULL; + + if (!ahnd) { + device_printf(dev, "Could not retrieve ACPI handle\n"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //besides the ACPI parent handle, get the newbus parent + device_t parent; + + if (ACPI_SUCCESS(AcpiGetParent(ahnd, &phnd)) && (parent = acpi_get_device(phnd)) && device_is_attached(parent)) + { + //device_printf(dev, "my parent is a %s and its ACPI path is %s\n", device_get_driver(parent)->name, acpi_name(phnd)); + } else { + device_printf(dev, "could not retrieve parent device or parent is not attached (driver loaded?)"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + + //function (_DSM) to be evaluated to retrieve the address of the configuration register of the hi device + /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ + static uint8_t acpi_iichid_dsm_guid[] = { + 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, + 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, + }; + + //prepare 4 arguments + ACPI_OBJECT obj[4]; + ACPI_OBJECT_LIST acpi_arg; + + ACPI_BUFFER acpi_buf; + + acpi_buf.Pointer = NULL; + acpi_buf.Length = ACPI_ALLOCATE_BUFFER; + + acpi_arg.Pointer = &obj[0]; + acpi_arg.Count = 4; + + obj[0].Type = ACPI_TYPE_BUFFER; + obj[0].Buffer.Length = sizeof(acpi_iichid_dsm_guid); + obj[0].Buffer.Pointer = &acpi_iichid_dsm_guid[0]; + + obj[1].Type = ACPI_TYPE_INTEGER; + obj[1].Integer.Value = 1; + + obj[2].Type = ACPI_TYPE_INTEGER; + obj[2].Integer.Value = 1; + + obj[3].Type = ACPI_TYPE_PACKAGE; + obj[3].Package.Count = 0; + + //evaluate + ACPI_STATUS status = ACPI_EVALUATE_OBJECT(device_get_parent(dev), dev, "_DSM", &acpi_arg, &acpi_buf); + + if (ACPI_FAILURE(status)) { + device_printf(dev, "error evaluating _DSM\n"); + if (acpi_buf.Pointer != NULL) + AcpiOsFree(acpi_buf.Pointer); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //the result will contain the register address (int type) + ACPI_OBJECT *result = (ACPI_OBJECT*)acpi_buf.Pointer; + if( result->Type != ACPI_TYPE_INTEGER ) { + device_printf(dev, "_DSM should return descriptor register address as integer\n"); + AcpiOsFree(result); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //take it (much work done for one byte -.-) + device_printf(dev, "descriptor register address is %lx\n", result->Integer.Value); + sc->hw.config_reg = result->Integer.Value; + + //cleanup + AcpiOsFree(result); + + //_CSR holds more data (device address and irq) and only needs a callback to evaluate its data + status = AcpiWalkResources(ahnd, "_CRS", acpi_iichid_walk_handler, sc); + + if (ACPI_FAILURE(status)) { + device_printf(dev, "could not evaluate _CRS\n"); + mtx_destroy(&sc->lock); + return (ENXIO); + } + + //get the full ACPI pathname of dev's parent + acpi_buf.Pointer = NULL; + acpi_buf.Length = ACPI_ALLOCATE_BUFFER; + AcpiGetName(phnd, ACPI_FULL_PATHNAME, &acpi_buf); + + device_printf(dev, "parent device is \"%s\"\n", (const char*)acpi_buf.Pointer); + AcpiOsFree(acpi_buf.Pointer); + + //padev will hold the newbus device handle of this (dev) devices ACPI parent, which is not necessarily the same as this (dev) devices parent. + //I.e. both parent devices might even be on different branches of the device tree. The ACPI parent is most likely a iicbus + //device (e.g. ig4's ig4iic_pci0), while the newbus parent of dev will in most cases acpi0. + device_t padev = acpi_get_device(phnd); //ACPI parent, the one we are interested in because it is a iicbus +#if 0 + device_t pbdev = device_get_parent(dev); //newbus parent, just for reference + + device_printf(dev, "parent devices: 0x%lx (ACPI, %s) and 0x%lx (Newbus, %s)\n", (uint64_t)padev, device_get_name(padev), (uint64_t)pbdev, device_get_name(padev)); +#endif + //look below padev whether there already is a iichid device that can be reused or create a new one + if (padev) + { + //the should be a iicbus device, nevertheless no KASSERT here since the system will continue to function + // only iichid device won't operate + device_t iicbus_dev = device_find_child(padev, "iicbus", -1); + if (iicbus_dev) + { + device_t *children; + int ccount; + + device_t dnew = NULL; + + //get a list of all children below iicbus and check if parameters match + if (device_get_children(iicbus_dev, &children, &ccount) == 0) + { + for(int i=0; iname, "iichid") == 0) + { + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(children[i]); + if ( dsc->hw.device_addr == sc->hw.device_addr + && dsc->hw.config_reg == sc->hw.config_reg ) + { + //reuse this child, there shouldn't be more than one + //if there are more devices matching that is observable in dmesg + dnew = children[i]; + device_printf(dev, "device %s ADDR 0x%x REG 0x%x already present on %s\n", device_get_nameunit(children[i]), dsc->hw.device_addr, dsc->hw.config_reg, device_get_nameunit(iicbus_dev)); + } + } + } + free(children, M_TEMP); + } + + //no iichid device found to be reused, so one is created and parameters are set + if ( dnew == NULL ) + { + //add child of type iichid below iicbus + dnew = BUS_ADD_CHILD(iicbus_dev, 0, "iichid", -1); + if (dnew) + { + //config register and device address via resource_list/ivars + bus_set_resource(dnew, SYS_RES_IOPORT, 0, sc->hw.config_reg, 2); + BUS_WRITE_IVAR(iicbus_dev, dnew, IICBUS_IVAR_ADDR, sc->hw.device_addr); + + //try and attach: + if (device_probe_and_attach(dnew) == 0) + { + // success? print status and device configuration + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(dnew); + device_printf(dev, "added %s ADDR 0x%x REG 0x%x to %s\n", device_get_nameunit(dnew), dsc->hw.device_addr, dsc->hw.config_reg, device_get_nameunit(iicbus_dev)); + } else { + //failure? remove child, print error and leave + device_printf(dev, "probe or attach failed for %s! (ADDR: 0x%x, REG: 0x%x)\n", device_get_nameunit(dnew), sc->hw.device_addr, sc->hw.config_reg); + device_delete_child(iicbus_dev, dnew); + mtx_destroy(&sc->lock); + return (ENXIO); + } + } else { + device_printf(dev, "could not attach iichid device to %s! (ADDR: 0x%x, REG: 0x%x)\n", device_get_nameunit(iicbus_dev), sc->hw.device_addr, sc->hw.config_reg); + mtx_destroy(&sc->lock); + return (ENXIO); + } + } + + if ( dnew != NULL ) + { + struct iichid_softc *dsc = (struct iichid_softc*)device_get_softc(dnew); + if (dsc) + { + sc->iichid_sc = dsc; + TASK_INIT(&sc->event_task, 0, event_task, sc); + + sc->taskqueue = taskqueue_create("iichid_tq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->taskqueue); + if( sc->taskqueue == NULL ) + { + return (ENXIO); + }else{ + taskqueue_start_threads(&sc->taskqueue, 1, PI_TTY, "%s taskq", device_get_nameunit(sc->dev)); + } + + int error; + if (sc->sampling_rate >= 0) + { + error = acpi_iichid_setup_callout(dev, sc); + if (error != 0) + { + device_printf(dev, "please consider setting the sampling_rate sysctl to -1"); + } + } else { + error = acpi_iichid_setup_interrupt(dev, sc); + if (error != 0) + { + device_printf(dev, "please consider setting the sampling_rate sysctl greater than 0."); + } + } + } + } + } + } + + + + return (0); /* success */ +} + +static int +acpi_iichid_detach(device_t dev) +{ + //we leave the added devices below iicbus instances intact, since this module is only needed to parameterize + // them. Afterwards they function without this + struct acpi_iichid_softc *sc; + sc = device_get_softc(dev); + + mtx_lock(&sc->lock); + + if (sc->taskqueue) + { + taskqueue_block(sc->taskqueue); + taskqueue_drain(sc->taskqueue, &sc->event_task); + taskqueue_free(sc->taskqueue); + } + + acpi_iichid_teardown_callout(dev, sc); + acpi_iichid_teardown_interrupt(dev, sc); + + mtx_unlock(&sc->lock); + mtx_destroy(&sc->lock); + return (0); +} + +DRIVER_MODULE(acpi_iichid, acpi, acpi_iichid_driver, acpi_iichid_devclass, NULL, 0); +MODULE_DEPEND(acpi_iichid, acpi, 1, 1, 1); +MODULE_DEPEND(acpi_iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_DEPEND(acpi_iichid, iichid, 1, 1, 1); +MODULE_VERSION(acpi_iichid, 1); Index: sys/dev/iicbus/input/iichid.h =================================================================== --- /dev/null +++ sys/dev/iicbus/input/iichid.h @@ -0,0 +1,153 @@ +/* $OpenBSD: iichid.h,v 1.4 2016/01/31 18:24:35 jcs Exp $ */ +/* + * HID-over-i2c driver + * + * Copyright (c) 2015, 2016 joshua stein + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IIC_HID_H_ +#define _IIC_HID_H_ + +#include +#include +#include +#include +#include +#include +#include + +/* 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; + +#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) +#define MOUSE_FLAGS (HIO_RELATIVE) + +#define MS_BUF_SIZE 8 /* bytes */ +#define MS_BUFQ_MAXLEN 100 /* units */ +#define MS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ +#define MS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) +#define MS_INFO_MAX 2 /* maximum number of HID sets */ + +struct ms_info { + struct hid_location sc_loc_w; + struct hid_location sc_loc_x; + struct hid_location sc_loc_y; + struct hid_location sc_loc_z; + struct hid_location sc_loc_t; + struct hid_location sc_loc_btn[MS_BUTTON_MAX]; + + uint32_t sc_flags; +#define MS_FLAG_X_AXIS 0x0001 +#define MS_FLAG_Y_AXIS 0x0002 +#define MS_FLAG_Z_AXIS 0x0004 +#define MS_FLAG_T_AXIS 0x0008 +#define MS_FLAG_SBU 0x0010 /* spurious button up events */ +#define MS_FLAG_REVZ 0x0020 /* Z-axis is reversed */ +#define MS_FLAG_W_AXIS 0x0040 + + uint8_t sc_iid_w; + uint8_t sc_iid_x; + uint8_t sc_iid_y; + uint8_t sc_iid_z; + uint8_t sc_iid_t; + uint8_t sc_iid_btn[MS_BUTTON_MAX]; + uint8_t sc_buttons; +}; + +struct iichid_hw { + uint8_t device_addr; + uint16_t config_reg; +}; + +struct ms_tx_entry { + STAILQ_ENTRY(ms_tx_entry) next; + uint8_t buf[MS_BUF_SIZE]; +}; + +STAILQ_HEAD(ms_tx_buf, ms_tx_entry); + +struct iichid_softc { + device_t dev; + struct cdev *cdev; + bool isopen; + struct ms_tx_entry rbuf; + uint8_t bytesread; + + struct ms_tx_buf ms_unused_blocks; + struct ms_tx_buf ms_queue; + + struct iichid_hw hw; + + task_fn_t *event_handler; + + struct i2c_hid_desc desc; + struct ms_info info[MS_INFO_MAX]; + uint8_t sc_iid; + mousehw_t sc_hw; + mousestatus_t sc_status; + mousemode_t sc_mode; + struct mtx lock; + + struct cv cv; + bool detaching; + + int invert; + int power_state; + + uint8_t *input_buf; + int input_size; +}; + +struct acpi_iichid_softc { + device_t dev; + struct cdev *sc_devnode; + + struct iichid_hw hw; + + uint16_t irq; + int irq_rid; + struct resource* irq_res; + void* irq_cookie; + struct iichid_softc* iichid_sc; + + int sampling_rate; + struct callout periodic_callout; + bool callout_setup; + + struct taskqueue* taskqueue; + struct task event_task; + + struct mtx lock; +}; + +int acpi_iichid_get_report(device_t dev, struct i2c_hid_desc* hid_desc, enum hid_kind type, int id, void *data, int len); + + +#endif /* _IIC_HID_H_ */ Index: sys/dev/iicbus/input/iichid.c =================================================================== --- /dev/null +++ sys/dev/iicbus/input/iichid.c @@ -0,0 +1,957 @@ +/*- + * Copyright (c) 2019 Marc Priggemeyer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iicbus_if.h" + +static device_probe_t iichid_probe; +static device_attach_t iichid_attach; +static device_detach_t iichid_detach; +static device_suspend_t iichid_suspend; +static device_resume_t iichid_resume; + +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_END +}; + +static d_open_t iichid_open; +static d_close_t iichid_close; +static d_read_t iichid_read; +static d_write_t iichid_write; +static d_ioctl_t iichid_ioctl; + +static struct cdevsw iichid_cdevsw = { + .d_version = D_VERSION, + .d_open = iichid_open, + .d_close = iichid_close, + .d_read = iichid_read, + .d_write = iichid_write, + .d_ioctl = iichid_ioctl, + .d_name = "iichid" +}; + +static driver_t iichid_driver = { + .name = "iichid", + .methods = iichid_methods, + .size = sizeof(struct iichid_softc), +}; + +static int +iichid_fetch_buffer(device_t dev, uint8_t* cmd, int cmdlen, uint8_t *buf, int buflen) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr << 1, IIC_M_WR | IIC_M_NOSTOP, cmdlen, cmd }, + { addr << 1, IIC_M_RD, buflen, buf }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + +static int +iichid_write_register(device_t dev, uint8_t* cmd, int cmdlen) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr << 1, IIC_M_WR, cmdlen, cmd }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + +static int +iichid_fetch_report(device_t dev, struct i2c_hid_desc* hid_desc, uint8_t *data, int len, int *actual_len) +{ + struct iichid_softc* sc; + sc = device_get_softc(dev); + + mtx_assert(&sc->lock, MA_OWNED); + + *actual_len = 0; + + uint16_t dtareg = htole16(hid_desc->wInputRegister); + + uint8_t cmd[] = {dtareg & 0xff, dtareg >> 8}; + int cmdlen = 2; + uint8_t buf[len]; + + mtx_unlock(&sc->lock); + + int error = iichid_fetch_buffer(dev, cmd, cmdlen, buf, len); + + mtx_lock(&sc->lock); + + memcpy(data, buf, len); + + if (error != 0) + { + device_printf(dev, "could not retrieve input report (%d)\n", error); + return error; + } + + *actual_len = data[0] | data[1] << 8; + + return 0; +} + +static int +iichid_set_power(device_t dev, bool sleep) +{ + struct iichid_softc* sc; + sc = device_get_softc(dev); + + mtx_assert(&sc->lock, MA_OWNED); + + uint16_t cmdreg = htole16(sc->desc.wCommandRegister); + + uint8_t power_state = 0; //default on + if( sleep ) + power_state = 1; //sleep + + uint8_t cmd[] = {cmdreg & 0xff, cmdreg >> 8, power_state, 0x08}; //0x08, 4 reserved bits plus opcode (4 bit) + int cmdlen = 4; + + mtx_unlock(&sc->lock); + + int error = iichid_write_register(dev, cmd, cmdlen); + + mtx_lock(&sc->lock); + + return error; +} + +static int fetch_hid_descriptor(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + + uint16_t cr = sc->hw.config_reg; + return (iichid_fetch_buffer(dev, (uint8_t*)&cr, sizeof(cr), (uint8_t*)&sc->desc, sizeof(struct i2c_hid_desc))); +} + +static int fetch_report_descriptor(device_t dev, uint8_t **buf, int *len) +{ + struct iichid_softc *sc = device_get_softc(dev); + + if (sc->desc.wHIDDescLength != 30) + return -1; + + *buf = malloc(sc->desc.wReportDescLength, M_TEMP, M_NOWAIT | M_ZERO); + *len = sc->desc.wReportDescLength; + + uint16_t rdr = sc->desc.wReportDescRegister; + + int error = (iichid_fetch_buffer(dev, (uint8_t*)&rdr, sizeof(rdr), *buf, sc->desc.wReportDescLength)); + + return error; +} + +static void +ms_hid_parse(device_t dev, const uint8_t *buf, uint16_t len, struct ms_info *info, uint8_t index) +{ + uint32_t flags; + uint8_t i; + uint8_t j; + + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), + hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_X_AXIS; + } + } + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), + hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Y_AXIS; + } + } + /* Try the wheel first as the Z activator since it's tradition. */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z) || + hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z)) { + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Z_AXIS; + } + /* + * We might have both a wheel and Z direction, if so put + * put the Z on the W coordinate. + */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, index, &info->sc_loc_w, &flags, + &info->sc_iid_w)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_W_AXIS; + } + } + } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_Z), hid_input, index, &info->sc_loc_z, &flags, + &info->sc_iid_z)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_Z_AXIS; + } + } + /* + * The Microsoft Wireless Intellimouse 2.0 reports it's wheel + * using 0x0048, which is HUG_TWHEEL, and seems to expect you + * to know that the byte after the wheel is the tilt axis. + * There are no other HID axis descriptors other than X,Y and + * TWHEEL + */ + if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, + HUG_TWHEEL), hid_input, index, &info->sc_loc_t, + &flags, &info->sc_iid_t)) { + + info->sc_loc_t.pos += 8; + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { + info->sc_flags |= MS_FLAG_T_AXIS; + } + } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER, + HUC_AC_PAN), hid_input, index, &info->sc_loc_t, + &flags, &info->sc_iid_t)) { + + if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) + info->sc_flags |= MS_FLAG_T_AXIS; + } + /* figure out the number of buttons */ + + for (i = 0; i < MS_BUTTON_MAX; i++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), + hid_input, index, &info->sc_loc_btn[i], NULL, + &info->sc_iid_btn[i])) { + break; + } + } + + /* detect other buttons */ + + for (j = 0; (i < MS_BUTTON_MAX) && (j < 2); i++, j++) { + if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)), + hid_input, index, &info->sc_loc_btn[i], NULL, + &info->sc_iid_btn[i])) { + break; + } + } + + info->sc_buttons = i; + + if (info->sc_flags == 0) + return; + + /* announce information about the mouse */ + device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", + (info->sc_buttons), + (info->sc_flags & MS_FLAG_X_AXIS) ? "X" : "", + (info->sc_flags & MS_FLAG_Y_AXIS) ? "Y" : "", + (info->sc_flags & MS_FLAG_Z_AXIS) ? "Z" : "", + (info->sc_flags & MS_FLAG_T_AXIS) ? "T" : "", + (info->sc_flags & MS_FLAG_W_AXIS) ? "W" : "", + info->sc_iid_x); +} + +static int +iichid_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + + mtx_lock(&sc->lock); + if (sc->isopen) + { + mtx_unlock(&sc->lock); + return (EBUSY); + } + + sc->isopen = true; + sc->bytesread = sc->sc_mode.packetsize; + + + while (!STAILQ_EMPTY(&sc->ms_queue)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_queue); + STAILQ_REMOVE_HEAD(&sc->ms_queue, next); + STAILQ_INSERT_HEAD(&sc->ms_unused_blocks, u, next); + } + + mtx_unlock(&sc->lock); + + return 0; +} + +static int +iichid_close(struct cdev *dev, int fflags, int devtype, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + + cv_broadcastpri(&sc->cv, 0); + + mtx_lock(&sc->lock); + sc->isopen=false; + mtx_unlock(&sc->lock); + + return 0; +} + +static int +iichid_write(struct cdev *dev, struct uio *uio, int ioflags) +{ + return 0; +} + +static int +iichid_read(struct cdev *dev, struct uio* uio, int ioflags) +{ + struct iichid_softc *sc = dev->si_drv1; + + mtx_lock(&sc->lock); + + while (!sc->detaching && STAILQ_EMPTY(&sc->ms_queue) && sc->bytesread == sc->sc_mode.packetsize) + { + int error = cv_wait_sig(&sc->cv, &sc->lock); + if (error != 0) + { + mtx_unlock(&sc->lock); + return error; + } + } + + if (sc->detaching) + { + mtx_unlock(&sc->lock); + return ENXIO; + } + + if (sc->bytesread == sc->sc_mode.packetsize && !STAILQ_EMPTY(&sc->ms_queue)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_queue); + STAILQ_REMOVE_HEAD(&sc->ms_queue, next); + memcpy(&sc->rbuf, u, sizeof(struct ms_tx_entry)); + sc->bytesread = 0; + + STAILQ_INSERT_TAIL(&sc->ms_unused_blocks, u, next); + } + mtx_unlock(&sc->lock); + + int error = uiomove(sc->rbuf.buf+sc->bytesread, 1, uio); + sc->bytesread++; + if (error != 0) + device_printf(sc->dev, "I could not be read from"); + + return 0; +} + +static int +iichid_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +{ + struct iichid_softc *sc = dev->si_drv1; + int error = 0; + mousemode_t mode; + + mtx_lock(&sc->lock); + + switch (cmd) { + case MOUSE_SETMODE: + mode = *(mousemode_t*)data; + + if (mode.level == -1) { + /* don't change the current setting */ + } else if ((mode.level < 0) || (mode.level > 1)) { + error = EINVAL; + break; + } else { + sc->sc_mode.level = mode.level; + } + + if (sc->sc_mode.level == 0) { + if (sc->sc_hw.buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_hw.buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + break; + case MOUSE_SETLEVEL: + if (*(int *)data < 0 || *(int *)data > 1) { + error = EINVAL; + break; + } + sc->sc_mode.level = *(int *)data; + + if (sc->sc_mode.level == 0) { + if (sc->sc_hw.buttons > MOUSE_MSC_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_MSC; + sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; + } else if (sc->sc_mode.level == 1) { + if (sc->sc_hw.buttons > MOUSE_SYS_MAXBUTTON) + sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; + sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; + } + break; + case MOUSE_GETHWINFO: + *(mousehw_t *)data = sc->sc_hw; + break; + + case MOUSE_GETMODE: + *(mousemode_t *)data = sc->sc_mode; + break; + + case MOUSE_GETLEVEL: + *(int *)data = sc->sc_mode.level; + break; + + case MOUSE_GETSTATUS:{ + mousestatus_t *status = (mousestatus_t *)data; + + *status = sc->sc_status; + sc->sc_status.obutton = sc->sc_status.button; + sc->sc_status.button = 0; + sc->sc_status.dx = 0; + sc->sc_status.dy = 0; + sc->sc_status.dz = 0; + /* sc->sc_status.dt = 0; */ + + if (status->dx || status->dy || status->dz /* || status->dt */ ) { + status->flags |= MOUSE_POSCHANGED; + } + if (status->button != status->obutton) { + status->flags |= MOUSE_BUTTONSCHANGED; + } + break; + } + default: + error = ENOTTY; + break; + } + + mtx_unlock(&sc->lock); + return (error); +} + +static void +ms_put_queue(struct iichid_softc *sc, int32_t dx, int32_t dy, + int32_t dz, int32_t dt, int32_t buttons) +{ + if (dx > 254) + dx = 254; + if (dx < -256) + dx = -256; + if (dy > 254) + dy = 254; + if (dy < -256) + dy = -256; + if (dz > 126) + dz = 126; + if (dz < -128) + dz = -128; + if (dt > 126) + dt = 126; + if (dt < -128) + dt = -128; + + if (sc->invert != 0) + dz = -dz; + if (!STAILQ_EMPTY(&sc->ms_unused_blocks)) + { + struct ms_tx_entry *u = STAILQ_FIRST(&sc->ms_unused_blocks); + STAILQ_REMOVE_HEAD(&sc->ms_unused_blocks, next); + + u->buf[0] = MOUSE_MSC_SYNC; + u->buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; + u->buf[1] = dx >> 1; + u->buf[2] = dy >> 1; + u->buf[3] = dx - (dx >> 1); + u->buf[4] = dy - (dy >> 1); + + if (sc->sc_mode.level == 1) { + u->buf[5] = dz >> 1; + u->buf[6] = dz - (dz >> 1); + u->buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); + } + + STAILQ_INSERT_TAIL(&sc->ms_queue, u, next); + } else { + device_printf(sc->dev, "no blocks available\n"); + } +} + +static void +iichid_event(void* context, int pending) +{ + struct iichid_softc *sc = context; + + mtx_lock(&sc->lock); + + int actual = 0; + int error = iichid_fetch_report(sc->dev, &sc->desc, sc->input_buf, sc->input_size, &actual); + + if (error != 0) + { + device_printf(sc->dev, "an error occured\n"); + mtx_unlock(&sc->lock); + return; + } + + if (actual <= 0) + { + device_printf(sc->dev, "no data received\n"); + mtx_unlock(&sc->lock); + return; + } + + int32_t dw = 0; + int32_t dx = 0; + int32_t dy = 0; + int32_t dz = 0; + int32_t dt = 0; + int32_t buttons = 0; + int32_t buttons_found = 0; + uint8_t id = 0; + + uint8_t *buf = sc->input_buf; + buf++; buf++; + int len = actual; + + if (sc->sc_iid) + { + id = *buf; + buf++; + len--; + } + +// device_printf(sc->dev, "id: %d\n", id); + + for(int i=0; iinfo[i]; + if ((info->sc_flags & MS_FLAG_W_AXIS) && + (id == info->sc_iid_w)) + dw += hid_get_data(buf, len, &info->sc_loc_w); + + if ((info->sc_flags & MS_FLAG_X_AXIS) && + (id == info->sc_iid_x)) + dx += hid_get_data(buf, len, &info->sc_loc_x); + + if ((info->sc_flags & MS_FLAG_Y_AXIS) && + (id == info->sc_iid_y)) + dy -= hid_get_data(buf, len, &info->sc_loc_y); + + if ((info->sc_flags & MS_FLAG_Z_AXIS) && + (id == info->sc_iid_z)) { + int32_t temp; + temp = hid_get_data(buf, len, &info->sc_loc_z); + dz -= temp; + } + + if ((info->sc_flags & MS_FLAG_T_AXIS) && + (id == info->sc_iid_t)) { + dt -= hid_get_data(buf, len, &info->sc_loc_t); + /* T-axis is translated into button presses */ + buttons_found |= (1UL << 5) | (1UL << 6); + } + + for (i = 0; i < info->sc_buttons; i++) { + uint32_t mask; + mask = 1UL << MS_BUT(i); + /* check for correct button ID */ + if (id != info->sc_iid_btn[i]) + continue; + /* check for button pressed */ + if (hid_get_data(buf, len, &info->sc_loc_btn[i])) + buttons |= mask; + /* register button mask */ + buttons_found |= mask; + } + + buttons |= sc->sc_status.button & ~buttons_found; + + if (dx || dy || dz || dt || dw || + (buttons != sc->sc_status.button)) { + + /* translate T-axis into button presses until further */ + if (dt > 0) { + ms_put_queue(sc, 0, 0, 0, 0, buttons); + buttons |= 1UL << 5; + } else if (dt < 0) { + ms_put_queue(sc, 0, 0, 0, 0, buttons); + buttons |= 1UL << 6; + } + + sc->sc_status.button = buttons; + sc->sc_status.dx += dx; + sc->sc_status.dy += dy; + sc->sc_status.dz += dz; + + //device_printf(sc->dev, "dx: %d, dy: %d, dz: %d, dt: %d, dw: %d, btn: 0x%2x\n", dx, dy, dz, dt, dw, buttons); + + ms_put_queue(sc, dx, dy, dz, dt, buttons); + } + } + +// device_printf(sc->dev, "read: %d/%d\n", actual, sc->desc.wMaxInputLength); + mtx_unlock(&sc->lock); + + cv_signal(&sc->cv); +} + +static int +iichid_probe(device_t dev) +{ + device_t pdev = device_get_parent(dev); + + if (!pdev) + return (ENXIO); + + driver_t *pdrv = device_get_driver(pdev); + + if (!pdrv) + return (ENXIO); + + if (strcmp(pdrv->name, "iicbus") != 0) + return (ENXIO); + + device_set_desc(dev, "HID over I2C"); + + return (BUS_PROBE_VENDOR); +} + +static int +sysctl_invert_handler(SYSCTL_HANDLER_ARGS) +{ + int err, value; + struct iichid_softc *sc; + + sc = arg1; + + mtx_lock(&sc->lock); + + value = sc->invert; + err = sysctl_handle_int(oidp, &value, 0, req); + + if (err != 0 || req->newptr == NULL || value == sc->invert) + { + mtx_unlock(&sc->lock); + return (err); + } + + sc->invert = value; + + mtx_unlock(&sc->lock); + + return (0); +} + +static int +sysctl_power_state_handler(SYSCTL_HANDLER_ARGS) +{ + int err, value; + struct iichid_softc *sc; + + sc = arg1; + + mtx_lock(&sc->lock); + + value = sc->power_state; + err = sysctl_handle_int(oidp, &value, 0, req); + + if (err != 0 || req->newptr == NULL || value == sc->power_state) + { + mtx_unlock(&sc->lock); + return (err); + } + + sc->power_state = value; + bool sleep = false; + if( sc->power_state != 0 ) + sleep = true; + + err = iichid_set_power(sc->dev, sleep); + + if (err != 0) + { + device_printf(sc->dev, "Could not set power_state, error code: %d\n", err); + } else { + device_printf(sc->dev, "Successfully set power_state to %d\n", sc->power_state); + } + + mtx_unlock(&sc->lock); + + return (0); +} + +static int +iichid_attach(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + + uintptr_t addr = 0, cr = 0; + int error; + + sc->dev = dev; + sc->detaching = false; + sc->input_buf = NULL; + sc->isopen = 0; + sc->invert = 0; + sc->power_state = 0; + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "invert_scroll", CTLTYPE_INT | CTLFLAG_RWTUN, + sc, 0, + sysctl_invert_handler, "I", "invert mouse axis"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "power_state", CTLTYPE_INT | CTLFLAG_RWTUN, + sc, 0, + sysctl_power_state_handler, "I", "set to 1 for sleep"); + + // the config register is passed as resources, while the device address will always have a place in the iicbus child's (ivars) heart + bus_get_resource(dev, SYS_RES_IOPORT, 0, (rman_res_t*)&cr, NULL); + BUS_READ_IVAR(device_get_parent(dev), dev, IICBUS_IVAR_ADDR, &addr); + + // store the value in device's softc to have easy access + // values only have 1 byte length, still make the casts explicit + sc->hw.device_addr = (uint8_t)addr; + sc->hw.config_reg = (uint16_t)cr; + + sc->event_handler = iichid_event; + + sc->cdev = make_dev(&iichid_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ims%d", device_get_unit(dev)); + sc->cdev->si_drv1 = sc; + + device_printf(dev, "ADDR 0x%x REG 0x%x\n", sc->hw.device_addr, sc->hw.config_reg); + + if ( (error = fetch_hid_descriptor(dev)) != 0 ) + { + iichid_detach(dev); + device_printf(dev, "could not retrieve HID descriptor from device: %d\n", error); + } + + uint8_t *rdesc; + int len; + + if ( (error = fetch_report_descriptor(dev, &rdesc, &len)) != 0 ) + { + iichid_detach(dev); + device_printf(dev, "could not retrieve report descriptor from device: %d\n", error); + } + + sc->input_size = sc->desc.wMaxInputLength; + sc->input_buf = malloc(sc->desc.wMaxInputLength, M_DEVBUF, M_NOWAIT | M_ZERO); + + for (int i = 0; i < MS_INFO_MAX; i++) { + ms_hid_parse(dev, rdesc, len, &sc->info[0], 0); + } + + int isize = hid_report_size(rdesc, len, hid_input, &sc->sc_iid); + + + if (isize+2 != sc->desc.wMaxInputLength) + device_printf(dev, "determined (len=%d) and described (len=%d) input report lengths mismatch\n", isize+2, sc->desc.wMaxInputLength); + + mtx_init(&sc->lock, "iichid spin-lock", NULL, MTX_DEF); + cv_init(&sc->cv, "iichid cv"); + + sc->sc_hw.buttons = -1; + sc->sc_hw.iftype = MOUSE_IF_SYSMOUSE; + sc->sc_hw.type = MOUSE_MOUSE; + sc->sc_hw.model = MOUSE_MODEL_GENERIC; + sc->sc_hw.hwid = sc->desc.wVendorID; + + sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; + sc->sc_mode.rate = -1; + sc->sc_mode.resolution = -1; + sc->sc_mode.accelfactor = 1; + sc->sc_mode.level = 0; + sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; + + STAILQ_INIT(&sc->ms_queue); + STAILQ_INIT(&sc->ms_unused_blocks); + for(int i=0; ims_unused_blocks, u, next); + } + +// device_printf(dev, "len: %d\nbcdVer: %d\nreport len: %d\ninput len: %d\nvid: 0x%x\npid: 0x%x\n", hid_desc.wHIDDescLength, hid_desc.bcdVersion, hid_desc.wReportDescLength, hid_desc.wMaxInputLength, hid_desc.wVendorID, hid_desc.wProductID); + + return (0); /* success */ +} + +static int +iichid_detach(device_t dev) +{ + struct iichid_softc *sc = device_get_softc(dev); + if (sc) + { + if (sc->isopen) + return (EBUSY); + + mtx_lock(&sc->lock); + sc->detaching = true; + mtx_unlock(&sc->lock); + cv_broadcastpri(&sc->cv,0); + if (mtx_initialized(&sc->lock)) + { + mtx_destroy(&sc->lock); + } + + struct ms_tx_buf* queues[2] = {&sc->ms_queue, &sc->ms_unused_blocks}; + for(int i=0; i<2; i++) + { + while (!STAILQ_EMPTY(queues[i])) + { + struct ms_tx_entry *u = STAILQ_FIRST(queues[i]); + STAILQ_REMOVE_HEAD(queues[i], next); + free(u, M_DEVBUF); + } + } + + if (sc->cdev) + destroy_dev(sc->cdev); + + if (sc->input_buf) + free(sc->input_buf, M_DEVBUF); + } + return (0); +} + +static int +iichid_suspend(device_t dev) +{ + int err; + struct iichid_softc *sc = device_get_softc(dev); + mtx_lock(&sc->lock); + + device_printf(dev, "Suspend called, setting device to power_state 1\n"); + + err = iichid_set_power(sc->dev, true); + + if (err != 0) + { + device_printf(dev, "Could not set power_state, error code: %d\n", err); + } else { + device_printf(dev, "Successfully set power_state\n"); + } + + mtx_unlock(&sc->lock); + + return (err); +} + +static +int +iichid_resume(device_t dev) +{ + int err; + struct iichid_softc *sc = device_get_softc(dev); + mtx_lock(&sc->lock); + + device_printf(dev, "Suspend called, setting device to power_state 0\n"); + + err = iichid_set_power(sc->dev, false); + + if (err != 0) + { + device_printf(dev, "Could not set power_state, error code: %d\n", err); + } else { + device_printf(dev, "Successfully set power_state\n"); + } + + mtx_unlock(&sc->lock); + + return (err); +} + + +DRIVER_MODULE(iichid, iicbus, iichid_driver, iichid_devclass, NULL, 0); +MODULE_DEPEND(iichid, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); +MODULE_VERSION(iichid, 1); Index: sys/modules/acpi/Makefile =================================================================== --- sys/modules/acpi/Makefile +++ sys/modules/acpi/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ SUBDIR= acpi_asus acpi_asus_wmi acpi_dock acpi_fujitsu acpi_hp \ - acpi_ibm acpi_panasonic acpi_sony acpi_toshiba \ - acpi_video acpi_wmi aibs + acpi_ibm acpi_iichid acpi_panasonic acpi_sony \ + acpi_toshiba acpi_video acpi_wmi aibs .include Index: sys/modules/acpi/acpi_iichid/Makefile =================================================================== --- /dev/null +++ sys/modules/acpi/acpi_iichid/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/input +KMOD = acpi_iichid +SRCS = acpi_iichid.c device_if.h bus_if.h iicbus_if.h vnode_if.h opt_acpi.h acpi_if.h opt_usb.h + +.include Index: sys/modules/i2c/Makefile =================================================================== --- sys/modules/i2c/Makefile +++ sys/modules/i2c/Makefile @@ -12,6 +12,7 @@ iic \ iicbb \ iicbus \ + iichid \ iicsmb \ isl \ isl12xx \ Index: sys/modules/i2c/iichid/Makefile =================================================================== --- /dev/null +++ sys/modules/i2c/iichid/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/iicbus/input +KMOD = iichid +SRCS = iichid.c device_if.h bus_if.h iicbus_if.h vnode_if.h opt_usb.h + +.include