18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author	Karsten Keil <kkeil@novell.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2008  by Karsten Keil <kkeil@novell.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/mISDNif.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/export.h>
128c2ecf20Sopenharmony_ci#include "core.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic u_int	*debug;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic struct proto mISDN_proto = {
178c2ecf20Sopenharmony_ci	.name		= "misdn",
188c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
198c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct mISDN_sock)
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define _pms(sk)	((struct mISDN_sock *)sk)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct mISDN_sock_list	data_sockets = {
258c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic struct mISDN_sock_list	base_sockets = {
298c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define L2_HEADER_LEN	4
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic inline struct sk_buff *
358c2ecf20Sopenharmony_ci_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct sk_buff  *skb;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
408c2ecf20Sopenharmony_ci	if (likely(skb))
418c2ecf20Sopenharmony_ci		skb_reserve(skb, L2_HEADER_LEN);
428c2ecf20Sopenharmony_ci	return skb;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void
468c2ecf20Sopenharmony_cimISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	write_lock_bh(&l->lock);
498c2ecf20Sopenharmony_ci	sk_add_node(sk, &l->head);
508c2ecf20Sopenharmony_ci	write_unlock_bh(&l->lock);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	write_lock_bh(&l->lock);
568c2ecf20Sopenharmony_ci	sk_del_node_init(sk);
578c2ecf20Sopenharmony_ci	write_unlock_bh(&l->lock);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int
618c2ecf20Sopenharmony_cimISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct mISDN_sock *msk;
648c2ecf20Sopenharmony_ci	int	err;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	msk = container_of(ch, struct mISDN_sock, ch);
678c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
688c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
698c2ecf20Sopenharmony_ci	if (msk->sk.sk_state == MISDN_CLOSED)
708c2ecf20Sopenharmony_ci		return -EUNATCH;
718c2ecf20Sopenharmony_ci	__net_timestamp(skb);
728c2ecf20Sopenharmony_ci	err = sock_queue_rcv_skb(&msk->sk, skb);
738c2ecf20Sopenharmony_ci	if (err)
748c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: error %d\n", __func__, err);
758c2ecf20Sopenharmony_ci	return err;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int
798c2ecf20Sopenharmony_cimISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct mISDN_sock *msk;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	msk = container_of(ch, struct mISDN_sock, ch);
848c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
858c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
868c2ecf20Sopenharmony_ci	switch (cmd) {
878c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
888c2ecf20Sopenharmony_ci		msk->sk.sk_state = MISDN_CLOSED;
898c2ecf20Sopenharmony_ci		break;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline void
958c2ecf20Sopenharmony_cimISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct __kernel_old_timeval	tv;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
1008c2ecf20Sopenharmony_ci		skb_get_timestamp(skb, &tv);
1018c2ecf20Sopenharmony_ci		put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int
1068c2ecf20Sopenharmony_cimISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
1078c2ecf20Sopenharmony_ci		   int flags)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct sk_buff		*skb;
1108c2ecf20Sopenharmony_ci	struct sock		*sk = sock->sk;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	int		copied, err;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
1158c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
1168c2ecf20Sopenharmony_ci		       __func__, (int)len, flags, _pms(sk)->ch.nr,
1178c2ecf20Sopenharmony_ci		       sk->sk_protocol);
1188c2ecf20Sopenharmony_ci	if (flags & (MSG_OOB))
1198c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (sk->sk_state == MISDN_CLOSED)
1228c2ecf20Sopenharmony_ci		return 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
1258c2ecf20Sopenharmony_ci	if (!skb)
1268c2ecf20Sopenharmony_ci		return err;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (msg->msg_name) {
1298c2ecf20Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		maddr->family = AF_ISDN;
1328c2ecf20Sopenharmony_ci		maddr->dev = _pms(sk)->dev->id;
1338c2ecf20Sopenharmony_ci		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
1348c2ecf20Sopenharmony_ci		    (sk->sk_protocol == ISDN_P_LAPD_NT)) {
1358c2ecf20Sopenharmony_ci			maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
1368c2ecf20Sopenharmony_ci			maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
1378c2ecf20Sopenharmony_ci			maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
1388c2ecf20Sopenharmony_ci		} else {
1398c2ecf20Sopenharmony_ci			maddr->channel = _pms(sk)->ch.nr;
1408c2ecf20Sopenharmony_ci			maddr->sapi = _pms(sk)->ch.addr & 0xFF;
1418c2ecf20Sopenharmony_ci			maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		msg->msg_namelen = sizeof(*maddr);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	copied = skb->len + MISDN_HEADER_LEN;
1478c2ecf20Sopenharmony_ci	if (len < copied) {
1488c2ecf20Sopenharmony_ci		if (flags & MSG_PEEK)
1498c2ecf20Sopenharmony_ci			refcount_dec(&skb->users);
1508c2ecf20Sopenharmony_ci		else
1518c2ecf20Sopenharmony_ci			skb_queue_head(&sk->sk_receive_queue, skb);
1528c2ecf20Sopenharmony_ci		return -ENOSPC;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
1558c2ecf20Sopenharmony_ci	       MISDN_HEADER_LEN);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	err = skb_copy_datagram_msg(skb, 0, msg, copied);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	mISDN_sock_cmsg(sk, msg, skb);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	skb_free_datagram(sk, skb);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return err ? : copied;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int
1678c2ecf20Sopenharmony_cimISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct sock		*sk = sock->sk;
1708c2ecf20Sopenharmony_ci	struct sk_buff		*skb;
1718c2ecf20Sopenharmony_ci	int			err = -ENOMEM;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
1748c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
1758c2ecf20Sopenharmony_ci		       __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
1768c2ecf20Sopenharmony_ci		       sk->sk_protocol);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (msg->msg_flags & MSG_OOB)
1798c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE))
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (len < MISDN_HEADER_LEN)
1858c2ecf20Sopenharmony_ci		return -EINVAL;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (sk->sk_state != MISDN_BOUND)
1888c2ecf20Sopenharmony_ci		return -EBADFD;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	lock_sock(sk);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	skb = _l2_alloc_skb(len, GFP_KERNEL);
1938c2ecf20Sopenharmony_ci	if (!skb)
1948c2ecf20Sopenharmony_ci		goto done;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
1978c2ecf20Sopenharmony_ci		err = -EFAULT;
1988c2ecf20Sopenharmony_ci		goto done;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
2028c2ecf20Sopenharmony_ci	skb_pull(skb, MISDN_HEADER_LEN);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
2058c2ecf20Sopenharmony_ci		/* if we have a address, we use it */
2068c2ecf20Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
2078c2ecf20Sopenharmony_ci		mISDN_HEAD_ID(skb) = maddr->channel;
2088c2ecf20Sopenharmony_ci	} else { /* use default for L2 messages */
2098c2ecf20Sopenharmony_ci		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
2108c2ecf20Sopenharmony_ci		    (sk->sk_protocol == ISDN_P_LAPD_NT))
2118c2ecf20Sopenharmony_ci			mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
2158c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: ID:%x\n",
2168c2ecf20Sopenharmony_ci		       __func__, mISDN_HEAD_ID(skb));
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	err = -ENODEV;
2198c2ecf20Sopenharmony_ci	if (!_pms(sk)->ch.peer)
2208c2ecf20Sopenharmony_ci		goto done;
2218c2ecf20Sopenharmony_ci	err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb);
2228c2ecf20Sopenharmony_ci	if (err)
2238c2ecf20Sopenharmony_ci		goto done;
2248c2ecf20Sopenharmony_ci	else {
2258c2ecf20Sopenharmony_ci		skb = NULL;
2268c2ecf20Sopenharmony_ci		err = len;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cidone:
2308c2ecf20Sopenharmony_ci	kfree_skb(skb);
2318c2ecf20Sopenharmony_ci	release_sock(sk);
2328c2ecf20Sopenharmony_ci	return err;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int
2368c2ecf20Sopenharmony_cidata_sock_release(struct socket *sock)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
2418c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
2428c2ecf20Sopenharmony_ci	if (!sk)
2438c2ecf20Sopenharmony_ci		return 0;
2448c2ecf20Sopenharmony_ci	switch (sk->sk_protocol) {
2458c2ecf20Sopenharmony_ci	case ISDN_P_TE_S0:
2468c2ecf20Sopenharmony_ci	case ISDN_P_NT_S0:
2478c2ecf20Sopenharmony_ci	case ISDN_P_TE_E1:
2488c2ecf20Sopenharmony_ci	case ISDN_P_NT_E1:
2498c2ecf20Sopenharmony_ci		if (sk->sk_state == MISDN_BOUND)
2508c2ecf20Sopenharmony_ci			delete_channel(&_pms(sk)->ch);
2518c2ecf20Sopenharmony_ci		else
2528c2ecf20Sopenharmony_ci			mISDN_sock_unlink(&data_sockets, sk);
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_TE:
2558c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_NT:
2568c2ecf20Sopenharmony_ci	case ISDN_P_B_RAW:
2578c2ecf20Sopenharmony_ci	case ISDN_P_B_HDLC:
2588c2ecf20Sopenharmony_ci	case ISDN_P_B_X75SLP:
2598c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DTMF:
2608c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSP:
2618c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSPHDLC:
2628c2ecf20Sopenharmony_ci		delete_channel(&_pms(sk)->ch);
2638c2ecf20Sopenharmony_ci		mISDN_sock_unlink(&data_sockets, sk);
2648c2ecf20Sopenharmony_ci		break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	lock_sock(sk);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	sock_orphan(sk);
2708c2ecf20Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	release_sock(sk);
2738c2ecf20Sopenharmony_ci	sock_put(sk);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return 0;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int
2798c2ecf20Sopenharmony_cidata_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct mISDN_ctrl_req	cq;
2828c2ecf20Sopenharmony_ci	int			err = -EINVAL, val[2];
2838c2ecf20Sopenharmony_ci	struct mISDNchannel	*bchan, *next;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	lock_sock(sk);
2868c2ecf20Sopenharmony_ci	if (!_pms(sk)->dev) {
2878c2ecf20Sopenharmony_ci		err = -ENODEV;
2888c2ecf20Sopenharmony_ci		goto done;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	switch (cmd) {
2918c2ecf20Sopenharmony_ci	case IMCTRLREQ:
2928c2ecf20Sopenharmony_ci		if (copy_from_user(&cq, p, sizeof(cq))) {
2938c2ecf20Sopenharmony_ci			err = -EFAULT;
2948c2ecf20Sopenharmony_ci			break;
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci		if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
2978c2ecf20Sopenharmony_ci			list_for_each_entry_safe(bchan, next,
2988c2ecf20Sopenharmony_ci						 &_pms(sk)->dev->bchannels, list) {
2998c2ecf20Sopenharmony_ci				if (bchan->nr == cq.channel) {
3008c2ecf20Sopenharmony_ci					err = bchan->ctrl(bchan,
3018c2ecf20Sopenharmony_ci							  CONTROL_CHANNEL, &cq);
3028c2ecf20Sopenharmony_ci					break;
3038c2ecf20Sopenharmony_ci				}
3048c2ecf20Sopenharmony_ci			}
3058c2ecf20Sopenharmony_ci		} else
3068c2ecf20Sopenharmony_ci			err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
3078c2ecf20Sopenharmony_ci						    CONTROL_CHANNEL, &cq);
3088c2ecf20Sopenharmony_ci		if (err)
3098c2ecf20Sopenharmony_ci			break;
3108c2ecf20Sopenharmony_ci		if (copy_to_user(p, &cq, sizeof(cq)))
3118c2ecf20Sopenharmony_ci			err = -EFAULT;
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	case IMCLEAR_L2:
3148c2ecf20Sopenharmony_ci		if (sk->sk_protocol != ISDN_P_LAPD_NT) {
3158c2ecf20Sopenharmony_ci			err = -EINVAL;
3168c2ecf20Sopenharmony_ci			break;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		val[0] = cmd;
3198c2ecf20Sopenharmony_ci		if (get_user(val[1], (int __user *)p)) {
3208c2ecf20Sopenharmony_ci			err = -EFAULT;
3218c2ecf20Sopenharmony_ci			break;
3228c2ecf20Sopenharmony_ci		}
3238c2ecf20Sopenharmony_ci		err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
3248c2ecf20Sopenharmony_ci						  CONTROL_CHANNEL, val);
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case IMHOLD_L1:
3278c2ecf20Sopenharmony_ci		if (sk->sk_protocol != ISDN_P_LAPD_NT
3288c2ecf20Sopenharmony_ci		    && sk->sk_protocol != ISDN_P_LAPD_TE) {
3298c2ecf20Sopenharmony_ci			err = -EINVAL;
3308c2ecf20Sopenharmony_ci			break;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci		val[0] = cmd;
3338c2ecf20Sopenharmony_ci		if (get_user(val[1], (int __user *)p)) {
3348c2ecf20Sopenharmony_ci			err = -EFAULT;
3358c2ecf20Sopenharmony_ci			break;
3368c2ecf20Sopenharmony_ci		}
3378c2ecf20Sopenharmony_ci		err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
3388c2ecf20Sopenharmony_ci						  CONTROL_CHANNEL, val);
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	default:
3418c2ecf20Sopenharmony_ci		err = -EINVAL;
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_cidone:
3458c2ecf20Sopenharmony_ci	release_sock(sk);
3468c2ecf20Sopenharmony_ci	return err;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int
3508c2ecf20Sopenharmony_cidata_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	int			err = 0, id;
3538c2ecf20Sopenharmony_ci	struct sock		*sk = sock->sk;
3548c2ecf20Sopenharmony_ci	struct mISDNdevice	*dev;
3558c2ecf20Sopenharmony_ci	struct mISDNversion	ver;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	switch (cmd) {
3588c2ecf20Sopenharmony_ci	case IMGETVERSION:
3598c2ecf20Sopenharmony_ci		ver.major = MISDN_MAJOR_VERSION;
3608c2ecf20Sopenharmony_ci		ver.minor = MISDN_MINOR_VERSION;
3618c2ecf20Sopenharmony_ci		ver.release = MISDN_RELEASE;
3628c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
3638c2ecf20Sopenharmony_ci			err = -EFAULT;
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case IMGETCOUNT:
3668c2ecf20Sopenharmony_ci		id = get_mdevice_count();
3678c2ecf20Sopenharmony_ci		if (put_user(id, (int __user *)arg))
3688c2ecf20Sopenharmony_ci			err = -EFAULT;
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case IMGETDEVINFO:
3718c2ecf20Sopenharmony_ci		if (get_user(id, (int __user *)arg)) {
3728c2ecf20Sopenharmony_ci			err = -EFAULT;
3738c2ecf20Sopenharmony_ci			break;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci		dev = get_mdevice(id);
3768c2ecf20Sopenharmony_ci		if (dev) {
3778c2ecf20Sopenharmony_ci			struct mISDN_devinfo di;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci			memset(&di, 0, sizeof(di));
3808c2ecf20Sopenharmony_ci			di.id = dev->id;
3818c2ecf20Sopenharmony_ci			di.Dprotocols = dev->Dprotocols;
3828c2ecf20Sopenharmony_ci			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
3838c2ecf20Sopenharmony_ci			di.protocol = dev->D.protocol;
3848c2ecf20Sopenharmony_ci			memcpy(di.channelmap, dev->channelmap,
3858c2ecf20Sopenharmony_ci			       sizeof(di.channelmap));
3868c2ecf20Sopenharmony_ci			di.nrbchan = dev->nrbchan;
3878c2ecf20Sopenharmony_ci			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
3888c2ecf20Sopenharmony_ci			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
3898c2ecf20Sopenharmony_ci				err = -EFAULT;
3908c2ecf20Sopenharmony_ci		} else
3918c2ecf20Sopenharmony_ci			err = -ENODEV;
3928c2ecf20Sopenharmony_ci		break;
3938c2ecf20Sopenharmony_ci	default:
3948c2ecf20Sopenharmony_ci		if (sk->sk_state == MISDN_BOUND)
3958c2ecf20Sopenharmony_ci			err = data_sock_ioctl_bound(sk, cmd,
3968c2ecf20Sopenharmony_ci						    (void __user *)arg);
3978c2ecf20Sopenharmony_ci		else
3988c2ecf20Sopenharmony_ci			err = -ENOTCONN;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci	return err;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int data_sock_setsockopt(struct socket *sock, int level, int optname,
4048c2ecf20Sopenharmony_ci				sockptr_t optval, unsigned int len)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
4078c2ecf20Sopenharmony_ci	int err = 0, opt = 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
4108c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock,
4118c2ecf20Sopenharmony_ci		       level, optname, len);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	lock_sock(sk);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	switch (optname) {
4168c2ecf20Sopenharmony_ci	case MISDN_TIME_STAMP:
4178c2ecf20Sopenharmony_ci		if (copy_from_sockptr(&opt, optval, sizeof(int))) {
4188c2ecf20Sopenharmony_ci			err = -EFAULT;
4198c2ecf20Sopenharmony_ci			break;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		if (opt)
4238c2ecf20Sopenharmony_ci			_pms(sk)->cmask |= MISDN_TIME_STAMP;
4248c2ecf20Sopenharmony_ci		else
4258c2ecf20Sopenharmony_ci			_pms(sk)->cmask &= ~MISDN_TIME_STAMP;
4268c2ecf20Sopenharmony_ci		break;
4278c2ecf20Sopenharmony_ci	default:
4288c2ecf20Sopenharmony_ci		err = -ENOPROTOOPT;
4298c2ecf20Sopenharmony_ci		break;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci	release_sock(sk);
4328c2ecf20Sopenharmony_ci	return err;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int data_sock_getsockopt(struct socket *sock, int level, int optname,
4368c2ecf20Sopenharmony_ci				char __user *optval, int __user *optlen)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
4398c2ecf20Sopenharmony_ci	int len, opt;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (get_user(len, optlen))
4428c2ecf20Sopenharmony_ci		return -EFAULT;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (len != sizeof(char))
4458c2ecf20Sopenharmony_ci		return -EINVAL;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	switch (optname) {
4488c2ecf20Sopenharmony_ci	case MISDN_TIME_STAMP:
4498c2ecf20Sopenharmony_ci		if (_pms(sk)->cmask & MISDN_TIME_STAMP)
4508c2ecf20Sopenharmony_ci			opt = 1;
4518c2ecf20Sopenharmony_ci		else
4528c2ecf20Sopenharmony_ci			opt = 0;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		if (put_user(opt, optval))
4558c2ecf20Sopenharmony_ci			return -EFAULT;
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci	default:
4588c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	return 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int
4658c2ecf20Sopenharmony_cidata_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
4688c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
4698c2ecf20Sopenharmony_ci	struct sock *csk;
4708c2ecf20Sopenharmony_ci	int err = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (*debug & DEBUG_SOCKET)
4738c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
4748c2ecf20Sopenharmony_ci	if (addr_len != sizeof(struct sockaddr_mISDN))
4758c2ecf20Sopenharmony_ci		return -EINVAL;
4768c2ecf20Sopenharmony_ci	if (!maddr || maddr->family != AF_ISDN)
4778c2ecf20Sopenharmony_ci		return -EINVAL;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	lock_sock(sk);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (_pms(sk)->dev) {
4828c2ecf20Sopenharmony_ci		err = -EALREADY;
4838c2ecf20Sopenharmony_ci		goto done;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	_pms(sk)->dev = get_mdevice(maddr->dev);
4868c2ecf20Sopenharmony_ci	if (!_pms(sk)->dev) {
4878c2ecf20Sopenharmony_ci		err = -ENODEV;
4888c2ecf20Sopenharmony_ci		goto done;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (sk->sk_protocol < ISDN_P_B_START) {
4928c2ecf20Sopenharmony_ci		read_lock_bh(&data_sockets.lock);
4938c2ecf20Sopenharmony_ci		sk_for_each(csk, &data_sockets.head) {
4948c2ecf20Sopenharmony_ci			if (sk == csk)
4958c2ecf20Sopenharmony_ci				continue;
4968c2ecf20Sopenharmony_ci			if (_pms(csk)->dev != _pms(sk)->dev)
4978c2ecf20Sopenharmony_ci				continue;
4988c2ecf20Sopenharmony_ci			if (csk->sk_protocol >= ISDN_P_B_START)
4998c2ecf20Sopenharmony_ci				continue;
5008c2ecf20Sopenharmony_ci			if (IS_ISDN_P_TE(csk->sk_protocol)
5018c2ecf20Sopenharmony_ci			    == IS_ISDN_P_TE(sk->sk_protocol))
5028c2ecf20Sopenharmony_ci				continue;
5038c2ecf20Sopenharmony_ci			read_unlock_bh(&data_sockets.lock);
5048c2ecf20Sopenharmony_ci			err = -EBUSY;
5058c2ecf20Sopenharmony_ci			goto done;
5068c2ecf20Sopenharmony_ci		}
5078c2ecf20Sopenharmony_ci		read_unlock_bh(&data_sockets.lock);
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	_pms(sk)->ch.send = mISDN_send;
5118c2ecf20Sopenharmony_ci	_pms(sk)->ch.ctrl = mISDN_ctrl;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	switch (sk->sk_protocol) {
5148c2ecf20Sopenharmony_ci	case ISDN_P_TE_S0:
5158c2ecf20Sopenharmony_ci	case ISDN_P_NT_S0:
5168c2ecf20Sopenharmony_ci	case ISDN_P_TE_E1:
5178c2ecf20Sopenharmony_ci	case ISDN_P_NT_E1:
5188c2ecf20Sopenharmony_ci		mISDN_sock_unlink(&data_sockets, sk);
5198c2ecf20Sopenharmony_ci		err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
5208c2ecf20Sopenharmony_ci				     sk->sk_protocol, maddr);
5218c2ecf20Sopenharmony_ci		if (err)
5228c2ecf20Sopenharmony_ci			mISDN_sock_link(&data_sockets, sk);
5238c2ecf20Sopenharmony_ci		break;
5248c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_TE:
5258c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_NT:
5268c2ecf20Sopenharmony_ci		err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
5278c2ecf20Sopenharmony_ci				      sk->sk_protocol, maddr);
5288c2ecf20Sopenharmony_ci		break;
5298c2ecf20Sopenharmony_ci	case ISDN_P_B_RAW:
5308c2ecf20Sopenharmony_ci	case ISDN_P_B_HDLC:
5318c2ecf20Sopenharmony_ci	case ISDN_P_B_X75SLP:
5328c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DTMF:
5338c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSP:
5348c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSPHDLC:
5358c2ecf20Sopenharmony_ci		err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
5368c2ecf20Sopenharmony_ci				     sk->sk_protocol, maddr);
5378c2ecf20Sopenharmony_ci		break;
5388c2ecf20Sopenharmony_ci	default:
5398c2ecf20Sopenharmony_ci		err = -EPROTONOSUPPORT;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	if (err)
5428c2ecf20Sopenharmony_ci		goto done;
5438c2ecf20Sopenharmony_ci	sk->sk_state = MISDN_BOUND;
5448c2ecf20Sopenharmony_ci	_pms(sk)->ch.protocol = sk->sk_protocol;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cidone:
5478c2ecf20Sopenharmony_ci	release_sock(sk);
5488c2ecf20Sopenharmony_ci	return err;
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic int
5528c2ecf20Sopenharmony_cidata_sock_getname(struct socket *sock, struct sockaddr *addr,
5538c2ecf20Sopenharmony_ci		  int peer)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct sockaddr_mISDN	*maddr = (struct sockaddr_mISDN *) addr;
5568c2ecf20Sopenharmony_ci	struct sock		*sk = sock->sk;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (!_pms(sk)->dev)
5598c2ecf20Sopenharmony_ci		return -EBADFD;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	lock_sock(sk);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	maddr->family = AF_ISDN;
5648c2ecf20Sopenharmony_ci	maddr->dev = _pms(sk)->dev->id;
5658c2ecf20Sopenharmony_ci	maddr->channel = _pms(sk)->ch.nr;
5668c2ecf20Sopenharmony_ci	maddr->sapi = _pms(sk)->ch.addr & 0xff;
5678c2ecf20Sopenharmony_ci	maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
5688c2ecf20Sopenharmony_ci	release_sock(sk);
5698c2ecf20Sopenharmony_ci	return sizeof(*maddr);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic const struct proto_ops data_sock_ops = {
5738c2ecf20Sopenharmony_ci	.family		= PF_ISDN,
5748c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5758c2ecf20Sopenharmony_ci	.release	= data_sock_release,
5768c2ecf20Sopenharmony_ci	.ioctl		= data_sock_ioctl,
5778c2ecf20Sopenharmony_ci	.bind		= data_sock_bind,
5788c2ecf20Sopenharmony_ci	.getname	= data_sock_getname,
5798c2ecf20Sopenharmony_ci	.sendmsg	= mISDN_sock_sendmsg,
5808c2ecf20Sopenharmony_ci	.recvmsg	= mISDN_sock_recvmsg,
5818c2ecf20Sopenharmony_ci	.poll		= datagram_poll,
5828c2ecf20Sopenharmony_ci	.listen		= sock_no_listen,
5838c2ecf20Sopenharmony_ci	.shutdown	= sock_no_shutdown,
5848c2ecf20Sopenharmony_ci	.setsockopt	= data_sock_setsockopt,
5858c2ecf20Sopenharmony_ci	.getsockopt	= data_sock_getsockopt,
5868c2ecf20Sopenharmony_ci	.connect	= sock_no_connect,
5878c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
5888c2ecf20Sopenharmony_ci	.accept		= sock_no_accept,
5898c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap
5908c2ecf20Sopenharmony_ci};
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic int
5938c2ecf20Sopenharmony_cidata_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct sock *sk;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if (sock->type != SOCK_DGRAM)
5988c2ecf20Sopenharmony_ci		return -ESOCKTNOSUPPORT;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
6018c2ecf20Sopenharmony_ci	if (!sk)
6028c2ecf20Sopenharmony_ci		return -ENOMEM;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	sock_init_data(sock, sk);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	sock->ops = &data_sock_ops;
6078c2ecf20Sopenharmony_ci	sock->state = SS_UNCONNECTED;
6088c2ecf20Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	sk->sk_protocol = protocol;
6118c2ecf20Sopenharmony_ci	sk->sk_state    = MISDN_OPEN;
6128c2ecf20Sopenharmony_ci	mISDN_sock_link(&data_sockets, sk);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return 0;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int
6188c2ecf20Sopenharmony_cibase_sock_release(struct socket *sock)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
6238c2ecf20Sopenharmony_ci	if (!sk)
6248c2ecf20Sopenharmony_ci		return 0;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	mISDN_sock_unlink(&base_sockets, sk);
6278c2ecf20Sopenharmony_ci	sock_orphan(sk);
6288c2ecf20Sopenharmony_ci	sock_put(sk);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	return 0;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic int
6348c2ecf20Sopenharmony_cibase_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	int			err = 0, id;
6378c2ecf20Sopenharmony_ci	struct mISDNdevice	*dev;
6388c2ecf20Sopenharmony_ci	struct mISDNversion	ver;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	switch (cmd) {
6418c2ecf20Sopenharmony_ci	case IMGETVERSION:
6428c2ecf20Sopenharmony_ci		ver.major = MISDN_MAJOR_VERSION;
6438c2ecf20Sopenharmony_ci		ver.minor = MISDN_MINOR_VERSION;
6448c2ecf20Sopenharmony_ci		ver.release = MISDN_RELEASE;
6458c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
6468c2ecf20Sopenharmony_ci			err = -EFAULT;
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci	case IMGETCOUNT:
6498c2ecf20Sopenharmony_ci		id = get_mdevice_count();
6508c2ecf20Sopenharmony_ci		if (put_user(id, (int __user *)arg))
6518c2ecf20Sopenharmony_ci			err = -EFAULT;
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci	case IMGETDEVINFO:
6548c2ecf20Sopenharmony_ci		if (get_user(id, (int __user *)arg)) {
6558c2ecf20Sopenharmony_ci			err = -EFAULT;
6568c2ecf20Sopenharmony_ci			break;
6578c2ecf20Sopenharmony_ci		}
6588c2ecf20Sopenharmony_ci		dev = get_mdevice(id);
6598c2ecf20Sopenharmony_ci		if (dev) {
6608c2ecf20Sopenharmony_ci			struct mISDN_devinfo di;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci			memset(&di, 0, sizeof(di));
6638c2ecf20Sopenharmony_ci			di.id = dev->id;
6648c2ecf20Sopenharmony_ci			di.Dprotocols = dev->Dprotocols;
6658c2ecf20Sopenharmony_ci			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
6668c2ecf20Sopenharmony_ci			di.protocol = dev->D.protocol;
6678c2ecf20Sopenharmony_ci			memcpy(di.channelmap, dev->channelmap,
6688c2ecf20Sopenharmony_ci			       sizeof(di.channelmap));
6698c2ecf20Sopenharmony_ci			di.nrbchan = dev->nrbchan;
6708c2ecf20Sopenharmony_ci			strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
6718c2ecf20Sopenharmony_ci			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
6728c2ecf20Sopenharmony_ci				err = -EFAULT;
6738c2ecf20Sopenharmony_ci		} else
6748c2ecf20Sopenharmony_ci			err = -ENODEV;
6758c2ecf20Sopenharmony_ci		break;
6768c2ecf20Sopenharmony_ci	case IMSETDEVNAME:
6778c2ecf20Sopenharmony_ci	{
6788c2ecf20Sopenharmony_ci		struct mISDN_devrename dn;
6798c2ecf20Sopenharmony_ci		if (copy_from_user(&dn, (void __user *)arg,
6808c2ecf20Sopenharmony_ci				   sizeof(dn))) {
6818c2ecf20Sopenharmony_ci			err = -EFAULT;
6828c2ecf20Sopenharmony_ci			break;
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci		dn.name[sizeof(dn.name) - 1] = '\0';
6858c2ecf20Sopenharmony_ci		dev = get_mdevice(dn.id);
6868c2ecf20Sopenharmony_ci		if (dev)
6878c2ecf20Sopenharmony_ci			err = device_rename(&dev->dev, dn.name);
6888c2ecf20Sopenharmony_ci		else
6898c2ecf20Sopenharmony_ci			err = -ENODEV;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	break;
6928c2ecf20Sopenharmony_ci	default:
6938c2ecf20Sopenharmony_ci		err = -EINVAL;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	return err;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int
6998c2ecf20Sopenharmony_cibase_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
7028c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
7038c2ecf20Sopenharmony_ci	int err = 0;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (addr_len < sizeof(struct sockaddr_mISDN))
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (!maddr || maddr->family != AF_ISDN)
7098c2ecf20Sopenharmony_ci		return -EINVAL;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	lock_sock(sk);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (_pms(sk)->dev) {
7148c2ecf20Sopenharmony_ci		err = -EALREADY;
7158c2ecf20Sopenharmony_ci		goto done;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	_pms(sk)->dev = get_mdevice(maddr->dev);
7198c2ecf20Sopenharmony_ci	if (!_pms(sk)->dev) {
7208c2ecf20Sopenharmony_ci		err = -ENODEV;
7218c2ecf20Sopenharmony_ci		goto done;
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci	sk->sk_state = MISDN_BOUND;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cidone:
7268c2ecf20Sopenharmony_ci	release_sock(sk);
7278c2ecf20Sopenharmony_ci	return err;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic const struct proto_ops base_sock_ops = {
7318c2ecf20Sopenharmony_ci	.family		= PF_ISDN,
7328c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7338c2ecf20Sopenharmony_ci	.release	= base_sock_release,
7348c2ecf20Sopenharmony_ci	.ioctl		= base_sock_ioctl,
7358c2ecf20Sopenharmony_ci	.bind		= base_sock_bind,
7368c2ecf20Sopenharmony_ci	.getname	= sock_no_getname,
7378c2ecf20Sopenharmony_ci	.sendmsg	= sock_no_sendmsg,
7388c2ecf20Sopenharmony_ci	.recvmsg	= sock_no_recvmsg,
7398c2ecf20Sopenharmony_ci	.listen		= sock_no_listen,
7408c2ecf20Sopenharmony_ci	.shutdown	= sock_no_shutdown,
7418c2ecf20Sopenharmony_ci	.connect	= sock_no_connect,
7428c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
7438c2ecf20Sopenharmony_ci	.accept		= sock_no_accept,
7448c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap
7458c2ecf20Sopenharmony_ci};
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic int
7498c2ecf20Sopenharmony_cibase_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	struct sock *sk;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (sock->type != SOCK_RAW)
7548c2ecf20Sopenharmony_ci		return -ESOCKTNOSUPPORT;
7558c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_RAW))
7568c2ecf20Sopenharmony_ci		return -EPERM;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
7598c2ecf20Sopenharmony_ci	if (!sk)
7608c2ecf20Sopenharmony_ci		return -ENOMEM;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	sock_init_data(sock, sk);
7638c2ecf20Sopenharmony_ci	sock->ops = &base_sock_ops;
7648c2ecf20Sopenharmony_ci	sock->state = SS_UNCONNECTED;
7658c2ecf20Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
7668c2ecf20Sopenharmony_ci	sk->sk_protocol = protocol;
7678c2ecf20Sopenharmony_ci	sk->sk_state    = MISDN_OPEN;
7688c2ecf20Sopenharmony_ci	mISDN_sock_link(&base_sockets, sk);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	return 0;
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic int
7748c2ecf20Sopenharmony_cimISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	int err = -EPROTONOSUPPORT;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	switch (proto) {
7798c2ecf20Sopenharmony_ci	case ISDN_P_BASE:
7808c2ecf20Sopenharmony_ci		err = base_sock_create(net, sock, proto, kern);
7818c2ecf20Sopenharmony_ci		break;
7828c2ecf20Sopenharmony_ci	case ISDN_P_TE_S0:
7838c2ecf20Sopenharmony_ci	case ISDN_P_NT_S0:
7848c2ecf20Sopenharmony_ci	case ISDN_P_TE_E1:
7858c2ecf20Sopenharmony_ci	case ISDN_P_NT_E1:
7868c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_TE:
7878c2ecf20Sopenharmony_ci	case ISDN_P_LAPD_NT:
7888c2ecf20Sopenharmony_ci	case ISDN_P_B_RAW:
7898c2ecf20Sopenharmony_ci	case ISDN_P_B_HDLC:
7908c2ecf20Sopenharmony_ci	case ISDN_P_B_X75SLP:
7918c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DTMF:
7928c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSP:
7938c2ecf20Sopenharmony_ci	case ISDN_P_B_L2DSPHDLC:
7948c2ecf20Sopenharmony_ci		err = data_sock_create(net, sock, proto, kern);
7958c2ecf20Sopenharmony_ci		break;
7968c2ecf20Sopenharmony_ci	default:
7978c2ecf20Sopenharmony_ci		return err;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	return err;
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cistatic const struct net_proto_family mISDN_sock_family_ops = {
8048c2ecf20Sopenharmony_ci	.owner  = THIS_MODULE,
8058c2ecf20Sopenharmony_ci	.family = PF_ISDN,
8068c2ecf20Sopenharmony_ci	.create = mISDN_sock_create,
8078c2ecf20Sopenharmony_ci};
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ciint
8108c2ecf20Sopenharmony_cimisdn_sock_init(u_int *deb)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	int err;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	debug = deb;
8158c2ecf20Sopenharmony_ci	err = sock_register(&mISDN_sock_family_ops);
8168c2ecf20Sopenharmony_ci	if (err)
8178c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: error(%d)\n", __func__, err);
8188c2ecf20Sopenharmony_ci	return err;
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_civoid
8228c2ecf20Sopenharmony_cimisdn_sock_cleanup(void)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	sock_unregister(PF_ISDN);
8258c2ecf20Sopenharmony_ci}
826