/* HCI priority */
#define HCI_PRIO_MAX 7
+#ifdef TIZEN_BT
+/* Reserved ACL slots for Streaming packets */
+#define STREAMING_RESERVED_SLOTS 2
+#endif
+
/* HCI Core structures */
struct inquiry_data {
bdaddr_t bdaddr;
unsigned int acl_cnt;
unsigned int sco_cnt;
unsigned int le_cnt;
+#ifdef TIZEN_BT
+ unsigned int streaming_cnt;
+#endif
unsigned int acl_mtu;
unsigned int sco_mtu;
struct discovery_state le_discovery;
#endif
struct hci_conn_hash conn_hash;
+#ifdef TIZEN_BT
+ struct hci_conn *streaming_conn;
+#endif
struct list_head mgmt_pending;
struct list_head blacklist;
unsigned int sent;
#ifdef TIZEN_BT
+ unsigned int streaming_sent;
__u16 tx_len;
__u16 tx_time;
__u16 rx_len;
bdaddr_t *bdaddr, u16 tx_octets, u16 tx_time,
u16 rx_octets, u16 rx_time);
int hci_le_set_data_length(struct hci_conn *conn, u16 tx_octets, u16 tx_time);
+int hci_conn_streaming_mode(struct hci_conn *conn, bool streaming_mode);
#endif
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
} __packed;
#define MGMT_LE_SET_DATA_LENGTH_RSP_SIZE 3
+#define MGMT_OP_SET_STREAMING_MODE (TIZEN_OP_CODE_BASE + 0x1b)
+struct mgmt_cp_set_streaming_mode {
+ uint8_t streaming_mode;
+ bdaddr_t bdaddr;
+} __packed;
+#define MGMT_SET_STREAMING_MODE_SIZE 7
+
/* EVENTS */
/* For device name update changes */
/* Unacked frames */
hdev->acl_cnt += conn->sent;
+#ifdef TIZEN_BT
+ if (hdev->streaming_conn == conn) {
+ hdev->streaming_conn = NULL;
+ hdev->streaming_cnt = 0;
+ }
+#endif
} else if (conn->type == LE_LINK) {
cancel_delayed_work(&conn->le_conn_timeout);
return hchan;
}
+
+#ifdef TIZEN_BT
+int hci_conn_streaming_mode(struct hci_conn *conn, bool streaming_mode)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ if (streaming_mode &&
+ conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+ return -ENOTCONN;
+
+ if (conn->type != ACL_LINK)
+ return -EOPNOTSUPP;
+
+ if (hdev->acl_pkts < STREAMING_RESERVED_SLOTS * 2)
+ return -EOPNOTSUPP;
+
+ if (streaming_mode) {
+ if (hdev->streaming_conn) {
+ if (hdev->streaming_conn == conn)
+ return 0;
+
+ BT_ERR("already set [%p], request conn [%p]",
+ hdev->streaming_conn, conn);
+ return -EOPNOTSUPP;
+ }
+
+ hdev->streaming_cnt = STREAMING_RESERVED_SLOTS;
+ conn->streaming_sent = 0;
+ hdev->streaming_conn = conn;
+ } else {
+ if (hdev->streaming_conn != conn)
+ return -ENOENT;
+
+ hdev->streaming_conn = NULL;
+ hdev->streaming_cnt = 0;
+ conn->streaming_sent = 0;
+ }
+
+ return 0;
+}
+#endif
switch (chan->conn->type) {
case ACL_LINK:
+#ifdef TIZEN_BT
+ cnt = hdev->acl_cnt - hdev->streaming_cnt;
+ if (cnt <= 0)
+ return NULL;
+ break;
+#else
cnt = hdev->acl_cnt;
break;
+#endif
case AMP_LINK:
cnt = hdev->block_cnt;
break;
}
}
+#ifdef TIZEN_BT
+static void hci_sched_streaming_conn(struct hci_dev *hdev)
+{
+ struct hci_conn *conn = hdev->streaming_conn;
+
+ if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+ return;
+
+ while (hdev->streaming_cnt && hdev->acl_cnt) {
+ struct hci_chan *tmp;
+ struct sk_buff *skb;
+ struct hci_chan *chan = NULL;
+ unsigned int priority = 0;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(tmp, &conn->chan_list, list) {
+ if (skb_queue_empty(&tmp->data_q))
+ continue;
+
+ skb = skb_peek(&tmp->data_q);
+ if (skb->priority < priority)
+ continue;
+
+ if (skb->priority > priority) {
+ chan = tmp;
+ priority = skb->priority;
+ } else if (!chan) {
+ chan = tmp;
+ priority = skb->priority;
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (!chan)
+ break;
+
+ while (hdev->streaming_cnt && hdev->acl_cnt &&
+ (skb = skb_peek(&chan->data_q))) {
+ /* Stop if priority has changed */
+ if (skb->priority < priority)
+ break;
+
+ skb = skb_dequeue(&chan->data_q);
+
+ hci_conn_enter_active_mode(chan->conn,
+ bt_cb(skb)->force_active);
+
+ hci_send_frame(hdev, skb);
+ hdev->acl_last_tx = jiffies;
+
+ hdev->acl_cnt--;
+ hdev->streaming_cnt--;
+ chan->sent++;
+
+ conn->streaming_sent++;
+ conn->sent++;
+ }
+ }
+}
+#endif
+
static void hci_sched_acl_pkt(struct hci_dev *hdev)
{
unsigned int cnt = hdev->acl_cnt;
__check_timeout(hdev, cnt);
+#ifdef TIZEN_BT
+ while (hdev->acl_cnt > hdev->streaming_cnt &&
+ (chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
+#else
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
+#endif
u32 priority = (skb_peek(&chan->data_q))->priority;
while (quote-- && (skb = skb_peek(&chan->data_q))) {
BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
hdev->acl_cnt--;
chan->sent++;
+
chan->conn->sent++;
}
}
+#ifdef TIZEN_BT
+ if (hdev->streaming_conn)
+ hci_sched_streaming_conn(hdev);
+#endif
+
if (cnt != hdev->acl_cnt)
hci_prio_recalculate(hdev, ACL_LINK);
}
continue;
conn->sent -= count;
+#ifdef TIZEN_BT
+ if (hdev->streaming_conn && conn->sent < conn->streaming_sent) {
+ hdev->streaming_cnt +=
+ (conn->streaming_sent - conn->sent);
+ if (hdev->streaming_cnt > STREAMING_RESERVED_SLOTS)
+ hdev->streaming_cnt = STREAMING_RESERVED_SLOTS;
+
+ conn->streaming_sent = conn->sent;
+ }
+#endif
switch (conn->type) {
case ACL_LINK:
mgmt_event(MGMT_EV_LE_DEVICE_FOUND, hdev, ev, ev_size, NULL);
}
+
+static int set_streaming_mode(struct sock *sk, struct hci_dev *hdev,
+ void *cp_data, u16 len)
+{
+ struct mgmt_cp_set_streaming_mode *cp = cp_data;
+ struct hci_conn *conn;
+ int err;
+ int status = MGMT_STATUS_SUCCESS;
+
+ BT_DBG("request for %s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (!conn) {
+ err = -ENOTCONN;
+ goto unlock;
+ }
+
+ err = hci_conn_streaming_mode(conn, cp->streaming_mode);
+
+unlock:
+ if (err == -ENOTCONN)
+ status = MGMT_STATUS_NOT_CONNECTED;
+ else if (err == -EOPNOTSUPP)
+ status = MGMT_STATUS_NOT_SUPPORTED;
+ else if (err == -ENOENT)
+ status = MGMT_STATUS_INVALID_PARAMS;
+
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_STREAMING_MODE,
+ status, NULL, 0);
+
+ hci_dev_unlock(hdev);
+
+ return err;
+}
#endif
static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
MGMT_LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE },
{ set_le_data_length_params,
MGMT_LE_SET_DATA_LENGTH_SIZE },
+ { set_streaming_mode, MGMT_SET_STREAMING_MODE_SIZE },
};
#endif