* Re: [Bluez-users] H5 3 wire UART connect problem
2005-07-05 8:13 ` Marcel Holtmann
@ 2005-07-06 9:20 ` hariharan veerappan
2005-07-06 9:45 ` Marcel Holtmann
0 siblings, 1 reply; 17+ messages in thread
From: hariharan veerappan @ 2005-07-06 9:20 UTC (permalink / raw)
To: bluez-users
[-- Attachment #1: Type: text/plain, Size: 1212 bytes --]
Dear Marcel,
Thanks for your support.
Please find her with the source modified for the H:5 3 WIRE UART
Silicon wave dongle
implementation in Bluez stack.
Advise us if you find anything wrong
Thanks and regards
Hariharan.V
On 7/5/05, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi Hariharan,
>
> > We have changed the hciattach.c and hci_h5.c (H5 3 WIRE UART Bluetooth driver).
> > The Bluetooth module is up. We could read the Bluetooth device address
> > and scan the
> > bluetooth network.
>
> I think it is time to show us the source code for the 3-Wire UART
> support, before we discuss any other things.
>
> Regards
>
> Marcel
>
>
>
>
> -------------------------------------------------------
> SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
> from IBM. Find simple to follow Roadmaps, straightforward articles,
> informative Webcasts and more! Get everything you need to get up to
> speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
> _______________________________________________
> Bluez-users mailing list
> Bluez-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bluez-users
>
[-- Attachment #2: hci_h5.c --]
[-- Type: application/octet-stream, Size: 19257 bytes --]
/*
BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
Based on
hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
ABCSP by Carl Orsborn <cjo@csr.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/*
* $Id: hci_bcsp.c,v 1.2 2002/09/26 05:05:14 maxk Exp $
*/
/********************************************************************/
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* modified for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
/* Modification history */
/* BCSP and H5 (3 WIRE UART differs only with the portocol number definitions*/
/*The protocol definitions were changed in the following two functions */
/*static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
int len, int pkt_type)
*/
/********************************************************************/
#define VERSION "0.1"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#include "hci_h5.h"
//#undef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
//#define HCI_UART_DEBUG
#ifndef HCI_UART_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#undef BT_DMP
#define BT_DMP( A... )
#endif
/* ---- BCSP CRC calculation ---- */
/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
initial value 0xffff, bits shifted in reverse order. */
static const u16 crc_table[] = {
0x0000, 0x1081, 0x2102, 0x3183,
0x4204, 0x5285, 0x6306, 0x7387,
0x8408, 0x9489, 0xa50a, 0xb58b,
0xc60c, 0xd68d, 0xe70e, 0xf78f
};
/* Initialise the crc calculator */
#define BCSP_CRC_INIT(x) x = 0xffff
/*
Update crc with next data byte
Implementation note
The data byte is treated as two nibbles. The crc is generated
in reverse, i.e., bits are fed into the register from the top.
*/
static void bcsp_crc_update(u16 *crc, u8 d)
{
u16 reg = *crc;
reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
*crc = reg;
}
/*
Get reverse of generated crc
Implementation note
The crc generator (bcsp_crc_init() and bcsp_crc_update())
creates a reversed crc, so it needs to be swapped back before
being passed on.
*/
static u16 bcsp_crc_reverse(u16 crc)
{
u16 b, rev;
for (b = 0, rev = 0; b < 16; b++) {
rev = rev << 1;
rev |= (crc & 1);
crc = crc >> 1;
}
return (rev);
}
/* ---- BCSP core ---- */
static void bcsp_slip_msgdelim(struct sk_buff *skb)
{
const char pkt_delim = 0xc0;
memcpy(skb_put(skb, 1), &pkt_delim, 1);
}
static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
{
const char esc_c0[2] = { 0xdb, 0xdc };
const char esc_db[2] = { 0xdb, 0xdd };
switch (c) {
case 0xc0:
memcpy(skb_put(skb, 2), &esc_c0, 2);
break;
case 0xdb:
memcpy(skb_put(skb, 2), &esc_db, 2);
break;
default:
memcpy(skb_put(skb, 1), &c, 1);
}
}
static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct bcsp_struct *bcsp = hu->priv;
if (skb->len > 0xFFF) {
BT_ERR("Packet too long");
kfree_skb(skb);
return 0;
}
switch (skb->pkt_type) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&bcsp->rel, skb);
break;
case HCI_SCODATA_PKT:
skb_queue_tail(&bcsp->unrel, skb);
break;
default:
BT_ERR("Unknown packet type");
kfree_skb(skb);
break;
}
return 0;
}
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* modified for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
int len, int pkt_type)
{
struct sk_buff *nskb;
u8 hdr[4], chan;
int rel, i;
#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
#endif
switch (pkt_type) {
case HCI_ACLDATA_PKT:
chan =2; /*chan = 6;*/ /* BCSP ACL channel */
rel = 1; /* reliable channel */
break;
case HCI_COMMAND_PKT:
chan =1; /*chan = 5;*/ /* BCSP cmd/evt channel */
rel = 1; /* reliable channel */
break;
case HCI_SCODATA_PKT:
chan =3; /*chan = 7;*/ /* BCSP SCO channel */
rel = 0; /* unreliable channel */
break;
case BCSP_LE_PKT:
chan =15; /*chan = 1;*/ /* BCSP LE channel */
rel = 0; /* unreliable channel */
break;
case BCSP_ACK_PKT:
chan = 0; /* BCSP internal channel */
rel = 0; /* unreliable channel */
break;
default:
BT_ERR("Unknown packet type");
return NULL;
}
/* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
(because bytes 0xc0 and 0xdb are escaped, worst case is
when the packet is all made of 0xc0 and 0xdb :) )
+ 2 (0xc0 delimiters at start and end). */
nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
if (!nskb)
return NULL;
nskb->pkt_type = pkt_type;
bcsp_slip_msgdelim(nskb);
hdr[0] = bcsp->rxseq_txack << 3;
bcsp->txack_req = 0;
BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
if (rel) {
hdr[0] |= 0x80 + bcsp->msgq_txseq;
BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
}
#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
hdr[0] |= 0x40;
#endif
hdr[1] = (len << 4) & 0xFF;
hdr[1] |= chan;
hdr[2] = len >> 4;
hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
/* Put BCSP header */
for (i = 0; i < 4; i++) {
bcsp_slip_one_byte(nskb, hdr[i]);
#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
#endif
}
/* Put payload */
for (i = 0; i < len; i++) {
bcsp_slip_one_byte(nskb, data[i]);
#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
#endif
}
#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
/* Put CRC */
bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc);
bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
#endif
bcsp_slip_msgdelim(nskb);
return nskb;
}
/* This is a rewrite of pkt_avail in ABCSP */
static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
unsigned long flags;
struct sk_buff *skb;
/* First of all, check for unreliable messages in the queue,
since they have priority */
if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
if (nskb) {
kfree_skb(skb);
return nskb;
} else {
skb_queue_head(&bcsp->unrel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
/* Now, try to send a reliable pkt. We can only send a
reliable packet if the number of packets sent but not yet ack'ed
is < than the winsize */
spin_lock_irqsave(&bcsp->unack.lock, flags);
if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
if (nskb) {
__skb_queue_tail(&bcsp->unack, skb);
mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
return nskb;
} else {
skb_queue_head(&bcsp->rel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
/* We could not send a reliable packet, either because there are
none or because there are too many unack'ed pkts. Did we receive
any packets we have not acknowledged yet ? */
if (bcsp->txack_req) {
/* if so, craft an empty ACK pkt and send it on BCSP unreliable
channel 0 */
struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
return nskb;
}
/* We have nothing to send */
return NULL;
}
static int bcsp_flush(struct hci_uart *hu)
{
BT_DBG("hu %p", hu);
return 0;
}
/* Remove ack'ed packets */
static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
{
unsigned long flags;
struct sk_buff *skb;
int i, pkts_to_be_removed;
u8 seqno;
spin_lock_irqsave(&bcsp->unack.lock, flags);
pkts_to_be_removed = bcsp->unack.qlen;
seqno = bcsp->msgq_txseq;
while (pkts_to_be_removed) {
if (bcsp->rxack == seqno)
break;
pkts_to_be_removed--;
seqno = (seqno - 1) & 0x07;
}
if (bcsp->rxack != seqno)
BT_ERR("Peer acked invalid packet");
BT_DBG("Removing %u pkts out of %u, up to seqno %u",
pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07);
for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed
&& skb != (struct sk_buff *) &bcsp->unack; i++) {
struct sk_buff *nskb;
nskb = skb->next;
__skb_unlink(skb, &bcsp->unack);
kfree_skb(skb);
skb = nskb;
}
if (bcsp->unack.qlen == 0)
del_timer(&bcsp->tbcsp);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
if (i != pkts_to_be_removed)
BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
}
/* Handle BCSP link-establishment packets. When we
detect a "sync" packet, symptom that the BT module has reset,
we do nothing :) (yet) */
static void bcsp_handle_le_pkt(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
/* spot "conf" pkts and reply with a "conf rsp" pkt */
if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
BT_DBG("Found a LE conf pkt");
if (!nskb)
return;
memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
nskb->pkt_type = BCSP_LE_PKT;
skb_queue_head(&bcsp->unrel, nskb);
hci_uart_tx_wakeup(hu);
}
/* Spot "sync" pkts. If we find one...disaster! */
else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
BT_ERR("Found a LE sync pkt, card has reset");
}
}
static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
{
const u8 c0 = 0xc0, db = 0xdb;
switch (bcsp->rx_esc_state) {
case BCSP_ESCSTATE_NOESC:
switch (byte) {
case 0xdb:
bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
break;
default:
memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp->message_crc, byte);
bcsp->rx_count--;
}
break;
case BCSP_ESCSTATE_ESC:
switch (byte) {
case 0xdc:
memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp-> message_crc, 0xc0);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
case 0xdd:
memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp-> message_crc, 0xdb);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
default:
BT_ERR ("Invalid byte %02x after esc byte", byte);
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
}
}
}
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* modified for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
int pass_up;
if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
bcsp->rxseq_txack++;
bcsp->rxseq_txack %= 0x8;
bcsp->txack_req = 1;
/* If needed, transmit an ack pkt */
hci_uart_tx_wakeup(hu);
}
bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
BT_DBG("Request for pkt %u from card", bcsp->rxack);
bcsp_pkt_cull(bcsp);
if ((bcsp->rx_skb->data[1] & 0x0f) == 2 /*6*/ &&
bcsp->rx_skb->data[0] & 0x80) {
bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
pass_up = 1;
BT_DBG("GDA: HCI ACL Packet \n");
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 4 /*5*/ &&
bcsp->rx_skb->data[0] & 0x80) {
bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
pass_up = 1;
BT_DBG("GDA: HCI Event Packet \n");
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 3 /*7*/ ) {
bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
pass_up = 1;
BT_DBG("SCo data packet \n");
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 15 /*1*/ &&
!(bcsp->rx_skb->data[0] & 0x80)) {
bcsp_handle_le_pkt(hu);
pass_up = 0;
BT_DBG("GDA Line packet \n");
} else{
pass_up = 0;
BT_DBG("Default packet \n");
}
if (!pass_up) {
if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
(bcsp->rx_skb->data[1] & 0x0f) != 15 /*1*/) {
BT_ERR ("Packet for unknown channel (%u %s)",
bcsp->rx_skb->data[1] & 0x0f,
bcsp->rx_skb->data[0] & 0x80 ?
"reliable" : "unreliable");
}
kfree_skb(bcsp->rx_skb);
} else {
/* Pull out BCSP hdr */
skb_pull(bcsp->rx_skb, 4);
hci_recv_frame(bcsp->rx_skb);
}
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_skb = NULL;
}
/* Recv data */
static int bcsp_recv(struct hci_uart *hu, void *data, int count)
{
struct bcsp_struct *bcsp = hu->priv;
register unsigned char *ptr;
BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
hu, count, bcsp->rx_state, bcsp->rx_count);
ptr = data;
while (count) {
if (bcsp->rx_count) {
if (*ptr == 0xc0) {
BT_ERR("Short BCSP packet");
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_START;
bcsp->rx_count = 0;
} else
bcsp_unslip_one_byte(bcsp, *ptr);
ptr++; count--;
continue;
}
switch (bcsp->rx_state) {
case BCSP_W4_BCSP_HDR:
if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
BT_ERR("Error in BCSP hdr checksum");
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
}
if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
BT_ERR ("Out-of-order packet arrived, got %u expected %u",
bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
}
bcsp->rx_state = BCSP_W4_DATA;
bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
(bcsp->rx_skb->data[2] << 4); /* May be 0 */
continue;
case BCSP_W4_DATA:
if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
bcsp->rx_state = BCSP_W4_CRC;
bcsp->rx_count = 2;
} else
bcsp_complete_rx_pkt(hu);
continue;
case BCSP_W4_CRC:
if (bcsp_crc_reverse(bcsp->message_crc) !=
(bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) +
bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) {
BT_ERR ("Checksum failed: computed %04x received %04x",
bcsp_crc_reverse(bcsp->message_crc),
(bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) +
bcsp->rx_skb->data[bcsp->rx_skb->len - 1]);
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
}
skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
bcsp_complete_rx_pkt(hu);
continue;
case BCSP_W4_PKT_DELIMITER:
switch (*ptr) {
case 0xc0:
bcsp->rx_state = BCSP_W4_PKT_START;
break;
default:
/*BT_ERR("Ignoring byte %02x", *ptr);*/
break;
}
ptr++; count--;
break;
case BCSP_W4_PKT_START:
switch (*ptr) {
case 0xc0:
ptr++; count--;
break;
default:
bcsp->rx_state = BCSP_W4_BCSP_HDR;
bcsp->rx_count = 4;
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
BCSP_CRC_INIT(bcsp->message_crc);
/* Do not increment ptr or decrement count
* Allocate packet. Max len of a BCSP pkt=
* 0xFFF (payload) +4 (header) +2 (crc) */
bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC);
if (!bcsp->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
return 0;
}
bcsp->rx_skb->dev = (void *) &hu->hdev;
break;
}
break;
}
}
return count;
}
/* Arrange to retransmit all messages in the relq. */
static void bcsp_timed_event(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *) arg;
struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
struct sk_buff *skb;
unsigned long flags;
BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
spin_lock_irqsave(&bcsp->unack.lock, flags);
while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
skb_queue_head(&bcsp->rel, skb);
}
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
hci_uart_tx_wakeup(hu);
}
static int bcsp_open(struct hci_uart *hu)
{
struct bcsp_struct *bcsp;
BT_DBG("hu %p", hu);
bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC);
if (!bcsp)
return -ENOMEM;
memset(bcsp, 0, sizeof(*bcsp));
hu->priv = bcsp;
skb_queue_head_init(&bcsp->unack);
skb_queue_head_init(&bcsp->rel);
skb_queue_head_init(&bcsp->unrel);
init_timer(&bcsp->tbcsp);
bcsp->tbcsp.function = bcsp_timed_event;
bcsp->tbcsp.data = (u_long) hu;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
return 0;
}
static int bcsp_close(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
hu->priv = NULL;
BT_DBG("hu %p", hu);
skb_queue_purge(&bcsp->unack);
skb_queue_purge(&bcsp->rel);
skb_queue_purge(&bcsp->unrel);
del_timer(&bcsp->tbcsp);
kfree(bcsp);
return 0;
}
static struct hci_uart_proto bcsp = {
id: HCI_UART_BCSP,
open: bcsp_open,
close: bcsp_close,
enqueue: bcsp_enqueue,
dequeue: bcsp_dequeue,
recv: bcsp_recv,
flush: bcsp_flush
};
int bcsp_init(void)
{
return hci_uart_register_proto(&bcsp);
}
int bcsp_deinit(void)
{
return hci_uart_unregister_proto(&bcsp);
}
[-- Attachment #3: hciattach.c --]
[-- Type: application/octet-stream, Size: 34206 bytes --]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2002-2004 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* 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 OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
* CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
* COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
* SOFTWARE IS DISCLAIMED.
*
*
* $Id: hciattach.c,v 1.27 2004/10/11 13:59:57 holtmann Exp $
*/
/********************************************************************/
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* added for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
/* Modification history */
/* The silicon wave h5 protocol initialization is done thorugh the following function
swave_h5*/
/* the option, "gda" is added for Silicon wave h5 3 WIRE UART Siw3500*/
/* */
/********************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/hci_uart.h>
struct uart_t {
char *type;
int m_id;
int p_id;
int proto;
int init_speed;
int speed;
int flags;
int (*init) (int fd, struct uart_t *u, struct termios *ti);
};
#define FLOW_CTL 0x0001
static int uart_speed(int s)
{
switch (s) {
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
case 230400:
return B230400;
case 460800:
return B460800;
case 500000:
return B500000;
case 576000:
return B576000;
case 921600:
return B921600;
case 1000000:
return B1000000;
case 1152000:
return B1152000;
case 1500000:
return B1500000;
default:
return B57600;
}
}
static int set_speed(int fd, struct termios *ti, int speed)
{
cfsetospeed(ti, uart_speed(speed));
return tcsetattr(fd, TCSANOW, ti);
}
static void sig_alarm(int sig)
{
fprintf(stderr, "Initialization timed out.\n");
exit(1);
}
/*
* Read an HCI event from the given file descriptor.
*/
static int read_hci_event(int fd, unsigned char* buf, int size)
{
int remain, r;
int count = 0;
if (size <= 0)
return -1;
/* The first byte identifies the packet type. For HCI event packets, it
* should be 0x04, so we read until we get to the 0x04. */
while (1) {
r = read(fd, buf, 1);
if (r <= 0)
return -1;
if (buf[0] == 0x04)
break;
}
count++;
/* The next two bytes are the event code and parameter total length. */
while (count < 3) {
r = read(fd, buf + count, 3 - count);
if (r <= 0)
return -1;
count += r;
}
/* Now we read the parameters. */
if (buf[2] < (size - 3))
remain = buf[2];
else
remain = size - 3;
while ((count - 3) < remain) {
r = read(fd, buf + count, remain - (count - 3));
if (r <= 0)
return -1;
count += r;
}
return count;
}
/*
* Ericsson specific initialization
*/
static int ericsson(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 50000};
char cmd[5];
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x09;
cmd[2] = 0xfc;
cmd[3] = 0x01;
switch (u->speed) {
case 57600:
cmd[4] = 0x03;
break;
case 115200:
cmd[4] = 0x02;
break;
case 230400:
cmd[4] = 0x01;
break;
case 460800:
cmd[4] = 0x00;
break;
case 921600:
cmd[4] = 0x20;
break;
default:
cmd[4] = 0x03;
u->speed = 57600;
break;
}
/* Send initialization command */
if (write(fd, cmd, 5) != 5) {
perror("Failed to write init command");
return -1;
}
nanosleep(&tm, NULL);
return 0;
}
/*
* Digianswer specific initialization
*/
static int digi(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 50000};
char cmd[5];
/* DigiAnswer set baud rate command */
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x07;
cmd[2] = 0xfc;
cmd[3] = 0x01;
switch (u->speed) {
case 57600:
cmd[4] = 0x08;
break;
case 115200:
cmd[4] = 0x09;
break;
default:
cmd[4] = 0x09;
u->speed = 115200;
break;
}
/* Send initialization command */
if (write(fd, cmd, 5) != 5) {
perror("Failed to write init command");
return -1;
}
nanosleep(&tm, NULL);
return 0;
}
static int texas(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 50000};
char cmd[4];
unsigned char resp[100]; /* Response */
int n;
memset(resp,'\0', 100);
/* It is possible to get software version with manufacturer specific
HCI command HCI_VS_TI_Version_Number. But the only thing you get more
is if this is point-to-point or point-to-multipoint module */
/* Get Manufacturer and LMP version */
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x01;
cmd[2] = 0x10;
cmd[3] = 0x00;
do {
n = write(fd, cmd, 4);
if (n < 0) {
perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
return -1;
}
if (n < 4) {
fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
return -1;
}
/* Read reply. */
if (read_hci_event(fd, resp, 100) < 0) {
perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
return -1;
}
/* Wait for command complete event for our Opcode */
} while (resp[4] != cmd[1] && resp[5] != cmd[2]);
/* Verify manufacturer */
if ((resp[11] & 0xFF) != 0x0d)
fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
/* Print LMP version */
fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
/* Print LMP subversion */
fprintf(stderr, "Texas module LMP sub-version : 0x%02x%02x\n", resp[14] & 0xFF, resp[13] & 0xFF);
nanosleep(&tm, NULL);
return 0;
}
static int read_check(int fd, void *buf, int count)
{
int res;
do {
res = read(fd, buf, count);
if (res != -1) {
buf += res;
count -= res;
}
} while (count && (errno == 0 || errno == EINTR));
if (count)
return -1;
return 0;
}
/*
* BCSP specific initialization
*/
int serial_fd;
static void bcsp_tshy_sig_alarm(int sig)
{
static int retries=0;
unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
if (retries < 10) {
retries++;
write(serial_fd, &bcsp_sync_pkt, 10);
alarm(1);
return;
}
tcflush(serial_fd, TCIOFLUSH);
fprintf(stderr, "BCSP initialization timed out\n");
exit(1);
}
static void bcsp_tconf_sig_alarm(int sig)
{
static int retries=0;
unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
if (retries < 10){
retries++;
write(serial_fd, &bcsp_conf_pkt, 10);
alarm(1);
return;
}
tcflush(serial_fd, TCIOFLUSH);
fprintf(stderr, "BCSP initialization timed out\n");
exit(1);
}
static int bcsp(int fd, struct uart_t *u, struct termios *ti)
{
unsigned char byte, bcsph[4], bcspp[4],
bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0},
bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0},
bcspsync[4] = {0xda, 0xdc, 0xed, 0xed},
bcspsyncresp[4] = {0xac,0xaf,0xef,0xee},
bcspconf[4] = {0xad,0xef,0xac,0xed},
bcspconfresp[4] = {0xde,0xad,0xd0,0xd0};
struct sigaction sa;
if (set_speed(fd, ti, u->speed) < 0) {
perror("Can't set default baud rate");
return -1;
}
ti->c_cflag |= PARENB;
ti->c_cflag &= ~(PARODD);
if (tcsetattr(fd, TCSANOW, ti) < 0) {
perror("Can't set port settings");
return -1;
}
alarm(0);
serial_fd = fd;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = bcsp_tshy_sig_alarm;
sigaction(SIGALRM, &sa, NULL);
/* State = shy */
bcsp_tshy_sig_alarm(0);
while (1) {
do {
if (read_check(fd, &byte, 1) == -1){
perror("Failed to read");
return -1;
}
} while (byte != 0xC0);
do {
if ( read_check(fd, &bcsph[0], 1) == -1){
perror("Failed to read");
return -1;
}
} while (bcsph[0] == 0xC0);
if ( read_check(fd, &bcsph[1], 3) == -1){
perror("Failed to read");
return -1;
}
if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
continue;
if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
continue;
if (read_check(fd, &bcspp, 4) == -1){
perror("Failed to read");
return -1;
}
if (!memcmp(bcspp, bcspsync, 4)) {
write(fd, &bcsp_sync_resp_pkt,10);
} else if (!memcmp(bcspp, bcspsyncresp, 4))
break;
}
/* State = curious */
alarm(0);
sa.sa_handler = bcsp_tconf_sig_alarm;
sigaction(SIGALRM, &sa, NULL);
alarm(1);
while (1) {
do {
if (read_check(fd, &byte, 1) == -1){
perror("Failed to read");
return -1;
}
} while (byte != 0xC0);
do {
if (read_check(fd, &bcsph[0], 1) == -1){
perror("Failed to read");
return -1;
}
} while (bcsph[0] == 0xC0);
if (read_check(fd, &bcsph[1], 3) == -1){
perror("Failed to read");
return -1;
}
if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
continue;
if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
continue;
if (read_check(fd, &bcspp, 4) == -1){
perror("Failed to read");
return -1;
}
if (!memcmp(bcspp, bcspsync, 4))
write(fd, &bcsp_sync_resp_pkt, 10);
else if (!memcmp(bcspp, bcspconf, 4))
write(fd, &bcsp_conf_resp_pkt, 10);
else if (!memcmp(bcspp, bcspconfresp, 4))
break;
}
/* State = garrulous */
return 0;
}
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* added for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
static int compare_byte(char *src,char *dest,int count)
{
int i;
int ret=-1;
for(i=0;i<count;i++){
if(src[i] == dest[i]){
ret =0;
}else{
ret =-1;
break;
}
}
return ret;
}
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
/* Added for H5 3 WIRE UART by V.Hariharan (v.hariharan@gdatech.co.in) */
static int swave_h5(int fd, struct uart_t *u, struct termios *ti)
{
char command[24];
int cnt,i,j,ret;
char buf[10];
char sync_resp_pkt[8]={0xC0,0x00,0x2F,0x00,0xD0,0x02,0xFD,0xC0};
char sync_pkt[8]={0xC0,0x00,0x2F,0x00,0xD0,0x01,0xFE,0xC0};
char config_pkt[8]={0xC0,0x00,0x2F,0x00,0xD0,0x03,0xFC,0xC0};
char config_resp_pkt[8] = {0xC0,0x00,0x3F,0x00,0xdb,0xdc,0x04,0x7B};
struct timeval oneSecond;
fd_set fdSet;
ti->c_cflag |= PARENB;
ti->c_cflag &= ~(PARODD);
if (tcsetattr(fd, TCSANOW, ti) < 0) {
perror("Can't set port settings");
return -1;
}
for(j=0; j < 10 ; j++){
printf("sending sync message %d\n",j);
command[0] = 0xC0;
command[1] = 0x00;
command[2] = 0x2F;
command[3] = 0x00;
command[4] = 0xD0;
command[5] = 0x01;
command[6] = 0x7E;
command[7] = 0xC0;
if(write(fd,command,8) < 8){
printf("write error \n");
}
oneSecond.tv_sec = 1;
oneSecond.tv_usec = 0;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);
//do{
//usleep(1000000);
switch (ret = select(FD_SETSIZE, &fdSet, NULL, NULL, &oneSecond))
{
case 1:
cnt = read(fd,buf,8);
//}while(cnt <0);
if(cnt > 0){
for(i=0;i<cnt;i++)
printf("The reade byte valuye is %x\n",buf[i]);
if(compare_byte(sync_resp_pkt,buf,8) == 0)
j=10;
if(compare_byte(config_pkt,buf,8) == 0)
j=10;
}else{
printf("read error\n");
return -1;
}
break;
default:
break;
}
}
for(j=0;j<10;j++){
printf("sending sync respnse message \n");
command[0] = 0xC0;
command[1] = 0x00;
command[2] = 0x2F;
command[3] = 0x00;
command[4] = 0xD0;
command[5] = 0x02;
command[6] = 0x7D;
command[7] = 0xC0;
if(write(fd,command,8) < 8){
printf("write error \n");
}
oneSecond.tv_sec = 1;
oneSecond.tv_usec = 0;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);
//do{
//usleep(1000000);
switch (ret = select(FD_SETSIZE, &fdSet, NULL, NULL, &oneSecond))
{
case 1:
cnt = read(fd,buf,8);
//}while(cnt <0);
if(cnt > 0){
for(i=0;i<cnt;i++)
printf("The reade byte valuye is %x\n",buf[i]);
if(compare_byte(config_pkt,buf,8) == 0)
j=10;
}else{
printf("read error\n");
return -1;
}
break;
default:
break;
}
}
for(j=0;j<10;j++){
printf("sending config message %d\n",j);
command[0] = 0xC0;
command[1] = 0x00;
command[2] = 0x3F;
command[3] = 0x00;
command[4] = 0xdb;
command[5] = 0xdc;
command[6] = 0x03;
command[7] = 0xFC;
command[8] = 0x07;
command[9] = 0xC0;
if(write(fd,command,10) < 10){
printf("write error \n");
}
//do{
//usleep(1000000);
oneSecond.tv_sec = 1;
oneSecond.tv_usec = 0;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);
switch (ret = select(FD_SETSIZE, &fdSet, NULL, NULL, &oneSecond))
{
case 1:
cnt = read(fd,buf,10);
//}while(cnt < 0);
if(cnt > 0){
for(i=0;i<cnt;i++)
printf("The reade byte valuye is %x\n",buf[i]);
if(compare_byte(config_resp_pkt,buf,8) == 0)
j=10;
}else{
printf("read error\n");
return -1;
}
break;
default:
break;
}
}
command[0] = 0xC0;
command[1] = 0x00;
command[2] = 0x2F;
command[3] = 0x00;
command[4] = 0xD0;
command[5] = 0x04;
command[6] = 0x7B;
command[7] = 0xC0;
if(write(fd,command,8) < 8){
printf("write error \n");
}
return 0;
}
/***************************************************************************************/
/*
* CSR specific initialization
* Inspired strongly by code in OpenBT and experimentations with Brainboxes
* Pcmcia card.
* Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
*/
static int csr(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 10000000}; /* 10ms - be generous */
unsigned char cmd[30]; /* Command */
unsigned char resp[30]; /* Response */
int clen = 0; /* Command len */
static int csr_seq = 0; /* Sequence number of command */
int divisor;
/* It seems that if we set the CSR UART speed straight away, it
* won't work, the CSR UART gets into a state where we can't talk
* to it anymore.
* On the other hand, doing a read before setting the CSR speed
* seems to be ok.
* Therefore, the strategy is to read the build ID (useful for
* debugging) and only then set the CSR UART speed. Doing like
* this is more complex but at least it works ;-)
* The CSR UART control may be slow to wake up or something because
* every time I read its speed, its bogus...
* Jean II */
/* Try to read the build ID of the CSR chip */
clen = 5 + (5 + 6) * 2;
/* HCI header */
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x00; /* CSR command */
cmd[2] = 0xfc; /* MANUFACTURER_SPEC */
cmd[3] = 1 + (5 + 6) * 2; /* len */
/* CSR MSG header */
cmd[4] = 0xC2; /* first+last+channel=BCC */
/* CSR BCC header */
cmd[5] = 0x00; /* type = GET-REQ */
cmd[6] = 0x00; /* - msB */
cmd[7] = 5 + 4; /* len */
cmd[8] = 0x00; /* - msB */
cmd[9] = csr_seq & 0xFF;/* seq num */
cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
csr_seq++;
cmd[11] = 0x19; /* var_id = CSR_CMD_BUILD_ID */
cmd[12] = 0x28; /* - msB */
cmd[13] = 0x00; /* status = STATUS_OK */
cmd[14] = 0x00; /* - msB */
/* CSR BCC payload */
memset(cmd + 15, 0, 6 * 2);
/* Send command */
do {
if (write(fd, cmd, clen) != clen) {
perror("Failed to write init command (GET_BUILD_ID)");
return -1;
}
/* Read reply. */
if (read_hci_event(fd, resp, 100) < 0) {
perror("Failed to read init response (GET_BUILD_ID)");
return -1;
}
/* Event code 0xFF is for vendor-specific events, which is
* what we're looking for. */
} while (resp[1] != 0xFF);
#ifdef CSR_DEBUG
{
char temp[512];
int i;
for (i=0; i < rlen; i++)
sprintf(temp + (i*3), "-%02X", resp[i]);
fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
// In theory, it should look like :
// 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
}
#endif
/* Display that to user */
fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n",
resp[15] & 0xFF, resp[14] & 0xFF);
/* Try to read the current speed of the CSR chip */
clen = 5 + (5 + 4)*2;
/* -- HCI header */
cmd[3] = 1 + (5 + 4)*2; /* len */
/* -- CSR BCC header -- */
cmd[9] = csr_seq & 0xFF; /* seq num */
cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
csr_seq++;
cmd[11] = 0x02; /* var_id = CONFIG_UART */
cmd[12] = 0x68; /* - msB */
#ifdef CSR_DEBUG
/* Send command */
do {
if (write(fd, cmd, clen) != clen) {
perror("Failed to write init command (GET_BUILD_ID)");
return -1;
}
/* Read reply. */
if (read_hci_event(fd, resp, 100) < 0) {
perror("Failed to read init response (GET_BUILD_ID)");
return -1;
}
/* Event code 0xFF is for vendor-specific events, which is
* what we're looking for. */
} while (resp[1] != 0xFF);
{
char temp[512];
int i;
for (i=0; i < rlen; i++)
sprintf(temp + (i*3), "-%02X", resp[i]);
fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
}
#endif
if (u->speed > 1500000) {
fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
u->speed, u->init_speed);
u->speed = u->init_speed;
} else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
/* Unknown speed. Why oh why can't we just pass an int to the kernel? */
fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
u->speed, u->init_speed);
u->speed = u->init_speed;
}
if (u->speed == u->init_speed)
return 0;
/* Now, create the command that will set the UART speed */
/* CSR BCC header */
cmd[5] = 0x02; /* type = SET-REQ */
cmd[6] = 0x00; /* - msB */
cmd[9] = csr_seq & 0xFF; /* seq num */
cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
csr_seq++;
divisor = (u->speed*64+7812)/15625;
/* No parity, one stop bit -> divisor |= 0x0000; */
cmd[15] = (divisor) & 0xFF; /* divider */
cmd[16] = (divisor >> 8) & 0xFF; /* - msB */
/* The rest of the payload will be 0x00 */
#ifdef CSR_DEBUG
{
char temp[512];
int i;
for(i = 0; i < clen; i++)
sprintf(temp + (i*3), "-%02X", cmd[i]);
fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
// In theory, it should look like :
// 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
// 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
}
#endif
/* Send the command to set the CSR UART speed */
if (write(fd, cmd, clen) != clen) {
perror("Failed to write init command (SET_UART_SPEED)");
return -1;
}
nanosleep(&tm, NULL);
return 0;
}
static int gda_bluetooth(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 500000};
char cmd[10], rsp[100];
int r,i;
ti->c_cflag |= PARENB;
ti->c_cflag &= ~(PARODD);
if (tcsetattr(fd, TCSANOW, ti) < 0) {
perror("Can't set port settings");
return -1;
}
// we probably got the reply. Now we must send the "soft reset":
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x01; // 1 byte of data following
cmd[4] = 0x03; // HCI Reset Subcommand
// Send initialization command
if (write(fd, cmd, 5) != 5) {
perror(" First Can't write Silicon Wave reset cmd.");
return -1;
}
nanosleep(&tm, NULL);
// Silicon Wave set baud rate command
// see HCI Vendor Specific Interface from Silicon Wave
// first send a "param access set" command to set the
// appropriate data fields in RAM. Then send a "HCI Reset
// Subcommand", e.g. "soft reset" to make the changes effective.
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x06; // 6 bytes of data following
cmd[4] = 0x01; // param sub command
cmd[5] = 0x11; // tag 17 = 0x11 = HCI Transport Params
cmd[6] = 0x03; // length of the parameter following
cmd[7] = 0x06; //1; // HCI Transport flow control enable
cmd[8] = 0x04; //0x01; // HCI Transport Type = UART
switch (u->speed) {
case 19200:
cmd[9] = 0x03;
break;
case 38400:
cmd[9] = 0x02;
break;
case 57600:
cmd[9] = 0x01;
break;
case 115200:
cmd[9] = 0x00;
break;
default:
u->speed = 115200;
cmd[9] = 0x00;
break;
}
/* Send initialization command */
if (write(fd, cmd, 10) != 10) {
perror("Failed to write init command");
return -1;
}
// We should wait for a "GET Event" to confirm the success of
// the baud rate setting. Wait some time before reading. Better:
// read with timeout, parse data
// until correct answer, else error handling ... todo ...
nanosleep(&tm, NULL);
r = read(fd, rsp, sizeof(rsp));
if (r > 0) {
// guess it's okay, but we should parse the reply. But since
// I don't react on an error anyway ... todo
// Response packet format:
// 04 Event
// FF Vendor specific
// 07 Parameter length
// 0B Subcommand
// 01 Setevent
// 11 Tag specifying HCI Transport Layer Parameter
// 03 length
// 01 flow on
// 01 Hci Transport type = Uart
// xx Baud rate set (see above)
printf("No of bytes read %d\n",r);
for(i=0;i<r;i++)
printf("The byte is %x\n",rsp[i]);
} else {
// ups, got error.
return -1;
}
// we probably got the reply. Now we must send the "soft reset":
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x01; // 1 byte of data following
cmd[4] = 0x03; // HCI Reset Subcommand
// Send initialization command
if (write(fd, cmd, 5) != 5) {
perror("Can't write Silicon Wave reset cmd.");
return -1;
}
nanosleep(&tm, NULL);
// now the uart baud rate on the silicon wave module is set and effective.
// change our own baud rate as well. Then there is a reset event comming in
// on the *new* baud rate. This is *undocumented*! The packet looks like this:
// 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
// subcommand class". So: change to new baud rate, read with timeout, parse
// data, error handling. BTW: all param access in Silicon Wave is done this way.
// Maybe this code would belong in a seperate file, or at least code reuse...
return 0;
}
/*
* Silicon Wave specific initialization
* Thomas Moser <Thomas.Moser@tmoser.ch>
*/
static int swave(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 500000};
char cmd[10], rsp[100];
int r;
int i;
// we probably got the reply. Now we must send the "soft reset":
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x01; // 1 byte of data following
cmd[4] = 0x03; // HCI Reset Subcommand
// Send initialization command
if (write(fd, cmd, 5) != 5) {
perror(" First Can't write Silicon Wave reset cmd.");
return -1;
}
nanosleep(&tm, NULL);
// Silicon Wave set baud rate command
// see HCI Vendor Specific Interface from Silicon Wave
// first send a "param access set" command to set the
// appropriate data fields in RAM. Then send a "HCI Reset
// Subcommand", e.g. "soft reset" to make the changes effective.
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x06; // 6 bytes of data following
cmd[4] = 0x01; // param sub command
cmd[5] = 0x11; // tag 17 = 0x11 = HCI Transport Params
cmd[6] = 0x03; // length of the parameter following
cmd[7] = 0x01; // HCI Transport flow control enable
cmd[8] = 0x01; // HCI Transport Type = UART
switch (u->speed) {
case 19200:
cmd[9] = 0x03;
break;
case 38400:
cmd[9] = 0x02;
break;
case 57600:
cmd[9] = 0x01;
break;
case 115200:
cmd[9] = 0x00;
break;
default:
u->speed = 115200;
cmd[9] = 0x00;
break;
}
/* Send initialization command */
if (write(fd, cmd, 9) != 9) {
perror("Failed to write init command");
return -1;
}
// We should wait for a "GET Event" to confirm the success of
// the baud rate setting. Wait some time before reading. Better:
// read with timeout, parse data
// until correct answer, else error handling ... todo ...
nanosleep(&tm, NULL);
r = read(fd, rsp, sizeof(rsp));
if (r > 0) {
// guess it's okay, but we should parse the reply. But since
// I don't react on an error anyway ... todo
// Response packet format:
// 04 Event
// FF Vendor specific
// 07 Parameter length
// 0B Subcommand
// 01 Setevent
// 11 Tag specifying HCI Transport Layer Parameter
// 03 length
// 01 flow on
// 01 Hci Transport type = Uart
// xx Baud rate set (see above)
printf("The no of bytes read %d \n",r);
for(i=0;i<r;i++)
printf("The byte is %x\n",rsp[i]);
} else {
// ups, got error.
return -1;
}
// we probably got the reply. Now we must send the "soft reset":
cmd[0] = HCI_COMMAND_PKT; // it's a command packet
cmd[1] = 0x0B; // OCF 0x0B = param access set
cmd[2] = 0xfc; // OGF bx111111 = vendor specific
cmd[3] = 0x01; // 1 byte of data following
cmd[4] = 0x03; // HCI Reset Subcommand
// Send initialization command
if (write(fd, cmd, 5) != 5) {
perror("Can't write Silicon Wave reset cmd.");
return -1;
}
nanosleep(&tm, NULL);
// now the uart baud rate on the silicon wave module is set and effective.
// change our own baud rate as well. Then there is a reset event comming in
// on the *new* baud rate. This is *undocumented*! The packet looks like this:
// 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
// subcommand class". So: change to new baud rate, read with timeout, parse
// data, error handling. BTW: all param access in Silicon Wave is done this way.
// Maybe this code would belong in a seperate file, or at least code reuse...
return 0;
}
/*
* ST Microelectronics specific initialization
* Marcel Holtmann <marcel@holtmann.org>
*/
static int st(int fd, struct uart_t *u, struct termios *ti)
{
struct timespec tm = {0, 50000};
char cmd[5];
/* ST Microelectronics set baud rate command */
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x46; // OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate
cmd[2] = 0xfc; // OGF = Vendor specific
cmd[3] = 0x01;
switch (u->speed) {
case 9600:
cmd[4] = 0x09;
break;
case 19200:
cmd[4] = 0x0b;
break;
case 38400:
cmd[4] = 0x0d;
break;
case 57600:
cmd[4] = 0x0e;
break;
case 115200:
cmd[4] = 0x10;
break;
case 230400:
cmd[4] = 0x12;
break;
case 460800:
cmd[4] = 0x13;
break;
case 921600:
cmd[4] = 0x14;
break;
default:
cmd[4] = 0x10;
u->speed = 115200;
break;
}
/* Send initialization command */
if (write(fd, cmd, 5) != 5) {
perror("Failed to write init command");
return -1;
}
nanosleep(&tm, NULL);
return 0;
}
struct uart_t uart[] = {
{ "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL },
{ "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, FLOW_CTL, ericsson },
{ "digi", 0x0000, 0x0000, HCI_UART_H4, 9600, 115200, FLOW_CTL, digi },
{ "texas", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, texas},
{ "bcsp", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, 0, bcsp },
/* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
{ "xircom", 0x0105, 0x080a, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL },
/* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
{ "csr", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, csr },
/* BrainBoxes PCMCIA card (BL620) */
{ "bboxes", 0x0160, 0x0002, HCI_UART_H4, 115200, 460800, FLOW_CTL, csr },
/* Silicon Wave kits */
{ "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, swave },
/* Silicon wave H:5 3 WIRE UART - GDA Technologies Ltd Chennai INDIA*/
{ "gda", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, 0,swave_h5},
/* ST Microelectronics minikits based on STLC2410/STLC2415 */
{ "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, FLOW_CTL, st },
/* Sphinx Electronics PICO Card */
{ "picocard", 0x025e, 0x1000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL },
/* Inventel BlueBird Module */
{ "inventel", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL },
/* COM One Platinium Bluetooth PC Card */
{ "comone", 0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200, 0, bcsp },
/* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
{ "tdk", 0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200, 0, bcsp },
/* Socket Bluetooth CF Card (Rev G) */
{ "socket", 0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400, 0, bcsp },
/* 3Com Bluetooth Card (Version 3.0) */
{ "3com", 0x0101, 0x0041, HCI_UART_H4, 115200, 115200, FLOW_CTL, csr },
/* AmbiCom BT2000C Bluetooth PC/CF Card */
{ "bt2000c", 0x022d, 0x2000, HCI_UART_H4, 57600, 460800, FLOW_CTL, csr },
/* Zoom Bluetooth PCMCIA Card */
{ "zoom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0, bcsp },
{ NULL, 0 }
};
struct uart_t * get_by_id(int m_id, int p_id)
{
int i;
for (i = 0; uart[i].type; i++) {
if (uart[i].m_id == m_id && uart[i].p_id == p_id)
return &uart[i];
}
return NULL;
}
struct uart_t * get_by_type(char *type)
{
int i;
for (i = 0; uart[i].type; i++) {
if (!strcmp(uart[i].type, type))
return &uart[i];
}
return NULL;
}
/* Initialize UART driver */
int init_uart(char *dev, struct uart_t *u, int send_break)
{
struct termios ti;
int fd, i;
fd = open(dev, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("Can't open serial port");
return -1;
}
tcflush(fd, TCIOFLUSH);
if (tcgetattr(fd, &ti) < 0) {
perror("Can't get port settings");
return -1;
}
cfmakeraw(&ti);
ti.c_cflag |= CLOCAL;
if (u->flags & FLOW_CTL)
ti.c_cflag |= CRTSCTS;
else
ti.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &ti) < 0) {
perror("Can't set port settings");
return -1;
}
/* Set initial baudrate */
if (set_speed(fd, &ti, u->init_speed) < 0) {
perror("Can't set initial baud rate");
return -1;
}
tcflush(fd, TCIOFLUSH);
if (send_break) {
tcsendbreak(fd, 0);
usleep(500000);
}
if (u->init && u->init(fd, u, &ti) < 0)
return -1;
tcflush(fd, TCIOFLUSH);
/* Set actual baudrate */
if (set_speed(fd, &ti, u->speed) < 0) {
perror("Can't set baud rate");
return -1;
}
/* Set TTY to N_HCI line discipline */
i = N_HCI;
if (ioctl(fd, TIOCSETD, &i) < 0) {
perror("Can't set line discipline");
return -1;
}
if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
perror("Can't set device");
return -1;
}
return fd;
}
static void usage(void)
{
printf("hciattach - HCI UART driver initialization utility\n");
printf("Usage:\n");
printf("\thciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow]\n");
printf("\thciattach -l\n");
}
extern int optind, opterr, optopt;
extern char *optarg;
int main(int argc, char *argv[])
{
struct uart_t *u = NULL;
int detach, printpid, opt, i, n;
int to = 5;
int init_speed = 0;
int send_break = 0;
pid_t pid;
struct sigaction sa;
char dev[20];
char *ptr;
detach = 1;
printpid = 0;
while ((opt=getopt(argc, argv, "bnpt:s:l")) != EOF) {
switch(opt) {
case 'b':
send_break = 1;
break;
case 'n':
detach = 0;
break;
case 'p':
printpid = 1;
break;
case 't':
to = atoi(optarg);
break;
case 's':
init_speed = atoi(optarg);
break;
case 'l':
for (i = 0; uart[i].type; i++) {
printf("%-10s0x%04x,0x%04x\n", uart[i].type,
uart[i].m_id, uart[i].p_id);
}
exit(0);
default:
usage();
exit(1);
}
}
n = argc - optind;
if (n < 2) {
usage();
exit(1);
}
for (n = 0; optind < argc; n++, optind++) {
char *opt;
opt = argv[optind];
switch(n) {
case 0:
dev[0] = 0;
if (!strchr(opt, '/'))
strcpy(dev, "/dev/");
strcat(dev, opt);
break;
case 1:
if (strchr(argv[optind], ',')) {
int m_id, p_id;
sscanf(argv[optind], "%x,%x", &m_id, &p_id);
u = get_by_id(m_id, p_id);
} else {
u = get_by_type(opt);
}
if (!u) {
fprintf(stderr, "Unknown device type or id\n");
exit(1);
}
break;
case 2:
u->speed = atoi(argv[optind]);
break;
case 3:
if (!strcmp("flow", argv[optind]))
u->flags |= FLOW_CTL;
else
u->flags &= ~FLOW_CTL;
break;
}
}
if (!u) {
fprintf(stderr, "Unknown device type or id\n");
exit(1);
}
/* If user specified a initial speed, use that instead of
the hardware's default */
if (init_speed)
u->init_speed = init_speed;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = sig_alarm;
sigaction(SIGALRM, &sa, NULL);
/* 5 seconds should be enough for initialization */
alarm(to);
n = init_uart(dev, u, send_break);
if (n < 0) {
perror("Can't initialize device");
exit(1);
}
alarm(0);
if (detach) {
if ((pid = fork())) {
if (printpid)
printf("%d\n", pid);
return 0;
}
for (i=0; i<20; i++)
if (i != n) close(i);
}
while (1) sleep(999999999);
return 0;
}
[-- Attachment #4: hci_h5.h --]
[-- Type: application/octet-stream, Size: 2213 bytes --]
/*
BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
Based on
hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
ABCSP by Carl Orsborn <cjo@csr.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
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 OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/*
* $Id: hci_bcsp.h,v 1.2 2002/09/26 05:05:14 maxk Exp $
*/
#ifndef __HCI_BCSP_H__
#define __HCI_BCSP_H__
#define BCSP_TXWINSIZE 4
#define BCSP_ACK_PKT 0x05
#define BCSP_LE_PKT 0x06
struct bcsp_struct {
struct sk_buff_head unack; /* Unack'ed packets queue */
struct sk_buff_head rel; /* Reliable packets queue */
struct sk_buff_head unrel; /* Unreliable packets queue */
unsigned long rx_count;
struct sk_buff *rx_skb;
u8 rxseq_txack; /* rxseq == txack. */
u8 rxack; /* Last packet sent by us that the peer ack'ed */
struct timer_list tbcsp;
enum {
BCSP_W4_PKT_DELIMITER,
BCSP_W4_PKT_START,
BCSP_W4_BCSP_HDR,
BCSP_W4_DATA,
BCSP_W4_CRC
} rx_state;
enum {
BCSP_ESCSTATE_NOESC,
BCSP_ESCSTATE_ESC
} rx_esc_state;
u16 message_crc;
u8 txack_req; /* Do we need to send ack's to the peer? */
/* Reliable packet sequence number - used to assign seq to each rel pkt. */
u8 msgq_txseq;
};
#endif /* __HCI_BCSP_H__ */
^ permalink raw reply [flat|nested] 17+ messages in thread