* Re: [PATCH 5/6] Bluetooth: mgmt: Add support for switching to LE peripheral mode
From: Vinicius Costa Gomes @ 2012-10-25 18:48 UTC (permalink / raw)
To: Anderson Lizardo, Marcel Holtmann, linux-bluetooth
In-Reply-To: <20121025150754.GA5845@x220>
Hi Johan,
On 18:07 Thu 25 Oct, Johan Hedberg wrote:
> Hi Vinicius,
>
> On Thu, Oct 25, 2012, Vinicius Costa Gomes wrote:
> > > On Thu, Oct 25, 2012, Johan Hedberg wrote:
> > > > This will need some more thinking. We could add a new mgmt command for
> > > > that, or if you wanna go crazy why not map this to the L2CAP socket
> > > > interface's connect() system call. I.e. if LE GAP is in peripheral mode
> > > > instead of doing HCI_LE_Create_Connection or just failing with "not
> > > > allowed" a socket connect() would simply trigger directed advertising to
> > > > the device in question and deliver the successful connection in the same
> > > > way as a central role triggered connect would do (unless we time out
> > > > waiting for the remote device to connect). Now that I think of this
> > > > second option it actually sounds quite natural and not so crazy after
> > > > all :)
> > >
> > > There's on problem with this though: we'd still be undirected
> > > connectable between doing mgmt_set_le(peripheral) and issuing the socket
> > > connect(). So maybe we might need to introduce a mgmt_set_le_connectable
> > > command and a "le-connectable" setting after all (that could only be set
> > > in peripheral mode).
> >
> > Why not sending undirected connectable events when there's an active
> > listen()?
>
> But we always have that, don't we? (the GATT server socket). Or are you
> saying that bluetoothd could close its GATT server socket before
> switching to peripheral mode and that would ensure that the kernel
> doesn't enable connectable advertising?
Right now bluetoothd always has a listen() active, yes. But remember
that that listen() was added only to help testing, in the case that
BlueZ always initiates connections that listen() is not needed.
What I am thinking is having more control about when to advertise, for
example, each profile that needs the peripheral role would increase the
reference of a listener, when all the references are dropped I stop
advertising.
>
> Johan
Cheers,
--
Vinicius
^ permalink raw reply
* Re: [RFC v0 05/15] network: Add network .connect and .disconnect
From: Johan Hedberg @ 2012-10-25 17:38 UTC (permalink / raw)
To: Mikel Astiz; +Cc: Anderson Lizardo, linux-bluetooth, Mikel Astiz
In-Reply-To: <CANT-zCUSkGJ1SWxUNU7J80WxRpotNbfEhmuQihoXj6WquaBePw@mail.gmail.com>
Hi Mikel,
On Thu, Oct 25, 2012, Mikel Astiz wrote:
> >> > + req = g_new0(struct connect_req, 1);
> >> > + req->device = dev;
> >>
> >> Isn't btd_device_ref() required here?
> >
> > Looks like you're right and I missed this in my initial review. I went
> > ahead and pushed an extra patch to fix this.
>
> I don't think this patch was needed. Actually, the device pointer in
> struct connect_req can be completely removed, since the callback
> already receives such pointer, and the network_peer takes care of the
> device refcounting. Mea culpa for adding this unnecessary field.
Good point. Feel free to send a patch to remove it.
Johan
^ permalink raw reply
* Re: [RFC v0 05/15] network: Add network .connect and .disconnect
From: Mikel Astiz @ 2012-10-25 15:53 UTC (permalink / raw)
To: Anderson Lizardo, Mikel Astiz, linux-bluetooth, Mikel Astiz
In-Reply-To: <20121025104219.GB30500@x220>
Hi,
On Thu, Oct 25, 2012 at 12:42 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Lizardo,
>
> On Wed, Oct 24, 2012, Anderson Lizardo wrote:
>> Hi Mikel,
>>
>> On Fri, Oct 19, 2012 at 11:39 AM, Mikel Astiz <mikel.astiz.oss@gmail.com> wrote:
>> > +static void connect_profile_cb(struct btd_device *device, int err,
>> > + const char *pdev, void *data)
>> > +{
>> > + struct connect_req *req = data;
>> > +
>> > + req->cb(req->profile, req->device, err);
>> > +
>> > + g_free(req);
>> > +}
>> > +
>> > +static int connect_profile(struct btd_device *dev, struct btd_profile *profile,
>> > + uint16_t id, btd_profile_cb cb)
>> > +{
>> > + struct connect_req *req;
>> > + int err;
>> > +
>> > + DBG("path %s id %u", device_get_path(dev), id);
>> > +
>> > + req = g_new0(struct connect_req, 1);
>> > + req->device = dev;
>>
>> Isn't btd_device_ref() required here?
>
> Looks like you're right and I missed this in my initial review. I went
> ahead and pushed an extra patch to fix this.
I don't think this patch was needed. Actually, the device pointer in
struct connect_req can be completely removed, since the callback
already receives such pointer, and the network_peer takes care of the
device refcounting. Mea culpa for adding this unnecessary field.
Cheers,
Mikel
^ permalink raw reply
* Re: [RFC v0 12/15] input: Add profile .disconnect
From: Mikel Astiz @ 2012-10-25 15:49 UTC (permalink / raw)
To: Mikel Astiz, linux-bluetooth, Mikel Astiz
In-Reply-To: <20121025103922.GA30500@x220>
Hi Johan,
On Thu, Oct 25, 2012 at 12:39 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Mikel,
>
> On Fri, Oct 19, 2012, Mikel Astiz wrote:
>> From: Mikel Astiz <mikel.astiz@bmw-carit.de>
>>
>> Add the disconnect hook to the btd_profile.
>> ---
>> profiles/input/manager.c | 1 +
>> 1 file changed, 1 insertion(+)
>
> I've applied the patches up until this one with the exception of the
> audio ones which I'll let Luiz to look at and sync up with the work he's
> doing for audio right now. The last three will still need some
> discussion as I'm not convinced that it's a good idea to have these
> generic profile objects instead or Adapter.Connect/DisconnectProfile.
The problem with Adapter.Connect/DisconnectProfile is that you are not
able to expose the state of the profile. That was the reason to
propose such APIs, since UIs and applications could be interested in
this information.
Cheers,
Mikel
^ permalink raw reply
* Re: [PATCH] Bluetooth: l2cap_physical_cfm() can be static
From: Mat Martineau @ 2012-10-25 15:29 UTC (permalink / raw)
To: Andrei Emeltchenko
Cc: Marcel Holtmann, Fengguang Wu, Gustavo Padovan, linux-bluetooth
In-Reply-To: <20121025071307.GA2502@aemeltch-MOBL1>
Hi -
On Thu, 25 Oct 2012, Andrei Emeltchenko wrote:
> Hi,
>
> On Wed, Oct 24, 2012 at 06:55:34PM -0700, Marcel Holtmann wrote:
>> Hi Fengguang,
>>
>>> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
>>> ---
>>> net/bluetooth/l2cap_core.c | 2 +-
>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> --- linux-next.orig/net/bluetooth/l2cap_core.c 2012-10-25 08:23:52.456742699 +0800
>>> +++ linux-next/net/bluetooth/l2cap_core.c 2012-10-25 08:23:55.056742760 +0800
>>> @@ -4569,7 +4569,7 @@ static void l2cap_do_move_cancel(struct
>>> l2cap_ertm_send(chan);
>>> }
>>>
>>> -void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id,
>>> +static void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id,
>>> u8 remote_amp_id)
>>
>> I rather wait for Mat to ACK or NACK this one. Maybe it is an oversight
>> or we need that later on to be actually public.
>
> Agree with Marcel here. This shall be public.
Yes, it does need to be public. Andrei will need to call it from the
AMP physical link code, but I'm not sure exactly where (HCI event
handling or somewhere in amp.c or a2mp.c).
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* Re: [RFCv1 10/11] Bluetooth: Add put(hcon) when deleting hchan
From: Gustavo Padovan @ 2012-10-25 15:22 UTC (permalink / raw)
To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1351167652-12346-11-git-send-email-Andrei.Emeltchenko.news@gmail.com>
Hi Andrei,
* Andrei Emeltchenko <Andrei.Emeltchenko.news@gmail.com> [2012-10-25 15:20:51 +0300]:
> From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
>
> When refcnt reaches zero disconnect timeout will run and hci_conn
> will be disconnected.
>
> Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> ---
> net/bluetooth/hci_conn.c | 2 ++
> 1 file changed, 2 insertions(+)
Patches 1-4 and 10 have been applied to bluetooth-next. I'll wait for more
feedback on the others, as this is still an RFC. Thanks.
Gustavo
^ permalink raw reply
* Re: [PATCH 3/6] Bluetooth: Fix sending unnecessary HCI_LE_Host_Enable
From: Gustavo Padovan @ 2012-10-25 15:20 UTC (permalink / raw)
To: Marcel Holtmann, linux-bluetooth
In-Reply-To: <20121025151430.GA6105@x220>
Hi,
* Johan Hedberg <johan.hedberg@gmail.com> [2012-10-25 18:14:30 +0300]:
> Hi,
>
> On Thu, Oct 25, 2012, Marcel Holtmann wrote:
> > > > From: Johan Hedberg <johan.hedberg@intel.com>
> > > >
> > > > This patch fixes sending an unnecessary HCI_LE_Host_Enable command if
> > > > the command has already been sent as part of the default HCI init
> > > > sequence.
> > > >
> > > > Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
> > > > ---
> > > > net/bluetooth/mgmt.c | 10 ++++++++--
> > > > 1 file changed, 8 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> > > > index 02dc5f8..03dc176 100644
> > > > --- a/net/bluetooth/mgmt.c
> > > > +++ b/net/bluetooth/mgmt.c
> > > > @@ -2927,8 +2927,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
> > > > cp.le = 1;
> > > > cp.simul = !!lmp_le_br_capable(hdev);
> > > >
> > > > - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
> > > > - sizeof(cp), &cp);
> > > > + /* Check first if we already have the right
> > > > + * host state (host features set)
> > > > + */
> > > > + if (cp.le != !!lmp_host_le_capable(hdev) ||
> > > > + cp.simul != !!lmp_host_le_br_capable(hdev))
> > >
> > > Shouldn't the !! be part of the macro itself? Looks we will be using !! always
> > > with these macros.
> >
> > we could do that actually. However I prefer to do that with a separate
> > patch.
>
> I was thinking of the same earlier, and yes a separate patch to convert
> all of the feature test macros would be the cleanest.
I didn't say it needed to be in the same patch. Anyway, I applied this patch
now, I'll do the macro's conversion later today.
Gustavo
^ permalink raw reply
* Re: [PATCH 3/6] Bluetooth: Fix sending unnecessary HCI_LE_Host_Enable
From: Johan Hedberg @ 2012-10-25 15:14 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: Gustavo Padovan, linux-bluetooth
In-Reply-To: <1351177676.1785.94.camel@aeonflux>
Hi,
On Thu, Oct 25, 2012, Marcel Holtmann wrote:
> > > From: Johan Hedberg <johan.hedberg@intel.com>
> > >
> > > This patch fixes sending an unnecessary HCI_LE_Host_Enable command if
> > > the command has already been sent as part of the default HCI init
> > > sequence.
> > >
> > > Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
> > > ---
> > > net/bluetooth/mgmt.c | 10 ++++++++--
> > > 1 file changed, 8 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> > > index 02dc5f8..03dc176 100644
> > > --- a/net/bluetooth/mgmt.c
> > > +++ b/net/bluetooth/mgmt.c
> > > @@ -2927,8 +2927,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
> > > cp.le = 1;
> > > cp.simul = !!lmp_le_br_capable(hdev);
> > >
> > > - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
> > > - sizeof(cp), &cp);
> > > + /* Check first if we already have the right
> > > + * host state (host features set)
> > > + */
> > > + if (cp.le != !!lmp_host_le_capable(hdev) ||
> > > + cp.simul != !!lmp_host_le_br_capable(hdev))
> >
> > Shouldn't the !! be part of the macro itself? Looks we will be using !! always
> > with these macros.
>
> we could do that actually. However I prefer to do that with a separate
> patch.
I was thinking of the same earlier, and yes a separate patch to convert
all of the feature test macros would be the cleanest.
Johan
^ permalink raw reply
* Re: [PATCH 4/6] Bluetooth: Fix unnecessary EIR update during powering on
From: Gustavo Padovan @ 2012-10-25 15:08 UTC (permalink / raw)
To: Johan Hedberg; +Cc: linux-bluetooth
In-Reply-To: <1351112996-9597-5-git-send-email-johan.hedberg@gmail.com>
Hi Johan,
* Johan Hedberg <johan.hedberg@gmail.com> [2012-10-25 00:09:54 +0300]:
> From: Johan Hedberg <johan.hedberg@intel.com>
>
> When powered on the EIR data gets updated as the last step by mgmt.
> Therefore avoid an update when getting a local name update as that's
> part of the normal HCI init sequence.
>
> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
> ---
> net/bluetooth/mgmt.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
Patch 1, 2 and 4 have been applied to bluetooth-next. Thanks.
Gustavo
^ permalink raw reply
* Re: [PATCH 3/6] Bluetooth: Fix sending unnecessary HCI_LE_Host_Enable
From: Marcel Holtmann @ 2012-10-25 15:07 UTC (permalink / raw)
To: Gustavo Padovan; +Cc: Johan Hedberg, linux-bluetooth
In-Reply-To: <20121025150520.GA1764@joana>
Hi Gustavo,
> > From: Johan Hedberg <johan.hedberg@intel.com>
> >
> > This patch fixes sending an unnecessary HCI_LE_Host_Enable command if
> > the command has already been sent as part of the default HCI init
> > sequence.
> >
> > Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
> > ---
> > net/bluetooth/mgmt.c | 10 ++++++++--
> > 1 file changed, 8 insertions(+), 2 deletions(-)
> >
> > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> > index 02dc5f8..03dc176 100644
> > --- a/net/bluetooth/mgmt.c
> > +++ b/net/bluetooth/mgmt.c
> > @@ -2927,8 +2927,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
> > cp.le = 1;
> > cp.simul = !!lmp_le_br_capable(hdev);
> >
> > - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
> > - sizeof(cp), &cp);
> > + /* Check first if we already have the right
> > + * host state (host features set)
> > + */
> > + if (cp.le != !!lmp_host_le_capable(hdev) ||
> > + cp.simul != !!lmp_host_le_br_capable(hdev))
>
> Shouldn't the !! be part of the macro itself? Looks we will be using !! always
> with these macros.
we could do that actually. However I prefer to do that with a separate
patch.
Regards
Marcel
^ permalink raw reply
* Re: [PATCH 5/6] Bluetooth: mgmt: Add support for switching to LE peripheral mode
From: Johan Hedberg @ 2012-10-25 15:07 UTC (permalink / raw)
To: Vinicius Costa Gomes; +Cc: Anderson Lizardo, Marcel Holtmann, linux-bluetooth
In-Reply-To: <20121025143138.GA27907@samus>
Hi Vinicius,
On Thu, Oct 25, 2012, Vinicius Costa Gomes wrote:
> > On Thu, Oct 25, 2012, Johan Hedberg wrote:
> > > This will need some more thinking. We could add a new mgmt command for
> > > that, or if you wanna go crazy why not map this to the L2CAP socket
> > > interface's connect() system call. I.e. if LE GAP is in peripheral mode
> > > instead of doing HCI_LE_Create_Connection or just failing with "not
> > > allowed" a socket connect() would simply trigger directed advertising to
> > > the device in question and deliver the successful connection in the same
> > > way as a central role triggered connect would do (unless we time out
> > > waiting for the remote device to connect). Now that I think of this
> > > second option it actually sounds quite natural and not so crazy after
> > > all :)
> >
> > There's on problem with this though: we'd still be undirected
> > connectable between doing mgmt_set_le(peripheral) and issuing the socket
> > connect(). So maybe we might need to introduce a mgmt_set_le_connectable
> > command and a "le-connectable" setting after all (that could only be set
> > in peripheral mode).
>
> Why not sending undirected connectable events when there's an active
> listen()?
But we always have that, don't we? (the GATT server socket). Or are you
saying that bluetoothd could close its GATT server socket before
switching to peripheral mode and that would ensure that the kernel
doesn't enable connectable advertising?
Johan
^ permalink raw reply
* Re: [PATCH 3/6] Bluetooth: Fix sending unnecessary HCI_LE_Host_Enable
From: Gustavo Padovan @ 2012-10-25 15:05 UTC (permalink / raw)
To: Johan Hedberg; +Cc: linux-bluetooth
In-Reply-To: <1351112996-9597-4-git-send-email-johan.hedberg@gmail.com>
Hi Johan,
* Johan Hedberg <johan.hedberg@gmail.com> [2012-10-25 00:09:53 +0300]:
> From: Johan Hedberg <johan.hedberg@intel.com>
>
> This patch fixes sending an unnecessary HCI_LE_Host_Enable command if
> the command has already been sent as part of the default HCI init
> sequence.
>
> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
> ---
> net/bluetooth/mgmt.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 02dc5f8..03dc176 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -2927,8 +2927,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
> cp.le = 1;
> cp.simul = !!lmp_le_br_capable(hdev);
>
> - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
> - sizeof(cp), &cp);
> + /* Check first if we already have the right
> + * host state (host features set)
> + */
> + if (cp.le != !!lmp_host_le_capable(hdev) ||
> + cp.simul != !!lmp_host_le_br_capable(hdev))
Shouldn't the !! be part of the macro itself? Looks we will be using !! always
with these macros.
Gustavo
^ permalink raw reply
* [RFC 18/18] doc: Rename cycling API to cyclingspeed
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
doc/cycling-api.txt | 100 -----------------------------------------------
doc/cyclingspeed-api.txt | 100 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 100 deletions(-)
delete mode 100644 doc/cycling-api.txt
create mode 100644 doc/cyclingspeed-api.txt
diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
deleted file mode 100644
index 08e11c8..0000000
--- a/doc/cycling-api.txt
+++ /dev/null
@@ -1,100 +0,0 @@
-Cycling Speed and Cadence API description
-*****************************************
-
-Copyright (C) 2012 Tieto Poland
-
-Cycling Speed and Cadence Manager hierarchy
-===========================================
-
-Service org.bluez
-Interface org.bluez.CyclingSpeedManager
-Object path [variable prefix]/{hci0,hci1,...}
-
-Methods RegisterWatcher(object agent)
-
- Registers a watcher to monitor cycling speed and
- cadence measurements.
-
- Possible Errors: org.bluez.Error.InvalidArguments
-
- UnregisterWatcher(object agent)
-
- Unregisters a watcher.
-
-Cycling Speed and Cadence Profile hierarchy
-===========================================
-
-Service org.bluez
-Interface org.bluez.CyclingSpeed
-Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-
-Methods SetCumulativeWheelRevolutions(uint32 value)
-
- Sets cumulative wheel revolutions value if
- Cumulative Wheel Revolutions feature is supported.
-
- Possible Errors: org.bluez.Error.NotSupported
-
-Properties string Location (optional) [readwrite]
-
- Current sensor location, if supported.
- If Multiple Sensor Locations feature is supported,
- this property can be set to one of values read from
- SupportedLocations property.
-
- Possible values: "other", "top-of-shoe", "in-shoe",
- "hip", "front-wheel", "left-crank",
- "right-crank", "left-pedal",
- "right-pedal", "front-hub",
- "rear-dropout", "chainstay",
- "rear-wheel", "rear-hub"
-
- array{string} SupportedLocations (optional) [readonly]
-
- List of locations supported by sensor, only present
- if Multiple Sensor Locations feature is supported.
-
- boolean WheelRevolutionDataSupported [readonly]
-
- true if sensor can read and set Cumulative Wheel
- Revolutions value, false otherwise.
-
- boolean MultipleSensorLocationsSupported [readonly]
-
- true if sensor supports Multiple Sensor Locations
- feature and can set Location, false otherwise.
-
-Cycling Speed and Cadence Watcher hierarchy
-===========================================
-
-Service unique name
-Interface org.bluez.CyclingSpeedWatcher
-Object path freely definable
-
-Methods void MeasurementReceived(object device, dict measurement)
-
- This callback is called whenever wheel and/or crank
- revolutions measurement is received from sensor.
-
- Measurement:
-
- uint32 WheelRevolutions (optional):
-
- Cumulative number of wheel revolutions.
-
- uint16 LastWheelEventTime (optional):
-
- Time of last event from wheel sensor.
- Value is expressed in 1/1024 second
- units and can roll over during a ride.
-
- uint16 CrankRevolutions (optional):
-
- Cumulative number of crank revolutions.
- This value can occasionally roll over.
-
- uint16 LastCrankEventTime (optional):
-
- Time of last event from crank sensor.
- Value is expressed in 1/1024 second
- units and can roll over during a ride.
diff --git a/doc/cyclingspeed-api.txt b/doc/cyclingspeed-api.txt
new file mode 100644
index 0000000..08e11c8
--- /dev/null
+++ b/doc/cyclingspeed-api.txt
@@ -0,0 +1,100 @@
+Cycling Speed and Cadence API description
+*****************************************
+
+Copyright (C) 2012 Tieto Poland
+
+Cycling Speed and Cadence Manager hierarchy
+===========================================
+
+Service org.bluez
+Interface org.bluez.CyclingSpeedManager
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods RegisterWatcher(object agent)
+
+ Registers a watcher to monitor cycling speed and
+ cadence measurements.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ UnregisterWatcher(object agent)
+
+ Unregisters a watcher.
+
+Cycling Speed and Cadence Profile hierarchy
+===========================================
+
+Service org.bluez
+Interface org.bluez.CyclingSpeed
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods SetCumulativeWheelRevolutions(uint32 value)
+
+ Sets cumulative wheel revolutions value if
+ Cumulative Wheel Revolutions feature is supported.
+
+ Possible Errors: org.bluez.Error.NotSupported
+
+Properties string Location (optional) [readwrite]
+
+ Current sensor location, if supported.
+ If Multiple Sensor Locations feature is supported,
+ this property can be set to one of values read from
+ SupportedLocations property.
+
+ Possible values: "other", "top-of-shoe", "in-shoe",
+ "hip", "front-wheel", "left-crank",
+ "right-crank", "left-pedal",
+ "right-pedal", "front-hub",
+ "rear-dropout", "chainstay",
+ "rear-wheel", "rear-hub"
+
+ array{string} SupportedLocations (optional) [readonly]
+
+ List of locations supported by sensor, only present
+ if Multiple Sensor Locations feature is supported.
+
+ boolean WheelRevolutionDataSupported [readonly]
+
+ true if sensor can read and set Cumulative Wheel
+ Revolutions value, false otherwise.
+
+ boolean MultipleSensorLocationsSupported [readonly]
+
+ true if sensor supports Multiple Sensor Locations
+ feature and can set Location, false otherwise.
+
+Cycling Speed and Cadence Watcher hierarchy
+===========================================
+
+Service unique name
+Interface org.bluez.CyclingSpeedWatcher
+Object path freely definable
+
+Methods void MeasurementReceived(object device, dict measurement)
+
+ This callback is called whenever wheel and/or crank
+ revolutions measurement is received from sensor.
+
+ Measurement:
+
+ uint32 WheelRevolutions (optional):
+
+ Cumulative number of wheel revolutions.
+
+ uint16 LastWheelEventTime (optional):
+
+ Time of last event from wheel sensor.
+ Value is expressed in 1/1024 second
+ units and can roll over during a ride.
+
+ uint16 CrankRevolutions (optional):
+
+ Cumulative number of crank revolutions.
+ This value can occasionally roll over.
+
+ uint16 LastCrankEventTime (optional):
+
+ Time of last event from crank sensor.
+ Value is expressed in 1/1024 second
+ units and can roll over during a ride.
--
1.8.0
^ permalink raw reply related
* [RFC 17/18] test: Add cyclingspeed test script
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
Makefile.tools | 2 +-
test/test-cyclingspeed | 115 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 116 insertions(+), 1 deletion(-)
create mode 100755 test/test-cyclingspeed
diff --git a/Makefile.tools b/Makefile.tools
index ec79cea..9168be9 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -215,4 +215,4 @@ EXTRA_DIST += test/sap_client.py test/hsplay test/hsmicro \
test/test-health test/test-health-sink test/service-record.dtd \
test/service-did.xml test/service-spp.xml test/service-opp.xml \
test/service-ftp.xml test/simple-player test/test-nap \
- test/test-heartrate test/test-alert
+ test/test-heartrate test/test-alert test/test-cycling
diff --git a/test/test-cyclingspeed b/test/test-cyclingspeed
new file mode 100755
index 0000000..f3771d3
--- /dev/null
+++ b/test/test-cyclingspeed
@@ -0,0 +1,115 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Cycling Speed and Cadence test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+class Watcher(dbus.service.Object):
+ @dbus.service.method("org.bluez.CyclingSpeedWatcher",
+ in_signature="oa{sv}", out_signature="")
+ def MeasurementReceived(self, device, measure):
+ print("Measurement received from %s" % device)
+
+ if "WheelRevolutions" in measure:
+ print("WheelRevolutions: ", measure["WheelRevolutions"])
+
+ if "LastWheelEventTime" in measure:
+ print("LastWheelEventTime: ", measure["LastWheelEventTime"])
+
+ if "CrankRevolutions" in measure:
+ print("CrankRevolutions: ", measure["CrankRevolutions"])
+
+ if "LastCrankEventTime" in measure:
+ print("LastCrankEventTime: ", measure["LastCrankEventTime"])
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adapter"),
+ make_option("-b", "--device", action="store",
+ type="string", dest="address"),
+ ]
+
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if not options.address:
+ print("Usage: %s [-i <adapter>] -b <bdaddr> [-c <value>] [cmd]" % (sys.argv[0]))
+ print("Possible commands:")
+ print("\tShowSupportedLocations")
+ print("\tSetLocation <location>")
+ print("\tSetCumulativeWheelRevolutions <value>")
+ sys.exit(1)
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+ if options.adapter:
+ adapter_path = manager.FindAdapter(options.adapter)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ device_path = adapter.FindDevice(options.address)
+
+ cscmanager = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.CyclingSpeedManager")
+
+ watcher_path = "/test/watcher"
+ watcher = Watcher(bus, watcher_path)
+ cscmanager.RegisterWatcher(watcher_path)
+
+ csc = dbus.Interface(bus.get_object("org.bluez", device_path),
+ "org.bluez.CyclingSpeed")
+
+ device_prop = dbus.Interface(bus.get_object("org.bluez", device_path),
+ "org.freedesktop.DBus.Properties")
+
+ properties = device_prop.GetAll("org.bluez.CyclingSpeed")
+
+ if "Location" in properties:
+ print("Sensor location: %s" % properties["Location"])
+ else:
+ print("Sensor location is not supported")
+
+ if len(args) > 0:
+ if args[0] == "ShowSupportedLocations":
+ if properties["MultipleSensorLocationsSupported"]:
+ print("Supported locations: ", properties["SupportedLocations"])
+ else:
+ print("Multiple sensor locations not supported")
+
+ elif args[0] == "SetLocation":
+ if properties["MultipleSensorLocationsSupported"]:
+ device_prop.Set("org.bluez.CyclingSpeed", "Location", args[1])
+ else:
+ print("Multiple sensor locations not supported")
+
+ elif args[0] == "SetCumulativeWheelRevolutions":
+ if properties["WheelRevolutionDataSupported"]:
+ csc.SetCumulativeWheelRevolutions(dbus.UInt32(args[1]))
+ else:
+ print("Wheel revolution data not supported")
+
+ else:
+ print("Unknown command")
+ sys.exit(1)
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
--
1.8.0
^ permalink raw reply related
* [RFC 16/18] core: Add CyclingSpeedWatcher interface to default policy
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
src/bluetooth.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 2db43d9..a1269a4 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -19,6 +19,7 @@
<allow send_interface="org.bluez.AlertAgent"/>
<allow send_interface="org.bluez.Profile"/>
<allow send_interface="org.bluez.HeartRateWatcher"/>
+ <allow send_interface="org.bluez.CyclingSpeedWatcher"/>
</policy>
<policy at_console="true">
--
1.8.0
^ permalink raw reply related
* [RFC 15/18] cyclingspeed: Add support for Set Cumulative Value
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 92 +++++++++++++++++++++++++++++++++++-
1 file changed, 91 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index bf8ea46..7b5c3f5 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -75,6 +75,7 @@ struct controlpoint_req {
uint8_t opcode;
guint timeout;
GDBusPendingReply reply_id;
+ DBusMessage *msg;
};
struct csc_adapter {
@@ -287,6 +288,15 @@ static gboolean controlpoint_timeout(gpointer user_data)
g_dbus_pending_property_error(req->reply_id,
ERROR_INTERFACE ".Failed",
"Operation failed (timeout)");
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg,
+ "Operation failed (timeout)");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
}
req->csc->pending_req = NULL;
@@ -307,6 +317,15 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
g_dbus_pending_property_error(req->reply_id,
ERROR_INTERFACE ".Failed",
"Operation failed (%d)", status);
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg,
+ "Operation failed");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
}
req->csc->pending_req = NULL;
@@ -616,6 +635,34 @@ static void controlpoint_property_reply(struct controlpoint_req *req,
}
}
+static void controlpoint_method_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ DBusMessage *reply;
+
+ switch (code) {
+ case RSP_SUCCESS:
+ reply = dbus_message_new_method_return(req->msg);
+ break;
+ case RSP_NOT_SUPPORTED:
+ reply = btd_error_not_supported(req->msg);
+ break;
+ case RSP_INVALID_PARAM:
+ reply = btd_error_invalid_args(req->msg);
+ break;
+ case RSP_FAILED:
+ reply = btd_error_failed(req->msg, "Failed");
+ break;
+ default:
+ reply = btd_error_failed(req->msg, "Unknown error");
+ break;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
+}
+
static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
gpointer user_data)
{
@@ -667,6 +714,10 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
}
switch (req->opcode) {
+ case SET_CUMULATIVE_VALUE:
+ controlpoint_method_reply(req, rsp_code);
+ break;
+
case REQUEST_SUPPORTED_SENSOR_LOC:
if (rsp_code == RSP_SUCCESS) {
csc->num_locations = len;
@@ -1069,6 +1120,44 @@ static const GDBusPropertyTable cyclingspeed_device_properties[] = {
{ }
};
+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct csc *csc = data;
+ dbus_uint32_t value;
+ struct controlpoint_req *req;
+ uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (csc->pending_req != NULL)
+ return btd_error_in_progress(msg);
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = SET_CUMULATIVE_VALUE;
+ req->msg = dbus_message_ref(msg);
+
+ csc->pending_req = req;
+
+ att_val[0] = SET_CUMULATIVE_VALUE;
+ att_put_u32(value, att_val + 1);
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+
+ return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+ { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+ GDBUS_ARGS({ "value", "u" }), NULL,
+ set_cumulative_wheel_rev) },
+ { }
+};
+
int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
{
struct btd_adapter *adapter;
@@ -1088,7 +1177,8 @@ int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
if (!g_dbus_register_interface(btd_get_dbus_connection(),
device_get_path(device),
CYCLINGSPEED_INTERFACE,
- NULL, NULL,
+ cyclingspeed_device_methods,
+ NULL,
cyclingspeed_device_properties,
csc, destroy_csc)) {
error("D-Bus failed to register %s interface",
--
1.8.0
^ permalink raw reply related
* [RFC 14/18] cyclingspeed: Add support for Update Sensor Location
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 116 ++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 2 deletions(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 164a525..bf8ea46 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -74,6 +74,7 @@ struct controlpoint_req {
struct csc *csc;
uint8_t opcode;
guint timeout;
+ GDBusPendingReply reply_id;
};
struct csc_adapter {
@@ -148,6 +149,17 @@ static const gchar *location2str(uint8_t value)
return location_enum[0];
}
+static int str2location(const char *location)
+{
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+ if (!strcmp(location_enum[i], location))
+ return i;
+
+ return -1;
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct csc_adapter *cadapter = a;
@@ -271,6 +283,12 @@ static gboolean controlpoint_timeout(gpointer user_data)
{
struct controlpoint_req *req = user_data;
+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (timeout)");
+ }
+
req->csc->pending_req = NULL;
g_free(req);
@@ -285,6 +303,12 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
if (status != 0) {
error("SC Control Point write failed (opcode=%d)", req->opcode);
+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", status);
+ }
+
req->csc->pending_req = NULL;
g_free(req);
@@ -558,6 +582,40 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
process_measurement(csc, pdu + 3, len - 3);
}
+static void controlpoint_property_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ switch (code) {
+ case RSP_SUCCESS:
+ g_dbus_pending_property_success(req->reply_id);
+ break;
+
+ case RSP_NOT_SUPPORTED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+ break;
+
+ case RSP_INVALID_PARAM:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ break;
+
+ case RSP_FAILED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed");
+ break;
+
+ default:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", code);
+ break;
+ }
+}
+
static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
gpointer user_data)
{
@@ -565,6 +623,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
struct controlpoint_req *req = csc->pending_req;
uint8_t opcode;
uint8_t req_opcode;
+ uint8_t rsp_code;
uint8_t *opdu;
uint16_t olen;
size_t plen;
@@ -598,7 +657,7 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
}
req_opcode = *pdu;
- /* skip response code for now */
+ rsp_code = *(pdu + 1);
pdu += 2;
len -= 2;
@@ -616,6 +675,10 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
error("Failed to read Supported Sendor Locations");
}
break;
+
+ case UPDATE_SENSOR_LOC:
+ controlpoint_property_reply(req, rsp_code);
+ break;
}
csc->pending_req = NULL;
@@ -884,6 +947,55 @@ static gboolean property_get_location(const GDBusPropertyTable *property,
return TRUE;
}
+static void property_set_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct csc *csc = data;
+ char *loc;
+ int loc_val;
+ uint8_t att_val[2];
+ struct controlpoint_req *req;
+
+ if (csc->pending_req != NULL)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InProgress",
+ "Operation already in progress");
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+
+ dbus_message_iter_get_basic(iter, &loc);
+
+ loc_val = str2location(loc);
+
+ if (loc_val < 0)
+ return g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->reply_id = id;
+ req->opcode = UPDATE_SENSOR_LOC;
+
+ csc->pending_req = req;
+
+ att_val[0] = UPDATE_SENSOR_LOC;
+ att_val[1] = loc_val;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+
+}
+
static gboolean property_exists_location(const GDBusPropertyTable *property,
void *data)
{
@@ -948,7 +1060,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
}
static const GDBusPropertyTable cyclingspeed_device_properties[] = {
- { "Location", "s", property_get_location, NULL,
+ { "Location", "s", property_get_location, property_set_location,
property_exists_location },
{ "SupportedLocations", "as", property_get_locations, NULL,
property_exists_locations },
--
1.8.0
^ permalink raw reply related
* [RFC 13/18] cyclingspeed: Add support for Request Supported Sensor Locations
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 57 ++++++++++++++++++++++++++++++++++--
1 file changed, 54 insertions(+), 3 deletions(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 93b91a2..164a525 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -101,6 +101,8 @@ struct csc {
uint16_t feature;
gboolean has_location;
uint8_t location;
+ uint8_t num_locations;
+ uint8_t *locations;
struct controlpoint_req *pending_req;
};
@@ -250,6 +252,7 @@ static void destroy_csc(gpointer user_data)
btd_device_unref(csc->dev);
g_free(csc->svc_range);
+ g_free(csc->locations);
g_free(csc);
}
@@ -274,7 +277,6 @@ static gboolean controlpoint_timeout(gpointer user_data)
return FALSE;
}
-__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
@@ -293,6 +295,20 @@ static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
req);
}
+static void read_supported_locations(struct csc *csc)
+{
+ struct controlpoint_req *req;
+
+ req = g_new0(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+ csc->pending_req = req;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, &req->opcode,
+ sizeof(req->opcode), controlpoint_write_cb, req);
+}
+
static void read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -317,6 +333,9 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
}
csc->feature = att_get_u16(value);
+
+ if (csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+ read_supported_locations(csc);
}
static void read_location_cb(guint8 status, const guint8 *pdu,
@@ -588,7 +607,16 @@ static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
goto done;
}
- /* TODO: handle response */
+ switch (req->opcode) {
+ case REQUEST_SUPPORTED_SENSOR_LOC:
+ if (rsp_code == RSP_SUCCESS) {
+ csc->num_locations = len;
+ csc->locations = g_memdup(pdu, len);
+ } else {
+ error("Failed to read Supported Sendor Locations");
+ }
+ break;
+ }
csc->pending_req = NULL;
g_source_remove(req->timeout);
@@ -864,6 +892,29 @@ static gboolean property_exists_location(const GDBusPropertyTable *property,
return csc->has_location;
}
+static gboolean property_get_locations(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ DBusMessageIter entry;
+ int i;
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+ return FALSE;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ for (i = 0; i < csc->num_locations; i++) {
+ char *loc = g_strdup(location2str(csc->locations[i]));
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+ g_free(loc);
+ }
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
static gboolean property_exists_locations(const GDBusPropertyTable *property,
void *data)
{
@@ -899,7 +950,7 @@ static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
static const GDBusPropertyTable cyclingspeed_device_properties[] = {
{ "Location", "s", property_get_location, NULL,
property_exists_location },
- { "SupportedLocations", "as", NULL, NULL,
+ { "SupportedLocations", "as", property_get_locations, NULL,
property_exists_locations },
{ "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
{ "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
--
1.8.0
^ permalink raw reply related
* [RFC 12/18] cyclingspeed: Add stub to use SC Control Point
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
This patch implements common functions to use SC Control Point.
Individual procedures will be implemented in subsequent patches.
---
profiles/cyclingspeed/cyclingspeed.c | 145 +++++++++++++++++++++++++++++++++--
1 file changed, 140 insertions(+), 5 deletions(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 4f48121..93b91a2 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -41,6 +41,11 @@
#include "log.h"
#include "cyclingspeed.h"
+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
@@ -52,6 +57,25 @@
#define WHEEL_REV_PRESENT 0x01
#define CRANK_REV_PRESENT 0x02
+#define SET_CUMULATIVE_VALUE 0x01
+#define START_SENSOR_CALIBRATION 0x02
+#define UPDATE_SENSOR_LOC 0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC 0x04
+#define RESPONSE_CODE 0x10
+
+#define RSP_SUCCESS 0x01
+#define RSP_NOT_SUPPORTED 0x02
+#define RSP_INVALID_PARAM 0x03
+#define RSP_FAILED 0x04
+
+struct csc;
+
+struct controlpoint_req {
+ struct csc *csc;
+ uint8_t opcode;
+ guint timeout;
+};
+
struct csc_adapter {
struct btd_adapter *adapter;
GSList *devices; /* list of registered devices */
@@ -66,6 +90,8 @@ struct csc {
guint attioid;
/* attio id for measurement characteristics value notifications */
guint attio_measurement_id;
+ /* attio id for SC Control Point characteristics value indications */
+ guint attio_controlpoint_id;
struct att_range *svc_range;
@@ -75,6 +101,8 @@ struct csc {
uint16_t feature;
gboolean has_location;
uint8_t location;
+
+ struct controlpoint_req *pending_req;
};
struct watcher {
@@ -216,6 +244,7 @@ static void destroy_csc(gpointer user_data)
if (csc->attrib != NULL) {
g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
g_attrib_unref(csc->attrib);
}
@@ -235,6 +264,35 @@ static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
g_free(msg);
}
+static gboolean controlpoint_timeout(gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+
+ return FALSE;
+}
+
+__attribute__((unused)) /* TODO: remove once controlpoint ops are implemented */
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ if (status != 0) {
+ error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+
+ return;
+ }
+
+ req->timeout = g_timeout_add_seconds(ATT_TIMEOUT, controlpoint_timeout,
+ req);
+}
+
static void read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -312,6 +370,8 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
for (i = 0; i < list->num; i++) {
uint8_t *value;
uint16_t handle, uuid;
+ uint8_t attr_val[2];
+ char *msg;
value = list->data[i];
handle = att_get_u16(value);
@@ -321,9 +381,6 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
continue;
if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
- char *msg;
- uint8_t attr_val[2];
-
ch->csc->measurement_ccc_handle = handle;
if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
@@ -335,10 +392,16 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
msg = g_strdup("Enable measurement");
}
- gatt_write_char(ch->csc->attrib, handle, attr_val,
- sizeof(attr_val), char_write_cb, msg);
+ } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+ msg = g_strdup("Enable SC Control Point indications");
+ } else {
+ break;
}
+ gatt_write_char(ch->csc->attrib, handle, attr_val,
+ sizeof(attr_val), char_write_cb, msg);
+
/* We only want CCC, can break here */
break;
}
@@ -476,6 +539,67 @@ static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
process_measurement(csc, pdu + 3, len - 3);
}
+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+ struct controlpoint_req *req = csc->pending_req;
+ uint8_t opcode;
+ uint8_t req_opcode;
+ uint8_t *opdu;
+ uint16_t olen;
+ size_t plen;
+
+ if (len < ATT_HDR_LEN) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ /* skip ATT header */
+ pdu += ATT_HDR_LEN;
+ len -= ATT_HDR_LEN;
+
+ if (len < 1) {
+ error("Op Code missing");
+ goto done;
+ }
+
+ opcode = *pdu;
+ pdu++;
+ len--;
+
+ if (opcode != RESPONSE_CODE) {
+ DBG("Unsupported Op Code received (%d)", opcode);
+ goto done;
+ }
+
+ if (len < 2) {
+ error("Invalid Response Code PDU received");
+ goto done;
+ }
+
+ req_opcode = *pdu;
+ /* skip response code for now */
+ pdu += 2;
+ len -= 2;
+
+ if (req == NULL || req->opcode != req_opcode) {
+ DBG("Indication received without pending request");
+ goto done;
+ }
+
+ /* TODO: handle response */
+
+ csc->pending_req = NULL;
+ g_source_remove(req->timeout);
+ g_free(req);
+
+done:
+ opdu = g_attrib_get_buffer(csc->attrib, &plen);
+ olen = enc_confirmation(opdu, plen);
+ if (olen > 0)
+ g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
@@ -509,6 +633,12 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
+
+ csc->attio_controlpoint_id = g_attrib_register(
+ csc->attrib, ATT_OP_HANDLE_IND,
+ c->value_handle,
+ controlpoint_ind_handler, csc, NULL);
+
discover_desc(csc, c, c_next);
}
}
@@ -576,6 +706,11 @@ static void attio_disconnected_cb(gpointer user_data)
csc->attio_measurement_id = 0;
}
+ if (csc->attio_controlpoint_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+ csc->attio_controlpoint_id = 0;
+ }
+
g_attrib_unref(csc->attrib);
csc->attrib = NULL;
}
--
1.8.0
^ permalink raw reply related
* [RFC 11/18] doc: Remove Get-/SetProperties from CSC API document
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
cyclingspeed plugin uses DBus.Properties instead of custom methods.
---
doc/cycling-api.txt | 20 +-------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/doc/cycling-api.txt b/doc/cycling-api.txt
index adbcd33..08e11c8 100644
--- a/doc/cycling-api.txt
+++ b/doc/cycling-api.txt
@@ -28,31 +28,13 @@ Service org.bluez
Interface org.bluez.CyclingSpeed
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
-Methods void SetProperty(string name, variant value)
-
- Changes the value of the specified property. Only
- read-write properties can be changed. On success
- this will emit a PropertyChanged signal.
-
- Possible Errors: org.bluez.Error.InvalidArguments
-
- dict GetProperties()
-
- Returns all properties for the interface. See the
- Properties section for the available properties.
-
- SetCumulativeWheelRevolutions(uint32 value)
+Methods SetCumulativeWheelRevolutions(uint32 value)
Sets cumulative wheel revolutions value if
Cumulative Wheel Revolutions feature is supported.
Possible Errors: org.bluez.Error.NotSupported
-Signals PropertyChanged(string name, variant value)
-
- This signal indicates a changed value of the given
- property.
-
Properties string Location (optional) [readwrite]
Current sensor location, if supported.
--
1.8.0
^ permalink raw reply related
* [RFC 10/18] cyclingspeed: Add DBus.Properties for org.bluez.CyclingSpeed interface
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 99 +++++++++++++++++++++++++++++++++++-
1 file changed, 98 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index f7ffd2e..4f48121 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -43,6 +43,7 @@
#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
+#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed"
#define WHEEL_REV_SUPPORT 0x01
#define CRANK_REV_SUPPORT 0x02
@@ -102,6 +103,21 @@ struct characteristic {
static GSList *csc_adapters = NULL;
+static const char * const location_enum[] = {
+ "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+ "right-crank", "left-pedal", "right-pedal", "front-hub",
+ "rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const gchar *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_enum))
+ return location_enum[value];
+
+ info("Body Sensor Location [%d] is RFU", value);
+ return location_enum[0];
+}
+
static gint cmp_adapter(gconstpointer a, gconstpointer b)
{
const struct csc_adapter *cadapter = a;
@@ -687,6 +703,74 @@ void csc_adapter_unregister(struct btd_adapter *adapter)
CYCLINGSPEED_MANAGER_INTERFACE);
}
+static gboolean property_get_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ char *loc;
+
+ if (!csc->has_location)
+ return FALSE;
+
+ loc = g_strdup(location2str(csc->location));
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+ g_free(loc);
+
+ return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return csc->has_location;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & WHEEL_REV_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+ { "Location", "s", property_get_location, NULL,
+ property_exists_location },
+ { "SupportedLocations", "as", NULL, NULL,
+ property_exists_locations },
+ { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+ { "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+ { }
+};
+
int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
{
struct btd_adapter *adapter;
@@ -703,6 +787,18 @@ int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
csc->dev = btd_device_ref(device);
csc->cadapter = cadapter;
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ device_get_path(device),
+ CYCLINGSPEED_INTERFACE,
+ NULL, NULL,
+ cyclingspeed_device_properties,
+ csc, destroy_csc)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_INTERFACE);
+ destroy_csc(csc);
+ return -EIO;
+ }
+
csc->svc_range = g_new0(struct att_range, 1);
csc->svc_range->start = prim->range.start;
csc->svc_range->end = prim->range.end;
@@ -736,5 +832,6 @@ void csc_device_unregister(struct btd_device *device)
cadapter->devices = g_slist_remove(cadapter->devices, csc);
- destroy_csc(csc);
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(device), CYCLINGSPEED_INTERFACE);
}
--
1.8.0
^ permalink raw reply related
* [RFC 09/18] cyclingspeed: Process measurement notifications
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 139 ++++++++++++++++++++++++++++++++++-
1 file changed, 138 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 53fbb02..f7ffd2e 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -42,6 +42,14 @@
#include "cyclingspeed.h"
#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
+#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher"
+
+#define WHEEL_REV_SUPPORT 0x01
+#define CRANK_REV_SUPPORT 0x02
+#define MULTI_SENSOR_LOC_SUPPORT 0x04
+
+#define WHEEL_REV_PRESENT 0x01
+#define CRANK_REV_PRESENT 0x02
struct csc_adapter {
struct btd_adapter *adapter;
@@ -55,6 +63,8 @@ struct csc {
GAttrib *attrib;
guint attioid;
+ /* attio id for measurement characteristics value notifications */
+ guint attio_measurement_id;
struct att_range *svc_range;
@@ -73,6 +83,18 @@ struct watcher {
char *path;
};
+struct measurement {
+ struct csc *csc;
+
+ bool has_wheel_rev;
+ uint32_t wheel_rev;
+ uint16_t last_wheel_time;
+
+ bool has_crank_rev;
+ uint16_t crank_rev;
+ uint16_t last_crank_time;
+};
+
struct characteristic {
struct csc *csc;
char uuid[MAX_LEN_UUID_STR + 1];
@@ -176,8 +198,10 @@ static void destroy_csc(gpointer user_data)
if (csc->attioid > 0)
btd_device_remove_attio_callback(csc->dev, csc->attioid);
- if (csc->attrib != NULL)
+ if (csc->attrib != NULL) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
g_attrib_unref(csc->attrib);
+ }
btd_device_unref(csc->dev);
g_free(csc->svc_range);
@@ -334,6 +358,109 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
gatt_find_info(csc->attrib, start, end, discover_desc_cb, ch);
}
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ struct csc *csc = m->csc;
+ const gchar *path = device_get_path(csc->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (m->has_wheel_rev) {
+ dict_append_entry(&dict, "WheelRevolutions",
+ DBUS_TYPE_UINT32, &m->wheel_rev);
+ dict_append_entry(&dict, "LastWheelEventTime",
+ DBUS_TYPE_UINT16, &m->last_wheel_time);
+ }
+
+ if (m->has_crank_rev) {
+ dict_append_entry(&dict, "CrankRevolutions",
+ DBUS_TYPE_UINT16, &m->crank_rev);
+ dict_append_entry(&dict, "LastCrankEventTime",
+ DBUS_TYPE_UINT16, &m->last_crank_time);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct csc *csc, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ flags = *pdu;
+
+ pdu++;
+ len--;
+
+ memset(&m, 0, sizeof(m));
+
+ if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+ if (len < 6) {
+ error("Wheel revolutions data fields missing");
+ return;
+ }
+
+ m.has_wheel_rev = true;
+ m.wheel_rev = att_get_u32(pdu);
+ m.last_wheel_time = att_get_u16(pdu + 4);
+ pdu += 6;
+ len -= 6;
+ }
+
+ if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+ if (len < 4) {
+ error("Crank revolutions data fields missing");
+ return;
+ }
+
+ m.has_crank_rev = true;
+ m.crank_rev = att_get_u16(pdu);
+ m.last_crank_time = att_get_u16(pdu + 2);
+ pdu += 4;
+ len -= 4;
+ }
+
+ /* Notify all registered watchers */
+ m.csc = csc;
+ g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ /* should be at least opcode (1b) + handle (2b) */
+ if (len < 3) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ process_measurement(csc, pdu + 3, len - 3);
+}
+
+
static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
{
struct csc *csc = user_data;
@@ -351,6 +478,11 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
(chars->next ? chars->next->data : NULL);
if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ csc->attio_measurement_id =
+ g_attrib_register(csc->attrib,
+ ATT_OP_HANDLE_NOTIFY, c->value_handle,
+ measurement_notify_handler, csc, NULL);
+
discover_desc(csc, c, c_next);
} else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
feature_val_handle = c->value_handle;
@@ -423,6 +555,11 @@ static void attio_disconnected_cb(gpointer user_data)
DBG("");
+ if (csc->attio_measurement_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ csc->attio_measurement_id = 0;
+ }
+
g_attrib_unref(csc->attrib);
csc->attrib = NULL;
}
--
1.8.0
^ permalink raw reply related
* [RFC 08/18] cyclingspeed: Add support to enable measurement notifications
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 72 +++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 0b3742a..53fbb02 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -184,6 +184,17 @@ static void destroy_csc(gpointer user_data)
g_free(csc);
}
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
static void read_feature_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -269,9 +280,25 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
continue;
- if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0)
+ if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ char *msg;
+ uint8_t attr_val[2];
+
ch->csc->measurement_ccc_handle = handle;
+ if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+ att_put_u16(0x0000, attr_val);
+ msg = g_strdup("Disable measurement");
+ } else {
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+ attr_val);
+ msg = g_strdup("Enable measurement");
+ }
+
+ gatt_write_char(ch->csc->attrib, handle, attr_val,
+ sizeof(attr_val), char_write_cb, msg);
+ }
+
/* We only want CCC, can break here */
break;
}
@@ -343,6 +370,40 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
read_feature_cb, csc);
}
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ att_put_u16(0x0000, value);
+ msg = g_strdup("Disable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct csc *csc = user_data;
@@ -375,6 +436,9 @@ static void watcher_exit_cb(DBusConnection *conn, void *user_data)
cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, disable_measurement, 0);
}
static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
@@ -400,6 +464,9 @@ static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
watcher->srv = g_strdup(sender);
watcher->path = g_strdup(path);
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, enable_measurement, 0);
+
cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
DBG("cycling watcher [%s] registered", path);
@@ -426,6 +493,9 @@ static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
g_dbus_remove_watch(conn, watcher->id);
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
DBG("cycling watcher [%s] unregistered", path);
return dbus_message_new_method_return(msg);
--
1.8.0
^ permalink raw reply related
* [RFC 07/18] cyclingspeed: Add CyclingSpeedManager interface
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
This patch registers org.bluez.CyclingSpeedManager interface for each
adapter to allow registration and deregistration of measurement watchers.
---
profiles/cyclingspeed/cyclingspeed.c | 154 ++++++++++++++++++++++++++++++++++-
1 file changed, 153 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 67963d1..0b3742a 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -24,13 +24,16 @@
#include <config.h>
#endif
+#include <gdbus.h>
#include <errno.h>
#include <stdbool.h>
#include <glib.h>
#include <bluetooth/uuid.h>
#include "adapter.h"
+#include "dbus-common.h"
#include "device.h"
+#include "error.h"
#include "gattrib.h"
#include "att.h"
#include "gatt.h"
@@ -38,9 +41,12 @@
#include "log.h"
#include "cyclingspeed.h"
+#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager"
+
struct csc_adapter {
struct btd_adapter *adapter;
GSList *devices; /* list of registered devices */
+ GSList *watchers;
};
struct csc {
@@ -60,6 +66,13 @@ struct csc {
uint8_t location;
};
+struct watcher {
+ struct csc_adapter *cadapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
struct characteristic {
struct csc *csc;
char uuid[MAX_LEN_UUID_STR + 1];
@@ -89,6 +102,19 @@ static gint cmp_device(gconstpointer a, gconstpointer b)
return -1;
}
+static gint cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
{
GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
@@ -99,10 +125,47 @@ static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
return l->data;
}
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
static void destroy_csc_adapter(gpointer user_data)
{
struct csc_adapter *cadapter = user_data;
+ g_slist_free_full(cadapter->watchers, remove_watcher);
+
g_free(cadapter);
}
@@ -303,6 +366,81 @@ static void attio_disconnected_cb(gpointer user_data)
csc->attrib = NULL;
}
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct csc_adapter *cadapter = watcher->cadapter;
+
+ DBG("cycling watcher [%s] disconnected", watcher->path);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(cadapter->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->cadapter = cadapter;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+ watcher, destroy_watcher);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+
+ cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+ DBG("cycling watcher [%s] registered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(cadapter->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ DBG("cycling watcher [%s] unregistered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { }
+};
+
int csc_adapter_register(struct btd_adapter *adapter)
{
struct csc_adapter *cadapter;
@@ -312,6 +450,18 @@ int csc_adapter_register(struct btd_adapter *adapter)
csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE,
+ cyclingspeed_manager_methods,
+ NULL, NULL, cadapter,
+ destroy_csc_adapter)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_MANAGER_INTERFACE);
+ destroy_csc_adapter(cadapter);
+ return -EIO;
+ }
+
return 0;
}
@@ -325,7 +475,9 @@ void csc_adapter_unregister(struct btd_adapter *adapter)
csc_adapters = g_slist_remove(csc_adapters, cadapter);
- destroy_csc_adapter(cadapter);
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(cadapter->adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE);
}
int csc_device_register(struct btd_device *device, struct gatt_primary *prim)
--
1.8.0
^ permalink raw reply related
* [RFC 06/18] cyclingspeed: Read Sensor Location characteristic value
From: Andrzej Kaczmarek @ 2012-10-25 14:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1351176228-5789-1-git-send-email-andrzej.kaczmarek@tieto.com>
---
profiles/cyclingspeed/cyclingspeed.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index fa97911..67963d1 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -56,6 +56,8 @@ struct csc {
uint16_t controlpoint_val_handle;
uint16_t feature;
+ gboolean has_location;
+ uint8_t location;
};
struct characteristic {
@@ -145,6 +147,33 @@ static void read_feature_cb(guint8 status, const guint8 *pdu,
csc->feature = att_get_u16(value);
}
+static void read_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status) {
+ error("Sensor Location read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid value length for Sensor Location");
+ return;
+ }
+
+ csc->has_location = TRUE;
+ csc->location = value;
+}
+
static void discover_desc_cb(guint8 status, const guint8 *pdu,
guint16 len, gpointer user_data)
{
@@ -237,7 +266,8 @@ static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
feature_val_handle = c->value_handle;
} else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
DBG("Sensor Location supported");
- /* TODO: read characterictic value */
+ gatt_read_char(csc->attrib, c->value_handle,
+ read_location_cb, csc);
} else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
DBG("SC Control Point supported");
csc->controlpoint_val_handle = c->value_handle;
--
1.8.0
^ permalink raw reply related
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