mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-04 04:02:26 +00:00
net: mhi_net: Register wwan_ops for link creation
Register wwan_ops for link management via wwan rtnetlink. This is only basic support for now, since we only support creating one single link (link-0), but is useful to validate new wwan rtnetlink interface. For backward compatibity support, we still register a default netdev at probe time, except if 'create_default_iface' module parameter is set to false. This has been tested with iproute2 and mbimcli: $ ip link add dev wwan0-0 parentdev-name wwan0 type wwan linkid 0 $ mbimcli -p -d /dev/wwan0p2MBIM --connect apn=free $ ip link set dev wwan0-0 up $ ip addr add dev wwan0 ${IP} $ ip route replace default via ${IP} $ ping 8.8.8.8 ... Signed-off-by: Loic Poulain <loic.poulain@linaro.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
88b710532e
commit
13adac0329
@ -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.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/u64_stats_sync.h>
|
||||
#include <linux/wwan.h>
|
||||
|
||||
#include "mhi.h"
|
||||
|
||||
@ -18,6 +19,12 @@
|
||||
#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;
|
||||
@ -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);
|
||||
@ -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;
|
||||
|
||||
@ -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_dellink(void *ctxt, struct net_device *ndev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct mhi_net_dev *mhi_netdev = netdev_priv(ndev);
|
||||
struct mhi_device *mhi_dev = ctxt;
|
||||
|
||||
if (head)
|
||||
unregister_netdevice_queue(ndev, head);
|
||||
else
|
||||
unregister_netdev(ndev);
|
||||
|
||||
mhi_unprepare_from_transfer(mhi_dev);
|
||||
|
||||
kfree_skb(mhi_netdev->skbagg_head);
|
||||
|
||||
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;
|
||||
|
||||
unregister_netdev(mhi_netdev->ndev);
|
||||
/* rtnetlink takes care of removing remaining links */
|
||||
wwan_unregister_ops(&cntrl->mhi_dev->dev);
|
||||
|
||||
mhi_unprepare_from_transfer(mhi_netdev->mdev);
|
||||
|
||||
kfree_skb(mhi_netdev->skbagg_head);
|
||||
|
||||
free_netdev(mhi_netdev->ndev);
|
||||
if (create_default_iface)
|
||||
mhi_net_dellink(mhi_dev, mhi_netdev->ndev, NULL);
|
||||
}
|
||||
|
||||
static const struct mhi_device_info mhi_hwip0 = {
|
||||
|
Loading…
Reference in New Issue
Block a user