Skip to content

Commit 88b7105

Browse files
jmberg-inteldavem330
authored andcommitted
wwan: add interface creation support
Add support to create (and destroy) interfaces via a new rtnetlink kind "wwan". The responsible driver has to use the new wwan_register_ops() to make this possible. Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: Sergey Ryazanov <[email protected]> Signed-off-by: Loic Poulain <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 00e77ed commit 88b7105

File tree

4 files changed

+279
-7
lines changed

4 files changed

+279
-7
lines changed

drivers/net/wwan/wwan_core.c

Lines changed: 238 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <linux/types.h>
1515
#include <linux/termios.h>
1616
#include <linux/wwan.h>
17+
#include <net/rtnetlink.h>
18+
#include <uapi/linux/wwan.h>
1719

1820
/* Maximum number of minors in use */
1921
#define WWAN_MAX_MINORS (1 << MINORBITS)
@@ -35,10 +37,16 @@ static int wwan_major;
3537
*
3638
* @id: WWAN device unique ID.
3739
* @dev: Underlying device.
40+
* @port_id: Current available port ID to pick.
41+
* @ops: wwan device ops
42+
* @ops_ctxt: context to pass to ops
3843
*/
3944
struct wwan_device {
4045
unsigned int id;
4146
struct device dev;
47+
atomic_t port_id;
48+
const struct wwan_ops *ops;
49+
void *ops_ctxt;
4250
};
4351

4452
/**
@@ -102,7 +110,8 @@ static const struct device_type wwan_dev_type = {
102110

103111
static int wwan_dev_parent_match(struct device *dev, const void *parent)
104112
{
105-
return (dev->type == &wwan_dev_type && dev->parent == parent);
113+
return (dev->type == &wwan_dev_type &&
114+
(dev->parent == parent || dev == parent));
106115
}
107116

108117
static struct wwan_device *wwan_dev_get_by_parent(struct device *parent)
@@ -116,6 +125,23 @@ static struct wwan_device *wwan_dev_get_by_parent(struct device *parent)
116125
return to_wwan_dev(dev);
117126
}
118127

128+
static int wwan_dev_name_match(struct device *dev, const void *name)
129+
{
130+
return dev->type == &wwan_dev_type &&
131+
strcmp(dev_name(dev), name) == 0;
132+
}
133+
134+
static struct wwan_device *wwan_dev_get_by_name(const char *name)
135+
{
136+
struct device *dev;
137+
138+
dev = class_find_device(wwan_class, NULL, name, wwan_dev_name_match);
139+
if (!dev)
140+
return ERR_PTR(-ENODEV);
141+
142+
return to_wwan_dev(dev);
143+
}
144+
119145
/* This function allocates and registers a new WWAN device OR if a WWAN device
120146
* already exist for the given parent, it gets a reference and return it.
121147
* This function is not exported (for now), it is called indirectly via
@@ -180,9 +206,14 @@ static void wwan_remove_dev(struct wwan_device *wwandev)
180206
/* WWAN device is created and registered (get+add) along with its first
181207
* child port, and subsequent port registrations only grab a reference
182208
* (get). The WWAN device must then be unregistered (del+put) along with
183-
* its latest port, and reference simply dropped (put) otherwise.
209+
* its last port, and reference simply dropped (put) otherwise. In the
210+
* same fashion, we must not unregister it when the ops are still there.
184211
*/
185-
ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
212+
if (wwandev->ops)
213+
ret = 1;
214+
else
215+
ret = device_for_each_child(&wwandev->dev, NULL, is_wwan_child);
216+
186217
if (!ret)
187218
device_unregister(&wwandev->dev);
188219
else
@@ -750,26 +781,226 @@ static const struct file_operations wwan_port_fops = {
750781
.llseek = noop_llseek,
751782
};
752783

