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/09/2007 14:11:31
----Next_Part(Tue_Jan__9_14_11_31_2007_760)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Hi! Iain,
From: KIYOHARA Takashi <kiyohara@kk.iij4u.or.jp>
Date: Tue, 05 Dec 2006 02:45:33 +0900 (JST)
> > It should be possible to do similar but attach a pseudo device instance
> > when the line discipline is opened, and the data does not need to pass
> > through userland but can go directly through the line discipline to the
> > bluetooth stack (the btuartd is just a place holder). If the line
> > discipline is closed, the pseudo device is detached and all connections
> > through it are killed automatically.
>
> Sure. I will write this in several days. ;-)
ppp(4) was able to be done with btuart on my gumstix. 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 referred to the following URL.
http://wiki.netbsd.se/index.php/Bluetooth
However, SCO audio did not work. Is this the same reason as bt3c(4)?
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.
I will coordinate btuartd.c.
Thanks,
--
kiyohara
----Next_Part(Tue_Jan__9_14_11_31_2007_760)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="btuart.patch"
Index: sys/dev/bluetooth/files.bluetooth
===================================================================
RCS file: /cvsroot/src/sys/dev/bluetooth/files.bluetooth,v
retrieving revision 1.10
diff -u -r1.10 files.bluetooth
--- sys/dev/bluetooth/files.bluetooth 10 Sep 2006 15:45:56 -0000 1.10
+++ sys/dev/bluetooth/files.bluetooth 6 Jan 2007 09:14:09 -0000
@@ -31,3 +31,7 @@
device btsco: bluetooth, audiobus, auconv, mulaw, aurateconv
attach btsco at bthub
file dev/bluetooth/btsco.c btsco
+
+# Bluetooth HCI UART (H4)
+defpseudo btuart: btbus, bluetooth
+file dev/bluetooth/btuart.c btuart
Index: sys/dev/ic/com.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/com.c,v
retrieving revision 1.255
diff -u -r1.255 com.c
--- sys/dev/ic/com.c 12 Oct 2006 01:31:00 -0000 1.255
+++ sys/dev/ic/com.c 6 Jan 2007 09:14:10 -0000
@@ -271,12 +271,6 @@
const bus_size_t com_std_map[16] = COM_REG_16550;
#endif /* COM_REGMAP */
-#define COMUNIT_MASK 0x7ffff
-#define COMDIALOUT_MASK 0x80000
-
-#define COMUNIT(x) (minor(x) & COMUNIT_MASK)
-#define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK)
-
#define COM_ISALIVE(sc) ((sc)->enabled != 0 && \
device_is_active(&(sc)->sc_dev))
Index: sys/dev/ic/comvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/comvar.h,v
retrieving revision 1.54
diff -u -r1.54 comvar.h
--- sys/dev/ic/comvar.h 13 Jul 2006 22:56:02 -0000 1.54
+++ sys/dev/ic/comvar.h 6 Jan 2007 09:14:10 -0000
@@ -224,6 +224,12 @@
struct simplelock sc_lock;
};
+#define COMUNIT_MASK 0x7ffff
+#define COMDIALOUT_MASK 0x80000
+
+#define COMUNIT(x) (minor(x) & COMUNIT_MASK)
+#define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK)
+
int comprobe1(bus_space_tag_t, bus_space_handle_t);
int comintr(void *);
void com_attach_subr(struct com_softc *);
Index: sys/kern/tty_conf.c
===================================================================
RCS file: /cvsroot/src/sys/kern/tty_conf.c,v
retrieving revision 1.52
diff -u -r1.52 tty_conf.c
--- sys/kern/tty_conf.c 1 Nov 2006 10:17:59 -0000 1.52
+++ sys/kern/tty_conf.c 6 Jan 2007 09:14:14 -0000
@@ -276,6 +276,7 @@
{ "ppp", PPPDISC },
{ "strip", STRIPDISC },
{ "hdlc", HDLCDISC },
+ { "bth4", BTH4DISC },
{ NULL, 0 }
};
struct linesw *ldisc;
Index: sys/netbt/hci.h
===================================================================
RCS file: /cvsroot/src/sys/netbt/hci.h,v
retrieving revision 1.6
diff -u -r1.6 hci.h
--- sys/netbt/hci.h 1 Oct 2006 10:13:54 -0000 1.6
+++ sys/netbt/hci.h 6 Jan 2007 09:14:16 -0000
@@ -1589,8 +1589,12 @@
/* Ericsson specific FC */
#define HCI_CMD_ERICSSON_WRITE_PCM_SETTINGS 0xFC07
+#define HCI_CMD_ERICSSON_SET_UART_BAUD_RATE 0xFC09
#define HCI_CMD_ERICSSON_SET_SCO_DATA_PATH 0xFC1D
+/* Cambridge Silicon Radio specific FC */
+#define HCI_CMD_CSR_EXTN 0xFC00
+
/**************************************************************************
**************************************************************************
Index: sys/sys/ttycom.h
===================================================================
RCS file: /cvsroot/src/sys/sys/ttycom.h,v
retrieving revision 1.18
diff -u -r1.18 ttycom.h
--- sys/sys/ttycom.h 11 Dec 2005 12:25:21 -0000 1.18
+++ sys/sys/ttycom.h 6 Jan 2007 09:14:16 -0000
@@ -160,5 +160,6 @@
#define PPPDISC 5 /* ppp discipline */
#define STRIPDISC 6 /* metricom wireless IP discipline */
#define HDLCDISC 9 /* HDLC discipline */
+#define BTH4DISC 10 /* Bluetooth HCI UART (H4) discipline */
#endif /* !_SYS_TTYCOM_H_ */
--- sys/dev/bluetooth/btuart.c.orig 1970-01-01 09:00:00.000000000 +0900
+++ sys/dev/bluetooth/btuart.c 2007-01-06 18:07:39.000000000 +0900
@@ -0,0 +1,968 @@
+/* $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.
+ *
+ */
+#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/btuartvar.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 struct btuart_softc * btuart_alloc(size_t);
+static void btuart_dealloc(struct btuart_softc *);
+
+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 *);
+
+static struct linesw bth4_disc = {
+ .l_name = "bth4",
+ .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)
+{
+
+ ttyldisc_attach(&bth4_disc);
+}
+
+static struct btuart_softc *
+btuart_alloc(size_t size)
+{
+ struct cfdriver *cd = &btuart_cd;
+ struct device *dev;
+ int unit;
+
+ for (unit = 0; unit < cd->cd_ndevs; unit++)
+ if (cd->cd_devs[unit] == NULL)
+ break;
+ dev = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
+ memset(dev, 0, size);
+ dev->dv_class = cd->cd_class;
+ dev->dv_cfdriver = cd;
+ dev->dv_unit = unit;
+ snprintf(dev->dv_xname, sizeof dev->dv_xname, "btuart%d", unit);
+ dev->dv_flags = DVF_ACTIVE; /* always initially active */
+
+ config_makeroom(unit, cd);
+ cd->cd_devs[unit] = dev;
+
+ printf("%s", dev->dv_xname);
+
+ return ((struct btuart_softc *)dev);
+}
+
+static void
+btuart_dealloc(struct btuart_softc *sc)
+{
+ struct cfdriver *cd = &btuart_cd;
+ struct device *dev = &sc->sc_dev;
+ int unit;
+
+ for (unit = 0; unit < cd->cd_ndevs; unit++) {
+ if (cd->cd_devs[unit] == dev) {
+ cd->cd_devs[unit] = NULL;
+ free(dev, M_DEVBUF);
+ return;
+ }
+ }
+ panic("btuart_dealloc: device not found");
+}
+
+
+/*
+ * 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)
+{
+#ifdef nottest
+ 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;
+#else
+
+ /* not yet */
+ return EINVAL;
+#endif
+}
+
+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 lwp *l = curlwp; /* XXX */
+ int error, i, s;
+
+ 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);
+
+ tp->t_sc = sc = btuart_alloc(sizeof (struct btuart_softc));
+ sc->sc_tp = tp;
+ sc->sc_input_acl = bth4init_input;
+ sc->sc_input_sco = bth4init_input;
+ sc->sc_input_event = bth4init_input;
+ printf(": attached at tty%02d\n", COMUNIT(tp->t_dev));
+
+ ttyflush(tp, FREAD | FWRITE);
+
+ /* 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);
+
+ splx(s);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+bth4close(struct tty *tp, int flag __unused)
+{
+ struct btuart_softc *sc = (struct btuart_softc *)tp->t_sc;
+ 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;
+ printf("%s detached from tty%02d\n",
+ sc->sc_dev.dv_xname, COMUNIT(tp->t_dev));
+
+ hci_detach(&sc->sc_unit);
+ if (sc->sc_tp == tp)
+ btuart_dealloc(sc);
+ }
+ 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);
+}
--- sys/dev/bluetooth/btuartvar.h.orig 1970-01-01 09:00:00.000000000 +0900
+++ sys/dev/bluetooth/btuartvar.h 2006-12-15 19:33:44.000000000 +0900
@@ -0,0 +1,42 @@
+/* $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(Tue_Jan__9_14_11_31_2007_760)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="btuartd.c"
#include <sys/ioctl.h>
#include <bluetooth.h>
#include <err.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <netbt/hci.h>
#if 1
#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_BBOXES 5
#define BTUART_HCITYPE_SWAVE 6
#define BTUART_HCITYPE_ST 7
#define BTUART_HCITYPE_STLC2500 8
#define BTUART_HCITYPE_BT2000C 9
#define BTUART_HCITYPE_BCM2035 10
#define BTUART_INITSPEED _IOW('H', 2, uint32_t)
#define BTUART_START _IO('H', 3)
#endif
static void usage(void);
static int btuartd(int, char *, int, int, int);
static struct hcitypetbl {
char *hciname;
int hcitype;
} hcitypetbl[] = {
{ "ericsson", BTUART_HCITYPE_ERICSSON },
{ "digi", BTUART_HCITYPE_DIGI },
{ "texas", BTUART_HCITYPE_TEXAS },
{ "csr", BTUART_HCITYPE_CSR },
{ "bboxes", BTUART_HCITYPE_BBOXES },
{ "swave", BTUART_HCITYPE_SWAVE },
{ "st", BTUART_HCITYPE_ST },
{ "stlc2500", BTUART_HCITYPE_STLC2500 },
{ "bt2000c", BTUART_HCITYPE_BT2000C },
{ "bcm2035", BTUART_HCITYPE_BCM2035 },
{ NULL, BTUART_HCITYPE_ANY },
};
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 },
};
static void
usage()
{
printf("usage: %s [-f] [-i init_speed] "
"type comdev speed\n", getprogname());
printf(" type : \n");
printf(" comdev: com device. e.g. /dev/dty03\n");
printf(" speed : baud rate. e.g. 921600\n");
exit(EXIT_SUCCESS);
}
static int
btuartd(int type, char *comdev, int speed, int flow, int init_speed)
{
struct termios t;
int comfd, disc;
if ((comfd = open(comdev, O_RDWR | O_NOCTTY)) == -1)
err(EXIT_FAILURE, "open failed: ");
tcflush(comfd, TCIOFLUSH);
if (tcgetattr(comfd, &t) == -1)
err(EXIT_FAILURE, "tcgetattr failed: ");
cfmakeraw(&t);
t.c_cflag |= CLOCAL;
if (flow)
t.c_cflag |= CRTSCTS;
tcflush(comfd, TCIOFLUSH);
if (cfsetspeed(&t, speed) == -1)
err(EXIT_FAILURE, "cfsetspeed failed: ");
if (tcsetattr(comfd, TCSANOW, &t) == -1)
err(EXIT_FAILURE, "tcsetattr failed: ");
disc = BTH4DISC;
if (ioctl(comfd, TIOCSETD, &disc) < 0)
err(EXIT_FAILURE, "ioctl TIOCSETD failed: ");
if (init_speed != -1)
if (ioctl(comfd, BTUART_INITSPEED, &init_speed) < 0)
err(EXIT_FAILURE, "ioctl BTUART_INITSPEED failed: ");
if (ioctl(comfd, BTUART_HCITYPE, &type) < 0)
err(EXIT_FAILURE, "ioctl BTUART_HCITYPE failed: ");
if (ioctl(comfd, BTUART_START) < 0)
err(EXIT_FAILURE, "ioctl BTUART_START failed: ");
while (1);
disc = TTYDISC;
if (ioctl(comfd, TIOCSETD, &disc) < 0)
err(EXIT_FAILURE, "ioctl TIOCSETD failed: ");
if (close(comfd) == -1)
err(EXIT_FAILURE, "close failed: ");
return 0;
}
int
main(int argc, char *argv[])
{
extern char *optarg;
extern int optind;
int type, speed, init_speed = -1, flow = 0, ch, i;
char *comdev;
while ((ch = getopt(argc, argv, "fi:")) != -1) {
switch (ch) {
case 'f':
flow = 1;
break;
case 'i':
for (i = 0; speedtbl[i].speed != -1; i++)
if (speedtbl[i].s == atoi(optarg))
break;
init_speed = atoi(optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 3)
usage();
for (i = 0; hcitypetbl[i].hciname != NULL; i++)
if (!strncmp(hcitypetbl[i].hciname, argv[0], strlen(argv[0])))
break;
type = hcitypetbl[i].hcitype;
comdev = argv[1];
for (i = 0; speedtbl[i].speed != -1; i++)
if (speedtbl[i].s == atoi(argv[2]))
break;
speed = speedtbl[i].speed;
if (btuartd(type, comdev, speed, flow, init_speed) != 0)
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
----Next_Part(Tue_Jan__9_14_11_31_2007_760)----