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_core.h"
9#include "qeth_l2.h"
10
11#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
12struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
13
14static ssize_t qeth_bridge_port_role_state_show(struct device *dev,
15				struct device_attribute *attr, char *buf,
16				int show_state)
17{
18	struct qeth_card *card = dev_get_drvdata(dev);
19	enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE;
20	int rc = 0;
21	char *word;
22
23	if (!card)
24		return -EINVAL;
25
26	if (qeth_card_hw_is_reachable(card) &&
27					card->options.sbp.supported_funcs)
28		rc = qeth_bridgeport_query_ports(card,
29			&card->options.sbp.role, &state);
30	if (!rc) {
31		if (show_state)
32			switch (state) {
33			case QETH_SBP_STATE_INACTIVE:
34				word = "inactive"; break;
35			case QETH_SBP_STATE_STANDBY:
36				word = "standby"; break;
37			case QETH_SBP_STATE_ACTIVE:
38				word = "active"; break;
39			default:
40				rc = -EIO;
41			}
42		else
43			switch (card->options.sbp.role) {
44			case QETH_SBP_ROLE_NONE:
45				word = "none"; break;
46			case QETH_SBP_ROLE_PRIMARY:
47				word = "primary"; break;
48			case QETH_SBP_ROLE_SECONDARY:
49				word = "secondary"; break;
50			default:
51				rc = -EIO;
52			}
53		if (rc)
54			QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x",
55				card->options.sbp.role, state);
56		else
57			rc = sprintf(buf, "%s\n", word);
58	}
59
60	return rc;
61}
62
63static ssize_t qeth_bridge_port_role_show(struct device *dev,
64				struct device_attribute *attr, char *buf)
65{
66	return qeth_bridge_port_role_state_show(dev, attr, buf, 0);
67}
68
69static ssize_t qeth_bridge_port_role_store(struct device *dev,
70		struct device_attribute *attr, const char *buf, size_t count)
71{
72	struct qeth_card *card = dev_get_drvdata(dev);
73	int rc = 0;
74	enum qeth_sbp_roles role;
75
76	if (!card)
77		return -EINVAL;
78	if (sysfs_streq(buf, "primary"))
79		role = QETH_SBP_ROLE_PRIMARY;
80	else if (sysfs_streq(buf, "secondary"))
81		role = QETH_SBP_ROLE_SECONDARY;
82	else if (sysfs_streq(buf, "none"))
83		role = QETH_SBP_ROLE_NONE;
84	else
85		return -EINVAL;
86
87	mutex_lock(&card->conf_mutex);
88
89	if (card->options.sbp.reflect_promisc) /* Forbid direct manipulation */
90		rc = -EPERM;
91	else if (qeth_card_hw_is_reachable(card)) {
92		rc = qeth_bridgeport_setrole(card, role);
93		if (!rc)
94			card->options.sbp.role = role;
95	} else
96		card->options.sbp.role = role;
97
98	mutex_unlock(&card->conf_mutex);
99
100	return rc ? rc : count;
101}
102
103static DEVICE_ATTR(bridge_role, 0644, qeth_bridge_port_role_show,
104		   qeth_bridge_port_role_store);
105
106static ssize_t qeth_bridge_port_state_show(struct device *dev,
107				struct device_attribute *attr, char *buf)
108{
109	return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
110}
111
112static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show,
113		   NULL);
114
115static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
116				struct device_attribute *attr, char *buf)
117{
118	struct qeth_card *card = dev_get_drvdata(dev);
119	int enabled;
120
121	if (!card)
122		return -EINVAL;
123
124	enabled = card->options.sbp.hostnotification;
125
126	return sprintf(buf, "%d\n", enabled);
127}
128
129static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev,
130		struct device_attribute *attr, const char *buf, size_t count)
131{
132	struct qeth_card *card = dev_get_drvdata(dev);
133	int rc = 0;
134	int enable;
135
136	if (!card)
137		return -EINVAL;
138
139	if (sysfs_streq(buf, "0"))
140		enable = 0;
141	else if (sysfs_streq(buf, "1"))
142		enable = 1;
143	else
144		return -EINVAL;
145
146	mutex_lock(&card->conf_mutex);
147
148	if (qeth_card_hw_is_reachable(card)) {
149		rc = qeth_bridgeport_an_set(card, enable);
150		if (!rc)
151			card->options.sbp.hostnotification = enable;
152	} else
153		card->options.sbp.hostnotification = enable;
154
155	mutex_unlock(&card->conf_mutex);
156
157	return rc ? rc : count;
158}
159
160static DEVICE_ATTR(bridge_hostnotify, 0644,
161			qeth_bridgeport_hostnotification_show,
162			qeth_bridgeport_hostnotification_store);
163
164static ssize_t qeth_bridgeport_reflect_show(struct device *dev,
165				struct device_attribute *attr, char *buf)
166{
167	struct qeth_card *card = dev_get_drvdata(dev);
168	char *state;
169
170	if (!card)
171		return -EINVAL;
172
173	if (card->options.sbp.reflect_promisc) {
174		if (card->options.sbp.reflect_promisc_primary)
175			state = "primary";
176		else
177			state = "secondary";
178	} else
179		state = "none";
180
181	return sprintf(buf, "%s\n", state);
182}
183
184static ssize_t qeth_bridgeport_reflect_store(struct device *dev,
185		struct device_attribute *attr, const char *buf, size_t count)
186{
187	struct qeth_card *card = dev_get_drvdata(dev);
188	int enable, primary;
189	int rc = 0;
190
191	if (!card)
192		return -EINVAL;
193
194	if (sysfs_streq(buf, "none")) {
195		enable = 0;
196		primary = 0;
197	} else if (sysfs_streq(buf, "primary")) {
198		enable = 1;
199		primary = 1;
200	} else if (sysfs_streq(buf, "secondary")) {
201		enable = 1;
202		primary = 0;
203	} else
204		return -EINVAL;
205
206	mutex_lock(&card->conf_mutex);
207
208	if (card->options.sbp.role != QETH_SBP_ROLE_NONE)
209		rc = -EPERM;
210	else {
211		card->options.sbp.reflect_promisc = enable;
212		card->options.sbp.reflect_promisc_primary = primary;
213		rc = 0;
214	}
215
216	mutex_unlock(&card->conf_mutex);
217
218	return rc ? rc : count;
219}
220
221static DEVICE_ATTR(bridge_reflect_promisc, 0644,
222			qeth_bridgeport_reflect_show,
223			qeth_bridgeport_reflect_store);
224
225static struct attribute *qeth_l2_bridgeport_attrs[] = {
226	&dev_attr_bridge_role.attr,
227	&dev_attr_bridge_state.attr,
228	&dev_attr_bridge_hostnotify.attr,
229	&dev_attr_bridge_reflect_promisc.attr,
230	NULL,
231};
232
233static struct attribute_group qeth_l2_bridgeport_attr_group = {
234	.attrs = qeth_l2_bridgeport_attrs,
235};
236
237int qeth_l2_create_device_attributes(struct device *dev)
238{
239	return sysfs_create_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
240}
241
242void qeth_l2_remove_device_attributes(struct device *dev)
243{
244	sysfs_remove_group(&dev->kobj, &qeth_l2_bridgeport_attr_group);
245}
246
247/**
248 * qeth_l2_setup_bridgeport_attrs() - set/restore attrs when turning online.
249 * @card:			      qeth_card structure pointer
250 *
251 * Note: this function is called with conf_mutex held by the caller
252 */
253void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
254{
255	int rc;
256
257	if (!card)
258		return;
259	if (!card->options.sbp.supported_funcs)
260		return;
261	if (card->options.sbp.role != QETH_SBP_ROLE_NONE) {
262		/* Conditional to avoid spurious error messages */
263		qeth_bridgeport_setrole(card, card->options.sbp.role);
264		/* Let the callback function refresh the stored role value. */
265		qeth_bridgeport_query_ports(card,
266			&card->options.sbp.role, NULL);
267	}
268	if (card->options.sbp.hostnotification) {
269		rc = qeth_bridgeport_an_set(card, 1);
270		if (rc)
271			card->options.sbp.hostnotification = 0;
272	} else
273		qeth_bridgeport_an_set(card, 0);
274}
275