Subject: Re: Bluetooth module on com(4)
To: None <plunky@rya-online.net>
From: KIYOHARA Takashi <kiyohara@kk.iij4u.or.jp>
List: tech-kern
Date: 01/17/2007 01:38:47
----Next_Part(Wed_Jan_17_01_38_47_2007_363)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi! Iain,


From: Iain Hibbert <plunky@rya-online.net>
Date: Thu, 11 Jan 2007 19:13:50 +0000 (GMT)

> On Thu, 11 Jan 2007, KIYOHARA Takashi wrote:
> 
> > > > My LSI vendor is ericsson.  Besides, I think that the initialization of
> > > > CSR is also possible. However, I do not have the device of CSR.
> > > [...]
> > > > I am referring to hciattach(8) of bluez.  The data sheet of ericsson
> > > > and CSR was seen.  However, the rest was not able to be found.
> > >
> > > Hmm.. hciattach(8) does the initialisation of the device from userland,
> > > would that not work better? Seems that it makes it easier to upgrade
> > > device support if that is not in the kernel..
> >
> > I don't know that.  However that will work perhaps.
> > I be anxious about it,
> > Processing for LSI that cannot confirm the initialization procedure
> > uncertain, and operation is believed.
> > And, the necessity might not be in the initialization only of the
> > acquisition of the version number.
> 
> I'm not sure I understand your meaning..

hmm...
Should we mimic the initialization of bluz?  However, I cannot test.
And, is bluz believed?

# I got PCMCIA-card of Digianswer.  ;-)
--
kiyohara


----Next_Part(Wed_Jan_17_01_38_47_2007_363)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="btuart.c"

/*	$NetBSD$	*/
/*
 * Copyright (c) 2006, 2007 KIYOHARA Takashi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <sys/types.h>
#include <sys/param.h>

#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/kauth.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/tty.h>

#include <machine/bus.h>
#include <machine/intr.h>

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

#include <dev/bluetooth/btuart.h>
#include <dev/ic/comvar.h>

#include "ioconf.h"

#define BTUART_DEBUG
#ifdef BTUART_DEBUG
int btuart_debug = 1;
#define IFDBG		if (btuart_debug)
#else
#define IFDBG		if (0)
#endif

struct btuart_softc;
struct bth4hci {
	int type;
	int init_speed;
#define FLOW_CTL	1
	int flags;
	int (*init)(struct btuart_softc *);
};

struct btuart_softc {
	struct device sc_dev;

	struct tty *sc_tp;
	struct hci_unit sc_unit;		/* Bluetooth HCI Unit */

	struct bth4hci sc_bth4hci;
	int sc_speed;

	int sc_state;				/* receive state */
#define BTUART_RECV_PKT_TYPE	0			/* packet type */
#define BTUART_RECV_ACL_HDR	1			/* acl header */
#define BTUART_RECV_SCO_HDR	2			/* sco header */
#define BTUART_RECV_EVENT_HDR	3			/* event header */
#define BTUART_RECV_ACL_DATA	4			/* acl packet data */
#define BTUART_RECV_SCO_DATA	5			/* sco packet data */
#define BTUART_RECV_EVENT_DATA	6			/* event packet data */
	int sc_want;				/* how much we want */
	struct mbuf *sc_rxp;			/* incoming packet */
	struct mbuf *sc_txp;			/* outgoing packet */

	void (*sc_input_acl)(struct hci_unit *, struct mbuf *);
	void (*sc_input_sco)(struct hci_unit *, struct mbuf *);
	void (*sc_input_event)(struct hci_unit *, struct mbuf *);
};

void btuartattach(int);
static int bth4match(struct device *, struct cfdata *, void *);
static void bth4attach(struct device *, struct device *, void *);
static int bth4detach(struct device *, int);

static int init_ericsson(struct btuart_softc *);
static int init_digi(struct btuart_softc *);
static int init_texas(struct btuart_softc *);
static int init_csr(struct btuart_softc *);
static int init_swave(struct btuart_softc *);
static int init_st(struct btuart_softc *);
static int init_stlc2500(struct btuart_softc *);
static int init_bcm2035(struct btuart_softc *);
static int bth4init(struct btuart_softc *);
static void bth4init_input(struct hci_unit *, struct mbuf *);

