xref: /freebsd/sys/dev/hid/hrfkill.c (revision e2d56447b3a9d2ef9bd0b80c3aaa69e681f4be0f)
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