1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun Dell Airplane Mode Switch driver
4*4882a593Smuzhiyun Copyright (C) 2014-2015 Pali Rohár <pali@kernel.org>
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/acpi.h>
10*4882a593Smuzhiyun #include <linux/rfkill.h>
11*4882a593Smuzhiyun #include <linux/input.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include "dell-rbtn.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun enum rbtn_type {
16*4882a593Smuzhiyun RBTN_UNKNOWN,
17*4882a593Smuzhiyun RBTN_TOGGLE,
18*4882a593Smuzhiyun RBTN_SLIDER,
19*4882a593Smuzhiyun };
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun struct rbtn_data {
22*4882a593Smuzhiyun enum rbtn_type type;
23*4882a593Smuzhiyun struct rfkill *rfkill;
24*4882a593Smuzhiyun struct input_dev *input_dev;
25*4882a593Smuzhiyun bool suspended;
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /*
30*4882a593Smuzhiyun * acpi functions
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun
rbtn_check(struct acpi_device * device)33*4882a593Smuzhiyun static enum rbtn_type rbtn_check(struct acpi_device *device)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun unsigned long long output;
36*4882a593Smuzhiyun acpi_status status;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output);
39*4882a593Smuzhiyun if (ACPI_FAILURE(status))
40*4882a593Smuzhiyun return RBTN_UNKNOWN;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun switch (output) {
43*4882a593Smuzhiyun case 0:
44*4882a593Smuzhiyun case 1:
45*4882a593Smuzhiyun return RBTN_TOGGLE;
46*4882a593Smuzhiyun case 2:
47*4882a593Smuzhiyun case 3:
48*4882a593Smuzhiyun return RBTN_SLIDER;
49*4882a593Smuzhiyun default:
50*4882a593Smuzhiyun return RBTN_UNKNOWN;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
rbtn_get(struct acpi_device * device)54*4882a593Smuzhiyun static int rbtn_get(struct acpi_device *device)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun unsigned long long output;
57*4882a593Smuzhiyun acpi_status status;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output);
60*4882a593Smuzhiyun if (ACPI_FAILURE(status))
61*4882a593Smuzhiyun return -EINVAL;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun return !output;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
rbtn_acquire(struct acpi_device * device,bool enable)66*4882a593Smuzhiyun static int rbtn_acquire(struct acpi_device *device, bool enable)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun struct acpi_object_list input;
69*4882a593Smuzhiyun union acpi_object param;
70*4882a593Smuzhiyun acpi_status status;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun param.type = ACPI_TYPE_INTEGER;
73*4882a593Smuzhiyun param.integer.value = enable;
74*4882a593Smuzhiyun input.count = 1;
75*4882a593Smuzhiyun input.pointer = ¶m;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL);
78*4882a593Smuzhiyun if (ACPI_FAILURE(status))
79*4882a593Smuzhiyun return -EINVAL;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun return 0;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /*
86*4882a593Smuzhiyun * rfkill device
87*4882a593Smuzhiyun */
88*4882a593Smuzhiyun
rbtn_rfkill_query(struct rfkill * rfkill,void * data)89*4882a593Smuzhiyun static void rbtn_rfkill_query(struct rfkill *rfkill, void *data)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun struct acpi_device *device = data;
92*4882a593Smuzhiyun int state;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun state = rbtn_get(device);
95*4882a593Smuzhiyun if (state < 0)
96*4882a593Smuzhiyun return;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun rfkill_set_states(rfkill, state, state);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
rbtn_rfkill_set_block(void * data,bool blocked)101*4882a593Smuzhiyun static int rbtn_rfkill_set_block(void *data, bool blocked)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun /* NOTE: setting soft rfkill state is not supported */
104*4882a593Smuzhiyun return -EINVAL;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun static const struct rfkill_ops rbtn_ops = {
108*4882a593Smuzhiyun .query = rbtn_rfkill_query,
109*4882a593Smuzhiyun .set_block = rbtn_rfkill_set_block,
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun
rbtn_rfkill_init(struct acpi_device * device)112*4882a593Smuzhiyun static int rbtn_rfkill_init(struct acpi_device *device)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
115*4882a593Smuzhiyun int ret;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (rbtn_data->rfkill)
118*4882a593Smuzhiyun return 0;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun * NOTE: rbtn controls all radio devices, not only WLAN
122*4882a593Smuzhiyun * but rfkill interface does not support "ANY" type
123*4882a593Smuzhiyun * so "WLAN" type is used
124*4882a593Smuzhiyun */
125*4882a593Smuzhiyun rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev,
126*4882a593Smuzhiyun RFKILL_TYPE_WLAN, &rbtn_ops, device);
127*4882a593Smuzhiyun if (!rbtn_data->rfkill)
128*4882a593Smuzhiyun return -ENOMEM;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun ret = rfkill_register(rbtn_data->rfkill);
131*4882a593Smuzhiyun if (ret) {
132*4882a593Smuzhiyun rfkill_destroy(rbtn_data->rfkill);
133*4882a593Smuzhiyun rbtn_data->rfkill = NULL;
134*4882a593Smuzhiyun return ret;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun return 0;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
rbtn_rfkill_exit(struct acpi_device * device)140*4882a593Smuzhiyun static void rbtn_rfkill_exit(struct acpi_device *device)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun if (!rbtn_data->rfkill)
145*4882a593Smuzhiyun return;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun rfkill_unregister(rbtn_data->rfkill);
148*4882a593Smuzhiyun rfkill_destroy(rbtn_data->rfkill);
149*4882a593Smuzhiyun rbtn_data->rfkill = NULL;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
rbtn_rfkill_event(struct acpi_device * device)152*4882a593Smuzhiyun static void rbtn_rfkill_event(struct acpi_device *device)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun if (rbtn_data->rfkill)
157*4882a593Smuzhiyun rbtn_rfkill_query(rbtn_data->rfkill, device);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun /*
162*4882a593Smuzhiyun * input device
163*4882a593Smuzhiyun */
164*4882a593Smuzhiyun
rbtn_input_init(struct rbtn_data * rbtn_data)165*4882a593Smuzhiyun static int rbtn_input_init(struct rbtn_data *rbtn_data)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun int ret;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun rbtn_data->input_dev = input_allocate_device();
170*4882a593Smuzhiyun if (!rbtn_data->input_dev)
171*4882a593Smuzhiyun return -ENOMEM;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun rbtn_data->input_dev->name = "DELL Wireless hotkeys";
174*4882a593Smuzhiyun rbtn_data->input_dev->phys = "dellabce/input0";
175*4882a593Smuzhiyun rbtn_data->input_dev->id.bustype = BUS_HOST;
176*4882a593Smuzhiyun rbtn_data->input_dev->evbit[0] = BIT(EV_KEY);
177*4882a593Smuzhiyun set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun ret = input_register_device(rbtn_data->input_dev);
180*4882a593Smuzhiyun if (ret) {
181*4882a593Smuzhiyun input_free_device(rbtn_data->input_dev);
182*4882a593Smuzhiyun rbtn_data->input_dev = NULL;
183*4882a593Smuzhiyun return ret;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun return 0;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
rbtn_input_exit(struct rbtn_data * rbtn_data)189*4882a593Smuzhiyun static void rbtn_input_exit(struct rbtn_data *rbtn_data)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun input_unregister_device(rbtn_data->input_dev);
192*4882a593Smuzhiyun rbtn_data->input_dev = NULL;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
rbtn_input_event(struct rbtn_data * rbtn_data)195*4882a593Smuzhiyun static void rbtn_input_event(struct rbtn_data *rbtn_data)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1);
198*4882a593Smuzhiyun input_sync(rbtn_data->input_dev);
199*4882a593Smuzhiyun input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0);
200*4882a593Smuzhiyun input_sync(rbtn_data->input_dev);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun /*
205*4882a593Smuzhiyun * acpi driver
206*4882a593Smuzhiyun */
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun static int rbtn_add(struct acpi_device *device);
209*4882a593Smuzhiyun static int rbtn_remove(struct acpi_device *device);
210*4882a593Smuzhiyun static void rbtn_notify(struct acpi_device *device, u32 event);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun static const struct acpi_device_id rbtn_ids[] = {
213*4882a593Smuzhiyun { "DELRBTN", 0 },
214*4882a593Smuzhiyun { "DELLABCE", 0 },
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun /*
217*4882a593Smuzhiyun * This driver can also handle the "DELLABC6" device that
218*4882a593Smuzhiyun * appears on the XPS 13 9350, but that device is disabled by
219*4882a593Smuzhiyun * the DSDT unless booted with acpi_osi="!Windows 2012"
220*4882a593Smuzhiyun * acpi_osi="!Windows 2013".
221*4882a593Smuzhiyun *
222*4882a593Smuzhiyun * According to Mario at Dell:
223*4882a593Smuzhiyun *
224*4882a593Smuzhiyun * DELLABC6 is a custom interface that was created solely to
225*4882a593Smuzhiyun * have airplane mode support for Windows 7. For Windows 10
226*4882a593Smuzhiyun * the proper interface is to use that which is handled by
227*4882a593Smuzhiyun * intel-hid. A OEM airplane mode driver is not used.
228*4882a593Smuzhiyun *
229*4882a593Smuzhiyun * Since the kernel doesn't identify as Windows 7 it would be
230*4882a593Smuzhiyun * incorrect to do attempt to use that interface.
231*4882a593Smuzhiyun *
232*4882a593Smuzhiyun * Even if we override _OSI and bind to DELLABC6, we end up with
233*4882a593Smuzhiyun * inconsistent behavior in which userspace can get out of sync
234*4882a593Smuzhiyun * with the rfkill state as it conflicts with events from
235*4882a593Smuzhiyun * intel-hid.
236*4882a593Smuzhiyun *
237*4882a593Smuzhiyun * The upshot is that it is better to just ignore DELLABC6
238*4882a593Smuzhiyun * devices.
239*4882a593Smuzhiyun */
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun { "", 0 },
242*4882a593Smuzhiyun };
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
rbtn_clear_suspended_flag(void * context)245*4882a593Smuzhiyun static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun struct rbtn_data *rbtn_data = context;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun rbtn_data->suspended = false;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
rbtn_suspend(struct device * dev)252*4882a593Smuzhiyun static int rbtn_suspend(struct device *dev)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun struct acpi_device *device = to_acpi_device(dev);
255*4882a593Smuzhiyun struct rbtn_data *rbtn_data = acpi_driver_data(device);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun rbtn_data->suspended = true;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun return 0;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
rbtn_resume(struct device * dev)262*4882a593Smuzhiyun static int rbtn_resume(struct device *dev)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun struct acpi_device *device = to_acpi_device(dev);
265*4882a593Smuzhiyun struct rbtn_data *rbtn_data = acpi_driver_data(device);
266*4882a593Smuzhiyun acpi_status status;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun /*
269*4882a593Smuzhiyun * Upon resume, some BIOSes send an ACPI notification thet triggers
270*4882a593Smuzhiyun * an unwanted input event. In order to ignore it, we use a flag
271*4882a593Smuzhiyun * that we set at suspend and clear once we have received the extra
272*4882a593Smuzhiyun * ACPI notification. Since ACPI notifications are delivered
273*4882a593Smuzhiyun * asynchronously to drivers, we clear the flag from the workqueue
274*4882a593Smuzhiyun * used to deliver the notifications. This should be enough
275*4882a593Smuzhiyun * to have the flag cleared only after we received the extra
276*4882a593Smuzhiyun * notification, if any.
277*4882a593Smuzhiyun */
278*4882a593Smuzhiyun status = acpi_os_execute(OSL_NOTIFY_HANDLER,
279*4882a593Smuzhiyun rbtn_clear_suspended_flag, rbtn_data);
280*4882a593Smuzhiyun if (ACPI_FAILURE(status))
281*4882a593Smuzhiyun rbtn_clear_suspended_flag(rbtn_data);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun #endif
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun static struct acpi_driver rbtn_driver = {
290*4882a593Smuzhiyun .name = "dell-rbtn",
291*4882a593Smuzhiyun .ids = rbtn_ids,
292*4882a593Smuzhiyun .drv.pm = &rbtn_pm_ops,
293*4882a593Smuzhiyun .ops = {
294*4882a593Smuzhiyun .add = rbtn_add,
295*4882a593Smuzhiyun .remove = rbtn_remove,
296*4882a593Smuzhiyun .notify = rbtn_notify,
297*4882a593Smuzhiyun },
298*4882a593Smuzhiyun .owner = THIS_MODULE,
299*4882a593Smuzhiyun };
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun /*
303*4882a593Smuzhiyun * notifier export functions
304*4882a593Smuzhiyun */
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static bool auto_remove_rfkill = true;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
309*4882a593Smuzhiyun
rbtn_inc_count(struct device * dev,void * data)310*4882a593Smuzhiyun static int rbtn_inc_count(struct device *dev, void *data)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun struct acpi_device *device = to_acpi_device(dev);
313*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
314*4882a593Smuzhiyun int *count = data;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun if (rbtn_data->type == RBTN_SLIDER)
317*4882a593Smuzhiyun (*count)++;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return 0;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
rbtn_switch_dev(struct device * dev,void * data)322*4882a593Smuzhiyun static int rbtn_switch_dev(struct device *dev, void *data)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun struct acpi_device *device = to_acpi_device(dev);
325*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
326*4882a593Smuzhiyun bool enable = data;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun if (rbtn_data->type != RBTN_SLIDER)
329*4882a593Smuzhiyun return 0;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun if (enable)
332*4882a593Smuzhiyun rbtn_rfkill_init(device);
333*4882a593Smuzhiyun else
334*4882a593Smuzhiyun rbtn_rfkill_exit(device);
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun return 0;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
dell_rbtn_notifier_register(struct notifier_block * nb)339*4882a593Smuzhiyun int dell_rbtn_notifier_register(struct notifier_block *nb)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun bool first;
342*4882a593Smuzhiyun int count;
343*4882a593Smuzhiyun int ret;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun count = 0;
346*4882a593Smuzhiyun ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
347*4882a593Smuzhiyun rbtn_inc_count);
348*4882a593Smuzhiyun if (ret || count == 0)
349*4882a593Smuzhiyun return -ENODEV;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun first = !rbtn_chain_head.head;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
354*4882a593Smuzhiyun if (ret != 0)
355*4882a593Smuzhiyun return ret;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun if (auto_remove_rfkill && first)
358*4882a593Smuzhiyun ret = driver_for_each_device(&rbtn_driver.drv, NULL,
359*4882a593Smuzhiyun (void *)false, rbtn_switch_dev);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun return ret;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
364*4882a593Smuzhiyun
dell_rbtn_notifier_unregister(struct notifier_block * nb)365*4882a593Smuzhiyun int dell_rbtn_notifier_unregister(struct notifier_block *nb)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun int ret;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
370*4882a593Smuzhiyun if (ret != 0)
371*4882a593Smuzhiyun return ret;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun if (auto_remove_rfkill && !rbtn_chain_head.head)
374*4882a593Smuzhiyun ret = driver_for_each_device(&rbtn_driver.drv, NULL,
375*4882a593Smuzhiyun (void *)true, rbtn_switch_dev);
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun return ret;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun /*
383*4882a593Smuzhiyun * acpi driver functions
384*4882a593Smuzhiyun */
385*4882a593Smuzhiyun
rbtn_add(struct acpi_device * device)386*4882a593Smuzhiyun static int rbtn_add(struct acpi_device *device)
387*4882a593Smuzhiyun {
388*4882a593Smuzhiyun struct rbtn_data *rbtn_data;
389*4882a593Smuzhiyun enum rbtn_type type;
390*4882a593Smuzhiyun int ret = 0;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun type = rbtn_check(device);
393*4882a593Smuzhiyun if (type == RBTN_UNKNOWN) {
394*4882a593Smuzhiyun dev_info(&device->dev, "Unknown device type\n");
395*4882a593Smuzhiyun return -EINVAL;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun ret = rbtn_acquire(device, true);
399*4882a593Smuzhiyun if (ret < 0) {
400*4882a593Smuzhiyun dev_err(&device->dev, "Cannot enable device\n");
401*4882a593Smuzhiyun return ret;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
405*4882a593Smuzhiyun if (!rbtn_data)
406*4882a593Smuzhiyun return -ENOMEM;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun rbtn_data->type = type;
409*4882a593Smuzhiyun device->driver_data = rbtn_data;
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun switch (rbtn_data->type) {
412*4882a593Smuzhiyun case RBTN_TOGGLE:
413*4882a593Smuzhiyun ret = rbtn_input_init(rbtn_data);
414*4882a593Smuzhiyun break;
415*4882a593Smuzhiyun case RBTN_SLIDER:
416*4882a593Smuzhiyun if (auto_remove_rfkill && rbtn_chain_head.head)
417*4882a593Smuzhiyun ret = 0;
418*4882a593Smuzhiyun else
419*4882a593Smuzhiyun ret = rbtn_rfkill_init(device);
420*4882a593Smuzhiyun break;
421*4882a593Smuzhiyun default:
422*4882a593Smuzhiyun ret = -EINVAL;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun return ret;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
rbtn_remove(struct acpi_device * device)429*4882a593Smuzhiyun static int rbtn_remove(struct acpi_device *device)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun switch (rbtn_data->type) {
434*4882a593Smuzhiyun case RBTN_TOGGLE:
435*4882a593Smuzhiyun rbtn_input_exit(rbtn_data);
436*4882a593Smuzhiyun break;
437*4882a593Smuzhiyun case RBTN_SLIDER:
438*4882a593Smuzhiyun rbtn_rfkill_exit(device);
439*4882a593Smuzhiyun break;
440*4882a593Smuzhiyun default:
441*4882a593Smuzhiyun break;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun rbtn_acquire(device, false);
445*4882a593Smuzhiyun device->driver_data = NULL;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun return 0;
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
rbtn_notify(struct acpi_device * device,u32 event)450*4882a593Smuzhiyun static void rbtn_notify(struct acpi_device *device, u32 event)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun struct rbtn_data *rbtn_data = device->driver_data;
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun /*
455*4882a593Smuzhiyun * Some BIOSes send a notification at resume.
456*4882a593Smuzhiyun * Ignore it to prevent unwanted input events.
457*4882a593Smuzhiyun */
458*4882a593Smuzhiyun if (rbtn_data->suspended) {
459*4882a593Smuzhiyun dev_dbg(&device->dev, "ACPI notification ignored\n");
460*4882a593Smuzhiyun return;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun if (event != 0x80) {
464*4882a593Smuzhiyun dev_info(&device->dev, "Received unknown event (0x%x)\n",
465*4882a593Smuzhiyun event);
466*4882a593Smuzhiyun return;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun switch (rbtn_data->type) {
470*4882a593Smuzhiyun case RBTN_TOGGLE:
471*4882a593Smuzhiyun rbtn_input_event(rbtn_data);
472*4882a593Smuzhiyun break;
473*4882a593Smuzhiyun case RBTN_SLIDER:
474*4882a593Smuzhiyun rbtn_rfkill_event(device);
475*4882a593Smuzhiyun atomic_notifier_call_chain(&rbtn_chain_head, event, device);
476*4882a593Smuzhiyun break;
477*4882a593Smuzhiyun default:
478*4882a593Smuzhiyun break;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun /*
484*4882a593Smuzhiyun * module functions
485*4882a593Smuzhiyun */
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun module_acpi_driver(rbtn_driver);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun module_param(auto_remove_rfkill, bool, 0444);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
492*4882a593Smuzhiyun "other modules start receiving events "
493*4882a593Smuzhiyun "from this module and re-add them when "
494*4882a593Smuzhiyun "the last module stops receiving events "
495*4882a593Smuzhiyun "(default true)");
496*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, rbtn_ids);
497*4882a593Smuzhiyun MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
498*4882a593Smuzhiyun MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
499*4882a593Smuzhiyun MODULE_LICENSE("GPL");
500