static int bth4open(dev_t, struct tty *);
static int bth4close(struct tty *, int);
static int bth4ioctl(struct tty *, u_long, caddr_t, int, struct lwp *);
static int bth4input(int, struct tty *);
static int bth4start(struct tty *);

static int bth4_enable(struct hci_unit *);
static void bth4_disable(struct hci_unit *);
static void bth4_start(struct hci_unit *);

/*
 * It doesn't need to be exported, as only btuartattach() uses it,
 * but there's no "official" way to make it static.
 */
CFATTACH_DECL(btuart, sizeof(struct btuart_softc),
    bth4match, bth4attach, bth4detach, NULL);

static struct linesw bth4_disc = {
	.l_name = "btuart",
	.l_open = bth4open,
	.l_close = bth4close,
	.l_read = ttyerrio,
	.l_write = ttyerrio,
	.l_ioctl = bth4ioctl,
	.l_rint = bth4input,
	.l_start = bth4start,
	.l_modem = ttymodem,
	.l_poll = ttyerrpoll
};

static struct bth4hci bth4hci[] = {
	{ BTUART_HCITYPE_ANY,		     0, FLOW_CTL, NULL },
	{ BTUART_HCITYPE_ERICSSON,	 57600, FLOW_CTL, init_ericsson },
	{ BTUART_HCITYPE_DIGI,		  9600, FLOW_CTL, init_digi },
	{ BTUART_HCITYPE_TEXAS,		115200, FLOW_CTL, init_texas },
	/* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
	{ BTUART_HCITYPE_CSR,		115200, FLOW_CTL, init_csr },
	/* Silicon Wave kits */
	{ BTUART_HCITYPE_SWAVE,		115200, FLOW_CTL, init_swave },
	/* ST Microelectronics minikits based on STLC2410/STLC2415 */
	{ BTUART_HCITYPE_ST,		 57600, FLOW_CTL, init_st },
	/* ST Microelectronics minikits based on STLC2500 */
	{ BTUART_HCITYPE_STLC2500,	115200, FLOW_CTL, init_stlc2500 },
	/* AmbiCom BT2000C Bluetooth PC/CF Card */
	{ BTUART_HCITYPE_BT2000C,	 57600, FLOW_CTL, init_csr },
	/* Broadcom BCM2035 */
	{ BTUART_HCITYPE_BCM2035,	115200,        0, init_bcm2035 },

	{ -1 }
};
static struct speedtbl {
	int s;
	int speed;
} speedtbl[] = {
	{      0, B0 },
	{     50, B50 },
	{     75, B75 },
	{    110, B110 },
	{    134, B134 },
	{    150, B150 },
	{    200, B200 },
	{    300, B300 },
	{    600, B600 },
	{   1200, B1200 },
	{   1800, B1800 },
	{   2400, B2400 },
	{   4800, B4800 },
	{   9600, B9600 },
	{  19200, B19200 },
	{  38400, B38400 },
	{   7200, B7200 },
	{  14400, B14400 },
	{  28800, B28800 },
	{  57600, B57600 },
	{  76800, B76800 },
	{ 115200, B115200 },
	{ 230400, B230400 },
	{ 460800, B460800 },
	{ 921600, B921600 },
	{     -1, B57600 },
};


/* ARGSUSED */
void
btuartattach(int num __unused)
{
	int error;

	error = ttyldisc_attach(&bth4_disc);
	if (error) {
		printf("%s: unable to register line discipline, error = %d\n",
		    btuart_cd.cd_name, error);
		return;
	}

	error = config_cfattach_attach(btuart_cd.cd_name, &btuart_ca);
	if (error) {
		printf("%s: unable to register cfattach, error = %d\n",
		    btuart_cd.cd_name, error);
		config_cfdriver_detach(&btuart_cd);
		(void) ttyldisc_detach(&bth4_disc);
	}
}

/*
 * Autoconf match routine.
 *
 * XXX: unused: config_attach_pseudo(9) does not call ca_match.
 */
/* ARGSUSED */
static int
bth4match(struct device *self __unused,
    struct cfdata *cfdata __unused, void *arg __unused)
{

