• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 
3   Broadcom B43 wireless driver
4   RFKILL support
5 
6   Copyright (c) 2007 Michael Buesch <mb@bu3sch.de>
7 
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with this program; see the file COPYING.  If not, write to
20   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
21   Boston, MA 02110-1301, USA.
22 
23 */
24 
25 #include "rfkill.h"
26 #include "radio.h"
27 #include "b43legacy.h"
28 
29 #include <linux/kmod.h>
30 
31 
32 /* Returns TRUE, if the radio is enabled in hardware. */
b43legacy_is_hw_radio_enabled(struct b43legacy_wldev * dev)33 static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
34 {
35 	if (dev->phy.rev >= 3) {
36 		if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI)
37 		      & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
38 			return 1;
39 	} else {
40 		if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
41 		    & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
42 			return 1;
43 	}
44 	return 0;
45 }
46 
47 /* The poll callback for the hardware button. */
b43legacy_rfkill_poll(struct input_polled_dev * poll_dev)48 static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
49 {
50 	struct b43legacy_wldev *dev = poll_dev->private;
51 	struct b43legacy_wl *wl = dev->wl;
52 	bool enabled;
53 	bool report_change = 0;
54 
55 	mutex_lock(&wl->mutex);
56 	if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) {
57 		mutex_unlock(&wl->mutex);
58 		return;
59 	}
60 	enabled = b43legacy_is_hw_radio_enabled(dev);
61 	if (unlikely(enabled != dev->radio_hw_enable)) {
62 		dev->radio_hw_enable = enabled;
63 		report_change = 1;
64 		b43legacyinfo(wl, "Radio hardware status changed to %s\n",
65 			enabled ? "ENABLED" : "DISABLED");
66 	}
67 	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 }
76 
77 /* Called when the RFKILL toggled in software.
78  * This is called without locking. */
b43legacy_rfkill_soft_toggle(void * data,enum rfkill_state state)79 static int b43legacy_rfkill_soft_toggle(void *data, enum rfkill_state state)
80 {
81 	struct b43legacy_wldev *dev = data;
82 	struct b43legacy_wl *wl = dev->wl;
83 	int err = -EBUSY;
84 
85 	if (!wl->rfkill.registered)
86 		return 0;
87 
88 	mutex_lock(&wl->mutex);
89 	if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)
90 		goto out_unlock;
91 	err = 0;
92 	switch (state) {
93 	case RFKILL_STATE_UNBLOCKED:
94 		if (!dev->radio_hw_enable) {
95 			/* No luck. We can't toggle the hardware RF-kill
96 			 * button from software. */
97 			err = -EBUSY;
98 			goto out_unlock;
99 		}
100 		if (!dev->phy.radio_on)
101 			b43legacy_radio_turn_on(dev);
102 		break;
103 	case RFKILL_STATE_SOFT_BLOCKED:
104 		if (dev->phy.radio_on)
105 			b43legacy_radio_turn_off(dev, 0);
106 		break;
107 	default:
108 		b43legacywarn(wl, "Received unexpected rfkill state %d.\n",
109 			      state);
110 		break;
111 	}
112 
113 out_unlock:
114 	mutex_unlock(&wl->mutex);
115 
116 	return err;
117 }
118 
b43legacy_rfkill_led_name(struct b43legacy_wldev * dev)119 char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
120 {
121 	struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);
122 
123 	if (!rfk->registered)
124 		return NULL;
125 	return rfkill_get_led_name(rfk->rfkill);
126 }
127 
b43legacy_rfkill_init(struct b43legacy_wldev * dev)128 void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
129 {
130 	struct b43legacy_wl *wl = dev->wl;
131 	struct b43legacy_rfkill *rfk = &(wl->rfkill);
132 	int err;
133 
134 	rfk->registered = 0;
135 
136 	rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
137 	if (!rfk->rfkill)
138 		goto out_error;
139 	snprintf(rfk->name, sizeof(rfk->name),
140 		 "b43legacy-%s", wiphy_name(wl->hw->wiphy));
141 	rfk->rfkill->name = rfk->name;
142 	rfk->rfkill->state = RFKILL_STATE_UNBLOCKED;
143 	rfk->rfkill->data = dev;
144 	rfk->rfkill->toggle_radio = b43legacy_rfkill_soft_toggle;
145 	rfk->rfkill->user_claim_unsupported = 1;
146 
147 	rfk->poll_dev = input_allocate_polled_device();
148 	if (!rfk->poll_dev) {
149 		rfkill_free(rfk->rfkill);
150 		goto err_freed_rfk;
151 	}
152 
153 	rfk->poll_dev->private = dev;
154 	rfk->poll_dev->poll = b43legacy_rfkill_poll;
155 	rfk->poll_dev->poll_interval = 1000; /* msecs */
156 
157 	rfk->poll_dev->input->name = rfk->name;
158 	rfk->poll_dev->input->id.bustype = BUS_HOST;
159 	rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor;
160 	rfk->poll_dev->input->evbit[0] = BIT(EV_KEY);
161 	set_bit(KEY_WLAN, rfk->poll_dev->input->keybit);
162 
163 	err = rfkill_register(rfk->rfkill);
164 	if (err)
165 		goto err_free_polldev;
166 
167 #ifdef CONFIG_RFKILL_INPUT_MODULE
168 	/* B43legacy RF-kill isn't useful without the rfkill-input subsystem.
169 	 * Try to load the module. */
170 	err = request_module("rfkill-input");
171 	if (err)
172 		b43legacywarn(wl, "Failed to load the rfkill-input module."
173 			"The built-in radio LED will not work.\n");
174 #endif /* CONFIG_RFKILL_INPUT */
175 
176 	err = input_register_polled_device(rfk->poll_dev);
177 	if (err)
178 		goto err_unreg_rfk;
179 
180 	rfk->registered = 1;
181 
182 	return;
183 err_unreg_rfk:
184 	rfkill_unregister(rfk->rfkill);
185 err_free_polldev:
186 	input_free_polled_device(rfk->poll_dev);
187 	rfk->poll_dev = NULL;
188 err_freed_rfk:
189 	rfk->rfkill = NULL;
190 out_error:
191 	rfk->registered = 0;
192 	b43legacywarn(wl, "RF-kill button init failed\n");
193 }
194 
b43legacy_rfkill_exit(struct b43legacy_wldev * dev)195 void b43legacy_rfkill_exit(struct b43legacy_wldev *dev)
196 {
197 	struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);
198 
199 	if (!rfk->registered)
200 		return;
201 	rfk->registered = 0;
202 
203 	input_unregister_polled_device(rfk->poll_dev);
204 	rfkill_unregister(rfk->rfkill);
205 	input_free_polled_device(rfk->poll_dev);
206 	rfk->poll_dev = NULL;
207 	rfk->rfkill = NULL;
208 }
209 
210