Commit 78c235f9 authored by David S. Miller's avatar David S. Miller

Merge branch 'wwan-link-creation-improvements'

Sergey Ryazanov says:

====================
net: WWAN link creation improvements

This series is intended to make the WWAN network links management easier
for WWAN device drivers.

The series begins with adding support for network links creation to the
WWAN HW simulator to facilitate code testing. Then there are a couple of
changes that prepe the WWAN core code for further modifications. The
following patches (4-6) simplify driver unregistering procedures by
performing the created links cleanup in the WWAN core. 7th patch is to
avoid the odd hold of a driver module. Next patches (8th and 9th) make
it easier for drivers to create a network interface for a default data
channel. Finally, 10th patch adds support for reporting of data link
(aka channel aka context) id to make user aware which network
interface is bound to which WWAN device data channel.

All core changes have been tested with the HW simulator. The MHI and
IOSM drivers were only compile tested as I have no access to this
hardware. So the coresponding patches require ACK from the driver
authors.

Changelog:
  v1 -> v2:
    * rebased on top of latest net-next
    * patch that reworks the creation of mhi_net default netdev was
      dropped; as Loic explained, this network device has different
      purpose depending on a driver mode; Loic has a plan to rework the
      mhi_net driver, so we will defer the default netdev creation
      reworkings
    * add a new patch that creates a default network interface for IOSM
      modems
    * 7th, 8th, 10th patches have a minor updates (see the patches for
      details)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1a77de09 69940924
