summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.osdl.org>2004-01-18 01:22:30 -0800
committerLinus Torvalds <torvalds@home.osdl.org>2004-01-18 01:22:30 -0800
commit55b5b9683a59a854ab2b71632c19283a24cf5668 (patch)
tree9f7e2c9a2169d2fff49197300b3423b4cfacc831
parent755fa12bb5387c7866f641504424d146c9b62f58 (diff)
parentd9189b24bd2bbb9237589ef530f34c96913b985d (diff)
Merge bk://kernel.bkbits.net/davem/net-2.6
into home.osdl.org:/home/torvalds/v2.5/linux
-rw-r--r--CREDITS2
-rw-r--r--Documentation/networking/ip-sysctl.txt7
-rw-r--r--MAINTAINERS29
-rw-r--r--drivers/atm/atmdev_init.c6
-rw-r--r--drivers/atm/nicstar.c253
-rw-r--r--drivers/bluetooth/Kconfig47
-rw-r--r--drivers/bluetooth/Makefile2
-rw-r--r--drivers/bluetooth/bcm203x.c309
-rw-r--r--drivers/bluetooth/bfusb.c782
-rw-r--r--drivers/bluetooth/bluecard_cs.c8
-rw-r--r--drivers/bluetooth/bt3c_cs.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c6
-rw-r--r--drivers/bluetooth/dtl1_cs.c6
-rw-r--r--drivers/bluetooth/hci_usb.c62
-rw-r--r--drivers/bluetooth/hci_usb.h9
-rw-r--r--drivers/net/hamradio/bpqether.c25
-rw-r--r--drivers/net/wan/lapbether.c10
-rw-r--r--fs/compat_ioctl.c57
-rw-r--r--include/linux/compat_ioctl.h32
-rw-r--r--include/linux/ipv6.h4
-rw-r--r--include/linux/rtnetlink.h47
-rw-r--r--include/linux/sysctl.h3
-rw-r--r--include/net/addrconf.h6
-rw-r--r--include/net/bluetooth/bluetooth.h3
-rw-r--r--include/net/bluetooth/hci.h10
-rw-r--r--include/net/bluetooth/hci_core.h14
-rw-r--r--include/net/bluetooth/rfcomm.h9
-rw-r--r--include/net/flow.h1
-rw-r--r--include/net/if_inet6.h5
-rw-r--r--include/net/ipv6.h4
-rw-r--r--include/net/irda/ircomm_tty.h3
-rw-r--r--include/net/ndisc.h11
-rw-r--r--include/net/neighbour.h10
-rw-r--r--include/net/sock.h38
-rw-r--r--net/atm/clip.c5
-rw-r--r--net/atm/common.c21
-rw-r--r--net/bluetooth/Kconfig3
-rw-r--r--net/bluetooth/Makefile1
-rw-r--r--net/bluetooth/af_bluetooth.c2
-rw-r--r--net/bluetooth/bnep/core.c1
-rw-r--r--net/bluetooth/cmtp/Kconfig11
-rw-r--r--net/bluetooth/cmtp/Makefile7
-rw-r--r--net/bluetooth/cmtp/capi.c630
-rw-r--r--net/bluetooth/cmtp/cmtp.h136
-rw-r--r--net/bluetooth/cmtp/core.c507
-rw-r--r--net/bluetooth/cmtp/sock.c199
-rw-r--r--net/bluetooth/hci_conn.c2
-rw-r--r--net/bluetooth/hci_core.c1
-rw-r--r--net/bluetooth/hci_event.c27
-rw-r--r--net/bluetooth/hci_sock.c12
-rw-r--r--net/bluetooth/l2cap.c1
-rw-r--r--net/bluetooth/rfcomm/core.c63
-rw-r--r--net/bluetooth/sco.c1
-rw-r--r--net/bridge/br_if.c4
-rw-r--r--net/core/dev.c91
-rw-r--r--net/core/dev_mcast.c94
-rw-r--r--net/core/neighbour.c7
-rw-r--r--net/decnet/af_decnet.c11
-rw-r--r--net/ipv4/arp.c2
-rw-r--r--net/ipv4/devinet.c4
-rw-r--r--net/ipv4/igmp.c3
-rw-r--r--net/ipv4/ipcomp.c1
-rw-r--r--net/ipv4/ipmr.c3
-rw-r--r--net/ipv4/netfilter/Kconfig13
-rw-r--r--net/ipv6/addrconf.c191
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/ipv6_sockglue.c18
-rw-r--r--net/ipv6/mcast.c2
-rw-r--r--net/ipv6/ndisc.c22
-rw-r--r--net/irda/ircomm/ircomm_tty.c4
-rw-r--r--net/irda/ircomm/ircomm_tty_ioctl.c75
-rw-r--r--net/key/af_key.c4
-rw-r--r--net/packet/af_packet.c13
-rw-r--r--net/x25/af_x25.c19
-rw-r--r--net/xfrm/xfrm_algo.c2
-rw-r--r--net/xfrm/xfrm_policy.c27
-rw-r--r--net/xfrm/xfrm_user.c1
77 files changed, 3540 insertions, 529 deletions
diff --git a/CREDITS b/CREDITS
index 9d04104c1e1c..13e01cb8138e 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1394,7 +1394,9 @@ S: USA
N: Marcel Holtmann
E: marcel@holtmann.org
W: http://www.holtmann.org
+D: Maintainer of the Linux Bluetooth Subsystem
D: Author and maintainer of the various Bluetooth HCI drivers
+D: Author and maintainer of the CAPI message transport protocol driver
D: Various other Bluetooth related patches, cleanups and fixes
S: Germany
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 030a5b67313a..f2b360b5ef0e 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -667,6 +667,13 @@ regen_max_retry - INTEGER
valid temporary addresses.
Default: 5
+max_addresses - INTEGER
+ Number of maximum addresses per interface. 0 disables limitation.
+ It is recommended not set too large value (or 0) because it would
+ be too easy way to crash kernel to allow to create too much of
+ autoconfigured addresses.
+ Default: 16
+
icmp/*:
ratelimit - INTEGER
Limit the maximal rates for sending ICMPv6 packets.
diff --git a/MAINTAINERS b/MAINTAINERS
index 69753c92f331..8e94a100dc70 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -338,35 +338,64 @@ L: linux-kernel@vger.kernel.org
S: Maintained
BLUETOOTH SUBSYSTEM
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
+L: bluez-devel@lists.sf.net
W: http://bluez.sf.net
S: Maintained
BLUETOOTH RFCOMM LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
BLUETOOTH BNEP LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
+BLUETOOTH CMTP LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+S: Maintained
+
BLUETOOTH HCI USB DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
BLUETOOTH HCI UART DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
+BLUETOOTH HCI BCM203X DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+S: Maintained
+
+BLUETOOTH HCI BFUSB DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+S: Maintained
+
BLUETOOTH HCI DTL1 DRIVER
P: Marcel Holtmann
M: marcel@holtmann.org
diff --git a/drivers/atm/atmdev_init.c b/drivers/atm/atmdev_init.c
index c374dcfea77a..0e09e5c28e3f 100644
--- a/drivers/atm/atmdev_init.c
+++ b/drivers/atm/atmdev_init.c
@@ -10,9 +10,6 @@
#ifdef CONFIG_ATM_ZATM
extern int zatm_detect(void);
#endif
-#ifdef CONFIG_ATM_NICSTAR
-extern int nicstar_detect(void);
-#endif
#ifdef CONFIG_ATM_AMBASSADOR
extern int amb_detect(void);
#endif
@@ -41,9 +38,6 @@ int __init atmdev_init(void)
#ifdef CONFIG_ATM_ZATM
devs += zatm_detect();
#endif
-#ifdef CONFIG_ATM_NICSTAR
- devs += nicstar_detect();
-#endif
#ifdef CONFIG_ATM_AMBASSADOR
devs += amb_detect();
#endif
diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c
index 2473dc58e52a..59028ad6c317 100644
--- a/drivers/atm/nicstar.c
+++ b/drivers/atm/nicstar.c
@@ -214,8 +214,8 @@
static u32 ns_read_sram(ns_dev *card, u32 sram_address);
static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count);
-static int __init ns_init_card(int i, struct pci_dev *pcidev);
-static void __init ns_init_card_error(ns_dev *card, int error);
+static int __devinit ns_init_card(int i, struct pci_dev *pcidev);
+static void __devinit ns_init_card_error(ns_dev *card, int error);
static scq_info *get_scq(int size, u32 scd);
static void free_scq(scq_info *scq, struct atm_vcc *vcc);
static void push_rxbufs(ns_dev *card, u32 type, u32 handle1, u32 addr1,
@@ -276,136 +276,151 @@ MODULE_LICENSE("GPL");
/* Functions*******************************************************************/
-static int __init nicstar_module_init(void)
+static int __devinit nicstar_init_one(struct pci_dev *pcidev,
+ const struct pci_device_id *ent)
{
- int i;
- unsigned error = 0; /* Initialized to remove compile warning */
- struct pci_dev *pcidev;
+ static int index = -1;
+ unsigned int error;
- XPRINTK("nicstar: nicstar_module_init() called.\n");
+ index++;
+ cards[index] = NULL;
- for(i = 0; i < NS_MAX_CARDS; i++)
- cards[i] = NULL;
-
- pcidev = NULL;
- for(i = 0; i < NS_MAX_CARDS; i++)
- {
- if ((pcidev = pci_find_device(PCI_VENDOR_ID_IDT,
- PCI_DEVICE_ID_IDT_IDT77201,
- pcidev)) == NULL)
- break;
-
- error = ns_init_card(i, pcidev);
- if (error)
- cards[i--] = NULL; /* Try to find another card but don't increment index */
+ error = ns_init_card(index, pcidev);
+ if (error) {
+ cards[index--] = NULL; /* don't increment index */
+ goto err_out;
}
- if (i == 0)
- {
- if (!error)
- {
- printk("nicstar: no cards found.\n");
- return -ENXIO;
- }
- else
- return -EIO;
- }
- TXPRINTK("nicstar: TX debug enabled.\n");
- RXPRINTK("nicstar: RX debug enabled.\n");
- PRINTK("nicstar: General debug enabled.\n");
-#ifdef PHY_LOOPBACK
- printk("nicstar: using PHY loopback.\n");
-#endif /* PHY_LOOPBACK */
- XPRINTK("nicstar: nicstar_module_init() returned.\n");
-
- init_timer(&ns_timer);
- ns_timer.expires = jiffies + NS_POLL_PERIOD;
- ns_timer.data = 0UL;
- ns_timer.function = ns_poll;
- add_timer(&ns_timer);
return 0;
+err_out:
+ return -ENODEV;
}
-static void __exit nicstar_module_exit(void)
+static void __devexit nicstar_remove_one(struct pci_dev *pcidev)
{
int i, j;
- unsigned short pci_command;
- ns_dev *card;
+ ns_dev *card = pci_get_drvdata(pcidev);
struct sk_buff *hb;
struct sk_buff *iovb;
struct sk_buff *lb;
struct sk_buff *sb;
- XPRINTK("nicstar: cleanup_module() called.\n");
+ i = card->index;
- del_timer(&ns_timer);
+ if (cards[i] == NULL)
+ return;
- for (i = 0; i < NS_MAX_CARDS; i++)
+ if (card->atmdev->phy && card->atmdev->phy->stop)
+ card->atmdev->phy->stop(card->atmdev);
+
+ /* Stop everything */
+ writel(0x00000000, card->membase + CFG);
+
+ /* De-register device */
+ atm_dev_deregister(card->atmdev);
+
+ /* Disable PCI device */
+ pci_disable_device(pcidev);
+
+ /* Free up resources */
+ j = 0;
+ PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
+ while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
{
- if (cards[i] == NULL)
- continue;
+ dev_kfree_skb_any(hb);
+ j++;
+ }
+ PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
+ j = 0;
+ PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, card->iovpool.count);
+ while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
+ {
+ dev_kfree_skb_any(iovb);
+ j++;
+ }
+ PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
+ while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
+ dev_kfree_skb_any(lb);
+ while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
+ dev_kfree_skb_any(sb);
+ free_scq(card->scq0, NULL);
+ for (j = 0; j < NS_FRSCD_NUM; j++)
+ {
+ if (card->scd2vc[j] != NULL)
+ free_scq(card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
+ }
+ kfree(card->rsq.org);
+ kfree(card->tsq.org);
+ free_irq(card->pcidev->irq, card);
+ iounmap((void *) card->membase);
+ kfree(card);
+}
- card = cards[i];
- if (card->atmdev->phy && card->atmdev->phy->stop)
- card->atmdev->phy->stop(card->atmdev);
- /* Stop everything */
- writel(0x00000000, card->membase + CFG);
+static struct pci_device_id nicstar_pci_tbl[] __devinitdata =
+{
+ {PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_IDT_IDT77201,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,} /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, nicstar_pci_tbl);
- /* De-register device */
- atm_dev_deregister(card->atmdev);
- /* Disable memory mapping and busmastering */
- if (pci_read_config_word(card->pcidev, PCI_COMMAND, &pci_command) != 0)
- {
- printk("nicstar%d: can't read PCI_COMMAND.\n", i);
- }
- pci_command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- if (pci_write_config_word(card->pcidev, PCI_COMMAND, pci_command) != 0)
- {
- printk("nicstar%d: can't write PCI_COMMAND.\n", i);
- }
-
- /* Free up resources */
- j = 0;
- PRINTK("nicstar%d: freeing %d huge buffers.\n", i, card->hbpool.count);
- while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
- {
- dev_kfree_skb_any(hb);
- j++;
- }
- PRINTK("nicstar%d: %d huge buffers freed.\n", i, j);
- j = 0;
- PRINTK("nicstar%d: freeing %d iovec buffers.\n", i, card->iovpool.count);
- while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
- {
- dev_kfree_skb_any(iovb);
- j++;
- }
- PRINTK("nicstar%d: %d iovec buffers freed.\n", i, j);
- while ((lb = skb_dequeue(&card->lbpool.queue)) != NULL)
- dev_kfree_skb_any(lb);
- while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
- dev_kfree_skb_any(sb);
- free_scq(card->scq0, NULL);
- for (j = 0; j < NS_FRSCD_NUM; j++)
- {
- if (card->scd2vc[j] != NULL)
- free_scq(card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc);
- }
- kfree(card->rsq.org);
- kfree(card->tsq.org);
- free_irq(card->pcidev->irq, card);
- iounmap((void *) card->membase);
- kfree(card);
-
+
+static struct pci_driver nicstar_driver = {
+ .name = "nicstar",
+ .id_table = nicstar_pci_tbl,
+ .probe = nicstar_init_one,
+ .remove = __devexit_p(nicstar_remove_one),
+};
+
+
+
+static int __init nicstar_init(void)
+{
+ unsigned error = 0; /* Initialized to remove compile warning */
+
+ XPRINTK("nicstar: nicstar_init() called.\n");
+
+ error = pci_module_init(&nicstar_driver);
+
+ TXPRINTK("nicstar: TX debug enabled.\n");
+ RXPRINTK("nicstar: RX debug enabled.\n");
+ PRINTK("nicstar: General debug enabled.\n");
+#ifdef PHY_LOOPBACK
+ printk("nicstar: using PHY loopback.\n");
+#endif /* PHY_LOOPBACK */
+ XPRINTK("nicstar: nicstar_init() returned.\n");
+
+ if (!error) {
+ init_timer(&ns_timer);
+ ns_timer.expires = jiffies + NS_POLL_PERIOD;
+ ns_timer.data = 0UL;
+ ns_timer.function = ns_poll;
+ add_timer(&ns_timer);
}
- XPRINTK("nicstar: cleanup_module() returned.\n");
+
+ return error;
+}
+
+
+
+static void __exit nicstar_cleanup(void)
+{
+ XPRINTK("nicstar: nicstar_cleanup() called.\n");
+
+ del_timer(&ns_timer);
+
+ pci_unregister_driver(&nicstar_driver);
+
+ XPRINTK("nicstar: nicstar_cleanup() returned.\n");
}
+
+
static u32 ns_read_sram(ns_dev *card, u32 sram_address)
{
unsigned long flags;
@@ -445,11 +460,10 @@ static void ns_write_sram(ns_dev *card, u32 sram_address, u32 *value, int count)
}
-static int __init ns_init_card(int i, struct pci_dev *pcidev)
+static int __devinit ns_init_card(int i, struct pci_dev *pcidev)
{
int j;
struct ns_dev *card = NULL;
- unsigned short pci_command;
unsigned char pci_latency;
unsigned error;
u32 data;
@@ -478,6 +492,8 @@ static int __init ns_init_card(int i, struct pci_dev *pcidev)
spin_lock_init(&card->int_lock);
spin_lock_init(&card->res_lock);
+ pci_set_drvdata(pcidev, card);
+
card->index = i;
card->atmdev = NULL;
card->pcidev = pcidev;
@@ -492,21 +508,7 @@ static int __init ns_init_card(int i, struct pci_dev *pcidev)
}
PRINTK("nicstar%d: membase at 0x%x.\n", i, card->membase);
- if (pci_read_config_word(pcidev, PCI_COMMAND, &pci_command) != 0)
- {
- printk("nicstar%d: can't read PCI_COMMAND.\n", i);
- error = 4;
- ns_init_card_error(card, error);
- return error;
- }
- pci_command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- if (pci_write_config_word(pcidev, PCI_COMMAND, pci_command) != 0)
- {
- printk("nicstar%d: can't write PCI_COMMAND.\n", i);
- error = 5;
- ns_init_card_error(card, error);
- return error;
- }
+ pci_set_master(pcidev);
if (pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency) != 0)
{
@@ -932,7 +934,7 @@ static int __init ns_init_card(int i, struct pci_dev *pcidev)
-static void __init ns_init_card_error(ns_dev *card, int error)
+static void __devinit ns_init_card_error(ns_dev *card, int error)
{
if (error >= 17)
{
@@ -981,6 +983,7 @@ static void __init ns_init_card_error(ns_dev *card, int error)
}
if (error >= 3)
{
+ pci_disable_device(card->pcidev);
kfree(card);
}
}
@@ -3099,5 +3102,7 @@ static unsigned char ns_phy_get(struct atm_dev *dev, unsigned long addr)
return (unsigned char) data;
}
-module_init(nicstar_module_init);
-module_exit(nicstar_module_exit);
+
+
+module_init(nicstar_init);
+module_exit(nicstar_cleanup);
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 30d099c33aee..5ad7ea2b6bc6 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -13,22 +13,14 @@ config BT_HCIUSB
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (hci_usb).
-config BT_USB_SCO
- bool "SCO over HCI USB support"
+config BT_HCIUSB_SCO
+ bool "SCO (voice) support"
depends on BT_HCIUSB
help
This option enables the SCO support in the HCI USB driver. You need this
- to transmit voice data with your Bluetooth USB device.
- Say Y here to compile support for SCO over HCI USB.
+ to transmit voice data with your Bluetooth USB device.
-config BT_USB_ZERO_PACKET
- bool "USB zero packet support"
- depends on BT_HCIUSB
- help
- This option is provided only as a work around for buggy Bluetooth USB
- devices. Do _not_ enable it unless you know for sure that your device
- requires zero packets.
- Most people should say N here.
+ Say Y here to compile support for SCO over HCI USB.
config BT_HCIUART
tristate "HCI UART driver"
@@ -65,13 +57,38 @@ config BT_HCIUART_BCSP
Say Y here to compile support for HCI BCSP protocol.
config BT_HCIUART_BCSP_TXCRC
- bool "Transmit CRC with every BCSP packet"
- depends on BT_HCIUART_BCSP
- help
+ bool "Transmit CRC with every BCSP packet"
+ depends on BT_HCIUART_BCSP
+ help
If you say Y here, a 16-bit CRC checksum will be transmitted along with
every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip.
This increases reliability, but slightly reduces efficiency.
+config BT_HCIBCM203X
+ tristate "HCI BCM203x USB driver"
+ depends on USB && BT
+ select FW_LOADER
+ help
+ Bluetooth HCI BCM203x USB driver.
+ This driver provides the firmware loading mechanism for the Broadcom
+ Blutonium based devices.
+
+ Say Y here to compile support for HCI BCM203x devices into the
+ kernel or say M to compile it as module (bcm203x).
+
+config BT_HCIBFUSB
+ tristate "HCI BlueFRITZ! USB driver"
+ depends on USB && BT
+ select FW_LOADER
+ help
+ Bluetooth HCI BlueFRITZ! USB driver.
+ This driver provides support for Bluetooth USB devices with AVM
+ interface:
+ AVM BlueFRITZ! USB
+
+ Say Y here to compile support for HCI BFUSB devices into the
+ kernel or say M to compile it as module (bfusb).
+
config BT_HCIDTL1
tristate "HCI DTL1 (PC Card) driver"
depends on PCMCIA && BT
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 8a6af91bb52f..ec5345fac3a3 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -5,6 +5,8 @@
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_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
new file mode 100644
index 000000000000..12e44029a38a
--- /dev/null
+++ b/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,309 @@
+/*
+ *
+ * Broadcom Blutonium firmware driver
+ *
+ * Copyright (C) 2003 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/timer.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#ifndef CONFIG_BT_HCIBCM203X_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static struct usb_device_id bcm203x_table[] = {
+ /* Broadcom Blutonium (BCM2033) */
+ { USB_DEVICE(0x0a5c, 0x2033) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+
+#define BCM203X_ERROR 0
+#define BCM203X_RESET 1
+#define BCM203X_LOAD_MINIDRV 2
+#define BCM203X_SELECT_MEMORY 3
+#define BCM203X_CHECK_MEMORY 4
+#define BCM203X_LOAD_FIRMWARE 5
+#define BCM203X_CHECK_FIRMWARE 6
+
+#define BCM203X_IN_EP 0x81
+#define BCM203X_OUT_EP 0x02
+
+struct bcm203x_data {
+ struct usb_device *udev;
+
+ unsigned long state;
+
+ struct timer_list timer;
+
+ struct urb *urb;
+ unsigned char buffer[4096];
+
+ unsigned char *fw_data;
+ unsigned int fw_size;
+ unsigned int fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct bcm203x_data *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+
+ BT_DBG("udev %p urb %p", udev, urb);
+
+ if (urb->status) {
+ BT_ERR("URB failed with status %d", urb->status);
+ data->state = BCM203X_ERROR;
+ return;
+ }
+
+ switch (data->state) {
+ case BCM203X_LOAD_MINIDRV:
+ memcpy(data->buffer, "#", 1);
+
+ usb_fill_bulk_urb(urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, 1, bcm203x_complete, data);
+
+ data->state = BCM203X_SELECT_MEMORY;
+
+ mod_timer(&data->timer, jiffies + (HZ / 10));
+ break;
+
+ case BCM203X_SELECT_MEMORY:
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_MEMORY;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_MEMORY:
+ if (data->buffer[0] != '#') {
+ BT_ERR("Memory select failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_LOAD_FIRMWARE;
+
+ case BCM203X_LOAD_FIRMWARE:
+ if (data->fw_sent == data->fw_size) {
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32,
+ bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_FIRMWARE;
+ } else {
+ len = min_t(uint, data->fw_size - data->fw_sent,
+ sizeof(data->buffer));
+
+ usb_fill_bulk_urb(urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->fw_data + data->fw_sent, len,
+ bcm203x_complete, data);
+
+ data->fw_sent += len;
+ }
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_FIRMWARE:
+ if (data->buffer[0] != '.') {
+ BT_ERR("Firmware loading failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_RESET;
+ break;
+ }
+}
+
+static void bcm203x_timer(unsigned long user_data)
+{
+ struct bcm203x_data *data = (struct bcm203x_data *) user_data;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct bcm203x_data *data;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ if (intf->altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ BT_ERR("Can't allocate memory for data structure");
+ return -ENOMEM;
+ }
+
+ memset(data, 0, sizeof(*data));
+
+ data->udev = udev;
+ data->state = BCM203X_LOAD_MINIDRV;
+
+ data->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!data->urb) {
+ BT_ERR("Can't allocate URB");
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+ BT_ERR("Mini driver request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("minidrv data %p size %d", firmware->data, firmware->size);
+
+ if (firmware->size > sizeof(data->buffer)) {
+ BT_ERR("Mini driver exceeds size of buffer");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ memcpy(data->buffer, firmware->data, firmware->size);
+
+ usb_fill_bulk_urb(data->urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, firmware->size, bcm203x_complete, data);
+
+ release_firmware(firmware);
+
+ if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ data->fw_data = kmalloc(firmware->size, GFP_KERNEL);
+ if (!data->fw_data) {
+ BT_ERR("Can't allocate memory for firmware image");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ memcpy(data->fw_data, firmware->data, firmware->size);
+ data->fw_size = firmware->size;
+ data->fw_sent = 0;
+
+ release_firmware(firmware);
+
+ init_timer(&data->timer);
+ data->timer.function = bcm203x_timer;
+ data->timer.data = (unsigned long) data;
+
+ usb_set_intfdata(intf, data);
+
+ mod_timer(&data->timer, jiffies + HZ);
+
+ return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+ struct bcm203x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ usb_unlink_urb(data->urb);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_free_urb(data->urb);
+ kfree(data->fw_data);
+ kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm203x",
+ .probe = bcm203x_probe,
+ .disconnect = bcm203x_disconnect,
+ .id_table = bcm203x_table,
+};
+
+static int __init bcm203x_init(void)
+{
+ int err;
+
+ BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+ err = usb_register(&bcm203x_driver);
+ if (err < 0)
+ BT_ERR("Failed to register USB driver");
+
+ return err;
+}
+
+static void __exit bcm203x_cleanup(void)
+{
+ usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_cleanup);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
new file mode 100644
index 000000000000..0ff501d08a78
--- /dev/null
+++ b/drivers/bluetooth/bfusb.c
@@ -0,0 +1,782 @@
+/*
+ *
+ * AVM BlueFRITZ! USB driver
+ *
+ * Copyright (C) 2003 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/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBFUSB_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.1"
+
+static struct usb_driver bfusb_driver;
+
+static struct usb_device_id bfusb_table[] = {
+ /* AVM BlueFRITZ! USB */
+ { USB_DEVICE(0x057c, 0x2200) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+
+#define BFUSB_MAX_BLOCK_SIZE 256
+
+#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
+
+#define BFUSB_TX_PROCESS 1
+#define BFUSB_TX_WAKEUP 2
+
+#define BFUSB_MAX_BULK_TX 2
+#define BFUSB_MAX_BULK_RX 2
+
+struct bfusb {
+ struct hci_dev hdev;
+
+ unsigned long state;
+
+ struct usb_device *udev;
+
+ unsigned int bulk_in_ep;
+ unsigned int bulk_out_ep;
+ unsigned int bulk_pkt_size;
+
+ rwlock_t lock;
+
+ struct sk_buff_head transmit_q;
+
+ struct sk_buff *reassembly;
+
+ atomic_t pending_tx;
+ struct sk_buff_head pending_q;
+ struct sk_buff_head completed_q;
+};
+
+struct bfusb_scb {
+ struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs);
+static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs);
+
+static struct urb *bfusb_get_completed(struct bfusb *bfusb)
+{
+ struct sk_buff *skb;
+ struct urb *urb = NULL;
+
+ BT_DBG("bfusb %p", bfusb);
+
+ skb = skb_dequeue(&bfusb->completed_q);
+ if (skb) {
+ urb = ((struct bfusb_scb *) skb->cb)->urb;
+ kfree_skb(skb);
+ }
+
+ return urb;
+}
+
+static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
+{
+ struct sk_buff *skb;
+ struct urb *urb;
+
+ BT_DBG("bfusb %p", bfusb);
+
+ while ((skb = skb_dequeue(&bfusb->pending_q))) {
+ urb = ((struct bfusb_scb *) skb->cb)->urb;
+ usb_unlink_urb(urb);
+ skb_queue_tail(&bfusb->completed_q, skb);
+ }
+
+ while ((urb = bfusb_get_completed(bfusb)))
+ usb_free_urb(urb);
+}
+
+
+static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
+{
+ struct bfusb_scb *scb = (void *) skb->cb;
+ struct urb *urb = bfusb_get_completed(bfusb);
+ int err, pipe;
+
+ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
+ usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, skb->len,
+ bfusb_tx_complete, skb);
+
+ scb->urb = urb;
+
+ skb_queue_tail(&bfusb->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk tx submit failed urb %p err %d",
+ bfusb->hdev.name, urb, err);
+ skb_unlink(skb);
+ usb_free_urb(urb);
+ } else
+ atomic_inc(&bfusb->pending_tx);
+
+ return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb *bfusb)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("bfusb %p", bfusb);
+
+ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
+ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+ return;
+ }
+
+ do {
+ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+
+ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
+ (skb = skb_dequeue(&bfusb->transmit_q))) {
+ if (bfusb_send_bulk(bfusb, skb) < 0) {
+ skb_queue_head(&bfusb->transmit_q, skb);
+ break;
+ }
+ }
+
+ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
+
+ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb *bfusb = (struct bfusb *) skb->dev;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
+ atomic_dec(&bfusb->pending_tx);
+
+ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
+ return;
+
+ if (!urb->status)
+ bfusb->hdev.stat.byte_tx += skb->len;
+ else
+ bfusb->hdev.stat.err_tx++;
+
+ read_lock(&bfusb->lock);
+
+ skb_unlink(skb);
+ skb_queue_tail(&bfusb->completed_q, skb);
+
+ bfusb_tx_wakeup(bfusb);
+
+ read_unlock(&bfusb->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
+{
+ struct bfusb_scb *scb;
+ struct sk_buff *skb;
+ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+ BT_DBG("bfusb %p urb %p", bfusb, urb);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) bfusb;
+
+ scb = (struct bfusb_scb *) skb->cb;
+ scb->urb = urb;
+
+ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
+
+ usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, size,
+ bfusb_rx_complete, skb);
+
+ skb_queue_tail(&bfusb->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk rx submit failed urb %p err %d",
+ bfusb->hdev.name, urb, err);
+ skb_unlink(skb);
+ kfree_skb(skb);
+ usb_free_urb(urb);
+ }
+
+ return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
+{
+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
+
+ if (hdr & 0x10) {
+ BT_ERR("%s error in block", bfusb->hdev.name);
+ if (bfusb->reassembly)
+ kfree_skb(bfusb->reassembly);
+ bfusb->reassembly = NULL;
+ return -EIO;
+ }
+
+ if (hdr & 0x04) {
+ struct sk_buff *skb;
+ unsigned char pkt_type;
+ int pkt_len = 0;
+
+ if (bfusb->reassembly) {
+ BT_ERR("%s unexpected start block", bfusb->hdev.name);
+ kfree_skb(bfusb->reassembly);
+ bfusb->reassembly = NULL;
+ }
+
+ if (len < 1) {
+ BT_ERR("%s no packet type found", bfusb->hdev.name);
+ return -EPROTO;
+ }
+
+ pkt_type = *data++; len--;
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ if (len >= HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *) data;
+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+ } else {
+ BT_ERR("%s event block is too short", bfusb->hdev.name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (len >= HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) data;
+ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+ } else {
+ BT_ERR("%s data block is too short", bfusb->hdev.name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (len >= HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) data;
+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+ } else {
+ BT_ERR("%s audio block is too short", bfusb->hdev.name);
+ return -EILSEQ;
+ }
+ break;
+ }
+
+ skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) &bfusb->hdev;
+ skb->pkt_type = pkt_type;
+
+ bfusb->reassembly = skb;
+ } else {
+ if (!bfusb->reassembly) {
+ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
+ return -EIO;
+ }
+ }
+
+ if (len > 0)
+ memcpy(skb_put(bfusb->reassembly, len), data, len);
+
+ if (hdr & 0x08) {
+ hci_recv_frame(bfusb->reassembly);
+ bfusb->reassembly = NULL;
+ }
+
+ return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb *bfusb = (struct bfusb *) skb->dev;
+ unsigned char *buf = urb->transfer_buffer;
+ int count = urb->actual_length;
+ int err, hdr, len;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
+ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
+ return;
+
+ read_lock(&bfusb->lock);
+
+ if (urb->status || !count)
+ goto resubmit;
+
+ bfusb->hdev.stat.byte_rx += count;
+
+ skb_put(skb, count);
+
+ while (count) {
+ hdr = buf[0] | (buf[1] << 8);
+
+ if (hdr & 0x4000) {
+ len = 0;
+ count -= 2;
+ buf += 2;
+ } else {
+ len = (buf[2] == 0) ? 256 : buf[2];
+ count -= 3;
+ buf += 3;
+ }
+
+ if (count < len) {
+ BT_ERR("%s block extends over URB buffer ranges",
+ bfusb->hdev.name);
+ }
+
+ if ((hdr & 0xe1) == 0xc1)
+ bfusb_recv_block(bfusb, hdr, buf, len);
+
+ count -= len;
+ buf += len;
+ }
+
+ skb_unlink(skb);
+ kfree_skb(skb);
+
+ bfusb_rx_submit(bfusb, urb);
+
+ read_unlock(&bfusb->lock);
+
+ return;
+
+resubmit:
+ urb->dev = bfusb->udev;
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk resubmit failed urb %p err %d",
+ bfusb->hdev.name, urb, err);
+ }
+
+ read_unlock(&bfusb->lock);
+}
+
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ unsigned long flags;
+ int i, err;
+
+ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&bfusb->lock, flags);
+
+ err = bfusb_rx_submit(bfusb, NULL);
+ if (!err) {
+ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+ bfusb_rx_submit(bfusb, NULL);
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ }
+
+ write_unlock_irqrestore(&bfusb->lock, flags);
+
+ return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+ skb_queue_purge(&bfusb->transmit_q);
+
+ return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ unsigned long flags;
+
+ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&bfusb->lock, flags);
+ write_unlock_irqrestore(&bfusb->lock, flags);
+
+ bfusb_unlink_urbs(bfusb);
+ bfusb_flush(hdev);
+
+ return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct bfusb *bfusb;
+ struct sk_buff *nskb;
+ unsigned char buf[3];
+ int sent = 0, size, count;
+
+ 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 (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ bfusb = (struct bfusb *) hdev->driver_data;
+
+ switch (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;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+
+ count = skb->len;
+
+ /* Max HCI frame size seems to be 1511 + 1 */
+ if (!(nskb = bt_skb_alloc(count + 32, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for new packet");
+ return -ENOMEM;
+ }
+
+ nskb->dev = (void *) bfusb;
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+ buf[1] = 0x00;
+ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+ memcpy(skb_put(nskb, 3), buf, 3);
+ memcpy(skb_put(nskb, size), skb->data + sent, size);
+
+ sent += size;
+ count -= size;
+ }
+
+ /* Don't send frame with multiple size of bulk max packet */
+ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
+ buf[0] = 0xdd;
+ buf[1] = 0x00;
+ memcpy(skb_put(nskb, 2), buf, 2);
+ }
+
+ read_lock(&bfusb->lock);
+
+ skb_queue_tail(&bfusb->transmit_q, nskb);
+ bfusb_tx_wakeup(bfusb);
+
+ read_unlock(&bfusb->lock);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+ kfree(bfusb);
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
+{
+ unsigned char *buf;
+ int err, pipe, len, size, sent = 0;
+
+ BT_DBG("bfusb %p udev %p", bfusb, bfusb->udev);
+
+ BT_INFO("BlueFRITZ! USB loading firmware");
+
+ pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+ if (usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 1, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT) < 0) {
+ BT_ERR("Can't change to loading configuration");
+ return -EBUSY;
+ }
+
+ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
+ if (!buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+ memcpy(buf, firmware + sent, size);
+
+ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
+ &len, BFUSB_BLOCK_TIMEOUT);
+
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading");
+ goto error;
+ }
+
+ sent += size;
+ count -= size;
+ }
+
+ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
+ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
+ BT_ERR("Error in null packet request");
+ goto error;
+ }
+
+ pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+ if ((err = usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0) {
+ BT_ERR("Can't change to running configuration");
+ goto error;
+ }
+
+ BT_INFO("BlueFRITZ! USB device ready");
+
+ kfree(buf);
+ return 0;
+
+error:
+ kfree(buf);
+
+ pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 0, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+
+ return err;
+}
+
+static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_host_endpoint *bulk_out_ep;
+ struct usb_host_endpoint *bulk_in_ep;
+ struct hci_dev *hdev;
+ struct bfusb *bfusb;
+
+ BT_DBG("iface %p id %p", iface, id);
+
+ /* Check number of endpoints */
+ if (iface->altsetting[0].desc.bNumEndpoints < 2)
+ return -EIO;
+
+ bulk_out_ep = &iface->altsetting[0].endpoint[0];
+ bulk_in_ep = &iface->altsetting[0].endpoint[1];
+
+ if (!bulk_out_ep || !bulk_in_ep) {
+ BT_ERR("Bulk endpoints not found");
+ goto done;
+ }
+
+ /* Initialize control structure and load firmware */
+ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
+ BT_ERR("Can't allocate memory for control structure");
+ goto done;
+ }
+
+ memset(bfusb, 0, sizeof(struct bfusb));
+
+ bfusb->udev = udev;
+ bfusb->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
+ bfusb->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ bfusb->bulk_pkt_size = bulk_out_ep->desc.wMaxPacketSize;
+
+ bfusb->lock = RW_LOCK_UNLOCKED;
+
+ bfusb->reassembly = NULL;
+
+ skb_queue_head_init(&bfusb->transmit_q);
+ skb_queue_head_init(&bfusb->pending_q);
+ skb_queue_head_init(&bfusb->completed_q);
+
+ if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
+ BT_ERR("Firmware loading failed");
+ goto release;
+ }
+
+ release_firmware(firmware);
+
+ /* Initialize and register HCI device */
+ hdev = &bfusb->hdev;
+
+ hdev->type = HCI_USB;
+ hdev->driver_data = bfusb;
+
+ hdev->open = bfusb_open;
+ hdev->close = bfusb_close;
+ hdev->flush = bfusb_flush;
+ hdev->send = bfusb_send_frame;
+ hdev->destruct = bfusb_destruct;
+ hdev->ioctl = bfusb_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ goto error;
+ }
+
+ usb_set_intfdata(iface, bfusb);
+
+ return 0;
+
+release:
+ release_firmware(firmware);
+
+error:
+ kfree(bfusb);
+
+done:
+ return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *iface)
+{
+ struct bfusb *bfusb = usb_get_intfdata(iface);
+ struct hci_dev *hdev = &bfusb->hdev;
+
+ BT_DBG("iface %p", iface);
+
+ if (!hdev)
+ return;
+
+ usb_set_intfdata(iface, NULL);
+
+ bfusb_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+}
+
+static struct usb_driver bfusb_driver = {
+ .owner = THIS_MODULE,
+ .name = "bfusb",
+ .probe = bfusb_probe,
+ .disconnect = bfusb_disconnect,
+ .id_table = bfusb_table,
+};
+
+static int __init bfusb_init(void)
+{
+ int err;
+
+ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+
+ if ((err = usb_register(&bfusb_driver)) < 0)
+ BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+ return err;
+}
+
+static void __exit bfusb_cleanup(void)
+{
+ usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_cleanup);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index fee12e3828cf..6de94257b12a 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -499,7 +499,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
}
-void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
bluecard_info_t *info = dev_inst;
unsigned int iobase;
@@ -507,11 +507,11 @@ void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
if (!test_bit(CARD_READY, &(info->hw_state)))
- return;
+ return IRQ_NONE;
iobase = info->link.io.BasePort1;
@@ -556,6 +556,8 @@ void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
outb(info->ctrl_reg, iobase + REG_CONTROL);
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index b5a8135f9808..df16aad65de1 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -355,7 +355,7 @@ static void bt3c_receive(bt3c_info_t *info)
}
-void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
bt3c_info_t *info = dev_inst;
unsigned int iobase;
@@ -363,7 +363,7 @@ void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -396,6 +396,8 @@ void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index 8fb7b29fdc3d..337230b613e2 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -301,7 +301,7 @@ static void btuart_receive(btuart_info_t *info)
}
-void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
btuart_info_t *info = dev_inst;
unsigned int iobase;
@@ -310,7 +310,7 @@ void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -351,6 +351,8 @@ void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 96bfe85fdfec..74d9cf9e352d 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -304,7 +304,7 @@ static void dtl1_receive(dtl1_info_t *info)
}
-void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
dtl1_info_t *info = dev_inst;
unsigned int iobase;
@@ -314,7 +314,7 @@ void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -363,6 +363,8 @@ void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
index 3bef9b14ad7d..14b8350652e4 100644
--- a/drivers/bluetooth/hci_usb.c
+++ b/drivers/bluetooth/hci_usb.c
@@ -62,7 +62,7 @@
#define BT_DMP( A... )
#endif
-#ifndef CONFIG_BT_USB_ZERO_PACKET
+#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET
#undef URB_ZERO_PACKET
#define URB_ZERO_PACKET 0
#endif
@@ -70,12 +70,21 @@
static struct usb_driver hci_usb_driver;
static struct usb_device_id bluetooth_ids[] = {
+ /* Broadcom BCM2033 without firmware */
+ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
+
+ /* Digianswer device */
+ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
+
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
/* Ericsson with non-standard id */
{ USB_DEVICE(0x0bdb, 0x1002) },
+ /* ALPS Module with non-standard id */
+ { USB_DEVICE(0x044e, 0x3002) },
+
/* Bluetooth Ultraport Module from IBM */
{ USB_DEVICE(0x04bf, 0x030a) },
@@ -84,13 +93,6 @@ static struct usb_device_id bluetooth_ids[] = {
MODULE_DEVICE_TABLE (usb, bluetooth_ids);
-static struct usb_device_id ignore_ids[] = {
- /* Broadcom BCM2033 without firmware */
- { USB_DEVICE(0x0a5c, 0x2033) },
-
- { } /* Terminating entry */
-};
-
struct _urb *_urb_alloc(int isoc, int gfp)
{
struct _urb *_urb = kmalloc(sizeof(struct _urb) +
@@ -134,7 +136,7 @@ static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
return _urb_dequeue(__completed_q(husb, type));
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
{
int offset = 0, i;
@@ -232,7 +234,7 @@ static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
return err;
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
{
struct _urb *_urb;
@@ -301,9 +303,10 @@ static int hci_usb_open(struct hci_dev *hdev)
for (i = 0; i < HCI_MAX_BULK_RX; i++)
hci_usb_bulk_rx_submit(husb);
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
if (husb->isoc_iface)
- hci_usb_isoc_rx_submit(husb);
+ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
+ hci_usb_isoc_rx_submit(husb);
#endif
} else {
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -425,7 +428,7 @@ static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
} else
dr = (void *) _urb->urb.setup_packet;
- dr->bRequestType = HCI_CTRL_REQ;
+ dr->bRequestType = husb->ctrl_req;
dr->bRequest = 0;
dr->wIndex = 0;
dr->wValue = 0;
@@ -466,7 +469,7 @@ static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
return __tx_submit(husb, _urb);
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
{
struct _urb *_urb = __get_completed(husb, skb->pkt_type);
@@ -517,10 +520,10 @@ static void hci_usb_tx_process(struct hci_usb *husb)
skb_queue_head(q, skb);
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
/* Process SCO queue */
q = __transmit_q(husb, HCI_SCODATA_PKT);
- if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) &&
+ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
(skb = skb_dequeue(q))) {
if (hci_usb_send_isoc(husb, skb) < 0)
skb_queue_head(q, skb);
@@ -576,7 +579,7 @@ static int hci_usb_send_frame(struct sk_buff *skb)
hdev->stat.acl_tx++;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
@@ -626,7 +629,7 @@ static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int c
} else
return -EILSEQ;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case HCI_SCODATA_PKT:
if (count >= HCI_SCO_HDR_SIZE) {
struct hci_sco_hdr *h = data;
@@ -691,7 +694,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs)
goto resubmit;
if (_urb->type == HCI_SCODATA_PKT) {
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
int i;
for (i=0; i < urb->number_of_packets; i++) {
BT_DBG("desc %d status %d offset %d len %d", i,
@@ -785,9 +788,11 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
iface = udev->actconfig->interface[0];
- /* Check our black list */
- if (usb_match_id(intf, ignore_ids))
- return -EIO;
+ if (id->driver_info & HCI_IGNORE)
+ return -ENODEV;
+
+ if (intf->altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
/* Check number of endpoints */
if (intf->altsetting[0].desc.bNumEndpoints < 3)
@@ -826,9 +831,9 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
bulk_out_ep[i] = ep;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case USB_ENDPOINT_XFER_ISOC:
- if (ep->desc.wMaxPacketSize < size)
+ if (ep->desc.wMaxPacketSize < size || a > 2)
break;
size = ep->desc.wMaxPacketSize;
@@ -852,7 +857,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto done;
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
BT_DBG("Isoc endpoints not found");
isoc_iface = NULL;
@@ -871,7 +876,12 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
husb->bulk_in_ep = bulk_in_ep[0];
husb->intr_in_ep = intr_in_ep[0];
-#ifdef CONFIG_BT_USB_SCO
+ if (id->driver_info & HCI_DIGIANSWER)
+ husb->ctrl_req = HCI_DIGI_REQ;
+ else
+ husb->ctrl_req = HCI_CTRL_REQ;
+
+#ifdef CONFIG_BT_HCIUSB_SCO
if (isoc_iface) {
BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
index 1e7306286e56..e2ba50da3021 100644
--- a/drivers/bluetooth/hci_usb.h
+++ b/drivers/bluetooth/hci_usb.h
@@ -35,12 +35,19 @@
#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
#define HCI_CTRL_REQ 0x20
+#define HCI_DIGI_REQ 0x40
+
+#define HCI_IGNORE 0x01
+#define HCI_DIGIANSWER 0x02
#define HCI_MAX_IFACE_NUM 3
#define HCI_MAX_BULK_TX 4
#define HCI_MAX_BULK_RX 1
+#define HCI_MAX_ISOC_RX 2
+#define HCI_MAX_ISOC_TX 2
+
#define HCI_MAX_ISOC_FRAMES 10
struct _urb_queue {
@@ -119,6 +126,8 @@ struct hci_usb {
struct usb_host_endpoint *isoc_out_ep;
struct usb_host_endpoint *isoc_in_ep;
+ __u8 ctrl_req;
+
struct sk_buff_head transmit_q[4];
struct sk_buff *reassembly[4]; // Reassembly buffers
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 93865624e621..b0bf84f29bec 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -606,33 +606,20 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi
*/
static int __init bpq_init_driver(void)
{
- struct net_device *dev;
-
- dev_add_pack(&bpq_packet_type);
-
- register_netdevice_notifier(&bpq_dev_notifier);
-
- printk(banner);
-
#ifdef CONFIG_PROC_FS
if (!proc_net_fops_create("bpqether", S_IRUGO, &bpq_info_fops)) {
printk(KERN_ERR
"bpq: cannot create /proc/net/bpqether entry.\n");
- unregister_netdevice_notifier(&bpq_dev_notifier);
- dev_remove_pack(&bpq_packet_type);
return -ENOENT;
}
#endif /* CONFIG_PROC_FS */
- rtnl_lock();
- for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev_is_ethdev(dev) && bpq_new_device(dev)) {
- printk(KERN_ERR
- "bpq: cannot setup dev for '%s'\n",
- dev->name);
- }
- }
- rtnl_unlock();
+ dev_add_pack(&bpq_packet_type);
+
+ register_netdevice_notifier(&bpq_dev_notifier);
+
+ printk(banner);
+
return 0;
}
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index ebc414481ad9..13f7ae09e4cb 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -448,22 +448,12 @@ static char banner[] __initdata = KERN_INFO "LAPB Ethernet driver version 0.02\n
static int __init lapbeth_init_driver(void)
{
- struct net_device *dev;
-
dev_add_pack(&lapbeth_packet_type);
register_netdevice_notifier(&lapbeth_dev_notifier);
printk(banner);
- rtnl_lock();
- for (dev = dev_base; dev; dev = dev->next) {
- if (dev_is_ethdev(dev)) {
- lapbeth_new_device(dev);
- }
- }
- rtnl_unlock();
-
return 0;
}
module_init(lapbeth_init_driver);
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index bfd4938a1627..534e87dadded 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -65,6 +65,7 @@
#include <linux/ncp_fs.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
+#include <linux/wireless.h>
#include <net/sock.h> /* siocdevprivate_ioctl */
#include <net/bluetooth/bluetooth.h>
@@ -2970,6 +2971,48 @@ static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd, unsigned long a
return sys_ioctl(fd, cmd, (unsigned long)tdata);
}
+struct compat_iw_point {
+ compat_caddr_t pointer;
+ __u16 length;
+ __u16 flags;
+};
+
+static int do_wireless_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct iwreq *iwr, *iwr_u;
+ struct iw_point *iwp;
+ struct compat_iw_point *iwp_u;
+ compat_caddr_t pointer;
+ __u16 length, flags;
+
+ iwr_u = (struct iwreq *) compat_ptr(arg);
+ iwp_u = (struct compat_iw_point *) &iwr_u->u.data;
+ iwr = compat_alloc_user_space(sizeof(*iwr));
+ if (iwr == NULL)
+ return -ENOMEM;
+
+ iwp = &iwr->u.data;
+
+ if (verify_area(VERIFY_WRITE, iwr, sizeof(*iwr)))
+ return -EFAULT;
+
+ if (__copy_in_user(&iwr->ifr_ifrn.ifrn_name[0],
+ &iwr_u->ifr_ifrn.ifrn_name[0],
+ sizeof(iwr->ifr_ifrn.ifrn_name)))
+ return -EFAULT;
+
+ if (__get_user(pointer, &iwp_u->pointer) ||
+ __get_user(length, &iwp_u->length) ||
+ __get_user(flags, &iwp_u->flags))
+ return -EFAULT;
+
+ if (__put_user(compat_ptr(pointer), &iwp->pointer) ||
+ __put_user(length, &iwp->length) ||
+ __put_user(flags, &iwp->flags))
+ return -EFAULT;
+
+ return sys_ioctl(fd, cmd, (unsigned long) iwr);
+}
#undef CODE
#endif
@@ -3133,6 +3176,20 @@ HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal)
HANDLE_IOCTL(I2C_FUNCS, w_long)
HANDLE_IOCTL(I2C_RDWR, do_i2c_rdwr_ioctl)
HANDLE_IOCTL(I2C_SMBUS, do_i2c_smbus_ioctl)
+/* wireless */
+HANDLE_IOCTL(SIOCGIWRANGE, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCSIWSPY, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWSPY, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCSIWTHRSPY, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWTHRSPY, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWAPLIST, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWSCAN, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCSIWESSID, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWESSID, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCSIWNICKN, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWNICKN, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCSIWENCODE, do_wireless_ioctl)
+HANDLE_IOCTL(SIOCGIWENCODE, do_wireless_ioctl)
#undef DECLARES
#endif
diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h
index d87c417544bc..37c5a35ff237 100644
--- a/include/linux/compat_ioctl.h
+++ b/include/linux/compat_ioctl.h
@@ -260,6 +260,7 @@ COMPATIBLE_IOCTL(SIOCATMARK)
COMPATIBLE_IOCTL(SIOCSIFLINK)
COMPATIBLE_IOCTL(SIOCSIFENCAP)
COMPATIBLE_IOCTL(SIOCGIFENCAP)
+COMPATIBLE_IOCTL(SIOCSIFNAME)
COMPATIBLE_IOCTL(SIOCSIFBR)
COMPATIBLE_IOCTL(SIOCGIFBR)
COMPATIBLE_IOCTL(SIOCSARP)
@@ -685,3 +686,34 @@ COMPATIBLE_IOCTL(I2C_TENBIT)
COMPATIBLE_IOCTL(I2C_PEC)
COMPATIBLE_IOCTL(I2C_RETRIES)
COMPATIBLE_IOCTL(I2C_TIMEOUT)
+/* wireless */
+COMPATIBLE_IOCTL(SIOCSIWCOMMIT)
+COMPATIBLE_IOCTL(SIOCGIWNAME)
+COMPATIBLE_IOCTL(SIOCSIWNWID)
+COMPATIBLE_IOCTL(SIOCGIWNWID)
+COMPATIBLE_IOCTL(SIOCSIWFREQ)
+COMPATIBLE_IOCTL(SIOCGIWFREQ)
+COMPATIBLE_IOCTL(SIOCSIWMODE)
+COMPATIBLE_IOCTL(SIOCGIWMODE)
+COMPATIBLE_IOCTL(SIOCSIWSENS)
+COMPATIBLE_IOCTL(SIOCGIWSENS)
+COMPATIBLE_IOCTL(SIOCSIWRANGE)
+COMPATIBLE_IOCTL(SIOCSIWPRIV)
+COMPATIBLE_IOCTL(SIOCGIWPRIV)
+COMPATIBLE_IOCTL(SIOCSIWSTATS)
+COMPATIBLE_IOCTL(SIOCGIWSTATS)
+COMPATIBLE_IOCTL(SIOCSIWAP)
+COMPATIBLE_IOCTL(SIOCGIWAP)
+COMPATIBLE_IOCTL(SIOCSIWSCAN)
+COMPATIBLE_IOCTL(SIOCSIWRATE)
+COMPATIBLE_IOCTL(SIOCGIWRATE)
+COMPATIBLE_IOCTL(SIOCSIWRTS)
+COMPATIBLE_IOCTL(SIOCGIWRTS)
+COMPATIBLE_IOCTL(SIOCSIWFRAG)
+COMPATIBLE_IOCTL(SIOCGIWFRAG)
+COMPATIBLE_IOCTL(SIOCSIWTXPOW)
+COMPATIBLE_IOCTL(SIOCGIWTXPOW)
+COMPATIBLE_IOCTL(SIOCSIWRETRY)
+COMPATIBLE_IOCTL(SIOCGIWRETRY)
+COMPATIBLE_IOCTL(SIOCSIWPOWER)
+COMPATIBLE_IOCTL(SIOCGIWPOWER)
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 7f5a5c4280f7..ab97fc520921 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -143,6 +143,7 @@ struct ipv6_devconf {
__s32 regen_max_retry;
__s32 max_desync_factor;
#endif
+ __s32 max_addresses;
void *sysctl;
};
@@ -158,13 +159,12 @@ enum {
DEVCONF_RTR_SOLICITS,
DEVCONF_RTR_SOLICIT_INTERVAL,
DEVCONF_RTR_SOLICIT_DELAY,
-#ifdef CONFIG_IPV6_PRIVACY
DEVCONF_USE_TEMPADDR,
DEVCONF_TEMP_VALID_LFT,
DEVCONF_TEMP_PREFERED_LFT,
DEVCONF_REGEN_MAX_RETRY,
DEVCONF_MAX_DESYNC_FACTOR,
-#endif
+ DEVCONF_MAX_ADDRESSES,
DEVCONF_MAX
};
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 4a26f207b5d7..6cfd6f2f4a98 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -44,7 +44,10 @@
#define RTM_DELTFILTER (RTM_BASE+29)
#define RTM_GETTFILTER (RTM_BASE+30)
-#define RTM_MAX (RTM_BASE+31)
+#define RTM_NEWPREFIX (RTM_BASE+36)
+#define RTM_GETPREFIX (RTM_BASE+38)
+
+#define RTM_MAX (RTM_BASE+39)
/*
Generic structure for encapsulation of optional route information.
@@ -459,6 +462,34 @@ struct ifinfomsg
unsigned ifi_change; /* IFF_* change mask */
};
+/********************************************************************
+ * prefix information
+ ****/
+
+struct prefixmsg
+{
+ unsigned char prefix_family;
+ int prefix_ifindex;
+ unsigned char prefix_type;
+ unsigned char prefix_len;
+ unsigned char prefix_flags;
+};
+
+enum
+{
+ PREFIX_UNSPEC,
+ PREFIX_ADDRESS,
+ PREFIX_CACHEINFO,
+};
+
+#define PREFIX_MAX PREFIX_CACHEINFO
+
+struct prefix_cacheinfo
+{
+ __u32 preferred_time;
+ __u32 valid_time;
+};
+
/* The struct should be in sync with struct net_device_stats */
struct rtnl_link_stats
{
@@ -558,9 +589,18 @@ enum
IFLA_INET6_CONF, /* sysctl parameters */
IFLA_INET6_STATS, /* statistics */
IFLA_INET6_MCAST, /* MC things. What of them? */
+ IFLA_INET6_CACHEINFO, /* time values and max reasm size */
};
-#define IFLA_INET6_MAX IFLA_INET6_MCAST
+struct ifla_cacheinfo
+{
+ __u32 max_reasm_len;
+ __u32 tstamp; /* ipv6InterfaceTable updated timestamp */
+ __u32 reachable_time;
+ __u32 retrans_time;
+};
+
+#define IFLA_INET6_MAX IFLA_INET6_CACHEINFO
/*****************************************************************
* Traffic control messages.
@@ -611,10 +651,13 @@ enum
#define RTMGRP_IPV6_IFADDR 0x100
#define RTMGRP_IPV6_MROUTE 0x200
#define RTMGRP_IPV6_ROUTE 0x400
+#define RTMGRP_IPV6_IFINFO 0x800
#define RTMGRP_DECnet_IFADDR 0x1000
#define RTMGRP_DECnet_ROUTE 0x4000
+#define RTMGRP_IPV6_PREFIX 0x20000
+
/* End of information exported to user level */
#ifdef __KERNEL__
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index c94fb1d1862a..de7b8b5bc523 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -418,7 +418,8 @@ enum {
NET_IPV6_TEMP_VALID_LFT=12,
NET_IPV6_TEMP_PREFERED_LFT=13,
NET_IPV6_REGEN_MAX_RETRY=14,
- NET_IPV6_MAX_DESYNC_FACTOR=15
+ NET_IPV6_MAX_DESYNC_FACTOR=15,
+ NET_IPV6_MAX_ADDRESSES=16
};
/* /proc/sys/net/ipv6/icmp */
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 2537a6fc4bc0..92cbb7794cac 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -15,6 +15,8 @@
#define ADDR_CHECK_FREQUENCY (120*HZ)
+#define IPV6_MAX_ADDRESSES 16
+
struct prefix_info {
__u8 type;
__u8 length;
@@ -50,10 +52,6 @@ struct prefix_info {
extern void addrconf_init(void);
extern void addrconf_cleanup(void);
-extern int addrconf_notify(struct notifier_block *this,
- unsigned long event,
- void * data);
-
extern int addrconf_add_ifaddr(void *arg);
extern int addrconf_del_ifaddr(void *arg);
extern int addrconf_set_dstaddr(void *arg);
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 429847a82f5f..99301bd8096a 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -47,7 +47,8 @@
#define BTPROTO_HCI 1
#define BTPROTO_SCO 2
#define BTPROTO_RFCOMM 3
-#define BTPROTO_BNEP 4
+#define BTPROTO_BNEP 4
+#define BTPROTO_CMTP 5
#define SOL_HCI 0
#define SOL_L2CAP 6
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 228a44322ee0..59523c39d6fb 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -408,6 +408,16 @@ struct inquiry_info {
__u16 clock_offset;
} __attribute__ ((packed));
+#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22
+struct inquiry_info_with_rssi {
+ bdaddr_t bdaddr;
+ __u8 pscan_rep_mode;
+ __u8 pscan_period_mode;
+ __u8 dev_class[3];
+ __u16 clock_offset;
+ __u8 rssi;
+} __attribute__ ((packed));
+
#define HCI_EV_CONN_COMPLETE 0x03
struct hci_ev_conn_complete {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index fd010a9dc75e..6a429873df9c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -176,6 +176,12 @@ static inline void inquiry_cache_init(struct hci_dev *hdev)
c->list = NULL;
}
+static inline int inquiry_cache_empty(struct hci_dev *hdev)
+{
+ struct inquiry_cache *c = &hdev->inq_cache;
+ return (c->list == NULL);
+}
+
static inline long inquiry_cache_age(struct hci_dev *hdev)
{
struct inquiry_cache *c = &hdev->inq_cache;
@@ -281,10 +287,12 @@ static inline void hci_conn_hold(struct hci_conn *conn)
static inline void hci_conn_put(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->refcnt)) {
- if (conn->type == SCO_LINK)
+ if (conn->type == ACL_LINK) {
+ unsigned long timeo = (conn->out) ?
+ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
+ hci_conn_set_timer(conn, timeo);
+ } else
hci_conn_set_timer(conn, HZ / 100);
- else if (conn->out)
- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
}
}
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 7d410fed2520..e022bfbb5166 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -167,8 +167,8 @@ struct rfcomm_session {
int initiator;
/* Default DLC parameters */
+ int cfc;
uint mtu;
- uint credits;
struct list_head dlcs;
};
@@ -190,7 +190,7 @@ struct rfcomm_dlc {
u8 mscex;
uint mtu;
- uint credits;
+ uint cfc;
uint rx_credits;
uint tx_credits;
@@ -219,6 +219,11 @@ struct rfcomm_dlc {
#define RFCOMM_MSCEX_RX 2
#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
+/* CFC states */
+#define RFCOMM_CFC_UNKNOWN -1
+#define RFCOMM_CFC_DISABLED 0
+#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
+
extern struct task_struct *rfcomm_thread;
extern unsigned long rfcomm_event;
diff --git a/include/net/flow.h b/include/net/flow.h
index 7ec259b5d632..1b495278975c 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -8,6 +8,7 @@
#define _NET_FLOW_H
#include <linux/in6.h>
+#include <asm/atomic.h>
struct flowi {
int oif;
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 6dd6ebda5e6f..48280b138cb9 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -25,6 +25,10 @@
#define IF_RA_RCVD 0x20
#define IF_RS_SENT 0x10
+/* prefix flags */
+#define IF_PREFIX_ONLINK 0x01
+#define IF_PREFIX_AUTOCONF 0x02
+
#ifdef __KERNEL__
struct inet6_ifaddr
@@ -183,6 +187,7 @@ struct inet6_dev
struct inet6_dev *next;
struct ipv6_devconf cnf;
struct ipv6_devstat stats;
+ unsigned long tstamp; /* ipv6InterfaceTable update timestamp */
};
extern struct ipv6_devconf ipv6_devconf;
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 6ac444968292..1ad89018e99b 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -401,12 +401,8 @@ extern int ipv6_getsockopt(struct sock *sk, int level,
extern void ipv6_packet_init(void);
-extern void ipv6_netdev_notif_init(void);
-
extern void ipv6_packet_cleanup(void);
-extern void ipv6_netdev_notif_cleanup(void);
-
extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port,
u32 info, u8 *payload);
diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h
index b1f19269c026..d0e60ddf43a0 100644
--- a/include/net/irda/ircomm_tty.h
+++ b/include/net/irda/ircomm_tty.h
@@ -122,6 +122,9 @@ void ircomm_tty_stop(struct tty_struct *tty);
void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self);
extern void ircomm_tty_change_speed(struct ircomm_tty_cb *self);
+extern int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file);
+extern int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
extern int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
extern void ircomm_tty_set_termios(struct tty_struct *tty,
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index d364fc636912..95684d3363c1 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -98,6 +98,17 @@ extern int igmp6_event_report(struct sk_buff *skb);
extern void igmp6_cleanup(void);
+#ifdef CONFIG_SYSCTL
+extern int ndisc_ifinfo_sysctl_change(ctl_table *ctl,
+ int write,
+ struct file * filp,
+ void __user *buffer,
+ size_t *lenp);
+#endif
+
+extern void inet6_ifinfo_notify(int event,
+ struct inet6_dev *idev);
+
static inline struct neighbour * ndisc_get_neigh(struct net_device *dev, struct in6_addr *addr)
{
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 24bee28fd7fb..e016389694a8 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -47,6 +47,9 @@
#include <linux/skbuff.h>
#include <linux/err.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
#define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE)
#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
@@ -206,8 +209,11 @@ extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern void neigh_app_ns(struct neighbour *n);
-extern int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
- int p_id, int pdev_id, char *p_name);
+extern int neigh_sysctl_register(struct net_device *dev,
+ struct neigh_parms *p,
+ int p_id, int pdev_id,
+ char *p_name,
+ proc_handler *proc_handler);
extern void neigh_sysctl_unregister(struct neigh_parms *p);
/*
diff --git a/include/net/sock.h b/include/net/sock.h
index e2dc8c473482..7b38c08f0f64 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -304,7 +304,24 @@ static __inline__ int __sk_del_node_init(struct sock *sk)
return 0;
}
-static inline void __sock_put(struct sock *sk);
+/* Grab socket reference count. This operation is valid only
+ when sk is ALREADY grabbed f.e. it is found in hash table
+ or a list and the lookup is made under lock preventing hash table
+ modifications.
+ */
+
+static inline void sock_hold(struct sock *sk)
+{
+ atomic_inc(&sk->sk_refcnt);
+}
+
+/* Ungrab socket in the context, which assumes that socket refcnt
+ cannot hit zero, f.e. it is true in context of any socketcall.
+ */
+static inline void __sock_put(struct sock *sk)
+{
+ atomic_dec(&sk->sk_refcnt);
+}
static __inline__ int sk_del_node_init(struct sock *sk)
{
@@ -722,25 +739,6 @@ static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
* use separate SMP lock, so that they are prone too.
*/
-/* Grab socket reference count. This operation is valid only
- when sk is ALREADY grabbed f.e. it is found in hash table
- or a list and the lookup is made under lock preventing hash table
- modifications.
- */
-
-static inline void sock_hold(struct sock *sk)
-{
- atomic_inc(&sk->sk_refcnt);
-}
-
-/* Ungrab socket in the context, which assumes that socket refcnt
- cannot hit zero, f.e. it is true in context of any socketcall.
- */
-static inline void __sock_put(struct sock *sk)
-{
- atomic_dec(&sk->sk_refcnt);
-}
-
/* Ungrab socket and destroy it, if it was the last reference. */
static inline void sock_put(struct sock *sk)
{
diff --git a/net/atm/clip.c b/net/atm/clip.c
index d3861a38c71c..4bbc6fccbc56 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -731,8 +731,6 @@ static struct atm_dev atmarpd_dev = {
static int atm_init_atmarp(struct atm_vcc *vcc)
{
- struct net_device *dev;
-
if (atmarpd) return -EADDRINUSE;
if (start_timer) {
start_timer = 0;
@@ -754,9 +752,6 @@ static int atm_init_atmarp(struct atm_vcc *vcc)
printk(KERN_ERR "register_netdevice_notifier failed\n");
if (register_inetaddr_notifier(&clip_inet_notifier))
printk(KERN_ERR "register_inetaddr_notifier failed\n");
- for (dev = clip_devs; dev; dev = PRIV(dev)->next)
- if (dev->flags & IFF_UP)
- (void) to_atmarpd(act_up,PRIV(dev)->number,0);
return 0;
}
diff --git a/net/atm/common.c b/net/atm/common.c
index 2965cdfd0266..e5cef7542b39 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -476,9 +476,8 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
return -EOPNOTSUPP;
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
- test_bit(ATM_VF_CLOSE,&vcc->flags))
- return -sk->sk_err;
- if (!test_bit(ATM_VF_READY, &vcc->flags))
+ test_bit(ATM_VF_CLOSE,&vcc->flags) ||
+ !test_bit(ATM_VF_READY, &vcc->flags))
return 0;
skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &error);
@@ -530,12 +529,10 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
size = m->msg_iov->iov_len;
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags)) {
- error = -sk->sk_err;
- goto out;
- }
- if (!test_bit(ATM_VF_READY, &vcc->flags)) {
+ test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ !test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EPIPE;
+ send_sig(SIGPIPE, current, 0);
goto out;
}
if (!size) {
@@ -561,12 +558,10 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
break;
}
if (test_bit(ATM_VF_RELEASED,&vcc->flags) ||
- test_bit(ATM_VF_CLOSE,&vcc->flags)) {
- error = -sk->sk_err;
- break;
- }
- if (!test_bit(ATM_VF_READY,&vcc->flags)) {
+ test_bit(ATM_VF_CLOSE,&vcc->flags) ||
+ !test_bit(ATM_VF_READY,&vcc->flags)) {
error = -EPIPE;
+ send_sig(SIGPIPE, current, 0);
break;
}
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index a152eec283cb..4ba9e9500fee 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -21,6 +21,7 @@ config BT
SCO Module (SCO links)
RFCOMM Module (RFCOMM protocol)
BNEP Module (BNEP protocol)
+ CMTP Module (CMTP protocol)
Say Y here to enable Linux Bluetooth support and to build Bluetooth Core
layer.
@@ -57,6 +58,8 @@ source "net/bluetooth/rfcomm/Kconfig"
source "net/bluetooth/bnep/Kconfig"
+source "net/bluetooth/cmtp/Kconfig"
+
source "drivers/bluetooth/Kconfig"
endmenu
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index cf5916af802a..5c4d3ab34849 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_BT_L2CAP) += l2cap.o
obj-$(CONFIG_BT_SCO) += sco.o
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
+obj-$(CONFIG_BT_CMTP) += cmtp/
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_proc.o lib.o syms.o
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index b9b3bbf0a5e2..c17195bbcf8a 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -59,7 +59,7 @@
struct proc_dir_entry *proc_bt;
/* Bluetooth sockets */
-#define BT_MAX_PROTO 5
+#define BT_MAX_PROTO 6
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
static kmem_cache_t *bt_sock_cache;
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index efa44e162233..ece601f8df1d 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -707,3 +707,4 @@ module_exit(bnep_cleanup_module);
MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-4");
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig
new file mode 100644
index 000000000000..e832f20ceb14
--- /dev/null
+++ b/net/bluetooth/cmtp/Kconfig
@@ -0,0 +1,11 @@
+config BT_CMTP
+ tristate "CMTP protocol support"
+ depends on BT && BT_L2CAP && ISDN_CAPI
+ help
+ CMTP (CAPI Message Transport Protocol) is a transport layer
+ for CAPI messages. CMTP is required for the Bluetooth Common
+ ISDN Access Profile.
+
+ Say Y here to compile CMTP support into the kernel or say M to
+ compile it as module (cmtp).
+
diff --git a/net/bluetooth/cmtp/Makefile b/net/bluetooth/cmtp/Makefile
new file mode 100644
index 000000000000..890a9a5a6861
--- /dev/null
+++ b/net/bluetooth/cmtp/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux Bluetooth CMTP layer
+#
+
+obj-$(CONFIG_BT_CMTP) += cmtp.o
+
+cmtp-objs := core.o sock.o capi.o
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
new file mode 100644
index 000000000000..7e03820c2bfb
--- /dev/null
+++ b/net/bluetooth/cmtp/capi.c
@@ -0,0 +1,630 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 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 version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define CAPI_INTEROPERABILITY 0x20
+
+#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
+#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
+#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
+#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
+
+#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
+#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
+
+#define CAPI_FUNCTION_REGISTER 0
+#define CAPI_FUNCTION_RELEASE 1
+#define CAPI_FUNCTION_GET_PROFILE 2
+#define CAPI_FUNCTION_GET_MANUFACTURER 3
+#define CAPI_FUNCTION_GET_VERSION 4
+#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
+#define CAPI_FUNCTION_MANUFACTURER 6
+#define CAPI_FUNCTION_LOOPBACK 7
+
+
+#define CMTP_MSGNUM 1
+#define CMTP_APPLID 2
+#define CMTP_MAPPING 3
+
+static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
+{
+ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
+
+ BT_DBG("session %p application %p appl %d", session, app, appl);
+
+ if (!app)
+ return NULL;
+
+ memset(app, 0, sizeof(*app));
+
+ app->state = BT_OPEN;
+ app->appl = appl;
+
+ list_add_tail(&app->list, &session->applications);
+
+ return app;
+}
+
+static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
+{
+ BT_DBG("session %p application %p", session, app);
+
+ if (app) {
+ list_del(&app->list);
+ kfree(app);
+ }
+}
+
+static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
+{
+ struct cmtp_application *app;
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &session->applications) {
+ app = list_entry(p, struct cmtp_application, list);
+ switch (pattern) {
+ case CMTP_MSGNUM:
+ if (app->msgnum == value)
+ return app;
+ break;
+ case CMTP_APPLID:
+ if (app->appl == value)
+ return app;
+ break;
+ case CMTP_MAPPING:
+ if (app->mapping == value)
+ return app;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static int cmtp_msgnum_get(struct cmtp_session *session)
+{
+ session->msgnum++;
+
+ if ((session->msgnum & 0xff) > 200)
+ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
+
+ return session->msgnum;
+}
+
+
+static void cmtp_send_interopmsg(struct cmtp_session *session,
+ __u8 subcmd, __u16 appl, __u16 msgnum,
+ __u16 function, unsigned char *buf, int len)
+{
+ struct sk_buff *skb;
+ unsigned char *s;
+
+ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
+
+ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for interoperability packet");
+ return;
+ }
+
+ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
+
+ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
+ capimsg_setu16(s, 2, appl);
+ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
+ capimsg_setu8 (s, 5, subcmd);
+ capimsg_setu16(s, 6, msgnum);
+
+ /* Interoperability selector (Bluetooth Device Management) */
+ capimsg_setu16(s, 8, 0x0001);
+
+ capimsg_setu8 (s, 10, 3 + len);
+ capimsg_setu16(s, 11, function);
+ capimsg_setu8 (s, 13, len);
+
+ if (len > 0)
+ memcpy(s + 14, buf, len);
+
+ cmtp_send_capimsg(session, skb);
+}
+
+static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct capi_ctr *ctrl = &session->ctrl;
+ struct cmtp_application *application;
+ __u16 appl, msgnum, func, info;
+ __u32 controller;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
+ case CAPI_CONF:
+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
+ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
+
+ switch (func) {
+ case CAPI_FUNCTION_REGISTER:
+ msgnum = CAPIMSG_MSGID(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
+ if (application) {
+ application->state = BT_CONNECTED;
+ application->msgnum = 0;
+ application->mapping = CAPIMSG_APPID(skb->data);
+ wake_up_interruptible(&session->wait);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_RELEASE:
+ appl = CAPIMSG_APPID(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MAPPING, appl);
+ if (application) {
+ application->state = BT_CLOSED;
+ application->msgnum = 0;
+ wake_up_interruptible(&session->wait);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_PROFILE:
+ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
+ msgnum = CAPIMSG_MSGID(skb->data);
+
+ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
+ session->ncontroller = controller;
+ wake_up_interruptible(&session->wait);
+ break;
+ }
+
+ if (!info && ctrl) {
+ memcpy(&ctrl->profile,
+ skb->data + CAPI_MSG_BASELEN + 11,
+ sizeof(capi_profile));
+ session->state = BT_CONNECTED;
+ capi_ctr_ready(ctrl);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_MANUFACTURER:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
+
+ if (!info && ctrl) {
+ strncpy(ctrl->manu,
+ skb->data + CAPI_MSG_BASELEN + 15,
+ skb->data[CAPI_MSG_BASELEN + 14]);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_VERSION:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+ if (!info && ctrl) {
+ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
+ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
+ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
+ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+ if (!info && ctrl) {
+ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
+ strncpy(ctrl->serial,
+ skb->data + CAPI_MSG_BASELEN + 17,
+ skb->data[CAPI_MSG_BASELEN + 16]);
+ }
+
+ break;
+ }
+
+ break;
+
+ case CAPI_IND:
+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
+
+ if (func == CAPI_FUNCTION_LOOPBACK) {
+ appl = CAPIMSG_APPID(skb->data);
+ msgnum = CAPIMSG_MSGID(skb->data);
+ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
+ skb->data + CAPI_MSG_BASELEN + 6,
+ skb->data[CAPI_MSG_BASELEN + 5]);
+ }
+
+ break;
+ }
+
+ kfree_skb(skb);
+}
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct capi_ctr *ctrl = &session->ctrl;
+ struct cmtp_application *application;
+ __u16 cmd, appl;
+ __u32 contr;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
+ cmtp_recv_interopmsg(session, skb);
+ return;
+ }
+
+ if (session->flags & (1 << CMTP_LOOPBACK)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
+ appl = CAPIMSG_APPID(skb->data);
+ contr = CAPIMSG_CONTROL(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MAPPING, appl);
+ if (application) {
+ appl = application->appl;
+ CAPIMSG_SETAPPID(skb->data, appl);
+ } else {
+ BT_ERR("Can't find application with id %d", appl);
+ kfree_skb(skb);
+ return;
+ }
+
+ if ((contr & 0x7f) == 0x01) {
+ contr = (contr & 0xffffff80) | session->num;
+ CAPIMSG_SETCONTROL(skb->data, contr);
+ }
+
+ if (!ctrl) {
+ BT_ERR("Can't find controller %d for message", session->num);
+ kfree_skb(skb);
+ return;
+ }
+
+ capi_ctr_handle_message(ctrl, appl, skb);
+}
+
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct cmtp_scb *scb = (void *) skb->cb;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ scb->id = -1;
+ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
+
+ skb_queue_tail(&session->transmit, skb);
+
+ cmtp_schedule(session);
+}
+
+
+static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ BT_DBG("ctrl %p data %p", ctrl, data);
+
+ return 0;
+}
+
+static void cmtp_reset_ctr(struct capi_ctr *ctrl)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+
+ BT_DBG("ctrl %p", ctrl);
+
+ capi_ctr_reseted(ctrl);
+
+ atomic_inc(&session->terminate);
+ cmtp_schedule(session);
+}
+
+static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+ unsigned char buf[8];
+ int err = 0, nconn, want = rp->level3cnt;
+
+ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
+ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
+
+ application = cmtp_application_add(session, appl);
+ if (!application) {
+ BT_ERR("Can't allocate memory for new application");
+ return;
+ }
+
+ if (want < 0)
+ nconn = ctrl->profile.nbchannel * -want;
+ else
+ nconn = want;
+
+ if (nconn == 0)
+ nconn = ctrl->profile.nbchannel;
+
+ capimsg_setu16(buf, 0, nconn);
+ capimsg_setu16(buf, 2, rp->datablkcnt);
+ capimsg_setu16(buf, 4, rp->datablklen);
+
+ application->state = BT_CONFIG;
+ application->msgnum = cmtp_msgnum_get(session);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
+ CAPI_FUNCTION_REGISTER, buf, 6);
+
+ add_wait_queue(&session->wait, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (!timeo) {
+ err = -EAGAIN;
+ break;
+ }
+
+ if (application->state == BT_CLOSED) {
+ err = -application->err;
+ break;
+ }
+
+ if (application->state == BT_CONNECTED)
+ break;
+
+ if (signal_pending(current)) {
+ err = -EINTR;
+ break;
+ }
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ if (err) {
+ cmtp_application_del(session, application);
+ return;
+ }
+}
+
+static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+
+ BT_DBG("ctrl %p appl %d", ctrl, appl);
+
+ application = cmtp_application_get(session, CMTP_APPLID, appl);
+ if (!application) {
+ BT_ERR("Can't find application");
+ return;
+ }
+
+ application->msgnum = cmtp_msgnum_get(session);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
+ CAPI_FUNCTION_RELEASE, NULL, 0);
+
+ add_wait_queue(&session->wait, &wait);
+ while (timeo) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (application->state == BT_CLOSED)
+ break;
+
+ if (signal_pending(current))
+ break;
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ cmtp_application_del(session, application);
+}
+
+static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ __u16 appl;
+ __u32 contr;
+
+ BT_DBG("ctrl %p skb %p", ctrl, skb);
+
+ appl = CAPIMSG_APPID(skb->data);
+ contr = CAPIMSG_CONTROL(skb->data);
+
+ application = cmtp_application_get(session, CMTP_APPLID, appl);
+ if ((!application) || (application->state != BT_CONNECTED)) {
+ BT_ERR("Can't find application with id %d", appl);
+ kfree_skb(skb);
+ return CAPI_ILLAPPNR;
+ }
+
+ CAPIMSG_SETAPPID(skb->data, application->mapping);
+
+ if ((contr & 0x7f) == session->num) {
+ contr = (contr & 0xffffff80) | 0x01;
+ CAPIMSG_SETCONTROL(skb->data, contr);
+ }
+
+ cmtp_send_capimsg(session, skb);
+
+ return CAPI_NOERROR;
+}
+
+static char *cmtp_procinfo(struct capi_ctr *ctrl)
+{
+ return "CAPI Message Transport Protocol";
+}
+
+static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *app;
+ struct list_head *p, *n;
+ int len = 0;
+
+ len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl));
+ len += sprintf(page + len, "addr %s\n", session->name);
+ len += sprintf(page + len, "ctrl %d\n", session->num);
+
+ list_for_each_safe(p, n, &session->applications) {
+ app = list_entry(p, struct cmtp_application, list);
+ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
+ }
+
+ if (off + count >= len)
+ *eof = 1;
+
+ if (len < off)
+ return 0;
+
+ *start = page + off;
+
+ return ((count < len - off) ? count : len - off);
+}
+
+
+int cmtp_attach_device(struct cmtp_session *session)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+ unsigned char buf[4];
+
+ BT_DBG("session %p", session);
+
+ capimsg_setu32(buf, 0, 0);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
+ CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+ add_wait_queue(&session->wait, &wait);
+ while (timeo) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (session->ncontroller)
+ break;
+
+ if (signal_pending(current))
+ break;
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
+
+ if (!timeo)
+ return -ETIMEDOUT;
+
+ if (!session->ncontroller)
+ return -ENODEV;
+
+
+ if (session->ncontroller > 1)
+ BT_INFO("Setting up only CAPI controller 1");
+
+ session->ctrl.owner = THIS_MODULE;
+ session->ctrl.driverdata = session;
+ strcpy(session->ctrl.name, session->name);
+
+ session->ctrl.driver_name = "cmtp";
+ session->ctrl.load_firmware = cmtp_load_firmware;
+ session->ctrl.reset_ctr = cmtp_reset_ctr;
+ session->ctrl.register_appl = cmtp_register_appl;
+ session->ctrl.release_appl = cmtp_release_appl;
+ session->ctrl.send_message = cmtp_send_message;
+
+ session->ctrl.procinfo = cmtp_procinfo;
+ session->ctrl.ctr_read_proc = cmtp_ctr_read_proc;
+
+ if (attach_capi_ctr(&session->ctrl) < 0) {
+ BT_ERR("Can't attach new controller");
+ return -EBUSY;
+ }
+
+ session->num = session->ctrl.cnr;
+
+ BT_DBG("session %p num %d", session, session->num);
+
+ capimsg_setu32(buf, 0, 1);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_VERSION, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+ return 0;
+}
+
+void cmtp_detach_device(struct cmtp_session *session)
+{
+ BT_DBG("session %p", session);
+
+ detach_capi_ctr(&session->ctrl);
+}
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h
new file mode 100644
index 000000000000..0a0062607584
--- /dev/null
+++ b/net/bluetooth/cmtp/cmtp.h
@@ -0,0 +1,136 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 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 version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTNAMSIZ 18
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD _IOW('C', 200, int)
+#define CMTPCONNDEL _IOW('C', 201, int)
+#define CMTPGETCONNLIST _IOR('C', 210, int)
+#define CMTPGETCONNINFO _IOR('C', 211, int)
+
+#define CMTP_LOOPBACK 0
+
+struct cmtp_connadd_req {
+ int sock; // Connected socket
+ __u32 flags;
+};
+
+struct cmtp_conndel_req {
+ bdaddr_t bdaddr;
+ __u32 flags;
+};
+
+struct cmtp_conninfo {
+ bdaddr_t bdaddr;
+ __u32 flags;
+ __u16 state;
+ int num;
+};
+
+struct cmtp_connlist_req {
+ __u32 cnum;
+ struct cmtp_conninfo *ci;
+};
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
+int cmtp_del_connection(struct cmtp_conndel_req *req);
+int cmtp_get_connlist(struct cmtp_connlist_req *req);
+int cmtp_get_conninfo(struct cmtp_conninfo *ci);
+
+/* CMTP session defines */
+#define CMTP_INTEROP_TIMEOUT (HZ * 5)
+#define CMTP_INITIAL_MSGNUM 0xff00
+
+struct cmtp_session {
+ struct list_head list;
+
+ struct socket *sock;
+
+ bdaddr_t bdaddr;
+
+ unsigned long state;
+ unsigned long flags;
+
+ uint mtu;
+
+ char name[BTNAMSIZ];
+
+ atomic_t terminate;
+
+ wait_queue_head_t wait;
+
+ int ncontroller;
+ int num;
+ struct capi_ctr ctrl;
+
+ struct list_head applications;
+
+ unsigned long blockids;
+ int msgnum;
+
+ struct sk_buff_head transmit;
+
+ struct sk_buff *reassembly[16];
+};
+
+struct cmtp_application {
+ struct list_head list;
+
+ unsigned long state;
+ int err;
+
+ __u16 appl;
+ __u16 mapping;
+
+ __u16 msgnum;
+};
+
+struct cmtp_scb {
+ int id;
+ int data;
+};
+
+int cmtp_attach_device(struct cmtp_session *session);
+void cmtp_detach_device(struct cmtp_session *session);
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+
+static inline void cmtp_schedule(struct cmtp_session *session)
+{
+ struct sock *sk = session->sock->sk;
+
+ wake_up_interruptible(sk->sk_sleep);
+}
+
+/* CMTP init defines */
+int cmtp_init_sockets(void);
+void cmtp_cleanup_sockets(void);
+
+#endif /* __CMTP_H */
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
new file mode 100644
index 000000000000..c5243ce93e67
--- /dev/null
+++ b/net/bluetooth/cmtp/core.c
@@ -0,0 +1,507 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 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 version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static DECLARE_RWSEM(cmtp_session_sem);
+static LIST_HEAD(cmtp_session_list);
+
+static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
+{
+ struct cmtp_session *session;
+ struct list_head *p;
+
+ BT_DBG("");
+
+ list_for_each(p, &cmtp_session_list) {
+ session = list_entry(p, struct cmtp_session, list);
+ if (!bacmp(bdaddr, &session->bdaddr))
+ return session;
+ }
+ return NULL;
+}
+
+static void __cmtp_link_session(struct cmtp_session *session)
+{
+ __module_get(THIS_MODULE);
+ list_add(&session->list, &cmtp_session_list);
+}
+
+static void __cmtp_unlink_session(struct cmtp_session *session)
+{
+ list_del(&session->list);
+ module_put(THIS_MODULE);
+}
+
+static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
+{
+ bacpy(&ci->bdaddr, &session->bdaddr);
+
+ ci->flags = session->flags;
+ ci->state = session->state;
+
+ ci->num = session->num;
+}
+
+
+static inline int cmtp_alloc_block_id(struct cmtp_session *session)
+{
+ int i, id = -1;
+
+ for (i = 0; i < 16; i++)
+ if (!test_and_set_bit(i, &session->blockids)) {
+ id = i;
+ break;
+ }
+
+ return id;
+}
+
+static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
+{
+ clear_bit(id, &session->blockids);
+}
+
+static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
+{
+ struct sk_buff *skb = session->reassembly[id], *nskb;
+ int size;
+
+ BT_DBG("session %p buf %p count %d", session, buf, count);
+
+ size = (skb) ? skb->len + count : count;
+
+ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for CAPI message");
+ return;
+ }
+
+ if (skb && (skb->len > 0))
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+
+ memcpy(skb_put(nskb, count), buf, count);
+
+ session->reassembly[id] = nskb;
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
+{
+ __u8 hdr, hdrlen, id;
+ __u16 len;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ while (skb->len > 0) {
+ hdr = skb->data[0];
+
+ switch (hdr & 0xc0) {
+ case 0x40:
+ hdrlen = 2;
+ len = skb->data[1];
+ break;
+ case 0x80:
+ hdrlen = 3;
+ len = skb->data[1] | (skb->data[2] << 8);
+ break;
+ default:
+ hdrlen = 1;
+ len = 0;
+ break;
+ }
+
+ id = (hdr & 0x3c) >> 2;
+
+ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
+
+ if (hdrlen + len > skb->len) {
+ BT_ERR("Wrong size or header information in CMTP frame");
+ break;
+ }
+
+ if (len == 0) {
+ skb_pull(skb, hdrlen);
+ continue;
+ }
+
+ switch (hdr & 0x03) {
+ case 0x00:
+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+ cmtp_recv_capimsg(session, session->reassembly[id]);
+ session->reassembly[id] = NULL;
+ break;
+ case 0x01:
+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+ break;
+ default:
+ if (session->reassembly[id] != NULL)
+ kfree_skb(session->reassembly[id]);
+ session->reassembly[id] = NULL;
+ break;
+ }
+
+ skb_pull(skb, hdrlen + len);
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
+{
+ struct socket *sock = session->sock;
+ struct iovec iv = { data, len };
+ struct msghdr msg;
+
+ BT_DBG("session %p data %p len %d", session, data, len);
+
+ if (!len)
+ return 0;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iovlen = 1;
+ msg.msg_iov = &iv;
+
+ return sock_sendmsg(sock, &msg, len);
+}
+
+static int cmtp_process_transmit(struct cmtp_session *session)
+{
+ struct sk_buff *skb, *nskb;
+ unsigned char *hdr;
+ unsigned int size, tail;
+
+ BT_DBG("session %p", session);
+
+ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for new frame");
+ return -ENOMEM;
+ }
+
+ while ((skb = skb_dequeue(&session->transmit))) {
+ struct cmtp_scb *scb = (void *) skb->cb;
+
+ if ((tail = (session->mtu - nskb->len)) < 5) {
+ cmtp_send_frame(session, nskb->data, nskb->len);
+ skb_trim(nskb, 0);
+ tail = session->mtu;
+ }
+
+ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
+
+ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
+ skb_queue_head(&session->transmit, skb);
+ break;
+ }
+
+ if (size < 256) {
+ hdr = skb_put(nskb, 2);
+ hdr[0] = 0x40
+ | ((scb->id << 2) & 0x3c)
+ | ((skb->len == size) ? 0x00 : 0x01);
+ hdr[1] = size;
+ } else {
+ hdr = skb_put(nskb, 3);
+ hdr[0] = 0x80
+ | ((scb->id << 2) & 0x3c)
+ | ((skb->len == size) ? 0x00 : 0x01);
+ hdr[1] = size & 0xff;
+ hdr[2] = size >> 8;
+ }
+
+ memcpy(skb_put(nskb, size), skb->data, size);
+ skb_pull(skb, size);
+
+ if (skb->len > 0) {
+ skb_queue_head(&session->transmit, skb);
+ } else {
+ cmtp_free_block_id(session, scb->id);
+ if (scb->data) {
+ cmtp_send_frame(session, nskb->data, nskb->len);
+ skb_trim(nskb, 0);
+ }
+ kfree_skb(skb);
+ }
+ }
+
+ cmtp_send_frame(session, nskb->data, nskb->len);
+
+ kfree_skb(nskb);
+
+ return skb_queue_len(&session->transmit);
+}
+
+static int cmtp_session(void *arg)
+{
+ struct cmtp_session *session = arg;
+ struct sock *sk = session->sock->sk;
+ struct sk_buff *skb;
+ wait_queue_t wait;
+
+ BT_DBG("session %p", session);
+
+ daemonize("kcmtpd_ctr_%d", session->num);
+ set_user_nice(current, -15);
+ current->flags |= PF_IOTHREAD;
+
+ set_fs(KERNEL_DS);
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(sk->sk_sleep, &wait);
+ while (!atomic_read(&session->terminate)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (sk->sk_state != BT_CONNECTED)
+ break;
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ cmtp_recv_frame(session, skb);
+ }
+
+ cmtp_process_transmit(session);
+
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk->sk_sleep, &wait);
+
+ down_write(&cmtp_session_sem);
+
+ if (!(session->flags & (1 << CMTP_LOOPBACK)))
+ cmtp_detach_device(session);
+
+ fput(session->sock->file);
+
+ __cmtp_unlink_session(session);
+
+ up_write(&cmtp_session_sem);
+
+ kfree(session);
+ return 0;
+}
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
+{
+ struct cmtp_session *session, *s;
+ bdaddr_t src, dst;
+ int i, err;
+
+ BT_DBG("");
+
+ baswap(&src, &bt_sk(sock->sk)->src);
+ baswap(&dst, &bt_sk(sock->sk)->dst);
+
+ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
+ if (!session)
+ return -ENOMEM;
+ memset(session, 0, sizeof(struct cmtp_session));
+
+ down_write(&cmtp_session_sem);
+
+ s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
+ if (s && s->state == BT_CONNECTED) {
+ err = -EEXIST;
+ goto failed;
+ }
+
+ bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
+
+ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
+
+ BT_DBG("mtu %d", session->mtu);
+
+ sprintf(session->name, "%s", batostr(&dst));
+
+ session->sock = sock;
+ session->state = BT_CONFIG;
+
+ init_waitqueue_head(&session->wait);
+
+ session->msgnum = CMTP_INITIAL_MSGNUM;
+
+ INIT_LIST_HEAD(&session->applications);
+
+ skb_queue_head_init(&session->transmit);
+
+ for (i = 0; i < 16; i++)
+ session->reassembly[i] = NULL;
+
+ session->flags = req->flags;
+
+ __cmtp_link_session(session);
+
+ err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
+ if (err < 0)
+ goto unlink;
+
+ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+ err = cmtp_attach_device(session);
+ if (err < 0)
+ goto detach;
+ }
+
+ up_write(&cmtp_session_sem);
+ return 0;
+
+detach:
+ cmtp_detach_device(session);
+
+unlink:
+ __cmtp_unlink_session(session);
+
+failed:
+ up_write(&cmtp_session_sem);
+ kfree(session);
+ return err;
+}
+
+int cmtp_del_connection(struct cmtp_conndel_req *req)
+{
+ struct cmtp_session *session;
+ int err = 0;
+
+ BT_DBG("");
+
+ down_read(&cmtp_session_sem);
+
+ session = __cmtp_get_session(&req->bdaddr);
+ if (session) {
+ /* Flush the transmit queue */
+ skb_queue_purge(&session->transmit);
+
+ /* Kill session thread */
+ atomic_inc(&session->terminate);
+ cmtp_schedule(session);
+ } else
+ err = -ENOENT;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+int cmtp_get_connlist(struct cmtp_connlist_req *req)
+{
+ struct list_head *p;
+ int err = 0, n = 0;
+
+ BT_DBG("");
+
+ down_read(&cmtp_session_sem);
+
+ list_for_each(p, &cmtp_session_list) {
+ struct cmtp_session *session;
+ struct cmtp_conninfo ci;
+
+ session = list_entry(p, struct cmtp_session, list);
+
+ __cmtp_copy_session(session, &ci);
+
+ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (++n >= req->cnum)
+ break;
+
+ req->ci++;
+ }
+ req->cnum = n;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+int cmtp_get_conninfo(struct cmtp_conninfo *ci)
+{
+ struct cmtp_session *session;
+ int err = 0;
+
+ down_read(&cmtp_session_sem);
+
+ session = __cmtp_get_session(&ci->bdaddr);
+ if (session)
+ __cmtp_copy_session(session, ci);
+ else
+ err = -ENOENT;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+
+int __init init_cmtp(void)
+{
+ l2cap_load();
+
+ BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
+
+ cmtp_init_sockets();
+
+ return 0;
+}
+
+void __exit exit_cmtp(void)
+{
+ cmtp_cleanup_sockets();
+}
+
+module_init(init_cmtp);
+module_exit(exit_cmtp);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-5");
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
new file mode 100644
index 000000000000..fdb0eb6f8319
--- /dev/null
+++ b/net/bluetooth/cmtp/sock.c
@@ -0,0 +1,199 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 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 version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int cmtp_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct cmtp_connadd_req ca;
+ struct cmtp_conndel_req cd;
+ struct cmtp_connlist_req cl;
+ struct cmtp_conninfo ci;
+ struct socket *nsock;
+ int err;
+
+ BT_DBG("cmd %x arg %lx", cmd, arg);
+
+ switch (cmd) {
+ case CMTPCONNADD:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+ return -EFAULT;
+
+ nsock = sockfd_lookup(ca.sock, &err);
+ if (!nsock)
+ return err;
+
+ if (nsock->sk->sk_state != BT_CONNECTED)
+ return -EBADFD;
+
+ err = cmtp_add_connection(&ca, nsock);
+ if (!err) {
+ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+ err = -EFAULT;
+ } else
+ fput(nsock->file);
+
+ return err;
+
+ case CMTPCONNDEL:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+ return -EFAULT;
+
+ return cmtp_del_connection(&cd);
+
+ case CMTPGETCONNLIST:
+ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+ return -EFAULT;
+
+ if (cl.cnum <= 0)
+ return -EINVAL;
+
+ err = cmtp_get_connlist(&cl);
+ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+ return -EFAULT;
+
+ return err;
+
+ case CMTPGETCONNINFO:
+ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+ return -EFAULT;
+
+ err = cmtp_get_conninfo(&ci);
+ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+ return -EFAULT;
+
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+static struct proto_ops cmtp_sock_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .release = cmtp_sock_release,
+ .ioctl = cmtp_sock_ioctl,
+ .bind = sock_no_bind,
+ .getname = sock_no_getname,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .mmap = sock_no_mmap
+};
+
+static int cmtp_sock_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ BT_DBG("sock %p", sock);
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ if (!(sk = bt_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL)))
+ return -ENOMEM;
+
+ sk_set_owner(sk, THIS_MODULE);
+
+ sock->ops = &cmtp_sock_ops;
+
+ sock->state = SS_UNCONNECTED;
+
+ sk->sk_destruct = NULL;
+ sk->sk_protocol = protocol;
+
+ return 0;
+}
+
+static struct net_proto_family cmtp_sock_family_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .create = cmtp_sock_create
+};
+
+int cmtp_init_sockets(void)
+{
+ bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops);
+
+ return 0;
+}
+
+void cmtp_cleanup_sockets(void)
+{
+ if (bt_sock_unregister(BTPROTO_CMTP))
+ BT_ERR("Can't unregister CMTP socket");
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index dca73e34a16f..132ce9982783 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -71,7 +71,7 @@ void hci_acl_connect(struct hci_conn *conn)
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
- cp.pscan_rep_mode = 0x01;
+ cp.pscan_rep_mode = 0x02;
if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2436a71ed563..b760910ed84e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -406,6 +406,7 @@ int hci_inquiry(unsigned long arg)
hci_dev_lock_bh(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
+ inquiry_cache_empty(hdev) ||
ir.flags & IREQ_CACHE_FLUSH) {
inquiry_cache_flush(hdev);
do_inquiry = 1;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9d70c8c9ff35..262571e2f521 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -452,6 +452,29 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_unlock(hdev);
}
+/* Inquiry Result With RSSI */
+static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct inquiry_info_with_rssi *info = (struct inquiry_info_with_rssi *) (skb->data + 1);
+ int num_rsp = *((__u8 *) skb->data);
+
+ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
+
+ hci_dev_lock(hdev);
+ for (; num_rsp; num_rsp--) {
+ struct inquiry_info tmp;
+ bacpy(&tmp.bdaddr, &info->bdaddr);
+ tmp.pscan_rep_mode = info->pscan_rep_mode;
+ tmp.pscan_period_mode = info->pscan_period_mode;
+ tmp.pscan_mode = 0x00;
+ memcpy(tmp.dev_class, &info->dev_class, 3);
+ tmp.clock_offset = info->clock_offset;
+ info++;
+ inquiry_cache_update(hdev, &tmp);
+ }
+ hci_dev_unlock(hdev);
+}
+
/* Connect Request */
static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -744,6 +767,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_inquiry_result_evt(hdev, skb);
break;
+ case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
+ hci_inquiry_result_with_rssi_evt(hdev, skb);
+ break;
+
case HCI_EV_CONN_REQUEST:
hci_conn_request_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 349f6540f36e..886163ece9ac 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -66,20 +66,20 @@ static struct hci_sec_filter hci_sec_filter = {
/* Packet types */
0x10,
/* Events */
- { 0xd9fe, 0x0 },
+ { 0x1000d9fe, 0x0000300c },
/* Commands */
{
{ 0x0 },
/* OGF_LINK_CTL */
- { 0x2a000002, 0x0, 0x0, 0x0 },
+ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
/* OGF_LINK_POLICY */
- { 0x1200, 0x0, 0x0, 0x0 },
+ { 0x00005200, 0x00000000, 0x0000, 0x00 },
/* OGF_HOST_CTL */
- { 0x80100000, 0x202a, 0x0, 0x0 },
+ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
/* OGF_INFO_PARAM */
- { 0x22a, 0x0, 0x0, 0x0 },
+ { 0x000002be, 0x00000000, 0x0000, 0x00 },
/* OGF_STATUS_PARAM */
- { 0x2e, 0x0, 0x0, 0x0 }
+ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
}
};
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 7280fac62948..a382860e3fb8 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -2202,3 +2202,4 @@ module_exit(l2cap_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-0");
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 148be892afe6..8d2608c39d29 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -52,7 +52,7 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
-#define VERSION "1.0"
+#define VERSION "1.1"
#ifndef CONFIG_BT_RFCOMM_DEBUG
#undef BT_DBG
@@ -207,7 +207,7 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
d->mtu = RFCOMM_DEFAULT_MTU;
d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
- d->credits = RFCOMM_MAX_CREDITS;
+ d->cfc = RFCOMM_CFC_DISABLED;
d->rx_credits = RFCOMM_DEFAULT_CREDITS;
}
@@ -314,8 +314,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
d->state = BT_CONFIG;
rfcomm_dlc_link(s, d);
- d->mtu = s->mtu;
- d->credits = s->credits;
+ d->mtu = s->mtu;
+ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
if (s->state == BT_CONNECTED)
rfcomm_send_pn(s, 1, d);
@@ -415,7 +415,7 @@ void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
- if (!d->credits) {
+ if (!d->cfc) {
d->v24_sig |= RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
@@ -426,7 +426,7 @@ void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
- if (!d->credits) {
+ if (!d->cfc) {
d->v24_sig &= ~RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
@@ -479,8 +479,8 @@ struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
s->state = state;
s->sock = sock;
- s->mtu = RFCOMM_DEFAULT_MTU;
- s->credits = RFCOMM_MAX_CREDITS;
+ s->mtu = RFCOMM_DEFAULT_MTU;
+ s->cfc = RFCOMM_CFC_UNKNOWN;
list_add(&s->list, &session_list);
@@ -752,7 +752,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
pn->ack_timer = 0;
pn->max_retrans = 0;
- if (d->credits) {
+ if (s->cfc) {
pn->flow_ctrl = cr ? 0xf0 : 0xe0;
pn->credits = RFCOMM_DEFAULT_CREDITS;
} else {
@@ -1142,28 +1142,22 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
{
+ struct rfcomm_session *s = d->session;
+
BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
- if (cr) {
- if (pn->flow_ctrl == 0xf0) {
- d->tx_credits = pn->credits;
- } else {
- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
- d->credits = 0;
- }
+ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
+ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
+ d->tx_credits = pn->credits;
} else {
- if (pn->flow_ctrl == 0xe0) {
- d->tx_credits = pn->credits;
- } else {
- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
- d->credits = 0;
- }
+ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
}
d->priority = pn->priority;
- d->mtu = btohs(pn->mtu);
+ d->mtu = s->mtu = btohs(pn->mtu);
return 0;
}
@@ -1353,7 +1347,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
return 0;
if (cr) {
- if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
+ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
else
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
@@ -1444,7 +1438,7 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk
goto drop;
}
- if (pf && d->credits) {
+ if (pf && d->cfc) {
u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
d->tx_credits += credits;
@@ -1549,20 +1543,20 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
struct sk_buff *skb;
int err;
- BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d",
- d, d->state, d->credits, d->rx_credits, d->tx_credits);
+ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
+ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
/* Send pending MSC */
if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
- if (d->credits) {
+ if (d->cfc) {
/* CFC enabled.
* Give them some credits */
if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
- d->rx_credits <= (d->credits >> 2)) {
- rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
- d->rx_credits = d->credits;
+ d->rx_credits <= (d->cfc >> 2)) {
+ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
+ d->rx_credits = d->cfc;
}
} else {
/* CFC disabled.
@@ -1583,7 +1577,7 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
d->tx_credits--;
}
- if (d->credits && !d->tx_credits) {
+ if (d->cfc && !d->tx_credits) {
/* We're out of TX credits.
* Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
@@ -1655,7 +1649,9 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
nsock->type = sock->type;
nsock->ops = sock->ops;
-
+
+ __module_get(nsock->ops->owner);
+
err = sock->ops->accept(sock, nsock, O_NONBLOCK);
if (err < 0) {
sock_release(nsock);
@@ -1998,3 +1994,4 @@ module_exit(rfcomm_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-3");
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 987a01ac4b93..27c0f9ac3007 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -1055,3 +1055,4 @@ module_exit(sco_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-2");
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 220b97a09921..9450f1d7a711 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -252,12 +252,12 @@ int br_get_bridge_ifindices(int *indices, int num)
struct net_device *dev;
int i = 0;
- rtnl_shlock();
+ read_lock(&dev_base_lock);
for (dev = dev_base; dev && i < num; dev = dev->next) {
if (dev->priv_flags & IFF_EBRIDGE)
indices[i++] = dev->ifindex;
}
- rtnl_shunlock();
+ read_unlock(&dev_base_lock);
return i;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 30cba1e1c633..99fb6b454d01 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -621,6 +621,21 @@ struct net_device *__dev_get_by_flags(unsigned short if_flags, unsigned short ma
}
/**
+ * dev_valid_name - check if name is okay for network device
+ * @name: name string
+ *
+ * Network device names need to be valid file names to
+ * to allow sysfs to work
+ */
+int dev_valid_name(const char *name)
+{
+ return !(*name == '\0'
+ || !strcmp(name, ".")
+ || !strcmp(name, "..")
+ || strchr(name, '/'));
+}
+
+/**
* dev_alloc_name - allocate a name for a device
* @dev: device
* @name: name format string
@@ -660,6 +675,41 @@ int dev_alloc_name(struct net_device *dev, const char *name)
return -ENFILE; /* Over 100 of the things .. bail out! */
}
+
+/**
+ * dev_change_name - change name of a device
+ * @dev: device
+ * @name: name (or format string) must be at least IFNAMSIZ
+ *
+ * Change name of a device, can pass format strings "eth%d".
+ * for wildcarding.
+ */
+int dev_change_name(struct net_device *dev, char *newname)
+{
+ ASSERT_RTNL();
+
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (!dev_valid_name(newname))
+ return -EINVAL;
+
+ if (strchr(newname, '%')) {
+ int err = dev_alloc_name(dev, newname);
+ if (err < 0)
+ return err;
+ strcpy(newname, dev->name);
+ }
+ else if (__dev_get_by_name(newname))
+ return -EEXIST;
+ else
+ strlcpy(dev->name, newname, IFNAMSIZ);
+
+ class_device_rename(&dev->class_dev, dev->name);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+ return 0;
+}
+
/**
* dev_alloc - allocate a network device and name
* @name: name format string
@@ -946,11 +996,29 @@ int dev_close(struct net_device *dev)
* The notifier passed is linked into the kernel structures and must
* not be reused until it has been unregistered. A negative errno code
* is returned on a failure.
+ *
+ * When registered all registration and up events are replayed
+ * to the new notifier to allow device to have a race free
+ * view of the network device list.
*/
int register_netdevice_notifier(struct notifier_block *nb)
{
- return notifier_chain_register(&netdev_chain, nb);
+ struct net_device *dev;
+ int err;
+
+ rtnl_lock();
+ err = notifier_chain_register(&netdev_chain, nb);
+ if (!err) {
+ for (dev = dev_base; dev; dev = dev->next) {
+ nb->notifier_call(nb, NETDEV_REGISTER, dev);
+
+ if (dev->flags & IFF_UP)
+ nb->notifier_call(nb, NETDEV_UP, dev);
+ }
+ }
+ rtnl_unlock();
+ return err;
}
/**
@@ -2341,20 +2409,8 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
return 0;
case SIOCSIFNAME:
- if (dev->flags & IFF_UP)
- return -EBUSY;
ifr->ifr_newname[IFNAMSIZ-1] = '\0';
- if (__dev_get_by_name(ifr->ifr_newname))
- return -EEXIST;
- err = class_device_rename(&dev->class_dev,
- ifr->ifr_newname);
- if (!err) {
- strlcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
-
- notifier_call_chain(&netdev_chain,
- NETDEV_CHANGENAME, dev);
- }
- return err;
+ return dev_change_name(dev, ifr->ifr_newname);
/*
* Unknown or private ioctl
@@ -2487,6 +2543,7 @@ int dev_ioctl(unsigned int cmd, void *arg)
*/
case SIOCGMIIPHY:
case SIOCGMIIREG:
+ case SIOCSIFNAME:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev_load(ifr.ifr_name);
@@ -2518,7 +2575,6 @@ int dev_ioctl(unsigned int cmd, void *arg)
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
case SIOCSIFTXQLEN:
- case SIOCSIFNAME:
case SIOCSMIIREG:
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
@@ -2668,6 +2724,11 @@ int register_netdevice(struct net_device *dev)
goto out_err;
}
}
+
+ if (!dev_valid_name(dev->name)) {
+ ret = -EINVAL;
+ goto out_err;
+ }
dev->ifindex = dev_new_index();
if (dev->iflink == -1)
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 09864498d7e7..e8ce25b759ee 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -41,6 +41,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/init.h>
#include <net/ip.h>
#include <net/route.h>
@@ -219,59 +220,78 @@ void dev_mc_discard(struct net_device *dev)
}
#ifdef CONFIG_PROC_FS
-static int dev_mc_read_proc(char *buffer, char **start, off_t offset,
- int length, int *eof, void *data)
+static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos)
{
- off_t pos = 0, begin = 0;
- struct dev_mc_list *m;
- int len = 0;
struct net_device *dev;
+ loff_t off = 0;
read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
- spin_lock_bh(&dev->xmit_lock);
- for (m = dev->mc_list; m; m = m->next) {
- int i;
+ if (off++ == *pos)
+ return dev;
+ }
+ return NULL;
+}
+
+static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct net_device *dev = v;
+ ++*pos;
+ return dev->next;
+}
- len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex,
- dev->name, m->dmi_users, m->dmi_gusers);
+static void dev_mc_seq_stop(struct seq_file *seq, void *v)
+{
+ read_unlock(&dev_base_lock);
+}
- for (i = 0; i < m->dmi_addrlen; i++)
- len += sprintf(buffer+len, "%02x", m->dmi_addr[i]);
- len += sprintf(buffer+len, "\n");
+static int dev_mc_seq_show(struct seq_file *seq, void *v)
+{
+ struct dev_mc_list *m;
+ struct net_device *dev = v;
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset + length) {
- spin_unlock_bh(&dev->xmit_lock);
- goto done;
- }
- }
- spin_unlock_bh(&dev->xmit_lock);
+ spin_lock_bh(&dev->xmit_lock);
+ for (m = dev->mc_list; m; m = m->next) {
+ int i;
+
+ seq_printf(seq, "%-4d %-15s %-5d %-5d ", dev->ifindex,
+ dev->name, m->dmi_users, m->dmi_gusers);
+
+ for (i = 0; i < m->dmi_addrlen; i++)
+ seq_printf(seq, "%02x", m->dmi_addr[i]);
+
+ seq_putc(seq, '\n');
}
- *eof = 1;
+ spin_unlock_bh(&dev->xmit_lock);
+ return 0;
+}
-done:
- read_unlock(&dev_base_lock);
- *start = buffer + (offset - begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- if (len < 0)
- len = 0;
- return len;
+static struct seq_operations dev_mc_seq_ops = {
+ .start = dev_mc_seq_start,
+ .next = dev_mc_seq_next,
+ .stop = dev_mc_seq_stop,
+ .show = dev_mc_seq_show,
+};
+
+static int dev_mc_seq_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &dev_mc_seq_ops);
}
+
+static struct file_operations dev_mc_seq_fops = {
+ .owner = THIS_MODULE,
+ .open = dev_mc_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
#endif
void __init dev_mcast_init(void)
{
-#ifdef CONFIG_PROC_FS
- create_proc_read_entry("net/dev_mcast", 0, 0, dev_mc_read_proc, NULL);
-#endif
+ proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops);
}
EXPORT_SYMBOL(dev_mc_add);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 4b194b6be05a..c1b4a4496309 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1629,7 +1629,8 @@ struct neigh_sysctl_table {
};
int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
- int p_id, int pdev_id, char *p_name)
+ int p_id, int pdev_id, char *p_name,
+ proc_handler *handler)
{
struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
const char *dev_name_source = NULL;
@@ -1643,6 +1644,10 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
t->neigh_vars[1].data = &p->ucast_probes;
t->neigh_vars[2].data = &p->app_probes;
t->neigh_vars[3].data = &p->retrans_time;
+ if (handler) {
+ t->neigh_vars[3].proc_handler = handler;
+ t->neigh_vars[3].extra1 = dev;
+ }
t->neigh_vars[4].data = &p->base_reachable_time;
t->neigh_vars[5].data = &p->delay_probe_time;
t->neigh_vars[6].data = &p->gc_staletime;
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 19ce77c32ac3..03c5c499f5fc 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -2363,17 +2363,16 @@ static int __init decnet_init(void)
if (!dn_sk_cachep)
return -ENOMEM;
- sock_register(&dn_family_ops);
- dev_add_pack(&dn_dix_packet_type);
- register_netdevice_notifier(&dn_dev_notifier);
-
- proc_net_fops_create("decnet", S_IRUGO, &dn_socket_seq_fops);
-
dn_neigh_init();
dn_dev_init();
dn_route_init();
dn_fib_init();
+ sock_register(&dn_family_ops);
+ dev_add_pack(&dn_dix_packet_type);
+ register_netdevice_notifier(&dn_dev_notifier);
+
+ proc_net_fops_create("decnet", S_IRUGO, &dn_socket_seq_fops);
dn_register_sysctl();
return 0;
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index dc026cfacd3d..5c03f63502bc 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1122,7 +1122,7 @@ void __init arp_init(void)
arp_proc_init();
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
- NET_IPV4_NEIGH, "ipv4");
+ NET_IPV4_NEIGH, "ipv4", NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);
}
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index fe58fb5b59da..4bdfd7319ae6 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -155,7 +155,7 @@ struct in_device *inetdev_init(struct net_device *dev)
dev_hold(dev);
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
- NET_IPV4_NEIGH, "ipv4");
+ NET_IPV4_NEIGH, "ipv4", NULL);
#endif
write_lock_bh(&inetdev_lock);
dev->ip_ptr = in_dev;
@@ -910,7 +910,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
devinet_sysctl_unregister(&in_dev->cnf);
neigh_sysctl_unregister(in_dev->arp_parms);
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
- NET_IPV4_NEIGH, "ipv4");
+ NET_IPV4_NEIGH, "ipv4", NULL);
devinet_sysctl_register(in_dev, &in_dev->cnf);
#endif
break;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 1641e4d4a704..b9840662ad5d 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1749,11 +1749,10 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct
goto done;
} else if (pmc->sfmode != omode) {
/* allow mode switches for empty-set filters */
+ ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, 0, 0);
ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
0, 0);
pmc->sfmode = omode;
- ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0,
- 0, 0);
}
psl = pmc->sflist;
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 7ab0b1c81dd2..0676d6c6f813 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -294,6 +294,7 @@ out:
return t;
error:
+ t->km.state = XFRM_STATE_DEAD;
xfrm_state_put(t);
t = NULL;
goto out;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 15049a4d50b5..6421a5f64966 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -205,6 +205,9 @@ static struct net_device *ipmr_reg_vif(void)
dev = alloc_netdev(sizeof(struct net_device_stats), "pimreg",
reg_vif_setup);
+ if (dev == NULL)
+ return NULL;
+
if (register_netdevice(dev)) {
kfree(dev);
return NULL;
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index c0b628bbe549..aa2ac5d8e263 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -529,10 +529,23 @@ config IP_NF_TARGET_TCPMSS
config IP_NF_ARPTABLES
tristate "ARP tables support"
+ help
+ arptables is a general, extensible packet identification framework.
+ The ARP packet filtering and mangling (manipulation)subsystems
+ use this: say Y or M here if you want to use either of those.
+
+ To compile it as a module, choose M here. If unsure, say N.
config IP_NF_ARPFILTER
tristate "ARP packet filtering"
depends on IP_NF_ARPTABLES
+ help
+ ARP packet filtering defines a table `filter', which has a series of
+ rules for simple ARP packet filtering at local input and
+ local output. On a bridge, you can also specify filtering rules
+ for forwarded ARP packets. See the man page for arptables(8).
+
+ To compile it as a module, choose M here. If unsure, say N.
config IP_NF_ARP_MANGLE
tristate "ARP payload mangling"
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 17e1346baaff..d20f6647bc70 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -81,8 +81,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#define IPV6_MAX_ADDRESSES 16
-
/* Set to 3 to get tracing... */
#define ACONF_DEBUG 2
@@ -138,6 +136,8 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
static void addrconf_rs_timer(unsigned long data);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+static void inet6_prefix_notify(int event, struct inet6_dev *idev,
+ struct prefix_info *pinfo);
static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);
static struct notifier_block *inet6addr_chain;
@@ -160,6 +160,7 @@ struct ipv6_devconf ipv6_devconf = {
.regen_max_retry = REGEN_MAX_RETRY,
.max_desync_factor = MAX_DESYNC_FACTOR,
#endif
+ .max_addresses = IPV6_MAX_ADDRESSES,
};
static struct ipv6_devconf ipv6_devconf_dflt = {
@@ -180,6 +181,7 @@ static struct ipv6_devconf ipv6_devconf_dflt = {
.regen_max_retry = REGEN_MAX_RETRY,
.max_desync_factor = MAX_DESYNC_FACTOR,
#endif
+ .max_addresses = IPV6_MAX_ADDRESSES,
};
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -373,9 +375,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
write_unlock_bh(&addrconf_lock);
ipv6_mc_init_dev(ndev);
-
+ ndev->tstamp = jiffies;
#ifdef CONFIG_SYSCTL
- neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,
+ NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
addrconf_sysctl_register(ndev, &ndev->cnf);
#endif
}
@@ -630,6 +633,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
unsigned long tmp_prefered_lft, tmp_valid_lft;
int tmp_plen;
int ret = 0;
+ int max_addresses;
if (ift) {
spin_lock_bh(&ift->lock);
@@ -685,9 +689,11 @@ retry:
ifp->prefered_lft,
idev->cnf.temp_prefered_lft - desync_factor / HZ);
tmp_plen = ifp->prefix_len;
+ max_addresses = idev->cnf.max_addresses;
write_unlock(&idev->lock);
spin_unlock_bh(&ifp->lock);
- ift = ipv6_count_addresses(idev) < IPV6_MAX_ADDRESSES ?
+ ift = !max_addresses ||
+ ipv6_count_addresses(idev) < max_addresses ?
ipv6_add_addr(idev, &addr, tmp_plen,
ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : 0;
if (!ift || IS_ERR(ift)) {
@@ -1390,10 +1396,13 @@ ok:
ifp = ipv6_get_ifaddr(&addr, dev);
if (ifp == NULL && valid_lft) {
+ int max_addresses = in6_dev->cnf.max_addresses;
+
/* Do not allow to create too much of autoconfigured
* addresses; this would be too easy way to crash kernel.
*/
- if (ipv6_count_addresses(in6_dev) < IPV6_MAX_ADDRESSES)
+ if (!max_addresses ||
+ ipv6_count_addresses(in6_dev) < max_addresses)
ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
addr_type&IPV6_ADDR_SCOPE_MASK, 0);
@@ -1491,6 +1500,7 @@ ok:
addrconf_verify(0);
}
}
+ inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
in6_dev_put(in6_dev);
}
@@ -1858,8 +1868,8 @@ static void addrconf_ip6_tnl_config(struct net_device *dev)
addrconf_add_mroute(dev);
}
-int addrconf_notify(struct notifier_block *this, unsigned long event,
- void * data)
+static int addrconf_notify(struct notifier_block *this, unsigned long event,
+ void * data)
{
struct net_device *dev = (struct net_device *) data;
struct inet6_dev *idev = __in6_dev_get(dev);
@@ -1890,6 +1900,8 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
rt6_mtu_change(dev, dev->mtu);
idev->cnf.mtu6 = dev->mtu;
}
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
/* If the changed mtu during down is lower than IPV6_MIN_MTU
stop IPv6 on this interface.
*/
@@ -1921,7 +1933,7 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
if (idev) {
addrconf_sysctl_unregister(&idev->cnf);
neigh_sysctl_unregister(idev->nd_parms);
- neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
addrconf_sysctl_register(idev, &idev->cnf);
}
#endif
@@ -1931,6 +1943,14 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
return NOTIFY_OK;
}
+/*
+ * addrconf module should be notified of a device going up
+ */
+static struct notifier_block ipv6_dev_notf = {
+ .notifier_call = addrconf_notify,
+ .priority = 0
+};
+
static int addrconf_ifdown(struct net_device *dev, int how)
{
struct inet6_dev *idev;
@@ -2023,6 +2043,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
else
ipv6_mc_down(idev);
+ /* Step 5: netlink notification of this interface */
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
+
/* Shot the device (if unregistered) */
if (how == 1) {
@@ -2722,19 +2746,22 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
#endif
+ array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
}
-static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
- struct inet6_dev *idev,
- int type, u32 pid, u32 seq)
+static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
+ u32 pid, u32 seq, int event)
{
+ struct net_device *dev = idev->dev;
__s32 *array = NULL;
struct ifinfomsg *r;
struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
struct rtattr *subattr;
+ __u32 mtu = dev->mtu;
+ struct ifla_cacheinfo ci;
- nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
r = NLMSG_DATA(nlh);
r->ifi_family = AF_INET6;
@@ -2749,6 +2776,13 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+ if (dev->addr_len)
+ RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
+
+ RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
+ if (dev->ifindex != dev->iflink)
+ RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
+
subattr = (struct rtattr*)skb->tail;
RTA_PUT(skb, IFLA_PROTINFO, 0, NULL);
@@ -2756,6 +2790,14 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
/* return the device flags */
RTA_PUT(skb, IFLA_INET6_FLAGS, sizeof(__u32), &idev->if_flags);
+ /* return interface cacheinfo */
+ ci.max_reasm_len = IPV6_MAXPLEN;
+ ci.tstamp = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100
+ + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ);
+ ci.reachable_time = idev->nd_parms->reachable_time;
+ ci.retrans_time = idev->nd_parms->retrans_time;
+ RTA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci);
+
/* return the device sysctl params */
if ((array = kmalloc(DEVCONF_MAX * sizeof(*array), GFP_ATOMIC)) == NULL)
goto rtattr_failure;
@@ -2790,8 +2832,8 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
continue;
if ((idev = in6_dev_get(dev)) == NULL)
continue;
- err = inet6_fill_ifinfo(skb, dev, idev, RTM_NEWLINK,
- NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq);
+ err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWLINK);
in6_dev_put(idev);
if (err <= 0)
break;
@@ -2802,6 +2844,86 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
+void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
+{
+ struct sk_buff *skb;
+ /* 128 bytes ?? */
+ int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+128);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFINFO, ENOBUFS);
+ return;
+ }
+ if (inet6_fill_ifinfo(skb, idev, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFINFO, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFINFO;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFINFO, GFP_ATOMIC);
+}
+
+static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
+ struct prefix_info *pinfo, u32 pid, u32 seq, int event)
+{
+ struct prefixmsg *pmsg;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct prefix_cacheinfo ci;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*pmsg));
+
+ if (pid)
+ nlh->nlmsg_flags |= NLM_F_MULTI;
+
+ pmsg = NLMSG_DATA(nlh);
+ pmsg->prefix_family = AF_INET6;
+ pmsg->prefix_ifindex = idev->dev->ifindex;
+ pmsg->prefix_len = pinfo->prefix_len;
+ pmsg->prefix_type = pinfo->type;
+
+ pmsg->prefix_flags = 0;
+ if (pinfo->onlink)
+ pmsg->prefix_flags |= IF_PREFIX_ONLINK;
+ if (pinfo->autoconf)
+ pmsg->prefix_flags |= IF_PREFIX_AUTOCONF;
+
+ RTA_PUT(skb, PREFIX_ADDRESS, sizeof(pinfo->prefix), &pinfo->prefix);
+
+ ci.preferred_time = ntohl(pinfo->prefered);
+ ci.valid_time = ntohl(pinfo->valid);
+ RTA_PUT(skb, PREFIX_CACHEINFO, sizeof(ci), &ci);
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static void inet6_prefix_notify(int event, struct inet6_dev *idev,
+ struct prefix_info *pinfo)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct prefixmsg)+128);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_PREFIX, ENOBUFS);
+ return;
+ }
+ if (inet6_fill_prefix(skb, idev, pinfo, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_PREFIX, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_PREFIX;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_PREFIX, GFP_ATOMIC);
+}
+
static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
[RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, },
[RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, },
@@ -3050,6 +3172,14 @@ static struct addrconf_sysctl_table
.proc_handler = &proc_dointvec,
},
#endif
+ {
+ .ctl_name = NET_IPV6_MAX_ADDRESSES,
+ .procname = "max_addresses",
+ .data = &ipv6_devconf.max_addresses,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
},
.addrconf_dev = {
{
@@ -3179,9 +3309,7 @@ int unregister_inet6addr_notifier(struct notifier_block *nb)
void __init addrconf_init(void)
{
-#ifdef MODULE
- struct net_device *dev;
-#endif
+ register_netdevice_notifier(&ipv6_dev_notf);
#ifdef CONFIG_IPV6_PRIVACY
md5_tfm = crypto_alloc_tfm("md5", 0);
@@ -3190,30 +3318,6 @@ void __init addrconf_init(void)
"failed to load transform for md5\n");
#endif
-#ifdef MODULE
- /* This takes sense only during module load. */
- rtnl_lock();
- for (dev = dev_base; dev; dev = dev->next) {
- if (!(dev->flags&IFF_UP))
- continue;
-
- switch (dev->type) {
- case ARPHRD_LOOPBACK:
- init_loopback(dev);
- break;
- case ARPHRD_ETHER:
- case ARPHRD_FDDI:
- case ARPHRD_IEEE802_TR:
- case ARPHRD_ARCNET:
- addrconf_dev_config(dev);
- break;
- default:;
- /* Ignore all other */
- }
- }
- rtnl_unlock();
-#endif
-
addrconf_verify(0);
rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
#ifdef CONFIG_SYSCTL
@@ -3231,6 +3335,8 @@ void addrconf_cleanup(void)
struct inet6_ifaddr *ifa;
int i;
+ unregister_netdevice_notifier(&ipv6_dev_notf);
+
rtnetlink_links[PF_INET6] = NULL;
#ifdef CONFIG_SYSCTL
addrconf_sysctl_unregister(&ipv6_devconf_dflt);
@@ -3284,3 +3390,4 @@ void addrconf_cleanup(void)
#endif
}
#endif /* MODULE */
+
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index df60b4030a44..6d450f2909e9 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -802,7 +802,6 @@ static int __init inet6_init(void)
if (if6_proc_init())
goto proc_if6_fail;
#endif
- ipv6_netdev_notif_init();
ipv6_packet_init();
ip6_route_init();
ip6_flowlabel_init();
@@ -869,7 +868,6 @@ static void inet6_exit(void)
#endif
/* Cleanup code parts. */
sit_cleanup();
- ipv6_netdev_notif_cleanup();
ip6_flowlabel_cleanup();
addrconf_cleanup();
ip6_route_cleanup();
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 505abe0809de..c9aa51f318b3 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -62,14 +62,6 @@ static struct packet_type ipv6_packet_type = {
.func = ipv6_rcv,
};
-/*
- * addrconf module should be notified of a device going up
- */
-static struct notifier_block ipv6_dev_notf = {
- .notifier_call = addrconf_notify,
- .priority = 0
-};
-
struct ip6_ra_chain *ip6_ra_chain;
rwlock_t ip6_ra_lock = RW_LOCK_UNLOCKED;
@@ -707,19 +699,9 @@ void __init ipv6_packet_init(void)
dev_add_pack(&ipv6_packet_type);
}
-void __init ipv6_netdev_notif_init(void)
-{
- register_netdevice_notifier(&ipv6_dev_notf);
-}
-
#ifdef MODULE
void ipv6_packet_cleanup(void)
{
dev_remove_pack(&ipv6_packet_type);
}
-
-void ipv6_netdev_notif_cleanup(void)
-{
- unregister_netdevice_notifier(&ipv6_dev_notf);
-}
#endif
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 72fd445ab217..0e9dfa644e80 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -372,9 +372,9 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
goto done;
} else if (pmc->sfmode != omode) {
/* allow mode switches for empty-set filters */
+ ip6_mc_add_src(idev, group, omode, 0, 0, 0);
ip6_mc_del_src(idev, group, pmc->sfmode, 0, 0, 0);
pmc->sfmode = omode;
- ip6_mc_del_src(idev, group, pmc->sfmode, 0, 0, 0);
}
psl = pmc->sflist;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f2e7c8bb7fe9..f725744e6d48 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1115,6 +1115,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
if (rtime < HZ/10)
rtime = HZ/10;
in6_dev->nd_parms->retrans_time = rtime;
+ in6_dev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
}
rtime = ntohl(ra_msg->reachable_time);
@@ -1128,6 +1130,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
in6_dev->nd_parms->base_reachable_time = rtime;
in6_dev->nd_parms->gc_staletime = 3 * rtime;
in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
+ in6_dev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
}
}
}
@@ -1492,6 +1496,21 @@ struct notifier_block ndisc_netdev_notifier = {
.notifier_call = ndisc_netdev_event,
};
+#ifdef CONFIG_SYSCTL
+int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp)
+{
+ struct net_device *dev = ctl->extra1;
+ struct inet6_dev *idev;
+
+ if (write && dev && (idev = in6_dev_get(dev)) != NULL) {
+ idev->tstamp = jiffies;
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
+ in6_dev_put(idev);
+ }
+ return proc_dointvec(ctl, write, filp, buffer, lenp);
+}
+#endif
+
int __init ndisc_init(struct net_proto_family *ops)
{
struct ipv6_pinfo *np;
@@ -1522,7 +1541,8 @@ int __init ndisc_init(struct net_proto_family *ops)
neigh_table_init(&nd_tbl);
#ifdef CONFIG_SYSCTL
- neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
+ "ipv6", &ndisc_ifinfo_sysctl_change);
#endif
register_netdevice_notifier(&ndisc_netdev_notifier);
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 3dd5b294c58d..6b9ae8cd9062 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -86,7 +86,9 @@ static struct tty_operations ops = {
.write_room = ircomm_tty_write_room,
.chars_in_buffer = ircomm_tty_chars_in_buffer,
.flush_buffer = ircomm_tty_flush_buffer,
- .ioctl = ircomm_tty_ioctl,
+ .ioctl = ircomm_tty_ioctl, /* ircomm_tty_ioctl.c */
+ .tiocmget = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */
+ .tiocmset = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */
.throttle = ircomm_tty_throttle,
.unthrottle = ircomm_tty_unthrottle,
.send_xchar = ircomm_tty_send_xchar,
diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c
index d1ce743b5771..cf5778686c10 100644
--- a/net/irda/ircomm/ircomm_tty_ioctl.c
+++ b/net/irda/ircomm/ircomm_tty_ioctl.c
@@ -190,81 +190,62 @@ void ircomm_tty_set_termios(struct tty_struct *tty,
}
/*
- * Function ircomm_tty_get_modem_info (self, value)
+ * Function ircomm_tty_tiocmget (tty, file)
*
*
*
*/
-static int ircomm_tty_get_modem_info(struct ircomm_tty_cb *self,
- unsigned int *value)
+int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file)
{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int result;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+
result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
| ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
| ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
| ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
| ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
| ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
-
- return put_user(result, value);
+ return result;
}
/*
- * Function set_modem_info (driver, cmd, value)
+ * Function ircomm_tty_tiocmset (tty, file, set, clear)
*
*
*
*/
-static int ircomm_tty_set_modem_info(struct ircomm_tty_cb *self,
- unsigned int cmd, unsigned int *value)
+int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
{
- unsigned int arg;
- __u8 old_rts, old_dtr;
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
- if (get_user(arg, value))
- return -EFAULT;
+ if (set & TIOCM_RTS)
+ self->settings.dte |= IRCOMM_RTS;
+ if (set & TIOCM_DTR)
+ self->settings.dte |= IRCOMM_DTR;
- old_rts = self->settings.dte & IRCOMM_RTS;
- old_dtr = self->settings.dte & IRCOMM_DTR;
+ if (clear & TIOCM_RTS)
+ self->settings.dte &= ~IRCOMM_RTS;
+ if (clear & TIOCM_DTR)
+ self->settings.dte &= ~IRCOMM_DTR;
- switch (cmd) {
- case TIOCMBIS:
- if (arg & TIOCM_RTS)
- self->settings.dte |= IRCOMM_RTS;
- if (arg & TIOCM_DTR)
- self->settings.dte |= IRCOMM_DTR;
- break;
-
- case TIOCMBIC:
- if (arg & TIOCM_RTS)
- self->settings.dte &= ~IRCOMM_RTS;
- if (arg & TIOCM_DTR)
- self->settings.dte &= ~IRCOMM_DTR;
- break;
-
- case TIOCMSET:
- self->settings.dte =
- ((self->settings.dte & ~(IRCOMM_RTS | IRCOMM_DTR))
- | ((arg & TIOCM_RTS) ? IRCOMM_RTS : 0)
- | ((arg & TIOCM_DTR) ? IRCOMM_DTR : 0));
- break;
-
- default:
- return -EINVAL;
- }
-
- if ((self->settings.dte & IRCOMM_RTS) != old_rts)
+ if ((set|clear) & TIOCM_RTS)
self->settings.dte |= IRCOMM_DELTA_RTS;
-
- if ((self->settings.dte & IRCOMM_DTR) != old_dtr)
+ if ((set|clear) & TIOCM_DTR)
self->settings.dte |= IRCOMM_DELTA_DTR;
ircomm_param_request(self, IRCOMM_DTE, TRUE);
@@ -406,14 +387,6 @@ int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file,
}
switch (cmd) {
- case TIOCMGET:
- ret = ircomm_tty_get_modem_info(self, (unsigned int *) arg);
- break;
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- ret = ircomm_tty_set_modem_info(self, cmd, (unsigned int *) arg);
- break;
case TIOCGSERIAL:
ret = ircomm_tty_get_serial_info(self, (struct serial_struct *) arg);
break;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index b0bbc8c9918d..49a49d3d72ac 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1283,6 +1283,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ __u8 proto;
struct sk_buff *out_skb;
struct sadb_msg *out_hdr;
struct xfrm_state *x;
@@ -1297,6 +1298,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
return -ESRCH;
out_skb = pfkey_xfrm_state2msg(x, 1, 3);
+ proto = x->id.proto;
xfrm_state_put(x);
if (IS_ERR(out_skb))
return PTR_ERR(out_skb);
@@ -1304,7 +1306,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
out_hdr = (struct sadb_msg *) out_skb->data;
out_hdr->sadb_msg_version = hdr->sadb_msg_version;
out_hdr->sadb_msg_type = SADB_DUMP;
- out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
+ out_hdr->sadb_msg_satype = pfkey_proto2satype(proto);
out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_reserved = 0;
out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 3b1e907174e0..3b01f540b652 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1550,7 +1550,7 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
unsigned long *pg_vec = NULL;
struct tpacket_hdr **io_vec = NULL;
struct packet_opt *po = pkt_sk(sk);
- int order = 0;
+ int was_running, num, order = 0;
int err = 0;
if (req->tp_block_nr) {
@@ -1623,10 +1623,13 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
/* Detach socket from network */
spin_lock(&po->bind_lock);
- if (po->running) {
+ was_running = po->running;
+ num = po->num;
+ if (was_running) {
__dev_remove_pack(&po->prot_hook);
po->num = 0;
po->running = 0;
+ __sock_put(sk);
}
spin_unlock(&po->bind_lock);
@@ -1657,8 +1660,12 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
}
spin_lock(&po->bind_lock);
- if (po->running)
+ if (was_running && !po->running) {
+ sock_hold(sk);
+ po->running = 1;
+ po->num = num;
dev_add_pack(&po->prot_hook);
+ }
spin_unlock(&po->bind_lock);
release_sock(sk);
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 260d431650a9..7bc3194cdb97 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1372,9 +1372,6 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
static int __init x25_init(void)
{
-#ifdef MODULE
- struct net_device *dev;
-#endif /* MODULE */
sock_register(&x25_family_ops);
dev_add_pack(&x25_packet_type);
@@ -1386,23 +1383,7 @@ static int __init x25_init(void)
#ifdef CONFIG_SYSCTL
x25_register_sysctl();
#endif
-
x25_proc_init();
-#ifdef MODULE
- /*
- * Register any pre existing devices.
- */
- read_lock(&dev_base_lock);
- for (dev = dev_base; dev; dev = dev->next) {
- if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
-#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
- || dev->type == ARPHRD_ETHER
-#endif
- ))
- x25_link_device_up(dev);
- }
- read_unlock(&dev_base_lock);
-#endif /* MODULE */
return 0;
}
module_init(x25_init);
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index c6b6bcce318d..0612ecbc2764 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -85,7 +85,7 @@ static struct xfrm_algo_desc aalg_list[] = {
.uinfo = {
.auth = {
- .icv_truncbits = 128,
+ .icv_truncbits = 96,
.icv_fullbits = 256,
}
},
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0f970906c9ef..d4005d3bb2fd 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -775,33 +775,32 @@ restart:
if (unlikely(nx<0)) {
err = nx;
- if (err == -EAGAIN) {
- struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait, tsk);
- if (!flags)
- goto error;
+ if (err == -EAGAIN && !flags) {
+ DECLARE_WAITQUEUE(wait, current);
- __set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue(&km_waitq, &wait);
- err = xfrm_tmpl_resolve(policy, fl, xfrm, family);
- if (err == -EAGAIN)
- schedule();
- __set_task_state(tsk, TASK_RUNNING);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&km_waitq, &wait);
- if (err == -EAGAIN && signal_pending(current)) {
+ nx = xfrm_tmpl_resolve(policy, fl, xfrm, family);
+
+ if (nx == -EAGAIN && signal_pending(current)) {
err = -ERESTART;
goto error;
}
- if (err == -EAGAIN ||
+ if (nx == -EAGAIN ||
genid != atomic_read(&flow_cache_genid)) {
xfrm_pol_put(policy);
goto restart;
}
+ err = nx;
}
- if (err)
+ if (err < 0)
goto error;
- } else if (nx == 0) {
+ }
+ if (nx == 0) {
/* Flow passes not transformed. */
xfrm_pol_put(policy);
return 0;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 1296a7aa8f43..b02206f33b6a 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -241,6 +241,7 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
return x;
error:
+ x->km.state = XFRM_STATE_DEAD;
xfrm_state_put(x);
error_no_put:
*errp = err;