update atp870u driver to 0.78 from D-Link source
[linux-2.4.git] / net / atm / resources.c
1 /* net/atm/resources.c - Staticly allocated resources */
2
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5
6 #include <linux/config.h>
7 #include <linux/ctype.h>
8 #include <linux/string.h>
9 #include <linux/atmdev.h>
10 #include <linux/sonet.h>
11 #include <linux/kernel.h> /* for barrier */
12 #include <linux/module.h>
13 #include <linux/bitops.h>
14 #include <net/sock.h>    /* for struct sock */
15 #include <asm/segment.h> /* for get_fs_long and put_fs_long */
16
17 #include "common.h"
18 #include "resources.h"
19 #include "addr.h"
20
21
22 LIST_HEAD(atm_devs);
23 spinlock_t atm_dev_lock = SPIN_LOCK_UNLOCKED;
24
25
26 static struct atm_dev *__alloc_atm_dev(const char *type)
27 {
28         struct atm_dev *dev;
29
30         dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
31         if (!dev)
32                 return NULL;
33         memset(dev, 0, sizeof(*dev));
34         dev->type = type;
35         dev->signal = ATM_PHY_SIG_UNKNOWN;
36         dev->link_rate = ATM_OC3_PCR;
37         spin_lock_init(&dev->lock);
38
39         return dev;
40 }
41
42
43 static void __free_atm_dev(struct atm_dev *dev)
44 {
45         kfree(dev);
46 }
47
48 static struct atm_dev *__atm_dev_lookup(int number)
49 {
50         struct atm_dev *dev;
51         struct list_head *p;
52
53         list_for_each(p, &atm_devs) {
54                 dev = list_entry(p, struct atm_dev, dev_list);
55                 if ((dev->ops) && (dev->number == number)) {
56                         atm_dev_hold(dev);
57                         return dev;
58                 }
59         }
60         return NULL;
61 }
62
63 struct atm_dev *atm_dev_lookup(int number)
64 {
65         struct atm_dev *dev;
66
67         spin_lock(&atm_dev_lock);
68         dev = __atm_dev_lookup(number);
69         spin_unlock(&atm_dev_lock);
70         return dev;
71 }
72
73 struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
74                                  int number, atm_dev_flags_t *flags)
75 {
76         struct atm_dev *dev, *inuse;
77
78
79         dev = __alloc_atm_dev(type);
80         if (!dev) {
81                 printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
82                     type);
83                 return NULL;
84         }
85         spin_lock(&atm_dev_lock);
86         if (number != -1) {
87                 if ((inuse = __atm_dev_lookup(number))) {
88                         atm_dev_put(inuse);
89                         spin_unlock(&atm_dev_lock);
90                         __free_atm_dev(dev);
91                         return NULL;
92                 }
93                 dev->number = number;
94         } else {
95                 dev->number = 0;
96                 while ((inuse = __atm_dev_lookup(dev->number))) {
97                         atm_dev_put(inuse);
98                         dev->number++;
99                 }
100         }
101
102         dev->ops = ops;
103         if (flags) 
104                 dev->flags = *flags;
105         else 
106                 memset(&dev->flags, 0, sizeof(dev->flags));
107         memset(&dev->stats, 0, sizeof(dev->stats));
108         atomic_set(&dev->refcnt, 1);
109         list_add_tail(&dev->dev_list, &atm_devs);
110         spin_unlock(&atm_dev_lock);
111
112 #ifdef CONFIG_PROC_FS
113         if (ops->proc_read) {
114                 if (atm_proc_dev_register(dev) < 0) {
115                         printk(KERN_ERR "atm_dev_register: "
116                                "atm_proc_dev_register failed for dev %s\n",
117                                type);
118                         spin_lock(&atm_dev_lock);
119                         list_del(&dev->dev_list);
120                         spin_unlock(&atm_dev_lock);
121                         __free_atm_dev(dev);
122                         return NULL;
123                 }
124         }
125 #endif
126
127         return dev;
128 }
129
130
131 void atm_dev_deregister(struct atm_dev *dev)
132 {
133         unsigned long warning_time;
134
135 #ifdef CONFIG_PROC_FS
136         if (dev->ops->proc_read)
137                 atm_proc_dev_deregister(dev);
138 #endif
139         spin_lock(&atm_dev_lock);
140         list_del(&dev->dev_list);
141         spin_unlock(&atm_dev_lock);
142
143         warning_time = jiffies;
144         while (atomic_read(&dev->refcnt) != 1) {
145                 current->state = TASK_INTERRUPTIBLE;
146                 schedule_timeout(HZ / 4);
147                 current->state = TASK_RUNNING;
148                 if ((jiffies - warning_time) > 10 * HZ) {
149                         printk(KERN_EMERG "atm_dev_deregister: waiting for "
150                                "dev %d to become free. Usage count = %d\n",
151                                dev->number, atomic_read(&dev->refcnt));
152                         warning_time = jiffies;
153                 }
154         }
155
156         __free_atm_dev(dev);
157 }
158
159 void shutdown_atm_dev(struct atm_dev *dev)
160 {
161         if (atomic_read(&dev->refcnt) > 1) {
162                 set_bit(ATM_DF_CLOSE, &dev->flags);
163                 return;
164         }
165         if (dev->ops->dev_close)
166                 dev->ops->dev_close(dev);
167         atm_dev_deregister(dev);
168 }
169
170
171 static void copy_aal_stats(struct k_atm_aal_stats *from,
172     struct atm_aal_stats *to)
173 {
174 #define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
175         __AAL_STAT_ITEMS
176 #undef __HANDLE_ITEM
177 }
178
179
180 static void subtract_aal_stats(struct k_atm_aal_stats *from,
181     struct atm_aal_stats *to)
182 {
183 #define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
184         __AAL_STAT_ITEMS
185 #undef __HANDLE_ITEM
186 }
187
188
189 static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats *arg, int zero)
190 {
191         struct atm_dev_stats tmp;
192         int error = 0;
193
194         copy_aal_stats(&dev->stats.aal0, &tmp.aal0);
195         copy_aal_stats(&dev->stats.aal34, &tmp.aal34);
196         copy_aal_stats(&dev->stats.aal5, &tmp.aal5);
197         if (arg)
198                 error = copy_to_user(arg, &tmp, sizeof(tmp));
199         if (zero && !error) {
200                 subtract_aal_stats(&dev->stats.aal0, &tmp.aal0);
201                 subtract_aal_stats(&dev->stats.aal34, &tmp.aal34);
202                 subtract_aal_stats(&dev->stats.aal5, &tmp.aal5);
203         }
204         return error ? -EFAULT : 0;
205 }
206
207
208 int atm_dev_ioctl(unsigned int cmd, unsigned long arg)
209 {
210         void *buf;
211         int error = 0, len, number, size = 0;
212         struct atm_dev *dev;
213
214         if (cmd == ATM_GETNAMES) {
215                 int *tmp_buf, *tmp_bufp;
216                 struct list_head *p;
217                 /*
218                  * ATM_GETNAMES is a special case: it doesn't require a
219                  * device number argument
220                  */
221                 if (get_user(buf, &((struct atm_iobuf *) arg)->buffer))
222                         return -EFAULT;
223                 if (get_user(len, &((struct atm_iobuf *) arg)->length))
224                         return -EFAULT;
225                 spin_lock(&atm_dev_lock);
226                 list_for_each(p, &atm_devs)
227                         size += sizeof(int);
228                 if (size > len) {
229                         spin_unlock(&atm_dev_lock);
230                         return -E2BIG;
231                 }
232                 tmp_buf = tmp_bufp = kmalloc(size, GFP_ATOMIC);
233                 if (!tmp_buf) {
234                         spin_unlock(&atm_dev_lock);
235                         return -ENOMEM;
236                 }
237                 list_for_each(p, &atm_devs) {
238                         dev = list_entry(p, struct atm_dev, dev_list);
239                         *tmp_bufp++ = dev->number;
240                 }
241                 spin_unlock(&atm_dev_lock);
242                 error = (copy_to_user(buf, tmp_buf, size) ||
243                                 put_user(size, &((struct atm_iobuf *) arg)->length))
244                                         ? -EFAULT : 0;
245                 kfree(tmp_buf);
246                 return error;
247         }
248
249         if (get_user(buf, &((struct atmif_sioc *) arg)->arg))
250                 return -EFAULT;
251         if (get_user(len, &((struct atmif_sioc *) arg)->length))
252                 return -EFAULT;
253         if (get_user(number, &((struct atmif_sioc *) arg)->number))
254                 return -EFAULT;
255
256         if (!(dev = atm_dev_lookup(number)))
257                 return -ENODEV;
258         
259         switch (cmd) {
260                 case ATM_GETTYPE:
261                         size = strlen(dev->type) + 1;
262                         if (copy_to_user(buf, dev->type, size)) {
263                                 error = -EFAULT;
264                                 goto done;
265                         }
266                         break;
267                 case ATM_GETESI:
268                         size = ESI_LEN;
269                         if (copy_to_user(buf, dev->esi, size)) {
270                                 error = -EFAULT;
271                                 goto done;
272                         }
273                         break;
274                 case ATM_SETESI:
275                         {
276                                 int i;
277
278                                 for (i = 0; i < ESI_LEN; i++)
279                                         if (dev->esi[i]) {
280                                                 error = -EEXIST;
281                                                 goto done;
282                                         }
283                         }
284                         /* fall through */
285                 case ATM_SETESIF:
286                         {
287                                 unsigned char esi[ESI_LEN];
288
289                                 if (!capable(CAP_NET_ADMIN)) {
290                                         error = -EPERM;
291                                         goto done;
292                                 }
293                                 if (copy_from_user(esi, buf, ESI_LEN)) {
294                                         error = -EFAULT;
295                                         goto done;
296                                 }
297                                 memcpy(dev->esi, esi, ESI_LEN);
298                                 error =  ESI_LEN;
299                                 goto done;
300                         }
301                 case ATM_GETSTATZ:
302                         if (!capable(CAP_NET_ADMIN)) {
303                                 error = -EPERM;
304                                 goto done;
305                         }
306                         /* fall through */
307                 case ATM_GETSTAT:
308                         size = sizeof(struct atm_dev_stats);
309                         error = fetch_stats(dev, buf, cmd == ATM_GETSTATZ);
310                         if (error)
311                                 goto done;
312                         break;
313                 case ATM_GETCIRANGE:
314                         size = sizeof(struct atm_cirange);
315                         if (copy_to_user(buf, &dev->ci_range, size)) {
316                                 error = -EFAULT;
317                                 goto done;
318                         }
319                         break;
320                 case ATM_GETLINKRATE:
321                         size = sizeof(int);
322                         if (copy_to_user(buf, &dev->link_rate, size)) {
323                                 error = -EFAULT;
324                                 goto done;
325                         }
326                         break;
327                 case ATM_RSTADDR:
328                         if (!capable(CAP_NET_ADMIN)) {
329                                 error = -EPERM;
330                                 goto done;
331                         }
332                         atm_reset_addr(dev);
333                         break;
334                 case ATM_ADDADDR:
335                 case ATM_DELADDR:
336                         if (!capable(CAP_NET_ADMIN)) {
337                                 error = -EPERM;
338                                 goto done;
339                         }
340                         {
341                                 struct sockaddr_atmsvc addr;
342
343                                 if (copy_from_user(&addr, buf, sizeof(addr))) {
344                                         error = -EFAULT;
345                                         goto done;
346                                 }
347                                 if (cmd == ATM_ADDADDR)
348                                         error = atm_add_addr(dev, &addr);
349                                 else
350                                         error = atm_del_addr(dev, &addr);
351                                 goto done;
352                         }
353                 case ATM_GETADDR:
354                         error = atm_get_addr(dev, buf, len);
355                         if (error < 0)
356                                 goto done;
357                         size = error;
358                         /* write back size even if it's zero */
359                         goto write_size;
360                 case ATM_SETLOOP:
361                         if (__ATM_LM_XTRMT((int) (long) buf) &&
362                             __ATM_LM_XTLOC((int) (long) buf) >
363                             __ATM_LM_XTRMT((int) (long) buf)) {
364                                 error = -EINVAL;
365                                 goto done;
366                         }
367                         /* fall through */
368                 case ATM_SETCIRANGE:
369                 case SONET_GETSTATZ:
370                 case SONET_SETDIAG:
371                 case SONET_CLRDIAG:
372                 case SONET_SETFRAMING:
373                         if (!capable(CAP_NET_ADMIN)) {
374                                 error = -EPERM;
375                                 goto done;
376                         }
377                         /* fall through */
378                 default:
379                         if (!dev->ops->ioctl) {
380                                 error = -EINVAL;
381                                 goto done;
382                         }
383                         size = dev->ops->ioctl(dev, cmd, buf);
384                         if (size < 0) {
385                                 error = (size == -ENOIOCTLCMD ? -EINVAL : size);
386                                 goto done;
387                         }
388         }
389         
390         if (size) {
391 write_size:
392                 error = put_user(size,
393                           &((struct atmif_sioc *) arg)->length)
394                           ? -EFAULT : 0;
395         }
396 done:
397         atm_dev_put(dev);
398         return error;
399 }
400
401
402 EXPORT_SYMBOL(atm_dev_register);
403 EXPORT_SYMBOL(atm_dev_deregister);
404 EXPORT_SYMBOL(atm_dev_lookup);
405 EXPORT_SYMBOL(shutdown_atm_dev);