diff options
Diffstat (limited to 'drivers/s390/net/ctcm_main.c')
-rw-r--r-- | drivers/s390/net/ctcm_main.c | 168 |
1 files changed, 131 insertions, 37 deletions
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index c5b83874500c..e35713dd0504 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c | |||
@@ -51,12 +51,16 @@ | |||
51 | 51 | ||
52 | #include <asm/idals.h> | 52 | #include <asm/idals.h> |
53 | 53 | ||
54 | #include "cu3088.h" | ||
55 | #include "ctcm_fsms.h" | 54 | #include "ctcm_fsms.h" |
56 | #include "ctcm_main.h" | 55 | #include "ctcm_main.h" |
57 | 56 | ||
58 | /* Some common global variables */ | 57 | /* Some common global variables */ |
59 | 58 | ||
59 | /** | ||
60 | * The root device for ctcm group devices | ||
61 | */ | ||
62 | static struct device *ctcm_root_dev; | ||
63 | |||
60 | /* | 64 | /* |
61 | * Linked list of all detected channels. | 65 | * Linked list of all detected channels. |
62 | */ | 66 | */ |
@@ -246,7 +250,7 @@ static void channel_remove(struct channel *ch) | |||
246 | * | 250 | * |
247 | * returns Pointer to a channel or NULL if no matching channel available. | 251 | * returns Pointer to a channel or NULL if no matching channel available. |
248 | */ | 252 | */ |
249 | static struct channel *channel_get(enum channel_types type, | 253 | static struct channel *channel_get(enum ctcm_channel_types type, |
250 | char *id, int direction) | 254 | char *id, int direction) |
251 | { | 255 | { |
252 | struct channel *ch = channels; | 256 | struct channel *ch = channels; |
@@ -1342,7 +1346,7 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev) | |||
1342 | * | 1346 | * |
1343 | * returns 0 on success, !0 on error. | 1347 | * returns 0 on success, !0 on error. |
1344 | */ | 1348 | */ |
1345 | static int add_channel(struct ccw_device *cdev, enum channel_types type, | 1349 | static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, |
1346 | struct ctcm_priv *priv) | 1350 | struct ctcm_priv *priv) |
1347 | { | 1351 | { |
1348 | struct channel **c = &channels; | 1352 | struct channel **c = &channels; |
@@ -1501,13 +1505,13 @@ free_return: /* note that all channel pointers are 0 or valid */ | |||
1501 | /* | 1505 | /* |
1502 | * Return type of a detected device. | 1506 | * Return type of a detected device. |
1503 | */ | 1507 | */ |
1504 | static enum channel_types get_channel_type(struct ccw_device_id *id) | 1508 | static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id) |
1505 | { | 1509 | { |
1506 | enum channel_types type; | 1510 | enum ctcm_channel_types type; |
1507 | type = (enum channel_types)id->driver_info; | 1511 | type = (enum ctcm_channel_types)id->driver_info; |
1508 | 1512 | ||
1509 | if (type == channel_type_ficon) | 1513 | if (type == ctcm_channel_type_ficon) |
1510 | type = channel_type_escon; | 1514 | type = ctcm_channel_type_escon; |
1511 | 1515 | ||
1512 | return type; | 1516 | return type; |
1513 | } | 1517 | } |
@@ -1525,16 +1529,21 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) | |||
1525 | char read_id[CTCM_ID_SIZE]; | 1529 | char read_id[CTCM_ID_SIZE]; |
1526 | char write_id[CTCM_ID_SIZE]; | 1530 | char write_id[CTCM_ID_SIZE]; |
1527 | int direction; | 1531 | int direction; |
1528 | enum channel_types type; | 1532 | enum ctcm_channel_types type; |
1529 | struct ctcm_priv *priv; | 1533 | struct ctcm_priv *priv; |
1530 | struct net_device *dev; | 1534 | struct net_device *dev; |
1531 | struct ccw_device *cdev0; | 1535 | struct ccw_device *cdev0; |
1532 | struct ccw_device *cdev1; | 1536 | struct ccw_device *cdev1; |
1537 | struct channel *readc; | ||
1538 | struct channel *writec; | ||
1533 | int ret; | 1539 | int ret; |
1540 | int result; | ||
1534 | 1541 | ||
1535 | priv = dev_get_drvdata(&cgdev->dev); | 1542 | priv = dev_get_drvdata(&cgdev->dev); |
1536 | if (!priv) | 1543 | if (!priv) { |
1537 | return -ENODEV; | 1544 | result = -ENODEV; |
1545 | goto out_err_result; | ||
1546 | } | ||
1538 | 1547 | ||
1539 | cdev0 = cgdev->cdev[0]; | 1548 | cdev0 = cgdev->cdev[0]; |
1540 | cdev1 = cgdev->cdev[1]; | 1549 | cdev1 = cgdev->cdev[1]; |
@@ -1545,31 +1554,40 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) | |||
1545 | snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev)); | 1554 | snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev)); |
1546 | 1555 | ||
1547 | ret = add_channel(cdev0, type, priv); | 1556 | ret = add_channel(cdev0, type, priv); |
1548 | if (ret) | 1557 | if (ret) { |
1549 | return ret; | 1558 | result = ret; |
1559 | goto out_err_result; | ||
1560 | } | ||
1550 | ret = add_channel(cdev1, type, priv); | 1561 | ret = add_channel(cdev1, type, priv); |
1551 | if (ret) | 1562 | if (ret) { |
1552 | return ret; | 1563 | result = ret; |
1564 | goto out_remove_channel1; | ||
1565 | } | ||
1553 | 1566 | ||
1554 | ret = ccw_device_set_online(cdev0); | 1567 | ret = ccw_device_set_online(cdev0); |
1555 | if (ret != 0) { | 1568 | if (ret != 0) { |
1556 | /* may be ok to fail now - can be done later */ | ||
1557 | CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, | 1569 | CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, |
1558 | "%s(%s) set_online rc=%d", | 1570 | "%s(%s) set_online rc=%d", |
1559 | CTCM_FUNTAIL, read_id, ret); | 1571 | CTCM_FUNTAIL, read_id, ret); |
1572 | result = -EIO; | ||
1573 | goto out_remove_channel2; | ||
1560 | } | 1574 | } |
1561 | 1575 | ||
1562 | ret = ccw_device_set_online(cdev1); | 1576 | ret = ccw_device_set_online(cdev1); |
1563 | if (ret != 0) { | 1577 | if (ret != 0) { |
1564 | /* may be ok to fail now - can be done later */ | ||
1565 | CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, | 1578 | CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, |
1566 | "%s(%s) set_online rc=%d", | 1579 | "%s(%s) set_online rc=%d", |
1567 | CTCM_FUNTAIL, write_id, ret); | 1580 | CTCM_FUNTAIL, write_id, ret); |
1581 | |||
1582 | result = -EIO; | ||
1583 | goto out_ccw1; | ||
1568 | } | 1584 | } |
1569 | 1585 | ||
1570 | dev = ctcm_init_netdevice(priv); | 1586 | dev = ctcm_init_netdevice(priv); |
1571 | if (dev == NULL) | 1587 | if (dev == NULL) { |
1572 | goto out; | 1588 | result = -ENODEV; |
1589 | goto out_ccw2; | ||
1590 | } | ||
1573 | 1591 | ||
1574 | for (direction = READ; direction <= WRITE; direction++) { | 1592 | for (direction = READ; direction <= WRITE; direction++) { |
1575 | priv->channel[direction] = | 1593 | priv->channel[direction] = |
@@ -1587,12 +1605,14 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) | |||
1587 | /* sysfs magic */ | 1605 | /* sysfs magic */ |
1588 | SET_NETDEV_DEV(dev, &cgdev->dev); | 1606 | SET_NETDEV_DEV(dev, &cgdev->dev); |
1589 | 1607 | ||
1590 | if (register_netdev(dev)) | 1608 | if (register_netdev(dev)) { |
1591 | goto out_dev; | 1609 | result = -ENODEV; |
1610 | goto out_dev; | ||
1611 | } | ||
1592 | 1612 | ||
1593 | if (ctcm_add_attributes(&cgdev->dev)) { | 1613 | if (ctcm_add_attributes(&cgdev->dev)) { |
1594 | unregister_netdev(dev); | 1614 | result = -ENODEV; |
1595 | goto out_dev; | 1615 | goto out_unregister; |
1596 | } | 1616 | } |
1597 | 1617 | ||
1598 | strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); | 1618 | strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); |
@@ -1608,13 +1628,22 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev) | |||
1608 | priv->channel[WRITE]->id, priv->protocol); | 1628 | priv->channel[WRITE]->id, priv->protocol); |
1609 | 1629 | ||
1610 | return 0; | 1630 | return 0; |
1631 | out_unregister: | ||
1632 | unregister_netdev(dev); | ||
1611 | out_dev: | 1633 | out_dev: |
1612 | ctcm_free_netdevice(dev); | 1634 | ctcm_free_netdevice(dev); |
1613 | out: | 1635 | out_ccw2: |
1614 | ccw_device_set_offline(cgdev->cdev[1]); | 1636 | ccw_device_set_offline(cgdev->cdev[1]); |
1637 | out_ccw1: | ||
1615 | ccw_device_set_offline(cgdev->cdev[0]); | 1638 | ccw_device_set_offline(cgdev->cdev[0]); |
1616 | 1639 | out_remove_channel2: | |
1617 | return -ENODEV; | 1640 | readc = channel_get(type, read_id, READ); |
1641 | channel_remove(readc); | ||
1642 | out_remove_channel1: | ||
1643 | writec = channel_get(type, write_id, WRITE); | ||
1644 | channel_remove(writec); | ||
1645 | out_err_result: | ||
1646 | return result; | ||
1618 | } | 1647 | } |
1619 | 1648 | ||
1620 | /** | 1649 | /** |
@@ -1695,6 +1724,11 @@ static int ctcm_pm_suspend(struct ccwgroup_device *gdev) | |||
1695 | return 0; | 1724 | return 0; |
1696 | netif_device_detach(priv->channel[READ]->netdev); | 1725 | netif_device_detach(priv->channel[READ]->netdev); |
1697 | ctcm_close(priv->channel[READ]->netdev); | 1726 | ctcm_close(priv->channel[READ]->netdev); |
1727 | if (!wait_event_timeout(priv->fsm->wait_q, | ||
1728 | fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) { | ||
1729 | netif_device_attach(priv->channel[READ]->netdev); | ||
1730 | return -EBUSY; | ||
1731 | } | ||
1698 | ccw_device_set_offline(gdev->cdev[1]); | 1732 | ccw_device_set_offline(gdev->cdev[1]); |
1699 | ccw_device_set_offline(gdev->cdev[0]); | 1733 | ccw_device_set_offline(gdev->cdev[0]); |
1700 | return 0; | 1734 | return 0; |
@@ -1719,6 +1753,22 @@ err_out: | |||
1719 | return rc; | 1753 | return rc; |
1720 | } | 1754 | } |
1721 | 1755 | ||
1756 | static struct ccw_device_id ctcm_ids[] = { | ||
1757 | {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel}, | ||
1758 | {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon}, | ||
1759 | {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon}, | ||
1760 | {}, | ||
1761 | }; | ||
1762 | MODULE_DEVICE_TABLE(ccw, ctcm_ids); | ||
1763 | |||
1764 | static struct ccw_driver ctcm_ccw_driver = { | ||
1765 | .owner = THIS_MODULE, | ||
1766 | .name = "ctcm", | ||
1767 | .ids = ctcm_ids, | ||
1768 | .probe = ccwgroup_probe_ccwdev, | ||
1769 | .remove = ccwgroup_remove_ccwdev, | ||
1770 | }; | ||
1771 | |||
1722 | static struct ccwgroup_driver ctcm_group_driver = { | 1772 | static struct ccwgroup_driver ctcm_group_driver = { |
1723 | .owner = THIS_MODULE, | 1773 | .owner = THIS_MODULE, |
1724 | .name = CTC_DRIVER_NAME, | 1774 | .name = CTC_DRIVER_NAME, |
@@ -1733,6 +1783,33 @@ static struct ccwgroup_driver ctcm_group_driver = { | |||
1733 | .restore = ctcm_pm_resume, | 1783 | .restore = ctcm_pm_resume, |
1734 | }; | 1784 | }; |
1735 | 1785 | ||
1786 | static ssize_t | ||
1787 | ctcm_driver_group_store(struct device_driver *ddrv, const char *buf, | ||
1788 | size_t count) | ||
1789 | { | ||
1790 | int err; | ||
1791 | |||
1792 | err = ccwgroup_create_from_string(ctcm_root_dev, | ||
1793 | ctcm_group_driver.driver_id, | ||
1794 | &ctcm_ccw_driver, 2, buf); | ||
1795 | return err ? err : count; | ||
1796 | } | ||
1797 | |||
1798 | static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store); | ||
1799 | |||
1800 | static struct attribute *ctcm_group_attrs[] = { | ||
1801 | &driver_attr_group.attr, | ||
1802 | NULL, | ||
1803 | }; | ||
1804 | |||
1805 | static struct attribute_group ctcm_group_attr_group = { | ||
1806 | .attrs = ctcm_group_attrs, | ||
1807 | }; | ||
1808 | |||
1809 | static const struct attribute_group *ctcm_group_attr_groups[] = { | ||
1810 | &ctcm_group_attr_group, | ||
1811 | NULL, | ||
1812 | }; | ||
1736 | 1813 | ||
1737 | /* | 1814 | /* |
1738 | * Module related routines | 1815 | * Module related routines |
@@ -1746,7 +1823,10 @@ static struct ccwgroup_driver ctcm_group_driver = { | |||
1746 | */ | 1823 | */ |
1747 | static void __exit ctcm_exit(void) | 1824 | static void __exit ctcm_exit(void) |
1748 | { | 1825 | { |
1749 | unregister_cu3088_discipline(&ctcm_group_driver); | 1826 | driver_remove_file(&ctcm_group_driver.driver, &driver_attr_group); |
1827 | ccwgroup_driver_unregister(&ctcm_group_driver); | ||
1828 | ccw_driver_unregister(&ctcm_ccw_driver); | ||
1829 | root_device_unregister(ctcm_root_dev); | ||
1750 | ctcm_unregister_dbf_views(); | 1830 | ctcm_unregister_dbf_views(); |
1751 | pr_info("CTCM driver unloaded\n"); | 1831 | pr_info("CTCM driver unloaded\n"); |
1752 | } | 1832 | } |
@@ -1772,17 +1852,31 @@ static int __init ctcm_init(void) | |||
1772 | channels = NULL; | 1852 | channels = NULL; |
1773 | 1853 | ||
1774 | ret = ctcm_register_dbf_views(); | 1854 | ret = ctcm_register_dbf_views(); |
1775 | if (ret) { | 1855 | if (ret) |
1776 | return ret; | 1856 | goto out_err; |
1777 | } | 1857 | ctcm_root_dev = root_device_register("ctcm"); |
1778 | ret = register_cu3088_discipline(&ctcm_group_driver); | 1858 | ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; |
1779 | if (ret) { | 1859 | if (ret) |
1780 | ctcm_unregister_dbf_views(); | 1860 | goto register_err; |
1781 | pr_err("%s / register_cu3088_discipline failed, ret = %d\n", | 1861 | ret = ccw_driver_register(&ctcm_ccw_driver); |
1782 | __func__, ret); | 1862 | if (ret) |
1783 | return ret; | 1863 | goto ccw_err; |
1784 | } | 1864 | ctcm_group_driver.driver.groups = ctcm_group_attr_groups; |
1865 | ret = ccwgroup_driver_register(&ctcm_group_driver); | ||
1866 | if (ret) | ||
1867 | goto ccwgroup_err; | ||
1785 | print_banner(); | 1868 | print_banner(); |
1869 | return 0; | ||
1870 | |||
1871 | ccwgroup_err: | ||
1872 | ccw_driver_unregister(&ctcm_ccw_driver); | ||
1873 | ccw_err: | ||
1874 | root_device_unregister(ctcm_root_dev); | ||
1875 | register_err: | ||
1876 | ctcm_unregister_dbf_views(); | ||
1877 | out_err: | ||
1878 | pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n", | ||
1879 | __func__, ret); | ||
1786 | return ret; | 1880 | return ret; |
1787 | } | 1881 | } |
1788 | 1882 | ||