Skip to content

Commit

Permalink
Merge branch 'wwan-link-creation'
Browse files Browse the repository at this point in the history
Loic Poulain says:

====================
net: Add WWAN link creation support

Most of the modern WWAN modems are able to support multiple network
contexts, allowing user to connect to different APNs (e.g. Internet,
MMS, etc...). These contexts are usually dynamically configured via
a control channel such as MBIM, QMI or AT.

Each context is naturally represented as a network link/device, and
the muxing of these links is usually vendor/bus specific (QMAP, MBIM,
intel iosm...). Today some drivers create a static collection of
netdevs at init time, some relies on VLAN link for associating a context
(cdc-mbim), some exposes sysfs attribute for dynamically creating
additional netdev (qmi_wwan add_mux attr) or relies on vendor specific
link type (rmnet) for performing the muxing... so there is no generic
way to handle WWAN links, making user side integration painful.

This series introduces a generic WWAN link management interface to the
WWAN framework, allowing user to dynamically create and remove WWAN
links through rtnetlink ('wwan' type). The underlying 'muxing' vendor
implementation is completely abstracted.

The idea is to use this interface for upcoming WWAN drivers (intel
iosm) and to progressively integrate support into existing ones
(qmi_wwan, cdc-mbim, mhi_net, etc...).

v2: - Squashed Johannes and Sergey changes
    - Added IFLA_PARENT_DEV_BUS_NAME attribute
    - reworded commit message + introduce Sergey's comment

v3: - Added basic new interface user to this series (mhi_net)
    - Moved IFLA_PARENT_DEV_NAME nla_policy introduction to right patch
    - Added cover letter
    - moved kdoc to .c file
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 12, 2021
2 parents 73214a6 + 13adac0 commit 73a3786
Show file tree
Hide file tree
Showing 8 changed files with 419 additions and 35 deletions.
1 change: 1 addition & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ config VSOCKMON
config MHI_NET
tristate "MHI network driver"
depends on MHI_BUS
select WWAN_CORE
help
This is the network driver for MHI bus. It can be used with
QCOM based WWAN modems (like SDX55). Say Y or M.
Expand Down
123 changes: 100 additions & 23 deletions drivers/net/mhi/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/u64_stats_sync.h>
#include <linux/wwan.h>

#include "mhi.h"

#define MHI_NET_MIN_MTU ETH_MIN_MTU
#define MHI_NET_MAX_MTU 0xffff
#define MHI_NET_DEFAULT_MTU 0x4000

/* When set to false, the default netdev (link 0) is not created, and it's up
* to user to create the link (via wwan rtnetlink).
*/
static bool create_default_iface = true;
module_param(create_default_iface, bool, 0);

struct mhi_device_info {
const char *netname;
const struct mhi_net_proto *proto;
Expand Down Expand Up @@ -295,32 +302,33 @@ static void mhi_net_rx_refill_work(struct work_struct *work)
schedule_delayed_work(&mhi_netdev->rx_refill, HZ / 2);
}

static struct device_type wwan_type = {
.name = "wwan",
};

