diff options
author | Sjur Brændeland <sjur.brandeland@stericsson.com> | 2012-06-25 03:49:41 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-25 19:44:12 -0400 |
commit | c41254006377842013922fb1e407391f24c59522 (patch) | |
tree | 9ed40b855fe2ad5a9808cee6e622acd3935400b4 /drivers/net/caif/caif_hsi.c | |
parent | 5051c94bb3998ff24bf07ae3b72dca30f85962f8 (diff) |
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>
Diffstat (limited to 'drivers/net/caif/caif_hsi.c')
-rw-r--r-- | drivers/net/caif/caif_hsi.c | 226 |
1 files changed, 137 insertions, 89 deletions
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index d80759e58532..a14f85c0f0e8 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
21 | #include <linux/if_arp.h> | 21 | #include <linux/if_arp.h> |
22 | #include <linux/timer.h> | 22 | #include <linux/timer.h> |
23 | #include <linux/rtnetlink.h> | 23 | #include <net/rtnetlink.h> |
24 | #include <linux/pkt_sched.h> | 24 | #include <linux/pkt_sched.h> |
25 | #include <net/caif/caif_layer.h> | 25 | #include <net/caif/caif_layer.h> |
26 | #include <net/caif/caif_hsi.h> | 26 | #include <net/caif/caif_hsi.h> |
@@ -79,7 +79,6 @@ MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); | |||
79 | #define HIGH_WATER_MARK hsi_high_threshold | 79 | #define HIGH_WATER_MARK hsi_high_threshold |
80 | 80 | ||
81 | static LIST_HEAD(cfhsi_list); | 81 | static LIST_HEAD(cfhsi_list); |
82 | static spinlock_t cfhsi_list_lock; | ||
83 | 82 | ||
84 | static void cfhsi_inactivity_tout(unsigned long arg) | 83 | static void cfhsi_inactivity_tout(unsigned long arg) |
85 | { | 84 | { |
@@ -1148,42 +1147,6 @@ static void cfhsi_setup(struct net_device *dev) | |||
1148 | cfhsi->ndev = dev; | 1147 | cfhsi->ndev = dev; |
1149 | } | 1148 | } |
1150 | 1149 | ||
1151 | int cfhsi_probe(struct platform_device *pdev) | ||
1152 | { | ||
1153 | struct cfhsi *cfhsi = NULL; | ||
1154 | struct net_device *ndev; | ||
1155 | |||
1156 | int res; | ||
1157 | |||
1158 | ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); | ||
1159 | if (!ndev) | ||
1160 | return -ENODEV; | ||
1161 | |||
1162 | cfhsi = netdev_priv(ndev); | ||
1163 | cfhsi->ndev = ndev; | ||
1164 | cfhsi->pdev = pdev; | ||
1165 | |||
1166 | /* Assign the HSI device. */ | ||
1167 | cfhsi->dev = pdev->dev.platform_data; | ||
1168 | |||
1169 | /* Assign the driver to this HSI device. */ | ||
1170 | cfhsi->dev->drv = &cfhsi->drv; | ||
1171 | |||
1172 | /* Register network device. */ | ||
1173 | res = register_netdev(ndev); | ||
1174 | if (res) { | ||
1175 | dev_err(&ndev->dev, "%s: Registration error: %d.\n", | ||
1176 | __func__, res); | ||
1177 | free_netdev(ndev); | ||
1178 | } | ||
1179 | /* Add CAIF HSI device to list. */ | ||
1180 | spin_lock(&cfhsi_list_lock); | ||
1181 | list_add_tail(&cfhsi->list, &cfhsi_list); | ||
1182 | spin_unlock(&cfhsi_list_lock); | ||
1183 | |||
1184 | return res; | ||
1185 | } | ||
1186 | |||
1187 | static int cfhsi_open(struct net_device *ndev) | 1150 | static int cfhsi_open(struct net_device *ndev) |
1188 | { | 1151 | { |
1189 | struct cfhsi *cfhsi = netdev_priv(ndev); | 1152 | struct cfhsi *cfhsi = netdev_priv(ndev); |
@@ -1364,85 +1327,170 @@ static int cfhsi_close(struct net_device *ndev) | |||
1364 | return 0; | 1327 | return 0; |
1365 | } | 1328 | } |
1366 | 1329 | ||
1330 | static void cfhsi_uninit(struct net_device *dev) | ||
1331 | { | ||
1332 | struct cfhsi *cfhsi = netdev_priv(dev); | ||
1333 | ASSERT_RTNL(); | ||
1334 | symbol_put(cfhsi_get_device); | ||
1335 | list_del(&cfhsi->list); | ||
1336 | } | ||
1337 | |||
1367 | static const struct net_device_ops cfhsi_ops = { | 1338 | static const struct net_device_ops cfhsi_ops = { |
1339 | .ndo_uninit = cfhsi_uninit, | ||
1368 | .ndo_open = cfhsi_open, | 1340 | .ndo_open = cfhsi_open, |
1369 | .ndo_stop = cfhsi_close, | 1341 | .ndo_stop = cfhsi_close, |
1370 | .ndo_start_xmit = cfhsi_xmit | 1342 | .ndo_start_xmit = cfhsi_xmit |
1371 | }; | 1343 | }; |
1372 | 1344 | ||
1373 | int cfhsi_remove(struct platform_device *pdev) | 1345 | static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) |
1374 | { | 1346 | { |
1375 | struct list_head *list_node; | 1347 | int i; |
1376 | struct list_head *n; | ||
1377 | struct cfhsi *cfhsi = NULL; | ||
1378 | struct cfhsi_dev *dev; | ||
1379 | 1348 | ||
1380 | dev = (struct cfhsi_dev *)pdev->dev.platform_data; | 1349 | if (!data) { |
1381 | spin_lock(&cfhsi_list_lock); | 1350 | pr_debug("no params data found\n"); |
1382 | list_for_each_safe(list_node, n, &cfhsi_list) { | 1351 | return; |
1383 | cfhsi = list_entry(list_node, struct cfhsi, list); | ||
1384 | /* Find the corresponding device. */ | ||
1385 | if (cfhsi->dev == dev) { | ||
1386 | /* Remove from list. */ | ||
1387 | list_del(list_node); | ||
1388 | spin_unlock(&cfhsi_list_lock); | ||
1389 | return 0; | ||
1390 | } | ||
1391 | } | 1352 | } |
1392 | spin_unlock(&cfhsi_list_lock); | 1353 | |
1393 | return -ENODEV; | 1354 | i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; |
1355 | if (data[i]) | ||
1356 | inactivity_timeout = nla_get_u32(data[i]); | ||
1357 | |||
1358 | i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; | ||
1359 | if (data[i]) | ||
1360 | aggregation_timeout = nla_get_u32(data[i]); | ||
1361 | |||
1362 | i = __IFLA_CAIF_HSI_HEAD_ALIGN; | ||
1363 | if (data[i]) | ||
1364 | hsi_head_align = nla_get_u32(data[i]); | ||
1365 | |||
1366 | i = __IFLA_CAIF_HSI_TAIL_ALIGN; | ||
1367 | if (data[i]) | ||
1368 | hsi_tail_align = nla_get_u32(data[i]); | ||
1369 | |||
1370 | i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; | ||
1371 | if (data[i]) | ||
1372 | hsi_high_threshold = nla_get_u32(data[i]); | ||
1373 | } | ||
1374 | |||
1375 | static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], | ||
1376 | struct nlattr *data[]) | ||
1377 | { | ||
1378 | cfhsi_netlink_parms(data, netdev_priv(dev)); | ||
1379 | netdev_state_change(dev); | ||
1380 | return 0; | ||
1394 | } | 1381 | } |
1395 | 1382 | ||
1396 | struct platform_driver cfhsi_plat_drv = { | 1383 | static const struct nla_policy caif_hsi_policy[__IFLA_CAIF_HSI_MAX + 1] = { |
1397 | .probe = cfhsi_probe, | 1384 | [__IFLA_CAIF_HSI_INACTIVITY_TOUT] = { .type = NLA_U32, .len = 4 }, |
1398 | .remove = cfhsi_remove, | 1385 | [__IFLA_CAIF_HSI_AGGREGATION_TOUT] = { .type = NLA_U32, .len = 4 }, |
1399 | .driver = { | 1386 | [__IFLA_CAIF_HSI_HEAD_ALIGN] = { .type = NLA_U32, .len = 4 }, |
1400 | .name = "cfhsi", | 1387 | [__IFLA_CAIF_HSI_TAIL_ALIGN] = { .type = NLA_U32, .len = 4 }, |
1401 | .owner = THIS_MODULE, | 1388 | [__IFLA_CAIF_HSI_QHIGH_WATERMARK] = { .type = NLA_U32, .len = 4 }, |
1402 | }, | 1389 | [__IFLA_CAIF_HSI_QLOW_WATERMARK] = { .type = NLA_U32, .len = 4 }, |
1403 | }; | 1390 | }; |
1404 | 1391 | ||
1405 | static void __exit cfhsi_exit_module(void) | 1392 | static size_t caif_hsi_get_size(const struct net_device *dev) |
1393 | { | ||
1394 | int i; | ||
1395 | size_t s = 0; | ||
1396 | for (i = __IFLA_CAIF_HSI_UNSPEC + 1; i < __IFLA_CAIF_HSI_MAX; i++) | ||
1397 | s += nla_total_size(caif_hsi_policy[i].len); | ||
1398 | return s; | ||
1399 | } | ||
1400 | |||
1401 | static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) | ||
1402 | { | ||
1403 | if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, | ||
1404 | inactivity_timeout) || | ||
1405 | nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, | ||
1406 | aggregation_timeout) || | ||
1407 | nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) || | ||
1408 | nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) || | ||
1409 | nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, | ||
1410 | hsi_high_threshold) || | ||
1411 | nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, | ||
1412 | hsi_low_threshold)) | ||
1413 | return -EMSGSIZE; | ||
1414 | |||
1415 | return 0; | ||
1416 | } | ||
1417 | |||
1418 | static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, | ||
1419 | struct nlattr *tb[], struct nlattr *data[]) | ||
1406 | { | 1420 | { |
1407 | struct list_head *list_node; | ||
1408 | struct list_head *n; | ||
1409 | struct cfhsi *cfhsi = NULL; | 1421 | struct cfhsi *cfhsi = NULL; |
1422 | struct platform_device *(*get_dev)(void); | ||
1410 | 1423 | ||
1411 | spin_lock(&cfhsi_list_lock); | 1424 | ASSERT_RTNL(); |
1412 | list_for_each_safe(list_node, n, &cfhsi_list) { | 1425 | |
1413 | cfhsi = list_entry(list_node, struct cfhsi, list); | 1426 | cfhsi = netdev_priv(dev); |
1427 | cfhsi_netlink_parms(data, cfhsi); | ||
1428 | dev_net_set(cfhsi->ndev, src_net); | ||
1429 | |||
1430 | get_dev = symbol_get(cfhsi_get_device); | ||
1431 | if (!get_dev) { | ||
1432 | pr_err("%s: failed to get the cfhsi device symbol\n", __func__); | ||
1433 | return -ENODEV; | ||
1434 | } | ||
1435 | |||
1436 | /* Assign the HSI device. */ | ||
1437 | cfhsi->pdev = (*get_dev)(); | ||
1438 | if (!cfhsi->pdev) { | ||
1439 | pr_err("%s: failed to get the cfhsi device\n", __func__); | ||
1440 | goto err; | ||
1441 | } | ||
1414 | 1442 | ||
1415 | /* Remove from list. */ | 1443 | /* Assign the HSI device. */ |
1416 | list_del(list_node); | 1444 | cfhsi->dev = cfhsi->pdev->dev.platform_data; |
1417 | spin_unlock(&cfhsi_list_lock); | 1445 | |
1446 | /* Assign the driver to this HSI device. */ | ||
1447 | cfhsi->dev->drv = &cfhsi->drv; | ||
1418 | 1448 | ||
1419 | unregister_netdevice(cfhsi->ndev); | 1449 | if (register_netdevice(dev)) { |
1450 | pr_warn("%s: device rtml registration failed\n", __func__); | ||
1451 | goto err; | ||
1420 | 1452 | ||
1421 | spin_lock(&cfhsi_list_lock); | ||
1422 | } | 1453 | } |
1423 | spin_unlock(&cfhsi_list_lock); | 1454 | /* Add CAIF HSI device to list. */ |
1455 | list_add_tail(&cfhsi->list, &cfhsi_list); | ||
1424 | 1456 | ||
1425 | /* Unregister platform driver. */ | 1457 | return 0; |
1426 | platform_driver_unregister(&cfhsi_plat_drv); | 1458 | err: |
1459 | symbol_put(cfhsi_get_device); | ||
1460 | return -ENODEV; | ||
1427 | } | 1461 | } |
1428 | 1462 | ||
1429 | static int __init cfhsi_init_module(void) | 1463 | static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = { |
1464 | .kind = "cfhsi", | ||
1465 | .priv_size = sizeof(struct cfhsi), | ||
1466 | .setup = cfhsi_setup, | ||
1467 | .maxtype = __IFLA_CAIF_HSI_MAX, | ||
1468 | .policy = caif_hsi_policy, | ||
1469 | .newlink = caif_hsi_newlink, | ||
1470 | .changelink = caif_hsi_changelink, | ||
1471 | .get_size = caif_hsi_get_size, | ||
1472 | .fill_info = caif_hsi_fill_info, | ||
1473 | }; | ||
1474 | |||
1475 | static void __exit cfhsi_exit_module(void) | ||
1430 | { | 1476 | { |
1431 | int result; | 1477 | struct list_head *list_node; |
1478 | struct list_head *n; | ||
1479 | struct cfhsi *cfhsi; | ||
1432 | 1480 | ||
1433 | /* Initialize spin lock. */ | 1481 | rtnl_link_unregister(&caif_hsi_link_ops); |
1434 | spin_lock_init(&cfhsi_list_lock); | ||
1435 | 1482 | ||
1436 | /* Register platform driver. */ | 1483 | rtnl_lock(); |
1437 | result = platform_driver_register(&cfhsi_plat_drv); | 1484 | list_for_each_safe(list_node, n, &cfhsi_list) { |
1438 | if (result) { | 1485 | cfhsi = list_entry(list_node, struct cfhsi, list); |
1439 | printk(KERN_ERR "Could not register platform HSI driver: %d.\n", | 1486 | unregister_netdev(cfhsi->ndev); |
1440 | result); | ||
1441 | goto err_dev_register; | ||
1442 | } | 1487 | } |
1488 | rtnl_unlock(); | ||
1489 | } | ||
1443 | 1490 | ||
1444 | err_dev_register: | 1491 | static int __init cfhsi_init_module(void) |
1445 | return result; | 1492 | { |
1493 | return rtnl_link_register(&caif_hsi_link_ops); | ||
1446 | } | 1494 | } |
1447 | 1495 | ||
1448 | module_init(cfhsi_init_module); | 1496 | module_init(cfhsi_init_module); |