forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									3977458c9c
								
							
						
					
					
						commit
						b4d72c08b3
					
				
					 9 changed files with 685 additions and 2 deletions
				
			
		
							
								
								
									
										21
									
								
								Documentation/s390/qeth.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Documentation/s390/qeth.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | IBM s390 QDIO Ethernet Driver | ||||||
|  | 
 | ||||||
|  | HiperSockets Bridge Port Support | ||||||
|  | 
 | ||||||
|  | Uevents | ||||||
|  | 
 | ||||||
|  | To generate the events the device must be assigned a role of either | ||||||
|  | a primary or a secondary Bridge Port. For more information, see | ||||||
|  | "z/VM Connectivity, SC24-6174". | ||||||
|  | 
 | ||||||
|  | When run on HiperSockets Bridge Capable Port hardware, and the state | ||||||
|  | of some configured Bridge Port device on the channel changes, a udev | ||||||
|  | event with ACTION=CHANGE is emitted on behalf of the corresponding | ||||||
|  | ccwgroup device. The event has the following attributes: | ||||||
|  | 
 | ||||||
|  | BRIDGEPORT=statechange -  indicates that the Bridge Port device changed | ||||||
|  |   its state. | ||||||
|  | 
 | ||||||
|  | ROLE={primary|secondary|none} - the role assigned to the port. | ||||||
|  | 
 | ||||||
|  | STATE={active|standby|inactive} - the newly assumed state of the port. | ||||||
|  | @ -11,7 +11,7 @@ obj-$(CONFIG_LCS) += lcs.o | ||||||
| obj-$(CONFIG_CLAW) += claw.o | obj-$(CONFIG_CLAW) += claw.o | ||||||
| qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o | qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o | ||||||
| obj-$(CONFIG_QETH) += qeth.o | obj-$(CONFIG_QETH) += qeth.o | ||||||
| qeth_l2-y += qeth_l2_main.o | qeth_l2-y += qeth_l2_main.o qeth_l2_sys.o | ||||||
| obj-$(CONFIG_QETH_L2) += qeth_l2.o | obj-$(CONFIG_QETH_L2) += qeth_l2.o | ||||||
| qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o | qeth_l3-y += qeth_l3_main.o qeth_l3_sys.o | ||||||
| obj-$(CONFIG_QETH_L3) += qeth_l3.o | obj-$(CONFIG_QETH_L3) += qeth_l3.o | ||||||
|  |  | ||||||
|  | @ -156,6 +156,24 @@ struct qeth_ipa_info { | ||||||
| 	__u32 enabled_funcs; | 	__u32 enabled_funcs; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* SETBRIDGEPORT stuff */ | ||||||
|  | enum qeth_sbp_roles { | ||||||
|  | 	QETH_SBP_ROLE_NONE	= 0, | ||||||
|  | 	QETH_SBP_ROLE_PRIMARY	= 1, | ||||||
|  | 	QETH_SBP_ROLE_SECONDARY	= 2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum qeth_sbp_states { | ||||||
|  | 	QETH_SBP_STATE_INACTIVE	= 0, | ||||||
|  | 	QETH_SBP_STATE_STANDBY	= 1, | ||||||
|  | 	QETH_SBP_STATE_ACTIVE	= 2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_info { | ||||||
|  | 	__u32 supported_funcs; | ||||||
|  | 	enum qeth_sbp_roles role; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, | static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, | ||||||
| 		enum qeth_ipa_funcs func) | 		enum qeth_ipa_funcs func) | ||||||
| { | { | ||||||
|  | @ -672,6 +690,7 @@ struct qeth_card_options { | ||||||
| 	struct qeth_ipa_info adp; /*Adapter parameters*/ | 	struct qeth_ipa_info adp; /*Adapter parameters*/ | ||||||
| 	struct qeth_routing_info route6; | 	struct qeth_routing_info route6; | ||||||
| 	struct qeth_ipa_info ipa6; | 	struct qeth_ipa_info ipa6; | ||||||
|  | 	struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ | ||||||
| 	int fake_broadcast; | 	int fake_broadcast; | ||||||
| 	int add_hhlen; | 	int add_hhlen; | ||||||
| 	int layer2; | 	int layer2; | ||||||
|  | @ -857,6 +876,7 @@ extern struct qeth_discipline qeth_l2_discipline; | ||||||
| extern struct qeth_discipline qeth_l3_discipline; | extern struct qeth_discipline qeth_l3_discipline; | ||||||
| extern const struct attribute_group *qeth_generic_attr_groups[]; | extern const struct attribute_group *qeth_generic_attr_groups[]; | ||||||
| extern const struct attribute_group *qeth_osn_attr_groups[]; | extern const struct attribute_group *qeth_osn_attr_groups[]; | ||||||
|  | extern struct workqueue_struct *qeth_wq; | ||||||
| 
 | 
 | ||||||
| const char *qeth_get_cardname_short(struct qeth_card *); | const char *qeth_get_cardname_short(struct qeth_card *); | ||||||
| int qeth_realloc_buffer_pool(struct qeth_card *, int); | int qeth_realloc_buffer_pool(struct qeth_card *, int); | ||||||
|  | @ -925,6 +945,11 @@ int qeth_query_card_info(struct qeth_card *card, | ||||||
| int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, | int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, | ||||||
| 	int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), | 	int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), | ||||||
| 	void *reply_param); | 	void *reply_param); | ||||||
|  | void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd); | ||||||
|  | void qeth_bridgeport_query_support(struct qeth_card *card); | ||||||
|  | int qeth_bridgeport_query_ports(struct qeth_card *card, | ||||||
|  | 	enum qeth_sbp_roles *role, enum qeth_sbp_states *state); | ||||||
|  | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); | ||||||
| int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); | int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); | ||||||
| int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); | int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); | ||||||
| int qeth_get_elements_for_frags(struct sk_buff *); | int qeth_get_elements_for_frags(struct sk_buff *); | ||||||
|  |  | ||||||
|  | @ -68,7 +68,7 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, | ||||||
| 		enum qeth_qdio_buffer_states newbufstate); | 		enum qeth_qdio_buffer_states newbufstate); | ||||||
| static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); | static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); | ||||||
| 
 | 
 | ||||||
