Linux bluetooth development
 help / color / mirror / Atom feed
* [RFC 5/5] Bluetooth: Add set observer MGMT command
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes
In-Reply-To: <1344632507-21381-1-git-send-email-jefferson.delfes@openbossa.org>

This command will enable or disable observer mode. In that mode,
discovery will be started, so any broadcast will be received in device
found event.

Signed-off-by: Jefferson Delfes <jefferson.delfes@openbossa.org>
---
 include/net/bluetooth/hci.h  |  1 +
 include/net/bluetooth/mgmt.h |  3 +++
 net/bluetooth/mgmt.c         | 43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 80f585a..17ea1ad 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -119,6 +119,7 @@ enum {
 	HCI_PENDING_CLASS,
 	HCI_PERIODIC_INQ,
 	HCI_BROADCASTER,
+	HCI_OBSERVER,
 };
 
 /* HCI ioctl defines */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 2e85012..69525f9 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_HS			0x00000100
 #define MGMT_SETTING_LE			0x00000200
 #define MGMT_SETTING_BROADCASTER	0x00000400
+#define MGMT_SETTING_OBSERVER		0x00000800
 
 #define MGMT_OP_READ_INFO		0x0004
 #define MGMT_READ_INFO_SIZE		0
@@ -370,6 +371,8 @@ struct mgmt_cp_unset_controller_data {
 
 #define MGMT_OP_SET_BROADCASTER		0x002B
 
+#define MGMT_OP_SET_OBSERVER		0x002C
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ae4910a..845ce2d 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -380,6 +380,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
 	settings |= MGMT_SETTING_DISCOVERABLE;
 	settings |= MGMT_SETTING_PAIRABLE;
 	settings |= MGMT_SETTING_BROADCASTER;
+	settings |= MGMT_SETTING_OBSERVER;
 
 	if (lmp_ssp_capable(hdev))
 		settings |= MGMT_SETTING_SSP;
@@ -423,6 +424,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
 	if (test_bit(HCI_BROADCASTER, &hdev->dev_flags))
 		settings |= MGMT_SETTING_BROADCASTER;
 
+	if (test_bit(HCI_OBSERVER, &hdev->dev_flags))
+		settings |= MGMT_SETTING_OBSERVER;
+
 	if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
 		settings |= MGMT_SETTING_LINK_SECURITY;
 
@@ -2805,6 +2809,44 @@ failed:
 	return err;
 }
 
+static int set_observer_le(struct hci_dev *hdev, u8 enable)
+{
+	struct hci_cp_le_set_scan_enable cmd;
+
+	cmd.enable = enable;
+	cmd.filter_dup = 0;
+	return hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cmd), &cmd);
+}
+
+static int set_observer(struct sock *sk, struct hci_dev *hdev, void *data,
+			u16 len)
+{
+	struct mgmt_mode *cp = data;
+	int err;
+
+	BT_DBG("%s val:%i", hdev->name, cp->val);
+
+	if (cp->val)
+		set_bit(HCI_OBSERVER, &hdev->dev_flags);
+	else
+		clear_bit(HCI_OBSERVER, &hdev->dev_flags);
+
+	hci_dev_lock(hdev);
+
+	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+		set_observer_le(hdev, cp->val);
+
+	err = send_settings_rsp(sk, MGMT_OP_SET_OBSERVER, hdev);
+	if (err < 0)
+		goto failed;
+
+	err = new_settings(hdev, sk);
+
+failed:
+	hci_dev_unlock(hdev);
+	return err;
+}
+
 static const struct mgmt_handler {
 	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
 		     u16 data_len);
@@ -2855,6 +2897,7 @@ static const struct mgmt_handler {
 	{ set_controller_data,    true,  MGMT_SET_CONTROLLER_DATA_SIZE },
 	{ unset_controller_data,  false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
 	{ set_broadcaster,        false, MGMT_SETTING_SIZE },
+	{ set_observer,           false, MGMT_SETTING_SIZE },
 };
 
 
-- 
1.7.11.4


^ permalink raw reply related

* [RFC 4/5] Bluetooth: Add set broadcaster MGMT command
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes
In-Reply-To: <1344632507-21381-1-git-send-email-jefferson.delfes@openbossa.org>

This command will enable or disable broadcaster mode. Data can be added
with set_controller_data command. If there are more data that can be
broadcasted, it will be ignored for while.

Signed-off-by: Jefferson Delfes <jefferson.delfes@openbossa.org>
---
 include/net/bluetooth/hci.h  |  1 +
 include/net/bluetooth/mgmt.h |  3 ++
 net/bluetooth/mgmt.c         | 75 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 79 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index e4d4717..80f585a 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -118,6 +118,7 @@ enum {
 	HCI_LINK_SECURITY,
 	HCI_PENDING_CLASS,
 	HCI_PERIODIC_INQ,
+	HCI_BROADCASTER,
 };
 
 /* HCI ioctl defines */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index eda2755..2e85012 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -92,6 +92,7 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_BREDR		0x00000080
 #define MGMT_SETTING_HS			0x00000100
 #define MGMT_SETTING_LE			0x00000200
+#define MGMT_SETTING_BROADCASTER	0x00000400
 
 #define MGMT_OP_READ_INFO		0x0004
 #define MGMT_READ_INFO_SIZE		0
@@ -367,6 +368,8 @@ struct mgmt_cp_unset_controller_data {
 } __packed;
 #define MGMT_UNSET_CONTROLLER_DATA_SIZE	1
 
+#define MGMT_OP_SET_BROADCASTER		0x002B
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 51d0e2f..ae4910a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -379,6 +379,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
 	settings |= MGMT_SETTING_FAST_CONNECTABLE;
 	settings |= MGMT_SETTING_DISCOVERABLE;
 	settings |= MGMT_SETTING_PAIRABLE;
+	settings |= MGMT_SETTING_BROADCASTER;
 
 	if (lmp_ssp_capable(hdev))
 		settings |= MGMT_SETTING_SSP;
@@ -419,6 +420,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
 	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
 		settings |= MGMT_SETTING_LE;
 
+	if (test_bit(HCI_BROADCASTER, &hdev->dev_flags))
+		settings |= MGMT_SETTING_BROADCASTER;
+
 	if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
 		settings |= MGMT_SETTING_LINK_SECURITY;
 
@@ -2731,6 +2735,76 @@ static int unset_controller_data(struct sock *sk, struct hci_dev *hdev,
 			    NULL, 0);
 }
 
+static int set_broadcaster_le(struct hci_dev *hdev, u8 enable)
+{
+	BT_DBG("%s enable:%i", hdev->name, enable);
+
+	if (enable) {
+		struct hci_cp_le_set_adv_params params;
+		struct hci_cp_le_set_adv_data data;
+		struct controller_data *d;
+
+		params.interval_min = __constant_cpu_to_le16(0x0800);
+		params.interval_max = __constant_cpu_to_le16(0x0800);
+		params.type = ADV_NONCONN_IND;
+		params.own_address_type = ADDR_LE_DEV_PUBLIC;
+		params.direct_address_type = ADDR_LE_DEV_PUBLIC;
+		memset(&params.direct_address, 0,
+		       sizeof(params.direct_address));
+		params.channel_map = ADV_USE_ALL_CHANNELS;
+		params.filter_policy = 0x00;
+		hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_PARAMS, sizeof(params),
+			     &params);
+
+		data.data_len = 0;
+		memset(&data.data, 0, sizeof(data.data));
+		list_for_each_entry(d, &hdev->controller_data, list) {
+			if (data.data_len + d->data_length + 2 >
+			    HCI_MAX_ADV_LENGTH)
+				break;
+
+			data.data[data.data_len] = d->data_length + 1;
+			data.data[data.data_len + 1] = d->data_type;
+			memcpy(&data.data[data.data_len + 2], d->data,
+			       d->data_length);
+			data.data_len += d->data_length + 2;
+		}
+		hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(data), &data);
+	}
+
+	return hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
+			    &enable);
+}
+
+static int set_broadcaster(struct sock *sk, struct hci_dev *hdev, void *data,
+			   u16 len)
+{
+	struct mgmt_mode *cp = data;
+	int err;
+
+	BT_DBG("%s val:%i", hdev->name, cp->val);
+
+	if (cp->val)
+		set_bit(HCI_BROADCASTER, &hdev->dev_flags);
+	else
+		clear_bit(HCI_BROADCASTER, &hdev->dev_flags);
+
+	hci_dev_lock(hdev);
+
+	if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+		set_broadcaster_le(hdev, cp->val);
+
+	err = send_settings_rsp(sk, MGMT_OP_SET_BROADCASTER, hdev);
+	if (err < 0)
+		goto failed;
+
+	err = new_settings(hdev, sk);
+
+failed:
+	hci_dev_unlock(hdev);
+	return err;
+}
+
 static const struct mgmt_handler {
 	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
 		     u16 data_len);
@@ -2780,6 +2854,7 @@ static const struct mgmt_handler {
 	{ set_device_id,          false, MGMT_SET_DEVICE_ID_SIZE },
 	{ set_controller_data,    true,  MGMT_SET_CONTROLLER_DATA_SIZE },
 	{ unset_controller_data,  false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
+	{ set_broadcaster,        false, MGMT_SETTING_SIZE },
 };
 
 
-- 
1.7.11.4


^ permalink raw reply related

* [RFC 3/5] Bluetooth: Add unset controller data MGMT command
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes
In-Reply-To: <1344632507-21381-1-git-send-email-jefferson.delfes@openbossa.org>

This command will remove controller data from EIR and/or AD.

Signed-off-by: Jefferson Delfes <jefferson.delfes@openbossa.org>
---
 include/net/bluetooth/hci_core.h |  1 +
 include/net/bluetooth/mgmt.h     |  6 ++++++
 net/bluetooth/hci_core.c         | 16 ++++++++++++++++
 net/bluetooth/mgmt.c             | 16 ++++++++++++++++
 4 files changed, 39 insertions(+)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3a61227..5e6b477 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -728,6 +728,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 data_type,
 			    u8 data_length, u8 *data);
 int hci_controller_data_clear(struct hci_dev *hdev);
+int hci_controller_data_remove(struct hci_dev *hdev, u8 data_type);
 
 #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))
 
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 1afa399..eda2755 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -361,6 +361,12 @@ struct mgmt_cp_set_controller_data {
 } __packed;
 #define MGMT_SET_CONTROLLER_DATA_SIZE	3
 
+#define MGMT_OP_UNSET_CONTROLLER_DATA	0x002A
+struct mgmt_cp_unset_controller_data {
+	__u8	data_type;
+} __packed;
+#define MGMT_UNSET_CONTROLLER_DATA_SIZE	1
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2e38a1b..80280fa 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1471,6 +1471,22 @@ int hci_controller_data_clear(struct hci_dev *hdev)
 	return 0;
 }
 
+int hci_controller_data_remove(struct hci_dev *hdev, u8 data_type)
+{
+	struct controller_data *match, *n;
+	int matches = 0;
+
+	list_for_each_entry_safe(match, n, &hdev->controller_data, list) {
+		if (data_type == match->data_type) {
+			list_del(&match->list);
+			kfree(match);
+			matches++;
+		}
+	}
+
+	return matches;
+}
+
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
 	struct bdaddr_list *b;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9e9702a..51d0e2f 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2716,6 +2716,21 @@ static int set_controller_data(struct sock *sk, struct hci_dev *hdev,
 			    0);
 }
 
+static int unset_controller_data(struct sock *sk, struct hci_dev *hdev,
+				 void *data, u16 len)
+{
+	struct mgmt_cp_unset_controller_data *cp = data;
+
+	BT_DBG("%s type:0x%02x", hdev->name, cp->data_type);
+
+	hci_dev_lock(hdev);
+	hci_controller_data_remove(hdev, cp->data_type);
+	hci_dev_unlock(hdev);
+
+	return cmd_complete(sk, hdev->id, MGMT_OP_UNSET_CONTROLLER_DATA, 0,
+			    NULL, 0);
+}
+
 static const struct mgmt_handler {
 	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
 		     u16 data_len);
@@ -2764,6 +2779,7 @@ static const struct mgmt_handler {
 	{ unblock_device,         false, MGMT_UNBLOCK_DEVICE_SIZE },
 	{ set_device_id,          false, MGMT_SET_DEVICE_ID_SIZE },
 	{ set_controller_data,    true,  MGMT_SET_CONTROLLER_DATA_SIZE },
+	{ unset_controller_data,  false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
 };
 
 
-- 
1.7.11.4


^ permalink raw reply related

* [RFC 2/5] Bluetooth: Add set controller data MGMT command
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes
In-Reply-To: <1344632507-21381-1-git-send-email-jefferson.delfes@openbossa.org>

This command will be used to sets EIR data for BR/EDR and/or AD for LE.

Signed-off-by: Jefferson Delfes <jefferson.delfes@openbossa.org>
---
 include/net/bluetooth/hci_core.h | 14 ++++++++++++++
 include/net/bluetooth/mgmt.h     | 11 +++++++++++
 net/bluetooth/hci_core.c         | 32 ++++++++++++++++++++++++++++++++
 net/bluetooth/mgmt.c             | 33 +++++++++++++++++++++++++++++++++
 4 files changed, 90 insertions(+)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 41d9439..3a61227 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -122,6 +122,14 @@ struct le_scan_params {
 	int timeout;
 };
 
+struct controller_data {
+	struct list_head list;
+	u8 flags;
+	u8 data_type;
+	u8 data_length;
+	u8 data[0];
+};
+
 #define HCI_MAX_SHORT_NAME_LENGTH	10
 
 #define NUM_REASSEMBLY 4
@@ -269,6 +277,8 @@ struct hci_dev {
 	struct work_struct	le_scan;
 	struct le_scan_params	le_scan_params;
 
+	struct list_head	controller_data;
+
 	int (*open)(struct hci_dev *hdev);
 	int (*close)(struct hci_dev *hdev);
 	int (*flush)(struct hci_dev *hdev);
@@ -715,6 +725,10 @@ void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_add_sysfs(struct hci_conn *conn);
 void hci_conn_del_sysfs(struct hci_conn *conn);
 
+int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 data_type,
+			    u8 data_length, u8 *data);
+int hci_controller_data_clear(struct hci_dev *hdev);
+
 #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))
 
 /* ----- LMP capabilities ----- */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 4348ee8..1afa399 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -350,6 +350,17 @@ struct mgmt_cp_set_device_id {
 } __packed;
 #define MGMT_SET_DEVICE_ID_SIZE		8
 
+#define MGMT_DATA_HIGH_PRIORITY		BIT(0)
+
+#define MGMT_OP_SET_CONTROLLER_DATA	0x0029
+struct mgmt_cp_set_controller_data {
+	__u8	flags;
+	__u8	data_type;
+	__u8	data_length;
+	__u8	data[0];
+} __packed;
+#define MGMT_SET_CONTROLLER_DATA_SIZE	3
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16	opcode;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index fa974a1..2e38a1b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1441,6 +1441,36 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
 	return 0;
 }
 
+int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 data_type,
+			    u8 data_length, u8 *data)
+{
+	struct controller_data *c_data;
+
+	c_data = kmalloc(sizeof(*c_data) + data_length, GFP_KERNEL);
+	if (!c_data)
+		return -ENOMEM;
+
+	c_data->flags = flags;
+	c_data->data_type = data_type;
+	c_data->data_length = data_length;
+	memcpy(c_data->data, data, data_length);
+
+	list_add(&c_data->list, &hdev->controller_data);
+	return 0;
+}
+
+int hci_controller_data_clear(struct hci_dev *hdev)
+{
+	struct controller_data *c_data, *n;
+
+	list_for_each_entry_safe(c_data, n, &hdev->controller_data, list) {
+		list_del(&c_data->list);
+		kfree(c_data);
+	}
+
+	return 0;
+}
+
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
 	struct bdaddr_list *b;
@@ -1652,6 +1682,7 @@ struct hci_dev *hci_alloc_dev(void)
 	INIT_LIST_HEAD(&hdev->link_keys);
 	INIT_LIST_HEAD(&hdev->long_term_keys);
 	INIT_LIST_HEAD(&hdev->remote_oob_data);
+	INIT_LIST_HEAD(&hdev->controller_data);
 
 	INIT_WORK(&hdev->rx_work, hci_rx_work);
 	INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -1817,6 +1848,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
 	hci_link_keys_clear(hdev);
 	hci_smp_ltks_clear(hdev);
 	hci_remote_oob_data_clear(hdev);
+	hci_controller_data_clear(hdev);
 	hci_dev_unlock(hdev);
 
 	hci_dev_put(hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a3329cb..9e9702a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2684,6 +2684,38 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 	return 0;
 }
 
+static int set_controller_data(struct sock *sk, struct hci_dev *hdev,
+			       void *data, u16 len)
+{
+	struct mgmt_cp_set_controller_data *cp = data;
+
+	BT_DBG("%s", hdev->name);
+
+	if (cp->flags > MGMT_DATA_HIGH_PRIORITY)
+		return cmd_status(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA,
+				  MGMT_STATUS_INVALID_PARAMS);
+
+	if (cp->data_type != EIR_SERVICE_DATA &&
+	    cp->data_type != EIR_MANUFACTURER_DATA)
+		return cmd_status(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA,
+				  MGMT_STATUS_INVALID_PARAMS);
+
+	if (cp->data_length > HCI_MAX_EIR_LENGTH - 2)
+		return cmd_status(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA,
+				  MGMT_STATUS_INVALID_PARAMS);
+
+	BT_DBG("flags:0x%02x type:0x%02x length:%i", cp->flags, cp->data_type,
+	       cp->data_length);
+
+	hci_dev_lock(hdev);
+	hci_controller_data_add(hdev, cp->flags, cp->data_type, cp->data_length,
+				cp->data);
+	hci_dev_unlock(hdev);
+
+	return cmd_complete(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA, 0, NULL,
+			    0);
+}
+
 static const struct mgmt_handler {
 	int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
 		     u16 data_len);
@@ -2731,6 +2763,7 @@ static const struct mgmt_handler {
 	{ block_device,           false, MGMT_BLOCK_DEVICE_SIZE },
 	{ unblock_device,         false, MGMT_UNBLOCK_DEVICE_SIZE },
 	{ set_device_id,          false, MGMT_SET_DEVICE_ID_SIZE },
+	{ set_controller_data,    true,  MGMT_SET_CONTROLLER_DATA_SIZE },
 };
 
 
-- 
1.7.11.4


^ permalink raw reply related

* [RFC 1/5] Bluetooth: Add new commands HCI for LE and BR/EDR
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes
In-Reply-To: <1344632507-21381-1-git-send-email-jefferson.delfes@openbossa.org>

Add some commands to control Advertising data.

