Commit b9caaabb authored by David S. Miller's avatar David S. Miller
parents fc57e515 7e743090
......@@ -82,6 +82,8 @@ block/
- info on the Block I/O (BIO) layer.
blockdev/
- info on block devices & drivers
btmrvl.txt
- info on Marvell Bluetooth driver usage.
cachetlb.txt
- describes the cache/TLB flushing interfaces Linux uses.
cdrom/
......
=======================================================================
README for btmrvl driver
=======================================================================
All commands are used via debugfs interface.
=====================
Set/get driver configurations:
Path: /debug/btmrvl/config/
gpiogap=[n]
hscfgcmd
These commands are used to configure the host sleep parameters.
bit 8:0 -- Gap
bit 16:8 -- GPIO
where GPIO is the pin number of GPIO used to wake up the host.
It could be any valid GPIO pin# (e.g. 0-7) or 0xff (SDIO interface
wakeup will be used instead).
where Gap is the gap in milli seconds between wakeup signal and
wakeup event, or 0xff for special host sleep setting.
Usage:
# Use SDIO interface to wake up the host and set GAP to 0x80:
echo 0xff80 > /debug/btmrvl/config/gpiogap
echo 1 > /debug/btmrvl/config/hscfgcmd
# Use GPIO pin #3 to wake up the host and set GAP to 0xff:
echo 0x03ff > /debug/btmrvl/config/gpiogap
echo 1 > /debug/btmrvl/config/hscfgcmd
psmode=[n]
pscmd
These commands are used to enable/disable auto sleep mode
where the option is:
1 -- Enable auto sleep mode
0 -- Disable auto sleep mode
Usage:
# Enable auto sleep mode
echo 1 > /debug/btmrvl/config/psmode
echo 1 > /debug/btmrvl/config/pscmd
# Disable auto sleep mode
echo 0 > /debug/btmrvl/config/psmode
echo 1 > /debug/btmrvl/config/pscmd
hsmode=[n]
hscmd
These commands are used to enable host sleep or wake up firmware
where the option is:
1 -- Enable host sleep
0 -- Wake up firmware
Usage:
# Enable host sleep
echo 1 > /debug/btmrvl/config/hsmode
echo 1 > /debug/btmrvl/config/hscmd
# Wake up firmware
echo 0 > /debug/btmrvl/config/hsmode
echo 1 > /debug/btmrvl/config/hscmd
======================
Get driver status:
Path: /debug/btmrvl/status/
Usage:
cat /debug/btmrvl/status/<args>
where the args are:
curpsmode
This command displays current auto sleep status.
psstate
This command display the power save state.
hsstate
This command display the host sleep state.
txdnldrdy
This command displays the value of Tx download ready flag.
=====================
Use hcitool to issue raw hci command, refer to hcitool manual
Usage: Hcitool cmd <ogf> <ocf> [Parameters]
Interface Control Command
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x00 --Enable All interface
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x01 --Enable Wlan interface
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x02 --Enable BT interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x00 --Disable All interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x01 --Disable Wlan interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x02 --Disable BT interface
=======================================================================
SD8688 firmware:
/lib/firmware/sd8688_helper.bin
/lib/firmware/sd8688.bin
The images can be downloaded from:
git.infradead.org/users/dwmw2/linux-firmware.git/libertas/
......@@ -170,5 +170,30 @@ config BT_HCIVHCI
Say Y here to compile support for virtual HCI devices into the
kernel or say M to compile it as module (hci_vhci).
config BT_MRVL
tristate "Marvell Bluetooth driver support"
help
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
Marvell Bluetooth devices, such as 8688.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
config BT_MRVL_SDIO
tristate "Marvell BT-over-SDIO driver"
depends on BT_MRVL && MMC
select FW_LOADER
help
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently only SD8688 chipset is
supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.
endmenu
......@@ -15,6 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
......
This diff is collapsed.
/*
* Marvell Bluetooth driver: global definitions & declarations
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include <linux/kthread.h>
#include <linux/bitops.h>
#include <net/bluetooth/bluetooth.h>
#define BTM_HEADER_LEN 4
#define BTM_UPLD_SIZE 2312
/* Time to wait until Host Sleep state change in millisecond */
#define WAIT_UNTIL_HS_STATE_CHANGED 5000
/* Time to wait for command response in millisecond */
#define WAIT_UNTIL_CMD_RESP 5000
struct btmrvl_thread {
struct task_struct *task;
wait_queue_head_t wait_q;
void *priv;
};
struct btmrvl_device {
void *card;
struct hci_dev *hcidev;
u8 tx_dnld_rdy;
u8 psmode;
u8 pscmd;
u8 hsmode;
u8 hscmd;
/* Low byte is gap, high byte is GPIO */
u16 gpio_gap;
u8 hscfgcmd;
u8 sendcmdflag;
};
struct btmrvl_adapter {
u32 int_count;
struct sk_buff_head tx_queue;
u8 psmode;
u8 ps_state;
u8 hs_state;
u8 wakeup_tries;
wait_queue_head_t cmd_wait_q;
u8 cmd_complete;
};
struct btmrvl_private {
struct btmrvl_device btmrvl_dev;
struct btmrvl_adapter *adapter;
struct btmrvl_thread main_thread;
int (*hw_host_to_card) (struct btmrvl_private *priv,
u8 *payload, u16 nb);
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
spinlock_t driver_lock; /* spinlock used by driver */
#ifdef CONFIG_DEBUG_FS
void *debugfs_data;
#endif
};
#define MRVL_VENDOR_PKT 0xFE
/* Bluetooth commands */
#define BT_CMD_AUTO_SLEEP_MODE 0x23
#define BT_CMD_HOST_SLEEP_CONFIG 0x59
#define BT_CMD_HOST_SLEEP_ENABLE 0x5A
#define BT_CMD_MODULE_CFG_REQ 0x5B
/* Sub-commands: Module Bringup/Shutdown Request */
#define MODULE_BRINGUP_REQ 0xF1
#define MODULE_SHUTDOWN_REQ 0xF2
#define BT_EVENT_POWER_STATE 0x20
/* Bluetooth Power States */
#define BT_PS_ENABLE 0x02
#define BT_PS_DISABLE 0x03
#define BT_PS_SLEEP 0x01
#define OGF 0x3F
/* Host Sleep states */
#define HS_ACTIVATED 0x01
#define HS_DEACTIVATED 0x00
/* Power Save modes */
#define PS_SLEEP 0x01
#define PS_AWAKE 0x00
struct btmrvl_cmd {
__le16 ocf_ogf;
u8 length;
u8 data[4];
} __attribute__ ((packed));
struct btmrvl_event {
u8 ec; /* event counter */
u8 length;
u8 data[4];
} __attribute__ ((packed));
/* Prototype of global function */
struct btmrvl_private *btmrvl_add_card(void *card);
int btmrvl_remove_card(struct btmrvl_private *priv);
void btmrvl_interrupt(struct btmrvl_private *priv);
void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
int btmrvl_prepare_command(struct btmrvl_private *priv);
#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
void btmrvl_debugfs_remove(struct hci_dev *hdev);
#endif
This diff is collapsed.
This diff is collapsed.
/**
* Marvell BT-over-SDIO driver: SDIO interface related definitions
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
**/
#define SDIO_HEADER_LEN 4
/* SD block size can not bigger than 64 due to buf size limit in firmware */
/* define SD block size for data Tx/Rx */
#define SDIO_BLOCK_SIZE 64
/* Number of blocks for firmware transfer */
#define FIRMWARE_TRANSFER_NBLOCK 2
/* This is for firmware specific length */
#define FW_EXTRA_LEN 36
#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024)
#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
#define ALLOC_BUF_SIZE (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
* SDIO_BLOCK_SIZE)
/* The number of times to try when polling for status */
#define MAX_POLL_TRIES 100
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/* Host Control Registers */
#define IO_PORT_0_REG 0x00
#define IO_PORT_1_REG 0x01
#define IO_PORT_2_REG 0x02
#define CONFIG_REG 0x03
#define HOST_POWER_UP BIT(1)
#define HOST_CMD53_FIN BIT(2)
#define HOST_INT_MASK_REG 0x04
#define HIM_DISABLE 0xff
#define HIM_ENABLE (BIT(0) | BIT(1))
#define HOST_INTSTATUS_REG 0x05
#define UP_LD_HOST_INT_STATUS BIT(0)
#define DN_LD_HOST_INT_STATUS BIT(1)
/* Card Control Registers */
#define SQ_READ_BASE_ADDRESS_A0_REG 0x10
#define SQ_READ_BASE_ADDRESS_A1_REG 0x11
#define CARD_STATUS_REG 0x20
#define DN_LD_CARD_RDY BIT(0)
#define CARD_IO_READY BIT(3)
#define CARD_FW_STATUS0_REG 0x40
#define CARD_FW_STATUS1_REG 0x41
#define FIRMWARE_READY 0xfedc
#define CARD_RX_LEN_REG 0x42
#define CARD_RX_UNIT_REG 0x43
struct btmrvl_sdio_card {
struct sdio_func *func;
u32 ioport;
const char *helper;
const char *firmware;
u8 rx_unit;
struct btmrvl_private *priv;
};
struct btmrvl_sdio_device {
const char *helper;
const char *firmware;
};
/* Platform specific DMA alignment */
#define BTSDIO_DMA_ALIGN 8
/* Macros for Data Alignment : size */
#define ALIGN_SZ(p, a) \
(((p) + ((a) - 1)) & ~((a) - 1))
/* Macros for Data Alignment : address */
#define ALIGN_ADDR(p, a) \
((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
~(((unsigned long)(a)) - 1))
This diff is collapsed.
......@@ -373,8 +373,9 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
i = 0;
skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
if (i++ >= pkts_to_be_removed)
if (i >= pkts_to_be_removed)
break;
i++;
__skb_unlink(skb, &bcsp->unack);
kfree_skb(skb);
......
......@@ -138,8 +138,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock);
struct bt_skb_cb {
__u8 pkt_type;
__u8 incoming;
__u8 tx_seq;
__u8 retries;
__u8 sar;
};
#define bt_cb(skb) ((struct bt_skb_cb *)(skb->cb))
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
{
......
......@@ -117,7 +117,7 @@ struct hci_dev {
struct sk_buff *sent_cmd;
struct sk_buff *reassembly[3];
struct semaphore req_lock;
struct mutex req_lock;
wait_queue_head_t req_wait_q;
__u32 req_status;
__u32 req_result;
......@@ -187,6 +187,7 @@ struct hci_conn {
struct work_struct work_del;
struct device dev;
atomic_t devref;
struct hci_dev *hdev;
void *l2cap_data;
......@@ -339,6 +340,9 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
void hci_conn_enter_active_mode(struct hci_conn *conn);
void hci_conn_enter_sniff_mode(struct hci_conn *conn);
void hci_conn_hold_device(struct hci_conn *conn);
void hci_conn_put_device(struct hci_conn *conn);
static inline void hci_conn_hold(struct hci_conn *conn)
{
atomic_inc(&conn->refcnt);
......@@ -700,8 +704,8 @@ struct hci_sec_filter {
#define HCI_REQ_PEND 1
#define HCI_REQ_CANCELED 2
#define hci_req_lock(d) down(&d->req_lock)
#define hci_req_unlock(d) up(&d->req_lock)
#define hci_req_lock(d) mutex_lock(&d->req_lock)
#define hci_req_unlock(d) mutex_unlock(&d->req_lock)
void hci_req_complete(struct hci_dev *hdev, int result);
......
......@@ -27,12 +27,14 @@
/* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
#define L2CAP_DEFAULT_RX_WINDOW 1
#define L2CAP_DEFAULT_MAX_RECEIVE 1
#define L2CAP_DEFAULT_RETRANS_TO 300 /* 300 milliseconds */
#define L2CAP_DEFAULT_MONITOR_TO 1000 /* 1 second */
#define L2CAP_DEFAULT_MAX_RX_APDU 0xfff7
#define L2CAP_DEFAULT_TX_WINDOW 63
#define L2CAP_DEFAULT_NUM_TO_ACK (L2CAP_DEFAULT_TX_WINDOW/5)
#define L2CAP_DEFAULT_MAX_TX 3
#define L2CAP_DEFAULT_RETRANS_TO 1000 /* 1 second */
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
#define L2CAP_DEFAULT_MAX_PDU_SIZE 672
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
......@@ -52,6 +54,7 @@ struct l2cap_options {
__u16 imtu;
__u16 flush_to;
__u8 mode;
__u8 fcs;
};
#define L2CAP_CONNINFO 0x02
......@@ -93,6 +96,32 @@ struct l2cap_conninfo {
#define L2CAP_FCS_NONE 0x00
#define L2CAP_FCS_CRC16 0x01
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
#define L2CAP_CTRL_REQSEQ 0x3F00
#define L2CAP_CTRL_TXSEQ 0x007E
#define L2CAP_CTRL_RETRANS 0x0080
#define L2CAP_CTRL_FINAL 0x0080
#define L2CAP_CTRL_POLL 0x0010
#define L2CAP_CTRL_SUPERVISE 0x000C
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
#define L2CAP_CTRL_TXSEQ_SHIFT 1
#define L2CAP_CTRL_REQSEQ_SHIFT 8
#define L2CAP_CTRL_SAR_SHIFT 14
/* L2CAP Supervisory Function */
#define L2CAP_SUPER_RCV_READY 0x0000
#define L2CAP_SUPER_REJECT 0x0004
#define L2CAP_SUPER_RCV_NOT_READY 0x0008
#define L2CAP_SUPER_SELECT_REJECT 0x000C
/* L2CAP Segmentation and Reassembly */
#define L2CAP_SDU_UNSEGMENTED 0x0000
#define L2CAP_SDU_START 0x4000
#define L2CAP_SDU_END 0x8000
#define L2CAP_SDU_CONTINUE 0xC000
/* L2CAP structures */
struct l2cap_hdr {
__le16 len;
......@@ -190,7 +219,7 @@ struct l2cap_conf_rfc {
#define L2CAP_MODE_RETRANS 0x01
#define L2CAP_MODE_FLOWCTL 0x02
#define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAM 0x04
#define L2CAP_MODE_STREAMING 0x04
struct l2cap_disconn_req {
__le16 dcid;
......@@ -261,6 +290,14 @@ struct l2cap_conn {
/* ----- L2CAP channel and socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
#define TX_QUEUE(sk) (&l2cap_pi(sk)->tx_queue)
#define SREJ_QUEUE(sk) (&l2cap_pi(sk)->srej_queue)
#define SREJ_LIST(sk) (&l2cap_pi(sk)->srej_l.list)
struct srej_list {
__u8 tx_seq;
struct list_head list;
};
struct l2cap_pinfo {
struct bt_sock bt;
......@@ -271,30 +308,97 @@ struct l2cap_pinfo {
__u16 imtu;
__u16 omtu;
__u16 flush_to;
__u8 sec_level;
__u8 mode;
__u8 num_conf_req;
__u8 num_conf_rsp;
__u8 fcs;
__u8 sec_level;
__u8 role_switch;
__u8 force_reliable;
__u8 force_reliable;
__u8 conf_req[64];
__u8 conf_len;
__u8 conf_state;
__u8 conf_retry;
__u8 conn_state;
__u8 next_tx_seq;
__u8 expected_ack_seq;
__u8 req_seq;
__u8 expected_tx_seq;
__u8 buffer_seq;
__u8 buffer_seq_srej;
__u8 srej_save_reqseq;
__u8 unacked_frames;
__u8 retry_count;
__u8 num_to_ack;
__u16 sdu_len;
__u16 partial_sdu_len;
struct sk_buff *sdu;
__u8 ident;
__u8 remote_tx_win;
__u8 remote_max_tx;
__u16 retrans_timeout;
__u16 monitor_timeout;
__u16 max_pdu_size;
__le16 sport;
struct timer_list retrans_timer;
struct timer_list monitor_timer;
struct sk_buff_head tx_queue;
struct sk_buff_head srej_queue;
struct srej_list srej_l;
struct l2cap_conn *conn;
struct sock *next_c;
struct sock *prev_c;
};
#define L2CAP_CONF_REQ_SENT 0x01
#define L2CAP_CONF_INPUT_DONE 0x02
#define L2CAP_CONF_OUTPUT_DONE 0x04
#define L2CAP_CONF_CONNECT_PEND 0x80
#define L2CAP_CONF_MAX_RETRIES 2
#define L2CAP_CONF_REQ_SENT 0x01
#define L2CAP_CONF_INPUT_DONE 0x02
#define L2CAP_CONF_OUTPUT_DONE 0x04
#define L2CAP_CONF_MTU_DONE 0x08
#define L2CAP_CONF_MODE_DONE 0x10
#define L2CAP_CONF_CONNECT_PEND 0x20
#define L2CAP_CONF_NO_FCS_RECV 0x40
#define L2CAP_CONF_STATE2_DEVICE 0x80
#define L2CAP_CONF_MAX_CONF_REQ 2
#define L2CAP_CONF_MAX_CONF_RSP 2
#define L2CAP_CONN_SAR_SDU 0x01
#define L2CAP_CONN_SREJ_SENT 0x02
#define L2CAP_CONN_WAIT_F 0x04
#define L2CAP_CONN_SREJ_ACT 0x08
#define L2CAP_CONN_SEND_PBIT 0x10
#define L2CAP_CONN_REMOTE_BUSY 0x20
#define L2CAP_CONN_LOCAL_BUSY 0x40
#define __mod_retrans_timer() mod_timer(&l2cap_pi(sk)->retrans_timer, \
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
#define __mod_monitor_timer() mod_timer(&l2cap_pi(sk)->monitor_timer, \
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
static inline int l2cap_tx_window_full(struct sock *sk)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
int sub;
sub = (pi->next_tx_seq - pi->expected_ack_seq) % 64;
if (sub < 0)
sub += 64;
return (sub == pi->remote_tx_win);
}
#define __get_txseq(ctrl) ((ctrl) & L2CAP_CTRL_TXSEQ) >> 1
#define __get_reqseq(ctrl) ((ctrl) & L2CAP_CTRL_REQSEQ) >> 8
#define __is_iframe(ctrl) !((ctrl) & L2CAP_CTRL_FRAME_TYPE)
#define __is_sframe(ctrl) (ctrl) & L2CAP_CTRL_FRAME_TYPE
#define __is_sar_start(ctrl) ((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START
void l2cap_load(void);
......
......@@ -29,6 +29,7 @@
#define RFCOMM_CONN_TIMEOUT (HZ * 30)
#define RFCOMM_DISC_TIMEOUT (HZ * 20)
#define RFCOMM_AUTH_TIMEOUT (HZ * 25)
#define RFCOMM_IDLE_TIMEOUT (HZ * 2)
#define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDITS 7
......@@ -154,6 +155,7 @@ struct rfcomm_msc {
struct rfcomm_session {
struct list_head list;
struct socket *sock;
struct timer_list timer;
unsigned long state;
unsigned long flags;
atomic_t refcnt;
......
......@@ -34,6 +34,7 @@ menuconfig BT
config BT_L2CAP
tristate "L2CAP protocol support"
depends on BT
select CRC16
help
L2CAP (Logical Link Control and Adaptation Protocol) provides
connection oriented and connection-less data transport. L2CAP
......
......@@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
atomic_set(&conn->devref, 0);
hci_conn_init_sysfs(conn);
tasklet_enable(&hdev->tx_task);
......@@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn)
skb_queue_purge(&conn->data_q);
hci_conn_del_sysfs(conn);
hci_conn_put_device(conn);
hci_dev_put(hdev);
......@@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev)
hci_dev_unlock(hdev);
}
void hci_conn_hold_device(struct hci_conn *conn)
{
atomic_inc(&conn->devref);
}
EXPORT_SYMBOL(hci_conn_hold_device);
void hci_conn_put_device(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->devref))
hci_conn_del_sysfs(conn);
}
EXPORT_SYMBOL(hci_conn_put_device);
int hci_get_conn_list(void __user *arg)
{
struct hci_conn_list_req req, *cl;
......
......@@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->reassembly[i] = NULL;
init_waitqueue_head(&hdev->req_wait_q);
init_MUTEX(&hdev->req_lock);
mutex_init(&hdev->req_lock);
inquiry_cache_init(hdev);
......
......@@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
} else
conn->state = BT_CONNECTED;
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags))
......@@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
break;
......
......@@ -40,6 +40,7 @@
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
......@@ -92,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session)
{
__module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list);
hci_conn_hold_device(session->conn);
}
static void __hidp_unlink_session(struct hidp_session *session)
{
hci_conn_put_device(session->conn);
list_del(&session->list);
module_put(THIS_MODULE);
}
......@@ -374,6 +379,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
/* Kill session thread */
atomic_inc(&session->terminate);
hidp_schedule(session);
}
}
......@@ -573,7 +579,11 @@ static int hidp_session(void *arg)
if (session->hid) {
if (session->hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(session->hid);
if (session->hid->claimed & HID_CLAIMED_HIDRAW)
hidraw_disconnect(session->hid);
hid_destroy_device(session->hid);
session->hid = NULL;
}
/* Wakeup user-space polling for socket errors */
......@@ -601,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session)
{
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
struct device *device = NULL;
struct hci_dev *hdev;
struct hci_conn *conn;
hdev = hci_get_route(dst, src);
if (!hdev)
return NULL;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (session->conn)
device = &session->conn->dev;
hci_dev_put(hdev);
return conn ? &conn->dev : NULL;
return device;
}
static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req)
{
struct input_dev *input;
int i;
int err, i;
input = input_allocate_device();
if (!input)
......@@ -666,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session,
input->event = hidp_input_event;
return input_register_device(input);
err = input_register_device(input);
if (err < 0) {
hci_conn_put_device(session->conn);
return err;
}
return 0;
}
static int hidp_open(struct hid_device *hid)
......@@ -748,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session,
{
struct hid_device *hid;
bdaddr_t src, dst;
int ret;
int err;
hid = hid_allocate_device();
if (IS_ERR(hid)) {
ret = PTR_ERR(session->hid);
goto err;
}
if (IS_ERR(hid))
return PTR_ERR(session->hid);
session->hid = hid;
session->req = req;
......@@ -776,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = hidp_get_device(session);
hid->ll_driver = &hidp_hid_driver;
ret = hid_add_device(hid);
if (ret)
goto err_hid;
err = hid_add_device(hid);
if (err < 0)
goto failed;
return 0;
err_hid:
failed:
hid_destroy_device(hid);
session->hid = NULL;
err:
return ret;
return err;
}
int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
......@@ -835,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
if (req->rd_size > 0) {
err = hidp_setup_hid(session, req);
if (err && err != -ENODEV)
goto err_skb;
goto purge;
}
if (!session->hid) {
err = hidp_setup_input(session, req);
if (err < 0)
goto err_skb;
goto purge;
}
__hidp_link_session(session);
......@@ -869,13 +886,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
__hidp_unlink_session(session);
if (session->input)
if (session->input) {
input_unregister_device(session->input);
if (session->hid)
session->input = NULL;
}
if (session->hid) {
hid_destroy_device(session->hid);
err_skb:
session->hid = NULL;
}
purge:
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
failed:
up_write(&hidp_session_sem);
......
......@@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci);
struct hidp_session {
struct list_head list;
struct hci_conn *conn;
struct socket *ctrl_sock;
struct socket *intr_sock;
......
This diff is collapsed.
......@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
auth_type);
}
static void rfcomm_session_timeout(unsigned long arg)
{
struct rfcomm_session *s = (void *) arg;
BT_DBG("session %p state %ld", s, s->state);
set_bit(RFCOMM_TIMED_OUT, &s->flags);
rfcomm_session_put(s);
rfcomm_schedule(RFCOMM_SCHED_TIMEO);
}
static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
{
BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
if (!mod_timer(&s->timer, jiffies + timeout))
rfcomm_session_hold(s);
}
static void rfcomm_session_clear_timer(struct rfcomm_session *s)
{
BT_DBG("session %p state %ld", s, s->state);
if (timer_pending(&s->timer) && del_timer(&s->timer))
rfcomm_session_put(s);
}
/* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg)
{
......@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
rfcomm_session_hold(s);
rfcomm_session_clear_timer(s);
rfcomm_dlc_hold(d);
list_add(&d->list, &s->dlcs);
d->session = s;
......@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
d->session = NULL;
rfcomm_dlc_put(d);
if (list_empty(&s->dlcs))
rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
rfcomm_session_put(s);
}
......@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
BT_DBG("session %p sock %p", s, sock);
setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
INIT_LIST_HEAD(&s->dlcs);
s->state = state;
s->sock = sock;
......@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
if (state == BT_CONNECTED)
rfcomm_send_disc(s, 0);
rfcomm_session_clear_timer(s);
sock_release(s->sock);
kfree(s);
......@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
__rfcomm_dlc_close(d, err);
}
rfcomm_session_clear_timer(s);
rfcomm_session_put(s);
}
......@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
struct rfcomm_session *s;
s = list_entry(p, struct rfcomm_session, list);
if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
s->state = BT_DISCONN;
rfcomm_send_disc(s, 0);
continue;
}
if (s->state == BT_LISTEN) {
rfcomm_accept_connection(s);
continue;
......@@ -2080,7 +2121,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
/* ---- Initialization ---- */
static int __init rfcomm_init(void)
{
int ret;
int err;
l2cap_load();
......@@ -2088,33 +2129,35 @@ static int __init rfcomm_init(void)
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
if (IS_ERR(rfcomm_thread)) {
ret = PTR_ERR(rfcomm_thread);
goto out_thread;
err = PTR_ERR(rfcomm_thread);
goto unregister;
}
if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
BT_ERR("Failed to create RFCOMM info file");
ret = rfcomm_init_ttys();
if (ret)
goto out_tty;
err = rfcomm_init_ttys();
if (err < 0)
goto stop;
ret = rfcomm_init_sockets();
if (ret)
goto out_sock;
err = rfcomm_init_sockets();
if (err < 0)
goto cleanup;
BT_INFO("RFCOMM ver %s", VERSION);
return 0;
out_sock:
cleanup:
rfcomm_cleanup_ttys();
out_tty:
stop:
kthread_stop(rfcomm_thread);
out_thread:
unregister:
hci_unregister_cb(&rfcomm_cb);
return ret;
return err;
}
static void __exit rfcomm_exit(void)
......
......@@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk)
sock_put(sk);
}
/* Close socket.
* Must be called on unlocked socket.
*/
static void sco_sock_close(struct sock *sk)
static void __sco_sock_close(struct sock *sk)
{
struct sco_conn *conn;
sco_sock_clear_timer(sk);
lock_sock(sk);
conn = sco_pi(sk)->conn;
BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
switch (sk->sk_state) {
case BT_LISTEN:
......@@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk)
sock_set_flag(sk, SOCK_ZAPPED);
break;
}
}
/* Must be called on unlocked socket. */
static void sco_sock_close(struct sock *sk)
{
sco_sock_clear_timer(sk);
lock_sock(sk);
__sco_sock_close(sk);
release_sock(sk);
sco_sock_kill(sk);
}
......@@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
return err;
}
static int sco_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk)
return 0;
lock_sock(sk);
if (!sk->sk_shutdown) {
sk->sk_shutdown = SHUTDOWN_MASK;
sco_sock_clear_timer(sk);
__sco_sock_close(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
release_sock(sk);
return err;
}
static int sco_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
......@@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = {
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown,
.shutdown = sco_sock_shutdown,
.setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt
};
......
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