162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Digianswer Bluetooth USB driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (C) 2004-2007  Marcel Holtmann <marcel@holtmann.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/skbuff.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/usb.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
2162306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "h4_recv.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define VERSION "0.11"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct usb_device_id bpa10x_table[] = {
2862306a36Sopenharmony_ci	/* Tektronix BPA 100/105 (Digianswer) */
2962306a36Sopenharmony_ci	{ USB_DEVICE(0x08fd, 0x0002) },
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	{ }	/* Terminating entry */
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, bpa10x_table);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct bpa10x_data {
3762306a36Sopenharmony_ci	struct hci_dev    *hdev;
3862306a36Sopenharmony_ci	struct usb_device *udev;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	struct usb_anchor tx_anchor;
4162306a36Sopenharmony_ci	struct usb_anchor rx_anchor;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	struct sk_buff *rx_skb[2];
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void bpa10x_tx_complete(struct urb *urb)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct sk_buff *skb = urb->context;
4962306a36Sopenharmony_ci	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	BT_DBG("%s urb %p status %d count %d", hdev->name,
5262306a36Sopenharmony_ci					urb, urb->status, urb->actual_length);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!test_bit(HCI_RUNNING, &hdev->flags))
5562306a36Sopenharmony_ci		goto done;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!urb->status)
5862306a36Sopenharmony_ci		hdev->stat.byte_tx += urb->transfer_buffer_length;
5962306a36Sopenharmony_ci	else
6062306a36Sopenharmony_ci		hdev->stat.err_tx++;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cidone:
6362306a36Sopenharmony_ci	kfree(urb->setup_packet);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	kfree_skb(skb);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define HCI_VENDOR_HDR_SIZE 5
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define HCI_RECV_VENDOR \
7162306a36Sopenharmony_ci	.type = HCI_VENDOR_PKT, \
7262306a36Sopenharmony_ci	.hlen = HCI_VENDOR_HDR_SIZE, \
7362306a36Sopenharmony_ci	.loff = 3, \
7462306a36Sopenharmony_ci	.lsize = 2, \
7562306a36Sopenharmony_ci	.maxlen = HCI_MAX_FRAME_SIZE
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct h4_recv_pkt bpa10x_recv_pkts[] = {
7862306a36Sopenharmony_ci	{ H4_RECV_ACL,     .recv = hci_recv_frame },
7962306a36Sopenharmony_ci	{ H4_RECV_SCO,     .recv = hci_recv_frame },
8062306a36Sopenharmony_ci	{ H4_RECV_EVENT,   .recv = hci_recv_frame },
8162306a36Sopenharmony_ci	{ HCI_RECV_VENDOR, .recv = hci_recv_diag  },
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void bpa10x_rx_complete(struct urb *urb)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct hci_dev *hdev = urb->context;
8762306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
8862306a36Sopenharmony_ci	int err;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	BT_DBG("%s urb %p status %d count %d", hdev->name,
9162306a36Sopenharmony_ci					urb, urb->status, urb->actual_length);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!test_bit(HCI_RUNNING, &hdev->flags))
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (urb->status == 0) {
9762306a36Sopenharmony_ci		bool idx = usb_pipebulk(urb->pipe);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
10062306a36Sopenharmony_ci						urb->transfer_buffer,
10162306a36Sopenharmony_ci						urb->actual_length,
10262306a36Sopenharmony_ci						bpa10x_recv_pkts,
10362306a36Sopenharmony_ci						ARRAY_SIZE(bpa10x_recv_pkts));
10462306a36Sopenharmony_ci		if (IS_ERR(data->rx_skb[idx])) {
10562306a36Sopenharmony_ci			bt_dev_err(hdev, "corrupted event packet");
10662306a36Sopenharmony_ci			hdev->stat.err_rx++;
10762306a36Sopenharmony_ci			data->rx_skb[idx] = NULL;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	usb_anchor_urb(urb, &data->rx_anchor);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	err = usb_submit_urb(urb, GFP_ATOMIC);
11462306a36Sopenharmony_ci	if (err < 0) {
11562306a36Sopenharmony_ci		bt_dev_err(hdev, "urb %p failed to resubmit (%d)", urb, -err);
11662306a36Sopenharmony_ci		usb_unanchor_urb(urb);
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
12362306a36Sopenharmony_ci	struct urb *urb;
12462306a36Sopenharmony_ci	unsigned char *buf;
12562306a36Sopenharmony_ci	unsigned int pipe;
12662306a36Sopenharmony_ci	int err, size = 16;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
13162306a36Sopenharmony_ci	if (!urb)
13262306a36Sopenharmony_ci		return -ENOMEM;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
13562306a36Sopenharmony_ci	if (!buf) {
13662306a36Sopenharmony_ci		usb_free_urb(urb);
13762306a36Sopenharmony_ci		return -ENOMEM;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	pipe = usb_rcvintpipe(data->udev, 0x81);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
14362306a36Sopenharmony_ci						bpa10x_rx_complete, hdev, 1);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	urb->transfer_flags |= URB_FREE_BUFFER;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	usb_anchor_urb(urb, &data->rx_anchor);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	err = usb_submit_urb(urb, GFP_KERNEL);
15062306a36Sopenharmony_ci	if (err < 0) {
15162306a36Sopenharmony_ci		bt_dev_err(hdev, "urb %p submission failed (%d)", urb, -err);
15262306a36Sopenharmony_ci		usb_unanchor_urb(urb);
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	usb_free_urb(urb);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return err;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
16362306a36Sopenharmony_ci	struct urb *urb;
16462306a36Sopenharmony_ci	unsigned char *buf;
16562306a36Sopenharmony_ci	unsigned int pipe;
16662306a36Sopenharmony_ci	int err, size = 64;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
17162306a36Sopenharmony_ci	if (!urb)
17262306a36Sopenharmony_ci		return -ENOMEM;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
17562306a36Sopenharmony_ci	if (!buf) {
17662306a36Sopenharmony_ci		usb_free_urb(urb);
17762306a36Sopenharmony_ci		return -ENOMEM;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	pipe = usb_rcvbulkpipe(data->udev, 0x82);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	usb_fill_bulk_urb(urb, data->udev, pipe,
18362306a36Sopenharmony_ci					buf, size, bpa10x_rx_complete, hdev);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	urb->transfer_flags |= URB_FREE_BUFFER;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	usb_anchor_urb(urb, &data->rx_anchor);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	err = usb_submit_urb(urb, GFP_KERNEL);
19062306a36Sopenharmony_ci	if (err < 0) {
19162306a36Sopenharmony_ci		bt_dev_err(hdev, "urb %p submission failed (%d)", urb, -err);
19262306a36Sopenharmony_ci		usb_unanchor_urb(urb);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	usb_free_urb(urb);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return err;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int bpa10x_open(struct hci_dev *hdev)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
20362306a36Sopenharmony_ci	int err;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	err = bpa10x_submit_intr_urb(hdev);
20862306a36Sopenharmony_ci	if (err < 0)
20962306a36Sopenharmony_ci		goto error;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	err = bpa10x_submit_bulk_urb(hdev);
21262306a36Sopenharmony_ci	if (err < 0)
21362306a36Sopenharmony_ci		goto error;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cierror:
21862306a36Sopenharmony_ci	usb_kill_anchored_urbs(&data->rx_anchor);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return err;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int bpa10x_close(struct hci_dev *hdev)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	usb_kill_anchored_urbs(&data->rx_anchor);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int bpa10x_flush(struct hci_dev *hdev)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	usb_kill_anchored_urbs(&data->tx_anchor);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int bpa10x_setup(struct hci_dev *hdev)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	static const u8 req[] = { 0x07 };
24862306a36Sopenharmony_ci	struct sk_buff *skb;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Read revision string */
25362306a36Sopenharmony_ci	skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
25462306a36Sopenharmony_ci	if (IS_ERR(skb))
25562306a36Sopenharmony_ci		return PTR_ERR(skb);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	hci_set_fw_info(hdev, "%s", skb->data + 1);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	kfree_skb(skb);
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct bpa10x_data *data = hci_get_drvdata(hdev);
26862306a36Sopenharmony_ci	struct usb_ctrlrequest *dr;
26962306a36Sopenharmony_ci	struct urb *urb;
27062306a36Sopenharmony_ci	unsigned int pipe;
27162306a36Sopenharmony_ci	int err;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	skb->dev = (void *) hdev;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
27862306a36Sopenharmony_ci	if (!urb)
27962306a36Sopenharmony_ci		return -ENOMEM;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Prepend skb with frame type */
28262306a36Sopenharmony_ci	*(u8 *)skb_push(skb, 1) = hci_skb_pkt_type(skb);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	switch (hci_skb_pkt_type(skb)) {
28562306a36Sopenharmony_ci	case HCI_COMMAND_PKT:
28662306a36Sopenharmony_ci		dr = kmalloc(sizeof(*dr), GFP_KERNEL);
28762306a36Sopenharmony_ci		if (!dr) {
28862306a36Sopenharmony_ci			usb_free_urb(urb);
28962306a36Sopenharmony_ci			return -ENOMEM;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		dr->bRequestType = USB_TYPE_VENDOR;
29362306a36Sopenharmony_ci		dr->bRequest     = 0;
29462306a36Sopenharmony_ci		dr->wIndex       = 0;
29562306a36Sopenharmony_ci		dr->wValue       = 0;
29662306a36Sopenharmony_ci		dr->wLength      = __cpu_to_le16(skb->len);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		pipe = usb_sndctrlpipe(data->udev, 0x00);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
30162306a36Sopenharmony_ci				skb->data, skb->len, bpa10x_tx_complete, skb);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		hdev->stat.cmd_tx++;
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	case HCI_ACLDATA_PKT:
30762306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(data->udev, 0x02);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, data->udev, pipe,
31062306a36Sopenharmony_ci				skb->data, skb->len, bpa10x_tx_complete, skb);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		hdev->stat.acl_tx++;
31362306a36Sopenharmony_ci		break;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	case HCI_SCODATA_PKT:
31662306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(data->udev, 0x02);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		usb_fill_bulk_urb(urb, data->udev, pipe,
31962306a36Sopenharmony_ci				skb->data, skb->len, bpa10x_tx_complete, skb);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		hdev->stat.sco_tx++;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	default:
32562306a36Sopenharmony_ci		usb_free_urb(urb);
32662306a36Sopenharmony_ci		return -EILSEQ;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	usb_anchor_urb(urb, &data->tx_anchor);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	err = usb_submit_urb(urb, GFP_KERNEL);
33262306a36Sopenharmony_ci	if (err < 0) {
33362306a36Sopenharmony_ci		bt_dev_err(hdev, "urb %p submission failed", urb);
33462306a36Sopenharmony_ci		kfree(urb->setup_packet);
33562306a36Sopenharmony_ci		usb_unanchor_urb(urb);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	usb_free_urb(urb);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return err;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	const u8 req[] = { 0x00, enable };
34662306a36Sopenharmony_ci	struct sk_buff *skb;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	BT_DBG("%s", hdev->name);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (!test_bit(HCI_RUNNING, &hdev->flags))
35162306a36Sopenharmony_ci		return -ENETDOWN;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* Enable sniffer operation */
35462306a36Sopenharmony_ci	skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
35562306a36Sopenharmony_ci	if (IS_ERR(skb))
35662306a36Sopenharmony_ci		return PTR_ERR(skb);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	kfree_skb(skb);
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int bpa10x_probe(struct usb_interface *intf,
36362306a36Sopenharmony_ci			const struct usb_device_id *id)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct bpa10x_data *data;
36662306a36Sopenharmony_ci	struct hci_dev *hdev;
36762306a36Sopenharmony_ci	int err;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	BT_DBG("intf %p id %p", intf, id);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
37262306a36Sopenharmony_ci		return -ENODEV;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
37562306a36Sopenharmony_ci	if (!data)
37662306a36Sopenharmony_ci		return -ENOMEM;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	data->udev = interface_to_usbdev(intf);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	init_usb_anchor(&data->tx_anchor);
38162306a36Sopenharmony_ci	init_usb_anchor(&data->rx_anchor);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	hdev = hci_alloc_dev();
38462306a36Sopenharmony_ci	if (!hdev)
38562306a36Sopenharmony_ci		return -ENOMEM;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	hdev->bus = HCI_USB;
38862306a36Sopenharmony_ci	hci_set_drvdata(hdev, data);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	data->hdev = hdev;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	SET_HCIDEV_DEV(hdev, &intf->dev);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	hdev->open     = bpa10x_open;
39562306a36Sopenharmony_ci	hdev->close    = bpa10x_close;
39662306a36Sopenharmony_ci	hdev->flush    = bpa10x_flush;
39762306a36Sopenharmony_ci	hdev->setup    = bpa10x_setup;
39862306a36Sopenharmony_ci	hdev->send     = bpa10x_send_frame;
39962306a36Sopenharmony_ci	hdev->set_diag = bpa10x_set_diag;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	err = hci_register_dev(hdev);
40462306a36Sopenharmony_ci	if (err < 0) {
40562306a36Sopenharmony_ci		hci_free_dev(hdev);
40662306a36Sopenharmony_ci		return err;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	usb_set_intfdata(intf, data);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void bpa10x_disconnect(struct usb_interface *intf)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct bpa10x_data *data = usb_get_intfdata(intf);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	BT_DBG("intf %p", intf);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!data)
42162306a36Sopenharmony_ci		return;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	hci_unregister_dev(data->hdev);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	hci_free_dev(data->hdev);
42862306a36Sopenharmony_ci	kfree_skb(data->rx_skb[0]);
42962306a36Sopenharmony_ci	kfree_skb(data->rx_skb[1]);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic struct usb_driver bpa10x_driver = {
43362306a36Sopenharmony_ci	.name		= "bpa10x",
43462306a36Sopenharmony_ci	.probe		= bpa10x_probe,
43562306a36Sopenharmony_ci	.disconnect	= bpa10x_disconnect,
43662306a36Sopenharmony_ci	.id_table	= bpa10x_table,
43762306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cimodule_usb_driver(bpa10x_driver);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciMODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
44362306a36Sopenharmony_ciMODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
44462306a36Sopenharmony_ciMODULE_VERSION(VERSION);
44562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
446