Commit 6cab0e06 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-bt.bkbits.net/bt-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 61c4b8fb 5096ce89
......@@ -12,11 +12,20 @@ CONFIG_BLUEZ_HCIUART
HCI UART (H4) protocol support
CONFIG_BLUEZ_HCIUART_H4
UART (H4) is serial protocol for communication between Bluetooth
device and host. This protocol is required for most UART based
Bluetooth device (including PCMCIA and CF).
device and host. This protocol is required for most Bluetooth devices
with UART interface, including PCMCIA and CF cards.
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
CONFIG_BLUEZ_HCIUSB
Bluetooth HCI USB driver.
......@@ -26,15 +35,6 @@ CONFIG_BLUEZ_HCIUSB
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (hci_usb.o).
HCI USB firmware download support
CONFIG_BLUEZ_USB_FW_LOAD
Firmware download support for Bluetooth USB devices.
This support is required for devices like Broadcom BCM2033.
HCI USB driver uses external firmware downloader program provided
in BlueFW package.
For more information, see <http://bluez.sf.net/>.
HCI USB zero packet support
CONFIG_BLUEZ_USB_ZERO_PACKET
Support for USB zero packets.
......
......@@ -3,13 +3,13 @@ comment 'Bluetooth device drivers'
dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
bool ' Firmware download support' CONFIG_BLUEZ_USB_FW_LOAD
bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET
fi
dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
fi
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
hci_uart-y := hci_ldisc.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)
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 @@
/*
* 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/module.h>
......@@ -64,63 +64,61 @@
#endif
/* Initialize protocol */
static int h4_open(struct n_hci *n_hci)
static int h4_open(struct hci_uart *hu)
{
struct h4_struct *h4;
BT_DBG("n_hci %p", n_hci);
BT_DBG("hu %p", hu);
h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
if (!h4)
return -ENOMEM;
memset(h4, 0, sizeof(*h4));
n_hci->priv = h4;
skb_queue_head_init(&h4->txq);
hu->priv = h4;
return 0;
}
/* 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;
}
/* 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;
n_hci->priv = NULL;
struct h4_struct *h4 = hu->priv;
hu->priv = NULL;
BT_DBG("n_hci %p", n_hci);
BT_DBG("hu %p", hu);
skb_queue_purge(&h4->txq);
if (h4->rx_skb)
kfree_skb(h4->rx_skb);
hu->priv = NULL;
kfree(h4);
return 0;
}
/* Send data */
static int h4_send(struct n_hci *n_hci, void *data, int len)
/* Enqueue frame for transmittion (padding, crc, etc) */
static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct tty_struct *tty = n_hci->tty;
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);
}
struct h4_struct *h4 = hu->priv;
/* Init frame before queueing (padding, crc, etc) */
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);
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
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)
......@@ -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);
hci_recv_frame(h4->rx_skb);
} else if (len > room) {
BT_ERR("Data length is to large");
BT_ERR("Data length is too large");
kfree_skb(h4->rx_skb);
} else {
h4->rx_state = H4_W4_DATA;
......@@ -147,16 +145,17 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len)
}
/* 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;
hci_event_hdr *eh;
hci_acl_hdr *ah;
hci_sco_hdr *sh;
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;
while (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);
continue;
};
}
}
/* H4_W4_PACKET_TYPE */
......@@ -232,7 +231,7 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count)
default:
BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
n_hci->hdev.stat.err_rx++;
hu->hdev.stat.err_rx++;
ptr++; count--;
continue;
};
......@@ -246,20 +245,26 @@ static int h4_recv(struct n_hci *n_hci, void *data, int count)
h4->rx_count = 0;
return 0;
}
h4->rx_skb->dev = (void *) &n_hci->hdev;
h4->rx_skb->dev = (void *) &hu->hdev;
h4->rx_skb->pkt_type = type;
}
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 = {
.id = HCI_UART_H4,
.open = h4_open,
.close = h4_close,
.send = h4_send,
.recv = h4_recv,
.preq = h4_preq,
.flush = h4_flush,
.id = HCI_UART_H4,
.open = h4_open,
.close = h4_close,
.recv = h4_recv,
.enqueue = h4_enqueue,
.dequeue = h4_dequeue,
.flush = h4_flush,
};
int h4_init(void)
......
......@@ -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__
......@@ -31,6 +31,7 @@ struct h4_struct {
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
};
/* H4 receiver States */
......
......@@ -25,14 +25,15 @@
/*
* 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/module.h>
#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
......@@ -87,16 +88,86 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p)
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)
return NULL;
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 ------ */
/* 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);
......@@ -107,15 +178,16 @@ static int n_hci_open(struct hci_dev *hdev)
}
/* 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 tty_struct *tty = n_hci->tty;
struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
struct tty_struct *tty = hu->tty;
BT_DBG("hdev %p tty %p", hdev, tty);
/* Drop TX queue */
skb_queue_purge(&n_hci->txq);
if (hu->tx_skb) {
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
}
/* Flush any pending characters in the driver and discipline. */
if (tty->ldisc.flush_buffer)
......@@ -124,80 +196,30 @@ static int n_hci_flush(struct hci_dev *hdev)
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
if (n_hci->proto->flush)
n_hci->proto->flush(n_hci);
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
hu->proto->flush(hu);
return 0;
}
/* 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);
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
n_hci_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);
hci_uart_flush(hdev);
return 0;
}
/* 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 tty_struct *tty;
struct n_hci *n_hci;
struct hci_uart *hu;
if (!hdev) {
BT_ERR("Frame for uknown device (hdev=NULL)");
......@@ -207,66 +229,60 @@ static int n_hci_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
n_hci = (struct n_hci *) hdev->driver_data;
tty = n_hci->tty;
hu = (struct hci_uart *) hdev->driver_data;
tty = hu->tty;
BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
if (n_hci->proto->preq) {
skb = n_hci->proto->preq(n_hci, skb);
if (!skb)
return 0;
}
skb_queue_tail(&n_hci->txq, skb);
n_hci_tx_wakeup(n_hci);
hu->proto->enqueue(hu, skb);
hci_uart_tx_wakeup(hu);
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;
BT_DBG("%s", hdev->name);
n_hci = (struct n_hci *) hdev->driver_data;
kfree(n_hci);
hu = (struct hci_uart *) hdev->driver_data;
kfree(hu);
MOD_DEC_USE_COUNT;
}
/* ------ 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:
* tty pointer to tty info structure
* Return Value:
* 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);
if (n_hci)
if (hu)
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");
return -ENFILE;
}
memset(n_hci, 0, sizeof(struct n_hci));
memset(hu, 0, sizeof(struct hci_uart));
tty->disc_data = n_hci;
n_hci->tty = tty;
tty->disc_data = hu;
hu->tty = tty;
spin_lock_init(&n_hci->rx_lock);
skb_queue_head_init(&n_hci->txq);
spin_lock_init(&hu->rx_lock);
/* Flush any pending characters in the driver and line discipline */
if (tty->ldisc.flush_buffer)
......@@ -279,34 +295,34 @@ static int n_hci_tty_open(struct tty_struct *tty)
return 0;
}
/* n_hci_tty_close()
/* hci_uart_tty_close()
*
* Called when the line discipline is changed to something
* 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);
/* Detach from the tty */
tty->disc_data = NULL;
if (n_hci) {
struct hci_dev *hdev = &n_hci->hdev;
n_hci_close(hdev);
if (hu) {
struct hci_dev *hdev = &hu->hdev;
hci_uart_close(hdev);
if (test_and_clear_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
n_hci->proto->close(n_hci);
if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
hu->proto->close(hu);
hci_unregister_dev(hdev);
}
MOD_DEC_USE_COUNT;
}
}
/* n_hci_tty_wakeup()
/* hci_uart_tty_wakeup()
*
* Callback for transmit wakeup. Called when low level
* device driver can accept more send data.
......@@ -314,24 +330,24 @@ static void n_hci_tty_close(struct tty_struct *tty)
* Arguments: tty pointer to associated tty instance data
* 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("");
if (!n_hci)
if (!hu)
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;
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
* 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 )
* Arguments: tty pointer to associated tty instance data
* 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;
}
/* n_hci_tty_receive()
/* hci_uart_tty_receive()
*
* Called by tty low level driver when receive data is
* available.
......@@ -357,42 +373,42 @@ static int n_hci_tty_room (struct tty_struct *tty)
*
* 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;
if (!test_bit(N_HCI_PROTO_SET, &n_hci->flags))
if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
return;
spin_lock(&n_hci->rx_lock);
n_hci->proto->recv(n_hci, (void *) data, count);
n_hci->hdev.stat.byte_rx += count;
spin_unlock(&n_hci->rx_lock);
spin_lock(&hu->rx_lock);
hu->proto->recv(hu, (void *) data, count);
hu->hdev.stat.byte_rx += count;
spin_unlock(&hu->rx_lock);
if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
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;
BT_DBG("");
/* Initialize and register HCI device */
hdev = &n_hci->hdev;
hdev = &hu->hdev;
hdev->type = HCI_UART;
hdev->driver_data = n_hci;
hdev->driver_data = hu;
hdev->open = n_hci_open;
hdev->close = n_hci_close;
hdev->flush = n_hci_flush;
hdev->send = n_hci_send_frame;
hdev->destruct = n_hci_destruct;
hdev->open = hci_uart_open;
hdev->close = hci_uart_close;
hdev->flush = hci_uart_flush;
hdev->send = hci_uart_send_frame;
hdev->destruct = hci_uart_destruct;
if (hci_register_dev(hdev) < 0) {
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)
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;
int err;
p = n_hci_get_proto(id);
p = hci_uart_get_proto(id);
if (!p)
return -EPROTONOSUPPORT;
err = p->open(n_hci);
err = p->open(hu);
if (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) {
p->close(n_hci);
p->close(hu);
return err;
}
return 0;
}
/* n_hci_tty_ioctl()
/* hci_uart_tty_ioctl()
*
* 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)
*
* 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)
{
struct n_hci *n_hci = (void *)tty->disc_data;
struct hci_uart *hu = (void *)tty->disc_data;
int err = 0;
BT_DBG("");
/* Verify the status of the device */
if (!n_hci)
if (!hu)
return -EBADF;
switch (cmd) {
case HCIUARTSETPROTO:
if (!test_and_set_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
err = n_hci_set_proto(n_hci, arg);
if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
err = hci_uart_set_proto(hu, arg);
if (err) {
clear_bit(N_HCI_PROTO_SET, &n_hci->flags);
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
return err;
}
tty->low_latency = 1;
......@@ -463,8 +479,8 @@ static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file,
return -EBUSY;
case HCIUARTGETPROTO:
if (test_bit(N_HCI_PROTO_SET, &n_hci->flags))
return n_hci->proto->id;
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
return hu->proto->id;
return -EUNATCH;
default:
......@@ -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.
*/
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;
}
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;
}
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;
}
......@@ -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_deinit(void);
#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;
BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
......@@ -507,20 +527,20 @@ int __init n_hci_init(void)
/* Register the tty discipline */
memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
n_hci_ldisc.magic = TTY_LDISC_MAGIC;
n_hci_ldisc.name = "n_hci";
n_hci_ldisc.open = n_hci_tty_open;
n_hci_ldisc.close = n_hci_tty_close;
n_hci_ldisc.read = n_hci_tty_read;
n_hci_ldisc.write = n_hci_tty_write;
n_hci_ldisc.ioctl = n_hci_tty_ioctl;
n_hci_ldisc.poll = n_hci_tty_poll;
n_hci_ldisc.receive_room= n_hci_tty_room;
n_hci_ldisc.receive_buf = n_hci_tty_receive;
n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
hci_uart_ldisc.name = "n_hci";
hci_uart_ldisc.open = hci_uart_tty_open;
hci_uart_ldisc.close = hci_uart_tty_close;
hci_uart_ldisc.read = hci_uart_tty_read;
hci_uart_ldisc.write = hci_uart_tty_write;
hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
hci_uart_ldisc.poll = hci_uart_tty_poll;
hci_uart_ldisc.receive_room= hci_uart_tty_room;
hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
BT_ERR("Can't register HCI line discipline (%d)", err);
return err;
}
......@@ -528,25 +548,31 @@ int __init n_hci_init(void)
#ifdef CONFIG_BLUEZ_HCIUART_H4
h4_init();
#endif
#ifdef CONFIG_BLUEZ_HCIUART_BCSP
bcsp_init();
#endif
return 0;
}
void n_hci_cleanup(void)
void hci_uart_cleanup(void)
{
int err;
#ifdef CONFIG_BLUEZ_HCIUART_H4
h4_deinit();
#endif
#ifdef CONFIG_BLUEZ_HCIUART_BCSP
bcsp_deinit();
#endif
/* Release tty registration of line discipline */
if ((err = tty_register_ldisc(N_HCI, NULL)))
BT_ERR("Can't unregister HCI line discipline (%d)", err);
}
module_init(n_hci_init);
module_exit(n_hci_cleanup);
module_init(hci_uart_init);
module_exit(hci_uart_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
......
......@@ -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
#endif
......@@ -42,19 +42,19 @@
#define HCI_UART_NCSP 2
#ifdef __KERNEL__
struct n_hci;
struct hci_uart;
struct hci_uart_proto {
unsigned int id;
int (*open)(struct n_hci *n_hci);
int (*recv)(struct n_hci *n_hci, void *data, int len);
int (*send)(struct n_hci *n_hci, void *data, int len);
int (*close)(struct n_hci *n_hci);
int (*flush)(struct n_hci *n_hci);
struct sk_buff* (*preq)(struct n_hci *n_hci, struct sk_buff *skb);
int (*open)(struct hci_uart *hu);
int (*close)(struct hci_uart *hu);
int (*flush)(struct hci_uart *hu);
int (*recv)(struct hci_uart *hu, void *data, int len);
int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
struct sk_buff *(*dequeue)(struct hci_uart *hu);
};
struct n_hci {
struct hci_uart {
struct tty_struct *tty;
struct hci_dev hdev;
unsigned long flags;
......@@ -62,19 +62,20 @@ struct n_hci {
struct hci_uart_proto *proto;
void *priv;
struct sk_buff_head txq;
unsigned long tx_state;
spinlock_t rx_lock;
struct sk_buff *tx_skb;
unsigned long tx_state;
spinlock_t rx_lock;
};
/* N_HCI flag bits */
#define N_HCI_PROTO_SET 0x00
/* HCI_UART flag bits */
#define HCI_UART_PROTO_SET 0x00
/* TX states */
#define N_HCI_SENDING 1
#define N_HCI_TX_WAKEUP 2
#define HCI_UART_SENDING 1
#define HCI_UART_TX_WAKEUP 2
int hci_uart_register_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__ */
......@@ -28,9 +28,9 @@
* Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
*
* $Id: hci_usb.c,v 1.6 2002/04/17 17:37:20 maxk Exp $
* $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $
*/
#define VERSION "2.0"
#define VERSION "2.1"
#include <linux/config.h>
#include <linux/module.h>
......@@ -73,7 +73,7 @@
static struct usb_driver hci_usb_driver;
static struct usb_device_id usb_bluetooth_ids [] = {
static struct usb_device_id bluetooth_ids[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
......@@ -83,7 +83,14 @@ static struct usb_device_id usb_bluetooth_ids [] = {
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
MODULE_DEVICE_TABLE (usb, bluetooth_ids);
static struct usb_device_id ignore_ids[] = {
/* Broadcom BCM2033 without firmware */
{ USB_DEVICE(0x0a5c, 0x2033) },
{ } /* Terminating entry */
};
static void hci_usb_interrupt(struct urb *urb);
static void hci_usb_rx_complete(struct urb *urb);
......@@ -193,22 +200,26 @@ static int hci_usb_open(struct hci_dev *hdev)
{
struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
int i, err;
long flags;
unsigned long flags;
BT_DBG("%s", hdev->name);
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
return 0;
MOD_INC_USE_COUNT;
write_lock_irqsave(&husb->completion_lock, flags);
err = hci_usb_enable_intr(husb);
if (!err) {
for (i = 0; i < HCI_MAX_BULK_TX; i++)
for (i = 0; i < HCI_MAX_BULK_RX; i++)
hci_usb_rx_submit(husb, NULL);
} else
} else {
clear_bit(HCI_RUNNING, &hdev->flags);
MOD_DEC_USE_COUNT;
}
write_unlock_irqrestore(&husb->completion_lock, flags);
return err;
}
......@@ -246,7 +257,7 @@ static inline void hci_usb_unlink_urbs(struct hci_usb *husb)
static int hci_usb_close(struct hci_dev *hdev)
{
struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
long flags;
unsigned long flags;
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
......@@ -260,6 +271,8 @@ static int hci_usb_close(struct hci_dev *hdev)
hci_usb_flush(hdev);
write_unlock_irqrestore(&husb->completion_lock, flags);
MOD_DEC_USE_COUNT;
return 0;
}
......@@ -587,78 +600,11 @@ static void hci_usb_destruct(struct hci_dev *hdev)
husb = (struct hci_usb *) hdev->driver_data;
kfree(husb);
MOD_DEC_USE_COUNT;
}
#ifdef CONFIG_BLUEZ_USB_FW_LOAD
/* Support for user mode Bluetooth USB firmware loader */
#define FW_LOADER "/sbin/bluefw"
static int errno;
static int hci_usb_fw_exec(void *dev)
{
char *envp[] = { "HOME=/", "TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
char *argv[] = { FW_LOADER, dev, NULL };
int err;
err = exec_usermodehelper(FW_LOADER, argv, envp);
if (err)
BT_ERR("failed to exec %s %s", FW_LOADER, (char *)dev);
return err;
}
static int hci_usb_fw_load(struct usb_device *udev)
int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
sigset_t tmpsig;
char dev[16];
pid_t pid;
int result;
/* Check if root fs is mounted */
if (!current->fs->root) {
BT_ERR("root fs not mounted");
return -EPERM;
}
sprintf(dev, "%3.3d/%3.3d", udev->bus->busnum, udev->devnum);
pid = kernel_thread(hci_usb_fw_exec, (void *)dev, 0);
if (pid < 0) {
BT_ERR("fork failed, errno %d\n", -pid);
return pid;
}
/* Block signals, everything but SIGKILL/SIGSTOP */
spin_lock_irq(&current->sig->siglock);
tmpsig = current->blocked;
siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
recalc_sigpending();
spin_unlock_irq(&current->sig->siglock);
result = waitpid(pid, NULL, __WCLONE);
/* Allow signals again */
spin_lock_irq(&current->sig->siglock);
current->blocked = tmpsig;
recalc_sigpending();
spin_unlock_irq(&current->sig->siglock);
if (result != pid) {
BT_ERR("waitpid failed pid %d errno %d\n", pid, -result);
return -result;
}
return 0;
}
#endif /* CONFIG_BLUEZ_USB_FW_LOAD */
static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
......@@ -673,15 +619,13 @@ static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id
BT_DBG("intf %p", intf);
/* Check our black list */
if (usb_match_id(intf, ignore_ids))
return -EIO;
/* Check number of endpoints */
if (intf->altsetting[0].bNumEndpoints < 3)
return -ENODEV;
MOD_INC_USE_COUNT;
#ifdef CONFIG_BLUEZ_USB_FW_LOAD
hci_usb_fw_load(udev);
#endif
return -EIO;
memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
......@@ -802,7 +746,6 @@ static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id
kfree(husb);
done:
MOD_DEC_USE_COUNT;
return -EIO;
}
......@@ -811,9 +754,9 @@ static void hci_usb_disconnect(struct usb_interface *intf)
struct hci_usb *husb = dev_get_drvdata(&intf->dev);
struct hci_dev *hdev;
dev_set_drvdata(&intf->dev, NULL);
if (!husb)
return;
dev_set_drvdata(&intf->dev, NULL);
hdev = &husb->hdev;
BT_DBG("%s", hdev->name);
......@@ -828,10 +771,10 @@ static void hci_usb_disconnect(struct usb_interface *intf)
}
static struct usb_driver hci_usb_driver = {
.name = "hci_usb",
.probe = hci_usb_probe,
.disconnect = hci_usb_disconnect,
.id_table = usb_bluetooth_ids,
.name = "hci_usb",
.probe = hci_usb_probe,
.disconnect = hci_usb_disconnect,
.id_table = bluetooth_ids
};
int hci_usb_init(void)
......
......@@ -55,8 +55,8 @@ struct hci_usb {
__u8 intr_ep;
__u8 intr_interval;
struct urb * intr_urb;
struct sk_buff * intr_skb;
struct urb *intr_urb;
struct sk_buff *intr_skb;
rwlock_t completion_lock;
......
......@@ -50,6 +50,7 @@
#define BTPROTO_HCI 1
#define BTPROTO_SCO 2
#define BTPROTO_RFCOMM 3
#define BTPROTO_BNEP 4
#define SOL_HCI 0
#define SOL_L2CAP 6
......@@ -199,14 +200,4 @@ int hci_sock_cleanup(void);
int bterr(__u16 code);
#ifndef MODULE_LICENSE
#define MODULE_LICENSE(x)
#endif
#ifndef list_for_each_safe
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
#endif
#endif /* __BLUETOOTH_H */
......@@ -113,10 +113,10 @@ enum {
#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK)
/* ACL flags */
#define ACL_CONT 0x0001
#define ACL_START 0x0002
#define ACL_ACTIVE_BCAST 0x0010
#define ACL_PICO_BCAST 0x0020
#define ACL_CONT 0x01
#define ACL_START 0x02
#define ACL_ACTIVE_BCAST 0x04
#define ACL_PICO_BCAST 0x08
/* Baseband links */
#define SCO_LINK 0x00
......@@ -542,7 +542,7 @@ typedef struct {
bdaddr_t bdaddr;
__u8 role;
} __attribute__ ((packed)) evt_role_change;
#define EVT_ROLE_CHANGE_SIZE 1
#define EVT_ROLE_CHANGE_SIZE 8
#define EVT_PIN_CODE_REQ 0x16
typedef struct {
......@@ -658,9 +658,15 @@ struct sockaddr_hci {
#define HCI_DEV_NONE 0xffff
struct hci_filter {
__u32 type_mask;
__u32 event_mask[2];
__u16 opcode;
unsigned long type_mask;
unsigned long event_mask[2];
__u16 opcode;
};
struct hci_ufilter {
__u32 type_mask;
__u32 event_mask[2];
__u16 opcode;
};
#define HCI_FLT_TYPE_BITS 31
......@@ -668,20 +674,6 @@ struct hci_filter {
#define HCI_FLT_OGF_BITS 63
#define HCI_FLT_OCF_BITS 127
#if BITS_PER_LONG == 64
static inline void hci_set_bit(int nr, void *addr)
{
*((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
}
static inline int hci_test_bit(int nr, void *addr)
{
return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
}
#else
#define hci_set_bit set_bit
#define hci_test_bit test_bit
#endif
/* Ioctl requests structures */
struct hci_dev_stats {
__u32 err_rx;
......
......@@ -149,7 +149,7 @@ struct hci_conn {
extern struct hci_proto *hci_proto[];
extern struct list_head hdev_list;
extern spinlock_t hdev_list_lock;
extern rwlock_t hdev_list_lock;
/* ----- Inquiry cache ----- */
#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds
......@@ -339,8 +339,8 @@ static inline void hci_sched_tx(struct hci_dev *hdev)
/* ----- HCI protocols ----- */
struct hci_proto {
char *name;
__u32 id;
__u32 flags;
unsigned int id;
unsigned long flags;
void *priv;
......@@ -450,12 +450,11 @@ struct hci_pinfo {
#define HCI_SFLT_MAX_OGF 4
struct hci_sec_filter {
__u32 type_mask;
__u32 event_mask[2];
__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
unsigned long type_mask;
unsigned long event_mask[2];
unsigned long ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
};
/* ----- HCI requests ----- */
#define HCI_REQ_DONE 0
#define HCI_REQ_PEND 1
......
/*
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2002 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.
*/
/*
RPN support - Dirk Husemann <hud@zurich.ibm.com>
*/
/*
* $Id: rfcomm.h,v 1.29 2002/10/02 20:26:17 maxk Exp $
*/
#ifndef __RFCOMM_H
#define __RFCOMM_H
#define RFCOMM_PSM 3
#define RFCOMM_CONN_TIMEOUT (HZ * 30)
#define RFCOMM_DISC_TIMEOUT (HZ * 20)
#define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDITS 7
#define RFCOMM_MAX_L2CAP_MTU 1024
#define RFCOMM_MAX_CREDITS 40
#define RFCOMM_SKB_HEAD_RESERVE 8
#define RFCOMM_SKB_TAIL_RESERVE 2
#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE)
#define RFCOMM_SABM 0x2f
#define RFCOMM_DISC 0x43
#define RFCOMM_UA 0x63
#define RFCOMM_DM 0x0f
#define RFCOMM_UIH 0xef
#define RFCOMM_TEST 0x08
#define RFCOMM_FCON 0x28
#define RFCOMM_FCOFF 0x18
#define RFCOMM_MSC 0x38
#define RFCOMM_RPN 0x24
#define RFCOMM_RLS 0x14
#define RFCOMM_PN 0x20
#define RFCOMM_NSC 0x04
#define RFCOMM_V24_FC 0x02
#define RFCOMM_V24_RTC 0x04
#define RFCOMM_V24_RTR 0x08
#define RFCOMM_V24_IC 0x40
#define RFCOMM_V24_DV 0x80
#define RFCOMM_RPN_BR_2400 0x0
#define RFCOMM_RPN_BR_4800 0x1
#define RFCOMM_RPN_BR_7200 0x2
#define RFCOMM_RPN_BR_9600 0x3
#define RFCOMM_RPN_BR_19200 0x4
#define RFCOMM_RPN_BR_38400 0x5
#define RFCOMM_RPN_BR_57600 0x6
#define RFCOMM_RPN_BR_115200 0x7
#define RFCOMM_RPN_BR_230400 0x8
#define RFCOMM_RPN_DATA_5 0x0
#define RFCOMM_RPN_DATA_6 0x1
#define RFCOMM_RPN_DATA_7 0x2
#define RFCOMM_RPN_DATA_8 0x3
#define RFCOMM_RPN_STOP_1 0
#define RFCOMM_RPN_STOP_15 1
#define RFCOMM_RPN_PARITY_NONE 0x0
#define RFCOMM_RPN_PARITY_ODD 0x4
#define RFCOMM_RPN_PARITY_EVEN 0x5
#define RFCOMM_RPN_PARITY_MARK 0x6
#define RFCOMM_RPN_PARITY_SPACE 0x7
#define RFCOMM_RPN_FLOW_NONE 0x00
#define RFCOMM_RPN_XON_CHAR 0x11
#define RFCOMM_RPN_XOFF_CHAR 0x13
#define RFCOMM_RPN_PM_BITRATE 0x0001
#define RFCOMM_RPN_PM_DATA 0x0002
#define RFCOMM_RPN_PM_STOP 0x0004
#define RFCOMM_RPN_PM_PARITY 0x0008
#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010
#define RFCOMM_RPN_PM_XON 0x0020
#define RFCOMM_RPN_PM_XOFF 0x0040
#define RFCOMM_RPN_PM_FLOW 0x3F00
#define RFCOMM_RPN_PM_ALL 0x3F7F
struct rfcomm_hdr {
u8 addr;
u8 ctrl;
u8 len; // Actual size can be 2 bytes
} __attribute__ ((packed));
struct rfcomm_cmd {
u8 addr;
u8 ctrl;
u8 len;
u8 fcs;
} __attribute__ ((packed));
struct rfcomm_mcc {
u8 type;
u8 len;
} __attribute__ ((packed));
struct rfcomm_pn {
u8 dlci;
u8 flow_ctrl;
u8 priority;
u8 ack_timer;
u16 mtu;
u8 max_retrans;
u8 credits;
} __attribute__ ((packed));
struct rfcomm_rpn {
u8 dlci;
u8 bit_rate;
u8 line_settings;
u8 flow_ctrl;
u8 xon_char;
u8 xoff_char;
u16 param_mask;
} __attribute__ ((packed));
struct rfcomm_msc {
u8 dlci;
u8 v24_sig;
} __attribute__ ((packed));
/* ---- Core structures, flags etc ---- */
struct rfcomm_session {
struct list_head list;
struct socket *sock;
unsigned long state;
unsigned long flags;
atomic_t refcnt;
int initiator;
/* Default DLC parameters */
uint mtu;
uint credits;
struct list_head dlcs;
};
struct rfcomm_dlc {
struct list_head list;
struct rfcomm_session *session;
struct sk_buff_head tx_queue;
struct timer_list timer;
spinlock_t lock;
unsigned long state;
unsigned long flags;
atomic_t refcnt;
u8 dlci;
u8 addr;
uint mtu;
u8 v24_sig;
uint credits;
uint rx_credits;
uint tx_credits;
void *owner;
void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb);
void (*state_change)(struct rfcomm_dlc *d, int err);
void (*modem_status)(struct rfcomm_dlc *d, int v24_sig);
};
/* DLC and session flags */
#define RFCOMM_RX_THROTTLED 0
#define RFCOMM_TX_THROTTLED 1
#define RFCOMM_MSC_PENDING 2
#define RFCOMM_TIMED_OUT 3
/* Scheduling flags and events */
#define RFCOMM_SCHED_STATE 0
#define RFCOMM_SCHED_RX 1
#define RFCOMM_SCHED_TX 2
#define RFCOMM_SCHED_TIMEO 3
#define RFCOMM_SCHED_WAKEUP 31
extern struct task_struct *rfcomm_thread;
extern unsigned long rfcomm_event;
static inline void rfcomm_schedule(uint event)
{
if (!rfcomm_thread)
return;
//set_bit(event, &rfcomm_event);
if (!test_and_set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event))
wake_up_process(rfcomm_thread);
}
extern struct semaphore rfcomm_sem;
#define rfcomm_lock() down(&rfcomm_sem);
#define rfcomm_unlock() up(&rfcomm_sem);
/* ---- RFCOMM DLCs (channels) ---- */
struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
void rfcomm_dlc_free(struct rfcomm_dlc *d);
int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel);
int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
int rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig);
#define rfcomm_dlc_lock(d) spin_lock(&d->lock)
#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
{
atomic_inc(&d->refcnt);
}
static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
{
if (atomic_dec_and_test(&d->refcnt))
rfcomm_dlc_free(d);
}
extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d));
extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d));
static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
{
if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags))
__rfcomm_dlc_throttle(d);
}
static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
{
if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags))
__rfcomm_dlc_unthrottle(d);
}
/* ---- RFCOMM sessions ---- */
struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state);
struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
void rfcomm_session_del(struct rfcomm_session *s);
void rfcomm_session_close(struct rfcomm_session *s, int err);
static inline void rfcomm_session_hold(struct rfcomm_session *s)
{
atomic_inc(&s->refcnt);
}
static inline void rfcomm_session_put(struct rfcomm_session *s)
{
if (atomic_dec_and_test(&s->refcnt))
rfcomm_session_del(s);
}
/* ---- RFCOMM chechsum ---- */
extern u8 rfcomm_crc_table[];
/* ---- RFCOMM sockets ---- */
struct sockaddr_rc {
sa_family_t rc_family;
bdaddr_t rc_bdaddr;
u8 rc_channel;
};
#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) &sk->protinfo)
struct rfcomm_pinfo {
struct rfcomm_dlc *dlc;
u8 channel;
};
int rfcomm_init_sockets(void);
void rfcomm_cleanup_sockets(void);
int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d);
/* ---- RFCOMM TTY ---- */
#define RFCOMM_MAX_DEV 256
#define RFCOMMCREATEDEV _IOW('R', 200, int)
#define RFCOMMRELEASEDEV _IOW('R', 201, int)
#define RFCOMMGETDEVLIST _IOR('R', 210, int)
#define RFCOMMGETDEVINFO _IOR('R', 211, int)
#define RFCOMMSTEALDLC _IOW('R', 220, int)
#define RFCOMM_REUSE_DLC 0
#define RFCOMM_RELEASE_ONHUP 1
#define RFCOMM_HANGUP_NOW 2
#define RFCOMM_TTY_ATTACHED 3
struct rfcomm_dev_req {
s16 dev_id;
u32 flags;
bdaddr_t src;
bdaddr_t dst;
u8 channel;
};
struct rfcomm_dev_info {
s16 id;
u32 flags;
u16 state;
bdaddr_t src;
bdaddr_t dst;
u8 channel;
};
struct rfcomm_dev_list_req {
u16 dev_num;
struct rfcomm_dev_info dev_info[0];
};
int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
int rfcomm_init_ttys(void);
void rfcomm_cleanup_ttys(void);
#endif /* __RFCOMM_H */
......@@ -10,7 +10,9 @@ CONFIG_BLUEZ
BlueZ Core (HCI device and connection manager, scheduler)
HCI Device drivers (interface to the hardware)
L2CAP Module (L2CAP protocol)
RFCOMM Module (RFCOMM protocol)
SCO Module (SCO links)
BNEP Module (BNEP protocol)
Say Y here to enable Linux Bluetooth support and to build BlueZ Core
layer.
......@@ -31,6 +33,19 @@ CONFIG_BLUEZ_L2CAP
Say Y here to compile L2CAP support into the kernel or say M to
compile it as module (l2cap.o).
RFCOMM protocol support
CONFIG_BLUEZ_RFCOMM
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
applications.
Say Y here to compile RFCOMM support into the kernel or say M to
compile it as module (rfcomm.o).
RFCOMM TTY emulation support
CONFIG_RFCOMM_TTY
This options enables TTY emulation support for RFCOMM channels.
SCO links support
CONFIG_BLUEZ_SCO
SCO link provides voice transport over Bluetooth. SCO support is
......@@ -39,3 +54,16 @@ CONFIG_BLUEZ_SCO
Say Y here to compile SCO support into the kernel or say M to
compile it as module (sco.o).
BNEP protocol support
CONFIG_BLUEZ_BNEP
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
emulation layer on top of Bluetooth. BNEP is required for Bluetooth
PAN (Personal Area Network).
To use BNEP, you will need user-space utilities provided in the
BlueZ-PAN package.
For more information, see <http://bluez.sourceforge.net>.
Say Y here to compile BNEP support into the kernel or say M to
compile it as module (bnep.o).
......@@ -10,6 +10,8 @@ if [ "$CONFIG_NET" != "n" ]; then
if [ "$CONFIG_BLUEZ" != "n" ]; then
dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
source net/bluetooth/rfcomm/Config.in
source net/bluetooth/bnep/Config.in
source drivers/bluetooth/Config.in
fi
endmenu
......
......@@ -8,6 +8,18 @@ obj-$(CONFIG_BLUEZ) += bluez.o
obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
obj-$(CONFIG_BLUEZ_SCO) += sco.o
subdir-$(CONFIG_BLUEZ_BNEP) += bnep
ifeq ($(CONFIG_BLUEZ_BNEP),y)
obj-y += bnep/bnep.o
endif
subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
obj-y += rfcomm/rfcomm.o
endif
bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
include $(TOPDIR)/Rules.make
......@@ -27,7 +27,7 @@
*
* $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
*/
#define VERSION "2.0"
#define VERSION "2.2"
#include <linux/config.h>
#include <linux/module.h>
......@@ -57,7 +57,7 @@
#endif
/* Bluetooth sockets */
#define BLUEZ_MAX_PROTO 4
#define BLUEZ_MAX_PROTO 5
static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
static kmem_cache_t *bluez_sock_cache;
......@@ -136,18 +136,18 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p
void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
{
write_lock(&l->lock);
write_lock_bh(&l->lock);
sk->next = l->head;
l->head = sk;
sock_hold(sk);
write_unlock(&l->lock);
write_unlock_bh(&l->lock);
}
void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
{
struct sock **skp;
write_lock(&l->lock);
write_lock_bh(&l->lock);
for (skp = &l->head; *skp; skp = &((*skp)->next)) {
if (*skp == sk) {
*skp = sk->next;
......@@ -155,7 +155,7 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
break;
}
}
write_unlock(&l->lock);
write_unlock_bh(&l->lock);
}
void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
......@@ -265,6 +265,9 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table
if (sk->state == BT_CLOSED)
mask |= POLLHUP;
if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
return mask;
if (sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
......
dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
bool ' Multicast filter support' CONFIG_BNEP_MC_FILTER
bool ' Protocol filter support' CONFIG_BNEP_PROTO_FILTER
fi
#
# Makefile for BNEP protocol
#
O_TARGET := bnep.o
obj-y := core.o sock.o netdev.o crc32.o
obj-m += $(O_TARGET)
include $(TOPDIR)/Rules.make
/*
BNEP protocol definition for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* $Id: bnep.h,v 1.5 2002/08/04 21:23:58 maxk Exp $
*/
#ifndef _BNEP_H
#define _BNEP_H
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
#include "crc32.h"
// Limits
#define BNEP_MAX_PROTO_FILTERS 5
#define BNEP_MAX_MULTICAST_FILTERS 20
// UUIDs
#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
#define BNEP_UUID16 0x02
#define BNEP_UUID32 0x04
#define BNEP_UUID128 0x16
#define BNEP_SVC_PANU 0x1115
#define BNEP_SVC_NAP 0x1116
#define BNEP_SVC_GN 0x1117
// Packet types
#define BNEP_GENERAL 0x00
#define BNEP_CONTROL 0x01
#define BNEP_COMPRESSED 0x02
#define BNEP_COMPRESSED_SRC_ONLY 0x03
#define BNEP_COMPRESSED_DST_ONLY 0x04
// Control types
#define BNEP_CMD_NOT_UNDERSTOOD 0x00
#define BNEP_SETUP_CONN_REQ 0x01
#define BNEP_SETUP_CONN_RSP 0x02
#define BNEP_FILTER_NET_TYPE_SET 0x03
#define BNEP_FILTER_NET_TYPE_RSP 0x04
#define BNEP_FILTER_MULTI_ADDR_SET 0x05
#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
// Extension types
#define BNEP_EXT_CONTROL 0x00
// Response messages
#define BNEP_SUCCESS 0x00
#define BNEP_CONN_INVALID_DST 0x01
#define BNEP_CONN_INVALID_SRC 0x02
#define BNEP_CONN_INVALID_SVC 0x03
#define BNEP_CONN_NOT_ALLOWED 0x04
#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
#define BNEP_FILTER_INVALID_RANGE 0x02
#define BNEP_FILTER_INVALID_MCADDR 0x02
#define BNEP_FILTER_LIMIT_REACHED 0x03
#define BNEP_FILTER_DENIED_SECURITY 0x04
// L2CAP settings
#define BNEP_MTU 1691
#define BNEP_PSM 0x0f
#define BNEP_FLUSH_TO 0xffff
#define BNEP_CONNECT_TO 15
#define BNEP_FILTER_TO 15
// Headers
#define BNEP_TYPE_MASK 0x7f
#define BNEP_EXT_HEADER 0x80
struct bnep_setup_conn_req {
__u8 type;
__u8 ctrl;
__u8 uuid_size;
__u8 service[0];
} __attribute__((packed));
struct bnep_set_filter_req {
__u8 type;
__u8 ctrl;
__u16 len;
__u8 list[0];
} __attribute__((packed));
struct bnep_control_rsp {
__u8 type;
__u8 ctrl;
__u16 resp;
} __attribute__((packed));
struct bnep_ext_hdr {
__u8 type;
__u8 len;
__u8 data[0];
} __attribute__((packed));
// Ioctl interface
#define BNEPCONADD 1
#define BNEPCONDEL 2
#define BNEPGETCONLIST 3
#define BNEPGETCONINFO 4
struct bnep_conadd_req {
int sock; // Connected socket
__u32 flags;
__u16 role;
char device[16]; // Name of the Ethernet device
};
struct bnep_condel_req {
__u32 flags;
__u8 dst[ETH_ALEN];
};
struct bnep_coninfo {
__u32 flags;
__u16 role;
__u16 state;
__u8 dst[ETH_ALEN];
char device[16];
};
struct bnep_conlist_req {
__u32 cnum;
struct bnep_coninfo *ci;
};
struct bnep_proto_filter {
__u16 start;
__u16 end;
};
int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock);
int bnep_del_connection(struct bnep_condel_req *req);
int bnep_get_conlist(struct bnep_conlist_req *req);
int bnep_get_coninfo(struct bnep_coninfo *ci);
// BNEP sessions
struct bnep_session {
struct list_head list;
unsigned int role;
unsigned long state;
unsigned long flags;
atomic_t killed;
struct ethhdr eh;
struct msghdr msg;
struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
u64 mc_filter;
struct socket *sock;
struct net_device dev;
struct net_device_stats stats;
};
int bnep_net_init(struct net_device *dev);
int bnep_sock_init(void);
int bnep_sock_cleanup(void);
static inline int bnep_mc_hash(__u8 *addr)
{
return (bnep_crc32(~0, addr, ETH_ALEN) >> 26);
}
#endif
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
Clment Moreau <clement.moreau@inventel.fr>
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.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: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $
*/
#define __KERNEL_SYSCALLS__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/file.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
#define VERSION "1.0"
static LIST_HEAD(bnep_session_list);
static DECLARE_RWSEM(bnep_session_sem);
static struct bnep_session *__bnep_get_session(u8 *dst)
{
struct bnep_session *s;
struct list_head *p;
BT_DBG("");
list_for_each(p, &bnep_session_list) {
s = list_entry(p, struct bnep_session, list);
if (!memcmp(dst, s->eh.h_source, ETH_ALEN))
return s;
}
return NULL;
}
static void __bnep_link_session(struct bnep_session *s)
{
MOD_INC_USE_COUNT;
list_add(&s->list, &bnep_session_list);
}
static void __bnep_unlink_session(struct bnep_session *s)
{
list_del(&s->list);
MOD_DEC_USE_COUNT;
}
static int bnep_send(struct bnep_session *s, void *data, size_t len)
{
struct socket *sock = s->sock;
struct iovec iv = { data, len };
s->msg.msg_iov = &iv;
s->msg.msg_iovlen = 1;
return sock->ops->sendmsg(sock, &s->msg, len, NULL);
}
static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
{
struct bnep_control_rsp rsp;
rsp.type = BNEP_CONTROL;
rsp.ctrl = ctrl;
rsp.resp = htons(resp);
return bnep_send(s, &rsp, sizeof(rsp));
}
static int bnep_ctrl_set_netfilter(struct bnep_session *s, struct sk_buff *skb)
{
u16 *data;
int n;
data = (void *) skb->data;
if (!skb_pull(skb, 2))
return -EILSEQ;
n = ntohs(get_unaligned(data));
data = (void *) skb->data;
if (!skb_pull(skb, n))
return -EILSEQ;
BT_DBG("filter len %d", n);
#ifdef CONFIG_BNEP_PROTO_FILTER
n /= 4;
if (n <= BNEP_MAX_PROTO_FILTERS) {
struct bnep_proto_filter *f = s->proto_filter;
int i;
for (i = 0; i < n; i++) {
f[i].start = get_unaligned(data++);
f[i].end = get_unaligned(data++);
BT_DBG("proto filter start %d end %d",
f[i].start, f[i].end);
}
if (i < BNEP_MAX_PROTO_FILTERS)
memset(f + i, 0, sizeof(*f));
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
} else {
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
}
#else
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
#endif
return 0;
}
static int bnep_ctrl_set_mcfilter(struct bnep_session *s, struct sk_buff *skb)
{
u8 *data;
int n;
data = (void *) skb->data;
if (!skb_pull(skb, 2))
return -EILSEQ;
n = ntohs(get_unaligned((u16 *) data));
data = (void *) skb->data;
if (!skb_pull(skb, n))
return -EILSEQ;
BT_DBG("filter len %d", n);
#ifdef CONFIG_BNEP_MC_FILTER
n /= (ETH_ALEN * 2);
if (n > 0) {
s->mc_filter = 0;
/* Always send broadcast */
set_bit(bnep_mc_hash(s->dev.broadcast), (ulong *) &s->mc_filter);
/* Add address ranges to the multicast hash */
for (; n > 0; n--) {
u8 a1[6], *a2;
memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
a2 = data; data += ETH_ALEN;
BT_DBG("mc filter %s -> %s",
batostr((void *) a1), batostr((void *) a2));
#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
/* Iterate from a1 to a2 */
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
INCA(a1);
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
}
}
}
BT_DBG("mc filter hash 0x%llx", s->mc_filter);
bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
#else
bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
#endif
return 0;
}
static int bnep_rx_control(struct bnep_session *s, struct sk_buff *skb)
{
int err = 0;
u8 cmd = *(u8 *) skb->data;
skb_pull(skb, 1);
switch (cmd) {
case BNEP_CMD_NOT_UNDERSTOOD:
case BNEP_SETUP_CONN_REQ:
case BNEP_SETUP_CONN_RSP:
case BNEP_FILTER_NET_TYPE_RSP:
case BNEP_FILTER_MULTI_ADDR_RSP:
/* Ignore these for now */
break;
case BNEP_FILTER_NET_TYPE_SET:
err = bnep_ctrl_set_netfilter(s, skb);
break;
case BNEP_FILTER_MULTI_ADDR_SET:
err = bnep_ctrl_set_mcfilter(s, skb);
break;
default: {
u8 pkt[3];
pkt[0] = BNEP_CONTROL;
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
pkt[2] = cmd;
bnep_send(s, pkt, sizeof(pkt));
}
break;
}
return err;
}
static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
{
struct bnep_ext_hdr *h;
int err = 0;
do {
h = (void *) skb->data;
if (!skb_pull(skb, sizeof(*h))) {
err = -EILSEQ;
break;
}
BT_DBG("type 0x%x len %d", h->type, h->len);
switch (h->type & BNEP_TYPE_MASK) {
case BNEP_EXT_CONTROL:
err = bnep_rx_control(s, skb);
break;
default:
/* Unknown extension */
if (!skb_pull(skb, h->len))
err = -EILSEQ;
break;
}
} while (!err && (h->type & BNEP_EXT_HEADER));
return err;
}
static u8 __bnep_rx_hlen[] = {
ETH_HLEN, /* BNEP_GENERAL */
0, /* BNEP_CONTROL */
2, /* BNEP_COMPRESSED */
ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
};
#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
{
struct net_device *dev = &s->dev;
struct sk_buff *nskb;
u8 type;
dev->last_rx = jiffies;
s->stats.rx_bytes += skb->len;
type = *(u8 *) skb->data; skb_pull(skb, 1);
if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
bnep_rx_control(s, skb);
kfree_skb(skb);
return 0;
}
skb->mac.raw = skb->data;
/* Verify and pull out header */
if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
goto badframe;
s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
if (type & BNEP_EXT_HEADER) {
if (bnep_rx_extension(s, skb) < 0)
goto badframe;
}
/* Strip 802.1p header */
if (ntohs(s->eh.h_proto) == 0x8100) {
if (!skb_pull(skb, 4))
goto badframe;
s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
}
/* We have to alloc new skb and copy data here :(. Because original skb
* may not be modified and because of the alignment requirements. */
nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
if (!nskb) {
s->stats.rx_dropped++;
kfree_skb(skb);
return -ENOMEM;
}
skb_reserve(nskb, 2);
/* Decompress header and construct ether frame */
switch (type & BNEP_TYPE_MASK) {
case BNEP_COMPRESSED:
memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
break;
case BNEP_COMPRESSED_SRC_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
break;
case BNEP_COMPRESSED_DST_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2);
break;
case BNEP_GENERAL:
memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2);
put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
break;
}
memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
kfree_skb(skb);
s->stats.rx_packets++;
nskb->dev = dev;
nskb->ip_summed = CHECKSUM_UNNECESSARY;
nskb->protocol = eth_type_trans(nskb, dev);
netif_rx_ni(nskb);
return 0;
badframe:
s->stats.rx_errors++;
kfree_skb(skb);
return 0;
}
static u8 __bnep_tx_types[] = {
BNEP_GENERAL,
BNEP_COMPRESSED_SRC_ONLY,
BNEP_COMPRESSED_DST_ONLY,
BNEP_COMPRESSED
};
static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
{
struct ethhdr *eh = (void *) skb->data;
struct socket *sock = s->sock;
struct iovec iv[3];
int len = 0, il = 0;
u8 type = 0;
BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
if (!skb->dev) {
/* Control frame sent by us */
goto send;
}
iv[il++] = (struct iovec) { &type, 1 };
len++;
if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
type |= 0x01;
if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN))
type |= 0x02;
if (type)
skb_pull(skb, ETH_ALEN * 2);
type = __bnep_tx_types[type];
switch (type) {
case BNEP_COMPRESSED_SRC_ONLY:
iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
len += ETH_ALEN;
break;
case BNEP_COMPRESSED_DST_ONLY:
iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
len += ETH_ALEN;
break;
}
send:
iv[il++] = (struct iovec) { skb->data, skb->len };
len += skb->len;
/* FIXME: linearize skb */
s->msg.msg_iov = iv;
s->msg.msg_iovlen = il;
len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
kfree_skb(skb);
if (len > 0) {
s->stats.tx_bytes += len;
s->stats.tx_packets++;
return 0;
}
return len;
}
static int bnep_session(void *arg)
{
struct bnep_session *s = arg;
struct net_device *dev = &s->dev;
struct sock *sk = s->sock->sk;
struct sk_buff *skb;
wait_queue_t wait;
BT_DBG("");
daemonize();
set_user_nice(current, 19);
current->flags |= PF_IOTHREAD;
sigfillset(&current->blocked);
flush_signals(current);
sprintf(current->comm, "kbnepd %s", dev->name);
set_fs(KERNEL_DS);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk->sleep, &wait);
while (!atomic_read(&s->killed)) {
set_current_state(TASK_INTERRUPTIBLE);
// RX
while ((skb = skb_dequeue(&sk->receive_queue))) {
skb_orphan(skb);
bnep_rx_frame(s, skb);
}
if (sk->state != BT_CONNECTED)
break;
// TX
while ((skb = skb_dequeue(&sk->write_queue)))
if (bnep_tx_frame(s, skb))
break;
netif_wake_queue(dev);
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
/* Cleanup session */
down_write(&bnep_session_sem);
/* Delete network device */
unregister_netdev(dev);
/* Release the socket */
fput(s->sock->file);
__bnep_unlink_session(s);
up_write(&bnep_session_sem);
kfree(s);
return 0;
}
int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock)
{
struct net_device *dev;
struct bnep_session *s, *ss;
u8 dst[ETH_ALEN], src[ETH_ALEN];
int err;
BT_DBG("");
baswap((void *) dst, &bluez_sk(sock->sk)->dst);
baswap((void *) src, &bluez_sk(sock->sk)->src);
s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL);
if (!s)
return -ENOMEM;
memset(s, 0, sizeof(struct bnep_session));
down_write(&bnep_session_sem);
ss = __bnep_get_session(dst);
if (ss && ss->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
dev = &s->dev;
if (*req->device)
strcpy(dev->name, req->device);
else
strcpy(dev->name, "bnep%d");
/* This is rx header therefor addresses are swaped.
* ie eh.h_dest is our local address. */
memcpy(s->eh.h_dest, &src, ETH_ALEN);
memcpy(s->eh.h_source, &dst, ETH_ALEN);
s->sock = sock;
s->role = req->role;
s->state = BT_CONNECTED;
s->msg.msg_flags = MSG_NOSIGNAL;
#ifdef CONFIG_BNEP_MC_FILTER
/* Set default mc filter */
set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
/* Set default protocol filter */
/* (IPv4, ARP) */
s->proto_filter[0].start = htons(0x0800);
s->proto_filter[0].end = htons(0x0806);
/* (RARP, AppleTalk) */
s->proto_filter[1].start = htons(0x8035);
s->proto_filter[1].end = htons(0x80F3);
/* (IPX, IPv6) */
s->proto_filter[2].start = htons(0x8137);
s->proto_filter[2].end = htons(0x86DD);
#endif
dev->init = bnep_net_init;
dev->priv = s;
err = register_netdev(dev);
if (err) {
goto failed;
}
__bnep_link_session(s);
err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
if (err < 0) {
/* Session thread start failed, gotta cleanup. */
unregister_netdev(dev);
__bnep_unlink_session(s);
goto failed;
}
up_write(&bnep_session_sem);
strcpy(req->device, dev->name);
return 0;
failed:
up_write(&bnep_session_sem);
kfree(s);
return err;
}
int bnep_del_connection(struct bnep_condel_req *req)
{
struct bnep_session *s;
int err = 0;
BT_DBG("");
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
if (s) {
/* Wakeup user-space which is polling for socket errors.
* This is temporary hack untill we have shutdown in L2CAP */
s->sock->sk->err = EUNATCH;
/* Kill session thread */
atomic_inc(&s->killed);
wake_up_interruptible(s->sock->sk->sleep);
} else
err = -ENOENT;
up_read(&bnep_session_sem);
return err;
}
static void __bnep_copy_ci(struct bnep_coninfo *ci, struct bnep_session *s)
{
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
strcpy(ci->device, s->dev.name);
ci->flags = s->flags;
ci->state = s->state;
ci->role = s->role;
}
int bnep_get_conlist(struct bnep_conlist_req *req)
{
struct list_head *p;
int err = 0, n = 0;
down_read(&bnep_session_sem);
list_for_each(p, &bnep_session_list) {
struct bnep_session *s;
struct bnep_coninfo ci;
s = list_entry(p, struct bnep_session, list);
__bnep_copy_ci(&ci, s);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
if (++n >= req->cnum)
break;
req->ci++;
}
req->cnum = n;
up_read(&bnep_session_sem);
return err;
}
int bnep_get_coninfo(struct bnep_coninfo *ci)
{
struct bnep_session *s;
int err = 0;
down_read(&bnep_session_sem);
s = __bnep_get_session(ci->dst);
if (s)
__bnep_copy_ci(ci, s);
else
err = -ENOENT;
up_read(&bnep_session_sem);
return err;
}
static int __init bnep_init_module(void)
{
BT_INFO("BNEP: BNEP2 ver %s\n"
"BNEP: Copyright (C) 2002 Inventel\n"
"BNEP: Written 2001,2002 by\n"
"BNEP: \tClement Moreau <clement.moreau@inventel.fr> "
"David Libault <david.libault@inventel.fr>\n"
"BNEP: Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>",
VERSION);
bnep_sock_init();
bnep_crc32_init();
return 0;
}
static void __exit bnep_cleanup_module(void)
{
bnep_sock_cleanup();
bnep_crc32_cleanup();
}
module_init(bnep_init_module);
module_exit(bnep_cleanup_module);
MODULE_DESCRIPTION("BNEP ver " VERSION);
MODULE_AUTHOR("David Libault <david.libault@inventel.fr> Maxim Krasnyanskiy <maxk@qualcomm.com>");
MODULE_LICENSE("GPL");
/*
* Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com>
*
* FIXME: Remove in 2.5
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include "crc32.h"
#define CRCPOLY_BE 0x04c11db7
#define CRC_BE_BITS 8
static u32 *bnep_crc32_table;
/*
* This code is in the public domain; copyright abandoned.
* Liability for non-performance of this code is limited to the amount
* you paid for it. Since it is distributed for free, your refund will
* be very very small. If it breaks, you get to keep both pieces.
*/
u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len)
{
while (len--)
crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++];
return crc;
}
int __init bnep_crc32_init(void)
{
unsigned i, j;
u32 crc = 0x80000000;
bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL);
if (!bnep_crc32_table)
return -ENOMEM;
bnep_crc32_table[0] = 0;
for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
for (j = 0; j < i; j++)
bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j];
}
return 0;
}
void __exit bnep_crc32_cleanup(void)
{
if (bnep_crc32_table)
kfree(bnep_crc32_table);
bnep_crc32_table = NULL;
}
/*
* crc32.h
* See crc32.c for license and changes
*
* FIXME: Remove in 2.5
*/
int bnep_crc32_init(void);
void bnep_crc32_cleanup(void);
u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len);
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
Clment Moreau <clement.moreau@inventel.fr>
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.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: netdev.c,v 1.8 2002/08/04 21:23:58 maxk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
#define BNEP_TX_QUEUE_LEN 20
static int bnep_net_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int bnep_net_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
{
struct bnep_session *s = dev->priv;
return &s->stats;
}
static void bnep_net_set_mc_list(struct net_device *dev)
{
#ifdef CONFIG_BNEP_MC_FILTER
struct bnep_session *s = dev->priv;
struct sock *sk = s->sock->sk;
struct bnep_set_filter_req *r;
struct sk_buff *skb;
int size;
BT_DBG("%s mc_count %d", dev->name, dev->mc_count);
size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
BT_ERR("%s Multicast list allocation failed", dev->name);
return;
}
r = (void *) skb->data;
__skb_put(skb, sizeof(*r));
r->type = BNEP_CONTROL;
r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
u8 start[ETH_ALEN] = { 0x01 };
/* Request all addresses */
memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
r->len = htons(ETH_ALEN * 2);
} else {
struct dev_mc_list *dmi = dev->mc_list;
int i, len = skb->len;
if (dev->flags & IFF_BROADCAST) {
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
}
/* FIXME: We should group addresses here. */
for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) {
memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
dmi = dmi->next;
}
r->len = htons(skb->len - len);
}
skb_queue_tail(&sk->write_queue, skb);
wake_up_interruptible(sk->sleep);
#endif
}
static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
{
BT_DBG("%s", dev->name);
return 0;
}
static void bnep_net_timeout(struct net_device *dev)
{
BT_DBG("net_timeout");
netif_wake_queue(dev);
}
static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
return -EINVAL;
}
#ifdef CONFIG_BNEP_MC_FILTER
static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
{
struct ethhdr *eh = (void *) skb->data;
if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter))
return 1;
return 0;
}
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
/* Determine ether protocol. Based on eth_type_trans. */
static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
{
struct ethhdr *eh = (void *) skb->data;
if (ntohs(eh->h_proto) >= 1536)
return eh->h_proto;
if (get_unaligned((u16 *) skb->data) == 0xFFFF)
return htons(ETH_P_802_3);
return htons(ETH_P_802_2);
}
static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
{
u16 proto = bnep_net_eth_proto(skb);
struct bnep_proto_filter *f = s->proto_filter;
int i;
for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
if (proto >= f[i].start && proto <= f[i].end)
return 0;
}
BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
return 1;
}
#endif
static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bnep_session *s = dev->priv;
struct sock *sk = s->sock->sk;
BT_DBG("skb %p, dev %p", skb, dev);
#ifdef CONFIG_BNEP_MC_FILTER
if (bnep_net_mc_filter(skb, s)) {
kfree_skb(skb);
return 0;
}
#endif
#ifdef CONFIG_BNEP_PROTO_FILTER
if (bnep_net_proto_filter(skb, s)) {
kfree_skb(skb);
return 0;
}
#endif
/*
* We cannot send L2CAP packets from here as we are potentially in a bh.
* So we have to queue them and wake up session thread which is sleeping
* on the sk->sleep.
*/
dev->trans_start = jiffies;
skb_queue_tail(&sk->write_queue, skb);
wake_up_interruptible(sk->sleep);
if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) {
BT_DBG("tx queue is full");
/* Stop queuing.
* Session thread will do netif_wake_queue() */
netif_stop_queue(dev);
}
return 0;
}
int bnep_net_init(struct net_device *dev)
{
struct bnep_session *s = dev->priv;
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
dev->addr_len = ETH_ALEN;
ether_setup(dev);
dev->open = bnep_net_open;
dev->stop = bnep_net_close;
dev->hard_start_xmit = bnep_net_xmit;
dev->get_stats = bnep_net_get_stats;
dev->do_ioctl = bnep_net_ioctl;
dev->set_mac_address = bnep_net_set_mac_addr;
dev->set_multicast_list = bnep_net_set_mc_list;
dev->watchdog_timeo = HZ * 2;
dev->tx_timeout = bnep_net_timeout;
return 0;
}
/*
BNEP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2001-2002 Inventel Systemes
Written 2001-2002 by
David Libault <david.libault@inventel.fr>
Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.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: sock.c,v 1.4 2002/08/04 21:23:58 maxk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <net/sock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "bnep.h"
#ifndef CONFIG_BNEP_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#endif
static struct socket *sockfd_lookup(int fd, int *err)
{
struct file *file;
struct inode *inode;
struct socket *sock;
if (!(file = fget(fd))) {
*err = -EBADF;
return NULL;
}
inode = file->f_dentry->d_inode;
if (!inode->i_sock || !(sock = SOCKET_I(inode))) {
*err = -ENOTSOCK;
fput(file);
return NULL;
}
if (sock->file != file) {
printk(KERN_ERR "socki_lookup: socket file changed!\n");
sock->file = file;
}
return sock;
}
static int bnep_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p sk %p", sock, sk);
if (!sk)
return 0;
sock_orphan(sk);
sock_put(sk);
MOD_DEC_USE_COUNT;
return 0;
}
static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct bnep_conlist_req cl;
struct bnep_conadd_req ca;
struct bnep_condel_req cd;
struct bnep_coninfo ci;
struct socket *nsock;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
switch (cmd) {
case BNEPCONADD:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
return -EFAULT;
nsock = sockfd_lookup(ca.sock, &err);
if (!nsock)
return err;
if (nsock->sk->state != BT_CONNECTED)
return -EBADFD;
err = bnep_add_connection(&ca, nsock);
if (!err) {
if (copy_to_user((void *) arg, &ca, sizeof(ca)))
err = -EFAULT;
} else
fput(nsock->file);
return err;
case BNEPCONDEL:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
return -EFAULT;
return bnep_del_connection(&cd);
case BNEPGETCONLIST:
if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
return -EFAULT;
if (cl.cnum <= 0)
return -EINVAL;
err = bnep_get_conlist(&cl);
if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
return -EFAULT;
return err;
case BNEPGETCONINFO:
if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
return -EFAULT;
err = bnep_get_coninfo(&ci);
if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
return -EFAULT;
return err;
default:
return -EINVAL;
}
return 0;
}
static struct proto_ops bnep_sock_ops = {
.family = PF_BLUETOOTH,
.release = bnep_sock_release,
.ioctl = bnep_sock_ioctl,
.bind = sock_no_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.poll = sock_no_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static int bnep_sock_create(struct socket *sock, int protocol)
{
struct sock *sk;
BT_DBG("sock %p", sock);
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
if (!(sk = bluez_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL)))
return -ENOMEM;
sock->ops = &bnep_sock_ops;
MOD_INC_USE_COUNT;
sock->state = SS_UNCONNECTED;
sk->destruct = NULL;
sk->protocol = protocol;
return 0;
}
static struct net_proto_family bnep_sock_family_ops = {
.family = PF_BLUETOOTH,
.create = bnep_sock_create
};
int bnep_sock_init(void)
{
bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
return 0;
}
int bnep_sock_cleanup(void)
{
if (bluez_sock_unregister(BTPROTO_BNEP))
BT_ERR("Can't unregister BNEP socket");
return 0;
}
......@@ -73,7 +73,7 @@ void hci_acl_connect(struct hci_conn *conn)
bacpy(&cp.bdaddr, &conn->dst);
if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
inquiry_entry_age(ie) > INQUIRY_ENTRY_AGE_MAX) {
inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
cp.pscan_rep_mode = ie->info.pscan_rep_mode;
cp.pscan_mode = ie->info.pscan_mode;
cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000);
......@@ -188,9 +188,6 @@ int hci_conn_del(struct hci_conn *conn)
acl->link = NULL;
hci_conn_put(acl);
}
/* Unacked frames */
hdev->sco_cnt += conn->sent;
} else {
struct hci_conn *sco = conn->link;
if (sco)
......@@ -220,7 +217,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
BT_DBG("%s -> %s", batostr(src), batostr(dst));
spin_lock_bh(&hdev_list_lock);
read_lock_bh(&hdev_list_lock);
list_for_each(p, &hdev_list) {
struct hci_dev *d;
......@@ -248,7 +245,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
if (hdev)
hci_dev_hold(hdev);
spin_unlock_bh(&hdev_list_lock);
read_unlock_bh(&hdev_list_lock);
return hdev;
}
......
......@@ -30,6 +30,7 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/errno.h>
......@@ -66,7 +67,7 @@ rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
/* HCI device list */
LIST_HEAD(hdev_list);
spinlock_t hdev_list_lock;
rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
/* HCI protocols */
#define HCI_MAX_PROTO 2
......@@ -75,7 +76,6 @@ struct hci_proto *hci_proto[HCI_MAX_PROTO];
/* HCI notifiers list */
static struct notifier_block *hci_notifier;
/* ---- HCI notifications ---- */
int hci_register_notifier(struct notifier_block *nb)
......@@ -93,6 +93,32 @@ void hci_notify(struct hci_dev *hdev, int event)
notifier_call_chain(&hci_notifier, event, hdev);
}
/* ---- HCI hotplug support ---- */
#ifdef CONFIG_HOTPLUG
static int hci_run_hotplug(char *dev, char *action)
{
char *argv[3], *envp[5], dstr[20], astr[32];
sprintf(dstr, "DEVICE=%s", dev);
sprintf(astr, "ACTION=%s", action);
argv[0] = hotplug_path;
argv[1] = "bluetooth";
argv[2] = NULL;
envp[0] = "HOME=/";
envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
envp[2] = dstr;
envp[3] = astr;
envp[4] = NULL;
return call_usermodehelper(argv[0], argv, envp);
}
#else
#define hci_run_hotplug(A...)
#endif
/* ---- HCI requests ---- */
......@@ -270,7 +296,7 @@ struct hci_dev *hci_dev_get(int index)
if (index < 0)
return NULL;
spin_lock(&hdev_list_lock);
read_lock(&hdev_list_lock);
list_for_each(p, &hdev_list) {
hdev = list_entry(p, struct hci_dev, list);
if (hdev->id == index) {
......@@ -280,7 +306,7 @@ struct hci_dev *hci_dev_get(int index)
}
hdev = NULL;
done:
spin_unlock(&hdev_list_lock);
read_unlock(&hdev_list_lock);
return hdev;
}
......@@ -699,7 +725,7 @@ int hci_get_dev_list(unsigned long arg)
return -ENOMEM;
dr = dl->dev_req;
spin_lock_bh(&hdev_list_lock);
read_lock_bh(&hdev_list_lock);
list_for_each(p, &hdev_list) {
struct hci_dev *hdev;
hdev = list_entry(p, struct hci_dev, list);
......@@ -708,7 +734,7 @@ int hci_get_dev_list(unsigned long arg)
if (++n >= dev_num)
break;
}
spin_unlock_bh(&hdev_list_lock);
read_unlock_bh(&hdev_list_lock);
dl->dev_num = n;
size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
......@@ -768,7 +794,7 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->open || !hdev->close || !hdev->destruct)
return -EINVAL;
spin_lock_bh(&hdev_list_lock);
write_lock_bh(&hdev_list_lock);
/* Find first available device id */
list_for_each(p, &hdev_list) {
......@@ -807,11 +833,12 @@ int hci_register_dev(struct hci_dev *hdev)
atomic_set(&hdev->promisc, 0);
hci_notify(hdev, HCI_DEV_REG);
MOD_INC_USE_COUNT;
spin_unlock_bh(&hdev_list_lock);
write_unlock_bh(&hdev_list_lock);
hci_notify(hdev, HCI_DEV_REG);
hci_run_hotplug(hdev->name, "register");
return id;
}
......@@ -821,13 +848,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
{
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
spin_lock_bh(&hdev_list_lock);
write_lock_bh(&hdev_list_lock);
list_del(&hdev->list);
spin_unlock_bh(&hdev_list_lock);
write_unlock_bh(&hdev_list_lock);
hci_dev_do_close(hdev);
hci_notify(hdev, HCI_DEV_UNREG);
hci_run_hotplug(hdev->name, "unregister");
hci_dev_put(hdev);
MOD_DEC_USE_COUNT;
......@@ -1103,14 +1132,13 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
{
struct conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL;
int num = 0, min = 0xffff;
int num = 0, min = ~0;
struct list_head *p;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
list_for_each(p, &h->list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
if (c->type != type || c->state != BT_CONNECTED
......@@ -1118,14 +1146,15 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
continue;
num++;
if (c->sent < min || type == SCO_LINK) {
if (c->sent < min) {
min = c->sent;
conn = c;
}
}
if (conn) {
int q = hdev->acl_cnt / num;
int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
int q = cnt / num;
*quote = q ? q : 1;
} else
*quote = 0;
......@@ -1134,6 +1163,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
return conn;
}
static inline void hci_acl_tx_to(struct hci_dev *hdev)
{
struct conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c;
BT_ERR("%s ACL tx timeout", hdev->name);
/* Kill stalled connections */
list_for_each(p, &h->list) {
c = list_entry(p, struct hci_conn, list);
if (c->type == ACL_LINK && c->sent) {
BT_ERR("%s killing stalled ACL connection %s",
hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13);
}
}
}
static inline void hci_sched_acl(struct hci_dev *hdev)
{
struct hci_conn *conn;
......@@ -1142,21 +1190,19 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > HZ*5) {
BT_ERR("%s ACL tx timeout", hdev->name);
hdev->acl_cnt++;
}
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
hci_acl_tx_to(hdev);
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
while (quote && (skb = skb_dequeue(&conn->data_q))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
conn->sent++;
hdev->acl_cnt--;
quote--;
conn->sent++;
}
}
}
......@@ -1170,15 +1216,14 @@ static inline void hci_sched_sco(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
while ((conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
while (quote && (skb = skb_dequeue(&conn->data_q))) {
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb);
//conn->sent++;
//hdev->sco_cnt--;
quote--;
conn->sent++;
if (conn->sent == ~0)
conn->sent = 0;
}
}
}
......@@ -1205,7 +1250,6 @@ static void hci_tx_task(unsigned long arg)
read_unlock(&hci_task_lock);
}
/* ----- HCI RX task (incomming data proccessing) ----- */
/* ACL data packet */
......@@ -1367,9 +1411,6 @@ static void hci_cmd_task(unsigned long arg)
int hci_core_init(void)
{
/* Init locks */
spin_lock_init(&hdev_list_lock);
return 0;
}
......
......@@ -352,15 +352,12 @@ static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
hci_dev_lock(hdev);
acl = conn_hash_lookup_handle(hdev, handle);
if (!acl || !(sco = acl->link)) {
hci_dev_unlock(hdev);
break;
}
if (acl && (sco = acl->link)) {
sco->state = BT_CLOSED;
sco->state = BT_CLOSED;
hci_proto_connect_cfm(sco, status);
hci_conn_del(sco);
hci_proto_connect_cfm(sco, status);
hci_conn_del(sco);
}
hci_dev_unlock(hdev);
}
......
......@@ -86,7 +86,7 @@ static struct bluez_sock_list hci_sk_list = {
/* Send frame to RAW socket */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
{
struct sock * sk;
struct sock *sk;
BT_DBG("hdev %p len %d", hdev, skb->len);
......@@ -105,13 +105,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
/* Apply filter */
flt = &hci_pi(sk)->filter;
if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
if (!test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
continue;
if (skb->pkt_type == HCI_EVENT_PKT) {
register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
if (!hci_test_bit(evt, &flt->event_mask))
if (!test_bit(evt, flt->event_mask))
continue;
if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
......@@ -249,7 +249,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a
err = hci_sock_bound_ioctl(sk, cmd, arg);
release_sock(sk);
return err;
};
}
}
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
......@@ -393,12 +393,12 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
err = -EPERM;
if (skb->pkt_type == HCI_COMMAND_PKT) {
__u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
__u16 ogf = cmd_opcode_ogf(opcode) - 1;
__u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
u16 ogf = cmd_opcode_ogf(opcode) - 1;
u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
if (ogf > HCI_SFLT_MAX_OGF ||
!hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf]))
!test_bit(ocf, hci_sec_filter.ocf_mask[ogf]))
goto drop;
} else
goto drop;
......@@ -420,8 +420,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
{
struct hci_ufilter uf = { .opcode = 0 };
struct sock *sk = sock->sk;
struct hci_filter flt = { opcode: 0 };
int err = 0, opt = 0;
BT_DBG("sk %p, opt %d", sk, optname);
......@@ -454,32 +454,40 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva
break;
case HCI_FILTER:
len = MIN(len, sizeof(struct hci_filter));
if (copy_from_user(&flt, optval, len)) {
len = MIN(len, sizeof(uf));
if (copy_from_user(&uf, optval, len)) {
err = -EFAULT;
break;
}
if (!capable(CAP_NET_RAW)) {
flt.type_mask &= hci_sec_filter.type_mask;
flt.event_mask[0] &= hci_sec_filter.event_mask[0];
flt.event_mask[1] &= hci_sec_filter.event_mask[1];
uf.type_mask &= hci_sec_filter.type_mask;
uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0);
uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1);
}
{
struct hci_filter *f = &hci_pi(sk)->filter;
memcpy(&hci_pi(sk)->filter, &flt, len);
break;
f->type_mask = uf.type_mask;
f->opcode = uf.opcode;
*((u32 *) f->event_mask + 0) = uf.event_mask[0];
*((u32 *) f->event_mask + 1) = uf.event_mask[0];
}
break;
default:
err = -ENOPROTOOPT;
break;
};
}
release_sock(sk);
return err;
}
int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
int len, opt;
......@@ -508,15 +516,24 @@ int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optva
break;
case HCI_FILTER:
len = MIN(len, sizeof(struct hci_filter));
if (copy_to_user(optval, &hci_pi(sk)->filter, len))
{
struct hci_filter *f = &hci_pi(sk)->filter;
uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
uf.event_mask[0] = *((u32 *) f->event_mask + 1);
}
len = MIN(len, sizeof(uf));
if (copy_to_user(optval, &uf, len))
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
break;
};
}
return 0;
}
......
......@@ -25,9 +25,9 @@
/*
* BlueZ L2CAP core and sockets.
*
* $Id: l2cap.c,v 1.8 2002/04/19 00:01:39 maxk Exp $
* $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $
*/
#define VERSION "2.0"
#define VERSION "2.1"
#include <linux/config.h>
#include <linux/module.h>
......@@ -51,6 +51,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
......@@ -69,7 +70,7 @@ struct bluez_sock_list l2cap_sk_list = {
static int l2cap_conn_del(struct hci_conn *conn, int err);
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
static void l2cap_chan_del(struct sock *sk, int err);
static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
......@@ -177,6 +178,14 @@ static int l2cap_conn_del(struct hci_conn *hcon, int err)
return 0;
}
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
{
struct l2cap_chan_list *l = &conn->chan_list;
write_lock(&l->lock);
__l2cap_chan_add(conn, sk, parent);
write_unlock(&l->lock);
}
int l2cap_connect(struct sock *sk)
{
bdaddr_t *src = &bluez_sk(sk)->src;
......@@ -234,32 +243,26 @@ int l2cap_connect(struct sock *sk)
}
/* -------- Socket interface ---------- */
static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
{
bdaddr_t *src = &addr->l2_bdaddr;
__u16 psm = addr->l2_psm;
struct sock *sk;
for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
if (l2cap_pi(sk)->psm == psm &&
!bacmp(&bluez_sk(sk)->src, src))
!bacmp(&bluez_sk(sk)->src, src))
break;
}
return sk;
}
/* Find socket listening on psm and source bdaddr.
/* Find socket with psm and source bdaddr.
* Returns closest match.
*/
static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
{
struct sock *sk, *sk1 = NULL;
read_lock(&l2cap_sk_list.lock);
for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
if (sk->state != BT_LISTEN)
if (state && sk->state != state)
continue;
if (l2cap_pi(sk)->psm == psm) {
......@@ -272,9 +275,19 @@ static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
sk1 = sk;
}
}
return sk ? sk : sk1;
}
/* Find socket with given address (psm, src).
* Returns locked socket */
static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
{
struct sock *s;
read_lock(&l2cap_sk_list.lock);
s = __l2cap_get_sock_by_psm(state, psm, src);
if (s) bh_lock_sock(s);
read_unlock(&l2cap_sk_list.lock);
return sk ? sk : sk1;
return s;
}
static void l2cap_sock_destruct(struct sock *sk)
......@@ -283,7 +296,7 @@ static void l2cap_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->receive_queue);
skb_queue_purge(&sk->write_queue);
if (sk->protinfo)
kfree(sk->protinfo);
......@@ -320,8 +333,6 @@ static void l2cap_sock_kill(struct sock *sk)
sock_put(sk);
}
/* Close socket.
*/
static void __l2cap_sock_close(struct sock *sk, int reason)
{
BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
......@@ -333,6 +344,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
case BT_CONNECTED:
case BT_CONFIG:
case BT_CONNECT2:
if (sk->type == SOCK_SEQPACKET) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
l2cap_disconn_req req;
......@@ -349,7 +361,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
break;
case BT_CONNECT:
case BT_CONNECT2:
case BT_DISCONN:
l2cap_chan_del(sk, reason);
break;
......@@ -380,7 +391,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
if (parent) {
sk->type = parent->type;
pi->imtu = l2cap_pi(parent)->imtu;
pi->omtu = l2cap_pi(parent)->omtu;
pi->link_mode = l2cap_pi(parent)->link_mode;
......@@ -405,6 +415,8 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
sk->destruct = l2cap_sock_destruct;
sk->sndtimeo = L2CAP_CONN_TIMEOUT;
sk->protocol = proto;
sk->state = BT_OPEN;
l2cap_sock_init_timer(sk);
......@@ -421,11 +433,12 @@ static int l2cap_sock_create(struct socket *sock, int protocol)
BT_DBG("sock %p", sock);
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sock->state = SS_UNCONNECTED;
sock->ops = &l2cap_sock_ops;
sock->ops = &l2cap_sock_ops;
sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL);
if (!sk)
......@@ -453,20 +466,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
goto done;
}
write_lock(&l2cap_sk_list.lock);
if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
write_lock_bh(&l2cap_sk_list.lock);
if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
err = -EADDRINUSE;
goto unlock;
} else {
/* Save source address */
bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
l2cap_pi(sk)->psm = la->l2_psm;
sk->state = BT_BOUND;
}
/* Save source address */
bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
l2cap_pi(sk)->psm = la->l2_psm;
sk->state = BT_BOUND;
unlock:
write_unlock(&l2cap_sk_list.lock);
write_unlock_bh(&l2cap_sk_list.lock);
done:
release_sock(sk);
......@@ -629,7 +638,6 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src);
la->l2_psm = l2cap_pi(sk)->psm;
return 0;
}
......@@ -646,6 +654,10 @@ static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
/* Check outgoing MTU */
if (len > l2cap_pi(sk)->omtu)
return -EINVAL;
lock_sock(sk);
if (sk->state == BT_CONNECTED)
......@@ -691,7 +703,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
default:
err = -ENOPROTOOPT;
break;
};
}
release_sock(sk);
return err;
......@@ -743,20 +755,37 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
default:
err = -ENOPROTOOPT;
break;
};
}
release_sock(sk);
return err;
}
static int l2cap_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) return 0;
l2cap_sock_clear_timer(sk);
lock_sock(sk);
sk->shutdown = SHUTDOWN_MASK;
__l2cap_sock_close(sk, ECONNRESET);
release_sock(sk);
return 0;
}
static int l2cap_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk)
return 0;
if (!sk) return 0;
sock_orphan(sk);
l2cap_sock_close(sk);
......@@ -767,24 +796,20 @@ static int l2cap_sock_release(struct socket *sock)
static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
{
struct sock *s;
for (s = l->head; s; s = l2cap_pi(s)->next_c) {
if (l2cap_pi(s)->dcid == cid)
break;
}
return s;
}
static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
{
struct sock *s;
for (s = l->head; s; s = l2cap_pi(s)->next_c) {
if (l2cap_pi(s)->scid == cid)
break;
}
return s;
}
......@@ -850,10 +875,15 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
l2cap_pi(sk)->conn = conn;
if (sk->type == SOCK_SEQPACKET) {
/* Alloc CID for normal socket */
/* Alloc CID for connection-oriented socket */
l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
} else if (sk->type == SOCK_DGRAM) {
/* Connectionless socket */
l2cap_pi(sk)->scid = 0x0002;
l2cap_pi(sk)->dcid = 0x0002;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
} else {
/* Raw socket can send only signalling messages */
/* Raw socket can send/recv signalling messages only */
l2cap_pi(sk)->scid = 0x0001;
l2cap_pi(sk)->dcid = 0x0001;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
......@@ -865,14 +895,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
bluez_accept_enqueue(parent, sk);
}
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
{
struct l2cap_chan_list *l = &conn->chan_list;
write_lock(&l->lock);
__l2cap_chan_add(conn, sk, parent);
write_unlock(&l->lock);
}
/* Delete channel.
* Must be called on the locked socket. */
static void l2cap_chan_del(struct sock *sk, int err)
......@@ -984,25 +1006,31 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
struct sk_buff *skb, **frag;
int err, size, count, sent=0;
int err, hlen, count, sent=0;
l2cap_hdr *lh;
/* Check outgoing MTU */
if (len > l2cap_pi(sk)->omtu)
return -EINVAL;
BT_DBG("sk %p len %d", sk, len);
/* First fragment (with L2CAP header) */
count = MIN(conn->mtu - L2CAP_HDR_SIZE, len);
size = L2CAP_HDR_SIZE + count;
if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
if (sk->type == SOCK_DGRAM)
hlen = L2CAP_HDR_SIZE + 2;
else
hlen = L2CAP_HDR_SIZE;
count = MIN(conn->mtu - hlen, len);
skb = bluez_skb_send_alloc(sk, hlen + count,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err;
/* Create L2CAP header */
lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = __cpu_to_le16(len);
lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
if (sk->type == SOCK_DGRAM)
put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
err = -EFAULT;
......@@ -1315,37 +1343,41 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd,
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
/* Check if we have socket listening on psm */
if (!(parent = l2cap_get_sock_listen(conn->src, psm))) {
parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
if (!parent) {
result = L2CAP_CR_BAD_PSM;
goto resp;
goto sendresp;
}
write_lock(&list->lock);
bh_lock_sock(parent);
result = L2CAP_CR_NO_MEM;
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(list, scid))
goto unlock;
/* Check for backlog size */
if (parent->ack_backlog > parent->max_ack_backlog) {
BT_DBG("backlog full %d", parent->ack_backlog);
goto unlock;
goto response;
}
if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
goto unlock;
sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
if (!sk)
goto response;
l2cap_sock_init(sk, parent);
write_lock(&list->lock);
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(list, scid)) {
write_unlock(&list->lock);
sk->zapped = 1;
l2cap_sock_kill(sk);
goto response;
}
hci_conn_hold(conn->hcon);
l2cap_sock_init(sk, parent);
bacpy(&bluez_sk(sk)->src, conn->src);
bacpy(&bluez_sk(sk)->dst, conn->dst);
l2cap_pi(sk)->psm = psm;
l2cap_pi(sk)->dcid = scid;
hci_conn_hold(conn->hcon);
__l2cap_chan_add(conn, sk, parent);
dcid = l2cap_pi(sk)->scid;
......@@ -1360,20 +1392,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd,
if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
if (!hci_conn_encrypt(conn->hcon))
goto unlock;
goto done;
} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
if (!hci_conn_auth(conn->hcon))
goto unlock;
goto done;
}
sk->state = BT_CONFIG;
result = status = 0;
unlock:
bh_unlock_sock(parent);
done:
write_unlock(&list->lock);
resp:
response:
bh_unlock_sock(parent);
sendresp:
rsp.scid = __cpu_to_le16(scid);
rsp.dcid = __cpu_to_le16(dcid);
rsp.result = __cpu_to_le16(result);
......@@ -1678,10 +1712,37 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct
return 0;
}
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
{
struct sock *sk;
sk = l2cap_get_sock_by_psm(0, psm, conn->src);
if (!sk)
goto drop;
BT_DBG("sk %p, len %d", sk, skb->len);
if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
goto drop;
if (l2cap_pi(sk)->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
goto done;
drop:
kfree_skb(skb);
done:
if (sk) bh_unlock_sock(sk);
return 0;
}
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
{
l2cap_hdr *lh = (l2cap_hdr *) skb->data;
__u16 cid, len;
__u16 cid, psm, len;
skb_pull(skb, L2CAP_HDR_SIZE);
cid = __le16_to_cpu(lh->cid);
......@@ -1689,10 +1750,21 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("len %d, cid 0x%4.4x", len, cid);
if (cid == 0x0001)
switch (cid) {
case 0x0001:
l2cap_sig_channel(conn, skb);
else
break;
case 0x0002:
psm = get_unaligned((__u16 *) skb->data);
skb_pull(skb, 2);
l2cap_conless_channel(conn, psm, skb);
break;
default:
l2cap_data_channel(conn, cid, skb);
break;
}
}
/* ------------ L2CAP interface with lower layer (HCI) ------------- */
......@@ -1859,8 +1931,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16
BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
if (flags & ACL_START) {
int flen, tlen, size;
l2cap_hdr *lh;
l2cap_hdr *hdr;
int len;
if (conn->rx_len) {
BT_ERR("Unexpected start frame (len %d)", skb->len);
......@@ -1869,30 +1941,28 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16
conn->rx_len = 0;
}
if (skb->len < L2CAP_HDR_SIZE) {
if (skb->len < 2) {
BT_ERR("Frame is too small (len %d)", skb->len);
goto drop;
}
lh = (l2cap_hdr *)skb->data;
tlen = __le16_to_cpu(lh->len);
flen = skb->len - L2CAP_HDR_SIZE;
hdr = (l2cap_hdr *) skb->data;
len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
BT_DBG("Start: total len %d, frag len %d", tlen, flen);
BT_DBG("Start: total len %d, frag len %d", len, skb->len);
if (flen == tlen) {
if (len == skb->len) {
/* Complete frame received */
l2cap_recv_frame(conn, skb);
return 0;
}
/* Allocate skb for the complete frame (with header) */
size = L2CAP_HDR_SIZE + tlen;
if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
goto drop;
memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
conn->rx_len = tlen - flen;
conn->rx_len = len - skb->len;
} else {
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
......@@ -1932,7 +2002,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
struct sock *sk;
char *ptr = buf;
write_lock(&list->lock);
read_lock_bh(&list->lock);
for (sk = list->head; sk; sk = sk->next) {
pi = l2cap_pi(sk);
......@@ -1942,7 +2012,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
pi->link_mode);
}
write_unlock(&list->lock);
read_unlock_bh(&list->lock);
ptr += sprintf(ptr, "\n");
return ptr - buf;
......@@ -1973,38 +2043,38 @@ static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int
}
static struct proto_ops l2cap_sock_ops = {
.family = PF_BLUETOOTH,
.release = l2cap_sock_release,
.bind = l2cap_sock_bind,
.connect = l2cap_sock_connect,
.listen = l2cap_sock_listen,
.accept = l2cap_sock_accept,
.getname = l2cap_sock_getname,
.sendmsg = l2cap_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll,
.socketpair = sock_no_socketpair,
.ioctl = sock_no_ioctl,
.shutdown = sock_no_shutdown,
.setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt,
.mmap = sock_no_mmap
.family = PF_BLUETOOTH,
.release = l2cap_sock_release,
.bind = l2cap_sock_bind,
.connect = l2cap_sock_connect,
.listen = l2cap_sock_listen,
.accept = l2cap_sock_accept,
.getname = l2cap_sock_getname,
.sendmsg = l2cap_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.ioctl = sock_no_ioctl,
.shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt
};
static struct net_proto_family l2cap_sock_family_ops = {
.family = PF_BLUETOOTH,
.create = l2cap_sock_create
.family = PF_BLUETOOTH,
.create = l2cap_sock_create
};
static struct hci_proto l2cap_hci_proto = {
.name = "L2CAP",
.id = HCI_PROTO_L2CAP,
.connect_ind = l2cap_connect_ind,
.connect_cfm = l2cap_connect_cfm,
.disconn_ind = l2cap_disconn_ind,
.recv_acldata = l2cap_recv_acldata,
.auth_cfm = l2cap_auth_cfm,
.encrypt_cfm = l2cap_encrypt_cfm
.name = "L2CAP",
.id = HCI_PROTO_L2CAP,
.connect_ind = l2cap_connect_ind,
.connect_cfm = l2cap_connect_cfm,
.disconn_ind = l2cap_disconn_ind,
.auth_cfm = l2cap_auth_cfm,
.encrypt_cfm = l2cap_encrypt_cfm,
.recv_acldata = l2cap_recv_acldata
};
int __init l2cap_init(void)
......
......@@ -105,7 +105,7 @@ int bterr(__u16 code)
return EACCES;
case 0x06:
return EINVAL;
return EBADE;
case 0x07:
return ENOMEM;
......
dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
bool ' RFCOMM TTY support' CONFIG_RFCOMM_TTY
fi
#
# Makefile for BNEP protocol
#
O_TARGET := rfcomm.o
obj-y := core.o sock.o crc.o
obj-$(CONFIG_RFCOMM_TTY) += tty.o
obj-m += $(O_TARGET)
include $(TOPDIR)/Rules.make
/*
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2002 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.
*/
/*
RPN support - Dirk Husemann <hud@zurich.ibm.com>
*/
/*
* RFCOMM core.
*
* $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $
*/
#define __KERNEL_SYSCALLS__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/net.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
#define VERSION "0.3"
#ifndef CONFIG_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
struct task_struct *rfcomm_thread;
DECLARE_MUTEX(rfcomm_sem);
unsigned long rfcomm_event;
static LIST_HEAD(session_list);
static atomic_t terminate, running;
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
static int rfcomm_queue_disc(struct rfcomm_dlc *d);
static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type);
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d);
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig);
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len);
static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits);
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
static void rfcomm_process_connect(struct rfcomm_session *s);
/* ---- RFCOMM frame parsing macros ---- */
#define __get_dlci(b) ((b & 0xfc) >> 2)
#define __get_channel(b) ((b & 0xf8) >> 3)
#define __get_dir(b) ((b & 0x04) >> 2)
#define __get_type(b) ((b & 0xef))
#define __test_ea(b) ((b & 0x01))
#define __test_cr(b) ((b & 0x02))
#define __test_pf(b) ((b & 0x10))
#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir)
#define __srv_channel(dlci) (dlci >> 1)
#define __dir(dlci) (dlci & 0x01)
#define __len8(len) (((len) << 1) | 1)
#define __len16(len) ((len) << 1)
/* MCC macros */
#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01))
#define __get_mcc_type(b) ((b & 0xfc) >> 2)
#define __get_mcc_len(b) ((b & 0xfe) >> 1)
/* RPN macros */
#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
#define __get_rpn_data_bits(line) ((line) & 0x3)
#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
#define __get_rpn_parity(line) (((line) >> 3) & 0x3)
/* ---- RFCOMM FCS computation ---- */
/* CRC on 2 bytes */
#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
/* FCS on 2 bytes */
static inline u8 __fcs(u8 *data)
{
return (0xff - __crc(data));
}
/* FCS on 3 bytes */
static inline u8 __fcs2(u8 *data)
{
return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
}
/* Check FCS */
static inline int __check_fcs(u8 *data, int type, u8 fcs)
{
u8 f = __crc(data);
if (type != RFCOMM_UIH)
f = rfcomm_crc_table[f ^ data[2]];
return rfcomm_crc_table[f ^ fcs] != 0xcf;
}
/* ---- L2CAP callbacks ---- */
static void rfcomm_l2state_change(struct sock *sk)
{
BT_DBG("%p state %d", sk, sk->state);
rfcomm_schedule(RFCOMM_SCHED_STATE);
}
static void rfcomm_l2data_ready(struct sock *sk, int bytes)
{
BT_DBG("%p bytes %d", sk, bytes);
rfcomm_schedule(RFCOMM_SCHED_RX);
}
static int rfcomm_l2sock_create(struct socket **sock)
{
int err;
BT_DBG("");
err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
if (!err) {
struct sock *sk = (*sock)->sk;
sk->data_ready = rfcomm_l2data_ready;
sk->state_change = rfcomm_l2state_change;
}
return err;
}
/* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg)
{
struct rfcomm_dlc *d = (void *) arg;
BT_DBG("dlc %p state %ld", d, d->state);
set_bit(RFCOMM_TIMED_OUT, &d->flags);
rfcomm_dlc_put(d);
rfcomm_schedule(RFCOMM_SCHED_TIMEO);
}
static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout)
{
BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout);
if (!mod_timer(&d->timer, jiffies + timeout))
rfcomm_dlc_hold(d);
}
static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
if (timer_pending(&d->timer) && del_timer(&d->timer))
rfcomm_dlc_put(d);
}
static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
{
BT_DBG("%p", d);
d->state = BT_OPEN;
d->flags = 0;
d->mtu = RFCOMM_DEFAULT_MTU;
d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
d->credits = RFCOMM_MAX_CREDITS;
d->rx_credits = RFCOMM_DEFAULT_CREDITS;
}
struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
{
struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
if (!d)
return NULL;
memset(d, 0, sizeof(*d));
init_timer(&d->timer);
d->timer.function = rfcomm_dlc_timeout;
d->timer.data = (unsigned long) d;
skb_queue_head_init(&d->tx_queue);
spin_lock_init(&d->lock);
atomic_set(&d->refcnt, 1);
rfcomm_dlc_clear_state(d);
BT_DBG("%p", d);
return d;
}
void rfcomm_dlc_free(struct rfcomm_dlc *d)
{
BT_DBG("%p", d);
skb_queue_purge(&d->tx_queue);
kfree(d);
}
static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
{
BT_DBG("dlc %p session %p", d, s);
rfcomm_session_hold(s);
rfcomm_dlc_hold(d);
list_add(&d->list, &s->dlcs);
d->session = s;
}
static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
{
struct rfcomm_session *s = d->session;
BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
list_del(&d->list);
d->session = NULL;
rfcomm_dlc_put(d);
rfcomm_session_put(s);
}
static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, int dlci)
{
struct rfcomm_dlc *d;
struct list_head *p;
list_for_each(p, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
if (d->dlci == dlci)
return d;
}
return NULL;
}
static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
{
struct rfcomm_session *s;
int err = 0, dlci = __dlci(0, channel);
BT_DBG("dlc %p state %ld %s %s channel %d dlci %d",
d, d->state, batostr(src), batostr(dst), channel, dlci);
if (dlci < 1 || dlci > 62)
return -EINVAL;
if (d->state != BT_OPEN && d->state != BT_CLOSED)
return 0;
s = rfcomm_session_get(src, dst);
if (!s) {
s = rfcomm_session_create(src, dst, &err);
if (!s)
return err;
}
/* Check if DLCI already exists */
if (rfcomm_dlc_get(s, dlci))
return -EBUSY;
rfcomm_dlc_clear_state(d);
d->dlci = dlci;
d->addr = __addr(s->initiator, dlci);
d->state = BT_CONFIG;
rfcomm_dlc_link(s, d);
d->mtu = s->mtu;
d->credits = s->credits;
if (s->state == BT_CONNECTED)
rfcomm_send_pn(s, 1, d);
rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
return 0;
}
int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
{
mm_segment_t fs;
int r;
rfcomm_lock();
fs = get_fs(); set_fs(KERNEL_DS);
r = __rfcomm_dlc_open(d, src, dst, channel);
set_fs(fs);
rfcomm_unlock();
return r;
}
static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
{
struct rfcomm_session *s = d->session;
if (!s)
return 0;
BT_DBG("dlc %p state %ld dlci %d err %d session %p",
d, d->state, d->dlci, err, s);
switch (d->state) {
case BT_CONNECTED:
case BT_CONFIG:
case BT_CONNECT:
d->state = BT_DISCONN;
if (skb_queue_empty(&d->tx_queue)) {
rfcomm_send_disc(s, d->dlci);
rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
} else {
rfcomm_queue_disc(d);
rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
}
break;
default:
rfcomm_dlc_clear_timer(d);
rfcomm_dlc_lock(d);
d->state = BT_CLOSED;
d->state_change(d, err);
rfcomm_dlc_unlock(d);
skb_queue_purge(&d->tx_queue);
rfcomm_dlc_unlink(d);
}
return 0;
}
int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
{
mm_segment_t fs;
int r;
rfcomm_lock();
fs = get_fs(); set_fs(KERNEL_DS);
r = __rfcomm_dlc_close(d, err);
set_fs(fs);
rfcomm_unlock();
return r;
}
int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
{
int len = skb->len;
if (d->state != BT_CONNECTED)
return -ENOTCONN;
BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
if (len > d->mtu)
return -EINVAL;
rfcomm_make_uih(skb, d->addr);
skb_queue_tail(&d->tx_queue, skb);
if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
rfcomm_schedule(RFCOMM_SCHED_TX);
return len;
}
void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
if (!d->credits) {
d->v24_sig |= RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
rfcomm_schedule(RFCOMM_SCHED_TX);
}
void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
if (!d->credits) {
d->v24_sig &= ~RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
rfcomm_schedule(RFCOMM_SCHED_TX);
}
int rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig)
{
BT_DBG("dlc %p state %ld v24_sig 0x%x",
d, d->state, v24_sig);
if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
v24_sig |= RFCOMM_V24_FC;
else
v24_sig &= ~RFCOMM_V24_FC;
d->v24_sig = v24_sig;
if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
rfcomm_schedule(RFCOMM_SCHED_TX);
return 0;
}
/* ---- RFCOMM sessions ---- */
struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
{
struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return NULL;
memset(s, 0, sizeof(*s));
BT_DBG("session %p sock %p", s, sock);
INIT_LIST_HEAD(&s->dlcs);
s->state = state;
s->sock = sock;
s->mtu = RFCOMM_DEFAULT_MTU;
s->credits = RFCOMM_MAX_CREDITS;
list_add(&s->list, &session_list);
/* Do not increment module usage count for listeting sessions.
* Otherwise we won't be able to unload the module. */
if (state != BT_LISTEN)
MOD_INC_USE_COUNT;
return s;
}
void rfcomm_session_del(struct rfcomm_session *s)
{
int state = s->state;
BT_DBG("session %p state %ld", s, s->state);
list_del(&s->list);
if (state == BT_CONNECTED)
rfcomm_send_disc(s, 0);
sock_release(s->sock);
kfree(s);
if (state != BT_LISTEN)
MOD_DEC_USE_COUNT;
}
struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
{
struct rfcomm_session *s;
struct list_head *p, *n;
struct bluez_sock *sk;
list_for_each_safe(p, n, &session_list) {
s = list_entry(p, struct rfcomm_session, list);
sk = bluez_sk(s->sock->sk);
if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
!bacmp(&sk->dst, dst))
return s;
}
return NULL;
}
void rfcomm_session_close(struct rfcomm_session *s, int err)
{
struct rfcomm_dlc *d;
struct list_head *p, *n;
BT_DBG("session %p state %ld err %d", s, s->state, err);
rfcomm_session_hold(s);
s->state = BT_CLOSED;
/* Close all dlcs */
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, err);
}
rfcomm_session_put(s);
}
struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
{
struct rfcomm_session *s = NULL;
struct sockaddr_l2 addr;
struct l2cap_options opts;
struct socket *sock;
int size;
BT_DBG("%s %s", batostr(src), batostr(dst));
*err = rfcomm_l2sock_create(&sock);
if (*err < 0)
return NULL;
bacpy(&addr.l2_bdaddr, src);
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = 0;
*err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (*err < 0)
goto failed;
/* Set L2CAP options */
size = sizeof(opts);
sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
opts.imtu = RFCOMM_MAX_L2CAP_MTU;
sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
s = rfcomm_session_add(sock, BT_BOUND);
if (!s) {
*err = -ENOMEM;
goto failed;
}
s->initiator = 1;
bacpy(&addr.l2_bdaddr, dst);
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = htobs(RFCOMM_PSM);
*err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
if (*err == 0 || *err == -EAGAIN)
return s;
rfcomm_session_del(s);
return NULL;
failed:
sock_release(sock);
return NULL;
}
/* ---- RFCOMM frame sending ---- */
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
{
struct socket *sock = s->sock;
struct iovec iv = { data, len };
struct msghdr msg;
int err;
BT_DBG("session %p len %d", s, len);
memset(&msg, 0, sizeof(msg));
msg.msg_iovlen = 1;
msg.msg_iov = &iv;
err = sock->ops->sendmsg(sock, &msg, len, 0);
return err;
}
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_cmd cmd;
BT_DBG("%p dlci %d", s, dlci);
cmd.addr = __addr(s->initiator, dlci);
cmd.ctrl = __ctrl(RFCOMM_SABM, 1);
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
}
static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_cmd cmd;
BT_DBG("%p dlci %d", s, dlci);
cmd.addr = __addr(!s->initiator, dlci);
cmd.ctrl = __ctrl(RFCOMM_UA, 1);
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
}
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_cmd cmd;
BT_DBG("%p dlci %d", s, dlci);
cmd.addr = __addr(s->initiator, dlci);
cmd.ctrl = __ctrl(RFCOMM_DISC, 1);
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
}
static int rfcomm_queue_disc(struct rfcomm_dlc *d)
{
struct rfcomm_cmd *cmd;
struct sk_buff *skb;
BT_DBG("dlc %p dlci %d", d, d->dlci);
skb = alloc_skb(sizeof(*cmd), GFP_KERNEL);
if (!skb)
return -ENOMEM;
cmd = (void *) __skb_put(skb, sizeof(*cmd));
cmd->addr = d->addr;
cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
cmd->len = __len8(0);
cmd->fcs = __fcs2((u8 *) cmd);
skb_queue_tail(&d->tx_queue, skb);
rfcomm_schedule(RFCOMM_SCHED_TX);
return 0;
}
static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
{
struct rfcomm_cmd cmd;
BT_DBG("%p dlci %d", s, dlci);
cmd.addr = __addr(!s->initiator, dlci);
cmd.ctrl = __ctrl(RFCOMM_DM, 1);
cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
}
static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
{
struct rfcomm_hdr *hdr;
struct rfcomm_mcc *mcc;
u8 buf[16], *ptr = buf;
BT_DBG("%p cr %d type %d", s, cr, type);
hdr = (void *) ptr; ptr += sizeof(*hdr);
hdr->addr = __addr(s->initiator, 0);
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
hdr->len = __len8(sizeof(*mcc) + 1);
mcc = (void *) ptr; ptr += sizeof(*mcc);
mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
mcc->len = __len8(1);
/* Type that we didn't like */
*ptr = __mcc_type(cr, type); ptr++;
*ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf);
}
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
{
struct rfcomm_hdr *hdr;
struct rfcomm_mcc *mcc;
struct rfcomm_pn *pn;
u8 buf[16], *ptr = buf;
BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu);
hdr = (void *) ptr; ptr += sizeof(*hdr);
hdr->addr = __addr(s->initiator, 0);
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
mcc = (void *) ptr; ptr += sizeof(*mcc);
mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
mcc->len = __len8(sizeof(*pn));
pn = (void *) ptr; ptr += sizeof(*pn);
pn->dlci = d->dlci;
pn->priority = 0;
pn->ack_timer = 0;
pn->max_retrans = 0;
if (d->credits) {
pn->flow_ctrl = cr ? 0xf0 : 0xe0;
pn->credits = RFCOMM_DEFAULT_CREDITS;
} else {
pn->flow_ctrl = 0;
pn->credits = 0;
}
pn->mtu = htobs(d->mtu);
*ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf);
}
static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
u8 bit_rate, u8 data_bits, u8 stop_bits,
u8 parity, u8 flow_ctrl_settings,
u8 xon_char, u8 xoff_char, u16 param_mask)
{
struct rfcomm_hdr *hdr;
struct rfcomm_mcc *mcc;
struct rfcomm_rpn *rpn;
u8 buf[16], *ptr = buf;
BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
"flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x",
s, cr, dlci, bit_rate, data_bits, stop_bits, parity,
flow_ctrl_settings, xon_char, xoff_char, param_mask);
hdr = (void *) ptr; ptr += sizeof(*hdr);
hdr->addr = __addr(s->initiator, 0);
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn));
mcc = (void *) ptr; ptr += sizeof(*mcc);
mcc->type = __mcc_type(cr, RFCOMM_RPN);
mcc->len = __len8(sizeof(*rpn));
rpn = (void *) ptr; ptr += sizeof(*rpn);
rpn->dlci = __addr(1, dlci);
rpn->bit_rate = bit_rate;
rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity);
rpn->flow_ctrl = flow_ctrl_settings;
rpn->xon_char = xon_char;
rpn->xoff_char = xoff_char;
rpn->param_mask = param_mask;
*ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf);
}
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
{
struct rfcomm_hdr *hdr;
struct rfcomm_mcc *mcc;
struct rfcomm_msc *msc;
u8 buf[16], *ptr = buf;
BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig);
hdr = (void *) ptr; ptr += sizeof(*hdr);
hdr->addr = __addr(s->initiator, 0);
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
hdr->len = __len8(sizeof(*mcc) + sizeof(*msc));
mcc = (void *) ptr; ptr += sizeof(*mcc);
mcc->type = __mcc_type(cr, RFCOMM_MSC);
mcc->len = __len8(sizeof(*msc));
msc = (void *) ptr; ptr += sizeof(*msc);
msc->dlci = __addr(1, dlci);
msc->v24_sig = v24_sig;
*ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf);
}
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
{
struct socket *sock = s->sock;
struct iovec iv[3];
struct msghdr msg;
unsigned char hdr[5], crc[1];
if (len > 125)
return -EINVAL;
BT_DBG("%p cr %d", s, cr);
hdr[0] = __addr(s->initiator, 0);
hdr[1] = __ctrl(RFCOMM_UIH, 0);
hdr[2] = 0x01 | ((len + 2) << 1);
hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2);
hdr[4] = 0x01 | (len << 1);
crc[0] = __fcs(hdr);
iv[0].iov_base = hdr;
iv[0].iov_len = 5;
iv[1].iov_base = pattern;
iv[1].iov_len = len;
iv[2].iov_base = crc;
iv[2].iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_iovlen = 3;
msg.msg_iov = iv;
return sock->ops->sendmsg(sock, &msg, 6 + len, 0);
}
static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
{
struct rfcomm_hdr *hdr;
u8 buf[16], *ptr = buf;
BT_DBG("%p addr %d credits %d", s, addr, credits);
hdr = (void *) ptr; ptr += sizeof(*hdr);
hdr->addr = addr;
hdr->ctrl = __ctrl(RFCOMM_UIH, 1);
hdr->len = __len8(0);
*ptr = credits; ptr++;
*ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf);
}
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
{
struct rfcomm_hdr *hdr;
int len = skb->len;
u8 *crc;
if (len > 127) {
hdr = (void *) skb_push(skb, 4);
put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
} else {
hdr = (void *) skb_push(skb, 3);
hdr->len = __len8(len);
}
hdr->addr = addr;
hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
crc = skb_put(skb, 1);
*crc = __fcs((void *) hdr);
}
/* ---- RFCOMM frame reception ---- */
static int rfcomm_recv_ua(struct rfcomm_session *s, int dlci)
{
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
if (dlci) {
/* Data channel */
struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
if (!d) {
rfcomm_send_dm(s, dlci);
return 0;
}
switch (d->state) {
case BT_CONNECT:
rfcomm_dlc_clear_timer(d);
rfcomm_dlc_lock(d);
d->state = BT_CONNECTED;
d->state_change(d, 0);
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 1, dlci, d->v24_sig);
break;
case BT_DISCONN:
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, 0);
break;
}
} else {
/* Control channel */
switch (s->state) {
case BT_CONNECT:
s->state = BT_CONNECTED;
rfcomm_process_connect(s);
break;
}
}
return 0;
}
static int rfcomm_recv_dm(struct rfcomm_session *s, int dlci)
{
int err = 0;
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
if (dlci) {
/* Data DLC */
struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
if (d) {
if (d->state == BT_CONNECT || d->state == BT_CONFIG)
err = ECONNREFUSED;
else
err = ECONNRESET;
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, err);
}
} else {
if (s->state == BT_CONNECT)
err = ECONNREFUSED;
else
err = ECONNRESET;
s->state = BT_CLOSED;
rfcomm_session_close(s, err);
}
return 0;
}
static int rfcomm_recv_disc(struct rfcomm_session *s, int dlci)
{
int err = 0;
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
if (dlci) {
struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
if (d) {
rfcomm_send_ua(s, dlci);
if (d->state == BT_CONNECT || d->state == BT_CONFIG)
err = ECONNREFUSED;
else
err = ECONNRESET;
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, err);
} else
rfcomm_send_dm(s, dlci);
} else {
rfcomm_send_ua(s, 0);
if (s->state == BT_CONNECT)
err = ECONNREFUSED;
else
err = ECONNRESET;
s->state = BT_CLOSED;
rfcomm_session_close(s, err);
}
return 0;
}
static int rfcomm_recv_sabm(struct rfcomm_session *s, int dlci)
{
struct rfcomm_dlc *d;
int channel;
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
if (!dlci) {
rfcomm_send_ua(s, 0);
if (s->state == BT_OPEN) {
s->state = BT_CONNECTED;
rfcomm_process_connect(s);
}
return 0;
}
/* Check if DLC exists */
d = rfcomm_dlc_get(s, dlci);
if (d) {
if (d->state == BT_OPEN) {
/* DLC was previously opened by PN request */
rfcomm_send_ua(s, dlci);
rfcomm_dlc_lock(d);
d->state = BT_CONNECTED;
d->state_change(d, 0);
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 1, dlci, d->v24_sig);
}
return 0;
}
/* Notify socket layer about incomming connection */
channel = __srv_channel(dlci);
if (rfcomm_connect_ind(s, channel, &d)) {
d->dlci = dlci;
d->addr = __addr(s->initiator, dlci);
rfcomm_dlc_link(s, d);
rfcomm_send_ua(s, dlci);
rfcomm_dlc_lock(d);
d->state = BT_CONNECTED;
d->state_change(d, 0);
rfcomm_dlc_unlock(d);
} else {
rfcomm_send_dm(s, dlci);
}
return 0;
}
static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
{
BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
if (cr) {
if (pn->flow_ctrl == 0xf0) {
d->tx_credits = pn->credits;
} else {
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
d->credits = 0;
}
d->mtu = btohs(pn->mtu);
} else {
if (pn->flow_ctrl == 0xe0) {
d->tx_credits = pn->credits;
} else {
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
d->credits = 0;
}
d->mtu = btohs(pn->mtu);
}
return 0;
}
static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
{
struct rfcomm_pn *pn = (void *) skb->data;
struct rfcomm_dlc *d;
int dlci = pn->dlci;
BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
if (!dlci)
return 0;
d = rfcomm_dlc_get(s, dlci);
if (d) {
if (cr) {
/* PN request */
rfcomm_apply_pn(d, cr, pn);
rfcomm_send_pn(s, 0, d);
} else {
/* PN response */
switch (d->state) {
case BT_CONFIG:
rfcomm_apply_pn(d, cr, pn);
d->state = BT_CONNECT;
rfcomm_send_sabm(s, d->dlci);
break;
}
}
} else {
int channel = __srv_channel(dlci);
if (!cr)
return 0;
/* PN request for non existing DLC.
* Assume incomming connection. */
if (rfcomm_connect_ind(s, channel, &d)) {
d->dlci = dlci;
d->addr = __addr(s->initiator, dlci);
rfcomm_dlc_link(s, d);
rfcomm_apply_pn(d, cr, pn);
d->state = BT_OPEN;
rfcomm_send_pn(s, 0, d);
} else {
rfcomm_send_dm(s, dlci);
}
}
return 0;
}
static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
{
struct rfcomm_rpn *rpn = (void *) skb->data;
int dlci = __get_dlci(rpn->dlci);
u8 bit_rate = 0;
u8 data_bits = 0;
u8 stop_bits = 0;
u8 parity = 0;
u8 flow_ctrl = 0;
u8 xon_char = 0;
u8 xoff_char = 0;
u16 rpn_mask = RFCOMM_RPN_PM_ALL;
BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
rpn->xon_char, rpn->xoff_char, rpn->param_mask);
if (!cr)
return 0;
if (len == 1) {
/* request: return default setting */
bit_rate = RFCOMM_RPN_BR_115200;
data_bits = RFCOMM_RPN_DATA_8;
stop_bits = RFCOMM_RPN_STOP_1;
parity = RFCOMM_RPN_PARITY_NONE;
flow_ctrl = RFCOMM_RPN_FLOW_NONE;
xon_char = RFCOMM_RPN_XON_CHAR;
xoff_char = RFCOMM_RPN_XOFF_CHAR;
goto rpn_out;
}
/* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
no flow control lines, normal XON/XOFF chars */
if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
data_bits = __get_rpn_data_bits(rpn->line_settings);
if (data_bits != RFCOMM_RPN_DATA_8) {
BT_DBG("RPN data bits mismatch 0x%x", data_bits);
data_bits = RFCOMM_RPN_DATA_8;
rpn_mask ^= RFCOMM_RPN_PM_DATA;
}
}
if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
stop_bits = __get_rpn_stop_bits(rpn->line_settings);
if (stop_bits != RFCOMM_RPN_STOP_1) {
BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
stop_bits = RFCOMM_RPN_STOP_1;
rpn_mask ^= RFCOMM_RPN_PM_STOP;
}
}
if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
parity = __get_rpn_parity(rpn->line_settings);
if (parity != RFCOMM_RPN_PARITY_NONE) {
BT_DBG("RPN parity mismatch 0x%x", parity);
parity = RFCOMM_RPN_PARITY_NONE;
rpn_mask ^= RFCOMM_RPN_PM_PARITY;
}
}
if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
rpn_mask ^= RFCOMM_RPN_PM_FLOW;
}
}
if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
rpn->xon_char = RFCOMM_RPN_XON_CHAR;
rpn_mask ^= RFCOMM_RPN_PM_XON;
}
}
if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
rpn_mask ^= RFCOMM_RPN_PM_XOFF;
}
}
rpn_out:
rfcomm_send_rpn(s, 0, dlci,
bit_rate, data_bits, stop_bits, parity, flow_ctrl,
xon_char, xoff_char, rpn_mask);
return 0;
}
static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
{
struct rfcomm_msc *msc = (void *) skb->data;
struct rfcomm_dlc *d;
int dlci = __get_dlci(msc->dlci);
BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
if (!cr)
return 0;
d = rfcomm_dlc_get(s, dlci);
if (d) {
if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
else
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
rfcomm_dlc_lock(d);
if (d->modem_status)
d->modem_status(d, msc->v24_sig);
rfcomm_dlc_unlock(d);
rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
}
return 0;
}
static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
{
struct rfcomm_mcc *mcc = (void *) skb->data;
u8 type, cr, len;
cr = __test_cr(mcc->type);
type = __get_mcc_type(mcc->type);
len = __get_mcc_len(mcc->len);
BT_DBG("%p type 0x%x cr %d", s, type, cr);
skb_pull(skb, 2);
switch (type) {
case RFCOMM_PN:
rfcomm_recv_pn(s, cr, skb);
break;
case RFCOMM_RPN:
rfcomm_recv_rpn(s, cr, len, skb);
break;
case RFCOMM_MSC:
rfcomm_recv_msc(s, cr, skb);
break;
case RFCOMM_TEST:
if (cr)
rfcomm_send_test(s, 0, skb->data, skb->len);
break;
default:
BT_ERR("Unknown control type 0x%02x", type);
rfcomm_send_nsc(s, cr, type);
break;
}
return 0;
}
static int rfcomm_recv_data(struct rfcomm_session *s, int dlci, int pf, struct sk_buff *skb)
{
struct rfcomm_dlc *d;
BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf);
d = rfcomm_dlc_get(s, dlci);
if (!d) {
rfcomm_send_dm(s, dlci);
goto drop;
}
if (pf && d->credits) {
u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
d->tx_credits += credits;
if (d->tx_credits)
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
}
if (skb->len && d->state == BT_CONNECTED) {
rfcomm_dlc_lock(d);
d->rx_credits--;
d->data_ready(d, skb);
rfcomm_dlc_unlock(d);
return 0;
}
drop:
kfree_skb(skb);
return 0;
}
static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
{
struct rfcomm_hdr *hdr = (void *) skb->data;
u8 type, dlci, fcs;
dlci = __get_dlci(hdr->addr);
type = __get_type(hdr->ctrl);
/* Trim FCS */
skb->len--; skb->tail--;
fcs = *(u8 *) skb->tail;
if (__check_fcs(skb->data, type, fcs)) {
BT_ERR("bad checksum in packet");
kfree_skb(skb);
return -EILSEQ;
}
if (__test_ea(hdr->len))
skb_pull(skb, 3);
else
skb_pull(skb, 4);
switch (type) {
case RFCOMM_SABM:
if (__test_pf(hdr->ctrl))
rfcomm_recv_sabm(s, dlci);
break;
case RFCOMM_DISC:
if (__test_pf(hdr->ctrl))
rfcomm_recv_disc(s, dlci);
break;
case RFCOMM_UA:
if (__test_pf(hdr->ctrl))
rfcomm_recv_ua(s, dlci);
break;
case RFCOMM_DM:
rfcomm_recv_dm(s, dlci);
break;
case RFCOMM_UIH:
if (dlci)
return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
rfcomm_recv_mcc(s, skb);
break;
default:
BT_ERR("Unknown packet type 0x%02x\n", type);
break;
}
kfree_skb(skb);
return 0;
}
/* ---- Connection and data processing ---- */
static void rfcomm_process_connect(struct rfcomm_session *s)
{
struct rfcomm_dlc *d;
struct list_head *p, *n;
BT_DBG("session %p state %ld", s, s->state);
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
if (d->state == BT_CONFIG) {
d->mtu = s->mtu;
rfcomm_send_pn(s, 1, d);
}
}
}
/* Send data queued for the DLC.
* Return number of frames left in the queue.
*/
static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
{
struct sk_buff *skb;
int err;
BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d",
d, d->state, d->credits, d->rx_credits, d->tx_credits);
/* Send pending MSC */
if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
rfcomm_send_msc(d->session, d->dlci, 1, d->v24_sig);
if (d->credits) {
/* CFC enabled.
* Give them some credits */
if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
d->rx_credits <= (d->credits >> 2)) {
rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
d->rx_credits = d->credits;
}
} else {
/* CFC disabled.
* Give ourselves some credits */
d->tx_credits = RFCOMM_MAX_CREDITS;
}
if (test_bit(RFCOMM_TX_THROTTLED, &d->flags))
return skb_queue_len(&d->tx_queue);
while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
err = rfcomm_send_frame(d->session, skb->data, skb->len);
if (err < 0) {
skb_queue_head(&d->tx_queue, skb);
break;
}
kfree_skb(skb);
d->tx_credits--;
}
if (d->credits && !d->tx_credits) {
/* We're out of TX credits.
* Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
}
return skb_queue_len(&d->tx_queue);
}
static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
{
struct rfcomm_dlc *d;
struct list_head *p, *n;
BT_DBG("session %p state %ld", s, s->state);
list_for_each_safe(p, n, &s->dlcs) {
d = list_entry(p, struct rfcomm_dlc, list);
if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
__rfcomm_dlc_close(d, ETIMEDOUT);
continue;
}
if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
rfcomm_process_tx(d);
}
}
static inline void rfcomm_process_rx(struct rfcomm_session *s)
{
struct socket *sock = s->sock;
struct sock *sk = sock->sk;
struct sk_buff *skb;
BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue));
/* Get data directly from socket receive queue without copying it. */
while ((skb = skb_dequeue(&sk->receive_queue))) {
skb_orphan(skb);
rfcomm_recv_frame(s, skb);
}
if (sk->state == BT_CLOSED) {
if (!s->initiator)
rfcomm_session_put(s);
rfcomm_session_close(s, sk->err);
}
}
static inline void rfcomm_accept_connection(struct rfcomm_session *s)
{
struct socket *sock = s->sock, *nsock;
int err;
/* Fast check for a new connection.
* Avoids unnesesary socket allocations. */
if (list_empty(&bluez_sk(sock->sk)->accept_q))
return;
BT_DBG("session %p", s);
nsock = sock_alloc();
if (!nsock)
return;
nsock->type = sock->type;
nsock->ops = sock->ops;
err = sock->ops->accept(sock, nsock, O_NONBLOCK);
if (err < 0) {
sock_release(nsock);
return;
}
/* Set our callbacks */
nsock->sk->data_ready = rfcomm_l2data_ready;
nsock->sk->state_change = rfcomm_l2state_change;
s = rfcomm_session_add(nsock, BT_OPEN);
if (s)
rfcomm_session_hold(s);
else
sock_release(nsock);
}
static inline void rfcomm_check_connection(struct rfcomm_session *s)
{
struct sock *sk = s->sock->sk;
BT_DBG("%p state %ld", s, s->state);
switch(sk->state) {
case BT_CONNECTED:
s->state = BT_CONNECT;
/* We can adjust MTU on outgoing sessions.
* L2CAP MTU minus UIH header and FCS. */
s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
rfcomm_send_sabm(s, 0);
break;
case BT_CLOSED:
s->state = BT_CLOSED;
rfcomm_session_close(s, sk->err);
break;
}
}
static inline void rfcomm_process_sessions(void)
{
struct list_head *p, *n;
rfcomm_lock();
list_for_each_safe(p, n, &session_list) {
struct rfcomm_session *s;
s = list_entry(p, struct rfcomm_session, list);
if (s->state == BT_LISTEN) {
rfcomm_accept_connection(s);
continue;
}
rfcomm_session_hold(s);
switch (s->state) {
case BT_BOUND:
rfcomm_check_connection(s);
break;
default:
rfcomm_process_rx(s);
break;
}
rfcomm_process_dlcs(s);
rfcomm_session_put(s);
}
rfcomm_unlock();
}
static void rfcomm_worker(void)
{
BT_DBG("");
while (!atomic_read(&terminate)) {
if (!test_and_clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
/* No pending events. Let's sleep.
* Incomming connections and data will wake us up. */
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
/* Process stuff */
rfcomm_process_sessions();
}
set_current_state(TASK_RUNNING);
return;
}
static int rfcomm_add_listener(bdaddr_t *ba)
{
struct sockaddr_l2 addr;
struct l2cap_options opts;
struct socket *sock;
struct rfcomm_session *s;
int size, err = 0;
/* Create socket */
err = rfcomm_l2sock_create(&sock);
if (err < 0) {
BT_ERR("Create socket failed %d", err);
return err;
}
/* Bind socket */
bacpy(&addr.l2_bdaddr, ba);
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = htobs(RFCOMM_PSM);
err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0) {
BT_ERR("Bind failed %d", err);
goto failed;
}
/* Set L2CAP options */
size = sizeof(opts);
sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
opts.imtu = RFCOMM_MAX_L2CAP_MTU;
sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
/* Start listening on the socket */
err = sock->ops->listen(sock, 10);
if (err) {
BT_ERR("Listen failed %d", err);
goto failed;
}
/* Add listening session */
s = rfcomm_session_add(sock, BT_LISTEN);
if (!s)
goto failed;
rfcomm_session_hold(s);
return 0;
failed:
sock_release(sock);
return err;
}
static void rfcomm_kill_listener(void)
{
struct rfcomm_session *s;
struct list_head *p, *n;
BT_DBG("");
list_for_each_safe(p, n, &session_list) {
s = list_entry(p, struct rfcomm_session, list);
rfcomm_session_del(s);
}
}
static int rfcomm_run(void *unused)
{
rfcomm_thread = current;
atomic_inc(&running);
daemonize();
set_user_nice(current, -10);
current->flags |= PF_IOTHREAD;
sigfillset(&current->blocked);
flush_signals(current);
sprintf(current->comm, "krfcommd");
set_fs(KERNEL_DS);
BT_DBG("");
rfcomm_add_listener(BDADDR_ANY);
rfcomm_worker();
rfcomm_kill_listener();
atomic_dec(&running);
return 0;
}
int __init rfcomm_init(void)
{
kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
rfcomm_init_sockets();
rfcomm_init_ttys();
BT_INFO("BlueZ RFCOMM ver %s", VERSION);
BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>");
return 0;
}
void rfcomm_cleanup(void)
{
/* Terminate working thread.
* ie. Set terminate flag and wake it up */
atomic_inc(&terminate);
rfcomm_schedule(RFCOMM_SCHED_STATE);
/* Wait until thread is running */
while (atomic_read(&running))
schedule();
rfcomm_cleanup_ttys();
rfcomm_cleanup_sockets();
return;
}
module_init(rfcomm_init);
module_exit(rfcomm_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION);
MODULE_LICENSE("GPL");
/*
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2002 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.
*/
/*
* RFCOMM FCS calculation.
*
* $Id: crc.c,v 1.2 2002/09/21 09:54:32 holtmann Exp $
*/
/* reversed, 8-bit, poly=0x07 */
unsigned char rfcomm_crc_table[256] = {
0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
};
/*
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2002 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.
*/
/*
* RFCOMM sockets.
*
* $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <net/sock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/rfcomm.h>
#ifndef CONFIG_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
static struct proto_ops rfcomm_sock_ops;
static struct bluez_sock_list rfcomm_sk_list = {
lock: RW_LOCK_UNLOCKED
};
static void rfcomm_sock_close(struct sock *sk);
static void rfcomm_sock_kill(struct sock *sk);
/* ---- DLC callbacks ----
*
* called under rfcomm_dlc_lock()
*/
static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
{
struct sock *sk = d->owner;
if (!sk)
return;
atomic_add(skb->len, &sk->rmem_alloc);
skb_queue_tail(&sk->receive_queue, skb);
sk->data_ready(sk, skb->len);
if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
rfcomm_dlc_throttle(d);
return;
}
static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
{
struct sock *sk = d->owner, *parent;
if (!sk)
return;
BT_DBG("dlc %p state %ld err %d", d, d->state, err);
if (err)
sk->err = err;
sk->state = d->state;
parent = bluez_sk(sk)->parent;
if (!parent)
sk->state_change(sk);
else
parent->data_ready(parent, 0);
return;
}
static void rfcomm_sk_modem_status(struct rfcomm_dlc *d, int v24_sig)
{
BT_DBG("dlc %p v24_sig 0x%02x", d, v24_sig);
return;
}
/* ---- Socket functions ---- */
static struct sock *__rfcomm_get_sock_by_addr(int channel, bdaddr_t *src)
{
struct sock *sk;
for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
if (rfcomm_pi(sk)->channel == channel &&
!bacmp(&bluez_sk(sk)->src, src))
break;
}
return sk;
}
/* Find socket with channel and source bdaddr.
* Returns closest match.
*/
static struct sock *__rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
{
struct sock *sk, *sk1 = NULL;
for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
if (state && sk->state != state)
continue;
if (rfcomm_pi(sk)->channel == channel) {
/* Exact match. */
if (!bacmp(&bluez_sk(sk)->src, src))
break;
/* Closest match */
if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY))
sk1 = sk;
}
}
return sk ? sk : sk1;
}
/* Find socket with given address (channel, src).
* Returns locked socket */
static inline struct sock *rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
{
struct sock *s;
read_lock(&rfcomm_sk_list.lock);
s = __rfcomm_get_sock_by_channel(state, channel, src);
if (s) bh_lock_sock(s);
read_unlock(&rfcomm_sk_list.lock);
return s;
}
static void rfcomm_sock_destruct(struct sock *sk)
{
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
BT_DBG("sk %p dlc %p", sk, d);
skb_queue_purge(&sk->receive_queue);
skb_queue_purge(&sk->write_queue);
rfcomm_dlc_lock(d);
rfcomm_pi(sk)->dlc = NULL;
/* Detach DLC if it's owned by this socket */
if (d->owner == sk)
d->owner = NULL;
rfcomm_dlc_unlock(d);
rfcomm_dlc_put(d);
if (sk->protinfo)
kfree(sk->protinfo);
MOD_DEC_USE_COUNT;
}
static void rfcomm_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
BT_DBG("parent %p", parent);
/* Close not yet accepted dlcs */
while ((sk = bluez_accept_dequeue(parent, NULL)))
rfcomm_sock_close(sk);
parent->state = BT_CLOSED;
parent->zapped = 1;
}
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket.
*/
static void rfcomm_sock_kill(struct sock *sk)
{
if (!sk->zapped || sk->socket)
return;
BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt));
/* Kill poor orphan */
bluez_sock_unlink(&rfcomm_sk_list, sk);
sk->dead = 1;
sock_put(sk);
}
/* Close socket.
* Must be called on unlocked socket.
*/
static void rfcomm_sock_close(struct sock *sk)
{
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
lock_sock(sk);
BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
switch (sk->state) {
case BT_LISTEN:
rfcomm_sock_cleanup_listen(sk);
break;
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
case BT_CONNECTED:
rfcomm_dlc_close(d, 0);
default:
sk->zapped = 1;
break;
};
release_sock(sk);
rfcomm_sock_kill(sk);
}
static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
{
BT_DBG("sk %p", sk);
if (parent)
sk->type = parent->type;
}
static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio)
{
struct rfcomm_dlc *d;
struct sock *sk;
sk = bluez_sock_alloc(sock, BTPROTO_RFCOMM, sizeof(struct rfcomm_pinfo), prio);
if (!sk)
return NULL;
d = rfcomm_dlc_alloc(prio);
if (!d) {
sk_free(sk);
return NULL;
}
d->data_ready = rfcomm_sk_data_ready;
d->state_change = rfcomm_sk_state_change;
d->modem_status = rfcomm_sk_modem_status;
rfcomm_pi(sk)->dlc = d;
d->owner = sk;
sk->destruct = rfcomm_sock_destruct;
sk->sndtimeo = RFCOMM_CONN_TIMEOUT;
sk->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
sk->rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
sk->protocol = proto;
sk->state = BT_OPEN;
bluez_sock_link(&rfcomm_sk_list, sk);
BT_DBG("sk %p", sk);
MOD_INC_USE_COUNT;
return sk;
}
static int rfcomm_sock_create(struct socket *sock, int protocol)
{
struct sock *sk;
BT_DBG("sock %p", sock);
sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sock->ops = &rfcomm_sock_ops;
if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL)))
return -ENOMEM;
rfcomm_sock_init(sk, NULL);
return 0;
}
static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
{
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
lock_sock(sk);
if (sk->state != BT_OPEN) {
err = -EBADFD;
goto done;
}
write_lock_bh(&rfcomm_sk_list.lock);
if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
err = -EADDRINUSE;
} else {
/* Save source address */
bacpy(&bluez_sk(sk)->src, &sa->rc_bdaddr);
rfcomm_pi(sk)->channel = sa->rc_channel;
sk->state = BT_BOUND;
}
write_unlock_bh(&rfcomm_sk_list.lock);
done:
release_sock(sk);
return err;
}
static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
{
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
struct sock *sk = sock->sk;
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
int err = 0;
BT_DBG("sk %p", sk);
if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc))
return -EINVAL;
if (sk->state != BT_OPEN && sk->state != BT_BOUND)
return -EBADFD;
if (sk->type != SOCK_STREAM)
return -EINVAL;
lock_sock(sk);
sk->state = BT_CONNECT;
bacpy(&bluez_sk(sk)->dst, &sa->rc_bdaddr);
err = rfcomm_dlc_open(d, &bluez_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
if (!err)
err = bluez_sock_w4_connect(sk, flags);
release_sock(sk);
return err;
}
int rfcomm_sock_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sk %p backlog %d", sk, backlog);
lock_sock(sk);
if (sk->state != BT_BOUND) {
err = -EBADFD;
goto done;
}
sk->max_ack_backlog = backlog;
sk->ack_backlog = 0;
sk->state = BT_LISTEN;
done:
release_sock(sk);
return err;
}
int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
{
DECLARE_WAITQUEUE(wait, current);
struct sock *sk = sock->sk, *nsk;
long timeo;
int err = 0;
lock_sock(sk);
if (sk->state != BT_LISTEN) {
err = -EBADFD;
goto done;
}
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
BT_DBG("sk %p timeo %ld", sk, timeo);
/* Wait for an incoming connection. (wake-one). */
add_wait_queue_exclusive(sk->sleep, &wait);
while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
set_current_state(TASK_INTERRUPTIBLE);
if (!timeo) {
err = -EAGAIN;
break;
}
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
if (sk->state != BT_LISTEN) {
err = -EBADFD;
break;
}
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
}
set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
if (err)
goto done;
newsock->state = SS_CONNECTED;
BT_DBG("new socket %p", nsk);
done:
release_sock(sk);
return err;
}
static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
{
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
sa->rc_family = AF_BLUETOOTH;
sa->rc_channel = rfcomm_pi(sk)->channel;
if (peer)
bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->dst);
else
bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->src);
*len = sizeof(struct sockaddr_rc);
return 0;
}
static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
struct sk_buff *skb;
int err, size;
int sent = 0;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
if (sk->shutdown & SEND_SHUTDOWN)
return -EPIPE;
BT_DBG("sock %p, sk %p", sock, sk);
lock_sock(sk);
while (len) {
size = min_t(uint, len, d->mtu);
skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
break;
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err) {
kfree_skb(skb);
sent = err;
break;
}
err = rfcomm_dlc_send(d, skb);
if (err < 0) {
kfree_skb(skb);
break;
}
sent += size;
len -= size;
}
release_sock(sk);
return sent ? sent : err;
}
static long rfcomm_sock_data_wait(struct sock *sk, long timeo)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(sk->sleep, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) ||
signal_pending(current) || !timeo)
break;
set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return timeo;
}
static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size,
int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
int target, err = 0, copied = 0;
long timeo;
if (sk->state != BT_CONNECTED)
return -EINVAL;
if (flags & MSG_OOB)
return -EOPNOTSUPP;
msg->msg_namelen = 0;
BT_DBG("sk %p size %d", sk, size);
lock_sock(sk);
target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do {
struct sk_buff *skb;
int chunk;
skb = skb_dequeue(&sk->receive_queue);
if (!skb) {
if (copied >= target)
break;
if ((err = sock_error(sk)) != 0)
break;
if (sk->shutdown & RCV_SHUTDOWN)
break;
err = -EAGAIN;
if (!timeo)
break;
timeo = rfcomm_sock_data_wait(sk, timeo);
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
goto out;
}
continue;
}
chunk = min_t(unsigned int, skb->len, size);
if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
skb_queue_head(&sk->receive_queue, skb);
if (!copied)
copied = -EFAULT;
break;
}
copied += chunk;
size -= chunk;
if (!(flags & MSG_PEEK)) {
atomic_sub(chunk, &sk->rmem_alloc);
skb_pull(skb, chunk);
if (skb->len) {
skb_queue_head(&sk->receive_queue, skb);
break;
}
kfree_skb(skb);
} else {
/* put message back and return */
skb_queue_head(&sk->receive_queue, skb);
break;
}
} while (size);
out:
if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2))
rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc);
release_sock(sk);
return copied ? : err;
}
static int rfcomm_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) return 0;
lock_sock(sk);
sk->shutdown = SHUTDOWN_MASK;
if (sk->state == BT_CONNECTED)
rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
release_sock(sk);
return 0;
}
static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sk %p", sk);
lock_sock(sk);
switch (optname) {
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
struct sock *sk = sock->sk;
int len, err = 0;
BT_DBG("sk %p", sk);
if (get_user(len, optlen))
return -EFAULT;
lock_sock(sk);
switch (optname) {
default:
err = -ENOPROTOOPT;
break;
};
release_sock(sk);
return err;
}
static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err;
lock_sock(sk);
#ifdef CONFIG_RFCOMM_TTY
err = rfcomm_dev_ioctl(sk, cmd, arg);
#else
err = -EOPNOTSUPP;
#endif
release_sock(sk);
return err;
}
static int rfcomm_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk)
return 0;
sock_orphan(sk);
rfcomm_sock_close(sk);
return 0;
}
/* ---- RFCOMM core layer callbacks ----
*
* called under rfcomm_lock()
*/
int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d)
{
struct bluez_sock *bsk = bluez_sk(s->sock->sk);
struct sock *sk, *parent;
int result = 0;
BT_DBG("session %p channel %d", s, channel);
/* Check if we have socket listening on channel */
parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &bsk->src);
if (!parent)
return 0;
/* Check for backlog size */
if (parent->ack_backlog > parent->max_ack_backlog) {
BT_DBG("backlog full %d", parent->ack_backlog);
goto done;
}
sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
if (!sk)
goto done;
rfcomm_sock_init(sk, parent);
bacpy(&bluez_sk(sk)->src, &bsk->src);
bacpy(&bluez_sk(sk)->dst, &bsk->dst);
rfcomm_pi(sk)->channel = channel;
sk->state = BT_CONFIG;
bluez_accept_enqueue(parent, sk);
/* Accept connection and return socket DLC */
*d = rfcomm_pi(sk)->dlc;
result = 1;
done:
bh_unlock_sock(parent);
return result;
}
/* ---- Proc fs support ---- */
static int rfcomm_sock_dump(char *buf, struct bluez_sock_list *list)
{
struct rfcomm_pinfo *pi;
struct sock *sk;
char *ptr = buf;
write_lock_bh(&list->lock);
for (sk = list->head; sk; sk = sk->next) {
pi = rfcomm_pi(sk);
ptr += sprintf(ptr, "%s %s %d %d\n",
batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst),
sk->state, rfcomm_pi(sk)->channel);
}
write_unlock_bh(&list->lock);
ptr += sprintf(ptr, "\n");
return ptr - buf;
}
static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
{
char *ptr = buf;
int len;
BT_DBG("count %d, offset %ld", count, offset);
ptr += rfcomm_sock_dump(ptr, &rfcomm_sk_list);
len = ptr - buf;
if (len <= count + offset)
*eof = 1;
*start = buf + offset;
len -= offset;
if (len > count)
len = count;
if (len < 0)
len = 0;
return len;
}
static struct proto_ops rfcomm_sock_ops = {
.family = PF_BLUETOOTH,
.release = rfcomm_sock_release,
.bind = rfcomm_sock_bind,
.connect = rfcomm_sock_connect,
.listen = rfcomm_sock_listen,
.accept = rfcomm_sock_accept,
.getname = rfcomm_sock_getname,
.sendmsg = rfcomm_sock_sendmsg,
.recvmsg = rfcomm_sock_recvmsg,
.shutdown = rfcomm_sock_shutdown,
.setsockopt = rfcomm_sock_setsockopt,
.getsockopt = rfcomm_sock_getsockopt,
.ioctl = rfcomm_sock_ioctl,
.poll = bluez_sock_poll,
.socketpair = sock_no_socketpair,
.mmap = sock_no_mmap
};
static struct net_proto_family rfcomm_sock_family_ops = {
.family = PF_BLUETOOTH,
.create = rfcomm_sock_create
};
int rfcomm_init_sockets(void)
{
int err;
if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) {
BT_ERR("Can't register RFCOMM socket layer");
return err;
}
create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
return 0;
}
void rfcomm_cleanup_sockets(void)
{
int err;
remove_proc_entry("bluetooth/rfcomm", NULL);
/* Unregister socket, protocol and notifier */
if ((err = bluez_sock_unregister(BTPROTO_RFCOMM)))
BT_ERR("Can't unregister RFCOMM socket layer %d", err);
}
/*
RFCOMM implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2002 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.
*/
/*
* RFCOMM TTY.
*
* $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/rfcomm.h>
#ifndef CONFIG_RFCOMM_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */
#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */
#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */
#define RFCOMM_TTY_MINOR 0
struct rfcomm_dev {
struct list_head list;
atomic_t refcnt;
char name[12];
int id;
unsigned long flags;
int opened;
int err;
bdaddr_t src;
bdaddr_t dst;
u8 channel;
struct rfcomm_dlc *dlc;
struct tty_struct *tty;
wait_queue_head_t wait;
struct tasklet_struct wakeup_task;
atomic_t wmem_alloc;
unsigned int sndbuf;
};
static LIST_HEAD(rfcomm_dev_list);
static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig);
static void rfcomm_tty_wakeup(unsigned long arg);
/* ---- Device functions ---- */
static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
{
struct rfcomm_dlc *dlc = dev->dlc;
BT_DBG("dev %p dlc %p", dev, dlc);
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
if (dlc->owner == dev)
dlc->owner = NULL;
rfcomm_dlc_unlock(dlc);
rfcomm_dlc_put(dlc);
kfree(dev);
MOD_DEC_USE_COUNT;
}
static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
{
atomic_inc(&dev->refcnt);
}
static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
{
if (atomic_dec_and_test(&dev->refcnt))
rfcomm_dev_destruct(dev);
}
static struct rfcomm_dev *__rfcomm_dev_get(int id)
{
struct rfcomm_dev *dev;
struct list_head *p;
list_for_each(p, &rfcomm_dev_list) {
dev = list_entry(p, struct rfcomm_dev, list);
if (dev->id == id)
return dev;
}
return NULL;
}
static inline struct rfcomm_dev *rfcomm_dev_get(int id)
{
struct rfcomm_dev *dev;
read_lock(&rfcomm_dev_lock);
dev = __rfcomm_dev_get(id);
read_unlock(&rfcomm_dev_lock);
if (dev) rfcomm_dev_hold(dev);
return dev;
}
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
{
struct rfcomm_dev *dev;
struct list_head *head = &rfcomm_dev_list, *p;
int err = 0;
BT_DBG("id %d channel %d", req->dev_id, req->channel);
dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
memset(dev, 0, sizeof(struct rfcomm_dev));
write_lock_bh(&rfcomm_dev_lock);
if (req->dev_id < 0) {
dev->id = 0;
list_for_each(p, &rfcomm_dev_list) {
if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
break;
dev->id++;
head = p;
}
} else {
dev->id = req->dev_id;
list_for_each(p, &rfcomm_dev_list) {
struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
if (entry->id == dev->id) {
err = -EADDRINUSE;
goto out;
}
if (entry->id > dev->id - 1)
break;
head = p;
}
}
if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) {
err = -ENFILE;
goto out;
}
sprintf(dev->name, "rfcomm%d", dev->id);
list_add(&dev->list, head);
atomic_set(&dev->refcnt, 1);
bacpy(&dev->src, &req->src);
bacpy(&dev->dst, &req->dst);
dev->channel = req->channel;
dev->flags = req->flags &
((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
dev->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
init_waitqueue_head(&dev->wait);
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
rfcomm_dlc_lock(dlc);
dlc->data_ready = rfcomm_dev_data_ready;
dlc->state_change = rfcomm_dev_state_change;
dlc->modem_status = rfcomm_dev_modem_status;
dlc->owner = dev;
dev->dlc = dlc;
rfcomm_dlc_unlock(dlc);
MOD_INC_USE_COUNT;
out:
write_unlock_bh(&rfcomm_dev_lock);
if (err) {
kfree(dev);
return err;
} else
return dev->id;
}
static void rfcomm_dev_del(struct rfcomm_dev *dev)
{
BT_DBG("dev %p", dev);
write_lock_bh(&rfcomm_dev_lock);
list_del_init(&dev->list);
write_unlock_bh(&rfcomm_dev_lock);
rfcomm_dev_put(dev);
}
/* ---- Send buffer ---- */
static void rfcomm_wfree(struct sk_buff *skb)
{
struct rfcomm_dev *dev = (void *) skb->sk;
atomic_sub(skb->truesize, &dev->wmem_alloc);
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
tasklet_schedule(&dev->wakeup_task);
rfcomm_dev_put(dev);
}
static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
{
rfcomm_dev_hold(dev);
atomic_add(skb->truesize, &dev->wmem_alloc);
skb->sk = (void *) dev;
skb->destructor = rfcomm_wfree;
}
static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
{
if (size || atomic_read(&dev->wmem_alloc) < dev->sndbuf) {
struct sk_buff *skb = alloc_skb(size, priority);
if (skb) {
rfcomm_set_owner_w(skb, dev);
return skb;
}
}
return NULL;
}
/* ---- Device IOCTLs ---- */
static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
{
struct rfcomm_dev_req req;
struct rfcomm_dlc *dlc;
int id;
if (copy_from_user(&req, (void *) arg, sizeof(req)))
return -EFAULT;
BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
dlc = rfcomm_pi(sk)->dlc;
rfcomm_dlc_hold(dlc);
} else {
dlc = rfcomm_dlc_alloc(GFP_KERNEL);
if (!dlc)
return -ENOMEM;
}
id = rfcomm_dev_add(&req, dlc);
if (id < 0) {
rfcomm_dlc_put(dlc);
return id;
}
if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
/* DLC is now used by device.
* Socket must be disconnected */
sk->state = BT_CLOSED;
}
return id;
}
static int rfcomm_release_dev(unsigned long arg)
{
struct rfcomm_dev_req req;
struct rfcomm_dev *dev;
if (copy_from_user(&req, (void *) arg, sizeof(req)))
return -EFAULT;
BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
if (!(dev = rfcomm_dev_get(req.dev_id)))
return -ENODEV;
if (req.flags & (1 << RFCOMM_HANGUP_NOW))
rfcomm_dlc_close(dev->dlc, 0);
rfcomm_dev_del(dev);
rfcomm_dev_put(dev);
return 0;
}
static int rfcomm_get_dev_list(unsigned long arg)
{
struct rfcomm_dev_list_req *dl;
struct rfcomm_dev_info *di;
struct list_head *p;
int n = 0, size;
u16 dev_num;
BT_DBG("");
if (get_user(dev_num, (u16 *) arg))
return -EFAULT;
if (!dev_num)
return -EINVAL;
size = sizeof(*dl) + dev_num * sizeof(*di);
if (verify_area(VERIFY_WRITE, (void *)arg, size))
return -EFAULT;
if (!(dl = kmalloc(size, GFP_KERNEL)))
return -ENOMEM;
di = dl->dev_info;
read_lock_bh(&rfcomm_dev_lock);
list_for_each(p, &rfcomm_dev_list) {
struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
(di + n)->id = dev->id;
(di + n)->flags = dev->flags;
(di + n)->state = dev->dlc->state;
(di + n)->channel = dev->channel;
bacpy(&(di + n)->src, &dev->src);
bacpy(&(di + n)->dst, &dev->dst);
if (++n >= dev_num)
break;
}
read_unlock_bh(&rfcomm_dev_lock);
dl->dev_num = n;
size = sizeof(*dl) + n * sizeof(*di);
copy_to_user((void *) arg, dl, size);
kfree(dl);
return 0;
}
static int rfcomm_get_dev_info(unsigned long arg)
{
struct rfcomm_dev *dev;
struct rfcomm_dev_info di;
int err = 0;
BT_DBG("");
if (copy_from_user(&di, (void *)arg, sizeof(di)))
return -EFAULT;
if (!(dev = rfcomm_dev_get(di.id)))
return -ENODEV;
di.flags = dev->flags;
di.channel = dev->channel;
di.state = dev->dlc->state;
bacpy(&di.src, &dev->src);
bacpy(&di.dst, &dev->dst);
if (copy_to_user((void *)arg, &di, sizeof(di)))
err = -EFAULT;
rfcomm_dev_put(dev);
return err;
}
int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
{
BT_DBG("cmd %d arg %ld", cmd, arg);
switch (cmd) {
case RFCOMMCREATEDEV:
return rfcomm_create_dev(sk, arg);
case RFCOMMRELEASEDEV:
return rfcomm_release_dev(arg);
case RFCOMMGETDEVLIST:
return rfcomm_get_dev_list(arg);
case RFCOMMGETDEVINFO:
return rfcomm_get_dev_info(arg);
}
return -EINVAL;
}
/* ---- DLC callbacks ---- */
static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
{
struct rfcomm_dev *dev = dlc->owner;
struct tty_struct *tty;
if (!dev || !(tty = dev->tty)) {
kfree(skb);
return;
}
BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
register int i;
for (i = 0; i < skb->len; i++) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, skb->data[i], 0);
}
tty_flip_buffer_push(tty);
} else
tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
kfree_skb(skb);
}
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
{
struct rfcomm_dev *dev = dlc->owner;
if (!dev)
return;
BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
dev->err = err;
wake_up_interruptible(&dev->wait);
if (dlc->state == BT_CLOSED) {
if (!dev->tty) {
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
rfcomm_dev_hold(dev);
rfcomm_dev_del(dev);
/* We have to drop DLC lock here, otherwise
* rfcomm_dev_put() will dead lock if it's the last refference */
rfcomm_dlc_unlock(dlc);
rfcomm_dev_put(dev);
rfcomm_dlc_lock(dlc);
}
} else
tty_hangup(dev->tty);
}
}
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig)
{
BT_DBG("dlc %p v24_sig 0x%02x", dlc, v24_sig);
}
/* ---- TTY functions ---- */
static void rfcomm_tty_wakeup(unsigned long arg)
{
struct rfcomm_dev *dev = (void *) arg;
struct tty_struct *tty = dev->tty;
if (!tty)
return;
BT_DBG("dev %p tty %p", dev, tty);
if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
#ifdef SERIAL_HAVE_POLL_WAIT
wake_up_interruptible(&tty->poll_wait);
#endif
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
#define __minor MINOR
#else
#define __minor minor
#endif
static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
{
DECLARE_WAITQUEUE(wait, current);
struct rfcomm_dev *dev;
struct rfcomm_dlc *dlc;
int err, id;
id = __minor(tty->device) - tty->driver.minor_start;
BT_DBG("tty %p id %d", tty, id);
dev = rfcomm_dev_get(id);
if (!dev)
return -ENODEV;
BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
if (dev->opened++ != 0)
return 0;
dlc = dev->dlc;
/* Attach TTY and open DLC */
rfcomm_dlc_lock(dlc);
tty->driver_data = dev;
dev->tty = tty;
rfcomm_dlc_unlock(dlc);
set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
if (err < 0)
return err;
/* Wait for DLC to connect */
add_wait_queue(&dev->wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (dlc->state == BT_CLOSED) {
err = -dev->err;
break;
}
if (dlc->state == BT_CONNECTED)
break;
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&dev->wait, &wait);
return err;
}
static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
if (!dev)
return;
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
if (--dev->opened == 0) {
/* Close DLC and dettach TTY */
rfcomm_dlc_close(dev->dlc, 0);
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
tasklet_kill(&dev->wakeup_task);
rfcomm_dlc_lock(dev->dlc);
tty->driver_data = NULL;
dev->tty = NULL;
rfcomm_dlc_unlock(dev->dlc);
}
rfcomm_dev_put(dev);
}
static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
struct rfcomm_dlc *dlc = dev->dlc;
struct sk_buff *skb;
int err = 0, sent = 0, size;
BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
while (count) {
size = min_t(uint, count, dlc->mtu);
if (from_user)
skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
else
skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
if (!skb)
break;
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
if (from_user)
copy_from_user(skb_put(skb, size), buf + sent, size);
else
memcpy(skb_put(skb, size), buf + sent, size);
if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
kfree_skb(skb);
break;
}
sent += size;
count -= size;
}
return sent ? sent : err;
}
static int rfcomm_tty_write_room(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
struct rfcomm_dlc *dlc = dev->dlc;
BT_DBG("tty %p", tty);
return dlc->mtu * (dlc->tx_credits ? : 10);
}
static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
struct rfcomm_dlc *dlc = dev->dlc;
unsigned int modeminfo;
int err;
BT_DBG("tty %p cmd 0x%02x", tty, cmd);
switch (cmd) {
case TCGETS:
BT_DBG("TCGETS is not supported");
return -ENOIOCTLCMD;
case TCSETS:
BT_DBG("TCSETS is not supported");
return -ENOIOCTLCMD;
case TIOCMGET:
BT_DBG("TIOCMGET");
modeminfo = ((dlc->v24_sig & RFCOMM_V24_RTC) ? TIOCM_DSR | TIOCM_DTR : 0)
| ((dlc->v24_sig & RFCOMM_V24_RTR) ? TIOCM_RTS | TIOCM_CTS : 0)
| ((dlc->v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0)
| ((dlc->v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0);
return put_user(modeminfo, (unsigned int *)arg);
case TIOCMBIS:
BT_DBG("TIOCMBIS");
if ((err = get_user(modeminfo, (unsigned int *)arg)))
return err;
dlc->v24_sig |= (modeminfo & TIOCM_DSR) ? RFCOMM_V24_RTC : 0;
dlc->v24_sig |= (modeminfo & TIOCM_DTR) ? RFCOMM_V24_RTC : 0;
dlc->v24_sig |= (modeminfo & TIOCM_RTS) ? RFCOMM_V24_RTR : 0;
dlc->v24_sig |= (modeminfo & TIOCM_CTS) ? RFCOMM_V24_RTR : 0;
dlc->v24_sig |= (modeminfo & TIOCM_RI) ? RFCOMM_V24_IC : 0;
dlc->v24_sig |= (modeminfo & TIOCM_CD) ? RFCOMM_V24_DV : 0;
// rfcomm_send_msc(dlc->session, dlc->dlci, 1, dlc->v24_sig);
return 0;
case TIOCMBIC:
case TIOCMSET:
BT_DBG("set modem info");
break;
case TIOCMIWAIT:
BT_DBG("TIOCMIWAIT");
break;
case TIOCGICOUNT:
BT_DBG("TIOCGICOUNT");
break;
case TIOCGSERIAL:
BT_ERR("TIOCGSERIAL is not supported");
return -ENOIOCTLCMD;
case TIOCSSERIAL:
BT_ERR("TIOCSSERIAL is not supported");
return -ENOIOCTLCMD;
case TIOCSERGSTRUCT:
BT_ERR("TIOCSERGSTRUCT is not supported");
return -ENOIOCTLCMD;
case TIOCSERGETLSR:
BT_ERR("TIOCSERGETLSR is not supported");
return -ENOIOCTLCMD;
case TIOCSERCONFIG:
BT_ERR("TIOCSERCONFIG is not supported");
return -ENOIOCTLCMD;
default:
return -ENOIOCTLCMD; /* ioctls which we must ignore */
}
return -ENOIOCTLCMD;
}
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
{
BT_DBG("tty %p", tty);
if ((tty->termios->c_cflag == old->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
return;
/* handle turning off CRTSCTS */
if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
BT_DBG("turning off CRTSCTS");
}
}
static void rfcomm_tty_throttle(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
BT_DBG("tty %p dev %p", tty, dev);
rfcomm_dlc_throttle(dev->dlc);
}
static void rfcomm_tty_unthrottle(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
BT_DBG("tty %p dev %p", tty, dev);
rfcomm_dlc_unthrottle(dev->dlc);
}
static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
struct rfcomm_dlc *dlc = dev->dlc;
BT_DBG("tty %p dev %p", tty, dev);
if (skb_queue_len(&dlc->tx_queue))
return dlc->mtu;
return 0;
}
static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
if (!dev)
return;
BT_DBG("tty %p dev %p", tty, dev);
skb_queue_purge(&dev->dlc->tx_queue);
if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
tty->ldisc.write_wakeup(tty);
}
static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
{
BT_DBG("tty %p ch %c", tty, ch);
}
static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
{
BT_DBG("tty %p timeout %d", tty, timeout);
}
static void rfcomm_tty_hangup(struct tty_struct *tty)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
if (!dev)
return;
BT_DBG("tty %p dev %p", tty, dev);
rfcomm_tty_flush_buffer(tty);
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
rfcomm_dev_del(dev);
}
static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
{
return 0;
}
/* ---- TTY structure ---- */
static int rfcomm_tty_refcount; /* If we manage several devices */
static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];
static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];
static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];
static struct tty_driver rfcomm_tty_driver = {
.magic = TTY_DRIVER_MAGIC,
.driver_name = "rfcomm",
#ifdef CONFIG_DEVFS_FS
.name = "bluetooth/rfcomm/%d",
#else
.name = "rfcomm%d",
#endif
.major = RFCOMM_TTY_MAJOR,
.minor_start = RFCOMM_TTY_MINOR,
.num = RFCOMM_TTY_PORTS,
.type = TTY_DRIVER_TYPE_SERIAL,
.subtype = SERIAL_TYPE_NORMAL,
.flags = TTY_DRIVER_REAL_RAW,
.refcount = &rfcomm_tty_refcount,
.table = rfcomm_tty_table,
.termios = rfcomm_tty_termios,
.termios_locked = rfcomm_tty_termios_locked,
.open = rfcomm_tty_open,
.close = rfcomm_tty_close,
.write = rfcomm_tty_write,
.write_room = rfcomm_tty_write_room,
.chars_in_buffer = rfcomm_tty_chars_in_buffer,
.flush_buffer = rfcomm_tty_flush_buffer,
.ioctl = rfcomm_tty_ioctl,
.throttle = rfcomm_tty_throttle,
.unthrottle = rfcomm_tty_unthrottle,
.set_termios = rfcomm_tty_set_termios,
.send_xchar = rfcomm_tty_send_xchar,
.stop = NULL,
.start = NULL,
.hangup = rfcomm_tty_hangup,
.wait_until_sent = rfcomm_tty_wait_until_sent,
.read_proc = rfcomm_tty_read_proc,
};
int rfcomm_init_ttys(void)
{
int i;
/* Initalize our global data */
for (i = 0; i < RFCOMM_TTY_PORTS; i++)
rfcomm_tty_table[i] = NULL;
/* Register the TTY driver */
rfcomm_tty_driver.init_termios = tty_std_termios;
rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
if (tty_register_driver(&rfcomm_tty_driver)) {
BT_ERR("Can't register RFCOMM TTY driver");
return -1;
}
return 0;
}
void rfcomm_cleanup_ttys(void)
{
tty_unregister_driver(&rfcomm_tty_driver);
return;
}
......@@ -27,7 +27,7 @@
*
* $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $
*/
#define VERSION "0.2"
#define VERSION "0.3"
#include <linux/config.h>
#include <linux/module.h>
......@@ -67,16 +67,15 @@ static struct bluez_sock_list sco_sk_list = {
.lock = RW_LOCK_UNLOCKED
};
static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
static void sco_chan_del(struct sock *sk, int err);
static inline struct sock * sco_chan_get(struct sco_conn *conn);
static int sco_conn_del(struct hci_conn *conn, int err);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);
/* ----- SCO timers ------ */
/* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
......@@ -115,7 +114,7 @@ static void sco_sock_init_timer(struct sock *sk)
sk->timer.data = (unsigned long)sk;
}
/* -------- SCO connections --------- */
/* ---- SCO connections ---- */
static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
{
struct hci_dev *hdev = hcon->hdev;
......@@ -150,6 +149,15 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
return conn;
}
static inline struct sock *sco_chan_get(struct sco_conn *conn)
{
struct sock *sk = NULL;
sco_conn_lock(conn);
sk = conn->sk;
sco_conn_unlock(conn);
return sk;
}
static int sco_conn_del(struct hci_conn *hcon, int err)
{
struct sco_conn *conn;
......@@ -176,6 +184,20 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
return 0;
}
static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
{
int err = 0;
sco_conn_lock(conn);
if (conn->sk) {
err = -EBUSY;
} else {
__sco_chan_add(conn, sk, parent);
}
sco_conn_unlock(conn);
return err;
}
int sco_connect(struct sock *sk)
{
bdaddr_t *src = &bluez_sk(sk)->src;
......@@ -462,23 +484,20 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
goto done;
}
write_lock(&sco_sk_list.lock);
write_lock_bh(&sco_sk_list.lock);
if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
err = -EADDRINUSE;
goto unlock;
} else {
/* Save source address */
bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
sk->state = BT_BOUND;
}
/* Save source address */
bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
sk->state = BT_BOUND;
unlock:
write_unlock(&sco_sk_list.lock);
write_unlock_bh(&sco_sk_list.lock);
done:
release_sock(sk);
return err;
}
......@@ -649,7 +668,7 @@ int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optva
default:
err = -ENOPROTOOPT;
break;
};
}
release_sock(sk);
return err;
......@@ -703,7 +722,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva
default:
err = -ENOPROTOOPT;
break;
};
}
release_sock(sk);
return err;
......@@ -735,29 +754,6 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *
bluez_accept_enqueue(parent, sk);
}
static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
{
int err = 0;
sco_conn_lock(conn);
if (conn->sk) {
err = -EBUSY;
} else {
__sco_chan_add(conn, sk, parent);
}
sco_conn_unlock(conn);
return err;
}
static inline struct sock * sco_chan_get(struct sco_conn *conn)
{
struct sock *sk = NULL;
sco_conn_lock(conn);
sk = conn->sk;
sco_conn_unlock(conn);
return sk;
}
/* Delete channel.
* Must be called on the locked socket. */
static void sco_chan_del(struct sock *sk, int err)
......@@ -895,7 +891,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
struct sock *sk;
char *ptr = buf;
write_lock(&list->lock);
read_lock_bh(&list->lock);
for (sk = list->head; sk; sk = sk->next) {
pi = sco_pi(sk);
......@@ -904,7 +900,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
sk->state);
}
write_unlock(&list->lock);
read_unlock_bh(&list->lock);
ptr += sprintf(ptr, "\n");
......@@ -936,36 +932,36 @@ static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *
}
static struct proto_ops sco_sock_ops = {
.family = PF_BLUETOOTH,
.release = sco_sock_release,
.bind = sco_sock_bind,
.connect = sco_sock_connect,
.listen = sco_sock_listen,
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll,
.socketpair = sock_no_socketpair,
.ioctl = sock_no_ioctl,
.shutdown = sock_no_shutdown,
.setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt,
.mmap = sock_no_mmap
.family = PF_BLUETOOTH,
.release = sco_sock_release,
.bind = sco_sock_bind,
.connect = sco_sock_connect,
.listen = sco_sock_listen,
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll,
.ioctl = sock_no_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown,
.setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt
};
static struct net_proto_family sco_sock_family_ops = {
.family = PF_BLUETOOTH,
.create = sco_sock_create
.family = PF_BLUETOOTH,
.create = sco_sock_create
};
static struct hci_proto sco_hci_proto = {
.name = "SCO",
.id = HCI_PROTO_SCO,
.connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm,
.disconn_ind = sco_disconn_ind,
.recv_scodata = sco_recv_scodata,
.name = "SCO",
.id = HCI_PROTO_SCO,
.connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm,
.disconn_ind = sco_disconn_ind,
.recv_scodata = sco_recv_scodata
};
int __init sco_init(void)
......
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