	/* pseudo-device; always present */
	return 1;
}

/*
 * Autoconf attach routine.  Called by config_attach_pseudo(9) when we
 * open the line discipline.
 */
/* ARGSUSED */
static void
bth4attach(struct device *parent __unused,
    struct device *self, void *aux __unused)
{
	struct btuart_softc *sc = device_private(self);
	int i;

	printf("\n");

	sc->sc_input_acl = bth4init_input;
	sc->sc_input_sco = bth4init_input;
	sc->sc_input_event = bth4init_input;

	/* Copy default type */
	for (i = 0; bth4hci[i].type != BTUART_HCITYPE_ANY; i++);
	memcpy(&sc->sc_bth4hci, &bth4hci[i], sizeof (struct bth4hci));

	/* Attach Bluetooth unit */
	sc->sc_unit.hci_softc = sc;
	sc->sc_unit.hci_devname = sc->sc_dev.dv_xname;
	sc->sc_unit.hci_enable = bth4_enable;
	sc->sc_unit.hci_disable = bth4_disable;
	sc->sc_unit.hci_start_cmd = bth4_start;
	sc->sc_unit.hci_start_acl = bth4_start;
	sc->sc_unit.hci_start_sco = bth4_start;
	sc->sc_unit.hci_ipl = IPL_TTY;
	hci_attach(&sc->sc_unit);
}

/*
 * Autoconf detach routine.  Called when we close the line discipline.
 */
static int
bth4detach(struct device *self, int flags __unused)
{
	struct btuart_softc *sc = device_private(self);

	hci_detach(&sc->sc_unit);

	return 0;
}


/*
 * LSI initialize functions.
 */
static int
init_ericsson(struct btuart_softc *sc)
{
	struct mbuf *m;
	struct hci_unit *unit = &sc->sc_unit;
	hci_cmd_hdr_t *p;
	int i, error = 0;
	struct {
		int speed;
		uint8_t param;
	} baudratetbl[] = {
		{ B460800, 0x00 },
		{ B230400, 0x01 },
		{ B115200, 0x02 },
		{  B57600, 0x03 },
		{  B28800, 0x04 },
		{  B14400, 0x05 },
		{   B7200, 0x06 },
#if defined(B3600)
		{   B3600, 0x07 },
#endif
		{   B1800, 0x08 },
#if defined(B900)
		{    B900, 0x09 },
#endif
#if defined(B153600)
		{ B153600, 0x10 },
#endif
		{  B76800, 0x11 },
		{  B38400, 0x12 },
		{  B19200, 0x13 },
		{   B9600, 0x14 },
		{   B4800, 0x15 },
		{   B2400, 0x16 },
		{   B1200, 0x17 },
		{    B600, 0x18 },
		{    B300, 0x19 },
		{ B921600, 0x20 },
		{ -1 }
	};

	m = m_gethdr(M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return ENOMEM;

	p = mtod(m, hci_cmd_hdr_t *);
	p->type = HCI_CMD_PKT;
	p->opcode = htole16(HCI_CMD_ERICSSON_SET_UART_BAUD_RATE);
	p->length = 1;
	m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);

	for (i = 0; baudratetbl[i].speed != sc->sc_speed; i++)
		if (baudratetbl[i].speed == -1)
			return EINVAL;
	m_copyback(m, sizeof(hci_cmd_hdr_t), p->length, &baudratetbl[i].param);

	MBUFQ_ENQUEUE(&unit->hci_cmdq, m);
	bth4_start(unit);

#if 0
	m = NULL;
	while (1 /* CONSTCOND */) {
		hci_event_hdr_t *e;

		if ((error =
		    tsleep(&unit->hci_eventq, PCATCH, "bth4init", 0)) != 0)
			break;

		MBUFQ_DEQUEUE(&unit->hci_eventq, m);
		unit->hci_eventqlen--;
		KASSERT(m != NULL);

		e = mtod(m, hci_event_hdr_t *);
		if (e->event == HCI_EVENT_VENDOR)
			break;
		m_freem(m);
	}
	if (m != NULL)
		m_freem(m);

	return error;
#else
	/*
	 * XXXX: We cannot correctly receive this response perhaps.
	 * Wait until the transmission of the data of 5 bytes is completed.
	 * And it is assumed that init_speed is a baud rate value.
	 */
	delay(1000000 * 10/*bit*/ * 5/*byte*/ / sc->sc_bth4hci.init_speed);
	return error;
#endif
}