784+
/**
785+
* wwan_register_ops - register WWAN device ops
786+
* @parent: Device to use as parent and shared by all WWAN ports and
787+
* created netdevs
788+
* @ops: operations to register
789+
* @ctxt: context to pass to operations
790+
*
791+
* Returns: 0 on success, a negative error code on failure
792+
*/
793+
int wwan_register_ops(struct device *parent, const struct wwan_ops *ops,
794+
void *ctxt)
795+
{
796+
struct wwan_device *wwandev;
797+
798+
if (WARN_ON(!parent || !ops))
799+
return -EINVAL;
800+
801+
wwandev = wwan_create_dev(parent);
802+
if (!wwandev)
803+
return -ENOMEM;
804+
805+
if (WARN_ON(wwandev->ops)) {
806+
wwan_remove_dev(wwandev);
807+
return -EBUSY;
808+
}
809+
810+
if (!try_module_get(ops->owner)) {
811+
wwan_remove_dev(wwandev);
812+
return -ENODEV;
813+
}
814+
815+
wwandev->ops = ops;
816+
wwandev->ops_ctxt = ctxt;
817+
818+
return 0;
819+
}
820+
EXPORT_SYMBOL_GPL(wwan_register_ops);
821+
822+
/**
823+
* wwan_unregister_ops - remove WWAN device ops
824+
* @parent: Device to use as parent and shared by all WWAN ports and
825+
* created netdevs
826+
*/
827+
void wwan_unregister_ops(struct device *parent)
828+
{
829+
struct wwan_device *wwandev = wwan_dev_get_by_parent(parent);
830+
bool has_ops;
831+
832+
if (WARN_ON(IS_ERR(wwandev)))
833+
return;
834+
835+
has_ops = wwandev->ops;
836+
837+
/* put the reference obtained by wwan_dev_get_by_parent(),
838+
* we should still have one (that the owner is giving back
839+
* now) due to the ops being assigned, check that below
840+
* and return if not.
841+
*/
842+
put_device(&wwandev->dev);
843+
844+
if (WARN_ON(!has_ops))
845+
return;
846+
847+
module_put(wwandev->ops->owner);
848+
849+
wwandev->ops = NULL;
850+
wwandev->ops_ctxt = NULL;
851+
wwan_remove_dev(wwandev);
852+
}
853+
EXPORT_SYMBOL_GPL(wwan_unregister_ops);
854+
855+
static int wwan_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
856+
struct netlink_ext_ack *extack)
857+
{
858+
if (!data)
859+
return -EINVAL;
860+
861+
if (!tb[IFLA_PARENT_DEV_NAME])
862+
return -EINVAL;
863+
864+
if (!data[IFLA_WWAN_LINK_ID])
865+
return -EINVAL;
866+
867+
return 0;
868+
}
869+
870+
static struct device_type wwan_type = { .name = "wwan" };
871+
872+
static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[],
873+
const char *ifname,
874+
unsigned char name_assign_type,
875+
unsigned int num_tx_queues,
876+
unsigned int num_rx_queues)
877+
{
878+
const char *devname = nla_data(tb[IFLA_PARENT_DEV_NAME]);
879+
struct wwan_device *wwandev = wwan_dev_get_by_name(devname);
880+
struct net_device *dev;
881+
882+
if (IS_ERR(wwandev))
883+
return ERR_CAST(wwandev);
884+
885+
/* only supported if ops were registered (not just ports) */
886+
if (!wwandev->ops) {
887+
dev = ERR_PTR(-EOPNOTSUPP);
888+
goto out;
889+
}
890+
891+
dev = alloc_netdev_mqs(wwandev->ops->priv_size, ifname, name_assign_type,
892+
wwandev->ops->setup, num_tx_queues, num_rx_queues);
893+
894+
if (dev) {
895+
SET_NETDEV_DEV(dev, &wwandev->dev);
896+
SET_NETDEV_DEVTYPE(dev, &wwan_type);
897+
}
898+
899+
out:
900+
/* release the reference */
901+
put_device(&wwandev->dev);
902+
return dev;
903+
}
904+
905+
static int wwan_rtnl_newlink(struct net *src_net, struct net_device *dev,
906+
struct nlattr *tb[], struct nlattr *data[],
907+
struct netlink_ext_ack *extack)
908+
{
909+
struct wwan_device *wwandev = wwan_dev_get_by_parent(dev->dev.parent);
910+
u32 link_id = nla_get_u32(data[IFLA_WWAN_LINK_ID]);
911+
int ret;
912+
913+
if (IS_ERR(wwandev))
914+
return PTR_ERR(wwandev);
915+
916+
/* shouldn't have a netdev (left) with us as parent so WARN */
917+
if (WARN_ON(!wwandev->ops)) {
918+
ret = -EOPNOTSUPP;
919+
goto out;
920+
}
921+
922+
if (wwandev->ops->newlink)
923+
ret = wwandev->ops->newlink(wwandev->ops_ctxt, dev,
924+
link_id, extack);
925+
else
926+
ret = register_netdevice(dev);
927+
928+
out:
929+
/* release the reference */
930+
put_device(&wwandev->dev);
931+
return ret;
932+
}
933+
934+
static void wwan_rtnl_dellink(struct net_device *dev, struct list_head *head)
935+
{
936+
struct wwan_device *wwandev = wwan_dev_get_by_parent(dev->dev.parent);
937+
938+
if (IS_ERR(wwandev))
939+
return;
940+
941+
/* shouldn't have a netdev (left) with us as parent so WARN */
942+
if (WARN_ON(!wwandev->ops))
943+
goto out;
944+
945+
if (wwandev->ops->dellink)
946+
wwandev->ops->dellink(wwandev->ops_ctxt, dev, head);
947+
else
948+
unregister_netdevice(dev);
949+
950+
out:
951+
/* release the reference */
952+
put_device(&wwandev->dev);
953+
}
954+
955+
static const struct nla_policy wwan_rtnl_policy[IFLA_WWAN_MAX + 1] = {
956+
[IFLA_WWAN_LINK_ID] = { .type = NLA_U32 },
957+
};
958+
959+
static struct rtnl_link_ops wwan_rtnl_link_ops __read_mostly = {
960+
.kind = "wwan",
961+
.maxtype = __IFLA_WWAN_MAX,
962+
.alloc = wwan_rtnl_alloc,
963+
.validate = wwan_rtnl_validate,
964+
.newlink = wwan_rtnl_newlink,
965+
.dellink = wwan_rtnl_dellink,
966+
.policy = wwan_rtnl_policy,
967+
};
968+
753969
static int __init wwan_init(void)
754970
{
971+
int err;
972+
973+
err = rtnl_link_register(&wwan_rtnl_link_ops);
974+
if (err)
975+
return err;
976+
755977
wwan_class = class_create(THIS_MODULE, "wwan");
756-
if (IS_ERR(wwan_class))
757-
return PTR_ERR(wwan_class);
978+
if (IS_ERR(wwan_class)) {
979+
err = PTR_ERR(wwan_class);
980+
goto unregister;
981+
}
758982

759983
/* chrdev used for wwan ports */
760984
wwan_major = __register_chrdev(0, 0, WWAN_MAX_MINORS, "wwan_port",
761985
&wwan_port_fops);
762986
if (wwan_major < 0) {
763-
class_destroy(wwan_class);
764-
return wwan_major;
987+
err = wwan_major;
988+
goto destroy;
765989
}
766990

767991
return 0;
992+
993+
destroy:
994+
class_destroy(wwan_class);
995+
unregister:
996+
rtnl_link_unregister(&wwan_rtnl_link_ops);
997+
return err;
768998
}
769999

