/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2026 The FreeBSD Foundation * Copyright (c) 2026 Framework Computer Inc * * This software was developed by Aymeric Wibo * under sponsorship from the FreeBSD Foundation. * * Portions of this software were developed by Daniel Schaefer * under sponsorship from Framework Computer Inc. */ #include "opt_hid.h" #include #include #include #include #include #include #include #include #include /* * Reference document: * https://www.usb.org/sites/default/files/hutrr40radiohidusagesfinal_0.pdf */ static hidmap_cb_t hrfkill_cb; static const struct hidmap_item hrfkill_map[] = { { HIDMAP_REL_CB(HUP_GENERIC_DESKTOP, HUG_RADIO_BUTTON, &hrfkill_cb) }, }; static const struct hid_device_id hrfkill_devs[] = { { HID_TLC(HUP_GENERIC_DESKTOP, HUG_RADIO_CONTROL) }, }; /* * Synthesize key-down + key-up for the wireless radio button, * which only reports a relative pulse (value=1) on press. * The firmware latches value=1 and never resets it, so we * track the last value via HIDMAP_CB_UDATA64 and only fire * on the 0 -> non-zero transition. */ static int hrfkill_cb(HIDMAP_CB_ARGS) { struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); int32_t last; switch (HIDMAP_CB_GET_STATE()) { case HIDMAP_CB_IS_ATTACHING: evdev_support_event(evdev, EV_KEY); evdev_support_key(evdev, KEY_RFKILL); HIDMAP_CB_UDATA64 = 0; break; case HIDMAP_CB_IS_RUNNING: last = (int32_t)HIDMAP_CB_UDATA64; HIDMAP_CB_UDATA64 = (uint64_t)ctx.data; if (ctx.data == 0 || ctx.data == last) return (ENOMSG); evdev_push_key(evdev, KEY_RFKILL, 1); evdev_push_key(evdev, KEY_RFKILL, 0); break; default: break; } return (0); } static int hrfkill_probe(device_t dev) { return (HIDMAP_PROBE(device_get_softc(dev), dev, hrfkill_devs, hrfkill_map, "RFKILL Switch")); } static int hrfkill_attach(device_t dev) { return (hidmap_attach(device_get_softc(dev))); } static int hrfkill_detach(device_t dev) { return (hidmap_detach(device_get_softc(dev))); } static device_method_t hrfkill_methods[] = { DEVMETHOD(device_probe, hrfkill_probe), DEVMETHOD(device_attach, hrfkill_attach), DEVMETHOD(device_detach, hrfkill_detach), DEVMETHOD_END }; DEFINE_CLASS_0(hrfkill, hrfkill_driver, hrfkill_methods, sizeof(struct hidmap)); DRIVER_MODULE(hrfkill, hidbus, hrfkill_driver, NULL, NULL); MODULE_DEPEND(hrfkill, hid, 1, 1, 1); MODULE_DEPEND(hrfkill, hidbus, 1, 1, 1); MODULE_DEPEND(hrfkill, hidmap, 1, 1, 1); MODULE_DEPEND(hrfkill, evdev, 1, 1, 1); MODULE_VERSION(hrfkill, 1); HID_PNP_INFO(hrfkill_devs);