Phonet: support active connection without pipe controller on modem

This provides support for newer ISI modems with no need for the
earlier experimental compile-time alternative choice. With this,
we can now use the same kernel and userspace with both types of
modems.

This also avoids confusing two different and incompatible state
machines, actively connected vs accepted sockets, and adds
connection response error handling (processing "SYN/RST" of sorts).

Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Rémi Denis-Courmont
2011-03-08 22:44:12 +00:00
committed by David S. Miller
parent acaf7df610
commit 297edb6003
3 changed files with 167 additions and 164 deletions

View File

@@ -225,15 +225,18 @@ static int pn_socket_autobind(struct socket *sock)
return 0; /* socket was already bound */
}
#ifdef CONFIG_PHONET_PIPECTRLR
static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,
int len, int flags)
{
struct sock *sk = sock->sk;
struct pn_sock *pn = pn_sk(sk);
struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
long timeo;
struct task_struct *tsk = current;
long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
int err;
if (pn_socket_autobind(sock))
return -ENOBUFS;
if (len < sizeof(struct sockaddr_pn))
return -EINVAL;
if (spn->spn_family != AF_PHONET)
@@ -243,82 +246,61 @@ static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,
switch (sock->state) {
case SS_UNCONNECTED:
sk->sk_state = TCP_CLOSE;
if (sk->sk_state != TCP_CLOSE) {
err = -EISCONN;
goto out;
}
break;
case SS_CONNECTING:
switch (sk->sk_state) {
case TCP_SYN_RECV:
sock->state = SS_CONNECTED;
err = -EISCONN;
goto out;
case TCP_CLOSE:
err = -EALREADY;
if (flags & O_NONBLOCK)
goto out;
goto wait_connect;
}
break;
case SS_CONNECTED:
switch (sk->sk_state) {
case TCP_SYN_RECV:
err = -EISCONN;
goto out;
case TCP_CLOSE:
sock->state = SS_UNCONNECTED;
break;
}
break;
case SS_DISCONNECTING:
case SS_FREE:
break;
err = -EALREADY;
goto out;
default:
err = -EISCONN;
goto out;
}
sk->sk_state = TCP_CLOSE;
sk_stream_kill_queues(sk);
pn->dobject = pn_sockaddr_get_object(spn);
pn->resource = pn_sockaddr_get_resource(spn);
sock->state = SS_CONNECTING;
err = sk->sk_prot->connect(sk, addr, len);
if (err < 0) {
if (err) {
sock->state = SS_UNCONNECTED;
sk->sk_state = TCP_CLOSE;
pn->dobject = 0;
goto out;
}
err = -EINPROGRESS;
wait_connect:
if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK))
goto out;
while (sk->sk_state == TCP_SYN_SENT) {
DEFINE_WAIT(wait);
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
release_sock(sk);
if (!timeo) {
err = -EINPROGRESS;
goto out;
}
if (signal_pending(tsk)) {
err = sock_intr_errno(timeo);
goto out;
}
err = -ERESTARTSYS;
timeo = wait_event_interruptible_timeout(*sk_sleep(sk),
sk->sk_state != TCP_CLOSE,
timeo);
lock_sock(sk);
if (timeo < 0)
goto out; /* -ERESTARTSYS */
err = -ETIMEDOUT;
if (timeo == 0 && sk->sk_state != TCP_SYN_RECV)
goto out;
if (sk->sk_state != TCP_SYN_RECV) {
sock->state = SS_UNCONNECTED;
err = sock_error(sk);
if (!err)
err = -ECONNREFUSED;
goto out;
prepare_to_wait_exclusive(sk_sleep(sk), &wait,
TASK_INTERRUPTIBLE);
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
finish_wait(sk_sleep(sk), &wait);
}
sock->state = SS_CONNECTED;
err = 0;
if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED))
err = 0;
else if (sk->sk_state == TCP_CLOSE_WAIT)
err = -ECONNRESET;
else
err = -ECONNREFUSED;
sock->state = err ? SS_UNCONNECTED : SS_CONNECTED;
out:
release_sock(sk);
return err;
}
#endif
static int pn_socket_accept(struct socket *sock, struct socket *newsock,
int flags)
@@ -486,11 +468,7 @@ const struct proto_ops phonet_stream_ops = {
.owner = THIS_MODULE,
.release = pn_socket_release,
.bind = pn_socket_bind,
#ifdef CONFIG_PHONET_PIPECTRLR
.connect = pn_socket_connect,
#else
.connect = sock_no_connect,
#endif
.socketpair = sock_no_socketpair,
.accept = pn_socket_accept,
.getname = pn_socket_getname,