diff options
| author | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-18 01:22:30 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2004-01-18 01:22:30 -0800 |
| commit | 55b5b9683a59a854ab2b71632c19283a24cf5668 (patch) | |
| tree | 9f7e2c9a2169d2fff49197300b3423b4cfacc831 | |
| parent | 755fa12bb5387c7866f641504424d146c9b62f58 (diff) | |
| parent | d9189b24bd2bbb9237589ef530f34c96913b985d (diff) | |
Merge bk://kernel.bkbits.net/davem/net-2.6
into home.osdl.org:/home/torvalds/v2.5/linux
77 files changed, 3540 insertions, 529 deletions
@@ -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; |
