18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006 - 2007 Ivo van Doorn 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Dmitry Torokhov 58c2ecf20Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 128c2ecf20Sopenharmony_ci#include <linux/capability.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/rfkill.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/wait.h> 218c2ecf20Sopenharmony_ci#include <linux/poll.h> 228c2ecf20Sopenharmony_ci#include <linux/fs.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "rfkill.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define POLL_INTERVAL (5 * HZ) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define RFKILL_BLOCK_HW BIT(0) 308c2ecf20Sopenharmony_ci#define RFKILL_BLOCK_SW BIT(1) 318c2ecf20Sopenharmony_ci#define RFKILL_BLOCK_SW_PREV BIT(2) 328c2ecf20Sopenharmony_ci#define RFKILL_BLOCK_ANY (RFKILL_BLOCK_HW |\ 338c2ecf20Sopenharmony_ci RFKILL_BLOCK_SW |\ 348c2ecf20Sopenharmony_ci RFKILL_BLOCK_SW_PREV) 358c2ecf20Sopenharmony_ci#define RFKILL_BLOCK_SW_SETCALL BIT(31) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct rfkill { 388c2ecf20Sopenharmony_ci spinlock_t lock; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci enum rfkill_type type; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci unsigned long state; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci u32 idx; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci bool registered; 478c2ecf20Sopenharmony_ci bool persistent; 488c2ecf20Sopenharmony_ci bool polling_paused; 498c2ecf20Sopenharmony_ci bool suspended; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci const struct rfkill_ops *ops; 528c2ecf20Sopenharmony_ci void *data; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_LEDS 558c2ecf20Sopenharmony_ci struct led_trigger led_trigger; 568c2ecf20Sopenharmony_ci const char *ledtrigname; 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci struct device dev; 608c2ecf20Sopenharmony_ci struct list_head node; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci struct delayed_work poll_work; 638c2ecf20Sopenharmony_ci struct work_struct uevent_work; 648c2ecf20Sopenharmony_ci struct work_struct sync_work; 658c2ecf20Sopenharmony_ci char name[]; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci#define to_rfkill(d) container_of(d, struct rfkill, dev) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct rfkill_int_event { 708c2ecf20Sopenharmony_ci struct list_head list; 718c2ecf20Sopenharmony_ci struct rfkill_event ev; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct rfkill_data { 758c2ecf20Sopenharmony_ci struct list_head list; 768c2ecf20Sopenharmony_ci struct list_head events; 778c2ecf20Sopenharmony_ci struct mutex mtx; 788c2ecf20Sopenharmony_ci wait_queue_head_t read_wait; 798c2ecf20Sopenharmony_ci bool input_handler; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>"); 848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RF switch support"); 868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * The locking here should be made much smarter, we currently have 918c2ecf20Sopenharmony_ci * a bit of a stupid situation because drivers might want to register 928c2ecf20Sopenharmony_ci * the rfkill struct under their own lock, and take this lock during 938c2ecf20Sopenharmony_ci * rfkill method calls -- which will cause an AB-BA deadlock situation. 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * To fix that, we need to rework this code here to be mostly lock-free 968c2ecf20Sopenharmony_ci * and only use the mutex for list manipulations, not to protect the 978c2ecf20Sopenharmony_ci * various other global variables. Then we can avoid holding the mutex 988c2ecf20Sopenharmony_ci * around driver operations, and all is happy. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic LIST_HEAD(rfkill_list); /* list of registered rf switches */ 1018c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(rfkill_global_mutex); 1028c2ecf20Sopenharmony_cistatic LIST_HEAD(rfkill_fds); /* list of open fds of /dev/rfkill */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic unsigned int rfkill_default_state = 1; 1058c2ecf20Sopenharmony_cimodule_param_named(default_state, rfkill_default_state, uint, 0444); 1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_state, 1078c2ecf20Sopenharmony_ci "Default initial state for all radio types, 0 = radio off"); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct { 1108c2ecf20Sopenharmony_ci bool cur, sav; 1118c2ecf20Sopenharmony_ci} rfkill_global_states[NUM_RFKILL_TYPES]; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic bool rfkill_epo_lock_active; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_LEDS 1178c2ecf20Sopenharmony_cistatic void rfkill_led_trigger_event(struct rfkill *rfkill) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct led_trigger *trigger; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!rfkill->registered) 1228c2ecf20Sopenharmony_ci return; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci trigger = &rfkill->led_trigger; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (rfkill->state & RFKILL_BLOCK_ANY) 1278c2ecf20Sopenharmony_ci led_trigger_event(trigger, LED_OFF); 1288c2ecf20Sopenharmony_ci else 1298c2ecf20Sopenharmony_ci led_trigger_event(trigger, LED_FULL); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int rfkill_led_trigger_activate(struct led_classdev *led) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct rfkill *rfkill; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci rfkill = container_of(led->trigger, struct rfkill, led_trigger); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rfkill_led_trigger_event(rfkill); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciconst char *rfkill_get_led_trigger_name(struct rfkill *rfkill) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return rfkill->led_trigger.name; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_get_led_trigger_name); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_civoid rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci rfkill->ledtrigname = name; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_set_led_trigger_name); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int rfkill_led_trigger_register(struct rfkill *rfkill) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci rfkill->led_trigger.name = rfkill->ledtrigname 1608c2ecf20Sopenharmony_ci ? : dev_name(&rfkill->dev); 1618c2ecf20Sopenharmony_ci rfkill->led_trigger.activate = rfkill_led_trigger_activate; 1628c2ecf20Sopenharmony_ci return led_trigger_register(&rfkill->led_trigger); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void rfkill_led_trigger_unregister(struct rfkill *rfkill) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci led_trigger_unregister(&rfkill->led_trigger); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic struct led_trigger rfkill_any_led_trigger; 1718c2ecf20Sopenharmony_cistatic struct led_trigger rfkill_none_led_trigger; 1728c2ecf20Sopenharmony_cistatic struct work_struct rfkill_global_led_trigger_work; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void rfkill_global_led_trigger_worker(struct work_struct *work) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci enum led_brightness brightness = LED_OFF; 1778c2ecf20Sopenharmony_ci struct rfkill *rfkill; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 1808c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) { 1818c2ecf20Sopenharmony_ci if (!(rfkill->state & RFKILL_BLOCK_ANY)) { 1828c2ecf20Sopenharmony_ci brightness = LED_FULL; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci led_trigger_event(&rfkill_any_led_trigger, brightness); 1898c2ecf20Sopenharmony_ci led_trigger_event(&rfkill_none_led_trigger, 1908c2ecf20Sopenharmony_ci brightness == LED_OFF ? LED_FULL : LED_OFF); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void rfkill_global_led_trigger_event(void) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci schedule_work(&rfkill_global_led_trigger_work); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int rfkill_global_led_trigger_register(void) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci INIT_WORK(&rfkill_global_led_trigger_work, 2038c2ecf20Sopenharmony_ci rfkill_global_led_trigger_worker); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci rfkill_any_led_trigger.name = "rfkill-any"; 2068c2ecf20Sopenharmony_ci ret = led_trigger_register(&rfkill_any_led_trigger); 2078c2ecf20Sopenharmony_ci if (ret) 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci rfkill_none_led_trigger.name = "rfkill-none"; 2118c2ecf20Sopenharmony_ci ret = led_trigger_register(&rfkill_none_led_trigger); 2128c2ecf20Sopenharmony_ci if (ret) 2138c2ecf20Sopenharmony_ci led_trigger_unregister(&rfkill_any_led_trigger); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci /* Delay activation until all global triggers are registered */ 2168c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void rfkill_global_led_trigger_unregister(void) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci led_trigger_unregister(&rfkill_none_led_trigger); 2248c2ecf20Sopenharmony_ci led_trigger_unregister(&rfkill_any_led_trigger); 2258c2ecf20Sopenharmony_ci cancel_work_sync(&rfkill_global_led_trigger_work); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci#else 2288c2ecf20Sopenharmony_cistatic void rfkill_led_trigger_event(struct rfkill *rfkill) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic inline int rfkill_led_trigger_register(struct rfkill *rfkill) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic inline void rfkill_led_trigger_unregister(struct rfkill *rfkill) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void rfkill_global_led_trigger_event(void) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int rfkill_global_led_trigger_register(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void rfkill_global_led_trigger_unregister(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci#endif /* CONFIG_RFKILL_LEDS */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void rfkill_fill_event(struct rfkill_event *ev, struct rfkill *rfkill, 2568c2ecf20Sopenharmony_ci enum rfkill_operation op) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci unsigned long flags; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ev->idx = rfkill->idx; 2618c2ecf20Sopenharmony_ci ev->type = rfkill->type; 2628c2ecf20Sopenharmony_ci ev->op = op; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 2658c2ecf20Sopenharmony_ci ev->hard = !!(rfkill->state & RFKILL_BLOCK_HW); 2668c2ecf20Sopenharmony_ci ev->soft = !!(rfkill->state & (RFKILL_BLOCK_SW | 2678c2ecf20Sopenharmony_ci RFKILL_BLOCK_SW_PREV)); 2688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct rfkill_data *data; 2748c2ecf20Sopenharmony_ci struct rfkill_int_event *ev; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci list_for_each_entry(data, &rfkill_fds, list) { 2778c2ecf20Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 2788c2ecf20Sopenharmony_ci if (!ev) 2798c2ecf20Sopenharmony_ci continue; 2808c2ecf20Sopenharmony_ci rfkill_fill_event(&ev->ev, rfkill, op); 2818c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 2828c2ecf20Sopenharmony_ci list_add_tail(&ev->list, &data->events); 2838c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 2848c2ecf20Sopenharmony_ci wake_up_interruptible(&data->read_wait); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void rfkill_event(struct rfkill *rfkill) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (!rfkill->registered) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* also send event to /dev/rfkill */ 2968c2ecf20Sopenharmony_ci rfkill_send_events(rfkill, RFKILL_OP_CHANGE); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/** 3008c2ecf20Sopenharmony_ci * rfkill_set_block - wrapper for set_block method 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * @rfkill: the rfkill struct to use 3038c2ecf20Sopenharmony_ci * @blocked: the new software state 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * Calls the set_block method (when applicable) and handles notifications 3068c2ecf20Sopenharmony_ci * etc. as well. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic void rfkill_set_block(struct rfkill *rfkill, bool blocked) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci unsigned long flags; 3118c2ecf20Sopenharmony_ci bool prev, curr; 3128c2ecf20Sopenharmony_ci int err; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* 3188c2ecf20Sopenharmony_ci * Some platforms (...!) generate input events which affect the 3198c2ecf20Sopenharmony_ci * _hard_ kill state -- whenever something tries to change the 3208c2ecf20Sopenharmony_ci * current software state query the hardware state too. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci if (rfkill->ops->query) 3238c2ecf20Sopenharmony_ci rfkill->ops->query(rfkill, rfkill->data); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 3268c2ecf20Sopenharmony_ci prev = rfkill->state & RFKILL_BLOCK_SW; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (prev) 3298c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_SW_PREV; 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_SW_PREV; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (blocked) 3348c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_SW; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_SW; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_SW_SETCALL; 3398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci err = rfkill->ops->set_block(rfkill->data, blocked); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 3448c2ecf20Sopenharmony_ci if (err) { 3458c2ecf20Sopenharmony_ci /* 3468c2ecf20Sopenharmony_ci * Failed -- reset status to _PREV, which may be different 3478c2ecf20Sopenharmony_ci * from what we have set _PREV to earlier in this function 3488c2ecf20Sopenharmony_ci * if rfkill_set_sw_state was invoked. 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci if (rfkill->state & RFKILL_BLOCK_SW_PREV) 3518c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_SW; 3528c2ecf20Sopenharmony_ci else 3538c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_SW; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL; 3568c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_SW_PREV; 3578c2ecf20Sopenharmony_ci curr = rfkill->state & RFKILL_BLOCK_SW; 3588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci rfkill_led_trigger_event(rfkill); 3618c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (prev != curr) 3648c2ecf20Sopenharmony_ci rfkill_event(rfkill); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void rfkill_update_global_state(enum rfkill_type type, bool blocked) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int i; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (type != RFKILL_TYPE_ALL) { 3728c2ecf20Sopenharmony_ci rfkill_global_states[type].cur = blocked; 3738c2ecf20Sopenharmony_ci return; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RFKILL_TYPES; i++) 3778c2ecf20Sopenharmony_ci rfkill_global_states[i].cur = blocked; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 3818c2ecf20Sopenharmony_cistatic atomic_t rfkill_input_disabled = ATOMIC_INIT(0); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/** 3848c2ecf20Sopenharmony_ci * __rfkill_switch_all - Toggle state of all switches of given type 3858c2ecf20Sopenharmony_ci * @type: type of interfaces to be affected 3868c2ecf20Sopenharmony_ci * @blocked: the new state 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * This function sets the state of all switches of given type, 3898c2ecf20Sopenharmony_ci * unless a specific switch is suspended. 3908c2ecf20Sopenharmony_ci * 3918c2ecf20Sopenharmony_ci * Caller must have acquired rfkill_global_mutex. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_cistatic void __rfkill_switch_all(const enum rfkill_type type, bool blocked) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct rfkill *rfkill; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci rfkill_update_global_state(type, blocked); 3988c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) { 3998c2ecf20Sopenharmony_ci if (rfkill->type != type && type != RFKILL_TYPE_ALL) 4008c2ecf20Sopenharmony_ci continue; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, blocked); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/** 4078c2ecf20Sopenharmony_ci * rfkill_switch_all - Toggle state of all switches of given type 4088c2ecf20Sopenharmony_ci * @type: type of interfaces to be affected 4098c2ecf20Sopenharmony_ci * @blocked: the new state 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). 4128c2ecf20Sopenharmony_ci * Please refer to __rfkill_switch_all() for details. 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * Does nothing if the EPO lock is active. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_civoid rfkill_switch_all(enum rfkill_type type, bool blocked) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (atomic_read(&rfkill_input_disabled)) 4198c2ecf20Sopenharmony_ci return; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!rfkill_epo_lock_active) 4248c2ecf20Sopenharmony_ci __rfkill_switch_all(type, blocked); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/** 4308c2ecf20Sopenharmony_ci * rfkill_epo - emergency power off all transmitters 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, 4338c2ecf20Sopenharmony_ci * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex. 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * The global state before the EPO is saved and can be restored later 4368c2ecf20Sopenharmony_ci * using rfkill_restore_states(). 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_civoid rfkill_epo(void) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct rfkill *rfkill; 4418c2ecf20Sopenharmony_ci int i; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (atomic_read(&rfkill_input_disabled)) 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci rfkill_epo_lock_active = true; 4498c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) 4508c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, true); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RFKILL_TYPES; i++) { 4538c2ecf20Sopenharmony_ci rfkill_global_states[i].sav = rfkill_global_states[i].cur; 4548c2ecf20Sopenharmony_ci rfkill_global_states[i].cur = true; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/** 4618c2ecf20Sopenharmony_ci * rfkill_restore_states - restore global states 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Restore (and sync switches to) the global state from the 4648c2ecf20Sopenharmony_ci * states in rfkill_default_states. This can undo the effects of 4658c2ecf20Sopenharmony_ci * a call to rfkill_epo(). 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_civoid rfkill_restore_states(void) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci int i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (atomic_read(&rfkill_input_disabled)) 4728c2ecf20Sopenharmony_ci return; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci rfkill_epo_lock_active = false; 4778c2ecf20Sopenharmony_ci for (i = 0; i < NUM_RFKILL_TYPES; i++) 4788c2ecf20Sopenharmony_ci __rfkill_switch_all(i, rfkill_global_states[i].sav); 4798c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/** 4838c2ecf20Sopenharmony_ci * rfkill_remove_epo_lock - unlock state changes 4848c2ecf20Sopenharmony_ci * 4858c2ecf20Sopenharmony_ci * Used by rfkill-input manually unlock state changes, when 4868c2ecf20Sopenharmony_ci * the EPO switch is deactivated. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_civoid rfkill_remove_epo_lock(void) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci if (atomic_read(&rfkill_input_disabled)) 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 4948c2ecf20Sopenharmony_ci rfkill_epo_lock_active = false; 4958c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * rfkill_is_epo_lock_active - returns true EPO is active 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Returns 0 (false) if there is NOT an active EPO condition, 5028c2ecf20Sopenharmony_ci * and 1 (true) if there is an active EPO condition, which 5038c2ecf20Sopenharmony_ci * locks all radios in one of the BLOCKED states. 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * Can be called in atomic context. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_cibool rfkill_is_epo_lock_active(void) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci return rfkill_epo_lock_active; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/** 5138c2ecf20Sopenharmony_ci * rfkill_get_global_sw_state - returns global state for a type 5148c2ecf20Sopenharmony_ci * @type: the type to get the global state of 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Returns the current global state for a given wireless 5178c2ecf20Sopenharmony_ci * device type. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cibool rfkill_get_global_sw_state(const enum rfkill_type type) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci return rfkill_global_states[type].cur; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci#endif 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cibool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci unsigned long flags; 5288c2ecf20Sopenharmony_ci bool ret, prev; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 5338c2ecf20Sopenharmony_ci prev = !!(rfkill->state & RFKILL_BLOCK_HW); 5348c2ecf20Sopenharmony_ci if (blocked) 5358c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_HW; 5368c2ecf20Sopenharmony_ci else 5378c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_HW; 5388c2ecf20Sopenharmony_ci ret = !!(rfkill->state & RFKILL_BLOCK_ANY); 5398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci rfkill_led_trigger_event(rfkill); 5428c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (rfkill->registered && prev != blocked) 5458c2ecf20Sopenharmony_ci schedule_work(&rfkill->uevent_work); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_set_hw_state); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci u32 bit = RFKILL_BLOCK_SW; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* if in a ops->set_block right now, use other bit */ 5568c2ecf20Sopenharmony_ci if (rfkill->state & RFKILL_BLOCK_SW_SETCALL) 5578c2ecf20Sopenharmony_ci bit = RFKILL_BLOCK_SW_PREV; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (blocked) 5608c2ecf20Sopenharmony_ci rfkill->state |= bit; 5618c2ecf20Sopenharmony_ci else 5628c2ecf20Sopenharmony_ci rfkill->state &= ~bit; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cibool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci unsigned long flags; 5688c2ecf20Sopenharmony_ci bool prev, hwblock; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 5738c2ecf20Sopenharmony_ci prev = !!(rfkill->state & RFKILL_BLOCK_SW); 5748c2ecf20Sopenharmony_ci __rfkill_set_sw_state(rfkill, blocked); 5758c2ecf20Sopenharmony_ci hwblock = !!(rfkill->state & RFKILL_BLOCK_HW); 5768c2ecf20Sopenharmony_ci blocked = blocked || hwblock; 5778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (!rfkill->registered) 5808c2ecf20Sopenharmony_ci return blocked; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (prev != blocked && !hwblock) 5838c2ecf20Sopenharmony_ci schedule_work(&rfkill->uevent_work); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci rfkill_led_trigger_event(rfkill); 5868c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return blocked; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_set_sw_state); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_civoid rfkill_init_sw_state(struct rfkill *rfkill, bool blocked) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci unsigned long flags; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 5978c2ecf20Sopenharmony_ci BUG_ON(rfkill->registered); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 6008c2ecf20Sopenharmony_ci __rfkill_set_sw_state(rfkill, blocked); 6018c2ecf20Sopenharmony_ci rfkill->persistent = true; 6028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_init_sw_state); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_civoid rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci unsigned long flags; 6098c2ecf20Sopenharmony_ci bool swprev, hwprev; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * No need to care about prev/setblock ... this is for uevent only 6178c2ecf20Sopenharmony_ci * and that will get triggered by rfkill_set_block anyway. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci swprev = !!(rfkill->state & RFKILL_BLOCK_SW); 6208c2ecf20Sopenharmony_ci hwprev = !!(rfkill->state & RFKILL_BLOCK_HW); 6218c2ecf20Sopenharmony_ci __rfkill_set_sw_state(rfkill, sw); 6228c2ecf20Sopenharmony_ci if (hw) 6238c2ecf20Sopenharmony_ci rfkill->state |= RFKILL_BLOCK_HW; 6248c2ecf20Sopenharmony_ci else 6258c2ecf20Sopenharmony_ci rfkill->state &= ~RFKILL_BLOCK_HW; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!rfkill->registered) { 6308c2ecf20Sopenharmony_ci rfkill->persistent = true; 6318c2ecf20Sopenharmony_ci } else { 6328c2ecf20Sopenharmony_ci if (swprev != sw || hwprev != hw) 6338c2ecf20Sopenharmony_ci schedule_work(&rfkill->uevent_work); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci rfkill_led_trigger_event(rfkill); 6368c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_set_states); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic const char * const rfkill_types[] = { 6428c2ecf20Sopenharmony_ci NULL, /* RFKILL_TYPE_ALL */ 6438c2ecf20Sopenharmony_ci "wlan", 6448c2ecf20Sopenharmony_ci "bluetooth", 6458c2ecf20Sopenharmony_ci "ultrawideband", 6468c2ecf20Sopenharmony_ci "wimax", 6478c2ecf20Sopenharmony_ci "wwan", 6488c2ecf20Sopenharmony_ci "gps", 6498c2ecf20Sopenharmony_ci "fm", 6508c2ecf20Sopenharmony_ci "nfc", 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cienum rfkill_type rfkill_find_type(const char *name) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci int i; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(rfkill_types) != NUM_RFKILL_TYPES); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (!name) 6608c2ecf20Sopenharmony_ci return RFKILL_TYPE_ALL; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (i = 1; i < NUM_RFKILL_TYPES; i++) 6638c2ecf20Sopenharmony_ci if (!strcmp(name, rfkill_types[i])) 6648c2ecf20Sopenharmony_ci return i; 6658c2ecf20Sopenharmony_ci return RFKILL_TYPE_ALL; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_find_type); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 6708c2ecf20Sopenharmony_ci char *buf) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rfkill->name); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev, struct device_attribute *attr, 6798c2ecf20Sopenharmony_ci char *buf) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", rfkill_types[rfkill->type]); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(type); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic ssize_t index_show(struct device *dev, struct device_attribute *attr, 6888c2ecf20Sopenharmony_ci char *buf) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", rfkill->idx); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(index); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic ssize_t persistent_show(struct device *dev, 6978c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", rfkill->persistent); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(persistent); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic ssize_t hard_show(struct device *dev, struct device_attribute *attr, 7068c2ecf20Sopenharmony_ci char *buf) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_HW) ? 1 : 0 ); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(hard); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic ssize_t soft_show(struct device *dev, struct device_attribute *attr, 7158c2ecf20Sopenharmony_ci char *buf) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0 ); 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic ssize_t soft_store(struct device *dev, struct device_attribute *attr, 7238c2ecf20Sopenharmony_ci const char *buf, size_t count) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7268c2ecf20Sopenharmony_ci unsigned long state; 7278c2ecf20Sopenharmony_ci int err; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 7308c2ecf20Sopenharmony_ci return -EPERM; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &state); 7338c2ecf20Sopenharmony_ci if (err) 7348c2ecf20Sopenharmony_ci return err; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (state > 1 ) 7378c2ecf20Sopenharmony_ci return -EINVAL; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 7408c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, state); 7418c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return count; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(soft); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic u8 user_state_from_blocked(unsigned long state) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci if (state & RFKILL_BLOCK_HW) 7508c2ecf20Sopenharmony_ci return RFKILL_USER_STATE_HARD_BLOCKED; 7518c2ecf20Sopenharmony_ci if (state & RFKILL_BLOCK_SW) 7528c2ecf20Sopenharmony_ci return RFKILL_USER_STATE_SOFT_BLOCKED; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return RFKILL_USER_STATE_UNBLOCKED; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *attr, 7588c2ecf20Sopenharmony_ci char *buf) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", user_state_from_blocked(rfkill->state)); 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic ssize_t state_store(struct device *dev, struct device_attribute *attr, 7668c2ecf20Sopenharmony_ci const char *buf, size_t count) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 7698c2ecf20Sopenharmony_ci unsigned long state; 7708c2ecf20Sopenharmony_ci int err; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 7738c2ecf20Sopenharmony_ci return -EPERM; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &state); 7768c2ecf20Sopenharmony_ci if (err) 7778c2ecf20Sopenharmony_ci return err; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (state != RFKILL_USER_STATE_SOFT_BLOCKED && 7808c2ecf20Sopenharmony_ci state != RFKILL_USER_STATE_UNBLOCKED) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 7848c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED); 7858c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return count; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(state); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic struct attribute *rfkill_dev_attrs[] = { 7928c2ecf20Sopenharmony_ci &dev_attr_name.attr, 7938c2ecf20Sopenharmony_ci &dev_attr_type.attr, 7948c2ecf20Sopenharmony_ci &dev_attr_index.attr, 7958c2ecf20Sopenharmony_ci &dev_attr_persistent.attr, 7968c2ecf20Sopenharmony_ci &dev_attr_state.attr, 7978c2ecf20Sopenharmony_ci &dev_attr_soft.attr, 7988c2ecf20Sopenharmony_ci &dev_attr_hard.attr, 7998c2ecf20Sopenharmony_ci NULL, 8008c2ecf20Sopenharmony_ci}; 8018c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(rfkill_dev); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic void rfkill_release(struct device *dev) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci kfree(rfkill); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 8138c2ecf20Sopenharmony_ci unsigned long flags; 8148c2ecf20Sopenharmony_ci u32 state; 8158c2ecf20Sopenharmony_ci int error; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); 8188c2ecf20Sopenharmony_ci if (error) 8198c2ecf20Sopenharmony_ci return error; 8208c2ecf20Sopenharmony_ci error = add_uevent_var(env, "RFKILL_TYPE=%s", 8218c2ecf20Sopenharmony_ci rfkill_types[rfkill->type]); 8228c2ecf20Sopenharmony_ci if (error) 8238c2ecf20Sopenharmony_ci return error; 8248c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 8258c2ecf20Sopenharmony_ci state = rfkill->state; 8268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 8278c2ecf20Sopenharmony_ci error = add_uevent_var(env, "RFKILL_STATE=%d", 8288c2ecf20Sopenharmony_ci user_state_from_blocked(state)); 8298c2ecf20Sopenharmony_ci return error; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_civoid rfkill_pause_polling(struct rfkill *rfkill) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (!rfkill->ops->poll) 8378c2ecf20Sopenharmony_ci return; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci rfkill->polling_paused = true; 8408c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rfkill->poll_work); 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_pause_polling); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_civoid rfkill_resume_polling(struct rfkill *rfkill) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (!rfkill->ops->poll) 8498c2ecf20Sopenharmony_ci return; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci rfkill->polling_paused = false; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (rfkill->suspended) 8548c2ecf20Sopenharmony_ci return; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 8578c2ecf20Sopenharmony_ci &rfkill->poll_work, 0); 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_resume_polling); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 8628c2ecf20Sopenharmony_cistatic int rfkill_suspend(struct device *dev) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci rfkill->suspended = true; 8678c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rfkill->poll_work); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return 0; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int rfkill_resume(struct device *dev) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct rfkill *rfkill = to_rfkill(dev); 8758c2ecf20Sopenharmony_ci bool cur; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci rfkill->suspended = false; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (!rfkill->registered) 8808c2ecf20Sopenharmony_ci return 0; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (!rfkill->persistent) { 8838c2ecf20Sopenharmony_ci cur = !!(rfkill->state & RFKILL_BLOCK_SW); 8848c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, cur); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (rfkill->ops->poll && !rfkill->polling_paused) 8888c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 8898c2ecf20Sopenharmony_ci &rfkill->poll_work, 0); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume); 8958c2ecf20Sopenharmony_ci#define RFKILL_PM_OPS (&rfkill_pm_ops) 8968c2ecf20Sopenharmony_ci#else 8978c2ecf20Sopenharmony_ci#define RFKILL_PM_OPS NULL 8988c2ecf20Sopenharmony_ci#endif 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic struct class rfkill_class = { 9018c2ecf20Sopenharmony_ci .name = "rfkill", 9028c2ecf20Sopenharmony_ci .dev_release = rfkill_release, 9038c2ecf20Sopenharmony_ci .dev_groups = rfkill_dev_groups, 9048c2ecf20Sopenharmony_ci .dev_uevent = rfkill_dev_uevent, 9058c2ecf20Sopenharmony_ci .pm = RFKILL_PM_OPS, 9068c2ecf20Sopenharmony_ci}; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cibool rfkill_blocked(struct rfkill *rfkill) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci unsigned long flags; 9118c2ecf20Sopenharmony_ci u32 state; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci spin_lock_irqsave(&rfkill->lock, flags); 9148c2ecf20Sopenharmony_ci state = rfkill->state; 9158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rfkill->lock, flags); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return !!(state & RFKILL_BLOCK_ANY); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_blocked); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistruct rfkill * __must_check rfkill_alloc(const char *name, 9238c2ecf20Sopenharmony_ci struct device *parent, 9248c2ecf20Sopenharmony_ci const enum rfkill_type type, 9258c2ecf20Sopenharmony_ci const struct rfkill_ops *ops, 9268c2ecf20Sopenharmony_ci void *ops_data) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct rfkill *rfkill; 9298c2ecf20Sopenharmony_ci struct device *dev; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (WARN_ON(!ops)) 9328c2ecf20Sopenharmony_ci return NULL; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (WARN_ON(!ops->set_block)) 9358c2ecf20Sopenharmony_ci return NULL; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (WARN_ON(!name)) 9388c2ecf20Sopenharmony_ci return NULL; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES)) 9418c2ecf20Sopenharmony_ci return NULL; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL); 9448c2ecf20Sopenharmony_ci if (!rfkill) 9458c2ecf20Sopenharmony_ci return NULL; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci spin_lock_init(&rfkill->lock); 9488c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rfkill->node); 9498c2ecf20Sopenharmony_ci rfkill->type = type; 9508c2ecf20Sopenharmony_ci strcpy(rfkill->name, name); 9518c2ecf20Sopenharmony_ci rfkill->ops = ops; 9528c2ecf20Sopenharmony_ci rfkill->data = ops_data; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci dev = &rfkill->dev; 9558c2ecf20Sopenharmony_ci dev->class = &rfkill_class; 9568c2ecf20Sopenharmony_ci dev->parent = parent; 9578c2ecf20Sopenharmony_ci device_initialize(dev); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return rfkill; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_alloc); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void rfkill_poll(struct work_struct *work) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct rfkill *rfkill; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci rfkill = container_of(work, struct rfkill, poll_work.work); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* 9708c2ecf20Sopenharmony_ci * Poll hardware state -- driver will use one of the 9718c2ecf20Sopenharmony_ci * rfkill_set{,_hw,_sw}_state functions and use its 9728c2ecf20Sopenharmony_ci * return value to update the current status. 9738c2ecf20Sopenharmony_ci */ 9748c2ecf20Sopenharmony_ci rfkill->ops->poll(rfkill, rfkill->data); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 9778c2ecf20Sopenharmony_ci &rfkill->poll_work, 9788c2ecf20Sopenharmony_ci round_jiffies_relative(POLL_INTERVAL)); 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic void rfkill_uevent_work(struct work_struct *work) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct rfkill *rfkill; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci rfkill = container_of(work, struct rfkill, uevent_work); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 9888c2ecf20Sopenharmony_ci rfkill_event(rfkill); 9898c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic void rfkill_sync_work(struct work_struct *work) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct rfkill *rfkill; 9958c2ecf20Sopenharmony_ci bool cur; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci rfkill = container_of(work, struct rfkill, sync_work); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 10008c2ecf20Sopenharmony_ci cur = rfkill_global_states[rfkill->type].cur; 10018c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, cur); 10028c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ciint __must_check rfkill_register(struct rfkill *rfkill) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci static unsigned long rfkill_no; 10088c2ecf20Sopenharmony_ci struct device *dev; 10098c2ecf20Sopenharmony_ci int error; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (!rfkill) 10128c2ecf20Sopenharmony_ci return -EINVAL; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci dev = &rfkill->dev; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (rfkill->registered) { 10198c2ecf20Sopenharmony_ci error = -EALREADY; 10208c2ecf20Sopenharmony_ci goto unlock; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci rfkill->idx = rfkill_no; 10248c2ecf20Sopenharmony_ci dev_set_name(dev, "rfkill%lu", rfkill_no); 10258c2ecf20Sopenharmony_ci rfkill_no++; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci list_add_tail(&rfkill->node, &rfkill_list); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci error = device_add(dev); 10308c2ecf20Sopenharmony_ci if (error) 10318c2ecf20Sopenharmony_ci goto remove; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci error = rfkill_led_trigger_register(rfkill); 10348c2ecf20Sopenharmony_ci if (error) 10358c2ecf20Sopenharmony_ci goto devdel; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci rfkill->registered = true; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll); 10408c2ecf20Sopenharmony_ci INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work); 10418c2ecf20Sopenharmony_ci INIT_WORK(&rfkill->sync_work, rfkill_sync_work); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (rfkill->ops->poll) 10448c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 10458c2ecf20Sopenharmony_ci &rfkill->poll_work, 10468c2ecf20Sopenharmony_ci round_jiffies_relative(POLL_INTERVAL)); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (!rfkill->persistent || rfkill_epo_lock_active) { 10498c2ecf20Sopenharmony_ci schedule_work(&rfkill->sync_work); 10508c2ecf20Sopenharmony_ci } else { 10518c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 10528c2ecf20Sopenharmony_ci bool soft_blocked = !!(rfkill->state & RFKILL_BLOCK_SW); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (!atomic_read(&rfkill_input_disabled)) 10558c2ecf20Sopenharmony_ci __rfkill_switch_all(rfkill->type, soft_blocked); 10568c2ecf20Sopenharmony_ci#endif 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 10608c2ecf20Sopenharmony_ci rfkill_send_events(rfkill, RFKILL_OP_ADD); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 10638c2ecf20Sopenharmony_ci return 0; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci devdel: 10668c2ecf20Sopenharmony_ci device_del(&rfkill->dev); 10678c2ecf20Sopenharmony_ci remove: 10688c2ecf20Sopenharmony_ci list_del_init(&rfkill->node); 10698c2ecf20Sopenharmony_ci unlock: 10708c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 10718c2ecf20Sopenharmony_ci return error; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_register); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_civoid rfkill_unregister(struct rfkill *rfkill) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci BUG_ON(!rfkill); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (rfkill->ops->poll) 10808c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rfkill->poll_work); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci cancel_work_sync(&rfkill->uevent_work); 10838c2ecf20Sopenharmony_ci cancel_work_sync(&rfkill->sync_work); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci rfkill->registered = false; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci device_del(&rfkill->dev); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 10908c2ecf20Sopenharmony_ci rfkill_send_events(rfkill, RFKILL_OP_DEL); 10918c2ecf20Sopenharmony_ci list_del_init(&rfkill->node); 10928c2ecf20Sopenharmony_ci rfkill_global_led_trigger_event(); 10938c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci rfkill_led_trigger_unregister(rfkill); 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_unregister); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_civoid rfkill_destroy(struct rfkill *rfkill) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci if (rfkill) 11028c2ecf20Sopenharmony_ci put_device(&rfkill->dev); 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rfkill_destroy); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic int rfkill_fop_open(struct inode *inode, struct file *file) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct rfkill_data *data; 11098c2ecf20Sopenharmony_ci struct rfkill *rfkill; 11108c2ecf20Sopenharmony_ci struct rfkill_int_event *ev, *tmp; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 11138c2ecf20Sopenharmony_ci if (!data) 11148c2ecf20Sopenharmony_ci return -ENOMEM; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&data->events); 11178c2ecf20Sopenharmony_ci mutex_init(&data->mtx); 11188c2ecf20Sopenharmony_ci init_waitqueue_head(&data->read_wait); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 11218c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 11228c2ecf20Sopenharmony_ci /* 11238c2ecf20Sopenharmony_ci * start getting events from elsewhere but hold mtx to get 11248c2ecf20Sopenharmony_ci * startup events added first 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) { 11288c2ecf20Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_KERNEL); 11298c2ecf20Sopenharmony_ci if (!ev) 11308c2ecf20Sopenharmony_ci goto free; 11318c2ecf20Sopenharmony_ci rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); 11328c2ecf20Sopenharmony_ci list_add_tail(&ev->list, &data->events); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci list_add(&data->list, &rfkill_fds); 11358c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 11368c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci file->private_data = data; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return stream_open(inode, file); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci free: 11438c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 11448c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 11458c2ecf20Sopenharmony_ci mutex_destroy(&data->mtx); 11468c2ecf20Sopenharmony_ci list_for_each_entry_safe(ev, tmp, &data->events, list) 11478c2ecf20Sopenharmony_ci kfree(ev); 11488c2ecf20Sopenharmony_ci kfree(data); 11498c2ecf20Sopenharmony_ci return -ENOMEM; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic __poll_t rfkill_fop_poll(struct file *file, poll_table *wait) 11538c2ecf20Sopenharmony_ci{ 11548c2ecf20Sopenharmony_ci struct rfkill_data *data = file->private_data; 11558c2ecf20Sopenharmony_ci __poll_t res = EPOLLOUT | EPOLLWRNORM; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci poll_wait(file, &data->read_wait, wait); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 11608c2ecf20Sopenharmony_ci if (!list_empty(&data->events)) 11618c2ecf20Sopenharmony_ci res = EPOLLIN | EPOLLRDNORM; 11628c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci return res; 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_cistatic ssize_t rfkill_fop_read(struct file *file, char __user *buf, 11688c2ecf20Sopenharmony_ci size_t count, loff_t *pos) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci struct rfkill_data *data = file->private_data; 11718c2ecf20Sopenharmony_ci struct rfkill_int_event *ev; 11728c2ecf20Sopenharmony_ci unsigned long sz; 11738c2ecf20Sopenharmony_ci int ret; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci while (list_empty(&data->events)) { 11788c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 11798c2ecf20Sopenharmony_ci ret = -EAGAIN; 11808c2ecf20Sopenharmony_ci goto out; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 11838c2ecf20Sopenharmony_ci /* since we re-check and it just compares pointers, 11848c2ecf20Sopenharmony_ci * using !list_empty() without locking isn't a problem 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci ret = wait_event_interruptible(data->read_wait, 11878c2ecf20Sopenharmony_ci !list_empty(&data->events)); 11888c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (ret) 11918c2ecf20Sopenharmony_ci goto out; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci ev = list_first_entry(&data->events, struct rfkill_int_event, 11958c2ecf20Sopenharmony_ci list); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci sz = min_t(unsigned long, sizeof(ev->ev), count); 11988c2ecf20Sopenharmony_ci ret = sz; 11998c2ecf20Sopenharmony_ci if (copy_to_user(buf, &ev->ev, sz)) 12008c2ecf20Sopenharmony_ci ret = -EFAULT; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci list_del(&ev->list); 12038c2ecf20Sopenharmony_ci kfree(ev); 12048c2ecf20Sopenharmony_ci out: 12058c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 12068c2ecf20Sopenharmony_ci return ret; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic ssize_t rfkill_fop_write(struct file *file, const char __user *buf, 12108c2ecf20Sopenharmony_ci size_t count, loff_t *pos) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci struct rfkill *rfkill; 12138c2ecf20Sopenharmony_ci struct rfkill_event ev; 12148c2ecf20Sopenharmony_ci int ret; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* we don't need the 'hard' variable but accept it */ 12178c2ecf20Sopenharmony_ci if (count < RFKILL_EVENT_SIZE_V1 - 1) 12188c2ecf20Sopenharmony_ci return -EINVAL; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* 12218c2ecf20Sopenharmony_ci * Copy as much data as we can accept into our 'ev' buffer, 12228c2ecf20Sopenharmony_ci * but tell userspace how much we've copied so it can determine 12238c2ecf20Sopenharmony_ci * our API version even in a write() call, if it cares. 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci count = min(count, sizeof(ev)); 12268c2ecf20Sopenharmony_ci if (copy_from_user(&ev, buf, count)) 12278c2ecf20Sopenharmony_ci return -EFAULT; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (ev.type >= NUM_RFKILL_TYPES) 12308c2ecf20Sopenharmony_ci return -EINVAL; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci switch (ev.op) { 12358c2ecf20Sopenharmony_ci case RFKILL_OP_CHANGE_ALL: 12368c2ecf20Sopenharmony_ci rfkill_update_global_state(ev.type, ev.soft); 12378c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) 12388c2ecf20Sopenharmony_ci if (rfkill->type == ev.type || 12398c2ecf20Sopenharmony_ci ev.type == RFKILL_TYPE_ALL) 12408c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, ev.soft); 12418c2ecf20Sopenharmony_ci ret = 0; 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci case RFKILL_OP_CHANGE: 12448c2ecf20Sopenharmony_ci list_for_each_entry(rfkill, &rfkill_list, node) 12458c2ecf20Sopenharmony_ci if (rfkill->idx == ev.idx && 12468c2ecf20Sopenharmony_ci (rfkill->type == ev.type || 12478c2ecf20Sopenharmony_ci ev.type == RFKILL_TYPE_ALL)) 12488c2ecf20Sopenharmony_ci rfkill_set_block(rfkill, ev.soft); 12498c2ecf20Sopenharmony_ci ret = 0; 12508c2ecf20Sopenharmony_ci break; 12518c2ecf20Sopenharmony_ci default: 12528c2ecf20Sopenharmony_ci ret = -EINVAL; 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci return ret ?: count; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic int rfkill_fop_release(struct inode *inode, struct file *file) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct rfkill_data *data = file->private_data; 12648c2ecf20Sopenharmony_ci struct rfkill_int_event *ev, *tmp; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci mutex_lock(&rfkill_global_mutex); 12678c2ecf20Sopenharmony_ci list_del(&data->list); 12688c2ecf20Sopenharmony_ci mutex_unlock(&rfkill_global_mutex); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci mutex_destroy(&data->mtx); 12718c2ecf20Sopenharmony_ci list_for_each_entry_safe(ev, tmp, &data->events, list) 12728c2ecf20Sopenharmony_ci kfree(ev); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 12758c2ecf20Sopenharmony_ci if (data->input_handler) 12768c2ecf20Sopenharmony_ci if (atomic_dec_return(&rfkill_input_disabled) == 0) 12778c2ecf20Sopenharmony_ci printk(KERN_DEBUG "rfkill: input handler enabled\n"); 12788c2ecf20Sopenharmony_ci#endif 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci kfree(data); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci return 0; 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 12868c2ecf20Sopenharmony_cistatic long rfkill_fop_ioctl(struct file *file, unsigned int cmd, 12878c2ecf20Sopenharmony_ci unsigned long arg) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct rfkill_data *data = file->private_data; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci if (_IOC_TYPE(cmd) != RFKILL_IOC_MAGIC) 12928c2ecf20Sopenharmony_ci return -ENOSYS; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (_IOC_NR(cmd) != RFKILL_IOC_NOINPUT) 12958c2ecf20Sopenharmony_ci return -ENOSYS; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci mutex_lock(&data->mtx); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (!data->input_handler) { 13008c2ecf20Sopenharmony_ci if (atomic_inc_return(&rfkill_input_disabled) == 1) 13018c2ecf20Sopenharmony_ci printk(KERN_DEBUG "rfkill: input handler disabled\n"); 13028c2ecf20Sopenharmony_ci data->input_handler = true; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci mutex_unlock(&data->mtx); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return 0; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci#endif 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic const struct file_operations rfkill_fops = { 13128c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 13138c2ecf20Sopenharmony_ci .open = rfkill_fop_open, 13148c2ecf20Sopenharmony_ci .read = rfkill_fop_read, 13158c2ecf20Sopenharmony_ci .write = rfkill_fop_write, 13168c2ecf20Sopenharmony_ci .poll = rfkill_fop_poll, 13178c2ecf20Sopenharmony_ci .release = rfkill_fop_release, 13188c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 13198c2ecf20Sopenharmony_ci .unlocked_ioctl = rfkill_fop_ioctl, 13208c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 13218c2ecf20Sopenharmony_ci#endif 13228c2ecf20Sopenharmony_ci .llseek = no_llseek, 13238c2ecf20Sopenharmony_ci}; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci#define RFKILL_NAME "rfkill" 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic struct miscdevice rfkill_miscdev = { 13288c2ecf20Sopenharmony_ci .fops = &rfkill_fops, 13298c2ecf20Sopenharmony_ci .name = RFKILL_NAME, 13308c2ecf20Sopenharmony_ci .minor = RFKILL_MINOR, 13318c2ecf20Sopenharmony_ci}; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic int __init rfkill_init(void) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci int error; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci rfkill_update_global_state(RFKILL_TYPE_ALL, !rfkill_default_state); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci error = class_register(&rfkill_class); 13408c2ecf20Sopenharmony_ci if (error) 13418c2ecf20Sopenharmony_ci goto error_class; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci error = misc_register(&rfkill_miscdev); 13448c2ecf20Sopenharmony_ci if (error) 13458c2ecf20Sopenharmony_ci goto error_misc; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci error = rfkill_global_led_trigger_register(); 13488c2ecf20Sopenharmony_ci if (error) 13498c2ecf20Sopenharmony_ci goto error_led_trigger; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 13528c2ecf20Sopenharmony_ci error = rfkill_handler_init(); 13538c2ecf20Sopenharmony_ci if (error) 13548c2ecf20Sopenharmony_ci goto error_input; 13558c2ecf20Sopenharmony_ci#endif 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 13608c2ecf20Sopenharmony_cierror_input: 13618c2ecf20Sopenharmony_ci rfkill_global_led_trigger_unregister(); 13628c2ecf20Sopenharmony_ci#endif 13638c2ecf20Sopenharmony_cierror_led_trigger: 13648c2ecf20Sopenharmony_ci misc_deregister(&rfkill_miscdev); 13658c2ecf20Sopenharmony_cierror_misc: 13668c2ecf20Sopenharmony_ci class_unregister(&rfkill_class); 13678c2ecf20Sopenharmony_cierror_class: 13688c2ecf20Sopenharmony_ci return error; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_cisubsys_initcall(rfkill_init); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic void __exit rfkill_exit(void) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci#ifdef CONFIG_RFKILL_INPUT 13758c2ecf20Sopenharmony_ci rfkill_handler_exit(); 13768c2ecf20Sopenharmony_ci#endif 13778c2ecf20Sopenharmony_ci rfkill_global_led_trigger_unregister(); 13788c2ecf20Sopenharmony_ci misc_deregister(&rfkill_miscdev); 13798c2ecf20Sopenharmony_ci class_unregister(&rfkill_class); 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_cimodule_exit(rfkill_exit); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(RFKILL_MINOR); 13848c2ecf20Sopenharmony_ciMODULE_ALIAS("devname:" RFKILL_NAME); 1385