]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
4a71df50 | 2 | /* |
4a71df50 FB |
3 | * Copyright IBM Corp. 2007 |
4 | * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, | |
5 | * Frank Pavlic <fpavlic@de.ibm.com>, | |
6 | * Thomas Spatzier <tspat@de.ibm.com>, | |
7 | * Frank Blaschka <frank.blaschka@de.ibm.com> | |
8 | */ | |
9 | ||
5a0e3ad6 | 10 | #include <linux/slab.h> |
b3332930 | 11 | #include <asm/ebcdic.h> |
5f78e29c | 12 | #include <linux/hashtable.h> |
4a71df50 FB |
13 | #include "qeth_l3.h" |
14 | ||
15 | #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ | |
16 | struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) | |
17 | ||
4a71df50 FB |
18 | static ssize_t qeth_l3_dev_route_show(struct qeth_card *card, |
19 | struct qeth_routing_info *route, char *buf) | |
20 | { | |
21 | switch (route->type) { | |
22 | case PRIMARY_ROUTER: | |
23 | return sprintf(buf, "%s\n", "primary router"); | |
24 | case SECONDARY_ROUTER: | |
25 | return sprintf(buf, "%s\n", "secondary router"); | |
26 | case MULTICAST_ROUTER: | |
27 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
28 | return sprintf(buf, "%s\n", "multicast router+"); | |
29 | else | |
30 | return sprintf(buf, "%s\n", "multicast router"); | |
31 | case PRIMARY_CONNECTOR: | |
32 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
33 | return sprintf(buf, "%s\n", "primary connector+"); | |
34 | else | |
35 | return sprintf(buf, "%s\n", "primary connector"); | |
36 | case SECONDARY_CONNECTOR: | |
37 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
38 | return sprintf(buf, "%s\n", "secondary connector+"); | |
39 | else | |
40 | return sprintf(buf, "%s\n", "secondary connector"); | |
41 | default: | |
42 | return sprintf(buf, "%s\n", "no"); | |
43 | } | |
44 | } | |
45 | ||
46 | static ssize_t qeth_l3_dev_route4_show(struct device *dev, | |
47 | struct device_attribute *attr, char *buf) | |
48 | { | |
49 | struct qeth_card *card = dev_get_drvdata(dev); | |
50 | ||
51 | if (!card) | |
52 | return -EINVAL; | |
53 | ||
54 | return qeth_l3_dev_route_show(card, &card->options.route4, buf); | |
55 | } | |
56 | ||
57 | static ssize_t qeth_l3_dev_route_store(struct qeth_card *card, | |
58 | struct qeth_routing_info *route, enum qeth_prot_versions prot, | |
59 | const char *buf, size_t count) | |
60 | { | |
61 | enum qeth_routing_types old_route_type = route->type; | |
c4949f07 | 62 | int rc = 0; |
4a71df50 | 63 | |
c4949f07 | 64 | mutex_lock(&card->conf_mutex); |
7e846d6b | 65 | if (sysfs_streq(buf, "no_router")) { |
4a71df50 | 66 | route->type = NO_ROUTER; |
7e846d6b | 67 | } else if (sysfs_streq(buf, "primary_connector")) { |
4a71df50 | 68 | route->type = PRIMARY_CONNECTOR; |
7e846d6b | 69 | } else if (sysfs_streq(buf, "secondary_connector")) { |
4a71df50 | 70 | route->type = SECONDARY_CONNECTOR; |
7e846d6b | 71 | } else if (sysfs_streq(buf, "primary_router")) { |
4a71df50 | 72 | route->type = PRIMARY_ROUTER; |
7e846d6b | 73 | } else if (sysfs_streq(buf, "secondary_router")) { |
4a71df50 | 74 | route->type = SECONDARY_ROUTER; |
7e846d6b | 75 | } else if (sysfs_streq(buf, "multicast_router")) { |
4a71df50 FB |
76 | route->type = MULTICAST_ROUTER; |
77 | } else { | |
c4949f07 FB |
78 | rc = -EINVAL; |
79 | goto out; | |
4a71df50 | 80 | } |
c3521254 | 81 | if (qeth_card_hw_is_reachable(card) && |
4a71df50 FB |
82 | (old_route_type != route->type)) { |
83 | if (prot == QETH_PROT_IPV4) | |
84 | rc = qeth_l3_setrouting_v4(card); | |
85 | else if (prot == QETH_PROT_IPV6) | |
86 | rc = qeth_l3_setrouting_v6(card); | |
87 | } | |
c4949f07 | 88 | out: |
82e2e782 SR |
89 | if (rc) |
90 | route->type = old_route_type; | |
c4949f07 FB |
91 | mutex_unlock(&card->conf_mutex); |
92 | return rc ? rc : count; | |
4a71df50 FB |
93 | } |
94 | ||
95 | static ssize_t qeth_l3_dev_route4_store(struct device *dev, | |
96 | struct device_attribute *attr, const char *buf, size_t count) | |
97 | { | |
98 | struct qeth_card *card = dev_get_drvdata(dev); | |
99 | ||
100 | if (!card) | |
101 | return -EINVAL; | |
102 | ||
103 | return qeth_l3_dev_route_store(card, &card->options.route4, | |
104 | QETH_PROT_IPV4, buf, count); | |
105 | } | |
106 | ||
107 | static DEVICE_ATTR(route4, 0644, qeth_l3_dev_route4_show, | |
108 | qeth_l3_dev_route4_store); | |
109 | ||
110 | static ssize_t qeth_l3_dev_route6_show(struct device *dev, | |
111 | struct device_attribute *attr, char *buf) | |
112 | { | |
113 | struct qeth_card *card = dev_get_drvdata(dev); | |
114 | ||
115 | if (!card) | |
116 | return -EINVAL; | |
117 | ||
4a71df50 FB |
118 | return qeth_l3_dev_route_show(card, &card->options.route6, buf); |
119 | } | |
120 | ||
121 | static ssize_t qeth_l3_dev_route6_store(struct device *dev, | |
122 | struct device_attribute *attr, const char *buf, size_t count) | |
123 | { | |
124 | struct qeth_card *card = dev_get_drvdata(dev); | |
125 | ||
126 | if (!card) | |
127 | return -EINVAL; | |
128 | ||
4a71df50 FB |
129 | return qeth_l3_dev_route_store(card, &card->options.route6, |
130 | QETH_PROT_IPV6, buf, count); | |
131 | } | |
132 | ||
133 | static DEVICE_ATTR(route6, 0644, qeth_l3_dev_route6_show, | |
134 | qeth_l3_dev_route6_store); | |
135 | ||
136 | static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev, | |
137 | struct device_attribute *attr, char *buf) | |
138 | { | |
139 | struct qeth_card *card = dev_get_drvdata(dev); | |
140 | ||
141 | if (!card) | |
142 | return -EINVAL; | |
143 | ||
144 | return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); | |
145 | } | |
146 | ||
147 | static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev, | |
148 | struct device_attribute *attr, const char *buf, size_t count) | |
149 | { | |
150 | struct qeth_card *card = dev_get_drvdata(dev); | |
151 | char *tmp; | |
c4949f07 | 152 | int i, rc = 0; |
4a71df50 FB |
153 | |
154 | if (!card) | |
155 | return -EINVAL; | |
156 | ||
c4949f07 | 157 | mutex_lock(&card->conf_mutex); |
4a71df50 | 158 | if ((card->state != CARD_STATE_DOWN) && |
c4949f07 FB |
159 | (card->state != CARD_STATE_RECOVER)) { |
160 | rc = -EPERM; | |
161 | goto out; | |
162 | } | |
4a71df50 FB |
163 | |
164 | i = simple_strtoul(buf, &tmp, 16); | |
165 | if ((i == 0) || (i == 1)) | |
166 | card->options.fake_broadcast = i; | |
c4949f07 FB |
167 | else |
168 | rc = -EINVAL; | |
169 | out: | |
170 | mutex_unlock(&card->conf_mutex); | |
171 | return rc ? rc : count; | |
4a71df50 FB |
172 | } |
173 | ||
174 | static DEVICE_ATTR(fake_broadcast, 0644, qeth_l3_dev_fake_broadcast_show, | |
175 | qeth_l3_dev_fake_broadcast_store); | |
176 | ||
76b11f8e UB |
177 | static ssize_t qeth_l3_dev_sniffer_show(struct device *dev, |
178 | struct device_attribute *attr, char *buf) | |
179 | { | |
180 | struct qeth_card *card = dev_get_drvdata(dev); | |
181 | ||
182 | if (!card) | |
183 | return -EINVAL; | |
184 | ||
185 | return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0); | |
186 | } | |
187 | ||
188 | static ssize_t qeth_l3_dev_sniffer_store(struct device *dev, | |
189 | struct device_attribute *attr, const char *buf, size_t count) | |
190 | { | |
191 | struct qeth_card *card = dev_get_drvdata(dev); | |
c4949f07 | 192 | int rc = 0; |
76b11f8e UB |
193 | unsigned long i; |
194 | ||
195 | if (!card) | |
196 | return -EINVAL; | |
197 | ||
198 | if (card->info.type != QETH_CARD_TYPE_IQD) | |
199 | return -EPERM; | |
b3332930 FB |
200 | if (card->options.cq == QETH_CQ_ENABLED) |
201 | return -EPERM; | |
76b11f8e | 202 | |
c4949f07 | 203 | mutex_lock(&card->conf_mutex); |
76b11f8e | 204 | if ((card->state != CARD_STATE_DOWN) && |
c4949f07 FB |
205 | (card->state != CARD_STATE_RECOVER)) { |
206 | rc = -EPERM; | |
207 | goto out; | |
208 | } | |
76b11f8e | 209 | |
0178722b | 210 | rc = kstrtoul(buf, 16, &i); |
c4949f07 FB |
211 | if (rc) { |
212 | rc = -EINVAL; | |
213 | goto out; | |
214 | } | |
76b11f8e UB |
215 | switch (i) { |
216 | case 0: | |
217 | card->options.sniffer = i; | |
218 | break; | |
219 | case 1: | |
c4949f07 | 220 | qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd); |
76b11f8e UB |
221 | if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) { |
222 | card->options.sniffer = i; | |
223 | if (card->qdio.init_pool.buf_count != | |
224 | QETH_IN_BUF_COUNT_MAX) | |
225 | qeth_realloc_buffer_pool(card, | |
226 | QETH_IN_BUF_COUNT_MAX); | |
76b11f8e | 227 | } else |
c4949f07 | 228 | rc = -EPERM; |
6cc31d09 UB |
229 | break; |
230 | default: | |
c4949f07 | 231 | rc = -EINVAL; |
76b11f8e | 232 | } |
c4949f07 FB |
233 | out: |
234 | mutex_unlock(&card->conf_mutex); | |
235 | return rc ? rc : count; | |
76b11f8e UB |
236 | } |
237 | ||
238 | static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show, | |
239 | qeth_l3_dev_sniffer_store); | |
240 | ||
b3332930 FB |
241 | |
242 | static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, | |
243 | struct device_attribute *attr, char *buf) | |
244 | { | |
245 | struct qeth_card *card = dev_get_drvdata(dev); | |
246 | char tmp_hsuid[9]; | |
247 | ||
248 | if (!card) | |
249 | return -EINVAL; | |
250 | ||
251 | if (card->info.type != QETH_CARD_TYPE_IQD) | |
252 | return -EPERM; | |
253 | ||
b3332930 FB |
254 | memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid)); |
255 | EBCASC(tmp_hsuid, 8); | |
256 | return sprintf(buf, "%s\n", tmp_hsuid); | |
257 | } | |
258 | ||
259 | static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, | |
260 | struct device_attribute *attr, const char *buf, size_t count) | |
261 | { | |
262 | struct qeth_card *card = dev_get_drvdata(dev); | |
263 | struct qeth_ipaddr *addr; | |
264 | char *tmp; | |
265 | int i; | |
266 | ||
267 | if (!card) | |
268 | return -EINVAL; | |
269 | ||
270 | if (card->info.type != QETH_CARD_TYPE_IQD) | |
271 | return -EPERM; | |
272 | if (card->state != CARD_STATE_DOWN && | |
273 | card->state != CARD_STATE_RECOVER) | |
274 | return -EPERM; | |
275 | if (card->options.sniffer) | |
276 | return -EPERM; | |
277 | if (card->options.cq == QETH_CQ_NOTAVAILABLE) | |
278 | return -EPERM; | |
279 | ||
280 | tmp = strsep((char **)&buf, "\n"); | |
281 | if (strlen(tmp) > 8) | |
282 | return -EINVAL; | |
283 | ||
284 | if (card->options.hsuid[0]) { | |
285 | /* delete old ip address */ | |
286 | addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); | |
5f78e29c | 287 | if (!addr) |
b3332930 | 288 | return -ENOMEM; |
5f78e29c | 289 | |
6bee4e26 | 290 | addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000); |
5f78e29c LD |
291 | addr->u.a6.addr.s6_addr32[1] = 0x00000000; |
292 | for (i = 8; i < 16; i++) | |
293 | addr->u.a6.addr.s6_addr[i] = | |
294 | card->options.hsuid[i - 8]; | |
295 | addr->u.a6.pfxlen = 0; | |
296 | addr->type = QETH_IP_TYPE_NORMAL; | |
297 | ||
016930b8 | 298 | spin_lock_bh(&card->ip_lock); |
5f78e29c | 299 | qeth_l3_delete_ip(card, addr); |
016930b8 | 300 | spin_unlock_bh(&card->ip_lock); |
5f78e29c | 301 | kfree(addr); |
b3332930 FB |
302 | } |
303 | ||
304 | if (strlen(tmp) == 0) { | |
305 | /* delete ip address only */ | |
306 | card->options.hsuid[0] = '\0'; | |
307 | if (card->dev) | |
308 | memcpy(card->dev->perm_addr, card->options.hsuid, 9); | |
309 | qeth_configure_cq(card, QETH_CQ_DISABLED); | |
310 | return count; | |
311 | } | |
312 | ||
313 | if (qeth_configure_cq(card, QETH_CQ_ENABLED)) | |
314 | return -EPERM; | |
315 | ||
096a8aac KC |
316 | snprintf(card->options.hsuid, sizeof(card->options.hsuid), |
317 | "%-8s", tmp); | |
b3332930 FB |
318 | ASCEBC(card->options.hsuid, 8); |
319 | if (card->dev) | |
320 | memcpy(card->dev->perm_addr, card->options.hsuid, 9); | |
321 | ||
322 | addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); | |
323 | if (addr != NULL) { | |
6bee4e26 | 324 | addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000); |
b3332930 FB |
325 | addr->u.a6.addr.s6_addr32[1] = 0x00000000; |
326 | for (i = 8; i < 16; i++) | |
327 | addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8]; | |
328 | addr->u.a6.pfxlen = 0; | |
329 | addr->type = QETH_IP_TYPE_NORMAL; | |
330 | } else | |
331 | return -ENOMEM; | |
016930b8 UB |
332 | |
333 | spin_lock_bh(&card->ip_lock); | |
5f78e29c | 334 | qeth_l3_add_ip(card, addr); |
016930b8 | 335 | spin_unlock_bh(&card->ip_lock); |
5f78e29c | 336 | kfree(addr); |
b3332930 FB |
337 | |
338 | return count; | |
339 | } | |
340 | ||
341 | static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show, | |
342 | qeth_l3_dev_hsuid_store); | |
343 | ||
344 | ||
4a71df50 FB |
345 | static struct attribute *qeth_l3_device_attrs[] = { |
346 | &dev_attr_route4.attr, | |
347 | &dev_attr_route6.attr, | |
348 | &dev_attr_fake_broadcast.attr, | |
76b11f8e | 349 | &dev_attr_sniffer.attr, |
b3332930 | 350 | &dev_attr_hsuid.attr, |
4a71df50 FB |
351 | NULL, |
352 | }; | |
353 | ||
cfe9a042 | 354 | static const struct attribute_group qeth_l3_device_attr_group = { |
4a71df50 FB |
355 | .attrs = qeth_l3_device_attrs, |
356 | }; | |
357 | ||
358 | static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev, | |
359 | struct device_attribute *attr, char *buf) | |
360 | { | |
361 | struct qeth_card *card = dev_get_drvdata(dev); | |
362 | ||
363 | if (!card) | |
364 | return -EINVAL; | |
365 | ||
366 | return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); | |
367 | } | |
368 | ||
369 | static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, | |
370 | struct device_attribute *attr, const char *buf, size_t count) | |
371 | { | |
372 | struct qeth_card *card = dev_get_drvdata(dev); | |
7fbd9493 | 373 | bool enable; |
02f510f3 | 374 | int rc = 0; |
4a71df50 FB |
375 | |
376 | if (!card) | |
377 | return -EINVAL; | |
378 | ||
c4949f07 | 379 | mutex_lock(&card->conf_mutex); |
4a71df50 | 380 | if ((card->state != CARD_STATE_DOWN) && |
c4949f07 FB |
381 | (card->state != CARD_STATE_RECOVER)) { |
382 | rc = -EPERM; | |
383 | goto out; | |
384 | } | |
4a71df50 | 385 | |
7e846d6b | 386 | if (sysfs_streq(buf, "toggle")) { |
7fbd9493 JW |
387 | enable = !card->ipato.enabled; |
388 | } else if (kstrtobool(buf, &enable)) { | |
c4949f07 | 389 | rc = -EINVAL; |
7fbd9493 JW |
390 | goto out; |
391 | } | |
392 | ||
02f510f3 JW |
393 | if (card->ipato.enabled != enable) { |
394 | card->ipato.enabled = enable; | |
395 | spin_lock_bh(&card->ip_lock); | |
396 | qeth_l3_update_ipato(card); | |
397 | spin_unlock_bh(&card->ip_lock); | |
7fbd9493 | 398 | } |
c4949f07 FB |
399 | out: |
400 | mutex_unlock(&card->conf_mutex); | |
401 | return rc ? rc : count; | |
4a71df50 FB |
402 | } |
403 | ||
404 | static QETH_DEVICE_ATTR(ipato_enable, enable, 0644, | |
405 | qeth_l3_dev_ipato_enable_show, | |
406 | qeth_l3_dev_ipato_enable_store); | |
407 | ||
408 | static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev, | |
409 | struct device_attribute *attr, char *buf) | |
410 | { | |
411 | struct qeth_card *card = dev_get_drvdata(dev); | |
412 | ||
413 | if (!card) | |
414 | return -EINVAL; | |
415 | ||
416 | return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); | |
417 | } | |
418 | ||
419 | static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev, | |
420 | struct device_attribute *attr, | |
421 | const char *buf, size_t count) | |
422 | { | |
423 | struct qeth_card *card = dev_get_drvdata(dev); | |
02f510f3 | 424 | bool invert; |
c4949f07 | 425 | int rc = 0; |
4a71df50 FB |
426 | |
427 | if (!card) | |
428 | return -EINVAL; | |
429 | ||
c4949f07 | 430 | mutex_lock(&card->conf_mutex); |
02f510f3 JW |
431 | if (sysfs_streq(buf, "toggle")) { |
432 | invert = !card->ipato.invert4; | |
433 | } else if (kstrtobool(buf, &invert)) { | |
c4949f07 | 434 | rc = -EINVAL; |
02f510f3 JW |
435 | goto out; |
436 | } | |
437 | ||
438 | if (card->ipato.invert4 != invert) { | |
439 | card->ipato.invert4 = invert; | |
440 | spin_lock_bh(&card->ip_lock); | |
441 | qeth_l3_update_ipato(card); | |
442 | spin_unlock_bh(&card->ip_lock); | |
443 | } | |
444 | out: | |
c4949f07 FB |
445 | mutex_unlock(&card->conf_mutex); |
446 | return rc ? rc : count; | |
4a71df50 FB |
447 | } |
448 | ||
449 | static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644, | |
450 | qeth_l3_dev_ipato_invert4_show, | |
451 | qeth_l3_dev_ipato_invert4_store); | |
452 | ||
453 | static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, | |
454 | enum qeth_prot_versions proto) | |
455 | { | |
456 | struct qeth_ipato_entry *ipatoe; | |
4a71df50 FB |
457 | char addr_str[40]; |
458 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ | |
459 | int i = 0; | |
460 | ||
461 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | |
462 | /* add strlen for "/<mask>\n" */ | |
463 | entry_len += (proto == QETH_PROT_IPV4)? 5 : 6; | |
5f78e29c | 464 | spin_lock_bh(&card->ip_lock); |
4a71df50 FB |
465 | list_for_each_entry(ipatoe, &card->ipato.entries, entry) { |
466 | if (ipatoe->proto != proto) | |
467 | continue; | |
468 | /* String must not be longer than PAGE_SIZE. So we check if | |
469 | * string length gets near PAGE_SIZE. Then we can savely display | |
470 | * the next IPv6 address (worst case, compared to IPv4) */ | |
471 | if ((PAGE_SIZE - i) <= entry_len) | |
472 | break; | |
473 | qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str); | |
474 | i += snprintf(buf + i, PAGE_SIZE - i, | |
475 | "%s/%i\n", addr_str, ipatoe->mask_bits); | |
476 | } | |
5f78e29c | 477 | spin_unlock_bh(&card->ip_lock); |
4a71df50 FB |
478 | i += snprintf(buf + i, PAGE_SIZE - i, "\n"); |
479 | ||
480 | return i; | |
481 | } | |
482 | ||
483 | static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, | |
484 | struct device_attribute *attr, char *buf) | |
485 | { | |
486 | struct qeth_card *card = dev_get_drvdata(dev); | |
487 | ||
488 | if (!card) | |
489 | return -EINVAL; | |
490 | ||
491 | return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); | |
492 | } | |
493 | ||
494 | static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto, | |
495 | u8 *addr, int *mask_bits) | |
496 | { | |
497 | const char *start, *end; | |
498 | char *tmp; | |
499 | char buffer[40] = {0, }; | |
500 | ||
501 | start = buf; | |
502 | /* get address string */ | |
503 | end = strchr(start, '/'); | |
504 | if (!end || (end - start >= 40)) { | |
4a71df50 FB |
505 | return -EINVAL; |
506 | } | |
507 | strncpy(buffer, start, end - start); | |
508 | if (qeth_l3_string_to_ipaddr(buffer, proto, addr)) { | |
4a71df50 FB |
509 | return -EINVAL; |
510 | } | |
511 | start = end + 1; | |
512 | *mask_bits = simple_strtoul(start, &tmp, 10); | |
513 | if (!strlen(start) || | |
514 | (tmp == start) || | |
515 | (*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) { | |
4a71df50 FB |
516 | return -EINVAL; |
517 | } | |
518 | return 0; | |
519 | } | |
520 | ||
521 | static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, | |
522 | struct qeth_card *card, enum qeth_prot_versions proto) | |
523 | { | |
524 | struct qeth_ipato_entry *ipatoe; | |
525 | u8 addr[16]; | |
526 | int mask_bits; | |
c4949f07 | 527 | int rc = 0; |
4a71df50 | 528 | |
c4949f07 | 529 | mutex_lock(&card->conf_mutex); |
4a71df50 FB |
530 | rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); |
531 | if (rc) | |
c4949f07 | 532 | goto out; |
4a71df50 FB |
533 | |
534 | ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL); | |
535 | if (!ipatoe) { | |
c4949f07 FB |
536 | rc = -ENOMEM; |
537 | goto out; | |
4a71df50 FB |
538 | } |
539 | ipatoe->proto = proto; | |
540 | memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); | |
541 | ipatoe->mask_bits = mask_bits; | |
542 | ||
543 | rc = qeth_l3_add_ipato_entry(card, ipatoe); | |
c4949f07 | 544 | if (rc) |
4a71df50 | 545 | kfree(ipatoe); |
c4949f07 FB |
546 | out: |
547 | mutex_unlock(&card->conf_mutex); | |
548 | return rc ? rc : count; | |
4a71df50 FB |
549 | } |
550 | ||
551 | static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev, | |
552 | struct device_attribute *attr, const char *buf, size_t count) | |
553 | { | |
554 | struct qeth_card *card = dev_get_drvdata(dev); | |
555 | ||
556 | if (!card) | |
557 | return -EINVAL; | |
558 | ||
559 | return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); | |
560 | } | |
561 | ||
562 | static QETH_DEVICE_ATTR(ipato_add4, add4, 0644, | |
563 | qeth_l3_dev_ipato_add4_show, | |
564 | qeth_l3_dev_ipato_add4_store); | |
565 | ||
566 | static ssize_t qeth_l3_dev_ipato_del_store(const char *buf, size_t count, | |
567 | struct qeth_card *card, enum qeth_prot_versions proto) | |
568 | { | |
569 | u8 addr[16]; | |
570 | int mask_bits; | |
c4949f07 | 571 | int rc = 0; |
4a71df50 | 572 | |
c4949f07 | 573 | mutex_lock(&card->conf_mutex); |
4a71df50 | 574 | rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); |
c4949f07 FB |
575 | if (!rc) |
576 | qeth_l3_del_ipato_entry(card, proto, addr, mask_bits); | |
577 | mutex_unlock(&card->conf_mutex); | |
578 | return rc ? rc : count; | |
4a71df50 FB |
579 | } |
580 | ||
581 | static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev, | |
582 | struct device_attribute *attr, const char *buf, size_t count) | |
583 | { | |
584 | struct qeth_card *card = dev_get_drvdata(dev); | |
585 | ||
586 | if (!card) | |
587 | return -EINVAL; | |
588 | ||
589 | return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); | |
590 | } | |
591 | ||
592 | static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL, | |
593 | qeth_l3_dev_ipato_del4_store); | |
594 | ||
595 | static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev, | |
596 | struct device_attribute *attr, char *buf) | |
597 | { | |
598 | struct qeth_card *card = dev_get_drvdata(dev); | |
599 | ||
600 | if (!card) | |
601 | return -EINVAL; | |
602 | ||
603 | return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); | |
604 | } | |
605 | ||
606 | static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev, | |
607 | struct device_attribute *attr, const char *buf, size_t count) | |
608 | { | |
609 | struct qeth_card *card = dev_get_drvdata(dev); | |
02f510f3 | 610 | bool invert; |
c4949f07 | 611 | int rc = 0; |
4a71df50 FB |
612 | |
613 | if (!card) | |
614 | return -EINVAL; | |
615 | ||
c4949f07 | 616 | mutex_lock(&card->conf_mutex); |
02f510f3 JW |
617 | if (sysfs_streq(buf, "toggle")) { |
618 | invert = !card->ipato.invert6; | |
619 | } else if (kstrtobool(buf, &invert)) { | |
c4949f07 | 620 | rc = -EINVAL; |
02f510f3 JW |
621 | goto out; |
622 | } | |
623 | ||
624 | if (card->ipato.invert6 != invert) { | |
625 | card->ipato.invert6 = invert; | |
626 | spin_lock_bh(&card->ip_lock); | |
627 | qeth_l3_update_ipato(card); | |
628 | spin_unlock_bh(&card->ip_lock); | |
629 | } | |
630 | out: | |
c4949f07 FB |
631 | mutex_unlock(&card->conf_mutex); |
632 | return rc ? rc : count; | |
4a71df50 FB |
633 | } |
634 | ||
635 | static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644, | |
636 | qeth_l3_dev_ipato_invert6_show, | |
637 | qeth_l3_dev_ipato_invert6_store); | |
638 | ||
639 | ||
640 | static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev, | |
641 | struct device_attribute *attr, char *buf) | |
642 | { | |
643 | struct qeth_card *card = dev_get_drvdata(dev); | |
644 | ||
645 | if (!card) | |
646 | return -EINVAL; | |
647 | ||
648 | return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); | |
649 | } | |
650 | ||
651 | static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev, | |
652 | struct device_attribute *attr, const char *buf, size_t count) | |
653 | { | |
654 | struct qeth_card *card = dev_get_drvdata(dev); | |
655 | ||
656 | if (!card) | |
657 | return -EINVAL; | |
658 | ||
659 | return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); | |
660 | } | |
661 | ||
662 | static QETH_DEVICE_ATTR(ipato_add6, add6, 0644, | |
663 | qeth_l3_dev_ipato_add6_show, | |
664 | qeth_l3_dev_ipato_add6_store); | |
665 | ||
666 | static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev, | |
667 | struct device_attribute *attr, const char *buf, size_t count) | |
668 | { | |
669 | struct qeth_card *card = dev_get_drvdata(dev); | |
670 | ||
671 | if (!card) | |
672 | return -EINVAL; | |
673 | ||
674 | return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); | |
675 | } | |
676 | ||
677 | static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL, | |
678 | qeth_l3_dev_ipato_del6_store); | |
679 | ||
680 | static struct attribute *qeth_ipato_device_attrs[] = { | |
681 | &dev_attr_ipato_enable.attr, | |
682 | &dev_attr_ipato_invert4.attr, | |
683 | &dev_attr_ipato_add4.attr, | |
684 | &dev_attr_ipato_del4.attr, | |
685 | &dev_attr_ipato_invert6.attr, | |
686 | &dev_attr_ipato_add6.attr, | |
687 | &dev_attr_ipato_del6.attr, | |
688 | NULL, | |
689 | }; | |
690 | ||
cfe9a042 | 691 | static const struct attribute_group qeth_device_ipato_group = { |
4a71df50 FB |
692 | .name = "ipa_takeover", |
693 | .attrs = qeth_ipato_device_attrs, | |
694 | }; | |
695 | ||
696 | static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card, | |
697 | enum qeth_prot_versions proto) | |
698 | { | |
699 | struct qeth_ipaddr *ipaddr; | |
700 | char addr_str[40]; | |
e48b9eaa | 701 | int str_len = 0; |
4a71df50 | 702 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ |
e48b9eaa | 703 | int i; |
4a71df50 FB |
704 | |
705 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | |
706 | entry_len += 2; /* \n + terminator */ | |
5f78e29c | 707 | spin_lock_bh(&card->ip_lock); |
e48b9eaa | 708 | hash_for_each(card->ip_htable, i, ipaddr, hnode) { |
4a71df50 FB |
709 | if (ipaddr->proto != proto) |
710 | continue; | |
711 | if (ipaddr->type != QETH_IP_TYPE_VIPA) | |
712 | continue; | |
713 | /* String must not be longer than PAGE_SIZE. So we check if | |
714 | * string length gets near PAGE_SIZE. Then we can savely display | |
715 | * the next IPv6 address (worst case, compared to IPv4) */ | |
e48b9eaa | 716 | if ((PAGE_SIZE - str_len) <= entry_len) |
4a71df50 FB |
717 | break; |
718 | qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, | |
719 | addr_str); | |
e48b9eaa UB |
720 | str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", |
721 | addr_str); | |
4a71df50 | 722 | } |
5f78e29c | 723 | spin_unlock_bh(&card->ip_lock); |
e48b9eaa | 724 | str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); |
4a71df50 | 725 | |
e48b9eaa | 726 | return str_len; |
4a71df50 FB |
727 | } |
728 | ||
729 | static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, | |
730 | struct device_attribute *attr, char *buf) | |
731 | { | |
732 | struct qeth_card *card = dev_get_drvdata(dev); | |
733 | ||
734 | if (!card) | |
735 | return -EINVAL; | |
736 | ||
737 | return qeth_l3_dev_vipa_add_show(buf, card, QETH_PROT_IPV4); | |
738 | } | |
739 | ||
740 | static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto, | |
741 | u8 *addr) | |
742 | { | |
743 | if (qeth_l3_string_to_ipaddr(buf, proto, addr)) { | |
4a71df50 FB |
744 | return -EINVAL; |
745 | } | |
746 | return 0; | |
747 | } | |
748 | ||
749 | static ssize_t qeth_l3_dev_vipa_add_store(const char *buf, size_t count, | |
750 | struct qeth_card *card, enum qeth_prot_versions proto) | |
751 | { | |
752 | u8 addr[16] = {0, }; | |
753 | int rc; | |
754 | ||
c4949f07 | 755 | mutex_lock(&card->conf_mutex); |
4a71df50 | 756 | rc = qeth_l3_parse_vipae(buf, proto, addr); |
c4949f07 FB |
757 | if (!rc) |
758 | rc = qeth_l3_add_vipa(card, proto, addr); | |
759 | mutex_unlock(&card->conf_mutex); | |
760 | return rc ? rc : count; | |
4a71df50 FB |
761 | } |
762 | ||
763 | static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, | |
764 | struct device_attribute *attr, const char *buf, size_t count) | |
765 | { | |
766 | struct qeth_card *card = dev_get_drvdata(dev); | |
767 | ||
768 | if (!card) | |
769 | return -EINVAL; | |
770 | ||
771 | return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); | |
772 | } | |
773 | ||
774 | static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, | |
775 | qeth_l3_dev_vipa_add4_show, | |
776 | qeth_l3_dev_vipa_add4_store); | |
777 | ||
778 | static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count, | |
779 | struct qeth_card *card, enum qeth_prot_versions proto) | |
780 | { | |
781 | u8 addr[16]; | |
782 | int rc; | |
783 | ||
c4949f07 | 784 | mutex_lock(&card->conf_mutex); |
4a71df50 | 785 | rc = qeth_l3_parse_vipae(buf, proto, addr); |
c4949f07 FB |
786 | if (!rc) |
787 | qeth_l3_del_vipa(card, proto, addr); | |
788 | mutex_unlock(&card->conf_mutex); | |
789 | return rc ? rc : count; | |
4a71df50 FB |
790 | } |
791 | ||
792 | static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, | |
793 | struct device_attribute *attr, const char *buf, size_t count) | |
794 | { | |
795 | struct qeth_card *card = dev_get_drvdata(dev); | |
796 | ||
797 | if (!card) | |
798 | return -EINVAL; | |
799 | ||
800 | return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); | |
801 | } | |
802 | ||
803 | static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, | |
804 | qeth_l3_dev_vipa_del4_store); | |
805 | ||
806 | static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev, | |
807 | struct device_attribute *attr, char *buf) | |
808 | { | |
809 | struct qeth_card *card = dev_get_drvdata(dev); | |
810 | ||
811 | if (!card) | |
812 | return -EINVAL; | |
813 | ||
814 | return qeth_l3_dev_vipa_add_show(buf, card, QETH_PROT_IPV6); | |
815 | } | |
816 | ||
817 | static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, | |
818 | struct device_attribute *attr, const char *buf, size_t count) | |
819 | { | |
820 | struct qeth_card *card = dev_get_drvdata(dev); | |
821 | ||
822 | if (!card) | |
823 | return -EINVAL; | |
824 | ||
825 | return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); | |
826 | } | |
827 | ||
828 | static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, | |
829 | qeth_l3_dev_vipa_add6_show, | |
830 | qeth_l3_dev_vipa_add6_store); | |
831 | ||
832 | static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, | |
833 | struct device_attribute *attr, const char *buf, size_t count) | |
834 | { | |
835 | struct qeth_card *card = dev_get_drvdata(dev); | |
836 | ||
837 | if (!card) | |
838 | return -EINVAL; | |
839 | ||
840 | return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); | |
841 | } | |
842 | ||
843 | static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, | |
844 | qeth_l3_dev_vipa_del6_store); | |
845 | ||
846 | static struct attribute *qeth_vipa_device_attrs[] = { | |
847 | &dev_attr_vipa_add4.attr, | |
848 | &dev_attr_vipa_del4.attr, | |
849 | &dev_attr_vipa_add6.attr, | |
850 | &dev_attr_vipa_del6.attr, | |
851 | NULL, | |
852 | }; | |
853 | ||
cfe9a042 | 854 | static const struct attribute_group qeth_device_vipa_group = { |
4a71df50 FB |
855 | .name = "vipa", |
856 | .attrs = qeth_vipa_device_attrs, | |
857 | }; | |
858 | ||
859 | static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card, | |
860 | enum qeth_prot_versions proto) | |
861 | { | |
862 | struct qeth_ipaddr *ipaddr; | |
863 | char addr_str[40]; | |
e48b9eaa | 864 | int str_len = 0; |
4a71df50 | 865 | int entry_len; /* length of 1 entry string, differs between v4 and v6 */ |
e48b9eaa | 866 | int i; |
4a71df50 FB |
867 | |
868 | entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; | |
869 | entry_len += 2; /* \n + terminator */ | |
5f78e29c | 870 | spin_lock_bh(&card->ip_lock); |
e48b9eaa | 871 | hash_for_each(card->ip_htable, i, ipaddr, hnode) { |
4a71df50 FB |
872 | if (ipaddr->proto != proto) |
873 | continue; | |
874 | if (ipaddr->type != QETH_IP_TYPE_RXIP) | |
875 | continue; | |
876 | /* String must not be longer than PAGE_SIZE. So we check if | |
877 | * string length gets near PAGE_SIZE. Then we can savely display | |
878 | * the next IPv6 address (worst case, compared to IPv4) */ | |
e48b9eaa | 879 | if ((PAGE_SIZE - str_len) <= entry_len) |
4a71df50 FB |
880 | break; |
881 | qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, | |
882 | addr_str); | |
e48b9eaa UB |
883 | str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", |
884 | addr_str); | |
4a71df50 | 885 | } |
5f78e29c | 886 | spin_unlock_bh(&card->ip_lock); |
e48b9eaa | 887 | str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); |
4a71df50 | 888 | |
e48b9eaa | 889 | return str_len; |
4a71df50 FB |
890 | } |
891 | ||
892 | static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev, | |
893 | struct device_attribute *attr, char *buf) | |
894 | { | |
895 | struct qeth_card *card = dev_get_drvdata(dev); | |
896 | ||
897 | if (!card) | |
898 | return -EINVAL; | |
899 | ||
900 | return qeth_l3_dev_rxip_add_show(buf, card, QETH_PROT_IPV4); | |
901 | } | |
902 | ||
903 | static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto, | |
904 | u8 *addr) | |
905 | { | |
aa9bea0b KM |
906 | __be32 ipv4_addr; |
907 | struct in6_addr ipv6_addr; | |
908 | ||
4a71df50 | 909 | if (qeth_l3_string_to_ipaddr(buf, proto, addr)) { |
4a71df50 FB |
910 | return -EINVAL; |
911 | } | |
aa9bea0b KM |
912 | if (proto == QETH_PROT_IPV4) { |
913 | memcpy(&ipv4_addr, addr, sizeof(ipv4_addr)); | |
914 | if (ipv4_is_multicast(ipv4_addr)) { | |
915 | QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n"); | |
916 | return -EINVAL; | |
917 | } | |
918 | } else if (proto == QETH_PROT_IPV6) { | |
919 | memcpy(&ipv6_addr, addr, sizeof(ipv6_addr)); | |
920 | if (ipv6_addr_is_multicast(&ipv6_addr)) { | |
921 | QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n"); | |
922 | return -EINVAL; | |
923 | } | |
924 | } | |
925 | ||
4a71df50 FB |
926 | return 0; |
927 | } | |
928 | ||
929 | static ssize_t qeth_l3_dev_rxip_add_store(const char *buf, size_t count, | |
930 | struct qeth_card *card, enum qeth_prot_versions proto) | |
931 | { | |
932 | u8 addr[16] = {0, }; | |
933 | int rc; | |
934 | ||
c4949f07 | 935 | mutex_lock(&card->conf_mutex); |
4a71df50 | 936 | rc = qeth_l3_parse_rxipe(buf, proto, addr); |
c4949f07 FB |
937 | if (!rc) |
938 | rc = qeth_l3_add_rxip(card, proto, addr); | |
939 | mutex_unlock(&card->conf_mutex); | |
940 | return rc ? rc : count; | |
4a71df50 FB |
941 | } |
942 | ||
943 | static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, | |
944 | struct device_attribute *attr, const char *buf, size_t count) | |
945 | { | |
946 | struct qeth_card *card = dev_get_drvdata(dev); | |
947 | ||
948 | if (!card) | |
949 | return -EINVAL; | |
950 | ||
951 | return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); | |
952 | } | |
953 | ||
954 | static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, | |
955 | qeth_l3_dev_rxip_add4_show, | |
956 | qeth_l3_dev_rxip_add4_store); | |
957 | ||
958 | static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count, | |
959 | struct qeth_card *card, enum qeth_prot_versions proto) | |
960 | { | |
961 | u8 addr[16]; | |
962 | int rc; | |
963 | ||
c4949f07 | 964 | mutex_lock(&card->conf_mutex); |
4a71df50 | 965 | rc = qeth_l3_parse_rxipe(buf, proto, addr); |
c4949f07 FB |
966 | if (!rc) |
967 | qeth_l3_del_rxip(card, proto, addr); | |
968 | mutex_unlock(&card->conf_mutex); | |
969 | return rc ? rc : count; | |
4a71df50 FB |
970 | } |
971 | ||
972 | static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, | |
973 | struct device_attribute *attr, const char *buf, size_t count) | |
974 | { | |
975 | struct qeth_card *card = dev_get_drvdata(dev); | |
976 | ||
977 | if (!card) | |
978 | return -EINVAL; | |
979 | ||
980 | return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); | |
981 | } | |
982 | ||
983 | static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, | |
984 | qeth_l3_dev_rxip_del4_store); | |
985 | ||
986 | static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev, | |
987 | struct device_attribute *attr, char *buf) | |
988 | { | |
989 | struct qeth_card *card = dev_get_drvdata(dev); | |
990 | ||
991 | if (!card) | |
992 | return -EINVAL; | |
993 | ||
994 | return qeth_l3_dev_rxip_add_show(buf, card, QETH_PROT_IPV6); | |
995 | } | |
996 | ||
997 | static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, | |
998 | struct device_attribute *attr, const char *buf, size_t count) | |
999 | { | |
1000 | struct qeth_card *card = dev_get_drvdata(dev); | |
1001 | ||
1002 | if (!card) | |
1003 | return -EINVAL; | |
1004 | ||
1005 | return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); | |
1006 | } | |
1007 | ||
1008 | static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, | |
1009 | qeth_l3_dev_rxip_add6_show, | |
1010 | qeth_l3_dev_rxip_add6_store); | |
1011 | ||
1012 | static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, | |
1013 | struct device_attribute *attr, const char *buf, size_t count) | |
1014 | { | |
1015 | struct qeth_card *card = dev_get_drvdata(dev); | |
1016 | ||
1017 | if (!card) | |
1018 | return -EINVAL; | |
1019 | ||
1020 | return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); | |
1021 | } | |
1022 | ||
1023 | static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, | |
1024 | qeth_l3_dev_rxip_del6_store); | |
1025 | ||
1026 | static struct attribute *qeth_rxip_device_attrs[] = { | |
1027 | &dev_attr_rxip_add4.attr, | |
1028 | &dev_attr_rxip_del4.attr, | |
1029 | &dev_attr_rxip_add6.attr, | |
1030 | &dev_attr_rxip_del6.attr, | |
1031 | NULL, | |
1032 | }; | |
1033 | ||
cfe9a042 | 1034 | static const struct attribute_group qeth_device_rxip_group = { |
4a71df50 FB |
1035 | .name = "rxip", |
1036 | .attrs = qeth_rxip_device_attrs, | |
1037 | }; | |
1038 | ||
ab25a501 JW |
1039 | static const struct attribute_group *qeth_l3_only_attr_groups[] = { |
1040 | &qeth_l3_device_attr_group, | |
1041 | &qeth_device_ipato_group, | |
1042 | &qeth_device_vipa_group, | |
1043 | &qeth_device_rxip_group, | |
1044 | NULL, | |
1045 | }; | |
1046 | ||
4a71df50 FB |
1047 | int qeth_l3_create_device_attributes(struct device *dev) |
1048 | { | |
ab25a501 | 1049 | return sysfs_create_groups(&dev->kobj, qeth_l3_only_attr_groups); |
4a71df50 FB |
1050 | } |
1051 | ||
1052 | void qeth_l3_remove_device_attributes(struct device *dev) | |
1053 | { | |
ab25a501 | 1054 | sysfs_remove_groups(&dev->kobj, qeth_l3_only_attr_groups); |
4a71df50 | 1055 | } |
79a04e40 UB |
1056 | |
1057 | const struct attribute_group *qeth_l3_attr_groups[] = { | |
1058 | &qeth_device_attr_group, | |
1059 | &qeth_device_blkt_group, | |
ab25a501 | 1060 | /* l3 specific, see qeth_l3_only_attr_groups: */ |
79a04e40 UB |
1061 | &qeth_l3_device_attr_group, |
1062 | &qeth_device_ipato_group, | |
1063 | &qeth_device_vipa_group, | |
1064 | &qeth_device_rxip_group, | |
ab25a501 | 1065 | NULL, |
79a04e40 | 1066 | }; |