* Re: RFC: btusb firmware load help
From: Luis R. Rodriguez @ 2010-10-06 17:37 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Luis Rodriguez, linux-bluetooth, linux-kernel@vger.kernel.org,
linux-wireless@vger.kernel.org, Deepak Dhamdhere, Sree Durbha
In-Reply-To: <20101006163816.GE7070@tux>
On Wed, Oct 6, 2010 at 9:38 AM, Luis R. Rodriguez
<lrodriguez@atheros.com> wrote:
> On Wed, Oct 06, 2010 at 08:56:06AM -0700, Marcel Holtmann wrote:
>> Hi Luis,
>>
>> > > Now I am failing to understand why this was done wrong in the first
>> > > place. Especially if the loading procedure happens as you say it
>> > > happens.
>> >
>> > You got me :) Anyone?
>> >
>> > > This is the example for the Broadcom 203x devices:
>> > >
>> > > static struct usb_device_id blacklist_table[] = {
>> > > /* Broadcom BCM2033 without firmware */
>> > > { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
>> > >
>> > > The btusb driver clearly blacklists them. And then bcm203x can take over
>> > > loading the firmware:
>> > >
>> > > static const struct usb_device_id bcm203x_table[] = {
>> > > /* Broadcom Blutonium (BCM2033) */
>> > > { USB_DEVICE(0x0a5c, 0x2033) },
>> > >
>> > > So there is a working example of this already in the kernel tree since
>> > > forever.
>> >
>> > Nice, thanks for the pointer. Our team will review and try to address
>> > an alternative patch.
>> >
>> > Now for my own sanity -- I still don't think I get this how this
>> > BCM2033 blacklist trick works, I take it the device once plugged in
>> > gets a generic btusb USB device vendor:device ID. The btusb driver
>> > then picks up the the blacklist table, and searches for a
>> > usb_match_id() on it for the given interface... What I don't get is
>> > how there will be a match here if the USB vendor:device ID is just the
>> > generic btusb one. Can you help me understand how this trick works?
>>
>> the generic Bluetooth USB class descriptors is what btusb uses. With a
>> few expectation for devices that use VID:PID combination.
>
> Ahhh... got it..
>
>> So in general what happens if a device gets matched via the Bluetooth
>> USB class descriptors the btusb driver will claim. We do however check
>> against out blacklist first. If the VID:PID is listed in the blacklist
>> we do return ENODEV. That means that the USB subsystem goes ahead and
>> tries the next driver. In this case bcm203x driver. This will claim it,
>> load the firmware, reset it and come back with different VID:PID values.
>> After that the btusb can successfully claim it since it is no longer in
>> the blacklist.
>
> Ah neat.
>
>> If I understand this all correct without having the hardware available
>> for verifying this, then it should be like this:
>>
>> Just add this to blacklist_table[] in btusb.c:
>>
>> /* Atheros AR3011 without firmware */
>> { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
>>
>> And then we can add the firmware loading to ath3k driver to load the
>> specific 1st stage firmware. And then ath3k can load the 2nd stage as
>> well. After that it should become a default Bluetooth USB device and the
>> btusb driver can take care of it.
>
> Got it... thanks for the clarification. So ath3k actually doesn't seem to
> have 2-stage firmware files, ath3k-2.fw actually seems to be a new firmware
> upgrade. The firmware already made it into linux-firmware.git tree but the
> respective patch just never made it upstream. I am not sure of the
> differences between these firmware but I do remember reading from Vikram
> that no new API was changed. I asked for clarification on the firmware
> updates and asked if it can be documented here:
>
> http://wireless.kernel.org/en/users/Drivers/ath3k
>
> If the device can live with simply getting ath3k-1.fw loaded once then
> perhaps the change you described above is all that is needed, not sure.
>
> Deepak, can you please try this patch, I don't have hardware to test
> this with.
>
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index d22ce3c..a62c1b2 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -140,6 +140,9 @@ static struct usb_device_id blacklist_table[] = {
> /* Frontline ComProbe Bluetooth Sniffer */
> { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
>
> + /* Atheros AR3011 without firmware */
> + { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
> +
> { } /* Terminating entry */
> };
I just got this description from Sady:
-------------------------------------------------------------------------------------------------------------------
With eeprom based AR3011 hardware, normally this device gets detected
as a normal USB device with VID=0x0CF3, PID=0x3000.
Ath3k DFU driver will download the firmware in to RAM.
Due to firmware download in the RAM it is exposed as new device
with VID=0x0CF3, PID=0x3002 to host Bluetooth sub system and btusb.ko
driver probe routine gets called to bring up Bluetooth interface.
This is the normal procedure we have done so far on Linux.
With sflash based AR3011 hardware, when we connect the device to USB
port it gets detected as a Bluetooth device because of firmware in
Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
system driver (btusb.ko) directly in the host instead of ath3k
DFU driver. Therefore, there is no firmware downloaded in to the
RAM to bring up Bluetooth at this point. This is the problem
we're trying to "fix".
-------------------------------------------------------------------------------------------------------------------
With the above patch we'd get ath3k to do the firmware uploading but
I'm afraid that we'll go into a loop here unless we can figure out a
way to get btusb to know the device is now ready.
Luis
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Luis R. Rodriguez @ 2010-10-06 17:39 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Luis Rodriguez, linux-bluetooth, linux-kernel@vger.kernel.org,
linux-wireless@vger.kernel.org, Deepak Dhamdhere, Sree Durbha
In-Reply-To: <AANLkTik9w4_PdcEsbRHakLcxcD334_1Q8hGArxTXPCGF@mail.gmail.com>
On Wed, Oct 06, 2010 at 10:37:46AM -0700, Luis R. Rodriguez wrote:
> On Wed, Oct 6, 2010 at 9:38 AM, Luis R. Rodriguez
> <lrodriguez@atheros.com> wrote:
> > On Wed, Oct 06, 2010 at 08:56:06AM -0700, Marcel Holtmann wrote:
> >> Hi Luis,
> >>
> >> > > Now I am failing to understand why this was done wrong in the first
> >> > > place. Especially if the loading procedure happens as you say it
> >> > > happens.
> >> >
> >> > You got me :) Anyone?
> >> >
> >> > > This is the example for the Broadcom 203x devices:
> >> > >
> >> > > static struct usb_device_id blacklist_table[] = {
> >> > > /* Broadcom BCM2033 without firmware */
> >> > > { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
> >> > >
> >> > > The btusb driver clearly blacklists them. And then bcm203x can take over
> >> > > loading the firmware:
> >> > >
> >> > > static const struct usb_device_id bcm203x_table[] = {
> >> > > /* Broadcom Blutonium (BCM2033) */
> >> > > { USB_DEVICE(0x0a5c, 0x2033) },
> >> > >
> >> > > So there is a working example of this already in the kernel tree since
> >> > > forever.
> >> >
> >> > Nice, thanks for the pointer. Our team will review and try to address
> >> > an alternative patch.
> >> >
> >> > Now for my own sanity -- I still don't think I get this how this
> >> > BCM2033 blacklist trick works, I take it the device once plugged in
> >> > gets a generic btusb USB device vendor:device ID. The btusb driver
> >> > then picks up the the blacklist table, and searches for a
> >> > usb_match_id() on it for the given interface... What I don't get is
> >> > how there will be a match here if the USB vendor:device ID is just the
> >> > generic btusb one. Can you help me understand how this trick works?
> >>
> >> the generic Bluetooth USB class descriptors is what btusb uses. With a
> >> few expectation for devices that use VID:PID combination.
> >
> > Ahhh... got it..
> >
> >> So in general what happens if a device gets matched via the Bluetooth
> >> USB class descriptors the btusb driver will claim. We do however check
> >> against out blacklist first. If the VID:PID is listed in the blacklist
> >> we do return ENODEV. That means that the USB subsystem goes ahead and
> >> tries the next driver. In this case bcm203x driver. This will claim it,
> >> load the firmware, reset it and come back with different VID:PID values.
> >> After that the btusb can successfully claim it since it is no longer in
> >> the blacklist.
> >
> > Ah neat.
> >
> >> If I understand this all correct without having the hardware available
> >> for verifying this, then it should be like this:
> >>
> >> Just add this to blacklist_table[] in btusb.c:
> >>
> >> /* Atheros AR3011 without firmware */
> >> { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
> >>
> >> And then we can add the firmware loading to ath3k driver to load the
> >> specific 1st stage firmware. And then ath3k can load the 2nd stage as
> >> well. After that it should become a default Bluetooth USB device and the
> >> btusb driver can take care of it.
> >
> > Got it... thanks for the clarification. So ath3k actually doesn't seem to
> > have 2-stage firmware files, ath3k-2.fw actually seems to be a new firmware
> > upgrade. The firmware already made it into linux-firmware.git tree but the
> > respective patch just never made it upstream. I am not sure of the
> > differences between these firmware but I do remember reading from Vikram
> > that no new API was changed. I asked for clarification on the firmware
> > updates and asked if it can be documented here:
> >
> > http://wireless.kernel.org/en/users/Drivers/ath3k
> >
> > If the device can live with simply getting ath3k-1.fw loaded once then
> > perhaps the change you described above is all that is needed, not sure.
> >
> > Deepak, can you please try this patch, I don't have hardware to test
> > this with.
> >
> > diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> > index d22ce3c..a62c1b2 100644
> > --- a/drivers/bluetooth/btusb.c
> > +++ b/drivers/bluetooth/btusb.c
> > @@ -140,6 +140,9 @@ static struct usb_device_id blacklist_table[] = {
> > /* Frontline ComProbe Bluetooth Sniffer */
> > { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
> >
> > + /* Atheros AR3011 without firmware */
> > + { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
> > +
> > { } /* Terminating entry */
> > };
>
> I just got this description from Sady:
>
> -------------------------------------------------------------------------------------------------------------------
> With eeprom based AR3011 hardware, normally this device gets detected
> as a normal USB device with VID=0x0CF3, PID=0x3000.
> Ath3k DFU driver will download the firmware in to RAM.
> Due to firmware download in the RAM it is exposed as new device
> with VID=0x0CF3, PID=0x3002 to host Bluetooth sub system and btusb.ko
> driver probe routine gets called to bring up Bluetooth interface.
> This is the normal procedure we have done so far on Linux.
>
> With sflash based AR3011 hardware, when we connect the device to USB
> port it gets detected as a Bluetooth device because of firmware in
> Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
> system driver (btusb.ko) directly in the host instead of ath3k
> DFU driver. Therefore, there is no firmware downloaded in to the
> RAM to bring up Bluetooth at this point. This is the problem
> we're trying to "fix".
> -------------------------------------------------------------------------------------------------------------------
>
> With the above patch we'd get ath3k to do the firmware uploading but
> I'm afraid that we'll go into a loop here unless we can figure out a
> way to get btusb to know the device is now ready.
Oh and we'd still need something like this instead:
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 128cae4..c90512d 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -35,6 +35,7 @@
static struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 */
{ USB_DEVICE(0x0CF3, 0x3000) },
+ { USB_DEVICE(0x0CF3, 0x3002) },
{ } /* Terminating entry */
};
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index d22ce3c..a62c1b2 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -140,6 +140,9 @@ static struct usb_device_id blacklist_table[] = {
/* Frontline ComProbe Bluetooth Sniffer */
{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+ /* Atheros AR3011 without firmware */
+ { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
+
{ } /* Terminating entry */
};
But yeah, not sure how to prevent a loop. I'm actually not sure
what would happen once we hit the ath3k driver on the sflash
based devices with this patch.
Luis
^ permalink raw reply related
* Re: RFC: btusb firmware load help
From: Sven-Haegar Koch @ 2010-10-06 17:52 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Marcel Holtmann, Luis Rodriguez, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <AANLkTik9w4_PdcEsbRHakLcxcD334_1Q8hGArxTXPCGF@mail.gmail.com>
On Wed, 6 Oct 2010, Luis R. Rodriguez wrote:
> I just got this description from Sady:
>
> -------------------------------------------------------------------------------------------------------------------
> With eeprom based AR3011 hardware, normally this device gets detected
> as a normal USB device with VID=0x0CF3, PID=0x3000.
> Ath3k DFU driver will download the firmware in to RAM.
> Due to firmware download in the RAM it is exposed as new device
> with VID=0x0CF3, PID=0x3002 to host Bluetooth sub system and btusb.ko
> driver probe routine gets called to bring up Bluetooth interface.
> This is the normal procedure we have done so far on Linux.
>
> With sflash based AR3011 hardware, when we connect the device to USB
> port it gets detected as a Bluetooth device because of firmware in
> Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
> system driver (btusb.ko) directly in the host instead of ath3k
> DFU driver. Therefore, there is no firmware downloaded in to the
> RAM to bring up Bluetooth at this point. This is the problem
> we're trying to "fix".
> -------------------------------------------------------------------------------------------------------------------
>
> With the above patch we'd get ath3k to do the firmware uploading but
> I'm afraid that we'll go into a loop here unless we can figure out a
> way to get btusb to know the device is now ready.
Modify the firmware file so that after loading the firmware it gets for
example VID=0x0CF3, PID=0x3003.
Blacklist VID=0x0CF3, PID=0x3002 in btusb and add it to ath3k.
>From then on VID=0x0CF3, PID=0x3000 and PID=0x3002 mean "need firmware"
and PID=0x3003 is the operational state (like half of your PID=0x3002
usage is now)
c'ya
sven-haegar
--
Three may keep a secret, if two of them are dead.
- Ben F.
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Johannes Berg @ 2010-10-06 17:54 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Marcel Holtmann, Luis Rodriguez, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <20101006173949.GG7070@tux>
On Wed, 2010-10-06 at 10:39 -0700, Luis R. Rodriguez wrote:
> > With sflash based AR3011 hardware, when we connect the device to USB
> > port it gets detected as a Bluetooth device because of firmware in
> > Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
> > system driver (btusb.ko) directly in the host instead of ath3k
> > DFU driver. Therefore, there is no firmware downloaded in to the
> > RAM to bring up Bluetooth at this point. This is the problem
> > we're trying to "fix".
So the easiest fix for this would be to
a) ignore 0x0cf3,0x3002 in btusb
b) add it to ath3k firmware loading
c) change the ath3k firmware to load with 0x0cf3,0x3003 (or whatever
else you want, as long as it's not 0x3000 and not 0x3002)
Then the ignore in btusb won't affect ath3k after that new firmware was
loaded.
johannes
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Luis R. Rodriguez @ 2010-10-06 18:22 UTC (permalink / raw)
To: Johannes Berg
Cc: Marcel Holtmann, Luis Rodriguez, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <1286387660.3655.382.camel@jlt3.sipsolutions.net>
On Wed, Oct 6, 2010 at 10:54 AM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> On Wed, 2010-10-06 at 10:39 -0700, Luis R. Rodriguez wrote:
>
>
>> > With sflash based AR3011 hardware, when we connect the device to USB
>> > port it gets detected as a Bluetooth device because of firmware in
>> > Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
>> > system driver (btusb.ko) directly in the host instead of ath3k
>> > DFU driver. Therefore, there is no firmware downloaded in to the
>> > RAM to bring up Bluetooth at this point. This is the problem
>> > we're trying to "fix".
>
> So the easiest fix for this would be to
> a) ignore 0x0cf3,0x3002 in btusb
> b) add it to ath3k firmware loading
> c) change the ath3k firmware to load with 0x0cf3,0x3003 (or whatever
> else you want, as long as it's not 0x3000 and not 0x3002)
>
> Then the ignore in btusb won't affect ath3k after that new firmware was
> loaded.
Good idea, I forgot about possible firmware changes :) Lets see if our
team can do that. Thanks for all the feedback.
Luis
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Luis R. Rodriguez @ 2010-10-06 18:26 UTC (permalink / raw)
To: Johannes Berg
Cc: Marcel Holtmann, Luis Rodriguez, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <AANLkTikYRSQw-+Tcj4VS1qxi=OaeF9C5hjAMWVu+0M=v@mail.gmail.com>
On Wed, Oct 6, 2010 at 11:22 AM, Luis R. Rodriguez
<lrodriguez@atheros.com> wrote:
> On Wed, Oct 6, 2010 at 10:54 AM, Johannes Berg
> <johannes@sipsolutions.net> wrote:
>> On Wed, 2010-10-06 at 10:39 -0700, Luis R. Rodriguez wrote:
>>
>>
>>> > With sflash based AR3011 hardware, when we connect the device to USB
>>> > port it gets detected as a Bluetooth device because of firmware in
>>> > Flash (VID=0x0CF3, PID=0x3002). This triggers the Bluetooth sub
>>> > system driver (btusb.ko) directly in the host instead of ath3k
>>> > DFU driver. Therefore, there is no firmware downloaded in to the
>>> > RAM to bring up Bluetooth at this point. This is the problem
>>> > we're trying to "fix".
>>
>> So the easiest fix for this would be to
>> a) ignore 0x0cf3,0x3002 in btusb
>> b) add it to ath3k firmware loading
>> c) change the ath3k firmware to load with 0x0cf3,0x3003 (or whatever
>> else you want, as long as it's not 0x3000 and not 0x3002)
>>
>> Then the ignore in btusb won't affect ath3k after that new firmware was
>> loaded.
>
> Good idea, I forgot about possible firmware changes :) Lets see if our
> team can do that. Thanks for all the feedback.
Deepak a proof of concept test can involve simply hex-editing the
ath3k-1.fw and replacing 0x3002 to 0x3003, then the above patch might
work.
Luis
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Johannes Berg @ 2010-10-06 18:28 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Marcel Holtmann, Luis Rodriguez, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <AANLkTi=o4Mc72NrNoALBdS3YHeY7RMyyAJPsCy=uW3+F@mail.gmail.com>
On Wed, 2010-10-06 at 11:26 -0700, Luis R. Rodriguez wrote:
> > Good idea, I forgot about possible firmware changes :) Lets see if our
> > team can do that. Thanks for all the feedback.
>
> Deepak a proof of concept test can involve simply hex-editing the
> ath3k-1.fw and replacing 0x3002 to 0x3003, then the above patch might
> work.
$ hd ath3k-1.fw
...
00000670 00 00 00 00 00 00 00 00 00 00 00 00 f3 0c 02 30 |...............0|
00000680 12 01 10 01 e0 01 01 40 f3 0c 02 30 01 00 00 00 |.......@...0....|
...
that looks a lot like the IDs right there, in little endian :-)
johannes
^ permalink raw reply
* Re: RFC: btusb firmware load help
From: Luis R. Rodriguez @ 2010-10-06 18:33 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, Marcel Holtmann, linux-bluetooth,
linux-kernel@vger.kernel.org, linux-wireless@vger.kernel.org,
Deepak Dhamdhere, Sree Durbha
In-Reply-To: <1286389697.3655.401.camel@jlt3.sipsolutions.net>
On Wed, Oct 06, 2010 at 11:28:17AM -0700, Johannes Berg wrote:
> On Wed, 2010-10-06 at 11:26 -0700, Luis R. Rodriguez wrote:
>
> > > Good idea, I forgot about possible firmware changes :) Lets see if our
> > > team can do that. Thanks for all the feedback.
> >
> > Deepak a proof of concept test can involve simply hex-editing the
> > ath3k-1.fw and replacing 0x3002 to 0x3003, then the above patch might
> > work.
>
> $ hd ath3k-1.fw
> ...
> 00000670 00 00 00 00 00 00 00 00 00 00 00 00 f3 0c 02 30 |...............0|
> 00000680 12 01 10 01 e0 01 01 40 f3 0c 02 30 01 00 00 00 |.......@...0....|
> ...
>
> that looks a lot like the IDs right there, in little endian :-)
Furthermore another idea by johannes is that if we cannot fix this
in firmware by changing the exposed device ID, we could just check
in btusb for some USB component that would come alive once the firmware
does get loaded, like endpoints, or speed, or whatever. But that would
be last resort.
Luis
^ permalink raw reply
* [RFC] Basic Bluetooth LE support
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth
Hi,
This pathcset introduses basic Bluetooth LE support. Please review.
^ permalink raw reply
* [PATCH 1/7] Bluetooth: Add low energy commands and events
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
Add needed HCI command and event to create LE connections.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/hci.h | 52 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 52 insertions(+), 0 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 740e5de..b86aed5 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -597,6 +597,39 @@ struct hci_rp_read_bd_addr {
} __packed;
/* --- HCI LE Commands --- */
+#define HCI_OP_LE_SET_EVENT_MASK 0x2001
+struct hci_cp_le_set_event_mask {
+ __u8 mask[8];
+} __packed;
+
+#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002
+struct hci_rp_le_read_buffer_size {
+ __u8 status;
+ __le16 le_mtu;
+ __u8 le_max_pkt;
+} __packed;
+
+#define HCI_OP_LE_CREATE_CONN 0x200d
+struct hci_cp_le_create_conn {
+ __le16 scan_interval;
+ __le16 scan_window;
+ __u8 filter_policy;
+ __u8 peer_addr_type;
+ bdaddr_t peer_addr;
+ __u8 own_address_type;
+ __le16 conn_interval_min;
+ __le16 conn_interval_max;
+ __le16 conn_latency;
+ __le16 supervision_timeout;
+ __le16 min_ce_len;
+ __le16 max_ce_len;
+} __packed;
+
+#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
+struct hci_cp_le_create_conn_cancel {
+ __u8 status;
+} __packed;
+
#define HCI_OP_LE_SET_ADVERTISE_ENABLE 0x200a
#define LE_ADVERTISE_ENABLED 0x01
#define LE_ADVERTISE_DISABLED 0x00
@@ -857,6 +890,25 @@ struct hci_ev_remote_host_features {
__u8 features[8];
} __packed;
+#define HCI_EV_LE_META 0x3e
+struct hci_ev_le_meta {
+ __u8 subevent;
+} __packed;
+
+/* Low energy meta events */
+#define HCI_EV_LE_CONN_COMPLETE 0x01
+struct hci_ev_le_conn_complete {
+ __u8 status;
+ __le16 handle;
+ __u8 role;
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+ __le16 interval;
+ __le16 latency;
+ __le16 supervision_timeout;
+ __u8 clk_accurancy;
+} __packed;
+
/* Internal events generated by Bluetooth stack */
#define HCI_EV_STACK_INTERNAL 0xfd
struct hci_ev_stack_internal {
--
1.7.0.1
^ permalink raw reply related
* [PATCH 2/7] Bluetooth: Add LE connect support
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
Add logic to create LE connections.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 6 ++-
net/bluetooth/hci_conn.c | 38 ++++++++++++++-
net/bluetooth/hci_event.c | 100 +++++++++++++++++++++++++++++++++++++-
4 files changed, 141 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index b86aed5..b326240 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -162,6 +162,7 @@ enum {
#define SCO_LINK 0x00
#define ACL_LINK 0x01
#define ESCO_LINK 0x02
+#define LE_LINK 0x03
/* LMP features */
#define LMP_3SLOT 0x01
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ebec8c9..89f4b10 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -170,6 +170,7 @@ struct hci_conn {
bdaddr_t dst;
__u16 handle;
__u16 state;
+ __u16 le_state;
__u8 mode;
__u8 type;
__u8 out;
@@ -203,6 +204,7 @@ struct hci_conn {
struct hci_dev *hdev;
void *l2cap_data;
void *sco_data;
+ void *le_data;
void *priv;
struct hci_conn *link;
@@ -272,7 +274,7 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_add(&c->list, &h->list);
- if (c->type == ACL_LINK)
+ if (c->type == ACL_LINK || c->type == LE_LINK)
h->acl_num++;
else
h->sco_num++;
@@ -282,7 +284,7 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
{
struct hci_conn_hash *h = &hdev->conn_hash;
list_del(&c->list);
- if (c->type == ACL_LINK)
+ if (c->type == ACL_LINK || c->type == LE_LINK)
h->acl_num--;
else
h->sco_num--;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 145993f..cb41d64 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -45,6 +45,27 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+void hci_le_connect(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_create_conn cp;
+
+ conn->le_state = BT_CONNECT;
+ conn->out = 1;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.scan_interval = cpu_to_le16(0x0004);
+ cp.scan_window = cpu_to_le16(0x0004);
+ bacpy(&cp.peer_addr, &conn->dst);
+ cp.conn_interval_min = cpu_to_le16(0x0008);
+ cp.conn_interval_max = cpu_to_le16(0x0100);
+ cp.supervision_timeout = cpu_to_le16(0x0064);
+ cp.min_ce_len = cpu_to_le16(0x0001);
+ cp.max_ce_len = cpu_to_le16(0xffff);
+
+ hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+}
+
void hci_acl_connect(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -365,15 +386,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(hci_get_route);
-/* Create SCO or ACL connection.
+/* Create SCO, ACL or LE connection.
* Device _must_ be locked */
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
{
struct hci_conn *acl;
struct hci_conn *sco;
+ struct hci_conn *le;
BT_DBG("%s dst %s", hdev->name, batostr(dst));
+ if (type == LE_LINK) {
+ le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+
+ if (!le)
+ le = hci_conn_add(hdev, LE_LINK, dst);
+
+ if (!le)
+ return NULL;
+
+ hci_le_connect(le);
+
+ return le;
+ }
+
if (!(acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
return NULL;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d3c68de..0b979ae 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -868,6 +868,44 @@ static void hci_cc_le_set_scan(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, status);
}
+static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
+{
+ struct hci_cp_le_create_conn *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%x", hdev->name, status);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
+
+ BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
+ conn);
+
+ if (status) {
+ if (conn && conn->le_state == BT_CONNECT) {
+ conn->le_state = BT_CLOSED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_del(conn);
+ }
+ } else {
+ if (!conn) {
+ conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
+ if (conn) {
+ conn->out = 1;
+ conn->link_mode |= HCI_LM_MASTER;
+ } else
+ BT_ERR("No memory for new connection");
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1069,7 +1107,10 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
- conn->state = BT_CLOSED;
+ if (conn->type == LE_LINK)
+ conn->le_state = BT_CLOSED;
+ else
+ conn->state = BT_CLOSED;
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
@@ -1430,6 +1471,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_exit_sniff_mode(hdev, ev->status);
break;
+ case HCI_OP_LE_CREATE_CONN:
+ hci_cs_le_create_conn(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
break;
@@ -1875,6 +1920,55 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
hci_dev_unlock(hdev);
}
+static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_le_conn_complete *ev = (void *) skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status %d", hdev->name, ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
+
+ if (!conn)
+ goto unlock;
+
+ if (ev->status) {
+ hci_proto_connect_cfm(conn, ev->status);
+ conn->le_state = BT_CLOSED;
+ hci_conn_del(conn);
+ goto unlock;
+ }
+
+ conn->handle = __le16_to_cpu(ev->handle);
+ conn->le_state = BT_CONNECTED;
+
+ hci_conn_hold_device(conn);
+ hci_conn_add_sysfs(conn);
+
+ hci_proto_connect_cfm(conn, ev->status);
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_le_meta *le_ev = (void *) skb->data;
+ __u8 event = le_ev->subevent;
+
+ skb_pull(skb, sizeof(*le_ev));
+
+ switch (event) {
+ case HCI_EV_LE_CONN_COMPLETE:
+ hci_le_conn_complete_evt(hdev, skb);
+ break;
+
+ default:
+ break;
+ }
+}
+
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
@@ -2011,6 +2105,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_remote_host_features_evt(hdev, skb);
break;
+ case HCI_EV_LE_META:
+ hci_le_meta_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%x", hdev->name, event);
break;
--
1.7.0.1
^ permalink raw reply related
* [PATCH 3/7] Bluetooth: LE disconnection and connect cancel support
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
Add supprt to cancel and disconnet connections.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/hci.h | 5 ++---
include/net/bluetooth/hci_core.h | 3 +++
net/bluetooth/hci_conn.c | 30 ++++++++++++++++++++++++++++++
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index b326240..d04ecea 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -191,6 +191,8 @@ enum {
#define LMP_EV4 0x01
#define LMP_EV5 0x02
+#define LMP_NO_BR 0x20
+#define LMP_LE 0x40
#define LMP_SNIFF_SUBR 0x02
#define LMP_EDR_ESCO_2M 0x20
@@ -627,9 +629,6 @@ struct hci_cp_le_create_conn {
} __packed;
#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
-struct hci_cp_le_create_conn_cancel {
- __u8 status;
-} __packed;
#define HCI_OP_LE_SET_ADVERTISE_ENABLE 0x200a
#define LE_ADVERTISE_ENABLED 0x01
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 89f4b10..a430a57 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -455,10 +455,13 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH)
#define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT)
#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF)
+#define lmp_br_capable(dev) (!((dev)->features[4] & LMP_NO_BR))
+#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
+
/* ----- HCI protocols ----- */
struct hci_proto {
char *name;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index cb41d64..50f8973 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -66,6 +66,31 @@ void hci_le_connect(struct hci_conn *conn)
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
}
+static void hci_le_connect_cancel(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("%p", conn);
+
+ if (!lmp_le_capable(hdev))
+ return;
+
+ hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
+}
+
+void hci_le_disconn(struct hci_conn *conn, __u8 reason)
+{
+ struct hci_cp_disconnect cp;
+
+ BT_DBG("%p", conn);
+
+ conn->le_state = BT_DISCONN;
+
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.reason = reason;
+ hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
+}
+
void hci_acl_connect(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -221,6 +246,8 @@ static void hci_conn_timeout(unsigned long arg)
case BT_CONNECT2:
if (conn->type == ACL_LINK && conn->out)
hci_acl_connect_cancel(conn);
+ if (conn->type == LE_LINK && conn->out)
+ hci_le_connect_cancel(conn);
break;
case BT_CONFIG:
case BT_CONNECTED:
@@ -397,6 +424,9 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
BT_DBG("%s dst %s", hdev->name, batostr(dst));
if (type == LE_LINK) {
+ if (!lmp_le_capable(hdev))
+ return NULL;
+
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
if (!le)
--
1.7.0.1
^ permalink raw reply related
* [PATCH 4/7] Bluetooth: Use LE buffers for LE traffic
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
BLuetooth chips may have separate buffers for
LE traffic. This patch add support to use LE
buffers provided by the chip.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/hci_core.h | 6 +++
net/bluetooth/hci_conn.c | 11 +++++-
net/bluetooth/hci_core.c | 77 +++++++++++++++++++++++++++++++++++--
net/bluetooth/hci_event.c | 34 ++++++++++++++++-
4 files changed, 120 insertions(+), 8 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a430a57..2eb314f 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -102,15 +102,19 @@ struct hci_dev {
atomic_t cmd_cnt;
unsigned int acl_cnt;
unsigned int sco_cnt;
+ unsigned int le_cnt;
unsigned int acl_mtu;
unsigned int sco_mtu;
+ unsigned int le_mtu;
unsigned int acl_pkts;
unsigned int sco_pkts;
+ unsigned int le_pkts;
unsigned long cmd_last_tx;
unsigned long acl_last_tx;
unsigned long sco_last_tx;
+ unsigned long le_last_tx;
struct workqueue_struct *workqueue;
@@ -358,6 +362,8 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn);
void hci_conn_hold_device(struct hci_conn *conn);
void hci_conn_put_device(struct hci_conn *conn);
+void hci_le_disconn(struct hci_conn *conn, __u8 reason);
+
static inline void hci_conn_hold(struct hci_conn *conn)
{
atomic_inc(&conn->refcnt);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 50f8973..3443065 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -232,6 +232,7 @@ static void hci_conn_timeout(unsigned long arg)
{
struct hci_conn *conn = (void *) arg;
struct hci_dev *hdev = conn->hdev;
+ __u16 state;
__u8 reason;
BT_DBG("conn %p state %d", conn, conn->state);
@@ -241,7 +242,12 @@ static void hci_conn_timeout(unsigned long arg)
hci_dev_lock(hdev);
- switch (conn->state) {
+ if (conn->type == LE_LINK)
+ state = conn->le_state;
+ else
+ state = conn->state;
+
+ switch (state) {
case BT_CONNECT:
case BT_CONNECT2:
if (conn->type == ACL_LINK && conn->out)
@@ -348,6 +354,8 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */
hdev->acl_cnt += conn->sent;
+ } else if (conn->type == LE_LINK) {
+ hdev->le_cnt += conn->sent;
} else {
struct hci_conn *acl = conn->link;
if (acl) {
@@ -436,6 +444,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
return NULL;
hci_le_connect(le);
+ hci_conn_hold(le);
return le;
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index bc2a052..b320798 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -254,6 +254,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m);
}
+static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
+{
+ BT_DBG("%s", hdev->name);
+
+ /* Read LE buffer size */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+}
+
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{
__u8 scan = opt;
@@ -509,6 +517,10 @@ int hci_dev_open(__u16 dev)
ret = __hci_request(hdev, hci_init_req, 0,
msecs_to_jiffies(HCI_INIT_TIMEOUT));
+ if (lmp_le_capable(hdev))
+ ret = __hci_request(hdev, hci_le_init_req, 0,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
+
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -645,7 +657,7 @@ int hci_dev_reset(__u16 dev)
hdev->flush(hdev);
atomic_set(&hdev->cmd_cnt, 1);
- hdev->acl_cnt = 0; hdev->sco_cnt = 0;
+ hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
if (!test_bit(HCI_RAW, &hdev->flags))
ret = __hci_request(hdev, hci_reset_req, 0,
@@ -1444,7 +1456,8 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
if (c->type != type || skb_queue_empty(&c->data_q))
continue;
- if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
+ if ((c->state != BT_CONNECTED && c->state != BT_CONFIG) &&
+ (c->le_state != BT_CONNECTED && c->le_state != BT_CONFIG))
continue;
num++;
@@ -1456,8 +1469,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
}
if (conn) {
- int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
- int q = cnt / num;
+ int cnt, q;
+
+ switch (conn->type) {
+ case ACL_LINK:
+ cnt = hdev->acl_cnt;
+ break;
+ case SCO_LINK:
+ case ESCO_LINK:
+ cnt = hdev->sco_cnt;
+ break;
+ case LE_LINK:
+ cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
+ break;
+ default:
+ cnt = 0;
+ BT_ERR("Unknown link type");
+ }
+
+ q = cnt / num;
*quote = q ? q : 1;
} else
*quote = 0;
@@ -1482,6 +1512,11 @@ static inline void hci_acl_tx_to(struct hci_dev *hdev)
hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13);
}
+ if (c->type == LE_LINK && c->sent) {
+ BT_ERR("%s killing stalled LE connection %s",
+ hdev->name, batostr(&c->dst));
+ hci_le_disconn(c, 0x13);
+ }
}
}
@@ -1556,6 +1591,35 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
}
}
+static inline void hci_sched_le(struct hci_dev *hdev)
+{
+ struct hci_conn *conn;
+ struct sk_buff *skb;
+ int quote;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RAW, &hdev->flags)) {
+ /* ACL tx timeout must be longer than maximum
+ * link supervision timeout (40.9 seconds) */
+ if (!hdev->le_cnt &&
+ time_after(jiffies, hdev->le_last_tx + HZ * 45))
+ hci_acl_tx_to(hdev);
+ }
+
+ while (hdev->le_cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+
+ hci_send_frame(skb);
+ hdev->le_last_tx = jiffies;
+
+ hdev->le_cnt--;
+ conn->sent++;
+ }
+ }
+}
+
static void hci_tx_task(unsigned long arg)
{
struct hci_dev *hdev = (struct hci_dev *) arg;
@@ -1563,7 +1627,8 @@ static void hci_tx_task(unsigned long arg)
read_lock(&hci_task_lock);
- BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+ BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
+ hdev->sco_cnt, hdev->le_cnt);
/* Schedule queues and send stuff to HCI driver */
@@ -1573,6 +1638,8 @@ static void hci_tx_task(unsigned long arg)
hci_sched_esco(hdev);
+ hci_sched_le(hdev);
+
/* Send next queued raw (unknown type) packet */
while ((skb = skb_dequeue(&hdev->raw_q)))
hci_send_frame(skb);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 0b979ae..a914314 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -539,6 +539,26 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, rp->status);
}
+static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
+ hdev->le_pkts = rp->le_max_pkt;
+
+ hdev->le_cnt = hdev->le_pkts;
+
+ BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
+
+ hci_req_complete(hdev, rp->status);
+}
+
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%x", hdev->name, status);
@@ -1397,6 +1417,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_read_bd_addr(hdev, skb);
break;
+ case HCI_OP_LE_READ_BUFFER_SIZE:
+ hci_cc_le_read_buffer_size(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_ADVERTISE_ENABLE:
hci_cc_le_set_advertise(hdev, skb);
break;
@@ -1542,10 +1566,16 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
conn->sent -= count;
if (conn->type == ACL_LINK) {
- if ((hdev->acl_cnt += count) > hdev->acl_pkts)
+ hdev->acl_cnt += count;
+ if (hdev->acl_cnt > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts;
+ } else if (conn->type == LE_LINK) {
+ hdev->le_cnt += count;
+ if (hdev->le_cnt > hdev->le_pkts)
+ hdev->le_cnt = hdev->le_pkts;
} else {
- if ((hdev->sco_cnt += count) > hdev->sco_pkts)
+ hdev->sco_cnt += count;
+ if (hdev->sco_cnt > hdev->sco_pkts)
hdev->sco_cnt = hdev->sco_pkts;
}
}
--
1.7.0.1
^ permalink raw reply related
* [PATCH 5/7] Bluetooth: Add LE connection support to L2CAP
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
Add basic LE connection support to L2CAP
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/l2cap.h | 4 ++++
net/bluetooth/l2cap_core.c | 32 ++++++++++++++++++++++++--------
2 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index df599dc..41374ad 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -160,6 +160,9 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_LE_DATA 0x0004
+#define L2CAP_CID_LE_SIGNALING 0x0005
+#define L2CAP_CID_SMP 0x0006
#define L2CAP_CID_DYN_START 0x0040
#define L2CAP_CID_DYN_END 0xffff
@@ -269,6 +272,7 @@ struct l2cap_conn {
bdaddr_t *src;
unsigned int mtu;
+ unsigned int le_mtu;
__u32 feat_mask;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 07b55c1..1d43280 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -588,6 +588,12 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
+ if (conn->hcon->type == LE_LINK) {
+ l2cap_sock_clear_timer(sk);
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ }
+
if (sk->sk_type != SOCK_SEQPACKET &&
sk->sk_type != SOCK_STREAM) {
l2cap_sock_clear_timer(sk);
@@ -646,7 +652,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p conn %p", hcon, conn);
- conn->mtu = hcon->hdev->acl_mtu;
+ if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
+ conn->mtu = hcon->hdev->le_mtu;
+ else
+ conn->mtu = hcon->hdev->acl_mtu;
+
conn->src = &hcon->hdev->bdaddr;
conn->dst = &hcon->dst;
@@ -962,8 +972,13 @@ static int l2cap_do_connect(struct sock *sk)
}
}
- hcon = hci_connect(hdev, ACL_LINK, dst,
+ if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA)
+ hcon = hci_connect(hdev, LE_LINK, dst,
+ l2cap_pi(sk)->sec_level, auth_type);
+ else
+ hcon = hci_connect(hdev, ACL_LINK, dst,
l2cap_pi(sk)->sec_level, auth_type);
+
if (!hcon)
goto done;
@@ -1014,13 +1029,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
- if (la.l2_cid)
++ if (la.l2_cid && la.l2_psm)
return -EINVAL;
lock_sock(sk);
if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
- && !la.l2_psm) {
+ && !(la.l2_psm || la.la_cid)) {
err = -EINVAL;
goto done;
}
@@ -1062,14 +1077,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 &&
- sk->sk_type != SOCK_RAW) {
+ sk->sk_type != SOCK_RAW && !la.l2_cid) {
err = -EINVAL;
goto done;
}
- /* Set destination address and psm */
+ /* Set destination address and psm or cid */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
l2cap_pi(sk)->psm = la.l2_psm;
++ l2cap_pi(sk)->dcid = la.l2_cid;
err = l2cap_do_connect(sk);
if (err)
@@ -4380,7 +4396,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
- if (hcon->type != ACL_LINK)
++ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
if (!status) {
@@ -4409,7 +4425,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
- if (hcon->type != ACL_LINK)
++ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
l2cap_conn_del(hcon, bt_err(reason));
--
1.7.0.1
^ permalink raw reply related
* [PATCH 6/7] Bluetooth: Add server socket support for LE connection
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
Add support for LE server sockets.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/hci_event.c | 10 +++-
net/bluetooth/l2cap_core.c | 117 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 113 insertions(+), 15 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 41374ad..e655b31 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -38,6 +38,7 @@
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
#define L2CAP_DEFAULT_ACK_TO 200
#define L2CAP_LOCAL_BUSY_TRIES 12
+#define L2CAP_LE_DEFAULT_MTU 23
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index a914314..fa945e9 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1961,8 +1961,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
- if (!conn)
- goto unlock;
+ if (!conn) {
+ conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
+ if (!conn) {
+ BT_ERR("No memory for new connection");
+ hci_dev_unlock(hdev);
+ return;
+ }
+ }
if (ev->status) {
hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 1d43280..20be2f9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -78,6 +78,8 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
+static void l2cap_le_conn_ready(struct l2cap_conn *conn);
+
/* ---- L2CAP timers ---- */
static void l2cap_sock_set_timer(struct sock *sk, long timeout)
{
@@ -199,8 +201,16 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
l2cap_pi(sk)->conn = conn;
if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) {
- /* Alloc CID for connection-oriented socket */
- l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+ if (conn->hcon->type == LE_LINK) {
+ /* LE connection */
+ l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU;
+ l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA;
+ l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA;
+ } else {
+ /* Alloc CID for connection-oriented socket */
+ l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+ }
} else if (sk->sk_type == SOCK_DGRAM) {
/* Connectionless socket */
l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS;
@@ -583,15 +593,18 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
BT_DBG("conn %p", conn);
+ if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
+
read_lock(&l->lock);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
- l2cap_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
+ l2cap_sock_clear_timer(sk);
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
}
if (sk->sk_type != SOCK_SEQPACKET &&
@@ -665,7 +678,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
- setup_timer(&conn->info_timer, l2cap_info_timeout,
+ if (hcon->type != LE_LINK)
+ setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);
conn->disc_reason = 0x13;
@@ -759,6 +773,37 @@ static inline struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t
return s;
}
+static inline struct sock *l2cap_get_sock_by_cid(int state, __le16 cid, bdaddr_t *src)
+{
+ struct sock *s;
+ struct sock *sk = NULL, *sk1 = NULL;
+ struct hlist_node *node;
+
+ read_lock(&l2cap_sk_list.lock);
+ sk_for_each(sk, node, &l2cap_sk_list.head) {
+ if (state && sk->sk_state != state)
+ continue;
+
+ if (l2cap_pi(sk)->dcid == cid) {
+ /* Exact match. */
+ if (!bacmp(&bt_sk(sk)->src, src))
+ break;
+
+ /* Closest match */
+ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+ sk1 = sk;
+ }
+ }
+
+ s = node ? sk : sk1;
+
+ if (s)
+ bh_lock_sock(s);
+ read_unlock(&l2cap_sk_list.lock);
+
+ return s;
+}
+
static void l2cap_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
@@ -868,7 +913,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
- if (la.l2_cid)
+ if (la.l2_cid && la.l2_psm)
return -EINVAL;
lock_sock(sk);
@@ -910,6 +955,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
}
+ if (la.l2_cid)
+ l2cap_pi(sk)->dcid = la.l2_cid;
+
write_unlock_bh(&l2cap_sk_list.lock);
done:
@@ -1029,13 +1077,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
-+ if (la.l2_cid && la.l2_psm)
+ if (la.l2_cid && la.l2_psm)
return -EINVAL;
lock_sock(sk);
if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
- && !(la.l2_psm || la.la_cid)) {
+ && !(la.l2_psm || la.l2_cid)) {
err = -EINVAL;
goto done;
}
@@ -1085,7 +1133,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
/* Set destination address and psm or cid */
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
l2cap_pi(sk)->psm = la.l2_psm;
-+ l2cap_pi(sk)->dcid = la.l2_cid;
+ l2cap_pi(sk)->dcid = la.l2_cid;
err = l2cap_do_connect(sk);
if (err)
@@ -1127,7 +1175,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
- if (!l2cap_pi(sk)->psm) {
+ if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) {
bdaddr_t *src = &bt_sk(sk)->src;
u16 psm;
@@ -1237,6 +1285,49 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
return 0;
}
+static void l2cap_le_conn_ready(struct l2cap_conn *conn)
+{
+ struct l2cap_chan_list *list = &conn->chan_list;
+ struct sock *parent, *uninitialized_var(sk);
+
+ BT_DBG("");
+
+ /* Check if we have socket listening on cid */
+ parent = l2cap_get_sock_by_cid(BT_LISTEN, 0x04, conn->src);
+ if (!parent)
+ goto clean;
+
+ /* Check for backlog size */
+ if (sk_acceptq_is_full(parent)) {
+ BT_DBG("backlog full %d", parent->sk_ack_backlog);
+ goto clean;
+ }
+
+ sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
+ if (!sk)
+ goto clean;
+
+ write_lock_bh(&list->lock);
+
+ hci_conn_hold(conn->hcon);
+
+ l2cap_sock_init(sk, parent);
+ bacpy(&bt_sk(sk)->src, conn->src);
+ bacpy(&bt_sk(sk)->dst, conn->dst);
+
+ __l2cap_chan_add(conn, sk, parent);
+
+ l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
+
+ sk->sk_state = BT_CONNECTED;
+ parent->sk_data_ready(parent, 0);
+
+ write_unlock_bh(&list->lock);
+
+clean:
+ bh_unlock_sock(parent);
+}
+
static int __l2cap_wait_ack(struct sock *sk)
{
DECLARE_WAITQUEUE(wait, current);
@@ -4396,7 +4487,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
-+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
if (!status) {
@@ -4425,7 +4516,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
-+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
+ if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
return -EINVAL;
l2cap_conn_del(hcon, bt_err(reason));
--
1.7.0.1
^ permalink raw reply related
* [PATCH 7/7] Bluetooth: Do not send disconn comand over LE links
From: Ville Tervo @ 2010-10-06 18:42 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Ville Tervo
In-Reply-To: <1286390535-27462-1-git-send-email-ville.tervo@nokia.com>
l2cap over LE links can be disconnected without sending
disconnect command first.
Signed-off-by: Ville Tervo <ville.tervo@nokia.com>
---
net/bluetooth/l2cap_core.c | 15 ++++++++++-----
1 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 20be2f9..53860f6 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -836,6 +836,8 @@ void l2cap_sock_kill(struct sock *sk)
void __l2cap_sock_close(struct sock *sk, int reason)
{
+ struct l2cap_conn *conn;
+
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
switch (sk->sk_state) {
@@ -845,8 +847,10 @@ void __l2cap_sock_close(struct sock *sk, int reason)
case BT_CONNECTED:
case BT_CONFIG:
- if (sk->sk_type == SOCK_SEQPACKET ||
- sk->sk_type == SOCK_STREAM) {
+ conn = l2cap_pi(sk)->conn;
+ if ((sk->sk_type == SOCK_SEQPACKET ||
+ sk->sk_type == SOCK_STREAM) &&
+ conn->hcon->type != LE_LINK) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
@@ -856,9 +860,10 @@ void __l2cap_sock_close(struct sock *sk, int reason)
break;
case BT_CONNECT2:
- if (sk->sk_type == SOCK_SEQPACKET ||
- sk->sk_type == SOCK_STREAM) {
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ conn = l2cap_pi(sk)->conn;
+ if ((sk->sk_type == SOCK_SEQPACKET ||
+ sk->sk_type == SOCK_STREAM) &&
+ conn->hcon->type != LE_LINK) {
struct l2cap_conn_rsp rsp;
__u16 result;
--
1.7.0.1
^ permalink raw reply related
* Re: [PATCH 3/7] Bluetooth: LE disconnection and connect cancel support
From: Anderson Lizardo @ 2010-10-06 19:57 UTC (permalink / raw)
To: Ville Tervo; +Cc: linux-bluetooth
In-Reply-To: <1286390535-27462-4-git-send-email-ville.tervo@nokia.com>
Hi Ville,
On Wed, Oct 6, 2010 at 2:42 PM, Ville Tervo <ville.tervo@nokia.com> wrote:
> @@ -627,9 +629,6 @@ struct hci_cp_le_create_conn {
> } __packed;
>
> #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
> -struct hci_cp_le_create_conn_cancel {
> - __u8 status;
> -} __packed;
>
> #define HCI_OP_LE_SET_ADVERTISE_ENABLE 0x200a
> #define LE_ADVERTISE_ENABLED 0x01
The struct above is added in 1/7 then removed on this one, so you
could drop it from patch 1/7 instead?
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 89f4b10..a430a57 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -455,10 +455,13 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
> #define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH)
> #define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT)
> #define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF)
> +#define lmp_br_capable(dev) (!((dev)->features[4] & LMP_NO_BR))
> +#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
> #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
> #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
> #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
>
> +
> /* ----- HCI protocols ----- */
Unrelated extra line added above.
> struct hci_proto {
> char *name;
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index cb41d64..50f8973 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -66,6 +66,31 @@ void hci_le_connect(struct hci_conn *conn)
> hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
> }
>
> +static void hci_le_connect_cancel(struct hci_conn *conn)
> +{
> + struct hci_dev *hdev = conn->hdev;
> +
> + BT_DBG("%p", conn);
This debug message could be made more readable, e.g.:
BT_DBG("%s conn %p", hdev->name, conn);
(or simply dropped if not that useful).
> +
> + if (!lmp_le_capable(hdev))
> + return;
> +
> + hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
> +}
> +
> +void hci_le_disconn(struct hci_conn *conn, __u8 reason)
> +{
> + struct hci_cp_disconnect cp;
> +
> + BT_DBG("%p", conn);
Same here.
Regards,
--
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil
^ permalink raw reply
* Re: [PATCH 5/7] Bluetooth: Add LE connection support to L2CAP
From: Anderson Lizardo @ 2010-10-06 20:14 UTC (permalink / raw)
To: Ville Tervo; +Cc: linux-bluetooth
In-Reply-To: <1286390535-27462-6-git-send-email-ville.tervo@nokia.com>
On Wed, Oct 6, 2010 at 2:42 PM, Ville Tervo <ville.tervo@nokia.com> wrote:
> @@ -1014,13 +1029,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
> len = min_t(unsigned int, sizeof(la), alen);
> memcpy(&la, addr, len);
>
> - if (la.l2_cid)
> ++ if (la.l2_cid && la.l2_psm)
> return -EINVAL;
>
> lock_sock(sk);
>
> if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
> - && !la.l2_psm) {
> + && !(la.l2_psm || la.la_cid)) {
> err = -EINVAL;
> goto done;
> }
This snippet looks strange for two reasons:
1) the "++" marker looks weird. Corrupted patch?
2) there is a typo here : la.la_cid -> la.l2_cid . Didn't the compiler
catch this?
Note that there are some other occurrences of "++" in this patch.
Regards,
--
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil
^ permalink raw reply
* [PATCH] TODO: Add attribute permission verification for attribute server
From: Claudio Takahasi @ 2010-10-06 20:34 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
---
TODO | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/TODO b/TODO
index 7ebbbe3..2cd141b 100644
--- a/TODO
+++ b/TODO
@@ -32,6 +32,13 @@ ATT/GATT
Priority: Low
Complexity: C1
+- Attribute server shall implement attribute permission verification,
+ returning an error code if necessary. See Volume 3, Part F, 3.2.5
+ for more information.
+
+ Priority: Low
+ Complexity: C2
+
- Long reads/writes don't work (consisting of multiple request packets)
Priority: Low
--
1.7.3.1
^ permalink raw reply related
* [PATCH 1/2] Change Battery Service on attribute sample server
From: Claudio Takahasi @ 2010-10-06 20:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
Add an optional Client Characteristic Configuration attribute for
Battery State to control which clients want notification/indication
when this attribute value changes.
---
attrib/example.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/attrib/example.c b/attrib/example.c
index 76afce9..c29e1e4 100644
--- a/attrib/example.c
+++ b/attrib/example.c
@@ -161,10 +161,15 @@ static int register_attributes(void)
/* Battery: battery state attribute */
sdp_uuid16_create(&uuid, BATTERY_STATE_UUID);
- u16 = htons(BATTERY_STATE_UUID);
atval[0] = 0x04;
attrib_db_add(0x0110, &uuid, atval, 1);
+ /* Battery: Client Characteristic Configuration */
+ sdp_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ attrib_db_add(0x0111, &uuid, atval, 2);
+
timeout_id = g_timeout_add_seconds(10, change_battery_state, NULL);
/* Thermometer: primary service definition */
--
1.7.3.1
^ permalink raw reply related
* [PATCH 2/2] Client Characteristic Configuration on attribute server
From: Claudio Takahasi @ 2010-10-06 20:39 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
In-Reply-To: <1286397594-12366-1-git-send-email-claudio.takahasi@openbossa.org>
Initial implementation of per client attribute configuration for the
attribute server. Notification and indication shall be sent to the peer
only if Client Characteristic Configuration bit field is set.
---
TODO | 7 --
src/attrib-server.c | 194 +++++++++++++++++++++++++++++++++++---------------
src/storage.c | 44 ++++++++++++
src/storage.h | 4 +
4 files changed, 184 insertions(+), 65 deletions(-)
diff --git a/TODO b/TODO
index 2cd141b..6722af7 100644
--- a/TODO
+++ b/TODO
@@ -18,13 +18,6 @@ Background
ATT/GATT
========
-- Sample server shouldn't send any indications or notifications without
- the client requesting them
-
- Priority: Medium
- Complexity: C2
- Owner: Claudio Takahasi <claudio.takahasi@openbossa.org>
-
- gatttool should have the ability to wait for req responses before
quitting (some servers require a small sleep even with cmd's). Maybe a
--delay-exit or --timeout command line switch.
diff --git a/src/attrib-server.c b/src/attrib-server.c
index b45f300..a95dbda 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -37,6 +37,7 @@
#include <bluetooth/sdp_lib.h>
#include "log.h"
+#include "storage.h"
#include "glib-helper.h"
#include "btio.h"
#include "sdpd.h"
@@ -48,14 +49,25 @@
#define GATT_PSM 0x1f
#define GATT_CID 4
+/* Client Characteristic Configuration bit field */
+#define CONFIG_NOTIFICATION 0x0001
+#define CONFIG_INDICATION 0x0002
+
static GSList *database = NULL;
+struct client_char_config {
+ uint16_t chr_handle;
+ uint16_t cfg_handle;
+ uint16_t value;
+};
+
struct gatt_channel {
bdaddr_t src;
bdaddr_t dst;
GAttrib *attrib;
guint mtu;
guint id;
+ GSList *config;
};
struct group_elem {
@@ -137,6 +149,81 @@ static sdp_record_t *server_record_new(void)
return record;
}
+static gint config_chr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct client_char_config *config = a;
+ uint16_t handle = GPOINTER_TO_UINT(b);
+
+ return config->chr_handle - handle;
+}
+
+static gint config_cfg_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct client_char_config *config = a;
+ uint16_t handle = GPOINTER_TO_UINT(b);
+
+ return config->cfg_handle - handle;
+}
+
+static void channel_free(struct gatt_channel *channel)
+{
+ g_attrib_unref(channel->attrib);
+ g_slist_foreach(channel->config, (GFunc) g_free, NULL);
+ g_slist_free(channel->config);
+ g_free(channel);
+}
+
+static GSList *read_client_config(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct client_char_config *config;
+ struct attribute *a1, *a2;
+ GSList *l, *ltmp, *lconfig;
+ uuid_t chr_uuid, cfg_uuid;
+ uint16_t chr_handle;
+
+ sdp_uuid16_create(&chr_uuid, GATT_CHARAC_UUID);
+ sdp_uuid16_create(&cfg_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ for (l = database, ltmp = NULL; l; l = l->next) {
+ a1 = l->data;
+
+ if (sdp_uuid_cmp(&a1->uuid, &chr_uuid) != 0 &&
+ sdp_uuid_cmp(&a1->uuid, &cfg_uuid) != 0)
+ continue;
+
+ /* Temporary list of characteristic declaration and config */
+ ltmp = g_slist_append(ltmp, l->data);
+ }
+
+ for (l = ltmp, lconfig = NULL; l;) {
+ a1 = l->data;
+
+ l = l->next;
+ if (l == NULL)
+ break;
+
+ a2 = l->data;
+ if (sdp_uuid_cmp(&a2->uuid, &cfg_uuid) != 0)
+ continue;
+
+ /* Skip the first byte: attribute permission */
+ chr_handle = att_get_u16(&a1->data[1]);
+
+ config = g_malloc0(sizeof(*config));
+ config->chr_handle = chr_handle;
+ config->cfg_handle = a2->handle;
+
+ read_device_config(src, dst, config->cfg_handle,
+ &config->value);
+
+ lconfig = g_slist_append(lconfig, config);
+ l = l->next;
+ }
+
+ g_slist_free(ltmp);
+
+ return lconfig;
+}
+
static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
uint8_t *pdu, int len)
{
@@ -414,12 +501,14 @@ static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
return enc_read_resp(a->data, a->len, pdu, len);
}
-static void write_value(uint16_t handle, const uint8_t *value, int vlen)
+static void write_value(struct gatt_channel *channel, uint16_t handle,
+ const uint8_t *value, int vlen)
{
+ struct client_char_config *config;
struct attribute *a;
GSList *l;
guint h = handle;
- uuid_t uuid;
+ uuid_t uuid, cfg_uuid;
l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
if (!l)
@@ -427,7 +516,30 @@ static void write_value(uint16_t handle, const uint8_t *value, int vlen)
a = l->data;
memcpy(&uuid, &a->uuid, sizeof(uuid_t));
- attrib_db_update(handle, &uuid, value, vlen);
+
+ sdp_uuid16_create(&cfg_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ if (sdp_uuid_cmp(&cfg_uuid, &uuid) != 0) {
+ attrib_db_update(handle, &uuid, value, vlen);
+ return;
+ }
+
+ if (vlen != 2) {
+ /* FIXME: Needs to handle error */
+ error("Client Characteristic Configuration: wrong length");
+ return;
+ }
+
+ /* Per client attribute: Client Characteristic Config */
+ l = g_slist_find_custom(channel->config, GUINT_TO_POINTER(h),
+ config_cfg_cmp);
+ if (!l)
+ return;
+
+ config = l->data;
+ config->value = att_get_u16(value);
+
+ write_device_config(&channel->src, &channel->dst, handle,
+ config->value);
}
static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
@@ -442,10 +554,9 @@ static void channel_disconnect(void *user_data)
{
struct gatt_channel *channel = user_data;
- g_attrib_unref(channel->attrib);
clients = g_slist_remove(clients, channel);
- g_free(channel);
+ channel_free(channel);
}
static void channel_handler(const uint8_t *ipdu, uint16_t len,
@@ -507,7 +618,7 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
case ATT_OP_WRITE_CMD:
length = dec_write_cmd(ipdu, len, &start, value, &vlen);
if (length > 0)
- write_value(start, value, vlen);
+ write_value(channel, start, value, vlen);
return;
case ATT_OP_FIND_BY_TYPE_REQ:
case ATT_OP_READ_BLOB_REQ:
@@ -557,6 +668,7 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data)
channel->attrib = g_attrib_new(io);
channel->mtu = ATT_DEFAULT_MTU;
+ channel->config = read_client_config(&channel->src, &channel->dst);
channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_EVENTS,
channel_handler, channel, NULL);
@@ -580,52 +692,34 @@ static void confirm_event(GIOChannel *io, void *user_data)
return;
}
-static gboolean send_notification(gpointer user_data)
+static void report_attrib_changes(gpointer data, gpointer user_data)
{
- uint8_t pdu[ATT_MAX_MTU];
- guint handle = GPOINTER_TO_UINT(user_data);
- struct attribute *a;
+ struct gatt_channel *channel = data;
+ struct attribute *a = user_data;
+ struct client_char_config *config;
GSList *l;
+ uint8_t pdu[ATT_MAX_MTU];
uint16_t length;
+ guint h = a->handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(handle), handle_cmp);
+ l = g_slist_find_custom(channel->config, GUINT_TO_POINTER(h),
+ config_chr_cmp);
if (!l)
- return FALSE;
-
- a = l->data;
+ return;
- for (l = clients; l; l = l->next) {
- struct gatt_channel *channel = l->data;
+ config = l->data;
+ if (config->value & CONFIG_NOTIFICATION) {
length = enc_notification(a, pdu, channel->mtu);
- g_attrib_send(channel->attrib, pdu[0], pdu, length, NULL, NULL, NULL);
+ g_attrib_send(channel->attrib, pdu[0], pdu, length, NULL,
+ NULL, NULL);
}
- return FALSE;
-}
-
-static gboolean send_indication(gpointer user_data)
-{
- uint8_t pdu[ATT_MAX_MTU];
- guint handle = GPOINTER_TO_UINT(user_data);
- struct attribute *a;
- GSList *l;
- uint16_t length;
-
- l = g_slist_find_custom(database, GUINT_TO_POINTER(handle), handle_cmp);
- if (!l)
- return FALSE;
-
- a = l->data;
-
- for (l = clients; l; l = l->next) {
- struct gatt_channel *channel = l->data;
-
+ if (config->value & CONFIG_INDICATION) {
length = enc_indication(a, pdu, channel->mtu);
- g_attrib_send(channel->attrib, pdu[0], pdu, length, NULL, NULL, NULL);
+ g_attrib_send(channel->attrib, pdu[0], pdu, length, NULL,
+ NULL, NULL);
}
-
- return FALSE;
}
int attrib_server_init(void)
@@ -680,8 +774,6 @@ int attrib_server_init(void)
void attrib_server_exit(void)
{
- GSList *l;
-
g_slist_foreach(database, (GFunc) g_free, NULL);
g_slist_free(database);
@@ -695,13 +787,7 @@ void attrib_server_exit(void)
g_io_channel_shutdown(le_io, FALSE, NULL);
}
- for (l = clients; l; l = l->next) {
- struct gatt_channel *channel = l->data;
-
- g_attrib_unref(channel->attrib);
- g_free(channel);
- }
-
+ g_slist_foreach(clients, (GFunc) channel_free, NULL);
g_slist_free(clients);
if (sdp_handle)
@@ -746,15 +832,7 @@ int attrib_db_update(uint16_t handle, uuid_t *uuid, const uint8_t *value,
a->len = len;
memcpy(a->data, value, len);
- /*
- * Characteristic configuration descriptor is not being used yet.
- * If the attribute changes, all connected clients will be notified.
- * For testing purposes, we send a Notification and a Indication for
- * each update.
- */
- g_idle_add(send_notification, GUINT_TO_POINTER(h));
-
- g_idle_add(send_indication, GUINT_TO_POINTER(h));
+ g_slist_foreach(clients, report_attrib_changes, a);
return 0;
}
diff --git a/src/storage.c b/src/storage.c
index 06b36f1..b3fee08 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -1391,3 +1391,47 @@ int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data)
return textfile_foreach(filename, func, data);
}
+
+int write_device_config(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, uint16_t value)
+{
+ char filename[PATH_MAX + 1], addr[18], key[23], str[5];
+
+ create_filename(filename, PATH_MAX, sba, "clientconfig");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(str, sizeof(str), "%04X", value);
+
+ return textfile_put(filename, key, str);
+}
+
+int read_device_config(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, uint16_t *value)
+{
+ char filename[PATH_MAX + 1], addr[18], key[23];
+ char *str;
+ long int val;
+
+ create_filename(filename, PATH_MAX, sba, "clientconfig");
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+
+ str = textfile_caseget(filename, key);
+ if (str == NULL)
+ return -ENOENT;
+
+ val = strtol(str, NULL, 16);
+
+ if (value)
+ *value = val;
+
+ g_free(str);
+
+ return 0;
+}
diff --git a/src/storage.h b/src/storage.h
index c7e342c..22333b2 100644
--- a/src/storage.h
+++ b/src/storage.h
@@ -91,6 +91,10 @@ char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
uint16_t handle, const char *chars);
int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data);
+int write_device_config(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, uint16_t value);
+int read_device_config(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t handle, uint16_t *value);
#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
--
1.7.3.1
^ permalink raw reply related
* [PATCH] TODO: Export Client Characteristic Configuration as a property
From: Claudio Takahasi @ 2010-10-06 20:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
---
TODO | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/TODO b/TODO
index 6722af7..59a20d3 100644
--- a/TODO
+++ b/TODO
@@ -25,6 +25,16 @@ ATT/GATT
Priority: Low
Complexity: C1
+- Client needs to export a property in the Device Characteristic hierarchy
+ to manage characteristic value changes reports in the remote device.
+ Currently, Client Characteristic Configuration attribute is not exposed
+ as an object. The user needs to use gatttool to change the value of the
+ this attribute to receive notification/indications. Export this attribute
+ as a property is a proposal that needs further discussion.
+
+ Priority: Low
+ Complexity: C1
+
- Attribute server shall implement attribute permission verification,
returning an error code if necessary. See Volume 3, Part F, 3.2.5
for more information.
--
1.7.3.1
^ permalink raw reply related
* Re: [PATCH 6/7] Bluetooth: Add server socket support for LE connection
From: Anderson Lizardo @ 2010-10-06 20:49 UTC (permalink / raw)
To: Ville Tervo; +Cc: linux-bluetooth
In-Reply-To: <1286390535-27462-7-git-send-email-ville.tervo@nokia.com>
On Wed, Oct 6, 2010 at 2:42 PM, Ville Tervo <ville.tervo@nokia.com> wrote:
> if (conn->hcon->type == LE_LINK) {
> - l2cap_sock_clear_timer(sk);
> - sk->sk_state = BT_CONNECTED;
> - sk->sk_state_change(sk);
> + l2cap_sock_clear_timer(sk);
> + sk->sk_state = BT_CONNECTED;
> + sk->sk_state_change(sk);
> }
>
> if (sk->sk_type != SOCK_SEQPACKET &&
Unrelated indentation above.
> @@ -1029,13 +1077,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
> len = min_t(unsigned int, sizeof(la), alen);
> memcpy(&la, addr, len);
>
> -+ if (la.l2_cid && la.l2_psm)
> + if (la.l2_cid && la.l2_psm)
> return -EINVAL;
>
> lock_sock(sk);
>
> if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
> - && !(la.l2_psm || la.la_cid)) {
> + && !(la.l2_psm || la.l2_cid)) {
> err = -EINVAL;
> goto done;
> }
These typos were introduced in a previous patch. You should fix there instead.
--
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil
^ permalink raw reply
* [PATCH] TODO: Attribute server should process queued commands at disconnection
From: Claudio Takahasi @ 2010-10-06 21:00 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
---
TODO | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/TODO b/TODO
index 59a20d3..a9f1339 100644
--- a/TODO
+++ b/TODO
@@ -42,6 +42,15 @@ ATT/GATT
Priority: Low
Complexity: C2
+- Attribute server should process queued GATT/ATT commands if the
+ client disconnects. The client can simply send a command and quit,
+ without wait for a response(ex: Write Command). For this scenario
+ that the client disconnects the link quickly the queued received
+ command is ignored.
+
+ Priority: Low
+ Complecity: C1
+
- Long reads/writes don't work (consisting of multiple request packets)
Priority: Low
--
1.7.3.1
^ permalink raw reply related
* Re: [PATCH] TODO: Add attribute permission verification for attribute server
From: Johan Hedberg @ 2010-10-06 21:38 UTC (permalink / raw)
To: Claudio Takahasi; +Cc: linux-bluetooth
In-Reply-To: <1286397282-11921-1-git-send-email-claudio.takahasi@openbossa.org>
Hi Claudio,
On Wed, Oct 06, 2010, Claudio Takahasi wrote:
> ---
> TODO | 7 +++++++
> 1 files changed, 7 insertions(+), 0 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox