Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[linux-2.6.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 int sprom2hex(const u16 *sprom, char *buf, size_t buf_len)
75 {
76         int i, pos = 0;
77
78         for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
79                 pos += snprintf(buf + pos, buf_len - pos - 1,
80                                 "%04X", swab16(sprom[i]) & 0xFFFF);
81         }
82         pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
83
84         return pos + 1;
85 }
86
87 static int hex2sprom(u16 *sprom, const char *dump, size_t len)
88 {
89         char tmp[5] = { 0 };
90         int cnt = 0;
91         unsigned long parsed;
92
93         if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
94                 return -EINVAL;
95
96         while (cnt < BCM43xx_SPROM_SIZE) {
97                 memcpy(tmp, dump, 4);
98                 dump += 4;
99                 parsed = simple_strtoul(tmp, NULL, 16);
100                 sprom[cnt++] = swab16((u16)parsed);
101         }
102
103         return 0;
104 }
105
106 static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
107                                        struct device_attribute *attr,
108                                        char *buf)
109 {
110         struct bcm43xx_private *bcm = dev_to_bcm(dev);
111         u16 *sprom;
112         unsigned long flags;
113         int err;
114
115         if (!capable(CAP_NET_ADMIN))
116                 return -EPERM;
117
118         assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
119         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
120                         GFP_KERNEL);
121         if (!sprom)
122                 return -ENOMEM;
123         bcm43xx_lock_irqsafe(bcm, flags);
124         err = bcm43xx_sprom_read(bcm, sprom);
125         if (!err)
126                 err = sprom2hex(sprom, buf, PAGE_SIZE);
127         mmiowb();
128         bcm43xx_unlock_irqsafe(bcm, flags);
129         kfree(sprom);
130
131         return err;
132 }
133
134 static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
135                                         struct device_attribute *attr,
136                                         const char *buf, size_t count)
137 {
138         struct bcm43xx_private *bcm = dev_to_bcm(dev);
139         u16 *sprom;
140         unsigned long flags;
141         int err;
142
143         if (!capable(CAP_NET_ADMIN))
144                 return -EPERM;
145
146         sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
147                         GFP_KERNEL);
148         if (!sprom)
149                 return -ENOMEM;
150         err = hex2sprom(sprom, buf, count);
151         if (err)
152                 goto out_kfree;
153         bcm43xx_lock_irqsafe(bcm, flags);
154         err = bcm43xx_sprom_write(bcm, sprom);
155         mmiowb();
156         bcm43xx_unlock_irqsafe(bcm, flags);
157 out_kfree:
158         kfree(sprom);
159
160         return err ? err : count;
161
162 }
163
164 static DEVICE_ATTR(sprom, 0600,
165                    bcm43xx_attr_sprom_show,
166                    bcm43xx_attr_sprom_store);
167
168 static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
169                                             struct device_attribute *attr,
170                                             char *buf)
171 {
172         struct bcm43xx_private *bcm = dev_to_bcm(dev);
173         int err;
174         ssize_t count = 0;
175
176         if (!capable(CAP_NET_ADMIN))
177                 return -EPERM;
178
179         bcm43xx_lock_noirq(bcm);
180
181         switch (bcm43xx_current_radio(bcm)->interfmode) {
182         case BCM43xx_RADIO_INTERFMODE_NONE:
183                 count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
184                 break;
185         case BCM43xx_RADIO_INTERFMODE_NONWLAN:
186                 count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
187                 break;
188         case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
189                 count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
190                 break;
191         default:
192                 assert(0);
193         }
194         err = 0;
195
196         bcm43xx_unlock_noirq(bcm);
197
198         return err ? err : count;
199
200 }
201
202 static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
203                                              struct device_attribute *attr,
204                                              const char *buf, size_t count)
205 {
206         struct bcm43xx_private *bcm = dev_to_bcm(dev);
207         unsigned long flags;
208         int err;
209         int mode;
210
211         if (!capable(CAP_NET_ADMIN))
212                 return -EPERM;
213
214         mode = get_integer(buf, count);
215         switch (mode) {
216         case 0:
217                 mode = BCM43xx_RADIO_INTERFMODE_NONE;
218                 break;
219         case 1:
220                 mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
221                 break;
222         case 2:
223                 mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
224                 break;
225         case 3:
226                 mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
227                 break;
228         default:
229                 return -EINVAL;
230         }
231
232         bcm43xx_lock_irqsafe(bcm, flags);
233
234         err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
235         if (err) {
236                 printk(KERN_ERR PFX "Interference Mitigation not "
237                                     "supported by device\n");
238         }
239         mmiowb();
240         bcm43xx_unlock_irqsafe(bcm, flags);
241
242         return err ? err : count;
243 }
244
245 static DEVICE_ATTR(interference, 0644,
246                    bcm43xx_attr_interfmode_show,
247                    bcm43xx_attr_interfmode_store);
248
249 static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
250                                           struct device_attribute *attr,
251                                           char *buf)
252 {
253         struct bcm43xx_private *bcm = dev_to_bcm(dev);
254         int err;
255         ssize_t count;
256
257         if (!capable(CAP_NET_ADMIN))
258                 return -EPERM;
259
260         bcm43xx_lock_noirq(bcm);
261
262         if (bcm->short_preamble)
263                 count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");
264         else
265                 count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");
266
267         err = 0;
268         bcm43xx_unlock_noirq(bcm);
269
270         return err ? err : count;
271 }
272
273 static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
274                                            struct device_attribute *attr,
275                                            const char *buf, size_t count)
276 {
277         struct bcm43xx_private *bcm = dev_to_bcm(dev);
278         unsigned long flags;
279         int err;
280         int value;
281
282         if (!capable(CAP_NET_ADMIN))
283                 return -EPERM;
284
285         value = get_boolean(buf, count);
286         if (value < 0)
287                 return value;
288         bcm43xx_lock_irqsafe(bcm, flags);
289
290         bcm->short_preamble = !!value;
291
292         err = 0;
293         bcm43xx_unlock_irqsafe(bcm, flags);
294
295         return err ? err : count;
296 }
297
298 static DEVICE_ATTR(shortpreamble, 0644,
299                    bcm43xx_attr_preamble_show,
300                    bcm43xx_attr_preamble_store);
301
302 int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
303 {
304         struct device *dev = &bcm->pci_dev->dev;
305         int err;
306
307         assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
308
309         err = device_create_file(dev, &dev_attr_sprom);
310         if (err)
311                 goto out;
312         err = device_create_file(dev, &dev_attr_interference);
313         if (err)
314                 goto err_remove_sprom;
315         err = device_create_file(dev, &dev_attr_shortpreamble);
316         if (err)
317                 goto err_remove_interfmode;
318
319 out:
320         return err;
321 err_remove_interfmode:
322         device_remove_file(dev, &dev_attr_interference);
323 err_remove_sprom:
324         device_remove_file(dev, &dev_attr_sprom);
325         goto out;
326 }
327
328 void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
329 {
330         struct device *dev = &bcm->pci_dev->dev;
331
332         device_remove_file(dev, &dev_attr_shortpreamble);
333         device_remove_file(dev, &dev_attr_interference);
334         device_remove_file(dev, &dev_attr_sprom);
335 }