dect
/
linux-2.6
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/net/dect/af_dect.c

457 lines
9.7 KiB
C

/*
* DECT sockets
*
* Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/poll.h>
#include <linux/dect.h>
#include <net/sock.h>
#include <net/dect/dect.h>
static struct dect_proto *dect_protos[DECT_PROTO_NUM];
static DEFINE_SPINLOCK(dect_proto_lock);
void (*dect_raw_rcv_hook)(struct sk_buff *skb);
EXPORT_SYMBOL_GPL(dect_raw_rcv_hook);
int dect_proto_register(struct dect_proto *proto)
{
int err;
err = proto_register(&proto->proto, true);
if (err < 0)
return err;
spin_lock(&dect_proto_lock);
dect_protos[proto->protocol] = proto;
spin_unlock(&dect_proto_lock);
return 0;
}
EXPORT_SYMBOL_GPL(dect_proto_register);
void dect_proto_unregister(struct dect_proto *proto)
{
spin_lock(&dect_proto_lock);
dect_protos[proto->protocol] = NULL;
spin_unlock(&dect_proto_lock);
proto_unregister(&proto->proto);
}
EXPORT_SYMBOL_GPL(dect_proto_unregister);
struct sk_buff *dect_alloc_notification(u32 type, const void *data,
unsigned int size)
{
struct sk_buff *skb;
skb = alloc_skb(size, GFP_ATOMIC);
if (skb == NULL)
return NULL;
DECT_NOTIFY_CB(skb)->type = type;
memcpy(skb_put(skb, size), data, size);
return skb;
}
EXPORT_SYMBOL_GPL(dect_alloc_notification);
static void dect_destruct(struct sock *sk)
{
__skb_queue_purge(&sk->sk_receive_queue);
__skb_queue_purge(&sk->sk_error_queue);
__skb_queue_purge(&sk->sk_write_queue);
}
static int dect_release(struct socket *sock)
{
struct sock *sk = sock->sk;
long timeout;
if (sk == NULL)
return 0;
timeout = 0;
if (sock_flag(sk, SOCK_LINGER) && !(current->flags & PF_EXITING))
timeout = sk->sk_lingertime;
sock->sk = NULL;
sk->sk_prot->close(sk, timeout);
return 0;
}
static int dect_bind(struct socket *sock, struct sockaddr *uaddr, int len)
{
struct sock *sk = sock->sk;
int err;
err = 0;
if (sk->sk_prot->bind != NULL)
err = sk->sk_prot->bind(sk, uaddr, len);
return err;
}
static int dect_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
int err;
lock_sock(sk);
err = -EINVAL;
if (sock->state != SS_UNCONNECTED ||
(sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET))
goto out;
if (sk->sk_state != DECT_SK_RELEASED && sk->sk_state != DECT_SK_LISTEN)
goto out;
if (sk->sk_state != DECT_SK_LISTEN)
sk->sk_prot->hash(sk);
sk->sk_max_ack_backlog = backlog;
err = 0;
out:
release_sock(sk);
return err;
}
static int dect_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct sock *sk = sock->sk, *newsk;
int err;
newsk = sk->sk_prot->accept(sk, flags, &err);
if (newsk == NULL)
return err;
lock_sock(newsk);
sock_graft(newsk, newsock);
newsock->state = SS_CONNECTED;
release_sock(newsk);
return 0;
}
static unsigned int dect_poll(struct file *file, struct socket *sock,
struct poll_table_struct *wait)
{
struct sock *sk = sock->sk;
unsigned int mask;
poll_wait(file, sk_sleep(sk), wait);
mask = 0;
if (sk->sk_state == DECT_SK_LISTEN) {
if (!hlist_empty(&dect_csk(sk)->accept_queue))
return POLLIN | POLLRDNORM;
return 0;
}
/* exceptional events? */
if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
mask |= POLLERR;
if (sk->sk_shutdown & RCV_SHUTDOWN)
mask |= POLLRDHUP;
if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= POLLHUP;
/* readable? */
if (!skb_queue_empty(&sk->sk_receive_queue) ||
(sk->sk_shutdown & RCV_SHUTDOWN))
mask |= POLLIN | POLLRDNORM;
/* Connection-based need to check for termination and startup */
if (sk->sk_state == DECT_SK_RELEASED)
mask |= POLLHUP;
/* connection hasn't started yet? */
if (sk->sk_state == DECT_SK_ESTABLISH_PENDING)
return mask;
/* writable? */
if (sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
return mask;
}
static int dect_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int err = 0;
how++;
if ((how & ~SHUTDOWN_MASK) || !how)
return -EINVAL;
lock_sock(sk);
if (sock->state == SS_CONNECTING &&
sk->sk_state == DECT_SK_ESTABLISH_PENDING)
sock->state = SS_DISCONNECTING;
switch (sk->sk_state) {
case DECT_SK_RELEASED:
err = -ENOTCONN;
break;
case DECT_SK_LISTEN:
if (!(how & RCV_SHUTDOWN))
break;
default:
sk->sk_shutdown |= how;
if (sk->sk_prot->shutdown != NULL)
sk->sk_prot->shutdown(sk, how);
}
/* wake up processes sleeping in poll() */
sk->sk_state_change(sk);
release_sock(sk);
return err;
}
static int dect_connect(struct socket *sock, struct sockaddr *uaddr, int len,
int flags)
{
struct sock *sk = sock->sk;
long timeo;
int err;
lock_sock(sk);
switch (sock->state) {
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;
goto out;
case SS_UNCONNECTED:
err = -EISCONN;
if (sk->sk_state != DECT_SK_RELEASED)
goto out;
err = sk->sk_prot->connect(sk, uaddr, len);
if (err < 0)
goto out;
sock->state = SS_CONNECTING;
err = -EINPROGRESS;
break;
default:
err = -EINVAL;
goto out;
}
if (sk->sk_state == DECT_SK_ESTABLISH_PENDING) {
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
err = sk_stream_wait_connect(sk, &timeo);
if (err < 0)
goto out;
err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
}
/* Connection establishment was aborted or failed */
if (sk->sk_state == DECT_SK_RELEASED)
goto sock_error;
sock->state = SS_CONNECTED;
err = 0;
out:
release_sock(sk);
return err;
sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;
goto out;
}
static int dect_getname(struct socket *sock, struct sockaddr *uaddr, int *len,
int peer)
{
const struct dect_proto *p;
/* AF_DECT uses different address formats for the different SAPs */
p = container_of(sock->sk->sk_prot, struct dect_proto, proto);
if (p->getname != NULL)
return p->getname(sock->sk, uaddr, len, peer);
*len = 0;
return 0;
}
static int dect_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
static int dect_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err;
if (level != SOL_DECT)
return -ENOPROTOOPT;
switch (optname) {
default:
if (sk->sk_prot->setsockopt)
err = sk->sk_prot->setsockopt(sk, level, optname,
optval, optlen);
else
err = -ENOPROTOOPT;
}
return err;
}
static int dect_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
int err;
if (level != SOL_DECT)
return -ENOPROTOOPT;
switch (optname) {
default:
if (sk->sk_prot->getsockopt)
err = sk->sk_prot->getsockopt(sk, level, optname,
optval, optlen);
else
err = -ENOPROTOOPT;
}
return err;
}
static int dect_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct dect_proto *p;
struct sock *sk;
int err = 0;
if (protocol < 0 || protocol >= DECT_PROTO_NUM)
return -EPROTONOSUPPORT;
#ifdef CONFIG_MODULES
if (dect_protos[protocol] == NULL) {
err = request_module("net-pf-%d-proto-%d", PF_DECT, protocol);
if (err < 0)
return err;
}
#endif
spin_lock(&dect_proto_lock);
p = dect_protos[protocol];
if (p != NULL && !try_module_get(p->proto.owner))
p = NULL;
spin_unlock(&dect_proto_lock);
if (p == NULL)
return -EPROTONOSUPPORT;
if (p->type != sock->type) {
err = -EPROTONOSUPPORT;
goto err;
}
if (cap_valid(p->capability) && !capable(p->capability)) {
err = -EACCES;
goto err;
}
sock->state = SS_UNCONNECTED;
sock->ops = p->ops;
sk = sk_alloc(net, PF_DECT, GFP_KERNEL, &p->proto);
if (sk == NULL) {
err = -ENOMEM;
goto err;
}
sock_init_data(sock, sk);
sk->sk_protocol = protocol;
sk->sk_destruct = dect_destruct;
if (sk->sk_prot->init != NULL) {
err = sk->sk_prot->init(sk);
if (err < 0) {
sock_orphan(sk);
sock_put(sk);
}
}
err:
module_put(p->proto.owner);
return err;
}
const struct proto_ops dect_stream_ops = {
.family = PF_DECT,
.owner = THIS_MODULE,
.release = dect_release,
.bind = dect_bind,
.connect = dect_connect,
.socketpair = sock_no_socketpair,
.getname = dect_getname,
.poll = dect_poll,
.ioctl = sock_no_ioctl,
.listen = dect_listen,
.accept = dect_accept,
.shutdown = dect_shutdown,
.setsockopt = dect_setsockopt,
.getsockopt = dect_getsockopt,
.sendmsg = dect_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
EXPORT_SYMBOL_GPL(dect_stream_ops);
const struct proto_ops dect_dgram_ops = {
.family = PF_DECT,
.owner = THIS_MODULE,
.release = dect_release,
.bind = dect_bind,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.getname = dect_getname,
.poll = datagram_poll,
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
.accept = sock_no_accept,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.sendmsg = dect_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
EXPORT_SYMBOL_GPL(dect_dgram_ops);
static struct net_proto_family dect_family_ops = {
.family = PF_DECT,
.create = dect_create,
.owner = THIS_MODULE,
};
int __init dect_af_module_init(void)
{
return sock_register(&dect_family_ops);
}
void dect_af_module_exit(void)
{
sock_unregister(PF_DECT);
}
MODULE_ALIAS_NETPROTO(PF_DECT);