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: Sjur Brændeland <sjur.brandeland@stericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5051c94bb3
commit
c412540063
@ -20,7 +20,7 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/rtnetlink.h>
|
#include <net/rtnetlink.h>
|
||||||
#include <linux/pkt_sched.h>
|
#include <linux/pkt_sched.h>
|
||||||
#include <net/caif/caif_layer.h>
|
#include <net/caif/caif_layer.h>
|
||||||
#include <net/caif/caif_hsi.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
|
#define HIGH_WATER_MARK hsi_high_threshold
|
||||||
|
|
||||||
static LIST_HEAD(cfhsi_list);
|
static LIST_HEAD(cfhsi_list);
|
||||||
static spinlock_t cfhsi_list_lock;
|
|
||||||
|
|
||||||
static void cfhsi_inactivity_tout(unsigned long arg)
|
static void cfhsi_inactivity_tout(unsigned long arg)
|
||||||
{
|
{
|
||||||
@ -1148,42 +1147,6 @@ static void cfhsi_setup(struct net_device *dev)
|
|||||||
cfhsi->ndev = 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)
|
static int cfhsi_open(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
struct cfhsi *cfhsi = netdev_priv(ndev);
|
struct cfhsi *cfhsi = netdev_priv(ndev);
|
||||||
@ -1364,85 +1327,170 @@ static int cfhsi_close(struct net_device *ndev)
|
|||||||
return 0;
|
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 = {
|
static const struct net_device_ops cfhsi_ops = {
|
||||||
|
.ndo_uninit = cfhsi_uninit,
|
||||||
.ndo_open = cfhsi_open,
|
.ndo_open = cfhsi_open,
|
||||||
.ndo_stop = cfhsi_close,
|
.ndo_stop = cfhsi_close,
|
||||||
.ndo_start_xmit = cfhsi_xmit
|
.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;
|
int i;
|
||||||
struct list_head *n;
|
|
||||||
struct cfhsi *cfhsi = NULL;
|
|
||||||
struct cfhsi_dev *dev;
|
|
||||||
|
|
||||||
dev = (struct cfhsi_dev *)pdev->dev.platform_data;
|
if (!data) {
|
||||||
spin_lock(&cfhsi_list_lock);
|
pr_debug("no params data found\n");
|
||||||
list_for_each_safe(list_node, n, &cfhsi_list) {
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
spin_unlock(&cfhsi_list_lock);
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 cfhsi *cfhsi = NULL;
|
||||||
|
struct platform_device *(*get_dev)(void);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
symbol_put(cfhsi_get_device);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct platform_driver cfhsi_plat_drv = {
|
static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = {
|
||||||
.probe = cfhsi_probe,
|
.kind = "cfhsi",
|
||||||
.remove = cfhsi_remove,
|
.priv_size = sizeof(struct cfhsi),
|
||||||
.driver = {
|
.setup = cfhsi_setup,
|
||||||
.name = "cfhsi",
|
.maxtype = __IFLA_CAIF_HSI_MAX,
|
||||||
.owner = THIS_MODULE,
|
.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)
|
static void __exit cfhsi_exit_module(void)
|
||||||
{
|
{
|
||||||
struct list_head *list_node;
|
struct list_head *list_node;
|
||||||
struct list_head *n;
|
struct list_head *n;
|
||||||
struct cfhsi *cfhsi = NULL;
|
struct cfhsi *cfhsi;
|
||||||
|
|
||||||
spin_lock(&cfhsi_list_lock);
|
rtnl_link_unregister(&caif_hsi_link_ops);
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
list_for_each_safe(list_node, n, &cfhsi_list) {
|
list_for_each_safe(list_node, n, &cfhsi_list) {
|
||||||
cfhsi = list_entry(list_node, struct cfhsi, list);
|
cfhsi = list_entry(list_node, struct cfhsi, list);
|
||||||
|
unregister_netdev(cfhsi->ndev);
|
||||||
/* Remove from list. */
|
|
||||||
list_del(list_node);
|
|
||||||
spin_unlock(&cfhsi_list_lock);
|
|
||||||
|
|
||||||
unregister_netdevice(cfhsi->ndev);
|
|
||||||
|
|
||||||
spin_lock(&cfhsi_list_lock);
|
|
||||||
}
|
}
|
||||||
spin_unlock(&cfhsi_list_lock);
|
rtnl_unlock();
|
||||||
|
|
||||||
/* Unregister platform driver. */
|
|
||||||
platform_driver_unregister(&cfhsi_plat_drv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init cfhsi_init_module(void)
|
static int __init cfhsi_init_module(void)
|
||||||
{
|
{
|
||||||
int result;
|
return rtnl_link_register(&caif_hsi_link_ops);
|
||||||
|
|
||||||
/* Initialize spin lock. */
|
|
||||||
spin_lock_init(&cfhsi_list_lock);
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
err_dev_register:
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(cfhsi_init_module);
|
module_init(cfhsi_init_module);
|
||||||
|
@ -170,7 +170,26 @@ struct cfhsi {
|
|||||||
|
|
||||||
unsigned long bits;
|
unsigned long bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct platform_driver cfhsi_driver;
|
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_ */
|
#endif /* CAIF_HSI_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user