| static struct workqueue_struct *qeth_wq; | struct workqueue_struct *qeth_wq; | ||||||
| 
 | 
 | ||||||
| static void qeth_close_dev_handler(struct work_struct *work) | static void qeth_close_dev_handler(struct work_struct *work) | ||||||
| { | { | ||||||
|  | @ -615,6 +615,13 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, | ||||||
| 					card->info.hwtrap = 2; | 					card->info.hwtrap = 2; | ||||||
| 				qeth_schedule_recovery(card); | 				qeth_schedule_recovery(card); | ||||||
| 				return NULL; | 				return NULL; | ||||||
|  | 			case IPA_CMD_SETBRIDGEPORT: | ||||||
|  | 				if (cmd->data.sbp.hdr.command_code == | ||||||
|  | 					IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { | ||||||
|  | 					qeth_bridge_state_change(card, cmd); | ||||||
|  | 					return NULL; | ||||||
|  | 				} else | ||||||
|  | 					return cmd; | ||||||
| 			case IPA_CMD_MODCCID: | 			case IPA_CMD_MODCCID: | ||||||
| 				return cmd; | 				return cmd; | ||||||
| 			case IPA_CMD_REGISTER_LOCAL_ADDR: | 			case IPA_CMD_REGISTER_LOCAL_ADDR: | ||||||
|  | @ -4956,12 +4963,17 @@ int qeth_core_hardsetup_card(struct qeth_card *card) | ||||||
| 
 | 
 | ||||||
| 	card->options.ipa4.supported_funcs = 0; | 	card->options.ipa4.supported_funcs = 0; | ||||||
| 	card->options.adp.supported_funcs = 0; | 	card->options.adp.supported_funcs = 0; | ||||||
|  | 	card->options.sbp.supported_funcs = 0; | ||||||
| 	card->info.diagass_support = 0; | 	card->info.diagass_support = 0; | ||||||
| 	qeth_query_ipassists(card, QETH_PROT_IPV4); | 	qeth_query_ipassists(card, QETH_PROT_IPV4); | ||||||
| 	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) | 	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) | ||||||
| 		qeth_query_setadapterparms(card); | 		qeth_query_setadapterparms(card); | ||||||
| 	if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) | 	if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) | ||||||
| 		qeth_query_setdiagass(card); | 		qeth_query_setdiagass(card); | ||||||
|  | 	qeth_bridgeport_query_support(card); | ||||||
|  | 	if (card->options.sbp.supported_funcs) | ||||||
|  | 		dev_info(&card->gdev->dev, | ||||||
|  | 		"The device represents a HiperSockets Bridge Capable Port\n"); | ||||||
| 	return 0; | 	return 0; | ||||||
| out: | out: | ||||||
| 	dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " | 	dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " | ||||||
|  |  | ||||||
|  | @ -249,6 +249,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = { | ||||||
| 	{IPA_CMD_DELIP,		"delip"}, | 	{IPA_CMD_DELIP,		"delip"}, | ||||||
| 	{IPA_CMD_SETADAPTERPARMS, "setadapterparms"}, | 	{IPA_CMD_SETADAPTERPARMS, "setadapterparms"}, | ||||||
| 	{IPA_CMD_SET_DIAG_ASS,	"set_diag_ass"}, | 	{IPA_CMD_SET_DIAG_ASS,	"set_diag_ass"}, | ||||||
|  | 	{IPA_CMD_SETBRIDGEPORT,	"set_bridge_port"}, | ||||||
| 	{IPA_CMD_CREATE_ADDR,	"create_addr"}, | 	{IPA_CMD_CREATE_ADDR,	"create_addr"}, | ||||||
| 	{IPA_CMD_DESTROY_ADDR,	"destroy_addr"}, | 	{IPA_CMD_DESTROY_ADDR,	"destroy_addr"}, | ||||||
| 	{IPA_CMD_REGISTER_LOCAL_ADDR,	"register_local_addr"}, | 	{IPA_CMD_REGISTER_LOCAL_ADDR,	"register_local_addr"}, | ||||||
|  |  | ||||||
|  | @ -104,6 +104,7 @@ enum qeth_ipa_cmds { | ||||||
| 	IPA_CMD_DELIP			= 0xb7, | 	IPA_CMD_DELIP			= 0xb7, | ||||||
| 	IPA_CMD_SETADAPTERPARMS		= 0xb8, | 	IPA_CMD_SETADAPTERPARMS		= 0xb8, | ||||||
| 	IPA_CMD_SET_DIAG_ASS		= 0xb9, | 	IPA_CMD_SET_DIAG_ASS		= 0xb9, | ||||||
|  | 	IPA_CMD_SETBRIDGEPORT		= 0xbe, | ||||||
| 	IPA_CMD_CREATE_ADDR		= 0xc3, | 	IPA_CMD_CREATE_ADDR		= 0xc3, | ||||||
| 	IPA_CMD_DESTROY_ADDR		= 0xc4, | 	IPA_CMD_DESTROY_ADDR		= 0xc4, | ||||||
| 	IPA_CMD_REGISTER_LOCAL_ADDR	= 0xd1, | 	IPA_CMD_REGISTER_LOCAL_ADDR	= 0xd1, | ||||||
|  | @ -500,6 +501,88 @@ struct qeth_ipacmd_diagass { | ||||||
| 	__u8   cdata[64]; | 	__u8   cdata[64]; | ||||||
| } __attribute__ ((packed)); | } __attribute__ ((packed)); | ||||||
| 
 | 
 | ||||||
|  | /* SETBRIDGEPORT IPA Command:	 *********************************************/ | ||||||
|  | enum qeth_ipa_sbp_cmd { | ||||||
|  | 	IPA_SBP_QUERY_COMMANDS_SUPPORTED	= 0x00000000L, | ||||||
|  | 	IPA_SBP_RESET_BRIDGE_PORT_ROLE		= 0x00000001L, | ||||||
|  | 	IPA_SBP_SET_PRIMARY_BRIDGE_PORT		= 0x00000002L, | ||||||
|  | 	IPA_SBP_SET_SECONDARY_BRIDGE_PORT	= 0x00000004L, | ||||||
|  | 	IPA_SBP_QUERY_BRIDGE_PORTS		= 0x00000008L, | ||||||
|  | 	IPA_SBP_BRIDGE_PORT_STATE_CHANGE	= 0x00000010L, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct net_if_token { | ||||||
|  | 	__u16 devnum; | ||||||
|  | 	__u8 cssid; | ||||||
|  | 	__u8 iid; | ||||||
|  | 	__u8 ssid; | ||||||
|  | 	__u8 chpid; | ||||||
|  | 	__u16 chid; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_ipacmd_sbp_hdr { | ||||||
|  | 	__u32 supported_sbp_cmds; | ||||||
|  | 	__u32 enabled_sbp_cmds; | ||||||
|  | 	__u16 cmdlength; | ||||||
|  | 	__u16 reserved1; | ||||||
|  | 	__u32 command_code; | ||||||
|  | 	__u16 return_code; | ||||||
|  | 	__u8  used_total; | ||||||
|  | 	__u8  seq_no; | ||||||
|  | 	__u32 reserved2; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_query_cmds_supp { | ||||||
|  | 	__u32 supported_cmds; | ||||||
|  | 	__u32 reserved; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_reset_role { | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_set_primary { | ||||||
|  | 	struct net_if_token token; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_set_secondary { | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_port_entry { | ||||||
|  | 		__u8 role; | ||||||
|  | 		__u8 state; | ||||||
|  | 		__u8 reserved1; | ||||||
|  | 		__u8 reserved2; | ||||||
|  | 		struct net_if_token token; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_query_ports { | ||||||
|  | 	__u8 primary_bp_supported; | ||||||
|  | 	__u8 secondary_bp_supported; | ||||||
|  | 	__u8 num_entries; | ||||||
|  | 	__u8 entry_length; | ||||||
|  | 	struct qeth_sbp_port_entry entry[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_sbp_state_change { | ||||||
|  | 	__u8 primary_bp_supported; | ||||||
|  | 	__u8 secondary_bp_supported; | ||||||
|  | 	__u8 num_entries; | ||||||
|  | 	__u8 entry_length; | ||||||
|  | 	struct qeth_sbp_port_entry entry[]; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct qeth_ipacmd_setbridgeport { | ||||||
|  | 	struct qeth_ipacmd_sbp_hdr hdr; | ||||||
|  | 	union { | ||||||
|  | 		struct qeth_sbp_query_cmds_supp query_cmds_supp; | ||||||
|  | 		struct qeth_sbp_reset_role reset_role; | ||||||
|  | 		struct qeth_sbp_set_primary set_primary; | ||||||
|  | 		struct qeth_sbp_set_secondary set_secondary; | ||||||
|  | 		struct qeth_sbp_query_ports query_ports; | ||||||
|  | 		struct qeth_sbp_state_change state_change; | ||||||
|  | 	} data; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
| /* Header for each IPA command */ | /* Header for each IPA command */ | ||||||
| struct qeth_ipacmd_hdr { | struct qeth_ipacmd_hdr { | ||||||
| 	__u8   command; | 	__u8   command; | ||||||
|  | @ -529,6 +612,7 @@ struct qeth_ipa_cmd { | ||||||
| 		struct qeth_ipacmd_setadpparms		setadapterparms; | 		struct qeth_ipacmd_setadpparms		setadapterparms; | ||||||
| 		struct qeth_set_routing			setrtg; | 		struct qeth_set_routing			setrtg; | ||||||
| 		struct qeth_ipacmd_diagass		diagass; | 		struct qeth_ipacmd_diagass		diagass; | ||||||
|  | 		struct qeth_ipacmd_setbridgeport	sbp; | ||||||
| 	} data; | 	} data; | ||||||
| } __attribute__ ((packed)); | } __attribute__ ((packed)); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								drivers/s390/net/qeth_l2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								drivers/s390/net/qeth_l2.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | /*
 | ||||||
|  |  *    Copyright IBM Corp. 2013 | ||||||
|  |  *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef __QETH_L2_H__ | ||||||
|  | #define __QETH_L2_H__ | ||||||
|  | 
 | ||||||
|  | #include "qeth_core.h" | ||||||
|  | 
 | ||||||
|  | int qeth_l2_create_device_attributes(struct device *); | ||||||
|  | void qeth_l2_remove_device_attributes(struct device *); | ||||||
|  | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); | ||||||
|  | 
 | ||||||
|  | #endif /* __QETH_L2_H__ */ | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| 
 | 
 | ||||||
| #include "qeth_core.h" | #include "qeth_core.h" | ||||||
|  | #include "qeth_l2.h" | ||||||
| 
 | 
 | ||||||
| static int qeth_l2_set_offline(struct ccwgroup_device *); | static int qeth_l2_set_offline(struct ccwgroup_device *); | ||||||
| static int qeth_l2_stop(struct net_device *); | static int qeth_l2_stop(struct net_device *); | ||||||
|  | @ -880,6 +881,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) | ||||||
| { | { | ||||||
| 	struct qeth_card *card = dev_get_drvdata(&gdev->dev); | 	struct qeth_card *card = dev_get_drvdata(&gdev->dev); | ||||||
| 
 | 
 | ||||||
|  | 	qeth_l2_create_device_attributes(&gdev->dev); | ||||||
| 	INIT_LIST_HEAD(&card->vid_list); | 	INIT_LIST_HEAD(&card->vid_list); | ||||||
| 	INIT_LIST_HEAD(&card->mc_list); | 	INIT_LIST_HEAD(&card->mc_list); | ||||||
| 	card->options.layer2 = 1; | 	card->options.layer2 = 1; | ||||||
|  | @ -891,6 +893,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) | ||||||
| { | { | ||||||
| 	struct qeth_card *card = dev_get_drvdata(&cgdev->dev); | 	struct qeth_card *card = dev_get_drvdata(&cgdev->dev); | ||||||
| 
 | 
 | ||||||
|  | 	qeth_l2_remove_device_attributes(&cgdev->dev); | ||||||
| 	qeth_set_allowed_threads(card, 0, 1); | 	qeth_set_allowed_threads(card, 0, 1); | ||||||
| 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); | 	wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); | ||||||
| 
 | 
 | ||||||
|  | @ -1003,6 +1006,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) | ||||||
| 	} else | 	} else | ||||||
| 		card->info.hwtrap = 0; | 		card->info.hwtrap = 0; | ||||||
| 
 | 
 | ||||||
|  | 	qeth_l2_setup_bridgeport_attrs(card); | ||||||
|  | 
 | ||||||
| 	card->state = CARD_STATE_HARDSETUP; | 	card->state = CARD_STATE_HARDSETUP; | ||||||
| 	memset(&card->rx, 0, sizeof(struct qeth_rx)); | 	memset(&card->rx, 0, sizeof(struct qeth_rx)); | ||||||
| 	qeth_print_status_message(card); | 	qeth_print_status_message(card); | ||||||
|  | @ -1347,6 +1352,365 @@ void qeth_osn_deregister(struct net_device *dev) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(qeth_osn_deregister); | EXPORT_SYMBOL(qeth_osn_deregister); | ||||||
| 
 | 
 | ||||||
|  | /* SETBRIDGEPORT support, async notifications */ | ||||||
|  | 
 | ||||||
|  | struct qeth_bridge_state_data { | ||||||
|  | 	struct work_struct worker; | ||||||
|  | 	struct qeth_card *card; | ||||||
|  | 	struct qeth_sbp_state_change qports; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void qeth_bridge_state_change_worker(struct work_struct *work) | ||||||
|  | { | ||||||
|  | 	struct qeth_bridge_state_data *data = | ||||||
|  | 		container_of(work, struct qeth_bridge_state_data, worker); | ||||||
|  | 	/* We are only interested in the first entry - local port */ | ||||||
|  | 	struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; | ||||||
|  | 	char env_locrem[32]; | ||||||
|  | 	char env_role[32]; | ||||||
|  | 	char env_state[32]; | ||||||
|  | 	char *env[] = { | ||||||
|  | 		env_locrem, | ||||||
|  | 		env_role, | ||||||
|  | 		env_state, | ||||||
|  | 		NULL | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	/* Role should not change by itself, but if it did, */ | ||||||
|  | 	/* information from the hardware is authoritative.  */ | ||||||
|  | 	mutex_lock(&data->card->conf_mutex); | ||||||
|  | 	data->card->options.sbp.role = entry->role; | ||||||
|  | 	mutex_unlock(&data->card->conf_mutex); | ||||||
|  | 
 | ||||||
|  | 	snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); | ||||||
|  | 	snprintf(env_role, sizeof(env_role), "ROLE=%s", | ||||||
|  | 		(entry->role == QETH_SBP_ROLE_NONE) ? "none" : | ||||||
|  | 		(entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : | ||||||
|  | 		(entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : | ||||||
|  | 		"<INVALID>"); | ||||||
|  | 	snprintf(env_state, sizeof(env_state), "STATE=%s", | ||||||
|  | 		(entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : | ||||||
|  | 		(entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : | ||||||
|  | 		(entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : | ||||||
|  | 		"<INVALID>"); | ||||||
|  | 	kobject_uevent_env(&data->card->gdev->dev.kobj, | ||||||
|  | 				KOBJ_CHANGE, env); | ||||||
|  | 	kfree(data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd) | ||||||
|  | { | ||||||
|  | 	struct qeth_sbp_state_change *qports = | ||||||
|  | 		 &cmd->data.sbp.data.state_change; | ||||||
|  | 	struct qeth_bridge_state_data *data; | ||||||
|  | 	int extrasize; | ||||||
|  | 
 | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brstchng"); | ||||||
|  | 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||||||
|  | 		QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; | ||||||
|  | 	data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, | ||||||
|  | 		GFP_ATOMIC); | ||||||
|  | 	if (!data) { | ||||||
|  | 		QETH_CARD_TEXT(card, 2, "BPSalloc"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	INIT_WORK(&data->worker, qeth_bridge_state_change_worker); | ||||||
|  | 	data->card = card; | ||||||
|  | 	memcpy(&data->qports, qports, | ||||||
|  | 			sizeof(struct qeth_sbp_state_change) + extrasize); | ||||||
|  | 	queue_work(qeth_wq, &data->worker); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(qeth_bridge_state_change); | ||||||
|  | 
 | ||||||
|  | /* SETBRIDGEPORT support; sending commands */ | ||||||
|  | 
 | ||||||
|  | struct _qeth_sbp_cbctl { | ||||||
|  | 	u16 ipa_rc; | ||||||
|  | 	u16 cmd_rc; | ||||||
|  | 	union { | ||||||
|  | 		u32 supported; | ||||||
|  | 		struct { | ||||||
|  | 			enum qeth_sbp_roles *role; | ||||||
|  | 			enum qeth_sbp_states *state; | ||||||
|  | 		} qports; | ||||||
|  | 	} data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. | ||||||
|  |  * @card:		      qeth_card structure pointer, for debug messages. | ||||||
|  |  * @cbctl:		      state structure with hardware return codes. | ||||||
|  |  * @setcmd:		      IPA command code | ||||||
|  |  * | ||||||
|  |  * Returns negative errno-compatible error indication or 0 on success. | ||||||
|  |  */ | ||||||
|  | static int qeth_bridgeport_makerc(struct qeth_card *card, | ||||||
|  | 	struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	switch (cbctl->ipa_rc) { | ||||||
|  | 	case IPA_RC_SUCCESS: | ||||||
|  | 		switch (cbctl->cmd_rc) { | ||||||
|  | 		case 0x0000: | ||||||
|  | 			rc = 0; | ||||||
|  | 			break; | ||||||
|  | 		case 0x0004: | ||||||
|  | 			rc = -ENOSYS; | ||||||
|  | 			break; | ||||||
|  | 		case 0x000C: /* Not configured as bridge Port */ | ||||||
|  | 			rc = -ENODEV; /* maybe not the best code here? */ | ||||||
|  | 			dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets device is not configured as a Bridge Port\n"); | ||||||
|  | 			break; | ||||||
|  | 		case 0x0014: /* Another device is Primary */ | ||||||
|  | 			switch (setcmd) { | ||||||
|  | 			case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: | ||||||
|  | 				rc = -EEXIST; | ||||||
|  | 				dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets LAN already has a primary Bridge Port\n"); | ||||||
|  | 				break; | ||||||
|  | 			case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: | ||||||
|  | 				rc = -EBUSY; | ||||||
|  | 				dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets device is already a primary Bridge Port\n"); | ||||||
|  | 				break; | ||||||
|  | 			default: | ||||||
|  | 				rc = -EIO; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		case 0x0018: /* This device is currently Secondary */ | ||||||
|  | 			rc = -EBUSY; | ||||||
|  | 			dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets device is already a secondary Bridge Port\n"); | ||||||
|  | 			break; | ||||||
|  | 		case 0x001C: /* Limit for Secondary devices reached */ | ||||||
|  | 			rc = -EEXIST; | ||||||
|  | 			dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets LAN cannot have more secondary Bridge Ports\n"); | ||||||
|  | 			break; | ||||||
|  | 		case 0x0024: /* This device is currently Primary */ | ||||||
|  | 			rc = -EBUSY; | ||||||
|  | 			dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets device is already a primary Bridge Port\n"); | ||||||
|  | 			break; | ||||||
|  | 		case 0x0020: /* Not authorized by zManager */ | ||||||
|  | 			rc = -EACCES; | ||||||
|  | 			dev_err(&card->gdev->dev, | ||||||
|  | 	"The HiperSockets device is not authorized to be a Bridge Port\n"); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			rc = -EIO; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case IPA_RC_NOTSUPP: | ||||||
|  | 		rc = -ENOSYS; | ||||||
|  | 		break; | ||||||
|  | 	case IPA_RC_UNSUPPORTED_COMMAND: | ||||||
|  | 		rc = -ENOSYS; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		rc = -EIO; | ||||||
|  | 	} | ||||||
|  | 	if (rc) { | ||||||
|  | 		QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); | ||||||
|  | 		QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); | ||||||
|  | 	} | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int qeth_bridgeport_query_support_cb(struct qeth_card *card, | ||||||
|  | 	struct qeth_reply *reply, unsigned long data) | ||||||
|  | { | ||||||
|  | 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||||||
|  | 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brqsupcb"); | ||||||
|  | 	cbctl->ipa_rc = cmd->hdr.return_code; | ||||||
|  | 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||||||
|  | 	if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { | ||||||
|  | 		cbctl->data.supported = | ||||||
|  | 			cmd->data.sbp.data.query_cmds_supp.supported_cmds; | ||||||
|  | 	} else { | ||||||
|  | 		cbctl->data.supported = 0; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. | ||||||
|  |  * @card:			     qeth_card structure pointer. | ||||||
|  |  * | ||||||
|  |  * Sets bitmask of supported setbridgeport subfunctions in the qeth_card | ||||||
|  |  * strucutre: card->options.sbp.supported_funcs. | ||||||
|  |  */ | ||||||
|  | void qeth_bridgeport_query_support(struct qeth_card *card) | ||||||
|  | { | ||||||
|  | 	struct qeth_cmd_buffer *iob; | ||||||
|  | 	struct qeth_ipa_cmd *cmd; | ||||||
|  | 	struct _qeth_sbp_cbctl cbctl; | ||||||
|  | 
 | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brqsuppo"); | ||||||
|  | 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||||||
|  | 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||||||
|  | 	cmd->data.sbp.hdr.cmdlength = | ||||||
|  | 		sizeof(struct qeth_ipacmd_sbp_hdr) + | ||||||
|  | 		sizeof(struct qeth_sbp_query_cmds_supp); | ||||||
|  | 	cmd->data.sbp.hdr.command_code = | ||||||
|  | 		IPA_SBP_QUERY_COMMANDS_SUPPORTED; | ||||||
|  | 	cmd->data.sbp.hdr.used_total = 1; | ||||||
|  | 	cmd->data.sbp.hdr.seq_no = 1; | ||||||
|  | 	if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, | ||||||
|  | 							(void *)&cbctl) || | ||||||
|  | 	    qeth_bridgeport_makerc(card, &cbctl, | ||||||
|  | 					IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { | ||||||
|  | 		/* non-zero makerc signifies failure, and produce messages */ | ||||||
|  | 		card->options.sbp.role = QETH_SBP_ROLE_NONE; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	card->options.sbp.supported_funcs = cbctl.data.supported; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(qeth_bridgeport_query_support); | ||||||
|  | 
 | ||||||
|  | static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, | ||||||
|  | 	struct qeth_reply *reply, unsigned long data) | ||||||
|  | { | ||||||
|  | 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||||||
|  | 	struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; | ||||||
|  | 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||||||
|  | 
 | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brqprtcb"); | ||||||
|  | 	cbctl->ipa_rc = cmd->hdr.return_code; | ||||||
|  | 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||||||
|  | 	if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) | ||||||
|  | 		return 0; | ||||||
|  | 	if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||||||
|  | 		cbctl->cmd_rc = 0xffff; | ||||||
|  | 		QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	/* first entry contains the state of the local port */ | ||||||
|  | 	if (qports->num_entries > 0) { | ||||||
|  | 		if (cbctl->data.qports.role) | ||||||
|  | 			*cbctl->data.qports.role = qports->entry[0].role; | ||||||
|  | 		if (cbctl->data.qports.state) | ||||||
|  | 			*cbctl->data.qports.state = qports->entry[0].state; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * qeth_bridgeport_query_ports() - query local bridgeport status. | ||||||
|  |  * @card:			   qeth_card structure pointer. | ||||||
|  |  * @role:   Role of the port: 0-none, 1-primary, 2-secondary. | ||||||
|  |  * @state:  State of the port: 0-inactive, 1-standby, 2-active. | ||||||
|  |  * | ||||||
|  |  * Returns negative errno-compatible error indication or 0 on success. | ||||||
|  |  * | ||||||
|  |  * 'role' and 'state' are not updated in case of hardware operation failure. | ||||||
|  |  */ | ||||||
|  | int qeth_bridgeport_query_ports(struct qeth_card *card, | ||||||
|  | 	enum qeth_sbp_roles *role, enum qeth_sbp_states *state) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 	struct qeth_cmd_buffer *iob; | ||||||
|  | 	struct qeth_ipa_cmd *cmd; | ||||||
|  | 	struct _qeth_sbp_cbctl cbctl = { | ||||||
|  | 		.data = { | ||||||
|  | 			.qports = { | ||||||
|  | 				.role = role, | ||||||
|  | 				.state = state, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brqports"); | ||||||
|  | 	if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||||||
|  | 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||||||
|  | 	cmd->data.sbp.hdr.cmdlength = | ||||||
|  | 		sizeof(struct qeth_ipacmd_sbp_hdr); | ||||||
|  | 	cmd->data.sbp.hdr.command_code = | ||||||
|  | 		IPA_SBP_QUERY_BRIDGE_PORTS; | ||||||
|  | 	cmd->data.sbp.hdr.used_total = 1; | ||||||
|  | 	cmd->data.sbp.hdr.seq_no = 1; | ||||||
|  | 	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, | ||||||
|  | 				(void *)&cbctl); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 	rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); | ||||||
|  | 
 | ||||||
|  | static int qeth_bridgeport_set_cb(struct qeth_card *card, | ||||||
|  | 	struct qeth_reply *reply, unsigned long data) | ||||||
|  | { | ||||||
|  | 	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; | ||||||
|  | 	struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brsetrcb"); | ||||||
|  | 	cbctl->ipa_rc = cmd->hdr.return_code; | ||||||
|  | 	cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * qeth_bridgeport_setrole() - Assign primary role to the port. | ||||||
|  |  * @card:		       qeth_card structure pointer. | ||||||
|  |  * @role:		       Role to assign. | ||||||
|  |  * | ||||||
|  |  * Returns negative errno-compatible error indication or 0 on success. | ||||||
|  |  */ | ||||||
|  | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int cmdlength; | ||||||
|  | 	struct qeth_cmd_buffer *iob; | ||||||
|  | 	struct qeth_ipa_cmd *cmd; | ||||||
|  | 	struct _qeth_sbp_cbctl cbctl; | ||||||
|  | 	enum qeth_ipa_sbp_cmd setcmd; | ||||||
|  | 
 | ||||||
|  | 	QETH_CARD_TEXT(card, 2, "brsetrol"); | ||||||
|  | 	switch (role) { | ||||||
|  | 	case QETH_SBP_ROLE_NONE: | ||||||
|  | 		setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; | ||||||
|  | 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + | ||||||
|  | 			sizeof(struct qeth_sbp_reset_role); | ||||||
|  | 		break; | ||||||
|  | 	case QETH_SBP_ROLE_PRIMARY: | ||||||
|  | 		setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; | ||||||
|  | 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + | ||||||
|  | 			sizeof(struct qeth_sbp_set_primary); | ||||||
|  | 		break; | ||||||
|  | 	case QETH_SBP_ROLE_SECONDARY: | ||||||
|  | 		setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; | ||||||
|  | 		cmdlength =  sizeof(struct qeth_ipacmd_sbp_hdr) + | ||||||
|  | 			sizeof(struct qeth_sbp_set_secondary); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 	if (!(card->options.sbp.supported_funcs & setcmd)) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 	iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||||||
|  | 	cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||||||
|  | 	cmd->data.sbp.hdr.cmdlength = cmdlength; | ||||||
|  | 	cmd->data.sbp.hdr.command_code = setcmd; | ||||||
|  | 	cmd->data.sbp.hdr.used_total = 1; | ||||||
|  | 	cmd->data.sbp.hdr.seq_no = 1; | ||||||
|  | 	rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, | ||||||
|  | 				(void *)&cbctl); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 	rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| module_init(qeth_l2_init); | module_init(qeth_l2_init); | ||||||
| module_exit(qeth_l2_exit); | module_exit(qeth_l2_exit); | ||||||
| MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | ||||||
|  |  | ||||||
							
								
								
									
										161
									
								
								drivers/s390/net/qeth_l2_sys.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								drivers/s390/net/qeth_l2_sys.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,161 @@ | ||||||
|  | /*
 | ||||||
|  |  *    Copyright IBM Corp. 2013 | ||||||
|  |  *    Author(s): Eugene Crosser <eugene.crosser@ru.ibm.com> | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <asm/ebcdic.h> | ||||||
|  | #include "qeth_l2.h" | ||||||
|  | 
 | ||||||
|  | #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ | ||||||
|  | struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) | ||||||
|  | 
 | ||||||
|  | static int qeth_card_hw_is_reachable(struct qeth_card *card) | ||||||
|  | { | ||||||
|  | 	return (card->state == CARD_STATE_SOFTSETUP) || | ||||||
|  | 		(card->state == CARD_STATE_UP); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ssize_t qeth_bridge_port_role_state_show(struct device *dev, | ||||||
|  | 				struct device_attribute *attr, char *buf, | ||||||
|  | 				int show_state) | ||||||
|  | { | ||||||
|  | 	struct qeth_card *card = dev_get_drvdata(dev); | ||||||
|  | 	enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	char *word; | ||||||
|  | 
 | ||||||
|  | 	if (!card) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&card->conf_mutex); | ||||||
|  | 
 | ||||||
|  | 	if (qeth_card_hw_is_reachable(card) && | ||||||
|  | 					card->options.sbp.supported_funcs) | ||||||
|  | 		rc = qeth_bridgeport_query_ports(card, | ||||||
|  | 			&card->options.sbp.role, &state); | ||||||
|  | 	if (!rc) { | ||||||
|  | 		if (show_state) | ||||||
|  | 			switch (state) { | ||||||
|  | 			case QETH_SBP_STATE_INACTIVE: | ||||||
|  | 				word = "inactive"; break; | ||||||
|  | 			case QETH_SBP_STATE_STANDBY: | ||||||
|  | 				word = "standby"; break; | ||||||
|  | 			case QETH_SBP_STATE_ACTIVE: | ||||||
|  | 				word = "active"; break; | ||||||
|  | 			default: | ||||||
|  | 				rc = -EIO; | ||||||
|  | 			} | ||||||
|  | 		else | ||||||
|  | 			switch (card->options.sbp.role) { | ||||||
|  | 			case QETH_SBP_ROLE_NONE: | ||||||
|  | 				word = "none"; break; | ||||||
|  | 			case QETH_SBP_ROLE_PRIMARY: | ||||||
|  | 				word = "primary"; break; | ||||||
|  | 			case QETH_SBP_ROLE_SECONDARY: | ||||||
|  | 				word = "secondary"; break; | ||||||
|  | 			default: | ||||||
|  | 				rc = -EIO; | ||||||
|  | 			} | ||||||
|  | 		if (rc) | ||||||
|  | 			QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", | ||||||
|  | 				card->options.sbp.role, state); | ||||||
|  | 		else | ||||||
|  | 			rc = sprintf(buf, "%s\n", word); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&card->conf_mutex); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ssize_t qeth_bridge_port_role_show(struct device *dev, | ||||||
|  | 				struct device_attribute *attr, char *buf) | ||||||
|  | { | ||||||
|  | 	return qeth_bridge_port_role_state_show(dev, attr, buf, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ssize_t qeth_bridge_port_role_store(struct device *dev, | ||||||
|  | 		struct device_attribute *attr, const char *buf, size_t count) | ||||||
|  | { | ||||||
|  | 	struct qeth_card *card = dev_get_drvdata(dev); | ||||||
|  | 	int rc = 0; | ||||||
|  | 	enum qeth_sbp_roles role; | ||||||
|  | 
 | ||||||
|  | 	if (!card) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	if (sysfs_streq(buf, "primary")) | ||||||
|  | 		role = QETH_SBP_ROLE_PRIMARY; | ||||||
|  | 	else if (sysfs_streq(buf, "secondary")) | ||||||
|  | 		role = QETH_SBP_ROLE_SECONDARY; | ||||||
|  | 	else if (sysfs_streq(buf, "none")) | ||||||
|  | 		role = QETH_SBP_ROLE_NONE; | ||||||
|  | 	else | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&card->conf_mutex); | ||||||
|  | 
 | ||||||
|  | 	if (qeth_card_hw_is_reachable(card)) { | ||||||
|  | 		rc = qeth_bridgeport_setrole(card, role); | ||||||
|  | 		if (!rc) | ||||||
|  | 			card->options.sbp.role = role; | ||||||
|  | 	} else | ||||||
|  | 		card->options.sbp.role = role; | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&card->conf_mutex); | ||||||
|  | 
 | ||||||
|  | 	return rc ? rc : count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show, | ||||||
|  | 		   qeth_bridge_port_role_store); | ||||||
|  | 
 | ||||||
|  | static ssize_t qeth_bridge_port_state_show(struct device *dev, | ||||||
|  | 				struct device_attribute *attr, char *buf) | ||||||
|  | { | ||||||
|  | 	return qeth_bridge_port_role_state_show(dev, attr, buf, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, | ||||||
|  | 		   NULL); | ||||||
|  | 
 | ||||||
|  | static struct attribute *qeth_l2_bridgeport_attrs[] = { | ||||||
|  | 	&dev_attr_bridge_role.attr, | ||||||
|  | 	&dev_attr_bridge_state.attr, | ||||||
|  | 	NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct attribute_group qeth_l2_bridgeport_attr_group = { | ||||||
|  | 	.attrs = qeth_l2_bridgeport_attrs, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int qeth_l2_create_device_attributes(struct device *dev) | ||||||
|  | { | ||||||
|  | 	return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void qeth_l2_remove_device_attributes(struct device *dev) | ||||||
|  | { | ||||||
|  | 	sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online. | ||||||
|  |  * @card:			      qeth_card structure pointer | ||||||
|  |  * | ||||||
|  |  * Note: this function is called with conf_mutex held by the caller | ||||||
|  |  */ | ||||||
|  | void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) | ||||||
|  | { | ||||||
|  | 	if (!card) | ||||||
|  | 		return; | ||||||
|  | 	if (!card->options.sbp.supported_funcs) | ||||||
|  | 		return; | ||||||
|  | 	if (card->options.sbp.role != QETH_SBP_ROLE_NONE) { | ||||||
|  | 		/* Conditional to avoid spurious error messages */ | ||||||
|  | 		qeth_bridgeport_setrole(card, card->options.sbp.role); | ||||||
|  | 		/* Let the callback function refresh the stored role value. */ | ||||||
|  | 		qeth_bridgeport_query_ports(card, | ||||||
|  | 			&card->options.sbp.role, NULL); | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eugene Crosser
						Eugene Crosser