]> err.no Git - linux-2.6/blob - drivers/net/wireless/b43/rfkill.c
456930ffef2dc68ffa62814a60aa5fd8e947a07c
[linux-2.6] / drivers / net / wireless / b43 / rfkill.c
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 "b43.h"
27
28
29 /* Returns TRUE, if the radio is enabled in hardware. */
30 static bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
31 {
32         if (dev->phy.rev >= 3) {
33                 if (!(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI)
34                       & B43_MMIO_RADIO_HWENABLED_HI_MASK))
35                         return 1;
36         } else {
37                 if (b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO)
38                     & B43_MMIO_RADIO_HWENABLED_LO_MASK)
39                         return 1;
40         }
41         return 0;
42 }
43
44 /* The poll callback for the hardware button. */
45 static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
46 {
47         struct b43_wldev *dev = poll_dev->private;
48         struct b43_wl *wl = dev->wl;
49         bool enabled;
50
51         mutex_lock(&wl->mutex);
52         B43_WARN_ON(b43_status(dev) < B43_STAT_INITIALIZED);
53         enabled = b43_is_hw_radio_enabled(dev);
54         if (unlikely(enabled != dev->radio_hw_enable)) {
55                 dev->radio_hw_enable = enabled;
56                 b43info(wl, "Radio hardware status changed to %s\n",
57                         enabled ? "ENABLED" : "DISABLED");
58                 mutex_unlock(&wl->mutex);
59                 input_report_key(poll_dev->input, KEY_WLAN, enabled);
60         } else
61                 mutex_unlock(&wl->mutex);
62 }
63
64 /* Called when the RFKILL toggled in software. */
65 static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state)
66 {
67         struct b43_wldev *dev = data;
68         struct b43_wl *wl = dev->wl;
69         int err = 0;
70
71         /* When RFKILL is registered, it will call back into this callback.
72          * wl->mutex will already be locked when this happens.
73          * So first trylock. On contention check if we are in initialization.
74          * Silently return if that happens to avoid a deadlock. */
75         if (mutex_trylock(&wl->mutex) == 0) {
76                 if (b43_status(dev) < B43_STAT_INITIALIZED)
77                         return 0;
78                 mutex_lock(&wl->mutex);
79         }
80         if (b43_status(dev) < B43_STAT_INITIALIZED)
81                 goto out_unlock;
82
83         switch (state) {
84         case RFKILL_STATE_ON:
85                 if (!dev->radio_hw_enable) {
86                         /* No luck. We can't toggle the hardware RF-kill
87                          * button from software. */
88                         err = -EBUSY;
89                         goto out_unlock;
90                 }
91                 if (!dev->phy.radio_on)
92                         b43_radio_turn_on(dev);
93                 break;
94         case RFKILL_STATE_OFF:
95                 if (dev->phy.radio_on)
96                         b43_radio_turn_off(dev, 0);
97                 break;
98         }
99 out_unlock:
100         mutex_unlock(&wl->mutex);
101
102         return err;
103 }
104
105 char * b43_rfkill_led_name(struct b43_wldev *dev)
106 {
107         struct b43_wl *wl = dev->wl;
108
109         if (!wl->rfkill.rfkill)
110                 return NULL;
111         return rfkill_get_led_name(wl->rfkill.rfkill);
112 }
113
114 void b43_rfkill_init(struct b43_wldev *dev)
115 {
116         struct b43_wl *wl = dev->wl;
117         struct b43_rfkill *rfk = &(wl->rfkill);
118         int err;
119
120         if (rfk->rfkill) {
121                 err = rfkill_register(rfk->rfkill);
122                 if (err) {
123                         b43warn(wl, "Failed to register RF-kill button\n");
124                         goto err_free_rfk;
125                 }
126         }
127         if (rfk->poll_dev) {
128                 err = input_register_polled_device(rfk->poll_dev);
129                 if (err) {
130                         b43warn(wl, "Failed to register RF-kill polldev\n");
131                         goto err_free_polldev;
132                 }
133         }
134
135         return;
136 err_free_rfk:
137         rfkill_free(rfk->rfkill);
138         rfk->rfkill = NULL;
139 err_free_polldev:
140         input_free_polled_device(rfk->poll_dev);
141         rfk->poll_dev = NULL;
142 }
143
144 void b43_rfkill_exit(struct b43_wldev *dev)
145 {
146         struct b43_rfkill *rfk = &(dev->wl->rfkill);
147
148         if (rfk->poll_dev)
149                 input_unregister_polled_device(rfk->poll_dev);
150         if (rfk->rfkill)
151                 rfkill_unregister(rfk->rfkill);
152 }
153
154 void b43_rfkill_alloc(struct b43_wldev *dev)
155 {
156         struct b43_wl *wl = dev->wl;
157         struct b43_rfkill *rfk = &(wl->rfkill);
158
159         snprintf(rfk->name, sizeof(rfk->name),
160                  "b43-%s", wiphy_name(wl->hw->wiphy));
161
162         rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
163         if (!rfk->rfkill) {
164                 b43warn(wl, "Failed to allocate RF-kill button\n");
165                 return;
166         }
167         rfk->rfkill->name = rfk->name;
168         rfk->rfkill->state = RFKILL_STATE_ON;
169         rfk->rfkill->data = dev;
170         rfk->rfkill->toggle_radio = b43_rfkill_soft_toggle;
171         rfk->rfkill->user_claim_unsupported = 1;
172
173         rfk->poll_dev = input_allocate_polled_device();
174         if (rfk->poll_dev) {
175                 rfk->poll_dev->private = dev;
176                 rfk->poll_dev->poll = b43_rfkill_poll;
177                 rfk->poll_dev->poll_interval = 1000; /* msecs */
178         } else
179                 b43warn(wl, "Failed to allocate RF-kill polldev\n");
180 }
181
182 void b43_rfkill_free(struct b43_wldev *dev)
183 {
184         struct b43_rfkill *rfk = &(dev->wl->rfkill);
185
186         input_free_polled_device(rfk->poll_dev);
187         rfk->poll_dev = NULL;
188         rfkill_free(rfk->rfkill);
189         rfk->rfkill = NULL;
190 }