2007-10-20 20:12:34 +08:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Generic Bluetooth USB driver
|
|
|
|
*
|
2008-08-18 19:23:52 +08:00
|
|
|
* Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
|
2007-10-20 20:12:34 +08:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/usb.h>
|
2013-04-20 00:57:43 +08:00
|
|
|
#include <linux/firmware.h>
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
#include <net/bluetooth/bluetooth.h>
|
|
|
|
#include <net/bluetooth/hci_core.h>
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
#define VERSION "0.6"
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2012-01-13 07:02:20 +08:00
|
|
|
static bool ignore_dga;
|
|
|
|
static bool ignore_csr;
|
|
|
|
static bool ignore_sniffer;
|
|
|
|
static bool disable_scofix;
|
|
|
|
static bool force_scofix;
|
2008-11-30 19:17:26 +08:00
|
|
|
|
2012-01-13 07:02:20 +08:00
|
|
|
static bool reset = 1;
|
2008-08-08 04:26:56 +08:00
|
|
|
|
|
|
|
static struct usb_driver btusb_driver;
|
|
|
|
|
|
|
|
#define BTUSB_IGNORE 0x01
|
2008-11-30 19:17:26 +08:00
|
|
|
#define BTUSB_DIGIANSWER 0x02
|
|
|
|
#define BTUSB_CSR 0x04
|
|
|
|
#define BTUSB_SNIFFER 0x08
|
|
|
|
#define BTUSB_BCM92035 0x10
|
|
|
|
#define BTUSB_BROKEN_ISOC 0x20
|
|
|
|
#define BTUSB_WRONG_SCO_MTU 0x40
|
2011-07-01 14:02:36 +08:00
|
|
|
#define BTUSB_ATH3012 0x80
|
2013-04-20 00:57:43 +08:00
|
|
|
#define BTUSB_INTEL 0x100
|
2007-10-20 20:12:34 +08:00
|
|
|
|
2013-10-11 22:46:18 +08:00
|
|
|
static const struct usb_device_id btusb_table[] = {
|
2007-10-20 20:12:34 +08:00
|
|
|
/* Generic Bluetooth USB device */
|
|
|
|
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
|
|
|
|
|
2012-08-26 01:28:06 +08:00
|
|
|
/* Apple-specific (Broadcom) devices */
|
|
|
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
|
|
|
|
|
2013-06-04 21:40:26 +08:00
|
|
|
/* MediaTek MT76x0E */
|
|
|
|
{ USB_DEVICE(0x0e8d, 0x763f) },
|
|
|
|
|
2011-09-21 17:41:45 +08:00
|
|
|
/* Broadcom SoftSailing reporting vendor specific */
|
2012-03-29 04:41:11 +08:00
|
|
|
{ USB_DEVICE(0x0a5c, 0x21e1) },
|
2011-09-21 17:41:45 +08:00
|
|
|
|
2010-08-20 15:24:07 +08:00
|
|
|
/* Apple MacBookPro 7,1 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x8213) },
|
|
|
|
|
2010-07-14 14:29:27 +08:00
|
|
|
/* Apple iMac11,1 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x8215) },
|
|
|
|
|
2010-08-20 15:24:06 +08:00
|
|
|
/* Apple MacBookPro6,2 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x8218) },
|
|
|
|
|
2010-11-04 15:04:33 +08:00
|
|
|
/* Apple MacBookAir3,1, MacBookAir3,2 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x821b) },
|
|
|
|
|
2011-09-07 14:28:10 +08:00
|
|
|
/* Apple MacBookAir4,1 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x821f) },
|
|
|
|
|
2011-03-25 01:51:21 +08:00
|
|
|
/* Apple MacBookPro8,2 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x821a) },
|
|
|
|
|
2011-09-05 00:01:42 +08:00
|
|
|
/* Apple MacMini5,1 */
|
|
|
|
{ USB_DEVICE(0x05ac, 0x8281) },
|
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
/* AVM BlueFRITZ! USB v2.0 */
|
|
|
|
{ USB_DEVICE(0x057c, 0x3800) },
|
|
|
|
|
|
|
|
/* Bluetooth Ultraport Module from IBM */
|
|
|
|
{ USB_DEVICE(0x04bf, 0x030a) },
|
|
|
|
|
|
|
|
/* ALPS Modules with non-standard id */
|
|
|
|
{ USB_DEVICE(0x044e, 0x3001) },
|
|
|
|
{ USB_DEVICE(0x044e, 0x3002) },
|
|
|
|
|
|
|
|
/* Ericsson with non-standard id */
|
|
|
|
{ USB_DEVICE(0x0bdb, 0x1002) },
|
|
|
|
|
|
|
|
/* Canyon CN-BTU1 with HID interfaces */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x0c10, 0x0000) },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2011-11-08 14:30:22 +08:00
|
|
|
/* Broadcom BCM20702A0 */
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0489, 0xe042) },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x2003) },
|
2012-11-10 07:39:48 +08:00
|
|
|
{ USB_DEVICE(0x0b05, 0x17b5) },
|
2013-09-02 19:57:51 +08:00
|
|
|
{ USB_DEVICE(0x0b05, 0x17cb) },
|
2011-11-08 14:30:22 +08:00
|
|
|
{ USB_DEVICE(0x413c, 0x8197) },
|
|
|
|
|
2012-04-14 02:45:55 +08:00
|
|
|
/* Foxconn - Hon Hai */
|
2012-08-15 12:38:11 +08:00
|
|
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
|
2012-04-14 02:45:55 +08:00
|
|
|
|
2014-02-19 00:26:19 +08:00
|
|
|
/* Broadcom devices with vendor specific id */
|
2012-08-07 02:36:49 +08:00
|
|
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
|
|
|
|
|
2013-09-22 02:14:43 +08:00
|
|
|
/* Belkin F8065bf - Broadcom based */
|
|
|
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
|
|
|
|
|
2014-02-15 19:01:09 +08:00
|
|
|
/* IMC Networks - Broadcom based */
|
|
|
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
{ } /* Terminating entry */
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(usb, btusb_table);
|
|
|
|
|
2013-10-11 22:46:18 +08:00
|
|
|
static const struct usb_device_id blacklist_table[] = {
|
2008-08-08 04:26:56 +08:00
|
|
|
/* CSR BlueCore devices */
|
|
|
|
{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
|
|
|
|
|
|
|
|
/* Broadcom BCM2033 without firmware */
|
|
|
|
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
|
|
|
|
|
2010-11-26 20:05:46 +08:00
|
|
|
/* Atheros 3011 with sflash firmware */
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
|
|
|
|
{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
|
|
|
|
{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
|
2010-11-26 20:05:46 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
|
2012-06-08 20:32:50 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
|
2011-05-10 07:11:16 +08:00
|
|
|
{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
|
2010-11-26 20:05:46 +08:00
|
|
|
|
2011-01-26 17:10:59 +08:00
|
|
|
/* Atheros AR9285 Malbec with sflash firmware */
|
|
|
|
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
|
|
|
|
|
2011-02-11 18:08:53 +08:00
|
|
|
/* Atheros 3012 with sflash firmware */
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
2014-02-19 00:26:20 +08:00
|
|
|
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
2013-03-15 11:00:39 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
|
2011-07-01 14:02:36 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
|
2013-08-30 17:09:52 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 },
|
2013-03-12 03:41:58 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
|
2012-03-15 04:01:21 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
|
2014-01-16 22:37:11 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 },
|
2014-01-16 23:02:58 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
|
2013-03-18 23:45:11 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
|
2012-04-19 14:53:45 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
2013-08-30 17:41:40 +08:00
|
|
|
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
|
|
|
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
|
2012-12-11 11:41:20 +08:00
|
|
|
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
2013-07-15 11:59:03 +08:00
|
|
|
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
|
2011-02-11 18:08:53 +08:00
|
|
|
|
2011-02-15 10:20:07 +08:00
|
|
|
/* Atheros AR5BBU12 with sflash firmware */
|
|
|
|
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
|
|
|
|
|
2012-05-03 04:33:40 +08:00
|
|
|
/* Atheros AR5BBU12 with sflash firmware */
|
2012-08-07 22:18:10 +08:00
|
|
|
{ USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
|
2012-05-03 04:33:40 +08:00
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
/* Broadcom BCM2035 */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
2014-02-19 00:26:19 +08:00
|
|
|
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
|
|
|
/* Broadcom BCM2045 */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
{ USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-09-23 06:16:35 +08:00
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
/* IBM/Lenovo ThinkPad with Broadcom chip */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
{ USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
|
|
|
/* HP laptop with Broadcom chip */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
|
|
|
/* Dell laptop with Broadcom chip */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2008-11-30 19:17:27 +08:00
|
|
|
/* Dell Wireless 370 and 410 devices */
|
2008-11-30 19:17:26 +08:00
|
|
|
{ USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-11-30 19:17:27 +08:00
|
|
|
{ USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
/* Belkin F8T012 and F8T013 devices */
|
|
|
|
{ USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
{ USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU },
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2008-11-30 19:17:27 +08:00
|
|
|
/* Asus WL-BTD202 device */
|
|
|
|
{ USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
|
|
|
|
/* Kensington Bluetooth USB adapter */
|
|
|
|
{ USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU },
|
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
/* RTX Telecom based adapters with buggy SCO support */
|
|
|
|
{ USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
|
|
|
|
{ USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
|
|
|
|
|
|
|
|
/* CONWISE Technology based adapters with buggy SCO support */
|
|
|
|
{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
|
|
|
|
|
|
|
|
/* Digianswer devices */
|
|
|
|
{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
|
|
|
|
{ USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
|
|
|
|
|
|
|
|
/* CSR BlueCore Bluetooth Sniffer */
|
|
|
|
{ USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
|
|
|
|
|
|
|
|
/* Frontline ComProbe Bluetooth Sniffer */
|
|
|
|
{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
|
|
|
|
|
2013-04-20 00:57:43 +08:00
|
|
|
/* Intel Bluetooth device */
|
|
|
|
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
2013-11-13 05:10:58 +08:00
|
|
|
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
|
2013-04-20 00:57:43 +08:00
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
{ } /* Terminating entry */
|
|
|
|
};
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
#define BTUSB_MAX_ISOC_FRAMES 10
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
#define BTUSB_INTR_RUNNING 0
|
|
|
|
#define BTUSB_BULK_RUNNING 1
|
2008-08-18 19:23:52 +08:00
|
|
|
#define BTUSB_ISOC_RUNNING 2
|
2009-08-25 05:44:59 +08:00
|
|
|
#define BTUSB_SUSPENDING 3
|
2010-07-17 04:20:33 +08:00
|
|
|
#define BTUSB_DID_ISO_RESUME 4
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
struct btusb_data {
|
|
|
|
struct hci_dev *hdev;
|
|
|
|
struct usb_device *udev;
|
2008-09-23 06:16:36 +08:00
|
|
|
struct usb_interface *intf;
|
2008-08-18 19:23:52 +08:00
|
|
|
struct usb_interface *isoc;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
spinlock_t lock;
|
|
|
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
struct work_struct work;
|
2009-08-25 05:44:59 +08:00
|
|
|
struct work_struct waker;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
struct usb_anchor tx_anchor;
|
|
|
|
struct usb_anchor intr_anchor;
|
|
|
|
struct usb_anchor bulk_anchor;
|
2008-08-18 19:23:52 +08:00
|
|
|
struct usb_anchor isoc_anchor;
|
2009-08-25 05:44:59 +08:00
|
|
|
struct usb_anchor deferred;
|
|
|
|
int tx_in_flight;
|
|
|
|
spinlock_t txlock;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
struct usb_endpoint_descriptor *intr_ep;
|
|
|
|
struct usb_endpoint_descriptor *bulk_tx_ep;
|
|
|
|
struct usb_endpoint_descriptor *bulk_rx_ep;
|
2008-08-18 19:23:52 +08:00
|
|
|
struct usb_endpoint_descriptor *isoc_tx_ep;
|
|
|
|
struct usb_endpoint_descriptor *isoc_rx_ep;
|
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
__u8 cmdreq_type;
|
|
|
|
|
2009-02-05 00:41:38 +08:00
|
|
|
unsigned int sco_num;
|
2008-08-18 19:23:52 +08:00
|
|
|
int isoc_altsetting;
|
2008-11-30 19:17:14 +08:00
|
|
|
int suspend_count;
|
2007-10-20 20:12:34 +08:00
|
|
|
};
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
static int inc_tx(struct btusb_data *data)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&data->txlock, flags);
|
|
|
|
rv = test_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
|
if (!rv)
|
|
|
|
data->tx_in_flight++;
|
|
|
|
spin_unlock_irqrestore(&data->txlock, flags);
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static void btusb_intr_complete(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hci_dev *hdev = urb->context;
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
|
urb, urb->status, urb->actual_length);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (urb->status == 0) {
|
2008-08-18 19:23:52 +08:00
|
|
|
hdev->stat.byte_rx += urb->actual_length;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
|
|
|
|
urb->transfer_buffer,
|
|
|
|
urb->actual_length) < 0) {
|
|
|
|
BT_ERR("%s corrupted event packet", hdev->name);
|
|
|
|
hdev->stat.err_rx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
|
|
|
|
return;
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
usb_mark_last_busy(data->udev);
|
2007-10-20 20:12:34 +08:00
|
|
|
usb_anchor_urb(urb, &data->intr_anchor);
|
|
|
|
|
|
|
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (err < 0) {
|
2011-08-09 23:16:28 +08:00
|
|
|
/* -EPERM: urb is being killed;
|
|
|
|
* -ENODEV: device got disconnected */
|
|
|
|
if (err != -EPERM && err != -ENODEV)
|
2010-12-01 04:49:08 +08:00
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
2007-10-20 20:12:34 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
2007-10-20 20:12:34 +08:00
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
struct urb *urb;
|
|
|
|
unsigned char *buf;
|
|
|
|
unsigned int pipe;
|
|
|
|
int err, size;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
if (!data->intr_ep)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
urb = usb_alloc_urb(0, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
buf = kmalloc(size, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (!buf) {
|
|
|
|
usb_free_urb(urb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
|
|
|
|
|
|
|
|
usb_fill_int_urb(urb, data->udev, pipe, buf, size,
|
|
|
|
btusb_intr_complete, hdev,
|
|
|
|
data->intr_ep->bInterval);
|
|
|
|
|
|
|
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
|
|
|
|
|
|
|
usb_anchor_urb(urb, &data->intr_anchor);
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
err = usb_submit_urb(urb, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (err < 0) {
|
2011-10-09 18:12:22 +08:00
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
2007-10-20 20:12:34 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_free_urb(urb);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btusb_bulk_complete(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hci_dev *hdev = urb->context;
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
|
urb, urb->status, urb->actual_length);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (urb->status == 0) {
|
2008-08-18 19:23:52 +08:00
|
|
|
hdev->stat.byte_rx += urb->actual_length;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
|
|
|
|
urb->transfer_buffer,
|
|
|
|
urb->actual_length) < 0) {
|
|
|
|
BT_ERR("%s corrupted ACL packet", hdev->name);
|
|
|
|
hdev->stat.err_rx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
usb_anchor_urb(urb, &data->bulk_anchor);
|
2009-12-17 02:23:43 +08:00
|
|
|
usb_mark_last_busy(data->udev);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (err < 0) {
|
2011-08-09 23:16:28 +08:00
|
|
|
/* -EPERM: urb is being killed;
|
|
|
|
* -ENODEV: device got disconnected */
|
|
|
|
if (err != -EPERM && err != -ENODEV)
|
2010-12-01 04:49:08 +08:00
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
2007-10-20 20:12:34 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
2007-10-20 20:12:34 +08:00
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
struct urb *urb;
|
|
|
|
unsigned char *buf;
|
|
|
|
unsigned int pipe;
|
2009-07-02 17:01:59 +08:00
|
|
|
int err, size = HCI_MAX_FRAME_SIZE;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
if (!data->bulk_rx_ep)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
urb = usb_alloc_urb(0, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
buf = kmalloc(size, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (!buf) {
|
|
|
|
usb_free_urb(urb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
|
|
|
|
|
|
|
|
usb_fill_bulk_urb(urb, data->udev, pipe,
|
|
|
|
buf, size, btusb_bulk_complete, hdev);
|
|
|
|
|
|
|
|
urb->transfer_flags |= URB_FREE_BUFFER;
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
usb_mark_last_busy(data->udev);
|
2007-10-20 20:12:34 +08:00
|
|
|
usb_anchor_urb(urb, &data->bulk_anchor);
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
err = usb_submit_urb(urb, mem_flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (err < 0) {
|
2011-10-09 18:12:22 +08:00
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
2007-10-20 20:12:34 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_free_urb(urb);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
static void btusb_isoc_complete(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct hci_dev *hdev = urb->context;
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2008-08-18 19:23:52 +08:00
|
|
|
int i, err;
|
|
|
|
|
|
|
|
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
|
urb, urb->status, urb->actual_length);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (urb->status == 0) {
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
unsigned int offset = urb->iso_frame_desc[i].offset;
|
|
|
|
unsigned int length = urb->iso_frame_desc[i].actual_length;
|
|
|
|
|
|
|
|
if (urb->iso_frame_desc[i].status)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
hdev->stat.byte_rx += length;
|
|
|
|
|
|
|
|
if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
|
|
|
|
urb->transfer_buffer + offset,
|
|
|
|
length) < 0) {
|
|
|
|
BT_ERR("%s corrupted SCO packet", hdev->name);
|
|
|
|
hdev->stat.err_rx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
|
|
|
|
return;
|
|
|
|
|
|
|
|
usb_anchor_urb(urb, &data->isoc_anchor);
|
|
|
|
|
|
|
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (err < 0) {
|
2011-08-09 23:16:28 +08:00
|
|
|
/* -EPERM: urb is being killed;
|
|
|
|
* -ENODEV: device got disconnected */
|
|
|
|
if (err != -EPERM && err != -ENODEV)
|
2010-12-01 04:49:08 +08:00
|
|
|
BT_ERR("%s urb %p failed to resubmit (%d)",
|
2008-08-18 19:23:52 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-17 07:09:38 +08:00
|
|
|
static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
|
2008-08-18 19:23:52 +08:00
|
|
|
{
|
|
|
|
int i, offset = 0;
|
|
|
|
|
|
|
|
BT_DBG("len %d mtu %d", len, mtu);
|
|
|
|
|
|
|
|
for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
|
|
|
|
i++, offset += mtu, len -= mtu) {
|
|
|
|
urb->iso_frame_desc[i].offset = offset;
|
|
|
|
urb->iso_frame_desc[i].length = mtu;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len && i < BTUSB_MAX_ISOC_FRAMES) {
|
|
|
|
urb->iso_frame_desc[i].offset = offset;
|
|
|
|
urb->iso_frame_desc[i].length = len;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
urb->number_of_packets = i;
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
2008-08-18 19:23:52 +08:00
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2008-08-18 19:23:52 +08:00
|
|
|
struct urb *urb;
|
|
|
|
unsigned char *buf;
|
|
|
|
unsigned int pipe;
|
|
|
|
int err, size;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
if (!data->isoc_rx_ep)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
|
2008-08-18 19:23:52 +08:00
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
|
|
|
|
BTUSB_MAX_ISOC_FRAMES;
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
buf = kmalloc(size, mem_flags);
|
2008-08-18 19:23:52 +08:00
|
|
|
if (!buf) {
|
|
|
|
usb_free_urb(urb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
|
|
|
|
|
2011-12-21 10:19:00 +08:00
|
|
|
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
|
|
|
|
hdev, data->isoc_rx_ep->bInterval);
|
2008-08-18 19:23:52 +08:00
|
|
|
|
|
|
|
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
|
|
|
|
|
|
|
__fill_isoc_descriptor(urb, size,
|
|
|
|
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
|
|
|
|
|
|
|
usb_anchor_urb(urb, &data->isoc_anchor);
|
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
err = usb_submit_urb(urb, mem_flags);
|
2008-08-18 19:23:52 +08:00
|
|
|
if (err < 0) {
|
2011-10-09 18:12:22 +08:00
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
2008-08-18 19:23:52 +08:00
|
|
|
hdev->name, urb, -err);
|
|
|
|
usb_unanchor_urb(urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_free_urb(urb);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static void btusb_tx_complete(struct urb *urb)
|
2009-08-25 05:44:59 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb = urb->context;
|
|
|
|
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2009-08-25 05:44:59 +08:00
|
|
|
|
|
|
|
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
|
urb, urb->status, urb->actual_length);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (!urb->status)
|
|
|
|
hdev->stat.byte_tx += urb->transfer_buffer_length;
|
|
|
|
else
|
|
|
|
hdev->stat.err_tx++;
|
|
|
|
|
|
|
|
done:
|
|
|
|
spin_lock(&data->txlock);
|
|
|
|
data->tx_in_flight--;
|
|
|
|
spin_unlock(&data->txlock);
|
|
|
|
|
|
|
|
kfree(urb->setup_packet);
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btusb_isoc_tx_complete(struct urb *urb)
|
2007-10-20 20:12:34 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb = urb->context;
|
|
|
|
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
|
|
|
|
|
|
|
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
|
|
|
urb, urb->status, urb->actual_length);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (!urb->status)
|
|
|
|
hdev->stat.byte_tx += urb->transfer_buffer_length;
|
|
|
|
else
|
|
|
|
hdev->stat.err_tx++;
|
|
|
|
|
|
|
|
done:
|
|
|
|
kfree(urb->setup_packet);
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btusb_open(struct hci_dev *hdev)
|
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
err = usb_autopm_get_interface(data->intf);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
data->intf->needs_remote_wakeup = 1;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
|
2009-08-25 05:44:59 +08:00
|
|
|
goto done;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
|
2009-08-25 05:44:59 +08:00
|
|
|
goto done;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
2008-11-30 19:17:10 +08:00
|
|
|
err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
|
2009-02-05 00:41:38 +08:00
|
|
|
if (err < 0)
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (err < 0) {
|
2009-02-05 00:41:38 +08:00
|
|
|
usb_kill_anchored_urbs(&data->intr_anchor);
|
|
|
|
goto failed;
|
2007-10-20 20:12:34 +08:00
|
|
|
}
|
|
|
|
|
2009-02-05 00:41:38 +08:00
|
|
|
set_bit(BTUSB_BULK_RUNNING, &data->flags);
|
|
|
|
btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
done:
|
|
|
|
usb_autopm_put_interface(data->intf);
|
2009-02-05 00:41:38 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
failed:
|
|
|
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
|
|
|
clear_bit(HCI_RUNNING, &hdev->flags);
|
2009-08-25 05:44:59 +08:00
|
|
|
usb_autopm_put_interface(data->intf);
|
2007-10-20 20:12:34 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
static void btusb_stop_traffic(struct btusb_data *data)
|
|
|
|
{
|
|
|
|
usb_kill_anchored_urbs(&data->intr_anchor);
|
|
|
|
usb_kill_anchored_urbs(&data->bulk_anchor);
|
|
|
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static int btusb_close(struct hci_dev *hdev)
|
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2009-08-25 05:44:59 +08:00
|
|
|
int err;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
return 0;
|
|
|
|
|
2008-09-23 06:16:36 +08:00
|
|
|
cancel_work_sync(&data->work);
|
2009-11-12 05:32:29 +08:00
|
|
|
cancel_work_sync(&data->waker);
|
2008-09-23 06:16:36 +08:00
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
2007-10-20 20:12:34 +08:00
|
|
|
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
|
|
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
2009-08-25 05:44:59 +08:00
|
|
|
|
|
|
|
btusb_stop_traffic(data);
|
|
|
|
err = usb_autopm_get_interface(data->intf);
|
|
|
|
if (err < 0)
|
2009-11-13 21:26:23 +08:00
|
|
|
goto failed;
|
2009-08-25 05:44:59 +08:00
|
|
|
|
|
|
|
data->intf->needs_remote_wakeup = 0;
|
|
|
|
usb_autopm_put_interface(data->intf);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
2009-11-13 21:26:23 +08:00
|
|
|
failed:
|
|
|
|
usb_scuttle_anchored_urbs(&data->deferred);
|
2007-10-20 20:12:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btusb_flush(struct hci_dev *hdev)
|
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
usb_kill_anchored_urbs(&data->tx_anchor);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:19:18 +08:00
|
|
|
static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
2007-10-20 20:12:34 +08:00
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
struct usb_ctrlrequest *dr;
|
|
|
|
struct urb *urb;
|
|
|
|
unsigned int pipe;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2013-10-11 21:19:18 +08:00
|
|
|
skb->dev = (void *) hdev;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
switch (bt_cb(skb)->pkt_type) {
|
|
|
|
case HCI_COMMAND_PKT:
|
|
|
|
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
|
|
|
|
if (!dr) {
|
|
|
|
usb_free_urb(urb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
dr->bRequestType = data->cmdreq_type;
|
2007-10-20 20:12:34 +08:00
|
|
|
dr->bRequest = 0;
|
|
|
|
dr->wIndex = 0;
|
|
|
|
dr->wValue = 0;
|
|
|
|
dr->wLength = __cpu_to_le16(skb->len);
|
|
|
|
|
|
|
|
pipe = usb_sndctrlpipe(data->udev, 0x00);
|
|
|
|
|
|
|
|
usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
|
|
|
|
skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
|
|
|
|
|
hdev->stat.cmd_tx++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HCI_ACLDATA_PKT:
|
2011-07-14 20:48:32 +08:00
|
|
|
if (!data->bulk_tx_ep)
|
2008-08-18 19:23:52 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
pipe = usb_sndbulkpipe(data->udev,
|
|
|
|
data->bulk_tx_ep->bEndpointAddress);
|
|
|
|
|
|
|
|
usb_fill_bulk_urb(urb, data->udev, pipe,
|
|
|
|
skb->data, skb->len, btusb_tx_complete, skb);
|
|
|
|
|
|
|
|
hdev->stat.acl_tx++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HCI_SCODATA_PKT:
|
2013-10-11 00:47:55 +08:00
|
|
|
if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1)
|
2008-08-18 19:23:52 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
|
|
|
|
if (!urb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
pipe = usb_sndisocpipe(data->udev,
|
|
|
|
data->isoc_tx_ep->bEndpointAddress);
|
|
|
|
|
2011-02-15 05:53:43 +08:00
|
|
|
usb_fill_int_urb(urb, data->udev, pipe,
|
|
|
|
skb->data, skb->len, btusb_isoc_tx_complete,
|
|
|
|
skb, data->isoc_tx_ep->bInterval);
|
2008-08-18 19:23:52 +08:00
|
|
|
|
|
|
|
urb->transfer_flags = URB_ISO_ASAP;
|
|
|
|
|
|
|
|
__fill_isoc_descriptor(urb, skb->len,
|
|
|
|
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
hdev->stat.sco_tx++;
|
2009-08-25 05:44:59 +08:00
|
|
|
goto skip_waking;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
return -EILSEQ;
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
err = inc_tx(data);
|
|
|
|
if (err) {
|
|
|
|
usb_anchor_urb(urb, &data->deferred);
|
|
|
|
schedule_work(&data->waker);
|
|
|
|
err = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_waking:
|
2007-10-20 20:12:34 +08:00
|
|
|
usb_anchor_urb(urb, &data->tx_anchor);
|
|
|
|
|
|
|
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (err < 0) {
|
2011-10-09 18:12:16 +08:00
|
|
|
if (err != -EPERM && err != -ENODEV)
|
|
|
|
BT_ERR("%s urb %p submission failed (%d)",
|
|
|
|
hdev->name, urb, -err);
|
2007-10-20 20:12:34 +08:00
|
|
|
kfree(urb->setup_packet);
|
|
|
|
usb_unanchor_urb(urb);
|
2009-08-25 05:44:59 +08:00
|
|
|
} else {
|
|
|
|
usb_mark_last_busy(data->udev);
|
2007-10-20 20:12:34 +08:00
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
done:
|
2011-11-22 09:32:57 +08:00
|
|
|
usb_free_urb(urb);
|
2007-10-20 20:12:34 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
|
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
BT_DBG("%s evt %d", hdev->name, evt);
|
|
|
|
|
2013-10-11 00:47:55 +08:00
|
|
|
if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
|
|
|
|
data->sco_num = hci_conn_num(hdev, SCO_LINK);
|
2009-02-05 00:41:38 +08:00
|
|
|
schedule_work(&data->work);
|
2008-11-30 19:17:12 +08:00
|
|
|
}
|
2007-10-20 20:12:34 +08:00
|
|
|
}
|
|
|
|
|
2011-01-17 07:09:38 +08:00
|
|
|
static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
|
2008-08-18 19:23:52 +08:00
|
|
|
{
|
2012-02-10 04:58:32 +08:00
|
|
|
struct btusb_data *data = hci_get_drvdata(hdev);
|
2008-08-18 19:23:52 +08:00
|
|
|
struct usb_interface *intf = data->isoc;
|
|
|
|
struct usb_endpoint_descriptor *ep_desc;
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
if (!data->isoc)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
err = usb_set_interface(data->udev, 1, altsetting);
|
|
|
|
if (err < 0) {
|
|
|
|
BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->isoc_altsetting = altsetting;
|
|
|
|
|
|
|
|
data->isoc_tx_ep = NULL;
|
|
|
|
data->isoc_rx_ep = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
|
|
|
|
ep_desc = &intf->cur_altsetting->endpoint[i].desc;
|
|
|
|
|
|
|
|
if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
|
|
|
|
data->isoc_tx_ep = ep_desc;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
|
|
|
|
data->isoc_rx_ep = ep_desc;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
|
|
|
|
BT_ERR("%s invalid SCO descriptors", hdev->name);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static void btusb_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct btusb_data *data = container_of(work, struct btusb_data, work);
|
|
|
|
struct hci_dev *hdev = data->hdev;
|
2012-04-11 14:48:51 +08:00
|
|
|
int new_alts;
|
2009-08-25 05:44:59 +08:00
|
|
|
int err;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
2013-10-11 00:47:55 +08:00
|
|
|
if (data->sco_num > 0) {
|
2010-07-17 04:20:33 +08:00
|
|
|
if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
|
2011-02-11 20:00:06 +08:00
|
|
|
err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
|
2009-08-25 05:44:59 +08:00
|
|
|
if (err < 0) {
|
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
|
|
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-07-17 04:20:33 +08:00
|
|
|
set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
|
2009-08-25 05:44:59 +08:00
|
|
|
}
|
2012-04-11 14:48:51 +08:00
|
|
|
|
|
|
|
if (hdev->voice_setting & 0x0020) {
|
|
|
|
static const int alts[3] = { 2, 4, 5 };
|
2013-10-11 00:47:55 +08:00
|
|
|
new_alts = alts[data->sco_num - 1];
|
2012-04-11 14:48:51 +08:00
|
|
|
} else {
|
2013-10-11 00:47:55 +08:00
|
|
|
new_alts = data->sco_num;
|
2012-04-11 14:48:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data->isoc_altsetting != new_alts) {
|
2008-08-18 19:23:52 +08:00
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
|
|
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
|
|
|
|
2012-04-11 14:48:51 +08:00
|
|
|
if (__set_isoc_interface(hdev, new_alts) < 0)
|
2008-08-18 19:23:52 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
|
2008-11-30 19:17:10 +08:00
|
|
|
if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
|
2008-08-18 19:23:52 +08:00
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
|
|
|
else
|
2008-11-30 19:17:10 +08:00
|
|
|
btusb_submit_isoc_urb(hdev, GFP_KERNEL);
|
2008-08-18 19:23:52 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
|
|
|
usb_kill_anchored_urbs(&data->isoc_anchor);
|
|
|
|
|
|
|
|
__set_isoc_interface(hdev, 0);
|
2010-07-17 04:20:33 +08:00
|
|
|
if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
|
2011-02-11 20:00:06 +08:00
|
|
|
usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
|
2007-10-20 20:12:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
static void btusb_waker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct btusb_data *data = container_of(work, struct btusb_data, waker);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = usb_autopm_get_interface(data->intf);
|
|
|
|
if (err < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
usb_autopm_put_interface(data->intf);
|
|
|
|
}
|
|
|
|
|
2013-04-10 23:11:35 +08:00
|
|
|
static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u8 val = 0x00;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc3b, 1, &val, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb))
|
|
|
|
BT_ERR("BCM92035 command failed (%ld)", -PTR_ERR(skb));
|
|
|
|
else
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-03 19:02:36 +08:00
|
|
|
static int btusb_setup_csr(struct hci_dev *hdev)
|
|
|
|
{
|
|
|
|
struct hci_rp_read_local_version *rp;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
|
|
|
|
HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("Reading local version failed (%ld)", -PTR_ERR(skb));
|
|
|
|
return -PTR_ERR(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
rp = (struct hci_rp_read_local_version *) skb->data;
|
|
|
|
|
|
|
|
if (!rp->status) {
|
|
|
|
if (le16_to_cpu(rp->manufacturer) != 10) {
|
|
|
|
/* Clear the reset quirk since this is not an actual
|
|
|
|
* early Bluetooth 1.1 device from CSR.
|
|
|
|
*/
|
|
|
|
clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
|
|
|
|
|
|
|
/* These fake CSR controllers have all a broken
|
|
|
|
* stored link key handling and so just disable it.
|
|
|
|
*/
|
|
|
|
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY,
|
|
|
|
&hdev->quirks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -bt_to_errno(rp->status);
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-20 00:57:43 +08:00
|
|
|
struct intel_version {
|
|
|
|
u8 status;
|
|
|
|
u8 hw_platform;
|
|
|
|
u8 hw_variant;
|
|
|
|
u8 hw_revision;
|
|
|
|
u8 fw_variant;
|
|
|
|
u8 fw_revision;
|
|
|
|
u8 fw_build_num;
|
|
|
|
u8 fw_build_ww;
|
|
|
|
u8 fw_build_yy;
|
|
|
|
u8 fw_patch_num;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
|
|
|
struct intel_version *ver)
|
|
|
|
{
|
|
|
|
const struct firmware *fw;
|
|
|
|
char fwname[64];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
snprintf(fwname, sizeof(fwname),
|
|
|
|
"intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
|
|
|
|
ver->hw_platform, ver->hw_variant, ver->hw_revision,
|
|
|
|
ver->fw_variant, ver->fw_revision, ver->fw_build_num,
|
|
|
|
ver->fw_build_ww, ver->fw_build_yy);
|
|
|
|
|
|
|
|
ret = request_firmware(&fw, fwname, &hdev->dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (ret == -EINVAL) {
|
|
|
|
BT_ERR("%s Intel firmware file request failed (%d)",
|
|
|
|
hdev->name, ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_ERR("%s failed to open Intel firmware file: %s(%d)",
|
|
|
|
hdev->name, fwname, ret);
|
|
|
|
|
|
|
|
/* If the correct firmware patch file is not found, use the
|
|
|
|
* default firmware patch file instead
|
|
|
|
*/
|
|
|
|
snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bseq",
|
|
|
|
ver->hw_platform, ver->hw_variant);
|
|
|
|
if (request_firmware(&fw, fwname, &hdev->dev) < 0) {
|
|
|
|
BT_ERR("%s failed to open default Intel fw file: %s",
|
|
|
|
hdev->name, fwname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_INFO("%s: Intel Bluetooth firmware file: %s", hdev->name, fwname);
|
|
|
|
|
|
|
|
return fw;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
|
|
|
const struct firmware *fw,
|
|
|
|
const u8 **fw_ptr, int *disable_patch)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct hci_command_hdr *cmd;
|
|
|
|
const u8 *cmd_param;
|
|
|
|
struct hci_event_hdr *evt = NULL;
|
|
|
|
const u8 *evt_param = NULL;
|
|
|
|
int remain = fw->size - (*fw_ptr - fw->data);
|
|
|
|
|
|
|
|
/* The first byte indicates the types of the patch command or event.
|
|
|
|
* 0x01 means HCI command and 0x02 is HCI event. If the first bytes
|
|
|
|
* in the current firmware buffer doesn't start with 0x01 or
|
|
|
|
* the size of remain buffer is smaller than HCI command header,
|
|
|
|
* the firmware file is corrupted and it should stop the patching
|
|
|
|
* process.
|
|
|
|
*/
|
|
|
|
if (remain > HCI_COMMAND_HDR_SIZE && *fw_ptr[0] != 0x01) {
|
|
|
|
BT_ERR("%s Intel fw corrupted: invalid cmd read", hdev->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
(*fw_ptr)++;
|
|
|
|
remain--;
|
|
|
|
|
|
|
|
cmd = (struct hci_command_hdr *)(*fw_ptr);
|
|
|
|
*fw_ptr += sizeof(*cmd);
|
|
|
|
remain -= sizeof(*cmd);
|
|
|
|
|
|
|
|
/* Ensure that the remain firmware data is long enough than the length
|
|
|
|
* of command parameter. If not, the firmware file is corrupted.
|
|
|
|
*/
|
|
|
|
if (remain < cmd->plen) {
|
|
|
|
BT_ERR("%s Intel fw corrupted: invalid cmd len", hdev->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is a command that loads a patch in the firmware
|
|
|
|
* file, then enable the patch upon success, otherwise just
|
|
|
|
* disable the manufacturer mode, for example patch activation
|
|
|
|
* is not required when the default firmware patch file is used
|
|
|
|
* because there are no patch data to load.
|
|
|
|
*/
|
|
|
|
if (*disable_patch && le16_to_cpu(cmd->opcode) == 0xfc8e)
|
|
|
|
*disable_patch = 0;
|
|
|
|
|
|
|
|
cmd_param = *fw_ptr;
|
|
|
|
*fw_ptr += cmd->plen;
|
|
|
|
remain -= cmd->plen;
|
|
|
|
|
|
|
|
/* This reads the expected events when the above command is sent to the
|
|
|
|
* device. Some vendor commands expects more than one events, for
|
|
|
|
* example command status event followed by vendor specific event.
|
|
|
|
* For this case, it only keeps the last expected event. so the command
|
|
|
|
* can be sent with __hci_cmd_sync_ev() which returns the sk_buff of
|
|
|
|
* last expected event.
|
|
|
|
*/
|
|
|
|
while (remain > HCI_EVENT_HDR_SIZE && *fw_ptr[0] == 0x02) {
|
|
|
|
(*fw_ptr)++;
|
|
|
|
remain--;
|
|
|
|
|
|
|
|
evt = (struct hci_event_hdr *)(*fw_ptr);
|
|
|
|
*fw_ptr += sizeof(*evt);
|
|
|
|
remain -= sizeof(*evt);
|
|
|
|
|
|
|
|
if (remain < evt->plen) {
|
|
|
|
BT_ERR("%s Intel fw corrupted: invalid evt len",
|
|
|
|
hdev->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
evt_param = *fw_ptr;
|
|
|
|
*fw_ptr += evt->plen;
|
|
|
|
remain -= evt->plen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Every HCI commands in the firmware file has its correspond event.
|
|
|
|
* If event is not found or remain is smaller than zero, the firmware
|
|
|
|
* file is corrupted.
|
|
|
|
*/
|
|
|
|
if (!evt || !evt_param || remain < 0) {
|
|
|
|
BT_ERR("%s Intel fw corrupted: invalid evt read", hdev->name);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cmd->opcode), cmd->plen,
|
|
|
|
cmd_param, evt->evt, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s sending Intel patch command (0x%4.4x) failed (%ld)",
|
|
|
|
hdev->name, cmd->opcode, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* It ensures that the returned event matches the event data read from
|
|
|
|
* the firmware file. At fist, it checks the length and then
|
|
|
|
* the contents of the event.
|
|
|
|
*/
|
|
|
|
if (skb->len != evt->plen) {
|
|
|
|
BT_ERR("%s mismatch event length (opcode 0x%4.4x)", hdev->name,
|
|
|
|
le16_to_cpu(cmd->opcode));
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(skb->data, evt_param, evt->plen)) {
|
|
|
|
BT_ERR("%s mismatch event parameter (opcode 0x%4.4x)",
|
|
|
|
hdev->name, le16_to_cpu(cmd->opcode));
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int btusb_setup_intel(struct hci_dev *hdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
const struct firmware *fw;
|
|
|
|
const u8 *fw_ptr;
|
|
|
|
int disable_patch;
|
|
|
|
struct intel_version *ver;
|
|
|
|
|
|
|
|
const u8 mfg_enable[] = { 0x01, 0x00 };
|
|
|
|
const u8 mfg_disable[] = { 0x00, 0x00 };
|
|
|
|
const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
|
|
|
|
const u8 mfg_reset_activate[] = { 0x00, 0x02 };
|
|
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
|
|
|
|
/* The controller has a bug with the first HCI command sent to it
|
|
|
|
* returning number of completed commands as zero. This would stall the
|
|
|
|
* command processing in the Bluetooth core.
|
|
|
|
*
|
|
|
|
* As a workaround, send HCI Reset command first which will reset the
|
|
|
|
* number of completed commands and allow normal command processing
|
|
|
|
* from now on.
|
|
|
|
*/
|
|
|
|
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s sending initial HCI reset command failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
/* Read Intel specific controller version first to allow selection of
|
|
|
|
* which firmware file to load.
|
|
|
|
*
|
|
|
|
* The returned information are hardware variant and revision plus
|
|
|
|
* firmware variant, revision and build number.
|
|
|
|
*/
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s reading Intel fw version command failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (skb->len != sizeof(*ver)) {
|
|
|
|
BT_ERR("%s Intel version event length mismatch", hdev->name);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ver = (struct intel_version *)skb->data;
|
|
|
|
if (ver->status) {
|
|
|
|
BT_ERR("%s Intel fw version event failed (%02x)", hdev->name,
|
|
|
|
ver->status);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -bt_to_errno(ver->status);
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
|
|
hdev->name, ver->hw_platform, ver->hw_variant,
|
|
|
|
ver->hw_revision, ver->fw_variant, ver->fw_revision,
|
|
|
|
ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
|
|
|
|
ver->fw_patch_num);
|
|
|
|
|
|
|
|
/* fw_patch_num indicates the version of patch the device currently
|
|
|
|
* have. If there is no patch data in the device, it is always 0x00.
|
|
|
|
* So, if it is other than 0x00, no need to patch the deivce again.
|
|
|
|
*/
|
|
|
|
if (ver->fw_patch_num) {
|
|
|
|
BT_INFO("%s: Intel device is already patched. patch num: %02x",
|
|
|
|
hdev->name, ver->fw_patch_num);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Opens the firmware patch file based on the firmware version read
|
|
|
|
* from the controller. If it fails to open the matching firmware
|
|
|
|
* patch file, it tries to open the default firmware patch file.
|
|
|
|
* If no patch file is found, allow the device to operate without
|
|
|
|
* a patch.
|
|
|
|
*/
|
|
|
|
fw = btusb_setup_intel_get_fw(hdev, ver);
|
|
|
|
if (!fw) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
fw_ptr = fw->data;
|
|
|
|
|
|
|
|
/* This Intel specific command enables the manufacturer mode of the
|
|
|
|
* controller.
|
|
|
|
*
|
|
|
|
* Only while this mode is enabled, the driver can download the
|
|
|
|
* firmware patch data and configuration parameters.
|
|
|
|
*/
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
|
|
|
release_firmware(fw);
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (skb->data[0]) {
|
|
|
|
u8 evt_status = skb->data[0];
|
|
|
|
BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
|
|
|
|
hdev->name, evt_status);
|
|
|
|
kfree_skb(skb);
|
|
|
|
release_firmware(fw);
|
|
|
|
return -bt_to_errno(evt_status);
|
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
disable_patch = 1;
|
|
|
|
|
|
|
|
/* The firmware data file consists of list of Intel specific HCI
|
|
|
|
* commands and its expected events. The first byte indicates the
|
|
|
|
* type of the message, either HCI command or HCI event.
|
|
|
|
*
|
|
|
|
* It reads the command and its expected event from the firmware file,
|
|
|
|
* and send to the controller. Once __hci_cmd_sync_ev() returns,
|
|
|
|
* the returned event is compared with the event read from the firmware
|
|
|
|
* file and it will continue until all the messages are downloaded to
|
|
|
|
* the controller.
|
|
|
|
*
|
|
|
|
* Once the firmware patching is completed successfully,
|
|
|
|
* the manufacturer mode is disabled with reset and activating the
|
|
|
|
* downloaded patch.
|
|
|
|
*
|
|
|
|
* If the firmware patching fails, the manufacturer mode is
|
|
|
|
* disabled with reset and deactivating the patch.
|
|
|
|
*
|
|
|
|
* If the default patch file is used, no reset is done when disabling
|
|
|
|
* the manufacturer.
|
|
|
|
*/
|
|
|
|
while (fw->size > fw_ptr - fw->data) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = btusb_setup_intel_patching(hdev, fw, &fw_ptr,
|
|
|
|
&disable_patch);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_mfg_deactivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
release_firmware(fw);
|
|
|
|
|
|
|
|
if (disable_patch)
|
|
|
|
goto exit_mfg_disable;
|
|
|
|
|
|
|
|
/* Patching completed successfully and disable the manufacturer mode
|
|
|
|
* with reset and activate the downloaded firmware patches.
|
|
|
|
*/
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
|
|
|
|
mfg_reset_activate, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
|
|
|
|
hdev->name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
exit_mfg_disable:
|
|
|
|
/* Disable the manufacturer mode without reset */
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
|
|
|
|
HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
exit_mfg_deactivate:
|
|
|
|
release_firmware(fw);
|
|
|
|
|
|
|
|
/* Patching failed. Disable the manufacturer mode with reset and
|
|
|
|
* deactivate the downloaded firmware patches.
|
|
|
|
*/
|
|
|
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
|
|
|
|
mfg_reset_deactivate, HCI_INIT_TIMEOUT);
|
|
|
|
if (IS_ERR(skb)) {
|
|
|
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
|
|
|
hdev->name, PTR_ERR(skb));
|
2013-07-10 10:02:12 +08:00
|
|
|
return PTR_ERR(skb);
|
2013-04-20 00:57:43 +08:00
|
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
|
|
|
|
hdev->name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static int btusb_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *id)
|
|
|
|
{
|
|
|
|
struct usb_endpoint_descriptor *ep_desc;
|
|
|
|
struct btusb_data *data;
|
|
|
|
struct hci_dev *hdev;
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
BT_DBG("intf %p id %p", intf, id);
|
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
/* interface numbers are hardcoded in the spec */
|
2007-10-20 20:12:34 +08:00
|
|
|
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (!id->driver_info) {
|
|
|
|
const struct usb_device_id *match;
|
|
|
|
match = usb_match_id(intf, blacklist_table);
|
|
|
|
if (match)
|
|
|
|
id = match;
|
|
|
|
}
|
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
if (id->driver_info == BTUSB_IGNORE)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (ignore_csr && id->driver_info & BTUSB_CSR)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2011-07-01 14:02:36 +08:00
|
|
|
if (id->driver_info & BTUSB_ATH3012) {
|
|
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
|
|
|
|
|
|
/* Old firmware would otherwise let ath3k driver load
|
|
|
|
* patch and sysconfig files */
|
|
|
|
if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2012-07-27 15:08:39 +08:00
|
|
|
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
|
2007-10-20 20:12:34 +08:00
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
|
|
|
|
ep_desc = &intf->cur_altsetting->endpoint[i].desc;
|
|
|
|
|
|
|
|
if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
|
|
|
|
data->intr_ep = ep_desc;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
|
|
|
|
data->bulk_tx_ep = ep_desc;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
|
|
|
|
data->bulk_rx_ep = ep_desc;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-27 15:08:39 +08:00
|
|
|
if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep)
|
2007-10-20 20:12:34 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
data->cmdreq_type = USB_TYPE_CLASS;
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
data->udev = interface_to_usbdev(intf);
|
2008-09-23 06:16:36 +08:00
|
|
|
data->intf = intf;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
spin_lock_init(&data->lock);
|
|
|
|
|
|
|
|
INIT_WORK(&data->work, btusb_work);
|
2009-08-25 05:44:59 +08:00
|
|
|
INIT_WORK(&data->waker, btusb_waker);
|
|
|
|
spin_lock_init(&data->txlock);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
init_usb_anchor(&data->tx_anchor);
|
|
|
|
init_usb_anchor(&data->intr_anchor);
|
|
|
|
init_usb_anchor(&data->bulk_anchor);
|
2008-08-18 19:23:52 +08:00
|
|
|
init_usb_anchor(&data->isoc_anchor);
|
2009-08-25 05:44:59 +08:00
|
|
|
init_usb_anchor(&data->deferred);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
hdev = hci_alloc_dev();
|
2012-07-27 15:08:39 +08:00
|
|
|
if (!hdev)
|
2007-10-20 20:12:34 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-02-08 22:27:07 +08:00
|
|
|
hdev->bus = HCI_USB;
|
2012-02-10 04:58:32 +08:00
|
|
|
hci_set_drvdata(hdev, data);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
data->hdev = hdev;
|
|
|
|
|
|
|
|
SET_HCIDEV_DEV(hdev, &intf->dev);
|
|
|
|
|
2013-04-10 23:11:35 +08:00
|
|
|
hdev->open = btusb_open;
|
|
|
|
hdev->close = btusb_close;
|
|
|
|
hdev->flush = btusb_flush;
|
|
|
|
hdev->send = btusb_send_frame;
|
|
|
|
hdev->notify = btusb_notify;
|
|
|
|
|
|
|
|
if (id->driver_info & BTUSB_BCM92035)
|
|
|
|
hdev->setup = btusb_setup_bcm92035;
|
2007-10-20 20:12:34 +08:00
|
|
|
|
Bluetooth: Enable autosuspend for Intel Bluetooth device
This patch enables autosuspend for Intel Bluetooth device.
After btusb is loaded for Intel Bluetooth device, the power/control
attribute contains "on" value by default which disables the autosuspend.
Based on the USB PM document(Documentation/usb/power-management.txt),
kernel disabled the autosuspend for all devices other than hub by default.
"The USB specification states that all USB devices must support power
management. Nevertheless, the sad fact is that many devices do not
support it very well. You can suspend them all right, but when you
try to resume them they disconnect themselves from the USB bus or
they stop working entirely. This seems to be especially prevalent
among printers and scanners, but plenty of other types of device have
the same deficiency.
For this reason, by default the kernel disables autosuspend (the
power/control attribute is initialized to "on") for all devices other
than hubs. Hubs, at least, appear to be reasonably well-behaved in
this regard."
This document also described how the driver can enables the autosuspend
by using an USB api.
"Drivers can enable autosuspend for their devices by calling
usb_enable_autosuspend(struct usb_device *udev);
in their probe() routine, if they know that the device is capable of
suspending and resuming correctly. This is exactly equivalent to
writing "auto" to the device's power/control attribute."
For Intel Bluetooth device, the autosuspend needs to be enabled so the
device can transit to LPM(Low Power Mode) and ULPM(Ultra LPM) states after
receiving suspend message from the host.
Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2013-11-13 05:16:41 +08:00
|
|
|
if (id->driver_info & BTUSB_INTEL) {
|
|
|
|
usb_enable_autosuspend(data->udev);
|
2013-04-20 00:57:43 +08:00
|
|
|
hdev->setup = btusb_setup_intel;
|
Bluetooth: Enable autosuspend for Intel Bluetooth device
This patch enables autosuspend for Intel Bluetooth device.
After btusb is loaded for Intel Bluetooth device, the power/control
attribute contains "on" value by default which disables the autosuspend.
Based on the USB PM document(Documentation/usb/power-management.txt),
kernel disabled the autosuspend for all devices other than hub by default.
"The USB specification states that all USB devices must support power
management. Nevertheless, the sad fact is that many devices do not
support it very well. You can suspend them all right, but when you
try to resume them they disconnect themselves from the USB bus or
they stop working entirely. This seems to be especially prevalent
among printers and scanners, but plenty of other types of device have
the same deficiency.
For this reason, by default the kernel disables autosuspend (the
power/control attribute is initialized to "on") for all devices other
than hubs. Hubs, at least, appear to be reasonably well-behaved in
this regard."
This document also described how the driver can enables the autosuspend
by using an USB api.
"Drivers can enable autosuspend for their devices by calling
usb_enable_autosuspend(struct usb_device *udev);
in their probe() routine, if they know that the device is capable of
suspending and resuming correctly. This is exactly equivalent to
writing "auto" to the device's power/control attribute."
For Intel Bluetooth device, the autosuspend needs to be enabled so the
device can transit to LPM(Low Power Mode) and ULPM(Ultra LPM) states after
receiving suspend message from the host.
Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2013-11-13 05:16:41 +08:00
|
|
|
}
|
2013-04-20 00:57:43 +08:00
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
/* Interface numbers are hardcoded in the specification */
|
2008-08-18 19:23:52 +08:00
|
|
|
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
if (!reset)
|
2012-05-23 18:35:46 +08:00
|
|
|
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
2008-08-08 04:26:56 +08:00
|
|
|
|
|
|
|
if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
|
|
|
|
if (!disable_scofix)
|
|
|
|
set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
|
|
|
|
}
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
if (id->driver_info & BTUSB_BROKEN_ISOC)
|
|
|
|
data->isoc = NULL;
|
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
if (id->driver_info & BTUSB_DIGIANSWER) {
|
|
|
|
data->cmdreq_type = USB_TYPE_VENDOR;
|
2012-05-23 18:35:46 +08:00
|
|
|
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
2008-11-30 19:17:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (id->driver_info & BTUSB_CSR) {
|
|
|
|
struct usb_device *udev = data->udev;
|
2014-01-03 19:02:36 +08:00
|
|
|
u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);
|
2008-11-30 19:17:26 +08:00
|
|
|
|
|
|
|
/* Old firmware would otherwise execute USB reset */
|
2014-01-03 19:02:36 +08:00
|
|
|
if (bcdDevice < 0x117)
|
2012-05-23 18:35:46 +08:00
|
|
|
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
2014-01-03 19:02:36 +08:00
|
|
|
|
|
|
|
/* Fake CSR devices with broken commands */
|
|
|
|
if (bcdDevice <= 0x100)
|
|
|
|
hdev->setup = btusb_setup_csr;
|
2008-11-30 19:17:26 +08:00
|
|
|
}
|
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
if (id->driver_info & BTUSB_SNIFFER) {
|
2008-08-18 19:23:52 +08:00
|
|
|
struct usb_device *udev = data->udev;
|
2008-08-08 04:26:56 +08:00
|
|
|
|
2008-11-30 19:17:26 +08:00
|
|
|
/* New sniffer firmware has crippled HCI interface */
|
2008-08-08 04:26:56 +08:00
|
|
|
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
|
|
|
|
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
2008-08-18 19:23:52 +08:00
|
|
|
|
|
|
|
data->isoc = NULL;
|
2008-08-08 04:26:56 +08:00
|
|
|
}
|
|
|
|
|
2008-08-18 19:23:52 +08:00
|
|
|
if (data->isoc) {
|
|
|
|
err = usb_driver_claim_interface(&btusb_driver,
|
2008-09-23 06:16:36 +08:00
|
|
|
data->isoc, data);
|
2008-08-18 19:23:52 +08:00
|
|
|
if (err < 0) {
|
|
|
|
hci_free_dev(hdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
err = hci_register_dev(hdev);
|
|
|
|
if (err < 0) {
|
|
|
|
hci_free_dev(hdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_set_intfdata(intf, data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btusb_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct btusb_data *data = usb_get_intfdata(intf);
|
|
|
|
struct hci_dev *hdev;
|
|
|
|
|
|
|
|
BT_DBG("intf %p", intf);
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdev = data->hdev;
|
2008-09-23 06:16:36 +08:00
|
|
|
usb_set_intfdata(data->intf, NULL);
|
|
|
|
|
|
|
|
if (data->isoc)
|
|
|
|
usb_set_intfdata(data->isoc, NULL);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
|
|
|
hci_unregister_dev(hdev);
|
|
|
|
|
2008-09-23 06:16:36 +08:00
|
|
|
if (intf == data->isoc)
|
|
|
|
usb_driver_release_interface(&btusb_driver, data->intf);
|
|
|
|
else if (data->isoc)
|
|
|
|
usb_driver_release_interface(&btusb_driver, data->isoc);
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
hci_free_dev(hdev);
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
#ifdef CONFIG_PM
|
2008-11-30 19:17:14 +08:00
|
|
|
static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
|
|
|
|
{
|
|
|
|
struct btusb_data *data = usb_get_intfdata(intf);
|
|
|
|
|
|
|
|
BT_DBG("intf %p", intf);
|
|
|
|
|
|
|
|
if (data->suspend_count++)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
spin_lock_irq(&data->txlock);
|
2011-08-20 05:49:48 +08:00
|
|
|
if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
|
2009-08-25 05:44:59 +08:00
|
|
|
set_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
|
spin_unlock_irq(&data->txlock);
|
|
|
|
} else {
|
|
|
|
spin_unlock_irq(&data->txlock);
|
|
|
|
data->suspend_count--;
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:14 +08:00
|
|
|
cancel_work_sync(&data->work);
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
btusb_stop_traffic(data);
|
2008-11-30 19:17:14 +08:00
|
|
|
usb_kill_anchored_urbs(&data->tx_anchor);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
static void play_deferred(struct btusb_data *data)
|
|
|
|
{
|
|
|
|
struct urb *urb;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
while ((urb = usb_get_from_anchor(&data->deferred))) {
|
|
|
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (err < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
data->tx_in_flight++;
|
|
|
|
}
|
|
|
|
usb_scuttle_anchored_urbs(&data->deferred);
|
|
|
|
}
|
|
|
|
|
2008-11-30 19:17:14 +08:00
|
|
|
static int btusb_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct btusb_data *data = usb_get_intfdata(intf);
|
|
|
|
struct hci_dev *hdev = data->hdev;
|
2009-08-25 05:44:59 +08:00
|
|
|
int err = 0;
|
2008-11-30 19:17:14 +08:00
|
|
|
|
|
|
|
BT_DBG("intf %p", intf);
|
|
|
|
|
|
|
|
if (--data->suspend_count)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
2009-08-25 05:44:59 +08:00
|
|
|
goto done;
|
2008-11-30 19:17:14 +08:00
|
|
|
|
|
|
|
if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
|
|
|
|
err = btusb_submit_intr_urb(hdev, GFP_NOIO);
|
|
|
|
if (err < 0) {
|
|
|
|
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
2009-08-25 05:44:59 +08:00
|
|
|
goto failed;
|
2008-11-30 19:17:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
|
2009-02-05 00:41:38 +08:00
|
|
|
err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
|
|
|
if (err < 0) {
|
2008-11-30 19:17:14 +08:00
|
|
|
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
2009-08-25 05:44:59 +08:00
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
2008-11-30 19:17:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
|
|
|
|
if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
|
|
|
|
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
|
|
|
else
|
|
|
|
btusb_submit_isoc_urb(hdev, GFP_NOIO);
|
|
|
|
}
|
|
|
|
|
2009-08-25 05:44:59 +08:00
|
|
|
spin_lock_irq(&data->txlock);
|
|
|
|
play_deferred(data);
|
|
|
|
clear_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
|
spin_unlock_irq(&data->txlock);
|
|
|
|
schedule_work(&data->work);
|
|
|
|
|
2008-11-30 19:17:14 +08:00
|
|
|
return 0;
|
2009-08-25 05:44:59 +08:00
|
|
|
|
|
|
|
failed:
|
|
|
|
usb_scuttle_anchored_urbs(&data->deferred);
|
|
|
|
done:
|
|
|
|
spin_lock_irq(&data->txlock);
|
|
|
|
clear_bit(BTUSB_SUSPENDING, &data->flags);
|
|
|
|
spin_unlock_irq(&data->txlock);
|
|
|
|
|
|
|
|
return err;
|
2008-11-30 19:17:14 +08:00
|
|
|
}
|
2009-08-25 05:44:59 +08:00
|
|
|
#endif
|
2008-11-30 19:17:14 +08:00
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
static struct usb_driver btusb_driver = {
|
|
|
|
.name = "btusb",
|
|
|
|
.probe = btusb_probe,
|
|
|
|
.disconnect = btusb_disconnect,
|
2009-08-25 05:44:59 +08:00
|
|
|
#ifdef CONFIG_PM
|
2008-11-30 19:17:14 +08:00
|
|
|
.suspend = btusb_suspend,
|
|
|
|
.resume = btusb_resume,
|
2009-08-25 05:44:59 +08:00
|
|
|
#endif
|
2007-10-20 20:12:34 +08:00
|
|
|
.id_table = btusb_table,
|
2009-08-25 05:44:59 +08:00
|
|
|
.supports_autosuspend = 1,
|
2012-04-24 01:08:51 +08:00
|
|
|
.disable_hub_initiated_lpm = 1,
|
2007-10-20 20:12:34 +08:00
|
|
|
};
|
|
|
|
|
2011-11-19 01:47:34 +08:00
|
|
|
module_usb_driver(btusb_driver);
|
2007-10-20 20:12:34 +08:00
|
|
|
|
2008-08-08 04:26:56 +08:00
|
|
|
module_param(ignore_dga, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
|
|
|
|
|
|
|
|
module_param(ignore_csr, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
|
|
|
|
|
|
|
|
module_param(ignore_sniffer, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
|
|
|
|
|
|
|
|
module_param(disable_scofix, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
|
|
|
|
|
|
|
|
module_param(force_scofix, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
|
|
|
|
|
|
|
|
module_param(reset, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
|
|
|
|
|
2007-10-20 20:12:34 +08:00
|
|
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
|
|
|
MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION);
|
|
|
|
MODULE_VERSION(VERSION);
|
|
|
|
MODULE_LICENSE("GPL");
|