Root/
1 | /* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ |
2 | |
3 | /* Written 1997-2000 by Werner Almesberger, EPFL LRC/ICA */ |
4 | |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/wait.h> |
8 | #include <linux/atmdev.h> |
9 | #include <linux/atm_tcp.h> |
10 | #include <linux/bitops.h> |
11 | #include <linux/init.h> |
12 | #include <linux/slab.h> |
13 | #include <asm/uaccess.h> |
14 | #include <linux/atomic.h> |
15 | |
16 | |
17 | extern int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */ |
18 | |
19 | |
20 | #define PRIV(dev) ((struct atmtcp_dev_data *) ((dev)->dev_data)) |
21 | |
22 | |
23 | struct atmtcp_dev_data { |
24 | struct atm_vcc *vcc; /* control VCC; NULL if detached */ |
25 | int persist; /* non-zero if persistent */ |
26 | }; |
27 | |
28 | |
29 | #define DEV_LABEL "atmtcp" |
30 | |
31 | #define MAX_VPI_BITS 8 /* simplifies life */ |
32 | #define MAX_VCI_BITS 16 |
33 | |
34 | |
35 | /* |
36 | * Hairy code ahead: the control VCC may be closed while we're still |
37 | * waiting for an answer, so we need to re-validate out_vcc every once |
38 | * in a while. |
39 | */ |
40 | |
41 | |
42 | static int atmtcp_send_control(struct atm_vcc *vcc,int type, |
43 | const struct atmtcp_control *msg,int flag) |
44 | { |
45 | DECLARE_WAITQUEUE(wait,current); |
46 | struct atm_vcc *out_vcc; |
47 | struct sk_buff *skb; |
48 | struct atmtcp_control *new_msg; |
49 | int old_test; |
50 | int error = 0; |
51 | |
52 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; |
53 | if (!out_vcc) return -EUNATCH; |
54 | skb = alloc_skb(sizeof(*msg),GFP_KERNEL); |
55 | if (!skb) return -ENOMEM; |
56 | mb(); |
57 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; |
58 | if (!out_vcc) { |
59 | dev_kfree_skb(skb); |
60 | return -EUNATCH; |
61 | } |
62 | atm_force_charge(out_vcc,skb->truesize); |
63 | new_msg = (struct atmtcp_control *) skb_put(skb,sizeof(*new_msg)); |
64 | *new_msg = *msg; |
65 | new_msg->hdr.length = ATMTCP_HDR_MAGIC; |
66 | new_msg->type = type; |
67 | memset(&new_msg->vcc,0,sizeof(atm_kptr_t)); |
68 | *(struct atm_vcc **) &new_msg->vcc = vcc; |
69 | old_test = test_bit(flag,&vcc->flags); |
70 | out_vcc->push(out_vcc,skb); |
71 | add_wait_queue(sk_sleep(sk_atm(vcc)), &wait); |
72 | while (test_bit(flag,&vcc->flags) == old_test) { |
73 | mb(); |
74 | out_vcc = PRIV(vcc->dev) ? PRIV(vcc->dev)->vcc : NULL; |
75 | if (!out_vcc) { |
76 | error = -EUNATCH; |
77 | break; |
78 | } |
79 | set_current_state(TASK_UNINTERRUPTIBLE); |
80 | schedule(); |
81 | } |
82 | set_current_state(TASK_RUNNING); |
83 | remove_wait_queue(sk_sleep(sk_atm(vcc)), &wait); |
84 | return error; |
85 | } |
86 | |
87 | |
88 | static int atmtcp_recv_control(const struct atmtcp_control *msg) |
89 | { |
90 | struct atm_vcc *vcc = *(struct atm_vcc **) &msg->vcc; |
91 | |
92 | vcc->vpi = msg->addr.sap_addr.vpi; |
93 | vcc->vci = msg->addr.sap_addr.vci; |
94 | vcc->qos = msg->qos; |
95 | sk_atm(vcc)->sk_err = -msg->result; |
96 | switch (msg->type) { |
97 | case ATMTCP_CTRL_OPEN: |
98 | change_bit(ATM_VF_READY,&vcc->flags); |
99 | break; |
100 | case ATMTCP_CTRL_CLOSE: |
101 | change_bit(ATM_VF_ADDR,&vcc->flags); |
102 | break; |
103 | default: |
104 | printk(KERN_ERR "atmtcp_recv_control: unknown type %d\n", |
105 | msg->type); |
106 | return -EINVAL; |
107 | } |
108 | wake_up(sk_sleep(sk_atm(vcc))); |
109 | return 0; |
110 | } |
111 | |
112 | |
113 | static void atmtcp_v_dev_close(struct atm_dev *dev) |
114 | { |
115 | /* Nothing.... Isn't this simple :-) -- REW */ |
116 | } |
117 | |
118 | |
119 | static int atmtcp_v_open(struct atm_vcc *vcc) |
120 | { |
121 | struct atmtcp_control msg; |
122 | int error; |
123 | short vpi = vcc->vpi; |
124 | int vci = vcc->vci; |
125 | |
126 | memset(&msg,0,sizeof(msg)); |
127 | msg.addr.sap_family = AF_ATMPVC; |
128 | msg.hdr.vpi = htons(vpi); |
129 | msg.addr.sap_addr.vpi = vpi; |
130 | msg.hdr.vci = htons(vci); |
131 | msg.addr.sap_addr.vci = vci; |
132 | if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) return 0; |
133 | msg.type = ATMTCP_CTRL_OPEN; |
134 | msg.qos = vcc->qos; |
135 | set_bit(ATM_VF_ADDR,&vcc->flags); |
136 | clear_bit(ATM_VF_READY,&vcc->flags); /* just in case ... */ |
137 | error = atmtcp_send_control(vcc,ATMTCP_CTRL_OPEN,&msg,ATM_VF_READY); |
138 | if (error) return error; |
139 | return -sk_atm(vcc)->sk_err; |
140 | } |
141 | |
142 | |
143 | static void atmtcp_v_close(struct atm_vcc *vcc) |
144 | { |
145 | struct atmtcp_control msg; |
146 | |
147 | memset(&msg,0,sizeof(msg)); |
148 | msg.addr.sap_family = AF_ATMPVC; |
149 | msg.addr.sap_addr.vpi = vcc->vpi; |
150 | msg.addr.sap_addr.vci = vcc->vci; |
151 | clear_bit(ATM_VF_READY,&vcc->flags); |
152 | (void) atmtcp_send_control(vcc,ATMTCP_CTRL_CLOSE,&msg,ATM_VF_ADDR); |
153 | } |
154 | |
155 | |
156 | static int atmtcp_v_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) |
157 | { |
158 | struct atm_cirange ci; |
159 | struct atm_vcc *vcc; |
160 | struct hlist_node *node; |
161 | struct sock *s; |
162 | int i; |
163 | |
164 | if (cmd != ATM_SETCIRANGE) return -ENOIOCTLCMD; |
165 | if (copy_from_user(&ci, arg,sizeof(ci))) return -EFAULT; |
166 | if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS; |
167 | if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; |
168 | if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || |
169 | ci.vci_bits > MAX_VCI_BITS || ci.vci_bits < 0) return -EINVAL; |
170 | read_lock(&vcc_sklist_lock); |
171 | for(i = 0; i < VCC_HTABLE_SIZE; ++i) { |
172 | struct hlist_head *head = &vcc_hash[i]; |
173 | |
174 | sk_for_each(s, node, head) { |
175 | vcc = atm_sk(s); |
176 | if (vcc->dev != dev) |
177 | continue; |
178 | if ((vcc->vpi >> ci.vpi_bits) || |
179 | (vcc->vci >> ci.vci_bits)) { |
180 | read_unlock(&vcc_sklist_lock); |
181 | return -EBUSY; |
182 | } |
183 | } |
184 | } |
185 | read_unlock(&vcc_sklist_lock); |
186 | dev->ci_range = ci; |
187 | return 0; |
188 | } |
189 | |
190 | |
191 | static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) |
192 | { |
193 | struct atmtcp_dev_data *dev_data; |
194 | struct atm_vcc *out_vcc=NULL; /* Initializer quietens GCC warning */ |
195 | struct sk_buff *new_skb; |
196 | struct atmtcp_hdr *hdr; |
197 | int size; |
198 | |
199 | if (vcc->qos.txtp.traffic_class == ATM_NONE) { |
200 | if (vcc->pop) vcc->pop(vcc,skb); |
201 | else dev_kfree_skb(skb); |
202 | return -EINVAL; |
203 | } |
204 | dev_data = PRIV(vcc->dev); |
205 | if (dev_data) out_vcc = dev_data->vcc; |
206 | if (!dev_data || !out_vcc) { |
207 | if (vcc->pop) vcc->pop(vcc,skb); |
208 | else dev_kfree_skb(skb); |
209 | if (dev_data) return 0; |
210 | atomic_inc(&vcc->stats->tx_err); |
211 | return -ENOLINK; |
212 | } |
213 | size = skb->len+sizeof(struct atmtcp_hdr); |
214 | new_skb = atm_alloc_charge(out_vcc,size,GFP_ATOMIC); |
215 | if (!new_skb) { |
216 | if (vcc->pop) vcc->pop(vcc,skb); |
217 | else dev_kfree_skb(skb); |
218 | atomic_inc(&vcc->stats->tx_err); |
219 | return -ENOBUFS; |
220 | } |
221 | hdr = (void *) skb_put(new_skb,sizeof(struct atmtcp_hdr)); |
222 | hdr->vpi = htons(vcc->vpi); |
223 | hdr->vci = htons(vcc->vci); |
224 | hdr->length = htonl(skb->len); |
225 | skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); |
226 | if (vcc->pop) vcc->pop(vcc,skb); |
227 | else dev_kfree_skb(skb); |
228 | out_vcc->push(out_vcc,new_skb); |
229 | atomic_inc(&vcc->stats->tx); |
230 | atomic_inc(&out_vcc->stats->rx); |
231 | return 0; |
232 | } |
233 | |
234 | |
235 | static int atmtcp_v_proc(struct atm_dev *dev,loff_t *pos,char *page) |
236 | { |
237 | struct atmtcp_dev_data *dev_data = PRIV(dev); |
238 | |
239 | if (*pos) return 0; |
240 | if (!dev_data->persist) return sprintf(page,"ephemeral\n"); |
241 | return sprintf(page,"persistent, %sconnected\n", |
242 | dev_data->vcc ? "" : "dis"); |
243 | } |
244 | |
245 | |
246 | static void atmtcp_c_close(struct atm_vcc *vcc) |
247 | { |
248 | struct atm_dev *atmtcp_dev; |
249 | struct atmtcp_dev_data *dev_data; |
250 | |
251 | atmtcp_dev = (struct atm_dev *) vcc->dev_data; |
252 | dev_data = PRIV(atmtcp_dev); |
253 | dev_data->vcc = NULL; |
254 | if (dev_data->persist) return; |
255 | atmtcp_dev->dev_data = NULL; |
256 | kfree(dev_data); |
257 | atm_dev_deregister(atmtcp_dev); |
258 | vcc->dev_data = NULL; |
259 | module_put(THIS_MODULE); |
260 | } |
261 | |
262 | |
263 | static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci) |
264 | { |
265 | struct hlist_head *head; |
266 | struct atm_vcc *vcc; |
267 | struct hlist_node *node; |
268 | struct sock *s; |
269 | |
270 | head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; |
271 | |
272 | sk_for_each(s, node, head) { |
273 | vcc = atm_sk(s); |
274 | if (vcc->dev == dev && |
275 | vcc->vci == vci && vcc->vpi == vpi && |
276 | vcc->qos.rxtp.traffic_class != ATM_NONE) { |
277 | return vcc; |
278 | } |
279 | } |
280 | return NULL; |
281 | } |
282 | |
283 | |
284 | static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) |
285 | { |
286 | struct atm_dev *dev; |
287 | struct atmtcp_hdr *hdr; |
288 | struct atm_vcc *out_vcc; |
289 | struct sk_buff *new_skb; |
290 | int result = 0; |
291 | |
292 | if (!skb->len) return 0; |
293 | dev = vcc->dev_data; |
294 | hdr = (struct atmtcp_hdr *) skb->data; |
295 | if (hdr->length == ATMTCP_HDR_MAGIC) { |
296 | result = atmtcp_recv_control( |
297 | (struct atmtcp_control *) skb->data); |
298 | goto done; |
299 | } |
300 | read_lock(&vcc_sklist_lock); |
301 | out_vcc = find_vcc(dev, ntohs(hdr->vpi), ntohs(hdr->vci)); |
302 | read_unlock(&vcc_sklist_lock); |
303 | if (!out_vcc) { |
304 | atomic_inc(&vcc->stats->tx_err); |
305 | goto done; |
306 | } |
307 | skb_pull(skb,sizeof(struct atmtcp_hdr)); |
308 | new_skb = atm_alloc_charge(out_vcc,skb->len,GFP_KERNEL); |
309 | if (!new_skb) { |
310 | result = -ENOBUFS; |
311 | goto done; |
312 | } |
313 | __net_timestamp(new_skb); |
314 | skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); |
315 | out_vcc->push(out_vcc,new_skb); |
316 | atomic_inc(&vcc->stats->tx); |
317 | atomic_inc(&out_vcc->stats->rx); |
318 | done: |
319 | if (vcc->pop) vcc->pop(vcc,skb); |
320 | else dev_kfree_skb(skb); |
321 | return result; |
322 | } |
323 | |
324 | |
325 | /* |
326 | * Device operations for the virtual ATM devices created by ATMTCP. |
327 | */ |
328 | |
329 | |
330 | static struct atmdev_ops atmtcp_v_dev_ops = { |
331 | .dev_close = atmtcp_v_dev_close, |
332 | .open = atmtcp_v_open, |
333 | .close = atmtcp_v_close, |
334 | .ioctl = atmtcp_v_ioctl, |
335 | .send = atmtcp_v_send, |
336 | .proc_read = atmtcp_v_proc, |
337 | .owner = THIS_MODULE |
338 | }; |
339 | |
340 | |
341 | /* |
342 | * Device operations for the ATMTCP control device. |
343 | */ |
344 | |
345 | |
346 | static struct atmdev_ops atmtcp_c_dev_ops = { |
347 | .close = atmtcp_c_close, |
348 | .send = atmtcp_c_send |
349 | }; |
350 | |
351 | |
352 | static struct atm_dev atmtcp_control_dev = { |
353 | .ops = &atmtcp_c_dev_ops, |
354 | .type = "atmtcp", |
355 | .number = 999, |
356 | .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock) |
357 | }; |
358 | |
359 | |
360 | static int atmtcp_create(int itf,int persist,struct atm_dev **result) |
361 | { |
362 | struct atmtcp_dev_data *dev_data; |
363 | struct atm_dev *dev; |
364 | |
365 | dev_data = kmalloc(sizeof(*dev_data),GFP_KERNEL); |
366 | if (!dev_data) |
367 | return -ENOMEM; |
368 | |
369 | dev = atm_dev_register(DEV_LABEL,NULL,&atmtcp_v_dev_ops,itf,NULL); |
370 | if (!dev) { |
371 | kfree(dev_data); |
372 | return itf == -1 ? -ENOMEM : -EBUSY; |
373 | } |
374 | dev->ci_range.vpi_bits = MAX_VPI_BITS; |
375 | dev->ci_range.vci_bits = MAX_VCI_BITS; |
376 | dev->dev_data = dev_data; |
377 | PRIV(dev)->vcc = NULL; |
378 | PRIV(dev)->persist = persist; |
379 | if (result) *result = dev; |
380 | return 0; |
381 | } |
382 | |
383 | |
384 | static int atmtcp_attach(struct atm_vcc *vcc,int itf) |
385 | { |
386 | struct atm_dev *dev; |
387 | |
388 | dev = NULL; |
389 | if (itf != -1) dev = atm_dev_lookup(itf); |
390 | if (dev) { |
391 | if (dev->ops != &atmtcp_v_dev_ops) { |
392 | atm_dev_put(dev); |
393 | return -EMEDIUMTYPE; |
394 | } |
395 | if (PRIV(dev)->vcc) { |
396 | atm_dev_put(dev); |
397 | return -EBUSY; |
398 | } |
399 | } |
400 | else { |
401 | int error; |
402 | |
403 | error = atmtcp_create(itf,0,&dev); |
404 | if (error) return error; |
405 | } |
406 | PRIV(dev)->vcc = vcc; |
407 | vcc->dev = &atmtcp_control_dev; |
408 | vcc_insert_socket(sk_atm(vcc)); |
409 | set_bit(ATM_VF_META,&vcc->flags); |
410 | set_bit(ATM_VF_READY,&vcc->flags); |
411 | vcc->dev_data = dev; |
412 | (void) atm_init_aal5(vcc); /* @@@ losing AAL in transit ... */ |
413 | vcc->stats = &atmtcp_control_dev.stats.aal5; |
414 | return dev->number; |
415 | } |
416 | |
417 | |
418 | static int atmtcp_create_persistent(int itf) |
419 | { |
420 | return atmtcp_create(itf,1,NULL); |
421 | } |
422 | |
423 | |
424 | static int atmtcp_remove_persistent(int itf) |
425 | { |
426 | struct atm_dev *dev; |
427 | struct atmtcp_dev_data *dev_data; |
428 | |
429 | dev = atm_dev_lookup(itf); |
430 | if (!dev) return -ENODEV; |
431 | if (dev->ops != &atmtcp_v_dev_ops) { |
432 | atm_dev_put(dev); |
433 | return -EMEDIUMTYPE; |
434 | } |
435 | dev_data = PRIV(dev); |
436 | if (!dev_data->persist) return 0; |
437 | dev_data->persist = 0; |
438 | if (PRIV(dev)->vcc) return 0; |
439 | kfree(dev_data); |
440 | atm_dev_put(dev); |
441 | atm_dev_deregister(dev); |
442 | return 0; |
443 | } |
444 | |
445 | static int atmtcp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
446 | { |
447 | int err = 0; |
448 | struct atm_vcc *vcc = ATM_SD(sock); |
449 | |
450 | if (cmd != SIOCSIFATMTCP && cmd != ATMTCP_CREATE && cmd != ATMTCP_REMOVE) |
451 | return -ENOIOCTLCMD; |
452 | |
453 | if (!capable(CAP_NET_ADMIN)) |
454 | return -EPERM; |
455 | |
456 | switch (cmd) { |
457 | case SIOCSIFATMTCP: |
458 | err = atmtcp_attach(vcc, (int) arg); |
459 | if (err >= 0) { |
460 | sock->state = SS_CONNECTED; |
461 | __module_get(THIS_MODULE); |
462 | } |
463 | break; |
464 | case ATMTCP_CREATE: |
465 | err = atmtcp_create_persistent((int) arg); |
466 | break; |
467 | case ATMTCP_REMOVE: |
468 | err = atmtcp_remove_persistent((int) arg); |
469 | break; |
470 | } |
471 | return err; |
472 | } |
473 | |
474 | static struct atm_ioctl atmtcp_ioctl_ops = { |
475 | .owner = THIS_MODULE, |
476 | .ioctl = atmtcp_ioctl, |
477 | }; |
478 | |
479 | static __init int atmtcp_init(void) |
480 | { |
481 | register_atm_ioctl(&atmtcp_ioctl_ops); |
482 | return 0; |
483 | } |
484 | |
485 | |
486 | static void __exit atmtcp_exit(void) |
487 | { |
488 | deregister_atm_ioctl(&atmtcp_ioctl_ops); |
489 | } |
490 | |
491 | MODULE_LICENSE("GPL"); |
492 | module_init(atmtcp_init); |
493 | module_exit(atmtcp_exit); |
494 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9