static int mhi_net_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
static int mhi_net_newlink(void *ctxt, struct net_device *ndev, u32 if_id,
struct netlink_ext_ack *extack)
{
const struct mhi_device_info *info = (struct mhi_device_info *)id->driver_data;
struct device *dev = &mhi_dev->dev;
const struct mhi_device_info *info;
struct mhi_device *mhi_dev = ctxt;
struct mhi_net_dev *mhi_netdev;
struct net_device *ndev;
int err;

ndev = alloc_netdev(sizeof(*mhi_netdev), info->netname,
NET_NAME_PREDICTABLE, mhi_net_setup);
if (!ndev)
return -ENOMEM;
info = (struct mhi_device_info *)mhi_dev->id->driver_data;

/* For now we only support one link (link context 0), driver must be
* reworked to break 1:1 relationship for net MBIM and to forward setup
* call to rmnet(QMAP) otherwise.
*/
if (if_id != 0)
return -EINVAL;

if (dev_get_drvdata(&mhi_dev->dev))
return -EBUSY;

mhi_netdev = netdev_priv(ndev);
dev_set_drvdata(dev, mhi_netdev);

dev_set_drvdata(&mhi_dev->dev, mhi_netdev);
mhi_netdev->ndev = ndev;
mhi_netdev->mdev = mhi_dev;
mhi_netdev->skbagg_head = NULL;
mhi_netdev->proto = info->proto;
SET_NETDEV_DEV(ndev, &mhi_dev->dev);
SET_NETDEV_DEVTYPE(ndev, &wwan_type);

INIT_DELAYED_WORK(&mhi_netdev->rx_refill, mhi_net_rx_refill_work);
u64_stats_init(&mhi_netdev->stats.rx_syncp);
Expand All @@ -334,7 +342,10 @@ static int mhi_net_probe(struct mhi_device *mhi_dev,
/* Number of transfer descriptors determines size of the queue */
mhi_netdev->rx_queue_sz = mhi_get_free_desc_count(mhi_dev, DMA_FROM_DEVICE);

err = register_netdev(ndev);
if (extack)
err = register_netdevice(ndev);
else
err = register_netdev(ndev);
if (err)
goto out_err;

Expand All @@ -347,23 +358,89 @@ static int mhi_net_probe(struct mhi_device *mhi_dev,
return 0;

out_err_proto:
unregister_netdev(ndev);
unregister_netdevice(ndev);
out_err:
free_netdev(ndev);
return err;
}

static void mhi_net_remove(struct mhi_device *mhi_dev)
static void mhi_net_dellink(void *ctxt, struct net_device *ndev,
struct list_head *head)
{
struct mhi_net_dev *mhi_netdev = dev_get_drvdata(&mhi_dev->dev);
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
struct mhi_device *mhi_dev = ctxt;

unregister_netdev(mhi_netdev->ndev);
if (head)
unregister_netdevice_queue(ndev, head);
else
unregister_netdev(ndev);

mhi_unprepare_from_transfer(mhi_netdev->mdev);
mhi_unprepare_from_transfer(mhi_dev);

kfree_skb(mhi_netdev->skbagg_head);

free_netdev(mhi_netdev->ndev);
dev_set_drvdata(&mhi_dev->dev, NULL);
}

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,
.dellink = mhi_net_dellink,
};

static int mhi_net_probe(struct mhi_device *mhi_dev,
const struct mhi_device_id *id)
{
const struct mhi_device_info *info = (struct mhi_device_info *)id->driver_data;
struct mhi_controller *cntrl = mhi_dev->mhi_cntrl;
struct net_device *ndev;
int err;

err = wwan_register_ops(&cntrl->mhi_dev->dev, &mhi_wwan_ops, mhi_dev);
if (err)
return err;

if (!create_default_iface)
return 0;

/* Create a default interface which is used as either RMNET real-dev,
* MBIM link 0 or ip link 0)
*/
ndev = alloc_netdev(sizeof(struct mhi_net_dev), info->netname,
NET_NAME_PREDICTABLE, mhi_net_setup);
if (!ndev) {
err = -ENOMEM;
goto err_unregister;
}

SET_NETDEV_DEV(ndev, &mhi_dev->dev);

err = mhi_net_newlink(mhi_dev, ndev, 0, NULL);
if (err)
goto err_release;

return 0;

err_release:
free_netdev(ndev);
err_unregister:
wwan_unregister_ops(&cntrl->mhi_dev->dev);

return err;
}

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_unregister_ops(&cntrl->mhi_dev->dev);

if (create_default_iface)
mhi_net_dellink(mhi_dev, mhi_netdev->ndev, NULL);
}

static const struct mhi_device_info mhi_hwip0 = {
Expand Down
Loading

0 comments on commit 73a3786

Please sign in to comment.