diff -Naur linux-2.4.20-18.8/arch/sparc64/kernel/ioctl32.c linux-2.4.20-18.8-bt/arch/sparc64/kernel/ioctl32.c --- linux-2.4.20-18.8/arch/sparc64/kernel/ioctl32.c 2003-05-29 06:45:02.000000000 -0400 +++ linux-2.4.20-18.8-bt/arch/sparc64/kernel/ioctl32.c 2003-07-08 07:37:11.000000000 -0400 @@ -4296,6 +4296,11 @@ #define BNEPGETCONNLIST _IOR('B', 210, int) #define BNEPGETCONNINFO _IOR('B', 211, int) +#define CMTPCONNADD _IOW('C', 200, int) +#define CMTPCONNDEL _IOW('C', 201, int) +#define CMTPGETCONNLIST _IOR('C', 210, int) +#define CMTPGETCONNINFO _IOR('C', 211, int) + struct ioctl_trans { unsigned int cmd; unsigned int handler; @@ -5008,6 +5013,10 @@ COMPATIBLE_IOCTL(BNEPCONNDEL) COMPATIBLE_IOCTL(BNEPGETCONNLIST) COMPATIBLE_IOCTL(BNEPGETCONNINFO) +COMPATIBLE_IOCTL(CMTPCONNADD) +COMPATIBLE_IOCTL(CMTPCONNDEL) +COMPATIBLE_IOCTL(CMTPGETCONNLIST) +COMPATIBLE_IOCTL(CMTPGETCONNINFO) /* Misc. */ COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ diff -Naur linux-2.4.20-18.8/Documentation/Configure.help linux-2.4.20-18.8-bt/Documentation/Configure.help --- linux-2.4.20-18.8/Documentation/Configure.help 2003-05-29 06:46:38.000000000 -0400 +++ linux-2.4.20-18.8-bt/Documentation/Configure.help 2003-07-08 07:34:31.000000000 -0400 @@ -21424,6 +21424,7 @@ SCO Module (SCO links) RFCOMM Module (RFCOMM protocol) BNEP Module (BNEP protocol) + CMTP Module (CMTP protocol) Say Y here to enable Linux Bluetooth support and to build BlueZ Core layer. @@ -21478,6 +21479,15 @@ Say Y here to compile BNEP support into the kernel or say M to compile it as module (bnep.o). +CMTP protocol support +CONFIG_BLUEZ_CMTP + CMTP (CAPI Message Transport Protocol) is a transport layer + for CAPI messages. CMTP is required for the Bluetooth Common + ISDN Access Profile. + + Say Y here to compile CMTP support into the kernel or say M to + compile it as module (cmtp.o). + BNEP multicast filter support CONFIG_BLUEZ_BNEP_MC_FILTER This option enables the multicast filter support for BNEP. diff -Naur linux-2.4.20-18.8/drivers/bluetooth/hci_usb.c linux-2.4.20-18.8-bt/drivers/bluetooth/hci_usb.c --- linux-2.4.20-18.8/drivers/bluetooth/hci_usb.c 2003-05-29 06:45:39.000000000 -0400 +++ linux-2.4.20-18.8-bt/drivers/bluetooth/hci_usb.c 2003-07-08 07:34:31.000000000 -0400 @@ -301,7 +301,8 @@ hci_usb_bulk_rx_submit(husb); #ifdef CONFIG_BLUEZ_USB_SCO - hci_usb_isoc_rx_submit(husb); + if (husb->isoc_iface) + hci_usb_isoc_rx_submit(husb); #endif } else { clear_bit(HCI_RUNNING, &hdev->flags); diff -Naur linux-2.4.20-18.8/include/net/bluetooth/bluetooth.h linux-2.4.20-18.8-bt/include/net/bluetooth/bluetooth.h --- linux-2.4.20-18.8/include/net/bluetooth/bluetooth.h 2003-05-29 08:57:57.000000000 -0400 +++ linux-2.4.20-18.8-bt/include/net/bluetooth/bluetooth.h 2003-07-08 07:34:31.000000000 -0400 @@ -51,6 +51,7 @@ #define BTPROTO_SCO 2 #define BTPROTO_RFCOMM 3 #define BTPROTO_BNEP 4 +#define BTPROTO_CMTP 5 #define SOL_HCI 0 #define SOL_L2CAP 6 @@ -155,7 +156,7 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s); int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait); -int bluez_sock_w4_connect(struct sock *sk, int flags); +int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo); void bluez_accept_enqueue(struct sock *parent, struct sock *sk); struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock); diff -Naur linux-2.4.20-18.8/include/net/bluetooth/hci.h linux-2.4.20-18.8-bt/include/net/bluetooth/hci.h --- linux-2.4.20-18.8/include/net/bluetooth/hci.h 2003-05-29 06:45:54.000000000 -0400 +++ linux-2.4.20-18.8-bt/include/net/bluetooth/hci.h 2003-07-08 07:34:31.000000000 -0400 @@ -160,6 +160,7 @@ #define HCI_LM_AUTH 0x0002 #define HCI_LM_ENCRYPT 0x0004 #define HCI_LM_TRUSTED 0x0008 +#define HCI_LM_RELIABLE 0x0010 /* ----- HCI Commands ----- */ /* OGF & OCF values */ diff -Naur linux-2.4.20-18.8/include/net/bluetooth/l2cap.h linux-2.4.20-18.8-bt/include/net/bluetooth/l2cap.h --- linux-2.4.20-18.8/include/net/bluetooth/l2cap.h 2002-08-02 20:39:46.000000000 -0400 +++ linux-2.4.20-18.8-bt/include/net/bluetooth/l2cap.h 2003-07-08 07:34:31.000000000 -0400 @@ -60,6 +60,7 @@ #define L2CAP_LM_AUTH 0x0002 #define L2CAP_LM_ENCRYPT 0x0004 #define L2CAP_LM_TRUSTED 0x0008 +#define L2CAP_LM_RELIABLE 0x0010 #define L2CAP_QOS 0x04 struct l2cap_qos { @@ -229,6 +230,7 @@ __u32 link_mode; __u8 conf_state; + __u8 conf_retry; __u16 conf_mtu; __u8 ident; @@ -238,8 +240,9 @@ struct sock *prev_c; }; -#define CONF_REQ_SENT 0x01 -#define CONF_INPUT_DONE 0x02 -#define CONF_OUTPUT_DONE 0x04 +#define L2CAP_CONF_REQ_SENT 0x01 +#define L2CAP_CONF_INPUT_DONE 0x02 +#define L2CAP_CONF_OUTPUT_DONE 0x04 +#define L2CAP_CONF_MAX_RETRIES 2 #endif /* __L2CAP_H */ diff -Naur linux-2.4.20-18.8/include/net/bluetooth/rfcomm.h linux-2.4.20-18.8-bt/include/net/bluetooth/rfcomm.h --- linux-2.4.20-18.8/include/net/bluetooth/rfcomm.h 2003-05-29 06:46:23.000000000 -0400 +++ linux-2.4.20-18.8-bt/include/net/bluetooth/rfcomm.h 2003-07-08 07:34:31.000000000 -0400 @@ -185,10 +185,11 @@ atomic_t refcnt; u8 dlci; u8 addr; - - uint mtu; + u8 priority; u8 v24_sig; + u8 mscex; + uint mtu; uint credits; uint rx_credits; uint tx_credits; @@ -213,6 +214,11 @@ #define RFCOMM_SCHED_TIMEO 3 #define RFCOMM_SCHED_WAKEUP 31 +/* MSC exchange flags */ +#define RFCOMM_MSCEX_TX 1 +#define RFCOMM_MSCEX_RX 2 +#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX) + extern struct task_struct *rfcomm_thread; extern unsigned long rfcomm_event; diff -Naur linux-2.4.20-18.8/net/bluetooth/af_bluetooth.c linux-2.4.20-18.8-bt/net/bluetooth/af_bluetooth.c --- linux-2.4.20-18.8/net/bluetooth/af_bluetooth.c 2003-05-29 06:46:23.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/af_bluetooth.c 2003-07-08 07:34:31.000000000 -0400 @@ -27,7 +27,7 @@ * * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $ */ -#define VERSION "2.2" +#define VERSION "2.3" #include #include @@ -253,39 +253,35 @@ return mask; } -int bluez_sock_w4_connect(struct sock *sk, int flags) +int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo) { DECLARE_WAITQUEUE(wait, current); - long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); int err = 0; BT_DBG("sk %p", sk); add_wait_queue(sk->sleep, &wait); - while (sk->state != BT_CONNECTED) { + while (sk->state != state) { set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { err = -EAGAIN; break; } + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); - err = 0; - if (sk->state == BT_CONNECTED) - break; - if (sk->err) { err = sock_error(sk); break; } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } } set_current_state(TASK_RUNNING); remove_wait_queue(sk->sleep, &wait); diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/capi.c linux-2.4.20-18.8-bt/net/bluetooth/cmtp/capi.c --- linux-2.4.20-18.8/net/bluetooth/cmtp/capi.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/capi.c 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,707 @@ +/* + CMTP implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002-2003 Marcel Holtmann + + 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. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../drivers/isdn/avmb1/capilli.h" +#include "../drivers/isdn/avmb1/capicmd.h" +#include "../drivers/isdn/avmb1/capiutil.h" + +#include "cmtp.h" + +#ifndef CONFIG_BLUEZ_CMTP_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define REVISION "1.0" + +#define CAPI_INTEROPERABILITY 0x20 + +#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) +#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) +#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) +#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) + +#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) +#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) +#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) +#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) + +#define CAPI_FUNCTION_REGISTER 0 +#define CAPI_FUNCTION_RELEASE 1 +#define CAPI_FUNCTION_GET_PROFILE 2 +#define CAPI_FUNCTION_GET_MANUFACTURER 3 +#define CAPI_FUNCTION_GET_VERSION 4 +#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 +#define CAPI_FUNCTION_MANUFACTURER 6 +#define CAPI_FUNCTION_LOOPBACK 7 + +static struct capi_driver_interface *di; + + +#define CMTP_MSGNUM 1 +#define CMTP_APPLID 2 +#define CMTP_MAPPING 3 + +static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) +{ + struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL); + + BT_DBG("session %p application %p appl %d", session, app, appl); + + if (!app) + return NULL; + + memset(app, 0, sizeof(*app)); + + app->state = BT_OPEN; + app->appl = appl; + + list_add_tail(&app->list, &session->applications); + + return app; +} + +static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) +{ + BT_DBG("session %p application %p", session, app); + + if (app) { + list_del(&app->list); + kfree(app); + } +} + +static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) +{ + struct cmtp_application *app; + struct list_head *p, *n; + + list_for_each_safe(p, n, &session->applications) { + app = list_entry(p, struct cmtp_application, list); + switch (pattern) { + case CMTP_MSGNUM: + if (app->msgnum == value) + return app; + break; + case CMTP_APPLID: + if (app->appl == value) + return app; + break; + case CMTP_MAPPING: + if (app->mapping == value) + return app; + break; + } + } + + return NULL; +} + +static int cmtp_msgnum_get(struct cmtp_session *session) +{ + session->msgnum++; + + if ((session->msgnum & 0xff) > 200) + session->msgnum = CMTP_INITIAL_MSGNUM + 1; + + return session->msgnum; +} + + +static void cmtp_send_interopmsg(struct cmtp_session *session, + __u8 subcmd, __u16 appl, __u16 msgnum, + __u16 function, unsigned char *buf, int len) +{ + struct sk_buff *skb; + unsigned char *s; + + BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); + + if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for interoperability packet"); + return; + } + + s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); + + capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); + capimsg_setu16(s, 2, appl); + capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); + capimsg_setu8 (s, 5, subcmd); + capimsg_setu16(s, 6, msgnum); + + /* Interoperability selector (Bluetooth Device Management) */ + capimsg_setu16(s, 8, 0x0001); + + capimsg_setu8 (s, 10, 3 + len); + capimsg_setu16(s, 11, function); + capimsg_setu8 (s, 13, len); + + if (len > 0) + memcpy(s + 14, buf, len); + + cmtp_send_capimsg(session, skb); +} + +static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) +{ + struct capi_ctr *ctrl = session->ctrl; + struct cmtp_application *application; + __u16 appl, msgnum, func, info; + __u32 controller; + + BT_DBG("session %p skb %p len %d", session, skb, skb->len); + + switch (CAPIMSG_SUBCOMMAND(skb->data)) { + case CAPI_CONF: + func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); + info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); + + switch (func) { + case CAPI_FUNCTION_REGISTER: + msgnum = CAPIMSG_MSGID(skb->data); + + application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); + if (application) { + application->state = BT_CONNECTED; + application->msgnum = 0; + application->mapping = CAPIMSG_APPID(skb->data); + wake_up_interruptible(&session->wait); + } + + break; + + case CAPI_FUNCTION_RELEASE: + appl = CAPIMSG_APPID(skb->data); + + application = cmtp_application_get(session, CMTP_MAPPING, appl); + if (application) { + application->state = BT_CLOSED; + application->msgnum = 0; + wake_up_interruptible(&session->wait); + } + + break; + + case CAPI_FUNCTION_GET_PROFILE: + controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); + msgnum = CAPIMSG_MSGID(skb->data); + + if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { + session->ncontroller = controller; + wake_up_interruptible(&session->wait); + break; + } + + if (!info && ctrl) { + memcpy(&ctrl->profile, + skb->data + CAPI_MSG_BASELEN + 11, + sizeof(capi_profile)); + session->state = BT_CONNECTED; + ctrl->ready(ctrl); + } + + break; + + case CAPI_FUNCTION_GET_MANUFACTURER: + controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); + + if (!info && ctrl) { + strncpy(ctrl->manu, + skb->data + CAPI_MSG_BASELEN + 15, + skb->data[CAPI_MSG_BASELEN + 14]); + } + + break; + + case CAPI_FUNCTION_GET_VERSION: + controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); + + if (!info && ctrl) { + ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); + ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); + ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); + ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); + } + + break; + + case CAPI_FUNCTION_GET_SERIAL_NUMBER: + controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); + + if (!info && ctrl) { + memset(ctrl->serial, 0, CAPI_SERIAL_LEN); + strncpy(ctrl->serial, + skb->data + CAPI_MSG_BASELEN + 17, + skb->data[CAPI_MSG_BASELEN + 16]); + } + + break; + } + + break; + + case CAPI_IND: + func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); + + if (func == CAPI_FUNCTION_LOOPBACK) { + appl = CAPIMSG_APPID(skb->data); + msgnum = CAPIMSG_MSGID(skb->data); + cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, + skb->data + CAPI_MSG_BASELEN + 6, + skb->data[CAPI_MSG_BASELEN + 5]); + } + + break; + } + + kfree_skb(skb); +} + +void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) +{ + struct capi_ctr *ctrl = session->ctrl; + struct cmtp_application *application; + __u16 cmd, appl, info; + __u32 ncci, contr; + + BT_DBG("session %p skb %p len %d", session, skb, skb->len); + + if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { + cmtp_recv_interopmsg(session, skb); + return; + } + + if (session->flags & (1 << CMTP_LOOPBACK)) { + kfree_skb(skb); + return; + } + + cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); + appl = CAPIMSG_APPID(skb->data); + contr = CAPIMSG_CONTROL(skb->data); + + application = cmtp_application_get(session, CMTP_MAPPING, appl); + if (application) { + appl = application->appl; + CAPIMSG_SETAPPID(skb->data, appl); + } else { + BT_ERR("Can't find application with id %d", appl); + kfree_skb(skb); + return; + } + + if ((contr & 0x7f) == 0x01) { + contr = (contr & 0xffffff80) | session->num; + CAPIMSG_SETCONTROL(skb->data, contr); + } + + if (!ctrl) { + BT_ERR("Can't find controller %d for message", session->num); + kfree_skb(skb); + return; + } + + switch (cmd) { + case CAPI_CONNECT_B3_CONF: + ncci = CAPIMSG_NCCI(skb->data); + info = CAPIMSG_U16(skb->data, 12); + + BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info); + + if (info == 0) + ctrl->new_ncci(ctrl, appl, ncci, 8); + + ctrl->handle_capimsg(ctrl, appl, skb); + break; + + case CAPI_CONNECT_B3_IND: + ncci = CAPIMSG_NCCI(skb->data); + + BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci); + + ctrl->new_ncci(ctrl, appl, ncci, 8); + ctrl->handle_capimsg(ctrl, appl, skb); + break; + + case CAPI_DISCONNECT_B3_IND: + ncci = CAPIMSG_NCCI(skb->data); + + BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci); + + if (ncci == 0xffffffff) + BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff"); + + ctrl->handle_capimsg(ctrl, appl, skb); + ctrl->free_ncci(ctrl, appl, ncci); + break; + + default: + ctrl->handle_capimsg(ctrl, appl, skb); + break; + } +} + +void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) +{ + struct cmtp_scb *scb = (void *) skb->cb; + + BT_DBG("session %p skb %p len %d", session, skb, skb->len); + + scb->id = -1; + scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); + + skb_queue_tail(&session->transmit, skb); + + cmtp_schedule(session); +} + + +static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + BT_DBG("ctrl %p data %p", ctrl, data); + + return -EIO; +} + +static void cmtp_reset_ctr(struct capi_ctr *ctrl) +{ + BT_DBG("ctrl %p", ctrl); + + ctrl->reseted(ctrl); +} + +static void cmtp_remove_ctr(struct capi_ctr *ctrl) +{ + struct cmtp_session *session = ctrl->driverdata; + + BT_DBG("ctrl %p", ctrl); + + ctrl->suspend_output(ctrl); + + atomic_inc(&session->terminate); + cmtp_schedule(session); +} + +static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) +{ + DECLARE_WAITQUEUE(wait, current); + struct cmtp_session *session = ctrl->driverdata; + struct cmtp_application *application; + unsigned long timeo = CMTP_INTEROP_TIMEOUT; + unsigned char buf[8]; + int err = 0, nconn, want = rp->level3cnt; + + BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", + ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); + + application = cmtp_application_add(session, appl); + if (!application) { + BT_ERR("Can't allocate memory for new application"); + ctrl->appl_released(ctrl, appl); + return; + } + + if (want < 0) + nconn = ctrl->profile.nbchannel * -want; + else + nconn = want; + + if (nconn == 0) + nconn = ctrl->profile.nbchannel; + + capimsg_setu16(buf, 0, nconn); + capimsg_setu16(buf, 2, rp->datablkcnt); + capimsg_setu16(buf, 4, rp->datablklen); + + application->state = BT_CONFIG; + application->msgnum = cmtp_msgnum_get(session); + + cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, + CAPI_FUNCTION_REGISTER, buf, 6); + + add_wait_queue(&session->wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!timeo) { + err = -EAGAIN; + break; + } + + if (application->state == BT_CLOSED) { + err = -application->err; + break; + } + + if (application->state == BT_CONNECTED) + break; + + if (signal_pending(current)) { + err = -EINTR; + break; + } + + timeo = schedule_timeout(timeo); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&session->wait, &wait); + + if (err) { + ctrl->appl_released(ctrl, appl); + cmtp_application_del(session, application); + return; + } + + ctrl->appl_registered(ctrl, appl); +} + +static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) +{ + DECLARE_WAITQUEUE(wait, current); + struct cmtp_session *session = ctrl->driverdata; + struct cmtp_application *application; + unsigned long timeo = CMTP_INTEROP_TIMEOUT; + + BT_DBG("ctrl %p appl %d", ctrl, appl); + + application = cmtp_application_get(session, CMTP_APPLID, appl); + if (!application) { + BT_ERR("Can't find application"); + return; + } + + application->msgnum = cmtp_msgnum_get(session); + + cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, + CAPI_FUNCTION_RELEASE, NULL, 0); + + add_wait_queue(&session->wait, &wait); + while (timeo) { + set_current_state(TASK_INTERRUPTIBLE); + + if (application->state == BT_CLOSED) + break; + + if (signal_pending(current)) + break; + + timeo = schedule_timeout(timeo); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&session->wait, &wait); + + cmtp_application_del(session, application); + ctrl->appl_released(ctrl, appl); +} + +static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + struct cmtp_session *session = ctrl->driverdata; + struct cmtp_application *application; + __u16 appl; + __u32 contr; + + BT_DBG("ctrl %p skb %p", ctrl, skb); + + appl = CAPIMSG_APPID(skb->data); + contr = CAPIMSG_CONTROL(skb->data); + + application = cmtp_application_get(session, CMTP_APPLID, appl); + if ((!application) || (application->state != BT_CONNECTED)) { + BT_ERR("Can't find application with id %d", appl); + kfree_skb(skb); + return; + } + + CAPIMSG_SETAPPID(skb->data, application->mapping); + + if ((contr & 0x7f) == session->num) { + contr = (contr & 0xffffff80) | 0x01; + CAPIMSG_SETCONTROL(skb->data, contr); + } + + cmtp_send_capimsg(session, skb); +} + +static char *cmtp_procinfo(struct capi_ctr *ctrl) +{ + return "CAPI Message Transport Protocol"; +} + +static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) +{ + struct cmtp_session *session = ctrl->driverdata; + struct cmtp_application *app; + struct list_head *p, *n; + int len = 0; + + len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION); + len += sprintf(page + len, "addr %s\n", session->name); + len += sprintf(page + len, "ctrl %d\n", session->num); + + list_for_each_safe(p, n, &session->applications) { + app = list_entry(p, struct cmtp_application, list); + len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); + } + + if (off + count >= len) + *eof = 1; + + if (len < off) + return 0; + + *start = page + off; + + return ((count < len - off) ? count : len - off); +} + +static struct capi_driver cmtp_driver = { + name: "cmtp", + revision: REVISION, + load_firmware: cmtp_load_firmware, + reset_ctr: cmtp_reset_ctr, + remove_ctr: cmtp_remove_ctr, + register_appl: cmtp_register_appl, + release_appl: cmtp_release_appl, + send_message: cmtp_send_message, + procinfo: cmtp_procinfo, + ctr_read_proc: cmtp_ctr_read_proc, + + driver_read_proc: 0, + add_card: 0, +}; + + +int cmtp_attach_device(struct cmtp_session *session) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = CMTP_INTEROP_TIMEOUT; + unsigned char buf[4]; + + BT_DBG("session %p", session); + + capimsg_setu32(buf, 0, 0); + + cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, + CAPI_FUNCTION_GET_PROFILE, buf, 4); + + add_wait_queue(&session->wait, &wait); + while (timeo) { + set_current_state(TASK_INTERRUPTIBLE); + + if (session->ncontroller) + break; + + if (signal_pending(current)) + break; + + timeo = schedule_timeout(timeo); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&session->wait, &wait); + + BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); + + if (!timeo) + return -ETIMEDOUT; + + if (!session->ncontroller) + return -ENODEV; + + + if (session->ncontroller > 1) + BT_INFO("Setting up only CAPI controller 1"); + + if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) { + BT_ERR("Can't attach new controller"); + return -EBUSY; + } + + session->num = session->ctrl->cnr; + + BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num); + + capimsg_setu32(buf, 0, 1); + + cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), + CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); + + cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), + CAPI_FUNCTION_GET_VERSION, buf, 4); + + cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), + CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); + + cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), + CAPI_FUNCTION_GET_PROFILE, buf, 4); + + return 0; +} + +void cmtp_detach_device(struct cmtp_session *session) +{ + struct capi_ctr *ctrl = session->ctrl; + + BT_DBG("session %p ctrl %p", session, ctrl); + + if (!ctrl) + return; + + ctrl->reseted(ctrl); + + di->detach_ctr(ctrl); +} + +int cmtp_init_capi(void) +{ + if (!(di = attach_capi_driver(&cmtp_driver))) { + BT_ERR("Can't attach CAPI driver"); + return -EIO; + } + + return 0; +} + +void cmtp_cleanup_capi(void) +{ + detach_capi_driver(&cmtp_driver); +} diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/cmtp.h linux-2.4.20-18.8-bt/net/bluetooth/cmtp/cmtp.h --- linux-2.4.20-18.8/net/bluetooth/cmtp/cmtp.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/cmtp.h 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,138 @@ +/* + CMTP implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002-2003 Marcel Holtmann + + 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. +*/ + +#ifndef __CMTP_H +#define __CMTP_H + +#include +#include + +#define BTNAMSIZ 18 + +/* CMTP ioctl defines */ +#define CMTPCONNADD _IOW('C', 200, int) +#define CMTPCONNDEL _IOW('C', 201, int) +#define CMTPGETCONNLIST _IOR('C', 210, int) +#define CMTPGETCONNINFO _IOR('C', 211, int) + +#define CMTP_LOOPBACK 0 + +struct cmtp_connadd_req { + int sock; // Connected socket + __u32 flags; +}; + +struct cmtp_conndel_req { + bdaddr_t bdaddr; + __u32 flags; +}; + +struct cmtp_conninfo { + bdaddr_t bdaddr; + __u32 flags; + __u16 state; + int num; +}; + +struct cmtp_connlist_req { + __u32 cnum; + struct cmtp_conninfo *ci; +}; + +int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); +int cmtp_del_connection(struct cmtp_conndel_req *req); +int cmtp_get_connlist(struct cmtp_connlist_req *req); +int cmtp_get_conninfo(struct cmtp_conninfo *ci); + +/* CMTP session defines */ +#define CMTP_INTEROP_TIMEOUT (HZ * 5) +#define CMTP_INITIAL_MSGNUM 0xff00 + +struct cmtp_session { + struct list_head list; + + struct socket *sock; + + bdaddr_t bdaddr; + + unsigned long state; + unsigned long flags; + + uint mtu; + + char name[BTNAMSIZ]; + + atomic_t terminate; + + wait_queue_head_t wait; + + int ncontroller; + int num; + struct capi_ctr *ctrl; + + struct list_head applications; + + unsigned long blockids; + int msgnum; + + struct sk_buff_head transmit; + + struct sk_buff *reassembly[16]; +}; + +struct cmtp_application { + struct list_head list; + + unsigned long state; + int err; + + __u16 appl; + __u16 mapping; + + __u16 msgnum; +}; + +struct cmtp_scb { + int id; + int data; +}; + +int cmtp_attach_device(struct cmtp_session *session); +void cmtp_detach_device(struct cmtp_session *session); + +void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); +void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb); + +static inline void cmtp_schedule(struct cmtp_session *session) +{ + struct sock *sk = session->sock->sk; + + wake_up_interruptible(sk->sleep); +} + +/* CMTP init defines */ +int cmtp_init_capi(void); +int cmtp_init_sockets(void); +void cmtp_cleanup_capi(void); +void cmtp_cleanup_sockets(void); + +#endif /* __CMTP_H */ diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/Config.in linux-2.4.20-18.8-bt/net/bluetooth/cmtp/Config.in --- linux-2.4.20-18.8/net/bluetooth/cmtp/Config.in 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/Config.in 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,6 @@ +# +# Bluetooth CMTP layer configuration +# + +dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_BLUEZ_L2CAP + diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/core.c linux-2.4.20-18.8-bt/net/bluetooth/cmtp/core.c --- linux-2.4.20-18.8/net/bluetooth/cmtp/core.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/core.c 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,512 @@ +/* + CMTP implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002-2003 Marcel Holtmann + + 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. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmtp.h" + +#ifndef CONFIG_BLUEZ_CMTP_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "1.0" + +static DECLARE_RWSEM(cmtp_session_sem); +static LIST_HEAD(cmtp_session_list); + +static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) +{ + struct cmtp_session *session; + struct list_head *p; + + BT_DBG(""); + + list_for_each(p, &cmtp_session_list) { + session = list_entry(p, struct cmtp_session, list); + if (!bacmp(bdaddr, &session->bdaddr)) + return session; + } + return NULL; +} + +static void __cmtp_link_session(struct cmtp_session *session) +{ + MOD_INC_USE_COUNT; + list_add(&session->list, &cmtp_session_list); +} + +static void __cmtp_unlink_session(struct cmtp_session *session) +{ + list_del(&session->list); + MOD_DEC_USE_COUNT; +} + +static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) +{ + bacpy(&ci->bdaddr, &session->bdaddr); + + ci->flags = session->flags; + ci->state = session->state; + + ci->num = session->num; +} + + +static inline int cmtp_alloc_block_id(struct cmtp_session *session) +{ + int i, id = -1; + + for (i = 0; i < 16; i++) + if (!test_and_set_bit(i, &session->blockids)) { + id = i; + break; + } + + return id; +} + +static inline void cmtp_free_block_id(struct cmtp_session *session, int id) +{ + clear_bit(id, &session->blockids); +} + +static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) +{ + struct sk_buff *skb = session->reassembly[id], *nskb; + int size; + + BT_DBG("session %p buf %p count %d", session, buf, count); + + size = (skb) ? skb->len + count : count; + + if (!(nskb = alloc_skb(size, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for CAPI message"); + return; + } + + if (skb && (skb->len > 0)) + memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + + memcpy(skb_put(nskb, count), buf, count); + + session->reassembly[id] = nskb; + + if (skb) + kfree_skb(skb); +} + +static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) +{ + __u8 hdr, hdrlen, id; + __u16 len; + + BT_DBG("session %p skb %p len %d", session, skb, skb->len); + + while (skb->len > 0) { + hdr = skb->data[0]; + + switch (hdr & 0xc0) { + case 0x40: + hdrlen = 2; + len = skb->data[1]; + break; + case 0x80: + hdrlen = 3; + len = skb->data[1] | (skb->data[2] << 8); + break; + default: + hdrlen = 1; + len = 0; + break; + } + + id = (hdr & 0x3c) >> 2; + + BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); + + if (hdrlen + len > skb->len) { + BT_ERR("Wrong size or header information in CMTP frame"); + break; + } + + if (len == 0) { + skb_pull(skb, hdrlen); + continue; + } + + switch (hdr & 0x03) { + case 0x00: + cmtp_add_msgpart(session, id, skb->data + hdrlen, len); + cmtp_recv_capimsg(session, session->reassembly[id]); + session->reassembly[id] = NULL; + break; + case 0x01: + cmtp_add_msgpart(session, id, skb->data + hdrlen, len); + break; + default: + if (session->reassembly[id] != NULL) + kfree_skb(session->reassembly[id]); + session->reassembly[id] = NULL; + break; + } + + skb_pull(skb, hdrlen + len); + } + + kfree_skb(skb); + return 0; +} + +static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) +{ + struct socket *sock = session->sock; + struct iovec iv = { data, len }; + struct msghdr msg; + int err; + + BT_DBG("session %p data %p len %d", session, data, len); + + if (!len) + return 0; + + 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 cmtp_process_transmit(struct cmtp_session *session) +{ + struct sk_buff *skb, *nskb; + unsigned char *hdr; + unsigned int size, tail; + + BT_DBG("session %p", session); + + if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for new frame"); + return -ENOMEM; + } + + while ((skb = skb_dequeue(&session->transmit))) { + struct cmtp_scb *scb = (void *) skb->cb; + + if ((tail = (session->mtu - nskb->len)) < 5) { + cmtp_send_frame(session, nskb->data, nskb->len); + skb_trim(nskb, 0); + tail = session->mtu; + } + + size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); + + if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) { + skb_queue_head(&session->transmit, skb); + break; + } + + if (size < 256) { + hdr = skb_put(nskb, 2); + hdr[0] = 0x40 + | ((scb->id << 2) & 0x3c) + | ((skb->len == size) ? 0x00 : 0x01); + hdr[1] = size; + } else { + hdr = skb_put(nskb, 3); + hdr[0] = 0x80 + | ((scb->id << 2) & 0x3c) + | ((skb->len == size) ? 0x00 : 0x01); + hdr[1] = size & 0xff; + hdr[2] = size >> 8; + } + + memcpy(skb_put(nskb, size), skb->data, size); + skb_pull(skb, size); + + if (skb->len > 0) { + skb_queue_head(&session->transmit, skb); + } else { + cmtp_free_block_id(session, scb->id); + if (scb->data) { + cmtp_send_frame(session, nskb->data, nskb->len); + skb_trim(nskb, 0); + } + kfree_skb(skb); + } + } + + cmtp_send_frame(session, nskb->data, nskb->len); + + kfree_skb(nskb); + + return skb_queue_len(&session->transmit); +} + +static int cmtp_session(void *arg) +{ + struct cmtp_session *session = arg; + struct sock *sk = session->sock->sk; + struct sk_buff *skb; + wait_queue_t wait; + + BT_DBG("session %p", session); + + daemonize(); reparent_to_init(); + + sprintf(current->comm, "kcmtpd_ctr_%d", session->num); + + sigfillset(¤t->blocked); + flush_signals(current); + + current->nice = -15; + + set_fs(KERNEL_DS); + + init_waitqueue_entry(&wait, current); + add_wait_queue(sk->sleep, &wait); + while (!atomic_read(&session->terminate)) { + set_current_state(TASK_INTERRUPTIBLE); + + if (sk->state != BT_CONNECTED) + break; + + while ((skb = skb_dequeue(&sk->receive_queue))) { + skb_orphan(skb); + cmtp_recv_frame(session, skb); + } + + cmtp_process_transmit(session); + + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk->sleep, &wait); + + down_write(&cmtp_session_sem); + + if (!(session->flags & (1 << CMTP_LOOPBACK))) + cmtp_detach_device(session); + + fput(session->sock->file); + + __cmtp_unlink_session(session); + + up_write(&cmtp_session_sem); + + kfree(session); + return 0; +} + +int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) +{ + struct cmtp_session *session, *s; + bdaddr_t src, dst; + int i, err; + + BT_DBG(""); + + baswap(&src, &bluez_pi(sock->sk)->src); + baswap(&dst, &bluez_pi(sock->sk)->dst); + + session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL); + if (!session) + return -ENOMEM; + memset(session, 0, sizeof(struct cmtp_session)); + + down_write(&cmtp_session_sem); + + s = __cmtp_get_session(&bluez_pi(sock->sk)->dst); + if (s && s->state == BT_CONNECTED) { + err = -EEXIST; + goto failed; + } + + bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst); + + session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu); + + BT_DBG("mtu %d", session->mtu); + + sprintf(session->name, "%s", batostr(&dst)); + + session->sock = sock; + session->state = BT_CONFIG; + + init_waitqueue_head(&session->wait); + + session->ctrl = NULL; + session->msgnum = CMTP_INITIAL_MSGNUM; + + INIT_LIST_HEAD(&session->applications); + + skb_queue_head_init(&session->transmit); + + for (i = 0; i < 16; i++) + session->reassembly[i] = NULL; + + session->flags = req->flags; + + __cmtp_link_session(session); + + err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + if (err < 0) + goto unlink; + + if (!(session->flags & (1 << CMTP_LOOPBACK))) { + err = cmtp_attach_device(session); + if (err < 0) + goto detach; + } + + up_write(&cmtp_session_sem); + return 0; + +detach: + cmtp_detach_device(session); + +unlink: + __cmtp_unlink_session(session); + +failed: + up_write(&cmtp_session_sem); + kfree(session); + return err; +} + +int cmtp_del_connection(struct cmtp_conndel_req *req) +{ + struct cmtp_session *session; + int err = 0; + + BT_DBG(""); + + down_read(&cmtp_session_sem); + + session = __cmtp_get_session(&req->bdaddr); + if (session) { + /* Flush the transmit queue */ + skb_queue_purge(&session->transmit); + + /* Kill session thread */ + atomic_inc(&session->terminate); + cmtp_schedule(session); + } else + err = -ENOENT; + + up_read(&cmtp_session_sem); + return err; +} + +int cmtp_get_connlist(struct cmtp_connlist_req *req) +{ + struct list_head *p; + int err = 0, n = 0; + + BT_DBG(""); + + down_read(&cmtp_session_sem); + + list_for_each(p, &cmtp_session_list) { + struct cmtp_session *session; + struct cmtp_conninfo ci; + + session = list_entry(p, struct cmtp_session, list); + + __cmtp_copy_session(session, &ci); + + if (copy_to_user(req->ci, &ci, sizeof(ci))) { + err = -EFAULT; + break; + } + + if (++n >= req->cnum) + break; + + req->ci++; + } + req->cnum = n; + + up_read(&cmtp_session_sem); + return err; +} + +int cmtp_get_conninfo(struct cmtp_conninfo *ci) +{ + struct cmtp_session *session; + int err = 0; + + down_read(&cmtp_session_sem); + + session = __cmtp_get_session(&ci->bdaddr); + if (session) + __cmtp_copy_session(session, ci); + else + err = -ENOENT; + + up_read(&cmtp_session_sem); + return err; +} + + +int __init init_cmtp(void) +{ + cmtp_init_capi(); + cmtp_init_sockets(); + + BT_INFO("BlueZ CMTP ver %s", VERSION); + BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann "); + + return 0; +} + +void __exit exit_cmtp(void) +{ + cmtp_cleanup_sockets(); + cmtp_cleanup_capi(); +} + +module_init(init_cmtp); +module_exit(exit_cmtp); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION); +MODULE_LICENSE("GPL"); diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/Makefile linux-2.4.20-18.8-bt/net/bluetooth/cmtp/Makefile --- linux-2.4.20-18.8/net/bluetooth/cmtp/Makefile 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/Makefile 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,10 @@ +# +# Makefile for the Linux Bluetooth CMTP layer +# + +O_TARGET := cmtp.o + +obj-y := core.o sock.o capi.o +obj-m += $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Naur linux-2.4.20-18.8/net/bluetooth/cmtp/sock.c linux-2.4.20-18.8-bt/net/bluetooth/cmtp/sock.c --- linux-2.4.20-18.8/net/bluetooth/cmtp/sock.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.4.20-18.8-bt/net/bluetooth/cmtp/sock.c 2003-07-08 07:34:31.000000000 -0400 @@ -0,0 +1,236 @@ +/* + CMTP implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002-2003 Marcel Holtmann + + 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. +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmtp.h" + +#ifndef CONFIG_BLUEZ_CMTP_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +static inline struct socket *socki_lookup(struct inode *inode) +{ + return &inode->u.socket_i; +} + +static struct socket *sockfd_lookup(int fd, int *err) +{ + struct file *file; + struct inode *inode; + struct socket *sock; + + if (!(file = fget(fd))) { + *err = -EBADF; + return NULL; + } + + inode = file->f_dentry->d_inode; + if (!inode->i_sock || !(sock = socki_lookup(inode))) { + *err = -ENOTSOCK; + fput(file); + return NULL; + } + + if (sock->file != file) { + printk(KERN_ERR "socki_lookup: socket file changed!\n"); + sock->file = file; + } + return sock; +} + +static int cmtp_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + BT_DBG("sock %p sk %p", sock, sk); + + if (!sk) + return 0; + + sock_orphan(sk); + sock_put(sk); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct cmtp_connadd_req ca; + struct cmtp_conndel_req cd; + struct cmtp_connlist_req cl; + struct cmtp_conninfo ci; + struct socket *nsock; + int err; + + BT_DBG("cmd %x arg %lx", cmd, arg); + + switch (cmd) { + case CMTPCONNADD: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + if (copy_from_user(&ca, (void *) arg, sizeof(ca))) + return -EFAULT; + + nsock = sockfd_lookup(ca.sock, &err); + if (!nsock) + return err; + + if (nsock->sk->state != BT_CONNECTED) + return -EBADFD; + + err = cmtp_add_connection(&ca, nsock); + if (!err) { + if (copy_to_user((void *) arg, &ca, sizeof(ca))) + err = -EFAULT; + } else + fput(nsock->file); + + return err; + + case CMTPCONNDEL: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + if (copy_from_user(&cd, (void *) arg, sizeof(cd))) + return -EFAULT; + + return cmtp_del_connection(&cd); + + case CMTPGETCONNLIST: + if (copy_from_user(&cl, (void *) arg, sizeof(cl))) + return -EFAULT; + + if (cl.cnum <= 0) + return -EINVAL; + + err = cmtp_get_connlist(&cl); + if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) + return -EFAULT; + + return err; + + case CMTPGETCONNINFO: + if (copy_from_user(&ci, (void *) arg, sizeof(ci))) + return -EFAULT; + + err = cmtp_get_conninfo(&ci); + if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) + return -EFAULT; + + return err; + } + + return -EINVAL; +} + +static struct proto_ops cmtp_sock_ops = { + family: PF_BLUETOOTH, + release: cmtp_sock_release, + ioctl: cmtp_sock_ioctl, + bind: sock_no_bind, + getname: sock_no_getname, + sendmsg: sock_no_sendmsg, + recvmsg: sock_no_recvmsg, + poll: sock_no_poll, + listen: sock_no_listen, + shutdown: sock_no_shutdown, + setsockopt: sock_no_setsockopt, + getsockopt: sock_no_getsockopt, + connect: sock_no_connect, + socketpair: sock_no_socketpair, + accept: sock_no_accept, + mmap: sock_no_mmap +}; + +static int cmtp_sock_create(struct socket *sock, int protocol) +{ + struct sock *sk; + + BT_DBG("sock %p", sock); + + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + sock->ops = &cmtp_sock_ops; + + if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) + return -ENOMEM; + + MOD_INC_USE_COUNT; + + sock->state = SS_UNCONNECTED; + sock_init_data(sock, sk); + + sk->destruct = NULL; + sk->protocol = protocol; + + return 0; +} + +static struct net_proto_family cmtp_sock_family_ops = { + family: PF_BLUETOOTH, + create: cmtp_sock_create +}; + +int cmtp_init_sockets(void) +{ + int err; + + if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) { + BT_ERR("Can't register CMTP socket layer (%d)", err); + return err; + } + + return 0; +} + +void cmtp_cleanup_sockets(void) +{ + int err; + + if ((err = bluez_sock_unregister(BTPROTO_CMTP))) + BT_ERR("Can't unregister CMTP socket layer (%d)", err); + + return; +} diff -Naur linux-2.4.20-18.8/net/bluetooth/Config.in linux-2.4.20-18.8-bt/net/bluetooth/Config.in --- linux-2.4.20-18.8/net/bluetooth/Config.in 2003-05-29 06:45:25.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/Config.in 2003-07-08 07:34:31.000000000 -0400 @@ -13,6 +13,7 @@ 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/cmtp/Config.in source drivers/bluetooth/Config.in fi diff -Naur linux-2.4.20-18.8/net/bluetooth/l2cap.c linux-2.4.20-18.8-bt/net/bluetooth/l2cap.c --- linux-2.4.20-18.8/net/bluetooth/l2cap.c 2003-05-29 06:46:23.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/l2cap.c 2003-07-08 07:34:31.000000000 -0400 @@ -27,7 +27,7 @@ * * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $ */ -#define VERSION "2.1" +#define VERSION "2.3" #include #include @@ -284,7 +284,7 @@ l2cap_disconn_req req; sk->state = BT_DISCONN; - l2cap_sock_set_timer(sk, HZ * 5); + l2cap_sock_set_timer(sk, sk->sndtimeo); req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); @@ -309,11 +309,9 @@ static void l2cap_sock_close(struct sock *sk) { l2cap_sock_clear_timer(sk); - lock_sock(sk); __l2cap_sock_close(sk, ECONNRESET); release_sock(sk); - l2cap_sock_kill(sk); } @@ -527,7 +525,8 @@ goto done; wait: - err = bluez_sock_w4_connect(sk, flags); + err = bluez_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); done: release_sock(sk); @@ -760,32 +759,39 @@ static int l2cap_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; - l2cap_sock_clear_timer(sk); - lock_sock(sk); - sk->shutdown = SHUTDOWN_MASK; - __l2cap_sock_close(sk, ECONNRESET); - release_sock(sk); + if (!sk->shutdown) { + sk->shutdown = SHUTDOWN_MASK; + l2cap_sock_clear_timer(sk); + __l2cap_sock_close(sk, 0); - return 0; + if (sk->linger) + err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); + } + release_sock(sk); + return err; } static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; + int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; + err = l2cap_sock_shutdown(sock, 2); + sock_orphan(sk); - l2cap_sock_close(sk); - return 0; + l2cap_sock_kill(sk); + return err; } /* --------- L2CAP channels --------- */ @@ -917,10 +923,12 @@ hci_conn_put(conn->hcon); } - sk->state = BT_CLOSED; - sk->err = err; + sk->state = BT_CLOSED; sk->zapped = 1; + if (err) + sk->err = err; + if (parent) parent->data_ready(parent, 0); else @@ -956,6 +964,22 @@ read_unlock(&l->lock); } +/* Notify sockets that we cannot guaranty reliability anymore */ +static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) + sk->err = err; + } + read_unlock(&l->lock); +} + static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bluez_pi(sk)->parent; @@ -1320,15 +1344,18 @@ { l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data; void *ptr = rsp->data; + u16 flags = 0; BT_DBG("sk %p complete %d", sk, result ? 1 : 0); if (result) *result = l2cap_conf_output(sk, &ptr); + else + flags |= 0x0001; rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid); rsp->result = __cpu_to_le16(result ? *result : 0); - rsp->flags = __cpu_to_le16(0); + rsp->flags = __cpu_to_le16(flags); return ptr - data; } @@ -1441,7 +1468,7 @@ case L2CAP_CR_SUCCESS: sk->state = BT_CONFIG; l2cap_pi(sk)->dcid = dcid; - l2cap_pi(sk)->conf_state |= CONF_REQ_SENT; + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); break; @@ -1476,7 +1503,7 @@ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE); - if (flags & 0x01) { + if (flags & 0x0001) { /* Incomplete config. Send empty response. */ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp); goto unlock; @@ -1489,12 +1516,12 @@ goto unlock; /* Output config done */ - l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE; + l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE; - if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) { + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { sk->state = BT_CONNECTED; l2cap_chan_ready(sk); - } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) { + } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { char req[64]; l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); } @@ -1520,18 +1547,34 @@ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) return -ENOENT; - if (result) { - l2cap_disconn_req req; + switch (result) { + case L2CAP_CONF_SUCCESS: + break; - /* They didn't like our options. Well... we do not negotiate. - * Close channel. - */ + case L2CAP_CONF_UNACCEPT: + if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) { + char req[128]; + /* + It does not make sense to adjust L2CAP parameters + that are currently defined in the spec. We simply + resend config request that we sent earlier. It is + stupid :) but it helps qualification testing + which expects at least some response from us. + */ + l2cap_send_req(conn, L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, req), req); + goto done; + } + default: sk->state = BT_DISCONN; + sk->err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); - - req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); - req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); - l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); + { + l2cap_disconn_req req; + req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); + req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); + l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); + } goto done; } @@ -1539,9 +1582,9 @@ goto done; /* Input config done */ - l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE; + l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; - if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) { + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { sk->state = BT_CONNECTED; l2cap_chan_ready(sk); } @@ -1592,7 +1635,7 @@ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) return 0; - l2cap_chan_del(sk, ECONNABORTED); + l2cap_chan_del(sk, 0); bh_unlock_sock(sk); l2cap_sock_kill(sk); @@ -1943,26 +1986,36 @@ kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; + l2cap_conn_unreliable(conn, ECOMM); } if (skb->len < 2) { - BT_ERR("Frame is too small (len %d)", skb->len); + BT_ERR("Frame is too short (len %d)", skb->len); + l2cap_conn_unreliable(conn, ECOMM); goto drop; } hdr = (l2cap_hdr *) skb->data; len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; - BT_DBG("Start: total len %d, frag len %d", len, skb->len); - if (len == skb->len) { /* Complete frame received */ l2cap_recv_frame(conn, skb); return 0; } - /* Allocate skb for the complete frame (with header) */ - if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC))) + BT_DBG("Start: total len %d, frag len %d", len, skb->len); + + if (skb->len > len) { + BT_ERR("Frame is too long (len %d, expected len %d)", + skb->len, len); + l2cap_conn_unreliable(conn, ECOMM); + goto drop; + } + + /* Allocate skb for the complete frame including header */ + conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC); + if (!conn->rx_skb) goto drop; memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); @@ -1972,15 +2025,17 @@ if (!conn->rx_len) { BT_ERR("Unexpected continuation frame (len %d)", skb->len); + l2cap_conn_unreliable(conn, ECOMM); goto drop; } if (skb->len > conn->rx_len) { - BT_ERR("Fragment is too large (len %d, expect %d)", + BT_ERR("Fragment is too long (len %d, expected %d)", skb->len, conn->rx_len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; + l2cap_conn_unreliable(conn, ECOMM); goto drop; } diff -Naur linux-2.4.20-18.8/net/bluetooth/Makefile linux-2.4.20-18.8-bt/net/bluetooth/Makefile --- linux-2.4.20-18.8/net/bluetooth/Makefile 2003-05-29 06:45:25.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/Makefile 2003-07-08 07:34:31.000000000 -0400 @@ -15,6 +15,7 @@ subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm subdir-$(CONFIG_BLUEZ_BNEP) += bnep +subdir-$(CONFIG_BLUEZ_CMTP) += cmtp ifeq ($(CONFIG_BLUEZ_RFCOMM),y) obj-y += rfcomm/rfcomm.o diff -Naur linux-2.4.20-18.8/net/bluetooth/rfcomm/core.c linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/core.c --- linux-2.4.20-18.8/net/bluetooth/rfcomm/core.c 2003-05-29 06:46:23.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/core.c 2003-07-08 07:34:31.000000000 -0400 @@ -198,6 +198,7 @@ d->state = BT_OPEN; d->flags = 0; + d->mscex = 0; d->mtu = RFCOMM_DEFAULT_MTU; d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; @@ -274,13 +275,13 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) { struct rfcomm_session *s; - u8 dlci = __dlci(0, channel); int err = 0; + u8 dlci; - BT_DBG("dlc %p state %ld %s %s channel %d dlci %d", - d, d->state, batostr(src), batostr(dst), channel, dlci); + BT_DBG("dlc %p state %ld %s %s channel %d", + d, d->state, batostr(src), batostr(dst), channel); - if (dlci < 1 || dlci > 62) + if (channel < 1 || channel > 30) return -EINVAL; if (d->state != BT_OPEN && d->state != BT_CLOSED) @@ -293,16 +294,19 @@ return err; } + dlci = __dlci(!s->initiator, channel); + /* 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->dlci = dlci; + d->addr = __addr(s->initiator, dlci); + d->priority = 7; - d->state = BT_CONFIG; + d->state = BT_CONFIG; rfcomm_dlc_link(s, d); d->mtu = s->mtu; @@ -707,7 +711,7 @@ hdr->len = __len8(sizeof(*mcc) + 1); mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(s->initiator, RFCOMM_NSC); + mcc->type = __mcc_type(cr, RFCOMM_NSC); mcc->len = __len8(1); /* Type that we didn't like */ @@ -733,12 +737,12 @@ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(s->initiator, RFCOMM_PN); + mcc->type = __mcc_type(cr, RFCOMM_PN); mcc->len = __len8(sizeof(*pn)); pn = (void *) ptr; ptr += sizeof(*pn); pn->dlci = d->dlci; - pn->priority = 0; + pn->priority = d->priority; pn->ack_timer = 0; pn->max_retrans = 0; @@ -842,7 +846,7 @@ msc = (void *) ptr; ptr += sizeof(*msc); msc->dlci = __addr(1, dlci); - msc->v24_sig = v24_sig; + msc->v24_sig = v24_sig | 0x01; *ptr = __fcs(buf); ptr++; @@ -1076,6 +1080,8 @@ d->state = BT_CONNECTED; d->state_change(d, 0); rfcomm_dlc_unlock(d); + + rfcomm_send_msc(s, 1, dlci, d->v24_sig); } else { rfcomm_send_dm(s, dlci); } @@ -1095,8 +1101,6 @@ 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; @@ -1104,10 +1108,12 @@ set_bit(RFCOMM_TX_THROTTLED, &d->flags); d->credits = 0; } - - d->mtu = btohs(pn->mtu); } + d->priority = pn->priority; + + d->mtu = btohs(pn->mtu); + return 0; } @@ -1133,7 +1139,7 @@ switch (d->state) { case BT_CONFIG: rfcomm_apply_pn(d, cr, pn); - + d->state = BT_CONNECT; rfcomm_send_sabm(s, d->dlci); break; @@ -1144,7 +1150,7 @@ if (!cr) return 0; - + /* PN request for non existing DLC. * Assume incomming connection. */ if (rfcomm_connect_ind(s, channel, &d)) { @@ -1153,7 +1159,7 @@ rfcomm_dlc_link(s, d); rfcomm_apply_pn(d, cr, pn); - + d->state = BT_OPEN; rfcomm_send_pn(s, 0, d); } else { @@ -1198,6 +1204,14 @@ } /* 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_BITRATE) { + bit_rate = rpn->bit_rate; + if (bit_rate != RFCOMM_RPN_BR_115200) { + BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); + bit_rate = RFCOMM_RPN_BR_115200; + rpn_mask ^= RFCOMM_RPN_PM_BITRATE; + } + } if (rpn->param_mask & RFCOMM_RPN_PM_DATA) { data_bits = __get_rpn_data_bits(rpn->line_settings); if (data_bits != RFCOMM_RPN_DATA_8) { @@ -1223,23 +1237,26 @@ } } 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; + flow_ctrl = rpn->flow_ctrl; + if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { + BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); + 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; + xon_char = rpn->xon_char; + if (xon_char != RFCOMM_RPN_XON_CHAR) { + BT_DBG("RPN XON char mismatch 0x%x", xon_char); + 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; + xoff_char = rpn->xoff_char; + if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { + BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); + xoff_char = RFCOMM_RPN_XOFF_CHAR; rpn_mask ^= RFCOMM_RPN_PM_XOFF; } } @@ -1280,11 +1297,11 @@ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); - if (!cr) + d = rfcomm_dlc_get(s, dlci); + if (!d) return 0; - d = rfcomm_dlc_get(s, dlci); - if (d) { + if (cr) { if (msc->v24_sig & RFCOMM_V24_FC && !d->credits) set_bit(RFCOMM_TX_THROTTLED, &d->flags); else @@ -1296,7 +1313,11 @@ rfcomm_dlc_unlock(d); rfcomm_send_msc(s, 0, dlci, msc->v24_sig); - } + + d->mscex |= RFCOMM_MSCEX_RX; + } else + d->mscex |= RFCOMM_MSCEX_TX; + return 0; } @@ -1520,7 +1541,8 @@ continue; } - if (d->state == BT_CONNECTED || d->state == BT_DISCONN) + if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && + d->mscex == RFCOMM_MSCEX_OK) rfcomm_process_tx(d); } } diff -Naur linux-2.4.20-18.8/net/bluetooth/rfcomm/sock.c linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/sock.c --- linux-2.4.20-18.8/net/bluetooth/rfcomm/sock.c 2003-05-29 06:45:25.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/sock.c 2003-07-08 07:34:31.000000000 -0400 @@ -188,8 +188,10 @@ BT_DBG("parent %p", parent); /* Close not yet accepted dlcs */ - while ((sk = bluez_accept_dequeue(parent, NULL))) + while ((sk = bluez_accept_dequeue(parent, NULL))) { rfcomm_sock_close(sk); + rfcomm_sock_kill(sk); + } parent->state = BT_CLOSED; parent->zapped = 1; @@ -211,15 +213,10 @@ sock_put(sk); } -/* Close socket. - * Must be called on unlocked socket. - */ -static void rfcomm_sock_close(struct sock *sk) +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) { @@ -236,11 +233,17 @@ default: sk->zapped = 1; break; - }; + } +} +/* Close socket. + * Must be called on unlocked socket. + */ +static void rfcomm_sock_close(struct sock *sk) +{ + lock_sock(sk); + __rfcomm_sock_close(sk); release_sock(sk); - - rfcomm_sock_kill(sk); } static void rfcomm_sock_init(struct sock *sk, struct sock *parent) @@ -374,7 +377,8 @@ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel); if (!err) - err = bluez_sock_w4_connect(sk, flags); + err = bluez_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); release_sock(sk); return err; @@ -558,9 +562,6 @@ int target, err = 0, copied = 0; long timeo; - if (sk->state != BT_CONNECTED) - return -EINVAL; - if (flags & MSG_OOB) return -EOPNOTSUPP; @@ -635,23 +636,6 @@ 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; @@ -711,19 +695,42 @@ return err; } +static int rfcomm_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->shutdown) { + sk->shutdown = SHUTDOWN_MASK; + __rfcomm_sock_close(sk); + + if (sk->linger) + err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); + } + release_sock(sk); + return err; +} + static int rfcomm_sock_release(struct socket *sock) { struct sock *sk = sock->sk; + int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; - sock_orphan(sk); - rfcomm_sock_close(sk); + err = rfcomm_sock_shutdown(sock, 2); - return 0; + sock_orphan(sk); + rfcomm_sock_kill(sk); + return err; } /* ---- RFCOMM core layer callbacks ---- diff -Naur linux-2.4.20-18.8/net/bluetooth/rfcomm/tty.c linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/tty.c --- linux-2.4.20-18.8/net/bluetooth/rfcomm/tty.c 2003-05-29 06:46:23.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/rfcomm/tty.c 2003-07-08 07:34:31.000000000 -0400 @@ -260,9 +260,9 @@ skb->destructor = rfcomm_wfree; } -static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority) +static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority) { - if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { + if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { struct sk_buff *skb = alloc_skb(size, priority); if (skb) { rfcomm_set_owner_w(skb, dev); @@ -627,9 +627,9 @@ size = min_t(uint, count, dlc->mtu); if (from_user) - skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL); + skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL); else - skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); + skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC); if (!skb) break; @@ -653,6 +653,27 @@ return sent ? sent : err; } +static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dlc *dlc = dev->dlc; + struct sk_buff *skb; + + BT_DBG("tty %p char %x", tty, ch); + + skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC); + + if (!skb) + return; + + skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); + + *(char *)skb_put(skb, 1) = ch; + + if ((rfcomm_dlc_send(dlc, skb)) < 0) + kfree_skb(skb); +} + static int rfcomm_tty_write_room(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; @@ -879,6 +900,7 @@ open: rfcomm_tty_open, close: rfcomm_tty_close, + put_char: rfcomm_tty_put_char, write: rfcomm_tty_write, write_room: rfcomm_tty_write_room, chars_in_buffer: rfcomm_tty_chars_in_buffer, diff -Naur linux-2.4.20-18.8/net/bluetooth/sco.c linux-2.4.20-18.8-bt/net/bluetooth/sco.c --- linux-2.4.20-18.8/net/bluetooth/sco.c 2003-05-29 06:45:54.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/sco.c 2003-07-08 07:34:31.000000000 -0400 @@ -332,8 +332,10 @@ BT_DBG("parent %p", parent); /* Close not yet accepted channels */ - while ((sk = bluez_accept_dequeue(parent, NULL))) + while ((sk = bluez_accept_dequeue(parent, NULL))) { sco_sock_close(sk); + sco_sock_kill(sk); + } parent->state = BT_CLOSED; parent->zapped = 1; @@ -388,8 +390,6 @@ }; release_sock(sk); - - sco_sock_kill(sk); } static void sco_sock_init(struct sock *sk, struct sock *parent) @@ -508,7 +508,8 @@ if ((err = sco_connect(sk))) goto done; - err = bluez_sock_w4_connect(sk, flags); + err = bluez_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); done: release_sock(sk); @@ -712,16 +713,23 @@ static int sco_sock_release(struct socket *sock) { struct sock *sk = sock->sk; + int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; - sock_orphan(sk); sco_sock_close(sk); + if (sk->linger) { + lock_sock(sk); + err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); + release_sock(sk); + } - return 0; + sock_orphan(sk); + sco_sock_kill(sk); + return err; } static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) diff -Naur linux-2.4.20-18.8/net/bluetooth/syms.c linux-2.4.20-18.8-bt/net/bluetooth/syms.c --- linux-2.4.20-18.8/net/bluetooth/syms.c 2003-05-29 06:45:54.000000000 -0400 +++ linux-2.4.20-18.8-bt/net/bluetooth/syms.c 2003-07-08 07:34:31.000000000 -0400 @@ -78,4 +78,4 @@ EXPORT_SYMBOL(bluez_sock_poll); EXPORT_SYMBOL(bluez_accept_enqueue); EXPORT_SYMBOL(bluez_accept_dequeue); -EXPORT_SYMBOL(bluez_sock_w4_connect); +EXPORT_SYMBOL(bluez_sock_wait_state);