caif-hsi: Add rtnl support
authorSjur Brændeland <sjur.brandeland@stericsson.com>
Mon, 25 Jun 2012 07:49:41 +0000 (07:49 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Jun 2012 23:44:12 +0000 (16:44 -0700)
Add RTNL support for managing the caif hsi interface.
The HSI HW interface is no longer registering as a device,
instead we use symbol_get to get hold of the HSI API.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/caif/caif_hsi.c
include/net/caif/caif_hsi.h

index d80759e..a14f85c 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/sched.h>
 #include <linux/if_arp.h>
 #include <linux/timer.h>
-#include <linux/rtnetlink.h>
+#include <net/rtnetlink.h>
 #include <linux/pkt_sched.h>
 #include <net/caif/caif_layer.h>
 #include <net/caif/caif_hsi.h>
@@ -79,7 +79,6 @@ MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON).");
 #define HIGH_WATER_MARK  hsi_high_threshold
 
 static LIST_HEAD(cfhsi_list);
-static spinlock_t cfhsi_list_lock;
 
 static void cfhsi_inactivity_tout(unsigned long arg)
 {
@@ -1148,42 +1147,6 @@ static void cfhsi_setup(struct net_device *dev)
        cfhsi->ndev = dev;
 }
 
-int cfhsi_probe(struct platform_device *pdev)
-{
-       struct cfhsi *cfhsi = NULL;
-       struct net_device *ndev;
-
-       int res;
-
-       ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup);
-       if (!ndev)
-               return -ENODEV;
-
-       cfhsi = netdev_priv(ndev);
-       cfhsi->ndev = ndev;
-       cfhsi->pdev = pdev;
-
-       /* Assign the HSI device. */
-       cfhsi->dev = pdev->dev.platform_data;
-
-       /* Assign the driver to this HSI device. */
-       cfhsi->dev->drv = &cfhsi->drv;
-
-       /* Register network device. */
-       res = register_netdev(ndev);
-       if (res) {
-               dev_err(&ndev->dev, "%s: Registration error: %d.\n",
-                       __func__, res);
-               free_netdev(ndev);
-       }
-       /* Add CAIF HSI device to list. */
-       spin_lock(&cfhsi_list_lock);
-       list_add_tail(&cfhsi->list, &cfhsi_list);
-       spin_unlock(&cfhsi_list_lock);
-
-       return res;
-}
-
 static int cfhsi_open(struct net_device *ndev)
 {
        struct cfhsi *cfhsi = netdev_priv(ndev);
@@ -1364,85 +1327,170 @@ static int cfhsi_close(struct net_device *ndev)
        return 0;
 }
 
+static void cfhsi_uninit(struct net_device *dev)
+{
+       struct cfhsi *cfhsi = netdev_priv(dev);
+       ASSERT_RTNL();
+       symbol_put(cfhsi_get_device);
+       list_del(&cfhsi->list);
+}
+
 static const struct net_device_ops cfhsi_ops = {
+       .ndo_uninit = cfhsi_uninit,
        .ndo_open = cfhsi_open,
        .ndo_stop = cfhsi_close,
        .ndo_start_xmit = cfhsi_xmit
 };
 
