diff options
Diffstat (limited to 'drivers/s390/net/qeth_l2_main.c')
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ec8ccdae7aba..908d82529ee9 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 *); |
@@ -32,6 +33,11 @@ static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *, | |||
32 | unsigned long)); | 33 | unsigned long)); |
33 | static void qeth_l2_set_multicast_list(struct net_device *); | 34 | static void qeth_l2_set_multicast_list(struct net_device *); |
34 | static int qeth_l2_recover(void *); | 35 | static int qeth_l2_recover(void *); |
36 | static void qeth_bridgeport_query_support(struct qeth_card *card); | ||
37 | static void qeth_bridge_state_change(struct qeth_card *card, | ||
38 | struct qeth_ipa_cmd *cmd); | ||
39 | static void qeth_bridge_host_event(struct qeth_card *card, | ||
40 | struct qeth_ipa_cmd *cmd); | ||
35 | 41 | ||
36 | static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | 42 | static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) |
37 | { | 43 | { |
@@ -880,6 +886,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev) | |||
880 | { | 886 | { |
881 | struct qeth_card *card = dev_get_drvdata(&gdev->dev); | 887 | struct qeth_card *card = dev_get_drvdata(&gdev->dev); |
882 | 888 | ||
889 | qeth_l2_create_device_attributes(&gdev->dev); | ||
883 | INIT_LIST_HEAD(&card->vid_list); | 890 | INIT_LIST_HEAD(&card->vid_list); |
884 | INIT_LIST_HEAD(&card->mc_list); | 891 | INIT_LIST_HEAD(&card->mc_list); |
885 | card->options.layer2 = 1; | 892 | card->options.layer2 = 1; |
@@ -891,6 +898,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) | |||
891 | { | 898 | { |
892 | struct qeth_card *card = dev_get_drvdata(&cgdev->dev); | 899 | struct qeth_card *card = dev_get_drvdata(&cgdev->dev); |
893 | 900 | ||
901 | qeth_l2_remove_device_attributes(&cgdev->dev); | ||
894 | qeth_set_allowed_threads(card, 0, 1); | 902 | qeth_set_allowed_threads(card, 0, 1); |
895 | wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); | 903 | wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); |
896 | 904 | ||
@@ -986,6 +994,10 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) | |||
986 | rc = -ENODEV; | 994 | rc = -ENODEV; |
987 | goto out_remove; | 995 | goto out_remove; |
988 | } | 996 | } |
997 | qeth_bridgeport_query_support(card); | ||
998 | if (card->options.sbp.supported_funcs) | ||
999 | dev_info(&card->gdev->dev, | ||
1000 | "The device represents a HiperSockets Bridge Capable Port\n"); | ||
989 | qeth_trace_features(card); | 1001 | qeth_trace_features(card); |
990 | 1002 | ||
991 | if (!card->dev && qeth_l2_setup_netdev(card)) { | 1003 | if (!card->dev && qeth_l2_setup_netdev(card)) { |
@@ -1003,6 +1015,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) | |||
1003 | } else | 1015 | } else |
1004 | card->info.hwtrap = 0; | 1016 | card->info.hwtrap = 0; |
1005 | 1017 | ||
1018 | qeth_l2_setup_bridgeport_attrs(card); | ||
1019 | |||
1006 | card->state = CARD_STATE_HARDSETUP; | 1020 | card->state = CARD_STATE_HARDSETUP; |
1007 | memset(&card->rx, 0, sizeof(struct qeth_rx)); | 1021 | memset(&card->rx, 0, sizeof(struct qeth_rx)); |
1008 | qeth_print_status_message(card); | 1022 | qeth_print_status_message(card); |
@@ -1077,6 +1091,7 @@ out_remove: | |||
1077 | ccw_device_set_offline(CARD_DDEV(card)); | 1091 | ccw_device_set_offline(CARD_DDEV(card)); |
1078 | ccw_device_set_offline(CARD_WDEV(card)); | 1092 | ccw_device_set_offline(CARD_WDEV(card)); |
1079 | ccw_device_set_offline(CARD_RDEV(card)); | 1093 | ccw_device_set_offline(CARD_RDEV(card)); |
1094 | qdio_free(CARD_DDEV(card)); | ||
1080 | if (recover_flag == CARD_STATE_RECOVER) | 1095 | if (recover_flag == CARD_STATE_RECOVER) |
1081 | card->state = CARD_STATE_RECOVER; | 1096 | card->state = CARD_STATE_RECOVER; |
1082 | else | 1097 | else |
@@ -1118,6 +1133,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, | |||
1118 | rc = (rc2) ? rc2 : rc3; | 1133 | rc = (rc2) ? rc2 : rc3; |
1119 | if (rc) | 1134 | if (rc) |
1120 | QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); | 1135 | QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); |
1136 | qdio_free(CARD_DDEV(card)); | ||
1121 | if (recover_flag == CARD_STATE_UP) | 1137 | if (recover_flag == CARD_STATE_UP) |
1122 | card->state = CARD_STATE_RECOVER; | 1138 | card->state = CARD_STATE_RECOVER; |
1123 | /* let user_space know that device is offline */ | 1139 | /* let user_space know that device is offline */ |
@@ -1180,6 +1196,7 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev) | |||
1180 | qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); | 1196 | qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); |
1181 | qeth_qdio_clear_card(card, 0); | 1197 | qeth_qdio_clear_card(card, 0); |
1182 | qeth_clear_qdio_buffers(card); | 1198 | qeth_clear_qdio_buffers(card); |
1199 | qdio_free(CARD_DDEV(card)); | ||
1183 | } | 1200 | } |
1184 | 1201 | ||
1185 | static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) | 1202 | static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) |
@@ -1228,6 +1245,26 @@ out: | |||
1228 | return rc; | 1245 | return rc; |
1229 | } | 1246 | } |
1230 | 1247 | ||
1248 | /* Returns zero if the command is successfully "consumed" */ | ||
1249 | static int qeth_l2_control_event(struct qeth_card *card, | ||
1250 | struct qeth_ipa_cmd *cmd) | ||
1251 | { | ||
1252 | switch (cmd->hdr.command) { | ||
1253 | case IPA_CMD_SETBRIDGEPORT: | ||
1254 | if (cmd->data.sbp.hdr.command_code == | ||
1255 | IPA_SBP_BRIDGE_PORT_STATE_CHANGE) { | ||
1256 | qeth_bridge_state_change(card, cmd); | ||
1257 | return 0; | ||
1258 | } else | ||
1259 | return 1; | ||
1260 | case IPA_CMD_ADDRESS_CHANGE_NOTIF: | ||
1261 | qeth_bridge_host_event(card, cmd); | ||
1262 | return 0; | ||
1263 | default: | ||
1264 | return 1; | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1231 | struct qeth_discipline qeth_l2_discipline = { | 1268 | struct qeth_discipline qeth_l2_discipline = { |
1232 | .start_poll = qeth_qdio_start_poll, | 1269 | .start_poll = qeth_qdio_start_poll, |
1233 | .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, | 1270 | .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, |
@@ -1241,6 +1278,7 @@ struct qeth_discipline qeth_l2_discipline = { | |||
1241 | .freeze = qeth_l2_pm_suspend, | 1278 | .freeze = qeth_l2_pm_suspend, |
1242 | .thaw = qeth_l2_pm_resume, | 1279 | .thaw = qeth_l2_pm_resume, |
1243 | .restore = qeth_l2_pm_resume, | 1280 | .restore = qeth_l2_pm_resume, |
1281 | .control_event_handler = qeth_l2_control_event, | ||
1244 | }; | 1282 | }; |
1245 | EXPORT_SYMBOL_GPL(qeth_l2_discipline); | 1283 | EXPORT_SYMBOL_GPL(qeth_l2_discipline); |
1246 | 1284 | ||
@@ -1347,6 +1385,594 @@ void qeth_osn_deregister(struct net_device *dev) | |||
1347 | } | 1385 | } |
1348 | EXPORT_SYMBOL(qeth_osn_deregister); | 1386 | EXPORT_SYMBOL(qeth_osn_deregister); |
1349 | 1387 | ||
1388 | /* SETBRIDGEPORT support, async notifications */ | ||
1389 | |||
1390 | enum qeth_an_event_type {anev_reg_unreg, anev_abort, anev_reset}; | ||
1391 | |||
1392 | /** | ||
1393 | * qeth_bridge_emit_host_event() - bridgeport address change notification | ||
1394 | * @card: qeth_card structure pointer, for udev events. | ||
1395 | * @evtype: "normal" register/unregister, or abort, or reset. For abort | ||
1396 | * and reset token and addr_lnid are unused and may be NULL. | ||
1397 | * @code: event bitmask: high order bit 0x80 value 1 means removal of an | ||
1398 | * object, 0 - addition of an object. | ||
1399 | * 0x01 - VLAN, 0x02 - MAC, 0x03 - VLAN and MAC. | ||
1400 | * @token: "network token" structure identifying physical address of the port. | ||
1401 | * @addr_lnid: pointer to structure with MAC address and VLAN ID. | ||
1402 | * | ||
1403 | * This function is called when registrations and deregistrations are | ||
1404 | * reported by the hardware, and also when notifications are enabled - | ||
1405 | * for all currently registered addresses. | ||
1406 | */ | ||
1407 | static void qeth_bridge_emit_host_event(struct qeth_card *card, | ||
1408 | enum qeth_an_event_type evtype, | ||
1409 | u8 code, struct net_if_token *token, struct mac_addr_lnid *addr_lnid) | ||
1410 | { | ||
1411 | char str[7][32]; | ||
1412 | char *env[8]; | ||
1413 | int i = 0; | ||
1414 | |||
1415 | switch (evtype) { | ||
1416 | case anev_reg_unreg: | ||
1417 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=%s", | ||
1418 | (code & IPA_ADDR_CHANGE_CODE_REMOVAL) | ||
1419 | ? "deregister" : "register"); | ||
1420 | env[i] = str[i]; i++; | ||
1421 | if (code & IPA_ADDR_CHANGE_CODE_VLANID) { | ||
1422 | snprintf(str[i], sizeof(str[i]), "VLAN=%d", | ||
1423 | addr_lnid->lnid); | ||
1424 | env[i] = str[i]; i++; | ||
1425 | } | ||
1426 | if (code & IPA_ADDR_CHANGE_CODE_MACADDR) { | ||
1427 | snprintf(str[i], sizeof(str[i]), "MAC=%pM6", | ||
1428 | &addr_lnid->mac); | ||
1429 | env[i] = str[i]; i++; | ||
1430 | } | ||
1431 | snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x", | ||
1432 | token->cssid, token->ssid, token->devnum); | ||
1433 | env[i] = str[i]; i++; | ||
1434 | snprintf(str[i], sizeof(str[i]), "NTOK_IID=%02x", token->iid); | ||
1435 | env[i] = str[i]; i++; | ||
1436 | snprintf(str[i], sizeof(str[i]), "NTOK_CHPID=%02x", | ||
1437 | token->chpid); | ||
1438 | env[i] = str[i]; i++; | ||
1439 | snprintf(str[i], sizeof(str[i]), "NTOK_CHID=%04x", token->chid); | ||
1440 | env[i] = str[i]; i++; | ||
1441 | break; | ||
1442 | case anev_abort: | ||
1443 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=abort"); | ||
1444 | env[i] = str[i]; i++; | ||
1445 | break; | ||
1446 | case anev_reset: | ||
1447 | snprintf(str[i], sizeof(str[i]), "BRIDGEDHOST=reset"); | ||
1448 | env[i] = str[i]; i++; | ||
1449 | break; | ||
1450 | } | ||
1451 | env[i] = NULL; | ||
1452 | kobject_uevent_env(&card->gdev->dev.kobj, KOBJ_CHANGE, env); | ||
1453 | } | ||
1454 | |||
1455 | struct qeth_bridge_state_data { | ||
1456 | struct work_struct worker; | ||
1457 | struct qeth_card *card; | ||
1458 | struct qeth_sbp_state_change qports; | ||
1459 | }; | ||
1460 | |||
1461 | static void qeth_bridge_state_change_worker(struct work_struct *work) | ||
1462 | { | ||
1463 | struct qeth_bridge_state_data *data = | ||
1464 | container_of(work, struct qeth_bridge_state_data, worker); | ||
1465 | /* We are only interested in the first entry - local port */ | ||
1466 | struct qeth_sbp_port_entry *entry = &data->qports.entry[0]; | ||
1467 | char env_locrem[32]; | ||
1468 | char env_role[32]; | ||
1469 | char env_state[32]; | ||
1470 | char *env[] = { | ||
1471 | env_locrem, | ||
1472 | env_role, | ||
1473 | env_state, | ||
1474 | NULL | ||
1475 | }; | ||
1476 | |||
1477 | /* Role should not change by itself, but if it did, */ | ||
1478 | /* information from the hardware is authoritative. */ | ||
1479 | mutex_lock(&data->card->conf_mutex); | ||
1480 | data->card->options.sbp.role = entry->role; | ||
1481 | mutex_unlock(&data->card->conf_mutex); | ||
1482 | |||
1483 | snprintf(env_locrem, sizeof(env_locrem), "BRIDGEPORT=statechange"); | ||
1484 | snprintf(env_role, sizeof(env_role), "ROLE=%s", | ||
1485 | (entry->role == QETH_SBP_ROLE_NONE) ? "none" : | ||
1486 | (entry->role == QETH_SBP_ROLE_PRIMARY) ? "primary" : | ||
1487 | (entry->role == QETH_SBP_ROLE_SECONDARY) ? "secondary" : | ||
1488 | "<INVALID>"); | ||
1489 | snprintf(env_state, sizeof(env_state), "STATE=%s", | ||
1490 | (entry->state == QETH_SBP_STATE_INACTIVE) ? "inactive" : | ||
1491 | (entry->state == QETH_SBP_STATE_STANDBY) ? "standby" : | ||
1492 | (entry->state == QETH_SBP_STATE_ACTIVE) ? "active" : | ||
1493 | "<INVALID>"); | ||
1494 | kobject_uevent_env(&data->card->gdev->dev.kobj, | ||
1495 | KOBJ_CHANGE, env); | ||
1496 | kfree(data); | ||
1497 | } | ||
1498 | |||
1499 | static void qeth_bridge_state_change(struct qeth_card *card, | ||
1500 | struct qeth_ipa_cmd *cmd) | ||
1501 | { | ||
1502 | struct qeth_sbp_state_change *qports = | ||
1503 | &cmd->data.sbp.data.state_change; | ||
1504 | struct qeth_bridge_state_data *data; | ||
1505 | int extrasize; | ||
1506 | |||
1507 | QETH_CARD_TEXT(card, 2, "brstchng"); | ||
1508 | if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||
1509 | QETH_CARD_TEXT_(card, 2, "BPsz%.8d", qports->entry_length); | ||
1510 | return; | ||
1511 | } | ||
1512 | extrasize = sizeof(struct qeth_sbp_port_entry) * qports->num_entries; | ||
1513 | data = kzalloc(sizeof(struct qeth_bridge_state_data) + extrasize, | ||
1514 | GFP_ATOMIC); | ||
1515 | if (!data) { | ||
1516 | QETH_CARD_TEXT(card, 2, "BPSalloc"); | ||
1517 | return; | ||
1518 | } | ||
1519 | INIT_WORK(&data->worker, qeth_bridge_state_change_worker); | ||
1520 | data->card = card; | ||
1521 | memcpy(&data->qports, qports, | ||
1522 | sizeof(struct qeth_sbp_state_change) + extrasize); | ||
1523 | queue_work(qeth_wq, &data->worker); | ||
1524 | } | ||
1525 | |||
1526 | struct qeth_bridge_host_data { | ||
1527 | struct work_struct worker; | ||
1528 | struct qeth_card *card; | ||
1529 | struct qeth_ipacmd_addr_change hostevs; | ||
1530 | }; | ||
1531 | |||
1532 | static void qeth_bridge_host_event_worker(struct work_struct *work) | ||
1533 | { | ||
1534 | struct qeth_bridge_host_data *data = | ||
1535 | container_of(work, struct qeth_bridge_host_data, worker); | ||
1536 | int i; | ||
1537 | |||
1538 | if (data->hostevs.lost_event_mask) { | ||
1539 | dev_info(&data->card->gdev->dev, | ||
1540 | "Address notification from the HiperSockets Bridge Port stopped %s (%s)\n", | ||
1541 | data->card->dev->name, | ||
1542 | (data->hostevs.lost_event_mask == 0x01) | ||
1543 | ? "Overflow" | ||
1544 | : (data->hostevs.lost_event_mask == 0x02) | ||
1545 | ? "Bridge port state change" | ||
1546 | : "Unknown reason"); | ||
1547 | mutex_lock(&data->card->conf_mutex); | ||
1548 | data->card->options.sbp.hostnotification = 0; | ||
1549 | mutex_unlock(&data->card->conf_mutex); | ||
1550 | qeth_bridge_emit_host_event(data->card, anev_abort, | ||
1551 | 0, NULL, NULL); | ||
1552 | } else | ||
1553 | for (i = 0; i < data->hostevs.num_entries; i++) { | ||
1554 | struct qeth_ipacmd_addr_change_entry *entry = | ||
1555 | &data->hostevs.entry[i]; | ||
1556 | qeth_bridge_emit_host_event(data->card, | ||
1557 | anev_reg_unreg, | ||
1558 | entry->change_code, | ||
1559 | &entry->token, &entry->addr_lnid); | ||
1560 | } | ||
1561 | kfree(data); | ||
1562 | } | ||
1563 | |||
1564 | static void qeth_bridge_host_event(struct qeth_card *card, | ||
1565 | struct qeth_ipa_cmd *cmd) | ||
1566 | { | ||
1567 | struct qeth_ipacmd_addr_change *hostevs = | ||
1568 | &cmd->data.addrchange; | ||
1569 | struct qeth_bridge_host_data *data; | ||
1570 | int extrasize; | ||
1571 | |||
1572 | QETH_CARD_TEXT(card, 2, "brhostev"); | ||
1573 | if (cmd->hdr.return_code != 0x0000) { | ||
1574 | if (cmd->hdr.return_code == 0x0010) { | ||
1575 | if (hostevs->lost_event_mask == 0x00) | ||
1576 | hostevs->lost_event_mask = 0xff; | ||
1577 | } else { | ||
1578 | QETH_CARD_TEXT_(card, 2, "BPHe%04x", | ||
1579 | cmd->hdr.return_code); | ||
1580 | return; | ||
1581 | } | ||
1582 | } | ||
1583 | extrasize = sizeof(struct qeth_ipacmd_addr_change_entry) * | ||
1584 | hostevs->num_entries; | ||
1585 | data = kzalloc(sizeof(struct qeth_bridge_host_data) + extrasize, | ||
1586 | GFP_ATOMIC); | ||
1587 | if (!data) { | ||
1588 | QETH_CARD_TEXT(card, 2, "BPHalloc"); | ||
1589 | return; | ||
1590 | } | ||
1591 | INIT_WORK(&data->worker, qeth_bridge_host_event_worker); | ||
1592 | data->card = card; | ||
1593 | memcpy(&data->hostevs, hostevs, | ||
1594 | sizeof(struct qeth_ipacmd_addr_change) + extrasize); | ||
1595 | queue_work(qeth_wq, &data->worker); | ||
1596 | } | ||
1597 | |||
1598 | /* SETBRIDGEPORT support; sending commands */ | ||
1599 | |||
1600 | struct _qeth_sbp_cbctl { | ||
1601 | u16 ipa_rc; | ||
1602 | u16 cmd_rc; | ||
1603 | union { | ||
1604 | u32 supported; | ||
1605 | struct { | ||
1606 | enum qeth_sbp_roles *role; | ||
1607 | enum qeth_sbp_states *state; | ||
1608 | } qports; | ||
1609 | } data; | ||
1610 | }; | ||
1611 | |||
1612 | /** | ||
1613 | * qeth_bridgeport_makerc() - derive "traditional" error from hardware codes. | ||
1614 | * @card: qeth_card structure pointer, for debug messages. | ||
1615 | * @cbctl: state structure with hardware return codes. | ||
1616 | * @setcmd: IPA command code | ||
1617 | * | ||
1618 | * Returns negative errno-compatible error indication or 0 on success. | ||
1619 | */ | ||
1620 | static int qeth_bridgeport_makerc(struct qeth_card *card, | ||
1621 | struct _qeth_sbp_cbctl *cbctl, enum qeth_ipa_sbp_cmd setcmd) | ||
1622 | { | ||
1623 | int rc; | ||
1624 | |||
1625 | switch (cbctl->ipa_rc) { | ||
1626 | case IPA_RC_SUCCESS: | ||
1627 | switch (cbctl->cmd_rc) { | ||
1628 | case 0x0000: | ||
1629 | rc = 0; | ||
1630 | break; | ||
1631 | case 0x0004: | ||
1632 | rc = -ENOSYS; | ||
1633 | break; | ||
1634 | case 0x000C: /* Not configured as bridge Port */ | ||
1635 | rc = -ENODEV; /* maybe not the best code here? */ | ||
1636 | dev_err(&card->gdev->dev, | ||
1637 | "The HiperSockets device is not configured as a Bridge Port\n"); | ||
1638 | break; | ||
1639 | case 0x0014: /* Another device is Primary */ | ||
1640 | switch (setcmd) { | ||
1641 | case IPA_SBP_SET_PRIMARY_BRIDGE_PORT: | ||
1642 | rc = -EEXIST; | ||
1643 | dev_err(&card->gdev->dev, | ||
1644 | "The HiperSockets LAN already has a primary Bridge Port\n"); | ||
1645 | break; | ||
1646 | case IPA_SBP_SET_SECONDARY_BRIDGE_PORT: | ||
1647 | rc = -EBUSY; | ||
1648 | dev_err(&card->gdev->dev, | ||
1649 | "The HiperSockets device is already a primary Bridge Port\n"); | ||
1650 | break; | ||
1651 | default: | ||
1652 | rc = -EIO; | ||
1653 | } | ||
1654 | break; | ||
1655 | case 0x0018: /* This device is currently Secondary */ | ||
1656 | rc = -EBUSY; | ||
1657 | dev_err(&card->gdev->dev, | ||
1658 | "The HiperSockets device is already a secondary Bridge Port\n"); | ||
1659 | break; | ||
1660 | case 0x001C: /* Limit for Secondary devices reached */ | ||
1661 | rc = -EEXIST; | ||
1662 | dev_err(&card->gdev->dev, | ||
1663 | "The HiperSockets LAN cannot have more secondary Bridge Ports\n"); | ||
1664 | break; | ||
1665 | case 0x0024: /* This device is currently Primary */ | ||
1666 | rc = -EBUSY; | ||
1667 | dev_err(&card->gdev->dev, | ||
1668 | "The HiperSockets device is already a primary Bridge Port\n"); | ||
1669 | break; | ||
1670 | case 0x0020: /* Not authorized by zManager */ | ||
1671 | rc = -EACCES; | ||
1672 | dev_err(&card->gdev->dev, | ||
1673 | "The HiperSockets device is not authorized to be a Bridge Port\n"); | ||
1674 | break; | ||
1675 | default: | ||
1676 | rc = -EIO; | ||
1677 | } | ||
1678 | break; | ||
1679 | case IPA_RC_NOTSUPP: | ||
1680 | rc = -ENOSYS; | ||
1681 | break; | ||
1682 | case IPA_RC_UNSUPPORTED_COMMAND: | ||
1683 | rc = -ENOSYS; | ||
1684 | break; | ||
1685 | default: | ||
1686 | rc = -EIO; | ||
1687 | } | ||
1688 | if (rc) { | ||
1689 | QETH_CARD_TEXT_(card, 2, "SBPi%04x", cbctl->ipa_rc); | ||
1690 | QETH_CARD_TEXT_(card, 2, "SBPc%04x", cbctl->cmd_rc); | ||
1691 | } | ||
1692 | return rc; | ||
1693 | } | ||
1694 | |||
1695 | static int qeth_bridgeport_query_support_cb(struct qeth_card *card, | ||
1696 | struct qeth_reply *reply, unsigned long data) | ||
1697 | { | ||
1698 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||
1699 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1700 | QETH_CARD_TEXT(card, 2, "brqsupcb"); | ||
1701 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1702 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1703 | if ((cbctl->ipa_rc == 0) && (cbctl->cmd_rc == 0)) { | ||
1704 | cbctl->data.supported = | ||
1705 | cmd->data.sbp.data.query_cmds_supp.supported_cmds; | ||
1706 | } else { | ||
1707 | cbctl->data.supported = 0; | ||
1708 | } | ||
1709 | return 0; | ||
1710 | } | ||
1711 | |||
1712 | /** | ||
1713 | * qeth_bridgeport_query_support() - store bitmask of supported subfunctions. | ||
1714 | * @card: qeth_card structure pointer. | ||
1715 | * | ||
1716 | * Sets bitmask of supported setbridgeport subfunctions in the qeth_card | ||
1717 | * strucutre: card->options.sbp.supported_funcs. | ||
1718 | */ | ||
1719 | static void qeth_bridgeport_query_support(struct qeth_card *card) | ||
1720 | { | ||
1721 | struct qeth_cmd_buffer *iob; | ||
1722 | struct qeth_ipa_cmd *cmd; | ||
1723 | struct _qeth_sbp_cbctl cbctl; | ||
1724 | |||
1725 | QETH_CARD_TEXT(card, 2, "brqsuppo"); | ||
1726 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1727 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1728 | cmd->data.sbp.hdr.cmdlength = | ||
1729 | sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1730 | sizeof(struct qeth_sbp_query_cmds_supp); | ||
1731 | cmd->data.sbp.hdr.command_code = | ||
1732 | IPA_SBP_QUERY_COMMANDS_SUPPORTED; | ||
1733 | cmd->data.sbp.hdr.used_total = 1; | ||
1734 | cmd->data.sbp.hdr.seq_no = 1; | ||
1735 | if (qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_support_cb, | ||
1736 | (void *)&cbctl) || | ||
1737 | qeth_bridgeport_makerc(card, &cbctl, | ||
1738 | IPA_SBP_QUERY_COMMANDS_SUPPORTED)) { | ||
1739 | /* non-zero makerc signifies failure, and produce messages */ | ||
1740 | card->options.sbp.role = QETH_SBP_ROLE_NONE; | ||
1741 | return; | ||
1742 | } | ||
1743 | card->options.sbp.supported_funcs = cbctl.data.supported; | ||
1744 | } | ||
1745 | |||
1746 | static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, | ||
1747 | struct qeth_reply *reply, unsigned long data) | ||
1748 | { | ||
1749 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; | ||
1750 | struct qeth_sbp_query_ports *qports = &cmd->data.sbp.data.query_ports; | ||
1751 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1752 | |||
1753 | QETH_CARD_TEXT(card, 2, "brqprtcb"); | ||
1754 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1755 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1756 | if ((cbctl->ipa_rc != 0) || (cbctl->cmd_rc != 0)) | ||
1757 | return 0; | ||
1758 | if (qports->entry_length != sizeof(struct qeth_sbp_port_entry)) { | ||
1759 | cbctl->cmd_rc = 0xffff; | ||
1760 | QETH_CARD_TEXT_(card, 2, "SBPs%04x", qports->entry_length); | ||
1761 | return 0; | ||
1762 | } | ||
1763 | /* first entry contains the state of the local port */ | ||
1764 | if (qports->num_entries > 0) { | ||
1765 | if (cbctl->data.qports.role) | ||
1766 | *cbctl->data.qports.role = qports->entry[0].role; | ||
1767 | if (cbctl->data.qports.state) | ||
1768 | *cbctl->data.qports.state = qports->entry[0].state; | ||
1769 | } | ||
1770 | return 0; | ||
1771 | } | ||
1772 | |||
1773 | /** | ||
1774 | * qeth_bridgeport_query_ports() - query local bridgeport status. | ||
1775 | * @card: qeth_card structure pointer. | ||
1776 | * @role: Role of the port: 0-none, 1-primary, 2-secondary. | ||
1777 | * @state: State of the port: 0-inactive, 1-standby, 2-active. | ||
1778 | * | ||
1779 | * Returns negative errno-compatible error indication or 0 on success. | ||
1780 | * | ||
1781 | * 'role' and 'state' are not updated in case of hardware operation failure. | ||
1782 | */ | ||
1783 | int qeth_bridgeport_query_ports(struct qeth_card *card, | ||
1784 | enum qeth_sbp_roles *role, enum qeth_sbp_states *state) | ||
1785 | { | ||
1786 | int rc = 0; | ||
1787 | struct qeth_cmd_buffer *iob; | ||
1788 | struct qeth_ipa_cmd *cmd; | ||
1789 | struct _qeth_sbp_cbctl cbctl = { | ||
1790 | .data = { | ||
1791 | .qports = { | ||
1792 | .role = role, | ||
1793 | .state = state, | ||
1794 | }, | ||
1795 | }, | ||
1796 | }; | ||
1797 | |||
1798 | QETH_CARD_TEXT(card, 2, "brqports"); | ||
1799 | if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) | ||
1800 | return -EOPNOTSUPP; | ||
1801 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1802 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1803 | cmd->data.sbp.hdr.cmdlength = | ||
1804 | sizeof(struct qeth_ipacmd_sbp_hdr); | ||
1805 | cmd->data.sbp.hdr.command_code = | ||
1806 | IPA_SBP_QUERY_BRIDGE_PORTS; | ||
1807 | cmd->data.sbp.hdr.used_total = 1; | ||
1808 | cmd->data.sbp.hdr.seq_no = 1; | ||
1809 | rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_query_ports_cb, | ||
1810 | (void *)&cbctl); | ||
1811 | if (rc) | ||
1812 | return rc; | ||
1813 | rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); | ||
1814 | if (rc) | ||
1815 | return rc; | ||
1816 | return 0; | ||
1817 | } | ||
1818 | EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); | ||
1819 | |||
1820 | static int qeth_bridgeport_set_cb(struct qeth_card *card, | ||
1821 | struct qeth_reply *reply, unsigned long data) | ||
1822 | { | ||
1823 | struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; | ||
1824 | struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; | ||
1825 | QETH_CARD_TEXT(card, 2, "brsetrcb"); | ||
1826 | cbctl->ipa_rc = cmd->hdr.return_code; | ||
1827 | cbctl->cmd_rc = cmd->data.sbp.hdr.return_code; | ||
1828 | return 0; | ||
1829 | } | ||
1830 | |||
1831 | /** | ||
1832 | * qeth_bridgeport_setrole() - Assign primary role to the port. | ||
1833 | * @card: qeth_card structure pointer. | ||
1834 | * @role: Role to assign. | ||
1835 | * | ||
1836 | * Returns negative errno-compatible error indication or 0 on success. | ||
1837 | */ | ||
1838 | int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) | ||
1839 | { | ||
1840 | int rc = 0; | ||
1841 | int cmdlength; | ||
1842 | struct qeth_cmd_buffer *iob; | ||
1843 | struct qeth_ipa_cmd *cmd; | ||
1844 | struct _qeth_sbp_cbctl cbctl; | ||
1845 | enum qeth_ipa_sbp_cmd setcmd; | ||
1846 | |||
1847 | QETH_CARD_TEXT(card, 2, "brsetrol"); | ||
1848 | switch (role) { | ||
1849 | case QETH_SBP_ROLE_NONE: | ||
1850 | setcmd = IPA_SBP_RESET_BRIDGE_PORT_ROLE; | ||
1851 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1852 | sizeof(struct qeth_sbp_reset_role); | ||
1853 | break; | ||
1854 | case QETH_SBP_ROLE_PRIMARY: | ||
1855 | setcmd = IPA_SBP_SET_PRIMARY_BRIDGE_PORT; | ||
1856 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1857 | sizeof(struct qeth_sbp_set_primary); | ||
1858 | break; | ||
1859 | case QETH_SBP_ROLE_SECONDARY: | ||
1860 | setcmd = IPA_SBP_SET_SECONDARY_BRIDGE_PORT; | ||
1861 | cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + | ||
1862 | sizeof(struct qeth_sbp_set_secondary); | ||
1863 | break; | ||
1864 | default: | ||
1865 | return -EINVAL; | ||
1866 | } | ||
1867 | if (!(card->options.sbp.supported_funcs & setcmd)) | ||
1868 | return -EOPNOTSUPP; | ||
1869 | iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); | ||
1870 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
1871 | cmd->data.sbp.hdr.cmdlength = cmdlength; | ||
1872 | cmd->data.sbp.hdr.command_code = setcmd; | ||
1873 | cmd->data.sbp.hdr.used_total = 1; | ||
1874 | cmd->data.sbp.hdr.seq_no = 1; | ||
1875 | rc = qeth_send_ipa_cmd(card, iob, qeth_bridgeport_set_cb, | ||
1876 | (void *)&cbctl); | ||
1877 | if (rc) | ||
1878 | return rc; | ||
1879 | rc = qeth_bridgeport_makerc(card, &cbctl, setcmd); | ||
1880 | return rc; | ||
1881 | } | ||
1882 | |||
1883 | /** | ||
1884 | * qeth_anset_makerc() - derive "traditional" error from hardware codes. | ||
1885 | * @card: qeth_card structure pointer, for debug messages. | ||
1886 | * | ||
1887 | * Returns negative errno-compatible error indication or 0 on success. | ||
1888 | */ | ||
1889 | static int qeth_anset_makerc(struct qeth_card *card, int pnso_rc, u16 response) | ||
1890 | { | ||
1891 | int rc; | ||
1892 | |||
1893 | if (pnso_rc == 0) | ||
1894 | switch (response) { | ||
1895 | case 0x0001: | ||
1896 | rc = 0; | ||
1897 | break; | ||
1898 | case 0x0004: | ||
1899 | case 0x0100: | ||
1900 | case 0x0106: | ||
1901 | rc = -ENOSYS; | ||
1902 | dev_err(&card->gdev->dev, | ||
1903 | "Setting address notification failed\n"); | ||
1904 | break; | ||
1905 | case 0x0107: | ||
1906 | rc = -EAGAIN; | ||
1907 | break; | ||
1908 | default: | ||
1909 | rc = -EIO; | ||
1910 | } | ||
1911 | else | ||
1912 | rc = -EIO; | ||
1913 | |||
1914 | if (rc) { | ||
1915 | QETH_CARD_TEXT_(card, 2, "SBPp%04x", pnso_rc); | ||
1916 | QETH_CARD_TEXT_(card, 2, "SBPr%04x", response); | ||
1917 | } | ||
1918 | return rc; | ||
1919 | } | ||
1920 | |||
1921 | static void qeth_bridgeport_an_set_cb(void *priv, | ||
1922 | enum qdio_brinfo_entry_type type, void *entry) | ||
1923 | { | ||
1924 | struct qeth_card *card = (struct qeth_card *)priv; | ||
1925 | struct qdio_brinfo_entry_l2 *l2entry; | ||
1926 | u8 code; | ||
1927 | |||
1928 | if (type != l2_addr_lnid) { | ||
1929 | WARN_ON_ONCE(1); | ||
1930 | return; | ||
1931 | } | ||
1932 | |||
1933 | l2entry = (struct qdio_brinfo_entry_l2 *)entry; | ||
1934 | code = IPA_ADDR_CHANGE_CODE_MACADDR; | ||
1935 | if (l2entry->addr_lnid.lnid) | ||
1936 | code |= IPA_ADDR_CHANGE_CODE_VLANID; | ||
1937 | qeth_bridge_emit_host_event(card, anev_reg_unreg, code, | ||
1938 | (struct net_if_token *)&l2entry->nit, | ||
1939 | (struct mac_addr_lnid *)&l2entry->addr_lnid); | ||
1940 | } | ||
1941 | |||
1942 | /** | ||
1943 | * qeth_bridgeport_an_set() - Enable or disable bridgeport address notification | ||
1944 | * @card: qeth_card structure pointer. | ||
1945 | * @enable: 0 - disable, non-zero - enable notifications | ||
1946 | * | ||
1947 | * Returns negative errno-compatible error indication or 0 on success. | ||
1948 | * | ||
1949 | * On enable, emits a series of address notifications udev events for all | ||
1950 | * currently registered hosts. | ||
1951 | */ | ||
1952 | int qeth_bridgeport_an_set(struct qeth_card *card, int enable) | ||
1953 | { | ||
1954 | int rc; | ||
1955 | u16 response; | ||
1956 | struct ccw_device *ddev; | ||
1957 | struct subchannel_id schid; | ||
1958 | |||
1959 | if (!card) | ||
1960 | return -EINVAL; | ||
1961 | if (!card->options.sbp.supported_funcs) | ||
1962 | return -EOPNOTSUPP; | ||
1963 | ddev = CARD_DDEV(card); | ||
1964 | ccw_device_get_schid(ddev, &schid); | ||
1965 | |||
1966 | if (enable) { | ||
1967 | qeth_bridge_emit_host_event(card, anev_reset, 0, NULL, NULL); | ||
1968 | rc = qdio_pnso_brinfo(schid, 1, &response, | ||
1969 | qeth_bridgeport_an_set_cb, card); | ||
1970 | } else | ||
1971 | rc = qdio_pnso_brinfo(schid, 0, &response, NULL, NULL); | ||
1972 | return qeth_anset_makerc(card, rc, response); | ||
1973 | } | ||
1974 | EXPORT_SYMBOL_GPL(qeth_bridgeport_an_set); | ||
1975 | |||
1350 | module_init(qeth_l2_init); | 1976 | module_init(qeth_l2_init); |
1351 | module_exit(qeth_l2_exit); | 1977 | module_exit(qeth_l2_exit); |
1352 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); | 1978 | MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); |