Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2011 17:35:30 +0000 (10:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2011 17:35:30 +0000 (10:35 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (48 commits)
  HID: add support for Logitech Driving Force Pro wheel
  HID: hid-ortek: remove spurious reference
  HID: add support for Ortek PKB-1700
  HID: roccat-koneplus: vorrect mode of sysfs attr 'sensor'
  HID: hid-ntrig: init settle and mode check
  HID: merge hid-egalax into hid-multitouch
  HID: hid-multitouch: Send events per slot if CONTACTCOUNT is missing
  HID: ntrig remove if and drop an indent
  HID: ACRUX - activate the device immediately after binding
  HID: ntrig: apply NO_INIT_REPORTS quirk
  HID: hid-magicmouse: Correct touch orientation direction
  HID: ntrig don't dereference unclaimed hidinput
  HID: Do not create input devices for feature reports
  HID: bt hidp: send Output reports using SET_REPORT on the Control channel
  HID: hid-sony.c: Fix sending Output reports to the Sixaxis
  HID: add support for Keytouch IEC 60945
  HID: Add HID Report Descriptor to sysfs
  HID: add IRTOUCH infrared USB to hid_have_special_driver
  HID: kernel oops in out_cleanup in function hidinput_connect
  HID: Add teletext/color keys - gyration remote - EU version (GYAR3101CKDE)
  ...

1  2 
drivers/hid/Kconfig
net/bluetooth/hidp/core.c

diff --combined drivers/hid/Kconfig
@@@ -62,24 -62,30 +62,30 @@@ config HID_3M_PC
        Support for 3M PCT touch screens.
  
  config HID_A4TECH
 -      tristate "A4 tech mice" if EMBEDDED
 +      tristate "A4 tech mice" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for A4 tech X5 and WOP-35 / Trust 450L mice.
  
- config HID_ACRUX_FF
-       tristate "ACRUX force feedback"
+ config HID_ACRUX
+       tristate "ACRUX game controller support"
        depends on USB_HID
+       ---help---
+       Say Y here if you want to enable support for ACRUX game controllers.
+ config HID_ACRUX_FF
+       tristate "ACRUX force feedback support"
+       depends on HID_ACRUX
        select INPUT_FF_MEMLESS
        ---help---
        Say Y here if you want to enable force feedback support for ACRUX
        game controllers.
  
  config HID_APPLE
 -      tristate "Apple {i,Power,Mac}Books" if EMBEDDED
 +      tristate "Apple {i,Power,Mac}Books" if EXPERT
        depends on (USB_HID || BT_HIDP)
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for some Apple devices which less or more break
        HID specification.
@@@ -88,9 -94,9 +94,9 @@@
        MacBooks, MacBook Pros and Apple Aluminum.
  
  config HID_BELKIN
 -      tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED
 +      tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Belkin Flip KVM and Wireless keyboard.
  
@@@ -101,16 -107,16 +107,16 @@@ config HID_CAND
        Support for Cando dual touch panel.
  
  config HID_CHERRY
 -      tristate "Cherry Cymotion keyboard" if EMBEDDED
 +      tristate "Cherry Cymotion keyboard" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Cherry Cymotion keyboard.
  
  config HID_CHICONY
 -      tristate "Chicony Tactical pad" if EMBEDDED
 +      tristate "Chicony Tactical pad" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Chicony Tactical pad.
  
@@@ -130,9 -136,9 +136,9 @@@ config HID_PRODIKEY
          and some additional multimedia keys.
  
  config HID_CYPRESS
 -      tristate "Cypress mouse and barcode readers" if EMBEDDED
 +      tristate "Cypress mouse and barcode readers" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for cypress mouse and barcode readers.
  
@@@ -140,7 -146,12 +146,12 @@@ config HID_DRAGONRIS
        tristate "DragonRise Inc. game controller"
        depends on USB_HID
        ---help---
-       Say Y here if you have DragonRise Inc.game controllers.
+       Say Y here if you have DragonRise Inc. game controllers.
+       These might be branded as:
+       - Tesun USB-703
+       - Media-tech MT1504 "Rogue"
+       - DVTech JS19 "Gear"
+       - Defender Game Master
  
  config DRAGONRISE_FF
        bool "DragonRise Inc. force feedback"
@@@ -160,13 -171,6 +171,6 @@@ config HID_EMS_F
        Currently the following devices are known to be supported:
         - Trio Linker Plus II
  
- config HID_EGALAX
-       tristate "eGalax multi-touch panel"
-       depends on USB_HID
-       ---help---
-       Support for the eGalax dual-touch panels, including the
-       Joojoo and Wetab tablets.
  config HID_ELECOM
        tristate "ELECOM BM084 bluetooth mouse"
        depends on BT_HIDP
        Support for the ELECOM BM084 (bluetooth mouse).
  
  config HID_EZKEY
 -      tristate "Ezkey BTC 8193 keyboard" if EMBEDDED
 +      tristate "Ezkey BTC 8193 keyboard" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Ezkey BTC 8193 keyboard.
  
+ config HID_KEYTOUCH
+       tristate "Keyoutch HID devices"
+       depends on USB_HID
+       ---help---
+       Support for Keytouch HID devices not fully compliant with
+       the specification. Currently supported:
+               - Keytouch IEC 60945
  config HID_KYE
 -      tristate "Kye/Genius Ergo Mouse" if EMBEDDED
 +      tristate "Kye/Genius Ergo Mouse" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Kye/Genius Ergo Mouse.
  
@@@ -212,16 -224,22 +224,22 @@@ config HID_TWINHA
        Support for Twinhan IR remote control.
  
  config HID_KENSINGTON
 -      tristate "Kensington Slimblade Trackball" if EMBEDDED
 +      tristate "Kensington Slimblade Trackball" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Kensington Slimblade Trackball.
  
+ config HID_LCPOWER
+       tristate "LC-Power"
+       depends on USB_HID
+       ---help---
+       Support for LC-Power RC1000MCE RF remote control.
  config HID_LOGITECH
 -      tristate "Logitech devices" if EMBEDDED
 +      tristate "Logitech devices" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Logitech devices that are not fully compliant with HID standard.
  
@@@ -276,9 -294,9 +294,9 @@@ config HID_MAGICMOUS
        Apple Wireless "Magic" Mouse.
  
  config HID_MICROSOFT
 -      tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED
 +      tristate "Microsoft non-fully HID-compliant devices" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Microsoft devices that are not fully compliant with HID standard.
  
@@@ -289,9 -307,9 +307,9 @@@ config HID_MOSAR
        Support for MosArt dual-touch panels.
  
  config HID_MONTEREY
 -      tristate "Monterey Genius KB29E keyboard" if EMBEDDED
 +      tristate "Monterey Genius KB29E keyboard" if EXPERT
        depends on USB_HID
 -      default !EMBEDDED
 +      default !EXPERT
        ---help---
        Support for Monterey Genius KB29E.
  
@@@ -304,8 -322,11 +322,11 @@@ config HID_MULTITOUC
          Say Y here if you have one of the following devices:
          - Cypress TrueTouch panels
          - Hanvon dual touch panels
+         - IrTouch Infrared USB panels
          - Pixcir dual touch panels
          - 'Sensing Win7-TwoFinger' panel by GeneralTouch
+           - eGalax dual-touch panels, including the
+           Joojoo and Wetab tablets
  
          If unsure, say N.
  
@@@ -319,10 -340,10 +340,10 @@@ config HID_NTRI
        Support for N-Trig touch screen.
  
  config HID_ORTEK
-       tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad"
+       tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad"
        depends on USB_HID
        ---help---
-       Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
+       Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad.
  
  config HID_PANTHERLORD
        tristate "Pantherlord/GreenAsia game controller"
@@@ -365,8 -386,8 +386,8 @@@ config HID_PICOLC
          - IR
  
  config HID_PICOLCD_FB
 -      bool "Framebuffer support" if EMBEDDED
 -      default !EMBEDDED
 +      bool "Framebuffer support" if EXPERT
 +      default !EXPERT
        depends on HID_PICOLCD
        depends on HID_PICOLCD=FB || FB=y
        select FB_DEFERRED_IO
          frambuffer device.
  
  config HID_PICOLCD_BACKLIGHT
 -      bool "Backlight control" if EMBEDDED
 -      default !EMBEDDED
 +      bool "Backlight control" if EXPERT
 +      default !EXPERT
        depends on HID_PICOLCD
        depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y
        ---help---
          class.
  
  config HID_PICOLCD_LCD
 -      bool "Contrast control" if EMBEDDED
 -      default !EMBEDDED
 +      bool "Contrast control" if EXPERT
 +      default !EXPERT
        depends on HID_PICOLCD
        depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y
        ---help---
          Provide access to PicoLCD's LCD contrast via lcd class.
  
  config HID_PICOLCD_LEDS
 -      bool "GPO via leds class" if EMBEDDED
 -      default !EMBEDDED
 +      bool "GPO via leds class" if EXPERT
 +      default !EXPERT
        depends on HID_PICOLCD
        depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y
        ---help---
@@@ -417,10 -438,22 +438,22 @@@ config HID_ROCCA
        Say Y here if you have a Roccat mouse or keyboard and want OSD or
        macro execution support.
  
+ config HID_ROCCAT_COMMON
+       tristate
+ config HID_ROCCAT_ARVO
+       tristate "Roccat Arvo keyboard support"
+       depends on USB_HID
+       select HID_ROCCAT
+       select HID_ROCCAT_COMMON
+       ---help---
+       Support for Roccat Arvo keyboard.
  config HID_ROCCAT_KONE
        tristate "Roccat Kone Mouse support"
        depends on USB_HID
        select HID_ROCCAT
+       select HID_ROCCAT_COMMON
        ---help---
        Support for Roccat Kone mouse.
  
@@@ -428,13 -461,23 +461,23 @@@ config HID_ROCCAT_KONEPLU
        tristate "Roccat Kone[+] mouse support"
        depends on USB_HID
        select HID_ROCCAT
+       select HID_ROCCAT_COMMON
        ---help---
        Support for Roccat Kone[+] mouse.
  
+ config HID_ROCCAT_KOVAPLUS
+       tristate "Roccat Kova[+] mouse support"
+       depends on USB_HID
+       select HID_ROCCAT
+       select HID_ROCCAT_COMMON
+       ---help---
+       Support for Roccat Kova[+] mouse.
  config HID_ROCCAT_PYRA
        tristate "Roccat Pyra mouse support"
        depends on USB_HID
        select HID_ROCCAT
+       select HID_ROCCAT_COMMON
        ---help---
        Support for Roccat Pyra mouse.
  
@@@ -36,6 -36,7 +36,7 @@@
  #include <linux/file.h>
  #include <linux/init.h>
  #include <linux/wait.h>
+ #include <linux/mutex.h>
  #include <net/sock.h>
  
  #include <linux/input.h>
@@@ -157,8 -158,7 +158,8 @@@ static int hidp_queue_event(struct hidp
  
        session->leds = newleds;
  
 -      if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
 +      skb = alloc_skb(3, GFP_ATOMIC);
 +      if (!skb) {
                BT_ERR("Can't allocate memory for new frame");
                return -ENOMEM;
        }
@@@ -251,8 -251,7 +252,8 @@@ static int __hidp_send_ctrl_message(str
  
        BT_DBG("session %p data %p size %d", session, data, size);
  
 -      if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
 +      skb = alloc_skb(size + 1, GFP_ATOMIC);
 +      if (!skb) {
                BT_ERR("Can't allocate memory for new frame");
                return -ENOMEM;
        }
@@@ -285,8 -284,7 +286,8 @@@ static int hidp_queue_report(struct hid
  
        BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
  
 -      if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
 +      skb = alloc_skb(size + 1, GFP_ATOMIC);
 +      if (!skb) {
                BT_ERR("Can't allocate memory for new frame");
                return -ENOMEM;
        }
@@@ -316,24 -314,144 +317,144 @@@ static int hidp_send_report(struct hidp
        return hidp_queue_report(session, buf, rsize);
  }
  
+ 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;
+       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;
+       if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1))
+               goto err_eio;
+       /* Wait for the return of the report. The returned report
+          gets put in session->report_return.  */
+       while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
+               int res;
+               res = wait_event_interruptible_timeout(session->report_queue,
+                       !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags),
+                       5*HZ);
+               if (res == 0) {
+                       /* timeout */
+                       goto err_eio;
+               }
+               if (res < 0) {
+                       /* signal */
+                       goto err_restartsys;
+               }
+       }
+       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;
+ err_restartsys:
+       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+       mutex_unlock(&session->report_mutex);
+       return -ERESTARTSYS;
+ err_eio:
+       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+       mutex_unlock(&session->report_mutex);
+       return -EIO;
+ }
  static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
                unsigned char report_type)
  {
+       struct hidp_session *session = hid->driver_data;
+       int ret;
        switch (report_type) {
        case HID_FEATURE_REPORT:
                report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
                break;
        case HID_OUTPUT_REPORT:
-               report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
+               report_type = HIDP_TRANS_SET_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. */
+       set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
        if (hidp_send_ctrl_message(hid->driver_data, report_type,
-                       data, count))
-               return -ENOMEM;
-       return count;
+                       data, count)) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       /* Wait for the ACK from the device. */
+       while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
+               int res;
+               res = wait_event_interruptible_timeout(session->report_queue,
+                       !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags),
+                       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;
  }
  
  static void hidp_idle_timeout(unsigned long arg)
