Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4125400 authored by Sjur Brændeland's avatar Sjur Brændeland Committed by David S. Miller
Browse files

caif-hsi: Add rtnl support



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: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5051c94b
Loading
Loading
Loading
Loading
+137 −89
Original line number Diff line number Diff line
@@ -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;
	}

	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]);
}
	spin_unlock(&cfhsi_list_lock);
	return -ENODEV;

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();

		/* Remove from list. */
		list_del(list_node);
		spin_unlock(&cfhsi_list_lock);
	cfhsi = netdev_priv(dev);
	cfhsi_netlink_parms(data, cfhsi);
	dev_net_set(cfhsi->ndev, src_net);

		unregister_netdevice(cfhsi->ndev);
	get_dev = symbol_get(cfhsi_get_device);
	if (!get_dev) {
		pr_err("%s: failed to get the cfhsi device symbol\n", __func__);
		return -ENODEV;
	}

		spin_lock(&cfhsi_list_lock);
	/* Assign the HSI device. */
	cfhsi->pdev = (*get_dev)();
	if (!cfhsi->pdev) {
		pr_err("%s: failed to get the cfhsi device\n", __func__);
		goto err;
	}
	spin_unlock(&cfhsi_list_lock);

	/* Unregister platform driver. */
	platform_driver_unregister(&cfhsi_plat_drv);
	/* Assign the HSI device. */
	cfhsi->dev = cfhsi->pdev->dev.platform_data;

	/* Assign the driver to this HSI device. */
	cfhsi->dev->drv = &cfhsi->drv;

	if (register_netdevice(dev)) {
		pr_warn("%s: device rtml registration failed\n", __func__);
		goto err;

	}
	/* Add CAIF HSI device to list. */
	list_add_tail(&cfhsi->list, &cfhsi_list);

static int __init cfhsi_init_module(void)
	return 0;
err:
	symbol_put(cfhsi_get_device);
	return -ENODEV;
}

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);
+20 −1
Original line number Diff line number Diff line
@@ -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_ */