Commit c5e6d236 authored by Maksim Krasnyanskiy's avatar Maksim Krasnyanskiy Committed by Maksim Krasnyanskiy

RFCOMM protocol support.

RFCOMM socket and TTY emulation APIs.
parent 5c173c66
/*
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 ...@@ -10,7 +10,9 @@ CONFIG_BLUEZ
BlueZ Core (HCI device and connection manager, scheduler) BlueZ Core (HCI device and connection manager, scheduler)
HCI Device drivers (interface to the hardware) HCI Device drivers (interface to the hardware)
L2CAP Module (L2CAP protocol) L2CAP Module (L2CAP protocol)
RFCOMM Module (RFCOMM protocol)
SCO Module (SCO links) SCO Module (SCO links)
BNEP Module (BNEP protocol)
Say Y here to enable Linux Bluetooth support and to build BlueZ Core Say Y here to enable Linux Bluetooth support and to build BlueZ Core
layer. layer.
...@@ -31,6 +33,19 @@ CONFIG_BLUEZ_L2CAP ...@@ -31,6 +33,19 @@ CONFIG_BLUEZ_L2CAP
Say Y here to compile L2CAP support into the kernel or say M to Say Y here to compile L2CAP support into the kernel or say M to
compile it as module (l2cap.o). 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 SCO links support
CONFIG_BLUEZ_SCO CONFIG_BLUEZ_SCO
SCO link provides voice transport over Bluetooth. SCO support is SCO link provides voice transport over Bluetooth. SCO support is
......
...@@ -10,6 +10,7 @@ if [ "$CONFIG_NET" != "n" ]; then ...@@ -10,6 +10,7 @@ if [ "$CONFIG_NET" != "n" ]; then
if [ "$CONFIG_BLUEZ" != "n" ]; then if [ "$CONFIG_BLUEZ" != "n" ]; then
dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $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 net/bluetooth/bnep/Config.in
source drivers/bluetooth/Config.in source drivers/bluetooth/Config.in
fi fi
......
...@@ -14,6 +14,12 @@ ifeq ($(CONFIG_BLUEZ_BNEP),y) ...@@ -14,6 +14,12 @@ ifeq ($(CONFIG_BLUEZ_BNEP),y)
obj-y += bnep/bnep.o obj-y += bnep/bnep.o
endif 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 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 include $(TOPDIR)/Rules.make
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;
}
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