Commit 0d453bba authored by Maksim Krasnyanskiy's avatar Maksim Krasnyanskiy Committed by Maksim Krasnyanskiy

Syncup HCI UART driver with 2.4.x.

New improved UART proto interface.
Support for BCSP protocol.
parent 4523ef76
...@@ -12,11 +12,20 @@ CONFIG_BLUEZ_HCIUART ...@@ -12,11 +12,20 @@ CONFIG_BLUEZ_HCIUART
HCI UART (H4) protocol support HCI UART (H4) protocol support
CONFIG_BLUEZ_HCIUART_H4 CONFIG_BLUEZ_HCIUART_H4
UART (H4) is serial protocol for communication between Bluetooth UART (H4) is serial protocol for communication between Bluetooth
device and host. This protocol is required for most UART based device and host. This protocol is required for most Bluetooth devices
Bluetooth device (including PCMCIA and CF). with UART interface, including PCMCIA and CF cards.
Say Y here to compile support for HCI UART (H4) protocol. Say Y here to compile support for HCI UART (H4) protocol.
HCI BCSP protocol support
CONFIG_BLUEZ_HCIUART_BCSP
BCSP (BlueCore Serial Protocol) is serial protocol for communication
between Bluetooth device and host. This protocol is required for non
USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
CF cards.
Say Y here to compile support for HCI BCSP protocol.
HCI USB driver HCI USB driver
CONFIG_BLUEZ_HCIUSB CONFIG_BLUEZ_HCIUSB
Bluetooth HCI USB driver. Bluetooth HCI USB driver.
......
...@@ -9,6 +9,7 @@ fi ...@@ -9,6 +9,7 @@ fi
dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4 bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
fi fi
dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
......
...@@ -11,6 +11,7 @@ obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o ...@@ -11,6 +11,7 @@ obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
hci_uart-y := hci_ldisc.o hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o
hci_uart-objs := $(hci_uart-y) hci_uart-objs := $(hci_uart-y)
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
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 $
*/
#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_bcsp.h"
#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;
}
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 = 6; /* BCSP ACL channel */
rel = 1; /* reliable channel */
break;
case HCI_COMMAND_PKT:
chan = 5; /* BCSP cmd/evt channel */
rel = 1; /* reliable channel */
break;
case HCI_SCODATA_PKT:
chan = 7; /* BCSP SCO channel */
rel = 0; /* unreliable channel */
break;
case BCSP_LE_PKT:
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;
}
}
}
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) == 6 &&
bcsp->rx_skb->data[0] & 0x80) {
bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
bcsp->rx_skb->data[0] & 0x80) {
bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
!(bcsp->rx_skb->data[0] & 0x80)) {
bcsp_handle_le_pkt(hu);
pass_up = 0;
} else
pass_up = 0;
if (!pass_up) {
if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
(bcsp->rx_skb->data[1] & 0x0f) != 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);
}
/*
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__ */
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
/* /*
* BlueZ HCI UART(H4) protocol. * BlueZ HCI UART(H4) protocol.
* *
* $Id: hci_h4.c,v 1.2 2002/04/17 17:37:20 maxk Exp $ * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $
*/ */
#define VERSION "1.1" #define VERSION "1.2"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -64,63 +64,61 @@ ...@@ -64,63 +64,61 @@
#endif #endif
/* Initialize protocol */ /* Initialize protocol */
static int h4_open(struct n_hci *n_hci) static int h4_open(struct hci_uart *hu)
{ {
struct h4_struct *h4; struct h4_struct *h4;
BT_DBG("n_hci %p", n_hci); BT_DBG("hu %p", hu);
h4 = kmalloc(sizeof(*h4), GFP_ATOMIC); h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
if (!h4) if (!h4)
return -ENOMEM; return -ENOMEM;
memset(h4, 0, sizeof(*h4)); memset(h4, 0, sizeof(*h4));
n_hci->priv = h4; skb_queue_head_init(&h4->txq);
hu->priv = h4;
return 0; return 0;
} }
/* Flush protocol data */ /* Flush protocol data */
static int h4_flush(struct n_hci *n_hci) static int h4_flush(struct hci_uart *hu)
{ {
BT_DBG("n_hci %p", n_hci); struct h4_struct *h4 = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&h4->txq);
return 0; return 0;
} }
/* Close protocol */ /* Close protocol */
static int h4_close(struct n_hci *n_hci) static int h4_close(struct hci_uart *hu)
{ {
struct h4_struct *h4 = n_hci->priv; struct h4_struct *h4 = hu->priv;
n_hci->priv = NULL; hu->priv = NULL;
BT_DBG("n_hci %p", n_hci); BT_DBG("hu %p", hu);
skb_queue_purge(&h4->txq);
if (h4->rx_skb) if (h4->rx_skb)
kfree_skb(h4->rx_skb); kfree_skb(h4->rx_skb);
hu->priv = NULL;
kfree(h4); kfree(h4);
return 0; return 0;
} }
/* Send data */ /* Enqueue frame for transmittion (padding, crc, etc) */
static int h4_send(struct n_hci *n_hci, void *data, int len) static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{ {
struct tty_struct *tty = n_hci->tty; struct h4_struct *h4 = hu->priv;
BT_DBG("n_hci %p len %d", n_hci, len);
/* Send frame to TTY driver */
tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
return tty->driver.write(tty, 0, data, len);
}
/* Init frame before queueing (padding, crc, etc) */ BT_DBG("hu %p skb %p", hu, skb);
static struct sk_buff* h4_preq(struct n_hci *n_hci, struct sk_buff *skb)
{
BT_DBG("n_hci %p skb %p", n_hci, skb);
/* Prepend skb with frame type */ /* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &skb->pkt_type, 1); memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
return skb; skb_queue_tail(&h4->txq, skb);
return 0;
} }
static inline int h4_check_data_len(struct h4_struct *h4, int len) static inline int h4_check_data_len(struct h4_struct *h4, int len)
...@@ -132,7 +130,7 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len) ...@@ -132,7 +130,7 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len)
BT_DMP(h4->rx_skb->data, h4->rx_skb->len); BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
hci_recv_frame(h4->rx_skb); hci_recv_frame(h4->rx_skb);
} else if (len > room) { } else if (len > room) {
BT_ERR("Data length is to large"); BT_ERR("Data length is too large");
kfree_skb(h4->rx_skb); kfree_skb(h4->rx_skb);
} else { } else {
h4->rx_state = H4_W4_DATA; h4->rx_state = H4_W4_DATA;
...@@ -147,16 +145,17 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len) ...@@ -147,16 +145,17 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len)
} }
/* Recv data */ /* Recv data */
static int h4_recv(struct n_hci *n_hci, void *data, int count) static int h4_recv(struct hci_uart *hu, void *data, int count)
{ {
struct h4_struct *h4 = n_hci->priv; struct h4_struct *h4 = hu->priv;
register char *ptr; register char *ptr;
hci_event_hdr *eh; hci_event_hdr *eh;
hci_acl_hdr *ah; hci_acl_hdr *ah;
hci_sco_hdr *sh; hci_sco_hdr *sh;
register int len, type, dlen; register int len, type, dlen;
BT_DBG("n_hci %p count %d rx_state %ld rx_count %ld", n_hci, count, h4->rx_state, h4->rx_count); BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
hu, count, h4->rx_state, h4->rx_count);
ptr = data; ptr = data;
while (count) { while (count) {
...@@ -204,7 +203,7 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count) ...@@ -204,7 +203,7 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count)
h4_check_data_len(h4, sh->dlen); h4_check_data_len(h4, sh->dlen);
continue; continue;
}; }
} }
/* H4_W4_PACKET_TYPE */ /* H4_W4_PACKET_TYPE */
...@@ -232,7 +231,7 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count) ...@@ -232,7 +231,7 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count)
default: default:
BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
n_hci->hdev.stat.err_rx++; hu->hdev.stat.err_rx++;
ptr++; count--; ptr++; count--;
continue; continue;
}; };
...@@ -246,20 +245,26 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count) ...@@ -246,20 +245,26 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count)
h4->rx_count = 0; h4->rx_count = 0;
return 0; return 0;
} }
h4->rx_skb->dev = (void *) &n_hci->hdev; h4->rx_skb->dev = (void *) &hu->hdev;
h4->rx_skb->pkt_type = type; h4->rx_skb->pkt_type = type;
} }
return count; return count;
} }
static struct sk_buff *h4_dequeue(struct hci_uart *hu)
{
struct h4_struct *h4 = hu->priv;
return skb_dequeue(&h4->txq);
}
static struct hci_uart_proto h4p = { static struct hci_uart_proto h4p = {
.id = HCI_UART_H4, .id = HCI_UART_H4,
.open = h4_open, .open = h4_open,
.close = h4_close, .close = h4_close,
.send = h4_send, .recv = h4_recv,
.recv = h4_recv, .enqueue = h4_enqueue,
.preq = h4_preq, .dequeue = h4_dequeue,
.flush = h4_flush, .flush = h4_flush,
}; };
int h4_init(void) int h4_init(void)
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
*/ */
/* /*
* $Id: hci_h4.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ * $Id: hci_h4.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
*/ */
#ifdef __KERNEL__ #ifdef __KERNEL__
...@@ -31,6 +31,7 @@ struct h4_struct { ...@@ -31,6 +31,7 @@ struct h4_struct {
unsigned long rx_state; unsigned long rx_state;
unsigned long rx_count; unsigned long rx_count;
struct sk_buff *rx_skb; struct sk_buff *rx_skb;
struct sk_buff_head txq;
}; };
/* H4 receiver States */ /* H4 receiver States */
......
...@@ -25,14 +25,15 @@ ...@@ -25,14 +25,15 @@
/* /*
* BlueZ HCI UART driver. * BlueZ HCI UART driver.
* *
* $Id: hci_ldisc.c,v 1.2 2002/04/17 17:37:20 maxk Exp $ * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $
*/ */
#define VERSION "2.0" #define VERSION "2.1"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -87,16 +88,86 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p) ...@@ -87,16 +88,86 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p)
return 0; return 0;
} }
static struct hci_uart_proto *n_hci_get_proto(unsigned int id) static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
{ {
if (id >= HCI_UART_MAX_PROTO) if (id >= HCI_UART_MAX_PROTO)
return NULL; return NULL;
return hup[id]; return hup[id];
} }
static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
{
struct hci_dev *hdev = &hu->hdev;
/* Update HCI stat counters */
switch (pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.cmd_tx++;
break;
}
}
static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
{
struct sk_buff *skb = hu->tx_skb;
if (!skb)
skb = hu->proto->dequeue(hu);
else
hu->tx_skb = NULL;
return skb;
}
int hci_uart_tx_wakeup(struct hci_uart *hu)
{
struct tty_struct *tty = hu->tty;
struct hci_dev *hdev = &hu->hdev;
struct sk_buff *skb;
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
return 0;
}
BT_DBG("");
restart:
clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
while ((skb = hci_uart_dequeue(hu))) {
int len;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
len = tty->driver.write(tty, 0, skb->data, skb->len);
hdev->stat.byte_tx += len;
skb_pull(skb, len);
if (skb->len) {
hu->tx_skb = skb;
break;
}
hci_uart_tx_complete(hu, skb->pkt_type);
kfree_skb(skb);
}
if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
goto restart;
clear_bit(HCI_UART_SENDING, &hu->tx_state);
return 0;
}
/* ------- Interface to HCI layer ------ */ /* ------- Interface to HCI layer ------ */
/* Initialize device */ /* Initialize device */
static int n_hci_open(struct hci_dev *hdev) static int hci_uart_open(struct hci_dev *hdev)
{ {
BT_DBG("%s %p", hdev->name, hdev); BT_DBG("%s %p", hdev->name, hdev);
...@@ -107,15 +178,16 @@ static int n_hci_open(struct hci_dev *hdev) ...@@ -107,15 +178,16 @@ static int n_hci_open(struct hci_dev *hdev)
} }
/* Reset device */ /* Reset device */
static int n_hci_flush(struct hci_dev *hdev) static int hci_uart_flush(struct hci_dev *hdev)
{ {
struct n_hci *n_hci = (struct n_hci *) hdev->driver_data; struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
struct tty_struct *tty = n_hci->tty; struct tty_struct *tty = hu->tty;
BT_DBG("hdev %p tty %p", hdev, tty); BT_DBG("hdev %p tty %p", hdev, tty);
/* Drop TX queue */ if (hu->tx_skb) {
skb_queue_purge(&n_hci->txq); kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
}
/* Flush any pending characters in the driver and discipline. */ /* Flush any pending characters in the driver and discipline. */
if (tty->ldisc.flush_buffer) if (tty->ldisc.flush_buffer)
...@@ -124,80 +196,30 @@ static int n_hci_flush(struct hci_dev *hdev) ...@@ -124,80 +196,30 @@ static int n_hci_flush(struct hci_dev *hdev)
if (tty->driver.flush_buffer) if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty); tty->driver.flush_buffer(tty);
if (n_hci->proto->flush) if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
n_hci->proto->flush(n_hci); hu->proto->flush(hu);
return 0; return 0;
} }
/* Close device */ /* Close device */
static int n_hci_close(struct hci_dev *hdev) static int hci_uart_close(struct hci_dev *hdev)
{ {
BT_DBG("hdev %p", hdev); BT_DBG("hdev %p", hdev);
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0; return 0;
n_hci_flush(hdev); hci_uart_flush(hdev);
return 0;
}
static int n_hci_tx_wakeup(struct n_hci *n_hci)
{
struct hci_dev *hdev = &n_hci->hdev;
if (test_and_set_bit(N_HCI_SENDING, &n_hci->tx_state)) {
set_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
return 0;
}
BT_DBG("");
do {
register struct sk_buff *skb;
register int len;
clear_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
if (!(skb = skb_dequeue(&n_hci->txq)))
break;
len = n_hci->proto->send(n_hci, skb->data, skb->len);
n_hci->hdev.stat.byte_tx += len;
if (len == skb->len) {
/* Complete frame was sent */
switch (skb->pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.cmd_tx++;
break;
};
kfree_skb(skb);
} else {
/* Subtract sent part and requeue */
skb_pull(skb, len);
skb_queue_head(&n_hci->txq, skb);
}
} while (test_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state));
clear_bit(N_HCI_SENDING, &n_hci->tx_state);
return 0; return 0;
} }
/* Send frames from HCI layer */ /* Send frames from HCI layer */
static int n_hci_send_frame(struct sk_buff *skb) static int hci_uart_send_frame(struct sk_buff *skb)
{ {
struct hci_dev* hdev = (struct hci_dev *) skb->dev; struct hci_dev* hdev = (struct hci_dev *) skb->dev;
struct tty_struct *tty; struct tty_struct *tty;
struct n_hci *n_hci; struct hci_uart *hu;
if (!hdev) { if (!hdev) {
BT_ERR("Frame for uknown device (hdev=NULL)"); BT_ERR("Frame for uknown device (hdev=NULL)");
...@@ -207,66 +229,60 @@ static int n_hci_send_frame(struct sk_buff *skb) ...@@ -207,66 +229,60 @@ static int n_hci_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags)) if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY; return -EBUSY;
n_hci = (struct n_hci *) hdev->driver_data; hu = (struct hci_uart *) hdev->driver_data;
tty = n_hci->tty; tty = hu->tty;
BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len); BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
if (n_hci->proto->preq) { hu->proto->enqueue(hu, skb);
skb = n_hci->proto->preq(n_hci, skb);
if (!skb) hci_uart_tx_wakeup(hu);
return 0;
}
skb_queue_tail(&n_hci->txq, skb);
n_hci_tx_wakeup(n_hci);
return 0; return 0;
} }
static void n_hci_destruct(struct hci_dev *hdev) static void hci_uart_destruct(struct hci_dev *hdev)
{ {
struct n_hci *n_hci; struct hci_uart *hu;
if (!hdev) return; if (!hdev) return;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
n_hci = (struct n_hci *) hdev->driver_data; hu = (struct hci_uart *) hdev->driver_data;
kfree(n_hci); kfree(hu);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} }
/* ------ LDISC part ------ */ /* ------ LDISC part ------ */
/* n_hci_tty_open /* hci_uart_tty_open
* *
* Called when line discipline changed to N_HCI. * Called when line discipline changed to HCI_UART.
* *
* Arguments: * Arguments:
* tty pointer to tty info structure * tty pointer to tty info structure
* Return Value: * Return Value:
* 0 if success, otherwise error code * 0 if success, otherwise error code
*/ */
static int n_hci_tty_open(struct tty_struct *tty) static int hci_uart_tty_open(struct tty_struct *tty)
{ {
struct n_hci *n_hci = (void *)tty->disc_data; struct hci_uart *hu = (void *) tty->disc_data;
BT_DBG("tty %p", tty); BT_DBG("tty %p", tty);
if (n_hci) if (hu)
return -EEXIST; return -EEXIST;
if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) { if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
BT_ERR("Can't allocate controll structure"); BT_ERR("Can't allocate controll structure");
return -ENFILE; return -ENFILE;
} }
memset(n_hci, 0, sizeof(struct n_hci)); memset(hu, 0, sizeof(struct hci_uart));
tty->disc_data = n_hci; tty->disc_data = hu;
n_hci->tty = tty; hu->tty = tty;
spin_lock_init(&n_hci->rx_lock); spin_lock_init(&hu->rx_lock);
skb_queue_head_init(&n_hci->txq);
/* Flush any pending characters in the driver and line discipline */ /* Flush any pending characters in the driver and line discipline */
if (tty->ldisc.flush_buffer) if (tty->ldisc.flush_buffer)
...@@ -279,34 +295,34 @@ static int n_hci_tty_open(struct tty_struct *tty) ...@@ -279,34 +295,34 @@ static int n_hci_tty_open(struct tty_struct *tty)
return 0; return 0;
} }
/* n_hci_tty_close() /* hci_uart_tty_close()
* *
* Called when the line discipline is changed to something * Called when the line discipline is changed to something
* else, the tty is closed, or the tty detects a hangup. * else, the tty is closed, or the tty detects a hangup.
*/ */
static void n_hci_tty_close(struct tty_struct *tty) static void hci_uart_tty_close(struct tty_struct *tty)
{ {
struct n_hci *n_hci = (void *)tty->disc_data; struct hci_uart *hu = (void *)tty->disc_data;
BT_DBG("tty %p", tty); BT_DBG("tty %p", tty);
/* Detach from the tty */ /* Detach from the tty */
tty->disc_data = NULL; tty->disc_data = NULL;
if (n_hci) { if (hu) {
struct hci_dev *hdev = &n_hci->hdev; struct hci_dev *hdev = &hu->hdev;
n_hci_close(hdev); hci_uart_close(hdev);
if (test_and_clear_bit(N_HCI_PROTO_SET, &n_hci->flags)) { if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
n_hci->proto->close(n_hci); hu->proto->close(hu);
hci_unregister_dev(hdev); hci_unregister_dev(hdev);
} }
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} }
} }
/* n_hci_tty_wakeup() /* hci_uart_tty_wakeup()
* *
* Callback for transmit wakeup. Called when low level * Callback for transmit wakeup. Called when low level
* device driver can accept more send data. * device driver can accept more send data.
...@@ -314,24 +330,24 @@ static void n_hci_tty_close(struct tty_struct *tty) ...@@ -314,24 +330,24 @@ static void n_hci_tty_close(struct tty_struct *tty)
* Arguments: tty pointer to associated tty instance data * Arguments: tty pointer to associated tty instance data
* Return Value: None * Return Value: None
*/ */
static void n_hci_tty_wakeup( struct tty_struct *tty ) static void hci_uart_tty_wakeup(struct tty_struct *tty)
{ {
struct n_hci *n_hci = (void *)tty->disc_data; struct hci_uart *hu = (void *)tty->disc_data;
BT_DBG(""); BT_DBG("");
if (!n_hci) if (!hu)
return; return;
tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (tty != n_hci->tty) if (tty != hu->tty)
return; return;
n_hci_tx_wakeup(n_hci); hci_uart_tx_wakeup(hu);
} }
/* n_hci_tty_room() /* hci_uart_tty_room()
* *
* Callback function from tty driver. Return the amount of * Callback function from tty driver. Return the amount of
* space left in the receiver's buffer to decide if remote * space left in the receiver's buffer to decide if remote
...@@ -340,12 +356,12 @@ static void n_hci_tty_wakeup( struct tty_struct *tty ) ...@@ -340,12 +356,12 @@ static void n_hci_tty_wakeup( struct tty_struct *tty )
* Arguments: tty pointer to associated tty instance data * Arguments: tty pointer to associated tty instance data
* Return Value: number of bytes left in receive buffer * Return Value: number of bytes left in receive buffer
*/ */
static int n_hci_tty_room (struct tty_struct *tty) static int hci_uart_tty_room (struct tty_struct *tty)
{ {
return 65536; return 65536;
} }
/* n_hci_tty_receive() /* hci_uart_tty_receive()
* *
* Called by tty low level driver when receive data is * Called by tty low level driver when receive data is
* available. * available.
...@@ -357,42 +373,42 @@ static int n_hci_tty_room (struct tty_struct *tty) ...@@ -357,42 +373,42 @@ static int n_hci_tty_room (struct tty_struct *tty)
* *
* Return Value: None * Return Value: None
*/ */
static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count) static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
{ {
struct n_hci *n_hci = (void *)tty->disc_data; struct hci_uart *hu = (void *)tty->disc_data;
if (!n_hci || tty != n_hci->tty) if (!hu || tty != hu->tty)
return; return;
if (!test_bit(N_HCI_PROTO_SET, &n_hci->flags)) if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
return; return;
spin_lock(&n_hci->rx_lock); spin_lock(&hu->rx_lock);
n_hci->proto->recv(n_hci, (void *) data, count); hu->proto->recv(hu, (void *) data, count);
n_hci->hdev.stat.byte_rx += count; hu->hdev.stat.byte_rx += count;
spin_unlock(&n_hci->rx_lock); spin_unlock(&hu->rx_lock);
if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle) if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
tty->driver.unthrottle(tty); tty->driver.unthrottle(tty);
} }
static int n_hci_register_dev(struct n_hci *n_hci) static int hci_uart_register_dev(struct hci_uart *hu)
{ {
struct hci_dev *hdev; struct hci_dev *hdev;
BT_DBG(""); BT_DBG("");
/* Initialize and register HCI device */ /* Initialize and register HCI device */
hdev = &n_hci->hdev; hdev = &hu->hdev;
hdev->type = HCI_UART; hdev->type = HCI_UART;
hdev->driver_data = n_hci; hdev->driver_data = hu;
hdev->open = n_hci_open; hdev->open = hci_uart_open;
hdev->close = n_hci_close; hdev->close = hci_uart_close;
hdev->flush = n_hci_flush; hdev->flush = hci_uart_flush;
hdev->send = n_hci_send_frame; hdev->send = hci_uart_send_frame;
hdev->destruct = n_hci_destruct; hdev->destruct = hci_uart_destruct;
if (hci_register_dev(hdev) < 0) { if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device %s", hdev->name); BT_ERR("Can't register HCI device %s", hdev->name);
...@@ -402,30 +418,30 @@ static int n_hci_register_dev(struct n_hci *n_hci) ...@@ -402,30 +418,30 @@ static int n_hci_register_dev(struct n_hci *n_hci)
return 0; return 0;
} }
static int n_hci_set_proto(struct n_hci *n_hci, int id) static int hci_uart_set_proto(struct hci_uart *hu, int id)
{ {
struct hci_uart_proto *p; struct hci_uart_proto *p;
int err; int err;
p = n_hci_get_proto(id); p = hci_uart_get_proto(id);
if (!p) if (!p)
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
err = p->open(n_hci); err = p->open(hu);
if (err) if (err)
return err; return err;
n_hci->proto = p; hu->proto = p;
err = n_hci_register_dev(n_hci); err = hci_uart_register_dev(hu);
if (err) { if (err) {
p->close(n_hci); p->close(hu);
return err; return err;
} }
return 0; return 0;
} }
/* n_hci_tty_ioctl() /* hci_uart_tty_ioctl()
* *
* Process IOCTL system call for the tty device. * Process IOCTL system call for the tty device.
* *
...@@ -438,24 +454,24 @@ static int n_hci_set_proto(struct n_hci *n_hci, int id) ...@@ -438,24 +454,24 @@ static int n_hci_set_proto(struct n_hci *n_hci, int id)
* *
* Return Value: Command dependent * Return Value: Command dependent
*/ */
static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file, static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct n_hci *n_hci = (void *)tty->disc_data; struct hci_uart *hu = (void *)tty->disc_data;
int err = 0; int err = 0;
BT_DBG(""); BT_DBG("");
/* Verify the status of the device */ /* Verify the status of the device */
if (!n_hci) if (!hu)
return -EBADF; return -EBADF;
switch (cmd) { switch (cmd) {
case HCIUARTSETPROTO: case HCIUARTSETPROTO:
if (!test_and_set_bit(N_HCI_PROTO_SET, &n_hci->flags)) { if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
err = n_hci_set_proto(n_hci, arg); err = hci_uart_set_proto(hu, arg);
if (err) { if (err) {
clear_bit(N_HCI_PROTO_SET, &n_hci->flags); clear_bit(HCI_UART_PROTO_SET, &hu->flags);
return err; return err;
} }
tty->low_latency = 1; tty->low_latency = 1;
...@@ -463,8 +479,8 @@ static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file, ...@@ -463,8 +479,8 @@ static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file,
return -EBUSY; return -EBUSY;
case HCIUARTGETPROTO: case HCIUARTGETPROTO:
if (test_bit(N_HCI_PROTO_SET, &n_hci->flags)) if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
return n_hci->proto->id; return hu->proto->id;
return -EUNATCH; return -EUNATCH;
default: default:
...@@ -478,15 +494,15 @@ static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file, ...@@ -478,15 +494,15 @@ static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file,
/* /*
* We don't provide read/write/poll interface for user space. * We don't provide read/write/poll interface for user space.
*/ */
static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
{ {
return 0; return 0;
} }
static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
{ {
return 0; return 0;
} }
static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
{ {
return 0; return 0;
} }
...@@ -495,10 +511,14 @@ static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, po ...@@ -495,10 +511,14 @@ static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, po
int h4_init(void); int h4_init(void);
int h4_deinit(void); int h4_deinit(void);
#endif #endif
#ifdef CONFIG_BLUEZ_HCIUART_BCSP
int bcsp_init(void);
int bcsp_deinit(void);
#endif
int __init n_hci_init(void) int __init hci_uart_init(void)
{ {
static struct tty_ldisc n_hci_ldisc; static struct tty_ldisc hci_uart_ldisc;
int err; int err;
BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
...@@ -507,20 +527,20 @@ int __init n_hci_init(void) ...@@ -507,20 +527,20 @@ int __init n_hci_init(void)
/* Register the tty discipline */ /* Register the tty discipline */
memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc)); memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
n_hci_ldisc.magic = TTY_LDISC_MAGIC; hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
n_hci_ldisc.name = "n_hci"; hci_uart_ldisc.name = "n_hci";
n_hci_ldisc.open = n_hci_tty_open; hci_uart_ldisc.open = hci_uart_tty_open;
n_hci_ldisc.close = n_hci_tty_close; hci_uart_ldisc.close = hci_uart_tty_close;
n_hci_ldisc.read = n_hci_tty_read; hci_uart_ldisc.read = hci_uart_tty_read;
n_hci_ldisc.write = n_hci_tty_write; hci_uart_ldisc.write = hci_uart_tty_write;
n_hci_ldisc.ioctl = n_hci_tty_ioctl; hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
n_hci_ldisc.poll = n_hci_tty_poll; hci_uart_ldisc.poll = hci_uart_tty_poll;
n_hci_ldisc.receive_room= n_hci_tty_room; hci_uart_ldisc.receive_room= hci_uart_tty_room;
n_hci_ldisc.receive_buf = n_hci_tty_receive; hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
n_hci_ldisc.write_wakeup= n_hci_tty_wakeup; hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) { if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
BT_ERR("Can't register HCI line discipline (%d)", err); BT_ERR("Can't register HCI line discipline (%d)", err);
return err; return err;
} }
...@@ -528,25 +548,31 @@ int __init n_hci_init(void) ...@@ -528,25 +548,31 @@ int __init n_hci_init(void)
#ifdef CONFIG_BLUEZ_HCIUART_H4 #ifdef CONFIG_BLUEZ_HCIUART_H4
h4_init(); h4_init();
#endif #endif
#ifdef CONFIG_BLUEZ_HCIUART_BCSP
bcsp_init();
#endif
return 0; return 0;
} }
void n_hci_cleanup(void) void hci_uart_cleanup(void)
{ {
int err; int err;
#ifdef CONFIG_BLUEZ_HCIUART_H4 #ifdef CONFIG_BLUEZ_HCIUART_H4
h4_deinit(); h4_deinit();
#endif #endif
#ifdef CONFIG_BLUEZ_HCIUART_BCSP
bcsp_deinit();
#endif
/* Release tty registration of line discipline */ /* Release tty registration of line discipline */
if ((err = tty_register_ldisc(N_HCI, NULL))) if ((err = tty_register_ldisc(N_HCI, NULL)))
BT_ERR("Can't unregister HCI line discipline (%d)", err); BT_ERR("Can't unregister HCI line discipline (%d)", err);
} }
module_init(n_hci_init); module_init(hci_uart_init);
module_exit(n_hci_cleanup); module_exit(hci_uart_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION); MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
......
...@@ -23,10 +23,10 @@ ...@@ -23,10 +23,10 @@
*/ */
/* /*
* $Id: hci_uart.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ * $Id: hci_uart.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
*/ */
#ifndef N_HCI #ifndef N_HCI
#define N_HCI 15 #define N_HCI 15
#endif #endif
...@@ -42,19 +42,19 @@ ...@@ -42,19 +42,19 @@
#define HCI_UART_NCSP 2 #define HCI_UART_NCSP 2
#ifdef __KERNEL__ #ifdef __KERNEL__
struct n_hci; struct hci_uart;
struct hci_uart_proto { struct hci_uart_proto {
unsigned int id; unsigned int id;
int (*open)(struct n_hci *n_hci); int (*open)(struct hci_uart *hu);
int (*recv)(struct n_hci *n_hci, void *data, int len); int (*close)(struct hci_uart *hu);
int (*send)(struct n_hci *n_hci, void *data, int len); int (*flush)(struct hci_uart *hu);
int (*close)(struct n_hci *n_hci); int (*recv)(struct hci_uart *hu, void *data, int len);
int (*flush)(struct n_hci *n_hci); int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
struct sk_buff* (*preq)(struct n_hci *n_hci, struct sk_buff *skb); struct sk_buff *(*dequeue)(struct hci_uart *hu);
}; };
struct n_hci { struct hci_uart {
struct tty_struct *tty; struct tty_struct *tty;
struct hci_dev hdev; struct hci_dev hdev;
unsigned long flags; unsigned long flags;
...@@ -62,19 +62,20 @@ struct n_hci { ...@@ -62,19 +62,20 @@ struct n_hci {
struct hci_uart_proto *proto; struct hci_uart_proto *proto;
void *priv; void *priv;
struct sk_buff_head txq; struct sk_buff *tx_skb;
unsigned long tx_state; unsigned long tx_state;
spinlock_t rx_lock; spinlock_t rx_lock;
}; };
/* N_HCI flag bits */ /* HCI_UART flag bits */
#define N_HCI_PROTO_SET 0x00 #define HCI_UART_PROTO_SET 0x00
/* TX states */ /* TX states */
#define N_HCI_SENDING 1 #define HCI_UART_SENDING 1
#define N_HCI_TX_WAKEUP 2 #define HCI_UART_TX_WAKEUP 2
int hci_uart_register_proto(struct hci_uart_proto *p); int hci_uart_register_proto(struct hci_uart_proto *p);
int hci_uart_unregister_proto(struct hci_uart_proto *p); int hci_uart_unregister_proto(struct hci_uart_proto *p);
int hci_uart_tx_wakeup(struct hci_uart *hu);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment