include upstream ip1000a driver version 2.09f
[linux-2.4.git] / net / atm / addr.c
1 /* net/atm/addr.c - Local ATM address registry */
2
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5
6 #include <linux/atm.h>
7 #include <linux/atmdev.h>
8 #include <linux/sched.h>
9 #include <asm/uaccess.h>
10
11 #include "signaling.h"
12 #include "addr.h"
13
14
15 static int check_addr(struct sockaddr_atmsvc *addr)
16 {
17         int i;
18
19         if (addr->sas_family != AF_ATMSVC) return -EAFNOSUPPORT;
20         if (!*addr->sas_addr.pub)
21                 return *addr->sas_addr.prv ? 0 : -EINVAL;
22         for (i = 1; i < ATM_E164_LEN+1; i++) /* make sure it's \0-terminated */
23                 if (!addr->sas_addr.pub[i]) return 0;
24         return -EINVAL;
25 }
26
27
28 static int identical(struct sockaddr_atmsvc *a,struct sockaddr_atmsvc *b)
29 {
30         if (*a->sas_addr.prv)
31                 if (memcmp(a->sas_addr.prv,b->sas_addr.prv,ATM_ESA_LEN))
32                         return 0;
33         if (!*a->sas_addr.pub) return !*b->sas_addr.pub;
34         if (!*b->sas_addr.pub) return 0;
35         return !strcmp(a->sas_addr.pub,b->sas_addr.pub);
36 }
37
38
39 static void notify_sigd(struct atm_dev *dev)
40 {
41         struct sockaddr_atmpvc pvc;
42
43         pvc.sap_addr.itf = dev->number;
44         sigd_enq(NULL,as_itf_notify,NULL,&pvc,NULL);
45 }
46
47
48 void atm_reset_addr(struct atm_dev *dev)
49 {
50         unsigned long flags;
51         struct atm_dev_addr *this;
52
53         spin_lock_irqsave(&dev->lock, flags);
54         while (dev->local) {
55                 this = dev->local;
56                 dev->local = this->next;
57                 kfree(this);
58         }
59         spin_unlock_irqrestore(&dev->lock, flags);
60         notify_sigd(dev);
61 }
62
63
64 int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
65 {
66         unsigned long flags;
67         struct atm_dev_addr **walk;
68         int error;
69
70         error = check_addr(addr);
71         if (error)
72                 return error;
73         spin_lock_irqsave(&dev->lock, flags);
74         for (walk = &dev->local; *walk; walk = &(*walk)->next)
75                 if (identical(&(*walk)->addr,addr)) {
76                         spin_unlock_irqrestore(&dev->lock, flags);
77                         return -EEXIST;
78                 }
79         *walk = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
80         if (!*walk) {
81                 spin_unlock_irqrestore(&dev->lock, flags);
82                 return -ENOMEM;
83         }
84         (*walk)->addr = *addr;
85         (*walk)->next = NULL;
86         spin_unlock_irqrestore(&dev->lock, flags);
87         notify_sigd(dev);
88         return 0;
89 }
90
91
92 int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr)
93 {
94         unsigned long flags;
95         struct atm_dev_addr **walk,*this;
96         int error;
97
98         error = check_addr(addr);
99         if (error)
100                 return error;
101         spin_lock_irqsave(&dev->lock, flags);
102         for (walk = &dev->local; *walk; walk = &(*walk)->next)
103                 if (identical(&(*walk)->addr,addr)) break;
104         if (!*walk) {
105                 spin_unlock_irqrestore(&dev->lock, flags);
106                 return -ENOENT;
107         }
108         this = *walk;
109         *walk = this->next;
110         kfree(this);
111         spin_unlock_irqrestore(&dev->lock, flags);
112         notify_sigd(dev);
113         return 0;
114 }
115
116
117 int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc *u_buf,size_t size)
118 {
119         unsigned long flags;
120         struct atm_dev_addr *walk;
121         int total = 0, error;
122         struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
123
124
125         spin_lock_irqsave(&dev->lock, flags);
126         for (walk = dev->local; walk; walk = walk->next)
127                 total += sizeof(struct sockaddr_atmsvc);
128         tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
129         if (!tmp_buf) {
130                 spin_unlock_irqrestore(&dev->lock, flags);
131                 return -ENOMEM;
132         }
133         for (walk = dev->local; walk; walk = walk->next)
134                 memcpy(tmp_bufp++, &walk->addr, sizeof(struct sockaddr_atmsvc));
135         spin_unlock_irqrestore(&dev->lock, flags);
136         error = total > size ? -E2BIG : total;
137         if (copy_to_user(u_buf, tmp_buf, total < size ? total : size))
138                 error = -EFAULT;
139         kfree(tmp_buf);
140         return error;
141 }