Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
2df06ce5
Commit
2df06ce5
authored
Apr 22, 2003
by
Marcel Holtmann
Browse files
Options
Browse Files
Download
Plain Diff
Merge
bk://linux.bkbits.net/linux-2.5
into hostme.bitkeeper.com:/ua/repos/l/linux-bt/bt-2.5
parents
f52d1d03
dbb5f15d
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
693 additions
and
444 deletions
+693
-444
drivers/bluetooth/Kconfig
drivers/bluetooth/Kconfig
+8
-1
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bluecard_cs.c
+14
-21
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/bt3c_cs.c
+14
-21
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btuart_cs.c
+14
-21
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/dtl1_cs.c
+14
-21
drivers/bluetooth/hci_usb.c
drivers/bluetooth/hci_usb.c
+486
-334
drivers/bluetooth/hci_usb.h
drivers/bluetooth/hci_usb.h
+78
-21
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_core.h
+7
-3
include/net/bluetooth/rfcomm.h
include/net/bluetooth/rfcomm.h
+5
-0
net/bluetooth/hci_conn.c
net/bluetooth/hci_conn.c
+1
-0
net/bluetooth/l2cap.c
net/bluetooth/l2cap.c
+1
-1
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/core.c
+51
-0
No files found.
drivers/bluetooth/Kconfig
View file @
2df06ce5
...
...
@@ -13,11 +13,18 @@ config BT_HCIUSB
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (hci_usb).
config BT_USB_SCO
bool "SCO over HCI USB support"
depends on BT_HCIUSB
help
This option enables the SCO support in the HCI USB driver. You need this
to transmit voice data with your Bluetooth USB device.
Say Y here to compile support for SCO over HCI USB.
config BT_USB_ZERO_PACKET
bool "USB zero packet support"
depends on BT_HCIUSB
help
Support for USB zero packets.
This option is provided only as a work around for buggy Bluetooth USB
devices. Do _not_ enable it unless you know for sure that your device
requires zero packets.
...
...
drivers/bluetooth/bluecard_cs.c
View file @
2df06ce5
...
...
@@ -1075,36 +1075,29 @@ int bluecard_event(event_t event, int priority, event_callback_args_t *args)
return
0
;
}
/* ======================== Module initialization ======================== */
int
__init
init_bluecard_cs
(
void
)
static
struct
pcmcia_driver
bluecard_driver
=
{
.
owner
=
THIS_MODULE
,
.
drv
=
{
.
name
=
"bluecard_cs"
,
},
.
attach
=
bluecard_attach
,
.
detach
=
bluecard_detach
,
};
static
int
__init
init_bluecard_cs
(
void
)
{
servinfo_t
serv
;
int
err
;
CardServices
(
GetCardServicesInfo
,
&
serv
);
if
(
serv
.
Revision
!=
CS_RELEASE_CODE
)
{
printk
(
KERN_NOTICE
"bluecard_cs: Card Services release does not match!
\n
"
);
return
-
1
;
}
err
=
register_pccard_driver
(
&
dev_info
,
&
bluecard_attach
,
&
bluecard_detach
);
return
err
;
return
pcmcia_register_driver
(
&
bluecard_driver
);
}
void
__exit
exit_bluecard_cs
(
void
)
static
void
__exit
exit_bluecard_cs
(
void
)
{
unregister_pccard_driver
(
&
dev_info
);
pcmcia_unregister_driver
(
&
bluecard_driver
);
/* XXX: this really needs to move into generic code.. */
while
(
dev_list
!=
NULL
)
bluecard_detach
(
dev_list
);
}
module_init
(
init_bluecard_cs
);
module_exit
(
exit_bluecard_cs
);
drivers/bluetooth/bt3c_cs.c
View file @
2df06ce5
...
...
@@ -861,36 +861,29 @@ int bt3c_event(event_t event, int priority, event_callback_args_t *args)
return
0
;
}
/* ======================== Module initialization ======================== */
int
__init
init_bt3c_cs
(
void
)
static
struct
pcmcia_driver
bt3c_driver
=
{
.
owner
=
THIS_MODULE
,
.
drv
=
{
.
name
=
"bt3c_cs"
,
},
.
attach
=
bt3c_attach
,
.
detach
=
bt3c_detach
,
};
static
int
__init
init_bt3c_cs
(
void
)
{
servinfo_t
serv
;
int
err
;
CardServices
(
GetCardServicesInfo
,
&
serv
);
if
(
serv
.
Revision
!=
CS_RELEASE_CODE
)
{
printk
(
KERN_NOTICE
"bt3c_cs: Card Services release does not match!
\n
"
);
return
-
1
;
}
err
=
register_pccard_driver
(
&
dev_info
,
&
bt3c_attach
,
&
bt3c_detach
);
return
err
;
return
pcmcia_register_driver
(
&
bt3c_driver
);
}
void
__exit
exit_bt3c_cs
(
void
)
static
void
__exit
exit_bt3c_cs
(
void
)
{
unregister_pccard_driver
(
&
dev_info
);
pcmcia_unregister_driver
(
&
bt3c_driver
);
/* XXX: this really needs to move into generic code.. */
while
(
dev_list
!=
NULL
)
bt3c_detach
(
dev_list
);
}
module_init
(
init_bt3c_cs
);
module_exit
(
exit_bt3c_cs
);
drivers/bluetooth/btuart_cs.c
View file @
2df06ce5
...
...
@@ -868,36 +868,29 @@ int btuart_event(event_t event, int priority, event_callback_args_t *args)
return
0
;
}
/* ======================== Module initialization ======================== */
int
__init
init_btuart_cs
(
void
)
static
struct
pcmcia_driver
btuart_driver
=
{
.
owner
=
THIS_MODULE
,
.
drv
=
{
.
name
=
"btuart_cs"
,
},
.
attach
=
btuart_attach
,
.
detach
=
btuart_detach
,
};
static
int
__init
init_btuart_cs
(
void
)
{
servinfo_t
serv
;
int
err
;
CardServices
(
GetCardServicesInfo
,
&
serv
);
if
(
serv
.
Revision
!=
CS_RELEASE_CODE
)
{
printk
(
KERN_NOTICE
"btuart_cs: Card Services release does not match!
\n
"
);
return
-
1
;
}
err
=
register_pccard_driver
(
&
dev_info
,
&
btuart_attach
,
&
btuart_detach
);
return
err
;
return
pcmcia_register_driver
(
&
btuart_driver
);
}
void
__exit
exit_btuart_cs
(
void
)
static
void
__exit
exit_btuart_cs
(
void
)
{
unregister_pccard_driver
(
&
dev_info
);
pcmcia_unregister_driver
(
&
btuart_driver
);
/* XXX: this really needs to move into generic code.. */
while
(
dev_list
!=
NULL
)
btuart_detach
(
dev_list
);
}
module_init
(
init_btuart_cs
);
module_exit
(
exit_btuart_cs
);
drivers/bluetooth/dtl1_cs.c
View file @
2df06ce5
...
...
@@ -820,36 +820,29 @@ int dtl1_event(event_t event, int priority, event_callback_args_t *args)
return
0
;
}
/* ======================== Module initialization ======================== */
int
__init
init_dtl1_cs
(
void
)
static
struct
pcmcia_driver
dtl1_driver
=
{
.
owner
=
THIS_MODULE
,
.
drv
=
{
.
name
=
"dtl1_cs"
,
},
.
attach
=
dtl1_attach
,
.
detach
=
dtl1_detach
,
};
static
int
__init
init_dtl1_cs
(
void
)
{
servinfo_t
serv
;
int
err
;
CardServices
(
GetCardServicesInfo
,
&
serv
);
if
(
serv
.
Revision
!=
CS_RELEASE_CODE
)
{
printk
(
KERN_NOTICE
"dtl1_cs: Card Services release does not match!
\n
"
);
return
-
1
;
}
err
=
register_pccard_driver
(
&
dev_info
,
&
dtl1_attach
,
&
dtl1_detach
);
return
err
;
return
pcmcia_register_driver
(
&
dtl1_driver
);
}
void
__exit
exit_dtl1_cs
(
void
)
static
void
__exit
exit_dtl1_cs
(
void
)
{
unregister_pccard_driver
(
&
dev_info
);
pcmcia_unregister_driver
(
&
dtl1_driver
);
/* XXX: this really needs to move into generic code.. */
while
(
dev_list
!=
NULL
)
dtl1_detach
(
dev_list
);
}
module_init
(
init_dtl1_cs
);
module_exit
(
exit_dtl1_cs
);
drivers/bluetooth/hci_usb.c
View file @
2df06ce5
/*
BlueZ - Bluetooth protocol stack for Linux
HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
...
...
@@ -23,20 +24,17 @@
*/
/*
* Bluetooth HCI USB driver.
* Based on original USB Bluetooth driver for Linux kernel
* Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
*
* $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $
*/
#define VERSION "2.
1
"
#define VERSION "2.
4
"
#include <linux/config.h>
#include <linux/module.h>
#define __KERNEL_SYSCALLS__
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
...
...
@@ -49,17 +47,15 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/kmod.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_usb.h"
#
define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1)
#
include "hci_usb.h"
#ifndef
CONFIG_BT_HCI
USB_DEBUG
#ifndef
HCI_
USB_DEBUG
#undef BT_DBG
#define BT_DBG( A... )
#undef BT_DMP
...
...
@@ -67,8 +63,8 @@
#endif
#ifndef CONFIG_BT_USB_ZERO_PACKET
#undef U
R
B_ZERO_PACKET
#define U
R
B_ZERO_PACKET 0
#undef U
S
B_ZERO_PACKET
#define U
S
B_ZERO_PACKET 0
#endif
static
struct
usb_driver
hci_usb_driver
;
...
...
@@ -80,6 +76,9 @@ static struct usb_device_id bluetooth_ids[] = {
/* Ericsson with non-standard id */
{
USB_DEVICE
(
0x0bdb
,
0x1002
)
},
/* Bluetooth Ultraport Module from IBM */
{
USB_DEVICE
(
0x04bf
,
0x030a
)
},
{
}
/* Terminating entry */
};
...
...
@@ -92,108 +91,196 @@ static struct usb_device_id ignore_ids[] = {
{
}
/* Terminating entry */
};
static
void
hci_usb_interrupt
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
);
struct
_urb
*
_urb_alloc
(
int
isoc
,
int
gfp
)
{
struct
_urb
*
_urb
=
kmalloc
(
sizeof
(
struct
_urb
)
+
sizeof
(
struct
usb_iso_packet_descriptor
)
*
isoc
,
gfp
);
if
(
_urb
)
{
memset
(
_urb
,
0
,
sizeof
(
*
_urb
));
_urb
->
urb
.
count
=
(
atomic_t
)
ATOMIC_INIT
(
1
);
spin_lock_init
(
&
_urb
->
urb
.
lock
);
}
return
_urb
;
}
struct
_urb
*
_urb_dequeue
(
struct
_urb_queue
*
q
)
{
struct
_urb
*
_urb
=
NULL
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
q
->
lock
,
flags
);
{
struct
list_head
*
head
=
&
q
->
head
;
struct
list_head
*
next
=
head
->
next
;
if
(
next
!=
head
)
{
_urb
=
list_entry
(
next
,
struct
_urb
,
list
);
list_del
(
next
);
_urb
->
queue
=
NULL
;
}
}
spin_unlock_irqrestore
(
&
q
->
lock
,
flags
);
return
_urb
;
}
static
void
hci_usb_rx_complete
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
);
static
void
hci_usb_tx_complete
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
);
static
struct
urb
*
hci_usb_get_completed
(
struct
hci_usb
*
husb
)
#define __pending_tx(husb, type) (&husb->pending_tx[type-1])
#define __pending_q(husb, type) (&husb->pending_q[type-1])
#define __completed_q(husb, type) (&husb->completed_q[type-1])
#define __transmit_q(husb, type) (&husb->transmit_q[type-1])
#define __reassembly(husb, type) (husb->reassembly[type-1])
static
inline
struct
_urb
*
__get_completed
(
struct
hci_usb
*
husb
,
int
type
)
{
struct
sk_buff
*
skb
;
struct
urb
*
urb
=
NULL
;
return
_urb_dequeue
(
__completed_q
(
husb
,
type
));
}
skb
=
skb_dequeue
(
&
husb
->
completed_q
);
if
(
skb
)
{
urb
=
((
struct
hci_usb_scb
*
)
skb
->
cb
)
->
urb
;
kfree_skb
(
skb
);
}
#ifdef CONFIG_BT_USB_SCO
static
void
__fill_isoc_desc
(
struct
urb
*
urb
,
int
len
,
int
mtu
)
{
int
offset
=
0
,
i
;
BT_DBG
(
"%s urb %p"
,
husb
->
hdev
.
name
,
urb
);
return
urb
;
BT_DBG
(
"len %d mtu %d"
,
len
,
mtu
);
for
(
i
=
0
;
i
<
HCI_MAX_ISOC_FRAMES
&&
len
>=
mtu
;
i
++
,
offset
+=
mtu
,
len
-=
mtu
)
{
urb
->
iso_frame_desc
[
i
].
offset
=
offset
;
urb
->
iso_frame_desc
[
i
].
length
=
mtu
;
BT_DBG
(
"desc %d offset %d len %d"
,
i
,
offset
,
mtu
);
}
if
(
len
&&
i
<
HCI_MAX_ISOC_FRAMES
)
{
urb
->
iso_frame_desc
[
i
].
offset
=
offset
;
urb
->
iso_frame_desc
[
i
].
length
=
len
;
BT_DBG
(
"desc %d offset %d len %d"
,
i
,
offset
,
len
);
i
++
;
}
urb
->
number_of_packets
=
i
;
}
#endif
static
int
hci_usb_
enable_intr
(
struct
hci_usb
*
husb
)
static
int
hci_usb_
intr_rx_submit
(
struct
hci_usb
*
husb
)
{
struct
_urb
*
_urb
;
struct
urb
*
urb
;
int
pipe
,
size
;
int
err
,
pipe
,
interval
,
size
;
void
*
buf
;
BT_DBG
(
"%s"
,
husb
->
hdev
.
name
);
if
(
!
(
urb
=
usb_alloc_urb
(
0
,
GFP_KERNEL
)))
size
=
husb
->
intr_in_ep
->
desc
.
wMaxPacketSize
;
buf
=
kmalloc
(
size
,
GFP_ATOMIC
);
if
(
!
buf
)
return
-
ENOMEM
;
if
(
!
(
buf
=
kmalloc
(
HCI_MAX_EVENT_SIZE
,
GFP_KERNEL
)))
{
usb_free_urb
(
urb
);
_urb
=
_urb_alloc
(
0
,
GFP_ATOMIC
);
if
(
!
_urb
)
{
kfree
(
buf
);
return
-
ENOMEM
;
}
_urb
->
type
=
HCI_EVENT_PKT
;
_urb_queue_tail
(
__pending_q
(
husb
,
_urb
->
type
),
_urb
);
husb
->
intr_urb
=
urb
;
pipe
=
usb_rcvintpipe
(
husb
->
udev
,
husb
->
intr_ep
);
size
=
usb_maxpacket
(
husb
->
udev
,
pipe
,
usb_pipeout
(
pipe
));
usb_fill_int_urb
(
urb
,
husb
->
udev
,
pipe
,
buf
,
size
,
hci_usb_interrupt
,
husb
,
husb
->
intr_interval
);
urb
=
&
_urb
->
urb
;
pipe
=
usb_rcvintpipe
(
husb
->
udev
,
husb
->
intr_in_ep
->
desc
.
bEndpointAddress
);
interval
=
husb
->
intr_in_ep
->
desc
.
bInterval
;
usb_fill_int_urb
(
urb
,
husb
->
udev
,
pipe
,
buf
,
size
,
hci_usb_rx_complete
,
husb
,
interval
);
return
usb_submit_urb
(
urb
,
GFP_KERNEL
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s intr rx submit failed urb %p err %d"
,
husb
->
hdev
.
name
,
urb
,
err
);
_urb_unlink
(
_urb
);
_urb_free
(
_urb
);
kfree
(
buf
);
}
return
err
;
}
static
int
hci_usb_
disable_intr
(
struct
hci_usb
*
husb
)
static
int
hci_usb_
bulk_rx_submit
(
struct
hci_usb
*
husb
)
{
struct
urb
*
urb
=
husb
->
intr
_urb
;
struct
sk_buff
*
sk
b
;
BT_DBG
(
"%s"
,
husb
->
hdev
.
name
)
;
struct
_urb
*
_urb
;
struct
urb
*
ur
b
;
int
err
,
pipe
,
size
=
HCI_MAX_FRAME_SIZE
;
void
*
buf
;
usb_unlink_urb
(
urb
);
usb_free_urb
(
urb
);
husb
->
intr_urb
=
NULL
;
buf
=
kmalloc
(
size
,
GFP_ATOMIC
);
if
(
!
buf
)
return
-
ENOMEM
;
skb
=
husb
->
intr_skb
;
if
(
sk
b
)
{
husb
->
intr_skb
=
NULL
;
kfree_skb
(
skb
)
;
_urb
=
_urb_alloc
(
0
,
GFP_ATOMIC
)
;
if
(
!
_ur
b
)
{
kfree
(
buf
)
;
return
-
ENOMEM
;
}
_urb
->
type
=
HCI_ACLDATA_PKT
;
_urb_queue_tail
(
__pending_q
(
husb
,
_urb
->
type
),
_urb
);
return
0
;
urb
=
&
_urb
->
urb
;
pipe
=
usb_rcvbulkpipe
(
husb
->
udev
,
husb
->
bulk_in_ep
->
desc
.
bEndpointAddress
);
usb_fill_bulk_urb
(
urb
,
husb
->
udev
,
pipe
,
buf
,
size
,
hci_usb_rx_complete
,
husb
);
urb
->
transfer_flags
=
0
;
BT_DBG
(
"%s urb %p"
,
husb
->
hdev
.
name
,
urb
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s bulk rx submit failed urb %p err %d"
,
husb
->
hdev
.
name
,
urb
,
err
);
_urb_unlink
(
_urb
);
_urb_free
(
_urb
);
kfree
(
buf
);
}
return
err
;
}
static
int
hci_usb_rx_submit
(
struct
hci_usb
*
husb
,
struct
urb
*
urb
)
#ifdef CONFIG_BT_USB_SCO
static
int
hci_usb_isoc_rx_submit
(
struct
hci_usb
*
husb
)
{
struct
hci_usb_scb
*
scb
;
struct
sk_buff
*
skb
;
int
pipe
,
size
,
err
;
struct
_urb
*
_urb
;
struct
urb
*
urb
;
int
err
,
mtu
,
size
;
void
*
buf
;
if
(
!
urb
&&
!
(
urb
=
usb_alloc_urb
(
0
,
GFP_ATOMIC
)))
return
-
ENOMEM
;
mtu
=
husb
->
isoc_in_ep
->
desc
.
wMaxPacketSize
;
size
=
mtu
*
HCI_MAX_ISOC_FRAMES
;
size
=
HCI_MAX_FRAME_SIZE
;
buf
=
kmalloc
(
size
,
GFP_ATOMIC
);
if
(
!
buf
)
return
-
ENOMEM
;
if
(
!
(
skb
=
bt_skb_alloc
(
size
,
GFP_ATOMIC
)))
{
usb_free_urb
(
urb
);
_urb
=
_urb_alloc
(
HCI_MAX_ISOC_FRAMES
,
GFP_ATOMIC
);
if
(
!
_urb
)
{
kfree
(
buf
);
return
-
ENOMEM
;
}
BT_DBG
(
"%s urb %p"
,
husb
->
hdev
.
name
,
urb
);
_urb
->
type
=
HCI_SCODATA_PKT
;
_urb_queue_tail
(
__pending_q
(
husb
,
_urb
->
type
),
_
urb
);
skb
->
dev
=
(
void
*
)
&
husb
->
hdev
;
skb
->
pkt_type
=
HCI_ACLDATA_PKT
;
urb
=
&
_urb
->
urb
;
scb
=
(
struct
hci_usb_scb
*
)
skb
->
cb
;
scb
->
urb
=
urb
;
urb
->
context
=
husb
;
urb
->
dev
=
husb
->
udev
;
urb
->
pipe
=
usb_rcvisocpipe
(
husb
->
udev
,
husb
->
isoc_in_ep
->
desc
.
bEndpointAddress
);
urb
->
complete
=
hci_usb_rx_complete
;
pipe
=
usb_rcvbulkpipe
(
husb
->
udev
,
husb
->
bulk_in_ep
);
urb
->
transfer_buffer_length
=
size
;
urb
->
transfer_buffer
=
buf
;
urb
->
transfer_flags
=
URB_ISO_ASAP
;
usb_fill_bulk_urb
(
urb
,
husb
->
udev
,
pipe
,
skb
->
data
,
size
,
hci_usb_rx_complete
,
skb
);
__fill_isoc_desc
(
urb
,
size
,
mtu
);
BT_DBG
(
"%s urb %p"
,
husb
->
hdev
.
name
,
urb
);
skb_queue_tail
(
&
husb
->
pending_q
,
skb
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s
bulk
rx submit failed urb %p err %d"
,
BT_ERR
(
"%s
isoc
rx submit failed urb %p err %d"
,
husb
->
hdev
.
name
,
urb
,
err
);
skb_unlink
(
skb
);
usb_free_urb
(
urb
);
_urb_unlink
(
_urb
);
_urb_free
(
_urb
);
kfree
(
buf
);
}
return
err
;
}
#endif
/* Initialize device */
static
int
hci_usb_open
(
struct
hci_dev
*
hdev
)
...
...
@@ -209,13 +296,17 @@ static int hci_usb_open(struct hci_dev *hdev)
write_lock_irqsave
(
&
husb
->
completion_lock
,
flags
);
err
=
hci_usb_
enable_intr
(
husb
);
err
=
hci_usb_
intr_rx_submit
(
husb
);
if
(
!
err
)
{
for
(
i
=
0
;
i
<
HCI_MAX_BULK_RX
;
i
++
)
hci_usb_rx_submit
(
husb
,
NULL
);
}
else
hci_usb_bulk_rx_submit
(
husb
);
#ifdef CONFIG_BT_USB_SCO
hci_usb_isoc_rx_submit
(
husb
);
#endif
}
else
{
clear_bit
(
HCI_RUNNING
,
&
hdev
->
flags
);
}
write_unlock_irqrestore
(
&
husb
->
completion_lock
,
flags
);
return
err
;
...
...
@@ -225,29 +316,52 @@ static int hci_usb_open(struct hci_dev *hdev)
static
int
hci_usb_flush
(
struct
hci_dev
*
hdev
)
{
struct
hci_usb
*
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
int
i
;
BT_DBG
(
"%s"
,
hdev
->
name
);
skb_queue_purge
(
&
husb
->
cmd_q
);
skb_queue_purge
(
&
husb
->
acl_q
);
for
(
i
=
0
;
i
<
4
;
i
++
)
skb_queue_purge
(
&
husb
->
transmit_q
[
i
]
);
return
0
;
}
static
inline
void
hci_usb_unlink_urbs
(
struct
hci_usb
*
husb
)
static
void
hci_usb_unlink_urbs
(
struct
hci_usb
*
husb
)
{
struct
sk_buff
*
skb
;
struct
urb
*
urb
;
int
i
;
BT_DBG
(
"%s"
,
husb
->
hdev
.
name
);
while
((
skb
=
skb_dequeue
(
&
husb
->
pending_q
)))
{
urb
=
((
struct
hci_usb_scb
*
)
skb
->
cb
)
->
urb
;
usb_unlink_urb
(
urb
);
kfree_skb
(
skb
);
}
for
(
i
=
0
;
i
<
4
;
i
++
)
{
struct
_urb
*
_urb
;
struct
urb
*
urb
;
/* Kill pending requests */
while
((
_urb
=
_urb_dequeue
(
&
husb
->
pending_q
[
i
])))
{
urb
=
&
_urb
->
urb
;
BT_DBG
(
"%s unlinking _urb %p type %d urb %p"
,
husb
->
hdev
.
name
,
_urb
,
_urb
->
type
,
urb
);
usb_unlink_urb
(
urb
);
_urb_queue_tail
(
__completed_q
(
husb
,
_urb
->
type
),
_urb
);
}
/* Release completed requests */
while
((
_urb
=
_urb_dequeue
(
&
husb
->
completed_q
[
i
])))
{
urb
=
&
_urb
->
urb
;
BT_DBG
(
"%s freeing _urb %p type %d urb %p"
,
husb
->
hdev
.
name
,
_urb
,
_urb
->
type
,
urb
);
if
(
urb
->
setup_packet
)
kfree
(
urb
->
setup_packet
);
if
(
urb
->
transfer_buffer
)
kfree
(
urb
->
transfer_buffer
);
_urb_free
(
_urb
);
}
while
((
urb
=
hci_usb_get_completed
(
husb
)))
usb_free_urb
(
urb
);
/* Release reassembly buffers */
if
(
husb
->
reassembly
[
i
])
{
kfree_skb
(
husb
->
reassembly
[
i
]);
husb
->
reassembly
[
i
]
=
NULL
;
}
}
}
/* Close device */
...
...
@@ -263,7 +377,6 @@ static int hci_usb_close(struct hci_dev *hdev)
write_lock_irqsave
(
&
husb
->
completion_lock
,
flags
);
hci_usb_disable_intr
(
husb
);
hci_usb_unlink_urbs
(
husb
);
hci_usb_flush
(
hdev
);
...
...
@@ -271,104 +384,157 @@ static int hci_usb_close(struct hci_dev *hdev)
return
0
;
}
static
in
line
int
hci_usb_send_ctrl
(
struct
hci_usb
*
husb
,
struct
sk_buff
*
sk
b
)
static
in
t
__tx_submit
(
struct
hci_usb
*
husb
,
struct
_urb
*
_ur
b
)
{
struct
hci_usb_scb
*
scb
=
(
void
*
)
skb
->
cb
;
struct
urb
*
urb
=
hci_usb_get_completed
(
husb
);
struct
usb_ctrlrequest
*
cr
;
int
pipe
,
err
;
if
(
!
urb
&&
!
(
urb
=
usb_alloc_urb
(
0
,
GFP_ATOMIC
)))
return
-
ENOMEM
;
struct
urb
*
urb
=
&
_urb
->
urb
;
int
err
;
if
(
!
(
cr
=
kmalloc
(
sizeof
(
*
cr
),
GFP_ATOMIC
)))
{
usb_free_urb
(
urb
);
return
-
ENOMEM
;
}
BT_DBG
(
"%s urb %p type %d"
,
husb
->
hdev
.
name
,
urb
,
_urb
->
type
);
pipe
=
usb_sndctrlpipe
(
husb
->
udev
,
0
);
_urb_queue_tail
(
__pending_q
(
husb
,
_urb
->
type
),
_urb
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s tx submit failed urb %p type %d err %d"
,
husb
->
hdev
.
name
,
urb
,
_urb
->
type
,
err
);
_urb_unlink
(
_urb
);
_urb_queue_tail
(
__completed_q
(
husb
,
_urb
->
type
),
_urb
);
}
else
atomic_inc
(
__pending_tx
(
husb
,
_urb
->
type
));
cr
->
bRequestType
=
HCI_CTRL_REQ
;
cr
->
bRequest
=
0
;
cr
->
wIndex
=
0
;
cr
->
wValue
=
0
;
cr
->
wLength
=
__cpu_to_le16
(
skb
->
len
);
return
err
;
}
usb_fill_control_urb
(
urb
,
husb
->
udev
,
pipe
,
(
void
*
)
cr
,
skb
->
data
,
skb
->
len
,
hci_usb_tx_complete
,
skb
);
static
inline
int
hci_usb_send_ctrl
(
struct
hci_usb
*
husb
,
struct
sk_buff
*
skb
)
{
struct
_urb
*
_urb
=
__get_completed
(
husb
,
skb
->
pkt_type
);
struct
usb_ctrlrequest
*
dr
;
struct
urb
*
urb
;
BT_DBG
(
"%s urb %p len %d"
,
husb
->
hdev
.
name
,
urb
,
skb
->
len
);
if
(
!
_urb
)
{
_urb
=
_urb_alloc
(
0
,
GFP_ATOMIC
);
if
(
!
_urb
)
return
-
ENOMEM
;
_urb
->
type
=
skb
->
pkt_type
;
scb
->
urb
=
urb
;
dr
=
kmalloc
(
sizeof
(
*
dr
),
GFP_ATOMIC
);
if
(
!
dr
)
{
_urb_free
(
_urb
);
return
-
ENOMEM
;
}
}
else
dr
=
(
void
*
)
_urb
->
urb
.
setup_packet
;
skb_queue_tail
(
&
husb
->
pending_q
,
skb
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s ctrl tx submit failed urb %p err %d"
,
husb
->
hdev
.
name
,
urb
,
err
);
skb_unlink
(
skb
);
usb_free_urb
(
urb
);
kfree
(
cr
);
}
return
err
;
dr
->
bRequestType
=
HCI_CTRL_REQ
;
dr
->
bRequest
=
0
;
dr
->
wIndex
=
0
;
dr
->
wValue
=
0
;
dr
->
wLength
=
__cpu_to_le16
(
skb
->
len
);
urb
=
&
_urb
->
urb
;
usb_fill_control_urb
(
urb
,
husb
->
udev
,
usb_sndctrlpipe
(
husb
->
udev
,
0
),
(
void
*
)
dr
,
skb
->
data
,
skb
->
len
,
hci_usb_tx_complete
,
husb
);
BT_DBG
(
"%s skb %p len %d"
,
husb
->
hdev
.
name
,
skb
,
skb
->
len
);
_urb
->
priv
=
skb
;
return
__tx_submit
(
husb
,
_urb
);
}
static
inline
int
hci_usb_send_bulk
(
struct
hci_usb
*
husb
,
struct
sk_buff
*
skb
)
{
struct
hci_usb_scb
*
scb
=
(
void
*
)
skb
->
cb
;
struct
urb
*
urb
=
hci_usb_get_completed
(
husb
)
;
int
pipe
,
err
;
struct
_urb
*
_urb
=
__get_completed
(
husb
,
skb
->
pkt_type
)
;
struct
urb
*
urb
;
int
pipe
;
if
(
!
urb
&&
!
(
urb
=
usb_alloc_urb
(
0
,
GFP_ATOMIC
)))
return
-
ENOMEM
;
if
(
!
_urb
)
{
_urb
=
_urb_alloc
(
0
,
GFP_ATOMIC
);
if
(
!
_urb
)
return
-
ENOMEM
;
_urb
->
type
=
skb
->
pkt_type
;
}
pipe
=
usb_sndbulkpipe
(
husb
->
udev
,
husb
->
bulk_out_ep
)
;
usb_fill_bulk_urb
(
urb
,
husb
->
udev
,
pipe
,
skb
->
data
,
skb
->
len
,
hci_usb_tx_complete
,
sk
b
);
urb
->
transfer_flags
=
U
R
B_ZERO_PACKET
;
urb
=
&
_urb
->
urb
;
pipe
=
usb_sndbulkpipe
(
husb
->
udev
,
husb
->
bulk_out_ep
->
desc
.
bEndpointAddress
);
usb_fill_bulk_urb
(
urb
,
husb
->
udev
,
pipe
,
skb
->
data
,
skb
->
len
,
hci_usb_tx_complete
,
hus
b
);
urb
->
transfer_flags
=
U
S
B_ZERO_PACKET
;
BT_DBG
(
"%s
urb %p len %d"
,
husb
->
hdev
.
name
,
ur
b
,
skb
->
len
);
BT_DBG
(
"%s
skb %p len %d"
,
husb
->
hdev
.
name
,
sk
b
,
skb
->
len
);
scb
->
urb
=
urb
;
_urb
->
priv
=
skb
;
return
__tx_submit
(
husb
,
_urb
);
}
skb_queue_tail
(
&
husb
->
pending_q
,
skb
);
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
)
{
BT_ERR
(
"%s bulk tx submit failed urb %p err %d"
,
husb
->
hdev
.
name
,
urb
,
err
);
skb_unlink
(
skb
);
usb_free_urb
(
urb
);
#ifdef CONFIG_BT_USB_SCO
static
inline
int
hci_usb_send_isoc
(
struct
hci_usb
*
husb
,
struct
sk_buff
*
skb
)
{
struct
_urb
*
_urb
=
__get_completed
(
husb
,
skb
->
pkt_type
);
struct
urb
*
urb
;
if
(
!
_urb
)
{
_urb
=
_urb_alloc
(
HCI_MAX_ISOC_FRAMES
,
GFP_ATOMIC
);
if
(
!
_urb
)
return
-
ENOMEM
;
_urb
->
type
=
skb
->
pkt_type
;
}
return
err
;
BT_DBG
(
"%s skb %p len %d"
,
husb
->
hdev
.
name
,
skb
,
skb
->
len
);
urb
=
&
_urb
->
urb
;
urb
->
context
=
husb
;
urb
->
dev
=
husb
->
udev
;
urb
->
pipe
=
usb_sndisocpipe
(
husb
->
udev
,
husb
->
isoc_out_ep
->
desc
.
bEndpointAddress
);
urb
->
complete
=
hci_usb_tx_complete
;
urb
->
transfer_flags
=
URB_ISO_ASAP
;
urb
->
transfer_buffer
=
skb
->
data
;
urb
->
transfer_buffer_length
=
skb
->
len
;
__fill_isoc_desc
(
urb
,
skb
->
len
,
husb
->
isoc_out_ep
->
desc
.
wMaxPacketSize
);
_urb
->
priv
=
skb
;
return
__tx_submit
(
husb
,
_urb
);
}
#endif
static
void
hci_usb_tx_process
(
struct
hci_usb
*
husb
)
{
struct
sk_buff_head
*
q
;
struct
sk_buff
*
skb
;
BT_DBG
(
"%s"
,
husb
->
hdev
.
name
);
do
{
clear_bit
(
HCI_USB_TX_WAKEUP
,
&
husb
->
state
);
/* Process command queue */
q
=
__transmit_q
(
husb
,
HCI_COMMAND_PKT
);
if
(
!
atomic_read
(
__pending_tx
(
husb
,
HCI_COMMAND_PKT
))
&&
(
skb
=
skb_dequeue
(
q
)))
{
if
(
hci_usb_send_ctrl
(
husb
,
skb
)
<
0
)
skb_queue_head
(
q
,
skb
);
}
#ifdef CONFIG_BT_USB_SCO
/* Process SCO queue */
q
=
__transmit_q
(
husb
,
HCI_SCODATA_PKT
);
if
(
!
atomic_read
(
__pending_tx
(
husb
,
HCI_SCODATA_PKT
))
&&
(
skb
=
skb_dequeue
(
q
)))
{
if
(
hci_usb_send_isoc
(
husb
,
skb
)
<
0
)
skb_queue_head
(
q
,
skb
);
}
#endif
/* Process ACL queue */
while
(
skb_queue_len
(
&
husb
->
pending_q
)
<
HCI_MAX_PENDING
&&
(
skb
=
skb_dequeue
(
&
husb
->
acl_q
)))
{
q
=
__transmit_q
(
husb
,
HCI_ACLDATA_PKT
);
while
(
atomic_read
(
__pending_tx
(
husb
,
HCI_ACLDATA_PKT
))
<
HCI_MAX_BULK_TX
&&
(
skb
=
skb_dequeue
(
q
)))
{
if
(
hci_usb_send_bulk
(
husb
,
skb
)
<
0
)
{
skb_queue_head
(
&
husb
->
acl_
q
,
skb
);
skb_queue_head
(
q
,
skb
);
break
;
}
}
/* Process command queue */
if
(
!
test_bit
(
HCI_USB_CTRL_TX
,
&
husb
->
state
)
&&
(
skb
=
skb_dequeue
(
&
husb
->
cmd_q
))
!=
NULL
)
{
set_bit
(
HCI_USB_CTRL_TX
,
&
husb
->
state
);
if
(
hci_usb_send_ctrl
(
husb
,
skb
)
<
0
)
{
skb_queue_head
(
&
husb
->
cmd_q
,
skb
);
clear_bit
(
HCI_USB_CTRL_TX
,
&
husb
->
state
);
}
}
}
while
(
test_bit
(
HCI_USB_TX_WAKEUP
,
&
husb
->
state
));
}
...
...
@@ -383,7 +549,7 @@ static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
}
/* Send frames from HCI layer */
int
hci_usb_send_frame
(
struct
sk_buff
*
skb
)
static
int
hci_usb_send_frame
(
struct
sk_buff
*
skb
)
{
struct
hci_dev
*
hdev
=
(
struct
hci_dev
*
)
skb
->
dev
;
struct
hci_usb
*
husb
;
...
...
@@ -396,247 +562,227 @@ int hci_usb_send_frame(struct sk_buff *skb)
if
(
!
test_bit
(
HCI_RUNNING
,
&
hdev
->
flags
))
return
-
EBUSY
;
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
BT_DBG
(
"%s type %d len %d"
,
hdev
->
name
,
skb
->
pkt_type
,
skb
->
len
);
read_lock
(
&
husb
->
completion_lock
)
;
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
switch
(
skb
->
pkt_type
)
{
case
HCI_COMMAND_PKT
:
skb_queue_tail
(
&
husb
->
cmd_q
,
skb
);
hdev
->
stat
.
cmd_tx
++
;
break
;
case
HCI_ACLDATA_PKT
:
skb_queue_tail
(
&
husb
->
acl_q
,
skb
);
hdev
->
stat
.
acl_tx
++
;
break
;
#ifdef CONFIG_BT_USB_SCO
case
HCI_SCODATA_PKT
:
hdev
->
stat
.
sco_tx
++
;
break
;
#endif
default:
kfree_skb
(
skb
);
break
;
return
0
;
}
read_lock
(
&
husb
->
completion_lock
);
skb_queue_tail
(
__transmit_q
(
husb
,
skb
->
pkt_type
),
skb
);
hci_usb_tx_wakeup
(
husb
);
read_unlock
(
&
husb
->
completion_lock
);
return
0
;
}
static
void
hci_usb_interrupt
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
static
inline
int
__recv_frame
(
struct
hci_usb
*
husb
,
int
type
,
void
*
data
,
int
count
)
{
struct
hci_usb
*
husb
=
(
void
*
)
urb
->
context
;
struct
hci_usb_scb
*
scb
;
struct
sk_buff
*
skb
;
struct
hci_event_hdr
*
eh
;
__u8
*
data
=
urb
->
transfer_buffer
;
int
count
=
urb
->
actual_length
;
int
len
=
HCI_EVENT_HDR_SIZE
;
int
status
;
BT_DBG
(
"%s type %d data %p count %d"
,
husb
->
hdev
.
name
,
type
,
data
,
count
);
BT_DBG
(
"%s urb %p count %d"
,
husb
->
hdev
.
name
,
urb
,
count
)
;
husb
->
hdev
.
stat
.
byte_rx
+=
count
;
if
(
!
test_bit
(
HCI_RUNNING
,
&
husb
->
hdev
.
flags
))
return
;
while
(
count
)
{
struct
sk_buff
*
skb
=
__reassembly
(
husb
,
type
);
struct
{
int
expect
;
}
*
scb
;
int
len
=
0
;
if
(
!
skb
)
{
/* Start of the frame */
switch
(
type
)
{
case
HCI_EVENT_PKT
:
if
(
count
>=
HCI_EVENT_HDR_SIZE
)
{
struct
hci_event_hdr
*
h
=
data
;
len
=
HCI_EVENT_HDR_SIZE
+
h
->
plen
;
}
else
return
-
EILSEQ
;
break
;
switch
(
urb
->
status
)
{
case
0
:
/* success */
break
;
case
-
ECONNRESET
:
case
-
ENOENT
:
case
-
ESHUTDOWN
:
/* this urb is terminated, clean up */
BT_DBG
(
"%s urb shutting down with status: %d"
,
husb
->
hdev
.
name
,
urb
->
status
);
return
;
default:
BT_ERR
(
"%s nonzero urb status received: %d"
,
husb
->
hdev
.
name
,
urb
->
status
);
goto
exit
;
}
case
HCI_ACLDATA_PKT
:
if
(
count
>=
HCI_ACL_HDR_SIZE
)
{
struct
hci_acl_hdr
*
h
=
data
;
len
=
HCI_ACL_HDR_SIZE
+
__le16_to_cpu
(
h
->
dlen
);
}
else
return
-
EILSEQ
;
break
;
#ifdef CONFIG_BT_USB_SCO
case
HCI_SCODATA_PKT
:
if
(
count
>=
HCI_SCO_HDR_SIZE
)
{
struct
hci_sco_hdr
*
h
=
data
;
len
=
HCI_SCO_HDR_SIZE
+
h
->
dlen
;
}
else
return
-
EILSEQ
;
break
;
#endif
}
BT_DBG
(
"new packet len %d"
,
len
);
skb
=
bt_skb_alloc
(
len
,
GFP_ATOMIC
);
if
(
!
skb
)
{
BT_ERR
(
"%s no memory for the packet"
,
husb
->
hdev
.
name
);
return
-
ENOMEM
;
}
skb
->
dev
=
(
void
*
)
&
husb
->
hdev
;
skb
->
pkt_type
=
type
;
__reassembly
(
husb
,
type
)
=
skb
;
scb
=
(
void
*
)
skb
->
cb
;
scb
->
expect
=
len
;
}
else
{
/* Continuation */
scb
=
(
void
*
)
skb
->
cb
;
len
=
scb
->
expect
;
}
if
(
!
count
)
{
BT_DBG
(
"%s intr status %d, count %d"
,
husb
->
hdev
.
name
,
urb
->
status
,
count
);
goto
exit
;
}
len
=
min
(
len
,
count
);
memcpy
(
skb_put
(
skb
,
len
),
data
,
len
);
read_lock
(
&
husb
->
completion_lock
);
husb
->
hdev
.
stat
.
byte_rx
+=
count
;
scb
->
expect
-=
len
;
if
(
!
scb
->
expect
)
{
/* Complete frame */
__reassembly
(
husb
,
type
)
=
NULL
;
hci_recv_frame
(
skb
);
}
if
(
!
(
skb
=
husb
->
intr_skb
))
{
/* Start of the frame */
if
(
count
<
HCI_EVENT_HDR_SIZE
)
goto
bad_len
;
count
-=
len
;
data
+=
len
;
}
return
0
;
}
static
void
hci_usb_rx_complete
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
{
struct
_urb
*
_urb
=
container_of
(
urb
,
struct
_urb
,
urb
);
struct
hci_usb
*
husb
=
(
void
*
)
urb
->
context
;
struct
hci_dev
*
hdev
=
&
husb
->
hdev
;
int
err
,
count
=
urb
->
actual_length
;
eh
=
(
struct
hci_event_hdr
*
)
data
;
len
=
eh
->
plen
+
HCI_EVENT_HDR_SIZE
;
BT_DBG
(
"%s urb %p type %d status %d count %d flags %x"
,
hdev
->
name
,
urb
,
_urb
->
type
,
urb
->
status
,
count
,
urb
->
transfer_flags
)
;
if
(
count
>
len
)
goto
bad_le
n
;
if
(
!
test_bit
(
HCI_RUNNING
,
&
hdev
->
flags
)
)
retur
n
;
skb
=
bt_skb_alloc
(
len
,
GFP_ATOMIC
);
if
(
!
skb
)
{
BT_ERR
(
"%s no memory for event packet"
,
husb
->
hdev
.
name
);
goto
done
;
}
scb
=
(
void
*
)
skb
->
cb
;
read_lock
(
&
husb
->
completion_lock
);
skb
->
dev
=
(
void
*
)
&
husb
->
hdev
;
skb
->
pkt_type
=
HCI_EVENT_PKT
;
if
(
urb
->
status
||
!
count
)
goto
resubmit
;
husb
->
intr_skb
=
skb
;
scb
->
intr_len
=
len
;
if
(
_urb
->
type
==
HCI_SCODATA_PKT
)
{
#ifdef CONFIG_BT_USB_SCO
int
i
;
for
(
i
=
0
;
i
<
urb
->
number_of_packets
;
i
++
)
{
BT_DBG
(
"desc %d status %d offset %d len %d"
,
i
,
urb
->
iso_frame_desc
[
i
].
status
,
urb
->
iso_frame_desc
[
i
].
offset
,
urb
->
iso_frame_desc
[
i
].
actual_length
);
if
(
!
urb
->
iso_frame_desc
[
i
].
status
)
__recv_frame
(
husb
,
_urb
->
type
,
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
i
].
offset
,
urb
->
iso_frame_desc
[
i
].
actual_length
);
}
#else
;
#endif
}
else
{
/* Continuation */
scb
=
(
void
*
)
skb
->
cb
;
len
=
scb
->
intr_len
;
if
(
count
>
len
)
{
husb
->
intr_skb
=
NULL
;
kfree_skb
(
skb
);
goto
bad_len
;
err
=
__recv_frame
(
husb
,
_urb
->
type
,
urb
->
transfer_buffer
,
count
);
if
(
err
<
0
)
{
BT_ERR
(
"%s corrupted packet: type %d count %d"
,
husb
->
hdev
.
name
,
_urb
->
type
,
count
);
hdev
->
stat
.
err_rx
++
;
}
}
memcpy
(
skb_put
(
skb
,
count
),
data
,
count
);
scb
->
intr_len
-=
count
;
if
(
!
scb
->
intr_len
)
{
/* Complete frame */
husb
->
intr_skb
=
NULL
;
hci_recv_frame
(
skb
);
}
done:
read_unlock
(
&
husb
->
completion_lock
);
goto
exit
;
resubmit:
urb
->
dev
=
husb
->
udev
;
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
BT_DBG
(
"%s urb %p type %d resubmit status %d"
,
hdev
->
name
,
urb
,
_urb
->
type
,
err
);
bad_len:
BT_ERR
(
"%s bad frame len %d expected %d"
,
husb
->
hdev
.
name
,
count
,
len
);
husb
->
hdev
.
stat
.
err_rx
++
;
read_unlock
(
&
husb
->
completion_lock
);
exit:
status
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
status
)
BT_ERR
(
"%s usb_submit_urb failed with result %d"
,
husb
->
hdev
.
name
,
status
);
}
static
void
hci_usb_tx_complete
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
{
struct
sk_buff
*
skb
=
(
struct
sk_buff
*
)
urb
->
context
;
struct
hci_
dev
*
hdev
=
(
struct
hci_dev
*
)
skb
->
dev
;
struct
hci_
usb
*
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
struct
_urb
*
_urb
=
container_of
(
urb
,
struct
_urb
,
urb
)
;
struct
hci_
usb
*
husb
=
(
void
*
)
urb
->
context
;
struct
hci_
dev
*
hdev
=
&
husb
->
hdev
;
BT_DBG
(
"%s urb %p status %d flags %x"
,
h
usb
->
hdev
.
name
,
urb
,
BT_DBG
(
"%s urb %p status %d flags %x"
,
h
dev
->
name
,
urb
,
urb
->
status
,
urb
->
transfer_flags
);
if
(
urb
->
pipe
==
usb_sndctrlpipe
(
husb
->
udev
,
0
))
{
kfree
(
urb
->
setup_packet
);
clear_bit
(
HCI_USB_CTRL_TX
,
&
husb
->
state
)
;
}
atomic_dec
(
__pending_tx
(
husb
,
_urb
->
type
));
urb
->
transfer_buffer
=
NULL
;
kfree_skb
((
struct
sk_buff
*
)
_urb
->
priv
);
if
(
!
test_bit
(
HCI_RUNNING
,
&
hdev
->
flags
))
return
;
read_lock
(
&
husb
->
completion_lock
);
if
(
!
urb
->
status
)
h
usb
->
hdev
.
stat
.
byte_tx
+=
skb
->
len
;
h
dev
->
stat
.
byte_tx
+=
urb
->
transfer_buffer_length
;
else
husb
->
hdev
.
stat
.
err_tx
++
;
skb_unlink
(
skb
);
skb_queue_tail
(
&
husb
->
completed_q
,
skb
);
hci_usb_tx_wakeup
(
husb
);
read_unlock
(
&
husb
->
completion_lock
);
return
;
}
static
void
hci_usb_rx_complete
(
struct
urb
*
urb
,
struct
pt_regs
*
regs
)
{
struct
sk_buff
*
skb
=
(
struct
sk_buff
*
)
urb
->
context
;
struct
hci_dev
*
hdev
=
(
struct
hci_dev
*
)
skb
->
dev
;
struct
hci_usb
*
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
int
status
,
count
=
urb
->
actual_length
;
struct
hci_acl_hdr
*
ah
;
int
dlen
,
size
;
BT_DBG
(
"%s urb %p status %d count %d flags %x"
,
husb
->
hdev
.
name
,
urb
,
urb
->
status
,
count
,
urb
->
transfer_flags
);
if
(
!
test_bit
(
HCI_RUNNING
,
&
hdev
->
flags
))
return
;
hdev
->
stat
.
err_tx
++
;
read_lock
(
&
husb
->
completion_lock
);
if
(
urb
->
status
||
!
count
)
goto
resubmit
;
husb
->
hdev
.
stat
.
byte_rx
+=
count
;
ah
=
(
struct
hci_acl_hdr
*
)
skb
->
data
;
dlen
=
__le16_to_cpu
(
ah
->
dlen
);
size
=
HCI_ACL_HDR_SIZE
+
dlen
;
_urb_unlink
(
_urb
);
_urb_queue_tail
(
__completed_q
(
husb
,
_urb
->
type
),
_urb
);
/* Verify frame len and completeness */
if
(
count
!=
size
)
{
BT_ERR
(
"%s corrupted ACL packet: count %d, dlen %d"
,
husb
->
hdev
.
name
,
count
,
dlen
);
bt_dump
(
"hci_usb"
,
skb
->
data
,
count
);
husb
->
hdev
.
stat
.
err_rx
++
;
goto
resubmit
;
}
skb_unlink
(
skb
);
skb_put
(
skb
,
count
);
hci_recv_frame
(
skb
);
hci_usb_rx_submit
(
husb
,
urb
);
read_unlock
(
&
husb
->
completion_lock
);
return
;
resubmit:
urb
->
dev
=
husb
->
udev
;
status
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
BT_DBG
(
"%s URB resubmit status %d"
,
husb
->
hdev
.
name
,
status
);
hci_usb_tx_wakeup
(
husb
);
read_unlock
(
&
husb
->
completion_lock
);
}
static
void
hci_usb_destruct
(
struct
hci_dev
*
hdev
)
{
struct
hci_usb
*
husb
;
if
(
!
hdev
)
return
;
struct
hci_usb
*
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
BT_DBG
(
"%s"
,
hdev
->
name
);
husb
=
(
struct
hci_usb
*
)
hdev
->
driver_data
;
kfree
(
husb
);
}
int
hci_usb_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
{
struct
usb_device
*
udev
=
interface_to_usbdev
(
intf
);
struct
usb_device
*
udev
=
interface_to_usbdev
(
intf
);
struct
usb_host_endpoint
*
bulk_out_ep
[
HCI_MAX_IFACE_NUM
];
struct
usb_host_endpoint
*
isoc_out_ep
[
HCI_MAX_IFACE_NUM
];
struct
usb_host_endpoint
*
bulk_in_ep
[
HCI_MAX_IFACE_NUM
];
struct
usb_host_endpoint
*
isoc_in_ep
[
HCI_MAX_IFACE_NUM
];
struct
usb_host_endpoint
*
intr_in_ep
[
HCI_MAX_IFACE_NUM
];
struct
usb_host_endpoint
*
ep
;
struct
usb_host_interface
*
uif
;
struct
usb_host_endpoint
*
ep
;
struct
usb_interface
*
iface
,
*
isoc_iface
;
struct
hci_usb
*
husb
;
struct
hci_dev
*
hdev
;
int
i
,
a
,
e
,
size
,
ifn
,
isoc_ifnum
,
isoc_alts
;
BT_DBG
(
"intf %p"
,
intf
);
BT_DBG
(
"udev %p ifnum %d"
,
udev
,
ifnum
);
iface
=
&
udev
->
actconfig
->
interface
[
0
];
/* Check our black list */
if
(
usb_match_id
(
intf
,
ignore_ids
))
...
...
@@ -679,6 +825,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
bulk_out_ep
[
i
]
=
ep
;
break
;
#ifdef CONFIG_BT_USB_SCO
case
USB_ENDPOINT_XFER_ISOC
:
if
(
ep
->
desc
.
wMaxPacketSize
<
size
)
break
;
...
...
@@ -693,6 +840,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
else
isoc_out_ep
[
i
]
=
ep
;
break
;
#endif
}
}
}
...
...
@@ -703,10 +851,12 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto
done
;
}
#ifdef CONFIG_BT_USB_SCO
if
(
!
isoc_in_ep
[
1
]
||
!
isoc_out_ep
[
1
])
{
BT_DBG
(
"Isoc endpoints not found"
);
isoc_iface
=
NULL
;
}
#endif
if
(
!
(
husb
=
kmalloc
(
sizeof
(
struct
hci_usb
),
GFP_KERNEL
)))
{
BT_ERR
(
"Can't allocate: control structure"
);
...
...
@@ -716,35 +866,36 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
memset
(
husb
,
0
,
sizeof
(
struct
hci_usb
));
husb
->
udev
=
udev
;
husb
->
bulk_out_ep
=
bulk_out_ep
[
0
]
->
desc
.
bEndpointAddress
;
husb
->
bulk_in_ep
=
bulk_in_ep
[
0
]
->
desc
.
bEndpointAddress
;
husb
->
intr_ep
=
intr_in_ep
[
0
]
->
desc
.
bEndpointAddress
;
husb
->
intr_interval
=
intr_in_ep
[
0
]
->
desc
.
bInterval
;
husb
->
bulk_out_ep
=
bulk_out_ep
[
0
];
husb
->
bulk_in_ep
=
bulk_in_ep
[
0
];
husb
->
intr_in_ep
=
intr_in_ep
[
0
];
#ifdef CONFIG_BT_USB_SCO
if
(
isoc_iface
)
{
BT_DBG
(
"isoc ifnum %d alts %d"
,
isoc_ifnum
,
isoc_alts
);
if
(
usb_set_interface
(
udev
,
isoc_ifnum
,
isoc_alts
))
{
BT_ERR
(
"Can't set isoc interface settings"
);
isoc_iface
=
NULL
;
}
usb_driver_claim_interface
(
&
hci_usb_driver
,
isoc_iface
,
husb
);
husb
->
isoc_iface
=
isoc_iface
;
husb
->
isoc_in_ep
=
isoc_in_ep
[
1
]
->
desc
.
bEndpointAddress
;
husb
->
isoc_out_ep
=
isoc_in_ep
[
1
]
->
desc
.
bEndpointAddress
;
husb
->
isoc_in_ep
=
isoc_in_ep
[
isoc_ifnum
];
husb
->
isoc_out_ep
=
isoc_out_ep
[
isoc_ifnum
];
}
husb
->
completion_lock
=
RW_LOCK_UNLOCKED
;
#endif
skb_queue_head_init
(
&
husb
->
acl_q
);
skb_queue_head_init
(
&
husb
->
cmd_q
);
skb_queue_head_init
(
&
husb
->
pending_q
);
skb_queue_head_init
(
&
husb
->
completed_q
);
husb
->
completion_lock
=
RW_LOCK_UNLOCKED
;
for
(
i
=
0
;
i
<
4
;
i
++
)
{
skb_queue_head_init
(
&
husb
->
transmit_q
[
i
]);
_urb_queue_init
(
&
husb
->
pending_q
[
i
]);
_urb_queue_init
(
&
husb
->
completed_q
[
i
]);
}
/* Initialize and register HCI device */
hdev
=
&
husb
->
hdev
;
hdev
->
type
=
HCI_USB
;
hdev
->
type
=
HCI_USB
;
hdev
->
driver_data
=
husb
;
hdev
->
open
=
hci_usb_open
;
...
...
@@ -754,7 +905,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
hdev
->
destruct
=
hci_usb_destruct
;
hdev
->
owner
=
THIS_MODULE
;
if
(
hci_register_dev
(
hdev
)
<
0
)
{
BT_ERR
(
"Can't register HCI device"
);
goto
probe_error
;
...
...
@@ -773,13 +924,12 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
static
void
hci_usb_disconnect
(
struct
usb_interface
*
intf
)
{
struct
hci_usb
*
husb
=
usb_get_intfdata
(
intf
);
struct
hci_dev
*
hdev
;
struct
hci_dev
*
hdev
=
&
husb
->
hdev
;
if
(
!
husb
)
return
;
usb_set_intfdata
(
intf
,
NULL
);
hdev
=
&
husb
->
hdev
;
BT_DBG
(
"%s"
,
hdev
->
name
);
hci_usb_close
(
hdev
);
...
...
@@ -792,17 +942,19 @@ static void hci_usb_disconnect(struct usb_interface *intf)
}
static
struct
usb_driver
hci_usb_driver
=
{
.
name
=
"hci_usb"
,
.
probe
=
hci_usb_probe
,
.
disconnect
=
hci_usb_disconnect
,
.
id_table
=
bluetooth_ids
.
name
=
"hci_usb"
,
.
probe
=
hci_usb_probe
,
.
disconnect
=
hci_usb_disconnect
,
.
id_table
=
bluetooth_ids
,
};
int
hci_usb_init
(
void
)
{
int
err
;
BT_INFO
(
"HCI USB driver ver %s"
,
VERSION
);
BT_INFO
(
"BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc"
,
VERSION
);
BT_INFO
(
"Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"
);
if
((
err
=
usb_register
(
&
hci_usb_driver
))
<
0
)
BT_ERR
(
"Failed to register HCI USB driver"
);
...
...
@@ -819,5 +971,5 @@ module_init(hci_usb_init);
module_exit
(
hci_usb_cleanup
);
MODULE_AUTHOR
(
"Maxim Krasnyansky <maxk@qualcomm.com>"
);
MODULE_DESCRIPTION
(
"Blue
tooth
HCI USB driver ver "
VERSION
);
MODULE_DESCRIPTION
(
"Blue
Z
HCI USB driver ver "
VERSION
);
MODULE_LICENSE
(
"GPL"
);
drivers/bluetooth/hci_usb.h
View file @
2df06ce5
/*
BlueZ - Bluetooth protocol stack for Linux
HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
...
...
@@ -40,40 +41,96 @@
#define HCI_MAX_BULK_TX 4
#define HCI_MAX_BULK_RX 1
#define HCI_MAX_ISOC_FRAMES 10
struct
_urb_queue
{
struct
list_head
head
;
spinlock_t
lock
;
};
struct
_urb
{
struct
list_head
list
;
struct
_urb_queue
*
queue
;
int
type
;
void
*
priv
;
struct
urb
urb
;
};
struct
_urb
*
_urb_alloc
(
int
isoc
,
int
gfp
);
static
inline
void
_urb_free
(
struct
_urb
*
_urb
)
{
kfree
(
_urb
);
}
static
inline
void
_urb_queue_init
(
struct
_urb_queue
*
q
)
{
INIT_LIST_HEAD
(
&
q
->
head
);
spin_lock_init
(
&
q
->
lock
);
}
static
inline
void
_urb_queue_head
(
struct
_urb_queue
*
q
,
struct
_urb
*
_urb
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
q
->
lock
,
flags
);
list_add
(
&
_urb
->
list
,
&
q
->
head
);
_urb
->
queue
=
q
;
spin_unlock_irqrestore
(
&
q
->
lock
,
flags
);
}
static
inline
void
_urb_queue_tail
(
struct
_urb_queue
*
q
,
struct
_urb
*
_urb
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
q
->
lock
,
flags
);
list_add_tail
(
&
_urb
->
list
,
&
q
->
head
);
_urb
->
queue
=
q
;
spin_unlock_irqrestore
(
&
q
->
lock
,
flags
);
}
static
inline
void
_urb_unlink
(
struct
_urb
*
_urb
)
{
struct
_urb_queue
*
q
=
_urb
->
queue
;
unsigned
long
flags
;
if
(
q
)
{
spin_lock_irqsave
(
&
q
->
lock
,
flags
);
list_del
(
&
_urb
->
list
);
_urb
->
queue
=
NULL
;
spin_unlock_irqrestore
(
&
q
->
lock
,
flags
);
}
}
struct
_urb
*
_urb_dequeue
(
struct
_urb_queue
*
q
);
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif
struct
hci_usb
{
struct
hci_dev
hdev
;
unsigned
long
state
;
struct
usb_device
*
udev
;
struct
usb_interface
*
isoc_iface
;
__u8
bulk_out_ep
;
__u8
bulk_in_ep
;
__u8
isoc_out_ep
;
__u8
isoc_in_ep
;
struct
usb_host_endpoint
*
bulk_in_ep
;
struct
usb_host_endpoint
*
bulk_out_ep
;
struct
usb_host_endpoint
*
intr_in_ep
;
struct
usb_interface
*
isoc_iface
;
struct
usb_host_endpoint
*
isoc_out_ep
;
struct
usb_host_endpoint
*
isoc_in_ep
;
__u8
intr_ep
;
__u8
intr_interval
;
struct
urb
*
intr_urb
;
struct
sk_buff
*
intr_skb
;
struct
sk_buff_head
transmit_q
[
4
];
struct
sk_buff
*
reassembly
[
4
];
// Reassembly buffers
rwlock_t
completion_lock
;
struct
sk_buff_head
cmd_q
;
// TX Commands
struct
sk_buff_head
acl_q
;
// TX ACLs
struct
sk_buff_head
pending_q
;
// Pending requests
struct
sk_buff_head
completed_q
;
// Completed requests
};
struct
hci_usb_scb
{
struct
urb
*
urb
;
int
intr_len
;
atomic_t
pending_tx
[
4
];
// Number of pending requests
struct
_urb_queue
pending_q
[
4
];
// Pending requests
struct
_urb_queue
completed_q
[
4
];
// Completed requests
};
/* States */
#define HCI_USB_TX_PROCESS 1
#define HCI_USB_TX_WAKEUP 2
#define HCI_USB_CTRL_TX 3
#endif
/* __KERNEL__ */
include/net/bluetooth/hci_core.h
View file @
2df06ce5
...
...
@@ -262,7 +262,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
int
hci_conn_auth
(
struct
hci_conn
*
conn
);
int
hci_conn_encrypt
(
struct
hci_conn
*
conn
);
static
inline
void
hci_conn_set_timer
(
struct
hci_conn
*
conn
,
long
timeout
)
static
inline
void
hci_conn_set_timer
(
struct
hci_conn
*
conn
,
unsigned
long
timeout
)
{
mod_timer
(
&
conn
->
timer
,
jiffies
+
timeout
);
}
...
...
@@ -280,8 +280,12 @@ static inline void hci_conn_hold(struct hci_conn *conn)
static
inline
void
hci_conn_put
(
struct
hci_conn
*
conn
)
{
if
(
atomic_dec_and_test
(
&
conn
->
refcnt
)
&&
conn
->
out
)
hci_conn_set_timer
(
conn
,
HCI_DISCONN_TIMEOUT
);
if
(
atomic_dec_and_test
(
&
conn
->
refcnt
))
{
if
(
conn
->
type
==
SCO_LINK
)
hci_conn_set_timer
(
conn
,
HZ
/
100
);
else
if
(
conn
->
out
)
hci_conn_set_timer
(
conn
,
HCI_DISCONN_TIMEOUT
);
}
}
/* ----- HCI tasks ----- */
...
...
include/net/bluetooth/rfcomm.h
View file @
2df06ce5
...
...
@@ -146,6 +146,11 @@ struct rfcomm_rpn {
u16
param_mask
;
}
__attribute__
((
packed
));
struct
rfcomm_rls
{
u8
dlci
;
u8
status
;
}
__attribute__
((
packed
));
struct
rfcomm_msc
{
u8
dlci
;
u8
v24_sig
;
...
...
net/bluetooth/hci_conn.c
View file @
2df06ce5
...
...
@@ -71,6 +71,7 @@ void hci_acl_connect(struct hci_conn *conn)
memset
(
&
cp
,
0
,
sizeof
(
cp
));
bacpy
(
&
cp
.
bdaddr
,
&
conn
->
dst
);
cp
.
pscan_rep_mode
=
0x01
;
if
((
ie
=
inquiry_cache_lookup
(
hdev
,
&
conn
->
dst
))
&&
inquiry_entry_age
(
ie
)
<=
INQUIRY_ENTRY_AGE_MAX
)
{
...
...
net/bluetooth/l2cap.c
View file @
2df06ce5
...
...
@@ -1788,7 +1788,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
if
(
sk
->
state
!=
BT_LISTEN
)
continue
;
if
(
!
bacmp
(
&
bt_sk
(
sk
)
->
src
,
bdaddr
))
{
if
(
!
bacmp
(
&
bt_sk
(
sk
)
->
src
,
&
hdev
->
bdaddr
))
{
lm1
|=
(
HCI_LM_ACCEPT
|
l2cap_pi
(
sk
)
->
link_mode
);
exact
++
;
}
else
if
(
!
bacmp
(
&
bt_sk
(
sk
)
->
src
,
BDADDR_ANY
))
...
...
net/bluetooth/rfcomm/core.c
View file @
2df06ce5
...
...
@@ -798,6 +798,33 @@ static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
return
rfcomm_send_frame
(
s
,
buf
,
ptr
-
buf
);
}
static
int
rfcomm_send_rls
(
struct
rfcomm_session
*
s
,
int
cr
,
u8
dlci
,
u8
status
)
{
struct
rfcomm_hdr
*
hdr
;
struct
rfcomm_mcc
*
mcc
;
struct
rfcomm_rls
*
rls
;
u8
buf
[
16
],
*
ptr
=
buf
;
BT_DBG
(
"%p cr %d status 0x%x"
,
s
,
cr
,
status
);
hdr
=
(
void
*
)
ptr
;
ptr
+=
sizeof
(
*
hdr
);
hdr
->
addr
=
__addr
(
s
->
initiator
,
0
);
hdr
->
ctrl
=
__ctrl
(
RFCOMM_UIH
,
0
);
hdr
->
len
=
__len8
(
sizeof
(
*
mcc
)
+
sizeof
(
*
rls
));
mcc
=
(
void
*
)
ptr
;
ptr
+=
sizeof
(
*
mcc
);
mcc
->
type
=
__mcc_type
(
cr
,
RFCOMM_RLS
);
mcc
->
len
=
__len8
(
sizeof
(
*
rls
));
rls
=
(
void
*
)
ptr
;
ptr
+=
sizeof
(
*
rls
);
rls
->
dlci
=
__addr
(
1
,
dlci
);
rls
->
status
=
status
;
*
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
;
...
...
@@ -1229,6 +1256,26 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_
return
0
;
}
static
int
rfcomm_recv_rls
(
struct
rfcomm_session
*
s
,
int
cr
,
struct
sk_buff
*
skb
)
{
struct
rfcomm_rls
*
rls
=
(
void
*
)
skb
->
data
;
u8
dlci
=
__get_dlci
(
rls
->
dlci
);
BT_DBG
(
"dlci %d cr %d status 0x%x"
,
dlci
,
cr
,
rls
->
status
);
if
(
!
cr
)
return
0
;
/* FIXME: We should probably do something with this
information here. But for now it's sufficient just
to reply -- Bluetooth 1.1 says it's mandatory to
recognise and respond to RLS */
rfcomm_send_rls
(
s
,
0
,
dlci
,
rls
->
status
);
return
0
;
}
static
int
rfcomm_recv_msc
(
struct
rfcomm_session
*
s
,
int
cr
,
struct
sk_buff
*
skb
)
{
struct
rfcomm_msc
*
msc
=
(
void
*
)
skb
->
data
;
...
...
@@ -1279,6 +1326,10 @@ static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
rfcomm_recv_rpn
(
s
,
cr
,
len
,
skb
);
break
;
case
RFCOMM_RLS
:
rfcomm_recv_rls
(
s
,
cr
,
skb
);
break
;
case
RFCOMM_MSC
:
rfcomm_recv_msc
(
s
,
cr
,
skb
);
break
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment