/*
* linux/fs/ncpfs/sock.c
*
* Copyright (C) 1992, 1993 Rick Sladkey
*
* Modified 1995, 1996 by Volker Lendecke to be usable for ncp
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
*
*/
#include <linux/config.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <asm/uaccess.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/signal.h>
#include <net/scm.h>
#include <net/sock.h>
#include <linux/ipx.h>
#include <linux/poll.h>
#include <linux/file.h>
#include <linux/ncp_fs.h>
#include "ncpsign_kernel.h"
static int _recv(struct socket *sock, void *buf, int size, unsigned flags)
{
struct msghdr msg = {NULL, };
struct kvec iov = {buf, size};
return kernel_recvmsg(sock, &msg, &iov, 1, size, flags);
}
static inline int do_send(struct socket *sock, struct kvec *vec, int count,
int len, unsigned flags)
{
struct msghdr msg = { .msg_flags = flags };
return kernel_sendmsg(sock, &msg, vec, count, len);
}
static int _send(struct socket *sock, const void *buff, int len)
{
struct kvec vec;
vec.iov_base = (void *) buff;
vec.iov_len = len;
return do_send(sock, &vec, 1, len, 0);
}
struct ncp_request_reply {
struct list_head req;
wait_queue_head_t wq;
struct ncp_reply_header* reply_buf;
size_t datalen;
int result;
enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status;
struct kvec* tx_ciov;
size_t tx_totallen;
size_t tx_iovlen;
struct kvec tx_iov[3];
u_int16_t tx_type;
u_int32_t sign[6];
};
void ncp_tcp_data_ready(struct sock *sk, int len)
{
struct ncp_server *server = sk->sk_user_data;
server->data_ready(sk, len);
schedule_work(&server->rcv.tq);
}
void ncp_tcp_error_report(struct sock *sk)
{
struct ncp_server *server = sk->sk_user_data;
server->error_report(sk);
schedule_work(&server->rcv.tq);
}
void ncp_tcp_write_space(struct sock *sk)
{
struct ncp_server *server = sk->sk_user_data;
/* We do not need any locking: we first set tx.creq, and then we do sendmsg,
not vice versa... */
server->write_space(sk);
if (server->tx.creq)
schedule_work(&server->tx.tq);
}
void ncpdgram_timeout_call(unsigned long v)
{
struct ncp_server *server = (void*)v;
schedule_work(&server->timeout_tq);
}
static inline void ncp_finish_request(struct ncp_request_reply *req, int result)
{
req->result = result;
req->status = RQ_DONE;
wake_up_all(&req->wq);
}
static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err)
{
struct ncp_request_reply *req;
ncp_invalidate_conn(server);
del_timer(&server->timeout_tm);
while (!list_empty(&server->tx.requests)) {
req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
list_del_init(&req->req);
if (req == aborted) {
ncp_finish_request(req, err);
} else {
ncp_finish_request(req, -EIO);
}
}
req = server->rcv.creq;
if (req) {
server->rcv.creq = NULL;
if (req == aborted) {
ncp_finish_request(req, err);
} else {
ncp_finish_request(req, -EIO);
}
server->rcv.ptr = NULL;
server->rcv.state = 0;
}
req = server->tx.creq;
if (req) {
server->tx.creq = NULL;
if (req == aborted) {
ncp_finish_request(req, err);
} else {
ncp_finish_request(req, -EIO);
}
}
}
static inline int get_conn_number(struct ncp_reply_header *rp)
{
return rp->conn_low | (rp->conn_high << 8);
}
static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
{
/* If req is done, we got signal, but we also received answer... */
switch (req->status) {
case RQ_IDLE:
case RQ_DONE:
break;
case RQ_QUEUED:
list_del_init(&req->req);
ncp_finish_request(req, err);
break