Signed-off-by: Jefferson Delfes <jefferson.delfes@openbossa.org>
---
 include/net/bluetooth/hci.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 23cf413..e4d4717 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -326,6 +326,8 @@ enum {
 #define EIR_SSP_HASH_C		0x0E /* Simple Pairing Hash C */
 #define EIR_SSP_RAND_R		0x0F /* Simple Pairing Randomizer R */
 #define EIR_DEVICE_ID		0x10 /* device ID */
+#define EIR_SERVICE_DATA	0x16 /* Service Data */
+#define EIR_MANUFACTURER_DATA	0xFF /* Manufacturer Specific Data */
 
 /* -----  HCI Commands ---- */
 #define HCI_OP_NOP			0x0000
@@ -891,6 +893,32 @@ struct hci_rp_le_read_buffer_size {
 	__u8     le_max_pkt;
 } __packed;
 
+#define ADV_USE_ALL_CHANNELS	0x07
+
+#define HCI_OP_LE_SET_ADV_PARAMS	0x2006
+struct hci_cp_le_set_adv_params {
+	__le16   interval_min;
+	__le16   interval_max;
+	__u8     type;
+	__u8     own_address_type;
+	__u8     direct_address_type;
+	__u8     direct_address[6];
+	__u8     channel_map;
+	__u8     filter_policy;
+} __packed;
+
+#define HCI_MAX_ADV_LENGTH		31
+
+#define HCI_OP_LE_SET_ADV_DATA		0x2008
+struct hci_cp_le_set_adv_data {
+	__u8     data_len;
+	__u8     data[HCI_MAX_ADV_LENGTH];
+} __packed;
+
+#define HCI_OP_LE_SET_ADV_ENABLE	0x200a
+	#define ADVERTISING_DISABLE	0x00
+	#define ADVERTISING_ENABLE	0x01
+
 #define HCI_OP_LE_SET_SCAN_PARAM	0x200b
 struct hci_cp_le_set_scan_param {
 	__u8    type;
-- 
1.7.11.4


^ permalink raw reply related

* [RFC 0/5] Broadcaster/Observer MGMT API
From: Jefferson Delfes @ 2012-08-10 21:01 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Jefferson Delfes

After some discussions on BlueZ meeting, it was decided to have a MGMT API for
Broadcaster and Observer roles.

The normal flow for Broadcaster is set controller data for any data needed and
set broadcaster on. For now, kernel will try to pack as much data as possible
for Adv. In future, we may implement a rotation logic to send all data set by
userspace.
Use unset controller data with respective data_type to remove any data that
matches this data_type.
After adding or removing controller data, to update Adv data, you need to set
broadcaster off and after set broadcaster on, so adv. data can be updated on
the controller.

To start observer, just set observer to on. Any data will come from device
found event.

We have patches for userspace that will hit BlueZ ML next week.

Jefferson Delfes (5):
  Bluetooth: Add new commands HCI for LE and BR/EDR
  Bluetooth: Add set controller data MGMT command
  Bluetooth: Add unset controller data MGMT command
  Bluetooth: Add set broadcaster MGMT command
  Bluetooth: Add set observer MGMT command

 include/net/bluetooth/hci.h      |  30 +++++++
 include/net/bluetooth/hci_core.h |  15 ++++
 include/net/bluetooth/mgmt.h     |  23 ++++++
 net/bluetooth/hci_core.c         |  48 +++++++++++
 net/bluetooth/mgmt.c             | 167 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 283 insertions(+)

-- 
1.7.11.4


^ permalink raw reply

* Re: [RFCv2 12/20] Bluetooth: Choose connection based on capabilities
From: Andrei Emeltchenko @ 2012-08-10 12:50 UTC (permalink / raw)
  To: Gustavo Padovan, linux-bluetooth
In-Reply-To: <20120724211038.GE20029@joana>

Hi Gustavo,

On Tue, Jul 24, 2012 at 06:10:38PM -0300, Gustavo Padovan wrote:
> > +void l2cap_discover_amp(struct l2cap_chan *chan)
> 
> prefix a function with l2cap_ outside of the l2cap code is not really a good
> idea, we need a better solution here..

I will rename this to a2mp_discover_amp

Best regards 
Andrei Emeltchenko 


^ permalink raw reply

* Re: [PATCH obexd] client: Fix pbap_select using absolute path with known locations
From: Luiz Augusto von Dentz @ 2012-08-10 11:28 UTC (permalink / raw)
  To: Ludek Finstrle; +Cc: linux-bluetooth
In-Reply-To: <1344528453-27245-1-git-send-email-luf@pzkagis.cz>

Hi Ludek,

On Thu, Aug 9, 2012 at 7:07 PM, Ludek Finstrle <luf@pzkagis.cz> wrote:
> pbap_select has to use absolute path with known location to support
> repeatable pbap_select calls. In other way the second call fails.
> ---
>  client/pbap.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/client/pbap.c b/client/pbap.c
> index 48dbac1..d8c39e5 100644
> --- a/client/pbap.c
> +++ b/client/pbap.c
> @@ -232,14 +232,14 @@ static gchar *build_phonebook_path(const char *location, const char *item)
>
>         if (!g_ascii_strcasecmp(location, "INT") ||
>                         !g_ascii_strcasecmp(location, "INTERNAL"))
> -               path = g_strdup("telecom");
> +               path = g_strdup("/telecom");
>         else if (!g_ascii_strncasecmp(location, "SIM", 3)) {
>                 if (strlen(location) == 3)
>                         tmp = g_strdup("SIM1");
>                 else
>                         tmp = g_ascii_strup(location, 4);
>
> -               path = g_build_filename(tmp, "telecom", NULL);
> +               path = g_build_filename("/", tmp, "telecom", NULL);
>                 g_free(tmp);
>         } else
>                 return NULL;
> --
> 1.7.1
>

Applied, thanks.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [PATCH v17 01/15] doc: Add telephony interface documents
From: Frederic Danis @ 2012-08-10 10:09 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Marcel Holtmann, linux-bluetooth
In-Reply-To: <CABBYNZ+gQEC8iUMwUXO25TSXYFsktAjS083hXXH09Ec3DnDFzA@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2931 bytes --]

Hello Luiz,

On 09/08/2012 16:42, Luiz Augusto von Dentz wrote:
> Hi Frederic,
>
> On Thu, Aug 9, 2012 at 1:03 PM, Frederic Danis
> <frederic.danis@linux.intel.com> wrote:
>>> They are mostly independent, oFono will never really acquire or
>>> anything like that, but there are some AT commands (+VGS,+VGM) that
>>> does notify PA about volume gain changes and as I said above it could
>>> be useful to notify about wideband speech in the same way.
>>>
>>
>> It is also possible that telephony agent implements a TelephonyClient
>> interface for each connection, object which should be returned by
>> NewConnection method.
>
> You should return the properties as well to avoid another round trip
> to get the properties of the client. Btw this requires yet another
> object and interface that increases the complexity of the solution.

OK, I add the client properties to return of NewConnection.

I agree with you this increase complexity, but as media transport is 
linked to the codec it uses I do not find another solution.

>> In this case BlueZ will listen to properties changes on this interface (like
>> for remote volume change) or call SetProperty (when receiving volume change
>> from PulseAudio).
>>
>> As MediaTransport may change during wideband speech HFP session, due to
>> codec re-negotiation, this architecture may simplify media transport code.
>
> I don't think we need to do the codec negotiation on acquire, it
> actually doesn't work since the transport cannot be reconfigured with
> another codec as by design the endpoints can only have 1 codec, so I
> suggest having the list of available codecs be given upfront in
> NewConnection then you actually negotiate the codec before responding.

Yes , I pass them in NewConnection method.

> If the remote device attempts to change the codec oFono then can check
> if the codec is available in the list of available codecs given on
> NewConnection, if it find a match then it accepts and emit a signal of
> codec changes that triggers a new transport to be configured.

In HFP 1.6 specs (chapter 4.11.3, page 32) I saw that after codec 
negotiation "the AG shall open the synchronous connection". This may 
imply that we will need to do it when PA tries to use HFP audio connection.

> Btw, Im not sure if this is really productive to discuss before we
> even have this working with 1.5, IMO is easier to do things step by
> step and the first step should be to get HFP 1.5 working as the
> current upstream does then we think about 1.6 and other profiles such
> as DUN and SAP.

I agree with you that most important point is to get a working HFP 1.5, 
but moving to HFP 1.6 may break the telephony interface if we do not 
take a look at it now.

Find attached new version of documents proposal.

Regards

Fred

-- 
Frederic Danis                            Open Source Technology Center
frederic.danis@intel.com                              Intel Corporation


[-- Attachment #2: audio-telephony-design-new.txt --]
[-- Type: text/plain, Size: 14300 bytes --]

Telephony Interface Design
**************************

Introduction
============

The aim of this document is to briefly describe the telephony interface which
will allow external application to implement telephony related profiles
(headset, handsfree, dial-up networking and sim access).


The goal
========

Previous version of headset code in BlueZ needs the implementation of an AT
parser for each modem target or external telephony application (Maemo, oFono)
which is not the aim of Bluez.

The telephony interface allows BlueZ to focus on Bluetooth communication part
(connection, disconnection, authentication, authorization) and let external
application (i.e. oFono) take charge of the Telephony tasks (AT parsing and
modem specific code).
This will allow code to be simpler, easier to maintain and debug in both BlueZ
and telephony application.


Design
======

External applications, which should implement AT parsing and telephony part
will have to register an org.bluez.TelephonyAgent using this new interface.
This will setup a SDP record for the profile and a RFCOMM server listening for
incoming connection.

When a new device is connected, NewConnection method of TelephonyAgent is
called. The telephony agent must reply with a TelephonyClient object after
proper communication establishment (after SLC setup completes for HFP, or
directly for other profiles).

For Headset and Handsfree profiles, the interaction with the audio component
(i.e. PulseAudio) will be done by listening to TelephonyClient properties
changes.


Flow charts
===========

Here is some flowcharts of interactions between BlueZ, telephony agent (oFono)
and audio component (PulseAudio):

        .....>  Bluetooth communication between headset and phone
        ----->  Dbus messages and signals

Outgoing SCO connection - HFP <= 1.5
------------------------------------

When PulseAudio needs to setup the audio connection it will call media
transport acquire method. This will perform a SCO connection and return the SCO
socket file descriptor to PulseAUdio.

	PulseAudio              BlueZ           HF/AG
	|                         |               |
	|    transport acquire    |               |
	|------------------------>|               |
	|                         |  connect SCO  |
	|                         |..............>|
	|      return SCO fd      |               |
	|<------------------------|               |
	|                         |               |

Incoming SCO connection - HFP <= 1.5
------------------------------------

On an incoming SCO connection the profile will change to playing state.
On reception of this state change, PulseAudio will call media transport acquire
method to retrieve the SCO socket file descriptor.

	PulseAudio              BlueZ           HF/AG
	|                         |               |
	|                         |  connect SCO  |
	|                         |<..............|
	|  state changed signal   |               |
	|<------------------------|               |
	|                         |               |
	|    transport acquire    |               |
	|------------------------>|               |
	|                         |               |
	|      return SCO fd      |               |
	|<------------------------|               |
	|                         |               |

Codec negotiation - HFP AG - HFP v1.6
-------------------------------------------

On reception of HF available codecs command (AT+BAC), the gateway may start a
codec selection procedure which will end up by codec property update and setup
of the correct media transport.
When a media transport already exists and it uses a different codec, it should
be closed before correct one is setup. 

	PulseAudio          BlueZ            oFono           HF
	|                     |                |              |
	|                     |                | AT+BAC=u1,u2 |
	|                     |                |<.............|
	|                     |                |              |
	|                     |                |      OK      |
	|                     |                |.............>|
	|                     |                |              |
	|                     |                |   +BCS:id    |
	|                     |                |.............>|
	|                     |                |              |
	|                     |                |  AT+BCS=id   |
	|                     |                |<.............|
	|                     |                |              |
	|                     |                |      OK      |
	|                     |                |.............>|
	|                     | codec property |              |
	|                     | changed signal |              |
	|                     |<---------------|              |
	| configure Transport |                |              |
	|<--------------------|                |              |
	|                     |                |              |

It may also ne possible to force a codec selection procedure by calling "get
audio codec" procedure of TelephonyClient.

	PulseAudio          BlueZ             oFono          HF
	|                     |                 |             |
	|                     |    get codec    |             |
	|                     |---------------->|             |
	|                     |                 |   +BCS:id   |
	|                     |                 |............>|
	|                     |                 |             |
	|                     |                 |  AT+BCS=id  |
	|                     |                 |<............|
	|                     |                 |             |
	|                     |                 |      OK     |
	|                     |                 |............>|
	|                     | codec property  |             |
	|                     | changed signal  |             |
	|                     |<----------------|             |
	| configure Transport |                 |             |
	|<--------------------|                 |             |
	|                     |                 |             |

Outgoing SCO connection - HFP AG - HFP v1.6
-------------------------------------------

Idem than for HFP v1.5

Incoming SCO connection - HFP AG - HFP v1.6
-------------------------------------------

It is pretty the same here as for outgoing SCO connection, except that it is
started upon reception of AT+BCC from the headset.

	PulseAudio           BlueZ              oFono         HF
	|                      |                  |            |
	|                      |                  |   AT+BCC   |
	|                      |                  |<...........|
	|                      |                  |            |
	|                      |                  |     OK     |
	|                      |                  |...........>|
	|                      |    connection    |            |
	|                      | requested signal |            |
	|                      |<-----------------|            |
	|                      |                  |            |
	|                      |          connect SCO          |
	|                      |..............................>|
	| state changed signal |                  |            |
	|<---------------------|                  |            |
	|                      |                  |            |
	|  transport acquire   |                  |            |
	|--------------------->|                  |            |
	|                      |                  |            |
	|    return SCO fd     |                  |            |
	|<---------------------|                  |            |


Codec negotiation - HFP HF - HFP v1.6
-------------------------------------------

Codec selection procedure started by gateway will end up by codec property
update and setup of the correct media transport.
When a media transport already exists and it uses a different codec, it should
be closed before correct one is setup. 

	PulseAudio          BlueZ            oFono           HF
	|                     |                |              |
	|                     |                |   +BCS:id    |
	|                     |                |<.............|
	|                     |                |              |
	|                     |                |  AT+BCS=id   |
	|                     |                |.............>|
	|                     |                |              |
	|                     |                |      OK      |
	|                     |                |<.............|
	|                     | codec property |              |
	|                     | changed signal |              |
	|                     |<---------------|              |
	| configure Transport |                |              |
	|<--------------------|                |              |
	|                     |                |              |

Outgoing SCO connection - HFP HF - HFP v1.6
-------------------------------------------

On media transport acquire, the TelephonyClient is called to request connection
from the gateway.
Then incoming SCO socket file descriptor will be returned to PulseAudio.

	PulseAudio             BlueZ             oFono        AG
	|                        |                 |           |
	|   transport acquire    |                 |           |
	|----------------------->|                 |           |
	|                        |    request      |           |
	|                        |   connection    |           |
	|                        |---------------->|           |
	|                        |                 |  AT+BCC   |
	|                        |                 |..........>|
	|                        |                 |           |
	|                        |                 |     OK    |
	|                        |                 |<..........|
	|                        |                 |           |
	|                        |          connect SCO        |
	|                        |<............................|
	|      return SCO fd     |                 |           |
	|<-----------------------|                 |           |
	|                        |                 |           |

Incoming SCO connection - HFP HF - HFP v1.6
-------------------------------------------

Idem than for HFP v1.5

AT+NREC - HFP AG
----------------

Reception of AT+NREC will be signaled to Bluez by TelephonyClient.
This will update the NREC property of media transport interface (listened by
PulseAudio).

	HF          oFono            BlueZ         PulseAudio
	|   AT+NREC   |                |                |
	|............>|                |                |
	|             |    property    |                |
	|             | changed signal |                |
	|             |--------------->|                |
	|     OK      |                |    property    |
	|<............|                | changed signal |
	|             |                |--------------->|
	|             |                |                |

+BSIR - HFP AG
--------------

PulseAudio can change in-band ring tone by calling SetProperty method of media
transport interface.
This will call SetProperty of TelephonyClient interface, which will send the
proper +BSIR unsollicited event.

	HF          oFono            BlueZ         PulseAudio        app
	|             |                |                |             |
	|             |                |                |<------------|
	|             |                |  SetProperty   |             |
	|             |                |<---------------|             |
	|             |  SetProperty   |                |             |
	|             |<---------------|                |             |
	|   +BSIR:x   |                |                |             |
	|<............|                |                |             |
	|             |    property    |                |             |
	|             | changed signal |                |             |
	|             |--------------->|                |             |
	|             |                |                |             |

AT+VGS,AT+VGM - HFP AG
----------------------

Reception of volume management command will be signaled to Bluez by
TelephonyClient.
This will update the corresponding volume property of media transport interface
(listened by PulseAudio).

	HF          oFono            BlueZ         PulseAudio        app
	|             |                |                |             |
	|  AT+VGS=xx  |                |                |             |
	|............>|                |                |             |
	|             |    property    |                |             |
	|             | changed signal |                |             |
	|             |--------------->|                |             |
	|     OK      |                |                |             |
	|<............|                |    property    |             |
	|             |                | changed signal |             |
	|             |                |--------------->|             |
	|             |                |                |------------>|
	|             |                |                |             |

+VGS,+VGM - HFP AG
------------------

PulseAudio can change volume by calling SetProperty method of media transport
interface.
This will call SetProperty of TelephonyClient interface, which will send the
proper +VGx unsollicited event.

	HF          oFono            BlueZ         PulseAudio        app
	|             |                |                |             |
	|             |                |                |<------------|
	|             |                |  SetProperty   |             |
	|             |                |<---------------|             |
	|             |  SetProperty   |                |             |
	|             |<---------------|                |             |
	|   +VGS:xx   |                |    property    |             |
	|<............|                | changed signal |             |
	|             |    property    |--------------->|             |
	|             | changed signal |                |------------>|
	|             |--------------->|                |             |
	|             |                |                |             |

[-- Attachment #3: audio-api-new.txt --]
[-- Type: text/plain, Size: 4413 bytes --]

Telephony hierarchy
===================

Service		org.bluez
Interface	org.bluez.Telephony
Object path	[variable prefix]/{hci0,hci1,...}

Methods		void RegisterAgent(object path, dict properties)

			Register a TelephonyAgent to sender, the sender can
			register as many agents as it likes.
			Object path should be unique for an agent and a UUID.

			Note: If the sender disconnects its agents are
			automatically unregistered.

			possible properties:

				string UUID:

					UUID of the profile which the agent is
					for.

				uint16 Version:

					Version of the profile which the agent
					implements.

				uint16 Features:

					Agent supported features as defined in
					profile spec e.g. HFP.

			Possible Errors: org.bluez.Error.InvalidArguments

		void UnregisterAgent(object path)

			Unregister sender agent.

		dict GetProperties()

			Returns all properties for the interface. See the
			properties section for available properties.

			Possible Errors: org.bluez.Error.InvalidArguments

		void SetProperty(string name, variant value)

			Changes the value of the specified property. Only
			properties that are listed as read-write are changeable.
			On success this will emit a PropertyChanged signal.

			Possible Errors: org.bluez.Error.DoesNotExist
					 org.bluez.Error.InvalidArguments

Signals		void PropertyChanged(string name, variant value)

			This signal indicates a changed value of the given
			property.

Properties	boolean FastConnectable  [readwrite]

			Indicates if there is adapter in fast connectable mode.

TelephonyAgent hierarchy
========================

Service		unique name
Interface	org.bluez.TelephonyAgent
Object path	freely definable

Methods		object, dict properties NewConnection(filedescriptor fd,
		                                      dict properties)

			Returns a TelephonyClient object for the connection
			with its properties.

			This method gets called whenever a new connection
			has been established. This method assumes that D-Bus
			daemon with file descriptor passing capability is
			being used.

			The agent should only return successfully once the
			establishment of the service level connection (SLC)
			has been completed.  In the case of Handsfree this
			means that BRSF exchange has been performed and
			necessary initialization has been done.

			possible properties:

				object Device:

					BlueZ remote device object.

				uint16 Version:

					Remote profile version.

				uint16 Features:

					Optional. Remote profile features.

				string Codecs:

					Optional. List of supported audio
					codec ids separeted by a comma.

			Possible Errors: org.bluez.Error.InvalidArguments
					 org.bluez.Error.Failed

		void Release()

			This method gets called whenever the service daemon
			unregisters the agent or whenever the Adapter where
			the TelephonyAgent registers itself is removed.

TelephonyClient hierarchy
========================

Service		unique name
Interface	org.bluez.TelephonyClient
Object path	freely definable

Methods		dict GetProperties()

			Returns all properties for the interface. See the
			properties section for available properties.

			Possible Errors: org.bluez.Error.InvalidArguments

		void SetProperty(string name, variant value)

			Changes the value of the specified property. Only
			properties that are listed as read-write are changeable.
			On success this will emit a PropertyChanged signal.

			Possible Errors: org.bluez.Error.DoesNotExist
					 org.bluez.Error.InvalidArguments

		byte GetAudioCodec()

			Returns the codec to use for upcoming audio connection.
			This may start a new codec negotiation if needed.

Signals		void PropertyChanged(string name, variant value)

			This signal indicates a changed value of the given
			property.

		void AudioConnectionRequested()

			This signal indicates that remote has requested an audio
			connection.

Properties	byte	AudioCodec [readonly]

			Optional. Indicates the currently selected audio codec.

		boolean NREC [readwrite]

			Optional. Indicates if echo cancelling and noise
			reduction functions are active.

		boolean InbandRingtone [readwrite]

			Optional. Indicates if sending ringtones is supported.

		uint16 OutputGain  [readwrite]

			Optional. The speaker gain when available.

			Possible values: 0-15

		uint16 InputGain  [readwrite]

			Optional. The microphone gain when available.

			Possible values: 0-15

^ permalink raw reply

* [PATCH BlueZ V5 5/5] AVRCP: Register/Unregister Browsing handler
From: Vani-dineshbhai PATEL @ 2012-08-10  9:36 UTC (permalink / raw)
  To: User Name, Luiz Augusto; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Add functions to register and unregister Browsing
handler
---
 audio/avctp.c |   51 +++++++++++++++++++++++++++++++++++++-
 audio/avctp.h |    8 ++++++
 audio/avrcp.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index a159498..9b2bec6 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -157,6 +157,11 @@ struct avctp_pdu_handler {
 	unsigned int id;
 };
 
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+};
+
 static struct {
 	const char *name;
 	uint8_t avc;
@@ -176,6 +181,7 @@ static GSList *callbacks = NULL;
 static GSList *servers = NULL;
 static GSList *control_handlers = NULL;
 static uint8_t id = 0;
+static struct avctp_browsing_pdu_handler *browsing_handler = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
 
@@ -452,9 +458,9 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 				gpointer data)
 {
 	struct avctp *session = data;
-	uint8_t  *buf;
+	uint8_t *operands, *buf;
 	struct avctp_header *avctp;
-	int ret, sock;
+	int ret, packet_size, operand_count, sock;
 
 	buf = gnew(uint8_t, session->browsing_mtu);
 
@@ -467,6 +473,8 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 	if (ret <= 0)
 		goto failed;
 
+	DBG("Got %d bytes of data for AVCTP Browsing session %p", ret, session);
+
 	if ((unsigned int) ret < sizeof(struct avctp_header)) {
 		error("Too small AVRCP packet on browsing channel");
 		goto failed;
@@ -474,9 +482,30 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 
 	avctp = (struct avctp_header *) buf;
 
+	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
+			"PID 0x%04X",
+			avctp->transaction, avctp->packet_type,
+			avctp->cr, avctp->ipid, ntohs(avctp->pid));
+
 	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
 		goto failed;
 
+	operands = buf + AVCTP_HEADER_LENGTH;
+	ret -= AVCTP_HEADER_LENGTH;
+	operand_count = ret;
+
+	packet_size = AVCTP_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+	if (browsing_handler)
+		packet_size += browsing_handler->cb(session, avctp->transaction,
+			operands, operand_count, browsing_handler->user_data);
+
+	if (packet_size != 0) {
+		ret = write(sock, buf, packet_size);
+		if (ret != packet_size)
+			goto failed;
+	}
+
 	gfree(buf);
 	return TRUE;
 
@@ -1252,6 +1281,18 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 	return handler->id;
 }
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data)
+{
+	unsigned int id = 0;
+
+	browsing_handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	browsing_handler->cb = cb;
+	browsing_handler->user_data = user_data;
+
+	return ++id;
+}
+
 gboolean avctp_unregister_pdu_handler(unsigned int id)
 {
 	GSList *l;
@@ -1270,6 +1311,12 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
 	return FALSE;
 }
 
+gboolean avctp_unregister_browsing_pdu_handler()
+{
+	g_free(browsing_handler);
+	return TRUE;
+}
+
 struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 {
 	struct avctp *session;
diff --git a/audio/avctp.h b/audio/avctp.h
index b80e300..3e1dabe 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -83,6 +83,10 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
+typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 
 unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
 gboolean avctp_remove_state_cb(unsigned int id);
@@ -98,6 +102,10 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data);
 gboolean avctp_unregister_pdu_handler(unsigned int id);
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data);
+
+gboolean avctp_unregister_browsing_pdu_handler();
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
 				uint8_t code, uint8_t subunit,
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 9d29073..fa06f85 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -94,6 +94,10 @@
 #define AVRCP_ABORT_CONTINUING		0x41
 #define AVRCP_SET_ABSOLUTE_VOLUME	0x50
 
