From a9de9248064bfc8eb0a183a6a951a4e7b5ca10a4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Oct 2007 13:33:56 +0200 Subject: [Bluetooth] Switch from OGF+OCF to using only opcodes The Bluetooth HCI commands are divided into logical OGF groups for easier identification of their purposes. While this still makes sense for the written specification, its makes the code only more complex and harder to read. So instead of using separate OGF and OCF values to identify the commands, use a common 16-bit opcode that combines both values. As a side effect this also reduces the complexity of OGF and OCF calculations during command header parsing. Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcsp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index d66064ccb3..696f7528f0 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -237,7 +237,8 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, if (hciextn && chan == 5) { struct hci_command_hdr *hdr = (struct hci_command_hdr *) data; - if (hci_opcode_ogf(__le16_to_cpu(hdr->opcode)) == OGF_VENDOR_CMD) { + /* Vendor specific commands */ + if (hci_opcode_ogf(__le16_to_cpu(hdr->opcode)) == 0x3f) { u8 desc = *(data + HCI_COMMAND_HDR_SIZE); if ((desc & 0xf0) == 0xc0) { data += HCI_COMMAND_HDR_SIZE + 1; -- cgit v1.2.2 From e24b21ec85afda6f51b6bc403e971ff2aa7eacee Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Oct 2007 13:41:33 +0200 Subject: [Bluetooth] Change BPA 100/105 driver to use USB anchors With the new support for USB anchors the driver can become more simpler and also cleaner. This patch switches to the usage of USB anchors for all URBs. Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bpa10x.c | 624 +++++++++++++++++++-------------------------- 1 file changed, 262 insertions(+), 362 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index e8ebd5d3de..1375b5345a 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -2,7 +2,7 @@ * * Digianswer Bluetooth USB driver * - * Copyright (C) 2004-2005 Marcel Holtmann + * Copyright (C) 2004-2007 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify @@ -21,13 +21,14 @@ * */ -#include - #include +#include #include #include #include +#include #include +#include #include @@ -39,7 +40,7 @@ #define BT_DBG(D...) #endif -#define VERSION "0.8" +#define VERSION "0.9" static int ignore = 0; @@ -52,393 +53,285 @@ static struct usb_device_id bpa10x_table[] = { MODULE_DEVICE_TABLE(usb, bpa10x_table); -#define BPA10X_CMD_EP 0x00 -#define BPA10X_EVT_EP 0x81 -#define BPA10X_TX_EP 0x02 -#define BPA10X_RX_EP 0x82 - -#define BPA10X_CMD_BUF_SIZE 252 -#define BPA10X_EVT_BUF_SIZE 16 -#define BPA10X_TX_BUF_SIZE 384 -#define BPA10X_RX_BUF_SIZE 384 - struct bpa10x_data { - struct hci_dev *hdev; - struct usb_device *udev; + struct hci_dev *hdev; + struct usb_device *udev; - rwlock_t lock; + struct usb_anchor tx_anchor; + struct usb_anchor rx_anchor; - struct sk_buff_head cmd_queue; - struct urb *cmd_urb; - struct urb *evt_urb; - struct sk_buff *evt_skb; - unsigned int evt_len; - - struct sk_buff_head tx_queue; - struct urb *tx_urb; - struct urb *rx_urb; + struct sk_buff *rx_skb[2]; }; -#define HCI_VENDOR_HDR_SIZE 5 +#define HCI_VENDOR_HDR_SIZE 5 struct hci_vendor_hdr { - __u8 type; - __le16 snum; - __le16 dlen; + __u8 type; + __le16 snum; + __le16 dlen; } __attribute__ ((packed)); -static void bpa10x_recv_bulk(struct bpa10x_data *data, unsigned char *buf, int count) +static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) { - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - struct hci_vendor_hdr *vh; - struct sk_buff *skb; - int len; + struct bpa10x_data *data = hdev->driver_data; + + BT_DBG("%s queue %d buffer %p count %d", hdev->name, + queue, buf, count); + + if (queue < 0 || queue > 1) + return -EILSEQ; + + hdev->stat.byte_rx += count; while (count) { - switch (*buf++) { - case HCI_ACLDATA_PKT: - ah = (struct hci_acl_hdr *) buf; - len = HCI_ACL_HDR_SIZE + __le16_to_cpu(ah->dlen); - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_recv_frame(skb); - } - break; + struct sk_buff *skb = data->rx_skb[queue]; + struct { __u8 type; int expect; } *scb; + int type, len = 0; - case HCI_SCODATA_PKT: - sh = (struct hci_sco_hdr *) buf; - len = HCI_SCO_HDR_SIZE + sh->dlen; - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - hci_recv_frame(skb); + if (!skb) { + /* Start of the frame */ + + type = *((__u8 *) buf); + count--; buf++; + + switch (type) { + case HCI_EVENT_PKT: + if (count >= HCI_EVENT_HDR_SIZE) { + struct hci_event_hdr *h = buf; + len = HCI_EVENT_HDR_SIZE + h->plen; + } else + return -EILSEQ; + break; + + case HCI_ACLDATA_PKT: + if (count >= HCI_ACL_HDR_SIZE) { + struct hci_acl_hdr *h = buf; + len = HCI_ACL_HDR_SIZE + + __le16_to_cpu(h->dlen); + } else + return -EILSEQ; + break; + + case HCI_SCODATA_PKT: + if (count >= HCI_SCO_HDR_SIZE) { + struct hci_sco_hdr *h = buf; + len = HCI_SCO_HDR_SIZE + h->dlen; + } else + return -EILSEQ; + break; + + case HCI_VENDOR_PKT: + if (count >= HCI_VENDOR_HDR_SIZE) { + struct hci_vendor_hdr *h = buf; + len = HCI_VENDOR_HDR_SIZE + + __le16_to_cpu(h->dlen); + } else + return -EILSEQ; + break; } - break; - case HCI_VENDOR_PKT: - vh = (struct hci_vendor_hdr *) buf; - len = HCI_VENDOR_HDR_SIZE + __le16_to_cpu(vh->dlen); skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; - hci_recv_frame(skb); + if (!skb) { + BT_ERR("%s no memory for packet", hdev->name); + return -ENOMEM; } - break; - - default: - len = count - 1; - break; - } - buf += len; - count -= (len + 1); - } -} - -static int bpa10x_recv_event(struct bpa10x_data *data, unsigned char *buf, int size) -{ - BT_DBG("data %p buf %p size %d", data, buf, size); + skb->dev = (void *) hdev; - if (data->evt_skb) { - struct sk_buff *skb = data->evt_skb; + data->rx_skb[queue] = skb; - memcpy(skb_put(skb, size), buf, size); + scb = (void *) skb->cb; + scb->type = type; + scb->expect = len; + } else { + /* Continuation */ - if (skb->len == data->evt_len) { - data->evt_skb = NULL; - data->evt_len = 0; - hci_recv_frame(skb); - } - } else { - struct sk_buff *skb; - struct hci_event_hdr *hdr; - unsigned char pkt_type; - int pkt_len = 0; - - if (size < HCI_EVENT_HDR_SIZE + 1) { - BT_ERR("%s event packet block with size %d is too short", - data->hdev->name, size); - return -EILSEQ; + scb = (void *) skb->cb; + len = scb->expect; } - pkt_type = *buf++; - size--; - - if (pkt_type != HCI_EVENT_PKT) { - BT_ERR("%s unexpected event packet start byte 0x%02x", - data->hdev->name, pkt_type); - return -EPROTO; - } + len = min(len, count); - hdr = (struct hci_event_hdr *) buf; - pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; + memcpy(skb_put(skb, len), buf, len); - skb = bt_skb_alloc(pkt_len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for new event packet", - data->hdev->name); - return -ENOMEM; - } + scb->expect -= len; - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = pkt_type; + if (scb->expect == 0) { + /* Complete frame */ - memcpy(skb_put(skb, size), buf, size); + data->rx_skb[queue] = NULL; - if (pkt_len == size) { + bt_cb(skb)->pkt_type = scb->type; hci_recv_frame(skb); - } else { - data->evt_skb = skb; - data->evt_len = pkt_len; } + + count -= len; buf += len; } return 0; } -static void bpa10x_wakeup(struct bpa10x_data *data) +static void bpa10x_tx_complete(struct urb *urb) { - struct urb *urb; - struct sk_buff *skb; - int err; + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *) skb->dev; - BT_DBG("data %p", data); + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); - urb = data->cmd_urb; - if (urb->status == -EINPROGRESS) - skb = NULL; + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (!urb->status) + hdev->stat.byte_tx += urb->transfer_buffer_length; else - skb = skb_dequeue(&data->cmd_queue); + hdev->stat.err_tx++; - if (skb) { - struct usb_ctrlrequest *cr; +done: + kfree(urb->setup_packet); - if (skb->len > BPA10X_CMD_BUF_SIZE) { - BT_ERR("%s command packet with size %d is too big", - data->hdev->name, skb->len); - kfree_skb(skb); - return; - } + kfree_skb(skb); +} + +static void bpa10x_rx_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct bpa10x_data *data = hdev->driver_data; + int err; - cr = (struct usb_ctrlrequest *) urb->setup_packet; - cr->wLength = __cpu_to_le16(skb->len); + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); - skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); - urb->transfer_buffer_length = skb->len; + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s submit failed for command urb %p with error %d", - data->hdev->name, urb, err); - skb_queue_head(&data->cmd_queue, skb); - } else - kfree_skb(skb); + if (urb->status == 0) { + if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), + urb->transfer_buffer, + urb->actual_length) < 0) { + BT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } } - urb = data->tx_urb; - if (urb->status == -EINPROGRESS) - skb = NULL; - else - skb = skb_dequeue(&data->tx_queue); - - if (skb) { - skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); - urb->transfer_buffer_length = skb->len; - - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s submit failed for command urb %p with error %d", - data->hdev->name, urb, err); - skb_queue_head(&data->tx_queue, skb); - } else - kfree_skb(skb); + usb_anchor_urb(urb, &data->rx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); } } -static void bpa10x_complete(struct urb *urb) +static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) { - struct bpa10x_data *data = urb->context; - unsigned char *buf = urb->transfer_buffer; - int err, count = urb->actual_length; + struct bpa10x_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = 16; - BT_DBG("data %p urb %p buf %p count %d", data, urb, buf, count); + BT_DBG("%s", hdev->name); - read_lock(&data->lock); + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; - if (!test_bit(HCI_RUNNING, &data->hdev->flags)) - goto unlock; + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } - if (urb->status < 0 || !count) - goto resubmit; + pipe = usb_rcvintpipe(data->udev, 0x81); - if (usb_pipein(urb->pipe)) { - data->hdev->stat.byte_rx += count; + usb_fill_int_urb(urb, data->udev, pipe, buf, size, + bpa10x_rx_complete, hdev, 1); - if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) - bpa10x_recv_event(data, buf, count); + urb->transfer_flags |= URB_FREE_BUFFER; - if (usb_pipetype(urb->pipe) == PIPE_BULK) - bpa10x_recv_bulk(data, buf, count); - } else { - data->hdev->stat.byte_tx += count; + usb_anchor_urb(urb, &data->rx_anchor); - bpa10x_wakeup(data); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); } -resubmit: - if (usb_pipein(urb->pipe)) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s urb %p type %d resubmit status %d", - data->hdev->name, urb, usb_pipetype(urb->pipe), err); - } - } + usb_free_urb(urb); -unlock: - read_unlock(&data->lock); + return err; } -static inline struct urb *bpa10x_alloc_urb(struct usb_device *udev, unsigned int pipe, - size_t size, gfp_t flags, void *data) +static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) { + struct bpa10x_data *data = hdev->driver_data; struct urb *urb; - struct usb_ctrlrequest *cr; unsigned char *buf; + unsigned int pipe; + int err, size = 64; - BT_DBG("udev %p data %p", udev, data); + BT_DBG("%s", hdev->name); - urb = usb_alloc_urb(0, flags); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) - return NULL; + return -ENOMEM; - buf = kmalloc(size, flags); + buf = kmalloc(size, GFP_KERNEL); if (!buf) { usb_free_urb(urb); - return NULL; + return -ENOMEM; } - switch (usb_pipetype(pipe)) { - case PIPE_CONTROL: - cr = kmalloc(sizeof(*cr), flags); - if (!cr) { - kfree(buf); - usb_free_urb(urb); - return NULL; - } + pipe = usb_rcvbulkpipe(data->udev, 0x82); - cr->bRequestType = USB_TYPE_VENDOR; - cr->bRequest = 0; - cr->wIndex = 0; - cr->wValue = 0; - cr->wLength = __cpu_to_le16(0); + usb_fill_bulk_urb(urb, data->udev, pipe, + buf, size, bpa10x_rx_complete, hdev); - usb_fill_control_urb(urb, udev, pipe, (void *) cr, buf, 0, bpa10x_complete, data); - break; + urb->transfer_flags |= URB_FREE_BUFFER; - case PIPE_INTERRUPT: - usb_fill_int_urb(urb, udev, pipe, buf, size, bpa10x_complete, data, 1); - break; + usb_anchor_urb(urb, &data->rx_anchor); - case PIPE_BULK: - usb_fill_bulk_urb(urb, udev, pipe, buf, size, bpa10x_complete, data); - break; - - default: + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); kfree(buf); - usb_free_urb(urb); - return NULL; } - return urb; -} - -static inline void bpa10x_free_urb(struct urb *urb) -{ - BT_DBG("urb %p", urb); - - if (!urb) - return; - - kfree(urb->setup_packet); - kfree(urb->transfer_buffer); - usb_free_urb(urb); + + return err; } static int bpa10x_open(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - struct usb_device *udev = data->udev; - unsigned long flags; int err; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; - data->cmd_urb = bpa10x_alloc_urb(udev, usb_sndctrlpipe(udev, BPA10X_CMD_EP), - BPA10X_CMD_BUF_SIZE, GFP_KERNEL, data); - if (!data->cmd_urb) { - err = -ENOMEM; - goto done; - } - - data->evt_urb = bpa10x_alloc_urb(udev, usb_rcvintpipe(udev, BPA10X_EVT_EP), - BPA10X_EVT_BUF_SIZE, GFP_KERNEL, data); - if (!data->evt_urb) { - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } - - data->rx_urb = bpa10x_alloc_urb(udev, usb_rcvbulkpipe(udev, BPA10X_RX_EP), - BPA10X_RX_BUF_SIZE, GFP_KERNEL, data); - if (!data->rx_urb) { - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } - - data->tx_urb = bpa10x_alloc_urb(udev, usb_sndbulkpipe(udev, BPA10X_TX_EP), - BPA10X_TX_BUF_SIZE, GFP_KERNEL, data); - if (!data->rx_urb) { - bpa10x_free_urb(data->rx_urb); - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } + err = bpa10x_submit_intr_urb(hdev); + if (err < 0) + goto error; - write_lock_irqsave(&data->lock, flags); + err = bpa10x_submit_bulk_urb(hdev); + if (err < 0) + goto error; - err = usb_submit_urb(data->evt_urb, GFP_ATOMIC); - if (err < 0) { - BT_ERR("%s submit failed for event urb %p with error %d", - data->hdev->name, data->evt_urb, err); - } else { - err = usb_submit_urb(data->rx_urb, GFP_ATOMIC); - if (err < 0) { - BT_ERR("%s submit failed for rx urb %p with error %d", - data->hdev->name, data->evt_urb, err); - usb_kill_urb(data->evt_urb); - } - } + return 0; - write_unlock_irqrestore(&data->lock, flags); +error: + usb_kill_anchored_urbs(&data->rx_anchor); -done: - if (err < 0) - clear_bit(HCI_RUNNING, &hdev->flags); + clear_bit(HCI_RUNNING, &hdev->flags); return err; } @@ -446,27 +339,13 @@ done: static int bpa10x_close(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - unsigned long flags; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; - write_lock_irqsave(&data->lock, flags); - - skb_queue_purge(&data->cmd_queue); - usb_kill_urb(data->cmd_urb); - usb_kill_urb(data->evt_urb); - usb_kill_urb(data->rx_urb); - usb_kill_urb(data->tx_urb); - - write_unlock_irqrestore(&data->lock, flags); - - bpa10x_free_urb(data->cmd_urb); - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->rx_urb); - bpa10x_free_urb(data->tx_urb); + usb_kill_anchored_urbs(&data->rx_anchor); return 0; } @@ -475,9 +354,9 @@ static int bpa10x_flush(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); - skb_queue_purge(&data->cmd_queue); + usb_kill_anchored_urbs(&data->tx_anchor); return 0; } @@ -485,45 +364,78 @@ static int bpa10x_flush(struct hci_dev *hdev) static int bpa10x_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct bpa10x_data *data; - - BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); + struct bpa10x_data *data = hdev->driver_data; + struct usb_ctrlrequest *dr; + struct urb *urb; + unsigned int pipe; + int err; - if (!hdev) { - BT_ERR("Frame for unknown HCI device"); - return -ENODEV; - } + BT_DBG("%s", hdev->name); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - data = hdev->driver_data; + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + *skb_push(skb, 1) = bt_cb(skb)->pkt_type; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: + dr = kmalloc(sizeof(*dr), GFP_ATOMIC); + if (!dr) { + usb_free_urb(urb); + return -ENOMEM; + } + + dr->bRequestType = USB_TYPE_VENDOR; + 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, bpa10x_tx_complete, skb); + hdev->stat.cmd_tx++; - skb_queue_tail(&data->cmd_queue, skb); break; case HCI_ACLDATA_PKT: + pipe = usb_sndbulkpipe(data->udev, 0x02); + + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, bpa10x_tx_complete, skb); + hdev->stat.acl_tx++; - skb_queue_tail(&data->tx_queue, skb); break; case HCI_SCODATA_PKT: + pipe = usb_sndbulkpipe(data->udev, 0x02); + + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, bpa10x_tx_complete, skb); + hdev->stat.sco_tx++; - skb_queue_tail(&data->tx_queue, skb); break; - }; - read_lock(&data->lock); + default: + return -EILSEQ; + } + + usb_anchor_urb(urb, &data->tx_anchor); - bpa10x_wakeup(data); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed", hdev->name, urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } - read_unlock(&data->lock); + usb_free_urb(urb); return 0; } @@ -532,16 +444,17 @@ static void bpa10x_destruct(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); + kfree(data->rx_skb[0]); + kfree(data->rx_skb[1]); kfree(data); } static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); - struct hci_dev *hdev; struct bpa10x_data *data; + struct hci_dev *hdev; int err; BT_DBG("intf %p id %p", intf, id); @@ -549,48 +462,43 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * if (ignore) return -ENODEV; - if (intf->cur_altsetting->desc.bInterfaceNumber > 0) + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - BT_ERR("Can't allocate data structure"); + if (!data) return -ENOMEM; - } - - data->udev = udev; - rwlock_init(&data->lock); + data->udev = interface_to_usbdev(intf); - skb_queue_head_init(&data->cmd_queue); - skb_queue_head_init(&data->tx_queue); + init_usb_anchor(&data->tx_anchor); + init_usb_anchor(&data->rx_anchor); hdev = hci_alloc_dev(); if (!hdev) { - BT_ERR("Can't allocate HCI device"); kfree(data); return -ENOMEM; } - data->hdev = hdev; - hdev->type = HCI_USB; hdev->driver_data = data; + + data->hdev = hdev; + SET_HCIDEV_DEV(hdev, &intf->dev); - hdev->open = bpa10x_open; - hdev->close = bpa10x_close; - hdev->flush = bpa10x_flush; - hdev->send = bpa10x_send_frame; - hdev->destruct = bpa10x_destruct; + hdev->open = bpa10x_open; + hdev->close = bpa10x_close; + hdev->flush = bpa10x_flush; + hdev->send = bpa10x_send_frame; + hdev->destruct = bpa10x_destruct; hdev->owner = THIS_MODULE; err = hci_register_dev(hdev); if (err < 0) { - BT_ERR("Can't register HCI device"); - kfree(data); hci_free_dev(hdev); + kfree(data); return err; } @@ -602,19 +510,17 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * static void bpa10x_disconnect(struct usb_interface *intf) { struct bpa10x_data *data = usb_get_intfdata(intf); - struct hci_dev *hdev = data->hdev; BT_DBG("intf %p", intf); - if (!hdev) + if (!data) return; usb_set_intfdata(intf, NULL); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); + hci_unregister_dev(data->hdev); - hci_free_dev(hdev); + hci_free_dev(data->hdev); } static struct usb_driver bpa10x_driver = { @@ -626,15 +532,9 @@ static struct usb_driver bpa10x_driver = { static int __init bpa10x_init(void) { - int err; - BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION); - err = usb_register(&bpa10x_driver); - if (err < 0) - BT_ERR("Failed to register USB driver"); - - return err; + return usb_register(&bpa10x_driver); } static void __exit bpa10x_exit(void) -- cgit v1.2.2 From 166d2f6a4332aad53cb0750a296f76c06102552d Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sat, 20 Oct 2007 13:42:36 +0200 Subject: [Bluetooth] Add UART driver for Texas Instruments' BRF63xx chips Add support for Texas Instruments' HCI Low Level (HCILL) Bluetooth protocol, which is a power management extension to H4. The HCILL is widely used by TI's BRF63xx Bluetooth chips. Signed-off-by: Ohad Ben-Cohen Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 11 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ldisc.c | 8 +- drivers/bluetooth/hci_ll.c | 531 ++++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 drivers/bluetooth/hci_ll.c (limited to 'drivers') diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index b9fbe6e7f9..62b89b95ac 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -55,6 +55,17 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_LL + bool "HCILL protocol support" + depends on BT_HCIUART + help + HCILL (HCI Low Level) is a serial protocol for communication + between Bluetooth device and host. This protocol is required for + serial Bluetooth devices that are based on Texas Instruments' + BRF chips. + + Say Y here to compile support for HCILL protocol. + config BT_HCIBCM203X tristate "HCI BCM203x USB driver" depends on USB diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 08c10e178e..a543dfc2d6 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -16,4 +16,5 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o +hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 6055b9c0ac..e68821d074 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -549,7 +549,10 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_init(); #endif - +#ifdef CONFIG_BT_HCIUART_LL + ll_init(); +#endif + return 0; } @@ -563,6 +566,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_LL + ll_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c new file mode 100644 index 0000000000..8c3e62a17b --- /dev/null +++ b/drivers/bluetooth/hci_ll.c @@ -0,0 +1,531 @@ +/* + * Texas Instruments' Bluetooth HCILL UART protocol + * + * HCILL (HCI Low Level) is a Texas Instruments' power management + * protocol extension to H4. + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * Written by Ohad Ben-Cohen + * + * Acknowledgements: + * This file is based on hci_h4.c, which was written + * by Maxim Krasnyansky and Marcel Holtmann. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hci_uart.h" + +/* HCILL commands */ +#define HCILL_GO_TO_SLEEP_IND 0x30 +#define HCILL_GO_TO_SLEEP_ACK 0x31 +#define HCILL_WAKE_UP_IND 0x32 +#define HCILL_WAKE_UP_ACK 0x33 + +/* HCILL receiver States */ +#define HCILL_W4_PACKET_TYPE 0 +#define HCILL_W4_EVENT_HDR 1 +#define HCILL_W4_ACL_HDR 2 +#define HCILL_W4_SCO_HDR 3 +#define HCILL_W4_DATA 4 + +/* HCILL states */ +enum hcill_states_e { + HCILL_ASLEEP, + HCILL_ASLEEP_TO_AWAKE, + HCILL_AWAKE, + HCILL_AWAKE_TO_ASLEEP +}; + +struct hcill_cmd { + u8 cmd; +} __attribute__((packed)); + +struct ll_struct { + unsigned long rx_state; + unsigned long rx_count; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + spinlock_t hcill_lock; /* HCILL state lock */ + unsigned long hcill_state; /* HCILL power state */ + struct sk_buff_head tx_wait_q; /* HCILL wait queue */ +}; + +/* + * Builds and sends an HCILL command packet. + * These are very simple packets with only 1 cmd byte + */ +static int send_hcill_cmd(u8 cmd, struct hci_uart *hu) +{ + int err = 0; + struct sk_buff *skb = NULL; + struct ll_struct *ll = hu->priv; + struct hcill_cmd *hcill_packet; + + BT_DBG("hu %p cmd 0x%x", hu, cmd); + + /* allocate packet */ + skb = bt_skb_alloc(1, GFP_ATOMIC); + if (!skb) { + BT_ERR("cannot allocate memory for HCILL packet"); + err = -ENOMEM; + goto out; + } + + /* prepare packet */ + hcill_packet = (struct hcill_cmd *) skb_put(skb, 1); + hcill_packet->cmd = cmd; + skb->dev = (void *) hu->hdev; + + /* send packet */ + skb_queue_tail(&ll->txq, skb); +out: + return err; +} + +/* Initialize protocol */ +static int ll_open(struct hci_uart *hu) +{ + struct ll_struct *ll; + + BT_DBG("hu %p", hu); + + ll = kzalloc(sizeof(*ll), GFP_ATOMIC); + if (!ll) + return -ENOMEM; + + skb_queue_head_init(&ll->txq); + skb_queue_head_init(&ll->tx_wait_q); + spin_lock_init(&ll->hcill_lock); + + ll->hcill_state = HCILL_AWAKE; + + hu->priv = ll; + + return 0; +} + +/* Flush protocol data */ +static int ll_flush(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ll->tx_wait_q); + skb_queue_purge(&ll->txq); + + return 0; +} + +/* Close protocol */ +static int ll_close(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ll->tx_wait_q); + skb_queue_purge(&ll->txq); + + if (ll->rx_skb) + kfree_skb(ll->rx_skb); + + hu->priv = NULL; + + kfree(ll); + + return 0; +} + +/* + * internal function, which does common work of the device wake up process: + * 1. places all pending packets (waiting in tx_wait_q list) in txq list. + * 2. changes internal state to HCILL_AWAKE. + * Note: assumes that hcill_lock spinlock is taken, + * shouldn't be called otherwise! + */ +static void __ll_do_awake(struct ll_struct *ll) +{ + struct sk_buff *skb = NULL; + + while ((skb = skb_dequeue(&ll->tx_wait_q))) + skb_queue_tail(&ll->txq, skb); + + ll->hcill_state = HCILL_AWAKE; +} + +/* + * Called upon a wake-up-indication from the device + */ +static void ll_device_want_to_wakeup(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + switch (ll->hcill_state) { + case HCILL_ASLEEP: + /* acknowledge device wake up */ + if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) { + BT_ERR("cannot acknowledge device wake up"); + goto out; + } + break; + case HCILL_ASLEEP_TO_AWAKE: + /* + * this state means that a wake-up-indication + * is already on its way to the device, + * and will serve as the required wake-up-ack + */ + BT_DBG("dual wake-up-indication"); + break; + default: + /* any other state are illegal */ + BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state); + break; + } + + /* send pending packets and change state to HCILL_AWAKE */ + __ll_do_awake(ll); + +out: + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the packets */ + hci_uart_tx_wakeup(hu); +} + +/* + * Called upon a sleep-indication from the device + */ +static void ll_device_want_to_sleep(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* sanity check */ + if (ll->hcill_state != HCILL_AWAKE) + BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state); + + /* acknowledge device sleep */ + if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) { + BT_ERR("cannot acknowledge device sleep"); + goto out; + } + + /* update state */ + ll->hcill_state = HCILL_ASLEEP; + +out: + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the sleep ack packet */ + hci_uart_tx_wakeup(hu); +} + +/* + * Called upon wake-up-acknowledgement from the device + */ +static void ll_device_woke_up(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* sanity check */ + if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE) + BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state); + + /* send pending packets and change state to HCILL_AWAKE */ + __ll_do_awake(ll); + + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the packets */ + hci_uart_tx_wakeup(hu); +} + +/* Enqueue frame for transmittion (padding, crc, etc) */ +/* may be called from two simultaneous tasklets */ +static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + unsigned long flags = 0; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* act according to current state */ + switch (ll->hcill_state) { + case HCILL_AWAKE: + BT_DBG("device awake, sending normally"); + skb_queue_tail(&ll->txq, skb); + break; + case HCILL_ASLEEP: + BT_DBG("device asleep, waking up and queueing packet"); + /* save packet for later */ + skb_queue_tail(&ll->tx_wait_q, skb); + /* awake device */ + if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) { + BT_ERR("cannot wake up device"); + break; + } + ll->hcill_state = HCILL_ASLEEP_TO_AWAKE; + break; + case HCILL_ASLEEP_TO_AWAKE: + BT_DBG("device waking up, queueing packet"); + /* transient state; just keep packet for later */ + skb_queue_tail(&ll->tx_wait_q, skb); + break; + default: + BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state); + kfree_skb(skb); + break; + } + + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + return 0; +} + +static inline int ll_check_data_len(struct ll_struct *ll, int len) +{ + register int room = skb_tailroom(ll->rx_skb); + + BT_DBG("len %d room %d", len, room); + + if (!len) { + hci_recv_frame(ll->rx_skb); + } else if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ll->rx_skb); + } else { + ll->rx_state = HCILL_W4_DATA; + ll->rx_count = len; + return len; + } + + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_skb = NULL; + ll->rx_count = 0; + + return 0; +} + +/* Recv data */ +static int ll_recv(struct hci_uart *hu, void *data, int count) +{ + struct ll_struct *ll = hu->priv; + register char *ptr; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + register int len, type, dlen; + + BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count); + + ptr = data; + while (count) { + if (ll->rx_count) { + len = min_t(unsigned int, ll->rx_count, count); + memcpy(skb_put(ll->rx_skb, len), ptr, len); + ll->rx_count -= len; count -= len; ptr += len; + + if (ll->rx_count) + continue; + + switch (ll->rx_state) { + case HCILL_W4_DATA: + BT_DBG("Complete data"); + hci_recv_frame(ll->rx_skb); + + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_skb = NULL; + continue; + + case HCILL_W4_EVENT_HDR: + eh = (struct hci_event_hdr *) ll->rx_skb->data; + + BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); + + ll_check_data_len(ll, eh->plen); + continue; + + case HCILL_W4_ACL_HDR: + ah = (struct hci_acl_hdr *) ll->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + + BT_DBG("ACL header: dlen %d", dlen); + + ll_check_data_len(ll, dlen); + continue; + + case HCILL_W4_SCO_HDR: + sh = (struct hci_sco_hdr *) ll->rx_skb->data; + + BT_DBG("SCO header: dlen %d", sh->dlen); + + ll_check_data_len(ll, sh->dlen); + continue; + } + } + + /* HCILL_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ll->rx_state = HCILL_W4_EVENT_HDR; + ll->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ll->rx_state = HCILL_W4_ACL_HDR; + ll->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ll->rx_state = HCILL_W4_SCO_HDR; + ll->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + + /* HCILL signals */ + case HCILL_GO_TO_SLEEP_IND: + BT_DBG("HCILL_GO_TO_SLEEP_IND packet"); + ll_device_want_to_sleep(hu); + ptr++; count--; + continue; + + case HCILL_GO_TO_SLEEP_ACK: + /* shouldn't happen */ + BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state); + ptr++; count--; + continue; + + case HCILL_WAKE_UP_IND: + BT_DBG("HCILL_WAKE_UP_IND packet"); + ll_device_want_to_wakeup(hu); + ptr++; count--; + continue; + + case HCILL_WAKE_UP_ACK: + BT_DBG("HCILL_WAKE_UP_ACK packet"); + ll_device_woke_up(hu); + ptr++; count--; + continue; + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); + hu->hdev->stat.err_rx++; + ptr++; count--; + continue; + }; + + ptr++; count--; + + /* Allocate packet */ + ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ll->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_count = 0; + return 0; + } + + ll->rx_skb->dev = (void *) hu->hdev; + bt_cb(ll->rx_skb)->pkt_type = type; + } + + return count; +} + +static struct sk_buff *ll_dequeue(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + return skb_dequeue(&ll->txq); +} + +static struct hci_uart_proto llp = { + .id = HCI_UART_LL, + .open = ll_open, + .close = ll_close, + .recv = ll_recv, + .enqueue = ll_enqueue, + .dequeue = ll_dequeue, + .flush = ll_flush, +}; + +int ll_init(void) +{ + int err = hci_uart_register_proto(&llp); + + if (!err) + BT_INFO("HCILL protocol initialized"); + else + BT_ERR("HCILL protocol registration failed"); + + return err; +} + +int ll_deinit(void) +{ + return hci_uart_unregister_proto(&llp); +} diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 1097ce7239..50113db06b 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,12 +33,13 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 4 +#define HCI_UART_MAX_PROTO 5 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 +#define HCI_UART_LL 4 struct hci_uart; @@ -85,3 +86,8 @@ int h4_deinit(void); int bcsp_init(void); int bcsp_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_LL +int ll_init(void); +int ll_deinit(void); +#endif -- cgit v1.2.2 From ac019360fe311dd6aa11b358a02eb3a61675882e Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sat, 20 Oct 2007 13:45:57 +0200 Subject: [Bluetooth] Eliminate checks for impossible conditions in IRQ handler Our info structure and info->hdev is always passed to the IRQ handler, so we don't have to worry about these checks in every interrupt. Leave a BUG_ON() just to help unwary programmers, but these could probably be removed as well. Signed-off-by: Jeff Garzik Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bluecard_cs.c | 5 +---- drivers/bluetooth/bt3c_cs.c | 5 +---- drivers/bluetooth/btuart_cs.c | 5 +---- drivers/bluetooth/dtl1_cs.c | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 851de4d5b7..bcf57927b7 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -503,10 +503,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) unsigned int iobase; unsigned char reg; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 3951607463..a18f9b8c9e 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -344,10 +344,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) unsigned int iobase; int iir; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index d7d2ea0d86..08f48d577a 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -294,10 +294,7 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst) int boguscount = 0; int iir, lsr; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 7f9c54b996..dae45cdf02 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -298,10 +298,7 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) int boguscount = 0; int iir, lsr; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; -- cgit v1.2.2 From ddbaf13e3609442b64abb931ac21527772d87980 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Oct 2007 14:02:04 +0200 Subject: [Bluetooth] Add generic driver for Bluetooth SDIO devices This patch adds a generic driver for Bluetooth SDIO devices. It supports Type-A and Type-B devices. Signed-off-by: David Vrabel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 11 ++ drivers/bluetooth/Makefile | 2 + drivers/bluetooth/btsdio.c | 406 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 drivers/bluetooth/btsdio.c (limited to 'drivers') diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 62b89b95ac..77bded441c 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -22,6 +22,17 @@ config BT_HCIUSB_SCO Say Y here to compile support for SCO over HCI USB. +config BT_HCIBTSDIO + tristate "HCI SDIO driver" + depends on MMC + help + Bluetooth HCI SDIO driver. + This driver is required if you want to use Bluetooth device with + SDIO interface. + + Say Y here to compile support for Bluetooth SDIO devices into the + kernel or say M to compile it as module (btsdio). + config BT_HCIUART tristate "HCI UART driver" help diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index a543dfc2d6..aee12797aa 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o + hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c new file mode 100644 index 0000000000..b786f61879 --- /dev/null +++ b/drivers/bluetooth/btsdio.c @@ -0,0 +1,406 @@ +/* + * + * Generic Bluetooth SDIO driver + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * Copyright (C) 2007 Marcel Holtmann + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef CONFIG_BT_HCIBTSDIO_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "0.1" + +static const struct sdio_device_id btsdio_table[] = { + /* Generic Bluetooth Type-A SDIO device */ + { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) }, + + /* Generic Bluetooth Type-B SDIO device */ + { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) }, + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(sdio, btsdio_table); + +struct btsdio_data { + struct hci_dev *hdev; + struct sdio_func *func; + + struct work_struct work; + + struct sk_buff_head txq; +}; + +#define REG_RDAT 0x00 /* Receiver Data */ +#define REG_TDAT 0x00 /* Transmitter Data */ +#define REG_PC_RRT 0x10 /* Read Packet Control */ +#define REG_PC_WRT 0x11 /* Write Packet Control */ +#define REG_RTC_STAT 0x12 /* Retry Control Status */ +#define REG_RTC_SET 0x12 /* Retry Control Set */ +#define REG_INTRD 0x13 /* Interrupt Indication */ +#define REG_CL_INTRD 0x13 /* Interrupt Clear */ +#define REG_EN_INTRD 0x14 /* Interrupt Enable */ +#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */ + +static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb) +{ + int err; + + BT_DBG("%s", data->hdev->name); + + /* Prepend Type-A header */ + skb_push(skb, 4); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + + err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len); + if (err < 0) { + sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL); + return err; + } + + data->hdev->stat.byte_tx += skb->len; + + kfree_skb(skb); + + return 0; +} + +static void btsdio_work(struct work_struct *work) +{ + struct btsdio_data *data = container_of(work, struct btsdio_data, work); + struct sk_buff *skb; + int err; + + BT_DBG("%s", data->hdev->name); + + sdio_claim_host(data->func); + + while ((skb = skb_dequeue(&data->txq))) { + err = btsdio_tx_packet(data, skb); + if (err < 0) { + data->hdev->stat.err_tx++; + skb_queue_head(&data->txq, skb); + break; + } + } + + sdio_release_host(data->func); +} + +static int btsdio_rx_packet(struct btsdio_data *data) +{ + u8 hdr[4] __attribute__ ((aligned(4))); + struct sk_buff *skb; + int err, len; + + BT_DBG("%s", data->hdev->name); + + err = sdio_readsb(data->func, hdr, REG_RDAT, 4); + if (err < 0) + return err; + + len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16); + if (len < 4 || len > 65543) + return -EILSEQ; + + skb = bt_skb_alloc(len - 4, GFP_KERNEL); + if (!skb) { + /* Out of memory. Prepare a read retry and just + * return with the expectation that the next time + * we're called we'll have more memory. */ + return -ENOMEM; + } + + skb_put(skb, len - 4); + + err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4); + if (err < 0) { + kfree(skb); + return err; + } + + data->hdev->stat.byte_rx += len; + + skb->dev = (void *) data->hdev; + bt_cb(skb)->pkt_type = hdr[3]; + + err = hci_recv_frame(skb); + if (err < 0) { + kfree(skb); + return err; + } + + sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL); + + return 0; +} + +static void btsdio_interrupt(struct sdio_func *func) +{ + struct btsdio_data *data = sdio_get_drvdata(func); + int intrd; + + BT_DBG("%s", data->hdev->name); + + intrd = sdio_readb(func, REG_INTRD, NULL); + if (intrd & 0x01) { + sdio_writeb(func, 0x01, REG_CL_INTRD, NULL); + + if (btsdio_rx_packet(data) < 0) { + data->hdev->stat.err_rx++; + sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL); + } + } +} + +static int btsdio_open(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + int err; + + BT_DBG("%s", hdev->name); + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + sdio_claim_host(data->func); + + err = sdio_enable_func(data->func); + if (err < 0) { + clear_bit(HCI_RUNNING, &hdev->flags); + goto release; + } + + err = sdio_claim_irq(data->func, btsdio_interrupt); + if (err < 0) { + sdio_disable_func(data->func); + clear_bit(HCI_RUNNING, &hdev->flags); + goto release; + } + + if (data->func->class == SDIO_CLASS_BT_B) + sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL); + + sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL); + +release: + sdio_release_host(data->func); + + return err; +} + +static int btsdio_close(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + sdio_claim_host(data->func); + + sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL); + + sdio_release_irq(data->func); + sdio_disable_func(data->func); + + sdio_release_host(data->func); + + return 0; +} + +static int btsdio_flush(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + skb_queue_purge(&data->txq); + + return 0; +} + +static int btsdio_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + + default: + return -EILSEQ; + } + + skb_queue_tail(&data->txq, skb); + + schedule_work(&data->work); + + return 0; +} + +static void btsdio_destruct(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + kfree(data); +} + +static int btsdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct btsdio_data *data; + struct hci_dev *hdev; + struct sdio_func_tuple *tuple = func->tuples; + int err; + + BT_DBG("func %p id %p class 0x%04x", func, id, func->class); + + while (tuple) { + BT_DBG("code 0x%x size %d", tuple->code, tuple->size); + tuple = tuple->next; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->func = func; + + INIT_WORK(&data->work, btsdio_work); + + skb_queue_head_init(&data->txq); + + hdev = hci_alloc_dev(); + if (!hdev) { + kfree(data); + return -ENOMEM; + } + + hdev->type = HCI_SDIO; + hdev->driver_data = data; + + data->hdev = hdev; + + SET_HCIDEV_DEV(hdev, &func->dev); + + hdev->open = btsdio_open; + hdev->close = btsdio_close; + hdev->flush = btsdio_flush; + hdev->send = btsdio_send_frame; + hdev->destruct = btsdio_destruct; + + hdev->owner = THIS_MODULE; + + err = hci_register_dev(hdev); + if (err < 0) { + hci_free_dev(hdev); + kfree(data); + return err; + } + + sdio_set_drvdata(func, data); + + return 0; +} + +static void btsdio_remove(struct sdio_func *func) +{ + struct btsdio_data *data = sdio_get_drvdata(func); + struct hci_dev *hdev; + + BT_DBG("func %p", func); + + if (!data) + return; + + hdev = data->hdev; + + sdio_set_drvdata(func, NULL); + + hci_unregister_dev(hdev); + + hci_free_dev(hdev); +} + +static struct sdio_driver btsdio_driver = { + .name = "btsdio", + .probe = btsdio_probe, + .remove = btsdio_remove, + .id_table = btsdio_table, +}; + +static int __init btsdio_init(void) +{ + BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION); + + return sdio_register_driver(&btsdio_driver); +} + +static void __exit btsdio_exit(void) +{ + sdio_unregister_driver(&btsdio_driver); +} + +module_init(btsdio_init); +module_exit(btsdio_exit); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 5e23b923da03de7e41f00f5664ae22c4f7dcd2a3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Oct 2007 14:12:34 +0200 Subject: [Bluetooth] Add generic driver for Bluetooth USB devices This patch adds a new generic driver for Bluetooth USB devices. This driver is still experimental at this point, but it is cleaner and easier to maintain than the current Bluetooth USB driver. It is a much better starting point for power management improvements. Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 13 ++ drivers/bluetooth/Makefile | 1 + drivers/bluetooth/btusb.c | 564 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 drivers/bluetooth/btusb.c (limited to 'drivers') diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 77bded441c..075598e1c5 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -22,6 +22,19 @@ config BT_HCIUSB_SCO Say Y here to compile support for SCO over HCI USB. +config BT_HCIBTUSB + tristate "HCI USB driver (alternate version)" + depends on USB && EXPERIMENTAL && BT_HCIUSB=n + help + Bluetooth HCI USB driver. + This driver is required if you want to use Bluetooth devices with + USB interface. + + This driver is still experimental and has no SCO support. + + Say Y here to compile support for Bluetooth USB devices into the + kernel or say M to compile it as module (btusb). + config BT_HCIBTSDIO tristate "HCI SDIO driver" depends on MMC diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index aee12797aa..77444afbf1 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o hci_uart-y := hci_ldisc.o diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c new file mode 100644 index 0000000000..12e108914f --- /dev/null +++ b/drivers/bluetooth/btusb.c @@ -0,0 +1,564 @@ +/* + * + * Generic Bluetooth USB driver + * + * Copyright (C) 2005-2007 Marcel Holtmann + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +//#define CONFIG_BT_HCIBTUSB_DEBUG +#ifndef CONFIG_BT_HCIBTUSB_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "0.1" + +static struct usb_device_id btusb_table[] = { + /* Generic Bluetooth USB device */ + { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, btusb_table); + +static struct usb_device_id blacklist_table[] = { + { } /* Terminating entry */ +}; + +#define BTUSB_INTR_RUNNING 0 +#define BTUSB_BULK_RUNNING 1 + +struct btusb_data { + struct hci_dev *hdev; + struct usb_device *udev; + + spinlock_t lock; + + unsigned long flags; + + struct work_struct work; + + struct usb_anchor tx_anchor; + struct usb_anchor intr_anchor; + struct usb_anchor bulk_anchor; + + struct usb_endpoint_descriptor *intr_ep; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; +}; + +static void btusb_intr_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = hdev->driver_data; + 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) { + 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; + + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static inline int btusb_submit_intr_urb(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->intr_ep->wMaxPacketSize); + + buf = kmalloc(size, GFP_ATOMIC); + 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); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_bulk_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = hdev->driver_data; + 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) { + 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); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static inline int btusb_submit_bulk_urb(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize); + + buf = kmalloc(size, GFP_KERNEL); + 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; + + usb_anchor_urb(urb, &data->bulk_anchor); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_tx_complete(struct urb *urb) +{ + 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) +{ + struct btusb_data *data = hdev->driver_data; + int err; + + BT_DBG("%s", hdev->name); + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) + return 0; + + err = btusb_submit_intr_urb(hdev); + if (err < 0) { + clear_bit(BTUSB_INTR_RUNNING, &hdev->flags); + clear_bit(HCI_RUNNING, &hdev->flags); + } + + return err; +} + +static int btusb_close(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->bulk_anchor); + + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->intr_anchor); + + return 0; +} + +static int btusb_flush(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + usb_kill_anchored_urbs(&data->tx_anchor); + + return 0; +} + +static int btusb_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct btusb_data *data = hdev->driver_data; + 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; + + 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; + } + + dr->bRequestType = USB_TYPE_CLASS; + 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: + 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: + hdev->stat.sco_tx++; + kfree_skb(skb); + return 0; + + default: + return -EILSEQ; + } + + usb_anchor_urb(urb, &data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed", hdev->name, urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_destruct(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + kfree(data); +} + +static void btusb_notify(struct hci_dev *hdev, unsigned int evt) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s evt %d", hdev->name, evt); + + if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL) + schedule_work(&data->work); +} + +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; + + if (hdev->conn_hash.acl_num == 0) { + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->bulk_anchor); + return; + } + + if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) { + if (btusb_submit_bulk_urb(hdev) < 0) + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + else + btusb_submit_bulk_urb(hdev); + } +} + +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); + + 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; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + 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; + } + } + + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + kfree(data); + return -ENODEV; + } + + data->udev = interface_to_usbdev(intf); + + spin_lock_init(&data->lock); + + INIT_WORK(&data->work, btusb_work); + + init_usb_anchor(&data->tx_anchor); + init_usb_anchor(&data->intr_anchor); + init_usb_anchor(&data->bulk_anchor); + + hdev = hci_alloc_dev(); + if (!hdev) { + kfree(data); + return -ENOMEM; + } + + hdev->type = HCI_USB; + hdev->driver_data = data; + + data->hdev = hdev; + + SET_HCIDEV_DEV(hdev, &intf->dev); + + hdev->open = btusb_open; + hdev->close = btusb_close; + hdev->flush = btusb_flush; + hdev->send = btusb_send_frame; + hdev->destruct = btusb_destruct; + hdev->notify = btusb_notify; + + hdev->owner = THIS_MODULE; + + set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks); + + err = hci_register_dev(hdev); + if (err < 0) { + hci_free_dev(hdev); + kfree(data); + 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; + + usb_set_intfdata(intf, NULL); + + hci_unregister_dev(hdev); + + hci_free_dev(hdev); +} + +static struct usb_driver btusb_driver = { + .name = "btusb", + .probe = btusb_probe, + .disconnect = btusb_disconnect, + .id_table = btusb_table, +}; + +static int __init btusb_init(void) +{ + BT_INFO("Generic Bluetooth USB driver ver %s", VERSION); + + return usb_register(&btusb_driver); +} + +static void __exit btusb_exit(void) +{ + usb_deregister(&btusb_driver); +} + +module_init(btusb_init); +module_exit(btusb_exit); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From 6c7af27c8a2e8b85cb235a2409d3b2093b18f77d Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 21 Oct 2007 16:12:02 -0700 Subject: [TG3]: Add 5723 support This patch adds support for upcoming 5723 devices. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 014dc2cfe4..253d7613b9 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -200,6 +200,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906M)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5784)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5764)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5723)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761E)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, -- cgit v1.2.2 From 9c8a620e7f85fff050a54697da44bbd1a66e8e0b Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 21 Oct 2007 16:16:08 -0700 Subject: [TG3]: Add management FW version to ethtool report This patch appends the management firmware version to the bootcode firmware string reported through ethtool. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++--------- drivers/net/tg3.h | 9 +++++- 2 files changed, 78 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 253d7613b9..98f4658281 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -10821,9 +10821,24 @@ out_not_found: strcpy(tp->board_part_number, "none"); } +static int __devinit tg3_fw_img_is_valid(struct tg3 *tp, u32 offset) +{ + u32 val; + + if (tg3_nvram_read_swab(tp, offset, &val) || + (val & 0xfc000000) != 0x0c000000 || + tg3_nvram_read_swab(tp, offset + 4, &val) || + val != 0) + return 0; + + return 1; +} + static void __devinit tg3_read_fw_ver(struct tg3 *tp) { u32 val, offset, start; + u32 ver_offset; + int i, bcnt; if (tg3_nvram_read_swab(tp, 0, &val)) return; @@ -10836,29 +10851,71 @@ static void __devinit tg3_read_fw_ver(struct tg3 *tp) return; offset = tg3_nvram_logical_addr(tp, offset); - if (tg3_nvram_read_swab(tp, offset, &val)) + + if (!tg3_fw_img_is_valid(tp, offset) || + tg3_nvram_read_swab(tp, offset + 8, &ver_offset)) return; - if ((val & 0xfc000000) == 0x0c000000) { - u32 ver_offset, addr; - int i; + offset = offset + ver_offset - start; + for (i = 0; i < 16; i += 4) { + if (tg3_nvram_read(tp, offset + i, &val)) + return; + + val = le32_to_cpu(val); + memcpy(tp->fw_ver + i, &val, 4); + } - if (tg3_nvram_read_swab(tp, offset + 4, &val) || - tg3_nvram_read_swab(tp, offset + 8, &ver_offset)) + if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) || + (tp->tg3_flags & TG3_FLG3_ENABLE_APE)) + return; + + for (offset = TG3_NVM_DIR_START; + offset < TG3_NVM_DIR_END; + offset += TG3_NVM_DIRENT_SIZE) { + if (tg3_nvram_read_swab(tp, offset, &val)) return; - if (val != 0) + if ((val >> TG3_NVM_DIRTYPE_SHIFT) == TG3_NVM_DIRTYPE_ASFINI) + break; + } + + if (offset == TG3_NVM_DIR_END) + return; + + if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) + start = 0x08000000; + else if (tg3_nvram_read_swab(tp, offset - 4, &start)) + return; + + if (tg3_nvram_read_swab(tp, offset + 4, &offset) || + !tg3_fw_img_is_valid(tp, offset) || + tg3_nvram_read_swab(tp, offset + 8, &val)) + return; + + offset += val - start; + + bcnt = strlen(tp->fw_ver); + + tp->fw_ver[bcnt++] = ','; + tp->fw_ver[bcnt++] = ' '; + + for (i = 0; i < 4; i++) { + if (tg3_nvram_read(tp, offset, &val)) return; - addr = offset + ver_offset - start; - for (i = 0; i < 16; i += 4) { - if (tg3_nvram_read(tp, addr + i, &val)) - return; + val = le32_to_cpu(val); + offset += sizeof(val); - val = cpu_to_le32(val); - memcpy(tp->fw_ver + i, &val, 4); + if (bcnt > TG3_VER_SIZE - sizeof(val)) { + memcpy(&tp->fw_ver[bcnt], &val, TG3_VER_SIZE - bcnt); + break; } + + memcpy(&tp->fw_ver[bcnt], &val, sizeof(val)); + bcnt += sizeof(val); } + + tp->fw_ver[TG3_VER_SIZE - 1] = 0; } static struct pci_dev * __devinit tg3_find_peer(struct tg3 *); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 6dbdad2b8f..495a1dfaba 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -1540,6 +1540,12 @@ #define TG3_EEPROM_MAGIC_HW 0xabcd #define TG3_EEPROM_MAGIC_HW_MSK 0xffff +#define TG3_NVM_DIR_START 0x18 +#define TG3_NVM_DIR_END 0x78 +#define TG3_NVM_DIRENT_SIZE 0xc +#define TG3_NVM_DIRTYPE_SHIFT 24 +#define TG3_NVM_DIRTYPE_ASFINI 1 + /* 32K Window into NIC internal memory */ #define NIC_SRAM_WIN_BASE 0x00008000 @@ -2418,7 +2424,8 @@ struct tg3 { u32 pci_cmd; char board_part_number[24]; - char fw_ver[16]; +#define TG3_VER_SIZE 32 + char fw_ver[TG3_VER_SIZE]; u32 nic_sram_data_cfg; u32 pci_clock_ctrl; struct pci_dev *pdev_peer; -- cgit v1.2.2 From 8a6eac90e21633e054e17d21454a2c26824aeb18 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 21 Oct 2007 16:17:55 -0700 Subject: [TG3]: PCI command adjustment This patch changes the way the driver works with the PCI command register. It adjusts the access size from dwords to words. This patch is done both as a PCI configuration space cleanup and as preparatory work for PCI error recovery. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 7 ++----- drivers/net/tg3.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 98f4658281..328eb4adc1 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -5029,10 +5029,7 @@ static int tg3_poll_fw(struct tg3 *tp) /* Save PCI command register before chip reset */ static void tg3_save_pci_state(struct tg3 *tp) { - u32 val; - - pci_read_config_dword(tp->pdev, TG3PCI_COMMAND, &val); - tp->pci_cmd = val; + pci_read_config_word(tp->pdev, PCI_COMMAND, &tp->pci_cmd); } /* Restore PCI state after chip reset */ @@ -5055,7 +5052,7 @@ static void tg3_restore_pci_state(struct tg3 *tp) PCISTATE_ALLOW_APE_SHMEM_WR; pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, val); - pci_write_config_dword(tp->pdev, TG3PCI_COMMAND, tp->pci_cmd); + pci_write_config_word(tp->pdev, PCI_COMMAND, tp->pci_cmd); if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { pci_write_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE, diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 495a1dfaba..1d5b2a3dd2 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2421,7 +2421,7 @@ struct tg3 { #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ u32 led_ctrl; - u32 pci_cmd; + u16 pci_cmd; char board_part_number[24]; #define TG3_VER_SIZE 32 -- cgit v1.2.2 From 33b0c4fe6d0dd19fc7c9b801855f55c5260f2858 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 21 Oct 2007 16:22:38 -0700 Subject: [TG3]: Update version to 3.85 This patch updates the version number to 3.85. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 328eb4adc1..09440d783e 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -64,8 +64,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.84" -#define DRV_MODULE_RELDATE "October 12, 2007" +#define DRV_MODULE_VERSION "3.85" +#define DRV_MODULE_RELDATE "October 18, 2007" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 -- cgit v1.2.2 From 81429973cfff7745792c877dd083eec29724ec97 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 21 Oct 2007 16:32:58 -0700 Subject: [NIU]: Cleanup PAGE_SIZE checks a bit I get the following warning from a powerpc allyesconfig of current mainline: drivers/net/niu.c: In function 'niu_size_rbr': drivers/net/niu.c:3113: warning: large integer implicitly truncated to unsigned type PAGE_SIZE in this case is 64KB, so I don't quite get why gcc can't tell that the line in question will never be reached. I suggest the following instead, but I can unfortunately not do anything but build test it. Also, the driver does some other checks to make sure that PAGE_SIZE is a power of two (BUILD_BUG_ON() in niu_init()), doesn't seem like that could ever be untrue? Or are there really archs with non-power-of-two PAGE_SIZE? Signed-off-by: Olof Johansson Signed-off-by: David S. Miller --- drivers/net/niu.c | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/niu.c b/drivers/net/niu.c index ed1f9bbb2a..112ab079ce 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -3103,31 +3103,12 @@ static int niu_alloc_tx_ring_info(struct niu *np, static void niu_size_rbr(struct niu *np, struct rx_ring_info *rp) { - u16 bs; + u16 bss; - switch (PAGE_SIZE) { - case 4 * 1024: - case 8 * 1024: - case 16 * 1024: - case 32 * 1024: - rp->rbr_block_size = PAGE_SIZE; - rp->rbr_blocks_per_page = 1; - break; + bss = min(PAGE_SHIFT, 15); - default: - if (PAGE_SIZE % (32 * 1024) == 0) - bs = 32 * 1024; - else if (PAGE_SIZE % (16 * 1024) == 0) - bs = 16 * 1024; - else if (PAGE_SIZE % (8 * 1024) == 0) - bs = 8 * 1024; - else if (PAGE_SIZE % (4 * 1024) == 0) - bs = 4 * 1024; - else - BUG(); - rp->rbr_block_size = bs; - rp->rbr_blocks_per_page = PAGE_SIZE / bs; - } + rp->rbr_block_size = 1 << bss; + rp->rbr_blocks_per_page = 1 << (PAGE_SHIFT-bss); rp->rbr_sizes[0] = 256; rp->rbr_sizes[1] = 1024; @@ -7902,12 +7883,7 @@ static int __init niu_init(void) { int err = 0; - BUILD_BUG_ON((PAGE_SIZE < 4 * 1024) || - ((PAGE_SIZE > 32 * 1024) && - ((PAGE_SIZE % (32 * 1024)) != 0 && - (PAGE_SIZE % (16 * 1024)) != 0 && - (PAGE_SIZE % (8 * 1024)) != 0 && - (PAGE_SIZE % (4 * 1024)) != 0))); + BUILD_BUG_ON(PAGE_SIZE < 4 * 1024); niu_debug = netif_msg_init(debug, NIU_MSG_DEFAULT); -- cgit v1.2.2 From 4e3ab47a547616e583c7a5458beced6aa34c8ef3 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Sun, 21 Oct 2007 17:01:29 -0700 Subject: [NET]: Make and use skb_get_queue_mapping Make the helper for getting the field, symmetrical to the "set" one. Return 0 if CONFIG_NETDEVICES_MULTIQUEUE=n Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- drivers/net/cpmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index ed53aaab4c..ae41973615 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -471,7 +471,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) } len = max(skb->len, ETH_ZLEN); - queue = skb->queue_mapping; + queue = skb_get_queue_mapping(skb); #ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(dev, queue); #else -- cgit v1.2.2