@@@ -360,16 -478,22 +481,22 @@@ static void hidp_process_handshake(stru
                                        unsigned char param)
  {
        BT_DBG("session %p param 0x%02x", session, param);
+       session->output_report_success = 0; /* default condition */
  
        switch (param) {
        case HIDP_HSHK_SUCCESSFUL:
                /* FIXME: Call into SET_ GET_ handlers here */
+               session->output_report_success = 1;
                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:
+               if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) {
+                       clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
+                       wake_up_interruptible(&session->report_queue);
+               }
                /* FIXME: Call into SET_ GET_ handlers here */
                break;
  
                        HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
                break;
        }
+       /* Wake up the waiting thread. */
+       if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
+               clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
+               wake_up_interruptible(&session->report_queue);
+       }
  }
  
  static void hidp_process_hid_control(struct hidp_session *session,
        }
  }
  
- static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
+ /* 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,
                                unsigned char param)
  {
+       int done_with_skb = 1;
        BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
  
        switch (param) {
  
                if (session->hid)
                        hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
                break;
  
        case HIDP_DATA_RTYPE_OTHER:
                __hidp_send_ctrl_message(session,
                        HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
        }
+       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;
  }
  
  static void hidp_recv_ctrl_frame(struct hidp_session *session,
                                        struct sk_buff *skb)
  {
        unsigned char hdr, type, param;
+       int free_skb = 1;
  
        BT_DBG("session %p skb %p len %d", session, skb, skb->len);
  
                break;
  
        case HIDP_TRANS_DATA:
-               hidp_process_data(session, skb, param);
+               free_skb = hidp_process_data(session, skb, param);
                break;
  
        default:
                break;
        }
  
-       kfree_skb(skb);
+       if (free_skb)
+               kfree_skb(skb);
  }
  
  static void hidp_recv_intr_frame(struct hidp_session *session,
@@@ -566,6 -713,8 +716,8 @@@ static int hidp_session(void *arg
        init_waitqueue_entry(&intr_wait, current);
        add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
        add_wait_queue(sk_sleep(intr_sk), &intr_wait);
+       session->waiting_for_startup = 0;
+       wake_up_interruptible(&session->startup_queue);
        while (!atomic_read(&session->terminate)) {
                set_current_state(TASK_INTERRUPTIBLE);
  
@@@ -757,6 -906,8 +909,8 @@@ static struct hid_ll_driver hidp_hid_dr
        .hidinput_input_event = hidp_hidinput_event,
  };
  
+ /* This function sets up the hid device. It does not add it
+    to the HID system. That is done in hidp_add_connection(). */
  static int hidp_setup_hid(struct hidp_session *session,
                                struct hidp_connadd_req *req)
  {
        hid->dev.parent = hidp_get_device(session);
        hid->ll_driver = &hidp_hid_driver;
  
+       hid->hid_get_raw_report = hidp_get_raw_report;
        hid->hid_output_raw_report = hidp_output_raw_report;
  
-       err = hid_add_device(hid);
-       if (err < 0)
-               goto failed;
        return 0;
  
- failed:
-       hid_destroy_device(hid);
-       session->hid = NULL;
  fault:
        kfree(session->rd_data);
        session->rd_data = NULL;
@@@ -856,6 -1000,10 +1003,10 @@@ int hidp_add_connection(struct hidp_con
        skb_queue_head_init(&session->ctrl_transmit);
        skb_queue_head_init(&session->intr_transmit);
  
+       mutex_init(&session->report_mutex);
+       init_waitqueue_head(&session->report_queue);
+       init_waitqueue_head(&session->startup_queue);
+       session->waiting_for_startup = 1;
        session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
        session->idle_to = req->idle_to;
  
        err = kernel_thread(hidp_session, session, CLONE_KERNEL);
        if (err < 0)
                goto unlink;
+       while (session->waiting_for_startup) {
+               wait_event_interruptible(session->startup_queue,
+                       !session->waiting_for_startup);
+       }
+       err = hid_add_device(session->hid);
+       if (err < 0)
+               goto err_add_device;
  
        if (session->input) {
                hidp_send_ctrl_message(session,
        up_write(&hidp_session_sem);
        return 0;
  
+ err_add_device:
+       hid_destroy_device(session->hid);
+       session->hid = NULL;
+       atomic_inc(&session->terminate);
+       hidp_schedule(session);
  unlink:
        hidp_del_timer(session);
  
@@@ -1019,6 -1181,8 +1184,6 @@@ static int __init hidp_init(void
  {
        int ret;
  
 -      l2cap_load();
 -
        BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
  
        ret = hid_register_driver(&hidp_driver);