1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /* net/atm/resources.c - Statically allocated resources */
31da177e4SLinus Torvalds
41da177e4SLinus Torvalds /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
51da177e4SLinus Torvalds
61da177e4SLinus Torvalds /* Fixes
71da177e4SLinus Torvalds * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
81da177e4SLinus Torvalds * 2002/01 - don't free the whole struct sock on sk->destruct time,
91da177e4SLinus Torvalds * use the default destruct function initialized by sock_init_data */
101da177e4SLinus Torvalds
1199824461SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds #include <linux/ctype.h>
141da177e4SLinus Torvalds #include <linux/string.h>
151da177e4SLinus Torvalds #include <linux/atmdev.h>
161da177e4SLinus Torvalds #include <linux/sonet.h>
171da177e4SLinus Torvalds #include <linux/kernel.h> /* for barrier */
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/bitops.h>
204fc268d2SRandy Dunlap #include <linux/capability.h>
211da177e4SLinus Torvalds #include <linux/delay.h>
2257b47a53SIngo Molnar #include <linux/mutex.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
2457b47a53SIngo Molnar
251da177e4SLinus Torvalds #include <net/sock.h> /* for struct sock */
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds #include "common.h"
281da177e4SLinus Torvalds #include "resources.h"
291da177e4SLinus Torvalds #include "addr.h"
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds LIST_HEAD(atm_devs);
3357b47a53SIngo Molnar DEFINE_MUTEX(atm_dev_mutex);
341da177e4SLinus Torvalds
__alloc_atm_dev(const char * type)351da177e4SLinus Torvalds static struct atm_dev *__alloc_atm_dev(const char *type)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds struct atm_dev *dev;
381da177e4SLinus Torvalds
390da974f4SPanagiotis Issaris dev = kzalloc(sizeof(*dev), GFP_KERNEL);
401da177e4SLinus Torvalds if (!dev)
411da177e4SLinus Torvalds return NULL;
421da177e4SLinus Torvalds dev->type = type;
431da177e4SLinus Torvalds dev->signal = ATM_PHY_SIG_UNKNOWN;
441da177e4SLinus Torvalds dev->link_rate = ATM_OC3_PCR;
451da177e4SLinus Torvalds spin_lock_init(&dev->lock);
461da177e4SLinus Torvalds INIT_LIST_HEAD(&dev->local);
470f21ba7cSEric Kinzie INIT_LIST_HEAD(&dev->lecs);
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds return dev;
501da177e4SLinus Torvalds }
511da177e4SLinus Torvalds
__atm_dev_lookup(int number)521da177e4SLinus Torvalds static struct atm_dev *__atm_dev_lookup(int number)
531da177e4SLinus Torvalds {
541da177e4SLinus Torvalds struct atm_dev *dev;
551da177e4SLinus Torvalds
5673e42909SWang Hai list_for_each_entry(dev, &atm_devs, dev_list) {
57aaaaaadbSStanislaw Gruszka if (dev->number == number) {
581da177e4SLinus Torvalds atm_dev_hold(dev);
591da177e4SLinus Torvalds return dev;
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds return NULL;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds
atm_dev_lookup(int number)651da177e4SLinus Torvalds struct atm_dev *atm_dev_lookup(int number)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds struct atm_dev *dev;
681da177e4SLinus Torvalds
6957b47a53SIngo Molnar mutex_lock(&atm_dev_mutex);
701da177e4SLinus Torvalds dev = __atm_dev_lookup(number);
7157b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
721da177e4SLinus Torvalds return dev;
731da177e4SLinus Torvalds }
7407b54c9aSJoe Perches EXPORT_SYMBOL(atm_dev_lookup);
7564bf69ddSStanislaw Gruszka
atm_dev_register(const char * type,struct device * parent,const struct atmdev_ops * ops,int number,unsigned long * flags)76d9ca676bSDan Williams struct atm_dev *atm_dev_register(const char *type, struct device *parent,
77d9ca676bSDan Williams const struct atmdev_ops *ops, int number,
78d9ca676bSDan Williams unsigned long *flags)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds struct atm_dev *dev, *inuse;
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds dev = __alloc_atm_dev(type);
831da177e4SLinus Torvalds if (!dev) {
8499824461SJoe Perches pr_err("no space for dev %s\n", type);
851da177e4SLinus Torvalds return NULL;
861da177e4SLinus Torvalds }
8757b47a53SIngo Molnar mutex_lock(&atm_dev_mutex);
881da177e4SLinus Torvalds if (number != -1) {
8907b54c9aSJoe Perches inuse = __atm_dev_lookup(number);
9007b54c9aSJoe Perches if (inuse) {
911da177e4SLinus Torvalds atm_dev_put(inuse);
9257b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
93ebc37b61SAdrian Bunk kfree(dev);
941da177e4SLinus Torvalds return NULL;
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds dev->number = number;
971da177e4SLinus Torvalds } else {
981da177e4SLinus Torvalds dev->number = 0;
991da177e4SLinus Torvalds while ((inuse = __atm_dev_lookup(dev->number))) {
1001da177e4SLinus Torvalds atm_dev_put(inuse);
1011da177e4SLinus Torvalds dev->number++;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds dev->ops = ops;
1061da177e4SLinus Torvalds if (flags)
1071da177e4SLinus Torvalds dev->flags = *flags;
1081da177e4SLinus Torvalds else
1091da177e4SLinus Torvalds memset(&dev->flags, 0, sizeof(dev->flags));
1101da177e4SLinus Torvalds memset(&dev->stats, 0, sizeof(dev->stats));
111458bc30cSReshetova, Elena refcount_set(&dev->refcnt, 1);
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds if (atm_proc_dev_register(dev) < 0) {
11499824461SJoe Perches pr_err("atm_proc_dev_register failed for dev %s\n", type);
115656d98b0SRoman Kagan goto out_fail;
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds
118d9ca676bSDan Williams if (atm_register_sysfs(dev, parent) < 0) {
11999824461SJoe Perches pr_err("atm_register_sysfs failed for dev %s\n", type);
120656d98b0SRoman Kagan atm_proc_dev_deregister(dev);
121656d98b0SRoman Kagan goto out_fail;
122656d98b0SRoman Kagan }
123656d98b0SRoman Kagan
124656d98b0SRoman Kagan list_add_tail(&dev->dev_list, &atm_devs);
125656d98b0SRoman Kagan
126656d98b0SRoman Kagan out:
127656d98b0SRoman Kagan mutex_unlock(&atm_dev_mutex);
1281da177e4SLinus Torvalds return dev;
129656d98b0SRoman Kagan
130656d98b0SRoman Kagan out_fail:
131656d98b0SRoman Kagan kfree(dev);
132656d98b0SRoman Kagan dev = NULL;
133656d98b0SRoman Kagan goto out;
1341da177e4SLinus Torvalds }
13507b54c9aSJoe Perches EXPORT_SYMBOL(atm_dev_register);
1361da177e4SLinus Torvalds
atm_dev_deregister(struct atm_dev * dev)1371da177e4SLinus Torvalds void atm_dev_deregister(struct atm_dev *dev)
1381da177e4SLinus Torvalds {
13964bf69ddSStanislaw Gruszka BUG_ON(test_bit(ATM_DF_REMOVED, &dev->flags));
14064bf69ddSStanislaw Gruszka set_bit(ATM_DF_REMOVED, &dev->flags);
1411da177e4SLinus Torvalds
14264bf69ddSStanislaw Gruszka /*
14364bf69ddSStanislaw Gruszka * if we remove current device from atm_devs list, new device
14464bf69ddSStanislaw Gruszka * with same number can appear, such we need deregister proc,
14564bf69ddSStanislaw Gruszka * release async all vccs and remove them from vccs list too
14664bf69ddSStanislaw Gruszka */
14757b47a53SIngo Molnar mutex_lock(&atm_dev_mutex);
1481da177e4SLinus Torvalds list_del(&dev->dev_list);
14957b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
1501da177e4SLinus Torvalds
15164bf69ddSStanislaw Gruszka atm_dev_release_vccs(dev);
152656d98b0SRoman Kagan atm_unregister_sysfs(dev);
15364bf69ddSStanislaw Gruszka atm_proc_dev_deregister(dev);
1541da177e4SLinus Torvalds
15564bf69ddSStanislaw Gruszka atm_dev_put(dev);
1561da177e4SLinus Torvalds }
15707b54c9aSJoe Perches EXPORT_SYMBOL(atm_dev_deregister);
1581da177e4SLinus Torvalds
copy_aal_stats(struct k_atm_aal_stats * from,struct atm_aal_stats * to)1591da177e4SLinus Torvalds static void copy_aal_stats(struct k_atm_aal_stats *from,
1601da177e4SLinus Torvalds struct atm_aal_stats *to)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds #define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
1631da177e4SLinus Torvalds __AAL_STAT_ITEMS
1641da177e4SLinus Torvalds #undef __HANDLE_ITEM
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds
subtract_aal_stats(struct k_atm_aal_stats * from,struct atm_aal_stats * to)1671da177e4SLinus Torvalds static void subtract_aal_stats(struct k_atm_aal_stats *from,
1681da177e4SLinus Torvalds struct atm_aal_stats *to)
1691da177e4SLinus Torvalds {
1701da177e4SLinus Torvalds #define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
1711da177e4SLinus Torvalds __AAL_STAT_ITEMS
1721da177e4SLinus Torvalds #undef __HANDLE_ITEM
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds
fetch_stats(struct atm_dev * dev,struct atm_dev_stats __user * arg,int zero)17507b54c9aSJoe Perches static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg,
17607b54c9aSJoe Perches int zero)
1771da177e4SLinus Torvalds {
1781da177e4SLinus Torvalds struct atm_dev_stats tmp;
1791da177e4SLinus Torvalds int error = 0;
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
1821da177e4SLinus Torvalds copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
1831da177e4SLinus Torvalds copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
1841da177e4SLinus Torvalds if (arg)
1851da177e4SLinus Torvalds error = copy_to_user(arg, &tmp, sizeof(tmp));
1861da177e4SLinus Torvalds if (zero && !error) {
1871da177e4SLinus Torvalds subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
1881da177e4SLinus Torvalds subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
1891da177e4SLinus Torvalds subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds return error ? -EFAULT : 0;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds
atm_getnames(void __user * buf,int __user * iobuf_len)194a3929484SAl Viro int atm_getnames(void __user *buf, int __user *iobuf_len)
1951da177e4SLinus Torvalds {
1968c2348e3SAl Viro int error, len, size = 0;
1971da177e4SLinus Torvalds struct atm_dev *dev;
1981da177e4SLinus Torvalds struct list_head *p;
1991da177e4SLinus Torvalds int *tmp_buf, *tmp_p;
2008865c418SDavid Woodhouse
2018865c418SDavid Woodhouse if (get_user(len, iobuf_len))
2021da177e4SLinus Torvalds return -EFAULT;
20357b47a53SIngo Molnar mutex_lock(&atm_dev_mutex);
2041da177e4SLinus Torvalds list_for_each(p, &atm_devs)
2051da177e4SLinus Torvalds size += sizeof(int);
2061da177e4SLinus Torvalds if (size > len) {
20757b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
2081da177e4SLinus Torvalds return -E2BIG;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds tmp_buf = kmalloc(size, GFP_ATOMIC);
2111da177e4SLinus Torvalds if (!tmp_buf) {
21257b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
2131da177e4SLinus Torvalds return -ENOMEM;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds tmp_p = tmp_buf;
21673e42909SWang Hai list_for_each_entry(dev, &atm_devs, dev_list) {
2171da177e4SLinus Torvalds *tmp_p++ = dev->number;
2181da177e4SLinus Torvalds }
21957b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
2201da177e4SLinus Torvalds error = ((copy_to_user(buf, tmp_buf, size)) ||
2218865c418SDavid Woodhouse put_user(size, iobuf_len))
2221da177e4SLinus Torvalds ? -EFAULT : 0;
2231da177e4SLinus Torvalds kfree(tmp_buf);
2241da177e4SLinus Torvalds return error;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds
atm_dev_ioctl(unsigned int cmd,void __user * buf,int __user * sioc_len,int number,int compat)2278cacb416SAl Viro int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
2288cacb416SAl Viro int number, int compat)
2298c2348e3SAl Viro {
2308cacb416SAl Viro int error, len, size = 0;
2318c2348e3SAl Viro struct atm_dev *dev;
2328c2348e3SAl Viro
2338cacb416SAl Viro if (get_user(len, sioc_len))
2348865c418SDavid Woodhouse return -EFAULT;
23507b54c9aSJoe Perches
23607b54c9aSJoe Perches dev = try_then_request_module(atm_dev_lookup(number), "atm-device-%d",
23707b54c9aSJoe Perches number);
23807b54c9aSJoe Perches if (!dev)
2391da177e4SLinus Torvalds return -ENODEV;
2401da177e4SLinus Torvalds
2411da177e4SLinus Torvalds switch (cmd) {
2421da177e4SLinus Torvalds case ATM_GETTYPE:
2431da177e4SLinus Torvalds size = strlen(dev->type) + 1;
2441da177e4SLinus Torvalds if (copy_to_user(buf, dev->type, size)) {
2451da177e4SLinus Torvalds error = -EFAULT;
2461da177e4SLinus Torvalds goto done;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds break;
2491da177e4SLinus Torvalds case ATM_GETESI:
2501da177e4SLinus Torvalds size = ESI_LEN;
2511da177e4SLinus Torvalds if (copy_to_user(buf, dev->esi, size)) {
2521da177e4SLinus Torvalds error = -EFAULT;
2531da177e4SLinus Torvalds goto done;
2541da177e4SLinus Torvalds }
2551da177e4SLinus Torvalds break;
2561da177e4SLinus Torvalds case ATM_SETESI:
2571da177e4SLinus Torvalds {
2581da177e4SLinus Torvalds int i;
2591da177e4SLinus Torvalds
2601da177e4SLinus Torvalds for (i = 0; i < ESI_LEN; i++)
2611da177e4SLinus Torvalds if (dev->esi[i]) {
2621da177e4SLinus Torvalds error = -EEXIST;
2631da177e4SLinus Torvalds goto done;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds }
266df561f66SGustavo A. R. Silva fallthrough;
2671da177e4SLinus Torvalds case ATM_SETESIF:
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds unsigned char esi[ESI_LEN];
2701da177e4SLinus Torvalds
2711da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) {
2721da177e4SLinus Torvalds error = -EPERM;
2731da177e4SLinus Torvalds goto done;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds if (copy_from_user(esi, buf, ESI_LEN)) {
2761da177e4SLinus Torvalds error = -EFAULT;
2771da177e4SLinus Torvalds goto done;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds memcpy(dev->esi, esi, ESI_LEN);
2801da177e4SLinus Torvalds error = ESI_LEN;
2811da177e4SLinus Torvalds goto done;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds case ATM_GETSTATZ:
2841da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) {
2851da177e4SLinus Torvalds error = -EPERM;
2861da177e4SLinus Torvalds goto done;
2871da177e4SLinus Torvalds }
288df561f66SGustavo A. R. Silva fallthrough;
2891da177e4SLinus Torvalds case ATM_GETSTAT:
2901da177e4SLinus Torvalds size = sizeof(struct atm_dev_stats);
2911da177e4SLinus Torvalds error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
2921da177e4SLinus Torvalds if (error)
2931da177e4SLinus Torvalds goto done;
2941da177e4SLinus Torvalds break;
2951da177e4SLinus Torvalds case ATM_GETCIRANGE:
2961da177e4SLinus Torvalds size = sizeof(struct atm_cirange);
2971da177e4SLinus Torvalds if (copy_to_user(buf, &dev->ci_range, size)) {
2981da177e4SLinus Torvalds error = -EFAULT;
2991da177e4SLinus Torvalds goto done;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds break;
3021da177e4SLinus Torvalds case ATM_GETLINKRATE:
3031da177e4SLinus Torvalds size = sizeof(int);
3041da177e4SLinus Torvalds if (copy_to_user(buf, &dev->link_rate, size)) {
3051da177e4SLinus Torvalds error = -EFAULT;
3061da177e4SLinus Torvalds goto done;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds break;
3091da177e4SLinus Torvalds case ATM_RSTADDR:
3101da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) {
3111da177e4SLinus Torvalds error = -EPERM;
3121da177e4SLinus Torvalds goto done;
3131da177e4SLinus Torvalds }
3140f21ba7cSEric Kinzie atm_reset_addr(dev, ATM_ADDR_LOCAL);
3151da177e4SLinus Torvalds break;
3161da177e4SLinus Torvalds case ATM_ADDADDR:
3171da177e4SLinus Torvalds case ATM_DELADDR:
3180f21ba7cSEric Kinzie case ATM_ADDLECSADDR:
3190f21ba7cSEric Kinzie case ATM_DELLECSADDR:
32007b54c9aSJoe Perches {
32107b54c9aSJoe Perches struct sockaddr_atmsvc addr;
32207b54c9aSJoe Perches
3231da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) {
3241da177e4SLinus Torvalds error = -EPERM;
3251da177e4SLinus Torvalds goto done;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds if (copy_from_user(&addr, buf, sizeof(addr))) {
3291da177e4SLinus Torvalds error = -EFAULT;
3301da177e4SLinus Torvalds goto done;
3311da177e4SLinus Torvalds }
3320f21ba7cSEric Kinzie if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR)
3330f21ba7cSEric Kinzie error = atm_add_addr(dev, &addr,
3340f21ba7cSEric Kinzie (cmd == ATM_ADDADDR ?
3350f21ba7cSEric Kinzie ATM_ADDR_LOCAL : ATM_ADDR_LECS));
3361da177e4SLinus Torvalds else
3370f21ba7cSEric Kinzie error = atm_del_addr(dev, &addr,
3380f21ba7cSEric Kinzie (cmd == ATM_DELADDR ?
3390f21ba7cSEric Kinzie ATM_ADDR_LOCAL : ATM_ADDR_LECS));
3401da177e4SLinus Torvalds goto done;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds case ATM_GETADDR:
3430f21ba7cSEric Kinzie case ATM_GETLECSADDR:
3440f21ba7cSEric Kinzie error = atm_get_addr(dev, buf, len,
3450f21ba7cSEric Kinzie (cmd == ATM_GETADDR ?
3460f21ba7cSEric Kinzie ATM_ADDR_LOCAL : ATM_ADDR_LECS));
3471da177e4SLinus Torvalds if (error < 0)
3481da177e4SLinus Torvalds goto done;
3491da177e4SLinus Torvalds size = error;
3501da177e4SLinus Torvalds /* may return 0, but later on size == 0 means "don't
3511da177e4SLinus Torvalds write the length" */
35207b54c9aSJoe Perches error = put_user(size, sioc_len) ? -EFAULT : 0;
3531da177e4SLinus Torvalds goto done;
3541da177e4SLinus Torvalds case ATM_SETLOOP:
3551da177e4SLinus Torvalds if (__ATM_LM_XTRMT((int) (unsigned long) buf) &&
3561da177e4SLinus Torvalds __ATM_LM_XTLOC((int) (unsigned long) buf) >
3571da177e4SLinus Torvalds __ATM_LM_XTRMT((int) (unsigned long) buf)) {
3581da177e4SLinus Torvalds error = -EINVAL;
3591da177e4SLinus Torvalds goto done;
3601da177e4SLinus Torvalds }
361df561f66SGustavo A. R. Silva fallthrough;
3621da177e4SLinus Torvalds case ATM_SETCIRANGE:
3631da177e4SLinus Torvalds case SONET_GETSTATZ:
3641da177e4SLinus Torvalds case SONET_SETDIAG:
3651da177e4SLinus Torvalds case SONET_CLRDIAG:
3661da177e4SLinus Torvalds case SONET_SETFRAMING:
3671da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) {
3681da177e4SLinus Torvalds error = -EPERM;
3691da177e4SLinus Torvalds goto done;
3701da177e4SLinus Torvalds }
371df561f66SGustavo A. R. Silva fallthrough;
3721da177e4SLinus Torvalds default:
3730805a4b8SNathan Chancellor if (IS_ENABLED(CONFIG_COMPAT) && compat) {
3748865c418SDavid Woodhouse #ifdef CONFIG_COMPAT
3758865c418SDavid Woodhouse if (!dev->ops->compat_ioctl) {
3768865c418SDavid Woodhouse error = -EINVAL;
3778865c418SDavid Woodhouse goto done;
3788865c418SDavid Woodhouse }
3798865c418SDavid Woodhouse size = dev->ops->compat_ioctl(dev, cmd, buf);
3808865c418SDavid Woodhouse #endif
3818865c418SDavid Woodhouse } else {
3821da177e4SLinus Torvalds if (!dev->ops->ioctl) {
3831da177e4SLinus Torvalds error = -EINVAL;
3841da177e4SLinus Torvalds goto done;
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds size = dev->ops->ioctl(dev, cmd, buf);
3878865c418SDavid Woodhouse }
3881da177e4SLinus Torvalds if (size < 0) {
3894a2c2406SWanlong Gao error = (size == -ENOIOCTLCMD ? -ENOTTY : size);
3901da177e4SLinus Torvalds goto done;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds
3941da177e4SLinus Torvalds if (size)
39507b54c9aSJoe Perches error = put_user(size, sioc_len) ? -EFAULT : 0;
3961da177e4SLinus Torvalds else
3971da177e4SLinus Torvalds error = 0;
3981da177e4SLinus Torvalds done:
3991da177e4SLinus Torvalds atm_dev_put(dev);
4001da177e4SLinus Torvalds return error;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds
403*fb1b7be9SArnd Bergmann #ifdef CONFIG_PROC_FS
atm_dev_seq_start(struct seq_file * seq,loff_t * pos)4041da177e4SLinus Torvalds void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos)
4051da177e4SLinus Torvalds {
40657b47a53SIngo Molnar mutex_lock(&atm_dev_mutex);
40767de7924SLi Zefan return seq_list_start_head(&atm_devs, *pos);
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds
atm_dev_seq_stop(struct seq_file * seq,void * v)4101da177e4SLinus Torvalds void atm_dev_seq_stop(struct seq_file *seq, void *v)
4111da177e4SLinus Torvalds {
41257b47a53SIngo Molnar mutex_unlock(&atm_dev_mutex);
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds
atm_dev_seq_next(struct seq_file * seq,void * v,loff_t * pos)4151da177e4SLinus Torvalds void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
4161da177e4SLinus Torvalds {
41767de7924SLi Zefan return seq_list_next(v, &atm_devs, pos);
4181da177e4SLinus Torvalds }
419*fb1b7be9SArnd Bergmann #endif
420