1*e2d56447SAymeric Wibo /*
2*e2d56447SAymeric Wibo * SPDX-License-Identifier: BSD-2-Clause
3*e2d56447SAymeric Wibo *
4*e2d56447SAymeric Wibo * Copyright (c) 2026 The FreeBSD Foundation
5*e2d56447SAymeric Wibo * Copyright (c) 2026 Framework Computer Inc
6*e2d56447SAymeric Wibo *
7*e2d56447SAymeric Wibo * This software was developed by Aymeric Wibo <obiwac@freebsd.org>
8*e2d56447SAymeric Wibo * under sponsorship from the FreeBSD Foundation.
9*e2d56447SAymeric Wibo *
10*e2d56447SAymeric Wibo * Portions of this software were developed by Daniel Schaefer
11*e2d56447SAymeric Wibo * <git@danielschaefer.me> under sponsorship from Framework Computer Inc.
12*e2d56447SAymeric Wibo */
13*e2d56447SAymeric Wibo
14*e2d56447SAymeric Wibo #include "opt_hid.h"
15*e2d56447SAymeric Wibo
16*e2d56447SAymeric Wibo #include <sys/param.h>
17*e2d56447SAymeric Wibo #include <sys/bus.h>
18*e2d56447SAymeric Wibo #include <sys/kernel.h>
19*e2d56447SAymeric Wibo #include <sys/module.h>
20*e2d56447SAymeric Wibo
21*e2d56447SAymeric Wibo #include <dev/evdev/evdev.h>
22*e2d56447SAymeric Wibo #include <dev/evdev/input.h>
23*e2d56447SAymeric Wibo
24*e2d56447SAymeric Wibo #include <dev/hid/hid.h>
25*e2d56447SAymeric Wibo #include <dev/hid/hidbus.h>
26*e2d56447SAymeric Wibo #include <dev/hid/hidmap.h>
27*e2d56447SAymeric Wibo
28*e2d56447SAymeric Wibo /*
29*e2d56447SAymeric Wibo * Reference document:
30*e2d56447SAymeric Wibo * https://www.usb.org/sites/default/files/hutrr40radiohidusagesfinal_0.pdf
31*e2d56447SAymeric Wibo */
32*e2d56447SAymeric Wibo
33*e2d56447SAymeric Wibo static hidmap_cb_t hrfkill_cb;
34*e2d56447SAymeric Wibo
35*e2d56447SAymeric Wibo static const struct hidmap_item hrfkill_map[] = {
36*e2d56447SAymeric Wibo { HIDMAP_REL_CB(HUP_GENERIC_DESKTOP, HUG_RADIO_BUTTON, &hrfkill_cb) },
37*e2d56447SAymeric Wibo };
38*e2d56447SAymeric Wibo
39*e2d56447SAymeric Wibo static const struct hid_device_id hrfkill_devs[] = {
40*e2d56447SAymeric Wibo { HID_TLC(HUP_GENERIC_DESKTOP, HUG_RADIO_CONTROL) },
41*e2d56447SAymeric Wibo };
42*e2d56447SAymeric Wibo
43*e2d56447SAymeric Wibo /*
44*e2d56447SAymeric Wibo * Synthesize key-down + key-up for the wireless radio button,
45*e2d56447SAymeric Wibo * which only reports a relative pulse (value=1) on press.
46*e2d56447SAymeric Wibo * The firmware latches value=1 and never resets it, so we
47*e2d56447SAymeric Wibo * track the last value via HIDMAP_CB_UDATA64 and only fire
48*e2d56447SAymeric Wibo * on the 0 -> non-zero transition.
49*e2d56447SAymeric Wibo */
50*e2d56447SAymeric Wibo static int
hrfkill_cb(HIDMAP_CB_ARGS)51*e2d56447SAymeric Wibo hrfkill_cb(HIDMAP_CB_ARGS)
52*e2d56447SAymeric Wibo {
53*e2d56447SAymeric Wibo struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
54*e2d56447SAymeric Wibo int32_t last;
55*e2d56447SAymeric Wibo
56*e2d56447SAymeric Wibo switch (HIDMAP_CB_GET_STATE()) {
57*e2d56447SAymeric Wibo case HIDMAP_CB_IS_ATTACHING:
58*e2d56447SAymeric Wibo evdev_support_event(evdev, EV_KEY);
59*e2d56447SAymeric Wibo evdev_support_key(evdev, KEY_RFKILL);
60*e2d56447SAymeric Wibo HIDMAP_CB_UDATA64 = 0;
61*e2d56447SAymeric Wibo break;
62*e2d56447SAymeric Wibo case HIDMAP_CB_IS_RUNNING:
63*e2d56447SAymeric Wibo last = (int32_t)HIDMAP_CB_UDATA64;
64*e2d56447SAymeric Wibo HIDMAP_CB_UDATA64 = (uint64_t)ctx.data;
65*e2d56447SAymeric Wibo if (ctx.data == 0 || ctx.data == last)
66*e2d56447SAymeric Wibo return (ENOMSG);
67*e2d56447SAymeric Wibo evdev_push_key(evdev, KEY_RFKILL, 1);
68*e2d56447SAymeric Wibo evdev_push_key(evdev, KEY_RFKILL, 0);
69*e2d56447SAymeric Wibo break;
70*e2d56447SAymeric Wibo default:
71*e2d56447SAymeric Wibo break;
72*e2d56447SAymeric Wibo }
73*e2d56447SAymeric Wibo
74*e2d56447SAymeric Wibo return (0);
75*e2d56447SAymeric Wibo }
76*e2d56447SAymeric Wibo
77*e2d56447SAymeric Wibo static int
hrfkill_probe(device_t dev)78*e2d56447SAymeric Wibo hrfkill_probe(device_t dev)
79*e2d56447SAymeric Wibo {
80*e2d56447SAymeric Wibo return (HIDMAP_PROBE(device_get_softc(dev), dev,
81*e2d56447SAymeric Wibo hrfkill_devs, hrfkill_map, "RFKILL Switch"));
82*e2d56447SAymeric Wibo }
83*e2d56447SAymeric Wibo
84*e2d56447SAymeric Wibo static int
hrfkill_attach(device_t dev)85*e2d56447SAymeric Wibo hrfkill_attach(device_t dev)
86*e2d56447SAymeric Wibo {
87*e2d56447SAymeric Wibo return (hidmap_attach(device_get_softc(dev)));
88*e2d56447SAymeric Wibo }
89*e2d56447SAymeric Wibo
90*e2d56447SAymeric Wibo static int
hrfkill_detach(device_t dev)91*e2d56447SAymeric Wibo hrfkill_detach(device_t dev)
92*e2d56447SAymeric Wibo {
93*e2d56447SAymeric Wibo return (hidmap_detach(device_get_softc(dev)));
94*e2d56447SAymeric Wibo }
95*e2d56447SAymeric Wibo
96*e2d56447SAymeric Wibo static device_method_t hrfkill_methods[] = {
97*e2d56447SAymeric Wibo DEVMETHOD(device_probe, hrfkill_probe),
98*e2d56447SAymeric Wibo DEVMETHOD(device_attach, hrfkill_attach),
99*e2d56447SAymeric Wibo DEVMETHOD(device_detach, hrfkill_detach),
100*e2d56447SAymeric Wibo
101*e2d56447SAymeric Wibo DEVMETHOD_END
102*e2d56447SAymeric Wibo };
103*e2d56447SAymeric Wibo
104*e2d56447SAymeric Wibo DEFINE_CLASS_0(hrfkill, hrfkill_driver, hrfkill_methods, sizeof(struct hidmap));
105*e2d56447SAymeric Wibo DRIVER_MODULE(hrfkill, hidbus, hrfkill_driver, NULL, NULL);
106*e2d56447SAymeric Wibo MODULE_DEPEND(hrfkill, hid, 1, 1, 1);
107*e2d56447SAymeric Wibo MODULE_DEPEND(hrfkill, hidbus, 1, 1, 1);
108*e2d56447SAymeric Wibo MODULE_DEPEND(hrfkill, hidmap, 1, 1, 1);
109*e2d56447SAymeric Wibo MODULE_DEPEND(hrfkill, evdev, 1, 1, 1);
110*e2d56447SAymeric Wibo MODULE_VERSION(hrfkill, 1);
111*e2d56447SAymeric Wibo HID_PNP_INFO(hrfkill_devs);
112