diff options
Diffstat (limited to 'drivers/s390/net/qeth_sys.c')
-rw-r--r-- | drivers/s390/net/qeth_sys.c | 1788 |
1 files changed, 1788 insertions, 0 deletions
diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c new file mode 100644 index 000000000000..240348398211 --- /dev/null +++ b/drivers/s390/net/qeth_sys.c | |||
@@ -0,0 +1,1788 @@ | |||
1 | /* | ||
2 | * | ||
3 | * linux/drivers/s390/net/qeth_sys.c ($Revision: 1.51 $) | ||
4 | * | ||
5 | * Linux on zSeries OSA Express and HiperSockets support | ||
6 | * This file contains code related to sysfs. | ||
7 | * | ||
8 | * Copyright 2000,2003 IBM Corporation | ||
9 | * | ||
10 | * Author(s): Thomas Spatzier <tspat@de.ibm.com> | ||
11 | * Frank Pavlic <pavlic@de.ibm.com> | ||
12 | * | ||
13 | */ | ||
14 | #include <linux/list.h> | ||
15 | #include <linux/rwsem.h> | ||
16 | |||
17 | #include <asm/ebcdic.h> | ||
18 | |||
19 | #include "qeth.h" | ||
20 | #include "qeth_mpc.h" | ||
21 | #include "qeth_fs.h" | ||
22 | |||
23 | const char *VERSION_QETH_SYS_C = "$Revision: 1.51 $"; | ||
24 | |||
25 | /*****************************************************************************/ | ||
26 | /* */ | ||
27 | /* /sys-fs stuff UNDER DEVELOPMENT !!! */ | ||
28 | /* */ | ||
29 | /*****************************************************************************/ | ||
30 | //low/high watermark | ||
31 | |||
32 | static ssize_t | ||
33 | qeth_dev_state_show(struct device *dev, char *buf) | ||
34 | { | ||
35 | struct qeth_card *card = dev->driver_data; | ||
36 | if (!card) | ||
37 | return -EINVAL; | ||
38 | |||
39 | switch (card->state) { | ||
40 | case CARD_STATE_DOWN: | ||
41 | return sprintf(buf, "DOWN\n"); | ||
42 | case CARD_STATE_HARDSETUP: | ||
43 | return sprintf(buf, "HARDSETUP\n"); | ||
44 | case CARD_STATE_SOFTSETUP: | ||
45 | return sprintf(buf, "SOFTSETUP\n"); | ||
46 | case CARD_STATE_UP: | ||
47 | if (card->lan_online) | ||
48 | return sprintf(buf, "UP (LAN ONLINE)\n"); | ||
49 | else | ||
50 | return sprintf(buf, "UP (LAN OFFLINE)\n"); | ||
51 | case CARD_STATE_RECOVER: | ||
52 | return sprintf(buf, "RECOVER\n"); | ||
53 | default: | ||
54 | return sprintf(buf, "UNKNOWN\n"); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static DEVICE_ATTR(state, 0444, qeth_dev_state_show, NULL); | ||
59 | |||
60 | static ssize_t | ||
61 | qeth_dev_chpid_show(struct device *dev, char *buf) | ||
62 | { | ||
63 | struct qeth_card *card = dev->driver_data; | ||
64 | if (!card) | ||
65 | return -EINVAL; | ||
66 | |||
67 | return sprintf(buf, "%02X\n", card->info.chpid); | ||
68 | } | ||
69 | |||
70 | static DEVICE_ATTR(chpid, 0444, qeth_dev_chpid_show, NULL); | ||
71 | |||
72 | static ssize_t | ||
73 | qeth_dev_if_name_show(struct device *dev, char *buf) | ||
74 | { | ||
75 | struct qeth_card *card = dev->driver_data; | ||
76 | if (!card) | ||
77 | return -EINVAL; | ||
78 | return sprintf(buf, "%s\n", QETH_CARD_IFNAME(card)); | ||
79 | } | ||
80 | |||
81 | static DEVICE_ATTR(if_name, 0444, qeth_dev_if_name_show, NULL); | ||
82 | |||
83 | static ssize_t | ||
84 | qeth_dev_card_type_show(struct device *dev, char *buf) | ||
85 | { | ||
86 | struct qeth_card *card = dev->driver_data; | ||
87 | if (!card) | ||
88 | return -EINVAL; | ||
89 | |||
90 | return sprintf(buf, "%s\n", qeth_get_cardname_short(card)); | ||
91 | } | ||
92 | |||
93 | static DEVICE_ATTR(card_type, 0444, qeth_dev_card_type_show, NULL); | ||
94 | |||
95 | static ssize_t | ||
96 | qeth_dev_portno_show(struct device *dev, char *buf) | ||
97 | { | ||
98 | struct qeth_card *card = dev->driver_data; | ||
99 | if (!card) | ||
100 | return -EINVAL; | ||
101 | |||
102 | return sprintf(buf, "%i\n", card->info.portno); | ||
103 | } | ||
104 | |||
105 | static ssize_t | ||
106 | qeth_dev_portno_store(struct device *dev, const char *buf, size_t count) | ||
107 | { | ||
108 | struct qeth_card *card = dev->driver_data; | ||
109 | char *tmp; | ||
110 | unsigned int portno; | ||
111 | |||
112 | if (!card) | ||
113 | return -EINVAL; | ||
114 | |||
115 | if ((card->state != CARD_STATE_DOWN) && | ||
116 | (card->state != CARD_STATE_RECOVER)) | ||
117 | return -EPERM; | ||
118 | |||
119 | portno = simple_strtoul(buf, &tmp, 16); | ||
120 | if ((portno < 0) || (portno > MAX_PORTNO)){ | ||
121 | PRINT_WARN("portno 0x%X is out of range\n", portno); | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | |||
125 | card->info.portno = portno; | ||
126 | return count; | ||
127 | } | ||
128 | |||
129 | static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store); | ||
130 | |||
131 | static ssize_t | ||
132 | qeth_dev_portname_show(struct device *dev, char *buf) | ||
133 | { | ||
134 | struct qeth_card *card = dev->driver_data; | ||
135 | char portname[9] = {0, }; | ||
136 | |||
137 | if (!card) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (card->info.portname_required) { | ||
141 | memcpy(portname, card->info.portname + 1, 8); | ||
142 | EBCASC(portname, 8); | ||
143 | return sprintf(buf, "%s\n", portname); | ||
144 | } else | ||
145 | return sprintf(buf, "no portname required\n"); | ||
146 | } | ||
147 | |||
148 | static ssize_t | ||
149 | qeth_dev_portname_store(struct device *dev, const char *buf, size_t count) | ||
150 | { | ||
151 | struct qeth_card *card = dev->driver_data; | ||
152 | char *tmp; | ||
153 | int i; | ||
154 | |||
155 | if (!card) | ||
156 | return -EINVAL; | ||
157 | |||
158 | if ((card->state != CARD_STATE_DOWN) && | ||
159 | (card->state != CARD_STATE_RECOVER)) | ||
160 | return -EPERM; | ||
161 | |||
162 | tmp = strsep((char **) &buf, "\n"); | ||
163 | if ((strlen(tmp) > 8) || (strlen(tmp) < 2)) | ||
164 | return -EINVAL; | ||
165 | |||
166 | card->info.portname[0] = strlen(tmp); | ||
167 | /* for beauty reasons */ | ||
168 | for (i = 1; i < 9; i++) | ||
169 | card->info.portname[i] = ' '; | ||
170 | strcpy(card->info.portname + 1, tmp); | ||
171 | ASCEBC(card->info.portname + 1, 8); | ||
172 | |||
173 | return count; | ||
174 | } | ||
175 | |||
176 | static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show, | ||
177 | qeth_dev_portname_store); | ||
178 | |||
179 | static ssize_t | ||
180 | qeth_dev_checksum_show(struct device *dev, char *buf) | ||
181 | { | ||
182 | struct qeth_card *card = dev->driver_data; | ||
183 | |||
184 | if (!card) | ||
185 | return -EINVAL; | ||
186 | |||
187 | return sprintf(buf, "%s checksumming\n", qeth_get_checksum_str(card)); | ||
188 | } | ||
189 | |||
190 | static ssize_t | ||
191 | qeth_dev_checksum_store(struct device *dev, const char *buf, size_t count) | ||
192 | { | ||
193 | struct qeth_card *card = dev->driver_data; | ||
194 | char *tmp; | ||
195 | |||
196 | if (!card) | ||
197 | return -EINVAL; | ||
198 | |||
199 | if ((card->state != CARD_STATE_DOWN) && | ||
200 | (card->state != CARD_STATE_RECOVER)) | ||
201 | return -EPERM; | ||
202 | |||
203 | tmp = strsep((char **) &buf, "\n"); | ||
204 | if (!strcmp(tmp, "sw_checksumming")) | ||
205 | card->options.checksum_type = SW_CHECKSUMMING; | ||
206 | else if (!strcmp(tmp, "hw_checksumming")) | ||
207 | card->options.checksum_type = HW_CHECKSUMMING; | ||
208 | else if (!strcmp(tmp, "no_checksumming")) | ||
209 | card->options.checksum_type = NO_CHECKSUMMING; | ||
210 | else { | ||
211 | PRINT_WARN("Unknown checksumming type '%s'\n", tmp); | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | return count; | ||
215 | } | ||
216 | |||
217 | static DEVICE_ATTR(checksumming, 0644, qeth_dev_checksum_show, | ||
218 | qeth_dev_checksum_store); | ||
219 | |||
220 | static ssize_t | ||
221 | qeth_dev_prioqing_show(struct device *dev, char *buf) | ||
222 | { | ||
223 | struct qeth_card *card = dev->driver_data; | ||
224 | |||
225 | if (!card) | ||
226 | return -EINVAL; | ||
227 | |||
228 | switch (card->qdio.do_prio_queueing) { | ||
229 | case QETH_PRIO_Q_ING_PREC: | ||
230 | return sprintf(buf, "%s\n", "by precedence"); | ||
231 | case QETH_PRIO_Q_ING_TOS: | ||
232 | return sprintf(buf, "%s\n", "by type of service"); | ||
233 | default: | ||
234 | return sprintf(buf, "always queue %i\n", | ||
235 | card->qdio.default_out_queue); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static ssize_t | ||
240 | qeth_dev_prioqing_store(struct device *dev, const char *buf, size_t count) | ||
241 | { | ||
242 | struct qeth_card *card = dev->driver_data; | ||
243 | char *tmp; | ||
244 | |||
245 | if (!card) | ||
246 | return -EINVAL; | ||
247 | |||
248 | if ((card->state != CARD_STATE_DOWN) && | ||
249 | (card->state != CARD_STATE_RECOVER)) | ||
250 | return -EPERM; | ||
251 | |||
252 | /* check if 1920 devices are supported , | ||
253 | * if though we have to permit priority queueing | ||
254 | */ | ||
255 | if (card->qdio.no_out_queues == 1) { | ||
256 | PRINT_WARN("Priority queueing disabled due " | ||
257 | "to hardware limitations!\n"); | ||
258 | card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT; | ||
259 | return -EPERM; | ||
260 | } | ||
261 | |||
262 | tmp = strsep((char **) &buf, "\n"); | ||
263 | if (!strcmp(tmp, "prio_queueing_prec")) | ||
264 | card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC; | ||
265 | else if (!strcmp(tmp, "prio_queueing_tos")) | ||
266 | card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS; | ||
267 | else if (!strcmp(tmp, "no_prio_queueing:0")) { | ||
268 | card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; | ||
269 | card->qdio.default_out_queue = 0; | ||
270 | } else if (!strcmp(tmp, "no_prio_queueing:1")) { | ||
271 | card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; | ||
272 | card->qdio.default_out_queue = 1; | ||
273 | } else if (!strcmp(tmp, "no_prio_queueing:2")) { | ||
274 | card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; | ||
275 | card->qdio.default_out_queue = 2; | ||
276 | } else if (!strcmp(tmp, "no_prio_queueing:3")) { | ||
277 | card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; | ||
278 | card->qdio.default_out_queue = 3; | ||
279 | } else if (!strcmp(tmp, "no_prio_queueing")) { | ||
280 | card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; | ||
281 | card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; | ||
282 | } else { | ||
283 | PRINT_WARN("Unknown queueing type '%s'\n", tmp); | ||
284 | return -EINVAL; | ||
285 | } | ||
286 | return count; | ||
287 | } | ||
288 | |||
289 | static DEVICE_ATTR(priority_queueing, 0644, qeth_dev_prioqing_show, | ||
290 | qeth_dev_prioqing_store); | ||
291 | |||
292 | static ssize_t | ||
293 | qeth_dev_bufcnt_show(struct device *dev, char *buf) | ||
294 | { | ||
295 | struct qeth_card *card = dev->driver_data; | ||
296 | |||
297 | if (!card) | ||
298 | return -EINVAL; | ||
299 | |||
300 | return sprintf(buf, "%i\n", card->qdio.in_buf_pool.buf_count); | ||
301 | } | ||
302 | |||
303 | static ssize_t | ||
304 | qeth_dev_bufcnt_store(struct device *dev, const char *buf, size_t count) | ||
305 | { | ||
306 | struct qeth_card *card = dev->driver_data; | ||
307 | char *tmp; | ||
308 | int cnt, old_cnt; | ||
309 | int rc; | ||
310 | |||
311 | if (!card) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if ((card->state != CARD_STATE_DOWN) && | ||
315 | (card->state != CARD_STATE_RECOVER)) | ||
316 | return -EPERM; | ||
317 | |||
318 | old_cnt = card->qdio.in_buf_pool.buf_count; | ||
319 | cnt = simple_strtoul(buf, &tmp, 10); | ||
320 | cnt = (cnt < QETH_IN_BUF_COUNT_MIN) ? QETH_IN_BUF_COUNT_MIN : | ||
321 | ((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt); | ||
322 | if (old_cnt != cnt) { | ||
323 | if ((rc = qeth_realloc_buffer_pool(card, cnt))) | ||
324 | PRINT_WARN("Error (%d) while setting " | ||
325 | "buffer count.\n", rc); | ||
326 | } | ||
327 | return count; | ||
328 | } | ||
329 | |||
330 | static DEVICE_ATTR(buffer_count, 0644, qeth_dev_bufcnt_show, | ||
331 | qeth_dev_bufcnt_store); | ||
332 | |||
333 | static inline ssize_t | ||
334 | qeth_dev_route_show(struct qeth_card *card, struct qeth_routing_info *route, | ||
335 | char *buf) | ||
336 | { | ||
337 | switch (route->type) { | ||
338 | case PRIMARY_ROUTER: | ||
339 | return sprintf(buf, "%s\n", "primary router"); | ||
340 | case SECONDARY_ROUTER: | ||
341 | return sprintf(buf, "%s\n", "secondary router"); | ||
342 | case MULTICAST_ROUTER: | ||
343 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | ||
344 | return sprintf(buf, "%s\n", "multicast router+"); | ||
345 | else | ||
346 | return sprintf(buf, "%s\n", "multicast router"); | ||
347 | case PRIMARY_CONNECTOR: | ||
348 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | ||
349 | return sprintf(buf, "%s\n", "primary connector+"); | ||
350 | else | ||
351 | return sprintf(buf, "%s\n", "primary connector"); | ||
352 | case SECONDARY_CONNECTOR: | ||
353 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | ||
354 | return sprintf(buf, "%s\n", "secondary connector+"); | ||
355 | else | ||
356 | return sprintf(buf, "%s\n", "secondary connector"); | ||
357 | default: | ||
358 | return sprintf(buf, "%s\n", "no"); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | static ssize_t | ||
363 | qeth_dev_route4_show(struct device *dev, char *buf) | ||
364 | { | ||
365 | struct qeth_card *card = dev->driver_data; | ||
366 | |||
367 | if (!card) | ||
368 | return -EINVAL; | ||
369 | |||
370 | return qeth_dev_route_show(card, &card->options.route4, buf); | ||
371 | } | ||
372 | |||
373 | static inline ssize_t | ||
374 | qeth_dev_route_store(struct qeth_card *card, struct qeth_routing_info *route, | ||
375 | enum qeth_prot_versions prot, const char *buf, size_t count) | ||
376 | { | ||
377 | enum qeth_routing_types old_route_type = route->type; | ||
378 | char *tmp; | ||
379 | int rc; | ||
380 | |||
381 | tmp = strsep((char **) &buf, "\n"); | ||
382 | |||
383 | if (!strcmp(tmp, "no_router")){ | ||
384 | route->type = NO_ROUTER; | ||
385 | } else if (!strcmp(tmp, "primary_connector")) { | ||
386 | route->type = PRIMARY_CONNECTOR; | ||
387 | } else if (!strcmp(tmp, "secondary_connector")) { | ||
388 | route->type = SECONDARY_CONNECTOR; | ||
389 | } else if (!strcmp(tmp, "multicast_router")) { | ||
390 | route->type = MULTICAST_ROUTER; | ||
391 | } else if (!strcmp(tmp, "primary_router")) { | ||
392 | route->type = PRIMARY_ROUTER; | ||
393 | } else if (!strcmp(tmp, "secondary_router")) { | ||
394 | route->type = SECONDARY_ROUTER; | ||
395 | } else if (!strcmp(tmp, "multicast_router")) { | ||
396 | route->type = MULTICAST_ROUTER; | ||
397 | } else { | ||
398 | PRINT_WARN("Invalid routing type '%s'.\n", tmp); | ||
399 | return -EINVAL; | ||
400 | } | ||
401 | if (((card->state == CARD_STATE_SOFTSETUP) || | ||
402 | (card->state == CARD_STATE_UP)) && | ||
403 | (old_route_type != route->type)){ | ||
404 | if (prot == QETH_PROT_IPV4) | ||
405 | rc = qeth_setrouting_v4(card); | ||
406 | else if (prot == QETH_PROT_IPV6) | ||
407 | rc = qeth_setrouting_v6(card); | ||
408 | } | ||
409 | return count; | ||
410 | } | ||
411 | |||
412 | static ssize_t | ||
413 | qeth_dev_route4_store(struct device *dev, const char *buf, size_t count) | ||
414 | { | ||
415 | struct qeth_card *card = dev->driver_data; | ||
416 | |||
417 | if (!card) | ||
418 | return -EINVAL; | ||
419 | |||
420 | return qeth_dev_route_store(card, &card->options.route4, | ||
421 | QETH_PROT_IPV4, buf, count); | ||
422 | } | ||
423 | |||
424 | static DEVICE_ATTR(route4, 0644, qeth_dev_route4_show, qeth_dev_route4_store); | ||
425 | |||
426 | #ifdef CONFIG_QETH_IPV6 | ||
427 | static ssize_t | ||
428 | qeth_dev_route6_show(struct device *dev, char *buf) | ||
429 | { | ||
430 | struct qeth_card *card = dev->driver_data; | ||
431 | |||
432 | if (!card) | ||
433 | return -EINVAL; | ||
434 | |||
435 | if (!qeth_is_supported(card, IPA_IPV6)) | ||
436 | return sprintf(buf, "%s\n", "n/a"); | ||
437 | |||
438 | return qeth_dev_route_show(card, &card->options.route6, buf); | ||
439 | } | ||
440 | |||
441 | static ssize_t | ||
442 | qeth_dev_route6_store(struct device *dev, const char *buf, size_t count) | ||
443 | { | ||
444 | struct qeth_card *card = dev->driver_data; | ||
445 | |||
446 | if (!card) | ||
447 | return -EINVAL; | ||
448 | |||
449 | if (!qeth_is_supported(card, IPA_IPV6)){ | ||
450 | PRINT_WARN("IPv6 not supported for interface %s.\n" | ||
451 | "Routing status no changed.\n", | ||
452 | QETH_CARD_IFNAME(card)); | ||
453 | return -ENOTSUPP; | ||
454 | } | ||
455 | |||
456 | return qeth_dev_route_store(card, &card->options.route6, | ||
457 | QETH_PROT_IPV6, buf, count); | ||
458 | } | ||
459 | |||
460 | static DEVICE_ATTR(route6, 0644, qeth_dev_route6_show, qeth_dev_route6_store); | ||
461 | #endif | ||
462 | |||
463 | static ssize_t | ||
464 | qeth_dev_add_hhlen_show(struct device *dev, char *buf) | ||
465 | { | ||
466 | struct qeth_card *card = dev->driver_data; | ||
467 | |||
468 | if (!card) | ||
469 | return -EINVAL; | ||
470 | |||
471 | return sprintf(buf, "%i\n", card->options.add_hhlen); | ||
472 | } | ||
473 | |||
474 | static ssize_t | ||
475 | qeth_dev_add_hhlen_store(struct device *dev, const char *buf, size_t count) | ||
476 | { | ||
477 | struct qeth_card *card = dev->driver_data; | ||
478 | char *tmp; | ||
479 | int i; | ||
480 | |||
481 | if (!card) | ||
482 | return -EINVAL; | ||
483 | |||
484 | if ((card->state != CARD_STATE_DOWN) && | ||
485 | (card->state != CARD_STATE_RECOVER)) | ||
486 | return -EPERM; | ||
487 | |||
488 | i = simple_strtoul(buf, &tmp, 10); | ||
489 | if ((i < 0) || (i > MAX_ADD_HHLEN)) { | ||
490 | PRINT_WARN("add_hhlen out of range\n"); | ||
491 | return -EINVAL; | ||
492 | } | ||
493 | card->options.add_hhlen = i; | ||
494 | |||
495 | return count; | ||
496 | } | ||
497 | |||
498 | static DEVICE_ATTR(add_hhlen, 0644, qeth_dev_add_hhlen_show, | ||
499 | qeth_dev_add_hhlen_store); | ||
500 | |||
501 | static ssize_t | ||
502 | qeth_dev_fake_ll_show(struct device *dev, char *buf) | ||
503 | { | ||
504 | struct qeth_card *card = dev->driver_data; | ||
505 | |||
506 | if (!card) | ||
507 | return -EINVAL; | ||
508 | |||
509 | return sprintf(buf, "%i\n", card->options.fake_ll? 1:0); | ||
510 | } | ||
511 | |||
512 | static ssize_t | ||
513 | qeth_dev_fake_ll_store(struct device *dev, const char *buf, size_t count) | ||
514 | { | ||
515 | struct qeth_card *card = dev->driver_data; | ||
516 | char *tmp; | ||
517 | int i; | ||
518 | |||
519 | if (!card) | ||
520 | return -EINVAL; | ||
521 | |||
522 | if ((card->state != CARD_STATE_DOWN) && | ||
523 | (card->state != CARD_STATE_RECOVER)) | ||
524 | return -EPERM; | ||
525 | |||
526 | i = simple_strtoul(buf, &tmp, 16); | ||
527 | if ((i != 0) && (i != 1)) { | ||
528 | PRINT_WARN("fake_ll: write 0 or 1 to this file!\n"); | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | card->options.fake_ll = i; | ||
532 | return count; | ||
533 | } | ||
534 | |||
535 | static DEVICE_ATTR(fake_ll, 0644, qeth_dev_fake_ll_show, | ||
536 | qeth_dev_fake_ll_store); | ||
537 | |||
538 | static ssize_t | ||
539 | qeth_dev_fake_broadcast_show(struct device *dev, char *buf) | ||
540 | { | ||
541 | struct qeth_card *card = dev->driver_data; | ||
542 | |||
543 | if (!card) | ||
544 | return -EINVAL; | ||
545 | |||
546 | return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); | ||
547 | } | ||
548 | |||
549 | static ssize_t | ||
550 | qeth_dev_fake_broadcast_store(struct device *dev, const char *buf, size_t count) | ||
551 | { | ||
552 | struct qeth_card *card = dev->driver_data; | ||
553 | char *tmp; | ||
554 | int i; | ||
555 | |||
556 | if (!card) | ||
557 | return -EINVAL; | ||
558 | |||
559 | if ((card->state != CARD_STATE_DOWN) && | ||
560 | (card->state != CARD_STATE_RECOVER)) | ||
561 | return -EPERM; | ||
562 | |||
563 | i = simple_strtoul(buf, &tmp, 16); | ||
564 | if ((i == 0) || (i == 1)) | ||
565 | card->options.fake_broadcast = i; | ||
566 | else { | ||
567 | PRINT_WARN("fake_broadcast: write 0 or 1 to this file!\n"); | ||
568 | return -EINVAL; | ||
569 | } | ||
570 | return count; | ||
571 | } | ||
572 | |||
573 | static DEVICE_ATTR(fake_broadcast, 0644, qeth_dev_fake_broadcast_show, | ||
574 | qeth_dev_fake_broadcast_store); | ||
575 | |||
576 | static ssize_t | ||
577 | qeth_dev_recover_store(struct device *dev, const char *buf, size_t count) | ||
578 | { | ||
579 | struct qeth_card *card = dev->driver_data; | ||
580 | char *tmp; | ||
581 | int i; | ||
582 | |||
583 | if (!card) | ||
584 | return -EINVAL; | ||
585 | |||
586 | if (card->state != CARD_STATE_UP) | ||
587 | return -EPERM; | ||
588 | |||
589 | i = simple_strtoul(buf, &tmp, 16); | ||
590 | if (i == 1) | ||
591 | qeth_schedule_recovery(card); | ||
592 | |||
593 | return count; | ||
594 | } | ||
595 | |||
596 | static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store); | ||
597 | |||
598 | static ssize_t | ||
599 | qeth_dev_broadcast_mode_show(struct device *dev, char *buf) | ||
600 | { | ||
601 | struct qeth_card *card = dev->driver_data; | ||
602 | |||
603 | if (!card) | ||
604 | return -EINVAL; | ||
605 | |||
606 | if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || | ||
607 | (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) | ||
608 | return sprintf(buf, "n/a\n"); | ||
609 | |||
610 | return sprintf(buf, "%s\n", (card->options.broadcast_mode == | ||
611 | QETH_TR_BROADCAST_ALLRINGS)? | ||
612 | "all rings":"local"); | ||
613 | } | ||
614 | |||
615 | static ssize_t | ||
616 | qeth_dev_broadcast_mode_store(struct device *dev, const char *buf, size_t count) | ||
617 | { | ||
618 | struct qeth_card *card = dev->driver_data; | ||
619 | char *tmp; | ||
620 | |||
621 | if (!card) | ||
622 | return -EINVAL; | ||
623 | |||
624 | if ((card->state != CARD_STATE_DOWN) && | ||
625 | (card->state != CARD_STATE_RECOVER)) | ||
626 | return -EPERM; | ||
627 | |||
628 | if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || | ||
629 | (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){ | ||
630 | PRINT_WARN("Device is not a tokenring device!\n"); | ||
631 | return -EINVAL; | ||
632 | } | ||
633 | |||
634 | tmp = strsep((char **) &buf, "\n"); | ||
635 | |||
636 | if (!strcmp(tmp, "local")){ | ||
637 | card->options.broadcast_mode = QETH_TR_BROADCAST_LOCAL; | ||
638 | return count; | ||
639 | } else if (!strcmp(tmp, "all_rings")) { | ||
640 | card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS; | ||
641 | return count; | ||
642 | } else { | ||
643 | PRINT_WARN("broadcast_mode: invalid mode %s!\n", | ||
644 | tmp); | ||
645 | return -EINVAL; | ||
646 | } | ||
647 | return count; | ||
648 | } | ||
649 | |||
650 | static DEVICE_ATTR(broadcast_mode, 0644, qeth_dev_broadcast_mode_show, | ||
651 | qeth_dev_broadcast_mode_store); | ||
652 | |||
653 | static ssize_t | ||
654 | qeth_dev_canonical_macaddr_show(struct device *dev, char *buf) | ||
655 | { | ||
656 | struct qeth_card *card = dev->driver_data; | ||
657 | |||
658 | if (!card) | ||
659 | return -EINVAL; | ||
660 | |||
661 | if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || | ||
662 | (card->info.link_type == QETH_LINK_TYPE_LANE_TR))) | ||
663 | return sprintf(buf, "n/a\n"); | ||
664 | |||
665 | return sprintf(buf, "%i\n", (card->options.macaddr_mode == | ||
666 | QETH_TR_MACADDR_CANONICAL)? 1:0); | ||
667 | } | ||
668 | |||
669 | static ssize_t | ||
670 | qeth_dev_canonical_macaddr_store(struct device *dev, const char *buf, | ||
671 | size_t count) | ||
672 | { | ||
673 | struct qeth_card *card = dev->driver_data; | ||
674 | char *tmp; | ||
675 | int i; | ||
676 | |||
677 | if (!card) | ||
678 | return -EINVAL; | ||
679 | |||
680 | if ((card->state != CARD_STATE_DOWN) && | ||
681 | (card->state != CARD_STATE_RECOVER)) | ||
682 | return -EPERM; | ||
683 | |||
684 | if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) || | ||
685 | (card->info.link_type == QETH_LINK_TYPE_LANE_TR))){ | ||
686 | PRINT_WARN("Device is not a tokenring device!\n"); | ||
687 | return -EINVAL; | ||
688 | } | ||
689 | |||
690 | i = simple_strtoul(buf, &tmp, 16); | ||
691 | if ((i == 0) || (i == 1)) | ||
692 | card->options.macaddr_mode = i? | ||
693 | QETH_TR_MACADDR_CANONICAL : | ||
694 | QETH_TR_MACADDR_NONCANONICAL; | ||
695 | else { | ||
696 | PRINT_WARN("canonical_macaddr: write 0 or 1 to this file!\n"); | ||
697 | return -EINVAL; | ||
698 | } | ||
699 | return count; | ||
700 | } | ||
701 | |||
702 | static DEVICE_ATTR(canonical_macaddr, 0644, qeth_dev_canonical_macaddr_show, | ||
703 | qeth_dev_canonical_macaddr_store); | ||
704 | |||
705 | static ssize_t | ||
706 | qeth_dev_layer2_show(struct device *dev, char *buf) | ||
707 | { | ||
708 | struct qeth_card *card = dev->driver_data; | ||
709 | |||
710 | if (!card) | ||
711 | return -EINVAL; | ||
712 | |||
713 | return sprintf(buf, "%i\n", card->options.layer2 ? 1:0); | ||
714 | } | ||
715 | |||
716 | static ssize_t | ||
717 | qeth_dev_layer2_store(struct device *dev, const char *buf, size_t count) | ||
718 | { | ||
719 | struct qeth_card *card = dev->driver_data; | ||
720 | char *tmp; | ||
721 | int i; | ||
722 | |||
723 | if (!card) | ||
724 | return -EINVAL; | ||
725 | |||
726 | if (((card->state != CARD_STATE_DOWN) && | ||
727 | (card->state != CARD_STATE_RECOVER)) || | ||
728 | (card->info.type != QETH_CARD_TYPE_OSAE)) | ||
729 | return -EPERM; | ||
730 | |||
731 | i = simple_strtoul(buf, &tmp, 16); | ||
732 | if ((i == 0) || (i == 1)) | ||
733 | card->options.layer2 = i; | ||
734 | else { | ||
735 | PRINT_WARN("layer2: write 0 or 1 to this file!\n"); | ||
736 | return -EINVAL; | ||
737 | } | ||
738 | return count; | ||
739 | } | ||
740 | |||
741 | static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show, | ||
742 | qeth_dev_layer2_store); | ||
743 | |||
744 | static ssize_t | ||
745 | qeth_dev_large_send_show(struct device *dev, char *buf) | ||
746 | { | ||
747 | struct qeth_card *card = dev->driver_data; | ||
748 | |||
749 | if (!card) | ||
750 | return -EINVAL; | ||
751 | |||
752 | switch (card->options.large_send) { | ||
753 | case QETH_LARGE_SEND_NO: | ||
754 | return sprintf(buf, "%s\n", "no"); | ||
755 | case QETH_LARGE_SEND_EDDP: | ||
756 | return sprintf(buf, "%s\n", "EDDP"); | ||
757 | case QETH_LARGE_SEND_TSO: | ||
758 | return sprintf(buf, "%s\n", "TSO"); | ||
759 | default: | ||
760 | return sprintf(buf, "%s\n", "N/A"); | ||
761 | } | ||
762 | } | ||
763 | |||
764 | static ssize_t | ||
765 | qeth_dev_large_send_store(struct device *dev, const char *buf, size_t count) | ||
766 | { | ||
767 | struct qeth_card *card = dev->driver_data; | ||
768 | enum qeth_large_send_types type; | ||
769 | int rc = 0; | ||
770 | char *tmp; | ||
771 | |||
772 | if (!card) | ||
773 | return -EINVAL; | ||
774 | |||
775 | tmp = strsep((char **) &buf, "\n"); | ||
776 | |||
777 | if (!strcmp(tmp, "no")){ | ||
778 | type = QETH_LARGE_SEND_NO; | ||
779 | } else if (!strcmp(tmp, "EDDP")) { | ||
780 | type = QETH_LARGE_SEND_EDDP; | ||
781 | } else if (!strcmp(tmp, "TSO")) { | ||
782 | type = QETH_LARGE_SEND_TSO; | ||
783 | } else { | ||
784 | PRINT_WARN("large_send: invalid mode %s!\n", tmp); | ||
785 | return -EINVAL; | ||
786 | } | ||
787 | if (card->options.large_send == type) | ||
788 | return count; | ||
789 | card->options.large_send = type; | ||
790 | if ((rc = qeth_set_large_send(card))) | ||
791 | return rc; | ||
792 | |||
793 | return count; | ||
794 | } | ||
795 | |||
796 | static DEVICE_ATTR(large_send, 0644, qeth_dev_large_send_show, | ||
797 | qeth_dev_large_send_store); | ||
798 | |||
799 | static ssize_t | ||
800 | qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value ) | ||
801 | { | ||
802 | |||
803 | if (!card) | ||
804 | return -EINVAL; | ||
805 | |||
806 | return sprintf(buf, "%i\n", value); | ||
807 | } | ||
808 | |||
809 | static ssize_t | ||
810 | qeth_dev_blkt_store(struct qeth_card *card, const char *buf, size_t count, | ||
811 | int *value, int max_value) | ||
812 | { | ||
813 | char *tmp; | ||
814 | int i; | ||
815 | |||
816 | if (!card) | ||
817 | return -EINVAL; | ||
818 | |||
819 | if ((card->state != CARD_STATE_DOWN) && | ||
820 | (card->state != CARD_STATE_RECOVER)) | ||
821 | return -EPERM; | ||
822 | |||
823 | i = simple_strtoul(buf, &tmp, 10); | ||
824 | if (i <= max_value) { | ||
825 | *value = i; | ||
826 | } else { | ||
827 | PRINT_WARN("blkt total time: write values between" | ||
828 | " 0 and %d to this file!\n", max_value); | ||
829 | return -EINVAL; | ||
830 | } | ||
831 | return count; | ||
832 | } | ||
833 | |||
834 | static ssize_t | ||
835 | qeth_dev_blkt_total_show(struct device *dev, char *buf) | ||
836 | { | ||
837 | struct qeth_card *card = dev->driver_data; | ||
838 | |||
839 | return qeth_dev_blkt_show(buf, card, card->info.blkt.time_total); | ||
840 | } | ||
841 | |||
842 | |||
843 | static ssize_t | ||
844 | qeth_dev_blkt_total_store(struct device *dev, const char *buf, size_t count) | ||
845 | { | ||
846 | struct qeth_card *card = dev->driver_data; | ||
847 | |||
848 | return qeth_dev_blkt_store(card, buf, count, | ||
849 | &card->info.blkt.time_total,1000); | ||
850 | } | ||
851 | |||
852 | |||
853 | |||
854 | static DEVICE_ATTR(total, 0644, qeth_dev_blkt_total_show, | ||
855 | qeth_dev_blkt_total_store); | ||
856 | |||
857 | static ssize_t | ||
858 | qeth_dev_blkt_inter_show(struct device *dev, char *buf) | ||
859 | { | ||
860 | struct qeth_card *card = dev->driver_data; | ||
861 | |||
862 | return qeth_dev_blkt_show(buf, card, card->info.blkt.inter_packet); | ||
863 | } | ||
864 | |||
865 | |||
866 | static ssize_t | ||
867 | qeth_dev_blkt_inter_store(struct device *dev, const char *buf, size_t count) | ||
868 | { | ||
869 | struct qeth_card *card = dev->driver_data; | ||
870 | |||
871 | return qeth_dev_blkt_store(card, buf, count, | ||
872 | &card->info.blkt.inter_packet,100); | ||
873 | } | ||
874 | |||
875 | static DEVICE_ATTR(inter, 0644, qeth_dev_blkt_inter_show, | ||
876 | qeth_dev_blkt_inter_store); | ||
877 | |||
878 | static ssize_t | ||
879 | qeth_dev_blkt_inter_jumbo_show(struct device *dev, char *buf) | ||
880 | { | ||
881 | struct qeth_card *card = dev->driver_data; | ||
882 | |||
883 | return qeth_dev_blkt_show(buf, card, | ||
884 | card->info.blkt.inter_packet_jumbo); | ||
885 | } | ||
886 | |||
887 | |||
888 | static ssize_t | ||
889 | qeth_dev_blkt_inter_jumbo_store(struct device *dev, const char *buf, size_t count) | ||
890 | { | ||
891 | struct qeth_card *card = dev->driver_data; | ||
892 | |||
893 | return qeth_dev_blkt_store(card, buf, count, | ||
894 | &card->info.blkt.inter_packet_jumbo,100); | ||
895 | } | ||
896 | |||
897 | static DEVICE_ATTR(inter_jumbo, 0644, qeth_dev_blkt_inter_jumbo_show, | ||
898 | qeth_dev_blkt_inter_jumbo_store); | ||
899 | |||
900 | static struct device_attribute * qeth_blkt_device_attrs[] = { | ||
901 | &dev_attr_total, | ||
902 | &dev_attr_inter, | ||
903 | &dev_attr_inter_jumbo, | ||
904 | NULL, | ||
905 | }; | ||
906 | |||
907 | static struct attribute_group qeth_device_blkt_group = { | ||
908 | .name = "blkt", | ||
909 | .attrs = (struct attribute **)qeth_blkt_device_attrs, | ||
910 | }; | ||
911 | |||
912 | static struct device_attribute * qeth_device_attrs[] = { | ||
913 | &dev_attr_state, | ||
914 | &dev_attr_chpid, | ||
915 | &dev_attr_if_name, | ||
916 | &dev_attr_card_type, | ||
917 | &dev_attr_portno, | ||
918 | &dev_attr_portname, | ||
919 | &dev_attr_checksumming, | ||
920 | &dev_attr_priority_queueing, | ||
921 | &dev_attr_buffer_count, | ||
922 | &dev_attr_route4, | ||
923 | #ifdef CONFIG_QETH_IPV6 | ||
924 | &dev_attr_route6, | ||
925 | #endif | ||
926 | &dev_attr_add_hhlen, | ||
927 | &dev_attr_fake_ll, | ||
928 | &dev_attr_fake_broadcast, | ||
929 | &dev_attr_recover, | ||
930 | &dev_attr_broadcast_mode, | ||
931 | &dev_attr_canonical_macaddr, | ||
932 | &dev_attr_layer2, | ||
933 | &dev_attr_large_send, | ||
934 | NULL, | ||
935 | }; | ||
936 | |||
937 | static struct attribute_group qeth_device_attr_group = { | ||
938 | .attrs = (struct attribute **)qeth_device_attrs, | ||
939 | }; | ||
940 | |||
941 | |||
942 | #define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \ | ||
943 | struct device_attribute dev_attr_##_id = { \ | ||
944 | .attr = {.name=__stringify(_name), .mode=_mode, .owner=THIS_MODULE },\ | ||
945 | .show = _show, \ | ||
946 | .store = _store, \ | ||
947 | }; | ||
948 | |||
949 | int | ||
950 | qeth_check_layer2(struct qeth_card *card) | ||
951 | { | ||
952 | if (card->options.layer2) | ||
953 | return -EPERM; | ||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | |||
958 | static ssize_t | ||
959 | qeth_dev_ipato_enable_show(struct device *dev, char *buf) | ||
960 | { | ||
961 | struct qeth_card *card = dev->driver_data; | ||
962 | |||
963 | if (!card) | ||
964 | return -EINVAL; | ||
965 | |||
966 | if (qeth_check_layer2(card)) | ||
967 | return -EPERM; | ||
968 | return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); | ||
969 | } | ||
970 | |||
971 | static ssize_t | ||
972 | qeth_dev_ipato_enable_store(struct device *dev, const char *buf, size_t count) | ||
973 | { | ||
974 | struct qeth_card *card = dev->driver_data; | ||
975 | char *tmp; | ||
976 | |||
977 | if (!card) | ||
978 | return -EINVAL; | ||
979 | |||
980 | if ((card->state != CARD_STATE_DOWN) && | ||
981 | (card->state != CARD_STATE_RECOVER)) | ||
982 | return -EPERM; | ||
983 | |||
984 | if (qeth_check_layer2(card)) | ||
985 | return -EPERM; | ||
986 | |||
987 | tmp = strsep((char **) &buf, "\n"); | ||
988 | if (!strcmp(tmp, "toggle")){ | ||
989 | card->ipato.enabled = (card->ipato.enabled)? 0 : 1; | ||
990 | } else if (!strcmp(tmp, "1")){ | ||
991 | card->ipato.enabled = 1; | ||
992 | } else if (!strcmp(tmp, "0")){ | ||
993 | card->ipato.enabled = 0; | ||
994 | } else { | ||
995 | PRINT_WARN("ipato_enable: write 0, 1 or 'toggle' to " | ||
996 | "this file\n"); | ||
997 | return -EINVAL; | ||
998 | } | ||
999 | return count; | ||
1000 | } | ||
1001 | |||
1002 | static QETH_DEVICE_ATTR(ipato_enable, enable, 0644, | ||
1003 | qeth_dev_ipato_enable_show, | ||
1004 | qeth_dev_ipato_enable_store); | ||
1005 | |||
1006 | static ssize_t | ||
1007 | qeth_dev_ipato_invert4_show(struct device *dev, char *buf) | ||
1008 | { | ||
1009 | struct qeth_card *card = dev->driver_data; | ||
1010 | |||
1011 | if (!card) | ||
1012 | return -EINVAL; | ||
1013 | |||
1014 | if (qeth_check_layer2(card)) | ||
1015 | return -EPERM; | ||
1016 | |||
1017 | return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); | ||
1018 | } | ||
1019 | |||
1020 | static ssize_t | ||
1021 | qeth_dev_ipato_invert4_store(struct device *dev, const char *buf, size_t count) | ||
1022 | { | ||
1023 | struct qeth_card *card = dev->driver_data; | ||
1024 | char *tmp; | ||
1025 | |||
1026 | if (!card) | ||
1027 | return -EINVAL; | ||
1028 | |||
1029 | if (qeth_check_layer2(card)) | ||
1030 | return -EPERM; | ||
1031 | |||
1032 | tmp = strsep((char **) &buf, "\n"); | ||
1033 | if (!strcmp(tmp, "toggle")){ | ||
1034 | card->ipato.invert4 = (card->ipato.invert4)? 0 : 1; | ||
1035 | } else if (!strcmp(tmp, "1")){ | ||
1036 | card->ipato.invert4 = 1; | ||
1037 | } else if (!strcmp(tmp, "0")){ | ||
1038 | card->ipato.invert4 = 0; | ||
1039 | } else { | ||
1040 | PRINT_WARN("ipato_invert4: write 0, 1 or 'toggle' to " | ||
1041 | "this file\n"); | ||
1042 | return -EINVAL; | ||
1043 | } | ||
1044 | return count; | ||
1045 | } | ||
1046 | |||
1047 | static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644, | ||
1048 | qeth_dev_ipato_invert4_show, | ||
1049 | qeth_dev_ipato_invert4_store); | ||
1050 | |||
1051 | static inline ssize_t | ||
1052 | qeth_dev_ipato_add_show(char *buf, struct qeth_card *card, | ||
1053 | enum qeth_prot_versions proto) | ||
1054 | { | ||
1055 | struct qeth_ipato_entry *ipatoe; | ||
1056 | unsigned long flags; | ||
1057 | char addr_str[40]; | ||
1058 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ | ||
1059 | int i = 0; | ||
1060 | |||
1061 | if (qeth_check_layer2(card)) | ||
1062 | return -EPERM; | ||
1063 | |||
1064 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | ||
1065 | /* add strlen for "/<mask>\n" */ | ||
1066 | entry_len += (proto == QETH_PROT_IPV4)? 5 : 6; | ||
1067 | spin_lock_irqsave(&card->ip_lock, flags); | ||
1068 | list_for_each_entry(ipatoe, &card->ipato.entries, entry){ | ||
1069 | if (ipatoe->proto != proto) | ||
1070 | continue; | ||
1071 | /* String must not be longer than PAGE_SIZE. So we check if | ||
1072 | * string length gets near PAGE_SIZE. Then we can savely display | ||
1073 | * the next IPv6 address (worst case, compared to IPv4) */ | ||
1074 | if ((PAGE_SIZE - i) <= entry_len) | ||
1075 | break; | ||
1076 | qeth_ipaddr_to_string(proto, ipatoe->addr, addr_str); | ||
1077 | i += snprintf(buf + i, PAGE_SIZE - i, | ||
1078 | "%s/%i\n", addr_str, ipatoe->mask_bits); | ||
1079 | } | ||
1080 | spin_unlock_irqrestore(&card->ip_lock, flags); | ||
1081 | i += snprintf(buf + i, PAGE_SIZE - i, "\n"); | ||
1082 | |||
1083 | return i; | ||
1084 | } | ||
1085 | |||
1086 | static ssize_t | ||
1087 | qeth_dev_ipato_add4_show(struct device *dev, char *buf) | ||
1088 | { | ||
1089 | struct qeth_card *card = dev->driver_data; | ||
1090 | |||
1091 | if (!card) | ||
1092 | return -EINVAL; | ||
1093 | |||
1094 | return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); | ||
1095 | } | ||
1096 | |||
1097 | static inline int | ||
1098 | qeth_parse_ipatoe(const char* buf, enum qeth_prot_versions proto, | ||
1099 | u8 *addr, int *mask_bits) | ||
1100 | { | ||
1101 | const char *start, *end; | ||
1102 | char *tmp; | ||
1103 | char buffer[49] = {0, }; | ||
1104 | |||
1105 | start = buf; | ||
1106 | /* get address string */ | ||
1107 | end = strchr(start, '/'); | ||
1108 | if (!end){ | ||
1109 | PRINT_WARN("Invalid format for ipato_addx/delx. " | ||
1110 | "Use <ip addr>/<mask bits>\n"); | ||
1111 | return -EINVAL; | ||
1112 | } | ||
1113 | strncpy(buffer, start, end - start); | ||
1114 | if (qeth_string_to_ipaddr(buffer, proto, addr)){ | ||
1115 | PRINT_WARN("Invalid IP address format!\n"); | ||
1116 | return -EINVAL; | ||
1117 | } | ||
1118 | start = end + 1; | ||
1119 | *mask_bits = simple_strtoul(start, &tmp, 10); | ||
1120 | |||
1121 | return 0; | ||
1122 | } | ||
1123 | |||
1124 | static inline ssize_t | ||
1125 | qeth_dev_ipato_add_store(const char *buf, size_t count, | ||
1126 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1127 | { | ||
1128 | struct qeth_ipato_entry *ipatoe; | ||
1129 | u8 addr[16]; | ||
1130 | int mask_bits; | ||
1131 | int rc; | ||
1132 | |||
1133 | if (qeth_check_layer2(card)) | ||
1134 | return -EPERM; | ||
1135 | if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits))) | ||
1136 | return rc; | ||
1137 | |||
1138 | if (!(ipatoe = kmalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL))){ | ||
1139 | PRINT_WARN("No memory to allocate ipato entry\n"); | ||
1140 | return -ENOMEM; | ||
1141 | } | ||
1142 | memset(ipatoe, 0, sizeof(struct qeth_ipato_entry)); | ||
1143 | ipatoe->proto = proto; | ||
1144 | memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); | ||
1145 | ipatoe->mask_bits = mask_bits; | ||
1146 | |||
1147 | if ((rc = qeth_add_ipato_entry(card, ipatoe))){ | ||
1148 | kfree(ipatoe); | ||
1149 | return rc; | ||
1150 | } | ||
1151 | |||
1152 | return count; | ||
1153 | } | ||
1154 | |||
1155 | static ssize_t | ||
1156 | qeth_dev_ipato_add4_store(struct device *dev, const char *buf, size_t count) | ||
1157 | { | ||
1158 | struct qeth_card *card = dev->driver_data; | ||
1159 | |||
1160 | if (!card) | ||
1161 | return -EINVAL; | ||
1162 | |||
1163 | return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); | ||
1164 | } | ||
1165 | |||
1166 | static QETH_DEVICE_ATTR(ipato_add4, add4, 0644, | ||
1167 | qeth_dev_ipato_add4_show, | ||
1168 | qeth_dev_ipato_add4_store); | ||
1169 | |||
1170 | static inline ssize_t | ||
1171 | qeth_dev_ipato_del_store(const char *buf, size_t count, | ||
1172 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1173 | { | ||
1174 | u8 addr[16]; | ||
1175 | int mask_bits; | ||
1176 | int rc; | ||
1177 | |||
1178 | if (qeth_check_layer2(card)) | ||
1179 | return -EPERM; | ||
1180 | if ((rc = qeth_parse_ipatoe(buf, proto, addr, &mask_bits))) | ||
1181 | return rc; | ||
1182 | |||
1183 | qeth_del_ipato_entry(card, proto, addr, mask_bits); | ||
1184 | |||
1185 | return count; | ||
1186 | } | ||
1187 | |||
1188 | static ssize_t | ||
1189 | qeth_dev_ipato_del4_store(struct device *dev, const char *buf, size_t count) | ||
1190 | { | ||
1191 | struct qeth_card *card = dev->driver_data; | ||
1192 | |||
1193 | if (!card) | ||
1194 | return -EINVAL; | ||
1195 | |||
1196 | return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); | ||
1197 | } | ||
1198 | |||
1199 | static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL, | ||
1200 | qeth_dev_ipato_del4_store); | ||
1201 | |||
1202 | #ifdef CONFIG_QETH_IPV6 | ||
1203 | static ssize_t | ||
1204 | qeth_dev_ipato_invert6_show(struct device *dev, char *buf) | ||
1205 | { | ||
1206 | struct qeth_card *card = dev->driver_data; | ||
1207 | |||
1208 | if (!card) | ||
1209 | return -EINVAL; | ||
1210 | |||
1211 | if (qeth_check_layer2(card)) | ||
1212 | return -EPERM; | ||
1213 | |||
1214 | return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); | ||
1215 | } | ||
1216 | |||
1217 | static ssize_t | ||
1218 | qeth_dev_ipato_invert6_store(struct device *dev, const char *buf, size_t count) | ||
1219 | { | ||
1220 | struct qeth_card *card = dev->driver_data; | ||
1221 | char *tmp; | ||
1222 | |||
1223 | if (!card) | ||
1224 | return -EINVAL; | ||
1225 | |||
1226 | if (qeth_check_layer2(card)) | ||
1227 | return -EPERM; | ||
1228 | |||
1229 | tmp = strsep((char **) &buf, "\n"); | ||
1230 | if (!strcmp(tmp, "toggle")){ | ||
1231 | card->ipato.invert6 = (card->ipato.invert6)? 0 : 1; | ||
1232 | } else if (!strcmp(tmp, "1")){ | ||
1233 | card->ipato.invert6 = 1; | ||
1234 | } else if (!strcmp(tmp, "0")){ | ||
1235 | card->ipato.invert6 = 0; | ||
1236 | } else { | ||
1237 | PRINT_WARN("ipato_invert6: write 0, 1 or 'toggle' to " | ||
1238 | "this file\n"); | ||
1239 | return -EINVAL; | ||
1240 | } | ||
1241 | return count; | ||
1242 | } | ||
1243 | |||
1244 | static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644, | ||
1245 | qeth_dev_ipato_invert6_show, | ||
1246 | qeth_dev_ipato_invert6_store); | ||
1247 | |||
1248 | |||
1249 | static ssize_t | ||
1250 | qeth_dev_ipato_add6_show(struct device *dev, char *buf) | ||
1251 | { | ||
1252 | struct qeth_card *card = dev->driver_data; | ||
1253 | |||
1254 | if (!card) | ||
1255 | return -EINVAL; | ||
1256 | |||
1257 | return qeth_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); | ||
1258 | } | ||
1259 | |||
1260 | static ssize_t | ||
1261 | qeth_dev_ipato_add6_store(struct device *dev, const char *buf, size_t count) | ||
1262 | { | ||
1263 | struct qeth_card *card = dev->driver_data; | ||
1264 | |||
1265 | if (!card) | ||
1266 | return -EINVAL; | ||
1267 | |||
1268 | return qeth_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); | ||
1269 | } | ||
1270 | |||
1271 | static QETH_DEVICE_ATTR(ipato_add6, add6, 0644, | ||
1272 | qeth_dev_ipato_add6_show, | ||
1273 | qeth_dev_ipato_add6_store); | ||
1274 | |||
1275 | static ssize_t | ||
1276 | qeth_dev_ipato_del6_store(struct device *dev, const char *buf, size_t count) | ||
1277 | { | ||
1278 | struct qeth_card *card = dev->driver_data; | ||
1279 | |||
1280 | if (!card) | ||
1281 | return -EINVAL; | ||
1282 | |||
1283 | return qeth_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); | ||
1284 | } | ||
1285 | |||
1286 | static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL, | ||
1287 | qeth_dev_ipato_del6_store); | ||
1288 | #endif /* CONFIG_QETH_IPV6 */ | ||
1289 | |||
1290 | static struct device_attribute * qeth_ipato_device_attrs[] = { | ||
1291 | &dev_attr_ipato_enable, | ||
1292 | &dev_attr_ipato_invert4, | ||
1293 | &dev_attr_ipato_add4, | ||
1294 | &dev_attr_ipato_del4, | ||
1295 | #ifdef CONFIG_QETH_IPV6 | ||
1296 | &dev_attr_ipato_invert6, | ||
1297 | &dev_attr_ipato_add6, | ||
1298 | &dev_attr_ipato_del6, | ||
1299 | #endif | ||
1300 | NULL, | ||
1301 | }; | ||
1302 | |||
1303 | static struct attribute_group qeth_device_ipato_group = { | ||
1304 | .name = "ipa_takeover", | ||
1305 | .attrs = (struct attribute **)qeth_ipato_device_attrs, | ||
1306 | }; | ||
1307 | |||
1308 | static inline ssize_t | ||
1309 | qeth_dev_vipa_add_show(char *buf, struct qeth_card *card, | ||
1310 | enum qeth_prot_versions proto) | ||
1311 | { | ||
1312 | struct qeth_ipaddr *ipaddr; | ||
1313 | char addr_str[40]; | ||
1314 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ | ||
1315 | unsigned long flags; | ||
1316 | int i = 0; | ||
1317 | |||
1318 | if (qeth_check_layer2(card)) | ||
1319 | return -EPERM; | ||
1320 | |||
1321 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | ||
1322 | entry_len += 2; /* \n + terminator */ | ||
1323 | spin_lock_irqsave(&card->ip_lock, flags); | ||
1324 | list_for_each_entry(ipaddr, &card->ip_list, entry){ | ||
1325 | if (ipaddr->proto != proto) | ||
1326 | continue; | ||
1327 | if (ipaddr->type != QETH_IP_TYPE_VIPA) | ||
1328 | continue; | ||
1329 | /* String must not be longer than PAGE_SIZE. So we check if | ||
1330 | * string length gets near PAGE_SIZE. Then we can savely display | ||
1331 | * the next IPv6 address (worst case, compared to IPv4) */ | ||
1332 | if ((PAGE_SIZE - i) <= entry_len) | ||
1333 | break; | ||
1334 | qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); | ||
1335 | i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); | ||
1336 | } | ||
1337 | spin_unlock_irqrestore(&card->ip_lock, flags); | ||
1338 | i += snprintf(buf + i, PAGE_SIZE - i, "\n"); | ||
1339 | |||
1340 | return i; | ||
1341 | } | ||
1342 | |||
1343 | static ssize_t | ||
1344 | qeth_dev_vipa_add4_show(struct device *dev, char *buf) | ||
1345 | { | ||
1346 | struct qeth_card *card = dev->driver_data; | ||
1347 | |||
1348 | if (!card) | ||
1349 | return -EINVAL; | ||
1350 | |||
1351 | return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV4); | ||
1352 | } | ||
1353 | |||
1354 | static inline int | ||
1355 | qeth_parse_vipae(const char* buf, enum qeth_prot_versions proto, | ||
1356 | u8 *addr) | ||
1357 | { | ||
1358 | if (qeth_string_to_ipaddr(buf, proto, addr)){ | ||
1359 | PRINT_WARN("Invalid IP address format!\n"); | ||
1360 | return -EINVAL; | ||
1361 | } | ||
1362 | return 0; | ||
1363 | } | ||
1364 | |||
1365 | static inline ssize_t | ||
1366 | qeth_dev_vipa_add_store(const char *buf, size_t count, | ||
1367 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1368 | { | ||
1369 | u8 addr[16] = {0, }; | ||
1370 | int rc; | ||
1371 | |||
1372 | if (qeth_check_layer2(card)) | ||
1373 | return -EPERM; | ||
1374 | if ((rc = qeth_parse_vipae(buf, proto, addr))) | ||
1375 | return rc; | ||
1376 | |||
1377 | if ((rc = qeth_add_vipa(card, proto, addr))) | ||
1378 | return rc; | ||
1379 | |||
1380 | return count; | ||
1381 | } | ||
1382 | |||
1383 | static ssize_t | ||
1384 | qeth_dev_vipa_add4_store(struct device *dev, const char *buf, size_t count) | ||
1385 | { | ||
1386 | struct qeth_card *card = dev->driver_data; | ||
1387 | |||
1388 | if (!card) | ||
1389 | return -EINVAL; | ||
1390 | |||
1391 | return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); | ||
1392 | } | ||
1393 | |||
1394 | static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, | ||
1395 | qeth_dev_vipa_add4_show, | ||
1396 | qeth_dev_vipa_add4_store); | ||
1397 | |||
1398 | static inline ssize_t | ||
1399 | qeth_dev_vipa_del_store(const char *buf, size_t count, | ||
1400 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1401 | { | ||
1402 | u8 addr[16]; | ||
1403 | int rc; | ||
1404 | |||
1405 | if (qeth_check_layer2(card)) | ||
1406 | return -EPERM; | ||
1407 | if ((rc = qeth_parse_vipae(buf, proto, addr))) | ||
1408 | return rc; | ||
1409 | |||
1410 | qeth_del_vipa(card, proto, addr); | ||
1411 | |||
1412 | return count; | ||
1413 | } | ||
1414 | |||
1415 | static ssize_t | ||
1416 | qeth_dev_vipa_del4_store(struct device *dev, const char *buf, size_t count) | ||
1417 | { | ||
1418 | struct qeth_card *card = dev->driver_data; | ||
1419 | |||
1420 | if (!card) | ||
1421 | return -EINVAL; | ||
1422 | |||
1423 | return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); | ||
1424 | } | ||
1425 | |||
1426 | static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, | ||
1427 | qeth_dev_vipa_del4_store); | ||
1428 | |||
1429 | #ifdef CONFIG_QETH_IPV6 | ||
1430 | static ssize_t | ||
1431 | qeth_dev_vipa_add6_show(struct device *dev, char *buf) | ||
1432 | { | ||
1433 | struct qeth_card *card = dev->driver_data; | ||
1434 | |||
1435 | if (!card) | ||
1436 | return -EINVAL; | ||
1437 | |||
1438 | return qeth_dev_vipa_add_show(buf, card, QETH_PROT_IPV6); | ||
1439 | } | ||
1440 | |||
1441 | static ssize_t | ||
1442 | qeth_dev_vipa_add6_store(struct device *dev, const char *buf, size_t count) | ||
1443 | { | ||
1444 | struct qeth_card *card = dev->driver_data; | ||
1445 | |||
1446 | if (!card) | ||
1447 | return -EINVAL; | ||
1448 | |||
1449 | return qeth_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); | ||
1450 | } | ||
1451 | |||
1452 | static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, | ||
1453 | qeth_dev_vipa_add6_show, | ||
1454 | qeth_dev_vipa_add6_store); | ||
1455 | |||
1456 | static ssize_t | ||
1457 | qeth_dev_vipa_del6_store(struct device *dev, const char *buf, size_t count) | ||
1458 | { | ||
1459 | struct qeth_card *card = dev->driver_data; | ||
1460 | |||
1461 | if (!card) | ||
1462 | return -EINVAL; | ||
1463 | |||
1464 | if (qeth_check_layer2(card)) | ||
1465 | return -EPERM; | ||
1466 | |||
1467 | return qeth_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); | ||
1468 | } | ||
1469 | |||
1470 | static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, | ||
1471 | qeth_dev_vipa_del6_store); | ||
1472 | #endif /* CONFIG_QETH_IPV6 */ | ||
1473 | |||
1474 | static struct device_attribute * qeth_vipa_device_attrs[] = { | ||
1475 | &dev_attr_vipa_add4, | ||
1476 | &dev_attr_vipa_del4, | ||
1477 | #ifdef CONFIG_QETH_IPV6 | ||
1478 | &dev_attr_vipa_add6, | ||
1479 | &dev_attr_vipa_del6, | ||
1480 | #endif | ||
1481 | NULL, | ||
1482 | }; | ||
1483 | |||
1484 | static struct attribute_group qeth_device_vipa_group = { | ||
1485 | .name = "vipa", | ||
1486 | .attrs = (struct attribute **)qeth_vipa_device_attrs, | ||
1487 | }; | ||
1488 | |||
1489 | static inline ssize_t | ||
1490 | qeth_dev_rxip_add_show(char *buf, struct qeth_card *card, | ||
1491 | enum qeth_prot_versions proto) | ||
1492 | { | ||
1493 | struct qeth_ipaddr *ipaddr; | ||
1494 | char addr_str[40]; | ||
1495 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ | ||
1496 | unsigned long flags; | ||
1497 | int i = 0; | ||
1498 | |||
1499 | if (qeth_check_layer2(card)) | ||
1500 | return -EPERM; | ||
1501 | |||
1502 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | ||
1503 | entry_len += 2; /* \n + terminator */ | ||
1504 | spin_lock_irqsave(&card->ip_lock, flags); | ||
1505 | list_for_each_entry(ipaddr, &card->ip_list, entry){ | ||
1506 | if (ipaddr->proto != proto) | ||
1507 | continue; | ||
1508 | if (ipaddr->type != QETH_IP_TYPE_RXIP) | ||
1509 | continue; | ||
1510 | /* String must not be longer than PAGE_SIZE. So we check if | ||
1511 | * string length gets near PAGE_SIZE. Then we can savely display | ||
1512 | * the next IPv6 address (worst case, compared to IPv4) */ | ||
1513 | if ((PAGE_SIZE - i) <= entry_len) | ||
1514 | break; | ||
1515 | qeth_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); | ||
1516 | i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); | ||
1517 | } | ||
1518 | spin_unlock_irqrestore(&card->ip_lock, flags); | ||
1519 | i += snprintf(buf + i, PAGE_SIZE - i, "\n"); | ||
1520 | |||
1521 | return i; | ||
1522 | } | ||
1523 | |||
1524 | static ssize_t | ||
1525 | qeth_dev_rxip_add4_show(struct device *dev, char *buf) | ||
1526 | { | ||
1527 | struct qeth_card *card = dev->driver_data; | ||
1528 | |||
1529 | if (!card) | ||
1530 | return -EINVAL; | ||
1531 | |||
1532 | return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV4); | ||
1533 | } | ||
1534 | |||
1535 | static inline int | ||
1536 | qeth_parse_rxipe(const char* buf, enum qeth_prot_versions proto, | ||
1537 | u8 *addr) | ||
1538 | { | ||
1539 | if (qeth_string_to_ipaddr(buf, proto, addr)){ | ||
1540 | PRINT_WARN("Invalid IP address format!\n"); | ||
1541 | return -EINVAL; | ||
1542 | } | ||
1543 | return 0; | ||
1544 | } | ||
1545 | |||
1546 | static inline ssize_t | ||
1547 | qeth_dev_rxip_add_store(const char *buf, size_t count, | ||
1548 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1549 | { | ||
1550 | u8 addr[16] = {0, }; | ||
1551 | int rc; | ||
1552 | |||
1553 | if (qeth_check_layer2(card)) | ||
1554 | return -EPERM; | ||
1555 | if ((rc = qeth_parse_rxipe(buf, proto, addr))) | ||
1556 | return rc; | ||
1557 | |||
1558 | if ((rc = qeth_add_rxip(card, proto, addr))) | ||
1559 | return rc; | ||
1560 | |||
1561 | return count; | ||
1562 | } | ||
1563 | |||
1564 | static ssize_t | ||
1565 | qeth_dev_rxip_add4_store(struct device *dev, const char *buf, size_t count) | ||
1566 | { | ||
1567 | struct qeth_card *card = dev->driver_data; | ||
1568 | |||
1569 | if (!card) | ||
1570 | return -EINVAL; | ||
1571 | |||
1572 | return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); | ||
1573 | } | ||
1574 | |||
1575 | static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, | ||
1576 | qeth_dev_rxip_add4_show, | ||
1577 | qeth_dev_rxip_add4_store); | ||
1578 | |||
1579 | static inline ssize_t | ||
1580 | qeth_dev_rxip_del_store(const char *buf, size_t count, | ||
1581 | struct qeth_card *card, enum qeth_prot_versions proto) | ||
1582 | { | ||
1583 | u8 addr[16]; | ||
1584 | int rc; | ||
1585 | |||
1586 | if (qeth_check_layer2(card)) | ||
1587 | return -EPERM; | ||
1588 | if ((rc = qeth_parse_rxipe(buf, proto, addr))) | ||
1589 | return rc; | ||
1590 | |||
1591 | qeth_del_rxip(card, proto, addr); | ||
1592 | |||
1593 | return count; | ||
1594 | } | ||
1595 | |||
1596 | static ssize_t | ||
1597 | qeth_dev_rxip_del4_store(struct device *dev, const char *buf, size_t count) | ||
1598 | { | ||
1599 | struct qeth_card *card = dev->driver_data; | ||
1600 | |||
1601 | if (!card) | ||
1602 | return -EINVAL; | ||
1603 | |||
1604 | return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); | ||
1605 | } | ||
1606 | |||
1607 | static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, | ||
1608 | qeth_dev_rxip_del4_store); | ||
1609 | |||
1610 | #ifdef CONFIG_QETH_IPV6 | ||
1611 | static ssize_t | ||
1612 | qeth_dev_rxip_add6_show(struct device *dev, char *buf) | ||
1613 | { | ||
1614 | struct qeth_card *card = dev->driver_data; | ||
1615 | |||
1616 | if (!card) | ||
1617 | return -EINVAL; | ||
1618 | |||
1619 | return qeth_dev_rxip_add_show(buf, card, QETH_PROT_IPV6); | ||
1620 | } | ||
1621 | |||
1622 | static ssize_t | ||
1623 | qeth_dev_rxip_add6_store(struct device *dev, const char *buf, size_t count) | ||
1624 | { | ||
1625 | struct qeth_card *card = dev->driver_data; | ||
1626 | |||
1627 | if (!card) | ||
1628 | return -EINVAL; | ||
1629 | |||
1630 | return qeth_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); | ||
1631 | } | ||
1632 | |||
1633 | static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, | ||
1634 | qeth_dev_rxip_add6_show, | ||
1635 | qeth_dev_rxip_add6_store); | ||
1636 | |||
1637 | static ssize_t | ||
1638 | qeth_dev_rxip_del6_store(struct device *dev, const char *buf, size_t count) | ||
1639 | { | ||
1640 | struct qeth_card *card = dev->driver_data; | ||
1641 | |||
1642 | if (!card) | ||
1643 | return -EINVAL; | ||
1644 | |||
1645 | return qeth_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); | ||
1646 | } | ||
1647 | |||
1648 | static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, | ||
1649 | qeth_dev_rxip_del6_store); | ||
1650 | #endif /* CONFIG_QETH_IPV6 */ | ||
1651 | |||
1652 | static struct device_attribute * qeth_rxip_device_attrs[] = { | ||
1653 | &dev_attr_rxip_add4, | ||
1654 | &dev_attr_rxip_del4, | ||
1655 | #ifdef CONFIG_QETH_IPV6 | ||
1656 | &dev_attr_rxip_add6, | ||
1657 | &dev_attr_rxip_del6, | ||
1658 | #endif | ||
1659 | NULL, | ||
1660 | }; | ||
1661 | |||
1662 | static struct attribute_group qeth_device_rxip_group = { | ||
1663 | .name = "rxip", | ||
1664 | .attrs = (struct attribute **)qeth_rxip_device_attrs, | ||
1665 | }; | ||
1666 | |||
1667 | int | ||
1668 | qeth_create_device_attributes(struct device *dev) | ||
1669 | { | ||
1670 | int ret; | ||
1671 | |||
1672 | if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_attr_group))) | ||
1673 | return ret; | ||
1674 | if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_ipato_group))){ | ||
1675 | sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); | ||
1676 | return ret; | ||
1677 | } | ||
1678 | if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_vipa_group))){ | ||
1679 | sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); | ||
1680 | sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); | ||
1681 | return ret; | ||
1682 | } | ||
1683 | if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_rxip_group))){ | ||
1684 | sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); | ||
1685 | sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); | ||
1686 | sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group); | ||
1687 | } | ||
1688 | if ((ret = sysfs_create_group(&dev->kobj, &qeth_device_blkt_group))) | ||
1689 | return ret; | ||
1690 | |||
1691 | return ret; | ||
1692 | } | ||
1693 | |||
1694 | void | ||
1695 | qeth_remove_device_attributes(struct device *dev) | ||
1696 | { | ||
1697 | sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); | ||
1698 | sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); | ||
1699 | sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group); | ||
1700 | sysfs_remove_group(&dev->kobj, &qeth_device_rxip_group); | ||
1701 | sysfs_remove_group(&dev->kobj, &qeth_device_blkt_group); | ||
1702 | } | ||
1703 | |||
1704 | /**********************/ | ||
1705 | /* DRIVER ATTRIBUTES */ | ||
1706 | /**********************/ | ||
1707 | static ssize_t | ||
1708 | qeth_driver_group_store(struct device_driver *ddrv, const char *buf, | ||
1709 | size_t count) | ||
1710 | { | ||
1711 | const char *start, *end; | ||
1712 | char bus_ids[3][BUS_ID_SIZE], *argv[3]; | ||
1713 | int i; | ||
1714 | int err; | ||
1715 | |||
1716 | start = buf; | ||
1717 | for (i = 0; i < 3; i++) { | ||
1718 | static const char delim[] = { ',', ',', '\n' }; | ||
1719 | int len; | ||
1720 | |||
1721 | if (!(end = strchr(start, delim[i]))) | ||
1722 | return -EINVAL; | ||
1723 | len = min_t(ptrdiff_t, BUS_ID_SIZE, end - start); | ||
1724 | strncpy(bus_ids[i], start, len); | ||
1725 | bus_ids[i][len] = '\0'; | ||
1726 | start = end + 1; | ||
1727 | argv[i] = bus_ids[i]; | ||
1728 | } | ||
1729 | err = ccwgroup_create(qeth_root_dev, qeth_ccwgroup_driver.driver_id, | ||
1730 | &qeth_ccw_driver, 3, argv); | ||
1731 | if (err) | ||
1732 | return err; | ||
1733 | else | ||
1734 | return count; | ||
1735 | } | ||
1736 | |||
1737 | |||
1738 | static DRIVER_ATTR(group, 0200, 0, qeth_driver_group_store); | ||
1739 | |||
1740 | static ssize_t | ||
1741 | qeth_driver_notifier_register_store(struct device_driver *ddrv, const char *buf, | ||
1742 | size_t count) | ||
1743 | { | ||
1744 | int rc; | ||
1745 | int signum; | ||
1746 | char *tmp, *tmp2; | ||
1747 | |||
1748 | tmp = strsep((char **) &buf, "\n"); | ||
1749 | if (!strncmp(tmp, "unregister", 10)){ | ||
1750 | if ((rc = qeth_notifier_unregister(current))) | ||
1751 | return rc; | ||
1752 | return count; | ||
1753 | } | ||
1754 | |||
1755 | signum = simple_strtoul(tmp, &tmp2, 10); | ||
1756 | if ((signum < 0) || (signum > 32)){ | ||
1757 | PRINT_WARN("Signal number %d is out of range\n", signum); | ||
1758 | return -EINVAL; | ||
1759 | } | ||
1760 | if ((rc = qeth_notifier_register(current, signum))) | ||
1761 | return rc; | ||
1762 | |||
1763 | return count; | ||
1764 | } | ||
1765 | |||
1766 | static DRIVER_ATTR(notifier_register, 0200, 0, | ||
1767 | qeth_driver_notifier_register_store); | ||
1768 | |||
1769 | int | ||
1770 | qeth_create_driver_attributes(void) | ||
1771 | { | ||
1772 | int rc; | ||
1773 | |||
1774 | if ((rc = driver_create_file(&qeth_ccwgroup_driver.driver, | ||
1775 | &driver_attr_group))) | ||
1776 | return rc; | ||
1777 | return driver_create_file(&qeth_ccwgroup_driver.driver, | ||
1778 | &driver_attr_notifier_register); | ||
1779 | } | ||
1780 | |||
1781 | void | ||
1782 | qeth_remove_driver_attributes(void) | ||
1783 | { | ||
1784 | driver_remove_file(&qeth_ccwgroup_driver.driver, | ||
1785 | &driver_attr_group); | ||
1786 | driver_remove_file(&qeth_ccwgroup_driver.driver, | ||
1787 | &driver_attr_notifier_register); | ||
1788 | } | ||