1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * This file contains all networking devres helpers.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/device.h>
7*4882a593Smuzhiyun #include <linux/etherdevice.h>
8*4882a593Smuzhiyun #include <linux/netdevice.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun struct net_device_devres {
11*4882a593Smuzhiyun struct net_device *ndev;
12*4882a593Smuzhiyun };
13*4882a593Smuzhiyun
devm_free_netdev(struct device * dev,void * this)14*4882a593Smuzhiyun static void devm_free_netdev(struct device *dev, void *this)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun struct net_device_devres *res = this;
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun free_netdev(res->ndev);
19*4882a593Smuzhiyun }
20*4882a593Smuzhiyun
devm_alloc_etherdev_mqs(struct device * dev,int sizeof_priv,unsigned int txqs,unsigned int rxqs)21*4882a593Smuzhiyun struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
22*4882a593Smuzhiyun unsigned int txqs, unsigned int rxqs)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun struct net_device_devres *dr;
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
27*4882a593Smuzhiyun if (!dr)
28*4882a593Smuzhiyun return NULL;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun dr->ndev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
31*4882a593Smuzhiyun if (!dr->ndev) {
32*4882a593Smuzhiyun devres_free(dr);
33*4882a593Smuzhiyun return NULL;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun devres_add(dev, dr);
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun return dr->ndev;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
41*4882a593Smuzhiyun
devm_unregister_netdev(struct device * dev,void * this)42*4882a593Smuzhiyun static void devm_unregister_netdev(struct device *dev, void *this)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct net_device_devres *res = this;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun unregister_netdev(res->ndev);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
netdev_devres_match(struct device * dev,void * this,void * match_data)49*4882a593Smuzhiyun static int netdev_devres_match(struct device *dev, void *this, void *match_data)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct net_device_devres *res = this;
52*4882a593Smuzhiyun struct net_device *ndev = match_data;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun return ndev == res->ndev;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /**
58*4882a593Smuzhiyun * devm_register_netdev - resource managed variant of register_netdev()
59*4882a593Smuzhiyun * @dev: managing device for this netdev - usually the parent device
60*4882a593Smuzhiyun * @ndev: device to register
61*4882a593Smuzhiyun *
62*4882a593Smuzhiyun * This is a devres variant of register_netdev() for which the unregister
63*4882a593Smuzhiyun * function will be call automatically when the managing device is
64*4882a593Smuzhiyun * detached. Note: the net_device used must also be resource managed by
65*4882a593Smuzhiyun * the same struct device.
66*4882a593Smuzhiyun */
devm_register_netdev(struct device * dev,struct net_device * ndev)67*4882a593Smuzhiyun int devm_register_netdev(struct device *dev, struct net_device *ndev)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct net_device_devres *dr;
70*4882a593Smuzhiyun int ret;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /* struct net_device must itself be managed. For now a managed netdev
73*4882a593Smuzhiyun * can only be allocated by devm_alloc_etherdev_mqs() so the check is
74*4882a593Smuzhiyun * straightforward.
75*4882a593Smuzhiyun */
76*4882a593Smuzhiyun if (WARN_ON(!devres_find(dev, devm_free_netdev,
77*4882a593Smuzhiyun netdev_devres_match, ndev)))
78*4882a593Smuzhiyun return -EINVAL;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun dr = devres_alloc(devm_unregister_netdev, sizeof(*dr), GFP_KERNEL);
81*4882a593Smuzhiyun if (!dr)
82*4882a593Smuzhiyun return -ENOMEM;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun ret = register_netdev(ndev);
85*4882a593Smuzhiyun if (ret) {
86*4882a593Smuzhiyun devres_free(dr);
87*4882a593Smuzhiyun return ret;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun dr->ndev = ndev;
91*4882a593Smuzhiyun devres_add(ndev->dev.parent, dr);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun return 0;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun EXPORT_SYMBOL(devm_register_netdev);
96