+#define AVRCP_INVALID_BROWSING_PDU	0x00
+
+#define AVRCP_GENERAL_REJECT			0xA0
+
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
 #define CAP_EVENTS_SUPPORTED	0x03
@@ -140,6 +144,12 @@ struct avrcp_header {
 #error "Unknown byte order"
 #endif
 
+struct avrcp_browsing_header {
+	uint8_t browsing_pdu;
+	uint16_t param_len;
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
 #define AVRCP_MTU	(AVC_MTU - AVC_HEADER_LENGTH)
 #define AVRCP_PDU_MTU	(AVRCP_MTU - AVRCP_HEADER_LENGTH)
 
@@ -163,7 +173,9 @@ struct avrcp_player {
 	struct audio_device *dev;
 
 	unsigned int control_handler;
+	unsigned int browsing_handler;
 	uint16_t registered_events;
+	uint8_t transaction;
 	uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
 	struct pending_pdu *pending_pdu;
 
@@ -1075,6 +1087,15 @@ static struct control_pdu_handler {
 		{ },
 };
 
+static struct pdu_browsing_handler {
+	uint8_t browsing_pdu;
+	void (*func) (struct avrcp_player *player,
+					struct avrcp_browsing_header *pdu);
+	} browsing_handlers[] = {
+		{ AVRCP_INVALID_BROWSING_PDU,
+					NULL },
+};
+
 /* handle vendordep pdu inside an avctp packet */
 static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
 					uint8_t *code, uint8_t *subunit,
@@ -1134,6 +1155,49 @@ err_metadata:
 	return AVRCP_HEADER_LENGTH + 1;
 }
 
+static size_t handle_browsing_pdu(struct avctp *session,
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct pdu_browsing_handler *b_handler;
+	struct avrcp_browsing_header *avrcp_browsing = (void *) operands;
+	uint8_t status;
+
+	operand_count += AVRCP_BROWSING_HEADER_LENGTH;
+
+	for (b_handler = browsing_handlers; b_handler; b_handler++) {
+		if (b_handler->browsing_pdu == AVRCP_INVALID_BROWSING_PDU) {
+			b_handler = NULL;
+			break;
+		}
+		if (b_handler->browsing_pdu == avrcp_browsing->browsing_pdu)
+			break;
+	}
+
+	if (!b_handler) {
+		avrcp_browsing->browsing_pdu = AVRCP_GENERAL_REJECT;
+		status = E_INVALID_COMMAND;
+		goto err;
+	}
+
+	if (!b_handler->func) {
+		status = E_INVALID_PARAM;
+		avrcp_browsing->param_len = htons(sizeof(status));
+		goto err;
+	}
+	player->transaction = transaction;
+	b_handler->func(player, avrcp_browsing);
+	return AVRCP_BROWSING_HEADER_LENGTH + ntohs(avrcp_browsing->param_len);
+
+err:
+	avrcp_browsing->param_len = htons(sizeof(status));
+	memcpy(&operands[AVRCP_BROWSING_HEADER_LENGTH], &status,
+							(sizeof(status)));
+	return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
+}
+
+
 size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
 {
 	struct avrcp_header *pdu = (void *) operands;
@@ -1233,6 +1297,10 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 			avctp_unregister_pdu_handler(player->control_handler);
 			player->control_handler = 0;
 		}
+		if (player->browsing_handler) {
+			avctp_unregister_browsing_pdu_handler();
+			player->browsing_handler = 0;
+		}
 
 		break;
 	case AVCTP_STATE_CONNECTING:
@@ -1244,6 +1312,12 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 							AVC_OP_VENDORDEP,
 							handle_vendordep_pdu,
 							player);
+		if (!player->browsing_handler)
+			player->browsing_handler =
+					avctp_register_browsing_pdu_handler(
+							handle_browsing_pdu,
+							player);
+
 		break;
 	case AVCTP_STATE_CONNECTED:
 		rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V5 4/5] AVRCP: Register/Unregister Browsing handler
From: Vani-dineshbhai PATEL @ 2012-08-10  9:35 UTC (permalink / raw)
  To: User Name, Luiz Augusto; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Add functions to register and unregister Browsing
handler
---
 audio/avctp.c |   24 ++++++++++++++++++++++++
 audio/avctp.h |    8 ++++++++
 2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index a159498..ed6efbe 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -157,6 +157,11 @@ struct avctp_pdu_handler {
 	unsigned int id;
 };
 
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+};
+
 static struct {
 	const char *name;
 	uint8_t avc;
@@ -176,6 +181,7 @@ static GSList *callbacks = NULL;
 static GSList *servers = NULL;
 static GSList *control_handlers = NULL;
 static uint8_t id = 0;
+static struct avctp_browsing_pdu_handler *browsing_handler = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
 
@@ -1252,6 +1258,18 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 	return handler->id;
 }
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data)
+{
+	unsigned int id = 0;
+
+	browsing_handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	browsing_handler->cb = cb;
+	browsing_handler->user_data = user_data;
+
+	return ++id;
+}
+
 gboolean avctp_unregister_pdu_handler(unsigned int id)
 {
 	GSList *l;
@@ -1270,6 +1288,12 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
 	return FALSE;
 }
 
+gboolean avctp_unregister_browsing_pdu_handler()
+{
+	g_free(browsing_handler);
+	return TRUE;
+}
+
 struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 {
 	struct avctp *session;
diff --git a/audio/avctp.h b/audio/avctp.h
index b80e300..3e1dabe 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -83,6 +83,10 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
+typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 
 unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
 gboolean avctp_remove_state_cb(unsigned int id);
@@ -98,6 +102,10 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data);
 gboolean avctp_unregister_pdu_handler(unsigned int id);
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data);
+
+gboolean avctp_unregister_browsing_pdu_handler();
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
 				uint8_t code, uint8_t subunit,
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V5 3/5] AVRCP: Add browsing channel support
From: Vani-dineshbhai PATEL @ 2012-08-10  9:35 UTC (permalink / raw)
  To: User Name, Luiz Augusto; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Implements browsing channel creation and release.
---
 audio/avctp.c |  235 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 216 insertions(+), 19 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index 3bd021c..a159498 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -41,6 +41,7 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sdp.h>
 #include <bluetooth/uuid.h>
+#include <bluetooth/l2cap.h>
 
 #include <glib.h>
 
@@ -119,6 +120,7 @@ struct avctp_state_callback {
 struct avctp_server {
 	bdaddr_t src;
 	GIOChannel *control_io;
+	GIOChannel *browsing_io;
 	GSList *sessions;
 };
 
@@ -137,9 +139,12 @@ struct avctp {
 	int uinput;
 
 	GIOChannel *control_io;
+	GIOChannel *browsing_io;
 	guint control_io_id;
+	guint browsing_io_id;
 
 	uint16_t control_mtu;
+	uint16_t browsing_mtu;
 
 	uint8_t key_quirks[256];
 	GSList *handlers;
@@ -326,6 +331,17 @@ static void avctp_disconnected(struct avctp *session)
 	if (!session)
 		return;
 
+	if (session->browsing_io) {
+		g_io_channel_shutdown(session->browsing_io, TRUE, NULL);
+		g_io_channel_unref(session->browsing_io);
+		session->browsing_io = NULL;
+	}
+
+	if (session->browsing_io_id) {
+		g_source_remove(session->browsing_io_id);
+		session->browsing_io_id = 0;
+	}
+
 	if (session->control_io) {
 		g_io_channel_shutdown(session->control_io, TRUE, NULL);
 		g_io_channel_unref(session->control_io);
@@ -432,6 +448,43 @@ static void handle_response(struct avctp *session, struct avctp_header *avctp,
 	}
 }
 
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	uint8_t  *buf;
+	struct avctp_header *avctp;
+	int ret, sock;
+
+	buf = gnew(uint8_t, session->browsing_mtu);
+
+	if (!(cond & G_IO_IN))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(session->browsing_io);
+	ret = read(sock, buf, session->browsing_mtu);
+
+	if (ret <= 0)
+		goto failed;
+
+	if ((unsigned int) ret < sizeof(struct avctp_header)) {
+		error("Too small AVRCP packet on browsing channel");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
+		goto failed;
+
+	gfree(buf);
+	return TRUE;
+
+failed:
+	gfree(buf);
+	return FALSE;
+}
+
 static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 				gpointer data)
 {
@@ -613,6 +666,47 @@ static void init_uinput(struct avctp *session)
 		DBG("AVRCP: uinput initialized for %s", address);
 }
 
+static void avctp_connect_browsing_cb(GIOChannel *chan,
+					GError *err,
+					gpointer data)
+{
+	struct avctp *session = data;
+	char address[18];
+	uint16_t imtu;
+	GError *gerr = NULL;
+
+	if (err) {
+		error("Browsing: %s", err->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		session->browsing_io = NULL;
+		return;
+	}
+
+	bt_io_get(chan, BT_IO_L2CAP, &gerr,
+			BT_IO_OPT_DEST, &address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		session->browsing_io = NULL;
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	if (!session->browsing_io)
+		session->browsing_io = g_io_channel_ref(chan);
+
+	session->browsing_mtu = imtu;
+
+	session->browsing_io_id = g_io_add_watch(chan,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) session_browsing_cb, session);
+}
+
 static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
 	struct avctp *session = data;
@@ -651,6 +745,32 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 				(GIOFunc) session_cb, session);
 }
 
+static void auth_browsing_cb(DBusError *derr, void *user_data)
+{
+	struct avctp *session = user_data;
+	GError *err = NULL;
+
+	if (session->browsing_io_id) {
+		g_source_remove(session->browsing_io_id);
+		session->browsing_io_id = 0;
+	}
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Browsing Access denied: %s", derr->message);
+		return;
+	}
+
+	if (!bt_io_accept(session->browsing_io, avctp_connect_browsing_cb,
+						session, NULL, &err)) {
+		error("Browsing bt_io_accept: %s", err->message);
+		if (session && session->browsing_io) {
+			g_io_channel_unref(session->browsing_io);
+			session->browsing_io = NULL;
+		}
+		g_error_free(err);
+	}
+}
+
 static void auth_cb(DBusError *derr, void *user_data)
 {
 	struct avctp *session = user_data;
@@ -729,6 +849,65 @@ static struct avctp *avctp_get_internal(const bdaddr_t *src,
 	return session;
 }
 
+static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
+						struct audio_device *dev)
+{
+	if (session->control_io) {
+		error("Refusing unexpected connect from");
+		goto drop;
+	}
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING);
+	session->control_io = g_io_channel_ref(chan);
+
+	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+						auth_cb, session) < 0)
+		goto drop;
+
+	session->control_io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
+						G_IO_NVAL, session_cb, session);
+	return;
+
+drop:
+	if (!session || !session->control_io)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (session && session->control_io)
+		g_io_channel_unref(session->control_io);
+
+	if (session)
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
+
+static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
+						struct audio_device *dev)
+{
+
+	if (!session->control_io) {
+		error("Browsing: Refusing unexpected connect from");
+		goto drop;
+	}
+
+	if (session->browsing_io) {
+		error("Browsing channel already exists");
+		goto drop;
+	}
+
+	session->browsing_io = g_io_channel_ref(chan);
+
+	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+						auth_browsing_cb, session) < 0)
+		goto drop;
+	return;
+
+drop:
+	if (!session || !session->control_io || !session->browsing_io)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (session && session->browsing_io)
+		g_io_channel_unref(session->browsing_io);
+}
+
 static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 {
 	struct avctp *session;
@@ -736,11 +915,13 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 	char address[18];
 	bdaddr_t src, dst;
 	GError *err = NULL;
+	uint16_t psm;
 
 	bt_io_get(chan, BT_IO_L2CAP, &err,
 			BT_IO_OPT_SOURCE_BDADDR, &src,
 			BT_IO_OPT_DEST_BDADDR, &dst,
 			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_PSM, &psm,
 			BT_IO_OPT_INVALID);
 	if (err) {
 		error("%s", err->message);
@@ -771,40 +952,40 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 			goto drop;
 	}
 
-	if (session->control_io) {
-		error("Refusing unexpected connect from %s", address);
-		goto drop;
+	switch (psm) {
+	case AVCTP_CONTROL_PSM:
+		avctp_control_confirm(session, chan, dev);
+		break;
+	case AVCTP_BROWSING_PSM:
+		avctp_browsing_confirm(session, chan, dev);
+		break;
 	}
 
-	avctp_set_state(session, AVCTP_STATE_CONNECTING);
-	session->control_io = g_io_channel_ref(chan);
-
-	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
-						auth_cb, session) < 0)
-		goto drop;
-
-	session->control_io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
-						G_IO_NVAL, session_cb, session);
 	return;
 
 drop:
-	if (!session || !session->control_io)
-		g_io_channel_shutdown(chan, TRUE, NULL);
-	if (session)
+	if (session && session->browsing_io)
+		g_io_channel_unref(session->browsing_io);
+
+	if (session && session->control_io)
+		g_io_channel_unref(session->control_io);
+
+	if (session && psm == AVCTP_CONTROL_PSM)
 		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
 }
 