static int
init_digi(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
init_texas(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
init_csr(struct btuart_softc *sc)
{
	struct mbuf *m;
	struct hci_unit *unit = &sc->sc_unit;
	hci_cmd_hdr_t *p;
	hci_event_hdr_t *e;
	int error;
	struct {
		uint8_t last :1;
		uint8_t first :1;
#define CSR_BCCMD_CHANID_BCCMD		2
#define CSR_BCCMD_CHANID_HQ		3
#define CSR_BCCMD_CHANID_DEVMGRLIB	4
#define CSR_BCCMD_CHANID_L2CAPLIB	8
#define CSR_BCCMD_CHANID_RFCOMMLIB	9
#define CSR_BCCMD_CHANID_SDPLIB		10
#define CSR_BCCMD_CHANID_DFU		12
#define CSR_BCCMD_CHANID_VM		13
#define CSR_BCCMD_CHANID_LMDEBUG	20
		uint8_t chanid :6;

		struct {
#define CSR_BCCMD_MESSAGE_TYPE_GETREQ	0x0000
#define CSR_BCCMD_MESSAGE_TYPE_GETRESP	0x0001
#define CSR_BCCMD_MESSAGE_TYPE_SETREQ	0x0002
			uint16_t type;
			uint16_t length;
			uint16_t seqno;
#define CSR_BCCMD_MESSAGE_VARID_CONFIG_UART		0x6802
#define CSR_BCCMD_MESSAGE_VARID_CONFIG_UART_STOPB		0x2000
#define CSR_BCCMD_MESSAGE_VARID_CONFIG_UART_PARENB		0x4000
#define CSR_BCCMD_MESSAGE_VARID_CONFIG_UART_PARODD		0x8000
			uint16_t varid;
#define CSR_BCCMD_MESSAGE_STATUS_OK			0x0000
#define CSR_BCCMD_MESSAGE_STATUS_NO_SUCH_VARID		0x0001
#define CSR_BCCMD_MESSAGE_STATUS_TOO_BIG		0x0002
#define CSR_BCCMD_MESSAGE_STATUS_NO_VALUE		0x0003
#define CSR_BCCMD_MESSAGE_STATUS_BAD_REQ		0x0004
#define CSR_BCCMD_MESSAGE_STATUS_NO_ACCESS		0x0005
#define CSR_BCCMD_MESSAGE_STATUS_READ_ONLY		0x0006
#define CSR_BCCMD_MESSAGE_STATUS_WRITE_ONLY		0x0007
#define CSR_BCCMD_MESSAGE_STATUS_ERROR			0x0008
#define CSR_BCCMD_MESSAGE_STATUS_PERMISION_DENIED	0x0009
			uint16_t status;
			uint16_t payload[4];
		} message;
	} bccmd;

	m = m_gethdr(M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return ENOMEM;

	p = mtod(m, hci_cmd_hdr_t *);
	p->type = HCI_CMD_PKT;
	p->opcode =htole16(HCI_CMD_CSR_EXTN);
	p->length = sizeof (bccmd);
	m->m_pkthdr.len = m->m_len = sizeof(hci_cmd_hdr_t);

	bccmd.last = 1;
	bccmd.first = 1;
	bccmd.chanid = CSR_BCCMD_CHANID_BCCMD;
	bccmd.message.type = htole16(CSR_BCCMD_MESSAGE_TYPE_SETREQ);
	bccmd.message.length = htole16(sizeof (bccmd.message) >> 1);
	bccmd.message.seqno = htole16(0);
	bccmd.message.varid = htole16(CSR_BCCMD_MESSAGE_VARID_CONFIG_UART);
	bccmd.message.status = htole16(CSR_BCCMD_MESSAGE_STATUS_OK);
	bzero(bccmd.message.payload, sizeof (bccmd.message.payload));

	/*
	 * Value = (baud rate / 244.140625) | no parity | 1 stop bit.
	 * And it is assumed that sc_speed is a baud rate value.
	 */
	bccmd.message.payload[0] = htole16((sc->sc_speed * 64 + 7812) / 15625);

	m_copyback(m, sizeof(hci_cmd_hdr_t), p->length, &bccmd);
	MBUFQ_ENQUEUE(&unit->hci_cmdq, m);
	bth4_start(unit);

	m = NULL;
	while (1 /* CONSTCOND */) {
		if ((error =
		    tsleep(&unit->hci_eventq, PCATCH, "bth4init", 0)) != 0)
			break;

		MBUFQ_DEQUEUE(&unit->hci_eventq, m);
		unit->hci_eventqlen--;
		KASSERT(m != NULL);

		e = mtod(m, hci_event_hdr_t *);
		if (e->event == HCI_EVENT_VENDOR)
			break;
		m_freem(m);
	}
	if (m != NULL)
		m_freem(m);

	return error;
}

static int
init_swave(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
init_st(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
init_stlc2500(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
init_bcm2035(struct btuart_softc *sc)
{

	/* not yet */
	return EINVAL;
}

static int
bth4init(struct btuart_softc *sc)
{
	struct tty *tp = sc->sc_tp;
	struct termios t;
	int error = 0, s;

	sc->sc_speed = tp->t_ospeed;
	t.c_cflag = tp->t_cflag;
	t.c_ispeed = 0;
	t.c_ospeed = tp->t_ospeed;
	if ((tp->t_cflag & CRTSCTS) && !(sc->sc_bth4hci.flags & FLOW_CTL))
		t.c_cflag &= ~CRTSCTS;
	if (sc->sc_bth4hci.init_speed != 0 &&
	    tp->t_ospeed != sc->sc_bth4hci.init_speed)
		t.c_ospeed = sc->sc_bth4hci.init_speed;
	if (t.c_ospeed != tp->t_ospeed || t.c_cflag != tp->t_cflag)
		error = (*tp->t_param)(tp, &t);

	if (error == 0 && sc->sc_bth4hci.init != NULL)
		error = (*sc->sc_bth4hci.init)(sc);

	s = splhigh();
	sc->sc_input_acl = hci_input_acl;
	sc->sc_input_sco = hci_input_sco;
	sc->sc_input_event = hci_input_event;
	splx(s);

	if (sc->sc_bth4hci.init_speed != 0 &&
	    sc->sc_bth4hci.init_speed != sc->sc_speed) {
		t.c_ospeed = sc->sc_speed;
		t.c_cflag = tp->t_cflag;
		error = (*tp->t_param)(tp, &t);
	}

	return error;
}

static void
bth4init_input(struct hci_unit *unit, struct mbuf *m)
{
	uint8_t *rptr =  mtod(m, uint8_t *);

	IFDBG {
		int i;

		printf("%s:", __FUNCTION__);
		for (i = 0; i < m->m_len; i++)
			printf(" %02x", *(rptr + i));
		printf("\n");
	}

	switch (*rptr) {
	case HCI_ACL_DATA_PKT:
		if (unit->hci_aclrxqlen <= hci_aclrxq_max) {
			unit->hci_aclrxqlen++;
			MBUFQ_ENQUEUE(&unit->hci_aclrxq, m);
			m = NULL;
			wakeup(&unit->hci_aclrxq);
		}
		break;
	case HCI_SCO_DATA_PKT:
		if (unit->hci_scorxqlen <= hci_scorxq_max) {
			unit->hci_scorxqlen++;
			MBUFQ_ENQUEUE(&unit->hci_scorxq, m);
			m = NULL;
			wakeup(&unit->hci_scorxq);
		}
		break;
	case HCI_EVENT_PKT:
		if (unit->hci_eventqlen <= hci_eventq_max) {
			unit->hci_eventqlen++;
			MBUFQ_ENQUEUE(&unit->hci_eventq, m);
			m = NULL;
			wakeup(&unit->hci_eventq);
		}
		break;
	}
	if (m != NULL)
		m_freem(m);
}


/*
 * Line discipline functions.
 */
/* ARGSUSED */
static int
bth4open(dev_t device __unused, struct tty *tp)
{
	struct btuart_softc *sc;
	struct cfdata *cfdata;
	struct lwp *l = curlwp;		/* XXX */
	int error, unit, s;
	static char name[] = "btuart";

	if ((error = kauth_authorize_device_tty(l->l_cred,
	    KAUTH_DEVICE_TTY_OPEN, tp)) != 0)
		return error;

	s = spltty();

	if (tp->t_linesw == &bth4_disc) {
		sc = (struct btuart_softc *)tp->t_sc;
		if (sc != NULL) {
			splx(s);
			return EBUSY;
		}
	}

	KASSERT(tp->t_oproc != NULL);

	cfdata = malloc(sizeof (struct cfdata), M_DEVBUF, M_WAITOK);
	for (unit = 0; unit < btuart_cd.cd_ndevs; unit++)
		if (btuart_cd.cd_devs[unit] == NULL)
			break;
	cfdata->cf_name = name;
	cfdata->cf_atname = name;
	cfdata->cf_unit = unit;
	cfdata->cf_fstate = FSTATE_STAR,

	printf("%s%d at tty major %d minor %d\n",
	    name, unit, major(tp->t_dev), minor(tp->t_dev));
	sc = (struct btuart_softc *)config_attach_pseudo(cfdata);
	if (sc == NULL) {
		splx(s);
		return EIO;
	}
	tp->t_sc = sc;
	sc->sc_tp = tp;

	ttyflush(tp, FREAD | FWRITE);

	splx(s);

	return 0;
}

/* ARGSUSED */
static int
bth4close(struct tty *tp, int flag __unused)
{
	struct btuart_softc *sc = (struct btuart_softc *)tp->t_sc;
	struct cfdata *cfdata;
	int s;

	sc = tp->t_sc;

	s = spltty();
	ttyflush(tp, FREAD | FWRITE);
	ttyldisc_release(tp->t_linesw);
	tp->t_linesw = ttyldisc_default();
	if (sc != NULL) {
		tp->t_sc = NULL;
		if (sc->sc_tp == tp) {
			cfdata = sc->sc_dev.dv_cfdata;
			config_detach(&sc->sc_dev, 0);
			free(cfdata, M_DEVBUF);
		}

	}
	splx(s);
	return 0;
}

/* ARGSUSED */
static int
bth4ioctl(struct tty *tp, u_long cmd, caddr_t data,
    int flag __unused, struct lwp *l __unused)
{
	struct btuart_softc *sc = (struct btuart_softc *)tp->t_sc;
	int error, i;

	if (sc == NULL || tp != sc->sc_tp)
		return EPASSTHROUGH;

	error = 0;
	switch (cmd) {
	case BTUART_HCITYPE:
		for (i = 0; bth4hci[i].type != -1; i++)
			if (bth4hci[i].type == *(uint32_t *)data)
				break;
		if (bth4hci[i].type != -1)
			memcpy(&sc->sc_bth4hci, &bth4hci[i],
			    sizeof (struct bth4hci));
		else
			error = EINVAL;
		break;

	case BTUART_INITSPEED:
		for (i = 0; speedtbl[i].s != -1; i++)
			if (speedtbl[i].s == *(uint32_t *)data)
				break;
		if (speedtbl[i].s != -1)
			sc->sc_bth4hci.init_speed = speedtbl[i].speed;
		else
			error = EINVAL;
		break;

	case BTUART_START:
		error = bth4init(sc);
		break;

	default:
		error = EPASSTHROUGH;
		break;
	}

	return error;
}

static int
bth4input(int c, struct tty *tp)
{
	struct btuart_softc *sc = (struct btuart_softc *)tp->t_sc;
	struct mbuf *m = sc->sc_rxp;
	int space = 0;

	c &= TTY_CHARMASK;

	/* If we already started a packet, find the trailing end of it. */
	if (m) {
		while (m->m_next)
			m = m->m_next;

		space = M_TRAILINGSPACE(m);
	}

	if (space == 0) {
		if (m == NULL) {
			/* new packet */
			MGETHDR(m, M_DONTWAIT, MT_DATA);
			if (m == NULL) {
				printf("%s: out of memory\n",
				    sc->sc_dev.dv_xname);
				++sc->sc_unit.hci_stats.err_rx;
				return 0;	/* (lost sync) */
			}

			sc->sc_rxp = m;
			m->m_pkthdr.len = m->m_len = 0;
			space = MHLEN;

			sc->sc_state = BTUART_RECV_PKT_TYPE;
			sc->sc_want = 1;
		} else {
			/* extend mbuf */
			MGET(m->m_next, M_DONTWAIT, MT_DATA);
			if (m->m_next == NULL) {
				printf("%s: out of memory\n",
				    sc->sc_dev.dv_xname);
				++sc->sc_unit.hci_stats.err_rx;
				return 0;	/* (lost sync) */
			}

			m = m->m_next;
			m->m_len = 0;
			space = MLEN;

			if (sc->sc_want > MINCLSIZE) {
				MCLGET(m, M_DONTWAIT);
				if (m->m_flags & M_EXT)
					space = MCLBYTES;
			}
		}
	}

	mtod(m, uint8_t *)[m->m_len++] = c;
	sc->sc_rxp->m_pkthdr.len++;
	sc->sc_unit.hci_stats.byte_rx++;

	sc->sc_want--;
	if (sc->sc_want > 0)
		return 0;	/* want more */

	switch (sc->sc_state) {
	case BTUART_RECV_PKT_TYPE:	/* Got packet type */

		switch (c) {
		case HCI_ACL_DATA_PKT:
			sc->sc_state = BTUART_RECV_ACL_HDR;
			sc->sc_want = sizeof(hci_acldata_hdr_t) - 1;
			break;

		case HCI_SCO_DATA_PKT:
			sc->sc_state = BTUART_RECV_SCO_HDR;
			sc->sc_want = sizeof(hci_scodata_hdr_t) - 1;
			break;

		case HCI_EVENT_PKT:
			sc->sc_state = BTUART_RECV_EVENT_HDR;
			sc->sc_want = sizeof(hci_event_hdr_t) - 1;
			break;

		default:
			printf("%s: Unknown packet type=%#x!\n",
			    sc->sc_dev.dv_xname, c);
			sc->sc_unit.hci_stats.err_rx++;
			m_freem(sc->sc_rxp);
			sc->sc_rxp = NULL;
			return 0;	/* (lost sync) */
		}

		break;

	/*
	 * we assume (correctly of course :) that the packet headers all fit
	 * into a single pkthdr mbuf
	 */
	case BTUART_RECV_ACL_HDR:	/* Got ACL Header */
		sc->sc_state = BTUART_RECV_ACL_DATA;
		sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length;
		sc->sc_want = le16toh(sc->sc_want);
		break;

	case BTUART_RECV_SCO_HDR:	/* Got SCO Header */
		sc->sc_state = BTUART_RECV_SCO_DATA;
		sc->sc_want =  mtod(m, hci_scodata_hdr_t *)->length;
		break;

	case BTUART_RECV_EVENT_HDR:	/* Got Event Header */
		sc->sc_state = BTUART_RECV_EVENT_DATA;
		sc->sc_want =  mtod(m, hci_event_hdr_t *)->length;
		break;

	case BTUART_RECV_ACL_DATA:	/* ACL Packet Complete */
		(*sc->sc_input_acl)(&sc->sc_unit, sc->sc_rxp);
		sc->sc_unit.hci_stats.acl_rx++;
		sc->sc_rxp = m = NULL;
		break;

	case BTUART_RECV_SCO_DATA:	/* SCO Packet Complete */
		(*sc->sc_input_sco)(&sc->sc_unit, sc->sc_rxp);
		sc->sc_unit.hci_stats.sco_rx++;
		sc->sc_rxp = m = NULL;
		break;

	case BTUART_RECV_EVENT_DATA:	/* Event Packet Complete */
		sc->sc_unit.hci_stats.evt_rx++;
		(*sc->sc_input_event)(&sc->sc_unit, sc->sc_rxp);
		sc->sc_rxp = m = NULL;
		break;

	default:
		panic("%s: invalid state %d!\n",
		    sc->sc_dev.dv_xname, sc->sc_state);
	}

	return 0;
}

static int
bth4start(struct tty *tp)
{
	struct btuart_softc *sc = (struct btuart_softc *)tp->t_sc;
	struct mbuf *m;
	int count, rlen;
	uint8_t *rptr;

	m = sc->sc_txp;
	if (m == NULL) {
		sc->sc_unit.hci_flags &= ~BTF_XMIT;
		bth4_start(&sc->sc_unit);
		return 0;
	}

	count = 0;
	rlen = 0;
	rptr = mtod(m, uint8_t *);

	for(;;) {
		if (rlen >= m->m_len) {
			m = m->m_next;
			if (m == NULL) {
				m = sc->sc_txp;
				sc->sc_txp = NULL;

				if (M_GETCTX(m, void *) == NULL)
					m_freem(m);
				else
					hci_complete_sco(&sc->sc_unit, m);

				break;
			}

			rlen = 0;
			rptr = mtod(m, uint8_t *);
			continue;
		}

		if (putc(*rptr++, &tp->t_outq) < 0) {
			m_adj(m, rlen);
			break;
		}
		rlen++;
		count++;
	}

	sc->sc_unit.hci_stats.byte_tx += count;

	if (tp->t_outq.c_cc != 0)
		(*tp->t_oproc)(tp);

	return 0;
}


/*
 * HCI UART (H4) functions.
 */
static int
bth4_enable(struct hci_unit *unit)
{

	if (unit->hci_flags & BTF_RUNNING)
		return 0;

	unit->hci_flags |= BTF_RUNNING;
	unit->hci_flags &= ~BTF_XMIT;

	return 0;
}

static void
bth4_disable(struct hci_unit *unit)
{
	struct btuart_softc *sc = unit->hci_softc;

	if ((unit->hci_flags & BTF_RUNNING) == 0)
		return;

	if (sc->sc_rxp) {
		m_freem(sc->sc_rxp);
		sc->sc_rxp = NULL;
	}

	if (sc->sc_txp) {
		m_freem(sc->sc_txp);
		sc->sc_txp = NULL;
	}

	unit->hci_flags &= ~BTF_RUNNING;
}

static void
bth4_start(struct hci_unit *unit)
{
	struct btuart_softc *sc = unit->hci_softc;
	struct mbuf *m;

	KASSERT((unit->hci_flags & BTF_XMIT) == 0);
	KASSERT(sc->sc_txp == NULL);

	if (MBUFQ_FIRST(&unit->hci_cmdq)) {
		MBUFQ_DEQUEUE(&unit->hci_cmdq, m);
		unit->hci_stats.cmd_tx++;
		M_SETCTX(m, NULL);
		goto start;
	}

	if (MBUFQ_FIRST(&unit->hci_scotxq)) {
		MBUFQ_DEQUEUE(&unit->hci_scotxq, m);
		unit->hci_stats.sco_tx++;
		goto start;
	}

	if (MBUFQ_FIRST(&unit->hci_acltxq)) {
		MBUFQ_DEQUEUE(&unit->hci_acltxq, m);
		unit->hci_stats.acl_tx++;
		M_SETCTX(m, NULL);
		goto start;
	}

	/* Nothing to send */
	return;

start:
	sc->sc_txp = m;
	unit->hci_flags |= BTF_XMIT;
	bth4start(sc->sc_tp);
}

----Next_Part(Wed_Jan_17_01_38_47_2007_363)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="btuart.h"

/*	$NetBSD$	*/
/*
 * Copyright (c) 2006 KIYOHARA Takashi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#define BTUART_HCITYPE		_IOW ('H', 1, uint32_t)
#define BTUART_HCITYPE_ANY		0
#define BTUART_HCITYPE_ERICSSON		1
#define BTUART_HCITYPE_DIGI		2
#define BTUART_HCITYPE_TEXAS		3
#define BTUART_HCITYPE_CSR		4
#define BTUART_HCITYPE_SWAVE		5
#define BTUART_HCITYPE_ST		6
#define BTUART_HCITYPE_STLC2500		7
#define BTUART_HCITYPE_BT2000C		8
#define BTUART_HCITYPE_BCM2035		9

#define BTUART_INITSPEED	_IOW ('H', 2, uint32_t)
#define BTUART_START		_IO  ('H', 3)

----Next_Part(Wed_Jan_17_01_38_47_2007_363)----