diff options
author | Karsten Keil <kkeil@suse.de> | 2008-07-26 19:54:58 -0400 |
---|---|---|
committer | Karsten Keil <kkeil@suse.de> | 2008-07-26 19:54:58 -0400 |
commit | 1b2b03f8e514e4f68e293846ba511a948b80243c (patch) | |
tree | 5ffb07d532afca95170ea0615bb74af78b0d0483 /drivers/isdn/mISDN/socket.c | |
parent | 04578dd330f1ec6bc9c4233833bee0d0ca73ff09 (diff) |
Add mISDN core files
Add mISDN core files
Signed-off-by: Karsten Keil <kkeil@suse.de>
Diffstat (limited to 'drivers/isdn/mISDN/socket.c')
-rw-r--r-- | drivers/isdn/mISDN/socket.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c new file mode 100644 index 000000000000..4ba4cc364c9e --- /dev/null +++ b/drivers/isdn/mISDN/socket.c | |||
@@ -0,0 +1,781 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Author Karsten Keil <kkeil@novell.com> | ||
4 | * | ||
5 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/mISDNif.h> | ||
19 | #include "core.h" | ||
20 | |||
21 | static int *debug; | ||
22 | |||
23 | static struct proto mISDN_proto = { | ||
24 | .name = "misdn", | ||
25 | .owner = THIS_MODULE, | ||
26 | .obj_size = sizeof(struct mISDN_sock) | ||
27 | }; | ||
28 | |||
29 | #define _pms(sk) ((struct mISDN_sock *)sk) | ||
30 | |||
31 | static struct mISDN_sock_list data_sockets = { | ||
32 | .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) | ||
33 | }; | ||
34 | |||
35 | static struct mISDN_sock_list base_sockets = { | ||
36 | .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) | ||
37 | }; | ||
38 | |||
39 | #define L2_HEADER_LEN 4 | ||
40 | |||
41 | static inline struct sk_buff * | ||
42 | _l2_alloc_skb(unsigned int len, gfp_t gfp_mask) | ||
43 | { | ||
44 | struct sk_buff *skb; | ||
45 | |||
46 | skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); | ||
47 | if (likely(skb)) | ||
48 | skb_reserve(skb, L2_HEADER_LEN); | ||
49 | return skb; | ||
50 | } | ||
51 | |||
52 | static void | ||
53 | mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) | ||
54 | { | ||
55 | write_lock_bh(&l->lock); | ||
56 | sk_add_node(sk, &l->head); | ||
57 | write_unlock_bh(&l->lock); | ||
58 | } | ||
59 | |||
60 | static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) | ||
61 | { | ||
62 | write_lock_bh(&l->lock); | ||
63 | sk_del_node_init(sk); | ||
64 | write_unlock_bh(&l->lock); | ||
65 | } | ||
66 | |||
67 | static int | ||
68 | mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) | ||
69 | { | ||
70 | struct mISDN_sock *msk; | ||
71 | int err; | ||
72 | |||
73 | msk = container_of(ch, struct mISDN_sock, ch); | ||
74 | if (*debug & DEBUG_SOCKET) | ||
75 | printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); | ||
76 | if (msk->sk.sk_state == MISDN_CLOSED) | ||
77 | return -EUNATCH; | ||
78 | __net_timestamp(skb); | ||
79 | err = sock_queue_rcv_skb(&msk->sk, skb); | ||
80 | if (err) | ||
81 | printk(KERN_WARNING "%s: error %d\n", __func__, err); | ||
82 | return err; | ||
83 | } | ||
84 | |||
85 | static int | ||
86 | mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) | ||
87 | { | ||
88 | struct mISDN_sock *msk; | ||
89 | |||
90 | msk = container_of(ch, struct mISDN_sock, ch); | ||
91 | if (*debug & DEBUG_SOCKET) | ||
92 | printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); | ||
93 | switch (cmd) { | ||
94 | case CLOSE_CHANNEL: | ||
95 | msk->sk.sk_state = MISDN_CLOSED; | ||
96 | break; | ||
97 | } | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static inline void | ||
102 | mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) | ||
103 | { | ||
104 | struct timeval tv; | ||
105 | |||
106 | if (_pms(sk)->cmask & MISDN_TIME_STAMP) { | ||
107 | skb_get_timestamp(skb, &tv); | ||
108 | put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | static int | ||
113 | mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, | ||
114 | struct msghdr *msg, size_t len, int flags) | ||
115 | { | ||
116 | struct sk_buff *skb; | ||
117 | struct sock *sk = sock->sk; | ||
118 | struct sockaddr_mISDN *maddr; | ||
119 | |||
120 | int copied, err; | ||
121 | |||
122 | if (*debug & DEBUG_SOCKET) | ||
123 | printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", | ||
124 | __func__, (int)len, flags, _pms(sk)->ch.nr, | ||
125 | sk->sk_protocol); | ||
126 | if (flags & (MSG_OOB)) | ||
127 | return -EOPNOTSUPP; | ||
128 | |||
129 | if (sk->sk_state == MISDN_CLOSED) | ||
130 | return 0; | ||
131 | |||
132 | skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); | ||
133 | if (!skb) | ||
134 | return err; | ||
135 | |||
136 | if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { | ||
137 | msg->msg_namelen = sizeof(struct sockaddr_mISDN); | ||
138 | maddr = (struct sockaddr_mISDN *)msg->msg_name; | ||
139 | maddr->family = AF_ISDN; | ||
140 | maddr->dev = _pms(sk)->dev->id; | ||
141 | if ((sk->sk_protocol == ISDN_P_LAPD_TE) || | ||
142 | (sk->sk_protocol == ISDN_P_LAPD_NT)) { | ||
143 | maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; | ||
144 | maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; | ||
145 | maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; | ||
146 | } else { | ||
147 | maddr->channel = _pms(sk)->ch.nr; | ||
148 | maddr->sapi = _pms(sk)->ch.addr & 0xFF; | ||
149 | maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; | ||
150 | } | ||
151 | } else { | ||
152 | if (msg->msg_namelen) | ||
153 | printk(KERN_WARNING "%s: too small namelen %d\n", | ||
154 | __func__, msg->msg_namelen); | ||
155 | msg->msg_namelen = 0; | ||
156 | } | ||
157 | |||
158 | copied = skb->len + MISDN_HEADER_LEN; | ||
159 | if (len < copied) { | ||
160 | if (flags & MSG_PEEK) | ||
161 | atomic_dec(&skb->users); | ||
162 | else | ||
163 | skb_queue_head(&sk->sk_receive_queue, skb); | ||
164 | return -ENOSPC; | ||
165 | } | ||
166 | memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), | ||
167 | MISDN_HEADER_LEN); | ||
168 | |||
169 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); | ||
170 | |||
171 | mISDN_sock_cmsg(sk, msg, skb); | ||
172 | |||
173 | skb_free_datagram(sk, skb); | ||
174 | |||
175 | return err ? : copied; | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | ||
180 | struct msghdr *msg, size_t len) | ||
181 | { | ||
182 | struct sock *sk = sock->sk; | ||
183 | struct sk_buff *skb; | ||
184 | int err = -ENOMEM; | ||
185 | struct sockaddr_mISDN *maddr; | ||
186 | |||
187 | if (*debug & DEBUG_SOCKET) | ||
188 | printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", | ||
189 | __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, | ||
190 | sk->sk_protocol); | ||
191 | |||
192 | if (msg->msg_flags & MSG_OOB) | ||
193 | return -EOPNOTSUPP; | ||
194 | |||
195 | if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) | ||
196 | return -EINVAL; | ||
197 | |||
198 | if (len < MISDN_HEADER_LEN) | ||
199 | return -EINVAL; | ||
200 | |||
201 | if (sk->sk_state != MISDN_BOUND) | ||
202 | return -EBADFD; | ||
203 | |||
204 | lock_sock(sk); | ||
205 | |||
206 | skb = _l2_alloc_skb(len, GFP_KERNEL); | ||
207 | if (!skb) | ||
208 | goto done; | ||
209 | |||
210 | if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { | ||
211 | err = -EFAULT; | ||
212 | goto drop; | ||
213 | } | ||
214 | |||
215 | memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); | ||
216 | skb_pull(skb, MISDN_HEADER_LEN); | ||
217 | |||
218 | if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { | ||
219 | /* if we have a address, we use it */ | ||
220 | maddr = (struct sockaddr_mISDN *)msg->msg_name; | ||
221 | mISDN_HEAD_ID(skb) = maddr->channel; | ||
222 | } else { /* use default for L2 messages */ | ||
223 | if ((sk->sk_protocol == ISDN_P_LAPD_TE) || | ||
224 | (sk->sk_protocol == ISDN_P_LAPD_NT)) | ||
225 | mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; | ||
226 | } | ||
227 | |||
228 | if (*debug & DEBUG_SOCKET) | ||
229 | printk(KERN_DEBUG "%s: ID:%x\n", | ||
230 | __func__, mISDN_HEAD_ID(skb)); | ||
231 | |||
232 | err = -ENODEV; | ||
233 | if (!_pms(sk)->ch.peer || | ||
234 | (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb))) | ||
235 | goto drop; | ||
236 | |||
237 | err = len; | ||
238 | |||
239 | done: | ||
240 | release_sock(sk); | ||
241 | return err; | ||
242 | |||
243 | drop: | ||
244 | kfree_skb(skb); | ||
245 | goto done; | ||
246 | } | ||
247 | |||
248 | static int | ||
249 | data_sock_release(struct socket *sock) | ||
250 | { | ||
251 | struct sock *sk = sock->sk; | ||
252 | |||
253 | if (*debug & DEBUG_SOCKET) | ||
254 | printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); | ||
255 | if (!sk) | ||
256 | return 0; | ||
257 | switch (sk->sk_protocol) { | ||
258 | case ISDN_P_TE_S0: | ||
259 | case ISDN_P_NT_S0: | ||
260 | case ISDN_P_TE_E1: | ||
261 | case ISDN_P_NT_E1: | ||
262 | if (sk->sk_state == MISDN_BOUND) | ||
263 | delete_channel(&_pms(sk)->ch); | ||
264 | else | ||
265 | mISDN_sock_unlink(&data_sockets, sk); | ||
266 | break; | ||
267 | case ISDN_P_LAPD_TE: | ||
268 | case ISDN_P_LAPD_NT: | ||
269 | case ISDN_P_B_RAW: | ||
270 | case ISDN_P_B_HDLC: | ||
271 | case ISDN_P_B_X75SLP: | ||
272 | case ISDN_P_B_L2DTMF: | ||
273 | case ISDN_P_B_L2DSP: | ||
274 | case ISDN_P_B_L2DSPHDLC: | ||
275 | delete_channel(&_pms(sk)->ch); | ||
276 | mISDN_sock_unlink(&data_sockets, sk); | ||
277 | break; | ||
278 | } | ||
279 | |||
280 | lock_sock(sk); | ||
281 | |||
282 | sock_orphan(sk); | ||
283 | skb_queue_purge(&sk->sk_receive_queue); | ||
284 | |||
285 | release_sock(sk); | ||
286 | sock_put(sk); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int | ||
292 | data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) | ||
293 | { | ||
294 | struct mISDN_ctrl_req cq; | ||
295 | int err = -EINVAL, val; | ||
296 | struct mISDNchannel *bchan, *next; | ||
297 | |||
298 | lock_sock(sk); | ||
299 | if (!_pms(sk)->dev) { | ||
300 | err = -ENODEV; | ||
301 | goto done; | ||
302 | } | ||
303 | switch (cmd) { | ||
304 | case IMCTRLREQ: | ||
305 | if (copy_from_user(&cq, p, sizeof(cq))) { | ||
306 | err = -EFAULT; | ||
307 | break; | ||
308 | } | ||
309 | if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { | ||
310 | list_for_each_entry_safe(bchan, next, | ||
311 | &_pms(sk)->dev->bchannels, list) { | ||
312 | if (bchan->nr == cq.channel) { | ||
313 | err = bchan->ctrl(bchan, | ||
314 | CONTROL_CHANNEL, &cq); | ||
315 | break; | ||
316 | } | ||
317 | } | ||
318 | } else | ||
319 | err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, | ||
320 | CONTROL_CHANNEL, &cq); | ||
321 | if (err) | ||
322 | break; | ||
323 | if (copy_to_user(p, &cq, sizeof(cq))) | ||
324 | err = -EFAULT; | ||
325 | break; | ||
326 | case IMCLEAR_L2: | ||
327 | if (sk->sk_protocol != ISDN_P_LAPD_NT) { | ||
328 | err = -EINVAL; | ||
329 | break; | ||
330 | } | ||
331 | if (get_user(val, (int __user *)p)) { | ||
332 | err = -EFAULT; | ||
333 | break; | ||
334 | } | ||
335 | err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, | ||
336 | CONTROL_CHANNEL, &val); | ||
337 | break; | ||
338 | default: | ||
339 | err = -EINVAL; | ||
340 | break; | ||
341 | } | ||
342 | done: | ||
343 | release_sock(sk); | ||
344 | return err; | ||
345 | } | ||
346 | |||
347 | static int | ||
348 | data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
349 | { | ||
350 | int err = 0, id; | ||
351 | struct sock *sk = sock->sk; | ||
352 | struct mISDNdevice *dev; | ||
353 | struct mISDNversion ver; | ||
354 | |||
355 | switch (cmd) { | ||
356 | case IMGETVERSION: | ||
357 | ver.major = MISDN_MAJOR_VERSION; | ||
358 | ver.minor = MISDN_MINOR_VERSION; | ||
359 | ver.release = MISDN_RELEASE; | ||
360 | if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) | ||
361 | err = -EFAULT; | ||
362 | break; | ||
363 | case IMGETCOUNT: | ||
364 | id = get_mdevice_count(); | ||
365 | if (put_user(id, (int __user *)arg)) | ||
366 | err = -EFAULT; | ||
367 | break; | ||
368 | case IMGETDEVINFO: | ||
369 | if (get_user(id, (int __user *)arg)) { | ||
370 | err = -EFAULT; | ||
371 | break; | ||
372 | } | ||
373 | dev = get_mdevice(id); | ||
374 | if (dev) { | ||
375 | struct mISDN_devinfo di; | ||
376 | |||
377 | di.id = dev->id; | ||
378 | di.Dprotocols = dev->Dprotocols; | ||
379 | di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); | ||
380 | di.protocol = dev->D.protocol; | ||
381 | memcpy(di.channelmap, dev->channelmap, | ||
382 | MISDN_CHMAP_SIZE * 4); | ||
383 | di.nrbchan = dev->nrbchan; | ||
384 | strcpy(di.name, dev->name); | ||
385 | if (copy_to_user((void __user *)arg, &di, sizeof(di))) | ||
386 | err = -EFAULT; | ||
387 | } else | ||
388 | err = -ENODEV; | ||
389 | break; | ||
390 | default: | ||
391 | if (sk->sk_state == MISDN_BOUND) | ||
392 | err = data_sock_ioctl_bound(sk, cmd, | ||
393 | (void __user *)arg); | ||
394 | else | ||
395 | err = -ENOTCONN; | ||
396 | } | ||
397 | return err; | ||
398 | } | ||
399 | |||
400 | static int data_sock_setsockopt(struct socket *sock, int level, int optname, | ||
401 | char __user *optval, int len) | ||
402 | { | ||
403 | struct sock *sk = sock->sk; | ||
404 | int err = 0, opt = 0; | ||
405 | |||
406 | if (*debug & DEBUG_SOCKET) | ||
407 | printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock, | ||
408 | level, optname, optval, len); | ||
409 | |||
410 | lock_sock(sk); | ||
411 | |||
412 | switch (optname) { | ||
413 | case MISDN_TIME_STAMP: | ||
414 | if (get_user(opt, (int __user *)optval)) { | ||
415 | err = -EFAULT; | ||
416 | break; | ||
417 | } | ||
418 | |||
419 | if (opt) | ||
420 | _pms(sk)->cmask |= MISDN_TIME_STAMP; | ||
421 | else | ||
422 | _pms(sk)->cmask &= ~MISDN_TIME_STAMP; | ||
423 | break; | ||
424 | default: | ||
425 | err = -ENOPROTOOPT; | ||
426 | break; | ||
427 | } | ||
428 | release_sock(sk); | ||
429 | return err; | ||
430 | } | ||
431 | |||
432 | static int data_sock_getsockopt(struct socket *sock, int level, int optname, | ||
433 | char __user *optval, int __user *optlen) | ||
434 | { | ||
435 | struct sock *sk = sock->sk; | ||
436 | int len, opt; | ||
437 | |||
438 | if (get_user(len, optlen)) | ||
439 | return -EFAULT; | ||
440 | |||
441 | switch (optname) { | ||
442 | case MISDN_TIME_STAMP: | ||
443 | if (_pms(sk)->cmask & MISDN_TIME_STAMP) | ||
444 | opt = 1; | ||
445 | else | ||
446 | opt = 0; | ||
447 | |||
448 | if (put_user(opt, optval)) | ||
449 | return -EFAULT; | ||
450 | break; | ||
451 | default: | ||
452 | return -ENOPROTOOPT; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int | ||
459 | data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) | ||
460 | { | ||
461 | struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; | ||
462 | struct sock *sk = sock->sk; | ||
463 | int err = 0; | ||
464 | |||
465 | if (*debug & DEBUG_SOCKET) | ||
466 | printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); | ||
467 | if (addr_len != sizeof(struct sockaddr_mISDN)) | ||
468 | return -EINVAL; | ||
469 | if (!maddr || maddr->family != AF_ISDN) | ||
470 | return -EINVAL; | ||
471 | |||
472 | lock_sock(sk); | ||
473 | |||
474 | if (_pms(sk)->dev) { | ||
475 | err = -EALREADY; | ||
476 | goto done; | ||
477 | } | ||
478 | _pms(sk)->dev = get_mdevice(maddr->dev); | ||
479 | if (!_pms(sk)->dev) { | ||
480 | err = -ENODEV; | ||
481 | goto done; | ||
482 | } | ||
483 | _pms(sk)->ch.send = mISDN_send; | ||
484 | _pms(sk)->ch.ctrl = mISDN_ctrl; | ||
485 | |||
486 | switch (sk->sk_protocol) { | ||
487 | case ISDN_P_TE_S0: | ||
488 | case ISDN_P_NT_S0: | ||
489 | case ISDN_P_TE_E1: | ||
490 | case ISDN_P_NT_E1: | ||
491 | mISDN_sock_unlink(&data_sockets, sk); | ||
492 | err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, | ||
493 | sk->sk_protocol, maddr); | ||
494 | if (err) | ||
495 | mISDN_sock_link(&data_sockets, sk); | ||
496 | break; | ||
497 | case ISDN_P_LAPD_TE: | ||
498 | case ISDN_P_LAPD_NT: | ||
499 | err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, | ||
500 | sk->sk_protocol, maddr); | ||
501 | break; | ||
502 | case ISDN_P_B_RAW: | ||
503 | case ISDN_P_B_HDLC: | ||
504 | case ISDN_P_B_X75SLP: | ||
505 | case ISDN_P_B_L2DTMF: | ||
506 | case ISDN_P_B_L2DSP: | ||
507 | case ISDN_P_B_L2DSPHDLC: | ||
508 | err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, | ||
509 | sk->sk_protocol, maddr); | ||
510 | break; | ||
511 | default: | ||
512 | err = -EPROTONOSUPPORT; | ||
513 | } | ||
514 | if (err) | ||
515 | goto done; | ||
516 | sk->sk_state = MISDN_BOUND; | ||
517 | _pms(sk)->ch.protocol = sk->sk_protocol; | ||
518 | |||
519 | done: | ||
520 | release_sock(sk); | ||
521 | return err; | ||
522 | } | ||
523 | |||
524 | static int | ||
525 | data_sock_getname(struct socket *sock, struct sockaddr *addr, | ||
526 | int *addr_len, int peer) | ||
527 | { | ||
528 | struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; | ||
529 | struct sock *sk = sock->sk; | ||
530 | |||
531 | if (!_pms(sk)->dev) | ||
532 | return -EBADFD; | ||
533 | |||
534 | lock_sock(sk); | ||
535 | |||
536 | *addr_len = sizeof(*maddr); | ||
537 | maddr->dev = _pms(sk)->dev->id; | ||
538 | maddr->channel = _pms(sk)->ch.nr; | ||
539 | maddr->sapi = _pms(sk)->ch.addr & 0xff; | ||
540 | maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; | ||
541 | release_sock(sk); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static const struct proto_ops data_sock_ops = { | ||
546 | .family = PF_ISDN, | ||
547 | .owner = THIS_MODULE, | ||
548 | .release = data_sock_release, | ||
549 | .ioctl = data_sock_ioctl, | ||
550 | .bind = data_sock_bind, | ||
551 | .getname = data_sock_getname, | ||
552 | .sendmsg = mISDN_sock_sendmsg, | ||
553 | .recvmsg = mISDN_sock_recvmsg, | ||
554 | .poll = datagram_poll, | ||
555 | .listen = sock_no_listen, | ||
556 | .shutdown = sock_no_shutdown, | ||
557 | .setsockopt = data_sock_setsockopt, | ||
558 | .getsockopt = data_sock_getsockopt, | ||
559 | .connect = sock_no_connect, | ||
560 | .socketpair = sock_no_socketpair, | ||
561 | .accept = sock_no_accept, | ||
562 | .mmap = sock_no_mmap | ||
563 | }; | ||
564 | |||
565 | static int | ||
566 | data_sock_create(struct net *net, struct socket *sock, int protocol) | ||
567 | { | ||
568 | struct sock *sk; | ||
569 | |||
570 | if (sock->type != SOCK_DGRAM) | ||
571 | return -ESOCKTNOSUPPORT; | ||
572 | |||
573 | sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); | ||
574 | if (!sk) | ||
575 | return -ENOMEM; | ||
576 | |||
577 | sock_init_data(sock, sk); | ||
578 | |||
579 | sock->ops = &data_sock_ops; | ||
580 | sock->state = SS_UNCONNECTED; | ||
581 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
582 | |||
583 | sk->sk_protocol = protocol; | ||
584 | sk->sk_state = MISDN_OPEN; | ||
585 | mISDN_sock_link(&data_sockets, sk); | ||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | static int | ||
591 | base_sock_release(struct socket *sock) | ||
592 | { | ||
593 | struct sock *sk = sock->sk; | ||
594 | |||
595 | printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); | ||
596 | if (!sk) | ||
597 | return 0; | ||
598 | |||
599 | mISDN_sock_unlink(&base_sockets, sk); | ||
600 | sock_orphan(sk); | ||
601 | sock_put(sk); | ||
602 | |||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | static int | ||
607 | base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
608 | { | ||
609 | int err = 0, id; | ||
610 | struct mISDNdevice *dev; | ||
611 | struct mISDNversion ver; | ||
612 | |||
613 | switch (cmd) { | ||
614 | case IMGETVERSION: | ||
615 | ver.major = MISDN_MAJOR_VERSION; | ||
616 | ver.minor = MISDN_MINOR_VERSION; | ||
617 | ver.release = MISDN_RELEASE; | ||
618 | if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) | ||
619 | err = -EFAULT; | ||
620 | break; | ||
621 | case IMGETCOUNT: | ||
622 | id = get_mdevice_count(); | ||
623 | if (put_user(id, (int __user *)arg)) | ||
624 | err = -EFAULT; | ||
625 | break; | ||
626 | case IMGETDEVINFO: | ||
627 | if (get_user(id, (int __user *)arg)) { | ||
628 | err = -EFAULT; | ||
629 | break; | ||
630 | } | ||
631 | dev = get_mdevice(id); | ||
632 | if (dev) { | ||
633 | struct mISDN_devinfo di; | ||
634 | |||
635 | di.id = dev->id; | ||
636 | di.Dprotocols = dev->Dprotocols; | ||
637 | di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); | ||
638 | di.protocol = dev->D.protocol; | ||
639 | memcpy(di.channelmap, dev->channelmap, | ||
640 | MISDN_CHMAP_SIZE * 4); | ||
641 | di.nrbchan = dev->nrbchan; | ||
642 | strcpy(di.name, dev->name); | ||
643 | if (copy_to_user((void __user *)arg, &di, sizeof(di))) | ||
644 | err = -EFAULT; | ||
645 | } else | ||
646 | err = -ENODEV; | ||
647 | break; | ||
648 | default: | ||
649 | err = -EINVAL; | ||
650 | } | ||
651 | return err; | ||
652 | } | ||
653 | |||
654 | static int | ||
655 | base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) | ||
656 | { | ||
657 | struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; | ||
658 | struct sock *sk = sock->sk; | ||
659 | int err = 0; | ||
660 | |||
661 | if (!maddr || maddr->family != AF_ISDN) | ||
662 | return -EINVAL; | ||
663 | |||
664 | lock_sock(sk); | ||
665 | |||
666 | if (_pms(sk)->dev) { | ||
667 | err = -EALREADY; | ||
668 | goto done; | ||
669 | } | ||
670 | |||
671 | _pms(sk)->dev = get_mdevice(maddr->dev); | ||
672 | if (!_pms(sk)->dev) { | ||
673 | err = -ENODEV; | ||
674 | goto done; | ||
675 | } | ||
676 | sk->sk_state = MISDN_BOUND; | ||
677 | |||
678 | done: | ||
679 | release_sock(sk); | ||
680 | return err; | ||
681 | } | ||
682 | |||
683 | static const struct proto_ops base_sock_ops = { | ||
684 | .family = PF_ISDN, | ||
685 | .owner = THIS_MODULE, | ||
686 | .release = base_sock_release, | ||
687 | .ioctl = base_sock_ioctl, | ||
688 | .bind = base_sock_bind, | ||
689 | .getname = sock_no_getname, | ||
690 | .sendmsg = sock_no_sendmsg, | ||
691 | .recvmsg = sock_no_recvmsg, | ||
692 | .poll = sock_no_poll, | ||
693 | .listen = sock_no_listen, | ||
694 | .shutdown = sock_no_shutdown, | ||
695 | .setsockopt = sock_no_setsockopt, | ||
696 | .getsockopt = sock_no_getsockopt, | ||
697 | .connect = sock_no_connect, | ||
698 | .socketpair = sock_no_socketpair, | ||
699 | .accept = sock_no_accept, | ||
700 | .mmap = sock_no_mmap | ||
701 | }; | ||
702 | |||
703 | |||
704 | static int | ||
705 | base_sock_create(struct net *net, struct socket *sock, int protocol) | ||
706 | { | ||
707 | struct sock *sk; | ||
708 | |||
709 | if (sock->type != SOCK_RAW) | ||
710 | return -ESOCKTNOSUPPORT; | ||
711 | |||
712 | sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); | ||
713 | if (!sk) | ||
714 | return -ENOMEM; | ||
715 | |||
716 | sock_init_data(sock, sk); | ||
717 | sock->ops = &base_sock_ops; | ||
718 | sock->state = SS_UNCONNECTED; | ||
719 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
720 | sk->sk_protocol = protocol; | ||
721 | sk->sk_state = MISDN_OPEN; | ||
722 | mISDN_sock_link(&base_sockets, sk); | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static int | ||
728 | mISDN_sock_create(struct net *net, struct socket *sock, int proto) | ||
729 | { | ||
730 | int err = -EPROTONOSUPPORT; | ||
731 | |||
732 | switch (proto) { | ||
733 | case ISDN_P_BASE: | ||
734 | err = base_sock_create(net, sock, proto); | ||
735 | break; | ||
736 | case ISDN_P_TE_S0: | ||
737 | case ISDN_P_NT_S0: | ||
738 | case ISDN_P_TE_E1: | ||
739 | case ISDN_P_NT_E1: | ||
740 | case ISDN_P_LAPD_TE: | ||
741 | case ISDN_P_LAPD_NT: | ||
742 | case ISDN_P_B_RAW: | ||
743 | case ISDN_P_B_HDLC: | ||
744 | case ISDN_P_B_X75SLP: | ||
745 | case ISDN_P_B_L2DTMF: | ||
746 | case ISDN_P_B_L2DSP: | ||
747 | case ISDN_P_B_L2DSPHDLC: | ||
748 | err = data_sock_create(net, sock, proto); | ||
749 | break; | ||
750 | default: | ||
751 | return err; | ||
752 | } | ||
753 | |||
754 | return err; | ||
755 | } | ||
756 | |||
757 | static struct | ||
758 | net_proto_family mISDN_sock_family_ops = { | ||
759 | .owner = THIS_MODULE, | ||
760 | .family = PF_ISDN, | ||
761 | .create = mISDN_sock_create, | ||
762 | }; | ||
763 | |||
764 | int | ||
765 | misdn_sock_init(u_int *deb) | ||
766 | { | ||
767 | int err; | ||
768 | |||
769 | debug = deb; | ||
770 | err = sock_register(&mISDN_sock_family_ops); | ||
771 | if (err) | ||
772 | printk(KERN_ERR "%s: error(%d)\n", __func__, err); | ||
773 | return err; | ||
774 | } | ||
775 | |||
776 | void | ||
777 | misdn_sock_cleanup(void) | ||
778 | { | ||
779 | sock_unregister(PF_ISDN); | ||
780 | } | ||
781 | |||