/*
 * stub.c -- HCI-layer stubs for the l2uh_harness KLD (DF-5749).
 *
 * The production netbt files compiled verbatim into this module are
 *   l2cap_upper.c  (the file under audit)
 *   l2cap_lower.c  (l2cap_close / l2cap_start)
 *   l2cap_misc.c   (l2cap_request_alloc/free, l2cap_rtx, l2cap_cid_alloc)
 *   l2cap_signal.c (signalling)
 * Everything they need from the layers we do NOT compile (hci_link.c,
 * hci_event.c, hci_unit.c, hci_socket.c, bt_proto.c, l2cap_socket.c) is
 * provided here with the minimum faithful behaviour:
 *
 *   - M_BLUETOOTH malloc type (lives in bt_proto.c normally)
 *   - l2cap_pdu_pool / l2cap_req_pool vm_zones (bt_proto.c netbt_init)
 *   - hci_acl_close(): production hci_link.c:153-164 semantics minus the
 *     expiry-callout path: KKASSERT(link != NULL), drop the reference,
 *     kfree when it reaches zero with the link CLOSED (the immediate-free
 *     branch).  The NULL-deref sink of the double close
 *     (--link->hl_refcnt with link == NULL) is exactly the production
 *     code path.
 *   - hci_acl_send()/hci_acl_setmode()/hci_acl_open()/hci_route_lookup()/
 *     hci_unit_lookup(): symbols only; never on the raced path.
 *
 * The channel upper layer (what l2cap_socket.c provides in production)
 * is a fake btproto with counters.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <net/if.h>

#include <netbt/bluetooth.h>
#include <netbt/hci.h>
#include <netbt/l2cap.h>

#include "l2uh.h"

/* real malloc type lives in bt_proto.c which we do not compile in */
MALLOC_DEFINE(M_BLUETOOTH, "Bluetooth", "Bluetooth system memory");

/* zones: l2cap_pdu_pool / l2cap_req_pool are defined by the VERBATIM
 * l2cap_misc.c compiled into this module; harness.c zinits them at
 * MOD_LOAD (what netbt_init() in bt_proto.c does in production). */

/* counters for the fake upper layer (what l2cap_socket.c would do) */
int stub_proto_disconnected_count;
int stub_proto_connecting_count;

/* production hci_link.c:60 -- referenced by the harness diagnostics */
int hci_acl_expiry = 10;

static void stub_connecting(void *);
static void stub_connected(void *);
static void stub_disconnected(void *, int);
static void *stub_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
static void stub_complete(void *, int);
static void stub_linkmode(void *, int);
static void stub_input(void *, struct mbuf *);

const struct btproto stub_btproto = {
	stub_connecting,
	stub_connected,
	stub_disconnected,
	stub_newconn,
	stub_complete,
	stub_linkmode,
	stub_input,
};

static void
stub_connecting(void *arg)
{
	stub_proto_connecting_count++;
}

static void
stub_connected(void *arg)
{
}

static void
stub_disconnected(void *arg, int err)
{
	stub_proto_disconnected_count++;
}

static void *
stub_newconn(void *arg, struct sockaddr_bt *laddr, struct sockaddr_bt *raddr)
{
	return NULL;
}

static void
stub_complete(void *arg, int count)
{
}

static void
stub_linkmode(void *arg, int new)
{
}

static void
stub_input(void *arg, struct mbuf *m)
{
	m_freem(m);
}

/* ------------------------------------------------------------------ */
/* fake hci_link allocator: production hci_link_alloc() minus the unit
 * list attach (no struct hci_unit needed).  Refcount 0, state CLOSED. */
struct hci_link *
stub_link_new(void)
{
	struct hci_link *link;

	link = kmalloc(sizeof(*link), M_BLUETOOTH, M_NOWAIT | M_ZERO);
	if (link == NULL)
		return NULL;

	link->hl_state = HCI_LINK_CLOSED;
	TAILQ_INIT(&link->hl_txq);
	TAILQ_INIT(&link->hl_reqs);
	link->hl_mtu = L2CAP_MTU_DEFAULT;

	return link;
}

/* ------------------------------------------------------------------ */
/* HCI-layer symbols used by the compiled production files */

void
hci_acl_close(struct hci_link *link, int err)
{
	/* production hci_link.c:153-164 (immediate-free branch):
	 * KKASSERT(link != NULL) -- a no-op on production GENERIC, so a
	 * NULL link reaches "--link->hl_refcnt" and faults. */
	KKASSERT(link != NULL);

	if (--link->hl_refcnt == 0)
		kfree(link, M_BLUETOOTH);
}

int
hci_acl_send(struct mbuf *m, struct hci_link *link, struct l2cap_channel *chan)
{
	m_freem(m);
	return 0;
}

int
hci_acl_setmode(struct hci_link *link)
{
	return 0;
}

struct hci_link *
hci_acl_open(struct hci_unit *unit, bdaddr_t *bdaddr)
{
	/* the harness builds the channel state directly (what
	 * l2cap_connect() + hci_acl_open() produce), this is never called */
	return NULL;
}

int
hci_route_lookup(bdaddr_t *laddr, bdaddr_t *raddr)
{
	return EHOSTUNREACH;
}

struct hci_unit *
hci_unit_lookup(bdaddr_t *bdaddr)
{
	return NULL;
}
