aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/b43/rfkill.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-06-02 07:01:37 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-06-03 14:06:13 -0400
commit19d337dff95cbf76edd3ad95c0cee2732c3e1ec5 (patch)
tree33326eeb09cb9664cc8427a5dc7cd2b08b5a57c3 /drivers/net/wireless/b43/rfkill.c
parent0f6399c4c525b518644a9b09f8d6fb125a418c4d (diff)
rfkill: rewrite
This patch completely rewrites the rfkill core to address the following deficiencies: * all rfkill drivers need to implement polling where necessary rather than having one central implementation * updating the rfkill state cannot be done from arbitrary contexts, forcing drivers to use schedule_work and requiring lots of code * rfkill drivers need to keep track of soft/hard blocked internally -- the core should do this * the rfkill API has many unexpected quirks, for example being asymmetric wrt. alloc/free and register/unregister * rfkill can call back into a driver from within a function the driver called -- this is prone to deadlocks and generally should be avoided * rfkill-input pointlessly is a separate module * drivers need to #ifdef rfkill functions (unless they want to depend on or select RFKILL) -- rfkill should provide inlines that do nothing if it isn't compiled in * the rfkill structure is not opaque -- drivers need to initialise it correctly (lots of sanity checking code required) -- instead force drivers to pass the right variables to rfkill_alloc() * the documentation is hard to read because it always assumes the reader is completely clueless and contains way TOO MANY CAPS * the rfkill code needlessly uses a lot of locks and atomic operations in locked sections * fix LED trigger to actually change the LED when the radio state changes -- this wasn't done before Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad] Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/b43/rfkill.c')
-rw-r--r--drivers/net/wireless/b43/rfkill.c123
1 files changed, 34 insertions, 89 deletions
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c
index 9e1d00bc24d3..96047843cd56 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/b43/rfkill.c
@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
45} 45}
46 46
47/* The poll callback for the hardware button. */ 47/* The poll callback for the hardware button. */
48static void b43_rfkill_poll(struct input_polled_dev *poll_dev) 48static void b43_rfkill_poll(struct rfkill *rfkill, void *data)
49{ 49{
50 struct b43_wldev *dev = poll_dev->private; 50 struct b43_wldev *dev = data;
51 struct b43_wl *wl = dev->wl; 51 struct b43_wl *wl = dev->wl;
52 bool enabled; 52 bool enabled;
53 bool report_change = 0;
54 53
55 mutex_lock(&wl->mutex); 54 mutex_lock(&wl->mutex);
56 if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { 55 if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) {
@@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
60 enabled = b43_is_hw_radio_enabled(dev); 59 enabled = b43_is_hw_radio_enabled(dev);
61 if (unlikely(enabled != dev->radio_hw_enable)) { 60 if (unlikely(enabled != dev->radio_hw_enable)) {
62 dev->radio_hw_enable = enabled; 61 dev->radio_hw_enable = enabled;
63 report_change = 1;
64 b43info(wl, "Radio hardware status changed to %s\n", 62 b43info(wl, "Radio hardware status changed to %s\n",
65 enabled ? "ENABLED" : "DISABLED"); 63 enabled ? "ENABLED" : "DISABLED");
64 enabled = !rfkill_set_hw_state(rfkill, !enabled);
65 if (enabled != dev->phy.radio_on)
66 b43_software_rfkill(dev, !enabled);
66 } 67 }
67 mutex_unlock(&wl->mutex); 68 mutex_unlock(&wl->mutex);
68
69 /* send the radio switch event to the system - note both a key press
70 * and a release are required */
71 if (unlikely(report_change)) {
72 input_report_key(poll_dev->input, KEY_WLAN, 1);
73 input_report_key(poll_dev->input, KEY_WLAN, 0);
74 }
75} 69}
76 70
77/* Called when the RFKILL toggled in software. */ 71/* Called when the RFKILL toggled in software. */
78static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state) 72static int b43_rfkill_soft_set(void *data, bool blocked)
79{ 73{
80 struct b43_wldev *dev = data; 74 struct b43_wldev *dev = data;
81 struct b43_wl *wl = dev->wl; 75 struct b43_wl *wl = dev->wl;
82 int err = -EBUSY; 76 int err = -EINVAL;
83 77
84 if (!wl->rfkill.registered) 78 if (WARN_ON(!wl->rfkill.registered))
85 return 0; 79 return -EINVAL;
86 80
87 mutex_lock(&wl->mutex); 81 mutex_lock(&wl->mutex);
82
88 if (b43_status(dev) < B43_STAT_INITIALIZED) 83 if (b43_status(dev) < B43_STAT_INITIALIZED)
89 goto out_unlock; 84 goto out_unlock;
85
86 if (!dev->radio_hw_enable)
87 goto out_unlock;
88
89 if (!blocked != dev->phy.radio_on)
90 b43_software_rfkill(dev, blocked);
90 err = 0; 91 err = 0;
91 switch (state) {
92 case RFKILL_STATE_UNBLOCKED:
93 if (!dev->radio_hw_enable) {
94 /* No luck. We can't toggle the hardware RF-kill
95 * button from software. */
96 err = -EBUSY;
97 goto out_unlock;
98 }
99 if (!dev->phy.radio_on)
100 b43_software_rfkill(dev, state);
101 break;
102 case RFKILL_STATE_SOFT_BLOCKED:
103 if (dev->phy.radio_on)
104 b43_software_rfkill(dev, state);
105 break;
106 default:
107 b43warn(wl, "Received unexpected rfkill state %d.\n", state);
108 break;
109 }
110out_unlock: 92out_unlock:
111 mutex_unlock(&wl->mutex); 93 mutex_unlock(&wl->mutex);
112
113 return err; 94 return err;
114} 95}
115 96
116char *b43_rfkill_led_name(struct b43_wldev *dev) 97const char *b43_rfkill_led_name(struct b43_wldev *dev)
117{ 98{
118 struct b43_rfkill *rfk = &(dev->wl->rfkill); 99 struct b43_rfkill *rfk = &(dev->wl->rfkill);
119 100
120 if (!rfk->registered) 101 if (!rfk->registered)
121 return NULL; 102 return NULL;
122 return rfkill_get_led_name(rfk->rfkill); 103 return rfkill_get_led_trigger_name(rfk->rfkill);
123} 104}
124 105
106static const struct rfkill_ops b43_rfkill_ops = {
107 .set_block = b43_rfkill_soft_set,
108 .poll = b43_rfkill_poll,
109};
110
125void b43_rfkill_init(struct b43_wldev *dev) 111void b43_rfkill_init(struct b43_wldev *dev)
126{ 112{
127 struct b43_wl *wl = dev->wl; 113 struct b43_wl *wl = dev->wl;
@@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev)
130 116
131 rfk->registered = 0; 117 rfk->registered = 0;
132 118
133 rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
134 if (!rfk->rfkill)
135 goto out_error;
136 snprintf(rfk->name, sizeof(rfk->name), 119 snprintf(rfk->name, sizeof(rfk->name),
137 "b43-%s", wiphy_name(wl->hw->wiphy)); 120 "b43-%s", wiphy_name(wl->hw->wiphy));
138 rfk->rfkill->name = rfk->name;
139 rfk->rfkill->state = RFKILL_STATE_UNBLOCKED;
140 rfk->rfkill->data = dev;
141 rfk->rfkill->toggle_radio = b43_rfkill_soft_toggle;
142
143 rfk->poll_dev = input_allocate_polled_device();
144 if (!rfk->poll_dev) {
145 rfkill_free(rfk->rfkill);
146 goto err_freed_rfk;
147 }
148
149 rfk->poll_dev->private = dev;
150 rfk->poll_dev->poll = b43_rfkill_poll;
151 rfk->poll_dev->poll_interval = 1000; /* msecs */
152 121
153 rfk->poll_dev->input->name = rfk->name; 122 rfk->rfkill = rfkill_alloc(rfk->name,
154 rfk->poll_dev->input->id.bustype = BUS_HOST; 123 dev->dev->dev,
155 rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor; 124 RFKILL_TYPE_WLAN,
156 rfk->poll_dev->input->evbit[0] = BIT(EV_KEY); 125 &b43_rfkill_ops, dev);
157 set_bit(KEY_WLAN, rfk->poll_dev->input->keybit); 126 if (!rfk->rfkill)
127 goto out_error;
158 128
159 err = rfkill_register(rfk->rfkill); 129 err = rfkill_register(rfk->rfkill);
160 if (err) 130 if (err)
161 goto err_free_polldev; 131 goto err_free;
162
163#ifdef CONFIG_RFKILL_INPUT_MODULE
164 /* B43 RF-kill isn't useful without the rfkill-input subsystem.
165 * Try to load the module. */
166 err = request_module("rfkill-input");
167 if (err)
168 b43warn(wl, "Failed to load the rfkill-input module. "
169 "The built-in radio LED will not work.\n");
170#endif /* CONFIG_RFKILL_INPUT */
171
172#if !defined(CONFIG_RFKILL_INPUT) && !defined(CONFIG_RFKILL_INPUT_MODULE)
173 b43warn(wl, "The rfkill-input subsystem is not available. "
174 "The built-in radio LED will not work.\n");
175#endif
176
177 err = input_register_polled_device(rfk->poll_dev);
178 if (err)
179 goto err_unreg_rfk;
180 132
181 rfk->registered = 1; 133 rfk->registered = 1;
182 134
183 return; 135 return;
184err_unreg_rfk: 136 err_free:
185 rfkill_unregister(rfk->rfkill); 137 rfkill_destroy(rfk->rfkill);
186err_free_polldev: 138 out_error:
187 input_free_polled_device(rfk->poll_dev);
188 rfk->poll_dev = NULL;
189err_freed_rfk:
190 rfk->rfkill = NULL;
191out_error:
192 rfk->registered = 0; 139 rfk->registered = 0;
193 b43warn(wl, "RF-kill button init failed\n"); 140 b43warn(wl, "RF-kill button init failed\n");
194} 141}
@@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev)
201 return; 148 return;
202 rfk->registered = 0; 149 rfk->registered = 0;
203 150
204 input_unregister_polled_device(rfk->poll_dev);
205 rfkill_unregister(rfk->rfkill); 151 rfkill_unregister(rfk->rfkill);
206 input_free_polled_device(rfk->poll_dev); 152 rfkill_destroy(rfk->rfkill);
207 rfk->poll_dev = NULL;
208 rfk->rfkill = NULL; 153 rfk->rfkill = NULL;
209} 154}