diff options
author | Eugene Crosser <Eugene.Crosser@ru.ibm.com> | 2014-01-14 09:54:11 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-01-15 17:48:01 -0500 |
commit | b4d72c08b358fc5b259fad0f4971112d949efd1c (patch) | |
tree | f2bcd879d3957e353e3c7d61a91b5d1a594d7db8 /drivers/s390 | |
parent | 3977458c9c61fc27a5a88cfcf51696b838d1ecc9 (diff) |
qeth: bridgeport support - basic control
Introduce functions to assign roles and check state of bridgeport-capable
HiperSocket devices, and sysfs attributes providing access to these
functions from userspace. Introduce udev events emitted when the state
of a bridgeport device changes.
Signed-off-by: Eugene Crosser <eugene.crosser@ru.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Reviewed-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 25 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 14 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.c | 1 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 84 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2.h | 15 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 364 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_sys.c | 161 |
8 files changed, 664 insertions, 2 deletions
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile index 4dfe8c1092da..d28f05d0c75a 100644 --- a/drivers/s390/net/Makefile +++ b/drivers/s390/net/Makefile | |||
@@ -11,7 +11,7 @@ obj-$(CONFIG_LCS) += lcs.o | |||
11 | obj-$(CONFIG_CLAW) += claw.o | 11 | obj-$(CONFIG_CLAW) += claw.o |
12 | qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o | 12 | qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o |
13 | obj-$(CONFIG_QETH) += qeth.o | 13 | obj-$(CONFIG_QETH) += qeth.o |
14 | qeth_l2-y += qeth_l2_main.o | 14 | qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o |
15 | obj-$(CONFIG_QETH_L2) += qeth_l2.o | 15 | obj-$(CONFIG_QETH_L2) += qeth_l2.o |
16 | qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o | 16 | qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o |
17 | obj-$(CONFIG_QETH_L3) += qeth_l3.o | 17 | obj-$(CONFIG_QETH_L3) += qeth_l3.o |
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d45427c553b0..010f49e3e3ac 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h | |||
@@ -156,6 +156,24 @@ struct qeth_ipa_info { | |||
156 | __u32 enabled_funcs; | 156 | __u32 enabled_funcs; |
157 | }; | 157 | }; |
158 | 158 | ||
159 | /* SETBRIDGEPORT stuff */ | ||
160 | enum qeth_sbp_roles { | ||
161 | QETH_SBP_ROLE_NONE = 0, | ||
162 | QETH_SBP_ROLE_PRIMARY = 1, | ||
163 | QETH_SBP_ROLE_SECONDARY = 2, | ||
164 | }; | ||
165 | |||
166 | enum qeth_sbp_states { | ||
167 | QETH_SBP_STATE_INACTIVE = 0, | ||
168 | QETH_SBP_STATE_STANDBY = 1, | ||
169 | QETH_SBP_STATE_ACTIVE = 2, | ||
170 | }; | ||
171 | |||
172 | struct qeth_sbp_info { | ||
173 | __u32 supported_funcs; | ||
174 | enum qeth_sbp_roles role; | ||
175 | }; | ||
176 | |||
159 | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, | 177 | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, |
160 | enum qeth_ipa_funcs func) | 178 | enum qeth_ipa_funcs func) |
161 | { | 179 | { |
@@ -672,6 +690,7 @@ struct qeth_card_options { | |||
672 | struct qeth_ipa_info adp; /*Adapter parameters*/ | 690 | struct qeth_ipa_info adp; /*Adapter parameters*/ |
673 | struct qeth_routing_info route6; | 691 | struct qeth_routing_info route6; |
674 | struct qeth_ipa_info ipa6; | 692 | struct qeth_ipa_info ipa6; |
693 | struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ | ||
675 | int fake_broadcast; | 694 | int fake_broadcast; |
676 | int add_hhlen; | 695 | int add_hhlen; |
677 | int layer2; | 696 | int layer2; |
@@ -857,6 +876,7 @@ extern struct qeth_discipline qeth_l2_discipline; | |||
857 | extern struct qeth_discipline qeth_l3_discipline; | 876 | extern struct qeth_discipline qeth_l3_discipline; |
858 | extern const struct attribute_group *qeth_generic_attr_groups[]; | 877 | extern const struct attribute_group *qeth_generic_attr_groups[]; |
859 | extern const struct attribute_group *qeth_osn_attr_groups[]; | 878 | extern const struct attribute_group *qeth_osn_attr_groups[]; |
879 | extern struct workqueue_struct *qeth_wq; | ||
860 | 880 | ||
861 | const char *qeth_get_cardname_short(struct qeth_card *); | 881 | const char *qeth_get_cardname_short(struct qeth_card *); |
862 | int qeth_realloc_buffer_pool(struct qeth_card *, int); | 882 | int qeth_realloc_buffer_pool(struct qeth_card *, int); |
@@ -925,6 +945,11 @@ int qeth_query_card_info(struct qeth_card *card, | |||
925 | int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, | 945 | int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, |
926 | int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), | 946 | int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), |
927 | void *reply_param); | 947 | void *reply_param); |
948 | void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd); | ||
949 | void qeth_bridgeport_query_support(struct qeth_card *card); | ||
950 | int qeth_bridgeport_query_ports(struct qeth_card *card, | ||
951 | enum qeth_sbp_roles *role, enum qeth_sbp_states *state); | ||
952 | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); | ||
928 | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); | 953 | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); |
929 | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); | 954 | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); |
930 | int qeth_get_elements_for_frags(struct sk_buff *); | 955 | int qeth_get_elements_for_frags(struct sk_buff *); |
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f9a85b47e3c3..1ffea16f51c6 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c | |||
@@ -68,7 +68,7 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | |||
68 | enum qeth_qdio_buffer_states newbufstate); | 68 | enum qeth_qdio_buffer_states newbufstate); |
69 | static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); | 69 | static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); |
70 | 70 | ||
71 | static struct workqueue_struct *qeth_wq; | 71 | struct workqueue_struct *qeth_wq; |
72 | 72 | ||
73 | static void qeth_close_dev_handler(struct work_struct *work) | 73 | static void qeth_close_dev_handler(struct work_struct *work) |
74 | { | 74 | { |
@@ -615,6 +615,13 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, | |||
615 | card->info.hwtrap = 2; | 615 | card->info.hwtrap = 2; |
616 | qeth_schedule_recovery(card); | 616 | qeth_schedule_recovery(card); |
617 | return NULL; | 617 | return NULL; |
618 | case IPA_CMD_SETBRIDGEPORT: | ||
619 | if (cmd->data.sbp.hdr.command_code == | ||
620 | IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { | ||
621 | qeth_bridge_state_change(card, cmd); | ||
622 | return NULL; | ||
623 | } else | ||
624 | return cmd; | ||
618 | case IPA_CMD_MODCCID: | 625 | case IPA_CMD_MODCCID: |
619 | return cmd; | 626 | return cmd; |
620 | case IPA_CMD_REGISTER_LOCAL_ADDR: | 627 | case IPA_CMD_REGISTER_LOCAL_ADDR: |
@@ -4956,12 +4963,17 @@ retriable: | |||
4956 | 4963 | ||
4957 | card->options.ipa4.supported_funcs = 0; | 4964 | card->options.ipa4.supported_funcs = 0; |
4958 | card->options.adp.supported_funcs = 0; | 4965 | card->options.adp.supported_funcs = 0; |
4966 | card->options.sbp.supported_funcs = 0; | ||
4959 | card->info.diagass_support = 0; | 4967 | card->info.diagass_support = 0; |
4960 | qeth_query_ipassists(card, QETH_PROT_IPV4); | 4968 | qeth_query_ipassists(card, QETH_PROT_IPV4); |
4961 | if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) | 4969 | if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) |
4962 | qeth_query_setadapterparms(card); | 4970 | qeth_query_setadapterparms(card); |
4963 | if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) | 4971 | if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) |
4964 | qeth_query_setdiagass(card); | 4972 | qeth_query_setdiagass(card); |
4973 | qeth_bridgeport_query_support(card); | ||
4974 | if (card->options.sbp.supported_funcs) | ||
4975 | dev_info(&card->gdev->dev, | ||
4976 | "The device represents a HiperSockets Bridge Capable Port\n"); | ||
4965 | return 0; | 4977 | return 0; |
4966 | out: | 4978 | out: |
4967 | dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " | 4979 | dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " |
diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c index 06c55780005e..2f44cfd5dd00 100644 --- a/drivers/s390/net/qeth_core_mpc.c +++ b/drivers/s390/net/qeth_core_mpc.c | |||
@@ -249,6 +249,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = { | |||
249 | {IPA_CMD_DELIP, "delip"}, | 249 | {IPA_CMD_DELIP, "delip"}, |
250 | {IPA_CMD_SETADAPTERPARMS, "setadapterparms"}, | 250 | {IPA_CMD_SETADAPTERPARMS, "setadapterparms"}, |
251 | {IPA_CMD_SET_DIAG_ASS, "set_diag_ass"}, | 251 | {IPA_CMD_SET_DIAG_ASS, "set_diag_ass"}, |
252 | {IPA_CMD_SETBRIDGEPORT, "set_bridge_port"}, | ||
252 | {IPA_CMD_CREATE_ADDR, "create_addr"}, | 253 | {IPA_CMD_CREATE_ADDR, "create_addr"}, |
253 | {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, | 254 | {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, |
254 | {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, | 255 | {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, |
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 0a6e695578cd..de6267990c5e 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h | |||
@@ -104,6 +104,7 @@ enum qeth_ipa_cmds { | |||
104 | IPA_CMD_DELIP = 0xb7, | 104 | IPA_CMD_DELIP = 0xb7, |
105 | IPA_CMD_SETADAPTERPARMS = 0xb8, | 105 | IPA_CMD_SETADAPTERPARMS = 0xb8, |
106 | IPA_CMD_SET_DIAG_ASS = 0xb9, | 106 | IPA_CMD_SET_DIAG_ASS = 0xb9, |
107 | IPA_CMD_SETBRIDGEPORT = 0xbe, | ||
107 | IPA_CMD_CREATE_ADDR = 0xc3, | 108 | IPA_CMD_CREATE_ADDR = 0xc3, |
108 | IPA_CMD_DESTROY_ADDR = 0xc4, | 109 | IPA_CMD_DESTROY_ADDR = 0xc4, |
109 | IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, | 110 | IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, |
@@ -500,6 +501,88 @@ struct qeth_ipacmd_diagass { | |||
500 | __u8 cdata[64]; | 501 | __u8 cdata[64]; |
501 | } __attribute__ ((packed)); | 502 | } __attribute__ ((packed)); |
502 | 503 | ||
504 | /* SETBRIDGEPORT IPA Command: *********************************************/ | ||
505 | enum qeth_ipa_sbp_cmd { | ||
506 | IPA_SBP_QUERY_COMMANDS_SUPPORTED = 0x00000000L, | ||
507 | IPA_SBP_RESET_BRIDGE_PORT_ROLE = 0x00000001L, | ||
508 | IPA_SBP_SET_PRIMARY_BRIDGE_PORT = 0x00000002L, | ||
509 | IPA_SBP_SET_SECONDARY_BRIDGE_PORT = 0x00000004L, | ||
510 | IPA_SBP_QUERY_BRIDGE_PORTS = 0x00000008L, | ||
511 | IPA_SBP_BRIDGE_PORT_STATE_CHANGE = 0x00000010L, | ||
512 | }; | ||
513 | |||
514 | struct net_if_token { | ||
515 | __u16 devnum; | ||
516 | __u8 cssid; | ||
517 | __u8 iid; | ||
518 | __u8 ssid; | ||
519 | __u8 chpid; | ||
520 | __u16 chid; | ||
521 | } __packed; | ||
522 | |||
523 | struct qeth_ipacmd_sbp_hdr { | ||
524 | __u32 supported_sbp_cmds; | ||
525 | __u32 enabled_sbp_cmds; | ||
526 | __u16 cmdlength; | ||
527 | __u16 reserved1; | ||
528 | __u32 command_code; | ||
529 | __u16 return_code; | ||
530 | __u8 used_total; | ||
531 | __u8 seq_no; | ||
532 | __u32 reserved2; | ||
533 | } __packed; | ||
534 | |||
535 | struct qeth_sbp_query_cmds_supp { | ||
536 | __u32 supported_cmds; | ||
537 | __u32 reserved; | ||
538 | } __packed; | ||
539 | |||
540 | struct qeth_sbp_reset_role { | ||
541 | } __packed; | ||
542 | |||
543 | struct qeth_sbp_set_primary { | ||
544 | struct net_if_token token; | ||
545 | } __packed; | ||
546 | |||
547 | struct qeth_sbp_set_secondary { | ||
548 | } __packed; | ||
549 | |||
550 | struct qeth_sbp_port_entry { | ||
551 | __u8 role; | ||
552 | __u8 state; | ||
553 | __u8 reserved1; | ||
554 | __u8 reserved2; | ||
555 | struct net_if_token token; | ||
556 | } __packed; | ||
557 | |||
558 | struct qeth_sbp_query_ports { | ||
559 | __u8 primary_bp_supported; | ||
560 | __u8 secondary_bp_supported; | ||
561 | __u8 num_entries; | ||
562 | __u8 entry_length; | ||
563 | struct qeth_sbp_port_entry entry[]; | ||
564 | } __packed; | ||
565 | |||
566 | struct qeth_sbp_state_change { | ||
567 | __u8 primary_bp_supported; | ||
568 | __u8 secondary_bp_supported; | ||
569 | __u8 num_entries; | ||
570 | __u8 entry_length; | ||
571 | struct qeth_sbp_port_entry entry[]; | ||
572 | } __packed; | ||
573 | |||
574 | struct qeth_ipacmd_setbridgeport { | ||
575 | struct qeth_ipacmd_sbp_hdr hdr; | ||
576 | union { | ||
577 | struct qeth_sbp_query_cmds_supp query_cmds_supp; | ||
578 | struct qeth_sbp_reset_role reset_role; | ||
579 | struct qeth_sbp_set_primary set_primary; | ||
580 | struct qeth_sbp_set_secondary set_secondary; | ||
581 | struct qeth_sbp_query_ports query_ports; | ||
582 | struct qeth_sbp_state_change state_change; | ||
583 | } data; | ||
584 | } __packed; | ||
585 | |||
503 | /* Header for each IPA command */ | 586 | /* Header for each IPA command */ |
504 | struct qeth_ipacmd_hdr { | 587 | struct qeth_ipacmd_hdr { |
505 | __u8 command; | 588 | __u8 command; |
@@ -529,6 +612,7 @@ struct qeth_ipa_cmd { | |||
529 | struct qeth_ipacmd_setadpparms setadapterparms; | 612 | struct qeth_ipacmd_setadpparms setadapterparms; |
530 | struct qeth_set_routing setrtg; | 613 | struct qeth_set_routing setrtg; |
531 | struct qeth_ipacmd_diagass diagass; | 614 | struct qeth_ipacmd_diagass diagass; |
615 | struct qeth_ipacmd_setbridgeport sbp; | ||
532 | } data; | 616 | } data; |
533 | } __attribute__ ((packed)); | 617 | } __attribute__ ((packed)); |
534 | 618 | ||
diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h new file mode 100644 index 000000000000..0767556404bd --- /dev/null +++ b/drivers/s390/net/qeth_l2.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2013 | ||
3 | * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | ||
4 | */ | ||
5 | |||
6 | #ifndef __QETH_L2_H__ | ||
7 | #define __QETH_L2_H__ | ||
8 | |||
9 | #include "qeth_core.h" | ||
10 | |||
11 | int qeth_l2_create_device_attributes(struct device *); | ||
12 | void qeth_l2_remove_device_attributes(struct device *); | ||
13 | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); | ||
14 | |||
15 | #endif /* __QETH_L2_H__ */ | ||
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ec8ccdae7aba..875d080e4e86 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
22 | 22 | ||
23 | #include "qeth_core.h" | 23 | #include "qeth_core.h" |
24 | #include "qeth_l2.h" | ||
24 | 25 | ||
25 | static int qeth_l2_set_offline(struct ccwgroup_device *); | 26 | static int qeth_l2_set_offline(struct ccwgroup_device *); |
26 | static int qeth_l2_stop(struct net_device *); | 27 | static int qeth_l2_stop(struct net_device *); |
@@ -880,6 +881,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) | |||
880 | { | 881 | { |
881 | struct qeth_card *card = dev_get_drvdata(&gdev->dev); | 882 | struct qeth_card *card = dev_get_drvdata(&gdev->dev); |
882 | 883 | ||
884 | qeth_l2_create_device_attributes(&gdev->dev); | ||
883 | INIT_LIST_HEAD(&card->vid_list); | 885 | INIT_LIST_HEAD(&card->vid_list); |
884 | INIT_LIST_HEAD(&card->mc_list); | 886 | INIT_LIST_HEAD(&card->mc_list); |
885 | card->options.layer2 = 1; | 887 | card->options.layer2 = 1; |
@@ -891,6 +893,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) | |||
891 | { | 893 | { |
892 | struct qeth_card *card = dev_get_drvdata(&cgdev->dev); | 894 | struct qeth_card *card = dev_get_drvdata(&cgdev->dev); |
893 | 895 | ||
896 | qeth_l2_remove_device_attributes(&cgdev->dev); | ||
894 | qeth_set_allowed_threads(card, 0, 1); | 897 | qeth_set_allowed_threads(card, 0, 1); |
895 | wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); | 898 | wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); |
896 | 899 | ||
@@ -1003,6 +1006,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) | |||
1003 | } else | 1006 | } else |
1004 | card->info.hwtrap = 0; | 1007 | card->info.hwtrap = 0; |
1005 | 1008 | ||
1009 | qeth_l2_setup_bridgeport_attrs(card); | ||
1010 | |||
1006 | card->state = CARD_STATE_HARDSETUP; | 1011 | card->state = CARD_STATE_HARDSETUP; |
1007 | memset(&card->rx, 0, sizeof(struct qeth_rx)); | 1012 | memset(&card->rx, 0, sizeof(struct qeth_rx)); |
1008 | qeth_print_status_message(card); | 1013 | qeth_print_status_message(card); |
@@ -1347,6 +1352,365 @@ void qeth_osn_deregister(struct net_device *dev) | |||
1347 | } | 1352 | } |
1348 | EXPORT_SYMBOL(qeth_osn_deregister); | 1353 | EXPORT_SYMBOL(qeth_osn_deregister); |
1349 | 1354 | ||
1355 | /* SETBRIDGEPORT support, async notifications */ | ||
1356 | |||
1357 | struct qeth_bridge_state_data { | ||
1358 | struct work_struct worker; | ||
1359 | struct qeth_card *card; | ||
1360 | struct qeth_sbp_state_change qports; | ||
1361 | }; | ||
1362 | |||
1363 | static void qeth_bridge_state_change_worker(struct work_struct *work) | ||
1364 | { | ||
1365 | struct qeth_bridge_state_data *data = | ||
1366 | container_of(work, struct qeth_bridge_state_data, worker); | ||
1367 | /* We are only interested in the first entry - local port */ | ||
1368 | struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; | ||
1369 | char env_locrem[32]; | ||
1370 | char env_role[32]; | ||
1371 | char env_state[32]; | ||
1372 | char *env[] = { | ||
1373 | env_locrem, | ||
1374 | env_role, | ||
1375 | env_state, | ||
1376 | NULL | ||
1377 | }; | ||
1378 | |||
1379 | /* Role should not change by itself, but if it did, */ | ||
1380 | /* information from the hardware is authoritative. */ | ||
1381 | mutex_lock(&data->card->conf_mutex); | ||
1382 | data->card->options.sbp.role = entry->role; | ||
1383 | mutex_unlock(&data->card->conf_mutex); | ||
1384 | |||
1385 | snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); | ||
1386 | snprintf(env_role, sizeof(env_role), "ROLE=%s", | ||
1387 | (entry->role == QETH_SBP_ROLE_NONE) ? "none" : | ||
1388 | (entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : | ||
1389 | (entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : | ||
1390 | "<INVALID>"); | ||
1391 | snprintf(env_state, sizeof(env_state), "STATE=%s", | ||
1392 | (entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : | ||
1393 | (entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : | ||
1394 | (entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : | ||
1395 | "<INVALID>"); | ||
1396 | kobject_uevent_env(&data->card->gdev->dev.kobj, | ||
1397 | KOBJ_CHANGE, env); | ||
1398 | kfree(data); | ||
1399 | } | ||
1400 | |||
1401 | void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd) | ||
1402 | { | ||
1403 | struct qeth_sbp_state_change *qports = | ||
1404 | &cmd->data.sbp.data.state_change; | ||
1405 | struct qeth_bridge_state_data *data; | ||
1406 | int extrasize; | ||
1407 | |||
1408 | QETH_CARD_TEXT(card, 2, "brstchng"); | ||
1409 | if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||
1410 | QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); | ||
1411 | return; | ||
1412 | } | ||
1413 | extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; | ||
1414 | data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, | ||
1415 | GFP_ATOMIC); | ||
1416 | if (!data) { | ||
1417 | QETH_CARD_TEXT(card, 2, "BPSalloc"); | ||
1418 | return; | ||
1419 | } | ||
1420 | INIT_WORK(&data->worker, qeth_bridge_state_change_worker); | ||
1421 | data->card = card; | ||
1422 | memcpy(&data->qports, qports, | ||
1423 | sizeof(struct qeth_sbp_state_change) + extrasize); | ||
1424 | queue_work(qeth_wq, &data->worker); | ||
1425 | } | ||
1426 | EXPORT_SYMBOL(qeth_bridge_state_change); | ||
1427 | |||
1428 | /* SETBRIDGEPORT support; sending commands */ | ||
1429 | |||
1430 | struct _qeth_sbp_cbctl { | ||
1431 | u16 ipa_rc; | ||
1432 | u16 cmd_rc; | ||
1433 | union { | ||
1434 | u32 supported; | ||
1435 | struct { | ||
1436 | enum qeth_sbp_roles *role; | ||
1437 | enum qeth_sbp_states *state; | ||
1438 | } qports; | ||
1439 | } data; | ||
1440 | }; | ||
1441 | |||
1442 | /** | ||
1443 | * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. | ||
1444 | * @card: qeth_card structure pointer, for debug messages. | ||
1445 | * @cbctl: state structure with hardware return codes. | ||
1446 | * @setcmd: IPA command code | ||
1447 | * | ||
1448 | * Returns negative errno-compatible error indication or 0 on success. | ||
1449 | */ | ||
1450 | static int qeth_bridgeport_makerc(struct qeth_card *card, | ||
1451 | struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) | ||
1452 | { | ||
1453 | int rc; | ||
1454 | |||
1455 | switch (cbctl->ipa_rc) { | ||
1456 | case IPA_RC_SUCCESS: | ||
1457 | switch (cbctl->cmd_rc) { | ||
1458 | case 0x0000: | ||
1459 | rc = 0; | ||
1460 | break; | ||
1461 | case 0x0004: | ||
1462 | rc = -ENOSYS; | ||
1463 | break; | ||
1464 | case 0x000C: /* Not configured as bridge Port */ | ||
1465 | rc = -ENODEV; /* maybe not the best code here? */ | ||
1466 | dev_err(&card->gdev->dev, | ||
1467 | "The HiperSockets device is not configured as a Bridge Port\n"); | ||
1468 | break; | ||
1469 | case 0x0014: /* Another device is Primary */ | ||
1470 | switch (setcmd) { | ||
1471 | case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: | ||
1472 | rc = -EEXIST; | ||
1473 | dev_err(&card->gdev->dev, | ||
1474 | "The HiperSockets LAN already has a primary Bridge Port\n"); | ||
1475 | break; | ||
1476 | case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: | ||
1477 | rc = -EBUSY; | ||
1478 | dev_err(&card->gdev->dev, | ||
1479 | "The HiperSockets device is already a primary Bridge Port\n"); | ||
1480 | break; | ||
1481 | default: | ||
1482 | rc = -EIO; | ||
1483 | } | ||
1484 | break; | ||
1485 | case 0x0018: /* This device is currently Secondary */ | ||
1486 | rc = -EBUSY; | ||
1487 | dev_err(&card->gdev->dev, | ||
1488 | "The HiperSockets device is already a secondary Bridge Port\n"); | ||
1489 | break; | ||
1490 | case 0x001C: /* Limit for Secondary devices reached */ | ||
1491 | rc = -EEXIST; | ||
1492 | dev_err(&card->gdev->dev, | ||
1493 | "The HiperSockets LAN cannot have more secondary Bridge Ports\n"); | ||
1494 | break; | ||
1495 | case 0x0024: /* This device is currently Primary */ | ||
1496 | rc = -EBUSY; | ||
1497 | dev_err(&card->gdev->dev, | ||
1498 | "The HiperSockets device is already a primary Bridge Port\n"); | ||
1499 | break; | ||
1500 | case 0x0020: /* Not authorized by zManager */ | ||
1501 | rc = -EACCES; | ||
1502 | dev_err(&card->gdev->dev, | ||
1503 | "The HiperSockets device is not authorized to be a Bridge Port\n"); | ||
1504 | break; | ||
1505 | default: | ||
1506 | rc = -EIO; | ||
1507 | } | ||
1508 | break; | ||
1509 | case IPA_RC_NOTSUPP: | ||
1510 | rc = -ENOSYS; | ||
1511 | break; | ||
1512 | case IPA_RC_UNSUPPORTED_COMMAND: | ||
1513 | rc = -ENOSYS; | ||
1514 | break; | ||
1515 | default: | ||
1516 | rc = -EIO; | ||
1517 | } | ||
1518 | if (rc) { | ||
1519 | QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); | ||
1520 | QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); | ||
1521 | } | ||
1522 | return rc; | ||
1523 | } | ||
1524 | |||
1525 | static int qeth_bridgeport_query_support_cb(struct qeth_card *card, | ||
1526 | struct qeth_reply *reply, unsigned long data) | ||
1527 | { | ||
1528 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||
1529 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1530 | QETH_CARD_TEXT(card, 2, "brqsupcb"); | ||
1531 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1532 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1533 | if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { | ||
1534 | cbctl->data.supported = | ||
1535 | cmd->data.sbp.data.query_cmds_supp.supported_cmds; | ||
1536 | } else { | ||
1537 | cbctl->data.supported = 0; | ||
1538 | } | ||
1539 | return 0; | ||
1540 | } | ||
1541 | |||
1542 | /** | ||
1543 | * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. | ||
1544 | * @card: qeth_card structure pointer. | ||
1545 | * | ||
1546 | * Sets bitmask of supported setbridgeport subfunctions in the qeth_card | ||
1547 | * strucutre: card->options.sbp.supported_funcs. | ||
1548 | */ | ||
1549 | void qeth_bridgeport_query_support(struct qeth_card *card) | ||
1550 | { | ||
1551 | struct qeth_cmd_buffer *iob; | ||
1552 | struct qeth_ipa_cmd *cmd; | ||
1553 | struct _qeth_sbp_cbctl cbctl; | ||
1554 | |||
1555 | QETH_CARD_TEXT(card, 2, "brqsuppo"); | ||
1556 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1557 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1558 | cmd->data.sbp.hdr.cmdlength = | ||
1559 | sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1560 | sizeof(struct qeth_sbp_query_cmds_supp); | ||
1561 | cmd->data.sbp.hdr.command_code = | ||
1562 | IPA_SBP_QUERY_COMMANDS_SUPPORTED; | ||
1563 | cmd->data.sbp.hdr.used_total = 1; | ||
1564 | cmd->data.sbp.hdr.seq_no = 1; | ||
1565 | if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, | ||
1566 | (void *)&cbctl) || | ||
1567 | qeth_bridgeport_makerc(card, &cbctl, | ||
1568 | IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { | ||
1569 | /* non-zero makerc signifies failure, and produce messages */ | ||
1570 | card->options.sbp.role = QETH_SBP_ROLE_NONE; | ||
1571 | return; | ||
1572 | } | ||
1573 | card->options.sbp.supported_funcs = cbctl.data.supported; | ||
1574 | } | ||
1575 | EXPORT_SYMBOL_GPL(qeth_bridgeport_query_support); | ||
1576 | |||
1577 | static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, | ||
1578 | struct qeth_reply *reply, unsigned long data) | ||
1579 | { | ||
1580 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||
1581 | struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; | ||
1582 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1583 | |||
1584 | QETH_CARD_TEXT(card, 2, "brqprtcb"); | ||
1585 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1586 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1587 | if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) | ||
1588 | return 0; | ||
1589 | if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||
1590 | cbctl->cmd_rc = 0xffff; | ||
1591 | QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); | ||
1592 | return 0; | ||
1593 | } | ||
1594 | /* first entry contains the state of the local port */ | ||
1595 | if (qports->num_entries > 0) { | ||
1596 | if (cbctl->data.qports.role) | ||
1597 | *cbctl->data.qports.role = qports->entry[0].role; | ||
1598 | if (cbctl->data.qports.state) | ||
1599 | *cbctl->data.qports.state = qports->entry[0].state; | ||
1600 | } | ||
1601 | return 0; | ||
1602 | } | ||
1603 | |||
1604 | /** | ||
1605 | * qeth_bridgeport_query_ports() - query local bridgeport status. | ||
1606 | * @card: qeth_card structure pointer. | ||
1607 | * @role: Role of the port: 0-none, 1-primary, 2-secondary. | ||
1608 | * @state: State of the port: 0-inactive, 1-standby, 2-active. | ||
1609 | * | ||
1610 | * Returns negative errno-compatible error indication or 0 on success. | ||
1611 | * | ||
1612 | * 'role' and 'state' are not updated in case of hardware operation failure. | ||
1613 | */ | ||
1614 | int qeth_bridgeport_query_ports(struct qeth_card *card, | ||
1615 | enum qeth_sbp_roles *role, enum qeth_sbp_states *state) | ||
1616 | { | ||
1617 | int rc = 0; | ||
1618 | struct qeth_cmd_buffer *iob; | ||
1619 | struct qeth_ipa_cmd *cmd; | ||
1620 | struct _qeth_sbp_cbctl cbctl = { | ||
1621 | .data = { | ||
1622 | .qports = { | ||
1623 | .role = role, | ||
1624 | .state = state, | ||
1625 | }, | ||
1626 | }, | ||
1627 | }; | ||
1628 | |||
1629 | QETH_CARD_TEXT(card, 2, "brqports"); | ||
1630 | if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) | ||
1631 | return -EOPNOTSUPP; | ||
1632 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1633 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1634 | cmd->data.sbp.hdr.cmdlength = | ||
1635 | sizeof(struct qeth_ipacmd_sbp_hdr); | ||
1636 | cmd->data.sbp.hdr.command_code = | ||
1637 | IPA_SBP_QUERY_BRIDGE_PORTS; | ||
1638 | cmd->data.sbp.hdr.used_total = 1; | ||
1639 | cmd->data.sbp.hdr.seq_no = 1; | ||
1640 | rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, | ||
1641 | (void *)&cbctl); | ||
1642 | if (rc) | ||
1643 | return rc; | ||
1644 | rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); | ||
1645 | if (rc) | ||
1646 | return rc; | ||
1647 | return 0; | ||
1648 | } | ||
1649 | EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); | ||
1650 | |||
1651 | static int qeth_bridgeport_set_cb(struct qeth_card *card, | ||
1652 | struct qeth_reply *reply, unsigned long data) | ||
1653 | { | ||
1654 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; | ||
1655 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1656 | QETH_CARD_TEXT(card, 2, "brsetrcb"); | ||
1657 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1658 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1659 | return 0; | ||
1660 | } | ||
1661 | |||
1662 | /** | ||
1663 | * qeth_bridgeport_setrole() - Assign primary role to the port. | ||
1664 | * @card: qeth_card structure pointer. | ||
1665 | * @role: Role to assign. | ||
1666 | * | ||
1667 | * Returns negative errno-compatible error indication or 0 on success. | ||
1668 | */ | ||
1669 | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) | ||
1670 | { | ||
1671 | int rc = 0; | ||
1672 | int cmdlength; | ||
1673 | struct qeth_cmd_buffer *iob; | ||
1674 | struct qeth_ipa_cmd *cmd; | ||
1675 | struct _qeth_sbp_cbctl cbctl; | ||
1676 | enum qeth_ipa_sbp_cmd setcmd; | ||
1677 | |||
1678 | QETH_CARD_TEXT(card, 2, "brsetrol"); | ||
1679 | switch (role) { | ||
1680 | case QETH_SBP_ROLE_NONE: | ||
1681 | setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; | ||
1682 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1683 | sizeof(struct qeth_sbp_reset_role); | ||
1684 | break; | ||
1685 | case QETH_SBP_ROLE_PRIMARY: | ||
1686 | setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; | ||
1687 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1688 | sizeof(struct qeth_sbp_set_primary); | ||
1689 | break; | ||
1690 | case QETH_SBP_ROLE_SECONDARY: | ||
1691 | setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; | ||
1692 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1693 | sizeof(struct qeth_sbp_set_secondary); | ||
1694 | break; | ||
1695 | default: | ||
1696 | return -EINVAL; | ||
1697 | } | ||
1698 | if (!(card->options.sbp.supported_funcs & setcmd)) | ||
1699 | return -EOPNOTSUPP; | ||
1700 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1701 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1702 | cmd->data.sbp.hdr.cmdlength = cmdlength; | ||
1703 | cmd->data.sbp.hdr.command_code = setcmd; | ||
1704 | cmd->data.sbp.hdr.used_total = 1; | ||
1705 | cmd->data.sbp.hdr.seq_no = 1; | ||
1706 | rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, | ||
1707 | (void *)&cbctl); | ||
1708 | if (rc) | ||
1709 | return rc; | ||
1710 | rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); | ||
1711 | return rc; | ||
1712 | } | ||
1713 | |||
1350 | module_init(qeth_l2_init); | 1714 | module_init(qeth_l2_init); |
1351 | module_exit(qeth_l2_exit); | 1715 | module_exit(qeth_l2_exit); |
1352 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | 1716 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); |
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c new file mode 100644 index 000000000000..17fd4cd888f9 --- /dev/null +++ b/drivers/s390/net/qeth_l2_sys.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | * Copyright IBM Corp. 2013 | ||
3 | * Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | ||
4 | */ | ||
5 | |||
6 | #include <linux/slab.h> | ||
7 | #include <asm/ebcdic.h> | ||
8 | #include "qeth_l2.h" | ||
9 | |||
10 | #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ | ||
11 | struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) | ||
12 | |||
13 | static int qeth_card_hw_is_reachable(struct qeth_card *card) | ||
14 | { | ||
15 | return (card->state == CARD_STATE_SOFTSETUP) || | ||
16 | (card->state == CARD_STATE_UP); | ||
17 | } | ||
18 | |||
19 | static ssize_t qeth_bridge_port_role_state_show(struct device *dev, | ||
20 | struct device_attribute *attr, char *buf, | ||
21 | int show_state) | ||
22 | { | ||
23 | struct qeth_card *card = dev_get_drvdata(dev); | ||
24 | enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; | ||
25 | int rc = 0; | ||
26 | char *word; | ||
27 | |||
28 | if (!card) | ||
29 | return -EINVAL; | ||
30 | |||
31 | mutex_lock(&card->conf_mutex); | ||
32 | |||
33 | if (qeth_card_hw_is_reachable(card) && | ||
34 | card->options.sbp.supported_funcs) | ||
35 | rc = qeth_bridgeport_query_ports(card, | ||
36 | &card->options.sbp.role, &state); | ||
37 | if (!rc) { | ||
38 | if (show_state) | ||
39 | switch (state) { | ||
40 | case QETH_SBP_STATE_INACTIVE: | ||
41 | word = "inactive"; break; | ||
42 | case QETH_SBP_STATE_STANDBY: | ||
43 | word = "standby"; break; | ||
44 | case QETH_SBP_STATE_ACTIVE: | ||
45 | word = "active"; break; | ||
46 | default: | ||
47 | rc = -EIO; | ||
48 | } | ||
49 | else | ||
50 | switch (card->options.sbp.role) { | ||
51 | case QETH_SBP_ROLE_NONE: | ||
52 | word = "none"; break; | ||
53 | case QETH_SBP_ROLE_PRIMARY: | ||
54 | word = "primary"; break; | ||
55 | case QETH_SBP_ROLE_SECONDARY: | ||
56 | word = "secondary"; break; | ||
57 | default: | ||
58 | rc = -EIO; | ||
59 | } | ||
60 | if (rc) | ||
61 | QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", | ||
62 | card->options.sbp.role, state); | ||
63 | else | ||
64 | rc = sprintf(buf, "%s\n", word); | ||
65 | } | ||
66 | |||
67 | mutex_unlock(&card->conf_mutex); | ||
68 | |||
69 | return rc; | ||
70 | } | ||
71 | |||
72 | static ssize_t qeth_bridge_port_role_show(struct device *dev, | ||
73 | struct device_attribute *attr, char *buf) | ||
74 | { | ||
75 | return qeth_bridge_port_role_state_show(dev, attr, buf, 0); | ||
76 | } | ||
77 | |||
78 | static ssize_t qeth_bridge_port_role_store(struct device *dev, | ||
79 | struct device_attribute *attr, const char *buf, size_t count) | ||
80 | { | ||
81 | struct qeth_card *card = dev_get_drvdata(dev); | ||
82 | int rc = 0; | ||
83 | enum qeth_sbp_roles role; | ||
84 | |||
85 | if (!card) | ||
86 | return -EINVAL; | ||
87 | if (sysfs_streq(buf, "primary")) | ||
88 | role = QETH_SBP_ROLE_PRIMARY; | ||
89 | else if (sysfs_streq(buf, "secondary")) | ||
90 | role = QETH_SBP_ROLE_SECONDARY; | ||
91 | else if (sysfs_streq(buf, "none")) | ||
92 | role = QETH_SBP_ROLE_NONE; | ||
93 | else | ||
94 | return -EINVAL; | ||
95 | |||
96 | mutex_lock(&card->conf_mutex); | ||
97 | |||
98 | if (qeth_card_hw_is_reachable(card)) { | ||
99 | rc = qeth_bridgeport_setrole(card, role); | ||
100 | if (!rc) | ||
101 | card->options.sbp.role = role; | ||
102 | } else | ||
103 | card->options.sbp.role = role; | ||
104 | |||
105 | mutex_unlock(&card->conf_mutex); | ||
106 | |||
107 | return rc ? rc : count; | ||
108 | } | ||
109 | |||
110 | static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, | ||
111 | qeth_bridge_port_role_store); | ||
112 | |||
113 | static ssize_t qeth_bridge_port_state_show(struct device *dev, | ||
114 | struct device_attribute *attr, char *buf) | ||
115 | { | ||
116 | return qeth_bridge_port_role_state_show(dev, attr, buf, 1); | ||
117 | } | ||
118 | |||
119 | static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, | ||
120 | NULL); | ||
121 | |||
122 | static struct attribute *qeth_l2_bridgeport_attrs[] = { | ||
123 | &dev_attr_bridge_role.attr, | ||
124 | &dev_attr_bridge_state.attr, | ||
125 | NULL, | ||
126 | }; | ||
127 | |||
128 | static struct attribute_group qeth_l2_bridgeport_attr_group = { | ||
129 | .attrs = qeth_l2_bridgeport_attrs, | ||
130 | }; | ||
131 | |||
132 | int qeth_l2_create_device_attributes(struct device *dev) | ||
133 | { | ||
134 | return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | ||
135 | } | ||
136 | |||
137 | void qeth_l2_remove_device_attributes(struct device *dev) | ||
138 | { | ||
139 | sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online. | ||
144 | * @card: qeth_card structure pointer | ||
145 | * | ||
146 | * Note: this function is called with conf_mutex held by the caller | ||
147 | */ | ||
148 | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) | ||
149 | { | ||
150 | if (!card) | ||
151 | return; | ||
152 | if (!card->options.sbp.supported_funcs) | ||
153 | return; | ||
154 | if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { | ||
155 | /* Conditional to avoid spurious error messages */ | ||
156 | qeth_bridgeport_setrole(card, card->options.sbp.role); | ||
157 | /* Let the callback function refresh the stored role value. */ | ||
158 | qeth_bridgeport_query_ports(card, | ||
159 | &card->options.sbp.role, NULL); | ||
160 | } | ||
161 | } | ||