X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=net%2Fbluetooth%2Fhci_conn.c;h=90e3a285a17eaf9a4bde198879748d8a4da680a7;hb=0cd43f83d381c4246a08cd775834833d6fd64805;hp=5c0c2b1ef34a5f152a13aae2796e1e1497fb7508;hpb=05668381140309088443bf5dc53add4104610fbb;p=linux-2.6-omap-h63xx.git diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5c0c2b1ef34..90e3a285a17 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -84,6 +84,20 @@ static void hci_acl_connect(struct hci_conn *conn) hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, sizeof(cp), &cp); } +static void hci_acl_connect_cancel(struct hci_conn *conn) +{ + struct hci_cp_create_conn_cancel cp; + + BT_DBG("%p", conn); + + if (conn->hdev->hci_ver < 2) + return; + + bacpy(&cp.bdaddr, &conn->dst); + hci_send_cmd(conn->hdev, OGF_LINK_CTL, + OCF_CREATE_CONN_CANCEL, sizeof(cp), &cp); +} + void hci_acl_disconn(struct hci_conn *conn, __u8 reason) { struct hci_cp_disconnect cp; @@ -94,7 +108,8 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) cp.handle = __cpu_to_le16(conn->handle); cp.reason = reason; - hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, OGF_LINK_CTL, + OCF_DISCONNECT, sizeof(cp), &cp); } void hci_add_sco(struct hci_conn *conn, __u16 handle) @@ -115,8 +130,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) static void hci_conn_timeout(unsigned long arg) { - struct hci_conn *conn = (void *)arg; - struct hci_dev *hdev = conn->hdev; + struct hci_conn *conn = (void *) arg; + struct hci_dev *hdev = conn->hdev; BT_DBG("conn %p state %d", conn, conn->state); @@ -124,19 +139,29 @@ static void hci_conn_timeout(unsigned long arg) return; hci_dev_lock(hdev); - if (conn->state == BT_CONNECTED) + + switch (conn->state) { + case BT_CONNECT: + hci_acl_connect_cancel(conn); + break; + case BT_CONNECTED: hci_acl_disconn(conn, 0x13); - else + break; + default: conn->state = BT_CLOSED; + break; + } + hci_dev_unlock(hdev); - return; } -static void hci_conn_init_timer(struct hci_conn *conn) +static void hci_conn_idle(unsigned long arg) { - init_timer(&conn->timer); - conn->timer.function = hci_conn_timeout; - conn->timer.data = (unsigned long)conn; + struct hci_conn *conn = (void *) arg; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + hci_conn_enter_sniff_mode(conn); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) @@ -145,17 +170,27 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) BT_DBG("%s dst %s", hdev->name, batostr(dst)); - if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC))) + conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC); + if (!conn) return NULL; - memset(conn, 0, sizeof(struct hci_conn)); bacpy(&conn->dst, dst); - conn->type = type; conn->hdev = hdev; + conn->type = type; + conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; + conn->power_save = 1; + skb_queue_head_init(&conn->data_q); - hci_conn_init_timer(conn); + + init_timer(&conn->disc_timer); + conn->disc_timer.function = hci_conn_timeout; + conn->disc_timer.data = (unsigned long) conn; + + init_timer(&conn->idle_timer); + conn->idle_timer.function = hci_conn_idle; + conn->idle_timer.data = (unsigned long) conn; atomic_set(&conn->refcnt, 0); @@ -167,6 +202,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); + hci_conn_add_sysfs(conn); + tasklet_enable(&hdev->tx_task); return conn; @@ -178,7 +215,9 @@ int hci_conn_del(struct hci_conn *conn) BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); - hci_conn_del_timer(conn); + del_timer(&conn->idle_timer); + + del_timer(&conn->disc_timer); if (conn->type == SCO_LINK) { struct hci_conn *acl = conn->link; @@ -197,6 +236,8 @@ int hci_conn_del(struct hci_conn *conn) tasklet_disable(&hdev->tx_task); + hci_conn_del_sysfs(conn); + hci_conn_hash_del(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); @@ -207,7 +248,9 @@ int hci_conn_del(struct hci_conn *conn) hci_dev_put(hdev); - kfree(conn); + /* will free via device release */ + put_device(&conn->dev); + return 0; } @@ -364,6 +407,70 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) } EXPORT_SYMBOL(hci_conn_switch_role); +/* Enter active mode */ +void hci_conn_enter_active_mode(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + if (test_bit(HCI_RAW, &hdev->flags)) + return; + + if (conn->mode != HCI_CM_SNIFF || !conn->power_save) + goto timer; + + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + struct hci_cp_exit_sniff_mode cp; + cp.handle = __cpu_to_le16(conn->handle); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp); + } + +timer: + if (hdev->idle_timeout > 0) + mod_timer(&conn->idle_timer, + jiffies + msecs_to_jiffies(hdev->idle_timeout)); +} + +/* Enter sniff mode */ +void hci_conn_enter_sniff_mode(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + if (test_bit(HCI_RAW, &hdev->flags)) + return; + + if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) + return; + + if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) + return; + + if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { + struct hci_cp_sniff_subrate cp; + cp.handle = __cpu_to_le16(conn->handle); + cp.max_latency = __constant_cpu_to_le16(0); + cp.min_remote_timeout = __constant_cpu_to_le16(0); + cp.min_local_timeout = __constant_cpu_to_le16(0); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_SNIFF_SUBRATE, sizeof(cp), &cp); + } + + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + struct hci_cp_sniff_mode cp; + cp.handle = __cpu_to_le16(conn->handle); + cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval); + cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval); + cp.attempt = __constant_cpu_to_le16(4); + cp.timeout = __constant_cpu_to_le16(1); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_SNIFF_MODE, sizeof(cp), &cp); + } +} + /* Drop all connection on the device */ void hci_conn_hash_flush(struct hci_dev *hdev) {