......@@ -32,7 +32,7 @@ struct mhi_device_info {
static int mhi_ndo_open(struct net_device *ndev)
{
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_net_dev *mhi_netdev = wwan_netdev_drvpriv(ndev);
/* Feed the rx buffer pool */
schedule_delayed_work(&mhi_netdev->rx_refill, 0);
......@@ -47,7 +47,7 @@ static int mhi_ndo_open(struct net_device *ndev)
static int mhi_ndo_stop(struct net_device *ndev)
{
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_net_dev *mhi_netdev = wwan_netdev_drvpriv(ndev);
netif_stop_queue(ndev);
netif_carrier_off(ndev);
......@@ -58,7 +58,7 @@ static int mhi_ndo_stop(struct net_device *ndev)
static netdev_tx_t mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_net_dev *mhi_netdev = wwan_netdev_drvpriv(ndev);
const struct mhi_net_proto *proto = mhi_netdev->proto;
struct mhi_device *mdev = mhi_netdev->mdev;
int err;
......@@ -93,7 +93,7 @@ static netdev_tx_t mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev)
static void mhi_ndo_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats)
{
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_net_dev *mhi_netdev = wwan_netdev_drvpriv(ndev);
unsigned int start;
do {
......@@ -322,7 +322,7 @@ static int mhi_net_newlink(void *ctxt, struct net_device *ndev, u32 if_id,
if (dev_get_drvdata(&mhi_dev->dev))
return -EBUSY;
mhi_netdev = netdev_priv(ndev);
mhi_netdev = wwan_netdev_drvpriv(ndev);
dev_set_drvdata(&mhi_dev->dev, mhi_netdev);
mhi_netdev->ndev = ndev;
......@@ -367,7 +367,7 @@ static int mhi_net_newlink(void *ctxt, struct net_device *ndev, u32 if_id,
static void mhi_net_dellink(void *ctxt, struct net_device *ndev,
struct list_head *head)
{
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_net_dev *mhi_netdev = wwan_netdev_drvpriv(ndev);
struct mhi_device *mhi_dev = ctxt;
if (head)
......@@ -383,7 +383,6 @@ static void mhi_net_dellink(void *ctxt, struct net_device *ndev,
}
static const struct wwan_ops mhi_wwan_ops = {
.owner = THIS_MODULE,
.priv_size = sizeof(struct mhi_net_dev),
.setup = mhi_net_setup,
.newlink = mhi_net_newlink,
......@@ -398,7 +397,8 @@ static int mhi_net_probe(struct mhi_device *mhi_dev,
struct net_device *ndev;
int err;
err = wwan_register_ops(&cntrl->mhi_dev->dev, &mhi_wwan_ops, mhi_dev);
err = wwan_register_ops(&cntrl->mhi_dev->dev, &mhi_wwan_ops, mhi_dev,
WWAN_NO_DEFAULT_LINK);
if (err)
return err;
......@@ -436,7 +436,7 @@ static void mhi_net_remove(struct mhi_device *mhi_dev)
struct mhi_net_dev *mhi_netdev = dev_get_drvdata(&mhi_dev->dev);
struct mhi_controller *cntrl = mhi_dev->mhi_cntrl;
/* rtnetlink takes care of removing remaining links */
/* WWAN core takes care of removing remaining links */
wwan_unregister_ops(&cntrl->mhi_dev->dev);
if (create_default_iface)
......
......@@ -16,6 +16,7 @@
#include <linux/ip.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/wwan.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
......@@ -56,7 +57,7 @@ static void __mbim_errors_inc(struct mhi_net_dev *dev)
static int mbim_rx_verify_nth16(struct sk_buff *skb)
{
struct mhi_net_dev *dev = netdev_priv(skb->dev);
struct mhi_net_dev *dev = wwan_netdev_drvpriv(skb->dev);
struct mbim_context *ctx = dev->proto_data;
struct usb_cdc_ncm_nth16 *nth16;
int len;
......@@ -102,7 +103,7 @@ static int mbim_rx_verify_nth16(struct sk_buff *skb)
static int mbim_rx_verify_ndp16(struct sk_buff *skb, struct usb_cdc_ncm_ndp16 *ndp16)
{
struct mhi_net_dev *dev = netdev_priv(skb->dev);
struct mhi_net_dev *dev = wwan_netdev_drvpriv(skb->dev);
int ret;
if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) {
......
......@@ -30,6 +30,9 @@
#define IP_MUX_SESSION_START 1
#define IP_MUX_SESSION_END 8
/* Default IP MUX channel */
#define IP_MUX_SESSION_DEFAULT 1
/**
* ipc_imem_sys_port_open - Open a port link to CP.
* @ipc_imem: Imem instance.
......
......@@ -20,7 +20,7 @@
#define IOSM_IF_ID_PAYLOAD 2
/**
* struct iosm_netdev_priv - netdev private data
* struct iosm_netdev_priv - netdev WWAN driver specific private data
* @ipc_wwan: Pointer to iosm_wwan struct
* @netdev: Pointer to network interface device structure
* @if_id: Interface id for device.
......@@ -51,7 +51,7 @@ struct iosm_wwan {
/* Bring-up the wwan net link */
static int ipc_wwan_link_open(struct net_device *netdev)
{
struct iosm_netdev_priv *priv = netdev_priv(netdev);
struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev);
struct iosm_wwan *ipc_wwan = priv->ipc_wwan;
int if_id = priv->if_id;
int ret;
......@@ -88,7 +88,7 @@ static int ipc_wwan_link_open(struct net_device *netdev)
/* Bring-down the wwan net link */
static int ipc_wwan_link_stop(struct net_device *netdev)
{
struct iosm_netdev_priv *priv = netdev_priv(netdev);
struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev);
netif_stop_queue(netdev);
......@@ -105,7 +105,7 @@ static int ipc_wwan_link_stop(struct net_device *netdev)
static int ipc_wwan_link_transmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct iosm_netdev_priv *priv = netdev_priv(netdev);
struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(netdev);
struct iosm_wwan *ipc_wwan = priv->ipc_wwan;
int if_id = priv->if_id;
int ret;
......@@ -178,7 +178,7 @@ static int ipc_wwan_newlink(void *ctxt, struct net_device *dev,
if_id >= ARRAY_SIZE(ipc_wwan->sub_netlist))
return -EINVAL;
priv = netdev_priv(dev);
priv = wwan_netdev_drvpriv(dev);
priv->if_id = if_id;
priv->netdev = dev;
priv->ipc_wwan = ipc_wwan;
......@@ -208,8 +208,8 @@ static int ipc_wwan_newlink(void *ctxt, struct net_device *dev,
static void ipc_wwan_dellink(void *ctxt, struct net_device *dev,
struct list_head *head)
{
struct iosm_netdev_priv *priv = wwan_netdev_drvpriv(dev);
struct iosm_wwan *ipc_wwan = ctxt;
struct iosm_netdev_priv *priv = netdev_priv(dev);
int if_id = priv->if_id;
if (WARN_ON(if_id < IP_MUX_SESSION_START ||
......@@ -317,7 +317,9 @@ struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev)
ipc_wwan->dev = dev;
ipc_wwan->ipc_imem = ipc_imem;
if (wwan_register_ops(ipc_wwan->dev, &iosm_wwan_ops, ipc_wwan)) {
/* WWAN core will create a netdev for the default IP MUX channel */
if (wwan_register_ops(ipc_wwan->dev, &iosm_wwan_ops, ipc_wwan,
IP_MUX_SESSION_DEFAULT)) {
kfree(ipc_wwan);
return NULL;
}
......@@ -329,22 +331,9 @@ struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev)
void ipc_wwan_deinit(struct iosm_wwan *ipc_wwan)
{
int if_id;
/* This call will remove all child netdev(s) */
wwan_unregister_ops(ipc_wwan->dev);
for (if_id = 0; if_id < ARRAY_SIZE(ipc_wwan->sub_netlist); if_id++) {
struct iosm_netdev_priv *priv;
priv = rcu_access_pointer(ipc_wwan->sub_netlist[if_id]);
if (!priv)
continue;
rtnl_lock();
ipc_wwan_dellink(ipc_wwan, priv->netdev, NULL);
rtnl_unlock();
}
mutex_destroy(&ipc_wwan->if_mutex);
kfree(ipc_wwan);
......
......@@ -789,77 +789,6 @@ static const struct file_operations wwan_port_fops = {
.llseek = noop_llseek,
};
/**
* wwan_register_ops - register WWAN device ops
* @parent: Device to use as parent and shared by all WWAN ports and
* created netdevs
* @ops: operations to register
* @ctxt: context to pass to operations
*
* Returns: 0 on success, a negative error code on failure
*/
int wwan_register_ops(struct device *parent, const struct wwan_ops *ops,
void *ctxt)
{
struct wwan_device *wwandev;
if (WARN_ON(!parent || !ops))
return -EINVAL;
wwandev = wwan_create_dev(parent);
if (!wwandev)
return -ENOMEM;
if (WARN_ON(wwandev->ops)) {
wwan_remove_dev(wwandev);
return -EBUSY;
}
if (!try_module_get(ops->owner)) {
wwan_remove_dev(wwandev);
return -ENODEV;
}
wwandev->ops = ops;
wwandev->ops_ctxt = ctxt;
return 0;
}
EXPORT_SYMBOL_GPL(wwan_register_ops);
/**
* wwan_unregister_ops - remove WWAN device ops
* @parent: Device to use as parent and shared by all WWAN ports and
* created netdevs
*/
void wwan_unregister_ops(struct device *parent)
{
struct wwan_device *wwandev = wwan_dev_get_by_parent(parent);
bool has_ops;
if (WARN_ON(IS_ERR(wwandev)))
return;
has_ops = wwandev->ops;
/* put the reference obtained by wwan_dev_get_by_parent(),
* we should still have one (that the owner is giving back
* now) due to the ops being assigned, check that below
* and return if not.
*/
put_device(&wwandev->dev);
if (WARN_ON(!has_ops))
return;
module_put(wwandev->ops->owner);
wwandev->ops = NULL;
wwandev->ops_ctxt = NULL;
wwan_remove_dev(wwandev);
}
EXPORT_SYMBOL_GPL(wwan_unregister_ops);
static int wwan_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
......@@ -886,6 +815,7 @@ static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[],
const char *devname = nla_data(tb[IFLA_PARENT_DEV_NAME]);
struct wwan_device *wwandev = wwan_dev_get_by_name(devname);
struct net_device *dev;
unsigned int priv_size;
if (IS_ERR(wwandev))
return ERR_CAST(wwandev);
......@@ -896,7 +826,8 @@ static struct net_device *wwan_rtnl_alloc(struct nlattr *tb[],
goto out;
}
dev = alloc_netdev_mqs(wwandev->ops->priv_size, ifname, name_assign_type,
priv_size = sizeof(struct wwan_netdev_priv) + wwandev->ops->priv_size;
dev = alloc_netdev_mqs(priv_size, ifname, name_assign_type,
wwandev->ops->setup, num_tx_queues, num_rx_queues);
if (dev) {
......@@ -916,6 +847,7 @@ static int wwan_rtnl_newlink(struct net *src_net, struct net_device *dev,
{
struct wwan_device *wwandev = wwan_dev_get_by_parent(dev->dev.parent);
u32 link_id = nla_get_u32(data[IFLA_WWAN_LINK_ID]);
struct wwan_netdev_priv *priv = netdev_priv(dev);
int ret;
if (IS_ERR(wwandev))
......@@ -927,6 +859,7 @@ static int wwan_rtnl_newlink(struct net *src_net, struct net_device *dev,
goto out;
}
priv->link_id = link_id;
if (wwandev->ops->newlink)
ret = wwandev->ops->newlink(wwandev->ops_ctxt, dev,
link_id, extack);
......@@ -953,13 +886,34 @@ static void wwan_rtnl_dellink(struct net_device *dev, struct list_head *head)
if (wwandev->ops->dellink)
wwandev->ops->dellink(wwandev->ops_ctxt, dev, head);
else
unregister_netdevice(dev);
unregister_netdevice_queue(dev, head);
out:
/* release the reference */
put_device(&wwandev->dev);
}
static size_t wwan_rtnl_get_size(const struct net_device *dev)
{
return
nla_total_size(4) + /* IFLA_WWAN_LINK_ID */
0;
}
static int wwan_rtnl_fill_info(struct sk_buff *skb,
const struct net_device *dev)
{
struct wwan_netdev_priv *priv = netdev_priv(dev);
if (nla_put_u32(skb, IFLA_WWAN_LINK_ID, priv->link_id))
goto nla_put_failure;
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static const struct nla_policy wwan_rtnl_policy[IFLA_WWAN_MAX + 1] = {
[IFLA_WWAN_LINK_ID] = { .type = NLA_U32 },
};
......@@ -971,9 +925,167 @@ static struct rtnl_link_ops wwan_rtnl_link_ops __read_mostly = {
.validate = wwan_rtnl_validate,
.newlink = wwan_rtnl_newlink,
.dellink = wwan_rtnl_dellink,
.get_size = wwan_rtnl_get_size,
.fill_info = wwan_rtnl_fill_info,
.policy = wwan_rtnl_policy,
};
static void wwan_create_default_link(struct wwan_device *wwandev,
u32 def_link_id)
{
struct nlattr *tb[IFLA_MAX + 1], *linkinfo[IFLA_INFO_MAX + 1];
struct nlattr *data[IFLA_WWAN_MAX + 1];
struct net_device *dev;
struct nlmsghdr *nlh;
struct sk_buff *msg;
/* Forge attributes required to create a WWAN netdev. We first
* build a netlink message and then parse it. This looks
* odd, but such approach is less error prone.
*/
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (WARN_ON(!msg))
return;
nlh = nlmsg_put(msg, 0, 0, RTM_NEWLINK, 0, 0);
if (WARN_ON(!nlh))
goto free_attrs;
if (nla_put_string(msg, IFLA_PARENT_DEV_NAME, dev_name(&wwandev->dev)))
goto free_attrs;
tb[IFLA_LINKINFO] = nla_nest_start(msg, IFLA_LINKINFO);
if (!tb[IFLA_LINKINFO])
goto free_attrs;
linkinfo[IFLA_INFO_DATA] = nla_nest_start(msg, IFLA_INFO_DATA);
if (!linkinfo[IFLA_INFO_DATA])
goto free_attrs;
if (nla_put_u32(msg, IFLA_WWAN_LINK_ID, def_link_id))
goto free_attrs;
nla_nest_end(msg, linkinfo[IFLA_INFO_DATA]);
nla_nest_end(msg, tb[IFLA_LINKINFO]);
nlmsg_end(msg, nlh);
/* The next three parsing calls can not fail */
nlmsg_parse_deprecated(nlh, 0, tb, IFLA_MAX, NULL, NULL);
nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO],
NULL, NULL);
nla_parse_nested_deprecated(data, IFLA_WWAN_MAX,
linkinfo[IFLA_INFO_DATA], NULL, NULL);
rtnl_lock();
dev = rtnl_create_link(&init_net, "wwan%d", NET_NAME_ENUM,
&wwan_rtnl_link_ops, tb, NULL);
if (WARN_ON(IS_ERR(dev)))
goto unlock;
if (WARN_ON(wwan_rtnl_newlink(&init_net, dev, tb, data, NULL))) {
free_netdev(dev);
goto unlock;
}
unlock:
rtnl_unlock();
free_attrs:
nlmsg_free(msg);
}
/**
* wwan_register_ops - register WWAN device ops
* @parent: Device to use as parent and shared by all WWAN ports and
* created netdevs
* @ops: operations to register
* @ctxt: context to pass to operations
* @def_link_id: id of the default link that will be automatically created by
* the WWAN core for the WWAN device. The default link will not be created
* if the passed value is WWAN_NO_DEFAULT_LINK.
*
* Returns: 0 on success, a negative error code on failure
*/
int wwan_register_ops(struct device *parent, const struct wwan_ops *ops,
void *ctxt, u32 def_link_id)
{
struct wwan_device *wwandev;
if (WARN_ON(!parent || !ops || !ops->setup))
return -EINVAL;
wwandev = wwan_create_dev(parent);
if (!wwandev)
return -ENOMEM;
if (WARN_ON(wwandev->ops)) {
wwan_remove_dev(wwandev);
return -EBUSY;
}
wwandev->ops = ops;
wwandev->ops_ctxt = ctxt;
/* NB: we do not abort ops registration in case of default link
* creation failure. Link ops is the management interface, while the
* default link creation is a service option. And we should not prevent
* a user from manually creating a link latter if service option failed
* now.
*/
if (def_link_id != WWAN_NO_DEFAULT_LINK)
wwan_create_default_link(wwandev, def_link_id);
return 0;
}
EXPORT_SYMBOL_GPL(wwan_register_ops);
/* Enqueue child netdev deletion */
static int wwan_child_dellink(struct device *dev, void *data)
{
struct list_head *kill_list = data;
if (dev->type == &wwan_type)
wwan_rtnl_dellink(to_net_dev(dev), kill_list);
return 0;
}
/**
* wwan_unregister_ops - remove WWAN device ops
* @parent: Device to use as parent and shared by all WWAN ports and
* created netdevs
*/
void wwan_unregister_ops(struct device *parent)
{
struct wwan_device *wwandev = wwan_dev_get_by_parent(parent);
LIST_HEAD(kill_list);
if (WARN_ON(IS_ERR(wwandev)))
return;
if (WARN_ON(!wwandev->ops)) {
put_device(&wwandev->dev);
return;
}
/* put the reference obtained by wwan_dev_get_by_parent(),
* we should still have one (that the owner is giving back
* now) due to the ops being assigned.
*/
put_device(&wwandev->dev);
rtnl_lock(); /* Prevent concurent netdev(s) creation/destroying */
/* Remove all child netdev(s), using batch removing */
device_for_each_child(&wwandev->dev, &kill_list,
wwan_child_dellink);
unregister_netdevice_many(&kill_list);
wwandev->ops = NULL; /* Finally remove ops */
rtnl_unlock();
wwandev->ops_ctxt = NULL;
wwan_remove_dev(wwandev);
}
EXPORT_SYMBOL_GPL(wwan_unregister_ops);
static int __init wwan_init(void)
{
int err;
......
......@@ -14,10 +14,13 @@
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/wwan.h>
#include <linux/debugfs.h>
#include <linux/workqueue.h>
#include <net/arp.h>
static int wwan_hwsim_devsnum = 2;
module_param_named(devices, wwan_hwsim_devsnum, int, 0444);
MODULE_PARM_DESC(devices, "Number of simulated devices");
......@@ -64,6 +67,37 @@ static const struct file_operations wwan_hwsim_debugfs_devdestroy_fops;
static void wwan_hwsim_port_del_work(struct work_struct *work);
static void wwan_hwsim_dev_del_work(struct work_struct *work);
static netdev_tx_t wwan_hwsim_netdev_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
consume_skb(skb);
return NETDEV_TX_OK;
}
static const struct net_device_ops wwan_hwsim_netdev_ops = {
.ndo_start_xmit = wwan_hwsim_netdev_xmit,
};
static void wwan_hwsim_netdev_setup(struct net_device *ndev)
{
ndev->netdev_ops = &wwan_hwsim_netdev_ops;
ndev->needs_free_netdev = true;
ndev->mtu = ETH_DATA_LEN;
ndev->min_mtu = ETH_MIN_MTU;
ndev->max_mtu = ETH_MAX_MTU;
ndev->type = ARPHRD_NONE;
ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
}
static const struct wwan_ops wwan_hwsim_wwan_rtnl_ops = {
.priv_size = 0, /* No private data */
.setup = wwan_hwsim_netdev_setup,
};
static int wwan_hwsim_port_start(struct wwan_port *wport)
{
struct wwan_hwsim_port *port = wwan_port_get_drvdata(wport);
......@@ -254,6 +288,10 @@ static struct wwan_hwsim_dev *wwan_hwsim_dev_new(void)
INIT_WORK(&dev->del_work, wwan_hwsim_dev_del_work);
err = wwan_register_ops(&dev->dev, &wwan_hwsim_wwan_rtnl_ops, dev, 1);
if (err)
goto err_unreg_dev;
dev->debugfs_topdir = debugfs_create_dir(dev_name(&dev->dev),
wwan_hwsim_debugfs_topdir);
debugfs_create_file("destroy", 0200, dev->debugfs_topdir, dev,
......@@ -265,6 +303,12 @@ static struct wwan_hwsim_dev *wwan_hwsim_dev_new(void)
return dev;
err_unreg_dev:
device_unregister(&dev->dev);
/* Memory will be freed in the device release callback */
return ERR_PTR(err);
err_free_dev:
kfree(dev);
......@@ -290,6 +334,9 @@ static void wwan_hwsim_dev_del(struct wwan_hwsim_dev *dev)
debugfs_remove(dev->debugfs_topdir);
/* This will remove all child netdev(s) */
wwan_unregister_ops(&dev->dev);
/* Make sure that there is no pending deletion work */
if (current_work() != &dev->del_work)
cancel_work_sync(&dev->del_work);
......
......@@ -9,6 +9,7 @@
#include <linux/poll.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netdevice.h>
/**
* enum wwan_port_type - WWAN port types
......@@ -126,16 +127,37 @@ void wwan_port_txon(struct wwan_port *port);
*/
void *wwan_port_get_drvdata(struct wwan_port *port);
/**
* struct wwan_netdev_priv - WWAN core network device private data
* @link_id: WWAN device data link id
* @drv_priv: driver private data area, size is determined in &wwan_ops
*/
struct wwan_netdev_priv {
u32 link_id;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
static inline void *wwan_netdev_drvpriv(struct net_device *dev)
{
return ((struct wwan_netdev_priv *)netdev_priv(dev))->drv_priv;
}
/*
* Used to indicate that the WWAN core should not create a default network
* link.
*/
#define WWAN_NO_DEFAULT_LINK U32_MAX
/**
* struct wwan_ops - WWAN device ops
* @owner: module owner of the WWAN ops
* @priv_size: size of private netdev data area
* @setup: set up a new netdev
* @newlink: register the new netdev
* @dellink: remove the given netdev
*/
struct wwan_ops {
struct module *owner;
unsigned int priv_size;
void (*setup)(struct net_device *dev);
int (*newlink)(void *ctxt, struct net_device *dev,
......@@ -145,7 +167,7 @@ struct wwan_ops {
};
int wwan_register_ops(struct device *parent, const struct wwan_ops *ops,
void *ctxt);
void *ctxt, u32 def_link_id);
void wwan_unregister_ops(struct device *parent);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment