2007-02-09 17:24:33 +03:00
/*
2005-04-17 02:20:36 +04:00
HIDP implementation for Linux Bluetooth stack ( BlueZ ) .
Copyright ( C ) 2003 - 2004 Marcel Holtmann < marcel @ holtmann . org >
2013-04-06 22:28:46 +04:00
Copyright ( C ) 2013 David Herrmann < dh . herrmann @ gmail . com >
2005-04-17 02:20:36 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation ;
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
2007-02-09 17:24:33 +03:00
CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
2005-04-17 02:20:36 +04:00
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
2007-02-09 17:24:33 +03:00
ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
2005-04-17 02:20:36 +04:00
SOFTWARE IS DISCLAIMED .
*/
2013-04-06 22:28:46 +04:00
# include <linux/kref.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/file.h>
2011-04-05 17:37:45 +04:00
# include <linux/kthread.h>
2009-08-23 01:15:53 +04:00
# include <linux/hidraw.h>
2005-04-17 02:20:36 +04:00
# include <net/bluetooth/bluetooth.h>
2006-07-06 15:09:02 +04:00
# include <net/bluetooth/hci_core.h>
2005-04-17 02:20:36 +04:00
# include <net/bluetooth/l2cap.h>
# include "hidp.h"
2007-02-18 01:58:49 +03:00
# define VERSION "1.2"
2005-04-17 02:20:36 +04:00
static DECLARE_RWSEM ( hidp_session_sem ) ;
static LIST_HEAD ( hidp_session_list ) ;
static unsigned char hidp_keycode [ 256 ] = {
2011-03-21 16:20:01 +03:00
0 , 0 , 0 , 0 , 30 , 48 , 46 , 32 , 18 , 33 , 34 , 35 , 23 , 36 ,
37 , 38 , 50 , 49 , 24 , 25 , 16 , 19 , 31 , 20 , 22 , 47 , 17 , 45 ,
21 , 44 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 28 , 1 ,
14 , 15 , 57 , 12 , 13 , 26 , 27 , 43 , 43 , 39 , 40 , 41 , 51 , 52 ,
53 , 58 , 59 , 60 , 61 , 62 , 63 , 64 , 65 , 66 , 67 , 68 , 87 , 88 ,
99 , 70 , 119 , 110 , 102 , 104 , 111 , 107 , 109 , 106 , 105 , 108 , 103 , 69 ,
98 , 55 , 74 , 78 , 96 , 79 , 80 , 81 , 75 , 76 , 77 , 71 , 72 , 73 ,
82 , 83 , 86 , 127 , 116 , 117 , 183 , 184 , 185 , 186 , 187 , 188 , 189 , 190 ,
191 , 192 , 193 , 194 , 134 , 138 , 130 , 132 , 128 , 129 , 131 , 137 , 133 , 135 ,
136 , 113 , 115 , 114 , 0 , 0 , 0 , 121 , 0 , 89 , 93 , 124 , 92 , 94 ,
95 , 0 , 0 , 0 , 122 , 123 , 90 , 91 , 85 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
29 , 42 , 56 , 125 , 97 , 54 , 100 , 126 , 164 , 166 , 165 , 163 , 161 , 115 ,
114 , 113 , 150 , 158 , 159 , 128 , 136 , 177 , 178 , 176 , 142 , 152 , 173 , 140
2005-04-17 02:20:36 +04:00
} ;
static unsigned char hidp_mkeyspat [ ] = { 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 } ;
2013-04-06 22:28:46 +04:00
static int hidp_session_probe ( struct l2cap_conn * conn ,
struct l2cap_user * user ) ;
static void hidp_session_remove ( struct l2cap_conn * conn ,
struct l2cap_user * user ) ;
static int hidp_session_thread ( void * arg ) ;
static void hidp_session_terminate ( struct hidp_session * s ) ;
2013-04-06 22:28:43 +04:00
static inline void hidp_schedule ( struct hidp_session * session )
{
struct sock * ctrl_sk = session - > ctrl_sock - > sk ;
struct sock * intr_sk = session - > intr_sock - > sk ;
wake_up_interruptible ( sk_sleep ( ctrl_sk ) ) ;
wake_up_interruptible ( sk_sleep ( intr_sk ) ) ;
}
2005-04-17 02:20:36 +04:00
static struct hidp_session * __hidp_get_session ( bdaddr_t * bdaddr )
{
struct hidp_session * session ;
BT_DBG ( " " ) ;
2011-10-07 00:35:31 +04:00
list_for_each_entry ( session , & hidp_session_list , list ) {
2005-04-17 02:20:36 +04:00
if ( ! bacmp ( bdaddr , & session - > bdaddr ) )
return session ;
}
2011-11-01 12:58:56 +04:00
2005-04-17 02:20:36 +04:00
return NULL ;
}
static void __hidp_copy_session ( struct hidp_session * session , struct hidp_conninfo * ci )
{
2010-10-30 18:26:31 +04:00
memset ( ci , 0 , sizeof ( * ci ) ) ;
2005-04-17 02:20:36 +04:00
bacpy ( & ci - > bdaddr , & session - > bdaddr ) ;
ci - > flags = session - > flags ;
2013-04-06 22:28:40 +04:00
ci - > state = BT_CONNECTED ;
2005-04-17 02:20:36 +04:00
ci - > vendor = 0x0000 ;
ci - > product = 0x0000 ;
ci - > version = 0x0000 ;
if ( session - > input ) {
ci - > vendor = session - > input - > id . vendor ;
ci - > product = session - > input - > id . product ;
ci - > version = session - > input - > id . version ;
if ( session - > input - > name )
strncpy ( ci - > name , session - > input - > name , 128 ) ;
else
strncpy ( ci - > name , " HID Boot Device " , 128 ) ;
}
2007-02-18 01:58:49 +03:00
if ( session - > hid ) {
ci - > vendor = session - > hid - > vendor ;
ci - > product = session - > hid - > product ;
ci - > version = session - > hid - > version ;
strncpy ( ci - > name , session - > hid - > name , 128 ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-02-05 14:07:58 +03:00
static int hidp_queue_event ( struct hidp_session * session , struct input_dev * dev ,
unsigned int type , unsigned int code , int value )
2005-04-17 02:20:36 +04:00
{
unsigned char newleds ;
2007-02-18 01:58:49 +03:00
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
2007-02-18 01:58:49 +03:00
BT_DBG ( " session %p type %d code %d value %d " , session , type , code , value ) ;
2005-04-17 02:20:36 +04:00
if ( type ! = EV_LED )
return - 1 ;
newleds = ( ! ! test_bit ( LED_KANA , dev - > led ) < < 3 ) |
( ! ! test_bit ( LED_COMPOSE , dev - > led ) < < 3 ) |
( ! ! test_bit ( LED_SCROLLL , dev - > led ) < < 2 ) |
( ! ! test_bit ( LED_CAPSL , dev - > led ) < < 1 ) |
( ! ! test_bit ( LED_NUML , dev - > led ) ) ;
if ( session - > leds = = newleds )
return 0 ;
session - > leds = newleds ;
2011-01-11 18:20:20 +03:00
skb = alloc_skb ( 3 , GFP_ATOMIC ) ;
if ( ! skb ) {
2005-04-17 02:20:36 +04:00
BT_ERR ( " Can't allocate memory for new frame " ) ;
return - ENOMEM ;
}
* skb_put ( skb , 1 ) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT ;
* skb_put ( skb , 1 ) = 0x01 ;
* skb_put ( skb , 1 ) = newleds ;
skb_queue_tail ( & session - > intr_transmit , skb ) ;
hidp_schedule ( session ) ;
return 0 ;
}
2007-02-18 01:58:49 +03:00
static int hidp_hidinput_event ( struct input_dev * dev , unsigned int type , unsigned int code , int value )
{
2007-05-09 11:15:30 +04:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2007-02-18 01:58:49 +03:00
struct hidp_session * session = hid - > driver_data ;
return hidp_queue_event ( session , dev , type , code , value ) ;
}
static int hidp_input_event ( struct input_dev * dev , unsigned int type , unsigned int code , int value )
{
2007-05-09 11:15:30 +04:00
struct hidp_session * session = input_get_drvdata ( dev ) ;
2007-02-18 01:58:49 +03:00
return hidp_queue_event ( session , dev , type , code , value ) ;
}
2005-04-17 02:20:36 +04:00
static void hidp_input_report ( struct hidp_session * session , struct sk_buff * skb )
{
struct input_dev * dev = session - > input ;
unsigned char * keys = session - > keys ;
unsigned char * udata = skb - > data + 1 ;
signed char * sdata = skb - > data + 1 ;
int i , size = skb - > len - 1 ;
switch ( skb - > data [ 0 ] ) {
case 0x01 : /* Keyboard report */
for ( i = 0 ; i < 8 ; i + + )
input_report_key ( dev , hidp_keycode [ i + 224 ] , ( udata [ 0 ] > > i ) & 1 ) ;
/* If all the key codes have been set to 0x01, it means
* too many keys were pressed at the same time . */
if ( ! memcmp ( udata + 2 , hidp_mkeyspat , 6 ) )
break ;
for ( i = 2 ; i < 8 ; i + + ) {
if ( keys [ i ] > 3 & & memscan ( udata + 2 , keys [ i ] , 6 ) = = udata + 8 ) {
if ( hidp_keycode [ keys [ i ] ] )
input_report_key ( dev , hidp_keycode [ keys [ i ] ] , 0 ) ;
else
BT_ERR ( " Unknown key (scancode %#x) released. " , keys [ i ] ) ;
}
if ( udata [ i ] > 3 & & memscan ( keys + 2 , udata [ i ] , 6 ) = = keys + 8 ) {
if ( hidp_keycode [ udata [ i ] ] )
input_report_key ( dev , hidp_keycode [ udata [ i ] ] , 1 ) ;
else
BT_ERR ( " Unknown key (scancode %#x) pressed. " , udata [ i ] ) ;
}
}
memcpy ( keys , udata , 8 ) ;
break ;
case 0x02 : /* Mouse report */
input_report_key ( dev , BTN_LEFT , sdata [ 0 ] & 0x01 ) ;
input_report_key ( dev , BTN_RIGHT , sdata [ 0 ] & 0x02 ) ;
input_report_key ( dev , BTN_MIDDLE , sdata [ 0 ] & 0x04 ) ;
input_report_key ( dev , BTN_SIDE , sdata [ 0 ] & 0x08 ) ;
input_report_key ( dev , BTN_EXTRA , sdata [ 0 ] & 0x10 ) ;
input_report_rel ( dev , REL_X , sdata [ 1 ] ) ;
input_report_rel ( dev , REL_Y , sdata [ 2 ] ) ;
if ( size > 3 )
input_report_rel ( dev , REL_WHEEL , sdata [ 3 ] ) ;
break ;
}
input_sync ( dev ) ;
}
2010-01-20 15:00:42 +03:00
static int __hidp_send_ctrl_message ( struct hidp_session * session ,
2012-06-10 02:22:42 +04:00
unsigned char hdr , unsigned char * data ,
int size )
2010-01-20 15:00:42 +03:00
{
struct sk_buff * skb ;
BT_DBG ( " session %p data %p size %d " , session , data , size ) ;
2011-08-26 16:06:02 +04:00
if ( atomic_read ( & session - > terminate ) )
return - EIO ;
2011-01-11 18:20:20 +03:00
skb = alloc_skb ( size + 1 , GFP_ATOMIC ) ;
if ( ! skb ) {
2010-01-20 15:00:42 +03:00
BT_ERR ( " Can't allocate memory for new frame " ) ;
return - ENOMEM ;
}
* skb_put ( skb , 1 ) = hdr ;
if ( data & & size > 0 )
memcpy ( skb_put ( skb , size ) , data , size ) ;
skb_queue_tail ( & session - > ctrl_transmit , skb ) ;
return 0 ;
}
2012-05-23 11:04:18 +04:00
static int hidp_send_ctrl_message ( struct hidp_session * session ,
2010-01-20 15:00:42 +03:00
unsigned char hdr , unsigned char * data , int size )
{
int err ;
err = __hidp_send_ctrl_message ( session , hdr , data , size ) ;
hidp_schedule ( session ) ;
return err ;
}
2008-02-05 14:07:58 +03:00
static int hidp_queue_report ( struct hidp_session * session ,
unsigned char * data , int size )
2007-02-18 01:58:49 +03:00
{
struct sk_buff * skb ;
2007-10-20 16:15:39 +04:00
BT_DBG ( " session %p hid %p data %p size %d " , session , session - > hid , data , size ) ;
2007-02-18 01:58:49 +03:00
2011-01-11 18:20:20 +03:00
skb = alloc_skb ( size + 1 , GFP_ATOMIC ) ;
if ( ! skb ) {
2007-02-18 01:58:49 +03:00
BT_ERR ( " Can't allocate memory for new frame " ) ;
return - ENOMEM ;
}
* skb_put ( skb , 1 ) = 0xa2 ;
if ( size > 0 )
memcpy ( skb_put ( skb , size ) , data , size ) ;
skb_queue_tail ( & session - > intr_transmit , skb ) ;
hidp_schedule ( session ) ;
return 0 ;
}
static int hidp_send_report ( struct hidp_session * session , struct hid_report * report )
{
unsigned char buf [ 32 ] ;
int rsize ;
rsize = ( ( report - > size - 1 ) > > 3 ) + 1 + ( report - > id > 0 ) ;
if ( rsize > sizeof ( buf ) )
return - EIO ;
hid_output_report ( report , buf ) ;
return hidp_queue_report ( session , buf , rsize ) ;
}
2011-01-18 11:04:40 +03:00
static int hidp_get_raw_report ( struct hid_device * hid ,
unsigned char report_number ,
unsigned char * data , size_t count ,
unsigned char report_type )
{
struct hidp_session * session = hid - > driver_data ;
struct sk_buff * skb ;
size_t len ;
int numbered_reports = hid - > report_enum [ report_type ] . numbered ;
2011-08-26 16:06:02 +04:00
int ret ;
2011-01-18 11:04:40 +03:00
2013-02-20 22:16:19 +04:00
if ( atomic_read ( & session - > terminate ) )
return - EIO ;
2011-01-18 11:04:40 +03:00
switch ( report_type ) {
case HID_FEATURE_REPORT :
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE ;
break ;
case HID_INPUT_REPORT :
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT ;
break ;
case HID_OUTPUT_REPORT :
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT ;
break ;
default :
return - EINVAL ;
}
if ( mutex_lock_interruptible ( & session - > report_mutex ) )
return - ERESTARTSYS ;
/* Set up our wait, and send the report request to the device. */
session - > waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK ;
session - > waiting_report_number = numbered_reports ? report_number : - 1 ;
set_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) ;
data [ 0 ] = report_number ;
2011-08-26 16:06:02 +04:00
ret = hidp_send_ctrl_message ( hid - > driver_data , report_type , data , 1 ) ;
if ( ret )
goto err ;
2011-01-18 11:04:40 +03:00
/* Wait for the return of the report. The returned report
gets put in session - > report_return . */
2013-04-06 22:28:41 +04:00
while ( test_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) & &
! atomic_read ( & session - > terminate ) ) {
2011-01-18 11:04:40 +03:00
int res ;
res = wait_event_interruptible_timeout ( session - > report_queue ,
2013-04-06 22:28:41 +04:00
! test_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags )
| | atomic_read ( & session - > terminate ) ,
2011-01-18 11:04:40 +03:00
5 * HZ ) ;
if ( res = = 0 ) {
/* timeout */
2011-08-26 16:06:02 +04:00
ret = - EIO ;
goto err ;
2011-01-18 11:04:40 +03:00
}
if ( res < 0 ) {
/* signal */
2011-08-26 16:06:02 +04:00
ret = - ERESTARTSYS ;
goto err ;
2011-01-18 11:04:40 +03:00
}
}
skb = session - > report_return ;
if ( skb ) {
len = skb - > len < count ? skb - > len : count ;
memcpy ( data , skb - > data , len ) ;
kfree_skb ( skb ) ;
session - > report_return = NULL ;
} else {
/* Device returned a HANDSHAKE, indicating protocol error. */
len = - EIO ;
}
clear_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) ;
mutex_unlock ( & session - > report_mutex ) ;
return len ;
2011-08-26 16:06:02 +04:00
err :
2011-01-18 11:04:40 +03:00
clear_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) ;
mutex_unlock ( & session - > report_mutex ) ;
2011-08-26 16:06:02 +04:00
return ret ;
2011-01-18 11:04:40 +03:00
}
2010-01-29 17:03:36 +03:00
static int hidp_output_raw_report ( struct hid_device * hid , unsigned char * data , size_t count ,
unsigned char report_type )
2009-11-26 18:20:56 +03:00
{
2011-01-18 11:04:38 +03:00
struct hidp_session * session = hid - > driver_data ;
int ret ;
2010-01-29 17:03:36 +03:00
switch ( report_type ) {
case HID_FEATURE_REPORT :
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE ;
break ;
case HID_OUTPUT_REPORT :
2011-02-20 20:26:46 +03:00
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT ;
2010-01-29 17:03:36 +03:00
break ;
default :
return - EINVAL ;
}
2011-01-18 11:04:38 +03:00
if ( mutex_lock_interruptible ( & session - > report_mutex ) )
return - ERESTARTSYS ;
/* Set up our wait, and send the report request to the device. */
set_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags ) ;
2011-08-26 16:06:02 +04:00
ret = hidp_send_ctrl_message ( hid - > driver_data , report_type , data ,
count ) ;
if ( ret )
2011-01-18 11:04:38 +03:00
goto err ;
/* Wait for the ACK from the device. */
2013-04-06 22:28:41 +04:00
while ( test_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags ) & &
! atomic_read ( & session - > terminate ) ) {
2011-01-18 11:04:38 +03:00
int res ;
res = wait_event_interruptible_timeout ( session - > report_queue ,
2013-04-06 22:28:41 +04:00
! test_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags )
| | atomic_read ( & session - > terminate ) ,
2011-01-18 11:04:38 +03:00
10 * HZ ) ;
if ( res = = 0 ) {
/* timeout */
ret = - EIO ;
goto err ;
}
if ( res < 0 ) {
/* signal */
ret = - ERESTARTSYS ;
goto err ;
}
}
if ( ! session - > output_report_success ) {
ret = - EIO ;
goto err ;
}
ret = count ;
err :
clear_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags ) ;
mutex_unlock ( & session - > report_mutex ) ;
return ret ;
2009-11-26 18:20:56 +03:00
}
2005-04-17 02:20:36 +04:00
static void hidp_idle_timeout ( unsigned long arg )
{
struct hidp_session * session = ( struct hidp_session * ) arg ;
2011-06-30 21:53:53 +04:00
atomic_inc ( & session - > terminate ) ;
wake_up_process ( session - > task ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 14:07:58 +03:00
static void hidp_set_timer ( struct hidp_session * session )
2005-04-17 02:20:36 +04:00
{
if ( session - > idle_to > 0 )
mod_timer ( & session - > timer , jiffies + HZ * session - > idle_to ) ;
}
2012-05-23 11:04:18 +04:00
static void hidp_del_timer ( struct hidp_session * session )
2005-04-17 02:20:36 +04:00
{
if ( session - > idle_to > 0 )
del_timer ( & session - > timer ) ;
}
2008-02-05 14:07:58 +03:00
static void hidp_process_handshake ( struct hidp_session * session ,
unsigned char param )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " session %p param 0x%02x " , session , param ) ;
2011-01-18 11:04:38 +03:00
session - > output_report_success = 0 ; /* default condition */
2005-04-17 02:20:36 +04:00
switch ( param ) {
case HIDP_HSHK_SUCCESSFUL :
/* FIXME: Call into SET_ GET_ handlers here */
2011-01-18 11:04:38 +03:00
session - > output_report_success = 1 ;
2005-04-17 02:20:36 +04:00
break ;
case HIDP_HSHK_NOT_READY :
case HIDP_HSHK_ERR_INVALID_REPORT_ID :
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST :
case HIDP_HSHK_ERR_INVALID_PARAMETER :
2011-10-07 05:05:37 +04:00
if ( test_and_clear_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) )
2011-01-18 11:04:40 +03:00
wake_up_interruptible ( & session - > report_queue ) ;
2011-10-07 05:05:37 +04:00
2005-04-17 02:20:36 +04:00
/* FIXME: Call into SET_ GET_ handlers here */
break ;
case HIDP_HSHK_ERR_UNKNOWN :
break ;
case HIDP_HSHK_ERR_FATAL :
/* Device requests a reboot, as this is the only way this error
2007-02-09 17:24:33 +03:00
* can be recovered . */
2005-04-17 02:20:36 +04:00
__hidp_send_ctrl_message ( session ,
HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET , NULL , 0 ) ;
break ;
default :
__hidp_send_ctrl_message ( session ,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER , NULL , 0 ) ;
break ;
}
2011-01-18 11:04:38 +03:00
/* Wake up the waiting thread. */
2011-10-07 05:05:37 +04:00
if ( test_and_clear_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags ) )
2011-01-18 11:04:38 +03:00
wake_up_interruptible ( & session - > report_queue ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 14:07:58 +03:00
static void hidp_process_hid_control ( struct hidp_session * session ,
unsigned char param )
2005-04-17 02:20:36 +04:00
{
BT_DBG ( " session %p param 0x%02x " , session , param ) ;
2008-02-05 14:07:14 +03:00
if ( param = = HIDP_CTRL_VIRTUAL_CABLE_UNPLUG ) {
2005-04-17 02:20:36 +04:00
/* Flush the transmit queues */
skb_queue_purge ( & session - > ctrl_transmit ) ;
skb_queue_purge ( & session - > intr_transmit ) ;
2011-06-30 21:53:53 +04:00
atomic_inc ( & session - > terminate ) ;
wake_up_process ( current ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-18 11:04:40 +03:00
/* Returns true if the passed-in skb should be freed by the caller. */
static int hidp_process_data ( struct hidp_session * session , struct sk_buff * skb ,
2008-02-05 14:07:58 +03:00
unsigned char param )
2005-04-17 02:20:36 +04:00
{
2011-01-18 11:04:40 +03:00
int done_with_skb = 1 ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " session %p skb %p len %d param 0x%02x " , session , skb , skb - > len , param ) ;
switch ( param ) {
case HIDP_DATA_RTYPE_INPUT :
hidp_set_timer ( session ) ;
if ( session - > input )
hidp_input_report ( session , skb ) ;
2007-02-18 01:58:49 +03:00
if ( session - > hid )
hid_input_report ( session - > hid , HID_INPUT_REPORT , skb - > data , skb - > len , 0 ) ;
2005-04-17 02:20:36 +04:00
break ;
case HIDP_DATA_RTYPE_OTHER :
case HIDP_DATA_RTYPE_OUPUT :
case HIDP_DATA_RTYPE_FEATURE :
break ;
default :
__hidp_send_ctrl_message ( session ,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER , NULL , 0 ) ;
}
2011-01-18 11:04:40 +03:00
if ( test_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) & &
param = = session - > waiting_report_type ) {
if ( session - > waiting_report_number < 0 | |
session - > waiting_report_number = = skb - > data [ 0 ] ) {
/* hidp_get_raw_report() is waiting on this report. */
session - > report_return = skb ;
done_with_skb = 0 ;
clear_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) ;
wake_up_interruptible ( & session - > report_queue ) ;
}
}
return done_with_skb ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 14:07:58 +03:00
static void hidp_recv_ctrl_frame ( struct hidp_session * session ,
struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
unsigned char hdr , type , param ;
2011-01-18 11:04:40 +03:00
int free_skb = 1 ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " session %p skb %p len %d " , session , skb , skb - > len ) ;
hdr = skb - > data [ 0 ] ;
skb_pull ( skb , 1 ) ;
type = hdr & HIDP_HEADER_TRANS_MASK ;
param = hdr & HIDP_HEADER_PARAM_MASK ;
switch ( type ) {
case HIDP_TRANS_HANDSHAKE :
hidp_process_handshake ( session , param ) ;
break ;
case HIDP_TRANS_HID_CONTROL :
hidp_process_hid_control ( session , param ) ;
break ;
case HIDP_TRANS_DATA :
2011-01-18 11:04:40 +03:00
free_skb = hidp_process_data ( session , skb , param ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
__hidp_send_ctrl_message ( session ,
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST , NULL , 0 ) ;
break ;
}
2011-01-18 11:04:40 +03:00
if ( free_skb )
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 14:07:58 +03:00
static void hidp_recv_intr_frame ( struct hidp_session * session ,
struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
unsigned char hdr ;
BT_DBG ( " session %p skb %p len %d " , session , skb , skb - > len ) ;
hdr = skb - > data [ 0 ] ;
skb_pull ( skb , 1 ) ;
if ( hdr = = ( HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT ) ) {
hidp_set_timer ( session ) ;
2007-02-18 01:58:49 +03:00
2005-04-17 02:20:36 +04:00
if ( session - > input )
hidp_input_report ( session , skb ) ;
2007-02-18 01:58:49 +03:00
if ( session - > hid ) {
hid_input_report ( session - > hid , HID_INPUT_REPORT , skb - > data , skb - > len , 1 ) ;
BT_DBG ( " report len %d " , skb - > len ) ;
}
2005-04-17 02:20:36 +04:00
} else {
BT_DBG ( " Unsupported protocol header 0x%02x " , hdr ) ;
}
kfree_skb ( skb ) ;
}
static int hidp_send_frame ( struct socket * sock , unsigned char * data , int len )
{
struct kvec iv = { data , len } ;
struct msghdr msg ;
BT_DBG ( " sock %p data %p len %d " , sock , data , len ) ;
if ( ! len )
return 0 ;
memset ( & msg , 0 , sizeof ( msg ) ) ;
return kernel_sendmsg ( sock , & msg , & iv , 1 , len ) ;
}
2011-10-07 03:51:37 +04:00
static void hidp_process_intr_transmit ( struct hidp_session * session )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
BT_DBG ( " session %p " , session ) ;
2011-10-07 03:51:37 +04:00
while ( ( skb = skb_dequeue ( & session - > intr_transmit ) ) ) {
if ( hidp_send_frame ( session - > intr_sock , skb - > data , skb - > len ) < 0 ) {
skb_queue_head ( & session - > intr_transmit , skb ) ;
2005-04-17 02:20:36 +04:00
break ;
}
hidp_set_timer ( session ) ;
kfree_skb ( skb ) ;
}
2011-10-07 03:51:37 +04:00
}
2005-04-17 02:20:36 +04:00
2011-10-07 03:51:37 +04:00
static void hidp_process_ctrl_transmit ( struct hidp_session * session )
{
struct sk_buff * skb ;
BT_DBG ( " session %p " , session ) ;
while ( ( skb = skb_dequeue ( & session - > ctrl_transmit ) ) ) {
if ( hidp_send_frame ( session - > ctrl_sock , skb - > data , skb - > len ) < 0 ) {
skb_queue_head ( & session - > ctrl_transmit , skb ) ;
2005-04-17 02:20:36 +04:00
break ;
}
hidp_set_timer ( session ) ;
kfree_skb ( skb ) ;
}
}
static int hidp_session ( void * arg )
{
struct hidp_session * session = arg ;
struct sock * ctrl_sk = session - > ctrl_sock - > sk ;
struct sock * intr_sk = session - > intr_sock - > sk ;
struct sk_buff * skb ;
wait_queue_t ctrl_wait , intr_wait ;
BT_DBG ( " session %p " , session ) ;
2011-11-01 20:27:50 +04:00
__module_get ( THIS_MODULE ) ;
2005-04-17 02:20:36 +04:00
set_user_nice ( current , - 15 ) ;
init_waitqueue_entry ( & ctrl_wait , current ) ;
init_waitqueue_entry ( & intr_wait , current ) ;
2010-04-20 17:03:51 +04:00
add_wait_queue ( sk_sleep ( ctrl_sk ) , & ctrl_wait ) ;
add_wait_queue ( sk_sleep ( intr_sk ) , & intr_wait ) ;
2011-01-18 11:04:37 +03:00
session - > waiting_for_startup = 0 ;
wake_up_interruptible ( & session - > startup_queue ) ;
2011-06-30 21:53:53 +04:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
while ( ! atomic_read ( & session - > terminate ) ) {
2011-03-21 16:20:01 +03:00
if ( ctrl_sk - > sk_state ! = BT_CONNECTED | |
intr_sk - > sk_state ! = BT_CONNECTED )
2005-04-17 02:20:36 +04:00
break ;
2011-10-07 01:02:13 +04:00
while ( ( skb = skb_dequeue ( & intr_sk - > sk_receive_queue ) ) ) {
2005-04-17 02:20:36 +04:00
skb_orphan ( skb ) ;
2011-07-23 01:53:58 +04:00
if ( ! skb_linearize ( skb ) )
2011-10-07 01:02:13 +04:00
hidp_recv_intr_frame ( session , skb ) ;
2011-07-23 01:53:58 +04:00
else
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2011-10-07 03:51:37 +04:00
hidp_process_intr_transmit ( session ) ;
2011-10-07 01:02:13 +04:00
while ( ( skb = skb_dequeue ( & ctrl_sk - > sk_receive_queue ) ) ) {
2005-04-17 02:20:36 +04:00
skb_orphan ( skb ) ;
2011-07-23 01:53:58 +04:00
if ( ! skb_linearize ( skb ) )
2011-10-07 01:02:13 +04:00
hidp_recv_ctrl_frame ( session , skb ) ;
2011-07-23 01:53:58 +04:00
else
kfree_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2011-10-07 03:51:37 +04:00
hidp_process_ctrl_transmit ( session ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2011-06-30 21:53:53 +04:00
set_current_state ( TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
set_current_state ( TASK_RUNNING ) ;
2013-02-20 22:16:19 +04:00
atomic_inc ( & session - > terminate ) ;
2010-04-20 17:03:51 +04:00
remove_wait_queue ( sk_sleep ( intr_sk ) , & intr_wait ) ;
remove_wait_queue ( sk_sleep ( ctrl_sk ) , & ctrl_wait ) ;
2005-04-17 02:20:36 +04:00
2011-08-26 16:06:02 +04:00
clear_bit ( HIDP_WAITING_FOR_SEND_ACK , & session - > flags ) ;
clear_bit ( HIDP_WAITING_FOR_RETURN , & session - > flags ) ;
wake_up_interruptible ( & session - > report_queue ) ;
2005-04-17 02:20:36 +04:00
down_write ( & hidp_session_sem ) ;
hidp_del_timer ( session ) ;
if ( session - > input ) {
input_unregister_device ( session - > input ) ;
2005-09-15 11:01:40 +04:00
session - > input = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-02-18 01:58:49 +03:00
if ( session - > hid ) {
2008-05-16 13:49:15 +04:00
hid_destroy_device ( session - > hid ) ;
2009-08-23 01:22:15 +04:00
session - > hid = NULL ;
2007-02-18 01:58:49 +03:00
}
2008-07-14 22:13:53 +04:00
/* Wakeup user-space polling for socket errors */
session - > intr_sock - > sk - > sk_err = EUNATCH ;
session - > ctrl_sock - > sk - > sk_err = EUNATCH ;
hidp_schedule ( session ) ;
2007-07-07 22:58:39 +04:00
fput ( session - > intr_sock - > file ) ;
2010-04-20 17:03:51 +04:00
wait_event_timeout ( * ( sk_sleep ( ctrl_sk ) ) ,
2007-07-07 22:58:39 +04:00
( ctrl_sk - > sk_state = = BT_CLOSED ) , msecs_to_jiffies ( 500 ) ) ;
fput ( session - > ctrl_sock - > file ) ;
Bluetooth: remove unneeded hci_conn_hold/put_device()
hci_conn_hold/put_device() is used to control when hci_conn->dev is no
longer needed and can be deleted from the system. Lets first look how they
are currently used throughout the code (excluding HIDP!).
All code that uses hci_conn_hold_device() looks like this:
...
hci_conn_hold_device();
hci_conn_add_sysfs();
...
On the other side, hci_conn_put_device() is exclusively used in
hci_conn_del().
So, considering that hci_conn_del() must not be called twice (which would
fail horribly), we know that hci_conn_put_device() is only called _once_
(which is in hci_conn_del()).
On the other hand, hci_conn_add_sysfs() must not be called twice, either
(it would call device_add twice, which breaks the device, see
drivers/base/core.c). So we know that hci_conn_hold_device() is also
called only once (it's only called directly before hci_conn_add_sysfs()).
So hold and put are known to be called only once. That means we can safely
remove them and directly call hci_conn_del_sysfs() in hci_conn_del().
But there is one issue left: HIDP also uses hci_conn_hold/put_device().
However, this case can be ignored and simply removed as it is totally
broken. The issue is, the only thing HIDP delays with
hci_conn_hold_device() is the removal of the hci_conn->dev from sysfs.
But, the hci_conn device has no mechanism to get notified when its own
parent (hci_dev) gets removed from sysfs. hci_dev_hold/put() does _not_
control when it is removed but only when the device object is created
and destroyed.
And hci_dev calls hci_conn_flush_*() when it removes itself from sysfs,
which itself causes hci_conn_del() to be called, but it does _not_ cause
hci_conn_del_sysfs() to be called, which is wrong.
Hence, we fix it to call hci_conn_del_sysfs() in hci_conn_del(). This
guarantees that a hci_conn object is removed from sysfs _before_ its
parent hci_dev is removed.
The changes to HIDP look scary, wrong and broken. However, if you look at
the HIDP session management, you will notice they're already broken in the
exact _same_ way (ever tried "unplugging" HIDP devices? Breaks _all_ the
time).
So this patch only makes HIDP look _scary_ and _obviously broken_. It does
not break HIDP itself, it already is!
See later patches in this series which fix HIDP to use proper
session-management.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
2013-04-06 22:28:38 +04:00
list_del ( & session - > list ) ;
2007-07-07 22:58:39 +04:00
2005-04-17 02:20:36 +04:00
up_write ( & hidp_session_sem ) ;
2011-08-05 18:51:34 +04:00
kfree ( session - > rd_data ) ;
2005-04-17 02:20:36 +04:00
kfree ( session ) ;
2011-11-01 20:27:50 +04:00
module_put_and_exit ( 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-10-07 08:29:51 +04:00
static struct hci_conn * hidp_get_connection ( struct hidp_session * session )
2006-07-06 15:09:02 +04:00
{
bdaddr_t * src = & bt_sk ( session - > ctrl_sock - > sk ) - > src ;
bdaddr_t * dst = & bt_sk ( session - > ctrl_sock - > sk ) - > dst ;
2011-08-30 19:53:35 +04:00
struct hci_conn * conn ;
2006-07-06 15:09:02 +04:00
struct hci_dev * hdev ;
hdev = hci_get_route ( dst , src ) ;
if ( ! hdev )
return NULL ;
2011-06-17 20:03:21 +04:00
hci_dev_lock ( hdev ) ;
2011-08-30 19:53:35 +04:00
conn = hci_conn_hash_lookup_ba ( hdev , ACL_LINK , dst ) ;
2011-06-17 20:03:21 +04:00
hci_dev_unlock ( hdev ) ;
2006-07-06 15:09:02 +04:00
hci_dev_put ( hdev ) ;
2011-08-30 19:53:35 +04:00
return conn ;
2006-07-06 15:09:02 +04:00
}
2008-02-05 14:07:58 +03:00
static int hidp_setup_input ( struct hidp_session * session ,
struct hidp_connadd_req * req )
2005-04-17 02:20:36 +04:00
{
2008-05-16 13:49:16 +04:00
struct input_dev * input ;
2011-10-07 04:17:32 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2008-05-16 13:49:16 +04:00
input = input_allocate_device ( ) ;
if ( ! input )
return - ENOMEM ;
session - > input = input ;
2007-05-09 11:15:30 +04:00
input_set_drvdata ( input , session ) ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:40 +04:00
input - > name = " Bluetooth HID Boot Protocol Device " ;
2005-04-17 02:20:36 +04:00
input - > id . bustype = BUS_BLUETOOTH ;
input - > id . vendor = req - > vendor ;
input - > id . product = req - > product ;
input - > id . version = req - > version ;
if ( req - > subclass & 0x40 ) {
set_bit ( EV_KEY , input - > evbit ) ;
set_bit ( EV_LED , input - > evbit ) ;
set_bit ( EV_REP , input - > evbit ) ;
set_bit ( LED_NUML , input - > ledbit ) ;
set_bit ( LED_CAPSL , input - > ledbit ) ;
set_bit ( LED_SCROLLL , input - > ledbit ) ;
set_bit ( LED_COMPOSE , input - > ledbit ) ;
set_bit ( LED_KANA , input - > ledbit ) ;
for ( i = 0 ; i < sizeof ( hidp_keycode ) ; i + + )
set_bit ( hidp_keycode [ i ] , input - > keybit ) ;
clear_bit ( 0 , input - > keybit ) ;
}
if ( req - > subclass & 0x80 ) {
2007-10-19 10:40:32 +04:00
input - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REL ) ;
input - > keybit [ BIT_WORD ( BTN_MOUSE ) ] = BIT_MASK ( BTN_LEFT ) |
BIT_MASK ( BTN_RIGHT ) | BIT_MASK ( BTN_MIDDLE ) ;
input - > relbit [ 0 ] = BIT_MASK ( REL_X ) | BIT_MASK ( REL_Y ) ;
input - > keybit [ BIT_WORD ( BTN_MOUSE ) ] | = BIT_MASK ( BTN_SIDE ) |
BIT_MASK ( BTN_EXTRA ) ;
input - > relbit [ 0 ] | = BIT_MASK ( REL_WHEEL ) ;
2005-04-17 02:20:36 +04:00
}
2013-04-06 22:28:46 +04:00
input - > dev . parent = & session - > hconn - > dev ;
2006-07-06 15:09:02 +04:00
2005-04-17 02:20:36 +04:00
input - > event = hidp_input_event ;
2009-08-23 01:22:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-18 01:58:53 +03:00
static int hidp_open ( struct hid_device * hid )
{
return 0 ;
}
static void hidp_close ( struct hid_device * hid )
{
}
2008-05-16 13:49:16 +04:00
static int hidp_parse ( struct hid_device * hid )
{
struct hidp_session * session = hid - > driver_data ;
2010-02-05 20:23:43 +03:00
return hid_parse_report ( session - > hid , session - > rd_data ,
session - > rd_size ) ;
2008-05-16 13:49:16 +04:00
}
static int hidp_start ( struct hid_device * hid )
{
struct hidp_session * session = hid - > driver_data ;
struct hid_report * report ;
2011-08-26 15:27:12 +04:00
if ( hid - > quirks & HID_QUIRK_NO_INIT_REPORTS )
return 0 ;
2008-05-16 13:49:16 +04:00
list_for_each_entry ( report , & hid - > report_enum [ HID_INPUT_REPORT ] .
report_list , list )
hidp_send_report ( session , report ) ;
list_for_each_entry ( report , & hid - > report_enum [ HID_FEATURE_REPORT ] .
report_list , list )
hidp_send_report ( session , report ) ;
return 0 ;
}
static void hidp_stop ( struct hid_device * hid )
{
struct hidp_session * session = hid - > driver_data ;
skb_queue_purge ( & session - > ctrl_transmit ) ;
skb_queue_purge ( & session - > intr_transmit ) ;
hid - > claimed = 0 ;
}
static struct hid_ll_driver hidp_hid_driver = {
. parse = hidp_parse ,
. start = hidp_start ,
. stop = hidp_stop ,
. open = hidp_open ,
. close = hidp_close ,
. hidinput_input_event = hidp_hidinput_event ,
} ;
2011-01-18 11:04:37 +03:00
/* This function sets up the hid device. It does not add it
to the HID system . That is done in hidp_add_connection ( ) . */
2008-05-16 13:49:15 +04:00
static int hidp_setup_hid ( struct hidp_session * session ,
2008-02-05 14:07:58 +03:00
struct hidp_connadd_req * req )
2007-02-18 01:58:49 +03:00
{
2008-05-16 13:49:16 +04:00
struct hid_device * hid ;
2009-08-23 01:22:15 +04:00
int err ;
2007-02-18 01:58:49 +03:00
2010-02-05 20:23:43 +03:00
session - > rd_data = kzalloc ( req - > rd_size , GFP_KERNEL ) ;
if ( ! session - > rd_data )
return - ENOMEM ;
if ( copy_from_user ( session - > rd_data , req - > rd_data , req - > rd_size ) ) {
err = - EFAULT ;
goto fault ;
}
session - > rd_size = req - > rd_size ;
2008-05-16 13:49:16 +04:00
hid = hid_allocate_device ( ) ;
2010-02-05 20:23:43 +03:00
if ( IS_ERR ( hid ) ) {
err = PTR_ERR ( hid ) ;
goto fault ;
}
2007-02-18 01:58:49 +03:00
2008-05-16 13:49:16 +04:00
session - > hid = hid ;
2010-02-05 20:23:43 +03:00
2007-02-18 01:58:49 +03:00
hid - > driver_data = session ;
hid - > bus = BUS_BLUETOOTH ;
hid - > vendor = req - > vendor ;
hid - > product = req - > product ;
hid - > version = req - > version ;
2008-05-16 13:49:16 +04:00
hid - > country = req - > country ;
2007-02-18 01:58:49 +03:00
2013-01-07 02:28:53 +04:00
strncpy ( hid - > name , req - > name , sizeof ( req - > name ) - 1 ) ;
2012-09-25 13:49:44 +04:00
snprintf ( hid - > phys , sizeof ( hid - > phys ) , " %pMR " ,
& bt_sk ( session - > ctrl_sock - > sk ) - > src ) ;
snprintf ( hid - > uniq , sizeof ( hid - > uniq ) , " %pMR " ,
& bt_sk ( session - > ctrl_sock - > sk ) - > dst ) ;
2007-02-18 01:58:49 +03:00
2013-04-06 22:28:46 +04:00
hid - > dev . parent = & session - > hconn - > dev ;
2008-05-16 13:49:16 +04:00
hid - > ll_driver = & hidp_hid_driver ;
2008-05-16 13:49:15 +04:00
2011-01-18 11:04:40 +03:00
hid - > hid_get_raw_report = hidp_get_raw_report ;
2009-11-26 18:20:56 +03:00
hid - > hid_output_raw_report = hidp_output_raw_report ;
2012-12-06 18:39:55 +04:00
/* True if device is blacklisted in drivers/hid/hid-core.c */
if ( hid_ignore ( hid ) ) {
hid_destroy_device ( session - > hid ) ;
session - > hid = NULL ;
return - ENODEV ;
}
2008-05-16 13:49:16 +04:00
return 0 ;
2009-08-23 01:22:15 +04:00
2010-02-05 20:23:43 +03:00
fault :
kfree ( session - > rd_data ) ;
session - > rd_data = NULL ;
2009-08-23 01:22:15 +04:00
return err ;
2007-02-18 01:58:49 +03:00
}
2013-04-06 22:28:46 +04:00
/* initialize session devices */
static int hidp_session_dev_init ( struct hidp_session * session ,
struct hidp_connadd_req * req )
{
int ret ;
if ( req - > rd_size > 0 ) {
ret = hidp_setup_hid ( session , req ) ;
if ( ret & & ret ! = - ENODEV )
return ret ;
}
if ( ! session - > hid ) {
ret = hidp_setup_input ( session , req ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
/* destroy session devices */
static void hidp_session_dev_destroy ( struct hidp_session * session )
{
if ( session - > hid )
put_device ( & session - > hid - > dev ) ;
else if ( session - > input )
input_put_device ( session - > input ) ;
kfree ( session - > rd_data ) ;
session - > rd_data = NULL ;
}
/* add HID/input devices to their underlying bus systems */
static int hidp_session_dev_add ( struct hidp_session * session )
{
int ret ;
/* Both HID and input systems drop a ref-count when unregistering the
* device but they don ' t take a ref - count when registering them . Work
* around this by explicitly taking a refcount during registration
* which is dropped automatically by unregistering the devices . */
if ( session - > hid ) {
ret = hid_add_device ( session - > hid ) ;
if ( ret )
return ret ;
get_device ( & session - > hid - > dev ) ;
} else if ( session - > input ) {
ret = input_register_device ( session - > input ) ;
if ( ret )
return ret ;
input_get_device ( session - > input ) ;
}
return 0 ;
}
/* remove HID/input devices from their bus systems */
static void hidp_session_dev_del ( struct hidp_session * session )
{
if ( session - > hid )
hid_destroy_device ( session - > hid ) ;
else if ( session - > input )
input_unregister_device ( session - > input ) ;
}
/*
* Create new session object
* Allocate session object , initialize static fields , copy input data into the
* object and take a reference to all sub - objects .
* This returns 0 on success and puts a pointer to the new session object in
* \ out . Otherwise , an error code is returned .
* The new session object has an initial ref - count of 1.
*/
static int hidp_session_new ( struct hidp_session * * out , const bdaddr_t * bdaddr ,
struct socket * ctrl_sock ,
struct socket * intr_sock ,
struct hidp_connadd_req * req ,
struct l2cap_conn * conn )
{
struct hidp_session * session ;
int ret ;
struct bt_sock * ctrl , * intr ;
ctrl = bt_sk ( ctrl_sock - > sk ) ;
intr = bt_sk ( intr_sock - > sk ) ;
session = kzalloc ( sizeof ( * session ) , GFP_KERNEL ) ;
if ( ! session )
return - ENOMEM ;
/* object and runtime management */
kref_init ( & session - > ref ) ;
atomic_set ( & session - > state , HIDP_SESSION_IDLING ) ;
init_waitqueue_head ( & session - > state_queue ) ;
session - > flags = req - > flags & ( 1 < < HIDP_BLUETOOTH_VENDOR_ID ) ;
/* connection management */
bacpy ( & session - > bdaddr , bdaddr ) ;
session - > conn = conn ;
session - > user . probe = hidp_session_probe ;
session - > user . remove = hidp_session_remove ;
session - > ctrl_sock = ctrl_sock ;
session - > intr_sock = intr_sock ;
skb_queue_head_init ( & session - > ctrl_transmit ) ;
skb_queue_head_init ( & session - > intr_transmit ) ;
session - > ctrl_mtu = min_t ( uint , l2cap_pi ( ctrl ) - > chan - > omtu ,
l2cap_pi ( ctrl ) - > chan - > imtu ) ;
session - > intr_mtu = min_t ( uint , l2cap_pi ( intr ) - > chan - > omtu ,
l2cap_pi ( intr ) - > chan - > imtu ) ;
session - > idle_to = req - > idle_to ;
/* device management */
setup_timer ( & session - > timer , hidp_idle_timeout ,
( unsigned long ) session ) ;
/* session data */
mutex_init ( & session - > report_mutex ) ;
init_waitqueue_head ( & session - > report_queue ) ;
ret = hidp_session_dev_init ( session , req ) ;
if ( ret )
goto err_free ;
l2cap_conn_get ( session - > conn ) ;
get_file ( session - > intr_sock - > file ) ;
get_file ( session - > ctrl_sock - > file ) ;
* out = session ;
return 0 ;
err_free :
kfree ( session ) ;
return ret ;
}
/* increase ref-count of the given session by one */
static void hidp_session_get ( struct hidp_session * session )
{
kref_get ( & session - > ref ) ;
}
/* release callback */
static void session_free ( struct kref * ref )
{
struct hidp_session * session = container_of ( ref , struct hidp_session ,
ref ) ;
hidp_session_dev_destroy ( session ) ;
skb_queue_purge ( & session - > ctrl_transmit ) ;
skb_queue_purge ( & session - > intr_transmit ) ;
fput ( session - > intr_sock - > file ) ;
fput ( session - > ctrl_sock - > file ) ;
l2cap_conn_put ( session - > conn ) ;
kfree ( session ) ;
}
/* decrease ref-count of the given session by one */
static void hidp_session_put ( struct hidp_session * session )
{
kref_put ( & session - > ref , session_free ) ;
}
/*
* Search the list of active sessions for a session with target address
* \ bdaddr . You must hold at least a read - lock on \ hidp_session_sem . As long as
* you do not release this lock , the session objects cannot vanish and you can
* safely take a reference to the session yourself .
*/
static struct hidp_session * __hidp_session_find ( const bdaddr_t * bdaddr )
{
struct hidp_session * session ;
list_for_each_entry ( session , & hidp_session_list , list ) {
if ( ! bacmp ( bdaddr , & session - > bdaddr ) )
return session ;
}
return NULL ;
}
/*
* Same as __hidp_session_find ( ) but no locks must be held . This also takes a
* reference of the returned session ( if non - NULL ) so you must drop this
* reference if you no longer use the object .
*/
static struct hidp_session * hidp_session_find ( const bdaddr_t * bdaddr )
{
struct hidp_session * session ;
down_read ( & hidp_session_sem ) ;
session = __hidp_session_find ( bdaddr ) ;
if ( session )
hidp_session_get ( session ) ;
up_read ( & hidp_session_sem ) ;
return session ;
}
/*
* Start session synchronously
* This starts a session thread and waits until initialization
* is done or returns an error if it couldn ' t be started .
* If this returns 0 the session thread is up and running . You must call
* hipd_session_stop_sync ( ) before deleting any runtime resources .
*/
static int hidp_session_start_sync ( struct hidp_session * session )
{
unsigned int vendor , product ;
if ( session - > hid ) {
vendor = session - > hid - > vendor ;
product = session - > hid - > product ;
} else if ( session - > input ) {
vendor = session - > input - > id . vendor ;
product = session - > input - > id . product ;
} else {
vendor = 0x0000 ;
product = 0x0000 ;
}
session - > task = kthread_run ( hidp_session_thread , session ,
" khidpd_%04x%04x " , vendor , product ) ;
if ( IS_ERR ( session - > task ) )
return PTR_ERR ( session - > task ) ;
while ( atomic_read ( & session - > state ) < = HIDP_SESSION_IDLING )
wait_event ( session - > state_queue ,
atomic_read ( & session - > state ) > HIDP_SESSION_IDLING ) ;
return 0 ;
}
/*
* Terminate session thread
* Wake up session thread and notify it to stop . This is asynchronous and
* returns immediately . Call this whenever a runtime error occurs and you want
* the session to stop .
* Note : wake_up_process ( ) performs any necessary memory - barriers for us .
*/
static void hidp_session_terminate ( struct hidp_session * session )
{
atomic_inc ( & session - > terminate ) ;
wake_up_process ( session - > task ) ;
}
/*
* Probe HIDP session
* This is called from the l2cap_conn core when our l2cap_user object is bound
* to the hci - connection . We get the session via the \ user object and can now
* start the session thread , register the HID / input devices and link it into
* the global session list .
* The global session - list owns its own reference to the session object so you
* can drop your own reference after registering the l2cap_user object .
*/
static int hidp_session_probe ( struct l2cap_conn * conn ,
struct l2cap_user * user )
{
struct hidp_session * session = container_of ( user ,
struct hidp_session ,
user ) ;
struct hidp_session * s ;
int ret ;
down_write ( & hidp_session_sem ) ;
/* check that no other session for this device exists */
s = __hidp_session_find ( & session - > bdaddr ) ;
if ( s ) {
ret = - EEXIST ;
goto out_unlock ;
}
ret = hidp_session_start_sync ( session ) ;
if ( ret )
goto out_unlock ;
ret = hidp_session_dev_add ( session ) ;
if ( ret )
goto out_stop ;
hidp_session_get ( session ) ;
list_add ( & session - > list , & hidp_session_list ) ;
ret = 0 ;
goto out_unlock ;
out_stop :
hidp_session_terminate ( session ) ;
out_unlock :
up_write ( & hidp_session_sem ) ;
return ret ;
}
/*
* Remove HIDP session
* Called from the l2cap_conn core when either we explicitly unregistered
* the l2cap_user object or if the underlying connection is shut down .
* We signal the hidp - session thread to shut down , unregister the HID / input
* devices and unlink the session from the global list .
* This drops the reference to the session that is owned by the global
* session - list .
* Note : We _must_ not synchronosly wait for the session - thread to shut down .
* This is , because the session - thread might be waiting for an HCI lock that is
* held while we are called . Therefore , we only unregister the devices and
* notify the session - thread to terminate . The thread itself owns a reference
* to the session object so it can safely shut down .
*/
static void hidp_session_remove ( struct l2cap_conn * conn ,
struct l2cap_user * user )
{
struct hidp_session * session = container_of ( user ,
struct hidp_session ,
user ) ;
down_write ( & hidp_session_sem ) ;
hidp_session_terminate ( session ) ;
hidp_session_dev_del ( session ) ;
list_del ( & session - > list ) ;
up_write ( & hidp_session_sem ) ;
hidp_session_put ( session ) ;
}
/*
* Session Worker
* This performs the actual main - loop of the HIDP worker . We first check
* whether the underlying connection is still alive , then parse all pending
* messages and finally send all outstanding messages .
*/
static void hidp_session_run ( struct hidp_session * session )
{
struct sock * ctrl_sk = session - > ctrl_sock - > sk ;
struct sock * intr_sk = session - > intr_sock - > sk ;
struct sk_buff * skb ;
for ( ; ; ) {
/*
* This thread can be woken up two ways :
* - You call hidp_session_terminate ( ) which sets the
* session - > terminate flag and wakes this thread up .
* - Via modifying the socket state of ctrl / intr_sock . This
* thread is woken up by - > sk_state_changed ( ) .
*
* Note : set_current_state ( ) performs any necessary
* memory - barriers for us .
*/
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( atomic_read ( & session - > terminate ) )
break ;
if ( ctrl_sk - > sk_state ! = BT_CONNECTED | |
intr_sk - > sk_state ! = BT_CONNECTED )
break ;
/* parse incoming intr-skbs */
while ( ( skb = skb_dequeue ( & intr_sk - > sk_receive_queue ) ) ) {
skb_orphan ( skb ) ;
if ( ! skb_linearize ( skb ) )
hidp_recv_intr_frame ( session , skb ) ;
else
kfree_skb ( skb ) ;
}
/* send pending intr-skbs */
hidp_process_intr_transmit ( session ) ;
/* parse incoming ctrl-skbs */
while ( ( skb = skb_dequeue ( & ctrl_sk - > sk_receive_queue ) ) ) {
skb_orphan ( skb ) ;
if ( ! skb_linearize ( skb ) )
hidp_recv_ctrl_frame ( session , skb ) ;
else
kfree_skb ( skb ) ;
}
/* send pending ctrl-skbs */
hidp_process_ctrl_transmit ( session ) ;
schedule ( ) ;
}
atomic_inc ( & session - > terminate ) ;
set_current_state ( TASK_RUNNING ) ;
}
/*
* HIDP session thread
* This thread runs the I / O for a single HIDP session . Startup is synchronous
* which allows us to take references to ourself here instead of doing that in
* the caller .
* When we are ready to run we notify the caller and call hidp_session_run ( ) .
*/
static int hidp_session_thread ( void * arg )
{
struct hidp_session * session = arg ;
wait_queue_t ctrl_wait , intr_wait ;
BT_DBG ( " session %p " , session ) ;
/* initialize runtime environment */
hidp_session_get ( session ) ;
__module_get ( THIS_MODULE ) ;
set_user_nice ( current , - 15 ) ;
hidp_set_timer ( session ) ;
init_waitqueue_entry ( & ctrl_wait , current ) ;
init_waitqueue_entry ( & intr_wait , current ) ;
add_wait_queue ( sk_sleep ( session - > ctrl_sock - > sk ) , & ctrl_wait ) ;
add_wait_queue ( sk_sleep ( session - > intr_sock - > sk ) , & intr_wait ) ;
/* This memory barrier is paired with wq_has_sleeper(). See
* sock_poll_wait ( ) for more information why this is needed . */
smp_mb ( ) ;
/* notify synchronous startup that we're ready */
atomic_inc ( & session - > state ) ;
wake_up ( & session - > state_queue ) ;
/* run session */
hidp_session_run ( session ) ;
/* cleanup runtime environment */
remove_wait_queue ( sk_sleep ( session - > intr_sock - > sk ) , & intr_wait ) ;
remove_wait_queue ( sk_sleep ( session - > intr_sock - > sk ) , & ctrl_wait ) ;
wake_up_interruptible ( & session - > report_queue ) ;
hidp_del_timer ( session ) ;
/*
* If we stopped ourself due to any internal signal , we should try to
* unregister our own session here to avoid having it linger until the
* parent l2cap_conn dies or user - space cleans it up .
* This does not deadlock as we don ' t do any synchronous shutdown .
* Instead , this call has the same semantics as if user - space tried to
* delete the session .
*/
l2cap_unregister_user ( session - > conn , & session - > user ) ;
hidp_session_put ( session ) ;
module_put_and_exit ( 0 ) ;
return 0 ;
}
static int hidp_verify_sockets ( struct socket * ctrl_sock ,
struct socket * intr_sock )
{
struct bt_sock * ctrl , * intr ;
struct hidp_session * session ;
if ( ! l2cap_is_socket ( ctrl_sock ) | | ! l2cap_is_socket ( intr_sock ) )
return - EINVAL ;
ctrl = bt_sk ( ctrl_sock - > sk ) ;
intr = bt_sk ( intr_sock - > sk ) ;
if ( bacmp ( & ctrl - > src , & intr - > src ) | | bacmp ( & ctrl - > dst , & intr - > dst ) )
return - ENOTUNIQ ;
if ( ctrl - > sk . sk_state ! = BT_CONNECTED | |
intr - > sk . sk_state ! = BT_CONNECTED )
return - EBADFD ;
/* early session check, we check again during session registration */
session = hidp_session_find ( & ctrl - > dst ) ;
if ( session ) {
hidp_session_put ( session ) ;
return - EEXIST ;
}
return 0 ;
}
int hidp_connection_add ( struct hidp_connadd_req * req ,
struct socket * ctrl_sock ,
struct socket * intr_sock )
{
struct hidp_session * session ;
struct l2cap_conn * conn ;
struct l2cap_chan * chan = l2cap_pi ( ctrl_sock - > sk ) - > chan ;
int ret ;
ret = hidp_verify_sockets ( ctrl_sock , intr_sock ) ;
if ( ret )
return ret ;
conn = NULL ;
l2cap_chan_lock ( chan ) ;
if ( chan - > conn ) {
l2cap_conn_get ( chan - > conn ) ;
conn = chan - > conn ;
}
l2cap_chan_unlock ( chan ) ;
if ( ! conn )
return - EBADFD ;
ret = hidp_session_new ( & session , & bt_sk ( ctrl_sock - > sk ) - > dst , ctrl_sock ,
intr_sock , req , conn ) ;
if ( ret )
goto out_conn ;
ret = l2cap_register_user ( conn , & session - > user ) ;
if ( ret )
goto out_session ;
ret = 0 ;
out_session :
hidp_session_put ( session ) ;
out_conn :
l2cap_conn_put ( conn ) ;
return ret ;
}
int hidp_connection_del ( struct hidp_conndel_req * req )
{
struct hidp_session * session ;
session = hidp_session_find ( & req - > bdaddr ) ;
if ( ! session )
return - ENOENT ;
if ( req - > flags & ( 1 < < HIDP_VIRTUAL_CABLE_UNPLUG ) )
hidp_send_ctrl_message ( session ,
HIDP_TRANS_HID_CONTROL |
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG ,
NULL , 0 ) ;
else
l2cap_unregister_user ( session - > conn , & session - > user ) ;
hidp_session_put ( session ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
int hidp_add_connection ( struct hidp_connadd_req * req , struct socket * ctrl_sock , struct socket * intr_sock )
{
struct hidp_session * session , * s ;
2011-04-05 17:37:45 +04:00
int vendor , product ;
2005-04-17 02:20:36 +04:00
int err ;
BT_DBG ( " " ) ;
2013-04-05 16:57:34 +04:00
if ( ! l2cap_is_socket ( ctrl_sock ) | | ! l2cap_is_socket ( intr_sock ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( bacmp ( & bt_sk ( ctrl_sock - > sk ) - > src , & bt_sk ( intr_sock - > sk ) - > src ) | |
bacmp ( & bt_sk ( ctrl_sock - > sk ) - > dst , & bt_sk ( intr_sock - > sk ) - > dst ) )
return - ENOTUNIQ ;
2007-02-18 01:58:49 +03:00
BT_DBG ( " rd_data %p rd_size %d " , req - > rd_data , req - > rd_size ) ;
2005-04-17 02:20:36 +04:00
down_write ( & hidp_session_sem ) ;
s = __hidp_get_session ( & bt_sk ( ctrl_sock - > sk ) - > dst ) ;
2013-04-06 22:28:40 +04:00
if ( s ) {
2011-10-07 06:32:29 +04:00
up_write ( & hidp_session_sem ) ;
return - EEXIST ;
}
session = kzalloc ( sizeof ( struct hidp_session ) , GFP_KERNEL ) ;
if ( ! session ) {
up_write ( & hidp_session_sem ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
bacpy ( & session - > bdaddr , & bt_sk ( ctrl_sock - > sk ) - > dst ) ;
2011-04-14 00:20:49 +04:00
session - > ctrl_mtu = min_t ( uint , l2cap_pi ( ctrl_sock - > sk ) - > chan - > omtu ,
l2cap_pi ( ctrl_sock - > sk ) - > chan - > imtu ) ;
session - > intr_mtu = min_t ( uint , l2cap_pi ( intr_sock - > sk ) - > chan - > omtu ,
l2cap_pi ( intr_sock - > sk ) - > chan - > imtu ) ;
2005-04-17 02:20:36 +04:00
BT_DBG ( " ctrl mtu %d intr mtu %d " , session - > ctrl_mtu , session - > intr_mtu ) ;
session - > ctrl_sock = ctrl_sock ;
session - > intr_sock = intr_sock ;
2013-04-06 22:28:46 +04:00
session - > hconn = hidp_get_connection ( session ) ;
if ( ! session - > hconn ) {
2011-10-20 23:21:34 +04:00
err = - ENOTCONN ;
goto failed ;
}
2008-01-24 08:20:07 +03:00
setup_timer ( & session - > timer , hidp_idle_timeout , ( unsigned long ) session ) ;
2005-04-17 02:20:36 +04:00
skb_queue_head_init ( & session - > ctrl_transmit ) ;
skb_queue_head_init ( & session - > intr_transmit ) ;
2011-01-18 11:04:40 +03:00
mutex_init ( & session - > report_mutex ) ;
init_waitqueue_head ( & session - > report_queue ) ;
2011-01-18 11:04:37 +03:00
init_waitqueue_head ( & session - > startup_queue ) ;
session - > waiting_for_startup = 1 ;
2005-04-17 02:20:36 +04:00
session - > flags = req - > flags & ( 1 < < HIDP_BLUETOOTH_VENDOR_ID ) ;
session - > idle_to = req - > idle_to ;
Bluetooth: remove unneeded hci_conn_hold/put_device()
hci_conn_hold/put_device() is used to control when hci_conn->dev is no
longer needed and can be deleted from the system. Lets first look how they
are currently used throughout the code (excluding HIDP!).
All code that uses hci_conn_hold_device() looks like this:
...
hci_conn_hold_device();
hci_conn_add_sysfs();
...
On the other side, hci_conn_put_device() is exclusively used in
hci_conn_del().
So, considering that hci_conn_del() must not be called twice (which would
fail horribly), we know that hci_conn_put_device() is only called _once_
(which is in hci_conn_del()).
On the other hand, hci_conn_add_sysfs() must not be called twice, either
(it would call device_add twice, which breaks the device, see
drivers/base/core.c). So we know that hci_conn_hold_device() is also
called only once (it's only called directly before hci_conn_add_sysfs()).
So hold and put are known to be called only once. That means we can safely
remove them and directly call hci_conn_del_sysfs() in hci_conn_del().
But there is one issue left: HIDP also uses hci_conn_hold/put_device().
However, this case can be ignored and simply removed as it is totally
broken. The issue is, the only thing HIDP delays with
hci_conn_hold_device() is the removal of the hci_conn->dev from sysfs.
But, the hci_conn device has no mechanism to get notified when its own
parent (hci_dev) gets removed from sysfs. hci_dev_hold/put() does _not_
control when it is removed but only when the device object is created
and destroyed.
And hci_dev calls hci_conn_flush_*() when it removes itself from sysfs,
which itself causes hci_conn_del() to be called, but it does _not_ cause
hci_conn_del_sysfs() to be called, which is wrong.
Hence, we fix it to call hci_conn_del_sysfs() in hci_conn_del(). This
guarantees that a hci_conn object is removed from sysfs _before_ its
parent hci_dev is removed.
The changes to HIDP look scary, wrong and broken. However, if you look at
the HIDP session management, you will notice they're already broken in the
exact _same_ way (ever tried "unplugging" HIDP devices? Breaks _all_ the
time).
So this patch only makes HIDP look _scary_ and _obviously broken_. It does
not break HIDP itself, it already is!
See later patches in this series which fix HIDP to use proper
session-management.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
2013-04-06 22:28:38 +04:00
list_add ( & session - > list , & hidp_session_list ) ;
2011-08-30 19:53:35 +04:00
2008-05-16 13:49:16 +04:00
if ( req - > rd_size > 0 ) {
2008-05-16 13:49:15 +04:00
err = hidp_setup_hid ( session , req ) ;
2012-12-06 18:39:55 +04:00
if ( err & & err ! = - ENODEV )
2009-08-23 01:22:15 +04:00
goto purge ;
2008-05-16 13:49:16 +04:00
}
if ( ! session - > hid ) {
err = hidp_setup_input ( session , req ) ;
if ( err < 0 )
2009-08-23 01:22:15 +04:00
goto purge ;
2008-05-16 13:49:15 +04:00
}
2007-02-18 01:58:49 +03:00
2005-04-17 02:20:36 +04:00
hidp_set_timer ( session ) ;
2011-04-05 17:37:45 +04:00
if ( session - > hid ) {
vendor = session - > hid - > vendor ;
product = session - > hid - > product ;
} else if ( session - > input ) {
vendor = session - > input - > id . vendor ;
product = session - > input - > id . product ;
} else {
vendor = 0x0000 ;
product = 0x0000 ;
}
session - > task = kthread_run ( hidp_session , session , " khidpd_%04x%04x " ,
vendor , product ) ;
if ( IS_ERR ( session - > task ) ) {
err = PTR_ERR ( session - > task ) ;
2005-04-17 02:20:36 +04:00
goto unlink ;
2011-04-05 17:37:45 +04:00
}
2011-01-18 11:04:37 +03:00
while ( session - > waiting_for_startup ) {
wait_event_interruptible ( session - > startup_queue ,
! session - > waiting_for_startup ) ;
}
2011-10-07 04:17:32 +04:00
if ( session - > hid )
err = hid_add_device ( session - > hid ) ;
else
err = input_register_device ( session - > input ) ;
2011-08-05 18:51:26 +04:00
if ( err < 0 ) {
atomic_inc ( & session - > terminate ) ;
wake_up_process ( session - > task ) ;
up_write ( & hidp_session_sem ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
if ( session - > input ) {
hidp_send_ctrl_message ( session ,
HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT , NULL , 0 ) ;
session - > flags | = ( 1 < < HIDP_BOOT_PROTOCOL_MODE ) ;
session - > leds = 0xff ;
hidp_input_event ( session - > input , EV_LED , 0 , 0 ) ;
}
up_write ( & hidp_session_sem ) ;
return 0 ;
unlink :
hidp_del_timer ( session ) ;
2009-08-23 01:22:15 +04:00
if ( session - > input ) {
2005-04-17 02:20:36 +04:00
input_unregister_device ( session - > input ) ;
2009-08-23 01:22:15 +04:00
session - > input = NULL ;
}
if ( session - > hid ) {
2008-05-16 13:49:15 +04:00
hid_destroy_device ( session - > hid ) ;
2009-08-23 01:22:15 +04:00
session - > hid = NULL ;
}
2010-02-05 20:23:43 +03:00
kfree ( session - > rd_data ) ;
session - > rd_data = NULL ;
2009-08-23 01:22:15 +04:00
purge :
Bluetooth: remove unneeded hci_conn_hold/put_device()
hci_conn_hold/put_device() is used to control when hci_conn->dev is no
longer needed and can be deleted from the system. Lets first look how they
are currently used throughout the code (excluding HIDP!).
All code that uses hci_conn_hold_device() looks like this:
...
hci_conn_hold_device();
hci_conn_add_sysfs();
...
On the other side, hci_conn_put_device() is exclusively used in
hci_conn_del().
So, considering that hci_conn_del() must not be called twice (which would
fail horribly), we know that hci_conn_put_device() is only called _once_
(which is in hci_conn_del()).
On the other hand, hci_conn_add_sysfs() must not be called twice, either
(it would call device_add twice, which breaks the device, see
drivers/base/core.c). So we know that hci_conn_hold_device() is also
called only once (it's only called directly before hci_conn_add_sysfs()).
So hold and put are known to be called only once. That means we can safely
remove them and directly call hci_conn_del_sysfs() in hci_conn_del().
But there is one issue left: HIDP also uses hci_conn_hold/put_device().
However, this case can be ignored and simply removed as it is totally
broken. The issue is, the only thing HIDP delays with
hci_conn_hold_device() is the removal of the hci_conn->dev from sysfs.
But, the hci_conn device has no mechanism to get notified when its own
parent (hci_dev) gets removed from sysfs. hci_dev_hold/put() does _not_
control when it is removed but only when the device object is created
and destroyed.
And hci_dev calls hci_conn_flush_*() when it removes itself from sysfs,
which itself causes hci_conn_del() to be called, but it does _not_ cause
hci_conn_del_sysfs() to be called, which is wrong.
Hence, we fix it to call hci_conn_del_sysfs() in hci_conn_del(). This
guarantees that a hci_conn object is removed from sysfs _before_ its
parent hci_dev is removed.
The changes to HIDP look scary, wrong and broken. However, if you look at
the HIDP session management, you will notice they're already broken in the
exact _same_ way (ever tried "unplugging" HIDP devices? Breaks _all_ the
time).
So this patch only makes HIDP look _scary_ and _obviously broken_. It does
not break HIDP itself, it already is!
See later patches in this series which fix HIDP to use proper
session-management.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
2013-04-06 22:28:38 +04:00
list_del ( & session - > list ) ;
2011-08-30 19:53:35 +04:00
2008-05-16 13:49:16 +04:00
skb_queue_purge ( & session - > ctrl_transmit ) ;
skb_queue_purge ( & session - > intr_transmit ) ;
2009-08-23 01:22:15 +04:00
2008-05-16 13:49:16 +04:00
failed :
up_write ( & hidp_session_sem ) ;
2007-02-18 01:58:49 +03:00
2005-04-17 02:20:36 +04:00
kfree ( session ) ;
return err ;
}
int hidp_del_connection ( struct hidp_conndel_req * req )
{
struct hidp_session * session ;
int err = 0 ;
BT_DBG ( " " ) ;
down_read ( & hidp_session_sem ) ;
session = __hidp_get_session ( & req - > bdaddr ) ;
if ( session ) {
if ( req - > flags & ( 1 < < HIDP_VIRTUAL_CABLE_UNPLUG ) ) {
hidp_send_ctrl_message ( session ,
HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG , NULL , 0 ) ;
} else {
/* Flush the transmit queues */
skb_queue_purge ( & session - > ctrl_transmit ) ;
skb_queue_purge ( & session - > intr_transmit ) ;
2011-06-30 21:53:53 +04:00
atomic_inc ( & session - > terminate ) ;
wake_up_process ( session - > task ) ;
2005-04-17 02:20:36 +04:00
}
} else
err = - ENOENT ;
up_read ( & hidp_session_sem ) ;
return err ;
}
int hidp_get_connlist ( struct hidp_connlist_req * req )
{
2011-10-07 00:35:31 +04:00
struct hidp_session * session ;
2005-04-17 02:20:36 +04:00
int err = 0 , n = 0 ;
BT_DBG ( " " ) ;
down_read ( & hidp_session_sem ) ;
2011-10-07 00:35:31 +04:00
list_for_each_entry ( session , & hidp_session_list , list ) {
2005-04-17 02:20:36 +04:00
struct hidp_conninfo ci ;
__hidp_copy_session ( session , & ci ) ;
if ( copy_to_user ( req - > ci , & ci , sizeof ( ci ) ) ) {
err = - EFAULT ;
break ;
}
if ( + + n > = req - > cnum )
break ;
req - > ci + + ;
}
req - > cnum = n ;
up_read ( & hidp_session_sem ) ;
return err ;
}
int hidp_get_conninfo ( struct hidp_conninfo * ci )
{
struct hidp_session * session ;
int err = 0 ;
down_read ( & hidp_session_sem ) ;
session = __hidp_get_session ( & ci - > bdaddr ) ;
if ( session )
__hidp_copy_session ( session , ci ) ;
else
err = - ENOENT ;
up_read ( & hidp_session_sem ) ;
return err ;
}
static int __init hidp_init ( void )
{
BT_INFO ( " HIDP (Human Interface Emulation) ver %s " , VERSION ) ;
2012-04-23 14:07:07 +04:00
return hidp_init_sockets ( ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit hidp_exit ( void )
{
hidp_cleanup_sockets ( ) ;
}
module_init ( hidp_init ) ;
module_exit ( hidp_exit ) ;
MODULE_AUTHOR ( " Marcel Holtmann <marcel@holtmann.org> " ) ;
2013-04-06 22:28:46 +04:00
MODULE_AUTHOR ( " David Herrmann <dh.herrmann@gmail.com> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Bluetooth HIDP ver " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " bt-proto-6 " ) ;