162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains all networking devres helpers. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct net_device_devres { 1162306a36Sopenharmony_ci struct net_device *ndev; 1262306a36Sopenharmony_ci}; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic void devm_free_netdev(struct device *dev, void *this) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct net_device_devres *res = this; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci free_netdev(res->ndev); 1962306a36Sopenharmony_ci} 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv, 2262306a36Sopenharmony_ci unsigned int txqs, unsigned int rxqs) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct net_device_devres *dr; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL); 2762306a36Sopenharmony_ci if (!dr) 2862306a36Sopenharmony_ci return NULL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci dr->ndev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs); 3162306a36Sopenharmony_ci if (!dr->ndev) { 3262306a36Sopenharmony_ci devres_free(dr); 3362306a36Sopenharmony_ci return NULL; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci devres_add(dev, dr); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return dr->ndev; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL(devm_alloc_etherdev_mqs); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void devm_unregister_netdev(struct device *dev, void *this) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct net_device_devres *res = this; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci unregister_netdev(res->ndev); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int netdev_devres_match(struct device *dev, void *this, void *match_data) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct net_device_devres *res = this; 5262306a36Sopenharmony_ci struct net_device *ndev = match_data; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return ndev == res->ndev; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/** 5862306a36Sopenharmony_ci * devm_register_netdev - resource managed variant of register_netdev() 5962306a36Sopenharmony_ci * @dev: managing device for this netdev - usually the parent device 6062306a36Sopenharmony_ci * @ndev: device to register 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * This is a devres variant of register_netdev() for which the unregister 6362306a36Sopenharmony_ci * function will be called automatically when the managing device is 6462306a36Sopenharmony_ci * detached. Note: the net_device used must also be resource managed by 6562306a36Sopenharmony_ci * the same struct device. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ciint devm_register_netdev(struct device *dev, struct net_device *ndev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct net_device_devres *dr; 7062306a36Sopenharmony_ci int ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* struct net_device must itself be managed. For now a managed netdev 7362306a36Sopenharmony_ci * can only be allocated by devm_alloc_etherdev_mqs() so the check is 7462306a36Sopenharmony_ci * straightforward. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci if (WARN_ON(!devres_find(dev, devm_free_netdev, 7762306a36Sopenharmony_ci netdev_devres_match, ndev))) 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci dr = devres_alloc(devm_unregister_netdev, sizeof(*dr), GFP_KERNEL); 8162306a36Sopenharmony_ci if (!dr) 8262306a36Sopenharmony_ci return -ENOMEM; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ret = register_netdev(ndev); 8562306a36Sopenharmony_ci if (ret) { 8662306a36Sopenharmony_ci devres_free(dr); 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci dr->ndev = ndev; 9162306a36Sopenharmony_ci devres_add(ndev->dev.parent, dr); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL(devm_register_netdev); 96