-static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master,
+						uint8_t mode, uint16_t psm)
 {
 	GError *err = NULL;
 	GIOChannel *io;
-
 	io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
 				NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, src,
-				BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
+				BT_IO_OPT_PSM, psm,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
 				BT_IO_OPT_MASTER, master,
+				BT_IO_OPT_MODE, mode,
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("%s", err->message);
@@ -824,12 +1005,25 @@ int avctp_register(const bdaddr_t *src, gboolean master)
 
 	server = g_new0(struct avctp_server, 1);
 
-	server->control_io = avctp_server_socket(src, master);
+	server->control_io = avctp_server_socket(src, master, L2CAP_MODE_BASIC,
+							AVCTP_CONTROL_PSM);
 	if (!server->control_io) {
 		g_free(server);
 		return -1;
 	}
 
+	server->browsing_io = avctp_server_socket(src, master, L2CAP_MODE_ERTM,
+							AVCTP_BROWSING_PSM);
+	if (!server->browsing_io) {
+		if (server->control_io) {
+			g_io_channel_shutdown(server->control_io, TRUE, NULL);
+			g_io_channel_unref(server->control_io);
+			server->control_io = NULL;
+		}
+		g_free(server);
+		return -1;
+	}
+
 	bacpy(&server->src, src);
 
 	servers = g_slist_append(servers, server);
@@ -861,6 +1055,9 @@ void avctp_unregister(const bdaddr_t *src)
 		avctp_disconnected(server->sessions->data);
 
 	servers = g_slist_remove(servers, server);
+	g_io_channel_shutdown(server->browsing_io, TRUE, NULL);
+	g_io_channel_unref(server->browsing_io);
+	server->browsing_io = NULL;
 
 	g_io_channel_shutdown(server->control_io, TRUE, NULL);
 	g_io_channel_unref(server->control_io);
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V5 2/5] AVRCP: Rename variables used for control channel
From: Vani-dineshbhai PATEL @ 2012-08-10  9:35 UTC (permalink / raw)
  To: User Name, Luiz Augusto; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Prefix "control" is added to variables used for
control channel. This will improve redability
when Browsing channel is implemented
---
 audio/avctp.c |   83 +++++++++++++++++++++++++++++----------------------------
 audio/avctp.h |    5 ++-
 audio/avrcp.c |   54 ++++++++++++++++++------------------
 3 files changed, 72 insertions(+), 70 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index a20dba9..3bd021c 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -118,7 +118,7 @@ struct avctp_state_callback {
 
 struct avctp_server {
 	bdaddr_t src;
-	GIOChannel *io;
+	GIOChannel *control_io;
 	GSList *sessions;
 };
 
@@ -136,10 +136,10 @@ struct avctp {
 
 	int uinput;
 
-	GIOChannel *io;
-	guint io_id;
+	GIOChannel *control_io;
+	guint control_io_id;
 
-	uint16_t mtu;
+	uint16_t control_mtu;
 
 	uint8_t key_quirks[256];
 	GSList *handlers;
@@ -147,7 +147,7 @@ struct avctp {
 
 struct avctp_pdu_handler {
 	uint8_t opcode;
-	avctp_pdu_cb cb;
+	avctp_control_pdu_cb cb;
 	void *user_data;
 	unsigned int id;
 };
@@ -169,7 +169,7 @@ static struct {
 
 static GSList *callbacks = NULL;
 static GSList *servers = NULL;
-static GSList *handlers = NULL;
+static GSList *control_handlers = NULL;
 static uint8_t id = 0;
 
 static void auth_cb(DBusError *derr, void *user_data);
@@ -326,15 +326,15 @@ static void avctp_disconnected(struct avctp *session)
 	if (!session)
 		return;
 
-	if (session->io) {
-		g_io_channel_shutdown(session->io, TRUE, NULL);
-		g_io_channel_unref(session->io);
-		session->io = NULL;
+	if (session->control_io) {
+		g_io_channel_shutdown(session->control_io, TRUE, NULL);
+		g_io_channel_unref(session->control_io);
+		session->control_io = NULL;
 	}
 
-	if (session->io_id) {
-		g_source_remove(session->io_id);
-		session->io_id = 0;
+	if (session->control_io_id) {
+		g_source_remove(session->control_io_id);
+		session->control_io_id = 0;
 
 		if (session->state == AVCTP_STATE_CONNECTING) {
 			struct audio_device *dev;
@@ -445,7 +445,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 	if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
 		goto failed;
 
-	sock = g_io_channel_unix_get_fd(session->io);
+	sock = g_io_channel_unix_get_fd(session->control_io);
 
 	ret = read(sock, buf, sizeof(buf));
 	if (ret <= 0)
@@ -503,7 +503,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 		goto done;
 	}
 
-	handler = find_handler(handlers, avc->opcode);
+	handler = find_handler(control_handlers, avc->opcode);
 	if (!handler) {
 		DBG("handler not found for 0x%02x", avc->opcode);
 		packet_size += avrcp_handle_vendor_reject(&code, operands);
@@ -639,14 +639,14 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 
 	DBG("AVCTP: connected to %s", address);
 
-	if (!session->io)
-		session->io = g_io_channel_ref(chan);
+	if (!session->control_io)
+		session->control_io = g_io_channel_ref(chan);
 
 	init_uinput(session);
 
 	avctp_set_state(session, AVCTP_STATE_CONNECTED);
-	session->mtu = imtu;
-	session->io_id = g_io_add_watch(chan,
+	session->control_mtu = imtu;
+	session->control_io_id = g_io_add_watch(chan,
 				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) session_cb, session);
 }
@@ -656,9 +656,9 @@ static void auth_cb(DBusError *derr, void *user_data)
 	struct avctp *session = user_data;
 	GError *err = NULL;
 
-	if (session->io_id) {
-		g_source_remove(session->io_id);
-		session->io_id = 0;
+	if (session->control_io_id) {
+		g_source_remove(session->control_io_id);
+		session->control_io_id = 0;
 	}
 
 	if (derr && dbus_error_is_set(derr)) {
@@ -667,7 +667,7 @@ static void auth_cb(DBusError *derr, void *user_data)
 		return;
 	}
 
-	if (!bt_io_accept(session->io, avctp_connect_cb, session,
+	if (!bt_io_accept(session->control_io, avctp_connect_cb, session,
 								NULL, &err)) {
 		error("bt_io_accept: %s", err->message);
 		g_error_free(err);
@@ -771,24 +771,24 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 			goto drop;
 	}
 
-	if (session->io) {
+	if (session->control_io) {
 		error("Refusing unexpected connect from %s", address);
 		goto drop;
 	}
 
 	avctp_set_state(session, AVCTP_STATE_CONNECTING);
-	session->io = g_io_channel_ref(chan);
+	session->control_io = g_io_channel_ref(chan);
 
 	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
 						auth_cb, session) < 0)
 		goto drop;
 
-	session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-							session_cb, session);
+	session->control_io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
+						G_IO_NVAL, session_cb, session);
 	return;
 
 drop:
-	if (!session || !session->io)
+	if (!session || !session->control_io)
 		g_io_channel_shutdown(chan, TRUE, NULL);
 	if (session)
 		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
@@ -824,8 +824,8 @@ int avctp_register(const bdaddr_t *src, gboolean master)
 
 	server = g_new0(struct avctp_server, 1);
 
-	server->io = avctp_server_socket(src, master);
-	if (!server->io) {
+	server->control_io = avctp_server_socket(src, master);
+	if (!server->control_io) {
 		g_free(server);
 		return -1;
 	}
@@ -862,8 +862,8 @@ void avctp_unregister(const bdaddr_t *src)
 
 	servers = g_slist_remove(servers, server);
 
-	g_io_channel_shutdown(server->io, TRUE, NULL);
-	g_io_channel_unref(server->io);
+	g_io_channel_shutdown(server->control_io, TRUE, NULL);
+	g_io_channel_unref(server->control_io);
 	g_free(server);
 
 	if (servers)
@@ -910,7 +910,7 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
 	operands[0] = op & 0x7f;
 	operands[1] = 0;
 
-	sk = g_io_channel_unix_get_fd(session->io);
+	sk = g_io_channel_unix_get_fd(session->control_io);
 
 	if (write(sk, buf, sizeof(buf)) < 0)
 		return -errno;
@@ -939,7 +939,7 @@ static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
 	if (session->state != AVCTP_STATE_CONNECTED)
 		return -ENOTCONN;
 
-	sk = g_io_channel_unix_get_fd(session->io);
+	sk = g_io_channel_unix_get_fd(session->control_io);
 
 	memset(buf, 0, sizeof(buf));
 
@@ -1034,13 +1034,13 @@ gboolean avctp_remove_state_cb(unsigned int id)
 	return FALSE;
 }
 
-unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data)
 {
 	struct avctp_pdu_handler *handler;
 	static unsigned int id = 0;
 
-	handler = find_handler(handlers, opcode);
+	handler = find_handler(control_handlers, opcode);
 	if (handler)
 		return 0;
 
@@ -1050,7 +1050,7 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
 	handler->user_data = user_data;
 	handler->id = ++id;
 
-	handlers = g_slist_append(handlers, handler);
+	control_handlers = g_slist_append(control_handlers, handler);
 
 	return handler->id;
 }
@@ -1059,11 +1059,12 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
 {
 	GSList *l;
 
-	for (l = handlers; l != NULL; l = l->next) {
+	for (l = control_handlers; l != NULL; l = l->next) {
 		struct avctp_pdu_handler *handler = l->data;
 
 		if (handler->id == id) {
-			handlers = g_slist_remove(handlers, handler);
+			control_handlers = g_slist_remove(control_handlers,
+								handler);
 			g_free(handler);
 			return TRUE;
 		}
@@ -1099,14 +1100,14 @@ struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 		return NULL;
 	}
 
-	session->io = io;
+	session->control_io = io;
 
 	return session;
 }
 
 void avctp_disconnect(struct avctp *session)
 {
-	if (!session->io)
+	if (!session->control_io)
 		return;
 
 	avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
diff --git a/audio/avctp.h b/audio/avctp.h
index 34b0c1c..b80e300 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -75,7 +75,8 @@ typedef void (*avctp_state_cb) (struct audio_device *dev,
 				avctp_state_t new_state,
 				void *user_data);
 
-typedef size_t (*avctp_pdu_cb) (struct avctp *session, uint8_t transaction,
+typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
 					uint8_t *code, uint8_t *subunit,
 					uint8_t *operands, size_t operand_count,
 					void *user_data);
@@ -93,7 +94,7 @@ struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst);
 struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst);
 void avctp_disconnect(struct avctp *session);
 
-unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data);
 gboolean avctp_unregister_pdu_handler(unsigned int id);
 
diff --git a/audio/avrcp.c b/audio/avrcp.c
index ca40c1e..9d29073 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -162,7 +162,7 @@ struct avrcp_player {
 	struct avctp *session;
 	struct audio_device *dev;
 
-	unsigned int handler;
+	unsigned int control_handler;
 	uint16_t registered_events;
 	uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
 	struct pending_pdu *pending_pdu;
@@ -255,9 +255,9 @@ static sdp_record_t *avrcp_tg_record(void)
 	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
 	uuid_t root_uuid, l2cap, avctp, avrtg;
 	sdp_profile_desc_t profile[1];
-	sdp_list_t *aproto, *proto[2];
+	sdp_list_t *aproto_control, *proto_control[2];
 	sdp_record_t *record;
-	sdp_data_t *psm, *version, *features, *psm_browsing;
+	sdp_data_t *psm_control, *version, *features, *psm_browsing;
 	sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
 	uint16_t lp = AVCTP_CONTROL_PSM;
 	uint16_t lp_browsing = AVCTP_BROWSING_PSM;
@@ -283,19 +283,19 @@ static sdp_record_t *avrcp_tg_record(void)
 
 	/* Protocol Descriptor List */
 	sdp_uuid16_create(&l2cap, L2CAP_UUID);
-	proto[0] = sdp_list_append(0, &l2cap);
-	psm = sdp_data_alloc(SDP_UINT16, &lp);
-	proto[0] = sdp_list_append(proto[0], psm);
-	apseq = sdp_list_append(0, proto[0]);
+	proto_control[0] = sdp_list_append(0, &l2cap);
+	psm_control = sdp_data_alloc(SDP_UINT16, &lp);
+	proto_control[0] = sdp_list_append(proto_control[0], psm_control);
+	apseq = sdp_list_append(0, proto_control[0]);
 
 	sdp_uuid16_create(&avctp, AVCTP_UUID);
-	proto[1] = sdp_list_append(0, &avctp);
+	proto_control[1] = sdp_list_append(0, &avctp);
 	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
-	proto[1] = sdp_list_append(proto[1], version);
-	apseq = sdp_list_append(apseq, proto[1]);
+	proto_control[1] = sdp_list_append(proto_control[1], version);
+	apseq = sdp_list_append(apseq, proto_control[1]);
 
-	aproto = sdp_list_append(0, apseq);
-	sdp_set_access_protos(record, aproto);
+	aproto_control = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto_control);
 	proto_browsing[0] = sdp_list_append(0, &l2cap);
 	psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing);
 	proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing);
@@ -323,12 +323,12 @@ static sdp_record_t *avrcp_tg_record(void)
 	sdp_list_free(proto_browsing[0], 0);
 	sdp_list_free(proto_browsing[1], 0);
 	sdp_list_free(aproto_browsing, 0);
-	free(psm);
+	free(psm_control);
 	free(version);
-	sdp_list_free(proto[0], 0);
-	sdp_list_free(proto[1], 0);
+	sdp_list_free(proto_control[0], 0);
+	sdp_list_free(proto_control[1], 0);
 	sdp_list_free(apseq, 0);
-	sdp_list_free(aproto, 0);
+	sdp_list_free(aproto_control, 0);
 	sdp_list_free(pfseq, 0);
 	sdp_list_free(root, 0);
 	sdp_list_free(svclass_id, 0);
@@ -1037,13 +1037,13 @@ err:
 	return AVC_CTYPE_REJECTED;
 }
 
-static struct pdu_handler {
+static struct control_pdu_handler {
 	uint8_t pdu_id;
 	uint8_t code;
 	uint8_t (*func) (struct avrcp_player *player,
 					struct avrcp_header *pdu,
 					uint8_t transaction);
-} handlers[] = {
+} control_handlers[] = {
 		{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
 					avrcp_handle_get_capabilities },
 		{ AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
@@ -1082,7 +1082,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
 					void *user_data)
 {
 	struct avrcp_player *player = user_data;
-	struct pdu_handler *handler;
+	struct control_pdu_handler *handler;
 	struct avrcp_header *pdu = (void *) operands;
 	uint32_t company_id = get_company_id(pdu->company_id);
 
@@ -1102,7 +1102,7 @@ static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
 		goto err_metadata;
 	}
 
-	for (handler = handlers; handler; handler++) {
+	for (handler = control_handlers; handler; handler++) {
 		if (handler->pdu_id == pdu->pdu_id)
 			break;
 	}
@@ -1229,9 +1229,9 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 		player->dev = NULL;
 		player->registered_events = 0;
 
-		if (player->handler) {
-			avctp_unregister_pdu_handler(player->handler);
-			player->handler = 0;
+		if (player->control_handler) {
+			avctp_unregister_pdu_handler(player->control_handler);
+			player->control_handler = 0;
 		}
 
 		break;
@@ -1239,8 +1239,8 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 		player->session = avctp_connect(&dev->src, &dev->dst);
 		player->dev = dev;
 
-		if (!player->handler)
-			player->handler = avctp_register_pdu_handler(
+		if (!player->control_handler)
+			player->control_handler = avctp_register_pdu_handler(
 							AVC_OP_VENDORDEP,
 							handle_vendordep_pdu,
 							player);
@@ -1358,8 +1358,8 @@ static void player_destroy(gpointer data)
 
 	player_abort_pending_pdu(player);
 
-	if (player->handler)
-		avctp_unregister_pdu_handler(player->handler);
+	if (player->control_handler)
+		avctp_unregister_pdu_handler(player->control_handler);
 
 	g_free(player);
 }
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V5 1/5] AVRCP: Add TG Record to support AVRCP Browsing
From: Vani-dineshbhai PATEL @ 2012-08-10  9:35 UTC (permalink / raw)
  To: User Name, Luiz Augusto; +Cc: Vani, Joohi, Vani
In-Reply-To: <yes>

From: Vani Patel <vani.patel@stericsson.com>

Adds SDP record to support browsing
---
 audio/avctp.c |    4 ++--
 audio/avctp.h |    3 ++-
 audio/avrcp.c |   25 +++++++++++++++++++++----
 3 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index 074eabd..a20dba9 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -802,7 +802,7 @@ static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
 	io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
 				NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, src,
-				BT_IO_OPT_PSM, AVCTP_PSM,
+				BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
 				BT_IO_OPT_MASTER, master,
 				BT_IO_OPT_INVALID);
@@ -1090,7 +1090,7 @@ struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 	io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, session, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
 				BT_IO_OPT_DEST_BDADDR, &session->dst,
-				BT_IO_OPT_PSM, AVCTP_PSM,
+				BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
 				BT_IO_OPT_INVALID);
 	if (err) {
 		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
diff --git a/audio/avctp.h b/audio/avctp.h
index d0cbd97..34b0c1c 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -22,7 +22,8 @@
  *
  */
 
-#define AVCTP_PSM 23
+#define AVCTP_CONTROL_PSM	23
+#define AVCTP_BROWSING_PSM	27
 
 #define AVC_MTU 512
 #define AVC_HEADER_LENGTH 3
diff --git a/audio/avrcp.c b/audio/avrcp.c
index d925365..ca40c1e 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -190,7 +190,7 @@ static sdp_record_t *avrcp_ct_record(void)
 	sdp_list_t *aproto, *proto[2];
 	sdp_record_t *record;
 	sdp_data_t *psm, *version, *features;
-	uint16_t lp = AVCTP_PSM;
+	uint16_t lp = AVCTP_CONTROL_PSM;
 	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
 	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
 						AVRCP_FEATURE_CATEGORY_2 |
@@ -252,13 +252,15 @@ static sdp_record_t *avrcp_ct_record(void)
 
 static sdp_record_t *avrcp_tg_record(void)
 {
-	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
 	uuid_t root_uuid, l2cap, avctp, avrtg;
 	sdp_profile_desc_t profile[1];
 	sdp_list_t *aproto, *proto[2];
 	sdp_record_t *record;
-	sdp_data_t *psm, *version, *features;
-	uint16_t lp = AVCTP_PSM;
+	sdp_data_t *psm, *version, *features, *psm_browsing;
+	sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
+	uint16_t lp = AVCTP_CONTROL_PSM;
+	uint16_t lp_browsing = AVCTP_BROWSING_PSM;
 	uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
 	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
 					AVRCP_FEATURE_CATEGORY_2 |
@@ -294,6 +296,17 @@ static sdp_record_t *avrcp_tg_record(void)
 
 	aproto = sdp_list_append(0, apseq);
 	sdp_set_access_protos(record, aproto);
+	proto_browsing[0] = sdp_list_append(0, &l2cap);
+	psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing);
+	proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing);
+	apseq_browsing = sdp_list_append(0, proto_browsing[0]);
+
+	proto_browsing[1] = sdp_list_append(0, &avctp);
+	proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
+	apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);
+
+	aproto_browsing = sdp_list_append(0, apseq_browsing);
+	sdp_set_add_access_protos(record, aproto_browsing);
 
 	/* Bluetooth Profile Descriptor List */
 	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
@@ -306,6 +319,10 @@ static sdp_record_t *avrcp_tg_record(void)
 
 	sdp_set_info_attr(record, "AVRCP TG", 0, 0);
 
+	free(psm_browsing);
+	sdp_list_free(proto_browsing[0], 0);
+	sdp_list_free(proto_browsing[1], 0);
+	sdp_list_free(aproto_browsing, 0);
 	free(psm);
 	free(version);
 	sdp_list_free(proto[0], 0);
-- 
1.7.5.4


^ permalink raw reply related

* Re: RFCOMM disconnection problem
From: Dean Jenkins @ 2012-08-10  9:02 UTC (permalink / raw)
  To: Andrei Emeltchenko, Dean Jenkins, Frederic Danis,
	linux-bluetooth@vger.kernel.org
In-Reply-To: <20120809144555.GB2683@aemeltch-MOBL1>

On 9 August 2012 15:46, Andrei Emeltchenko
<andrei.emeltchenko.news@gmail.com> wrote:
> Hi Dean,
>
> On Thu, Aug 09, 2012 at 03:29:18PM +0100, Dean Jenkins wrote:
>> One obvious failure of the rfcomm session refcnt is that the refcnt
>> counter either starts with a value of 0 or 1 depending on which peer
>> initiated the connection request, that is wrong. The initiator
>> direction is not relevant for the session as connect and disconnect
>> are independent events. The refcnt should start with a value of 1 in
>> all cases.
>>
>> I am using a 2-core ARM environment that is under high processor
>> loading. The rfcomm session refcnt caused kernel crashes. I used a
>> 2.6.34 kernel but the latest 3.5-RC1 still has the poor rfcomm code.
>> My solution was to remove the rfcomm session refcnt and to ensure that
>> the freeing of the rfcomm session pointer was propagated through-out
>> the rfcomm core code. Some kernel crashes were due to reuse of the
>> freed rfcomm session pointer.
>
> Maybe it does make sense to fix refcounting instead of removing?
>
Hi Andrei,

The existing rfcomm session state machine is capable of determining
when to delete the rfcomm session structure. IMHO the refcnt is an
unnecessary complication. I have fixed rfcomm for our project by
removing the refcnt and ensuring no code can reuse a freed rfcomm
session pointer. This code I have already sent to the mailing list.

In order to get rfcomm to fail, high processor load is necessary
during disconnections to ensure that some of the loops in rfcomm
process more than 1 thing. It is because there are multiple copies of
the rfcomm session pointer s that failure occurs eg. double rfcomm
session free.

Here is an example in net/bluetooth/rfcomm/core.c:

static inline void rfcomm_process_rx(struct rfcomm_session *s)
{
	struct socket *sock = s->sock;
	struct sock *sk = sock->sk;
	struct sk_buff *skb;

	BT_DBG("session %p state %ld qlen %d", s, s->state,
skb_queue_len(&sk->sk_receive_queue));

	/* Get data directly from socket receive queue without copying it. */
	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
		skb_orphan(skb);
		if (!skb_linearize(skb))
			rfcomm_recv_frame(s, skb);
		else
			kfree_skb(skb);
	}

	if (sk->sk_state == BT_CLOSED) {
		if (!s->initiator)
			rfcomm_session_put(s);

		rfcomm_session_close(s, sk->sk_err);
	}
}

In rfcomm_process_rx(), you can see there is a while loop that causes
frames to be analysed and actioned in rfcomm_recv_frame(). This action
may cause the rfcomm session pointer to be freed because the rfcomm
session refcnt has reached zero. Therefore, the s pointer is now
invalid in the scope of rfcomm_process_rx() because the underlying
session structure was freed. Unfortunately,  if the socket state is
also detected as BT_CLOSED then !s->initiator may cause a crash or at
least accesses wrong data as the s pointer is invalid (pointing to
freed memory or reallocated memory). The refcnt in
rfcomm_session_put(s) is also invalid as s is invalid. In fact looking
at the initiator value is wrong for the rfcomm session. I suspect this
was a workaround that did not fix the root cause.

In practice, this failure scenario is difficult to reproduce (we hit
it in our embedded environment). It requires the socket to be in the
BT_CLOSED at the same time as the rfcomm session has been freed in a
single run of rfcomm_process_rx(). This is why high processor loading
triggers the malfunction because rfcomm_process_rx() maybe pre-empted
or time-sliced so rfcomm_process_rx() takes longer to complete than
normal. This gives the opportunity for the socket state to change to
BT_CLOSED during the run-time of rfcomm_process_rx().

In other words, the refcnt going to zero does not prevent copies of
the rfcomm session pointer s that now points to freed memory (or
reallocated memory) from being erroneously reused.

You can also see

		if (!s->initiator)
			rfcomm_session_put(s);

		rfcomm_session_close(s, sk->sk_err);

If rfcomm_session_put(s) succeeds in causing the refcnt to go to zero,
the rfcomm session structure will be freed. s is now invalid.
Unfortunately, rfcomm_session_close(s, sk->sk_err) reuses the s
pointer with a potential for a crash.

The refcnt could be fixed but fixing it will show it to be unnecessary
code IMHO. One of the weaknesses in the code is not managing the s
pointer after the session has been freed.

I do intend to release a patchset of all our rfcomm changes.

I welcome any comments.

Regards,
Dean

> Best regards
> Andrei Emeltchenko
>
>>
>> I intend to release a patchset of rfcomm fixes.
>>
>> Therefore, IMHO the fix "Bluetooth: Fix RFCOMM session reference
>> counting issue" (commit cf33e7 in linux-stable.git) from Octavian
>> Purdila in kernel 3.4.6 is in fact not fixing the root cause and
>> introduces a misbehaviour of the refcnt. In our project, we rejected
>> this commit because disconnections failed.
>>
>


-- 
Dean Jenkins
Embedded Software Engineer
Professional Services UK/EMEA
MontaVista Software, LLC

^ permalink raw reply

* Re: [PATCH BlueZ V5 5/5] AVRCP: Register/Unregister Browsing handler
From: Luiz Augusto von Dentz @ 2012-08-10  8:22 UTC (permalink / raw)
  To: Vani-dineshbhai PATEL; +Cc: User Name, Lucas De Marchi, Joohi, Vani
In-Reply-To: <1344585055-21482-1-git-send-email-vani.patel@stericsson.com>

Hi Vani,

On Fri, Aug 10, 2012 at 10:50 AM, Vani-dineshbhai PATEL
<vani.patel@stericsson.com> wrote:
> From: Vani Patel <vani.patel@stericsson.com>
>
> Add functions to register and unregister Browsing
> handler
> ---
>  audio/avctp.c |   51 +++++++++++++++++++++++++++++++++++++-
>  audio/avctp.h |    8 ++++++
>  audio/avrcp.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 131 insertions(+), 2 deletions(-)

Could you please resend all the patches in series with the latest version?

-- 
Luiz Augusto von Dentz

^ permalink raw reply

* [PATCH BlueZ V5 5/5] AVRCP: Register/Unregister Browsing handler
From: Vani-dineshbhai PATEL @ 2012-08-10  7:50 UTC (permalink / raw)
  To: User Name, Luiz Augusto, Lucas De Marchi; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Add functions to register and unregister Browsing
handler
---
 audio/avctp.c |   51 +++++++++++++++++++++++++++++++++++++-
 audio/avctp.h |    8 ++++++
 audio/avrcp.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index a159498..9b2bec6 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -157,6 +157,11 @@ struct avctp_pdu_handler {
 	unsigned int id;
 };
 
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+};
+
 static struct {
 	const char *name;
 	uint8_t avc;
@@ -176,6 +181,7 @@ static GSList *callbacks = NULL;
 static GSList *servers = NULL;
 static GSList *control_handlers = NULL;
 static uint8_t id = 0;
+static struct avctp_browsing_pdu_handler *browsing_handler = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
 
@@ -452,9 +458,9 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 				gpointer data)
 {
 	struct avctp *session = data;
-	uint8_t  *buf;
+	uint8_t *operands, *buf;
 	struct avctp_header *avctp;
-	int ret, sock;
+	int ret, packet_size, operand_count, sock;
 
 	buf = gnew(uint8_t, session->browsing_mtu);
 
@@ -467,6 +473,8 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 	if (ret <= 0)
 		goto failed;
 
+	DBG("Got %d bytes of data for AVCTP Browsing session %p", ret, session);
+
 	if ((unsigned int) ret < sizeof(struct avctp_header)) {
 		error("Too small AVRCP packet on browsing channel");
 		goto failed;
@@ -474,9 +482,30 @@ static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
 
 	avctp = (struct avctp_header *) buf;
 
+	DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
+			"PID 0x%04X",
+			avctp->transaction, avctp->packet_type,
+			avctp->cr, avctp->ipid, ntohs(avctp->pid));
+
 	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
 		goto failed;
 
+	operands = buf + AVCTP_HEADER_LENGTH;
+	ret -= AVCTP_HEADER_LENGTH;
+	operand_count = ret;
+
+	packet_size = AVCTP_HEADER_LENGTH;
+	avctp->cr = AVCTP_RESPONSE;
+	if (browsing_handler)
+		packet_size += browsing_handler->cb(session, avctp->transaction,
+			operands, operand_count, browsing_handler->user_data);
+
+	if (packet_size != 0) {
+		ret = write(sock, buf, packet_size);
+		if (ret != packet_size)
+			goto failed;
+	}
+
 	gfree(buf);
 	return TRUE;
 
@@ -1252,6 +1281,18 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 	return handler->id;
 }
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data)
+{
+	unsigned int id = 0;
+
+	browsing_handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	browsing_handler->cb = cb;
+	browsing_handler->user_data = user_data;
+
+	return ++id;
+}
+
 gboolean avctp_unregister_pdu_handler(unsigned int id)
 {
 	GSList *l;
@@ -1270,6 +1311,12 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
 	return FALSE;
 }
 
