18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci HIDP implementation for Linux Bluetooth stack (BlueZ). 38c2ecf20Sopenharmony_ci Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> 48c2ecf20Sopenharmony_ci Copyright (C) 2013 David Herrmann <dh.herrmann@gmail.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci This program is free software; you can redistribute it and/or modify 78c2ecf20Sopenharmony_ci it under the terms of the GNU General Public License version 2 as 88c2ecf20Sopenharmony_ci published by the Free Software Foundation; 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 118c2ecf20Sopenharmony_ci OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 128c2ecf20Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 138c2ecf20Sopenharmony_ci IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 148c2ecf20Sopenharmony_ci CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 158c2ecf20Sopenharmony_ci WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 168c2ecf20Sopenharmony_ci ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 178c2ecf20Sopenharmony_ci OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 208c2ecf20Sopenharmony_ci COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 218c2ecf20Sopenharmony_ci SOFTWARE IS DISCLAIMED. 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/kref.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/file.h> 278c2ecf20Sopenharmony_ci#include <linux/kthread.h> 288c2ecf20Sopenharmony_ci#include <linux/hidraw.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 318c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h> 328c2ecf20Sopenharmony_ci#include <net/bluetooth/l2cap.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "hidp.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VERSION "1.2" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(hidp_session_sem); 398c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(hidp_session_wq); 408c2ecf20Sopenharmony_cistatic LIST_HEAD(hidp_session_list); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic unsigned char hidp_keycode[256] = { 438c2ecf20Sopenharmony_ci 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 448c2ecf20Sopenharmony_ci 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 458c2ecf20Sopenharmony_ci 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 468c2ecf20Sopenharmony_ci 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, 478c2ecf20Sopenharmony_ci 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, 488c2ecf20Sopenharmony_ci 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69, 498c2ecf20Sopenharmony_ci 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, 508c2ecf20Sopenharmony_ci 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190, 518c2ecf20Sopenharmony_ci 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, 528c2ecf20Sopenharmony_ci 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, 538c2ecf20Sopenharmony_ci 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0, 548c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 558c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 568c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 578c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 588c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 598c2ecf20Sopenharmony_ci 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115, 608c2ecf20Sopenharmony_ci 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int hidp_session_probe(struct l2cap_conn *conn, 668c2ecf20Sopenharmony_ci struct l2cap_user *user); 678c2ecf20Sopenharmony_cistatic void hidp_session_remove(struct l2cap_conn *conn, 688c2ecf20Sopenharmony_ci struct l2cap_user *user); 698c2ecf20Sopenharmony_cistatic int hidp_session_thread(void *arg); 708c2ecf20Sopenharmony_cistatic void hidp_session_terminate(struct hidp_session *s); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u32 valid_flags = 0; 758c2ecf20Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 768c2ecf20Sopenharmony_ci bacpy(&ci->bdaddr, &session->bdaddr); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ci->flags = session->flags & valid_flags; 798c2ecf20Sopenharmony_ci ci->state = BT_CONNECTED; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (session->input) { 828c2ecf20Sopenharmony_ci ci->vendor = session->input->id.vendor; 838c2ecf20Sopenharmony_ci ci->product = session->input->id.product; 848c2ecf20Sopenharmony_ci ci->version = session->input->id.version; 858c2ecf20Sopenharmony_ci if (session->input->name) 868c2ecf20Sopenharmony_ci strlcpy(ci->name, session->input->name, 128); 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci strlcpy(ci->name, "HID Boot Device", 128); 898c2ecf20Sopenharmony_ci } else if (session->hid) { 908c2ecf20Sopenharmony_ci ci->vendor = session->hid->vendor; 918c2ecf20Sopenharmony_ci ci->product = session->hid->product; 928c2ecf20Sopenharmony_ci ci->version = session->hid->version; 938c2ecf20Sopenharmony_ci strlcpy(ci->name, session->hid->name, 128); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* assemble skb, queue message on @transmit and wake up the session thread */ 988c2ecf20Sopenharmony_cistatic int hidp_send_message(struct hidp_session *session, struct socket *sock, 998c2ecf20Sopenharmony_ci struct sk_buff_head *transmit, unsigned char hdr, 1008c2ecf20Sopenharmony_ci const unsigned char *data, int size) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct sk_buff *skb; 1038c2ecf20Sopenharmony_ci struct sock *sk = sock->sk; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci BT_DBG("session %p data %p size %d", session, data, size); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (atomic_read(&session->terminate)) 1098c2ecf20Sopenharmony_ci return -EIO; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci skb = alloc_skb(size + 1, GFP_ATOMIC); 1128c2ecf20Sopenharmony_ci if (!skb) { 1138c2ecf20Sopenharmony_ci BT_ERR("Can't allocate memory for new frame"); 1148c2ecf20Sopenharmony_ci return -ENOMEM; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci skb_put_u8(skb, hdr); 1188c2ecf20Sopenharmony_ci if (data && size > 0) { 1198c2ecf20Sopenharmony_ci skb_put_data(skb, data, size); 1208c2ecf20Sopenharmony_ci ret = size; 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci ret = 0; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci skb_queue_tail(transmit, skb); 1268c2ecf20Sopenharmony_ci wake_up_interruptible(sk_sleep(sk)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int hidp_send_ctrl_message(struct hidp_session *session, 1328c2ecf20Sopenharmony_ci unsigned char hdr, const unsigned char *data, 1338c2ecf20Sopenharmony_ci int size) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci return hidp_send_message(session, session->ctrl_sock, 1368c2ecf20Sopenharmony_ci &session->ctrl_transmit, hdr, data, size); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int hidp_send_intr_message(struct hidp_session *session, 1408c2ecf20Sopenharmony_ci unsigned char hdr, const unsigned char *data, 1418c2ecf20Sopenharmony_ci int size) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return hidp_send_message(session, session->intr_sock, 1448c2ecf20Sopenharmony_ci &session->intr_transmit, hdr, data, size); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int hidp_input_event(struct input_dev *dev, unsigned int type, 1488c2ecf20Sopenharmony_ci unsigned int code, int value) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct hidp_session *session = input_get_drvdata(dev); 1518c2ecf20Sopenharmony_ci unsigned char newleds; 1528c2ecf20Sopenharmony_ci unsigned char hdr, data[2]; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci BT_DBG("session %p type %d code %d value %d", 1558c2ecf20Sopenharmony_ci session, type, code, value); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (type != EV_LED) 1588c2ecf20Sopenharmony_ci return -1; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci newleds = (!!test_bit(LED_KANA, dev->led) << 3) | 1618c2ecf20Sopenharmony_ci (!!test_bit(LED_COMPOSE, dev->led) << 3) | 1628c2ecf20Sopenharmony_ci (!!test_bit(LED_SCROLLL, dev->led) << 2) | 1638c2ecf20Sopenharmony_ci (!!test_bit(LED_CAPSL, dev->led) << 1) | 1648c2ecf20Sopenharmony_ci (!!test_bit(LED_NUML, dev->led) << 0); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (session->leds == newleds) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci session->leds = newleds; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; 1728c2ecf20Sopenharmony_ci data[0] = 0x01; 1738c2ecf20Sopenharmony_ci data[1] = newleds; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return hidp_send_intr_message(session, hdr, data, 2); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct input_dev *dev = session->input; 1818c2ecf20Sopenharmony_ci unsigned char *keys = session->keys; 1828c2ecf20Sopenharmony_ci unsigned char *udata = skb->data + 1; 1838c2ecf20Sopenharmony_ci signed char *sdata = skb->data + 1; 1848c2ecf20Sopenharmony_ci int i, size = skb->len - 1; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci switch (skb->data[0]) { 1878c2ecf20Sopenharmony_ci case 0x01: /* Keyboard report */ 1888c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1898c2ecf20Sopenharmony_ci input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* If all the key codes have been set to 0x01, it means 1928c2ecf20Sopenharmony_ci * too many keys were pressed at the same time. */ 1938c2ecf20Sopenharmony_ci if (!memcmp(udata + 2, hidp_mkeyspat, 6)) 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci for (i = 2; i < 8; i++) { 1978c2ecf20Sopenharmony_ci if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) { 1988c2ecf20Sopenharmony_ci if (hidp_keycode[keys[i]]) 1998c2ecf20Sopenharmony_ci input_report_key(dev, hidp_keycode[keys[i]], 0); 2008c2ecf20Sopenharmony_ci else 2018c2ecf20Sopenharmony_ci BT_ERR("Unknown key (scancode %#x) released.", keys[i]); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) { 2058c2ecf20Sopenharmony_ci if (hidp_keycode[udata[i]]) 2068c2ecf20Sopenharmony_ci input_report_key(dev, hidp_keycode[udata[i]], 1); 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci memcpy(keys, udata, 8); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci case 0x02: /* Mouse report */ 2168c2ecf20Sopenharmony_ci input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); 2178c2ecf20Sopenharmony_ci input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); 2188c2ecf20Sopenharmony_ci input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); 2198c2ecf20Sopenharmony_ci input_report_key(dev, BTN_SIDE, sdata[0] & 0x08); 2208c2ecf20Sopenharmony_ci input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci input_report_rel(dev, REL_X, sdata[1]); 2238c2ecf20Sopenharmony_ci input_report_rel(dev, REL_Y, sdata[2]); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (size > 3) 2268c2ecf20Sopenharmony_ci input_report_rel(dev, REL_WHEEL, sdata[3]); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci input_sync(dev); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int hidp_get_raw_report(struct hid_device *hid, 2348c2ecf20Sopenharmony_ci unsigned char report_number, 2358c2ecf20Sopenharmony_ci unsigned char *data, size_t count, 2368c2ecf20Sopenharmony_ci unsigned char report_type) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct hidp_session *session = hid->driver_data; 2398c2ecf20Sopenharmony_ci struct sk_buff *skb; 2408c2ecf20Sopenharmony_ci size_t len; 2418c2ecf20Sopenharmony_ci int numbered_reports = hid->report_enum[report_type].numbered; 2428c2ecf20Sopenharmony_ci int ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (atomic_read(&session->terminate)) 2458c2ecf20Sopenharmony_ci return -EIO; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci switch (report_type) { 2488c2ecf20Sopenharmony_ci case HID_FEATURE_REPORT: 2498c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case HID_INPUT_REPORT: 2528c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci case HID_OUTPUT_REPORT: 2558c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci default: 2588c2ecf20Sopenharmony_ci return -EINVAL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&session->report_mutex)) 2628c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Set up our wait, and send the report request to the device. */ 2658c2ecf20Sopenharmony_ci session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; 2668c2ecf20Sopenharmony_ci session->waiting_report_number = numbered_reports ? report_number : -1; 2678c2ecf20Sopenharmony_ci set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); 2688c2ecf20Sopenharmony_ci data[0] = report_number; 2698c2ecf20Sopenharmony_ci ret = hidp_send_ctrl_message(session, report_type, data, 1); 2708c2ecf20Sopenharmony_ci if (ret < 0) 2718c2ecf20Sopenharmony_ci goto err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Wait for the return of the report. The returned report 2748c2ecf20Sopenharmony_ci gets put in session->report_return. */ 2758c2ecf20Sopenharmony_ci while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && 2768c2ecf20Sopenharmony_ci !atomic_read(&session->terminate)) { 2778c2ecf20Sopenharmony_ci int res; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci res = wait_event_interruptible_timeout(session->report_queue, 2808c2ecf20Sopenharmony_ci !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) 2818c2ecf20Sopenharmony_ci || atomic_read(&session->terminate), 2828c2ecf20Sopenharmony_ci 5*HZ); 2838c2ecf20Sopenharmony_ci if (res == 0) { 2848c2ecf20Sopenharmony_ci /* timeout */ 2858c2ecf20Sopenharmony_ci ret = -EIO; 2868c2ecf20Sopenharmony_ci goto err; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci if (res < 0) { 2898c2ecf20Sopenharmony_ci /* signal */ 2908c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 2918c2ecf20Sopenharmony_ci goto err; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci skb = session->report_return; 2968c2ecf20Sopenharmony_ci if (skb) { 2978c2ecf20Sopenharmony_ci len = skb->len < count ? skb->len : count; 2988c2ecf20Sopenharmony_ci memcpy(data, skb->data, len); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci kfree_skb(skb); 3018c2ecf20Sopenharmony_ci session->report_return = NULL; 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci /* Device returned a HANDSHAKE, indicating protocol error. */ 3048c2ecf20Sopenharmony_ci len = -EIO; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); 3088c2ecf20Sopenharmony_ci mutex_unlock(&session->report_mutex); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return len; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cierr: 3138c2ecf20Sopenharmony_ci clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); 3148c2ecf20Sopenharmony_ci mutex_unlock(&session->report_mutex); 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum, 3198c2ecf20Sopenharmony_ci unsigned char *data, size_t count, 3208c2ecf20Sopenharmony_ci unsigned char report_type) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct hidp_session *session = hid->driver_data; 3238c2ecf20Sopenharmony_ci int ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci switch (report_type) { 3268c2ecf20Sopenharmony_ci case HID_FEATURE_REPORT: 3278c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case HID_INPUT_REPORT: 3308c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT; 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case HID_OUTPUT_REPORT: 3338c2ecf20Sopenharmony_ci report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&session->report_mutex)) 3408c2ecf20Sopenharmony_ci return -ERESTARTSYS; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Set up our wait, and send the report request to the device. */ 3438c2ecf20Sopenharmony_ci data[0] = reportnum; 3448c2ecf20Sopenharmony_ci set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); 3458c2ecf20Sopenharmony_ci ret = hidp_send_ctrl_message(session, report_type, data, count); 3468c2ecf20Sopenharmony_ci if (ret < 0) 3478c2ecf20Sopenharmony_ci goto err; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Wait for the ACK from the device. */ 3508c2ecf20Sopenharmony_ci while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) && 3518c2ecf20Sopenharmony_ci !atomic_read(&session->terminate)) { 3528c2ecf20Sopenharmony_ci int res; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci res = wait_event_interruptible_timeout(session->report_queue, 3558c2ecf20Sopenharmony_ci !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) 3568c2ecf20Sopenharmony_ci || atomic_read(&session->terminate), 3578c2ecf20Sopenharmony_ci 10*HZ); 3588c2ecf20Sopenharmony_ci if (res == 0) { 3598c2ecf20Sopenharmony_ci /* timeout */ 3608c2ecf20Sopenharmony_ci ret = -EIO; 3618c2ecf20Sopenharmony_ci goto err; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci if (res < 0) { 3648c2ecf20Sopenharmony_ci /* signal */ 3658c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 3668c2ecf20Sopenharmony_ci goto err; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!session->output_report_success) { 3718c2ecf20Sopenharmony_ci ret = -EIO; 3728c2ecf20Sopenharmony_ci goto err; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ret = count; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cierr: 3788c2ecf20Sopenharmony_ci clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); 3798c2ecf20Sopenharmony_ci mutex_unlock(&session->report_mutex); 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct hidp_session *session = hid->driver_data; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return hidp_send_intr_message(session, 3888c2ecf20Sopenharmony_ci HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT, 3898c2ecf20Sopenharmony_ci data, count); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, 3938c2ecf20Sopenharmony_ci __u8 *buf, size_t len, unsigned char rtype, 3948c2ecf20Sopenharmony_ci int reqtype) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci switch (reqtype) { 3978c2ecf20Sopenharmony_ci case HID_REQ_GET_REPORT: 3988c2ecf20Sopenharmony_ci return hidp_get_raw_report(hid, reportnum, buf, len, rtype); 3998c2ecf20Sopenharmony_ci case HID_REQ_SET_REPORT: 4008c2ecf20Sopenharmony_ci return hidp_set_raw_report(hid, reportnum, buf, len, rtype); 4018c2ecf20Sopenharmony_ci default: 4028c2ecf20Sopenharmony_ci return -EIO; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void hidp_idle_timeout(struct timer_list *t) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct hidp_session *session = from_timer(session, t, timer); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* The HIDP user-space API only contains calls to add and remove 4118c2ecf20Sopenharmony_ci * devices. There is no way to forward events of any kind. Therefore, 4128c2ecf20Sopenharmony_ci * we have to forcefully disconnect a device on idle-timeouts. This is 4138c2ecf20Sopenharmony_ci * unfortunate and weird API design, but it is spec-compliant and 4148c2ecf20Sopenharmony_ci * required for backwards-compatibility. Hence, on idle-timeout, we 4158c2ecf20Sopenharmony_ci * signal driver-detach events, so poll() will be woken up with an 4168c2ecf20Sopenharmony_ci * error-condition on both sockets. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci session->intr_sock->sk->sk_err = EUNATCH; 4208c2ecf20Sopenharmony_ci session->ctrl_sock->sk->sk_err = EUNATCH; 4218c2ecf20Sopenharmony_ci wake_up_interruptible(sk_sleep(session->intr_sock->sk)); 4228c2ecf20Sopenharmony_ci wake_up_interruptible(sk_sleep(session->ctrl_sock->sk)); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci hidp_session_terminate(session); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic void hidp_set_timer(struct hidp_session *session) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci if (session->idle_to > 0) 4308c2ecf20Sopenharmony_ci mod_timer(&session->timer, jiffies + HZ * session->idle_to); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void hidp_del_timer(struct hidp_session *session) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci if (session->idle_to > 0) 4368c2ecf20Sopenharmony_ci del_timer_sync(&session->timer); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void hidp_process_report(struct hidp_session *session, int type, 4408c2ecf20Sopenharmony_ci const u8 *data, unsigned int len, int intr) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci if (len > HID_MAX_BUFFER_SIZE) 4438c2ecf20Sopenharmony_ci len = HID_MAX_BUFFER_SIZE; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci memcpy(session->input_buf, data, len); 4468c2ecf20Sopenharmony_ci hid_input_report(session->hid, type, session->input_buf, len, intr); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void hidp_process_handshake(struct hidp_session *session, 4508c2ecf20Sopenharmony_ci unsigned char param) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci BT_DBG("session %p param 0x%02x", session, param); 4538c2ecf20Sopenharmony_ci session->output_report_success = 0; /* default condition */ 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (param) { 4568c2ecf20Sopenharmony_ci case HIDP_HSHK_SUCCESSFUL: 4578c2ecf20Sopenharmony_ci /* FIXME: Call into SET_ GET_ handlers here */ 4588c2ecf20Sopenharmony_ci session->output_report_success = 1; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci case HIDP_HSHK_NOT_READY: 4628c2ecf20Sopenharmony_ci case HIDP_HSHK_ERR_INVALID_REPORT_ID: 4638c2ecf20Sopenharmony_ci case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: 4648c2ecf20Sopenharmony_ci case HIDP_HSHK_ERR_INVALID_PARAMETER: 4658c2ecf20Sopenharmony_ci if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) 4668c2ecf20Sopenharmony_ci wake_up_interruptible(&session->report_queue); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* FIXME: Call into SET_ GET_ handlers here */ 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci case HIDP_HSHK_ERR_UNKNOWN: 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci case HIDP_HSHK_ERR_FATAL: 4758c2ecf20Sopenharmony_ci /* Device requests a reboot, as this is the only way this error 4768c2ecf20Sopenharmony_ci * can be recovered. */ 4778c2ecf20Sopenharmony_ci hidp_send_ctrl_message(session, 4788c2ecf20Sopenharmony_ci HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci default: 4828c2ecf20Sopenharmony_ci hidp_send_ctrl_message(session, 4838c2ecf20Sopenharmony_ci HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Wake up the waiting thread. */ 4888c2ecf20Sopenharmony_ci if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) 4898c2ecf20Sopenharmony_ci wake_up_interruptible(&session->report_queue); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void hidp_process_hid_control(struct hidp_session *session, 4938c2ecf20Sopenharmony_ci unsigned char param) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci BT_DBG("session %p param 0x%02x", session, param); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) { 4988c2ecf20Sopenharmony_ci /* Flush the transmit queues */ 4998c2ecf20Sopenharmony_ci skb_queue_purge(&session->ctrl_transmit); 5008c2ecf20Sopenharmony_ci skb_queue_purge(&session->intr_transmit); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci hidp_session_terminate(session); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* Returns true if the passed-in skb should be freed by the caller. */ 5078c2ecf20Sopenharmony_cistatic int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, 5088c2ecf20Sopenharmony_ci unsigned char param) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int done_with_skb = 1; 5118c2ecf20Sopenharmony_ci BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci switch (param) { 5148c2ecf20Sopenharmony_ci case HIDP_DATA_RTYPE_INPUT: 5158c2ecf20Sopenharmony_ci hidp_set_timer(session); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (session->input) 5188c2ecf20Sopenharmony_ci hidp_input_report(session, skb); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (session->hid) 5218c2ecf20Sopenharmony_ci hidp_process_report(session, HID_INPUT_REPORT, 5228c2ecf20Sopenharmony_ci skb->data, skb->len, 0); 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci case HIDP_DATA_RTYPE_OTHER: 5268c2ecf20Sopenharmony_ci case HIDP_DATA_RTYPE_OUPUT: 5278c2ecf20Sopenharmony_ci case HIDP_DATA_RTYPE_FEATURE: 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci default: 5318c2ecf20Sopenharmony_ci hidp_send_ctrl_message(session, 5328c2ecf20Sopenharmony_ci HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && 5368c2ecf20Sopenharmony_ci param == session->waiting_report_type) { 5378c2ecf20Sopenharmony_ci if (session->waiting_report_number < 0 || 5388c2ecf20Sopenharmony_ci session->waiting_report_number == skb->data[0]) { 5398c2ecf20Sopenharmony_ci /* hidp_get_raw_report() is waiting on this report. */ 5408c2ecf20Sopenharmony_ci session->report_return = skb; 5418c2ecf20Sopenharmony_ci done_with_skb = 0; 5428c2ecf20Sopenharmony_ci clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); 5438c2ecf20Sopenharmony_ci wake_up_interruptible(&session->report_queue); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return done_with_skb; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void hidp_recv_ctrl_frame(struct hidp_session *session, 5518c2ecf20Sopenharmony_ci struct sk_buff *skb) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci unsigned char hdr, type, param; 5548c2ecf20Sopenharmony_ci int free_skb = 1; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci BT_DBG("session %p skb %p len %d", session, skb, skb->len); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci hdr = skb->data[0]; 5598c2ecf20Sopenharmony_ci skb_pull(skb, 1); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci type = hdr & HIDP_HEADER_TRANS_MASK; 5628c2ecf20Sopenharmony_ci param = hdr & HIDP_HEADER_PARAM_MASK; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci switch (type) { 5658c2ecf20Sopenharmony_ci case HIDP_TRANS_HANDSHAKE: 5668c2ecf20Sopenharmony_ci hidp_process_handshake(session, param); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci case HIDP_TRANS_HID_CONTROL: 5708c2ecf20Sopenharmony_ci hidp_process_hid_control(session, param); 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci case HIDP_TRANS_DATA: 5748c2ecf20Sopenharmony_ci free_skb = hidp_process_data(session, skb, param); 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci default: 5788c2ecf20Sopenharmony_ci hidp_send_ctrl_message(session, 5798c2ecf20Sopenharmony_ci HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (free_skb) 5848c2ecf20Sopenharmony_ci kfree_skb(skb); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic void hidp_recv_intr_frame(struct hidp_session *session, 5888c2ecf20Sopenharmony_ci struct sk_buff *skb) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci unsigned char hdr; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci BT_DBG("session %p skb %p len %d", session, skb, skb->len); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci hdr = skb->data[0]; 5958c2ecf20Sopenharmony_ci skb_pull(skb, 1); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { 5988c2ecf20Sopenharmony_ci hidp_set_timer(session); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (session->input) 6018c2ecf20Sopenharmony_ci hidp_input_report(session, skb); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (session->hid) { 6048c2ecf20Sopenharmony_ci hidp_process_report(session, HID_INPUT_REPORT, 6058c2ecf20Sopenharmony_ci skb->data, skb->len, 1); 6068c2ecf20Sopenharmony_ci BT_DBG("report len %d", skb->len); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci BT_DBG("Unsupported protocol header 0x%02x", hdr); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci kfree_skb(skb); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int hidp_send_frame(struct socket *sock, unsigned char *data, int len) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct kvec iv = { data, len }; 6188c2ecf20Sopenharmony_ci struct msghdr msg; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci BT_DBG("sock %p data %p len %d", sock, data, len); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (!len) 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return kernel_sendmsg(sock, &msg, &iv, 1, len); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* dequeue message from @transmit and send via @sock */ 6318c2ecf20Sopenharmony_cistatic void hidp_process_transmit(struct hidp_session *session, 6328c2ecf20Sopenharmony_ci struct sk_buff_head *transmit, 6338c2ecf20Sopenharmony_ci struct socket *sock) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct sk_buff *skb; 6368c2ecf20Sopenharmony_ci int ret; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci BT_DBG("session %p", session); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(transmit))) { 6418c2ecf20Sopenharmony_ci ret = hidp_send_frame(sock, skb->data, skb->len); 6428c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 6438c2ecf20Sopenharmony_ci skb_queue_head(transmit, skb); 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci } else if (ret < 0) { 6468c2ecf20Sopenharmony_ci hidp_session_terminate(session); 6478c2ecf20Sopenharmony_ci kfree_skb(skb); 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci hidp_set_timer(session); 6528c2ecf20Sopenharmony_ci kfree_skb(skb); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int hidp_setup_input(struct hidp_session *session, 6578c2ecf20Sopenharmony_ci const struct hidp_connadd_req *req) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct input_dev *input; 6608c2ecf20Sopenharmony_ci int i; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci input = input_allocate_device(); 6638c2ecf20Sopenharmony_ci if (!input) 6648c2ecf20Sopenharmony_ci return -ENOMEM; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci session->input = input; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci input_set_drvdata(input, session); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci input->name = "Bluetooth HID Boot Protocol Device"; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci input->id.bustype = BUS_BLUETOOTH; 6738c2ecf20Sopenharmony_ci input->id.vendor = req->vendor; 6748c2ecf20Sopenharmony_ci input->id.product = req->product; 6758c2ecf20Sopenharmony_ci input->id.version = req->version; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (req->subclass & 0x40) { 6788c2ecf20Sopenharmony_ci set_bit(EV_KEY, input->evbit); 6798c2ecf20Sopenharmony_ci set_bit(EV_LED, input->evbit); 6808c2ecf20Sopenharmony_ci set_bit(EV_REP, input->evbit); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci set_bit(LED_NUML, input->ledbit); 6838c2ecf20Sopenharmony_ci set_bit(LED_CAPSL, input->ledbit); 6848c2ecf20Sopenharmony_ci set_bit(LED_SCROLLL, input->ledbit); 6858c2ecf20Sopenharmony_ci set_bit(LED_COMPOSE, input->ledbit); 6868c2ecf20Sopenharmony_ci set_bit(LED_KANA, input->ledbit); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(hidp_keycode); i++) 6898c2ecf20Sopenharmony_ci set_bit(hidp_keycode[i], input->keybit); 6908c2ecf20Sopenharmony_ci clear_bit(0, input->keybit); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (req->subclass & 0x80) { 6948c2ecf20Sopenharmony_ci input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 6958c2ecf20Sopenharmony_ci input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | 6968c2ecf20Sopenharmony_ci BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); 6978c2ecf20Sopenharmony_ci input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 6988c2ecf20Sopenharmony_ci input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | 6998c2ecf20Sopenharmony_ci BIT_MASK(BTN_EXTRA); 7008c2ecf20Sopenharmony_ci input->relbit[0] |= BIT_MASK(REL_WHEEL); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci input->dev.parent = &session->conn->hcon->dev; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci input->event = hidp_input_event; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return 0; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int hidp_open(struct hid_device *hid) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void hidp_close(struct hid_device *hid) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int hidp_parse(struct hid_device *hid) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct hidp_session *session = hid->driver_data; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return hid_parse_report(session->hid, session->rd_data, 7248c2ecf20Sopenharmony_ci session->rd_size); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int hidp_start(struct hid_device *hid) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic void hidp_stop(struct hid_device *hid) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct hidp_session *session = hid->driver_data; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci skb_queue_purge(&session->ctrl_transmit); 7378c2ecf20Sopenharmony_ci skb_queue_purge(&session->intr_transmit); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci hid->claimed = 0; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistruct hid_ll_driver hidp_hid_driver = { 7438c2ecf20Sopenharmony_ci .parse = hidp_parse, 7448c2ecf20Sopenharmony_ci .start = hidp_start, 7458c2ecf20Sopenharmony_ci .stop = hidp_stop, 7468c2ecf20Sopenharmony_ci .open = hidp_open, 7478c2ecf20Sopenharmony_ci .close = hidp_close, 7488c2ecf20Sopenharmony_ci .raw_request = hidp_raw_request, 7498c2ecf20Sopenharmony_ci .output_report = hidp_output_report, 7508c2ecf20Sopenharmony_ci}; 7518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidp_hid_driver); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* This function sets up the hid device. It does not add it 7548c2ecf20Sopenharmony_ci to the HID system. That is done in hidp_add_connection(). */ 7558c2ecf20Sopenharmony_cistatic int hidp_setup_hid(struct hidp_session *session, 7568c2ecf20Sopenharmony_ci const struct hidp_connadd_req *req) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct hid_device *hid; 7598c2ecf20Sopenharmony_ci int err; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci session->rd_data = memdup_user(req->rd_data, req->rd_size); 7628c2ecf20Sopenharmony_ci if (IS_ERR(session->rd_data)) 7638c2ecf20Sopenharmony_ci return PTR_ERR(session->rd_data); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci session->rd_size = req->rd_size; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci hid = hid_allocate_device(); 7688c2ecf20Sopenharmony_ci if (IS_ERR(hid)) { 7698c2ecf20Sopenharmony_ci err = PTR_ERR(hid); 7708c2ecf20Sopenharmony_ci goto fault; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci session->hid = hid; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci hid->driver_data = session; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci hid->bus = BUS_BLUETOOTH; 7788c2ecf20Sopenharmony_ci hid->vendor = req->vendor; 7798c2ecf20Sopenharmony_ci hid->product = req->product; 7808c2ecf20Sopenharmony_ci hid->version = req->version; 7818c2ecf20Sopenharmony_ci hid->country = req->country; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci strscpy(hid->name, req->name, sizeof(hid->name)); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci snprintf(hid->phys, sizeof(hid->phys), "%pMR", 7868c2ecf20Sopenharmony_ci &l2cap_pi(session->ctrl_sock->sk)->chan->src); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* NOTE: Some device modules depend on the dst address being stored in 7898c2ecf20Sopenharmony_ci * uniq. Please be aware of this before making changes to this behavior. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci snprintf(hid->uniq, sizeof(hid->uniq), "%pMR", 7928c2ecf20Sopenharmony_ci &l2cap_pi(session->ctrl_sock->sk)->chan->dst); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci hid->dev.parent = &session->conn->hcon->dev; 7958c2ecf20Sopenharmony_ci hid->ll_driver = &hidp_hid_driver; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* True if device is blacklisted in drivers/hid/hid-quirks.c */ 7988c2ecf20Sopenharmony_ci if (hid_ignore(hid)) { 7998c2ecf20Sopenharmony_ci hid_destroy_device(session->hid); 8008c2ecf20Sopenharmony_ci session->hid = NULL; 8018c2ecf20Sopenharmony_ci return -ENODEV; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cifault: 8078c2ecf20Sopenharmony_ci kfree(session->rd_data); 8088c2ecf20Sopenharmony_ci session->rd_data = NULL; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return err; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci/* initialize session devices */ 8148c2ecf20Sopenharmony_cistatic int hidp_session_dev_init(struct hidp_session *session, 8158c2ecf20Sopenharmony_ci const struct hidp_connadd_req *req) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci int ret; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (req->rd_size > 0) { 8208c2ecf20Sopenharmony_ci ret = hidp_setup_hid(session, req); 8218c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) 8228c2ecf20Sopenharmony_ci return ret; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (!session->hid) { 8268c2ecf20Sopenharmony_ci ret = hidp_setup_input(session, req); 8278c2ecf20Sopenharmony_ci if (ret < 0) 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* destroy session devices */ 8358c2ecf20Sopenharmony_cistatic void hidp_session_dev_destroy(struct hidp_session *session) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci if (session->hid) 8388c2ecf20Sopenharmony_ci put_device(&session->hid->dev); 8398c2ecf20Sopenharmony_ci else if (session->input) 8408c2ecf20Sopenharmony_ci input_put_device(session->input); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci kfree(session->rd_data); 8438c2ecf20Sopenharmony_ci session->rd_data = NULL; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* add HID/input devices to their underlying bus systems */ 8478c2ecf20Sopenharmony_cistatic int hidp_session_dev_add(struct hidp_session *session) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci int ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Both HID and input systems drop a ref-count when unregistering the 8528c2ecf20Sopenharmony_ci * device but they don't take a ref-count when registering them. Work 8538c2ecf20Sopenharmony_ci * around this by explicitly taking a refcount during registration 8548c2ecf20Sopenharmony_ci * which is dropped automatically by unregistering the devices. */ 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (session->hid) { 8578c2ecf20Sopenharmony_ci ret = hid_add_device(session->hid); 8588c2ecf20Sopenharmony_ci if (ret) 8598c2ecf20Sopenharmony_ci return ret; 8608c2ecf20Sopenharmony_ci get_device(&session->hid->dev); 8618c2ecf20Sopenharmony_ci } else if (session->input) { 8628c2ecf20Sopenharmony_ci ret = input_register_device(session->input); 8638c2ecf20Sopenharmony_ci if (ret) 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci input_get_device(session->input); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return 0; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/* remove HID/input devices from their bus systems */ 8728c2ecf20Sopenharmony_cistatic void hidp_session_dev_del(struct hidp_session *session) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci if (session->hid) 8758c2ecf20Sopenharmony_ci hid_destroy_device(session->hid); 8768c2ecf20Sopenharmony_ci else if (session->input) 8778c2ecf20Sopenharmony_ci input_unregister_device(session->input); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/* 8818c2ecf20Sopenharmony_ci * Asynchronous device registration 8828c2ecf20Sopenharmony_ci * HID device drivers might want to perform I/O during initialization to 8838c2ecf20Sopenharmony_ci * detect device types. Therefore, call device registration in a separate 8848c2ecf20Sopenharmony_ci * worker so the HIDP thread can schedule I/O operations. 8858c2ecf20Sopenharmony_ci * Note that this must be called after the worker thread was initialized 8868c2ecf20Sopenharmony_ci * successfully. This will then add the devices and increase session state 8878c2ecf20Sopenharmony_ci * on success, otherwise it will terminate the session thread. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_cistatic void hidp_session_dev_work(struct work_struct *work) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct hidp_session *session = container_of(work, 8928c2ecf20Sopenharmony_ci struct hidp_session, 8938c2ecf20Sopenharmony_ci dev_init); 8948c2ecf20Sopenharmony_ci int ret; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci ret = hidp_session_dev_add(session); 8978c2ecf20Sopenharmony_ci if (!ret) 8988c2ecf20Sopenharmony_ci atomic_inc(&session->state); 8998c2ecf20Sopenharmony_ci else 9008c2ecf20Sopenharmony_ci hidp_session_terminate(session); 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci/* 9048c2ecf20Sopenharmony_ci * Create new session object 9058c2ecf20Sopenharmony_ci * Allocate session object, initialize static fields, copy input data into the 9068c2ecf20Sopenharmony_ci * object and take a reference to all sub-objects. 9078c2ecf20Sopenharmony_ci * This returns 0 on success and puts a pointer to the new session object in 9088c2ecf20Sopenharmony_ci * \out. Otherwise, an error code is returned. 9098c2ecf20Sopenharmony_ci * The new session object has an initial ref-count of 1. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_cistatic int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr, 9128c2ecf20Sopenharmony_ci struct socket *ctrl_sock, 9138c2ecf20Sopenharmony_ci struct socket *intr_sock, 9148c2ecf20Sopenharmony_ci const struct hidp_connadd_req *req, 9158c2ecf20Sopenharmony_ci struct l2cap_conn *conn) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct hidp_session *session; 9188c2ecf20Sopenharmony_ci int ret; 9198c2ecf20Sopenharmony_ci struct bt_sock *ctrl, *intr; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ctrl = bt_sk(ctrl_sock->sk); 9228c2ecf20Sopenharmony_ci intr = bt_sk(intr_sock->sk); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci session = kzalloc(sizeof(*session), GFP_KERNEL); 9258c2ecf20Sopenharmony_ci if (!session) 9268c2ecf20Sopenharmony_ci return -ENOMEM; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci /* object and runtime management */ 9298c2ecf20Sopenharmony_ci kref_init(&session->ref); 9308c2ecf20Sopenharmony_ci atomic_set(&session->state, HIDP_SESSION_IDLING); 9318c2ecf20Sopenharmony_ci init_waitqueue_head(&session->state_queue); 9328c2ecf20Sopenharmony_ci session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* connection management */ 9358c2ecf20Sopenharmony_ci bacpy(&session->bdaddr, bdaddr); 9368c2ecf20Sopenharmony_ci session->conn = l2cap_conn_get(conn); 9378c2ecf20Sopenharmony_ci session->user.probe = hidp_session_probe; 9388c2ecf20Sopenharmony_ci session->user.remove = hidp_session_remove; 9398c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&session->user.list); 9408c2ecf20Sopenharmony_ci session->ctrl_sock = ctrl_sock; 9418c2ecf20Sopenharmony_ci session->intr_sock = intr_sock; 9428c2ecf20Sopenharmony_ci skb_queue_head_init(&session->ctrl_transmit); 9438c2ecf20Sopenharmony_ci skb_queue_head_init(&session->intr_transmit); 9448c2ecf20Sopenharmony_ci session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl)->chan->omtu, 9458c2ecf20Sopenharmony_ci l2cap_pi(ctrl)->chan->imtu); 9468c2ecf20Sopenharmony_ci session->intr_mtu = min_t(uint, l2cap_pi(intr)->chan->omtu, 9478c2ecf20Sopenharmony_ci l2cap_pi(intr)->chan->imtu); 9488c2ecf20Sopenharmony_ci session->idle_to = req->idle_to; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* device management */ 9518c2ecf20Sopenharmony_ci INIT_WORK(&session->dev_init, hidp_session_dev_work); 9528c2ecf20Sopenharmony_ci timer_setup(&session->timer, hidp_idle_timeout, 0); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* session data */ 9558c2ecf20Sopenharmony_ci mutex_init(&session->report_mutex); 9568c2ecf20Sopenharmony_ci init_waitqueue_head(&session->report_queue); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ret = hidp_session_dev_init(session, req); 9598c2ecf20Sopenharmony_ci if (ret) 9608c2ecf20Sopenharmony_ci goto err_free; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci get_file(session->intr_sock->file); 9638c2ecf20Sopenharmony_ci get_file(session->ctrl_sock->file); 9648c2ecf20Sopenharmony_ci *out = session; 9658c2ecf20Sopenharmony_ci return 0; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cierr_free: 9688c2ecf20Sopenharmony_ci l2cap_conn_put(session->conn); 9698c2ecf20Sopenharmony_ci kfree(session); 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci/* increase ref-count of the given session by one */ 9748c2ecf20Sopenharmony_cistatic void hidp_session_get(struct hidp_session *session) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci kref_get(&session->ref); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/* release callback */ 9808c2ecf20Sopenharmony_cistatic void session_free(struct kref *ref) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct hidp_session *session = container_of(ref, struct hidp_session, 9838c2ecf20Sopenharmony_ci ref); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci hidp_session_dev_destroy(session); 9868c2ecf20Sopenharmony_ci skb_queue_purge(&session->ctrl_transmit); 9878c2ecf20Sopenharmony_ci skb_queue_purge(&session->intr_transmit); 9888c2ecf20Sopenharmony_ci fput(session->intr_sock->file); 9898c2ecf20Sopenharmony_ci fput(session->ctrl_sock->file); 9908c2ecf20Sopenharmony_ci l2cap_conn_put(session->conn); 9918c2ecf20Sopenharmony_ci kfree(session); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/* decrease ref-count of the given session by one */ 9958c2ecf20Sopenharmony_cistatic void hidp_session_put(struct hidp_session *session) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci kref_put(&session->ref, session_free); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/* 10018c2ecf20Sopenharmony_ci * Search the list of active sessions for a session with target address 10028c2ecf20Sopenharmony_ci * \bdaddr. You must hold at least a read-lock on \hidp_session_sem. As long as 10038c2ecf20Sopenharmony_ci * you do not release this lock, the session objects cannot vanish and you can 10048c2ecf20Sopenharmony_ci * safely take a reference to the session yourself. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_cistatic struct hidp_session *__hidp_session_find(const bdaddr_t *bdaddr) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct hidp_session *session; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci list_for_each_entry(session, &hidp_session_list, list) { 10118c2ecf20Sopenharmony_ci if (!bacmp(bdaddr, &session->bdaddr)) 10128c2ecf20Sopenharmony_ci return session; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return NULL; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci/* 10198c2ecf20Sopenharmony_ci * Same as __hidp_session_find() but no locks must be held. This also takes a 10208c2ecf20Sopenharmony_ci * reference of the returned session (if non-NULL) so you must drop this 10218c2ecf20Sopenharmony_ci * reference if you no longer use the object. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_cistatic struct hidp_session *hidp_session_find(const bdaddr_t *bdaddr) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci struct hidp_session *session; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci down_read(&hidp_session_sem); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci session = __hidp_session_find(bdaddr); 10308c2ecf20Sopenharmony_ci if (session) 10318c2ecf20Sopenharmony_ci hidp_session_get(session); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci up_read(&hidp_session_sem); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return session; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/* 10398c2ecf20Sopenharmony_ci * Start session synchronously 10408c2ecf20Sopenharmony_ci * This starts a session thread and waits until initialization 10418c2ecf20Sopenharmony_ci * is done or returns an error if it couldn't be started. 10428c2ecf20Sopenharmony_ci * If this returns 0 the session thread is up and running. You must call 10438c2ecf20Sopenharmony_ci * hipd_session_stop_sync() before deleting any runtime resources. 10448c2ecf20Sopenharmony_ci */ 10458c2ecf20Sopenharmony_cistatic int hidp_session_start_sync(struct hidp_session *session) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci unsigned int vendor, product; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (session->hid) { 10508c2ecf20Sopenharmony_ci vendor = session->hid->vendor; 10518c2ecf20Sopenharmony_ci product = session->hid->product; 10528c2ecf20Sopenharmony_ci } else if (session->input) { 10538c2ecf20Sopenharmony_ci vendor = session->input->id.vendor; 10548c2ecf20Sopenharmony_ci product = session->input->id.product; 10558c2ecf20Sopenharmony_ci } else { 10568c2ecf20Sopenharmony_ci vendor = 0x0000; 10578c2ecf20Sopenharmony_ci product = 0x0000; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci session->task = kthread_run(hidp_session_thread, session, 10618c2ecf20Sopenharmony_ci "khidpd_%04x%04x", vendor, product); 10628c2ecf20Sopenharmony_ci if (IS_ERR(session->task)) 10638c2ecf20Sopenharmony_ci return PTR_ERR(session->task); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci while (atomic_read(&session->state) <= HIDP_SESSION_IDLING) 10668c2ecf20Sopenharmony_ci wait_event(session->state_queue, 10678c2ecf20Sopenharmony_ci atomic_read(&session->state) > HIDP_SESSION_IDLING); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci/* 10738c2ecf20Sopenharmony_ci * Terminate session thread 10748c2ecf20Sopenharmony_ci * Wake up session thread and notify it to stop. This is asynchronous and 10758c2ecf20Sopenharmony_ci * returns immediately. Call this whenever a runtime error occurs and you want 10768c2ecf20Sopenharmony_ci * the session to stop. 10778c2ecf20Sopenharmony_ci * Note: wake_up_interruptible() performs any necessary memory-barriers for us. 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_cistatic void hidp_session_terminate(struct hidp_session *session) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci atomic_inc(&session->terminate); 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * See the comment preceding the call to wait_woken() 10848c2ecf20Sopenharmony_ci * in hidp_session_run(). 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci wake_up_interruptible(&hidp_session_wq); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/* 10908c2ecf20Sopenharmony_ci * Probe HIDP session 10918c2ecf20Sopenharmony_ci * This is called from the l2cap_conn core when our l2cap_user object is bound 10928c2ecf20Sopenharmony_ci * to the hci-connection. We get the session via the \user object and can now 10938c2ecf20Sopenharmony_ci * start the session thread, link it into the global session list and 10948c2ecf20Sopenharmony_ci * schedule HID/input device registration. 10958c2ecf20Sopenharmony_ci * The global session-list owns its own reference to the session object so you 10968c2ecf20Sopenharmony_ci * can drop your own reference after registering the l2cap_user object. 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_cistatic int hidp_session_probe(struct l2cap_conn *conn, 10998c2ecf20Sopenharmony_ci struct l2cap_user *user) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct hidp_session *session = container_of(user, 11028c2ecf20Sopenharmony_ci struct hidp_session, 11038c2ecf20Sopenharmony_ci user); 11048c2ecf20Sopenharmony_ci struct hidp_session *s; 11058c2ecf20Sopenharmony_ci int ret; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci down_write(&hidp_session_sem); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* check that no other session for this device exists */ 11108c2ecf20Sopenharmony_ci s = __hidp_session_find(&session->bdaddr); 11118c2ecf20Sopenharmony_ci if (s) { 11128c2ecf20Sopenharmony_ci ret = -EEXIST; 11138c2ecf20Sopenharmony_ci goto out_unlock; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (session->input) { 11178c2ecf20Sopenharmony_ci ret = hidp_session_dev_add(session); 11188c2ecf20Sopenharmony_ci if (ret) 11198c2ecf20Sopenharmony_ci goto out_unlock; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci ret = hidp_session_start_sync(session); 11238c2ecf20Sopenharmony_ci if (ret) 11248c2ecf20Sopenharmony_ci goto out_del; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* HID device registration is async to allow I/O during probe */ 11278c2ecf20Sopenharmony_ci if (session->input) 11288c2ecf20Sopenharmony_ci atomic_inc(&session->state); 11298c2ecf20Sopenharmony_ci else 11308c2ecf20Sopenharmony_ci schedule_work(&session->dev_init); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci hidp_session_get(session); 11338c2ecf20Sopenharmony_ci list_add(&session->list, &hidp_session_list); 11348c2ecf20Sopenharmony_ci ret = 0; 11358c2ecf20Sopenharmony_ci goto out_unlock; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ciout_del: 11388c2ecf20Sopenharmony_ci if (session->input) 11398c2ecf20Sopenharmony_ci hidp_session_dev_del(session); 11408c2ecf20Sopenharmony_ciout_unlock: 11418c2ecf20Sopenharmony_ci up_write(&hidp_session_sem); 11428c2ecf20Sopenharmony_ci return ret; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* 11468c2ecf20Sopenharmony_ci * Remove HIDP session 11478c2ecf20Sopenharmony_ci * Called from the l2cap_conn core when either we explicitly unregistered 11488c2ecf20Sopenharmony_ci * the l2cap_user object or if the underlying connection is shut down. 11498c2ecf20Sopenharmony_ci * We signal the hidp-session thread to shut down, unregister the HID/input 11508c2ecf20Sopenharmony_ci * devices and unlink the session from the global list. 11518c2ecf20Sopenharmony_ci * This drops the reference to the session that is owned by the global 11528c2ecf20Sopenharmony_ci * session-list. 11538c2ecf20Sopenharmony_ci * Note: We _must_ not synchronosly wait for the session-thread to shut down. 11548c2ecf20Sopenharmony_ci * This is, because the session-thread might be waiting for an HCI lock that is 11558c2ecf20Sopenharmony_ci * held while we are called. Therefore, we only unregister the devices and 11568c2ecf20Sopenharmony_ci * notify the session-thread to terminate. The thread itself owns a reference 11578c2ecf20Sopenharmony_ci * to the session object so it can safely shut down. 11588c2ecf20Sopenharmony_ci */ 11598c2ecf20Sopenharmony_cistatic void hidp_session_remove(struct l2cap_conn *conn, 11608c2ecf20Sopenharmony_ci struct l2cap_user *user) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct hidp_session *session = container_of(user, 11638c2ecf20Sopenharmony_ci struct hidp_session, 11648c2ecf20Sopenharmony_ci user); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci down_write(&hidp_session_sem); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci hidp_session_terminate(session); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci cancel_work_sync(&session->dev_init); 11718c2ecf20Sopenharmony_ci if (session->input || 11728c2ecf20Sopenharmony_ci atomic_read(&session->state) > HIDP_SESSION_PREPARING) 11738c2ecf20Sopenharmony_ci hidp_session_dev_del(session); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci list_del(&session->list); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci up_write(&hidp_session_sem); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci hidp_session_put(session); 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci/* 11838c2ecf20Sopenharmony_ci * Session Worker 11848c2ecf20Sopenharmony_ci * This performs the actual main-loop of the HIDP worker. We first check 11858c2ecf20Sopenharmony_ci * whether the underlying connection is still alive, then parse all pending 11868c2ecf20Sopenharmony_ci * messages and finally send all outstanding messages. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_cistatic void hidp_session_run(struct hidp_session *session) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct sock *ctrl_sk = session->ctrl_sock->sk; 11918c2ecf20Sopenharmony_ci struct sock *intr_sk = session->intr_sock->sk; 11928c2ecf20Sopenharmony_ci struct sk_buff *skb; 11938c2ecf20Sopenharmony_ci DEFINE_WAIT_FUNC(wait, woken_wake_function); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci add_wait_queue(&hidp_session_wq, &wait); 11968c2ecf20Sopenharmony_ci for (;;) { 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * This thread can be woken up two ways: 11998c2ecf20Sopenharmony_ci * - You call hidp_session_terminate() which sets the 12008c2ecf20Sopenharmony_ci * session->terminate flag and wakes this thread up. 12018c2ecf20Sopenharmony_ci * - Via modifying the socket state of ctrl/intr_sock. This 12028c2ecf20Sopenharmony_ci * thread is woken up by ->sk_state_changed(). 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (atomic_read(&session->terminate)) 12068c2ecf20Sopenharmony_ci break; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (ctrl_sk->sk_state != BT_CONNECTED || 12098c2ecf20Sopenharmony_ci intr_sk->sk_state != BT_CONNECTED) 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* parse incoming intr-skbs */ 12138c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { 12148c2ecf20Sopenharmony_ci skb_orphan(skb); 12158c2ecf20Sopenharmony_ci if (!skb_linearize(skb)) 12168c2ecf20Sopenharmony_ci hidp_recv_intr_frame(session, skb); 12178c2ecf20Sopenharmony_ci else 12188c2ecf20Sopenharmony_ci kfree_skb(skb); 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* send pending intr-skbs */ 12228c2ecf20Sopenharmony_ci hidp_process_transmit(session, &session->intr_transmit, 12238c2ecf20Sopenharmony_ci session->intr_sock); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* parse incoming ctrl-skbs */ 12268c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { 12278c2ecf20Sopenharmony_ci skb_orphan(skb); 12288c2ecf20Sopenharmony_ci if (!skb_linearize(skb)) 12298c2ecf20Sopenharmony_ci hidp_recv_ctrl_frame(session, skb); 12308c2ecf20Sopenharmony_ci else 12318c2ecf20Sopenharmony_ci kfree_skb(skb); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* send pending ctrl-skbs */ 12358c2ecf20Sopenharmony_ci hidp_process_transmit(session, &session->ctrl_transmit, 12368c2ecf20Sopenharmony_ci session->ctrl_sock); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * wait_woken() performs the necessary memory barriers 12408c2ecf20Sopenharmony_ci * for us; see the header comment for this primitive. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci remove_wait_queue(&hidp_session_wq, &wait); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci atomic_inc(&session->terminate); 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic int hidp_session_wake_function(wait_queue_entry_t *wait, 12508c2ecf20Sopenharmony_ci unsigned int mode, 12518c2ecf20Sopenharmony_ci int sync, void *key) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci wake_up_interruptible(&hidp_session_wq); 12548c2ecf20Sopenharmony_ci return false; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci/* 12588c2ecf20Sopenharmony_ci * HIDP session thread 12598c2ecf20Sopenharmony_ci * This thread runs the I/O for a single HIDP session. Startup is synchronous 12608c2ecf20Sopenharmony_ci * which allows us to take references to ourself here instead of doing that in 12618c2ecf20Sopenharmony_ci * the caller. 12628c2ecf20Sopenharmony_ci * When we are ready to run we notify the caller and call hidp_session_run(). 12638c2ecf20Sopenharmony_ci */ 12648c2ecf20Sopenharmony_cistatic int hidp_session_thread(void *arg) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct hidp_session *session = arg; 12678c2ecf20Sopenharmony_ci DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function); 12688c2ecf20Sopenharmony_ci DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci BT_DBG("session %p", session); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* initialize runtime environment */ 12738c2ecf20Sopenharmony_ci hidp_session_get(session); 12748c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 12758c2ecf20Sopenharmony_ci set_user_nice(current, -15); 12768c2ecf20Sopenharmony_ci hidp_set_timer(session); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait); 12798c2ecf20Sopenharmony_ci add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait); 12808c2ecf20Sopenharmony_ci /* This memory barrier is paired with wq_has_sleeper(). See 12818c2ecf20Sopenharmony_ci * sock_poll_wait() for more information why this is needed. */ 12828c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* notify synchronous startup that we're ready */ 12858c2ecf20Sopenharmony_ci atomic_inc(&session->state); 12868c2ecf20Sopenharmony_ci wake_up(&session->state_queue); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci /* run session */ 12898c2ecf20Sopenharmony_ci hidp_session_run(session); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* cleanup runtime environment */ 12928c2ecf20Sopenharmony_ci remove_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait); 12938c2ecf20Sopenharmony_ci remove_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait); 12948c2ecf20Sopenharmony_ci wake_up_interruptible(&session->report_queue); 12958c2ecf20Sopenharmony_ci hidp_del_timer(session); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* 12988c2ecf20Sopenharmony_ci * If we stopped ourself due to any internal signal, we should try to 12998c2ecf20Sopenharmony_ci * unregister our own session here to avoid having it linger until the 13008c2ecf20Sopenharmony_ci * parent l2cap_conn dies or user-space cleans it up. 13018c2ecf20Sopenharmony_ci * This does not deadlock as we don't do any synchronous shutdown. 13028c2ecf20Sopenharmony_ci * Instead, this call has the same semantics as if user-space tried to 13038c2ecf20Sopenharmony_ci * delete the session. 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_ci l2cap_unregister_user(session->conn, &session->user); 13068c2ecf20Sopenharmony_ci hidp_session_put(session); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci module_put_and_exit(0); 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic int hidp_verify_sockets(struct socket *ctrl_sock, 13138c2ecf20Sopenharmony_ci struct socket *intr_sock) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci struct l2cap_chan *ctrl_chan, *intr_chan; 13168c2ecf20Sopenharmony_ci struct bt_sock *ctrl, *intr; 13178c2ecf20Sopenharmony_ci struct hidp_session *session; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock)) 13208c2ecf20Sopenharmony_ci return -EINVAL; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan; 13238c2ecf20Sopenharmony_ci intr_chan = l2cap_pi(intr_sock->sk)->chan; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (bacmp(&ctrl_chan->src, &intr_chan->src) || 13268c2ecf20Sopenharmony_ci bacmp(&ctrl_chan->dst, &intr_chan->dst)) 13278c2ecf20Sopenharmony_ci return -ENOTUNIQ; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci ctrl = bt_sk(ctrl_sock->sk); 13308c2ecf20Sopenharmony_ci intr = bt_sk(intr_sock->sk); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (ctrl->sk.sk_state != BT_CONNECTED || 13338c2ecf20Sopenharmony_ci intr->sk.sk_state != BT_CONNECTED) 13348c2ecf20Sopenharmony_ci return -EBADFD; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* early session check, we check again during session registration */ 13378c2ecf20Sopenharmony_ci session = hidp_session_find(&ctrl_chan->dst); 13388c2ecf20Sopenharmony_ci if (session) { 13398c2ecf20Sopenharmony_ci hidp_session_put(session); 13408c2ecf20Sopenharmony_ci return -EEXIST; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci return 0; 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ciint hidp_connection_add(const struct hidp_connadd_req *req, 13478c2ecf20Sopenharmony_ci struct socket *ctrl_sock, 13488c2ecf20Sopenharmony_ci struct socket *intr_sock) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG) | 13518c2ecf20Sopenharmony_ci BIT(HIDP_BOOT_PROTOCOL_MODE); 13528c2ecf20Sopenharmony_ci struct hidp_session *session; 13538c2ecf20Sopenharmony_ci struct l2cap_conn *conn; 13548c2ecf20Sopenharmony_ci struct l2cap_chan *chan; 13558c2ecf20Sopenharmony_ci int ret; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci ret = hidp_verify_sockets(ctrl_sock, intr_sock); 13588c2ecf20Sopenharmony_ci if (ret) 13598c2ecf20Sopenharmony_ci return ret; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (req->flags & ~valid_flags) 13628c2ecf20Sopenharmony_ci return -EINVAL; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci chan = l2cap_pi(ctrl_sock->sk)->chan; 13658c2ecf20Sopenharmony_ci conn = NULL; 13668c2ecf20Sopenharmony_ci l2cap_chan_lock(chan); 13678c2ecf20Sopenharmony_ci if (chan->conn) 13688c2ecf20Sopenharmony_ci conn = l2cap_conn_get(chan->conn); 13698c2ecf20Sopenharmony_ci l2cap_chan_unlock(chan); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (!conn) 13728c2ecf20Sopenharmony_ci return -EBADFD; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci ret = hidp_session_new(&session, &chan->dst, ctrl_sock, 13758c2ecf20Sopenharmony_ci intr_sock, req, conn); 13768c2ecf20Sopenharmony_ci if (ret) 13778c2ecf20Sopenharmony_ci goto out_conn; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci ret = l2cap_register_user(conn, &session->user); 13808c2ecf20Sopenharmony_ci if (ret) 13818c2ecf20Sopenharmony_ci goto out_session; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci ret = 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ciout_session: 13868c2ecf20Sopenharmony_ci hidp_session_put(session); 13878c2ecf20Sopenharmony_ciout_conn: 13888c2ecf20Sopenharmony_ci l2cap_conn_put(conn); 13898c2ecf20Sopenharmony_ci return ret; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ciint hidp_connection_del(struct hidp_conndel_req *req) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG); 13958c2ecf20Sopenharmony_ci struct hidp_session *session; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (req->flags & ~valid_flags) 13988c2ecf20Sopenharmony_ci return -EINVAL; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci session = hidp_session_find(&req->bdaddr); 14018c2ecf20Sopenharmony_ci if (!session) 14028c2ecf20Sopenharmony_ci return -ENOENT; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG)) 14058c2ecf20Sopenharmony_ci hidp_send_ctrl_message(session, 14068c2ecf20Sopenharmony_ci HIDP_TRANS_HID_CONTROL | 14078c2ecf20Sopenharmony_ci HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, 14088c2ecf20Sopenharmony_ci NULL, 0); 14098c2ecf20Sopenharmony_ci else 14108c2ecf20Sopenharmony_ci l2cap_unregister_user(session->conn, &session->user); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci hidp_session_put(session); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return 0; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ciint hidp_get_connlist(struct hidp_connlist_req *req) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci struct hidp_session *session; 14208c2ecf20Sopenharmony_ci int err = 0, n = 0; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci BT_DBG(""); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci down_read(&hidp_session_sem); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci list_for_each_entry(session, &hidp_session_list, list) { 14278c2ecf20Sopenharmony_ci struct hidp_conninfo ci; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci hidp_copy_session(session, &ci); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (copy_to_user(req->ci, &ci, sizeof(ci))) { 14328c2ecf20Sopenharmony_ci err = -EFAULT; 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (++n >= req->cnum) 14378c2ecf20Sopenharmony_ci break; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci req->ci++; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci req->cnum = n; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci up_read(&hidp_session_sem); 14448c2ecf20Sopenharmony_ci return err; 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ciint hidp_get_conninfo(struct hidp_conninfo *ci) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct hidp_session *session; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci session = hidp_session_find(&ci->bdaddr); 14528c2ecf20Sopenharmony_ci if (session) { 14538c2ecf20Sopenharmony_ci hidp_copy_session(session, ci); 14548c2ecf20Sopenharmony_ci hidp_session_put(session); 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci return session ? 0 : -ENOENT; 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic int __init hidp_init(void) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci return hidp_init_sockets(); 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_cistatic void __exit hidp_exit(void) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci hidp_cleanup_sockets(); 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cimodule_init(hidp_init); 14738c2ecf20Sopenharmony_cimodule_exit(hidp_exit); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); 14768c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); 14778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION); 14788c2ecf20Sopenharmony_ciMODULE_VERSION(VERSION); 14798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14808c2ecf20Sopenharmony_ciMODULE_ALIAS("bt-proto-6"); 1481