/*
 *  linux/ibcs/socket.c
 *
 *  Copyright (C) 1994  Mike Jagdis (jaggy@purplet.demon.co.uk)
 *
 * $Id: socket.c,v 1.3 1994/04/26 16:16:39 mike Exp $
 * $Source: /var/CVS/ibcs/socket.c,v $
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/sys.h>
#include <asm/segment.h>

#include <ibcs/ibcs.h>
#include <ibcs/socket.h>
#include <ibcs/map.h>



int
ibcs_setsockopt(unsigned long *sp)
{
	int error;
	int level, optname;

	error = verify_area(VERIFY_READ,
			((unsigned long *)sp),
			5*sizeof(long));
	if (error)
		return error;

	/* As far as I can see SCO doesn't support anything other than
	 * socket level options and uses 0xffff for these.
	 * XXXXX This may need mapping.
	 */
	level = (int)get_fs_long(((unsigned long *)sp)+1);
	if (level != 0xffff)
		return -ENOPROTOOPT;
	put_fs_long(SOL_SOCKET, ((unsigned long *)sp)+1);

	optname = (int)get_fs_long(((unsigned long *)sp)+2);
	optname = map_value(sopt_map, optname, 0);
	put_fs_long(optname, ((unsigned long *)sp)+2);

	switch (optname) {
/* The following are all supported by Linux and seem to take compatible
 * arguments.
 */
		case SO_DEBUG:
		case SO_REUSEADDR:
		case SO_TYPE:
		case SO_ERROR:
		case SO_DONTROUTE:
		case SO_BROADCAST:
		case SO_SNDBUF:
		case SO_RCVBUF:
		case SO_KEEPALIVE:
		case SO_OOBINLINE:
		case SO_NO_CHECK:
		case SO_PRIORITY:
		case SO_LINGER:
		case SO_ACCEPTCONN:
			return SYS(socketcall)(SYS_SETSOCKOPT, sp);

/* The following are not currently implemented under Linux so we
 * must fake them in reasonable ways. (Only SO_PROTOTYPE is documented
 * in SCO's man page).
 */

		case SO_PROTOTYPE:
		case SO_ORDREL:
		case SO_SNDTIMEO:
		case SO_RCVTIMEO:
			return -ENOPROTOOPT;

		case SO_USELOOPBACK:
		case SO_SNDLOWAT:
		case SO_RCVLOWAT:
			return 0;

/* The following are not currenty implemented under Linux and probably
 * aren't settable anyway.
 */
		case SO_IMASOCKET:
			return -ENOPROTOOPT;
	}

	return -ENOPROTOOPT;
}


int
ibcs_getsockopt(unsigned long *sp)
{
	int error;
	int level, optname;

	error = verify_area(VERIFY_READ,
			((unsigned long *)sp),
			5*sizeof(long));
	if (error)
		return error;

	/* As far as I can see SCO doesn't support anything other than
	 * socket level options and uses 0xffff for these.
	 * XXXXX This may need mapping.
	 */
	level = (int)get_fs_long(((unsigned long *)sp)+1);
	if (level != 0xffff)
		return -ENOPROTOOPT;
	put_fs_long(SOL_SOCKET, ((unsigned long *)sp)+1);

	optname = (int)get_fs_long(((unsigned long *)sp)+2);
	optname = map_value(sopt_map, optname, 0);
	put_fs_long(optname, ((unsigned long *)sp)+2);

	switch (optname) {
/* The following are all supported by Linux and seem to take compatible
 * arguments.
 */
		case SO_DEBUG:
		case SO_REUSEADDR:
		case SO_TYPE:
		case SO_ERROR:
		case SO_DONTROUTE:
		case SO_BROADCAST:
		case SO_SNDBUF:
		case SO_RCVBUF:
		case SO_KEEPALIVE:
		case SO_OOBINLINE:
		case SO_NO_CHECK:
		case SO_PRIORITY:
		case SO_LINGER:
		case SO_ACCEPTCONN:
			return SYS(socketcall)(SYS_GETSOCKOPT, sp);

/* The following are not currently implemented under Linux so we
 * must fake them in reasonable ways. (Only SO_PROTOTYPE is documented
 * in SCO's man page).
 */

		case SO_PROTOTYPE: {
			char *optval = (char *)get_fs_long(((unsigned long *)sp)+0);

			error = verify_area(VERIFY_WRITE, (char *)optval,
					sizeof(long));
			if (!error)
				put_fs_long(0, optval);
			return error;
		}

		case SO_ORDREL:
		case SO_SNDTIMEO:
		case SO_RCVTIMEO:
			return -ENOPROTOOPT;

		case SO_USELOOPBACK:
		case SO_SNDLOWAT:
		case SO_RCVLOWAT:
		case SO_IMASOCKET: {
			char *optval = (char *)get_fs_long(((unsigned long *)sp)+0);

			error = verify_area(VERIFY_WRITE, (char *)optval,
					sizeof(long));
			if (!error)
				put_fs_long(1, optval);
			return error;
		}
	}

	return -ENOPROTOOPT;
}