+gboolean avctp_unregister_browsing_pdu_handler()
+{
+	g_free(browsing_handler);
+	return TRUE;
+}
+
 struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 {
 	struct avctp *session;
diff --git a/audio/avctp.h b/audio/avctp.h
index b80e300..3e1dabe 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -83,6 +83,10 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
+typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 
 unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
 gboolean avctp_remove_state_cb(unsigned int id);
@@ -98,6 +102,10 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data);
 gboolean avctp_unregister_pdu_handler(unsigned int id);
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data);
+
+gboolean avctp_unregister_browsing_pdu_handler();
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
 				uint8_t code, uint8_t subunit,
diff --git a/audio/avrcp.c b/audio/avrcp.c
index 9d29073..fa06f85 100644
--- a/audio/avrcp.c
+++ b/audio/avrcp.c
@@ -94,6 +94,10 @@
 #define AVRCP_ABORT_CONTINUING		0x41
 #define AVRCP_SET_ABSOLUTE_VOLUME	0x50
 
+#define AVRCP_INVALID_BROWSING_PDU	0x00
+
+#define AVRCP_GENERAL_REJECT			0xA0
+
 /* Capabilities for AVRCP_GET_CAPABILITIES pdu */
 #define CAP_COMPANY_ID		0x02
 #define CAP_EVENTS_SUPPORTED	0x03
@@ -140,6 +144,12 @@ struct avrcp_header {
 #error "Unknown byte order"
 #endif
 
+struct avrcp_browsing_header {
+	uint8_t browsing_pdu;
+	uint16_t param_len;
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
 #define AVRCP_MTU	(AVC_MTU - AVC_HEADER_LENGTH)
 #define AVRCP_PDU_MTU	(AVRCP_MTU - AVRCP_HEADER_LENGTH)
 
@@ -163,7 +173,9 @@ struct avrcp_player {
 	struct audio_device *dev;
 
 	unsigned int control_handler;
+	unsigned int browsing_handler;
 	uint16_t registered_events;
+	uint8_t transaction;
 	uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
 	struct pending_pdu *pending_pdu;
 
@@ -1075,6 +1087,15 @@ static struct control_pdu_handler {
 		{ },
 };
 
+static struct pdu_browsing_handler {
+	uint8_t browsing_pdu;
+	void (*func) (struct avrcp_player *player,
+					struct avrcp_browsing_header *pdu);
+	} browsing_handlers[] = {
+		{ AVRCP_INVALID_BROWSING_PDU,
+					NULL },
+};
+
 /* handle vendordep pdu inside an avctp packet */
 static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
 					uint8_t *code, uint8_t *subunit,
@@ -1134,6 +1155,49 @@ err_metadata:
 	return AVRCP_HEADER_LENGTH + 1;
 }
 
+static size_t handle_browsing_pdu(struct avctp *session,
+					uint8_t transaction, uint8_t *operands,
+					size_t operand_count, void *user_data)
+{
+	struct avrcp_player *player = user_data;
+	struct pdu_browsing_handler *b_handler;
+	struct avrcp_browsing_header *avrcp_browsing = (void *) operands;
+	uint8_t status;
+
+	operand_count += AVRCP_BROWSING_HEADER_LENGTH;
+
+	for (b_handler = browsing_handlers; b_handler; b_handler++) {
+		if (b_handler->browsing_pdu == AVRCP_INVALID_BROWSING_PDU) {
+			b_handler = NULL;
+			break;
+		}
+		if (b_handler->browsing_pdu == avrcp_browsing->browsing_pdu)
+			break;
+	}
+
+	if (!b_handler) {
+		avrcp_browsing->browsing_pdu = AVRCP_GENERAL_REJECT;
+		status = E_INVALID_COMMAND;
+		goto err;
+	}
+
+	if (!b_handler->func) {
+		status = E_INVALID_PARAM;
+		avrcp_browsing->param_len = htons(sizeof(status));
+		goto err;
+	}
+	player->transaction = transaction;
+	b_handler->func(player, avrcp_browsing);
+	return AVRCP_BROWSING_HEADER_LENGTH + ntohs(avrcp_browsing->param_len);
+
+err:
+	avrcp_browsing->param_len = htons(sizeof(status));
+	memcpy(&operands[AVRCP_BROWSING_HEADER_LENGTH], &status,
+							(sizeof(status)));
+	return AVRCP_BROWSING_HEADER_LENGTH + sizeof(status);
+}
+
+
 size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
 {
 	struct avrcp_header *pdu = (void *) operands;
@@ -1233,6 +1297,10 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 			avctp_unregister_pdu_handler(player->control_handler);
 			player->control_handler = 0;
 		}
+		if (player->browsing_handler) {
+			avctp_unregister_browsing_pdu_handler();
+			player->browsing_handler = 0;
+		}
 
 		break;
 	case AVCTP_STATE_CONNECTING:
@@ -1244,6 +1312,12 @@ static void state_changed(struct audio_device *dev, avctp_state_t old_state,
 							AVC_OP_VENDORDEP,
 							handle_vendordep_pdu,
 							player);
+		if (!player->browsing_handler)
+			player->browsing_handler =
+					avctp_register_browsing_pdu_handler(
+							handle_browsing_pdu,
+							player);
+
 		break;
 	case AVCTP_STATE_CONNECTED:
 		rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V5 4/5] AVRCP: Register/Unregister Browsing handler
From: Vani-dineshbhai PATEL @ 2012-08-10  7:49 UTC (permalink / raw)
  To: User Name, Luiz Augusto, Lucas De Marchi; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Add functions to register and unregister Browsing
handler
---
 audio/avctp.c |   24 ++++++++++++++++++++++++
 audio/avctp.h |    8 ++++++++
 2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index a159498..ed6efbe 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -157,6 +157,11 @@ struct avctp_pdu_handler {
 	unsigned int id;
 };
 
+struct avctp_browsing_pdu_handler {
+	avctp_browsing_pdu_cb cb;
+	void *user_data;
+};
+
 static struct {
 	const char *name;
 	uint8_t avc;
@@ -176,6 +181,7 @@ static GSList *callbacks = NULL;
 static GSList *servers = NULL;
 static GSList *control_handlers = NULL;
 static uint8_t id = 0;
+static struct avctp_browsing_pdu_handler *browsing_handler = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
 
@@ -1252,6 +1258,18 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 	return handler->id;
 }
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data)
+{
+	unsigned int id = 0;
+
+	browsing_handler = g_new(struct avctp_browsing_pdu_handler, 1);
+	browsing_handler->cb = cb;
+	browsing_handler->user_data = user_data;
+
+	return ++id;
+}
+
 gboolean avctp_unregister_pdu_handler(unsigned int id)
 {
 	GSList *l;
@@ -1270,6 +1288,12 @@ gboolean avctp_unregister_pdu_handler(unsigned int id)
 	return FALSE;
 }
 
+gboolean avctp_unregister_browsing_pdu_handler()
+{
+	g_free(browsing_handler);
+	return TRUE;
+}
+
 struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
 {
 	struct avctp *session;
diff --git a/audio/avctp.h b/audio/avctp.h
index b80e300..3e1dabe 100644
--- a/audio/avctp.h
+++ b/audio/avctp.h
@@ -83,6 +83,10 @@ typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
 typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data);
+typedef size_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+					uint8_t transaction,
+					uint8_t *operands, size_t operand_count,
+					void *user_data);
 
 unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
 gboolean avctp_remove_state_cb(unsigned int id);
@@ -98,6 +102,10 @@ unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_control_pdu_cb cb,
 							void *user_data);
 gboolean avctp_unregister_pdu_handler(unsigned int id);
 
+unsigned int avctp_register_browsing_pdu_handler(avctp_browsing_pdu_cb cb,
+							void *user_data);
+
+gboolean avctp_unregister_browsing_pdu_handler();
 int avctp_send_passthrough(struct avctp *session, uint8_t op);
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
 				uint8_t code, uint8_t subunit,
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH BlueZ V4 3/5] AVRCP: Add browsing channel support
From: Vani-dineshbhai PATEL @ 2012-08-10  7:48 UTC (permalink / raw)
  To: User Name, Luiz Augusto, Lucas De Marchi; +Cc: Vani, Joohi, Vani

From: Vani Patel <vani.patel@stericsson.com>

Implements browsing channel creation and release.
---
 audio/avctp.c |  235 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 216 insertions(+), 19 deletions(-)

diff --git a/audio/avctp.c b/audio/avctp.c
index 3bd021c..a159498 100644
--- a/audio/avctp.c
+++ b/audio/avctp.c
@@ -41,6 +41,7 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sdp.h>
 #include <bluetooth/uuid.h>
+#include <bluetooth/l2cap.h>
 
 #include <glib.h>
 
