diff options
Diffstat (limited to 'drivers/uwb/i1480/i1480u-wlp/sysfs.c')
-rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/sysfs.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c new file mode 100644 index 000000000000..a1d8ca6ac935 --- /dev/null +++ b/drivers/uwb/i1480/i1480u-wlp/sysfs.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /* | ||
2 | * WUSB Wire Adapter: WLP interface | ||
3 | * Sysfs interfaces | ||
4 | * | ||
5 | * Copyright (C) 2005-2006 Intel Corporation | ||
6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | * | ||
22 | * | ||
23 | * FIXME: docs | ||
24 | */ | ||
25 | |||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/uwb/debug.h> | ||
29 | #include <linux/device.h> | ||
30 | #include "i1480u-wlp.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * | ||
35 | * @dev: Class device from the net_device; assumed refcnted. | ||
36 | * | ||
37 | * Yes, I don't lock--we assume it is refcounted and I am getting a | ||
38 | * single byte value that is kind of atomic to read. | ||
39 | */ | ||
40 | ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf) | ||
41 | { | ||
42 | return sprintf(buf, "%u\n", | ||
43 | wlp_tx_hdr_phy_rate(&options->def_tx_hdr)); | ||
44 | } | ||
45 | EXPORT_SYMBOL_GPL(uwb_phy_rate_show); | ||
46 | |||
47 | |||
48 | ssize_t uwb_phy_rate_store(struct wlp_options *options, | ||
49 | const char *buf, size_t size) | ||
50 | { | ||
51 | ssize_t result; | ||
52 | unsigned rate; | ||
53 | |||
54 | result = sscanf(buf, "%u\n", &rate); | ||
55 | if (result != 1) { | ||
56 | result = -EINVAL; | ||
57 | goto out; | ||
58 | } | ||
59 | result = -EINVAL; | ||
60 | if (rate >= UWB_PHY_RATE_INVALID) | ||
61 | goto out; | ||
62 | wlp_tx_hdr_set_phy_rate(&options->def_tx_hdr, rate); | ||
63 | result = 0; | ||
64 | out: | ||
65 | return result < 0 ? result : size; | ||
66 | } | ||
67 | EXPORT_SYMBOL_GPL(uwb_phy_rate_store); | ||
68 | |||
69 | |||
70 | ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf) | ||
71 | { | ||
72 | return sprintf(buf, "%u\n", | ||
73 | wlp_tx_hdr_rts_cts(&options->def_tx_hdr)); | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(uwb_rts_cts_show); | ||
76 | |||
77 | |||
78 | ssize_t uwb_rts_cts_store(struct wlp_options *options, | ||
79 | const char *buf, size_t size) | ||
80 | { | ||
81 | ssize_t result; | ||
82 | unsigned value; | ||
83 | |||
84 | result = sscanf(buf, "%u\n", &value); | ||
85 | if (result != 1) { | ||
86 | result = -EINVAL; | ||
87 | goto out; | ||
88 | } | ||
89 | result = -EINVAL; | ||
90 | wlp_tx_hdr_set_rts_cts(&options->def_tx_hdr, !!value); | ||
91 | result = 0; | ||
92 | out: | ||
93 | return result < 0 ? result : size; | ||
94 | } | ||
95 | EXPORT_SYMBOL_GPL(uwb_rts_cts_store); | ||
96 | |||
97 | |||
98 | ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf) | ||
99 | { | ||
100 | return sprintf(buf, "%u\n", | ||
101 | wlp_tx_hdr_ack_policy(&options->def_tx_hdr)); | ||
102 | } | ||
103 | EXPORT_SYMBOL_GPL(uwb_ack_policy_show); | ||
104 | |||
105 | |||
106 | ssize_t uwb_ack_policy_store(struct wlp_options *options, | ||
107 | const char *buf, size_t size) | ||
108 | { | ||
109 | ssize_t result; | ||
110 | unsigned value; | ||
111 | |||
112 | result = sscanf(buf, "%u\n", &value); | ||
113 | if (result != 1 || value > UWB_ACK_B_REQ) { | ||
114 | result = -EINVAL; | ||
115 | goto out; | ||
116 | } | ||
117 | wlp_tx_hdr_set_ack_policy(&options->def_tx_hdr, value); | ||
118 | result = 0; | ||
119 | out: | ||
120 | return result < 0 ? result : size; | ||
121 | } | ||
122 | EXPORT_SYMBOL_GPL(uwb_ack_policy_store); | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Show the PCA base priority. | ||
127 | * | ||
128 | * We can access without locking, as the value is (for now) orthogonal | ||
129 | * to other values. | ||
130 | */ | ||
131 | ssize_t uwb_pca_base_priority_show(const struct wlp_options *options, | ||
132 | char *buf) | ||
133 | { | ||
134 | return sprintf(buf, "%u\n", | ||
135 | options->pca_base_priority); | ||
136 | } | ||
137 | EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show); | ||
138 | |||
139 | |||
140 | /** | ||
141 | * Set the PCA base priority. | ||
142 | * | ||
143 | * We can access without locking, as the value is (for now) orthogonal | ||
144 | * to other values. | ||
145 | */ | ||
146 | ssize_t uwb_pca_base_priority_store(struct wlp_options *options, | ||
147 | const char *buf, size_t size) | ||
148 | { | ||
149 | ssize_t result = -EINVAL; | ||
150 | u8 pca_base_priority; | ||
151 | |||
152 | result = sscanf(buf, "%hhu\n", &pca_base_priority); | ||
153 | if (result != 1) { | ||
154 | result = -EINVAL; | ||
155 | goto out; | ||
156 | } | ||
157 | result = -EINVAL; | ||
158 | if (pca_base_priority >= 8) | ||
159 | goto out; | ||
160 | options->pca_base_priority = pca_base_priority; | ||
161 | /* Update TX header if we are currently using PCA. */ | ||
162 | if (result >= 0 && (wlp_tx_hdr_delivery_id_type(&options->def_tx_hdr) & WLP_DRP) == 0) | ||
163 | wlp_tx_hdr_set_delivery_id_type(&options->def_tx_hdr, options->pca_base_priority); | ||
164 | result = 0; | ||
165 | out: | ||
166 | return result < 0 ? result : size; | ||
167 | } | ||
168 | EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store); | ||
169 | |||
170 | /** | ||
171 | * Show current inflight values | ||
172 | * | ||
173 | * Will print the current MAX and THRESHOLD values for the basic flow | ||
174 | * control. In addition it will report how many times the TX queue needed | ||
175 | * to be restarted since the last time this query was made. | ||
176 | */ | ||
177 | static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight, | ||
178 | char *buf) | ||
179 | { | ||
180 | ssize_t result; | ||
181 | unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ; | ||
182 | unsigned long restart_count = atomic_read(&inflight->restart_count); | ||
183 | |||
184 | result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n" | ||
185 | "#read: threshold max inflight_count restarts " | ||
186 | "seconds restarts/sec\n" | ||
187 | "#write: threshold max\n", | ||
188 | inflight->threshold, inflight->max, | ||
189 | atomic_read(&inflight->count), | ||
190 | restart_count, sec_elapsed, | ||
191 | sec_elapsed == 0 ? 0 : restart_count/sec_elapsed); | ||
192 | inflight->restart_ts = jiffies; | ||
193 | atomic_set(&inflight->restart_count, 0); | ||
194 | return result; | ||
195 | } | ||
196 | |||
197 | static | ||
198 | ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight, | ||
199 | const char *buf, size_t size) | ||
200 | { | ||
201 | unsigned long in_threshold, in_max; | ||
202 | ssize_t result; | ||
203 | result = sscanf(buf, "%lu %lu", &in_threshold, &in_max); | ||
204 | if (result != 2) | ||
205 | return -EINVAL; | ||
206 | if (in_max <= in_threshold) | ||
207 | return -EINVAL; | ||
208 | inflight->max = in_max; | ||
209 | inflight->threshold = in_threshold; | ||
210 | return size; | ||
211 | } | ||
212 | /* | ||
213 | * Glue (or function adaptors) for accesing info on sysfs | ||
214 | * | ||
215 | * [we need this indirection because the PCI driver does almost the | ||
216 | * same] | ||
217 | * | ||
218 | * Linux 2.6.21 changed how 'struct netdevice' does attributes (from | ||
219 | * having a 'struct class_dev' to having a 'struct device'). That is | ||
220 | * quite of a pain. | ||
221 | * | ||
222 | * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE() | ||
223 | * create adaptors for extracting the 'struct i1480u' from a 'struct | ||
224 | * dev' and calling a function for doing a sysfs operation (as we have | ||
225 | * them factorized already). i1480u_ATTR creates the attribute file | ||
226 | * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a | ||
227 | * class_device_attr_NAME or device_attr_NAME (for group registration). | ||
228 | */ | ||
229 | #include <linux/version.h> | ||
230 | |||
231 | #define i1480u_SHOW(name, fn, param) \ | ||
232 | static ssize_t i1480u_show_##name(struct device *dev, \ | ||
233 | struct device_attribute *attr,\ | ||
234 | char *buf) \ | ||
235 | { \ | ||
236 | struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ | ||
237 | return fn(&i1480u->param, buf); \ | ||
238 | } | ||
239 | |||
240 | #define i1480u_STORE(name, fn, param) \ | ||
241 | static ssize_t i1480u_store_##name(struct device *dev, \ | ||
242 | struct device_attribute *attr,\ | ||
243 | const char *buf, size_t size)\ | ||
244 | { \ | ||
245 | struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \ | ||
246 | return fn(&i1480u->param, buf, size); \ | ||
247 | } | ||
248 | |||
249 | #define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \ | ||
250 | i1480u_show_##name,\ | ||
251 | i1480u_store_##name) | ||
252 | |||
253 | #define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \ | ||
254 | S_IRUGO, \ | ||
255 | i1480u_show_##name, NULL) | ||
256 | |||
257 | #define i1480u_ATTR_NAME(a) (dev_attr_##a) | ||
258 | |||
259 | |||
260 | /* | ||
261 | * Sysfs adaptors | ||
262 | */ | ||
263 | i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options); | ||
264 | i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options); | ||
265 | i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR); | ||
266 | |||
267 | i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options); | ||
268 | i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options); | ||
269 | i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR); | ||
270 | |||
271 | i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options); | ||
272 | i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options); | ||
273 | i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR); | ||
274 | |||
275 | i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options); | ||
276 | i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options); | ||
277 | i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR); | ||
278 | |||
279 | i1480u_SHOW(wlp_eda, wlp_eda_show, wlp); | ||
280 | i1480u_STORE(wlp_eda, wlp_eda_store, wlp); | ||
281 | i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR); | ||
282 | |||
283 | i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp); | ||
284 | i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp); | ||
285 | i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR); | ||
286 | |||
287 | i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp); | ||
288 | i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp); | ||
289 | i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR); | ||
290 | |||
291 | i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp); | ||
292 | i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp); | ||
293 | i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR); | ||
294 | |||
295 | i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp); | ||
296 | i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp); | ||
297 | i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR); | ||
298 | |||
299 | i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp); | ||
300 | i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp); | ||
301 | i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR); | ||
302 | |||
303 | i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp); | ||
304 | i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp); | ||
305 | i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR); | ||
306 | |||
307 | i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp); | ||
308 | i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp); | ||
309 | i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR); | ||
310 | |||
311 | i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp); | ||
312 | i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp); | ||
313 | i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR); | ||
314 | |||
315 | i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp); | ||
316 | i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp); | ||
317 | i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR); | ||
318 | |||
319 | i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp); | ||
320 | i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp); | ||
321 | i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR); | ||
322 | |||
323 | i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp); | ||
324 | i1480u_ATTR_SHOW(wlp_neighborhood); | ||
325 | |||
326 | i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss); | ||
327 | i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss); | ||
328 | i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR); | ||
329 | |||
330 | /* | ||
331 | * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over | ||
332 | * the last 256 received WLP frames (ECMA-368 13.3). | ||
333 | * | ||
334 | * [the -7dB that have to be substracted from the LQI to make the LQE | ||
335 | * are already taken into account]. | ||
336 | */ | ||
337 | i1480u_SHOW(wlp_lqe, stats_show, lqe_stats); | ||
338 | i1480u_STORE(wlp_lqe, stats_store, lqe_stats); | ||
339 | i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR); | ||
340 | |||
341 | /* | ||
342 | * Show the Receive Signal Strength Indicator averaged over all the | ||
343 | * received WLP frames (ECMA-368 13.3). Still is not clear what | ||
344 | * this value is, but is kind of a percentage of the signal strength | ||
345 | * at the antenna. | ||
346 | */ | ||
347 | i1480u_SHOW(wlp_rssi, stats_show, rssi_stats); | ||
348 | i1480u_STORE(wlp_rssi, stats_store, rssi_stats); | ||
349 | i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR); | ||
350 | |||
351 | /** | ||
352 | * We maintain a basic flow control counter. "count" how many TX URBs are | ||
353 | * outstanding. Only allow "max" | ||
354 | * TX URBs to be outstanding. If this value is reached the queue will be | ||
355 | * stopped. The queue will be restarted when there are | ||
356 | * "threshold" URBs outstanding. | ||
357 | */ | ||
358 | i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight); | ||
359 | i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight); | ||
360 | i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR); | ||
361 | |||
362 | static struct attribute *i1480u_attrs[] = { | ||
363 | &i1480u_ATTR_NAME(uwb_phy_rate).attr, | ||
364 | &i1480u_ATTR_NAME(uwb_rts_cts).attr, | ||
365 | &i1480u_ATTR_NAME(uwb_ack_policy).attr, | ||
366 | &i1480u_ATTR_NAME(uwb_pca_base_priority).attr, | ||
367 | &i1480u_ATTR_NAME(wlp_lqe).attr, | ||
368 | &i1480u_ATTR_NAME(wlp_rssi).attr, | ||
369 | &i1480u_ATTR_NAME(wlp_eda).attr, | ||
370 | &i1480u_ATTR_NAME(wlp_uuid).attr, | ||
371 | &i1480u_ATTR_NAME(wlp_dev_name).attr, | ||
372 | &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr, | ||
373 | &i1480u_ATTR_NAME(wlp_dev_model_name).attr, | ||
374 | &i1480u_ATTR_NAME(wlp_dev_model_nr).attr, | ||
375 | &i1480u_ATTR_NAME(wlp_dev_serial).attr, | ||
376 | &i1480u_ATTR_NAME(wlp_dev_prim_category).attr, | ||
377 | &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr, | ||
378 | &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr, | ||
379 | &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr, | ||
380 | &i1480u_ATTR_NAME(wlp_neighborhood).attr, | ||
381 | &i1480u_ATTR_NAME(wss_activate).attr, | ||
382 | &i1480u_ATTR_NAME(wlp_tx_inflight).attr, | ||
383 | NULL, | ||
384 | }; | ||
385 | |||
386 | static struct attribute_group i1480u_attr_group = { | ||
387 | .name = NULL, /* we want them in the same directory */ | ||
388 | .attrs = i1480u_attrs, | ||
389 | }; | ||
390 | |||
391 | int i1480u_sysfs_setup(struct i1480u *i1480u) | ||
392 | { | ||
393 | int result; | ||
394 | struct device *dev = &i1480u->usb_iface->dev; | ||
395 | result = sysfs_create_group(&i1480u->net_dev->dev.kobj, | ||
396 | &i1480u_attr_group); | ||
397 | if (result < 0) | ||
398 | dev_err(dev, "cannot initialize sysfs attributes: %d\n", | ||
399 | result); | ||
400 | return result; | ||
401 | } | ||
402 | |||
403 | |||
404 | void i1480u_sysfs_release(struct i1480u *i1480u) | ||
405 | { | ||
406 | sysfs_remove_group(&i1480u->net_dev->dev.kobj, | ||
407 | &i1480u_attr_group); | ||
408 | } | ||