Merge branch 'upstream'
[pandora-kernel.git] / drivers / net / wireless / bcm43xx / bcm43xx_sysfs.c
1 /*
2
3   Broadcom BCM43xx wireless driver
4
5   SYSFS support routines
6
7   Copyright (c) 2006 Michael Buesch <mbuesch@freenet.de>
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program; see the file COPYING.  If not, write to
21   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
22   Boston, MA 02110-1301, USA.
23
24 */
25
26 #include "bcm43xx_sysfs.h"
27 #include "bcm43xx.h"
28 #include "bcm43xx_main.h"
29 #include "bcm43xx_radio.h"
30
31 #include <linux/capability.h>
32
33
34 #define GENERIC_FILESIZE        64
35
36
37 static int get_integer(const char *buf, size_t count)
38 {
39         char tmp[10 + 1] = { 0 };
40         int ret = -EINVAL;
41
42         if (count == 0)
43                 goto out;
44         count = min(count, (size_t)10);
45         memcpy(tmp, buf, count);
46         ret = simple_strtol(tmp, NULL, 10);
47 out:
48         return ret;
49 }
50
51 static int get_boolean(const char *buf, size_t count)
52 {
53         if (count != 0) {
54                 if (buf[0] == '1')
55                         return 1;
56                 if (buf[0] == '0')
57                         return 0;
58                 if (count >= 4 && memcmp(buf, "true", 4) == 0)
59                         return 1;
60                 if (count >= 5 && memcmp(buf, "false", 5) == 0)
61                         return 0;
62                 if (count >= 3 && memcmp(buf, "yes", 3) == 0)
63                         return 1;
64                 if (count >= 2 && memcmp(buf, "no", 2) == 0)
65                         return 0;
66                 if (count >= 2 && memcmp(buf, "on", 2) == 0)
67                         return 1;
68                 if (count >= 3 && memcmp(buf, "off", 3) == 0)
69                         return 0;
70         }
71         return -EINVAL;
72 }
73
74 static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
75                                        struct device_attribute *attr,
76                                        char *buf)
77 {
78         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
79         u16 *sprom;
80         unsigned long flags;
81         int i, err;
82
83         if (!capable(CAP_NET_ADMIN))
84                 return -EPERM;
85
86         assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
87         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
88                         GFP_KERNEL);
89         if (!sprom)
90                 return -ENOMEM;
91         bcm43xx_lock_mmio(bcm, flags);
92         assert(bcm->initialized);
93         err = bcm43xx_sprom_read(bcm, sprom);
94         if (!err) {
95                 for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
96                         buf[i * 2] = sprom[i] & 0x00FF;
97                         buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8;
98                 }
99         }
100         bcm43xx_unlock_mmio(bcm, flags);
101         kfree(sprom);
102
103         return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16);
104 }
105
106 static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
107                                         struct device_attribute *attr,
108                                         const char *buf, size_t count)
109 {
110         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
111         u16 *sprom;
112         unsigned long flags;
113         int i, err;
114
115         if (!capable(CAP_NET_ADMIN))
116                 return -EPERM;
117
118         if (count != BCM43xx_SPROM_SIZE * sizeof(u16))
119                 return -EINVAL;
120         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
121                         GFP_KERNEL);
122         if (!sprom)
123                 return -ENOMEM;
124         for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
125                 sprom[i] = buf[i * 2] & 0xFF;
126                 sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8;
127         }
128         bcm43xx_lock_mmio(bcm, flags);
129         assert(bcm->initialized);
130         err = bcm43xx_sprom_write(bcm, sprom);
131         bcm43xx_unlock_mmio(bcm, flags);
132         kfree(sprom);
133
134         return err ? err : count;
135
136 }
137
138 static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
139                                             struct device_attribute *attr,
140                                             char *buf)
141 {
142         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
143         unsigned long flags;
144         int err;
145         ssize_t count = 0;
146
147         if (!capable(CAP_NET_ADMIN))
148                 return -EPERM;
149
150         bcm43xx_lock(bcm, flags);
151         assert(bcm->initialized);
152
153         switch (bcm43xx_current_radio(bcm)->interfmode) {
154         case BCM43xx_RADIO_INTERFMODE_NONE:
155                 count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
156                 break;
157         case BCM43xx_RADIO_INTERFMODE_NONWLAN:
158                 count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
159                 break;
160         case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
161                 count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
162                 break;
163         default:
164                 assert(0);
165         }
166         err = 0;
167
168         bcm43xx_unlock(bcm, flags);
169
170         return err ? err : count;
171
172 }
173
174 static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
175                                              struct device_attribute *attr,
176                                              const char *buf, size_t count)
177 {
178         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
179         unsigned long flags;
180         int err;
181         int mode;
182
183         if (!capable(CAP_NET_ADMIN))
184                 return -EPERM;
185
186         mode = get_integer(buf, count);
187         switch (mode) {
188         case 0:
189                 mode = BCM43xx_RADIO_INTERFMODE_NONE;
190                 break;
191         case 1:
192                 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
193                 break;
194         case 2:
195                 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
196                 break;
197         case 3:
198                 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
199                 break;
200         default:
201                 return -EINVAL;
202         }
203
204         bcm43xx_lock_mmio(bcm, flags);
205         assert(bcm->initialized);
206
207         err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
208         if (err) {
209                 printk(KERN_ERR PFX "Interference Mitigation not "
210                                     "supported by device\n");
211         }
212
213         bcm43xx_unlock_mmio(bcm, flags);
214
215         return err ? err : count;
216 }
217
218 static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
219                                           struct device_attribute *attr,
220                                           char *buf)
221 {
222         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
223         unsigned long flags;
224         int err;
225         ssize_t count;
226
227         if (!capable(CAP_NET_ADMIN))
228                 return -EPERM;
229
230         bcm43xx_lock(bcm, flags);
231         assert(bcm->initialized);
232
233         if (bcm->short_preamble)
234                 count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");
235         else
236                 count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");
237
238         err = 0;
239         bcm43xx_unlock(bcm, flags);
240
241         return err ? err : count;
242 }
243
244 static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
245                                            struct device_attribute *attr,
246                                            const char *buf, size_t count)
247 {
248         struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
249         unsigned long flags;
250         int err;
251         int value;
252
253         if (!capable(CAP_NET_ADMIN))
254                 return -EPERM;
255
256         value = get_boolean(buf, count);
257         if (value < 0)
258                 return value;
259         bcm43xx_lock(bcm, flags);
260         assert(bcm->initialized);
261
262         bcm->short_preamble = !!value;
263
264         err = 0;
265         bcm43xx_unlock(bcm, flags);
266
267         return err ? err : count;
268 }
269
270 int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
271 {
272         struct device *dev = &bcm->pci_dev->dev;
273         struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
274         int err;
275
276         assert(bcm->initialized);
277
278         sysfs->attr_sprom.attr.name = "sprom";
279         sysfs->attr_sprom.attr.owner = THIS_MODULE;
280         sysfs->attr_sprom.attr.mode = 0600;
281         sysfs->attr_sprom.show = bcm43xx_attr_sprom_show;
282         sysfs->attr_sprom.store = bcm43xx_attr_sprom_store;
283         err = device_create_file(dev, &sysfs->attr_sprom);
284         if (err)
285                 goto out;
286
287         sysfs->attr_interfmode.attr.name = "interference";
288         sysfs->attr_interfmode.attr.owner = THIS_MODULE;
289         sysfs->attr_interfmode.attr.mode = 0600;
290         sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show;
291         sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store;
292         err = device_create_file(dev, &sysfs->attr_interfmode);
293         if (err)
294                 goto err_remove_sprom;
295
296         sysfs->attr_preamble.attr.name = "shortpreamble";
297         sysfs->attr_preamble.attr.owner = THIS_MODULE;
298         sysfs->attr_preamble.attr.mode = 0600;
299         sysfs->attr_preamble.show = bcm43xx_attr_preamble_show;
300         sysfs->attr_preamble.store = bcm43xx_attr_preamble_store;
301         err = device_create_file(dev, &sysfs->attr_preamble);
302         if (err)
303                 goto err_remove_interfmode;
304
305 out:
306         return err;
307 err_remove_interfmode:
308         device_remove_file(dev, &sysfs->attr_interfmode);
309 err_remove_sprom:
310         device_remove_file(dev, &sysfs->attr_sprom);
311         goto out;
312 }
313
314 void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
315 {
316         struct device *dev = &bcm->pci_dev->dev;
317         struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
318
319         device_remove_file(dev, &sysfs->attr_preamble);
320         device_remove_file(dev, &sysfs->attr_interfmode);
321         device_remove_file(dev, &sysfs->attr_sprom);
322 }