@@ -119,6 +120,7 @@ struct avctp_state_callback {
 struct avctp_server {
 	bdaddr_t src;
 	GIOChannel *control_io;
+	GIOChannel *browsing_io;
 	GSList *sessions;
 };
 
@@ -137,9 +139,12 @@ struct avctp {
 	int uinput;
 
 	GIOChannel *control_io;
+	GIOChannel *browsing_io;
 	guint control_io_id;
+	guint browsing_io_id;
 
 	uint16_t control_mtu;
+	uint16_t browsing_mtu;
 
 	uint8_t key_quirks[256];
 	GSList *handlers;
@@ -326,6 +331,17 @@ static void avctp_disconnected(struct avctp *session)
 	if (!session)
 		return;
 
+	if (session->browsing_io) {
+		g_io_channel_shutdown(session->browsing_io, TRUE, NULL);
+		g_io_channel_unref(session->browsing_io);
+		session->browsing_io = NULL;
+	}
+
+	if (session->browsing_io_id) {
+		g_source_remove(session->browsing_io_id);
+		session->browsing_io_id = 0;
+	}
+
 	if (session->control_io) {
 		g_io_channel_shutdown(session->control_io, TRUE, NULL);
 		g_io_channel_unref(session->control_io);
@@ -432,6 +448,43 @@ static void handle_response(struct avctp *session, struct avctp_header *avctp,
 	}
 }
 
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avctp *session = data;
+	uint8_t  *buf;
+	struct avctp_header *avctp;
+	int ret, sock;
+
+	buf = gnew(uint8_t, session->browsing_mtu);
+
+	if (!(cond & G_IO_IN))
+		goto failed;
+
+	sock = g_io_channel_unix_get_fd(session->browsing_io);
+	ret = read(sock, buf, session->browsing_mtu);
+
+	if (ret <= 0)
+		goto failed;
+
+	if ((unsigned int) ret < sizeof(struct avctp_header)) {
+		error("Too small AVRCP packet on browsing channel");
+		goto failed;
+	}
+
+	avctp = (struct avctp_header *) buf;
+
+	if (avctp->packet_type != AVCTP_PACKET_SINGLE)
+		goto failed;
+
+	gfree(buf);
+	return TRUE;
+
+failed:
+	gfree(buf);
+	return FALSE;
+}
+
 static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
 				gpointer data)
 {
@@ -613,6 +666,47 @@ static void init_uinput(struct avctp *session)
 		DBG("AVRCP: uinput initialized for %s", address);
 }
 
+static void avctp_connect_browsing_cb(GIOChannel *chan,
+					GError *err,
+					gpointer data)
+{
+	struct avctp *session = data;
+	char address[18];
+	uint16_t imtu;
+	GError *gerr = NULL;
+
+	if (err) {
+		error("Browsing: %s", err->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		session->browsing_io = NULL;
+		return;
+	}
+
+	bt_io_get(chan, BT_IO_L2CAP, &gerr,
+			BT_IO_OPT_DEST, &address,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s", gerr->message);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		session->browsing_io = NULL;
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	if (!session->browsing_io)
+		session->browsing_io = g_io_channel_ref(chan);
+
+	session->browsing_mtu = imtu;
+
+	session->browsing_io_id = g_io_add_watch(chan,
+				G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+				(GIOFunc) session_browsing_cb, session);
+}
+
 static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
 	struct avctp *session = data;
@@ -651,6 +745,32 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
 				(GIOFunc) session_cb, session);
 }
 
+static void auth_browsing_cb(DBusError *derr, void *user_data)
+{
+	struct avctp *session = user_data;
+	GError *err = NULL;
+
+	if (session->browsing_io_id) {
+		g_source_remove(session->browsing_io_id);
+		session->browsing_io_id = 0;
+	}
+
+	if (derr && dbus_error_is_set(derr)) {
+		error("Browsing Access denied: %s", derr->message);
+		return;
+	}
+
+	if (!bt_io_accept(session->browsing_io, avctp_connect_browsing_cb,
+						session, NULL, &err)) {
+		error("Browsing bt_io_accept: %s", err->message);
+		if (session && session->browsing_io) {
+			g_io_channel_unref(session->browsing_io);
+			session->browsing_io = NULL;
+		}
+		g_error_free(err);
+	}
+}
+
 static void auth_cb(DBusError *derr, void *user_data)
 {
 	struct avctp *session = user_data;
@@ -729,6 +849,65 @@ static struct avctp *avctp_get_internal(const bdaddr_t *src,
 	return session;
 }
 
+static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
+						struct audio_device *dev)
+{
+	if (session->control_io) {
+		error("Refusing unexpected connect from");
+		goto drop;
+	}
+
+	avctp_set_state(session, AVCTP_STATE_CONNECTING);
+	session->control_io = g_io_channel_ref(chan);
+
+	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+						auth_cb, session) < 0)
+		goto drop;
+
+	session->control_io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
+						G_IO_NVAL, session_cb, session);
+	return;
+
+drop:
+	if (!session || !session->control_io)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (session && session->control_io)
+		g_io_channel_unref(session->control_io);
+
+	if (session)
+		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
+
+static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
+						struct audio_device *dev)
+{
+
+	if (!session->control_io) {
+		error("Browsing: Refusing unexpected connect from");
+		goto drop;
+	}
+
+	if (session->browsing_io) {
+		error("Browsing channel already exists");
+		goto drop;
+	}
+
+	session->browsing_io = g_io_channel_ref(chan);
+
+	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+						auth_browsing_cb, session) < 0)
+		goto drop;
+	return;
+
+drop:
+	if (!session || !session->control_io || !session->browsing_io)
+		g_io_channel_shutdown(chan, TRUE, NULL);
+
+	if (session && session->browsing_io)
+		g_io_channel_unref(session->browsing_io);
+}
+
 static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 {
 	struct avctp *session;
@@ -736,11 +915,13 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 	char address[18];
 	bdaddr_t src, dst;
 	GError *err = NULL;
+	uint16_t psm;
 
 	bt_io_get(chan, BT_IO_L2CAP, &err,
 			BT_IO_OPT_SOURCE_BDADDR, &src,
 			BT_IO_OPT_DEST_BDADDR, &dst,
 			BT_IO_OPT_DEST, address,
+			BT_IO_OPT_PSM, &psm,
 			BT_IO_OPT_INVALID);
 	if (err) {
 		error("%s", err->message);
@@ -771,40 +952,40 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 			goto drop;
 	}
 
-	if (session->control_io) {
-		error("Refusing unexpected connect from %s", address);
-		goto drop;
+	switch (psm) {
+	case AVCTP_CONTROL_PSM:
+		avctp_control_confirm(session, chan, dev);
+		break;
+	case AVCTP_BROWSING_PSM:
+		avctp_browsing_confirm(session, chan, dev);
+		break;
 	}
 
-	avctp_set_state(session, AVCTP_STATE_CONNECTING);
-	session->control_io = g_io_channel_ref(chan);
-
-	if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
-						auth_cb, session) < 0)
-		goto drop;
-
-	session->control_io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP |
-						G_IO_NVAL, session_cb, session);
 	return;
 
 drop:
-	if (!session || !session->control_io)
-		g_io_channel_shutdown(chan, TRUE, NULL);
-	if (session)
+	if (session && session->browsing_io)
+		g_io_channel_unref(session->browsing_io);
+
+	if (session && session->control_io)
+		g_io_channel_unref(session->control_io);
+
+	if (session && psm == AVCTP_CONTROL_PSM)
 		avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
 }
 
-static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master,
+						uint8_t mode, uint16_t psm)
 {
 	GError *err = NULL;
 	GIOChannel *io;
-
 	io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
 				NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, src,
-				BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
+				BT_IO_OPT_PSM, psm,
 				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
 				BT_IO_OPT_MASTER, master,
+				BT_IO_OPT_MODE, mode,
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("%s", err->message);
@@ -824,12 +1005,25 @@ int avctp_register(const bdaddr_t *src, gboolean master)
 
 	server = g_new0(struct avctp_server, 1);
 
-	server->control_io = avctp_server_socket(src, master);
+	server->control_io = avctp_server_socket(src, master, L2CAP_MODE_BASIC,
+							AVCTP_CONTROL_PSM);
 	if (!server->control_io) {
 		g_free(server);
 		return -1;
 	}
 
+	server->browsing_io = avctp_server_socket(src, master, L2CAP_MODE_ERTM,
+							AVCTP_BROWSING_PSM);
+	if (!server->browsing_io) {
+		if (server->control_io) {
+			g_io_channel_shutdown(server->control_io, TRUE, NULL);
+			g_io_channel_unref(server->control_io);
+			server->control_io = NULL;
+		}
+		g_free(server);
+		return -1;
+	}
+
 	bacpy(&server->src, src);
 
 	servers = g_slist_append(servers, server);
@@ -861,6 +1055,9 @@ void avctp_unregister(const bdaddr_t *src)
 		avctp_disconnected(server->sessions->data);
 
 	servers = g_slist_remove(servers, server);
+	g_io_channel_shutdown(server->browsing_io, TRUE, NULL);
+	g_io_channel_unref(server->browsing_io);
+	server->browsing_io = NULL;
 
 	g_io_channel_shutdown(server->control_io, TRUE, NULL);
 	g_io_channel_unref(server->control_io);
-- 
1.7.5.4


^ permalink raw reply related

* Re: [PATCH BlueZ v3 1/3] rfcomm: Fix checking return value instead of errno
From: Marcel Holtmann @ 2012-08-10  5:56 UTC (permalink / raw)
  To: Lucas De Marchi; +Cc: linux-bluetooth
In-Reply-To: <1344544143-12805-1-git-send-email-lucas.demarchi@profusion.mobi>

Hi Lucas,

> We were checking by a positive return value instead of checking by -1
> and errno. However when there's no support for TTY kernel returns
> EOPNOTSUPP as usual, which in the end will have a return value of -1
> and errno will be set to EOPNOTSUPP.
> ---
>  tools/rfcomm.c | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)

all 3 patches have been applied.

Regards

Marcel



^ permalink raw reply

* [PATCH BlueZ v3 3/3] rfcomm: Fix typo in man page
From: Lucas De Marchi @ 2012-08-09 20:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi
In-Reply-To: <1344544143-12805-1-git-send-email-lucas.demarchi@profusion.mobi>

---
 tools/rfcomm.1 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/rfcomm.1 b/tools/rfcomm.1
index f880f52..51df284 100644
--- a/tools/rfcomm.1
+++ b/tools/rfcomm.1
@@ -108,7 +108,7 @@ parameters.
 .TP
 .BI bind " <dev> [bdaddr] [channel]"
 This binds the RFCOMM device to a remote Bluetooth device. The
-command did not establish a connection to the remote device, it
+command does not establish a connection to the remote device, it
 only creates the binding. The connection will be established right
 after an application tries to open the RFCOMM device. If no channel
 number is specified, it uses the channel number 1.
-- 
1.7.11.4


^ permalink raw reply related

* [PATCH BlueZ v3 2/3] rfcomm: Remove support for configuration file
From: Lucas De Marchi @ 2012-08-09 20:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi
In-Reply-To: <1344544143-12805-1-git-send-email-lucas.demarchi@profusion.mobi>

---
 .gitignore        |   5 --
 Makefile.am       |   2 +-
 Makefile.tools    |  15 +----
 configure.ac      |   2 -
 tools/kword.c     |  65 ---------------------
 tools/kword.h     |  46 ---------------
 tools/lexer.l     | 120 --------------------------------------
 tools/parser.y    | 171 ------------------------------------------------------
 tools/rfcomm.1    |  19 +-----
 tools/rfcomm.c    |  99 ++++++-------------------------
 tools/rfcomm.conf |  17 ------
 11 files changed, 24 insertions(+), 537 deletions(-)
 delete mode 100644 tools/kword.c
 delete mode 100644 tools/kword.h
 delete mode 100644 tools/lexer.l
 delete mode 100644 tools/parser.y
 delete mode 100644 tools/rfcomm.conf

diff --git a/.gitignore b/.gitignore
index 38318cd..c9f293a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,11 +26,6 @@ missing
 stamp-h1
 autom4te.cache
 
-ylwrap
-lexer.c
-parser.h
-parser.c
-
 bluez.pc
 lib/bluetooth
 src/builtin.h
diff --git a/Makefile.am b/Makefile.am
index 45a811c..a74709d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -423,7 +423,7 @@ DISTCLEANFILES = $(pkgconfig_DATA)
 
 MAINTAINERCLEANFILES = Makefile.in \
 	aclocal.m4 configure config.h.in config.sub config.guess \
-	ltmain.sh depcomp compile missing install-sh mkinstalldirs ylwrap
+	ltmain.sh depcomp compile missing install-sh mkinstalldirs
 
 src/builtin.h: src/genbuiltin $(builtin_sources)
 	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
diff --git a/Makefile.tools b/Makefile.tools
index 5579b86..d3b6f57 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -1,9 +1,5 @@
 
 if TOOLS
-if DATAFILES
-conf_DATA += tools/rfcomm.conf
-endif
-
 bin_PROGRAMS += tools/rfcomm tools/l2ping \
 				tools/hcitool tools/sdptool tools/ciptool
 
@@ -12,12 +8,7 @@ sbin_PROGRAMS += tools/hciattach tools/hciconfig
 noinst_PROGRAMS += tools/avinfo tools/ppporc \
 				tools/hcieventmask tools/hcisecfilter
 
-tools/kword.c: tools/parser.h
-
-tools_rfcomm_SOURCES = tools/rfcomm.c tools/parser.y tools/lexer.l \
-					tools/kword.h tools/kword.c
-EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
-							tools/lexer.c
+tools_rfcomm_SOURCES = tools/rfcomm.c
 tools_rfcomm_LDADD = lib/libbluetooth-private.la
 
 tools_l2ping_LDADD = lib/libbluetooth-private.la
@@ -88,10 +79,6 @@ EXTRA_DIST += tools/rfcomm.1 tools/l2ping.8 \
 			tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
 endif
 
-CLEANFILES += tools/lexer.c tools/parser.c tools/parser.h
-
-EXTRA_DIST += tools/rfcomm.conf
-
 if BCCMD
 sbin_PROGRAMS += tools/bccmd
 
diff --git a/configure.ac b/configure.ac
index 7f331ae..7d9a34d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,8 +20,6 @@ AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CC_PIE
 AC_PROG_INSTALL
-AC_PROG_YACC
-AM_PROG_LEX
 AM_PROG_MKDIR_P
 
 m4_define([_LT_AC_TAGCONFIG], [])
diff --git a/tools/kword.c b/tools/kword.c
deleted file mode 100644
index 62e24fe..0000000
--- a/tools/kword.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-
-#include "kword.h"
-#include "parser.h"
-
-int lineno;
-
-struct keyword_t rfcomm_keyword[] = {
-	{ "bind",	K_BIND		},
-	{ "device",	K_DEVICE	},
-	{ "channel",	K_CHANNEL	},
-	{ "comment",	K_COMMENT	},
-
-	{ "yes",	K_YES		},
-	{ "no",		K_NO		},
-	{ "enable",	K_YES		},
-	{ "disable",	K_NO		},
-
-	{ NULL , 0 }
-};
-
-int rfcomm_find_keyword(struct keyword_t *keyword, char *string)
-{
-	while (keyword->string) {
-		if (!strcmp(string, keyword->string))
-			return keyword->type;
-		keyword++;
-	}
-
-	return -1;
-}
-
-struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
diff --git a/tools/kword.h b/tools/kword.h
deleted file mode 100644
index 81a2a88..0000000
--- a/tools/kword.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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
- *
- */
-
-extern int lineno;
-
-struct keyword_t {
-	char *string;
-	int type;
-};
-
-extern struct keyword_t rfcomm_keyword[];
-
-int rfcomm_find_keyword(struct keyword_t *keyword, char *string);
-
-#define MAXCOMMENTLEN  100
-
-struct rfcomm_opts {
-	int bind;
-	bdaddr_t bdaddr;
-	int channel;
-	char comment[MAXCOMMENTLEN + 1];
-};
-
-extern struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
-
-int rfcomm_read_config(char *filename);
diff --git a/tools/lexer.l b/tools/lexer.l
deleted file mode 100644
index ff9ce81..0000000
--- a/tools/lexer.l
+++ /dev/null
@@ -1,120 +0,0 @@
-%{
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-/* Nasty workaround, but flex defines isatty() twice */
-#define _UNISTD_H
-
-#include <stdio.h>
-#include <errno.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-
-#include "kword.h"
-#include "parser.h"
-
-int yylex(void);
-
-#define YY_NO_INPUT
-
-#define ECHO {;}
-#define YY_DECL int yylex(void)
-
-int yyerror(char *str);
-
-%}
-
-%option nounput
-
-space		[ \t]
-linebreak	\n
-comment		\#.*\n
-keyword		[A-Za-z0-9\_\-]+
-
-number		[0-9]+
-string		\".*\"
-bdaddr		[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}
-
-%%
-
-{space}		{
-			/* Skip spaces and tabs */
-			;
-		}
-
-{comment}	{
-			/* Skip comments */
-			lineno++; 
-		}
-
-{number}	{
-			yylval.number = atoi(yytext);
-			return NUMBER;
-		}
-
-{string}	{
-			yylval.string = yytext;
-			return STRING;
-		}
-
-{bdaddr}	{
-			bdaddr_t *ba = malloc(sizeof(bdaddr_t));
-			str2ba(yytext, ba);
-			yylval.bdaddr = ba;
-			return BDADDR;
-		}
-
-{keyword}	{
-			int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
-			if (keyword != -1)
-				return keyword;
-
-			if (strncmp(yytext, "rfcomm", 6) == 0) {
-				yylval.number = atoi(yytext + 6);
-				return RFCOMM;
-			}
-
-			yylval.string = yytext;
-			return WORD;
-		}
-
-{linebreak}	{
-			lineno++;
-		}
-
-.		{
-			return *yytext;
-		}
-
-%%
-
-int yywrap(void) 
-{
-	return 1;
-}
diff --git a/tools/parser.y b/tools/parser.y
deleted file mode 100644
index 96e6a56..0000000
--- a/tools/parser.y
+++ /dev/null
@@ -1,171 +0,0 @@
-%{
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-
-#include "kword.h"
-
-int yylex(void);
-int yyerror(char *s); 
-
-struct rfcomm_opts *opts;
-
-%}
-
-%union {
-	int number;
-	char *string;
-	bdaddr_t *bdaddr;
-}
-
-%token K_BIND K_DEVICE K_CHANNEL K_COMMENT
-%token K_YES K_NO
-
-%token <number> NUMBER RFCOMM
-%token <string> STRING WORD
-%token <bdaddr> BDADDR
-
-%type <number> bool
-
-%%
-
-config		:
-		| statement
-		| config statement
-		;
-
-statement	: section '{' rfcomm_options '}'
-		| rfcomm  '{' rfcomm_options '}'
-		| WORD
-			{
-			}
-		| error
-			{
-				yyclearin;
-				yyerrok;
-			}
-		;
-
-section		: WORD
-			{
-				opts = NULL;
-			}
-		;
-
-rfcomm		: RFCOMM
-			{
-				if (($1 >= 0) && ($1 < RFCOMM_MAX_DEV))
-					opts = &rfcomm_opts[$1];
-				else
-					opts = NULL;
-			}
-		;
-
-rfcomm_options	: rfcomm_option ';'
-		| error ';'
-		| rfcomm_options rfcomm_option ';'
-		;
-
-rfcomm_option	: K_BIND bool
-			{
-				if (opts)
-					opts->bind = $2;
-			}
-		| K_DEVICE BDADDR
-			{
-				if (opts)
-					bacpy(&opts->bdaddr, $2);
-			}
-		| K_CHANNEL NUMBER
-			{
-				if (opts)
-					opts->channel = $2;
-			}
-		| K_COMMENT STRING
-			{
-				if (opts)
-					snprintf(opts->comment, MAXCOMMENTLEN, "%s", $2);
-			}
-		| WORD
-			{
-				// Unknown option
-			}
-		;
-
-bool		: K_YES	{ $$ = 1; }
-		| K_NO	{ $$ = 0; }
-		;
-
-%%
-
-int yyerror(char *s) 
-{
-	fprintf(stderr, "%s line %d\n", s, lineno);
-	return 0;
-}
-
-int rfcomm_read_config(char *filename)
-{
-	extern FILE *yyin;
-	char file[MAXPATHLEN + 1];
-	int i;
-
-	for (i = 0; i < RFCOMM_MAX_DEV; i++) {
-		rfcomm_opts[i].bind = 0;
-		bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
-		rfcomm_opts[i].channel = 1;
-	}
-
-	if (filename) {
-		snprintf(file, MAXPATHLEN,  "%s", filename);
-	} else {
-		snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
-
-		if ((getuid() == 0) || (access(file, R_OK) < 0))
-			snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
-	}
-
-	if (!(yyin = fopen(file, "r")))
-		return -1;
-
-	lineno = 1;
-	yyparse();
-
-	fclose(yyin);
-
-	return 0;
-}
diff --git a/tools/rfcomm.1 b/tools/rfcomm.1
index 06303cd..f880f52 100644
--- a/tools/rfcomm.1
+++ b/tools/rfcomm.1
@@ -48,9 +48,6 @@ Prints information about all configured RFCOMM devices.
 .BI -r
 Switch TTY into raw mode (doesn't work with "bind").
 .TP
-.BI -f " <file>"
-Specify alternate config file.
-.TP
 .BI -i " <hciX> | <bdaddr>"
 The command is applied to device
 .BI -A
@@ -89,9 +86,8 @@ Display the information about the specified device.
 .BI connect " <dev> [bdaddr] [channel]"
 Connect the RFCOMM device to the remote Bluetooth device on the
 specified channel. If no channel is specified, it will use the
-channel number 1. If also the Bluetooth address is left out, it
-tries to read the data from the config file. This command can
-be terminated with the key sequence CTRL-C.
+channel number 1. This command can be terminated with the key
+sequence CTRL-C.
 .TP
 .BI listen " <dev> [channel] [cmd]"
 Listen on a specified RFCOMM channel for incoming connections.
@@ -115,15 +111,7 @@ This binds the RFCOMM device to a remote Bluetooth device. The
 command did not establish a connection to the remote device, it
 only creates the binding. The connection will be established right
 after an application tries to open the RFCOMM device. If no channel
-number is specified, it uses the channel number 1. If the Bluetooth
-address is also left out, it tries to read the data from the config
-file.
-
-If
-.B all
-is specified for the RFCOMM device, then all devices that have
-.B "bind yes"
-set in the config will be bound.
+number is specified, it uses the channel number 1.
 .TP
 .BI release " <dev>"
 This command releases a defined RFCOMM binding.
@@ -131,7 +119,6 @@ This command releases a defined RFCOMM binding.
 If
 .B all
 is specified for the RFCOMM device, then all bindings will be removed.
-This command didn't care about the settings in the config file.
 .SH AUTHOR
 Written by Marcel Holtmann <marcel@holtmann.org>.
 .br
diff --git a/tools/rfcomm.c b/tools/rfcomm.c
index 0a80670..add9f3b 100644
--- a/tools/rfcomm.c
+++ b/tools/rfcomm.c
@@ -46,13 +46,10 @@
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/rfcomm.h>
 
-#include "kword.h"
-
 #ifdef NEED_PPOLL
 #include "ppoll.h"
 #endif
 
-static char *rfcomm_config_file = NULL;
 static int rfcomm_raw_tty = 0;
 static int auth = 0;
 static int encryption = 0;
@@ -159,27 +156,16 @@ static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int ar
 	bacpy(&req.src, bdaddr);
 
 	if (argc < 2) {
-		err = rfcomm_read_config(rfcomm_config_file);
-		if (err < 0) {
-			perror("Can't open RFCOMM config file");
-			return err;
-		}
-
-		bacpy(&req.dst, &rfcomm_opts[dev].bdaddr);
-		req.channel = rfcomm_opts[dev].channel;
+		fprintf(stderr, "Missing dev parameter");
+		return -EINVAL;
+	}
 
-		if (bacmp(&req.dst, BDADDR_ANY) == 0) {
-			fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
-			return -EFAULT;
-		}
-	} else {
-		str2ba(argv[1], &req.dst);
+	str2ba(argv[1], &req.dst);
 
-		if (argc > 2)
-			req.channel = atoi(argv[2]);
-		else
-			req.channel = 1;
-	}
+	if (argc > 2)
+		req.channel = atoi(argv[2]);
+	else
+		req.channel = 1;
 
 	err = ioctl(ctl, RFCOMMCREATEDEV, &req);
 	if (err == -1) {
@@ -194,35 +180,6 @@ static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int ar
 	return err;
 }
 
