/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <sys/sysmacros.h>
#include <sys/strsubr.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/vfs.h>
#include <inet/sdp_itf.h>
#include <fs/sockfs/sockcommon.h>
#include "socksdp.h"

struct sonode *socksdp_create(struct sockparams *, int, int, int,
    int, int, int *, cred_t *);
static void socksdp_destroy(struct sonode *);

static __smod_priv_t sosdp_priv = {
	socksdp_create,
	socksdp_destroy,
	NULL
};

static smod_reg_t sinfo = {
	SOCKMOD_VERSION,
	"socksdp",
	SOCK_UC_VERSION,
	SOCK_DC_VERSION,
	NULL,
	&sosdp_priv
};

/*
 * Module linkage information for the kernel
 */
static struct modlsockmod modlsockmod = {
	&mod_sockmodops, "SDP socket module", &sinfo
};

static struct modlinkage modlinkage = {
	MODREV_1,
	&modlsockmod,
	NULL
};

/*
 * Creates a sdp socket data structure.
 */
/* ARGSUSED */
struct sonode *
socksdp_create(struct sockparams *sp, int family, int type, int protocol,
		    int version, int sflags, int *errorp, cred_t *cr)
{
	struct sonode *so;
	int kmflags = (sflags & SOCKET_NOSLEEP) ? KM_NOSLEEP : KM_SLEEP;

	dprint(4, ("Inside sosdp_create: domain:%d proto:%d type:%d",
	    family, protocol, type));

	*errorp = 0;
	if (is_system_labeled()) {
		*errorp = EOPNOTSUPP;
		return (NULL);
	}

	if (version == SOV_STREAM) {
		*errorp = EINVAL;
		return (NULL);
	}

	/*
	 * We only support one type of SDP socket.  Let sotpi_create()
	 * handle all other cases, such as raw socket.
	 */
	if (!(family == AF_INET || family == AF_INET6) ||
	    !(type == SOCK_STREAM)) {
		*errorp = EINVAL;
		return (NULL);
	}

	so = kmem_cache_alloc(socket_cache, kmflags);
	if (so == NULL) {
		*errorp = ENOMEM;
		return (NULL);
	}

	sonode_init(so, sp, family, type, protocol, &sosdp_sonodeops);
	so->so_pollev |= SO_POLLEV_ALWAYS;

	dprint(2, ("sosdp_create: %p domain %d type %d\n", (void *)so, family,
	    type));

	if (version == SOV_DEFAULT) {
		version = so_default_version;
	}
	so->so_version = (short)version;

	/*
	 * set the default values to be INFPSZ
	 * if a protocol desires it can change the value later
	 */
	so->so_proto_props.sopp_rxhiwat = SOCKET_RECVHIWATER;
	so->so_proto_props.sopp_rxlowat = SOCKET_RECVLOWATER;
	so->so_proto_props.sopp_maxpsz = INFPSZ;
	so->so_proto_props.sopp_maxblk = INFPSZ;

	return (so);
}

static void
socksdp_destroy(struct sonode *so)
{
	ASSERT(so->so_ops == &sosdp_sonodeops);

	sosdp_fini(so, CRED());

	kmem_cache_free(socket_cache, so);
}

int
_init(void)
{
	return (mod_install(&modlinkage));
}

int
_fini(void)
{
	return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}