-int cfhsi_remove(struct platform_device *pdev)
+static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi)
 {
-       struct list_head *list_node;
-       struct list_head *n;
-       struct cfhsi *cfhsi = NULL;
-       struct cfhsi_dev *dev;
+       int i;
 
-       dev = (struct cfhsi_dev *)pdev->dev.platform_data;
-       spin_lock(&cfhsi_list_lock);
-       list_for_each_safe(list_node, n, &cfhsi_list) {
-               cfhsi = list_entry(list_node, struct cfhsi, list);
-               /* Find the corresponding device. */
-               if (cfhsi->dev == dev) {
-                       /* Remove from list. */
-                       list_del(list_node);
-                       spin_unlock(&cfhsi_list_lock);
-                       return 0;
-               }
+       if (!data) {
+               pr_debug("no params data found\n");
+               return;
        }
-       spin_unlock(&cfhsi_list_lock);
-       return -ENODEV;
+
+       i = __IFLA_CAIF_HSI_INACTIVITY_TOUT;
+       if (data[i])
+               inactivity_timeout = nla_get_u32(data[i]);
+
+       i = __IFLA_CAIF_HSI_AGGREGATION_TOUT;
+       if (data[i])
+               aggregation_timeout = nla_get_u32(data[i]);
+
+       i = __IFLA_CAIF_HSI_HEAD_ALIGN;
+       if (data[i])
+               hsi_head_align = nla_get_u32(data[i]);
+
+       i = __IFLA_CAIF_HSI_TAIL_ALIGN;
+       if (data[i])
+               hsi_tail_align = nla_get_u32(data[i]);
+
+       i = __IFLA_CAIF_HSI_QHIGH_WATERMARK;
+       if (data[i])
+               hsi_high_threshold = nla_get_u32(data[i]);
+}
+
+static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[],
+                               struct nlattr *data[])
+{
+       cfhsi_netlink_parms(data, netdev_priv(dev));
+       netdev_state_change(dev);
+       return 0;
 }
 