-static int create_all(int ctl)
-{
-	struct rfcomm_dev_req req;
-	int i, err;
-
-	err = rfcomm_read_config(rfcomm_config_file);
-	if (err < 0) {
-		perror("Can't open RFCOMM config file");
-		return err;
-	}
-
-	for (i = 0; i < RFCOMM_MAX_DEV; i++) {
-		if (!rfcomm_opts[i].bind)
-			continue;
-
-		memset(&req, 0, sizeof(req));
-		req.dev_id = i;
-		req.flags = 0;
-		bacpy(&req.src, BDADDR_ANY);
-		bacpy(&req.dst, &rfcomm_opts[i].bdaddr);
-		req.channel = rfcomm_opts[i].channel;
-
-		if (bacmp(&req.dst, BDADDR_ANY) != 0)
-			ioctl(ctl, RFCOMMCREATEDEV, &req);
-	}
-
-	return 0;
-}
-
 static int release_dev(int ctl, int dev, uint32_t flags)
 {
 	struct rfcomm_dev_req req;
@@ -335,28 +292,17 @@ static void cmd_connect(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **arg
 	laddr.rc_channel = 0;
 
 	if (argc < 2) {
-		if (rfcomm_read_config(rfcomm_config_file) < 0) {
-			perror("Can't open RFCOMM config file");
-			return;
-		}
+		fprintf(stderr, "Missing dev parameter");
+		return;
+	}
 
-		raddr.rc_family = AF_BLUETOOTH;
-		bacpy(&raddr.rc_bdaddr, &rfcomm_opts[dev].bdaddr);
-		raddr.rc_channel = rfcomm_opts[dev].channel;
+	raddr.rc_family = AF_BLUETOOTH;
+	str2ba(argv[1], &raddr.rc_bdaddr);
 
-		if (bacmp(&raddr.rc_bdaddr, BDADDR_ANY) == 0) {
-			fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
-			return;
-		}
-	} else {
-		raddr.rc_family = AF_BLUETOOTH;
-		str2ba(argv[1], &raddr.rc_bdaddr);
-
-		if (argc > 2)
-			raddr.rc_channel = atoi(argv[2]);
-		else
-			raddr.rc_channel = 1;
-	}
+	if (argc > 2)
+		raddr.rc_channel = atoi(argv[2]);
+	else
+		raddr.rc_channel = 1;
 
 	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
 	if (sk < 0) {
@@ -658,10 +604,7 @@ static void cmd_watch(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
 
 static void cmd_create(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
 {
-	if (strcmp(argv[0], "all") == 0)
-		create_all(ctl);
-	else
-		create_dev(ctl, dev, 0, bdaddr, argc, argv);
+	create_dev(ctl, dev, 0, bdaddr, argc, argv);
 }
 
 static void cmd_release(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
@@ -754,7 +697,7 @@ int main(int argc, char *argv[])
 
 	bacpy(&bdaddr, BDADDR_ANY);
 
-	while ((opt = getopt_long(argc, argv, "+i:f:rahAESML:", main_options, NULL)) != -1) {
+	while ((opt = getopt_long(argc, argv, "+i:rahAESML:", main_options, NULL)) != -1) {
 		switch(opt) {
 		case 'i':
 			if (strncmp(optarg, "hci", 3) == 0)
@@ -763,10 +706,6 @@ int main(int argc, char *argv[])
 				str2ba(optarg, &bdaddr);
 			break;
 
-		case 'f':
-			rfcomm_config_file = strdup(optarg);
-			break;
-
 		case 'r':
 			rfcomm_raw_tty = 1;
 			break;
diff --git a/tools/rfcomm.conf b/tools/rfcomm.conf
deleted file mode 100644
index 6179ef7..0000000
--- a/tools/rfcomm.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# RFCOMM configuration file.
-#
-
-#rfcomm0 {
-#	# Automatically bind the device at startup
-#	bind no;
-#
-#	# Bluetooth address of the device
-#	device 11:22:33:44:55:66;
-#
-#	# RFCOMM channel for the connection
-#	channel	1;
-#
-#	# Description of the connection
-#	comment "Example Bluetooth device";
-#}
-- 
1.7.11.4


^ permalink raw reply related

* [PATCH BlueZ v3 1/3] rfcomm: Fix checking return value instead of errno
From: Lucas De Marchi @ 2012-08-09 20:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lucas De Marchi

We were checking by a positive return value instead of checking by -1
and errno. However when there's no support for TTY kernel returns
EOPNOTSUPP as usual, which in the end will have a return value of -1
and errno will be set to EOPNOTSUPP.
---
 tools/rfcomm.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/tools/rfcomm.c b/tools/rfcomm.c
index e73b0ba..0a80670 100644
--- a/tools/rfcomm.c
+++ b/tools/rfcomm.c
@@ -182,10 +182,14 @@ static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int ar
 	}
 
 	err = ioctl(ctl, RFCOMMCREATEDEV, &req);
-	if (err == EOPNOTSUPP)
-		fprintf(stderr, "RFCOMM TTY support not available\n");
-	else if (err < 0)
-		perror("Can't create device");
+	if (err == -1) {
+		err = -errno;
+
+		if (err == -EOPNOTSUPP)
+			fprintf(stderr, "RFCOMM TTY support not available\n");
+		else
+			perror("Can't create device");
+	}
 
 	return err;
 }
-- 
1.7.11.4


^ permalink raw reply related

* [PATCH BlueZ 4/4] replay: Add config file parser and skip action
From: Anton Weber @ 2012-08-09 17:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Anton Weber
In-Reply-To: <1344533773-12914-1-git-send-email-ant@antweb.me>

Add support for config files to manipulate the loaded sequence.
A config file can be specified with the new -c command line parameter.

The config syntax is:
scope attr=value[,attr=value,...]

e.g.:
all delta=300,action=replay
1-4 action=emulate
1 delta=500
HCI_EVT_0x0e delta=0
HCI_CMD_0x03|0x0003 action=emulate
HCI_ACL action=replay

The inital version supports the attributes delta and action and
all scope types shown in the example.

HCISEQ_ACTION_SKIP allows to skip a packet in the sequence.
The action can be set in config files using "action=skip".
---
 Makefile.tools               |    1 +
 tools/replay/config-parser.c |  506 ++++++++++++++++++++++++++++++++++++++++++
 tools/replay/config-parser.h |   36 +++
 tools/replay/hciseq.h        |    3 +-
 tools/replay/main.c          |  117 +++++++++-
 tools/replay/main.h          |    2 -
 6 files changed, 661 insertions(+), 4 deletions(-)
 create mode 100644 tools/replay/config-parser.c
 create mode 100644 tools/replay/config-parser.h

diff --git a/Makefile.tools b/Makefile.tools
index 8b3c8d8..ba2db00 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -72,6 +72,7 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
 tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
 					tools/replay/hciseq.h tools/replay/hciseq.c \
 					tools/replay/time.h tools/replay/time.c \
+					tools/replay/config-parser.h tools/replay/config-parser.c \
 					monitor/packet.h monitor/packet.c \
 					monitor/btsnoop.h monitor/btsnoop.c \
 					monitor/control.h monitor/control.c \
diff --git a/tools/replay/config-parser.c b/tools/replay/config-parser.c
new file mode 100644
index 0000000..5219eb9
--- /dev/null
+++ b/tools/replay/config-parser.c
@@ -0,0 +1,506 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *  Copyright (C) 2012       Anton Weber <ant@antweb.me>
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include "config-parser.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+
+#define MAXLINE 128
+#define MAX_ATTR_KEY 32
+#define MAX_ATTR_VAL 32
+
+static struct hciseq_list *seq;
+static struct hciseq_type_cfg *type_cfg;
+
+static bool verbose;
+static int line;
+
+struct scope_list {
+	struct scope_node *head;
+};
+
+struct scope_node {
+	int pos;
+	struct hciseq_attr *attr;
+	struct scope_node *next;
+};
+
+struct attr_list {
+	struct attr_node *head;
+};
+
+struct attr_node {
+	char key[MAX_ATTR_KEY];
+	char val[MAX_ATTR_VAL];
+	struct attr_node *next;
+};
+
+static void attr_list_delete(struct attr_list *list)
+{
+	struct attr_node *node, *next;
+
+	if (list == NULL)
+		return;
+
+	node = list->head;
+	free(list);
+	while (node != NULL) {
+		next = node->next;
+		free(node);
+		node = next;
+	}
+}
+
+static void scope_list_delete(struct scope_list *list)
+{
+	struct scope_node *node, *next;
+
+	if (list == NULL)
+		return;
+
+	node = list->head;
+	free(list);
+	while (node != NULL) {
+		next = node->next;
+		free(node);
+		node = next;
+	}
+}
+
+static struct attr_list *parse_attrstr(char *attrstr)
+{
+	struct attr_list *list = NULL;
+	struct attr_node *node;
+	char *res;
+
+	do {
+		if (list == NULL) {
+			if ((res = strtok(attrstr, "=")) == NULL) {
+				/* nothing to parse */
+				return NULL;
+			}
+
+			list = malloc(sizeof(*list));
+			node = malloc(sizeof(*node));
+			list->head = node;
+		} else {
+			if ((res = strtok(NULL, "=")) == NULL) {
+				/* nothing left to parse */
+				break;
+			}
+
+			node->next = malloc(sizeof(*node));
+			node = node->next;
+		}
+
+		strncpy(node->key, res, sizeof(node->key));
+		node->key[sizeof(node->key) - 1] = '\0';
+
+		if ((res = strtok(NULL, ",")) == NULL) {
+			fprintf(stderr, "Invalid attribute");
+			goto err;
+		}
+		strncpy(node->val, res, sizeof(node->val));
+		node->val[sizeof(node->val) - 1] = '\0';
+
+		node->next = NULL;
+	} while (res != NULL);
+
+	return list;
+
+err:
+	attr_list_delete(list);
+	return NULL;
+}
+
+static int apply_attr(struct scope_node *scope_node,
+		      struct attr_list *list)
+{
+	struct attr_node *attr_node = list->head;
+	struct hciseq_attr *attr = scope_node->attr;
+	long lval;
+
+	while (attr_node != NULL) {
+		if (strcmp(attr_node->key, "delta") == 0) {
+			/* delta */
+			lval = strtol(attr_node->val, NULL, 10);
+			if (errno == ERANGE || errno == EINVAL)
+				return 1;
+
+			if (verbose) {
+				printf("\t[%d] set delta to %ld\n",
+				       scope_node->pos, lval);
+			}
+
+			attr->ts_diff.tv_sec = 0;
+			attr->ts_diff.tv_usec = lval;
+		} else if (strcmp(attr_node->key, "action") == 0) {
+			/* action */
+			if (strcmp(attr_node->val, "replay") == 0) {
+				lval = HCISEQ_ACTION_REPLAY;
+				if (verbose)
+					printf("\t[%d] set action to 'replay'\n",
+					     scope_node->pos);
+			} else if (strcmp(attr_node->val, "emulate") == 0) {
+				lval = HCISEQ_ACTION_EMULATE;
+				if (verbose)
+					printf("\t[%d] set action to 'emulate'\n",
+					     scope_node->pos);
+			} else if (strcmp(attr_node->val, "skip") == 0) {
+				lval = HCISEQ_ACTION_SKIP;
+				if (verbose)
+					printf("\t[%d] set action to 'skip'\n",
+					     scope_node->pos);
+			} else {
+				return 1;
+			}
+
+			attr->action = lval;
+		}
+
+		attr_node = attr_node->next;
+	}
+
+	return 0;
+}
+
+static int apply_attr_scope(struct scope_list *scope,
+			    struct attr_list *attr)
+{
+	struct scope_node *node = scope->head;
+
+	while (node != NULL) {
+		apply_attr(node, attr);
+		node = node->next;
+	}
+
+	return 0;
+}
+
+static struct scope_list *get_scope_range(int from, int to)
+{
+	struct scope_list *list = NULL;
+	struct scope_node *scope_node;
+	struct hciseq_node *seq_node = seq->current;
+	int pos = 1;
+
+	/* forward to 'from' */
+	while (pos < from) {
+		seq_node = seq_node->next;
+		pos++;
+	}
+
+	/* create scope list for range */
+	while (pos <= to) {
+		if (verbose)
+			printf("\tadd packet [%d]\n", pos);
+
+		if (list == NULL) {
+			list = malloc(sizeof(*list));
+			scope_node = malloc(sizeof(*scope_node));
+			list->head = scope_node;
+		} else {
+			scope_node->next = malloc(sizeof(*scope_node));
+			scope_node = scope_node->next;
+		}
+		scope_node->attr = seq_node->attr;
+		scope_node->pos = pos;
+		scope_node->next = NULL;
+
+		seq_node = seq_node->next;
+		pos++;
+	}
+
+	return list;
+}
+
+static struct scope_list *get_scope_type(uint8_t type, void *filter1,
+					 void *filter2)
+{
+	struct scope_list *list = NULL;
+	struct scope_node *scope_node;
+	struct hciseq_node *seq_node = seq->current;
+	uint16_t opcode, node_opcode;
+	uint8_t node_ogf, ogf = 0x00;
+	uint16_t node_ocf, ocf = 0x0000;
+	uint8_t node_evt, evt = 0x00;
+	bool match;
+	int pos = 1;
+	struct hciseq_attr *attr;
+
+	if (type == BT_H4_CMD_PKT) {
+		ogf = *((uint8_t *) filter1);
+		ocf = *((uint16_t *) filter2);
+		opcode = cmd_opcode_pack(ogf, ocf);
+
+		if (opcode > 0x2FFF) {
+			attr = NULL;
+		} else {
+			attr = type_cfg->cmd[opcode];
+			if (attr == NULL) {
+				attr = malloc(sizeof(*attr));
+				type_cfg->cmd[opcode] = attr;
+			}
+		}
+	} else if (type == BT_H4_EVT_PKT) {
+		evt = *((uint8_t *) filter1);
+		attr = type_cfg->evt[evt];
+
+		if (attr == NULL) {
+			attr = malloc(sizeof(*attr));
+			type_cfg->evt[evt] = attr;
+		}
+	} else if (type == BT_H4_ACL_PKT) {
+		attr = type_cfg->acl;
+		if (attr == NULL) {
+			attr = malloc(sizeof(*attr));
+			type_cfg->acl = attr;
+		}
+	} else {
+		attr = NULL;
+	}
+
+	/* add matching packets in sequence */
+	while (seq_node != NULL) {
+		match = false;
+		if (((uint8_t *) seq_node->frame->data)[0] == type) {
+			if (type == BT_H4_CMD_PKT) {
+				node_opcode = *((uint16_t *)
+						(seq_node->frame->data + 1));
+				node_ogf = cmd_opcode_ogf(node_opcode);
+				node_ocf = cmd_opcode_ocf(node_opcode);
+				if (node_ogf == ogf && node_ocf == ocf)
+					match = true;
+			} else if (type == BT_H4_EVT_PKT) {
+				node_evt = ((uint8_t *) seq_node->frame->data)[1];
+				if (evt == node_evt)
+					match = true;
+			} else if (type == BT_H4_ACL_PKT) {
+				match = true;
+			}
+		}
+
+		if (match) {
+			if (verbose)
+				printf("\tadd packet [%d]\n", pos);
+
+			if (list == NULL) {
+				list = malloc(sizeof(*list));
+				scope_node = malloc(sizeof(*scope_node));
+				list->head = scope_node;
+			} else {
+				scope_node->next = malloc(sizeof(*scope_node));
+				scope_node = scope_node->next;
+			}
+			scope_node->attr = seq_node->attr;
+			scope_node->pos = pos;
+			scope_node->next = NULL;
+		}
+		seq_node = seq_node->next;
+		pos++;
+	}
+
+	/* add type config */
+	if (attr != NULL) {
+		if (list == NULL) {
+			list = malloc(sizeof(*list));
+			scope_node = malloc(sizeof(*scope_node));
+			list->head = scope_node;
+		} else {
+			scope_node->next = malloc(sizeof(*scope_node));
+			scope_node = scope_node->next;
+		}
+
+		scope_node->attr = attr;
+		scope_node->pos = 0;
+		scope_node->next = NULL;
+	}
+
+	return list;
+}
+
+static int parse_line(char *buf)
+{
+	char *scopestr, *attrstr;
+	struct scope_list *scope_list = NULL;
+	struct attr_list *attr_list;
+	uint8_t evt, ogf;
+	uint16_t ocf;
+	char *res;
+	int from, to;
+
+	line++;
+
+	/* split line into scope and attributes */
+	if ((scopestr = strtok(buf, " ")) == NULL)
+		return 1;
+
+	if ((attrstr = strtok(NULL, "\n")) == NULL)
+		return 1;
+
+	if (verbose)
+		printf("Parsing scope (%s)\n", scopestr);
+
+	if (strcmp(scopestr, "all") == 0) {
+		if (verbose)
+			printf("\tadd all\n");
+
+		scope_list = get_scope_range(0, seq->len);
+	} else if ((strncmp(scopestr, "HCI_", 4) == 0) && strlen(scopestr) >= 7) {
+		/* make sure scopestr is at least 7 chars long, so we can check for HCI_XXX */
+
+		if (strncmp(scopestr + 4, "ACL", 3) == 0) {
+			/* scope is HCI_ACL */
+			if (verbose)
+				printf("\tadd all HCI ACL data packets:");
+
+			scope_list = get_scope_type(BT_H4_ACL_PKT, NULL, NULL);
+		} else if (strncmp(scopestr + 4, "CMD", 3) == 0) {
+			/* scope is HCI_CMD_
+			 * length must be exactly 19 (e.g. HCI_CMD_0x03|0x0003) */
+			if (strlen(scopestr) != 19 || scopestr[12] != '|')
+				return 1;
+
+			if (sscanf(scopestr + 8, "0x%2hhx", &ogf) <= 0)
+				return 1;
+
+			if (sscanf(scopestr + 13, "0x%4hx", &ocf) <= 0)
+				return 1;
+
+			if (verbose)
+				printf("\tadd all HCI command packets with opcode (0x%2.2x|0x%4.4x):\n",
+				     ogf, ocf);
+
+			scope_list = get_scope_type(BT_H4_CMD_PKT, &ogf, &ocf);
+		} else if (strncmp(scopestr + 4, "EVT", 3) == 0) {
+			/* scope is CMD_EVT_
+			 * length must be exactly 12 (e.g. HCI_EVT_0x0e) */
+			if (strlen(scopestr) != 12)
+				return 1;
+
+			if (sscanf(scopestr + 8, "0x%2hhx", &evt) <= 0)
+				return 1;
+
+			if (verbose)
+				printf("\tadd all HCI event packets with event type (0x%2.2x):\n",
+				     evt);
+
+			scope_list = get_scope_type(BT_H4_EVT_PKT, &evt, NULL);
+		}
+	} else if (scopestr[0] >= 48 || scopestr[0] <= 57) {
+		/* first char is a digit */
+		if ((res = strtok(scopestr, "-")) == NULL)
+			return 1;
+
+		from = atoi(res);
+		if (from <= 0)
+			return 1;
+
+		if ((res = strtok(NULL, ":")) == NULL) {
+			/* just one packet */
+			if (verbose)
+				printf("\tadd packet single packet\n");
+
+			scope_list = get_scope_range(from, from);
+		} else {
+			/* range */
+			to = atoi(res);
+			if (to > seq->len)
+				return 1;
+
+			if (verbose)
+				printf("\tadd packets %d to %d\n", from, to);
+
+			scope_list = get_scope_range(from, to);
+		}
+
+	}
+
+	if (verbose)
+		printf("Parsing attributes (%s)\n", attrstr);
+
+	if ((attr_list = parse_attrstr(attrstr)) == NULL) {
+		return 1;
+	}
+
+	if (scope_list != NULL) {
+		apply_attr_scope(scope_list, attr_list);
+		scope_list_delete(scope_list);
+	} else {
+		if (verbose)
+			printf("Empty scope, skipping\n");
+	}
+
+	attr_list_delete(attr_list);
+
+	return 0;
+}
+
+int parse_config(char *path, struct hciseq_list *_seq,
+		 struct hciseq_type_cfg *_type_cfg, bool _verbose)
+{
+	char *buf;
+	FILE *file;
+
+	seq = _seq;
+	type_cfg = _type_cfg;
+	verbose = _verbose;
+	line = 0;
+
+	printf("Reading config file...\n");
+
+	buf = malloc(sizeof(char) * MAXLINE);
+	if (buf == NULL) {
+		fprintf(stderr, "Failed to allocate buffer\n");
+		return 1;
+	}
+
+	if ((file = fopen(path, "r")) == NULL) {
+		perror("Failed to open config file");
+		return 1;
+	}
+
+	while (fgets(buf, MAXLINE, file) != NULL) {
+		if (parse_line(buf)) {
+			fprintf(stderr, "Error parsing config file - line %d\n",
+				line);
+			free(buf);
+			return 1;
+		}
+	}
+
+	printf("Done\n\n");
+	fclose(file);
+	free(buf);
+
+	return 0;
+}
diff --git a/tools/replay/config-parser.h b/tools/replay/config-parser.h
new file mode 100644
index 0000000..265628d
--- /dev/null
+++ b/tools/replay/config-parser.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *  Copyright (C) 2012       Anton Weber <ant@antweb.me>
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  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 "hciseq.h"
+
+struct hciseq_type_cfg {
+	struct hciseq_attr *cmd[9216]; /*
+					 * opcodes 0x0000 - 0x23FF
+					 * (OGF 0x01 - 0x08)
+					 */
+	struct hciseq_attr *evt[256]; /* events 0x00 - 0xFF */
+	struct hciseq_attr *acl;
+};
+
+int parse_config(char *path, struct hciseq_list *_seq,
+		 struct hciseq_type_cfg *_type_cfg, bool _verbose);
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index 1454147..fa589a3 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,7 +43,8 @@ struct frame {
 
 enum hciseq_action {
 	HCISEQ_ACTION_REPLAY = 0,
-	HCISEQ_ACTION_EMULATE = 1
+	HCISEQ_ACTION_EMULATE = 1,
+	HCISEQ_ACTION_SKIP = 2
 };
 
 struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index f108b25..9763b7a 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -41,6 +41,7 @@
 
 #include "main.h"
 #include "time.h"
+#include "config-parser.h"
 #include "lib/bluetooth.h"
 #include "lib/hci.h"
 #include "emulator/btdev.h"
@@ -56,6 +57,7 @@
 #define TIMING_DELTA 1
 
 static struct hciseq_list dumpseq;
+static struct hciseq_type_cfg type_cfg;
 
 static int fd;
 static int pos = 1;
@@ -281,6 +283,35 @@ static void btdev_recv(struct frame *frm)
 	btdev_receive_h4(btdev, frm->data, frm->data_len);
 }
 
+static struct hciseq_attr *get_type_attr(struct frame *frm)
+{
+	uint8_t pkt_type = ((const uint8_t *) frm->data)[0];
+	uint16_t opcode;
+	uint8_t evt;
+
+	switch (pkt_type) {
+	case BT_H4_CMD_PKT:
+		opcode = *((uint16_t *) (frm->data + 1));
+		if (opcode > 0x2FFF)
+			return NULL;
+		return type_cfg.cmd[opcode];
+	case BT_H4_EVT_PKT:
+		evt = *((uint8_t *) (frm->data + 1));
+
+		/* use attributes of opcode for 'Command Complete' events */
+		if (evt == 0x0e) {
+			opcode = *((uint16_t *) (frm->data + 4));
+			return type_cfg.cmd[opcode];
+		}
+
+		return type_cfg.evt[evt];
+	case BT_H4_ACL_PKT:
+		return type_cfg.acl;
+	default:
+		return NULL;
+	}
+}
+
 static bool check_match(struct frame *l, struct frame *r, char *msg)
 {
 	uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -341,6 +372,7 @@ static bool process_in()
 {
 	static struct frame frm;
 	static uint8_t data[HCI_MAX_FRAME_SIZE];
+	struct hciseq_attr *attr;
 	int n;
 	bool match;
 	char msg[MAX_MSG];
@@ -362,6 +394,38 @@ static bool process_in()
 	msg[0] = '\0';
 	match = check_match(dumpseq.current->frame, &frm, msg);
 
+	/* check type config */
+	attr = get_type_attr(&frm);
+	if (attr != NULL) {
+		if (attr->action == HCISEQ_ACTION_SKIP) {
+			if (match) {
+				printf("[%4d/%4d] SKIPPING\n", pos,
+				       dumpseq.len);
+				return 1;
+			} else {
+				printf("[ Unknown ] %s\n            ",
+				       msg);
+				dump_frame(&frm);
+				printf("            SKIPPING\n");
+				return 0;
+			}
+		}
+		if (attr->action == HCISEQ_ACTION_EMULATE) {
+			if (match) {
+				printf("[%4d/%4d] EMULATING\n", pos,
+				       dumpseq.len);
+			} else {
+				printf("[ Unknown ] %s\n            ",
+				       msg);
+				printf("EMULATING\n");
+			}
+
+			btdev_recv(&frm);
+
+			return match;
+		}
+	}
+
 	/* process packet if match */
 	if (match) {
 		printf("[%4d/%4d] ", pos, dumpseq.len);
@@ -381,12 +445,20 @@ static bool process_in()
 static bool process_out()
 {
 	uint8_t pkt_type;
+	struct hciseq_attr *attr;
 
 	/* emulator sends response automatically */
 	if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
 		return 1;
 	}
 
+	/* use type config if set */
+	attr = get_type_attr(dumpseq.current->frame);
+	if (attr != NULL) {
+		if (attr->action == HCISEQ_ACTION_SKIP || attr->action == HCISEQ_ACTION_EMULATE)
+			return true;
+	}
+
 	pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
 
 	switch (pkt_type) {
@@ -412,6 +484,15 @@ static void process()
 
 	gettimeofday(&last, NULL);
 	do {
+		if (dumpseq.current->attr->action == HCISEQ_ACTION_SKIP) {
+			printf("[%4d/%4d] SKIPPING\n            ", pos,
+			       dumpseq.len);
+			dump_frame(dumpseq.current->frame);
+			dumpseq.current = dumpseq.current->next;
+			pos++;
+			continue;
+		}
+
 		/* delay */
 		if (timing == TIMING_DELTA) {
 			/* consider exec time of process_out()/process_in() */
@@ -485,6 +566,19 @@ static void delete_list()
 	}
 }
 
+static void delete_type_cfg()
+{
+	int i;
+
+	for (i = 0; i < 12288; i++) {
+		free(type_cfg.cmd[i]);
+	}
+	for (i = 0; i < 256; i++) {
+		free(type_cfg.evt[i]);
+	}
+	free(type_cfg.acl);
+}
+
 static void usage(void)
 {
 	printf("hcireplay - Bluetooth replayer\n"
@@ -493,6 +587,7 @@ static void usage(void)
 	       "\t-d, --timing={none|delta}    Specify timing mode\n"
 	       "\t-m, --factor=<value>         Use timing modifier\n"
 	       "\t-t, --timeout=<value>        Use timeout when receiving\n"
+	       "\t-c, --config=<file>          Use config file\n"
 	       "\t-v, --verbose                Enable verbose output\n"
 	       "\t    --version                Give version information\n"
 	       "\t    --help                   Give a short usage message\n");
@@ -502,6 +597,7 @@ static const struct option main_options[] = {
 	{"timing", required_argument, NULL, 'd'},
 	{"factor", required_argument, NULL, 'm'},
 	{"timeout", required_argument, NULL, 't'},
+	{"config", required_argument, NULL, 'c'},
 	{"verbose", no_argument, NULL, 'v'},
 	{"version", no_argument, NULL, 'V'},
 	{"help", no_argument, NULL, 'H'},
@@ -512,11 +608,12 @@ int main(int argc, char *argv[])
 {
 	int dumpfd;
 	int i;
+	char *config = NULL;
 
 	while (1) {
 		int opt;
 
-		opt = getopt_long(argc, argv, "d:m:t:v",
+		opt = getopt_long(argc, argv, "d:m:t:c:v",
 						main_options, NULL);
 		if (opt < 0)
 			break;
@@ -535,6 +632,9 @@ int main(int argc, char *argv[])
 		case 't':
 			timeout = atoi(optarg);
 			break;
+		case 'c':
+			config = optarg;
+			break;
 		case 'v':
 			verbose = true;
 			break;
@@ -572,6 +672,20 @@ int main(int argc, char *argv[])
 	dumpseq.current = dumpseq.frames;
 	calc_rel_ts(&dumpseq);
 
+	/* init type config */
+	for (i = 0; i < 9216; i++)
+		type_cfg.cmd[i] = NULL;
+	for (i = 0; i < 256; i++)
+		type_cfg.evt[i] = NULL;
+	type_cfg.acl = NULL;
+
+	if (config != NULL) {
+		if (parse_config(config, &dumpseq, &type_cfg, verbose)) {
+			vhci_close();
+			return 1;
+		}
+	}
+
 	/* init emulator */
 	btdev = btdev_create(0);
 	btdev_set_send_handler(btdev, btdev_send, NULL);
@@ -595,6 +709,7 @@ int main(int argc, char *argv[])
 	vhci_close();
 	btdev_destroy(btdev);
 	delete_list();
+	delete_type_cfg();
 	printf("Terminating\n");
 
 	return EXIT_SUCCESS;
diff --git a/tools/replay/main.h b/tools/replay/main.h
index 2223789..e8b7365 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -23,8 +23,6 @@
  *
  */
 
-#include "hciseq.h"
-
 struct btsnoop_hdr {
 	uint8_t id[8];		/* Identification Pattern */
 	uint32_t version;	/* Version Number = 1 */
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH BlueZ 3/4] replay: Add emulation support
From: Anton Weber @ 2012-08-09 17:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Anton Weber
In-Reply-To: <1344533773-12914-1-git-send-email-ant@antweb.me>

Integrate emulator so it can process selected packets instead of
replaying them from the sequence. The behaviour is controlled
through the action attribute in hciseq_attr.

If set to HCISEQ_ACTION_EMULATE, hcireplay will use the emulator
to process the packet automatically instead of replaying it.
HCISEQ_ACTION_REPLAY keeps the previous behaviour and replays the
packet using the dump file.
---
 Makefile.tools        |    1 +
 tools/replay/hciseq.h |    1 +
 tools/replay/main.c   |   52 ++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/Makefile.tools b/Makefile.tools
index 6767300..8b3c8d8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -76,6 +76,7 @@ tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
 					monitor/btsnoop.h monitor/btsnoop.c \
 					monitor/control.h monitor/control.c \
 					monitor/mainloop.h monitor/mainloop.c \
+					emulator/btdev.h emulator/btdev.c \
 					lib/hci.h
 
 tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index f9fe7c8..1454147 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,6 +43,7 @@ struct frame {
 
 enum hciseq_action {
 	HCISEQ_ACTION_REPLAY = 0,
+	HCISEQ_ACTION_EMULATE = 1
 };
 
 struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 49ea21d..f108b25 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -43,6 +43,7 @@
 #include "time.h"
 #include "lib/bluetooth.h"
 #include "lib/hci.h"
+#include "emulator/btdev.h"
 #include "monitor/bt.h"
 #include "monitor/btsnoop.h"
 #include "monitor/control.h"
@@ -69,6 +70,8 @@ static int timing = TIMING_NONE;
 static double factor = 1;
 static bool verbose = false;
 
+static struct btdev *btdev;
+
 static inline int read_n(int fd, char *buf, int len)
 {
 	int t = 0, w;
@@ -250,6 +253,34 @@ static int recv_frm(int fd, struct frame *frm)
 	return n;
 }
 
+static void btdev_send(const void *data, uint16_t len, void *user_data)
+{
+	struct frame frm;
+	static void* tmpdata = NULL;
+
+	/* copy data so we respect 'const' qualifier */
+	if(tmpdata == NULL)
+		tmpdata = malloc(HCI_MAX_FRAME_SIZE);
+
+	memcpy(tmpdata, data, len);
+
+	frm.data = tmpdata;
+	frm.len = len;
+	frm.data_len = len;
+	frm.in = 1;
+	printf("[Emulator ] ");
+	dump_frame(&frm);
+	send_frm(&frm);
+}
+
+static void btdev_recv(struct frame *frm)
+{
+	frm->in = 0;
+	printf("[Emulator ] ");
+	dump_frame(frm);
+	btdev_receive_h4(btdev, frm->data, frm->data_len);
+}
+
 static bool check_match(struct frame *l, struct frame *r, char *msg)
 {
 	uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -332,11 +363,16 @@ static bool process_in()
 	match = check_match(dumpseq.current->frame, &frm, msg);
 
 	/* process packet if match */
-	if (match)
+	if (match) {
 		printf("[%4d/%4d] ", pos, dumpseq.len);
-	else
-		printf("[ Unknown ] %s\n            ", msg);
 
+		if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+			btdev_recv(&frm);
+			return true;
+		}
+	} else {
+		printf("[ Unknown ] %s\n            ", msg);
+	}
 	dump_frame(&frm);
 
 	return match;
@@ -346,6 +382,11 @@ static bool process_out()
 {
 	uint8_t pkt_type;
 
+	/* emulator sends response automatically */
+	if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+		return 1;
+	}
+
 	pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
 
 	switch (pkt_type) {
@@ -531,6 +572,10 @@ int main(int argc, char *argv[])
 	dumpseq.current = dumpseq.frames;
 	calc_rel_ts(&dumpseq);
 
+	/* init emulator */
+	btdev = btdev_create(0);
+	btdev_set_send_handler(btdev, btdev_send, NULL);
+
 	gettimeofday(&start, NULL);
 
 	/*
@@ -548,6 +593,7 @@ int main(int argc, char *argv[])
 	process();
 
 	vhci_close();
+	btdev_destroy(btdev);
 	delete_list();
 	printf("Terminating\n");
 
-- 
1.7.9.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox