18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* net/atm/resources.c - Statically allocated resources */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* Fixes 78c2ecf20Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 88c2ecf20Sopenharmony_ci * 2002/01 - don't free the whole struct sock on sk->destruct time, 98c2ecf20Sopenharmony_ci * use the default destruct function initialized by sock_init_data */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/ctype.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 168c2ecf20Sopenharmony_ci#include <linux/sonet.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* for barrier */ 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci#include <linux/capability.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <net/sock.h> /* for struct sock */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "common.h" 288c2ecf20Sopenharmony_ci#include "resources.h" 298c2ecf20Sopenharmony_ci#include "addr.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciLIST_HEAD(atm_devs); 338c2ecf20Sopenharmony_ciDEFINE_MUTEX(atm_dev_mutex); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct atm_dev *__alloc_atm_dev(const char *type) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct atm_dev *dev; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 408c2ecf20Sopenharmony_ci if (!dev) 418c2ecf20Sopenharmony_ci return NULL; 428c2ecf20Sopenharmony_ci dev->type = type; 438c2ecf20Sopenharmony_ci dev->signal = ATM_PHY_SIG_UNKNOWN; 448c2ecf20Sopenharmony_ci dev->link_rate = ATM_OC3_PCR; 458c2ecf20Sopenharmony_ci spin_lock_init(&dev->lock); 468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->local); 478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->lecs); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return dev; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct atm_dev *__atm_dev_lookup(int number) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct atm_dev *dev; 558c2ecf20Sopenharmony_ci struct list_head *p; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci list_for_each(p, &atm_devs) { 588c2ecf20Sopenharmony_ci dev = list_entry(p, struct atm_dev, dev_list); 598c2ecf20Sopenharmony_ci if (dev->number == number) { 608c2ecf20Sopenharmony_ci atm_dev_hold(dev); 618c2ecf20Sopenharmony_ci return dev; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci return NULL; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct atm_dev *atm_dev_lookup(int number) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct atm_dev *dev; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 728c2ecf20Sopenharmony_ci dev = __atm_dev_lookup(number); 738c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 748c2ecf20Sopenharmony_ci return dev; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_dev_lookup); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct atm_dev *atm_dev_register(const char *type, struct device *parent, 798c2ecf20Sopenharmony_ci const struct atmdev_ops *ops, int number, 808c2ecf20Sopenharmony_ci unsigned long *flags) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct atm_dev *dev, *inuse; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci dev = __alloc_atm_dev(type); 858c2ecf20Sopenharmony_ci if (!dev) { 868c2ecf20Sopenharmony_ci pr_err("no space for dev %s\n", type); 878c2ecf20Sopenharmony_ci return NULL; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 908c2ecf20Sopenharmony_ci if (number != -1) { 918c2ecf20Sopenharmony_ci inuse = __atm_dev_lookup(number); 928c2ecf20Sopenharmony_ci if (inuse) { 938c2ecf20Sopenharmony_ci atm_dev_put(inuse); 948c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 958c2ecf20Sopenharmony_ci kfree(dev); 968c2ecf20Sopenharmony_ci return NULL; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci dev->number = number; 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci dev->number = 0; 1018c2ecf20Sopenharmony_ci while ((inuse = __atm_dev_lookup(dev->number))) { 1028c2ecf20Sopenharmony_ci atm_dev_put(inuse); 1038c2ecf20Sopenharmony_ci dev->number++; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dev->ops = ops; 1088c2ecf20Sopenharmony_ci if (flags) 1098c2ecf20Sopenharmony_ci dev->flags = *flags; 1108c2ecf20Sopenharmony_ci else 1118c2ecf20Sopenharmony_ci memset(&dev->flags, 0, sizeof(dev->flags)); 1128c2ecf20Sopenharmony_ci memset(&dev->stats, 0, sizeof(dev->stats)); 1138c2ecf20Sopenharmony_ci refcount_set(&dev->refcnt, 1); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (atm_proc_dev_register(dev) < 0) { 1168c2ecf20Sopenharmony_ci pr_err("atm_proc_dev_register failed for dev %s\n", type); 1178c2ecf20Sopenharmony_ci goto out_fail; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (atm_register_sysfs(dev, parent) < 0) { 1218c2ecf20Sopenharmony_ci pr_err("atm_register_sysfs failed for dev %s\n", type); 1228c2ecf20Sopenharmony_ci atm_proc_dev_deregister(dev); 1238c2ecf20Sopenharmony_ci goto out_fail; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci list_add_tail(&dev->dev_list, &atm_devs); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciout: 1298c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 1308c2ecf20Sopenharmony_ci return dev; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciout_fail: 1338c2ecf20Sopenharmony_ci kfree(dev); 1348c2ecf20Sopenharmony_ci dev = NULL; 1358c2ecf20Sopenharmony_ci goto out; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_dev_register); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_civoid atm_dev_deregister(struct atm_dev *dev) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags)); 1428c2ecf20Sopenharmony_ci set_bit(ATM_DF_REMOVED, &dev->flags); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * if we remove current device from atm_devs list, new device 1468c2ecf20Sopenharmony_ci * with same number can appear, such we need deregister proc, 1478c2ecf20Sopenharmony_ci * release async all vccs and remove them from vccs list too 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 1508c2ecf20Sopenharmony_ci list_del(&dev->dev_list); 1518c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci atm_dev_release_vccs(dev); 1548c2ecf20Sopenharmony_ci atm_unregister_sysfs(dev); 1558c2ecf20Sopenharmony_ci atm_proc_dev_deregister(dev); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci atm_dev_put(dev); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_dev_deregister); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void copy_aal_stats(struct k_atm_aal_stats *from, 1628c2ecf20Sopenharmony_ci struct atm_aal_stats *to) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) 1658c2ecf20Sopenharmony_ci __AAL_STAT_ITEMS 1668c2ecf20Sopenharmony_ci#undef __HANDLE_ITEM 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void subtract_aal_stats(struct k_atm_aal_stats *from, 1708c2ecf20Sopenharmony_ci struct atm_aal_stats *to) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i) 1738c2ecf20Sopenharmony_ci __AAL_STAT_ITEMS 1748c2ecf20Sopenharmony_ci#undef __HANDLE_ITEM 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, 1788c2ecf20Sopenharmony_ci int zero) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct atm_dev_stats tmp; 1818c2ecf20Sopenharmony_ci int error = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci copy_aal_stats(&dev->stats.aal0, &tmp.aal0); 1848c2ecf20Sopenharmony_ci copy_aal_stats(&dev->stats.aal34, &tmp.aal34); 1858c2ecf20Sopenharmony_ci copy_aal_stats(&dev->stats.aal5, &tmp.aal5); 1868c2ecf20Sopenharmony_ci if (arg) 1878c2ecf20Sopenharmony_ci error = copy_to_user(arg, &tmp, sizeof(tmp)); 1888c2ecf20Sopenharmony_ci if (zero && !error) { 1898c2ecf20Sopenharmony_ci subtract_aal_stats(&dev->stats.aal0, &tmp.aal0); 1908c2ecf20Sopenharmony_ci subtract_aal_stats(&dev->stats.aal34, &tmp.aal34); 1918c2ecf20Sopenharmony_ci subtract_aal_stats(&dev->stats.aal5, &tmp.aal5); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci return error ? -EFAULT : 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ciint atm_getnames(void __user *buf, int __user *iobuf_len) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int error, len, size = 0; 1998c2ecf20Sopenharmony_ci struct atm_dev *dev; 2008c2ecf20Sopenharmony_ci struct list_head *p; 2018c2ecf20Sopenharmony_ci int *tmp_buf, *tmp_p; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (get_user(len, iobuf_len)) 2048c2ecf20Sopenharmony_ci return -EFAULT; 2058c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 2068c2ecf20Sopenharmony_ci list_for_each(p, &atm_devs) 2078c2ecf20Sopenharmony_ci size += sizeof(int); 2088c2ecf20Sopenharmony_ci if (size > len) { 2098c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 2108c2ecf20Sopenharmony_ci return -E2BIG; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci tmp_buf = kmalloc(size, GFP_ATOMIC); 2138c2ecf20Sopenharmony_ci if (!tmp_buf) { 2148c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci tmp_p = tmp_buf; 2188c2ecf20Sopenharmony_ci list_for_each(p, &atm_devs) { 2198c2ecf20Sopenharmony_ci dev = list_entry(p, struct atm_dev, dev_list); 2208c2ecf20Sopenharmony_ci *tmp_p++ = dev->number; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 2238c2ecf20Sopenharmony_ci error = ((copy_to_user(buf, tmp_buf, size)) || 2248c2ecf20Sopenharmony_ci put_user(size, iobuf_len)) 2258c2ecf20Sopenharmony_ci ? -EFAULT : 0; 2268c2ecf20Sopenharmony_ci kfree(tmp_buf); 2278c2ecf20Sopenharmony_ci return error; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciint atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len, 2318c2ecf20Sopenharmony_ci int number, int compat) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int error, len, size = 0; 2348c2ecf20Sopenharmony_ci struct atm_dev *dev; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (get_user(len, sioc_len)) 2378c2ecf20Sopenharmony_ci return -EFAULT; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d", 2408c2ecf20Sopenharmony_ci number); 2418c2ecf20Sopenharmony_ci if (!dev) 2428c2ecf20Sopenharmony_ci return -ENODEV; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci switch (cmd) { 2458c2ecf20Sopenharmony_ci case ATM_GETTYPE: 2468c2ecf20Sopenharmony_ci size = strlen(dev->type) + 1; 2478c2ecf20Sopenharmony_ci if (copy_to_user(buf, dev->type, size)) { 2488c2ecf20Sopenharmony_ci error = -EFAULT; 2498c2ecf20Sopenharmony_ci goto done; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case ATM_GETESI: 2538c2ecf20Sopenharmony_ci size = ESI_LEN; 2548c2ecf20Sopenharmony_ci if (copy_to_user(buf, dev->esi, size)) { 2558c2ecf20Sopenharmony_ci error = -EFAULT; 2568c2ecf20Sopenharmony_ci goto done; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case ATM_SETESI: 2608c2ecf20Sopenharmony_ci { 2618c2ecf20Sopenharmony_ci int i; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 2648c2ecf20Sopenharmony_ci if (dev->esi[i]) { 2658c2ecf20Sopenharmony_ci error = -EEXIST; 2668c2ecf20Sopenharmony_ci goto done; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci fallthrough; 2708c2ecf20Sopenharmony_ci case ATM_SETESIF: 2718c2ecf20Sopenharmony_ci { 2728c2ecf20Sopenharmony_ci unsigned char esi[ESI_LEN]; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 2758c2ecf20Sopenharmony_ci error = -EPERM; 2768c2ecf20Sopenharmony_ci goto done; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (copy_from_user(esi, buf, ESI_LEN)) { 2798c2ecf20Sopenharmony_ci error = -EFAULT; 2808c2ecf20Sopenharmony_ci goto done; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci memcpy(dev->esi, esi, ESI_LEN); 2838c2ecf20Sopenharmony_ci error = ESI_LEN; 2848c2ecf20Sopenharmony_ci goto done; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci case ATM_GETSTATZ: 2878c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 2888c2ecf20Sopenharmony_ci error = -EPERM; 2898c2ecf20Sopenharmony_ci goto done; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci fallthrough; 2928c2ecf20Sopenharmony_ci case ATM_GETSTAT: 2938c2ecf20Sopenharmony_ci size = sizeof(struct atm_dev_stats); 2948c2ecf20Sopenharmony_ci error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ); 2958c2ecf20Sopenharmony_ci if (error) 2968c2ecf20Sopenharmony_ci goto done; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case ATM_GETCIRANGE: 2998c2ecf20Sopenharmony_ci size = sizeof(struct atm_cirange); 3008c2ecf20Sopenharmony_ci if (copy_to_user(buf, &dev->ci_range, size)) { 3018c2ecf20Sopenharmony_ci error = -EFAULT; 3028c2ecf20Sopenharmony_ci goto done; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci case ATM_GETLINKRATE: 3068c2ecf20Sopenharmony_ci size = sizeof(int); 3078c2ecf20Sopenharmony_ci if (copy_to_user(buf, &dev->link_rate, size)) { 3088c2ecf20Sopenharmony_ci error = -EFAULT; 3098c2ecf20Sopenharmony_ci goto done; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci case ATM_RSTADDR: 3138c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 3148c2ecf20Sopenharmony_ci error = -EPERM; 3158c2ecf20Sopenharmony_ci goto done; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci atm_reset_addr(dev, ATM_ADDR_LOCAL); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case ATM_ADDADDR: 3208c2ecf20Sopenharmony_ci case ATM_DELADDR: 3218c2ecf20Sopenharmony_ci case ATM_ADDLECSADDR: 3228c2ecf20Sopenharmony_ci case ATM_DELLECSADDR: 3238c2ecf20Sopenharmony_ci { 3248c2ecf20Sopenharmony_ci struct sockaddr_atmsvc addr; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 3278c2ecf20Sopenharmony_ci error = -EPERM; 3288c2ecf20Sopenharmony_ci goto done; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (copy_from_user(&addr, buf, sizeof(addr))) { 3328c2ecf20Sopenharmony_ci error = -EFAULT; 3338c2ecf20Sopenharmony_ci goto done; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR) 3368c2ecf20Sopenharmony_ci error = atm_add_addr(dev, &addr, 3378c2ecf20Sopenharmony_ci (cmd == ATM_ADDADDR ? 3388c2ecf20Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci error = atm_del_addr(dev, &addr, 3418c2ecf20Sopenharmony_ci (cmd == ATM_DELADDR ? 3428c2ecf20Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 3438c2ecf20Sopenharmony_ci goto done; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci case ATM_GETADDR: 3468c2ecf20Sopenharmony_ci case ATM_GETLECSADDR: 3478c2ecf20Sopenharmony_ci error = atm_get_addr(dev, buf, len, 3488c2ecf20Sopenharmony_ci (cmd == ATM_GETADDR ? 3498c2ecf20Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 3508c2ecf20Sopenharmony_ci if (error < 0) 3518c2ecf20Sopenharmony_ci goto done; 3528c2ecf20Sopenharmony_ci size = error; 3538c2ecf20Sopenharmony_ci /* may return 0, but later on size == 0 means "don't 3548c2ecf20Sopenharmony_ci write the length" */ 3558c2ecf20Sopenharmony_ci error = put_user(size, sioc_len) ? -EFAULT : 0; 3568c2ecf20Sopenharmony_ci goto done; 3578c2ecf20Sopenharmony_ci case ATM_SETLOOP: 3588c2ecf20Sopenharmony_ci if (__ATM_LM_XTRMT((int) (unsigned long) buf) && 3598c2ecf20Sopenharmony_ci __ATM_LM_XTLOC((int) (unsigned long) buf) > 3608c2ecf20Sopenharmony_ci __ATM_LM_XTRMT((int) (unsigned long) buf)) { 3618c2ecf20Sopenharmony_ci error = -EINVAL; 3628c2ecf20Sopenharmony_ci goto done; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci fallthrough; 3658c2ecf20Sopenharmony_ci case ATM_SETCIRANGE: 3668c2ecf20Sopenharmony_ci case SONET_GETSTATZ: 3678c2ecf20Sopenharmony_ci case SONET_SETDIAG: 3688c2ecf20Sopenharmony_ci case SONET_CLRDIAG: 3698c2ecf20Sopenharmony_ci case SONET_SETFRAMING: 3708c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 3718c2ecf20Sopenharmony_ci error = -EPERM; 3728c2ecf20Sopenharmony_ci goto done; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci fallthrough; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_COMPAT) && compat) { 3778c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 3788c2ecf20Sopenharmony_ci if (!dev->ops->compat_ioctl) { 3798c2ecf20Sopenharmony_ci error = -EINVAL; 3808c2ecf20Sopenharmony_ci goto done; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci size = dev->ops->compat_ioctl(dev, cmd, buf); 3838c2ecf20Sopenharmony_ci#endif 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci if (!dev->ops->ioctl) { 3868c2ecf20Sopenharmony_ci error = -EINVAL; 3878c2ecf20Sopenharmony_ci goto done; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci size = dev->ops->ioctl(dev, cmd, buf); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci if (size < 0) { 3928c2ecf20Sopenharmony_ci error = (size == -ENOIOCTLCMD ? -ENOTTY : size); 3938c2ecf20Sopenharmony_ci goto done; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (size) 3988c2ecf20Sopenharmony_ci error = put_user(size, sioc_len) ? -EFAULT : 0; 3998c2ecf20Sopenharmony_ci else 4008c2ecf20Sopenharmony_ci error = 0; 4018c2ecf20Sopenharmony_cidone: 4028c2ecf20Sopenharmony_ci atm_dev_put(dev); 4038c2ecf20Sopenharmony_ci return error; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 4078c2ecf20Sopenharmony_civoid *atm_dev_seq_start(struct seq_file *seq, loff_t *pos) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci mutex_lock(&atm_dev_mutex); 4108c2ecf20Sopenharmony_ci return seq_list_start_head(&atm_devs, *pos); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_civoid atm_dev_seq_stop(struct seq_file *seq, void *v) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_civoid *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci return seq_list_next(v, &atm_devs, pos); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci#endif 423