xref: /openbmc/linux/drivers/isdn/mISDN/socket.c (revision 1760371b277718062211fc7eb6f3042c5051c1a5)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21b2b03f8SKarsten Keil /*
31b2b03f8SKarsten Keil  *
41b2b03f8SKarsten Keil  * Author	Karsten Keil <kkeil@novell.com>
51b2b03f8SKarsten Keil  *
61b2b03f8SKarsten Keil  * Copyright 2008  by Karsten Keil <kkeil@novell.com>
71b2b03f8SKarsten Keil  */
81b2b03f8SKarsten Keil 
91b2b03f8SKarsten Keil #include <linux/mISDNif.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
115d76fc21SPaul Gortmaker #include <linux/export.h>
121b2b03f8SKarsten Keil #include "core.h"
131b2b03f8SKarsten Keil 
14dfa96ec1SHannes Eder static u_int	*debug;
151b2b03f8SKarsten Keil 
161b2b03f8SKarsten Keil static struct proto mISDN_proto = {
171b2b03f8SKarsten Keil 	.name		= "misdn",
181b2b03f8SKarsten Keil 	.owner		= THIS_MODULE,
191b2b03f8SKarsten Keil 	.obj_size	= sizeof(struct mISDN_sock)
201b2b03f8SKarsten Keil };
211b2b03f8SKarsten Keil 
221b2b03f8SKarsten Keil #define _pms(sk)	((struct mISDN_sock *)sk)
231b2b03f8SKarsten Keil 
241b2b03f8SKarsten Keil static struct mISDN_sock_list	data_sockets = {
251b2b03f8SKarsten Keil 	.lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
261b2b03f8SKarsten Keil };
271b2b03f8SKarsten Keil 
281b2b03f8SKarsten Keil static struct mISDN_sock_list	base_sockets = {
291b2b03f8SKarsten Keil 	.lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
301b2b03f8SKarsten Keil };
311b2b03f8SKarsten Keil 
321b2b03f8SKarsten Keil #define L2_HEADER_LEN	4
331b2b03f8SKarsten Keil 
341b2b03f8SKarsten Keil static inline struct sk_buff *
_l2_alloc_skb(unsigned int len,gfp_t gfp_mask)351b2b03f8SKarsten Keil _l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
361b2b03f8SKarsten Keil {
371b2b03f8SKarsten Keil 	struct sk_buff  *skb;
381b2b03f8SKarsten Keil 
391b2b03f8SKarsten Keil 	skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
401b2b03f8SKarsten Keil 	if (likely(skb))
411b2b03f8SKarsten Keil 		skb_reserve(skb, L2_HEADER_LEN);
421b2b03f8SKarsten Keil 	return skb;
431b2b03f8SKarsten Keil }
441b2b03f8SKarsten Keil 
451b2b03f8SKarsten Keil static void
mISDN_sock_link(struct mISDN_sock_list * l,struct sock * sk)461b2b03f8SKarsten Keil mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
471b2b03f8SKarsten Keil {
481b2b03f8SKarsten Keil 	write_lock_bh(&l->lock);
491b2b03f8SKarsten Keil 	sk_add_node(sk, &l->head);
501b2b03f8SKarsten Keil 	write_unlock_bh(&l->lock);
511b2b03f8SKarsten Keil }
521b2b03f8SKarsten Keil 
mISDN_sock_unlink(struct mISDN_sock_list * l,struct sock * sk)531b2b03f8SKarsten Keil static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
541b2b03f8SKarsten Keil {
551b2b03f8SKarsten Keil 	write_lock_bh(&l->lock);
561b2b03f8SKarsten Keil 	sk_del_node_init(sk);
571b2b03f8SKarsten Keil 	write_unlock_bh(&l->lock);
581b2b03f8SKarsten Keil }
591b2b03f8SKarsten Keil 
601b2b03f8SKarsten Keil static int
mISDN_send(struct mISDNchannel * ch,struct sk_buff * skb)611b2b03f8SKarsten Keil mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
621b2b03f8SKarsten Keil {
631b2b03f8SKarsten Keil 	struct mISDN_sock *msk;
641b2b03f8SKarsten Keil 	int	err;
651b2b03f8SKarsten Keil 
661b2b03f8SKarsten Keil 	msk = container_of(ch, struct mISDN_sock, ch);
671b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
681b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
691b2b03f8SKarsten Keil 	if (msk->sk.sk_state == MISDN_CLOSED)
701b2b03f8SKarsten Keil 		return -EUNATCH;
711b2b03f8SKarsten Keil 	__net_timestamp(skb);
721b2b03f8SKarsten Keil 	err = sock_queue_rcv_skb(&msk->sk, skb);
731b2b03f8SKarsten Keil 	if (err)
741b2b03f8SKarsten Keil 		printk(KERN_WARNING "%s: error %d\n", __func__, err);
751b2b03f8SKarsten Keil 	return err;
761b2b03f8SKarsten Keil }
771b2b03f8SKarsten Keil 
781b2b03f8SKarsten Keil static int
mISDN_ctrl(struct mISDNchannel * ch,u_int cmd,void * arg)791b2b03f8SKarsten Keil mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
801b2b03f8SKarsten Keil {
811b2b03f8SKarsten Keil 	struct mISDN_sock *msk;
821b2b03f8SKarsten Keil 
831b2b03f8SKarsten Keil 	msk = container_of(ch, struct mISDN_sock, ch);
841b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
851b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
861b2b03f8SKarsten Keil 	switch (cmd) {
871b2b03f8SKarsten Keil 	case CLOSE_CHANNEL:
881b2b03f8SKarsten Keil 		msk->sk.sk_state = MISDN_CLOSED;
891b2b03f8SKarsten Keil 		break;
901b2b03f8SKarsten Keil 	}
911b2b03f8SKarsten Keil 	return 0;
921b2b03f8SKarsten Keil }
931b2b03f8SKarsten Keil 
941b2b03f8SKarsten Keil static inline void
mISDN_sock_cmsg(struct sock * sk,struct msghdr * msg,struct sk_buff * skb)951b2b03f8SKarsten Keil mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
961b2b03f8SKarsten Keil {
9713c6ee2aSDeepa Dinamani 	struct __kernel_old_timeval	tv;
981b2b03f8SKarsten Keil 
991b2b03f8SKarsten Keil 	if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
1001b2b03f8SKarsten Keil 		skb_get_timestamp(skb, &tv);
1011b2b03f8SKarsten Keil 		put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
1021b2b03f8SKarsten Keil 	}
1031b2b03f8SKarsten Keil }
1041b2b03f8SKarsten Keil 
1051b2b03f8SKarsten Keil static int
mISDN_sock_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)1061b784140SYing Xue mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
1071b784140SYing Xue 		   int flags)
1081b2b03f8SKarsten Keil {
1091b2b03f8SKarsten Keil 	struct sk_buff		*skb;
1101b2b03f8SKarsten Keil 	struct sock		*sk = sock->sk;
1111b2b03f8SKarsten Keil 
1121b2b03f8SKarsten Keil 	int		copied, err;
1131b2b03f8SKarsten Keil 
1141b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
1151b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
1161b2b03f8SKarsten Keil 		       __func__, (int)len, flags, _pms(sk)->ch.nr,
1171b2b03f8SKarsten Keil 		       sk->sk_protocol);
1181b2b03f8SKarsten Keil 	if (flags & (MSG_OOB))
1191b2b03f8SKarsten Keil 		return -EOPNOTSUPP;
1201b2b03f8SKarsten Keil 
1211b2b03f8SKarsten Keil 	if (sk->sk_state == MISDN_CLOSED)
1221b2b03f8SKarsten Keil 		return 0;
1231b2b03f8SKarsten Keil 
124f4b41f06SOliver Hartkopp 	skb = skb_recv_datagram(sk, flags, &err);
1251b2b03f8SKarsten Keil 	if (!skb)
1261b2b03f8SKarsten Keil 		return err;
1271b2b03f8SKarsten Keil 
128f3d33426SHannes Frederic Sowa 	if (msg->msg_name) {
129342dfc30SSteffen Hurrle 		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
130f3d33426SHannes Frederic Sowa 
1311b2b03f8SKarsten Keil 		maddr->family = AF_ISDN;
1321b2b03f8SKarsten Keil 		maddr->dev = _pms(sk)->dev->id;
1331b2b03f8SKarsten Keil 		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
1341b2b03f8SKarsten Keil 		    (sk->sk_protocol == ISDN_P_LAPD_NT)) {
1351b2b03f8SKarsten Keil 			maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
1361b2b03f8SKarsten Keil 			maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
1371b2b03f8SKarsten Keil 			maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
1381b2b03f8SKarsten Keil 		} else {
1391b2b03f8SKarsten Keil 			maddr->channel = _pms(sk)->ch.nr;
1401b2b03f8SKarsten Keil 			maddr->sapi = _pms(sk)->ch.addr & 0xFF;
1411b2b03f8SKarsten Keil 			maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
1421b2b03f8SKarsten Keil 		}
143f3d33426SHannes Frederic Sowa 		msg->msg_namelen = sizeof(*maddr);
1441b2b03f8SKarsten Keil 	}
1451b2b03f8SKarsten Keil 
1461b2b03f8SKarsten Keil 	copied = skb->len + MISDN_HEADER_LEN;
1471b2b03f8SKarsten Keil 	if (len < copied) {
1481b2b03f8SKarsten Keil 		if (flags & MSG_PEEK)
14963354797SReshetova, Elena 			refcount_dec(&skb->users);
1501b2b03f8SKarsten Keil 		else
1511b2b03f8SKarsten Keil 			skb_queue_head(&sk->sk_receive_queue, skb);
1521b2b03f8SKarsten Keil 		return -ENOSPC;
1531b2b03f8SKarsten Keil 	}
1541b2b03f8SKarsten Keil 	memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
1551b2b03f8SKarsten Keil 	       MISDN_HEADER_LEN);
1561b2b03f8SKarsten Keil 
15751f3d02bSDavid S. Miller 	err = skb_copy_datagram_msg(skb, 0, msg, copied);
1581b2b03f8SKarsten Keil 
1591b2b03f8SKarsten Keil 	mISDN_sock_cmsg(sk, msg, skb);
1601b2b03f8SKarsten Keil 
1611b2b03f8SKarsten Keil 	skb_free_datagram(sk, skb);
1621b2b03f8SKarsten Keil 
1631b2b03f8SKarsten Keil 	return err ? : copied;
1641b2b03f8SKarsten Keil }
1651b2b03f8SKarsten Keil 
1661b2b03f8SKarsten Keil static int
mISDN_sock_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)1671b784140SYing Xue mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
1681b2b03f8SKarsten Keil {
1691b2b03f8SKarsten Keil 	struct sock		*sk = sock->sk;
1701b2b03f8SKarsten Keil 	struct sk_buff		*skb;
1711b2b03f8SKarsten Keil 	int			err = -ENOMEM;
1721b2b03f8SKarsten Keil 
1731b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
1741b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
1751b2b03f8SKarsten Keil 		       __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
1761b2b03f8SKarsten Keil 		       sk->sk_protocol);
1771b2b03f8SKarsten Keil 
1781b2b03f8SKarsten Keil 	if (msg->msg_flags & MSG_OOB)
1791b2b03f8SKarsten Keil 		return -EOPNOTSUPP;
1801b2b03f8SKarsten Keil 
1811b2b03f8SKarsten Keil 	if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE))
1821b2b03f8SKarsten Keil 		return -EINVAL;
1831b2b03f8SKarsten Keil 
1841b2b03f8SKarsten Keil 	if (len < MISDN_HEADER_LEN)
1851b2b03f8SKarsten Keil 		return -EINVAL;
1861b2b03f8SKarsten Keil 
1871b2b03f8SKarsten Keil 	if (sk->sk_state != MISDN_BOUND)
1881b2b03f8SKarsten Keil 		return -EBADFD;
1891b2b03f8SKarsten Keil 
1901b2b03f8SKarsten Keil 	lock_sock(sk);
1911b2b03f8SKarsten Keil 
1921b2b03f8SKarsten Keil 	skb = _l2_alloc_skb(len, GFP_KERNEL);
1931b2b03f8SKarsten Keil 	if (!skb)
1941b2b03f8SKarsten Keil 		goto done;
1951b2b03f8SKarsten Keil 
1966ce8e9ceSAl Viro 	if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
1971b2b03f8SKarsten Keil 		err = -EFAULT;
1985df3b8bcSKarsten Keil 		goto done;
1991b2b03f8SKarsten Keil 	}
2001b2b03f8SKarsten Keil 
2011b2b03f8SKarsten Keil 	memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
2021b2b03f8SKarsten Keil 	skb_pull(skb, MISDN_HEADER_LEN);
2031b2b03f8SKarsten Keil 
2041b2b03f8SKarsten Keil 	if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
2051b2b03f8SKarsten Keil 		/* if we have a address, we use it */
206342dfc30SSteffen Hurrle 		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
2071b2b03f8SKarsten Keil 		mISDN_HEAD_ID(skb) = maddr->channel;
2081b2b03f8SKarsten Keil 	} else { /* use default for L2 messages */
2091b2b03f8SKarsten Keil 		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
2101b2b03f8SKarsten Keil 		    (sk->sk_protocol == ISDN_P_LAPD_NT))
2111b2b03f8SKarsten Keil 			mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
2121b2b03f8SKarsten Keil 	}
2131b2b03f8SKarsten Keil 
2141b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
2151b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s: ID:%x\n",
2161b2b03f8SKarsten Keil 		       __func__, mISDN_HEAD_ID(skb));
2171b2b03f8SKarsten Keil 
2181b2b03f8SKarsten Keil 	err = -ENODEV;
2195df3b8bcSKarsten Keil 	if (!_pms(sk)->ch.peer)
2205df3b8bcSKarsten Keil 		goto done;
2215df3b8bcSKarsten Keil 	err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb);
2225df3b8bcSKarsten Keil 	if (err)
2235df3b8bcSKarsten Keil 		goto done;
2245df3b8bcSKarsten Keil 	else {
2255df3b8bcSKarsten Keil 		skb = NULL;
2261b2b03f8SKarsten Keil 		err = len;
2275df3b8bcSKarsten Keil 	}
2281b2b03f8SKarsten Keil 
2291b2b03f8SKarsten Keil done:
2305df3b8bcSKarsten Keil 	kfree_skb(skb);
2311b2b03f8SKarsten Keil 	release_sock(sk);
2321b2b03f8SKarsten Keil 	return err;
2331b2b03f8SKarsten Keil }
2341b2b03f8SKarsten Keil 
2351b2b03f8SKarsten Keil static int
data_sock_release(struct socket * sock)2361b2b03f8SKarsten Keil data_sock_release(struct socket *sock)
2371b2b03f8SKarsten Keil {
2381b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
2391b2b03f8SKarsten Keil 
2401b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
2411b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
2421b2b03f8SKarsten Keil 	if (!sk)
2431b2b03f8SKarsten Keil 		return 0;
2441b2b03f8SKarsten Keil 	switch (sk->sk_protocol) {
2451b2b03f8SKarsten Keil 	case ISDN_P_TE_S0:
2461b2b03f8SKarsten Keil 	case ISDN_P_NT_S0:
2471b2b03f8SKarsten Keil 	case ISDN_P_TE_E1:
2481b2b03f8SKarsten Keil 	case ISDN_P_NT_E1:
2491b2b03f8SKarsten Keil 		if (sk->sk_state == MISDN_BOUND)
2501b2b03f8SKarsten Keil 			delete_channel(&_pms(sk)->ch);
2511b2b03f8SKarsten Keil 		else
2521b2b03f8SKarsten Keil 			mISDN_sock_unlink(&data_sockets, sk);
2531b2b03f8SKarsten Keil 		break;
2541b2b03f8SKarsten Keil 	case ISDN_P_LAPD_TE:
2551b2b03f8SKarsten Keil 	case ISDN_P_LAPD_NT:
2561b2b03f8SKarsten Keil 	case ISDN_P_B_RAW:
2571b2b03f8SKarsten Keil 	case ISDN_P_B_HDLC:
2581b2b03f8SKarsten Keil 	case ISDN_P_B_X75SLP:
2591b2b03f8SKarsten Keil 	case ISDN_P_B_L2DTMF:
2601b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSP:
2611b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSPHDLC:
2621b2b03f8SKarsten Keil 		delete_channel(&_pms(sk)->ch);
2631b2b03f8SKarsten Keil 		mISDN_sock_unlink(&data_sockets, sk);
2641b2b03f8SKarsten Keil 		break;
2651b2b03f8SKarsten Keil 	}
2661b2b03f8SKarsten Keil 
2671b2b03f8SKarsten Keil 	lock_sock(sk);
2681b2b03f8SKarsten Keil 
2691b2b03f8SKarsten Keil 	sock_orphan(sk);
2701b2b03f8SKarsten Keil 	skb_queue_purge(&sk->sk_receive_queue);
2711b2b03f8SKarsten Keil 
2721b2b03f8SKarsten Keil 	release_sock(sk);
2731b2b03f8SKarsten Keil 	sock_put(sk);
2741b2b03f8SKarsten Keil 
2751b2b03f8SKarsten Keil 	return 0;
2761b2b03f8SKarsten Keil }
2771b2b03f8SKarsten Keil 
2781b2b03f8SKarsten Keil static int
data_sock_ioctl_bound(struct sock * sk,unsigned int cmd,void __user * p)2791b2b03f8SKarsten Keil data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
2801b2b03f8SKarsten Keil {
2811b2b03f8SKarsten Keil 	struct mISDN_ctrl_req	cq;
282e73f6b22SAndreas Eversberg 	int			err = -EINVAL, val[2];
2831b2b03f8SKarsten Keil 	struct mISDNchannel	*bchan, *next;
2841b2b03f8SKarsten Keil 
2851b2b03f8SKarsten Keil 	lock_sock(sk);
2861b2b03f8SKarsten Keil 	if (!_pms(sk)->dev) {
2871b2b03f8SKarsten Keil 		err = -ENODEV;
2881b2b03f8SKarsten Keil 		goto done;
2891b2b03f8SKarsten Keil 	}
2901b2b03f8SKarsten Keil 	switch (cmd) {
2911b2b03f8SKarsten Keil 	case IMCTRLREQ:
2921b2b03f8SKarsten Keil 		if (copy_from_user(&cq, p, sizeof(cq))) {
2931b2b03f8SKarsten Keil 			err = -EFAULT;
2941b2b03f8SKarsten Keil 			break;
2951b2b03f8SKarsten Keil 		}
2961b2b03f8SKarsten Keil 		if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
2971b2b03f8SKarsten Keil 			list_for_each_entry_safe(bchan, next,
2981b2b03f8SKarsten Keil 						 &_pms(sk)->dev->bchannels, list) {
2991b2b03f8SKarsten Keil 				if (bchan->nr == cq.channel) {
3001b2b03f8SKarsten Keil 					err = bchan->ctrl(bchan,
3011b2b03f8SKarsten Keil 							  CONTROL_CHANNEL, &cq);
3021b2b03f8SKarsten Keil 					break;
3031b2b03f8SKarsten Keil 				}
3041b2b03f8SKarsten Keil 			}
3051b2b03f8SKarsten Keil 		} else
3061b2b03f8SKarsten Keil 			err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
3071b2b03f8SKarsten Keil 						    CONTROL_CHANNEL, &cq);
3081b2b03f8SKarsten Keil 		if (err)
3091b2b03f8SKarsten Keil 			break;
3101b2b03f8SKarsten Keil 		if (copy_to_user(p, &cq, sizeof(cq)))
3111b2b03f8SKarsten Keil 			err = -EFAULT;
3121b2b03f8SKarsten Keil 		break;
3131b2b03f8SKarsten Keil 	case IMCLEAR_L2:
3141b2b03f8SKarsten Keil 		if (sk->sk_protocol != ISDN_P_LAPD_NT) {
3151b2b03f8SKarsten Keil 			err = -EINVAL;
3161b2b03f8SKarsten Keil 			break;
3171b2b03f8SKarsten Keil 		}
318e73f6b22SAndreas Eversberg 		val[0] = cmd;
319e73f6b22SAndreas Eversberg 		if (get_user(val[1], (int __user *)p)) {
3201b2b03f8SKarsten Keil 			err = -EFAULT;
3211b2b03f8SKarsten Keil 			break;
3221b2b03f8SKarsten Keil 		}
3231b2b03f8SKarsten Keil 		err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
324e73f6b22SAndreas Eversberg 						  CONTROL_CHANNEL, val);
325e73f6b22SAndreas Eversberg 		break;
326e73f6b22SAndreas Eversberg 	case IMHOLD_L1:
327e73f6b22SAndreas Eversberg 		if (sk->sk_protocol != ISDN_P_LAPD_NT
328e73f6b22SAndreas Eversberg 		    && sk->sk_protocol != ISDN_P_LAPD_TE) {
329e73f6b22SAndreas Eversberg 			err = -EINVAL;
330e73f6b22SAndreas Eversberg 			break;
331e73f6b22SAndreas Eversberg 		}
332e73f6b22SAndreas Eversberg 		val[0] = cmd;
333e73f6b22SAndreas Eversberg 		if (get_user(val[1], (int __user *)p)) {
334e73f6b22SAndreas Eversberg 			err = -EFAULT;
335e73f6b22SAndreas Eversberg 			break;
336e73f6b22SAndreas Eversberg 		}
337e73f6b22SAndreas Eversberg 		err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
338e73f6b22SAndreas Eversberg 						  CONTROL_CHANNEL, val);
3391b2b03f8SKarsten Keil 		break;
3401b2b03f8SKarsten Keil 	default:
3411b2b03f8SKarsten Keil 		err = -EINVAL;
3421b2b03f8SKarsten Keil 		break;
3431b2b03f8SKarsten Keil 	}
3441b2b03f8SKarsten Keil done:
3451b2b03f8SKarsten Keil 	release_sock(sk);
3461b2b03f8SKarsten Keil 	return err;
3471b2b03f8SKarsten Keil }
3481b2b03f8SKarsten Keil 
3491b2b03f8SKarsten Keil static int
data_sock_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)3501b2b03f8SKarsten Keil data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
3511b2b03f8SKarsten Keil {
3521b2b03f8SKarsten Keil 	int			err = 0, id;
3531b2b03f8SKarsten Keil 	struct sock		*sk = sock->sk;
3541b2b03f8SKarsten Keil 	struct mISDNdevice	*dev;
3551b2b03f8SKarsten Keil 	struct mISDNversion	ver;
3561b2b03f8SKarsten Keil 
3571b2b03f8SKarsten Keil 	switch (cmd) {
3581b2b03f8SKarsten Keil 	case IMGETVERSION:
3591b2b03f8SKarsten Keil 		ver.major = MISDN_MAJOR_VERSION;
3601b2b03f8SKarsten Keil 		ver.minor = MISDN_MINOR_VERSION;
3611b2b03f8SKarsten Keil 		ver.release = MISDN_RELEASE;
3621b2b03f8SKarsten Keil 		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
3631b2b03f8SKarsten Keil 			err = -EFAULT;
3641b2b03f8SKarsten Keil 		break;
3651b2b03f8SKarsten Keil 	case IMGETCOUNT:
3661b2b03f8SKarsten Keil 		id = get_mdevice_count();
3671b2b03f8SKarsten Keil 		if (put_user(id, (int __user *)arg))
3681b2b03f8SKarsten Keil 			err = -EFAULT;
3691b2b03f8SKarsten Keil 		break;
3701b2b03f8SKarsten Keil 	case IMGETDEVINFO:
3711b2b03f8SKarsten Keil 		if (get_user(id, (int __user *)arg)) {
3721b2b03f8SKarsten Keil 			err = -EFAULT;
3731b2b03f8SKarsten Keil 			break;
3741b2b03f8SKarsten Keil 		}
3751b2b03f8SKarsten Keil 		dev = get_mdevice(id);
3761b2b03f8SKarsten Keil 		if (dev) {
3771b2b03f8SKarsten Keil 			struct mISDN_devinfo di;
3781b2b03f8SKarsten Keil 
379ce384d91SKulikov Vasiliy 			memset(&di, 0, sizeof(di));
3801b2b03f8SKarsten Keil 			di.id = dev->id;
3811b2b03f8SKarsten Keil 			di.Dprotocols = dev->Dprotocols;
3821b2b03f8SKarsten Keil 			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
3831b2b03f8SKarsten Keil 			di.protocol = dev->D.protocol;
3841b2b03f8SKarsten Keil 			memcpy(di.channelmap, dev->channelmap,
385ff4cc1deSKarsten Keil 			       sizeof(di.channelmap));
3861b2b03f8SKarsten Keil 			di.nrbchan = dev->nrbchan;
387ccfb62f2SDan Carpenter 			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
3881b2b03f8SKarsten Keil 			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
3891b2b03f8SKarsten Keil 				err = -EFAULT;
3901b2b03f8SKarsten Keil 		} else
3911b2b03f8SKarsten Keil 			err = -ENODEV;
3921b2b03f8SKarsten Keil 		break;
3931b2b03f8SKarsten Keil 	default:
3941b2b03f8SKarsten Keil 		if (sk->sk_state == MISDN_BOUND)
3951b2b03f8SKarsten Keil 			err = data_sock_ioctl_bound(sk, cmd,
3961b2b03f8SKarsten Keil 						    (void __user *)arg);
3971b2b03f8SKarsten Keil 		else
3981b2b03f8SKarsten Keil 			err = -ENOTCONN;
3991b2b03f8SKarsten Keil 	}
4001b2b03f8SKarsten Keil 	return err;
4011b2b03f8SKarsten Keil }
4021b2b03f8SKarsten Keil 
data_sock_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)4031b2b03f8SKarsten Keil static int data_sock_setsockopt(struct socket *sock, int level, int optname,
404*bae45e9bSEric Dumazet 				sockptr_t optval, unsigned int optlen)
4051b2b03f8SKarsten Keil {
4061b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
4071b2b03f8SKarsten Keil 	int err = 0, opt = 0;
4081b2b03f8SKarsten Keil 
4091b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
4102d6be17dSDavid S. Miller 		printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock,
411*bae45e9bSEric Dumazet 		       level, optname, optlen);
4121b2b03f8SKarsten Keil 
4131b2b03f8SKarsten Keil 	lock_sock(sk);
4141b2b03f8SKarsten Keil 
4151b2b03f8SKarsten Keil 	switch (optname) {
4161b2b03f8SKarsten Keil 	case MISDN_TIME_STAMP:
417*bae45e9bSEric Dumazet 		err = copy_safe_from_sockptr(&opt, sizeof(opt),
418*bae45e9bSEric Dumazet 					     optval, optlen);
419*bae45e9bSEric Dumazet 		if (err)
4201b2b03f8SKarsten Keil 			break;
4211b2b03f8SKarsten Keil 
4221b2b03f8SKarsten Keil 		if (opt)
4231b2b03f8SKarsten Keil 			_pms(sk)->cmask |= MISDN_TIME_STAMP;
4241b2b03f8SKarsten Keil 		else
4251b2b03f8SKarsten Keil 			_pms(sk)->cmask &= ~MISDN_TIME_STAMP;
4261b2b03f8SKarsten Keil 		break;
4271b2b03f8SKarsten Keil 	default:
4281b2b03f8SKarsten Keil 		err = -ENOPROTOOPT;
4291b2b03f8SKarsten Keil 		break;
4301b2b03f8SKarsten Keil 	}
4311b2b03f8SKarsten Keil 	release_sock(sk);
4321b2b03f8SKarsten Keil 	return err;
4331b2b03f8SKarsten Keil }
4341b2b03f8SKarsten Keil 
data_sock_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)4351b2b03f8SKarsten Keil static int data_sock_getsockopt(struct socket *sock, int level, int optname,
4361b2b03f8SKarsten Keil 				char __user *optval, int __user *optlen)
4371b2b03f8SKarsten Keil {
4381b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
4391b2b03f8SKarsten Keil 	int len, opt;
4401b2b03f8SKarsten Keil 
4411b2b03f8SKarsten Keil 	if (get_user(len, optlen))
4421b2b03f8SKarsten Keil 		return -EFAULT;
4431b2b03f8SKarsten Keil 
44481b424d9SDavid S. Miller 	if (len != sizeof(char))
44581b424d9SDavid S. Miller 		return -EINVAL;
44681b424d9SDavid S. Miller 
4471b2b03f8SKarsten Keil 	switch (optname) {
4481b2b03f8SKarsten Keil 	case MISDN_TIME_STAMP:
4491b2b03f8SKarsten Keil 		if (_pms(sk)->cmask & MISDN_TIME_STAMP)
4501b2b03f8SKarsten Keil 			opt = 1;
4511b2b03f8SKarsten Keil 		else
4521b2b03f8SKarsten Keil 			opt = 0;
4531b2b03f8SKarsten Keil 
4541b2b03f8SKarsten Keil 		if (put_user(opt, optval))
4551b2b03f8SKarsten Keil 			return -EFAULT;
4561b2b03f8SKarsten Keil 		break;
4571b2b03f8SKarsten Keil 	default:
4581b2b03f8SKarsten Keil 		return -ENOPROTOOPT;
4591b2b03f8SKarsten Keil 	}
4601b2b03f8SKarsten Keil 
4611b2b03f8SKarsten Keil 	return 0;
4621b2b03f8SKarsten Keil }
4631b2b03f8SKarsten Keil 
4641b2b03f8SKarsten Keil static int
data_sock_bind(struct socket * sock,struct sockaddr * addr,int addr_len)4651b2b03f8SKarsten Keil data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
4661b2b03f8SKarsten Keil {
4671b2b03f8SKarsten Keil 	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
4681b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
4691b4d3312SAndreas Eversberg 	struct sock *csk;
4701b2b03f8SKarsten Keil 	int err = 0;
4711b2b03f8SKarsten Keil 
4721b2b03f8SKarsten Keil 	if (*debug & DEBUG_SOCKET)
4731b2b03f8SKarsten Keil 		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
4741b2b03f8SKarsten Keil 	if (addr_len != sizeof(struct sockaddr_mISDN))
4751b2b03f8SKarsten Keil 		return -EINVAL;
4761b2b03f8SKarsten Keil 	if (!maddr || maddr->family != AF_ISDN)
4771b2b03f8SKarsten Keil 		return -EINVAL;
4781b2b03f8SKarsten Keil 
4791b2b03f8SKarsten Keil 	lock_sock(sk);
4801b2b03f8SKarsten Keil 
4811b2b03f8SKarsten Keil 	if (_pms(sk)->dev) {
4821b2b03f8SKarsten Keil 		err = -EALREADY;
4831b2b03f8SKarsten Keil 		goto done;
4841b2b03f8SKarsten Keil 	}
4851b2b03f8SKarsten Keil 	_pms(sk)->dev = get_mdevice(maddr->dev);
4861b2b03f8SKarsten Keil 	if (!_pms(sk)->dev) {
4871b2b03f8SKarsten Keil 		err = -ENODEV;
4881b2b03f8SKarsten Keil 		goto done;
4891b2b03f8SKarsten Keil 	}
4901b4d3312SAndreas Eversberg 
4919a812553SAndreas Eversberg 	if (sk->sk_protocol < ISDN_P_B_START) {
4921b4d3312SAndreas Eversberg 		read_lock_bh(&data_sockets.lock);
493b67bfe0dSSasha Levin 		sk_for_each(csk, &data_sockets.head) {
4941b4d3312SAndreas Eversberg 			if (sk == csk)
4951b4d3312SAndreas Eversberg 				continue;
4961b4d3312SAndreas Eversberg 			if (_pms(csk)->dev != _pms(sk)->dev)
4971b4d3312SAndreas Eversberg 				continue;
4981b4d3312SAndreas Eversberg 			if (csk->sk_protocol >= ISDN_P_B_START)
4991b4d3312SAndreas Eversberg 				continue;
5001b4d3312SAndreas Eversberg 			if (IS_ISDN_P_TE(csk->sk_protocol)
5011b4d3312SAndreas Eversberg 			    == IS_ISDN_P_TE(sk->sk_protocol))
5021b4d3312SAndreas Eversberg 				continue;
5031b4d3312SAndreas Eversberg 			read_unlock_bh(&data_sockets.lock);
5041b4d3312SAndreas Eversberg 			err = -EBUSY;
5051b4d3312SAndreas Eversberg 			goto done;
5061b4d3312SAndreas Eversberg 		}
5071b4d3312SAndreas Eversberg 		read_unlock_bh(&data_sockets.lock);
5089a812553SAndreas Eversberg 	}
5091b4d3312SAndreas Eversberg 
5101b2b03f8SKarsten Keil 	_pms(sk)->ch.send = mISDN_send;
5111b2b03f8SKarsten Keil 	_pms(sk)->ch.ctrl = mISDN_ctrl;
5121b2b03f8SKarsten Keil 
5131b2b03f8SKarsten Keil 	switch (sk->sk_protocol) {
5141b2b03f8SKarsten Keil 	case ISDN_P_TE_S0:
5151b2b03f8SKarsten Keil 	case ISDN_P_NT_S0:
5161b2b03f8SKarsten Keil 	case ISDN_P_TE_E1:
5171b2b03f8SKarsten Keil 	case ISDN_P_NT_E1:
5181b2b03f8SKarsten Keil 		mISDN_sock_unlink(&data_sockets, sk);
5191b2b03f8SKarsten Keil 		err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
5201b2b03f8SKarsten Keil 				     sk->sk_protocol, maddr);
5211b2b03f8SKarsten Keil 		if (err)
5221b2b03f8SKarsten Keil 			mISDN_sock_link(&data_sockets, sk);
5231b2b03f8SKarsten Keil 		break;
5241b2b03f8SKarsten Keil 	case ISDN_P_LAPD_TE:
5251b2b03f8SKarsten Keil 	case ISDN_P_LAPD_NT:
5261b2b03f8SKarsten Keil 		err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
5271b2b03f8SKarsten Keil 				      sk->sk_protocol, maddr);
5281b2b03f8SKarsten Keil 		break;
5291b2b03f8SKarsten Keil 	case ISDN_P_B_RAW:
5301b2b03f8SKarsten Keil 	case ISDN_P_B_HDLC:
5311b2b03f8SKarsten Keil 	case ISDN_P_B_X75SLP:
5321b2b03f8SKarsten Keil 	case ISDN_P_B_L2DTMF:
5331b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSP:
5341b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSPHDLC:
5351b2b03f8SKarsten Keil 		err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
5361b2b03f8SKarsten Keil 				     sk->sk_protocol, maddr);
5371b2b03f8SKarsten Keil 		break;
5381b2b03f8SKarsten Keil 	default:
5391b2b03f8SKarsten Keil 		err = -EPROTONOSUPPORT;
5401b2b03f8SKarsten Keil 	}
5411b2b03f8SKarsten Keil 	if (err)
5421b2b03f8SKarsten Keil 		goto done;
5431b2b03f8SKarsten Keil 	sk->sk_state = MISDN_BOUND;
5441b2b03f8SKarsten Keil 	_pms(sk)->ch.protocol = sk->sk_protocol;
5451b2b03f8SKarsten Keil 
5461b2b03f8SKarsten Keil done:
5471b2b03f8SKarsten Keil 	release_sock(sk);
5481b2b03f8SKarsten Keil 	return err;
5491b2b03f8SKarsten Keil }
5501b2b03f8SKarsten Keil 
5511b2b03f8SKarsten Keil static int
data_sock_getname(struct socket * sock,struct sockaddr * addr,int peer)5521b2b03f8SKarsten Keil data_sock_getname(struct socket *sock, struct sockaddr *addr,
5539b2c45d4SDenys Vlasenko 		  int peer)
5541b2b03f8SKarsten Keil {
5551b2b03f8SKarsten Keil 	struct sockaddr_mISDN	*maddr = (struct sockaddr_mISDN *) addr;
5561b2b03f8SKarsten Keil 	struct sock		*sk = sock->sk;
5571b2b03f8SKarsten Keil 
5581b2b03f8SKarsten Keil 	if (!_pms(sk)->dev)
5591b2b03f8SKarsten Keil 		return -EBADFD;
5601b2b03f8SKarsten Keil 
5611b2b03f8SKarsten Keil 	lock_sock(sk);
5621b2b03f8SKarsten Keil 
563d9152097SDan Carpenter 	maddr->family = AF_ISDN;
5641b2b03f8SKarsten Keil 	maddr->dev = _pms(sk)->dev->id;
5651b2b03f8SKarsten Keil 	maddr->channel = _pms(sk)->ch.nr;
5661b2b03f8SKarsten Keil 	maddr->sapi = _pms(sk)->ch.addr & 0xff;
5671b2b03f8SKarsten Keil 	maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
5681b2b03f8SKarsten Keil 	release_sock(sk);
5699b2c45d4SDenys Vlasenko 	return sizeof(*maddr);
5701b2b03f8SKarsten Keil }
5711b2b03f8SKarsten Keil 
5721b2b03f8SKarsten Keil static const struct proto_ops data_sock_ops = {
5731b2b03f8SKarsten Keil 	.family		= PF_ISDN,
5741b2b03f8SKarsten Keil 	.owner		= THIS_MODULE,
5751b2b03f8SKarsten Keil 	.release	= data_sock_release,
5761b2b03f8SKarsten Keil 	.ioctl		= data_sock_ioctl,
5771b2b03f8SKarsten Keil 	.bind		= data_sock_bind,
5781b2b03f8SKarsten Keil 	.getname	= data_sock_getname,
5791b2b03f8SKarsten Keil 	.sendmsg	= mISDN_sock_sendmsg,
5801b2b03f8SKarsten Keil 	.recvmsg	= mISDN_sock_recvmsg,
581a11e1d43SLinus Torvalds 	.poll		= datagram_poll,
5821b2b03f8SKarsten Keil 	.listen		= sock_no_listen,
5831b2b03f8SKarsten Keil 	.shutdown	= sock_no_shutdown,
5841b2b03f8SKarsten Keil 	.setsockopt	= data_sock_setsockopt,
5851b2b03f8SKarsten Keil 	.getsockopt	= data_sock_getsockopt,
5861b2b03f8SKarsten Keil 	.connect	= sock_no_connect,
5871b2b03f8SKarsten Keil 	.socketpair	= sock_no_socketpair,
5881b2b03f8SKarsten Keil 	.accept		= sock_no_accept,
5891b2b03f8SKarsten Keil 	.mmap		= sock_no_mmap
5901b2b03f8SKarsten Keil };
5911b2b03f8SKarsten Keil 
5921b2b03f8SKarsten Keil static int
data_sock_create(struct net * net,struct socket * sock,int protocol,int kern)59311aa9c28SEric W. Biederman data_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
5941b2b03f8SKarsten Keil {
5951b2b03f8SKarsten Keil 	struct sock *sk;
5961b2b03f8SKarsten Keil 
5971b2b03f8SKarsten Keil 	if (sock->type != SOCK_DGRAM)
5981b2b03f8SKarsten Keil 		return -ESOCKTNOSUPPORT;
5991b2b03f8SKarsten Keil 
60011aa9c28SEric W. Biederman 	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
6011b2b03f8SKarsten Keil 	if (!sk)
6021b2b03f8SKarsten Keil 		return -ENOMEM;
6031b2b03f8SKarsten Keil 
6041b2b03f8SKarsten Keil 	sock_init_data(sock, sk);
6051b2b03f8SKarsten Keil 
6061b2b03f8SKarsten Keil 	sock->ops = &data_sock_ops;
6071b2b03f8SKarsten Keil 	sock->state = SS_UNCONNECTED;
6081b2b03f8SKarsten Keil 	sock_reset_flag(sk, SOCK_ZAPPED);
6091b2b03f8SKarsten Keil 
6101b2b03f8SKarsten Keil 	sk->sk_protocol = protocol;
6111b2b03f8SKarsten Keil 	sk->sk_state    = MISDN_OPEN;
6121b2b03f8SKarsten Keil 	mISDN_sock_link(&data_sockets, sk);
6131b2b03f8SKarsten Keil 
6141b2b03f8SKarsten Keil 	return 0;
6151b2b03f8SKarsten Keil }
6161b2b03f8SKarsten Keil 
6171b2b03f8SKarsten Keil static int
base_sock_release(struct socket * sock)6181b2b03f8SKarsten Keil base_sock_release(struct socket *sock)
6191b2b03f8SKarsten Keil {
6201b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
6211b2b03f8SKarsten Keil 
6221b2b03f8SKarsten Keil 	printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
6231b2b03f8SKarsten Keil 	if (!sk)
6241b2b03f8SKarsten Keil 		return 0;
6251b2b03f8SKarsten Keil 
6261b2b03f8SKarsten Keil 	mISDN_sock_unlink(&base_sockets, sk);
6271b2b03f8SKarsten Keil 	sock_orphan(sk);
6281b2b03f8SKarsten Keil 	sock_put(sk);
6291b2b03f8SKarsten Keil 
6301b2b03f8SKarsten Keil 	return 0;
6311b2b03f8SKarsten Keil }
6321b2b03f8SKarsten Keil 
6331b2b03f8SKarsten Keil static int
base_sock_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)6341b2b03f8SKarsten Keil base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
6351b2b03f8SKarsten Keil {
6361b2b03f8SKarsten Keil 	int			err = 0, id;
6371b2b03f8SKarsten Keil 	struct mISDNdevice	*dev;
6381b2b03f8SKarsten Keil 	struct mISDNversion	ver;
6391b2b03f8SKarsten Keil 
6401b2b03f8SKarsten Keil 	switch (cmd) {
6411b2b03f8SKarsten Keil 	case IMGETVERSION:
6421b2b03f8SKarsten Keil 		ver.major = MISDN_MAJOR_VERSION;
6431b2b03f8SKarsten Keil 		ver.minor = MISDN_MINOR_VERSION;
6441b2b03f8SKarsten Keil 		ver.release = MISDN_RELEASE;
6451b2b03f8SKarsten Keil 		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
6461b2b03f8SKarsten Keil 			err = -EFAULT;
6471b2b03f8SKarsten Keil 		break;
6481b2b03f8SKarsten Keil 	case IMGETCOUNT:
6491b2b03f8SKarsten Keil 		id = get_mdevice_count();
6501b2b03f8SKarsten Keil 		if (put_user(id, (int __user *)arg))
6511b2b03f8SKarsten Keil 			err = -EFAULT;
6521b2b03f8SKarsten Keil 		break;
6531b2b03f8SKarsten Keil 	case IMGETDEVINFO:
6541b2b03f8SKarsten Keil 		if (get_user(id, (int __user *)arg)) {
6551b2b03f8SKarsten Keil 			err = -EFAULT;
6561b2b03f8SKarsten Keil 			break;
6571b2b03f8SKarsten Keil 		}
6581b2b03f8SKarsten Keil 		dev = get_mdevice(id);
6591b2b03f8SKarsten Keil 		if (dev) {
6601b2b03f8SKarsten Keil 			struct mISDN_devinfo di;
6611b2b03f8SKarsten Keil 
662ce384d91SKulikov Vasiliy 			memset(&di, 0, sizeof(di));
6631b2b03f8SKarsten Keil 			di.id = dev->id;
6641b2b03f8SKarsten Keil 			di.Dprotocols = dev->Dprotocols;
6651b2b03f8SKarsten Keil 			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
6661b2b03f8SKarsten Keil 			di.protocol = dev->D.protocol;
6671b2b03f8SKarsten Keil 			memcpy(di.channelmap, dev->channelmap,
668ff4cc1deSKarsten Keil 			       sizeof(di.channelmap));
6691b2b03f8SKarsten Keil 			di.nrbchan = dev->nrbchan;
670ccfb62f2SDan Carpenter 			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
6711b2b03f8SKarsten Keil 			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
6721b2b03f8SKarsten Keil 				err = -EFAULT;
6731b2b03f8SKarsten Keil 		} else
6741b2b03f8SKarsten Keil 			err = -ENODEV;
6751b2b03f8SKarsten Keil 		break;
6768b6015f7SMatthias Urlichs 	case IMSETDEVNAME:
6778b6015f7SMatthias Urlichs 	{
6788b6015f7SMatthias Urlichs 		struct mISDN_devrename dn;
6798b6015f7SMatthias Urlichs 		if (copy_from_user(&dn, (void __user *)arg,
6808b6015f7SMatthias Urlichs 				   sizeof(dn))) {
6818b6015f7SMatthias Urlichs 			err = -EFAULT;
6828b6015f7SMatthias Urlichs 			break;
6838b6015f7SMatthias Urlichs 		}
684ccfb62f2SDan Carpenter 		dn.name[sizeof(dn.name) - 1] = '\0';
6858b6015f7SMatthias Urlichs 		dev = get_mdevice(dn.id);
6868b6015f7SMatthias Urlichs 		if (dev)
687837468d1SMatthias Urlichs 			err = device_rename(&dev->dev, dn.name);
6888b6015f7SMatthias Urlichs 		else
6898b6015f7SMatthias Urlichs 			err = -ENODEV;
6908b6015f7SMatthias Urlichs 	}
6918b6015f7SMatthias Urlichs 	break;
6921b2b03f8SKarsten Keil 	default:
6931b2b03f8SKarsten Keil 		err = -EINVAL;
6941b2b03f8SKarsten Keil 	}
6951b2b03f8SKarsten Keil 	return err;
6961b2b03f8SKarsten Keil }
6971b2b03f8SKarsten Keil 
6981b2b03f8SKarsten Keil static int
base_sock_bind(struct socket * sock,struct sockaddr * addr,int addr_len)6991b2b03f8SKarsten Keil base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
7001b2b03f8SKarsten Keil {
7011b2b03f8SKarsten Keil 	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
7021b2b03f8SKarsten Keil 	struct sock *sk = sock->sk;
7031b2b03f8SKarsten Keil 	int err = 0;
7041b2b03f8SKarsten Keil 
705238ffdc4STetsuo Handa 	if (addr_len < sizeof(struct sockaddr_mISDN))
7061b2b03f8SKarsten Keil 		return -EINVAL;
7071b2b03f8SKarsten Keil 
708238ffdc4STetsuo Handa 	if (!maddr || maddr->family != AF_ISDN)
709b8216468SEmrah Demir 		return -EINVAL;
710b8216468SEmrah Demir 
7111b2b03f8SKarsten Keil 	lock_sock(sk);
7121b2b03f8SKarsten Keil 
7131b2b03f8SKarsten Keil 	if (_pms(sk)->dev) {
7141b2b03f8SKarsten Keil 		err = -EALREADY;
7151b2b03f8SKarsten Keil 		goto done;
7161b2b03f8SKarsten Keil 	}
7171b2b03f8SKarsten Keil 
7181b2b03f8SKarsten Keil 	_pms(sk)->dev = get_mdevice(maddr->dev);
7191b2b03f8SKarsten Keil 	if (!_pms(sk)->dev) {
7201b2b03f8SKarsten Keil 		err = -ENODEV;
7211b2b03f8SKarsten Keil 		goto done;
7221b2b03f8SKarsten Keil 	}
7231b2b03f8SKarsten Keil 	sk->sk_state = MISDN_BOUND;
7241b2b03f8SKarsten Keil 
7251b2b03f8SKarsten Keil done:
7261b2b03f8SKarsten Keil 	release_sock(sk);
7271b2b03f8SKarsten Keil 	return err;
7281b2b03f8SKarsten Keil }
7291b2b03f8SKarsten Keil 
7301b2b03f8SKarsten Keil static const struct proto_ops base_sock_ops = {
7311b2b03f8SKarsten Keil 	.family		= PF_ISDN,
7321b2b03f8SKarsten Keil 	.owner		= THIS_MODULE,
7331b2b03f8SKarsten Keil 	.release	= base_sock_release,
7341b2b03f8SKarsten Keil 	.ioctl		= base_sock_ioctl,
7351b2b03f8SKarsten Keil 	.bind		= base_sock_bind,
7361b2b03f8SKarsten Keil 	.getname	= sock_no_getname,
7371b2b03f8SKarsten Keil 	.sendmsg	= sock_no_sendmsg,
7381b2b03f8SKarsten Keil 	.recvmsg	= sock_no_recvmsg,
7391b2b03f8SKarsten Keil 	.listen		= sock_no_listen,
7401b2b03f8SKarsten Keil 	.shutdown	= sock_no_shutdown,
7411b2b03f8SKarsten Keil 	.connect	= sock_no_connect,
7421b2b03f8SKarsten Keil 	.socketpair	= sock_no_socketpair,
7431b2b03f8SKarsten Keil 	.accept		= sock_no_accept,
7441b2b03f8SKarsten Keil 	.mmap		= sock_no_mmap
7451b2b03f8SKarsten Keil };
7461b2b03f8SKarsten Keil 
7471b2b03f8SKarsten Keil 
7481b2b03f8SKarsten Keil static int
base_sock_create(struct net * net,struct socket * sock,int protocol,int kern)74911aa9c28SEric W. Biederman base_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
7501b2b03f8SKarsten Keil {
7511b2b03f8SKarsten Keil 	struct sock *sk;
7521b2b03f8SKarsten Keil 
7531b2b03f8SKarsten Keil 	if (sock->type != SOCK_RAW)
7541b2b03f8SKarsten Keil 		return -ESOCKTNOSUPPORT;
755b91ee4aaSOri Nimron 	if (!capable(CAP_NET_RAW))
756b91ee4aaSOri Nimron 		return -EPERM;
7571b2b03f8SKarsten Keil 
75811aa9c28SEric W. Biederman 	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
7591b2b03f8SKarsten Keil 	if (!sk)
7601b2b03f8SKarsten Keil 		return -ENOMEM;
7611b2b03f8SKarsten Keil 
7621b2b03f8SKarsten Keil 	sock_init_data(sock, sk);
7631b2b03f8SKarsten Keil 	sock->ops = &base_sock_ops;
7641b2b03f8SKarsten Keil 	sock->state = SS_UNCONNECTED;
7651b2b03f8SKarsten Keil 	sock_reset_flag(sk, SOCK_ZAPPED);
7661b2b03f8SKarsten Keil 	sk->sk_protocol = protocol;
7671b2b03f8SKarsten Keil 	sk->sk_state    = MISDN_OPEN;
7681b2b03f8SKarsten Keil 	mISDN_sock_link(&base_sockets, sk);
7691b2b03f8SKarsten Keil 
7701b2b03f8SKarsten Keil 	return 0;
7711b2b03f8SKarsten Keil }
7721b2b03f8SKarsten Keil 
7731b2b03f8SKarsten Keil static int
mISDN_sock_create(struct net * net,struct socket * sock,int proto,int kern)7743f378b68SEric Paris mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern)
7751b2b03f8SKarsten Keil {
7761b2b03f8SKarsten Keil 	int err = -EPROTONOSUPPORT;
7771b2b03f8SKarsten Keil 
7781b2b03f8SKarsten Keil 	switch (proto) {
7791b2b03f8SKarsten Keil 	case ISDN_P_BASE:
78011aa9c28SEric W. Biederman 		err = base_sock_create(net, sock, proto, kern);
7811b2b03f8SKarsten Keil 		break;
7821b2b03f8SKarsten Keil 	case ISDN_P_TE_S0:
7831b2b03f8SKarsten Keil 	case ISDN_P_NT_S0:
7841b2b03f8SKarsten Keil 	case ISDN_P_TE_E1:
7851b2b03f8SKarsten Keil 	case ISDN_P_NT_E1:
7861b2b03f8SKarsten Keil 	case ISDN_P_LAPD_TE:
7871b2b03f8SKarsten Keil 	case ISDN_P_LAPD_NT:
7881b2b03f8SKarsten Keil 	case ISDN_P_B_RAW:
7891b2b03f8SKarsten Keil 	case ISDN_P_B_HDLC:
7901b2b03f8SKarsten Keil 	case ISDN_P_B_X75SLP:
7911b2b03f8SKarsten Keil 	case ISDN_P_B_L2DTMF:
7921b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSP:
7931b2b03f8SKarsten Keil 	case ISDN_P_B_L2DSPHDLC:
79411aa9c28SEric W. Biederman 		err = data_sock_create(net, sock, proto, kern);
7951b2b03f8SKarsten Keil 		break;
7961b2b03f8SKarsten Keil 	default:
7971b2b03f8SKarsten Keil 		return err;
7981b2b03f8SKarsten Keil 	}
7991b2b03f8SKarsten Keil 
8001b2b03f8SKarsten Keil 	return err;
8011b2b03f8SKarsten Keil }
8021b2b03f8SKarsten Keil 
803ec1b4cf7SStephen Hemminger static const struct net_proto_family mISDN_sock_family_ops = {
8041b2b03f8SKarsten Keil 	.owner  = THIS_MODULE,
8051b2b03f8SKarsten Keil 	.family = PF_ISDN,
8061b2b03f8SKarsten Keil 	.create = mISDN_sock_create,
8071b2b03f8SKarsten Keil };
8081b2b03f8SKarsten Keil 
8091b2b03f8SKarsten Keil int
misdn_sock_init(u_int * deb)8101b2b03f8SKarsten Keil misdn_sock_init(u_int *deb)
8111b2b03f8SKarsten Keil {
8121b2b03f8SKarsten Keil 	int err;
8131b2b03f8SKarsten Keil 
8141b2b03f8SKarsten Keil 	debug = deb;
8151b2b03f8SKarsten Keil 	err = sock_register(&mISDN_sock_family_ops);
8161b2b03f8SKarsten Keil 	if (err)
8171b2b03f8SKarsten Keil 		printk(KERN_ERR "%s: error(%d)\n", __func__, err);
8181b2b03f8SKarsten Keil 	return err;
8191b2b03f8SKarsten Keil }
8201b2b03f8SKarsten Keil 
8211b2b03f8SKarsten Keil void
misdn_sock_cleanup(void)8221b2b03f8SKarsten Keil misdn_sock_cleanup(void)
8231b2b03f8SKarsten Keil {
8241b2b03f8SKarsten Keil 	sock_unregister(PF_ISDN);
8251b2b03f8SKarsten Keil }
826