/*
 * Copyright (C) 2006-2007 QLogic Corporation, All rights reserved.
 * Copyright (c) 2005. PathScale, Inc. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Patent licenses, if any, provided herein do not apply to
 * combinations of this program with other software, or any other
 * product whatsoever.
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "ipathverbs.h"
#include "ipath-abi.h"

static void ipath_free_context(struct ibv_context *ibctx);

#ifndef PCI_VENDOR_ID_PATHSCALE
#define PCI_VENDOR_ID_PATHSCALE			0x1fc1
#endif

#ifndef PCI_VENDOR_ID_QLOGIC
#define PCI_VENDOR_ID_QLOGIC			0x1077
#endif

#ifndef PCI_DEVICE_ID_INFINIPATH_HT
#define PCI_DEVICE_ID_INFINIPATH_HT		0x000d
#endif

#ifndef PCI_DEVICE_ID_INFINIPATH_PE800
#define PCI_DEVICE_ID_INFINIPATH_PE800		0x0010
#endif

#ifndef PCI_DEVICE_ID_INFINIPATH_6220
#define PCI_DEVICE_ID_INFINIPATH_6220		0x6220
#endif

#ifndef PCI_DEVICE_ID_INFINIPATH_7220
#define PCI_DEVICE_ID_INFINIPATH_7220		0x7220
#endif

#ifndef PCI_DEVICE_ID_INFINIPATH_7322
#define PCI_DEVICE_ID_INFINIPATH_7322		0x7322
#endif

#define HCA(v, d)                                                              \
	VERBS_PCI_MATCH(PCI_VENDOR_ID_##v, PCI_DEVICE_ID_INFINIPATH_##d, NULL)
static const struct verbs_match_ent hca_table[] = {
	VERBS_DRIVER_ID(RDMA_DRIVER_QIB),
	HCA(PATHSCALE,	HT),
	HCA(PATHSCALE,	PE800),
	HCA(QLOGIC,	6220),
	HCA(QLOGIC,	7220),
	HCA(QLOGIC,	7322),
	{}
};

static const struct verbs_context_ops ipath_ctx_common_ops = {
	.free_context	= ipath_free_context,
	.query_device_ex = ipath_query_device,
	.query_port	= ipath_query_port,

	.alloc_pd	= ipath_alloc_pd,
	.dealloc_pd	= ipath_free_pd,

	.reg_mr		= ipath_reg_mr,
	.dereg_mr	= ipath_dereg_mr,

	.create_cq	= ipath_create_cq,
	.poll_cq	= ipath_poll_cq,
	.req_notify_cq	= ibv_cmd_req_notify_cq,
	.resize_cq	= ipath_resize_cq,
	.destroy_cq	= ipath_destroy_cq,

	.create_srq	= ipath_create_srq,
	.modify_srq	= ipath_modify_srq,
	.query_srq	= ipath_query_srq,
	.destroy_srq	= ipath_destroy_srq,
	.post_srq_recv	= ipath_post_srq_recv,

	.create_qp	= ipath_create_qp,
	.query_qp	= ipath_query_qp,
	.modify_qp	= ipath_modify_qp,
	.destroy_qp	= ipath_destroy_qp,

	.post_send	= ipath_post_send,
	.post_recv	= ipath_post_recv,

	.create_ah	= ipath_create_ah,
	.destroy_ah	= ipath_destroy_ah,

	.attach_mcast	= ibv_cmd_attach_mcast,
	.detach_mcast	= ibv_cmd_detach_mcast
};

static const struct verbs_context_ops ipath_ctx_v1_ops = {
	.create_cq = ipath_create_cq_v1,
	.poll_cq = ibv_cmd_poll_cq,
	.resize_cq = ipath_resize_cq_v1,
	.destroy_cq = ipath_destroy_cq_v1,
	.create_srq = ipath_create_srq_v1,
	.destroy_srq = ipath_destroy_srq_v1,
	.modify_srq = ipath_modify_srq_v1,
	.post_srq_recv = ibv_cmd_post_srq_recv,
	.create_qp = ipath_create_qp_v1,
	.destroy_qp = ipath_destroy_qp_v1,
	.post_recv = ibv_cmd_post_recv,
};

static struct verbs_context *ipath_alloc_context(struct ibv_device *ibdev,
						 int cmd_fd,
						 void *private_data)
{
	struct ipath_context	    *context;
	struct ibv_get_context       cmd;
	struct ib_uverbs_get_context_resp  resp;
	struct ipath_device         *dev;

	context = verbs_init_and_alloc_context(ibdev, cmd_fd, context, ibv_ctx,
					       RDMA_DRIVER_QIB);
	if (!context)
		return NULL;

	if (ibv_cmd_get_context(&context->ibv_ctx, &cmd,
				sizeof cmd, &resp, sizeof resp))
		goto err_free;

	verbs_set_ops(&context->ibv_ctx, &ipath_ctx_common_ops);
	dev = to_idev(ibdev);
	if (dev->abi_version == 1)
		verbs_set_ops(&context->ibv_ctx, &ipath_ctx_v1_ops);
	return &context->ibv_ctx;

err_free:
	verbs_uninit_context(&context->ibv_ctx);
	free(context);
	return NULL;
}

static void ipath_free_context(struct ibv_context *ibctx)
{
	struct ipath_context *context = to_ictx(ibctx);

	verbs_uninit_context(&context->ibv_ctx);
	free(context);
}

static void ipath_uninit_device(struct verbs_device *verbs_device)
{
	struct ipath_device *dev = to_idev(&verbs_device->device);

	free(dev);
}

static struct verbs_device *
ipath_device_alloc(struct verbs_sysfs_dev *sysfs_dev)
{
	struct ipath_device    *dev;

	dev = calloc(1, sizeof(*dev));
	if (!dev)
		return NULL;

	dev->abi_version = sysfs_dev->abi_ver;

	return &dev->ibv_dev;
}

static const struct verbs_device_ops ipath_dev_ops = {
	.name = "ipathverbs",
	.match_min_abi_version = 0,
	.match_max_abi_version = INT_MAX,
	.match_table = hca_table,
	.alloc_device = ipath_device_alloc,
	.uninit_device  = ipath_uninit_device,
	.alloc_context = ipath_alloc_context,
};
PROVIDER_DRIVER(ipathverbs, ipath_dev_ops);