-struct platform_driver cfhsi_plat_drv = {
-       .probe = cfhsi_probe,
-       .remove = cfhsi_remove,
-       .driver = {
-                  .name = "cfhsi",
-                  .owner = THIS_MODULE,
-                  },
+static const struct nla_policy caif_hsi_policy[__IFLA_CAIF_HSI_MAX + 1] = {
+       [__IFLA_CAIF_HSI_INACTIVITY_TOUT] = { .type = NLA_U32, .len = 4 },
+       [__IFLA_CAIF_HSI_AGGREGATION_TOUT] = { .type = NLA_U32, .len = 4 },
+       [__IFLA_CAIF_HSI_HEAD_ALIGN] = { .type = NLA_U32, .len = 4 },
+       [__IFLA_CAIF_HSI_TAIL_ALIGN] = { .type = NLA_U32, .len = 4 },
+       [__IFLA_CAIF_HSI_QHIGH_WATERMARK] = { .type = NLA_U32, .len = 4 },
+       [__IFLA_CAIF_HSI_QLOW_WATERMARK] = { .type = NLA_U32, .len = 4 },
 };
 
-static void __exit cfhsi_exit_module(void)
+static size_t caif_hsi_get_size(const struct net_device *dev)
+{
+       int i;
+       size_t s = 0;
+       for (i = __IFLA_CAIF_HSI_UNSPEC + 1; i < __IFLA_CAIF_HSI_MAX; i++)
+               s += nla_total_size(caif_hsi_policy[i].len);
+       return s;
+}
+
+static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+       if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT,
+                       inactivity_timeout) ||
+           nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT,
+                       aggregation_timeout) ||
+           nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) ||
+           nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) ||
+           nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK,
+                       hsi_high_threshold) ||
+           nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK,
+                       hsi_low_threshold))
+               return -EMSGSIZE;
+
+       return 0;
+}
+
+static int caif_hsi_newlink(struct net *src_net, struct net_device *dev,
+                         struct nlattr *tb[], struct nlattr *data[])
 {
-       struct list_head *list_node;
-       struct list_head *n;
        struct cfhsi *cfhsi = NULL;
+       struct platform_device *(*get_dev)(void);
 
-       spin_lock(&cfhsi_list_lock);
-       list_for_each_safe(list_node, n, &cfhsi_list) {
-               cfhsi = list_entry(list_node, struct cfhsi, list);
+       ASSERT_RTNL();
+
+       cfhsi = netdev_priv(dev);
+       cfhsi_netlink_parms(data, cfhsi);
+       dev_net_set(cfhsi->ndev, src_net);
+
+       get_dev = symbol_get(cfhsi_get_device);
+       if (!get_dev) {
+               pr_err("%s: failed to get the cfhsi device symbol\n", __func__);
+               return -ENODEV;
+       }
+
+       /* Assign the HSI device. */
+       cfhsi->pdev = (*get_dev)();
+       if (!cfhsi->pdev) {
+               pr_err("%s: failed to get the cfhsi device\n", __func__);
+               goto err;
+       }
 
-               /* Remove from list. */
-               list_del(list_node);
-               spin_unlock(&cfhsi_list_lock);
+       /* Assign the HSI device. */
+       cfhsi->dev = cfhsi->pdev->dev.platform_data;
+
+       /* Assign the driver to this HSI device. */
+       cfhsi->dev->drv = &cfhsi->drv;
 
-               unregister_netdevice(cfhsi->ndev);
+       if (register_netdevice(dev)) {
+               pr_warn("%s: device rtml registration failed\n", __func__);
+               goto err;
 
-               spin_lock(&cfhsi_list_lock);
        }
-       spin_unlock(&cfhsi_list_lock);
+       /* Add CAIF HSI device to list. */
+       list_add_tail(&cfhsi->list, &cfhsi_list);
 
-       /* Unregister platform driver. */
-       platform_driver_unregister(&cfhsi_plat_drv);
+       return 0;
+err:
+       symbol_put(cfhsi_get_device);
+       return -ENODEV;
 }
 
-static int __init cfhsi_init_module(void)
+static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = {
+       .kind           = "cfhsi",
+       .priv_size      = sizeof(struct cfhsi),
+       .setup          = cfhsi_setup,
+       .maxtype        = __IFLA_CAIF_HSI_MAX,
+       .policy = caif_hsi_policy,
+       .newlink        = caif_hsi_newlink,
+       .changelink     = caif_hsi_changelink,
+       .get_size       = caif_hsi_get_size,
+       .fill_info      = caif_hsi_fill_info,
+};
+
+static void __exit cfhsi_exit_module(void)
 {
-       int result;
+       struct list_head *list_node;
+       struct list_head *n;
+       struct cfhsi *cfhsi;
 
-       /* Initialize spin lock. */
-       spin_lock_init(&cfhsi_list_lock);
+       rtnl_link_unregister(&caif_hsi_link_ops);
 
-       /* Register platform driver. */
-       result = platform_driver_register(&cfhsi_plat_drv);
-       if (result) {
-               printk(KERN_ERR "Could not register platform HSI driver: %d.\n",
-                       result);
-               goto err_dev_register;
+       rtnl_lock();
+       list_for_each_safe(list_node, n, &cfhsi_list) {
+               cfhsi = list_entry(list_node, struct cfhsi, list);
+               unregister_netdev(cfhsi->ndev);
        }
+       rtnl_unlock();
+}
 
- err_dev_register:
-       return result;
+static int __init cfhsi_init_module(void)
+{
+       return rtnl_link_register(&caif_hsi_link_ops);
 }
 
 module_init(cfhsi_init_module);
index 439dadc..a77b2bd 100644 (file)
@@ -170,7 +170,26 @@ struct cfhsi {
 
        unsigned long bits;
 };
-
 extern struct platform_driver cfhsi_driver;
 
+/**
+ * enum ifla_caif_hsi - CAIF HSI NetlinkRT parameters.
+ * @IFLA_CAIF_HSI_INACTIVITY_TOUT: Inactivity timeout before
+ *                     taking the HSI wakeline down, in milliseconds.
+ * When using RT Netlink to create, destroy or configure a CAIF HSI interface,
+ * enum ifla_caif_hsi is used to specify the configuration attributes.
+ */
+enum ifla_caif_hsi {
+       __IFLA_CAIF_HSI_UNSPEC,
+       __IFLA_CAIF_HSI_INACTIVITY_TOUT,
+       __IFLA_CAIF_HSI_AGGREGATION_TOUT,
+       __IFLA_CAIF_HSI_HEAD_ALIGN,
+       __IFLA_CAIF_HSI_TAIL_ALIGN,
+       __IFLA_CAIF_HSI_QHIGH_WATERMARK,
+       __IFLA_CAIF_HSI_QLOW_WATERMARK,
+       __IFLA_CAIF_HSI_MAX
+};
+
+extern struct platform_device *cfhsi_get_device(void);
+
 #endif         /* CAIF_HSI_H_ */