aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEugene Crosser <Eugene.Crosser@ru.ibm.com>2014-01-14 09:54:13 -0500
committerDavid S. Miller <davem@davemloft.net>2014-01-15 17:48:01 -0500
commit9f48b9db9a22bdbcff8a629b3d2e4a81dcd4ed26 (patch)
tree09cb6c0447e5808c5935c500567df3ab7a235058
parent59b55a4df24e2f105c87721bec3e6c80c3d7b20b (diff)
qeth: bridgeport support - address notifications
Introduce functions to enable and disable bridgeport address notification feature, sysfs attributes for access to these functions from userspace, and udev events emitted when a host joins or exits a bridgeport-enabled HiperSocket channel. 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>
-rw-r--r--Documentation/s390/qeth.txt29
-rw-r--r--drivers/s390/net/qeth_core.h5
-rw-r--r--drivers/s390/net/qeth_core_main.c3
-rw-r--r--drivers/s390/net/qeth_core_mpc.c1
-rw-r--r--drivers/s390/net/qeth_core_mpc.h38
-rw-r--r--drivers/s390/net/qeth_l2_main.c230
-rw-r--r--drivers/s390/net/qeth_l2_sys.c62
7 files changed, 368 insertions, 0 deletions
diff --git a/Documentation/s390/qeth.txt b/Documentation/s390/qeth.txt
index c08c305efd4d..74122ada9949 100644
--- a/Documentation/s390/qeth.txt
+++ b/Documentation/s390/qeth.txt
@@ -19,3 +19,32 @@ BRIDGEPORT=statechange - indicates that the Bridge Port device changed
19ROLE={primary|secondary|none} - the role assigned to the port. 19ROLE={primary|secondary|none} - the role assigned to the port.
20 20
21STATE={active|standby|inactive} - the newly assumed state of the port. 21STATE={active|standby|inactive} - the newly assumed state of the port.
22
23When run on HiperSockets Bridge Capable Port hardware with host address
24notifications enabled, a udev event with ACTION=CHANGE is emitted.
25It is emitted on behalf of the corresponding ccwgroup device when a host
26or a VLAN is registered or unregistered on the network served by the device.
27The event has the following attributes:
28
29BRIDGEDHOST={reset|register|deregister|abort} - host address
30 notifications are started afresh, a new host or VLAN is registered or
31 deregistered on the Bridge Port HiperSockets channel, or address
32 notifications are aborted.
33
34VLAN=numeric-vlan-id - VLAN ID on which the event occurred. Not included
35 if no VLAN is involved in the event.
36
37MAC=xx:xx:xx:xx:xx:xx - MAC address of the host that is being registered
38 or deregistered from the HiperSockets channel. Not reported if the
39 event reports the creation or destruction of a VLAN.
40
41NTOK_BUSID=x.y.zzzz - device bus ID (CSSID, SSID and device number).
42
43NTOK_IID=xx - device IID.
44
45NTOK_CHPID=xx - device CHPID.
46
47NTOK_CHID=xxxx - device channel ID.
48
49Note that the NTOK_* attributes refer to devices other than the one
50connected to the system on which the OS is running.
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 010f49e3e3ac..ac0bdded060f 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -169,9 +169,12 @@ enum qeth_sbp_states {
169 QETH_SBP_STATE_ACTIVE = 2, 169 QETH_SBP_STATE_ACTIVE = 2,
170}; 170};
171 171
172#define QETH_SBP_HOST_NOTIFICATION 1
173
172struct qeth_sbp_info { 174struct qeth_sbp_info {
173 __u32 supported_funcs; 175 __u32 supported_funcs;
174 enum qeth_sbp_roles role; 176 enum qeth_sbp_roles role;
177 __u32 hostnotification:1;
175}; 178};
176 179
177static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, 180static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa,
@@ -950,6 +953,8 @@ void qeth_bridgeport_query_support(struct qeth_card *card);
950int qeth_bridgeport_query_ports(struct qeth_card *card, 953int qeth_bridgeport_query_ports(struct qeth_card *card,
951 enum qeth_sbp_roles *role, enum qeth_sbp_states *state); 954 enum qeth_sbp_roles *role, enum qeth_sbp_states *state);
952int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); 955int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role);
956int qeth_bridgeport_an_set(struct qeth_card *card, int enable);
957void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd);
953int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); 958int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int);
954int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int); 959int qeth_get_elements_no(struct qeth_card *, struct sk_buff *, int);
955int qeth_get_elements_for_frags(struct sk_buff *); 960int 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 1ffea16f51c6..c05dacbf4e23 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -622,6 +622,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
622 return NULL; 622 return NULL;
623 } else 623 } else
624 return cmd; 624 return cmd;
625 case IPA_CMD_ADDRESS_CHANGE_NOTIF:
626 qeth_bridge_host_event(card, cmd);
627 return NULL;
625 case IPA_CMD_MODCCID: 628 case IPA_CMD_MODCCID:
626 return cmd; 629 return cmd;
627 case IPA_CMD_REGISTER_LOCAL_ADDR: 630 case IPA_CMD_REGISTER_LOCAL_ADDR:
diff --git a/drivers/s390/net/qeth_core_mpc.c b/drivers/s390/net/qeth_core_mpc.c
index 2f44cfd5dd00..7b55768a9592 100644
--- a/drivers/s390/net/qeth_core_mpc.c
+++ b/drivers/s390/net/qeth_core_mpc.c
@@ -254,6 +254,7 @@ static struct ipa_cmd_names qeth_ipa_cmd_names[] = {
254 {IPA_CMD_DESTROY_ADDR, "destroy_addr"}, 254 {IPA_CMD_DESTROY_ADDR, "destroy_addr"},
255 {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"}, 255 {IPA_CMD_REGISTER_LOCAL_ADDR, "register_local_addr"},
256 {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"}, 256 {IPA_CMD_UNREGISTER_LOCAL_ADDR, "unregister_local_addr"},
257 {IPA_CMD_ADDRESS_CHANGE_NOTIF, "address_change_notification"},
257 {IPA_CMD_UNKNOWN, "unknown"}, 258 {IPA_CMD_UNKNOWN, "unknown"},
258}; 259};
259 260
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index de6267990c5e..cf6a90ed42ae 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -109,6 +109,7 @@ enum qeth_ipa_cmds {
109 IPA_CMD_DESTROY_ADDR = 0xc4, 109 IPA_CMD_DESTROY_ADDR = 0xc4,
110 IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1, 110 IPA_CMD_REGISTER_LOCAL_ADDR = 0xd1,
111 IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2, 111 IPA_CMD_UNREGISTER_LOCAL_ADDR = 0xd2,
112 IPA_CMD_ADDRESS_CHANGE_NOTIF = 0xd3,
112 IPA_CMD_UNKNOWN = 0x00 113 IPA_CMD_UNKNOWN = 0x00
113}; 114};
114 115
@@ -520,6 +521,11 @@ struct net_if_token {
520 __u16 chid; 521 __u16 chid;
521} __packed; 522} __packed;
522 523
524struct mac_addr_lnid {
525 __u8 mac[6];
526 __u16 lnid;
527} __packed;
528
523struct qeth_ipacmd_sbp_hdr { 529struct qeth_ipacmd_sbp_hdr {
524 __u32 supported_sbp_cmds; 530 __u32 supported_sbp_cmds;
525 __u32 enabled_sbp_cmds; 531 __u32 enabled_sbp_cmds;
@@ -583,6 +589,37 @@ struct qeth_ipacmd_setbridgeport {
583 } data; 589 } data;
584} __packed; 590} __packed;
585 591
592/* ADDRESS_CHANGE_NOTIFICATION adapter-initiated "command" *******************/
593/* Bitmask for entry->change_code. Both bits may be raised. */
594enum qeth_ipa_addr_change_code {
595 IPA_ADDR_CHANGE_CODE_VLANID = 0x01,
596 IPA_ADDR_CHANGE_CODE_MACADDR = 0x02,
597 IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */
598};
599enum qeth_ipa_addr_change_retcode {
600 IPA_ADDR_CHANGE_RETCODE_OK = 0x0000,
601 IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010,
602};
603enum qeth_ipa_addr_change_lostmask {
604 IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01,
605 IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02,
606};
607
608struct qeth_ipacmd_addr_change_entry {
609 struct net_if_token token;
610 struct mac_addr_lnid addr_lnid;
611 __u8 change_code;
612 __u8 reserved1;
613 __u16 reserved2;
614} __packed;
615
616struct qeth_ipacmd_addr_change {
617 __u8 lost_event_mask;
618 __u8 reserved;
619 __u16 num_entries;
620 struct qeth_ipacmd_addr_change_entry entry[];
621} __packed;
622
586/* Header for each IPA command */ 623/* Header for each IPA command */
587struct qeth_ipacmd_hdr { 624struct qeth_ipacmd_hdr {
588 __u8 command; 625 __u8 command;
@@ -613,6 +650,7 @@ struct qeth_ipa_cmd {
613 struct qeth_set_routing setrtg; 650 struct qeth_set_routing setrtg;
614 struct qeth_ipacmd_diagass diagass; 651 struct qeth_ipacmd_diagass diagass;
615 struct qeth_ipacmd_setbridgeport sbp; 652 struct qeth_ipacmd_setbridgeport sbp;
653 struct qeth_ipacmd_addr_change addrchange;
616 } data; 654 } data;
617} __attribute__ ((packed)); 655} __attribute__ ((packed));
618 656
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 875d080e4e86..914d2c121fd8 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1354,6 +1354,71 @@ EXPORT_SYMBOL(qeth_osn_deregister);
1354 1354
1355/* SETBRIDGEPORT support, async notifications */ 1355/* SETBRIDGEPORT support, async notifications */
1356 1356
1357enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset};
1358
1359/**
1360 * qeth_bridge_emit_host_event() - bridgeport address change notification
1361 * @card: qeth_card structure pointer, for udev events.
1362 * @evtype: "normal" register/unregister, or abort, or reset. For abort
1363 * and reset token and addr_lnid are unused and may be NULL.
1364 * @code: event bitmask: high order bit 0x80 value 1 means removal of an
1365 * object, 0 - addition of an object.
1366 * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC.
1367 * @token: "network token" structure identifying physical address of the port.
1368 * @addr_lnid: pointer to structure with MAC address and VLAN ID.
1369 *
1370 * This function is called when registrations and deregistrations are
1371 * reported by the hardware, and also when notifications are enabled -
1372 * for all currently registered addresses.
1373 */
1374static void qeth_bridge_emit_host_event(struct qeth_card *card,
1375 enum qeth_an_event_type evtype,
1376 u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid)
1377{
1378 char str[7][32];
1379 char *env[8];
1380 int i = 0;
1381
1382 switch (evtype) {
1383 case anev_reg_unreg:
1384 snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s",
1385 (code & IPA_ADDR_CHANGE_CODE_REMOVAL)
1386 ? "deregister" : "register");
1387 env[i] = str[i]; i++;
1388 if (code & IPA_ADDR_CHANGE_CODE_VLANID) {
1389 snprintf(str[i], sizeof(str[i]), "VLAN=%d",
1390 addr_lnid->lnid);
1391 env[i] = str[i]; i++;
1392 }
1393 if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
1394 snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
1395 &addr_lnid->mac);
1396 env[i] = str[i]; i++;
1397 }
1398 snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
1399 token->cssid, token->ssid, token->devnum);
1400 env[i] = str[i]; i++;
1401 snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid);
1402 env[i] = str[i]; i++;
1403 snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x",
1404 token->chpid);
1405 env[i] = str[i]; i++;
1406 snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid);
1407 env[i] = str[i]; i++;
1408 break;
1409 case anev_abort:
1410 snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort");
1411 env[i] = str[i]; i++;
1412 break;
1413 case anev_reset:
1414 snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset");
1415 env[i] = str[i]; i++;
1416 break;
1417 }
1418 env[i] = NULL;
1419 kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env);
1420}
1421
1357struct qeth_bridge_state_data { 1422struct qeth_bridge_state_data {
1358 struct work_struct worker; 1423 struct work_struct worker;
1359 struct qeth_card *card; 1424 struct qeth_card *card;
@@ -1425,6 +1490,78 @@ void qeth_bridge_state_change(struct qeth_card *card, struct qeth_ipa_cmd *cmd)
1425} 1490}
1426EXPORT_SYMBOL(qeth_bridge_state_change); 1491EXPORT_SYMBOL(qeth_bridge_state_change);
1427 1492
1493struct qeth_bridge_host_data {
1494 struct work_struct worker;
1495 struct qeth_card *card;
1496 struct qeth_ipacmd_addr_change hostevs;
1497};
1498
1499static void qeth_bridge_host_event_worker(struct work_struct *work)
1500{
1501 struct qeth_bridge_host_data *data =
1502 container_of(work, struct qeth_bridge_host_data, worker);
1503 int i;
1504
1505 if (data->hostevs.lost_event_mask) {
1506 dev_info(&data->card->gdev->dev,
1507"Address notification from the HiperSockets Bridge Port stopped %s (%s)\n",
1508 data->card->dev->name,
1509 (data->hostevs.lost_event_mask == 0x01)
1510 ? "Overflow"
1511 : (data->hostevs.lost_event_mask == 0x02)
1512 ? "Bridge port state change"
1513 : "Unknown reason");
1514 mutex_lock(&data->card->conf_mutex);
1515 data->card->options.sbp.hostnotification = 0;
1516 mutex_unlock(&data->card->conf_mutex);
1517 qeth_bridge_emit_host_event(data->card, anev_abort,
1518 0, NULL, NULL);
1519 } else
1520 for (i = 0; i < data->hostevs.num_entries; i++) {
1521 struct qeth_ipacmd_addr_change_entry *entry =
1522 &data->hostevs.entry[i];
1523 qeth_bridge_emit_host_event(data->card,
1524 anev_reg_unreg,
1525 entry->change_code,
1526 &entry->token, &entry->addr_lnid);
1527 }
1528 kfree(data);
1529}
1530
1531void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd)
1532{
1533 struct qeth_ipacmd_addr_change *hostevs =
1534 &cmd->data.addrchange;
1535 struct qeth_bridge_host_data *data;
1536 int extrasize;
1537
1538 QETH_CARD_TEXT(card, 2, "brhostev");
1539 if (cmd->hdr.return_code != 0x0000) {
1540 if (cmd->hdr.return_code == 0x0010) {
1541 if (hostevs->lost_event_mask == 0x00)
1542 hostevs->lost_event_mask = 0xff;
1543 } else {
1544 QETH_CARD_TEXT_(card, 2, "BPHe%04x",
1545 cmd->hdr.return_code);
1546 return;
1547 }
1548 }
1549 extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) *
1550 hostevs->num_entries;
1551 data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize,
1552 GFP_ATOMIC);
1553 if (!data) {
1554 QETH_CARD_TEXT(card, 2, "BPHalloc");
1555 return;
1556 }
1557 INIT_WORK(&data->worker, qeth_bridge_host_event_worker);
1558 data->card = card;
1559 memcpy(&data->hostevs, hostevs,
1560 sizeof(struct qeth_ipacmd_addr_change) + extrasize);
1561 queue_work(qeth_wq, &data->worker);
1562}
1563EXPORT_SYMBOL(qeth_bridge_host_event);
1564
1428/* SETBRIDGEPORT support; sending commands */ 1565/* SETBRIDGEPORT support; sending commands */
1429 1566
1430struct _qeth_sbp_cbctl { 1567struct _qeth_sbp_cbctl {
@@ -1711,6 +1848,99 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role)
1711 return rc; 1848 return rc;
1712} 1849}
1713 1850
1851/**
1852 * qeth_anset_makerc() - derive "traditional" error from hardware codes.
1853 * @card: qeth_card structure pointer, for debug messages.
1854 *
1855 * Returns negative errno-compatible error indication or 0 on success.
1856 */
1857static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response)
1858{
1859 int rc;
1860
1861 if (pnso_rc == 0)
1862 switch (response) {
1863 case 0x0001:
1864 rc = 0;
1865 break;
1866 case 0x0004:
1867 case 0x0100:
1868 case 0x0106:
1869 rc = -ENOSYS;
1870 dev_err(&card->gdev->dev,
1871 "Setting address notification failed\n");
1872 break;
1873 case 0x0107:
1874 rc = -EAGAIN;
1875 break;
1876 default:
1877 rc = -EIO;
1878 }
1879 else
1880 rc = -EIO;
1881
1882 if (rc) {
1883 QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc);
1884 QETH_CARD_TEXT_(card, 2, "SBPr%04x", response);
1885 }
1886 return rc;
1887}
1888
1889static void qeth_bridgeport_an_set_cb(void *priv,
1890 enum qdio_brinfo_entry_type type, void *entry)
1891{
1892 struct qeth_card *card = (struct qeth_card *)priv;
1893 struct qdio_brinfo_entry_l2 *l2entry;
1894 u8 code;
1895
1896 if (type != l2_addr_lnid) {
1897 WARN_ON_ONCE(1);
1898 return;
1899 }
1900
1901 l2entry = (struct qdio_brinfo_entry_l2 *)entry;
1902 code = IPA_ADDR_CHANGE_CODE_MACADDR;
1903 if (l2entry->addr_lnid.lnid)
1904 code |= IPA_ADDR_CHANGE_CODE_VLANID;
1905 qeth_bridge_emit_host_event(card, anev_reg_unreg, code,
1906 (struct net_if_token *)&l2entry->nit,
1907 (struct mac_addr_lnid *)&l2entry->addr_lnid);
1908}
1909
1910/**
1911 * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification
1912 * @card: qeth_card structure pointer.
1913 * @enable: 0 - disable, non-zero - enable notifications
1914 *
1915 * Returns negative errno-compatible error indication or 0 on success.
1916 *
1917 * On enable, emits a series of address notifications udev events for all
1918 * currently registered hosts.
1919 */
1920int qeth_bridgeport_an_set(struct qeth_card *card, int enable)
1921{
1922 int rc;
1923 u16 response;
1924 struct ccw_device *ddev;
1925 struct subchannel_id schid;
1926
1927 if (!card)
1928 return -EINVAL;
1929 if (!card->options.sbp.supported_funcs)
1930 return -EOPNOTSUPP;
1931 ddev = CARD_DDEV(card);
1932 ccw_device_get_schid(ddev, &schid);
1933
1934 if (enable) {
1935 qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL);
1936 rc = qdio_pnso_brinfo(schid, 1, &response,
1937 qeth_bridgeport_an_set_cb, card);
1938 } else
1939 rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL);
1940 return qeth_anset_makerc(card, rc, response);
1941}
1942EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set);
1943
1714module_init(qeth_l2_init); 1944module_init(qeth_l2_init);
1715module_exit(qeth_l2_exit); 1945module_exit(qeth_l2_exit);
1716MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 1946MODULE_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
index 17fd4cd888f9..ae1bc04b8653 100644
--- a/drivers/s390/net/qeth_l2_sys.c
+++ b/drivers/s390/net/qeth_l2_sys.c
@@ -119,9 +119,63 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev,
119static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show, 119static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
120 NULL); 120 NULL);
121 121
122static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
123 struct device_attribute *attr, char *buf)
124{
125 struct qeth_card *card = dev_get_drvdata(dev);
126 int enabled;
127
128 if (!card)
129 return -EINVAL;
130
131 mutex_lock(&card->conf_mutex);
132
133 enabled = card->options.sbp.hostnotification;
134
135 mutex_unlock(&card->conf_mutex);
136
137 return sprintf(buf, "%d\n", enabled);
138}
139
140static ssize_t qeth_bridgeport_hostnotification_store(struct device *dev,
141 struct device_attribute *attr, const char *buf, size_t count)
142{
143 struct qeth_card *card = dev_get_drvdata(dev);
144 int rc = 0;
145 int enable;
146
147 if (!card)
148 return -EINVAL;
149
150 if (sysfs_streq(buf, "0"))
151 enable = 0;
152 else if (sysfs_streq(buf, "1"))
153 enable = 1;
154 else
155 return -EINVAL;
156
157 mutex_lock(&card->conf_mutex);
158
159 if (qeth_card_hw_is_reachable(card)) {
160 rc = qeth_bridgeport_an_set(card, enable);
161 if (!rc)
162 card->options.sbp.hostnotification = enable;
163 } else
164 card->options.sbp.hostnotification = enable;
165
166 mutex_unlock(&card->conf_mutex);
167
168 return rc ? rc : count;
169}
170
171static DEVICE_ATTR(bridge_hostnotify, 0644,
172 qeth_bridgeport_hostnotification_show,
173 qeth_bridgeport_hostnotification_store);
174
122static struct attribute *qeth_l2_bridgeport_attrs[] = { 175static struct attribute *qeth_l2_bridgeport_attrs[] = {
123 &dev_attr_bridge_role.attr, 176 &dev_attr_bridge_role.attr,
124 &dev_attr_bridge_state.attr, 177 &dev_attr_bridge_state.attr,
178 &dev_attr_bridge_hostnotify.attr,
125 NULL, 179 NULL,
126}; 180};
127 181
@@ -147,6 +201,8 @@ void qeth_l2_remove_device_attributes(struct device *dev)
147 */ 201 */
148void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) 202void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
149{ 203{
204 int rc;
205
150 if (!card) 206 if (!card)
151 return; 207 return;
152 if (!card->options.sbp.supported_funcs) 208 if (!card->options.sbp.supported_funcs)
@@ -158,4 +214,10 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card)
158 qeth_bridgeport_query_ports(card, 214 qeth_bridgeport_query_ports(card,
159 &card->options.sbp.role, NULL); 215 &card->options.sbp.role, NULL);
160 } 216 }
217 if (card->options.sbp.hostnotification) {
218 rc = qeth_bridgeport_an_set(card, 1);
219 if (rc)
220 card->options.sbp.hostnotification = 0;
221 } else
222 qeth_bridgeport_an_set(card, 0);
161} 223}