Skip to content

Commit

Permalink
caif-hsi: Add rtnl support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sjur Brændeland authored and davem330 committed Jun 25, 2012
1 parent 5051c94 commit c412540
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 90 deletions.
226 changes: 137 additions & 89 deletions drivers/net/caif/caif_hsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
21 changes: 20 additions & 1 deletion include/net/caif/caif_hsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */

0 comments on commit c412540

Please sign in to comment.