162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* net/atm/resources.c - Statically allocated resources */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* Fixes 762306a36Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 862306a36Sopenharmony_ci * 2002/01 - don't free the whole struct sock on sk->destruct time, 962306a36Sopenharmony_ci * use the default destruct function initialized by sock_init_data */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/ctype.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/atmdev.h> 1662306a36Sopenharmony_ci#include <linux/sonet.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> /* for barrier */ 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/bitops.h> 2062306a36Sopenharmony_ci#include <linux/capability.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <net/sock.h> /* for struct sock */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "common.h" 2862306a36Sopenharmony_ci#include "resources.h" 2962306a36Sopenharmony_ci#include "addr.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciLIST_HEAD(atm_devs); 3362306a36Sopenharmony_ciDEFINE_MUTEX(atm_dev_mutex); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct atm_dev *__alloc_atm_dev(const char *type) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct atm_dev *dev; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 4062306a36Sopenharmony_ci if (!dev) 4162306a36Sopenharmony_ci return NULL; 4262306a36Sopenharmony_ci dev->type = type; 4362306a36Sopenharmony_ci dev->signal = ATM_PHY_SIG_UNKNOWN; 4462306a36Sopenharmony_ci dev->link_rate = ATM_OC3_PCR; 4562306a36Sopenharmony_ci spin_lock_init(&dev->lock); 4662306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->local); 4762306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->lecs); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return dev; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct atm_dev *__atm_dev_lookup(int number) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct atm_dev *dev; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci list_for_each_entry(dev, &atm_devs, dev_list) { 5762306a36Sopenharmony_ci if (dev->number == number) { 5862306a36Sopenharmony_ci atm_dev_hold(dev); 5962306a36Sopenharmony_ci return dev; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci return NULL; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct atm_dev *atm_dev_lookup(int number) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct atm_dev *dev; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 7062306a36Sopenharmony_ci dev = __atm_dev_lookup(number); 7162306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 7262306a36Sopenharmony_ci return dev; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL(atm_dev_lookup); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct atm_dev *atm_dev_register(const char *type, struct device *parent, 7762306a36Sopenharmony_ci const struct atmdev_ops *ops, int number, 7862306a36Sopenharmony_ci unsigned long *flags) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct atm_dev *dev, *inuse; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci dev = __alloc_atm_dev(type); 8362306a36Sopenharmony_ci if (!dev) { 8462306a36Sopenharmony_ci pr_err("no space for dev %s\n", type); 8562306a36Sopenharmony_ci return NULL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 8862306a36Sopenharmony_ci if (number != -1) { 8962306a36Sopenharmony_ci inuse = __atm_dev_lookup(number); 9062306a36Sopenharmony_ci if (inuse) { 9162306a36Sopenharmony_ci atm_dev_put(inuse); 9262306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 9362306a36Sopenharmony_ci kfree(dev); 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci dev->number = number; 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci dev->number = 0; 9962306a36Sopenharmony_ci while ((inuse = __atm_dev_lookup(dev->number))) { 10062306a36Sopenharmony_ci atm_dev_put(inuse); 10162306a36Sopenharmony_ci dev->number++; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci dev->ops = ops; 10662306a36Sopenharmony_ci if (flags) 10762306a36Sopenharmony_ci dev->flags = *flags; 10862306a36Sopenharmony_ci else 10962306a36Sopenharmony_ci memset(&dev->flags, 0, sizeof(dev->flags)); 11062306a36Sopenharmony_ci memset(&dev->stats, 0, sizeof(dev->stats)); 11162306a36Sopenharmony_ci refcount_set(&dev->refcnt, 1); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (atm_proc_dev_register(dev) < 0) { 11462306a36Sopenharmony_ci pr_err("atm_proc_dev_register failed for dev %s\n", type); 11562306a36Sopenharmony_ci goto out_fail; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (atm_register_sysfs(dev, parent) < 0) { 11962306a36Sopenharmony_ci pr_err("atm_register_sysfs failed for dev %s\n", type); 12062306a36Sopenharmony_ci atm_proc_dev_deregister(dev); 12162306a36Sopenharmony_ci goto out_fail; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci list_add_tail(&dev->dev_list, &atm_devs); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciout: 12762306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 12862306a36Sopenharmony_ci return dev; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciout_fail: 13162306a36Sopenharmony_ci kfree(dev); 13262306a36Sopenharmony_ci dev = NULL; 13362306a36Sopenharmony_ci goto out; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL(atm_dev_register); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_civoid atm_dev_deregister(struct atm_dev *dev) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags)); 14062306a36Sopenharmony_ci set_bit(ATM_DF_REMOVED, &dev->flags); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * if we remove current device from atm_devs list, new device 14462306a36Sopenharmony_ci * with same number can appear, such we need deregister proc, 14562306a36Sopenharmony_ci * release async all vccs and remove them from vccs list too 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 14862306a36Sopenharmony_ci list_del(&dev->dev_list); 14962306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci atm_dev_release_vccs(dev); 15262306a36Sopenharmony_ci atm_unregister_sysfs(dev); 15362306a36Sopenharmony_ci atm_proc_dev_deregister(dev); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci atm_dev_put(dev); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciEXPORT_SYMBOL(atm_dev_deregister); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void copy_aal_stats(struct k_atm_aal_stats *from, 16062306a36Sopenharmony_ci struct atm_aal_stats *to) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i) 16362306a36Sopenharmony_ci __AAL_STAT_ITEMS 16462306a36Sopenharmony_ci#undef __HANDLE_ITEM 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void subtract_aal_stats(struct k_atm_aal_stats *from, 16862306a36Sopenharmony_ci struct atm_aal_stats *to) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i) 17162306a36Sopenharmony_ci __AAL_STAT_ITEMS 17262306a36Sopenharmony_ci#undef __HANDLE_ITEM 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, 17662306a36Sopenharmony_ci int zero) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct atm_dev_stats tmp; 17962306a36Sopenharmony_ci int error = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci copy_aal_stats(&dev->stats.aal0, &tmp.aal0); 18262306a36Sopenharmony_ci copy_aal_stats(&dev->stats.aal34, &tmp.aal34); 18362306a36Sopenharmony_ci copy_aal_stats(&dev->stats.aal5, &tmp.aal5); 18462306a36Sopenharmony_ci if (arg) 18562306a36Sopenharmony_ci error = copy_to_user(arg, &tmp, sizeof(tmp)); 18662306a36Sopenharmony_ci if (zero && !error) { 18762306a36Sopenharmony_ci subtract_aal_stats(&dev->stats.aal0, &tmp.aal0); 18862306a36Sopenharmony_ci subtract_aal_stats(&dev->stats.aal34, &tmp.aal34); 18962306a36Sopenharmony_ci subtract_aal_stats(&dev->stats.aal5, &tmp.aal5); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci return error ? -EFAULT : 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciint atm_getnames(void __user *buf, int __user *iobuf_len) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int error, len, size = 0; 19762306a36Sopenharmony_ci struct atm_dev *dev; 19862306a36Sopenharmony_ci struct list_head *p; 19962306a36Sopenharmony_ci int *tmp_buf, *tmp_p; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (get_user(len, iobuf_len)) 20262306a36Sopenharmony_ci return -EFAULT; 20362306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 20462306a36Sopenharmony_ci list_for_each(p, &atm_devs) 20562306a36Sopenharmony_ci size += sizeof(int); 20662306a36Sopenharmony_ci if (size > len) { 20762306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 20862306a36Sopenharmony_ci return -E2BIG; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci tmp_buf = kmalloc(size, GFP_ATOMIC); 21162306a36Sopenharmony_ci if (!tmp_buf) { 21262306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 21362306a36Sopenharmony_ci return -ENOMEM; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci tmp_p = tmp_buf; 21662306a36Sopenharmony_ci list_for_each_entry(dev, &atm_devs, dev_list) { 21762306a36Sopenharmony_ci *tmp_p++ = dev->number; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 22062306a36Sopenharmony_ci error = ((copy_to_user(buf, tmp_buf, size)) || 22162306a36Sopenharmony_ci put_user(size, iobuf_len)) 22262306a36Sopenharmony_ci ? -EFAULT : 0; 22362306a36Sopenharmony_ci kfree(tmp_buf); 22462306a36Sopenharmony_ci return error; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len, 22862306a36Sopenharmony_ci int number, int compat) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int error, len, size = 0; 23162306a36Sopenharmony_ci struct atm_dev *dev; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (get_user(len, sioc_len)) 23462306a36Sopenharmony_ci return -EFAULT; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d", 23762306a36Sopenharmony_ci number); 23862306a36Sopenharmony_ci if (!dev) 23962306a36Sopenharmony_ci return -ENODEV; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci switch (cmd) { 24262306a36Sopenharmony_ci case ATM_GETTYPE: 24362306a36Sopenharmony_ci size = strlen(dev->type) + 1; 24462306a36Sopenharmony_ci if (copy_to_user(buf, dev->type, size)) { 24562306a36Sopenharmony_ci error = -EFAULT; 24662306a36Sopenharmony_ci goto done; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci case ATM_GETESI: 25062306a36Sopenharmony_ci size = ESI_LEN; 25162306a36Sopenharmony_ci if (copy_to_user(buf, dev->esi, size)) { 25262306a36Sopenharmony_ci error = -EFAULT; 25362306a36Sopenharmony_ci goto done; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case ATM_SETESI: 25762306a36Sopenharmony_ci { 25862306a36Sopenharmony_ci int i; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 26162306a36Sopenharmony_ci if (dev->esi[i]) { 26262306a36Sopenharmony_ci error = -EEXIST; 26362306a36Sopenharmony_ci goto done; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci fallthrough; 26762306a36Sopenharmony_ci case ATM_SETESIF: 26862306a36Sopenharmony_ci { 26962306a36Sopenharmony_ci unsigned char esi[ESI_LEN]; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 27262306a36Sopenharmony_ci error = -EPERM; 27362306a36Sopenharmony_ci goto done; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci if (copy_from_user(esi, buf, ESI_LEN)) { 27662306a36Sopenharmony_ci error = -EFAULT; 27762306a36Sopenharmony_ci goto done; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci memcpy(dev->esi, esi, ESI_LEN); 28062306a36Sopenharmony_ci error = ESI_LEN; 28162306a36Sopenharmony_ci goto done; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci case ATM_GETSTATZ: 28462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 28562306a36Sopenharmony_ci error = -EPERM; 28662306a36Sopenharmony_ci goto done; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci fallthrough; 28962306a36Sopenharmony_ci case ATM_GETSTAT: 29062306a36Sopenharmony_ci size = sizeof(struct atm_dev_stats); 29162306a36Sopenharmony_ci error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ); 29262306a36Sopenharmony_ci if (error) 29362306a36Sopenharmony_ci goto done; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci case ATM_GETCIRANGE: 29662306a36Sopenharmony_ci size = sizeof(struct atm_cirange); 29762306a36Sopenharmony_ci if (copy_to_user(buf, &dev->ci_range, size)) { 29862306a36Sopenharmony_ci error = -EFAULT; 29962306a36Sopenharmony_ci goto done; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci case ATM_GETLINKRATE: 30362306a36Sopenharmony_ci size = sizeof(int); 30462306a36Sopenharmony_ci if (copy_to_user(buf, &dev->link_rate, size)) { 30562306a36Sopenharmony_ci error = -EFAULT; 30662306a36Sopenharmony_ci goto done; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case ATM_RSTADDR: 31062306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 31162306a36Sopenharmony_ci error = -EPERM; 31262306a36Sopenharmony_ci goto done; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci atm_reset_addr(dev, ATM_ADDR_LOCAL); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci case ATM_ADDADDR: 31762306a36Sopenharmony_ci case ATM_DELADDR: 31862306a36Sopenharmony_ci case ATM_ADDLECSADDR: 31962306a36Sopenharmony_ci case ATM_DELLECSADDR: 32062306a36Sopenharmony_ci { 32162306a36Sopenharmony_ci struct sockaddr_atmsvc addr; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 32462306a36Sopenharmony_ci error = -EPERM; 32562306a36Sopenharmony_ci goto done; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (copy_from_user(&addr, buf, sizeof(addr))) { 32962306a36Sopenharmony_ci error = -EFAULT; 33062306a36Sopenharmony_ci goto done; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR) 33362306a36Sopenharmony_ci error = atm_add_addr(dev, &addr, 33462306a36Sopenharmony_ci (cmd == ATM_ADDADDR ? 33562306a36Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci error = atm_del_addr(dev, &addr, 33862306a36Sopenharmony_ci (cmd == ATM_DELADDR ? 33962306a36Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 34062306a36Sopenharmony_ci goto done; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci case ATM_GETADDR: 34362306a36Sopenharmony_ci case ATM_GETLECSADDR: 34462306a36Sopenharmony_ci error = atm_get_addr(dev, buf, len, 34562306a36Sopenharmony_ci (cmd == ATM_GETADDR ? 34662306a36Sopenharmony_ci ATM_ADDR_LOCAL : ATM_ADDR_LECS)); 34762306a36Sopenharmony_ci if (error < 0) 34862306a36Sopenharmony_ci goto done; 34962306a36Sopenharmony_ci size = error; 35062306a36Sopenharmony_ci /* may return 0, but later on size == 0 means "don't 35162306a36Sopenharmony_ci write the length" */ 35262306a36Sopenharmony_ci error = put_user(size, sioc_len) ? -EFAULT : 0; 35362306a36Sopenharmony_ci goto done; 35462306a36Sopenharmony_ci case ATM_SETLOOP: 35562306a36Sopenharmony_ci if (__ATM_LM_XTRMT((int) (unsigned long) buf) && 35662306a36Sopenharmony_ci __ATM_LM_XTLOC((int) (unsigned long) buf) > 35762306a36Sopenharmony_ci __ATM_LM_XTRMT((int) (unsigned long) buf)) { 35862306a36Sopenharmony_ci error = -EINVAL; 35962306a36Sopenharmony_ci goto done; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci fallthrough; 36262306a36Sopenharmony_ci case ATM_SETCIRANGE: 36362306a36Sopenharmony_ci case SONET_GETSTATZ: 36462306a36Sopenharmony_ci case SONET_SETDIAG: 36562306a36Sopenharmony_ci case SONET_CLRDIAG: 36662306a36Sopenharmony_ci case SONET_SETFRAMING: 36762306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 36862306a36Sopenharmony_ci error = -EPERM; 36962306a36Sopenharmony_ci goto done; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci fallthrough; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_COMPAT) && compat) { 37462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 37562306a36Sopenharmony_ci if (!dev->ops->compat_ioctl) { 37662306a36Sopenharmony_ci error = -EINVAL; 37762306a36Sopenharmony_ci goto done; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci size = dev->ops->compat_ioctl(dev, cmd, buf); 38062306a36Sopenharmony_ci#endif 38162306a36Sopenharmony_ci } else { 38262306a36Sopenharmony_ci if (!dev->ops->ioctl) { 38362306a36Sopenharmony_ci error = -EINVAL; 38462306a36Sopenharmony_ci goto done; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci size = dev->ops->ioctl(dev, cmd, buf); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci if (size < 0) { 38962306a36Sopenharmony_ci error = (size == -ENOIOCTLCMD ? -ENOTTY : size); 39062306a36Sopenharmony_ci goto done; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (size) 39562306a36Sopenharmony_ci error = put_user(size, sioc_len) ? -EFAULT : 0; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci error = 0; 39862306a36Sopenharmony_cidone: 39962306a36Sopenharmony_ci atm_dev_put(dev); 40062306a36Sopenharmony_ci return error; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 40462306a36Sopenharmony_civoid *atm_dev_seq_start(struct seq_file *seq, loff_t *pos) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci mutex_lock(&atm_dev_mutex); 40762306a36Sopenharmony_ci return seq_list_start_head(&atm_devs, *pos); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_civoid atm_dev_seq_stop(struct seq_file *seq, void *v) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci mutex_unlock(&atm_dev_mutex); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_civoid *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci return seq_list_next(v, &atm_devs, pos); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci#endif 420