7701000
static void __exit wwan_exit(void)
7711001
{
7721002
__unregister_chrdev(wwan_major, 0, WWAN_MAX_MINORS, "wwan_port");
1003+
rtnl_link_unregister(&wwan_rtnl_link_ops);
7731004
class_destroy(wwan_class);
7741005
}
7751006

include/linux/wwan.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <linux/device.h>
88
#include <linux/kernel.h>
99
#include <linux/skbuff.h>
10+
#include <linux/netlink.h>
1011

1112
/**
1213
* enum wwan_port_type - WWAN port types
@@ -116,4 +117,27 @@ void wwan_port_txon(struct wwan_port *port);
116117
*/
117118
void *wwan_port_get_drvdata(struct wwan_port *port);
118119

120+
/**
121+
* struct wwan_ops - WWAN device ops
122+
* @owner: module owner of the WWAN ops
123+
* @priv_size: size of private netdev data area
124+
* @setup: set up a new netdev
125+
* @newlink: register the new netdev
126+
* @dellink: remove the given netdev
127+
*/
128+
struct wwan_ops {
129+
struct module *owner;
130+
unsigned int priv_size;
131+
void (*setup)(struct net_device *dev);
132+
int (*newlink)(void *ctxt, struct net_device *dev,
133+
u32 if_id, struct netlink_ext_ack *extack);
134+
void (*dellink)(void *ctxt, struct net_device *dev,
135+
struct list_head *head);
136+
};
137+
138+
int wwan_register_ops(struct device *parent, const struct wwan_ops *ops,
139+
void *ctxt);
140+
141+
void wwan_unregister_ops(struct device *parent);
142+
119143
#endif /* __WWAN_H */

include/uapi/linux/wwan.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
2+
/*
3+
* Copyright (C) 2021 Intel Corporation.
4+
*/
5+
#ifndef _UAPI_WWAN_H_
6+
#define _UAPI_WWAN_H_
7+
8+
enum {
9+
IFLA_WWAN_UNSPEC,
10+
IFLA_WWAN_LINK_ID, /* u32 */
11+
12+
__IFLA_WWAN_MAX
13+
};
14+
#define IFLA_WWAN_MAX (__IFLA_WWAN_MAX - 1)
15+
16+
#endif /* _UAPI_WWAN_H_ */

net/core/rtnetlink.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
18901890
[IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
18911891
[IFLA_PROTO_DOWN_REASON] = { .type = NLA_NESTED },
18921892
[IFLA_NEW_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1),
1893+
[IFLA_PARENT_DEV_NAME] = { .type = NLA_NUL_STRING },
18931894
};
18941895

18951896
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {

0 commit comments

Comments
 (0)