Commit e55dbfb1 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://gkernel.bkbits.net/misc-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents bad53498 7ac1bc3e
......@@ -4,8 +4,8 @@
David S. Miller
This document is intended to serve as a guide to Linux port
maintainers on how to implement atomic counter and bitops interfaces
properly.
maintainers on how to implement atomic counter, bitops, and spinlock
interfaces properly.
The atomic_t type should be defined as a signed integer.
Also, it should be made opaque such that any kind of cast to a normal
......@@ -242,6 +242,19 @@ happen. Specifically, in the above case the atomic_dec_and_test()
counter decrement would not become globally visible until the
obj->active update does.
As a historical note, 32-bit Sparc used to only allow usage of
24-bits of it's atomic_t type. This was because it used 8 bits
as a spinlock for SMP safety. Sparc32 lacked a "compare and swap"
type instruction. However, 32-bit Sparc has since been moved over
to a "hash table of spinlocks" scheme, that allows the full 32-bit
counter to be realized. Essentially, an array of spinlocks are
indexed into based upon the address of the atomic_t being operated
on, and that lock protects the atomic operation. Parisc uses the
same scheme.
Another note is that the atomic_t operations returning values are
extremely slow on an old 386.
We will now cover the atomic bitmask operations. You will find that
their SMP and memory barrier semantics are similar in shape and scope
to the atomic_t ops above.
......@@ -345,3 +358,99 @@ except that two underscores are prefixed to the interface name.
These non-atomic variants also do not require any special memory
barrier semantics.
The routines xchg() and cmpxchg() need the same exact memory barriers
as the atomic and bit operations returning values.
Spinlocks and rwlocks have memory barrier expectations as well.
The rule to follow is simple:
1) When acquiring a lock, the implementation must make it globally
visible before any subsequent memory operation.
2) When releasing a lock, the implementation must make it such that
all previous memory operations are globally visible before the
lock release.
Which finally brings us to _atomic_dec_and_lock(). There is an
architecture-neutral version implemented in lib/dec_and_lock.c,
but most platforms will wish to optimize this in assembler.
int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock);
Atomically decrement the given counter, and if will drop to zero
atomically acquire the given spinlock and perform the decrement
of the counter to zero. If it does not drop to zero, do nothing
with the spinlock.
It is actually pretty simple to get the memory barrier correct.
Simply satisfy the spinlock grab requirements, which is make
sure the spinlock operation is globally visible before any
subsequent memory operation.
We can demonstrate this operation more clearly if we define
an abstract atomic operation:
long cas(long *mem, long old, long new);
"cas" stands for "compare and swap". It atomically:
1) Compares "old" with the value currently at "mem".
2) If they are equal, "new" is written to "mem".
3) Regardless, the current value at "mem" is returned.
As an example usage, here is what an atomic counter update
might look like:
void example_atomic_inc(long *counter)
{
long old, new, ret;
while (1) {
old = *counter;
new = old + 1;
ret = cas(counter, old, new);
if (ret == old)
break;
}
}
Let's use cas() in order to build a pseudo-C atomic_dec_and_lock():
int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
{
long old, new, ret;
int went_to_zero;
went_to_zero = 0;
while (1) {
old = atomic_read(atomic);
new = old - 1;
if (new == 0) {
went_to_zero = 1;
spin_lock(lock);
}
ret = cas(atomic, old, new);
if (ret == old)
break;
if (went_to_zero) {
spin_unlock(lock);
went_to_zero = 0;
}
}
return went_to_zero;
}
Now, as far as memory barriers go, as long as spin_lock()
strictly orders all subsequent memory operations (including
the cas()) with respect to itself, things will be fine.
Said another way, _atomic_dec_and_lock() must guarentee that
a counter dropping to zero is never made visible before the
spinlock being acquired.
Note that this also means that for the case where the counter
is not dropping to zero, there are no memory ordering
requirements.
......@@ -93,12 +93,12 @@ static u32 rcon_tab[RC_LENGTH];
u32 ft_tab[4][256];
u32 fl_tab[4][256];
u32 ls_tab[4][256];
u32 im_tab[4][256];
static u32 ls_tab[4][256];
static u32 im_tab[4][256];
u32 il_tab[4][256];
u32 it_tab[4][256];
void gen_tabs(void)
static void gen_tabs(void)
{
u32 i, w;
u8 pow[512], log[256];
......
......@@ -172,6 +172,7 @@ void _do_read_unlock (rwlock_t *rw, char *str)
runlock_again:
/* Spin trying to decrement the counter using casx. */
__asm__ __volatile__(
" membar #StoreLoad | #LoadLoad\n"
" ldx [%0], %%g5\n"
" sub %%g5, 1, %%g7\n"
" casx [%0], %%g5, %%g7\n"
......@@ -290,6 +291,7 @@ void _do_write_unlock(rwlock_t *rw)
current->thread.smp_lock_count--;
wlock_again:
__asm__ __volatile__(
" membar #StoreLoad | #LoadLoad\n"
" mov 1, %%g3\n"
" sllx %%g3, 63, %%g3\n"
" ldx [%0], %%g5\n"
......
......@@ -39,6 +39,7 @@
#include <linux/uio.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/wait.h>
#include <asm/system.h>
#include <asm/io.h>
......@@ -1089,13 +1090,11 @@ static inline void rx_bus_master_complete_handler (hrz_dev * dev) {
/********** (queue to) become the next TX thread **********/
static inline int tx_hold (hrz_dev * dev) {
while (test_and_set_bit (tx_busy, &dev->flags)) {
PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags);
interruptible_sleep_on (&dev->tx_queue);
wait_event_interruptible(dev->tx_queue, (!test_and_set_bit(tx_busy, &dev->flags)));
PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags);
if (signal_pending (current))
return -1;
}
PRINTD (DBG_TX, "set tx_busy for dev %p", dev);
return 0;
}
......
......@@ -53,6 +53,7 @@
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/atomic.h>
......@@ -2587,13 +2588,13 @@ static int __init ia_start(struct atm_dev *dev)
static void ia_close(struct atm_vcc *vcc)
{
DEFINE_WAIT(wait);
u16 *vc_table;
IADEV *iadev;
struct ia_vcc *ia_vcc;
struct sk_buff *skb = NULL;
struct sk_buff_head tmp_tx_backlog, tmp_vcc_backlog;
unsigned long closetime, flags;
int ctimeout;
iadev = INPH_IA_DEV(vcc->dev);
ia_vcc = INPH_IA_VCC(vcc);
......@@ -2606,7 +2607,9 @@ static void ia_close(struct atm_vcc *vcc)
skb_queue_head_init (&tmp_vcc_backlog);
if (vcc->qos.txtp.traffic_class != ATM_NONE) {
iadev->close_pending++;
sleep_on_timeout(&iadev->timeout_wait, 50);
prepare_to_wait(&iadev->timeout_wait, &wait, TASK_UNINTERRUPTIBLE);
schedule_timeout(50);
finish_wait(&iadev->timeout_wait, &wait);
spin_lock_irqsave(&iadev->tx_lock, flags);
while((skb = skb_dequeue(&iadev->tx_backlog))) {
if (ATM_SKB(skb)->vcc == vcc){
......@@ -2619,17 +2622,12 @@ static void ia_close(struct atm_vcc *vcc)
while((skb = skb_dequeue(&tmp_tx_backlog)))
skb_queue_tail(&iadev->tx_backlog, skb);
IF_EVENT(printk("IA TX Done decs_cnt = %d\n", ia_vcc->vc_desc_cnt);)
closetime = jiffies;
ctimeout = 300000 / ia_vcc->pcr;
if (ctimeout == 0)
ctimeout = 1;
while (ia_vcc->vc_desc_cnt > 0){
if ((jiffies - closetime) >= ctimeout)
break;
closetime = 300000 / ia_vcc->pcr;
if (closetime == 0)
closetime = 1;
spin_unlock_irqrestore(&iadev->tx_lock, flags);
sleep_on(&iadev->close_wait);
wait_event_timeout(iadev->close_wait, (ia_vcc->vc_desc_cnt <= 0), closetime);
spin_lock_irqsave(&iadev->tx_lock, flags);
}
iadev->close_pending--;
iadev->testTable[vcc->vci]->lastTime = 0;
iadev->testTable[vcc->vci]->fract = 0;
......
......@@ -22,6 +22,7 @@
#include <linux/atm_zatm.h>
#include <linux/capability.h>
#include <linux/bitops.h>
#include <linux/wait.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/string.h>
......@@ -867,31 +868,21 @@ static void close_tx(struct atm_vcc *vcc)
struct zatm_vcc *zatm_vcc;
unsigned long flags;
int chan;
struct sk_buff *skb;
int once = 1;
zatm_vcc = ZATM_VCC(vcc);
zatm_dev = ZATM_DEV(vcc->dev);
chan = zatm_vcc->tx_chan;
if (!chan) return;
DPRINTK("close_tx\n");
while (skb_peek(&zatm_vcc->backlog)) {
if (once) {
printk("waiting for backlog to drain ...\n");
event_dump();
once = 0;
}
sleep_on(&zatm_vcc->tx_wait);
}
once = 1;
while ((skb = skb_peek(&zatm_vcc->tx_queue))) {
if (once) {
printk("waiting for TX queue to drain ... %p\n",skb);
event_dump();
once = 0;
}
DPRINTK("waiting for TX queue to drain ... %p\n",skb);
sleep_on(&zatm_vcc->tx_wait);
if (skb_peek(&zatm_vcc->backlog)) {
printk("waiting for backlog to drain ...\n");
event_dump();
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->backlog));
}
if (skb_peek(&zatm_vcc->tx_queue)) {
printk("waiting for TX queue to drain ...\n");
event_dump();
wait_event(zatm_vcc->tx_wait, !skb_peek(&zatm_vcc->tx_queue));
}
spin_lock_irqsave(&zatm_dev->lock, flags);
#if 0
......
......@@ -75,6 +75,17 @@ config BT_HCIBCM203X
Say Y here to compile support for HCI BCM203x devices into the
kernel or say M to compile it as module (bcm203x).
config BT_HCIBPA10X
tristate "HCI BPA10x USB driver"
depends on USB
help
Bluetooth HCI BPA10x USB driver.
This driver provides support for the Digianswer BPA 100/105 Bluetooth
sniffer devices.
Say Y here to compile support for HCI BPA10x devices into the
kernel or say M to compile it as module (bpa10x).
config BT_HCIBFUSB
tristate "HCI BlueFRITZ! USB driver"
depends on USB
......
......@@ -6,6 +6,7 @@ obj-$(CONFIG_BT_HCIUSB) += hci_usb.o
obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
obj-$(CONFIG_BT_HCIUART) += hci_uart.o
obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
......
/*
*
* Digianswer Bluetooth USB driver
*
* Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#ifndef CONFIG_BT_HCIBPA10X_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
#define VERSION "0.8"
static int ignore = 0;
static struct usb_device_id bpa10x_table[] = {
/* Tektronix BPA 100/105 (Digianswer) */
{ USB_DEVICE(0x08fd, 0x0002) },
{ } /* Terminating entry */
};
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;
rwlock_t lock;
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;
};
#define HCI_VENDOR_HDR_SIZE 5
struct hci_vendor_hdr {
__u8 type;
__u16 snum;
__u16 dlen;
} __attribute__ ((packed));
static void bpa10x_recv_bulk(struct bpa10x_data *data, unsigned char *buf, int count)
{
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
struct hci_vendor_hdr *vh;
struct sk_buff *skb;
int len;
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;
skb->pkt_type = HCI_ACLDATA_PKT;
hci_recv_frame(skb);
}
break;
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;
skb->pkt_type = HCI_SCODATA_PKT;
hci_recv_frame(skb);
}
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;
skb->pkt_type = HCI_VENDOR_PKT;
hci_recv_frame(skb);
}
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);
if (data->evt_skb) {
struct sk_buff *skb = data->evt_skb;
memcpy(skb_put(skb, size), buf, size);
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;
}
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;
}
hdr = (struct hci_event_hdr *) buf;
pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
if (!skb) {
BT_ERR("%s no memory for new event packet",
data->hdev->name);
return -ENOMEM;
}
skb->dev = (void *) data->hdev;
skb->pkt_type = pkt_type;
memcpy(skb_put(skb, size), buf, size);
if (pkt_len == size) {
hci_recv_frame(skb);
} else {
data->evt_skb = skb;
data->evt_len = pkt_len;
}
}
return 0;
}
static void bpa10x_wakeup(struct bpa10x_data *data)
{
struct urb *urb;
struct sk_buff *skb;
int err;
BT_DBG("data %p", data);
urb = data->cmd_urb;
if (urb->status == -EINPROGRESS)
skb = NULL;
else
skb = skb_dequeue(&data->cmd_queue);
if (skb) {
struct usb_ctrlrequest *cr;
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;
}
cr = (struct usb_ctrlrequest *) urb->setup_packet;
cr->wLength = __cpu_to_le16(skb->len);
memcpy(urb->transfer_buffer, skb->data, 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->cmd_queue, skb);
} else
kfree_skb(skb);
}
urb = data->tx_urb;
if (urb->status == -EINPROGRESS)
skb = NULL;
else
skb = skb_dequeue(&data->tx_queue);
if (skb) {
memcpy(urb->transfer_buffer, skb->data, 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);
}
}
static void bpa10x_complete(struct urb *urb, struct pt_regs *regs)
{
struct bpa10x_data *data = urb->context;
unsigned char *buf = urb->transfer_buffer;
int err, count = urb->actual_length;
BT_DBG("data %p urb %p buf %p count %d", data, urb, buf, count);
read_lock(&data->lock);
if (!test_bit(HCI_RUNNING, &data->hdev->flags))
goto unlock;
if (urb->status < 0 || !count)
goto resubmit;
if (usb_pipein(urb->pipe)) {
data->hdev->stat.byte_rx += count;
if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)
bpa10x_recv_event(data, buf, count);
if (usb_pipetype(urb->pipe) == PIPE_BULK)
bpa10x_recv_bulk(data, buf, count);
} else {
data->hdev->stat.byte_tx += count;
bpa10x_wakeup(data);
}
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);
}
}
unlock:
read_unlock(&data->lock);
}
static inline struct urb *bpa10x_alloc_urb(struct usb_device *udev, unsigned int pipe, size_t size, int flags, void *data)
{
struct urb *urb;
struct usb_ctrlrequest *cr;
unsigned char *buf;
BT_DBG("udev %p data %p", udev, data);
urb = usb_alloc_urb(0, flags);
if (!urb)
return NULL;
buf = kmalloc(size, flags);
if (!buf) {
usb_free_urb(urb);
return NULL;
}
switch (usb_pipetype(pipe)) {
case PIPE_CONTROL:
cr = kmalloc(sizeof(*cr), flags);
if (!cr) {
kfree(buf);
usb_free_urb(urb);
return NULL;
}
cr->bRequestType = USB_TYPE_VENDOR;
cr->bRequest = 0;
cr->wIndex = 0;
cr->wValue = 0;
cr->wLength = __cpu_to_le16(0);
usb_fill_control_urb(urb, udev, pipe, (void *) cr, buf, 0, bpa10x_complete, data);
break;
case PIPE_INTERRUPT:
usb_fill_int_urb(urb, udev, pipe, buf, size, bpa10x_complete, data, 1);
break;
case PIPE_BULK:
usb_fill_bulk_urb(urb, udev, pipe, buf, size, bpa10x_complete, data);
break;
default:
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;
if (urb->setup_packet)
kfree(urb->setup_packet);
if (urb->transfer_buffer)
kfree(urb->transfer_buffer);
usb_free_urb(urb);
}
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);
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;
}
write_lock_irqsave(&data->lock, flags);
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);
}
}
write_unlock_irqrestore(&data->lock, flags);
done:
if (err < 0)
clear_bit(HCI_RUNNING, &hdev->flags);
return err;
}
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);
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);
return 0;
}
static int bpa10x_flush(struct hci_dev *hdev)
{
struct bpa10x_data *data = hdev->driver_data;
BT_DBG("hdev %p data %p", hdev, data);
skb_queue_purge(&data->cmd_queue);
return 0;
}
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, skb->pkt_type, skb->len);
if (!hdev) {
BT_ERR("Frame for unknown HCI device");
return -ENODEV;
}
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
data = hdev->driver_data;
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
switch (skb->pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
skb_queue_tail(&data->cmd_queue, skb);
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
skb_queue_tail(&data->tx_queue, skb);
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
skb_queue_tail(&data->tx_queue, skb);
break;
};
read_lock(&data->lock);
bpa10x_wakeup(data);
read_unlock(&data->lock);
return 0;
}
static void bpa10x_destruct(struct hci_dev *hdev)
{
struct bpa10x_data *data = hdev->driver_data;
BT_DBG("hdev %p data %p", hdev, data);
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;
int err;
BT_DBG("intf %p id %p", intf, id);
if (ignore)
return -ENODEV;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
BT_ERR("Can't allocate data structure");
return -ENOMEM;
}
memset(data, 0, sizeof(*data));
data->udev = udev;
rwlock_init(&data->lock);
skb_queue_head_init(&data->cmd_queue);
skb_queue_head_init(&data->tx_queue);
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;
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->owner = THIS_MODULE;
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("Can't register HCI device");
kfree(data);
hci_free_dev(hdev);
return err;
}
usb_set_intfdata(intf, data);
return 0;
}
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)
return;
usb_set_intfdata(intf, NULL);
if (hci_unregister_dev(hdev) < 0)
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev);
}
static struct usb_driver bpa10x_driver = {
.owner = THIS_MODULE,
.name = "bpa10x",
.probe = bpa10x_probe,
.disconnect = bpa10x_disconnect,
.id_table = bpa10x_table,
};
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;
}
static void __exit bpa10x_exit(void)
{
usb_deregister(&bpa10x_driver);
}
module_init(bpa10x_init);
module_exit(bpa10x_exit);
module_param(ignore, bool, 0644);
MODULE_PARM_DESC(ignore, "Ignore devices from the matching table");
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
......@@ -73,7 +73,7 @@ static int reset = 0;
static int isoc = 2;
#endif
#define VERSION "2.7"
#define VERSION "2.8"
static struct usb_driver hci_usb_driver;
......@@ -104,11 +104,11 @@ static struct usb_device_id blacklist_ids[] = {
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = HCI_IGNORE },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_RESET | HCI_BROKEN_ISOC },
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = HCI_RESET | HCI_BROKEN_ISOC },
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_BCM92035 },
/* Microsoft Wireless Transceiver for Bluetooth 2.0 */
{ USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_RESET | HCI_BROKEN_ISOC },
{ USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_BCM92035 },
/* ISSC Bluetooth Adapter v3.1 */
{ USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },
......@@ -977,6 +977,17 @@ static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
}
if (id->driver_info & HCI_BCM92035) {
unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
struct sk_buff *skb;
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
if (skb) {
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
skb_queue_tail(&hdev->driver_init, skb);
}
}
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
hci_free_dev(hdev);
......
......@@ -33,6 +33,7 @@
#define HCI_DIGIANSWER 0x04
#define HCI_SNIFFER 0x08
#define HCI_BROKEN_ISOC 0x10
#define HCI_BCM92035 0x20
#define HCI_MAX_IFACE_NUM 3
......
......@@ -171,12 +171,13 @@ static void inline __read_unlock(rwlock_t *lock)
unsigned long tmp1, tmp2;
__asm__ __volatile__(
" membar #StoreLoad | #LoadLoad\n"
"1: lduw [%2], %0\n"
" sub %0, 1, %1\n"
" cas [%2], %0, %1\n"
" cmp %0, %1\n"
" bne,pn %%xcc, 1b\n"
" membar #StoreLoad | #StoreStore"
" nop"
: "=&r" (tmp1), "=&r" (tmp2)
: "r" (lock)
: "memory");
......
......@@ -229,6 +229,7 @@ do { if (test_thread_flag(TIF_PERFCTR)) { \
static __inline__ unsigned long xchg32(__volatile__ unsigned int *m, unsigned int val)
{
__asm__ __volatile__(
" membar #StoreLoad | #LoadLoad\n"
" mov %0, %%g5\n"
"1: lduw [%2], %%g7\n"
" cas [%2], %%g7, %0\n"
......@@ -245,6 +246,7 @@ static __inline__ unsigned long xchg32(__volatile__ unsigned int *m, unsigned in
static __inline__ unsigned long xchg64(__volatile__ unsigned long *m, unsigned long val)
{
__asm__ __volatile__(
" membar #StoreLoad | #LoadLoad\n"
" mov %0, %%g5\n"
"1: ldx [%2], %%g7\n"
" casx [%2], %%g7, %0\n"
......@@ -289,7 +291,8 @@ extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noret
static __inline__ unsigned long
__cmpxchg_u32(volatile int *m, int old, int new)
{
__asm__ __volatile__("cas [%2], %3, %0\n\t"
__asm__ __volatile__("membar #StoreLoad | #LoadLoad\n"
"cas [%2], %3, %0\n\t"
"membar #StoreLoad | #StoreStore"
: "=&r" (new)
: "0" (new), "r" (m), "r" (old)
......@@ -301,7 +304,8 @@ __cmpxchg_u32(volatile int *m, int old, int new)
static __inline__ unsigned long
__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
{
__asm__ __volatile__("casx [%2], %3, %0\n\t"
__asm__ __volatile__("membar #StoreLoad | #LoadLoad\n"
"casx [%2], %3, %0\n\t"
"membar #StoreLoad | #StoreStore"
: "=&r" (new)
: "0" (new), "r" (m), "r" (old)
......
......@@ -41,6 +41,7 @@ struct ip_ct_tcp
u_int8_t retrans; /* Number of retransmitted packets */
u_int8_t last_index; /* Index of the last packet */
u_int32_t last_seq; /* Last sequence number seen in dir */
u_int32_t last_ack; /* Last sequence number seen in opposite dir */
u_int32_t last_end; /* Last seq + len */
};
......
......@@ -64,10 +64,10 @@ struct ip_conntrack_tuple
} u;
/* The protocol. */
u8 protonum;
u_int8_t protonum;
/* The direction (for tuplehash) */
u8 dir;
u_int8_t dir;
} dst;
};
......
......@@ -133,10 +133,9 @@ int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
/*
* skb should fit one page. This choice is good for headerless malloc.
*
* FIXME: What is the best size for SLAB???? --ANK
*/
#define NLMSG_GOODSIZE (PAGE_SIZE - ((sizeof(struct sk_buff)+0xF)&~0xF))
#define NLMSG_GOODORDER 0
#define NLMSG_GOODSIZE (SKB_MAX_ORDER(0, NLMSG_GOODORDER))
struct netlink_callback
......
......@@ -119,6 +119,8 @@ struct hci_dev {
struct hci_dev_stats stat;
struct sk_buff_head driver_init;
void *driver_data;
void *core_data;
......
......@@ -183,10 +183,22 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
{
struct sk_buff *skb;
__u16 param;
BT_DBG("%s %ld", hdev->name, opt);
/* Driver initialization */
/* Special commands */
while ((skb = skb_dequeue(&hdev->driver_init))) {
skb->pkt_type = HCI_COMMAND_PKT;
skb->dev = (void *) hdev;
skb_queue_tail(&hdev->cmd_q, skb);
hci_sched_cmd(hdev);
}
skb_queue_purge(&hdev->driver_init);
/* Mandatory initialization */
/* Reset */
......@@ -792,6 +804,8 @@ struct hci_dev *hci_alloc_dev(void)
memset(hdev, 0, sizeof(struct hci_dev));
skb_queue_head_init(&hdev->driver_init);
return hdev;
}
EXPORT_SYMBOL(hci_alloc_dev);
......@@ -799,6 +813,8 @@ EXPORT_SYMBOL(hci_alloc_dev);
/* Free HCI device */
void hci_free_dev(struct hci_dev *hdev)
{
skb_queue_purge(&hdev->driver_init);
/* will free via class release */
class_device_put(&hdev->class_dev);
}
......
......@@ -1232,7 +1232,7 @@ u32 __init root_nfs_parse_addr(char *name)
if (*cp == ':')
*cp++ = '\0';
addr = in_aton(name);
strcpy(name, cp);
memmove(name, cp, strlen(cp) + 1);
} else
addr = INADDR_NONE;
......
......@@ -373,9 +373,8 @@ static int help(struct sk_buff **pskb,
goto out_update_nl;
}
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
(int)matchlen, data + matchoff,
matchlen, ntohl(th->seq) + matchoff);
DEBUGP("conntrack_ftp: match `%s' (%u bytes at %u)\n",
fb_ptr + matchoff, matchlen, ntohl(th->seq) + matchoff);
/* Allocate expectation which will be inserted */
exp = ip_conntrack_expect_alloc();
......
......@@ -665,11 +665,13 @@ static int tcp_in_window(struct ip_ct_tcp *state,
if (*index == TCP_ACK_SET) {
if (state->last_dir == dir
&& state->last_seq == seq
&& state->last_ack == ack
&& state->last_end == end)
state->retrans++;
else {
state->last_dir = dir;
state->last_seq = seq;
state->last_ack = ack;
state->last_end = end;
state->retrans = 0;
}
......
......@@ -543,6 +543,7 @@ int __init ip_nat_init(void)
static int clean_nat(struct ip_conntrack *i, void *data)
{
memset(&i->nat, 0, sizeof(i->nat));
i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK | IPS_SEQ_ADJUST);
return 0;
}
......
......@@ -570,7 +570,7 @@ static void *dl_seq_start(struct seq_file *s, loff_t *pos)
if (*pos >= htable->cfg.size)
return NULL;
bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
bucket = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
if (!bucket)
return ERR_PTR(-ENOMEM);
......
......@@ -180,10 +180,10 @@ ip6ip6_tnl_link(struct ip6_tnl *t)
{
struct ip6_tnl **tp = ip6ip6_bucket(&t->parms);
write_lock_bh(&ip6ip6_lock);
t->next = *tp;
write_unlock_bh(&ip6ip6_lock);
write_lock_bh(&ip6ip6_lock);
*tp = t;
write_unlock_bh(&ip6ip6_lock);
}
/**
......
......@@ -207,6 +207,11 @@ tcf_ipt(struct sk_buff **pskb, struct tc_action *a)
struct tcf_ipt *p = PRIV(a, ipt);
struct sk_buff *skb = *pskb;
if (skb_cloned(skb)) {
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
return TC_ACT_UNSPEC;
}
spin_lock(&p->lock);
p->tm.lastuse = jiffies;
......
......@@ -177,6 +177,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
if (q->loss && q->loss >= get_crandom(&q->loss_cor)) {
pr_debug("netem_enqueue: random loss\n");
sch->qstats.drops++;
kfree_skb(skb);
return 0; /* lie about loss so TCP doesn't know */
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment