* [PATCH 1/5] android: Add haltest skeleton
From: Jerzy Kasenberg @ 2013-10-14 9:59 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Jerzy Kasenberg
In-Reply-To: <1381744794-17994-1-git-send-email-jerzy.kasenberg@tieto.com>
This tool will be used to test Android Bluetooth HAL implementation.
---
android/Android.mk | 17 +++++
android/hal_client/haltest.c | 57 +++++++++++++++++
android/hal_client/pollhandler.c | 129 ++++++++++++++++++++++++++++++++++++++
android/hal_client/pollhandler.h | 32 ++++++++++
4 files changed, 235 insertions(+)
create mode 100644 android/hal_client/haltest.c
create mode 100644 android/hal_client/pollhandler.c
create mode 100644 android/hal_client/pollhandler.h
diff --git a/android/Android.mk b/android/Android.mk
index ec820ac..c6bab81 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -29,3 +29,20 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE := bluetoothd
include $(BUILD_EXECUTABLE)
+
+#
+# haltest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ hal_client/haltest.c \
+ hal_client/pollhandler.c \
+
+LOCAL_SHARED_LIBRARIES := libhardware
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := haltest
+
+include $(BUILD_EXECUTABLE)
diff --git a/android/hal_client/haltest.c b/android/hal_client/haltest.c
new file mode 100644
index 0000000..8a733a2
--- /dev/null
+++ b/android/hal_client/haltest.c
@@ -0,0 +1,57 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "pollhandler.h"
+
+
+/* called when there is something on stdin */
+static void stdin_handler(struct pollfd *pollfd)
+{
+ char buf[10];
+
+ if (pollfd->revents & POLLIN) {
+ int count = read(0, buf, 10);
+
+ if (count > 0) {
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ /* TODO: process input */
+ }
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ /* Register command line handler */
+ poll_register_fd(0, POLLIN, stdin_handler);
+
+ poll_dispatch_loop();
+
+ return 0;
+}
diff --git a/android/hal_client/pollhandler.c b/android/hal_client/pollhandler.c
new file mode 100644
index 0000000..14cff34
--- /dev/null
+++ b/android/hal_client/pollhandler.c
@@ -0,0 +1,129 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "pollhandler.h"
+
+/*
+ * Code that allows to poll multiply file descriptors for events
+ * File descriptors can be added and removed at runtime
+ *
+ * Call poll_register_fd function first to add file descriptors to monitor
+ * Then call poll_dispatch_loop that will poll all registered file descriptors
+ * as long as they are not unregistered.
+ *
+ * When event happen on given fd appropriate user supplied handler is called
+ */
+
+/* Maximum number of files to monitor */
+#define MAX_OPEN_FD 10
+
+/* Storage for pollfd structures for monitored file descriptors */
+static struct pollfd fds[MAX_OPEN_FD];
+static poll_handler fds_handler[MAX_OPEN_FD];
+/* Number of registered file descriptors */
+static int fds_count = 0;
+
+/*
+ * Function polls file descriptor in loop and calls appropriate handler
+ * on event. Function returns when there is no more file descriptor to
+ * monitor
+ */
+void poll_dispatch_loop(void)
+{
+ while (fds_count > 0) {
+ int i;
+ int cur_fds_count = fds_count;
+ int ready = poll(fds, fds_count, 1000);
+
+ for (i = 0; i < fds_count && ready > 0; ++i) {
+ if (fds[i].revents == 0)
+ continue;
+
+ fds_handler[i](fds + i);
+ ready--;
+ /*
+ * If handler was remove from table
+ * just skip the rest and poll again
+ * This is due to reordering of tables in
+ * register/unregister functions
+ */
+ if (cur_fds_count != fds_count)
+ break;
+ }
+ /*
+ * This seems to be needed for correct output handling
+ * when all waiting is performed in poll
+ */
+ fflush(stdout);
+ }
+}
+
+/*
+ * Registers file descriptor to be monitored for events (see man poll(2))
+ * for events.
+ *
+ * return non negative value on success
+ * -EMFILE when there are to much descriptors
+ */
+int poll_register_fd(int fd, short events, poll_handler ph)
+{
+ if (fds_count >= MAX_OPEN_FD)
+ return -EMFILE;
+
+ fds_handler[fds_count] = ph;
+ fds[fds_count].fd = fd;
+ fds[fds_count].events = events;
+ fds_count++;
+
+ return fds_count;
+}
+
+/*
+ * Unregisters file descriptor
+ * Both fd and ph must match previously registered data
+ *
+ * return 0 if unregister succeeded
+ * -EBADF if arguments do not match any register handler
+ */
+int poll_unregister_fd(int fd, poll_handler ph)
+{
+ int i;
+
+ for (i = 0; i < fds_count; ++i) {
+ if (fds_handler[i] == ph && fds[i].fd == fd) {
+ fds_count--;
+ if (i < fds_count) {
+ fds[i].fd = fds[fds_count].fd;
+ fds[i].events = fds[fds_count].events;
+ fds_handler[i] = fds_handler[fds_count];
+ }
+ return 0;
+ }
+ }
+ return -EBADF;
+}
+
diff --git a/android/hal_client/pollhandler.h b/android/hal_client/pollhandler.h
new file mode 100644
index 0000000..c011786
--- /dev/null
+++ b/android/hal_client/pollhandler.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <poll.h>
+
+/* Function to be called when there are event for some descriptor */
+typedef void (*poll_handler)(struct pollfd *pollfd);
+
+int poll_register_fd(int fd, short events, poll_handler ph);
+int poll_unregister_fd(int fd, poll_handler ph);
+
+void poll_dispatch_loop(void);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 0/5] Stack independent BT HAL test tool
From: Jerzy Kasenberg @ 2013-10-14 9:59 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Jerzy Kasenberg
This tool is for testing Android HAL interfaces from command line.
Due to lack of readline on Android simple equivalent is hand coded.
This tool can be used with bluedroid stack so no glib dependency.
Tool source code is in folder hal_client, please comment if it should be
somewhere else.
Comments welcome.
Best regards
Jerzy Kasenberg
Jerzy Kasenberg (5):
android: Add haltest skeleton
android: Add line editing to haltest
android: Add history to line editor in haltest
android: Add text conversion helpers to haltest
android: Add calls to adapter methods in haltest
android/Android.mk | 21 ++
android/hal_client/haltest.c | 167 ++++++++++
android/hal_client/history.c | 104 +++++++
android/hal_client/history.h | 27 ++
android/hal_client/if_bt.c | 637 ++++++++++++++++++++++++++++++++++++++
android/hal_client/if_main.h | 105 +++++++
android/hal_client/pollhandler.c | 129 ++++++++
android/hal_client/pollhandler.h | 32 ++
android/hal_client/terminal.c | 540 ++++++++++++++++++++++++++++++++
android/hal_client/terminal.h | 65 ++++
android/hal_client/textconv.c | 211 +++++++++++++
android/hal_client/textconv.h | 119 +++++++
12 files changed, 2157 insertions(+)
create mode 100644 android/hal_client/haltest.c
create mode 100644 android/hal_client/history.c
create mode 100644 android/hal_client/history.h
create mode 100644 android/hal_client/if_bt.c
create mode 100644 android/hal_client/if_main.h
create mode 100644 android/hal_client/pollhandler.c
create mode 100644 android/hal_client/pollhandler.h
create mode 100644 android/hal_client/terminal.c
create mode 100644 android/hal_client/terminal.h
create mode 100644 android/hal_client/textconv.c
create mode 100644 android/hal_client/textconv.h
--
1.7.9.5
^ permalink raw reply
* [PATCH] Bluetooth: Introduce L2CAP channel callback for resuming
From: Marcel Holtmann @ 2013-10-14 9:53 UTC (permalink / raw)
To: linux-bluetooth
Clearing the BT_SK_SUSPEND socket flag from the L2CAP core is causing
a dependency on the socket. So intead of doing that, use a channel
callback into the socket handling to resume.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 6 +-----
net/bluetooth/l2cap_sock.c | 9 +++++++++
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 1a38ede..07757a2 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -554,6 +554,7 @@ struct l2cap_ops {
int state);
void (*ready) (struct l2cap_chan *chan);
void (*defer) (struct l2cap_chan *chan);
+ void (*resume) (struct l2cap_chan *chan);
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
unsigned long len, int nb);
};
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index e5819cb..0c3446d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6669,11 +6669,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
- struct sock *sk = chan->sk;
-
- clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
- sk->sk_state_change(sk);
-
+ chan->ops->resume(chan);
l2cap_check_encryption(chan, encrypt);
l2cap_chan_unlock(chan);
continue;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index f5d9573..fcf012a 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1112,6 +1112,14 @@ static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
parent->sk_data_ready(parent, 0);
}
+static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+ sk->sk_state_change(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -1121,6 +1129,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.state_change = l2cap_sock_state_change_cb,
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
+ .resume = l2cap_sock_resume_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
};
--
1.8.3.1
^ permalink raw reply related
* [PATCH] Bluetooth: Introduce L2CAP channel flag for defer setup
From: Marcel Holtmann @ 2013-10-14 9:45 UTC (permalink / raw)
To: linux-bluetooth
The L2CAP core should not look into the socket flags to figure out the
setting of defer setup. So introduce a L2CAP channel flag that mirrors
the socket flag.
Since the defer setup option is only set in one place this becomes a
really easy thing to do.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 15 +++++++--------
net/bluetooth/l2cap_sock.c | 7 +++++--
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index a27d51d..1a38ede 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -652,6 +652,7 @@ enum {
FLAG_FLUSHABLE,
FLAG_EXT_CTRL,
FLAG_EFS_ENABLE,
+ FLAG_DEFER_SETUP,
};
enum {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index f6b5f94..e5819cb 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -655,14 +655,14 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONNECT2:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
conn->hcon->type == ACL_LINK) {
- struct sock *sk = chan->sk;
struct l2cap_conn_rsp rsp;
__u16 result;
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
+
l2cap_state_change(chan, BT_DISCONN);
rsp.scid = cpu_to_le16(chan->dcid);
@@ -1294,16 +1294,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
l2cap_start_connection(chan);
} else if (chan->state == BT_CONNECT2) {
- struct sock *sk = chan->sk;
struct l2cap_conn_rsp rsp;
char buf[128];
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_chan_check_security(chan)) {
+ struct sock *sk = chan->sk;
+
lock_sock(sk);
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
chan->ops->defer(chan);
@@ -3823,7 +3823,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
__l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
@@ -6693,8 +6693,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
lock_sock(sk);
if (!status) {
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index f1b462f..f5d9573 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -672,10 +672,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
- if (opt)
+ if (opt) {
set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
- else
+ set_bit(FLAG_DEFER_SETUP, &chan->flags);
+ } else {
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ clear_bit(FLAG_DEFER_SETUP, &chan->flags);
+ }
break;
case BT_FLUSHABLE:
--
1.8.3.1
^ permalink raw reply related
* Re: [RFC] Bluetooth: Set ISOC altsetting from within notify callback
From: Timo Mueller @ 2013-10-14 9:20 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <DC0E2A8A-DF32-4E70-ACF4-B203D17057B9@holtmann.org>
Hi Marcel,
Am 11.10.2013 14:36, schrieb Marcel Holtmann:
> Hi Timo,
>
>>> Since the event handling is done within a workqueue, the notify
>>> callback can now sleep. So no need to trigger a separate workqueue
>>> from within the Bluetooth USB driver.
>>>
>>> This should give a little bit better latency with the SCO packet
>>> processing since the ISOC altsetting is correct from the beginning.
>>>
>>> However I am not sure if we can actually sleep in the USB reset
>>> handler and what we need to do to restore the correct altsetting
>>> in there. This could potentially fail, so please test ;)
>>>
>>> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
>>> ---
>>> drivers/bluetooth/btusb.c | 34 ++++++++++++++--------------------
>>> 1 file changed, 14 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
>>> index f3dfc0a..32cae73 100644
>>> --- a/drivers/bluetooth/btusb.c
>>> +++ b/drivers/bluetooth/btusb.c
>>> @@ -245,7 +245,6 @@ struct btusb_data {
>>>
>>> unsigned long flags;
>>>
>>> - struct work_struct work;
>>> struct work_struct waker;
>>>
>>> struct usb_anchor tx_anchor;
>>> @@ -685,7 +684,6 @@ static int btusb_close(struct hci_dev *hdev)
>>> if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
>>> return 0;
>>>
>>> - cancel_work_sync(&data->work);
>>> cancel_work_sync(&data->waker);
>>>
>>> clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
>>> @@ -827,18 +825,6 @@ done:
>>> return err;
>>> }
>>>
>>> -static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
>>> -{
>>> - struct btusb_data *data = hci_get_drvdata(hdev);
>>> -
>>> - BT_DBG("%s evt %d", hdev->name, evt);
>>> -
>>> - if (hdev->conn_hash.sco_num != data->sco_num) {
>>> - data->sco_num = hdev->conn_hash.sco_num;
>>> - schedule_work(&data->work);
>>> - }
>>> -}
>>> -
>>> static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
>>> {
>>> struct btusb_data *data = hci_get_drvdata(hdev);
>>> @@ -882,9 +868,8 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
>>> return 0;
>>> }
>>>
>>> -static void btusb_work(struct work_struct *work)
>>> +static void btusb_update_isoc_altsetting(struct btusb_data *data)
>>> {
>>> - struct btusb_data *data = container_of(work, struct btusb_data, work);
>>> struct hci_dev *hdev = data->hdev;
>>> int new_alts;
>>> int err;
>>> @@ -932,6 +917,18 @@ static void btusb_work(struct work_struct *work)
>>> }
>>> }
>>>
>>> +static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
>>> +{
>>> + struct btusb_data *data = hci_get_drvdata(hdev);
>>> +
>>> + BT_DBG("%s evt %d", hdev->name, evt);
>>> +
>>> + if (hdev->conn_hash.sco_num != data->sco_num) {
>>> + data->sco_num = hdev->conn_hash.sco_num;
>>> + btusb_update_isoc_altsetting(data);
>>> + }
>>> +}
>>> +
>>> static void btusb_waker(struct work_struct *work)
>>> {
>>> struct btusb_data *data = container_of(work, struct btusb_data, waker);
>>> @@ -1404,7 +1401,6 @@ static int btusb_probe(struct usb_interface *intf,
>>>
>>> spin_lock_init(&data->lock);
>>>
>>> - INIT_WORK(&data->work, btusb_work);
>>> INIT_WORK(&data->waker, btusb_waker);
>>> spin_lock_init(&data->txlock);
>>>
>>> @@ -1540,8 +1536,6 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
>>> return -EBUSY;
>>> }
>>>
>>> - cancel_work_sync(&data->work);
>>> -
>>> btusb_stop_traffic(data);
>>> usb_kill_anchored_urbs(&data->tx_anchor);
>>>
>>> @@ -1606,8 +1600,8 @@ static int btusb_resume(struct usb_interface *intf)
>>> play_deferred(data);
>>> clear_bit(BTUSB_SUSPENDING, &data->flags);
>>> spin_unlock_irq(&data->txlock);
>>> - schedule_work(&data->work);
>>>
>>> + btusb_update_isoc_altsetting(data);
>>> return 0;
>>>
>>> failed:
>>>
>> I have been testing this patch for the last two days at the UPF in Vienna. It was running fine most of the time, but I experienced two crashes. Both crashes appeared when there was an active call and the phone transferred audio to the phone and back. Both times I wasn't able to reproduce, when I restarted everything and tested again it worked fine.
>>
>> Unfortunately the kernel log is not complete but, when it failed the kernel reported:
>> [147.344546] Bluetooth: hci0 SCO packet for unknown connection handle 5
>> [147.354515] Bluetooth: hci0 SCO packet for unknown connection handle 21
>> [147.354537] Bluetooth: hci0 SCO packet for unknown connection handle 29
>> [147.354548] Bluetooth: hci0 SCO packet for unknown connection handle 65534
>> [147.364574] Bluetooth: hci0 SCO packet for unknown connection handle 65532
>> [147.364581] Bluetooth: hci0 SCO packet for unknown connection handle 27
>> …
> what kind of hardware where you testing with?
It was nothing special, an off-the-shelf CSR USB BT Dongle.
>
> This handle mismatch normally means that our SCO packet frames are out of sync. I think we are not doing a good job trying to keep them nicely lined up.
>
> For the crash, do you happen to have a backtrace of the crash. Personally I was worried about the reset handling and not the actual alternate setting switching.
Unfortunately I haven't. To be precise the first crash wasn't even a
crash, as everything was continuing to run, but without a working audio
of course. The second time my system completely froze, but due to my
fault I wasn't able to pull the backtrace. I'll see if I can reproduce
this week and send a backtrace if I manage to.
>
> Regards
>
> Marcel
Regards,
Timo
^ permalink raw reply
* [PATCH] Bluetooth: Adjust header for proc socket information
From: Marcel Holtmann @ 2013-10-14 9:05 UTC (permalink / raw)
To: linux-bluetooth
The exposed socket information do not contain source or destination
addresses. So adjust the header accordingly.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
net/bluetooth/af_bluetooth.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 61501c9..1f1a111 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -611,7 +611,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
struct bt_sock_list *l = s->l;
if (v == SEQ_START_TOKEN) {
- seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent");
+ seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Parent");
if (l->custom_seq_show) {
seq_putc(seq, ' ');
--
1.8.3.1
^ permalink raw reply related
* [RFC] doc: Draft of 3D synchronization mgmt commands
From: Marcel Holtmann @ 2013-10-14 8:45 UTC (permalink / raw)
To: linux-bluetooth
---
doc/mgmt-api.txt | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index d68b70a..0395ddc 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -1338,6 +1338,104 @@ Set Scan Parameters Command
Invalid Index
+Get 3D Synchronization Features Command
+=======================================
+
+ Command Code: 0x002D
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Features (4 Octets)
+
+ This command allows for gettig the supported 3D synchronization
+ features. These features can be used to detect what kind of 3D
+ synchronization modes are supported.
+
+ When BR/EDR is not supported, this command is supported for
+ simplicity, but it will return an empty features set. This
+ command can also be used when the controller is powered off.
+
+ NOTES: Since 3DPS is its own profiles, these supported features
+ are not to be intermixed with the GAP settings. These are special
+ hardware features like Connectionless Slave Broadcast roles that
+ are either supported or not. The advantage with a separate command
+ for 3DPS lets it easy to detect if it is running on a 3DPS enabled
+ kernel and what features of 3DPS are available.
+
+ If it happens that the controller supports all the right features,
+ but not Secure Simple Pairing, then the broadcaster modes will be
+ left out.
+
+ This command generates a Command Complete event on success or a
+ Command Status event on failure.
+
+ Possible errors: Rejected
+ Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
+Set 3D Synchronization Broadcast Command
+========================================
+
+ Command Code: 0x002E
+ Controller Index: <controller id>
+ Command Parameters: Mode (1 Octets)
+ ...
+ Return Parameters:
+
+ This command allows for enabling 3D synchronization broadcast
+ modes. This is normally the 3D display role.
+
+ It is required that the controller has support for connectionless
+ slave broadcast and train synchronization in master role.
+
+ When Secure Simple Pairing is disabled, this command will fail
+ with an error.
+
+ NOTES: The actual list of parameters still needs to be figured
+ out, but it should include broadcast data and 3D information data.
+
+ This command generates a Command Complete event on success or a
+ Command Status event on failure.
+
+ Possible errors: Rejected
+ Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
+Set 3D Synchronization Receiver Command
+=======================================
+
+ Command Code: 0x002E
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+ ...
+ Return Parameters:
+
+ This command allows for enabling 3D synchronization receiver
+ modes. This is normally the 3D glasses role.
+
+ It is required that the controller has support for connectionless
+ slave broadcast and train synchronization in slave role.
+
+ NOTES: The actual list of parameters still needs to be figured
+ out, but it should include broadcast data and 3D information data.
+
+ NOTES: The actual data information can be either reported via
+ an event or via an L2CAP channel. That still needs to be figured
+ out.
+
+ This command generates a Command Complete event on success or a
+ Command Status event on failure.
+
+ Possible errors: Rejected
+ Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
Command Complete Event
======================
--
1.8.3.1
^ permalink raw reply related
* [PATCH 22/22] gitignore: Add blueatchat unit tests
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index f9bf975..c72c911 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ unit/test-sdp
unit/test-lib
unit/test-mgmt
unit/test-cbuffer
+unit/test-blueatchat
tools/mgmt-tester
tools/gap-tester
tools/btattach
--
1.7.9.5
^ permalink raw reply related
* [PATCH 21/22] gitignore: Add curcular buffer unit tests
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index e492c3f..f9bf975 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@ unit/test-gdbus-client
unit/test-sdp
unit/test-lib
unit/test-mgmt
+unit/test-cbuffer
tools/mgmt-tester
tools/gap-tester
tools/btattach
--
1.7.9.5
^ permalink raw reply related
* [PATCH 20/22] unit: Circular buffer overflow test
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
unit/test-blueatchat.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
index e0cd23b..e0c51cd 100644
--- a/unit/test-blueatchat.c
+++ b/unit/test-blueatchat.c
@@ -595,6 +595,50 @@ static void tc_blueat_atbac(struct blueat_fixture *fix,
g_assert_cmpint(called, ==, 1);
}
+static void tc_blueat_overflow(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n"
+ "\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n\r\nOK\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 180);
+}
+
/******************************************************************************
* Parser configuration and command registration
******************************************************************************/
@@ -850,5 +894,8 @@ int main(int argc, char **argv)
g_test_add("/blueatchat/cind_2", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_cind2,
blueat_fix_teardown);
+ g_test_add("/blueatchat/overflow", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_overflow,
+ blueat_fix_teardown);
return g_test_run();
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 19/22] blueatchat: Extend API for sending AT commands
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
src/shared/blueatchat.c | 20 ++++++++++++++++++++
src/shared/blueatchat.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index 06d8c65..430bb56 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -47,6 +47,7 @@ struct blueatchat_session {
struct circular_buffer *buffer;
GSList *data; /* stores parsed data */
blueatchat_debug_func_t debug_callback;
+ struct circular_buffer *send_buffer;
};
#ifdef BLUEATCHAT_VERBOSE
@@ -449,6 +450,18 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
return true;
}
+bool blueatchat_write(GIOChannel *gio, struct blueatchat_session *session)
+{
+ /* TODO: send as much buffered data as possible to io channel */
+ return true;
+}
+
+bool blueatchat_send(struct blueatchat_session *session, char *message)
+{
+ /* TODO: validate data and push it to the buffer */
+ return true;
+}
+
struct blueatchat_session *blueatchat_init_session(
struct blueatchat_config *config,
struct blueatchat_cmd_descriptor *cmd_list,
@@ -467,6 +480,12 @@ struct blueatchat_session *blueatchat_init_session(
g_free(session);
return NULL;
}
+ session->send_buffer = cbuffer_init(session->config->buff_size);
+ if (!session->send_buffer) {
+ cbuffer_free(session->buffer);
+ free(session);
+ return NULL;
+ }
return session;
}
@@ -478,6 +497,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
g_slist_free_full(session->data, g_free);
cbuffer_free(session->buffer);
+ cbuffer_free(session->send_buffer);
g_free(session);
session = NULL;
}
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
index 75d055c..ccdcaed 100644
--- a/src/shared/blueatchat.h
+++ b/src/shared/blueatchat.h
@@ -78,3 +78,5 @@ struct blueatchat_session *blueatchat_init_session(
blueatchat_debug_func_t debug_fun);
void blueatchat_cleanup_session(struct blueatchat_session *session);
bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio);
+bool blueatchat_write(GIOChannel *gio, struct blueatchat_session *session);
+bool blueatchat_send(struct blueatchat_session *session, char *message);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 18/22] blueatchat: Add more data parsing functionality
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds data recognition for the compound value, char, quoted
string and value sepparator.
---
src/shared/blueatchat.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/dparser.c | 39 +++++++++++++++++++++++++++++
src/shared/dparser.h | 2 ++
3 files changed, 103 insertions(+)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index 6a12b22..06d8c65 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -152,6 +152,7 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
struct blueatchat_session *session,
int *data_off, int amount)
{
+ /* TODO: Add simple recursive call depth counter? */
unsigned int i;
int begin_offset = *data_off;
bool optional = false;
@@ -169,9 +170,29 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
if (*data_off - begin_offset == amount)
return 0;
+ /* offset storage for recursive calls */
+ *descr_off = i;
+
switch (ch) {
case '(': /* compound value start */
BLUEATCHAT_DEBUG(session, "(");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off) ==
+ '(')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ ++(*descr_off);
+ if (blueatchat_parse_data(descriptor, descr_off,
+ session, data_off, amount) < 0)
+ return -ENODATA;
+ /*
+ * After recursive call returns, descr_off holds offset
+ * indicating how much the descriptor was processed.
+ * Advance by this value to not process this data again.
+ */
+ i = *descr_off;
break;
case 'o': /* optional value */
BLUEATCHAT_DEBUG(session, "o");
@@ -188,17 +209,58 @@ static int blueatchat_parse_data(char *descriptor, int *descr_off,
session->data = g_slist_prepend(session->data, newdata);
break;
+ case 'c': /* single char */
+ BLUEATCHAT_DEBUG(session, "c");
+
+ newdata = dparse_char(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
+ break;
case 'q': /* quoted string */
BLUEATCHAT_DEBUG(session, "q");
+
+ newdata = dparse_qstr(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
break;
case 's': /* TODO: unquoted string */
BLUEATCHAT_DEBUG(session, "s");
break;
case ')': /* compound value end */
BLUEATCHAT_DEBUG(session, ")");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off)
+ == ')')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ return 0;
+ case '-': /* TODO: value range */
+ BLUEATCHAT_DEBUG(session, "-");
+
+ ++(*data_off); /* For now it just skip it */
break;
case ',': /* value separator */
BLUEATCHAT_DEBUG(session, ",");
+
+ if (*cbuffer_peek_tail(session->buffer, *data_off)
+ == ',')
+ ++(*data_off);
+ else if (!optional)
+ return -ENODATA;
+
+ /*
+ * Optional classifier works on a single descriptor, but
+ * turn this flag off only when this is not the opening
+ * one, like in "uo,u", where ',' is also optional.
+ */
+ if (i > 0 && descriptor[i - 1] != 'o')
+ optional = 0;
break;
case '*':
/*
diff --git a/src/shared/dparser.c b/src/shared/dparser.c
index 7b7a2a2..0de7038 100644
--- a/src/shared/dparser.c
+++ b/src/shared/dparser.c
@@ -43,3 +43,42 @@ unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset)
}
return ret;
}
+
+char *dparse_qstr(struct circular_buffer *buffer, int *offset)
+{
+ char *str;
+ int len;
+ int i;
+
+ if (*cbuffer_peek_tail(buffer, *offset) != '"')
+ return NULL;
+
+ len = cbuffer_seek_tail_for_seq(buffer, "\"", *offset + 1) - *offset;
+ if (len <= 0)
+ return NULL;
+
+ str = g_malloc(len);
+
+ for (i = 0; i < len - 1; ++i)
+ str[i] = *cbuffer_peek_tail(buffer, *offset + i + 1);
+ str[i] = '\0';
+ /* Move offset on first char after closing quotation mark */
+ *offset += len + 1;
+ return str;
+}
+
+char *dparse_char(struct circular_buffer *buffer, int *offset)
+{
+ const char *c = NULL;
+ char *ret = NULL;
+
+ c = cbuffer_peek_tail(buffer, *offset);
+ if (!isascii(*c))
+ return NULL;
+
+ ret = g_new(char, 1);
+ *ret = *c;
+ ++(*offset);
+
+ return ret;
+}
diff --git a/src/shared/dparser.h b/src/shared/dparser.h
index 7c0bf58..66dd4a7 100644
--- a/src/shared/dparser.h
+++ b/src/shared/dparser.h
@@ -22,3 +22,5 @@
*/
unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset);
+char *dparse_qstr(struct circular_buffer *buffer, int *offset);
+char *dparse_char(struct circular_buffer *buffer, int *offset);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 17/22] unit: Add more test cases for the at parser
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds unit tests for AT commands required by HFP to establish
SLC.
---
unit/test-blueatchat.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 521 insertions(+), 1 deletion(-)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
index ae10ff7..e0cd23b 100644
--- a/unit/test-blueatchat.c
+++ b/unit/test-blueatchat.c
@@ -78,6 +78,24 @@ static void tc_blueat_ok(struct blueat_fixture *fix, gconstpointer test_data)
/* one ok command should be found */
g_assert_cmpint(called, ==, 1);
}
+
+static bool tc_blueat_error_cb(GSList *data)
+{
+ called += 1;
+ g_message("ERROR callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_error(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ fix->msg = "\r\nERROR\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* one error command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+
static void tc_blueat_garbagemix(struct blueat_fixture *fix,
gconstpointer test_data)
{
@@ -120,6 +138,28 @@ static void tc_blueat_atbrsf(struct blueat_fixture *fix,
g_assert_cmpint(called, ==, 1);
}
+static bool tc_blueat_brsf_cb(GSList *data)
+{
+ called += 1;
+ g_message("+BRSF: callback");
+
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 192);
+
+ return true;
+}
+static void tc_blueat_brsf(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "\r\n+BRSF: 192\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* 1 callback should be called */
+ g_assert_cmpint(called, ==, 1);
+}
+
static void tc_blueat_garbage(struct blueat_fixture *fix,
gconstpointer test_data)
{
@@ -178,6 +218,383 @@ static void tc_blueat_cnum(struct blueat_fixture *fix, gconstpointer test_data)
g_assert_cmpint(called, ==, 1);
}
+static bool tc_blueat_atchld_t_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CHLD=? callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atchld_t(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CHLD=?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atchld_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CHLD= callback");
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 3);
+
+ return true;
+}
+static void tc_blueat_atchld(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CHLD=3\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_chld_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CHLD: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(char *)item->data, ==, 'x');
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(char *)item->data, ==, 'x');
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ return true;
+}
+static void tc_blueat_chld(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CHLD: (0,1,1x,2,2x,3)\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcind_r_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CIND? callback");
+
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atcind_r(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CIND?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcind_t_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+CIND=? callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_atcind_t(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CIND=?\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_cind1_cb(GSList *data)
+{
+ GSList *item = NULL;
+ int values[] = { 1, 1, 0, 0, 5, 0, 0 };
+ int unsigned i;
+
+ called += 1;
+ g_message("1+CIND: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ for (i = 0; i < sizeof(values) / sizeof(int); ++i) {
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, values[i]);
+ item = g_slist_next(item);
+ }
+ /* We declared 20 parameters in descriptor so there can be more data.
+ * but of course not in this case, because we know what we have sent.
+ */
+ if (item)
+ for (; i < 19; ++i) {
+ g_assert(item->data == NULL);
+ item = g_slist_next(item);
+ }
+ /* there should be no more data */
+ g_assert(!item);
+ return true;
+}
+static void tc_blueat_cind1(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIND: 1,1,0,0,5,0,0\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_cind2_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("2+CIND: callback");
+ g_assert(data);
+
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert((char *)item->data);
+ g_assert_cmpint(strcmp(item->data, "Voice Mail"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "service"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "call"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "Roam"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "signal"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 5);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "callsetup"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(strcmp((char *)item->data, "smsfull"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)) == NULL);
+ return true;
+}
+static void tc_blueat_cind2(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIND: (\"Voice Mail\",(0,1)),(\"service\",(0,1)),"
+ "(\"call\",(0,1)),(\"Roam\",(0-2)),(\"signal\",(0-5)),"
+ "(\"callsetup\",(0-3)),(\"smsfull\",(0,1))\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atcmer_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("AT+CMER= callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ /* Check for data "ou,ou,ou,ou*" */
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ /* no second parameter because its optional */
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+ return true;
+}
+static void tc_blueat_atcmer(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+CMER=3,,0,1\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_ciev_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CIEV: callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 3);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 0);
+ return true;
+}
+static void tc_blueat_ciev(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\n+CIEV: 3,0\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
+static bool tc_blueat_atbac_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("AT+BAC= callback");
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ /* Check returned data "u,ou,ou,ou,ou,ou,ou" */
+
+ g_assert(item);
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 1);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 2);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert(item->data);
+ g_assert_cmpint(*(int *)item->data, ==, 12);
+
+ /* According to descriptor there should be 4 nulls but not
+ * if there was no more data - of course there wont be any here
+ */
+ item = g_slist_next(item);
+ if (item) {
+ g_assert(item->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ g_assert((item = g_slist_next(item))->data == NULL);
+ }
+
+ /* there should be no more data */
+ g_assert(!item);
+ return true;
+}
+static void tc_blueat_atbac(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ fix->msg = "\r\nAT+BAC=1,2,12\r\n";
+
+ g_main_loop_run(fix->loop);
+ g_assert_cmpint(called, ==, 1);
+}
+
/******************************************************************************
* Parser configuration and command registration
******************************************************************************/
@@ -193,8 +610,72 @@ static struct blueatchat_config cfg = {
static struct blueatchat_cmd_descriptor commands[] = {
/* recognised command | syntax descriptor | callback */
{"OK", NULL, tc_blueat_ok_cb},
- {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"ERROR", NULL, tc_blueat_error_cb},
+ {"NO CARRIER", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"BUSY", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"NO ANSWER", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"DELAYED", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"BLACKLISTED", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"RING", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"ATA", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CCWA=", "ou", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CCWA: ", "s,ou*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CHLD=?", NULL, tc_blueat_atchld_t_cb},
+ {"AT+CHLD=", "u", tc_blueat_atchld_cb},
+ {"+CHLD: ", "(ou,ou,ouc,ou,ouc,ou)", tc_blueat_chld_cb},
+ {"AT+CHUP", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+CHUP", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CIND?", NULL, tc_blueat_atcind_r_cb},
+ {"AT+CIND=?", NULL, tc_blueat_atcind_t_cb},
+ {"+CIND: ", "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou,"
+ "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou", tc_blueat_cind1_cb},
+ {"+CIND: ", "(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))o,(q,(u-u))"
+ "o,(q,(u-u))o,(q,(u-u))", tc_blueat_cind2_cb},
+ {"AT+CLCC", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+CLCC: ", "u,u,u,u,u,os,ou*",/*TODO*/ tc_blueat_unknown_cb},
+ {"AT+COPS=", "ou,ou,os*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+COPS?", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"+COPS: ", "u,ou,os*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CMEE=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CMEE ERROR: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CLIP=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+CLIP: ", "q,u*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CMER=", "ou,ou,ou,ou*", tc_blueat_atcmer_cb},
+ {"+CIEV: ", "u,u", tc_blueat_ciev_cb},
+ {"AT+VTS=", "c", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CNUM=?", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+CNUM", NULL, /*TODO*/ tc_blueat_unknown_cb},
{"+CNUM: ", "oq,q,u,ou,u*", tc_blueat_cnum_cb},
+ {"ATD>", "q", /*TODO*/ tc_blueat_unknown_cb},
+ {"ATD", "s", /*TODO*/ tc_blueat_unknown_cb},
+ /* Bluetooth cpecific */
+ {"AT+BIA=", "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou,"
+ "ou,ou,ou,ou,ou,ou,ou,ou,ou,ou", tc_blueat_unknown_cb},
+ {"AT+BINP=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BINP: ", "q*", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BLDN=", NULL, /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BVRA=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BVRA: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"+BRSF: ", "u", tc_blueat_brsf_cb},
+ {"AT+NREC=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+VGM=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+VGM:", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+VGS=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+VGS: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BSIR: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BTRH=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BTRH: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BCC=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BCS=", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"+BCS: ", "u", /*TODO*/ tc_blueat_unknown_cb},
+ {"AT+BAC=", "u,ou,ou,ou,ou,ou,ou", tc_blueat_atbac_cb},
+ /* used lastly if no other command matches */
{NULL, "s", tc_blueat_unknown_cb},
};
@@ -327,8 +808,47 @@ int main(int argc, char **argv)
g_test_add("/blueatchat/garbage", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_garbage,
blueat_fix_teardown);
+ g_test_add("/blueatchat/error", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_error,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/brsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_brsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atchld_test", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atchld_t,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atchld", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atchld,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/chld", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_chld,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/brsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_brsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcind_request", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcind_r,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcind_test", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcind_t,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atcmer", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atcmer,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/ciev", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_ciev,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atbac", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atbac,
+ blueat_fix_teardown);
g_test_add("/blueatchat/cnum", struct blueat_fixture, 0,
blueat_fix_setup, tc_blueat_cnum,
blueat_fix_teardown);
+ g_test_add("/blueatchat/cind_1", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cind1,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/cind_2", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cind2,
+ blueat_fix_teardown);
return g_test_run();
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 16/22] unit: Add basic unit test for the blueatchat module
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds first few basic unit test for blueatchat's AT parsing
functionality.
---
Makefile.am | 9 ++
unit/test-blueatchat.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 343 insertions(+)
create mode 100644 unit/test-blueatchat.c
diff --git a/Makefile.am b/Makefile.am
index 079405f..76bcc75 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -289,6 +289,15 @@ unit_test_cbuffer_SOURCES = unit/test-cbuffer.c \
src/shared/cbuffer.h src/shared/cbuffer.c
unit_test_cbuffer_LDADD = @GLIB_LIBS@
+unit_tests += unit/test-blueatchat
+
+unit_test_blueatchat_SOURCES = unit/test-blueatchat.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/cbuffer.h src/shared/cbuffer.c \
+ src/shared/blueatchat.h src/shared/blueatchat.c \
+ src/shared/dparser.h src/shared/dparser.c
+unit_test_blueatchat_LDADD = @GLIB_LIBS@
+
noinst_PROGRAMS += $(unit_tests)
TESTS = $(unit_tests)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
new file mode 100644
index 0000000..ae10ff7
--- /dev/null
+++ b/unit/test-blueatchat.c
@@ -0,0 +1,334 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/cbuffer.h"
+#include "src/shared/blueatchat.h"
+
+/* use power of 2 for the buffer size */
+#define BUFFER_SIZE (1<<8)
+
+/* #define BLUEATCHAT_DEBUG */
+
+/* storage used by unit testing framework */
+struct blueat_fixture {
+ GMainLoop *loop;
+ char *msg;
+ struct blueatchat_session *session;
+ GIOChannel *gio_read, *gio_write;
+ bool issent;
+};
+
+/* stores callback calls count for each test case if data was parsed */
+volatile int called;
+/* stores callback calls for unknown commands or with wring data */
+volatile int unknown;
+
+/******************************************************************************
+ * Add test cases below:
+ * - create test case for sending commands data
+ * - if sending new commands - remember to register this command
+ * and its callback which verifies parsed data
+ * - add prepared test case in the main function at the very bottom
+ ******************************************************************************/
+/* Unknown command test case and callback that verifies (non)parsed data */
+static bool tc_blueat_unknown_cb(GSList *data)
+{
+ unknown += 1;
+
+ /* In this test case there should be no parsed data */
+ g_assert(data);
+ g_assert(data->data);
+ g_message("Unknown command callback: %s",
+ g_strescape((char *)data->data, NULL));
+
+ return true;
+}
+static void tc_blueat_unknown(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Defined test case */
+ fix->msg = "\r\nunknown command and garbage\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* one unknown command should be found */
+ g_assert_cmpint(called, ==, 0);
+ g_assert_cmpint(unknown, ==, 1);
+}
+
+/* OK test case and callback that verifies parsed data */
+static bool tc_blueat_ok_cb(GSList *data)
+{
+ called += 1;
+ g_message("OK callback");
+ g_assert(!data);
+ return true;
+}
+static void tc_blueat_ok(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ fix->msg = "\r\nOK\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* one ok command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+static void tc_blueat_garbagemix(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "garbage\n\r" /* garbage */
+ "\r\nunknown\r\n" /* unknown */
+ "\r\nOK\r\n" /* ok */
+ "\r\n\n\rgarbage\r\n" /* unknown */
+ "\r\rgarbageggegg\n\n" /* garbage */
+ "\r\nOK\r\n"; /* ok */
+
+ g_main_loop_run(fix->loop);
+
+ /* 2 OK and 2 unknown commands should be found */
+ g_assert_cmpint(called, ==, 2);
+ g_assert_cmpint(unknown, ==, 2);
+}
+
+/* AT+BRSF test case and callback that verifies parsed data */
+static bool tc_blueat_atbrsf_cb(GSList *data)
+{
+ called += 1;
+ g_message("AT+BRSF= callback");
+
+ g_assert(data);
+ g_assert(data->data);
+ g_assert_cmpint(*(int *)data->data, ==, 128);
+
+ return true;
+}
+static void tc_blueat_atbrsf(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "\r\nAT+BRSF=128\r\n";
+
+ g_main_loop_run(fix->loop);
+
+ /* 1 callback should be called */
+ g_assert_cmpint(called, ==, 1);
+}
+
+static void tc_blueat_garbage(struct blueat_fixture *fix,
+ gconstpointer test_data)
+{
+ /* Define test case */
+ fix->msg = "dfg5745j46\74845r4\68r\48un468n4wn\4r4hgnn\r\nOKjf\rkfhn"
+ "\r\n\nlsfg\aargarbage\r;\nsrdgfhdnOK\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* no commands should be found in this garbage */
+ g_assert_cmpint(called, ==, 0);
+}
+
+static bool tc_blueat_cnum_cb(GSList *data)
+{
+ GSList *item = NULL;
+
+ called += 1;
+ g_message("+CNUM: callback");
+
+ g_assert(data);
+ item = g_slist_reverse(data);
+
+ g_assert(item);
+ /* no optional parameter */
+ g_assert((char *)item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((char *)item->data != NULL);
+ g_assert_cmpint(strcmp((char *)item->data, "5551212"), ==, 0);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data != NULL);
+ g_assert_cmpint(*(int *)item->data, ==, 129);
+
+ /* no optional parameter */
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data == NULL);
+
+ g_assert((item = g_slist_next(item)));
+ g_assert((int *)item->data != NULL);
+ g_assert_cmpint(*(int *)item->data, ==, 4);
+ return true;
+}
+static void tc_blueat_cnum(struct blueat_fixture *fix, gconstpointer test_data)
+{
+ /* Define test case */
+ /* two optional command parameters absent */
+ fix->msg = "\r\n+CNUM: ,\"5551212\",129,,4\r\n";
+
+ /* start the loop for IO ops execution */
+ g_main_loop_run(fix->loop);
+
+ /* 1 cnum command should be found */
+ g_assert_cmpint(called, ==, 1);
+}
+
+/******************************************************************************
+ * Parser configuration and command registration
+ ******************************************************************************/
+static struct blueatchat_config cfg = {
+ .buff_size = BUFFER_SIZE,
+ .cmd_prefix = "\r\n",
+ .cmd_postfix = "\r\n"
+};
+
+/* Commands that should be recognised by the parser.
+ * !IMPORTANT: unknown command callback should always be last.
+ */
+static struct blueatchat_cmd_descriptor commands[] = {
+ /* recognised command | syntax descriptor | callback */
+ {"OK", NULL, tc_blueat_ok_cb},
+ {"AT+BRSF=", "u", tc_blueat_atbrsf_cb},
+ {"+CNUM: ", "oq,q,u,ou,u*", tc_blueat_cnum_cb},
+ {NULL, "s", tc_blueat_unknown_cb},
+};
+
+/* Callback sending messages to the blueatchat module */
+static gboolean gio_out_cb(GIOChannel *gio, GIOCondition condition,
+ gpointer data)
+{
+ struct blueat_fixture *fix = data;
+
+ if (condition & G_IO_HUP)
+ g_error("Write end of pipe died!\n");
+
+ if (!fix->issent) {
+ gsize len;
+ GIOStatus ret;
+ GError *err = NULL;
+ const gchar *msg = fix->msg;
+
+ ret = g_io_channel_write_chars(gio, msg, -1, &len,
+ &err);
+ if (ret == G_IO_STATUS_ERROR)
+ g_error("Error writing: %s\n", err->message);
+ fix->issent = true;
+ return TRUE;
+ }
+
+ /* It will end up here at second io write,
+ * when message has already been written.
+ */
+ g_main_loop_quit(fix->loop);
+ return FALSE;
+}
+
+/* Make blueatchat silent */
+static void blueatchat_debug(const char *str, void *user_data)
+{
+#ifdef BLUEATCHAT_DEBUG
+ const char *prefix = user_data;
+ g_message("%s: %s", prefix, g_strescape(str, NULL));
+#endif
+}
+
+static gboolean blueat_read_cb(GIOChannel *gio, GIOCondition condition,
+ gpointer data)
+{
+ struct blueatchat_session *session = (struct blueatchat_session *)data;
+
+ if (!session)
+ g_error("Session died!\n");
+
+ g_message("Read started");
+
+ if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ g_message("Read end of pipe died!");
+ return FALSE;
+ }
+
+ if (!blueatchat_read(session, gio)) {
+ g_message("AT chat died!");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void blueat_fix_setup(struct blueat_fixture *fix, gconstpointer tdata)
+{
+ int fd[2], ret;
+
+ called = 0;
+ unknown = 0;
+ fix->issent = false;
+ fix->loop = g_main_loop_new(NULL, FALSE);
+
+ ret = pipe(fd);
+ if (ret == -1)
+ g_error("Creating pipe failed: %s\n", strerror(errno));
+
+ fix->gio_read = g_io_channel_unix_new(fd[0]);
+ fix->gio_write = g_io_channel_unix_new(fd[1]);
+
+ g_io_channel_set_encoding(fix->gio_write, NULL, NULL);
+ g_io_channel_set_buffered(fix->gio_write, FALSE);
+
+ g_io_channel_set_encoding(fix->gio_read, NULL, NULL);
+ g_io_channel_set_buffered(fix->gio_read, FALSE);
+ g_io_channel_set_flags(fix->gio_read, G_IO_FLAG_NONBLOCK, NULL);
+
+ fix->session =
+ blueatchat_init_session(&cfg, commands, blueatchat_debug);
+
+ if (!fix->session)
+ g_error("Cannot initialize the gat_parser!\n");
+
+ if (!fix->gio_read || !fix->gio_write)
+ g_error("Cannot create new GIOChannel!\n");
+
+ g_io_add_watch_full(fix->gio_read, G_PRIORITY_DEFAULT,
+ (G_IO_IN | G_IO_ERR | G_IO_NVAL),
+ blueat_read_cb,
+ fix->session, NULL);
+ if (!g_io_add_watch(fix->gio_write, G_IO_OUT | G_IO_HUP,
+ gio_out_cb, fix))
+ g_error("Cannot add watch on GIOChannel!\n");
+}
+
+static void blueat_fix_teardown(struct blueat_fixture *fix, gconstpointer tdata)
+{
+ /* channel clean up */
+ blueatchat_cleanup_session(fix->session);
+ g_io_channel_unref(fix->gio_read);
+ g_io_channel_unref(fix->gio_write);
+ g_main_loop_unref(fix->loop);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add("/blueatchat/unknown", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_unknown,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/ok", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_ok,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/garbagemix", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_garbagemix,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/atbrsf", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_atbrsf,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/garbage", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_garbage,
+ blueat_fix_teardown);
+ g_test_add("/blueatchat/cnum", struct blueat_fixture, 0,
+ blueat_fix_setup, tc_blueat_cnum,
+ blueat_fix_teardown);
+ return g_test_run();
+}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 15/22] blueatchat: Add functions for parsing data sent with the commands
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This is an initial attempt to parse data passed with the commands.
Parser uses syntax descriptor to find out what kind of data it should
expect and parse. Parsed data are being pushed to the list and sent
using proper callback.
---
src/shared/blueatchat.c | 182 ++++++++++++++++++++++++++++++++++++++++++++---
src/shared/blueatchat.h | 30 ++++++++
src/shared/dparser.c | 45 ++++++++++++
src/shared/dparser.h | 24 +++++++
4 files changed, 273 insertions(+), 8 deletions(-)
create mode 100644 src/shared/dparser.c
create mode 100644 src/shared/dparser.h
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index e9b3c9d..6a12b22 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -29,6 +29,7 @@
#include "cbuffer.h"
#include "blueatchat.h"
#include "util.h"
+#include "dparser.h"
/* uncomment for extended debug messages */
/* #define BLUEATCHAT_VERBOSE */
@@ -124,33 +125,196 @@ static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
return -ENODATA;
}
+static char *blueatchat_buffdup(struct circular_buffer *buffer,
+ unsigned int amount)
+{
+ /*
+ * Returns a pointer to newly allocated string containing 'amount'
+ * of data taken from the buffer.
+ */
+ unsigned int i;
+ char *msg = g_try_new(char, (amount + 1));
+
+ for (i = 0; i < amount; ++i)
+ msg[i] = *cbuffer_peek_tail(buffer, i);
+ msg[amount] = '\0';
+
+ return msg;
+}
+
+/*
+ * This function returns:
+ * < 0 for error,
+ * = 0 for success,
+ * > 0 for redundant data left in the buffer
+ */
+static int blueatchat_parse_data(char *descriptor, int *descr_off,
+ struct blueatchat_session *session,
+ int *data_off, int amount)
+{
+ unsigned int i;
+ int begin_offset = *data_off;
+ bool optional = false;
+ void *newdata;
+ unsigned int descrlen = strlen(descriptor);
+
+ for (i = *descr_off; i < descrlen; ++i) {
+ char ch = descriptor[i];
+ newdata = NULL;
+
+ /*
+ * Safety check if no more data in this command and descriptor
+ * indicates that more optional data may follow.
+ */
+ if (*data_off - begin_offset == amount)
+ return 0;
+
+ switch (ch) {
+ case '(': /* compound value start */
+ BLUEATCHAT_DEBUG(session, "(");
+ break;
+ case 'o': /* optional value */
+ BLUEATCHAT_DEBUG(session, "o");
+
+ /* Parse following data with proper flag set */
+ optional = true;
+ continue;
+ case 'u': /* integer value */
+ BLUEATCHAT_DEBUG(session, "u");
+
+ newdata = dparse_uint(session->buffer, data_off);
+ if (!newdata && !optional)
+ return -ENODATA;
+
+ session->data = g_slist_prepend(session->data, newdata);
+ break;
+ case 'q': /* quoted string */
+ BLUEATCHAT_DEBUG(session, "q");
+ break;
+ case 's': /* TODO: unquoted string */
+ BLUEATCHAT_DEBUG(session, "s");
+ break;
+ case ')': /* compound value end */
+ BLUEATCHAT_DEBUG(session, ")");
+ break;
+ case ',': /* value separator */
+ BLUEATCHAT_DEBUG(session, ",");
+ break;
+ case '*':
+ /*
+ * Do nothing as `*` should only exist at the very end
+ * of command descriptor and in next loop iteration
+ * loop will stop anyway.
+ */
+ break;
+ default:
+ BLUEATCHAT_DEBUG(session, "unknown descriptor");
+ break;
+ }
+ }
+ BLUEATCHAT_DEBUG(session, "parsed %i/%i bytes",
+ *data_off - begin_offset, amount);
+
+ /*
+ * If no data was parsed try different match returning error
+ * return 1 if not all data has been parsed but data partially matched.
+ */
+ if (*data_off - begin_offset == 0)
+ return -ENODATA;
+ else if (*data_off - begin_offset < amount)
+ return 1;
+
+ return 0;
+}
+
static int blueatchat_parse_command(struct blueatchat_session *session,
const int amount)
{
struct blueatchat_cmd_descriptor *item;
+ char *prefix = session->config->cmd_prefix;
+ char *postfix = session->config->cmd_postfix;
+ int data_off = 0;
+ int descr_off = 0;
BLUEATCHAT_DEBUG(session, "Starting");
#ifdef BLUEATCHAT_VERBOSE
blueatchat_buffer_dump(session, amount);
#endif
+ /* memory leaks shouldn't happen */
+ g_assert(!session->data);
+
for (item = session->cmd_list; item->cmd != NULL; ++item) {
+ descr_off = 0;
+ data_off = 0;
if (cbuffer_starts_with_seq(session->buffer, item->cmd,
- strlen(session->config->cmd_prefix))) {
- /* TODO: process buffered data
- *-> peek a char to choose appropriate parser routine
- * or use syntax descriptor string?
- *-> fill in sessions GSList* data with parsed data
+ strlen(prefix))) {
+ /*
+ * Parse only if data is expected, and do not populate
+ * the list if something was parsed wrong. Instead
+ * calling callback with wrong or without data treat
+ * this command as vendor/unknown/not matching one.
*/
+ if (item->cmd_descr) {
+ int res;
+ /* start parsing right where data is */
+ data_off = strlen(prefix) + strlen(item->cmd);
+
+ BLUEATCHAT_DEBUG(session,
+ "Checking for data: %s",
+ item->cmd_descr);
+
+ res = blueatchat_parse_data(item->cmd_descr,
+ &descr_off, session,
+ &data_off,
+ amount - (data_off +
+ strlen(postfix)));
+ if (res < 0) {
+ /*
+ * Error may be triggered in the middle
+ * of parsing when some data has already
+ * been pushed to the data list.
+ * Clean that up.
+ */
+ g_slist_free_full(session->data,
+ g_free);
+ session->data = NULL;
+ continue;
+ } else if (res > 0) {
+ BLUEATCHAT_DEBUG(session,
+ "redundant data found");
+ }
+ } else {
+ /*
+ * Check if there is really no data, like
+ * descriptor indicates. If there is, this
+ * may be a different command.
+ */
+ if ((strlen(session->config->cmd_prefix) +
+ strlen(postfix) + strlen(item->cmd)) !=
+ (unsigned int)amount)
+ continue;
+ }
+
item->notify(session->data);
+
+ /* Free all data after notification returns */
+ g_slist_free_full(session->data, g_free);
+ session->data = NULL;
cbuffer_discard(session->buffer, amount);
return 0;
}
}
- /* no matching command */
- /* TODO: push unknown or vendor message string to GSList and notify */
- item->notify(session->data);
+ /* Unknown or vendor command - push it to upper layers */
+ session->data = g_slist_prepend(session->data,
+ blueatchat_buffdup(session->buffer,
+ amount));
cbuffer_discard(session->buffer, amount);
+ item->notify(session->data);
+
+ /* Free data after notification returns */
+ g_slist_free_full(session->data, g_free);
+ session->data = NULL;
return 1;
}
@@ -234,6 +398,7 @@ struct blueatchat_session *blueatchat_init_session(
session->cmd_list = cmd_list;
session->config = config;
session->debug_callback = debug_fun;
+ session->data = NULL;
session->buffer = cbuffer_init(session->config->buff_size);
if (!session->buffer) {
@@ -249,6 +414,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
session->config = NULL;
session->debug_callback = NULL;
+ g_slist_free_full(session->data, g_free);
cbuffer_free(session->buffer);
g_free(session);
session = NULL;
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
index 9c1ca1b..75d055c 100644
--- a/src/shared/blueatchat.h
+++ b/src/shared/blueatchat.h
@@ -21,6 +21,36 @@
*
*/
+/*
+ * cmd_descr - string describing commands content
+ *
+ * It uses the following characters to describe expected data units:
+ * , - unit/value separator
+ * ( - compound value section begin
+ * ) - compound value section end
+ * o - optional value
+ * u - unsigned integer
+ * i - integer
+ * * - unsignificant - its should be used for ignoring the rest of
+ * data in this command - can only be used at the very end
+ * c - single unquoted char, like in "AT+VTS=A"
+ * s - string
+ * q - quoted string
+ * - - indicates value range like in "+CIND: ("callsetup",(0-3))"
+ *
+ * Example:
+ * (oi,q) - compund value with optional integer and a mandatory string.
+ *
+ * Combinations should be possible:
+ * (oq,oq),(oq,i),u,oq,oi
+ * which should match string like: `(,),("Test message",1023),256,,1`
+ *
+ * Nesting should be possible:
+ * (u,q,(oq,(q,i),i,u,q),i)
+ *
+ * TODO: add more descriptors if needed.
+ */
+
#include <stdbool.h>
typedef void (*blueatchat_debug_func_t)(const char *str, void *user_data);
diff --git a/src/shared/dparser.c b/src/shared/dparser.c
new file mode 100644
index 0000000..7b7a2a2
--- /dev/null
+++ b/src/shared/dparser.c
@@ -0,0 +1,45 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <ctype.h>
+#include <glib.h>
+
+#include "cbuffer.h"
+#include "dparser.h"
+
+unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset)
+{
+ unsigned int *ret;
+ const char *ch = cbuffer_peek_tail(buffer, *offset);
+
+ if (!isdigit(*ch))
+ return NULL;
+
+ ret = g_new0(unsigned int, 1);
+
+ while (isdigit(*ch)) {
+ *ret = (*ret * 10) + (*ch - '0');
+ ch = cbuffer_peek_tail(buffer, ++(*offset));
+ }
+ return ret;
+}
diff --git a/src/shared/dparser.h b/src/shared/dparser.h
new file mode 100644
index 0000000..7c0bf58
--- /dev/null
+++ b/src/shared/dparser.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+unsigned int *dparse_uint(struct circular_buffer *buffer, int *offset);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 14/22] blueatchat: Add basic parsing capability
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds registered commands recognition and callback calls.
---
src/shared/blueatchat.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index c7eee4a..e9b3c9d 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -30,8 +30,16 @@
#include "blueatchat.h"
#include "util.h"
+/* uncomment for extended debug messages */
+/* #define BLUEATCHAT_VERBOSE */
+
+#ifdef BLUEATCHAT_VERBOSE
#define BLUEATCHAT_DEBUG(session, ...) (util_debug(session->debug_callback, \
(void *)__func__, __VA_ARGS__))
+#else
+#define BLUEATCHAT_DEBUG(...) {}
+#endif
+
struct blueatchat_session {
struct blueatchat_config *config;
struct blueatchat_cmd_descriptor *cmd_list;
@@ -40,6 +48,23 @@ struct blueatchat_session {
blueatchat_debug_func_t debug_callback;
};
+#ifdef BLUEATCHAT_VERBOSE
+static void blueatchat_buffer_dump(struct blueatchat_session *session,
+ unsigned int amount)
+{
+ unsigned int i;
+ struct circular_buffer *buffer = session->buffer;
+
+ BLUEATCHAT_DEBUG(session, "BEGIN");
+ for (i = 0; i < cbuffer_get_length(buffer); ++i) {
+ if (i == amount)
+ break;
+ BLUEATCHAT_DEBUG(session, "%c", *cbuffer_peek_tail(buffer, i));
+ }
+ BLUEATCHAT_DEBUG(session, "END");
+}
+#endif
+
/*
* returns offset:
* > 0 - if complete command found, which ends at offset
@@ -99,13 +124,45 @@ static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
return -ENODATA;
}
+static int blueatchat_parse_command(struct blueatchat_session *session,
+ const int amount)
+{
+ struct blueatchat_cmd_descriptor *item;
+
+ BLUEATCHAT_DEBUG(session, "Starting");
+#ifdef BLUEATCHAT_VERBOSE
+ blueatchat_buffer_dump(session, amount);
+#endif
+
+ for (item = session->cmd_list; item->cmd != NULL; ++item) {
+ if (cbuffer_starts_with_seq(session->buffer, item->cmd,
+ strlen(session->config->cmd_prefix))) {
+ /* TODO: process buffered data
+ *-> peek a char to choose appropriate parser routine
+ * or use syntax descriptor string?
+ *-> fill in sessions GSList* data with parsed data
+ */
+ item->notify(session->data);
+ cbuffer_discard(session->buffer, amount);
+ return 0;
+ }
+ }
+ /* no matching command */
+ /* TODO: push unknown or vendor message string to GSList and notify */
+ item->notify(session->data);
+ cbuffer_discard(session->buffer, amount);
+ return 1;
+}
+
static void process_bufferred_data(struct blueatchat_session *session)
{
struct circular_buffer *buffer = session->buffer;
int offset;
+ /* Stop processing if buffer empty or offset < 0 (incomplete data) */
while ((offset = blueatchat_get_complete_cmd_end(session)) >= 0) {
- /* TODO: parse the data */
+ if (offset)
+ blueatchat_parse_command(session, offset);
if (cbuffer_is_empty(buffer))
return;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 13/22] blueatchat: Buffered data sanity check
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds checking for AT command prefix and postfix to determine
data completeness.
---
src/shared/blueatchat.c | 109 ++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 94 insertions(+), 15 deletions(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index a32a7bf..c7eee4a 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -22,6 +22,8 @@
*/
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
#include <glib.h>
#include "cbuffer.h"
@@ -38,6 +40,77 @@ struct blueatchat_session {
blueatchat_debug_func_t debug_callback;
};
+/*
+ * returns offset:
+ * > 0 - if complete command found, which ends at offset
+ * < 0 - if no complete command found wait for more
+ * = 0 - some garbage was consumed check again
+ */
+static int blueatchat_get_complete_cmd_end(struct blueatchat_session *session)
+{
+ struct blueatchat_config *config = session->config;
+ struct circular_buffer *buffer = session->buffer;
+ int offset;
+ unsigned int prelen = strlen(config->cmd_prefix);
+
+ /* Check for command prefix */
+ offset = cbuffer_seek_tail_for_seq(buffer, config->cmd_prefix, 0);
+
+ /* Not enough data if only prefix in the buffer */
+ if (cbuffer_get_length(session->buffer) == prelen)
+ return -ENODATA;
+
+ /* No prefix - drain the buffer */
+ if (offset < 0) {
+ cbuffer_drain(buffer);
+ return -ENODATA;
+ }
+
+ /* Discard data before the prefix */
+ if (offset) {
+ cbuffer_discard(session->buffer, offset);
+ return 0;
+ }
+
+ /* check for postfix */
+ offset = cbuffer_seek_tail_for_seq(buffer, config->cmd_postfix, prelen);
+
+ /* If prefix != postfix check for multiple prefixing before postfix */
+ if (strncmp(config->cmd_prefix, config->cmd_postfix, prelen)) {
+ int off = cbuffer_seek_tail_for_seq(buffer, config->cmd_prefix,
+ prelen);
+
+ /* If second prefix exist before postfix consume */
+ if (off > 0 && off < offset) {
+ cbuffer_discard(session->buffer, off);
+ return 0;
+ }
+ }
+
+ /* If <fix><fix> consume one fix and check again later */
+ if (offset == (int)prelen) {
+ cbuffer_discard(session->buffer, offset);
+ return 0;
+ } else if (offset > 0) {
+ return offset + strlen(config->cmd_postfix);
+ }
+
+ /* If offset == 0 no tail found wait for more */
+ return -ENODATA;
+}
+
+static void process_bufferred_data(struct blueatchat_session *session)
+{
+ struct circular_buffer *buffer = session->buffer;
+ int offset;
+
+ while ((offset = blueatchat_get_complete_cmd_end(session)) >= 0) {
+ /* TODO: parse the data */
+
+ if (cbuffer_is_empty(buffer))
+ return;
+ }
+}
bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
{
@@ -48,10 +121,24 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
gsize len;
gsize i;
- /* read all data into the buffer */
+ /* Fill the buffer in chunks if needed to drain the IO */
do {
freechunk = cbuffer_get_free_chunk_size(session->buffer);
+ /* Free some space by processing the buffer if its full */
+ if (!freechunk) {
+ process_bufferred_data(session);
+
+ /* If couldn't process anything discard all data */
+ if (!cbuffer_get_free_chunk_size(session->buffer))
+ cbuffer_drain(session->buffer);
+ continue;
+ }
+
+ /*
+ * If there is no space after processing or draining the buffer
+ * we have a serious buffer error.
+ */
msg = cbuffer_get_free_cell(session->buffer);
if (!msg) {
BLUEATCHAT_DEBUG(session, "Buffer malfunction!");
@@ -65,25 +152,17 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
return false;
}
- if (len != 0)
- BLUEATCHAT_DEBUG(session,
- "Blueatchat red %u bytes: %.*s",
- (unsigned int)len, (unsigned int)len,
- msg);
-
for (i = 0; i < len; ++i)
cbuffer_consume_next_cell(session->buffer);
+
+ freechunk = cbuffer_get_free_chunk_size(session->buffer);
} while ((len != 0) && (ret != G_IO_STATUS_EOF));
- /* TODO: process buffered data
- * -> check for complete command (check for head, tail)
- * -> start consuming buffered data and parsing it
- * -> iterate through session->cmd_list and match commands
- * -> peek a char to choose appropriate parser routine or use
- * syntax descriptor string?
- * -> fill in sessions GSList* data with parsed data
- * -> call '*(session->cmd_list[cmd_index].notify)(session->data)
+ /*
+ * Now check for data completeness (command prefix and postfix).
+ * Than parse it and consume parsed data from the buffer.
*/
+ process_bufferred_data(session);
return true;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 12/22] unit: Add test case for cbuffer's discard
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
unit/test-cbuffer.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index d51ce64..cb99506 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -462,6 +462,59 @@ static void cbt_get_length(struct cbt_fixture *fix, gconstpointer test_data)
cbuffer_get_length(fix->cbuff));
}
+static void cbt_discard(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+ g_assert_cmpint(3, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+ g_assert_cmpint(7, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_discard(fix->cbuff, 2);
+ g_assert_cmpint(5, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* unsuccessful discard */
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 2), ==, 0);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 1), ==, 1);
+ g_assert_cmpint(6, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('x', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 2), ==, 2);
+ g_assert_cmpint(4, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('v', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 3), ==, 3);
+ g_assert_cmpint(1, ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpint('m', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+
+ g_assert_cmpint(cbuffer_discard(fix->cbuff, 4), ==, 1);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -491,5 +544,7 @@ int main(int argc, char **argv)
cbt_check_for, cbt_fix_teardown);
g_test_add("/cbuffer/get_length", struct cbt_fixture, 0, cbt_fix_setup,
cbt_get_length, cbt_fix_teardown);
+ g_test_add("/cbuffer/discard", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_discard, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 11/22] cbuffer: Add discard method
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds discarding desired amount of oldest data from the buffer.
---
src/shared/cbuffer.c | 9 +++++++++
src/shared/cbuffer.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index 8d85059..a5ed7c5 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -124,6 +124,15 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
return cbuff->size;
}
+int cbuffer_discard(struct circular_buffer *cbuff, unsigned int amount)
+{
+ unsigned int len = cbuffer_get_length(cbuff);
+
+ amount = amount > len ? len : amount;
+ cbuff->start = (cbuff->start + amount) & (2 * cbuff->size - 1);
+ return amount;
+}
+
/* Manual mode gives direct access to buffers memory space */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff)
{
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index ff29d22..759d8a7 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -27,6 +27,7 @@ struct circular_buffer *cbuffer_init(unsigned int size);
void cbuffer_free(struct circular_buffer *cbuff);
int cbuffer_write(struct circular_buffer *cbuff, char source);
int cbuffer_read(struct circular_buffer *cbuff, char *dest);
+int cbuffer_discard(struct circular_buffer *cbuff, unsigned int amount);
void cbuffer_drain(struct circular_buffer *cbuff);
int cbuffer_is_full(struct circular_buffer *cbuff);
int cbuffer_is_empty(struct circular_buffer *cbuff);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 10/22] blueatchat: Use circullar buffer as data storage
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch makes blueatchat use circular buffer for storing data read
from the glib's IO channel.
---
src/shared/blueatchat.c | 49 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 38 insertions(+), 11 deletions(-)
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
index be26bad..a32a7bf 100644
--- a/src/shared/blueatchat.c
+++ b/src/shared/blueatchat.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <glib.h>
+#include "cbuffer.h"
#include "blueatchat.h"
#include "util.h"
@@ -32,9 +33,9 @@
struct blueatchat_session {
struct blueatchat_config *config;
struct blueatchat_cmd_descriptor *cmd_list;
+ struct circular_buffer *buffer;
GSList *data; /* stores parsed data */
blueatchat_debug_func_t debug_callback;
- /* TODO: Add ring buffer. */
};
@@ -42,16 +43,37 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
{
GIOStatus ret = G_IO_STATUS_EOF;
GError *err = NULL;
- gchar *msg;
+ gchar *msg = NULL;
+ int freechunk;
gsize len;
+ gsize i;
- /* TODO: use ring buffer inside the session object */
- ret = g_io_channel_read_line(gio, &msg, &len, NULL, &err);
- if (ret == G_IO_STATUS_ERROR)
- BLUEATCHAT_DEBUG(session, "Error reading: %s\n", err->message);
- else
- BLUEATCHAT_DEBUG(session, "Blueatchat red %u bytes: %s",
- (unsigned int)len, msg);
+ /* read all data into the buffer */
+ do {
+ freechunk = cbuffer_get_free_chunk_size(session->buffer);
+
+ msg = cbuffer_get_free_cell(session->buffer);
+ if (!msg) {
+ BLUEATCHAT_DEBUG(session, "Buffer malfunction!");
+ return false;
+ }
+
+ ret = g_io_channel_read_chars(gio, msg, freechunk, &len, &err);
+ if (ret == G_IO_STATUS_ERROR) {
+ BLUEATCHAT_DEBUG(session, "Error reading: %s\n",
+ err->message);
+ return false;
+ }
+
+ if (len != 0)
+ BLUEATCHAT_DEBUG(session,
+ "Blueatchat red %u bytes: %.*s",
+ (unsigned int)len, (unsigned int)len,
+ msg);
+
+ for (i = 0; i < len; ++i)
+ cbuffer_consume_next_cell(session->buffer);
+ } while ((len != 0) && (ret != G_IO_STATUS_EOF));
/* TODO: process buffered data
* -> check for complete command (check for head, tail)
@@ -62,7 +84,6 @@ bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
* -> fill in sessions GSList* data with parsed data
* -> call '*(session->cmd_list[cmd_index].notify)(session->data)
*/
- g_free(msg);
return true;
}
@@ -77,7 +98,12 @@ struct blueatchat_session *blueatchat_init_session(
session->cmd_list = cmd_list;
session->config = config;
session->debug_callback = debug_fun;
- /* TODO: initialize ring buffer */
+
+ session->buffer = cbuffer_init(session->config->buff_size);
+ if (!session->buffer) {
+ g_free(session);
+ return NULL;
+ }
return session;
}
@@ -87,6 +113,7 @@ void blueatchat_cleanup_session(struct blueatchat_session *session)
session->config = NULL;
session->debug_callback = NULL;
+ cbuffer_free(session->buffer);
g_free(session);
session = NULL;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 09/22] blueatchat: Add bluetooth at chat module skeleton
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds AT chat module skeleton used for sending and receiving
AT commands. This patch contains only part required for reading.
---
src/shared/blueatchat.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/blueatchat.h | 50 ++++++++++++++++++++++++++
2 files changed, 142 insertions(+)
create mode 100644 src/shared/blueatchat.c
create mode 100644 src/shared/blueatchat.h
diff --git a/src/shared/blueatchat.c b/src/shared/blueatchat.c
new file mode 100644
index 0000000..be26bad
--- /dev/null
+++ b/src/shared/blueatchat.c
@@ -0,0 +1,92 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include "blueatchat.h"
+#include "util.h"
+
+#define BLUEATCHAT_DEBUG(session, ...) (util_debug(session->debug_callback, \
+ (void *)__func__, __VA_ARGS__))
+struct blueatchat_session {
+ struct blueatchat_config *config;
+ struct blueatchat_cmd_descriptor *cmd_list;
+ GSList *data; /* stores parsed data */
+ blueatchat_debug_func_t debug_callback;
+ /* TODO: Add ring buffer. */
+};
+
+
+bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio)
+{
+ GIOStatus ret = G_IO_STATUS_EOF;
+ GError *err = NULL;
+ gchar *msg;
+ gsize len;
+
+ /* TODO: use ring buffer inside the session object */
+ ret = g_io_channel_read_line(gio, &msg, &len, NULL, &err);
+ if (ret == G_IO_STATUS_ERROR)
+ BLUEATCHAT_DEBUG(session, "Error reading: %s\n", err->message);
+ else
+ BLUEATCHAT_DEBUG(session, "Blueatchat red %u bytes: %s",
+ (unsigned int)len, msg);
+
+ /* TODO: process buffered data
+ * -> check for complete command (check for head, tail)
+ * -> start consuming buffered data and parsing it
+ * -> iterate through session->cmd_list and match commands
+ * -> peek a char to choose appropriate parser routine or use
+ * syntax descriptor string?
+ * -> fill in sessions GSList* data with parsed data
+ * -> call '*(session->cmd_list[cmd_index].notify)(session->data)
+ */
+ g_free(msg);
+ return true;
+}
+
+struct blueatchat_session *blueatchat_init_session(
+ struct blueatchat_config *config,
+ struct blueatchat_cmd_descriptor *cmd_list,
+ blueatchat_debug_func_t debug_fun)
+{
+ struct blueatchat_session *session =
+ g_new(struct blueatchat_session, 1);
+
+ session->cmd_list = cmd_list;
+ session->config = config;
+ session->debug_callback = debug_fun;
+ /* TODO: initialize ring buffer */
+ return session;
+}
+
+void blueatchat_cleanup_session(struct blueatchat_session *session)
+{
+ session->cmd_list = NULL;
+ session->config = NULL;
+ session->debug_callback = NULL;
+
+ g_free(session);
+ session = NULL;
+}
diff --git a/src/shared/blueatchat.h b/src/shared/blueatchat.h
new file mode 100644
index 0000000..9c1ca1b
--- /dev/null
+++ b/src/shared/blueatchat.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdbool.h>
+
+typedef void (*blueatchat_debug_func_t)(const char *str, void *user_data);
+typedef bool (*blueatchat_notify_cb)(GSList *);
+
+/* command/response descriptor */
+struct blueatchat_cmd_descriptor {
+ char *cmd; /* cmd/response string to match */
+ char *cmd_descr; /* data descriptor - expected data to be parsed */
+ blueatchat_notify_cb notify; /* callback for the given command */
+};
+
+/* simple config */
+struct blueatchat_config {
+ size_t buff_size;
+ char *cmd_prefix;
+ char *cmd_postfix;
+};
+
+struct blueatchat_session;
+
+struct blueatchat_session *blueatchat_init_session(
+ struct blueatchat_config *config,
+ struct blueatchat_cmd_descriptor *cmd_list,
+ blueatchat_debug_func_t debug_fun);
+void blueatchat_cleanup_session(struct blueatchat_session *session);
+bool blueatchat_read(struct blueatchat_session *session, GIOChannel *gio);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 08/22] unit: Add test cases for cbuffer seek, check, get_length
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
---
unit/test-cbuffer.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 153 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index d5507a1..d51ce64 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -317,6 +317,151 @@ static void cbt_get_freechunk(struct cbt_fixture *fix, gconstpointer test_data)
==, cbuffer_get_size(fix->cbuff) - 1);
}
+static void cbt_seek_head(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int found = -128;
+
+ /* tail */
+ cbuffer_write(fix->cbuff, '<');
+ cbuffer_write(fix->cbuff, '.');
+ cbuffer_write(fix->cbuff, '>');
+ cbuffer_write(fix->cbuff, 'a');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "<.>", 0);
+ g_assert_cmpint(found, ==, 0);
+
+ g_assert_cmpint('<', ==, *cbuffer_peek_tail(fix->cbuff, found));
+ g_assert_cmpint('.', ==, *cbuffer_peek_tail(fix->cbuff, found + 1));
+ g_assert_cmpint('>', ==, *cbuffer_peek_tail(fix->cbuff, found + 2));
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, ">.>", 0);
+ g_assert_cmpint(found, <, 0);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "<>", 0);
+ g_assert_cmpint(found, <, 0);
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "a", 1);
+ g_assert_cmpint(found, ==, 3);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "b", 2);
+ g_assert_cmpint(found, ==, 4);
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "c", 3);
+ g_assert_cmpint(found, ==, 5);
+
+ found = cbuffer_seek_head_for_seq(fix->cbuff, "abc", 0);
+ g_assert(found == 3);
+ g_assert_cmpint('a', ==, *cbuffer_peek_tail(fix->cbuff, found));
+ g_assert_cmpint('b', ==, *cbuffer_peek_tail(fix->cbuff, found + 1));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, found + 2));
+}
+
+static void cbt_seek_tail(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ int found = -1;
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+
+ /* starts seeking for the exact sequence 3 bytes from the end */
+ found = cbuffer_seek_tail_for_seq(fix->cbuff, "bnm", 3);
+ /* should return its distance from the begining */
+ g_assert_cmpint(found, ==, 4);
+ found = cbuffer_seek_tail_for_seq(fix->cbuff, "zxc", 0);
+ g_assert_cmpint(found, ==, 0);
+}
+
+static void cbt_check_for(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "zxcvbm", 0));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "zxcvbn", 0));
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 1));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 2));
+ g_assert_cmpint(0, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "cvb", 3));
+ g_assert_cmpint(1, ==,
+ cbuffer_starts_with_seq(fix->cbuff, "nm", 5));
+}
+
+static void cbt_get_length(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 0;
+ int unsigned i;
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* tail */
+ cbuffer_write(fix->cbuff, 'z');
+ cbuffer_write(fix->cbuff, 'x');
+ cbuffer_write(fix->cbuff, 'c');
+ /* head */
+ g_assert_cmpint(3, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_write(fix->cbuff, 'v');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'n');
+ cbuffer_write(fix->cbuff, 'm');
+ /* head */
+ g_assert_cmpint(7, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_read(fix->cbuff, &ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(5, ==, cbuffer_get_length(fix->cbuff));
+
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpint(0, ==, cbuffer_get_length(fix->cbuff));
+
+ /* test for buffer wrapping around */
+
+ /* fill the whole buffer */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff), ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* read half of the data */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff) / 2, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* fill it again */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+
+ /* after refilling should be full */
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff), ==,
+ cbuffer_get_length(fix->cbuff));
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -338,5 +483,13 @@ int main(int argc, char **argv)
cbt_peek, cbt_fix_teardown);
g_test_add("/cbuffer/get_freechunk", struct cbt_fixture, 0,
cbt_fix_setup, cbt_get_freechunk, cbt_fix_teardown);
+ g_test_add("/cbuffer/seek_head", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_seek_head, cbt_fix_teardown);
+ g_test_add("/cbuffer/seek_tail", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_seek_tail, cbt_fix_teardown);
+ g_test_add("/cbuffer/check_for", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_check_for, cbt_fix_teardown);
+ g_test_add("/cbuffer/get_length", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_get_length, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 07/22] cbuffer: Add seek and check routines
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds seek and check routines needed for some sanity checks
by the AT chat module. It will be needed for the boundary checks.
---
src/shared/cbuffer.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/cbuffer.h | 6 ++++++
2 files changed, 60 insertions(+)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index c1e5b08..8d85059 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -22,6 +22,7 @@
*/
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include "cbuffer.h"
@@ -156,6 +157,59 @@ const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n)
return &(cbuff->data[idx & (cbuff->size - 1)]);
}
+/* Checks for sequence in the given position */
+int cbuffer_starts_with_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ int seqlen = strlen(seq);
+ const char *ch;
+
+ ch = NULL;
+ for (i = 0; i < seqlen; ++i) {
+ ch = cbuffer_peek_tail(cbuff, offset + i);
+ if (seq[i] != *ch)
+ return 0;
+ }
+ return 1;
+}
+
+/* Seeks for sequence in the circular buffer */
+int cbuffer_seek_head_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ const char *pos;
+ const char *end;
+
+ end = cbuffer_peek_tail(cbuff, 0);
+ i = offset;
+ for (pos = cbuffer_peek_head(cbuff, i); pos != end; ++i) {
+ pos = cbuffer_peek_head(cbuff, i);
+ if (cbuffer_starts_with_seq(cbuff, seq, i))
+ return i;
+ }
+ return -ENODATA;
+}
+
+/* Seeks for sequence in the circular buffer */
+int cbuffer_seek_tail_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset)
+{
+ int i;
+ const char *pos;
+ const char *end;
+
+ end = cbuffer_peek_head(cbuff, 0);
+ i = offset;
+ for (pos = cbuffer_peek_tail(cbuff, i); pos != end; ++i) {
+ pos = cbuffer_peek_tail(cbuff, i);
+ if (cbuffer_starts_with_seq(cbuff, seq, i))
+ return i;
+ }
+ return -ENODATA;
+}
+
unsigned int cbuffer_get_length(struct circular_buffer *cbuff)
{
return abs(cbuff->end - cbuff->start);
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index 3880acd..ff29d22 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -40,3 +40,9 @@ const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n);
const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n);
unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff);
unsigned int cbuffer_get_length(struct circular_buffer *cbuff);
+int cbuffer_starts_with_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
+int cbuffer_seek_head_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
+int cbuffer_seek_tail_for_seq(struct circular_buffer *cbuff, const char *seq,
+ unsigned int offset);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 06/22] unit: Add test cases for cbuffer memory acess routines
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch adds unit tests for cbuffer's memory direct access
finctionality and browsing methods.
---
unit/test-cbuffer.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/unit/test-cbuffer.c b/unit/test-cbuffer.c
index 5a3482d..d5507a1 100644
--- a/unit/test-cbuffer.c
+++ b/unit/test-cbuffer.c
@@ -200,6 +200,123 @@ static void cbt_manual(struct cbt_fixture *fix, gconstpointer test_data)
g_assert(cbuffer_is_empty(fix->cbuff));
}
+static void cbt_peek(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 1;
+ /* tail */
+ cbuffer_write(fix->cbuff, 'a');
+ cbuffer_write(fix->cbuff, 'b');
+ cbuffer_write(fix->cbuff, 'c');
+ cbuffer_write(fix->cbuff, 'd');
+ /* head */
+
+ /* peek from head of "abcd" string */
+ g_assert_cmpint('a', ==, *cbuffer_peek_tail(fix->cbuff, 0));
+ g_assert_cmpint('b', ==, *cbuffer_peek_tail(fix->cbuff, 1));
+ g_assert_cmpint('c', ==, *cbuffer_peek_tail(fix->cbuff, 2));
+ g_assert_cmpint('d', ==, *cbuffer_peek_tail(fix->cbuff, 3));
+ /* no boundary check here - will wrap around if index > buffer size */
+ /* g_assert_cmpint('\0', ==, *cbuffer_peek_head(fix->cbuff, 4)); */
+
+ /* peek from tail of "abcd" string */
+ g_assert_cmpint('d', ==, *cbuffer_peek_head(fix->cbuff, 0));
+ g_assert_cmpint('c', ==, *cbuffer_peek_head(fix->cbuff, 1));
+ g_assert_cmpint('b', ==, *cbuffer_peek_head(fix->cbuff, 2));
+ g_assert_cmpint('a', ==, *cbuffer_peek_head(fix->cbuff, 3));
+ /* no boundary check here - will wrap around if index > buffer size */
+ /* g_assert_cmpint('\0', ==, *cbuffer_peek_tail(fix->cbuff, 4)); */
+
+ /* now read peeked chars in the right sequence */
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('b', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('c', ==, ch);
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('d', ==, ch);
+ ch = 1;
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint(1, ==, ch);
+ g_assert(cbuffer_is_empty(fix->cbuff));
+}
+
+static void cbt_get_freechunk(struct cbt_fixture *fix, gconstpointer test_data)
+{
+ char ch = 0;
+ unsigned int i;
+
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff));
+
+ /* fill the whole buffer */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff),
+ ==, cbuffer_get_length(fix->cbuff));
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff), ==, 0);
+
+ /* read more than a half of the data */
+ for (i = 0; i <= cbuffer_get_size(fix->cbuff) / 2; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ }
+ g_assert_cmpint((cbuffer_get_size(fix->cbuff) / 2) - 1, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* fill it again leaving 2 empty spaces */
+ for (i = 0; i < (cbuffer_get_size(fix->cbuff) / 2) - 1; ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'b');
+ }
+ g_assert_cmpint(cbuffer_get_size(fix->cbuff) - 2, ==,
+ cbuffer_get_length(fix->cbuff));
+
+ /* s- buffers start offset
+ * e- buffers end offset
+ * n- place where new data will be appended
+ */
+ /* [XXXX|n...|XXXXX]
+ * e s
+ */
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff), ==, 2);
+
+ /* [|n.............]
+ * s/e
+ */
+ cbuffer_drain(fix->cbuff);
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff));
+
+ /* [|X|n...........]
+ * s e
+ */
+ cbuffer_drain(fix->cbuff);
+ cbuffer_write(fix->cbuff, 'b');
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff) - 1);
+
+ /* [n...........|X|]
+ * e s
+ */
+ cbuffer_drain(fix->cbuff);
+ for (i = 0; i < cbuffer_get_size(fix->cbuff); ++i) {
+ g_assert(!cbuffer_is_full(fix->cbuff));
+ cbuffer_write(fix->cbuff, 'a');
+ }
+ /* read all but one */
+ for (i = 0; i < cbuffer_get_size(fix->cbuff) - 1; ++i) {
+ g_assert(!cbuffer_is_empty(fix->cbuff));
+ cbuffer_read(fix->cbuff, &ch);
+ g_assert_cmpint('a', ==, ch);
+ }
+ g_assert_cmpuint(cbuffer_get_free_chunk_size(fix->cbuff),
+ ==, cbuffer_get_size(fix->cbuff) - 1);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -217,5 +334,9 @@ int main(int argc, char **argv)
cbt_drain, cbt_fix_teardown);
g_test_add("/cbuffer/manual", struct cbt_fixture, 0, cbt_fix_setup,
cbt_manual, cbt_fix_teardown);
+ g_test_add("/cbuffer/peek", struct cbt_fixture, 0, cbt_fix_setup,
+ cbt_peek, cbt_fix_teardown);
+ g_test_add("/cbuffer/get_freechunk", struct cbt_fixture, 0,
+ cbt_fix_setup, cbt_get_freechunk, cbt_fix_teardown);
return g_test_run();
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 05/22] cbuffer: Add methods for browsing through data
From: Jakub Tyszkowski @ 2013-10-14 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1381739678-16260-1-git-send-email-jakub.tyszkowski@tieto.com>
This patch provides methods to read buffered data without taking it out
from the buffer. Its possible to take a peek at a byte stored at the
N-th positon from buffer's tail. It also adds method for checking the
size of free continous memory space of the buffer, which in pair with
manual memory access methods can reduce io read calls by allowing to
write more than one byte at a time.
---
src/shared/cbuffer.c | 37 ++++++++++++++++++++++++++++++++++++-
src/shared/cbuffer.h | 6 ++++++
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/shared/cbuffer.c b/src/shared/cbuffer.c
index 7fd95cc..c1e5b08 100644
--- a/src/shared/cbuffer.c
+++ b/src/shared/cbuffer.c
@@ -123,7 +123,7 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff)
return cbuff->size;
}
-/* manual mode gives direct access to buffers memory space */
+/* Manual mode gives direct access to buffers memory space */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff)
{
if (cbuffer_is_full(cbuff))
@@ -139,3 +139,38 @@ int cbuffer_consume_next_cell(struct circular_buffer *cbuff)
}
return -ENOSPC;
}
+
+const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n)
+{
+ int idx;
+ /* find decremented end offset */
+ idx = (cbuff->end - (n + 1)) & (2 * cbuff->size - 1);
+ return &cbuff->data[idx & (cbuff->size - 1)];
+}
+
+const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n)
+{
+ int idx;
+ /* find incremented start offset */
+ idx = (cbuff->start + n) & (2 * cbuff->size - 1);
+ return &(cbuff->data[idx & (cbuff->size - 1)]);
+}
+
+unsigned int cbuffer_get_length(struct circular_buffer *cbuff)
+{
+ return abs(cbuff->end - cbuff->start);
+}
+
+unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff)
+{
+ int realstart = (cbuff->start & (cbuff->size - 1));
+ int realend = (cbuff->end & (cbuff->size - 1));
+
+ if (cbuffer_is_full(cbuff))
+ return 0;
+
+ if (realend >= realstart)
+ return cbuff->size - realend;
+ else
+ return realstart - realend;
+}
diff --git a/src/shared/cbuffer.h b/src/shared/cbuffer.h
index 4aaacfa..3880acd 100644
--- a/src/shared/cbuffer.h
+++ b/src/shared/cbuffer.h
@@ -34,3 +34,9 @@ unsigned int cbuffer_get_size(struct circular_buffer *cbuff);
/* memory direct access functions */
char *cbuffer_get_free_cell(struct circular_buffer *cbuff);
int cbuffer_consume_next_cell(struct circular_buffer *cbuff);
+
+/* peek n-th char */
+const char *cbuffer_peek_head(struct circular_buffer *cbuff, unsigned int n);
+const char *cbuffer_peek_tail(struct circular_buffer *cbuff, unsigned int n);
+unsigned int cbuffer_get_free_chunk_size(struct circular_buffer *cbuff);
+unsigned int cbuffer_get_length(struct circular_buffer *cbuff);
--
1.7.9.5
^ 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