From 2c67568903d6ae1b8cfa343c649029180239418e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Aug 2014 13:02:36 +0200 Subject: spi: Add missing kerneldoc bits These are all arguments or fields that got added without updating the kerneldoc comments. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e713543336f1..46d188a9947c 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -253,6 +253,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * the device whose settings are being modified. * @transfer: adds a message to the controller's transfer queue. * @cleanup: frees controller-specific state + * @can_dma: determine whether this master supports DMA * @queued: whether this master is providing an internal message queue * @kworker: thread struct for message pump * @kworker_task: pointer to task for message pump kworker thread @@ -262,6 +263,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @cur_msg: the currently in-flight message * @cur_msg_prepared: spi_prepare_message was called for the currently * in-flight message + * @cur_msg_mapped: message has been mapped for DMA * @xfer_completion: used by core transfer_one_message() * @busy: message pump is busy * @running: message pump is running @@ -299,6 +301,10 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS * number. Any individual value may be -ENOENT for CS lines that * are not GPIOs (driven by the SPI controller itself). + * @dma_tx: DMA transmit channel + * @dma_rx: DMA receive channel + * @dummy_rx: dummy receive buffer for full-duplex devices + * @dummy_tx: dummy transmit buffer for full-duplex devices * * Each SPI master controller can communicate with one or more @spi_device * children. These make a small bus, sharing MOSI, MISO and SCK signals @@ -632,6 +638,7 @@ struct spi_transfer { * addresses for each transfer buffer * @complete: called to report transaction completions * @context: the argument to complete() when it's called + * @frame_length: the total number of bytes in the message * @actual_length: the total number of bytes that were transferred in all * successful segments * @status: zero for success, else negative errno -- cgit v1.2.3 From a5fe8e7695dc3f547e955ad2b662e3e72969e506 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 11 Jun 2014 10:23:35 +0300 Subject: regulatory: add NUL to alpha2 alpha2 is defined as 2-chars array, but is used in multiple places as string (e.g. with nla_put_string calls), which might leak kernel data. Solve it by simply adding an extra char for the NULL terminator, making such operations safe. Cc: stable@vger.kernel.org Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- include/net/regulatory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 259992444e80..dad7ab20a8cb 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -167,7 +167,7 @@ struct ieee80211_reg_rule { struct ieee80211_regdomain { struct rcu_head rcu_head; u32 n_reg_rules; - char alpha2[2]; + char alpha2[3]; enum nl80211_dfs_regions dfs_region; struct ieee80211_reg_rule reg_rules[]; }; -- cgit v1.2.3 From 8a58d1f1f373238cb0d6d7f8d3dd723aa164b8ac Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 15 Aug 2014 12:38:41 -0600 Subject: blk-mq: get rid of unused BLK_MQ_F_SHOULD_SORT flag We used to use this for determining whether to sort the dispatch list, but it's unused now. Signed-off-by: Jens Axboe --- include/linux/blk-mq.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index eb726b9c5762..a1e31f274fcd 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -127,10 +127,9 @@ enum { BLK_MQ_RQ_QUEUE_ERROR = 2, /* end IO with error */ BLK_MQ_F_SHOULD_MERGE = 1 << 0, - BLK_MQ_F_SHOULD_SORT = 1 << 1, - BLK_MQ_F_TAG_SHARED = 1 << 2, - BLK_MQ_F_SG_MERGE = 1 << 3, - BLK_MQ_F_SYSFS_UP = 1 << 4, + BLK_MQ_F_TAG_SHARED = 1 << 1, + BLK_MQ_F_SG_MERGE = 1 << 2, + BLK_MQ_F_SYSFS_UP = 1 << 3, BLK_MQ_S_STOPPED = 0, BLK_MQ_S_TAG_ACTIVE = 1, -- cgit v1.2.3 From 16466f4284154311f163a58b77379eb186274f87 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 14 Aug 2014 16:52:51 -0700 Subject: net: phy: bcm7xxx: remove 28nm wildcard entry A wildcard entry with the 32-bits OUI 0x600d8400 was added as part of the BCM7xxx internal PHY driver, but that entry might match other PHYs that are not covered by this driver, so let's just remove it. Fixes: b560a58c45c6 ("net: phy: add Broadcom BCM7xxx internal PHY driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 14 -------------- include/linux/brcmphy.h | 1 - 2 files changed, 15 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 526b94cea569..2b40548c85d5 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -286,19 +286,6 @@ static struct phy_driver bcm7xxx_driver[] = { .suspend = bcm7xxx_suspend, .resume = bcm7xxx_28nm_config_init, .driver = { .owner = THIS_MODULE }, -}, { - .name = "Broadcom BCM7XXX 28nm", - .phy_id = PHY_ID_BCM7XXX_28, - .phy_id_mask = PHY_BCM_OUI_MASK, - .features = PHY_GBIT_FEATURES | - SUPPORTED_Pause | SUPPORTED_Asym_Pause, - .flags = PHY_IS_INTERNAL, - .config_init = bcm7xxx_28nm_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - .suspend = bcm7xxx_suspend, - .resume = bcm7xxx_28nm_config_init, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_BCM_OUI_4, .phy_id_mask = 0xffff0000, @@ -331,7 +318,6 @@ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM7366, 0xfffffff0, }, { PHY_ID_BCM7439, 0xfffffff0, }, { PHY_ID_BCM7445, 0xfffffff0, }, - { PHY_ID_BCM7XXX_28, 0xfffffc00 }, { PHY_BCM_OUI_4, 0xffff0000 }, { PHY_BCM_OUI_5, 0xffffff00 }, { } diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 6f76277baf39..61219b9b3445 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -16,7 +16,6 @@ #define PHY_ID_BCM7366 0x600d8490 #define PHY_ID_BCM7439 0x600d8480 #define PHY_ID_BCM7445 0x600d8510 -#define PHY_ID_BCM7XXX_28 0x600d8400 #define PHY_BCM_OUI_MASK 0xfffffc00 #define PHY_BCM_OUI_1 0x00206000 -- cgit v1.2.3 From 4d61b39bc117b36682c1dd67ee386960ae826bef Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 18 Aug 2014 14:53:03 +0530 Subject: ASoC: core: fix .info for SND_SOC_BYTES_TLV Commit 7523a271 - "ASoC: core: add a helper for extended byte controls using TLV" introduced support for TLV byte controls but had a typo for the info function, so fix the same Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index be6ecae247b0..c83a334dd00f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -277,7 +277,7 @@ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ .tlv.c = (snd_soc_bytes_tlv_callback), \ - .info = snd_soc_info_bytes_ext, \ + .info = snd_soc_bytes_info_ext, \ .private_value = (unsigned long)&(struct soc_bytes_ext) \ {.max = xcount, .get = xhandler_get, .put = xhandler_put, } } #define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \ -- cgit v1.2.3 From 503e6636b6f96056210062be703356f4253b6db9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 11 Aug 2014 14:24:47 +0100 Subject: asm-generic: add memfd_create system call to unistd.h Commit 9183df25fe7b ("shm: add memfd_create() syscall") added a new system call (memfd_create) but didn't update the asm-generic unistd header. This patch adds the new system call to the asm-generic version of unistd.h so that it can be used by architectures such as arm64. Cc: Arnd Bergmann Reviewed-by: David Herrmann Signed-off-by: Will Deacon --- include/uapi/asm-generic/unistd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index f1afd607f043..11d11bc5c78f 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -703,9 +703,11 @@ __SYSCALL(__NR_renameat2, sys_renameat2) __SYSCALL(__NR_seccomp, sys_seccomp) #define __NR_getrandom 278 __SYSCALL(__NR_getrandom, sys_getrandom) +#define __NR_memfd_create 279 +__SYSCALL(__NR_memfd_create, sys_memfd_create) #undef __NR_syscalls -#define __NR_syscalls 279 +#define __NR_syscalls 280 /* * All syscalls below here should go away really, -- cgit v1.2.3 From 701e1e789142042144c8cc10b8f6d1554e960144 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 15 Aug 2014 11:52:53 +0200 Subject: drm/radeon: properly document reloc priority mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of hard coding the value properly document that this is an userspace interface. No intended functional change. Signed-off-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/radeon_cs.c | 3 ++- include/uapi/drm/radeon_drm.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index ee712c199b25..cb12df784d83 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -132,7 +132,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) * the buffers used for read only, which doubles the range * to 0 to 31. 32 is reserved for the kernel driver. */ - priority = (r->flags & 0xf) * 2 + !!r->write_domain; + priority = (r->flags & RADEON_RELOC_PRIO_MASK) * 2 + + !!r->write_domain; /* the first reloc of an UVD job is the msg and that must be in VRAM, also but everything into VRAM on AGP cards to avoid diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index 509b2d7a41b7..fea6099608ef 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -944,6 +944,7 @@ struct drm_radeon_cs_chunk { }; /* drm_radeon_cs_reloc.flags */ +#define RADEON_RELOC_PRIO_MASK (0xf << 0) struct drm_radeon_cs_reloc { uint32_t handle; -- cgit v1.2.3 From 366047515c6eab2ff886bc28d1c2b0ad041d040a Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Fri, 15 Aug 2014 13:38:59 +0800 Subject: i2c: rework kernel config I2C_ACPI Commit da3c6647(I2C/ACPI: Clean up I2C ACPI code and Add CONFIG_I2C_ACPI config) adds a new kernel config I2C_ACPI and make I2C core built in when the config is selected. This is wrong because distributions etc generally compile I2C as a module and the commit broken that. This patch is to rename I2C_ACPI to ACPI_I2C_OPREGION. New config only controls ACPI I2C operation region code and depends on I2C=y. Signed-off-by: Lan Tianyu Reviewed-by: Mika Westerberg [wsa: removed unrelated change for Kconfig] Signed-off-by: Wolfram Sang --- drivers/i2c/Kconfig | 15 ++++++--------- drivers/i2c/Makefile | 2 +- drivers/i2c/i2c-acpi.c | 2 ++ include/linux/i2c.h | 12 ++++++++---- 4 files changed, 17 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 3e3b680dc007..b51a402752c4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -23,17 +23,14 @@ config I2C This I2C support can also be built as a module. If so, the module will be called i2c-core. -config I2C_ACPI - bool "I2C ACPI support" - select I2C - depends on ACPI +config ACPI_I2C_OPREGION + bool "ACPI I2C Operation region support" + depends on I2C=y && ACPI default y help - Say Y here if you want to enable ACPI I2C support. This includes support - for automatic enumeration of I2C slave devices and support for ACPI I2C - Operation Regions. Operation Regions allow firmware (BIOS) code to - access I2C slave devices, such as smart batteries through an I2C host - controller driver. + Say Y here if you want to enable ACPI I2C operation region support. + Operation Regions allow firmware (BIOS) code to access I2C slave devices, + such as smart batteries through an I2C host controller driver. if I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a1f590cbb435..e0228b228256 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -3,7 +3,7 @@ # i2ccore-y := i2c-core.o -i2ccore-$(CONFIG_I2C_ACPI) += i2c-acpi.o +i2ccore-$(CONFIG_ACPI) += i2c-acpi.o obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2ccore.o diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c index e8b61967334b..0dbc18c15c43 100644 --- a/drivers/i2c/i2c-acpi.c +++ b/drivers/i2c/i2c-acpi.c @@ -126,6 +126,7 @@ void acpi_i2c_register_devices(struct i2c_adapter *adap) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } +#ifdef CONFIG_ACPI_I2C_OPREGION static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { @@ -360,3 +361,4 @@ void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) acpi_bus_detach_private_data(handle); } +#endif diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ea507665896c..a95efeb53a8b 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -577,16 +577,20 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ -#ifdef CONFIG_I2C_ACPI -int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); -void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +#ifdef CONFIG_ACPI void acpi_i2c_register_devices(struct i2c_adapter *adap); #else static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_ACPI_I2C_OPREGION +int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); +void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +#else static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) { } static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) { return 0; } -#endif +#endif /* CONFIG_ACPI_I2C_OPREGION */ #endif /* _LINUX_I2C_H */ -- cgit v1.2.3 From 6697dabe27e03302ddfddc975275e6401defe2dd Mon Sep 17 00:00:00 2001 From: Martin Townsend Date: Tue, 19 Aug 2014 19:03:32 +0200 Subject: ieee802154: 6lowpan: ensure MTU of 1280 for 6lowpan This patch drops the userspace accessable sysfs entry for the maximum datagram size of a 6LoWPAN fragment packet. A fragment should not have a datagram size value greater than 1280 byte. Instead of make this value configurable, we accept 1280 datagram size fragment packets only. Signed-off-by: Martin Townsend Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/net/netns/ieee802154_6lowpan.h | 1 - net/ieee802154/reassembly.c | 15 +++------------ 2 files changed, 3 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h index e2070960bac0..8170f8d7052b 100644 --- a/include/net/netns/ieee802154_6lowpan.h +++ b/include/net/netns/ieee802154_6lowpan.h @@ -16,7 +16,6 @@ struct netns_sysctl_lowpan { struct netns_ieee802154_lowpan { struct netns_sysctl_lowpan sysctl; struct netns_frags frags; - int max_dsize; }; #endif diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index ffec6ce51005..32755cb7e64e 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -355,8 +355,6 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) struct net *net = dev_net(skb->dev); struct lowpan_frag_info *frag_info = lowpan_cb(skb); struct ieee802154_addr source, dest; - struct netns_ieee802154_lowpan *ieee802154_lowpan = - net_ieee802154_lowpan(net); int err; source = mac_cb(skb)->source; @@ -366,8 +364,10 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) if (err < 0) goto err; - if (frag_info->d_size > ieee802154_lowpan->max_dsize) + if (frag_info->d_size > IPV6_MIN_MTU) { + net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n"); goto err; + } fq = fq_find(net, frag_info, &source, &dest); if (fq != NULL) { @@ -415,13 +415,6 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, - { - .procname = "6lowpanfrag_max_datagram_size", - .data = &init_net.ieee802154_lowpan.max_dsize, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { } }; @@ -458,7 +451,6 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) table[1].data = &ieee802154_lowpan->frags.low_thresh; table[1].extra2 = &ieee802154_lowpan->frags.high_thresh; table[2].data = &ieee802154_lowpan->frags.timeout; - table[3].data = &ieee802154_lowpan->max_dsize; /* Don't export sysctls to unprivileged users */ if (net->user_ns != &init_user_ns) @@ -533,7 +525,6 @@ static int __net_init lowpan_frags_init_net(struct net *net) ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; - ieee802154_lowpan->max_dsize = 0xFFFF; inet_frags_init_net(&ieee802154_lowpan->frags); -- cgit v1.2.3 From b25046b1e5e3f1423434da77ccc859f2f779d1ce Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Sun, 17 Aug 2014 10:29:42 +0200 Subject: mtd: nand: fix DocBook warnings on nand_sdr_timings doc Change the comment type (from /** to /*) to prevent DocBook from complaining about missing description for nand_sdr_timings fields. There is currently no need in documenting those fields because they are fully described in the ONFI specification (which is pointed out in the comment). Signed-off-by: Boris BREZILLON Reported-by: Randy Dunlap Acked-by: Randy Dunlap Tested-by: Randy Dunlap Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 3083c53e0270..c300db3ae285 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -949,7 +949,7 @@ static inline int jedec_feature(struct nand_chip *chip) : 0; } -/** +/* * struct nand_sdr_timings - SDR NAND chip timings * * This struct defines the timing requirements of a SDR NAND chip. -- cgit v1.2.3 From f161dd4122ffa73e4e12000309dca65bec80d416 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Aug 2014 21:06:54 +0300 Subject: Bluetooth: Fix hci_conn reference counting for auto-connections Recently the LE passive scanning and auto-connections feature was introduced. It uses the hci_connect_le() API which returns a hci_conn along with a reference count to that object. All previous users would tie this returned reference to some existing object, such as an L2CAP channel, and there'd be no leaked references this way. For auto-connections however the reference was returned but not stored anywhere, leaving established connections with one higher reference count than they should have. Instead of playing special tricks with hci_conn_hold/drop this patch associates the returned reference from hci_connect_le() with the object that in practice does own this reference, i.e. the hci_conn_params struct that caused us to initiate a connection in the first place. Once the connection is established or fails to establish this reference is removed appropriately. One extra thing needed is to call hci_pend_le_actions_clear() before calling hci_conn_hash_flush() so that the reference is cleared before the hci_conn objects are fully removed. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 8 ++++++++ net/bluetooth/hci_core.c | 14 ++++++++++++-- net/bluetooth/hci_event.c | 17 +++++++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b5d5af3aa469..6f884e6c731e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -464,6 +464,8 @@ struct hci_conn_params { HCI_AUTO_CONN_ALWAYS, HCI_AUTO_CONN_LINK_LOSS, } auto_connect; + + struct hci_conn *conn; }; extern struct list_head hci_dev_list; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b50dabb3f86a..faff6247ac8f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -589,6 +589,14 @@ EXPORT_SYMBOL(hci_get_route); void hci_le_conn_failed(struct hci_conn *conn, u8 status) { struct hci_dev *hdev = conn->hdev; + struct hci_conn_params *params; + + params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst, + conn->dst_type); + if (params && params->conn) { + hci_conn_drop(params->conn); + params->conn = NULL; + } conn->state = BT_CLOSED; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c32d361c0cf7..1d9c29a00568 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2536,8 +2536,13 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev) { struct hci_conn_params *p; - list_for_each_entry(p, &hdev->le_conn_params, list) + list_for_each_entry(p, &hdev->le_conn_params, list) { + if (p->conn) { + hci_conn_drop(p->conn); + p->conn = NULL; + } list_del_init(&p->action); + } BT_DBG("All LE pending actions cleared"); } @@ -2578,8 +2583,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); hci_pend_le_actions_clear(hdev); + hci_conn_hash_flush(hdev); hci_dev_unlock(hdev); hci_notify(hdev, HCI_DEV_DOWN); @@ -3727,6 +3732,9 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (!params) return; + if (params->conn) + hci_conn_drop(params->conn); + list_del(¶ms->action); list_del(¶ms->list); kfree(params); @@ -3757,6 +3765,8 @@ void hci_conn_params_clear_all(struct hci_dev *hdev) struct hci_conn_params *params, *tmp; list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + if (params->conn) + hci_conn_drop(params->conn); list_del(¶ms->action); list_del(¶ms->list); kfree(params); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index be35598984d9..a6000823f0ff 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4221,8 +4221,13 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); - if (params) + if (params) { list_del_init(¶ms->action); + if (params->conn) { + hci_conn_drop(params->conn); + params->conn = NULL; + } + } unlock: hci_update_background_scan(hdev); @@ -4304,8 +4309,16 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER); - if (!IS_ERR(conn)) + if (!IS_ERR(conn)) { + /* Store the pointer since we don't really have any + * other owner of the object besides the params that + * triggered it. This way we can abort the connection if + * the parameters get removed and keep the reference + * count consistent once the connection is established. + */ + params->conn = conn; return; + } switch (PTR_ERR(conn)) { case -EBUSY: -- cgit v1.2.3 From 58b84f6a97f7f8811e0636836734809ff52cad43 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Aug 2014 12:00:53 -0500 Subject: gpio: move GPIOD flags outside #ifdef The GPIOD flags are defined inside the #ifdef CONFIG_GPIOLIB switch, making the gpiolib stubs fail if these flags are used by a consumer. This is not correct: the stubs should compile fine without GPIOLIB. Reported-by: Ulf Hansson Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- include/linux/gpio/consumer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index b7ce0c64c6f3..c7e17de732f3 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -16,8 +16,6 @@ struct device; */ struct gpio_desc; -#ifdef CONFIG_GPIOLIB - #define GPIOD_FLAGS_BIT_DIR_SET BIT(0) #define GPIOD_FLAGS_BIT_DIR_OUT BIT(1) #define GPIOD_FLAGS_BIT_DIR_VAL BIT(2) @@ -34,6 +32,8 @@ enum gpiod_flags { GPIOD_FLAGS_BIT_DIR_VAL, }; +#ifdef CONFIG_GPIOLIB + /* Acquire and dispose GPIOs */ struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, -- cgit v1.2.3 From c200b1aa6cb460ce8c3ecf6fdc690d3949c3cc5d Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 20 Aug 2014 18:36:46 +0800 Subject: f2fs: fix incorrect calculation with total/free inode num Theoretically, our total inodes number is the same as total node number, but there are three node ids are reserved in f2fs, they are 0, 1 (node nid), and 2 (meta nid), and they should never be used by user, so our total/free inode number calculated in ->statfs is wrong. This patch indroduces F2FS_RESERVED_NODE_NUM and then fixes this issue by recalculating total/free inode number with the macro. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- fs/f2fs/super.c | 4 ++-- include/linux/f2fs_fs.h | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b4d964029fc7..044395c20ee9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1957,7 +1957,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi) nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; /* not used nids: 0, node, meta, (and root counted as valid node) */ - nm_i->available_nids = nm_i->max_nid - 3; + nm_i->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM; nm_i->fcnt = 0; nm_i->nat_cnt = 0; nm_i->ram_thresh = DEF_RAM_THRESHOLD; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ddb1e9d36363..0f61f5aa587c 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -508,8 +508,8 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = buf->f_blocks - valid_user_blocks(sbi) - ovp_count; buf->f_bavail = user_block_count - valid_user_blocks(sbi); - buf->f_files = sbi->total_node_count; - buf->f_ffree = sbi->total_node_count - valid_inode_count(sbi); + buf->f_files = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; + buf->f_ffree = buf->f_files - valid_inode_count(sbi); buf->f_namelen = F2FS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 6ff0b0b42d47..0ed77f32c9ac 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -24,6 +24,9 @@ #define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ +/* 0, 1(node nid), 2(meta nid) are reserved node id */ +#define F2FS_RESERVED_NODE_NUM 3 + #define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) #define F2FS_NODE_INO(sbi) (sbi->node_ino_num) #define F2FS_META_INO(sbi) (sbi->meta_ino_num) -- cgit v1.2.3 From 6dc14baf4ced769017c7a7045019c7a19f373865 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 21 Aug 2014 10:41:42 -0400 Subject: drm/radeon: add new KV pci id bug: https://bugs.freedesktop.org/show_bug.cgi?id=82912 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/cik.c | 1 + include/drm/drm_pciids.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index f4e14702639d..79a5a5519bd6 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3544,6 +3544,7 @@ static void cik_gpu_init(struct radeon_device *rdev) (rdev->pdev->device == 0x130B) || (rdev->pdev->device == 0x130E) || (rdev->pdev->device == 0x1315) || + (rdev->pdev->device == 0x1318) || (rdev->pdev->device == 0x131B)) { rdev->config.cik.max_cu_per_sh = 4; rdev->config.cik.max_backends_per_se = 1; diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 6dfd64b3a604..3a9281b2f792 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -17,6 +17,7 @@ {0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ -- cgit v1.2.3 From 5fc540edc8ea1297c76685f74bc82a2107fe6731 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 21 Aug 2014 10:48:11 -0400 Subject: drm/radeon: add new bonaire pci ids Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- include/drm/drm_pciids.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 3a9281b2f792..b75b9a572222 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -176,6 +176,8 @@ {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ -- cgit v1.2.3 From 37dbeab788a8f23fd946c0be083e5484d6f929a1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 21 Aug 2014 10:55:07 -0400 Subject: drm/radeon: add additional SI pci ids Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- include/drm/drm_pciids.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index b75b9a572222..e973540cd15b 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -165,8 +165,11 @@ {0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ @@ -300,6 +303,7 @@ {0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \ {0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \ {0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ -- cgit v1.2.3 From 33b7f99cf003ca6c1d31c42b50e1100ad71aaec0 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 15 Aug 2014 17:23:02 -0400 Subject: ftrace: Allow ftrace_ops to use the hashes from other ops Currently the top level debug file system function tracer shares its ftrace_ops with the function graph tracer. This was thought to be fine because the tracers are not used together, as one can only enable function or function_graph tracer in the current_tracer file. But that assumption proved to be incorrect. The function profiler can use the function graph tracer when function tracing is enabled. Since all function graph users uses the function tracing ftrace_ops this causes a conflict and when a user enables both function profiling as well as the function tracer it will crash ftrace and disable it. The quick solution so far is to move them as separate ftrace_ops like it was earlier. The problem though is to synchronize the functions that are traced because both function and function_graph tracer are limited by the selections made in the set_ftrace_filter and set_ftrace_notrace files. To handle this, a new structure is made called ftrace_ops_hash. This structure will now hold the filter_hash and notrace_hash, and the ftrace_ops will point to this structure. That will allow two ftrace_ops to share the same hashes. Since most ftrace_ops do not share the hashes, and to keep allocation simple, the ftrace_ops structure will include both a pointer to the ftrace_ops_hash called func_hash, as well as the structure itself, called local_hash. When the ops are registered, the func_hash pointer will be initialized to point to the local_hash within the ftrace_ops structure. Some of the ftrace internal ftrace_ops will be initialized statically. This will allow for the function and function_graph tracer to have separate ops but still share the same hash tables that determine what functions they trace. Cc: stable@vger.kernel.org # 3.16 (apply after 3.17-rc4 is out) Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 14 +++++-- kernel/trace/ftrace.c | 100 +++++++++++++++++++++++++------------------------ 2 files changed, 63 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 6bb5e3f2a3b4..f0b0edbf55a9 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -102,6 +102,15 @@ enum { FTRACE_OPS_FL_DELETED = 1 << 8, }; +#ifdef CONFIG_DYNAMIC_FTRACE +/* The hash used to know what functions callbacks trace */ +struct ftrace_ops_hash { + struct ftrace_hash *notrace_hash; + struct ftrace_hash *filter_hash; + struct mutex regex_lock; +}; +#endif + /* * Note, ftrace_ops can be referenced outside of RCU protection. * (Although, for perf, the control ops prevent that). If ftrace_ops is @@ -121,10 +130,9 @@ struct ftrace_ops { int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE int nr_trampolines; - struct ftrace_hash *notrace_hash; - struct ftrace_hash *filter_hash; + struct ftrace_ops_hash local_hash; + struct ftrace_ops_hash *func_hash; struct ftrace_hash *tramp_hash; - struct mutex regex_lock; unsigned long trampoline; #endif }; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1654b12c891a..c92757adba79 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -65,15 +65,17 @@ #define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_CONTROL) #ifdef CONFIG_DYNAMIC_FTRACE -#define INIT_REGEX_LOCK(opsname) \ - .regex_lock = __MUTEX_INITIALIZER(opsname.regex_lock), +#define INIT_OPS_HASH(opsname) \ + .func_hash = &opsname.local_hash, \ + .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock), #else -#define INIT_REGEX_LOCK(opsname) +#define INIT_OPS_HASH(opsname) #endif static struct ftrace_ops ftrace_list_end __read_mostly = { .func = ftrace_stub, .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_STUB, + INIT_OPS_HASH(ftrace_list_end) }; /* ftrace_enabled is a method to turn ftrace on or off */ @@ -140,7 +142,8 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops) { #ifdef CONFIG_DYNAMIC_FTRACE if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) { - mutex_init(&ops->regex_lock); + mutex_init(&ops->local_hash.regex_lock); + ops->func_hash = &ops->local_hash; ops->flags |= FTRACE_OPS_FL_INITIALIZED; } #endif @@ -899,7 +902,7 @@ static void unregister_ftrace_profiler(void) static struct ftrace_ops ftrace_profile_ops __read_mostly = { .func = function_profile_call, .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, - INIT_REGEX_LOCK(ftrace_profile_ops) + INIT_OPS_HASH(ftrace_profile_ops) }; static int register_ftrace_profiler(void) @@ -1081,11 +1084,12 @@ static const struct ftrace_hash empty_hash = { #define EMPTY_HASH ((struct ftrace_hash *)&empty_hash) static struct ftrace_ops global_ops = { - .func = ftrace_stub, - .notrace_hash = EMPTY_HASH, - .filter_hash = EMPTY_HASH, - .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, - INIT_REGEX_LOCK(global_ops) + .func = ftrace_stub, + .local_hash.notrace_hash = EMPTY_HASH, + .local_hash.filter_hash = EMPTY_HASH, + INIT_OPS_HASH(global_ops) + .flags = FTRACE_OPS_FL_RECURSION_SAFE | + FTRACE_OPS_FL_INITIALIZED, }; struct ftrace_page { @@ -1226,8 +1230,8 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) void ftrace_free_filter(struct ftrace_ops *ops) { ftrace_ops_init(ops); - free_ftrace_hash(ops->filter_hash); - free_ftrace_hash(ops->notrace_hash); + free_ftrace_hash(ops->func_hash->filter_hash); + free_ftrace_hash(ops->func_hash->notrace_hash); } static struct ftrace_hash *alloc_ftrace_hash(int size_bits) @@ -1382,8 +1386,8 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) return 0; #endif - filter_hash = rcu_dereference_raw_notrace(ops->filter_hash); - notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash); + filter_hash = rcu_dereference_raw_notrace(ops->func_hash->filter_hash); + notrace_hash = rcu_dereference_raw_notrace(ops->func_hash->notrace_hash); if ((ftrace_hash_empty(filter_hash) || ftrace_lookup_ip(filter_hash, ip)) && @@ -1554,14 +1558,14 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, * gets inversed. */ if (filter_hash) { - hash = ops->filter_hash; - other_hash = ops->notrace_hash; + hash = ops->func_hash->filter_hash; + other_hash = ops->func_hash->notrace_hash; if (ftrace_hash_empty(hash)) all = 1; } else { inc = !inc; - hash = ops->notrace_hash; - other_hash = ops->filter_hash; + hash = ops->func_hash->notrace_hash; + other_hash = ops->func_hash->filter_hash; /* * If the notrace hash has no items, * then there's nothing to do. @@ -2436,8 +2440,8 @@ static inline int ops_traces_mod(struct ftrace_ops *ops) * Filter_hash being empty will default to trace module. * But notrace hash requires a test of individual module functions. */ - return ftrace_hash_empty(ops->filter_hash) && - ftrace_hash_empty(ops->notrace_hash); + return ftrace_hash_empty(ops->func_hash->filter_hash) && + ftrace_hash_empty(ops->func_hash->notrace_hash); } /* @@ -2459,12 +2463,12 @@ ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec) return 0; /* The function must be in the filter */ - if (!ftrace_hash_empty(ops->filter_hash) && - !ftrace_lookup_ip(ops->filter_hash, rec->ip)) + if (!ftrace_hash_empty(ops->func_hash->filter_hash) && + !ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip)) return 0; /* If in notrace hash, we ignore it too */ - if (ftrace_lookup_ip(ops->notrace_hash, rec->ip)) + if (ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip)) return 0; return 1; @@ -2785,10 +2789,10 @@ t_next(struct seq_file *m, void *v, loff_t *pos) } else { rec = &iter->pg->records[iter->idx++]; if (((iter->flags & FTRACE_ITER_FILTER) && - !(ftrace_lookup_ip(ops->filter_hash, rec->ip))) || + !(ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip))) || ((iter->flags & FTRACE_ITER_NOTRACE) && - !ftrace_lookup_ip(ops->notrace_hash, rec->ip)) || + !ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip)) || ((iter->flags & FTRACE_ITER_ENABLED) && !(rec->flags & FTRACE_FL_ENABLED))) { @@ -2837,9 +2841,9 @@ static void *t_start(struct seq_file *m, loff_t *pos) * functions are enabled. */ if ((iter->flags & FTRACE_ITER_FILTER && - ftrace_hash_empty(ops->filter_hash)) || + ftrace_hash_empty(ops->func_hash->filter_hash)) || (iter->flags & FTRACE_ITER_NOTRACE && - ftrace_hash_empty(ops->notrace_hash))) { + ftrace_hash_empty(ops->func_hash->notrace_hash))) { if (*pos > 0) return t_hash_start(m, pos); iter->flags |= FTRACE_ITER_PRINTALL; @@ -3001,12 +3005,12 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, iter->ops = ops; iter->flags = flag; - mutex_lock(&ops->regex_lock); + mutex_lock(&ops->func_hash->regex_lock); if (flag & FTRACE_ITER_NOTRACE) - hash = ops->notrace_hash; + hash = ops->func_hash->notrace_hash; else - hash = ops->filter_hash; + hash = ops->func_hash->filter_hash; if (file->f_mode & FMODE_WRITE) { const int size_bits = FTRACE_HASH_DEFAULT_BITS; @@ -3041,7 +3045,7 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, file->private_data = iter; out_unlock: - mutex_unlock(&ops->regex_lock); + mutex_unlock(&ops->func_hash->regex_lock); return ret; } @@ -3279,7 +3283,7 @@ static struct ftrace_ops trace_probe_ops __read_mostly = { .func = function_trace_probe_call, .flags = FTRACE_OPS_FL_INITIALIZED, - INIT_REGEX_LOCK(trace_probe_ops) + INIT_OPS_HASH(trace_probe_ops) }; static int ftrace_probe_registered; @@ -3342,7 +3346,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, void *data) { struct ftrace_func_probe *entry; - struct ftrace_hash **orig_hash = &trace_probe_ops.filter_hash; + struct ftrace_hash **orig_hash = &trace_probe_ops.func_hash->filter_hash; struct ftrace_hash *hash; struct ftrace_page *pg; struct dyn_ftrace *rec; @@ -3359,7 +3363,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, if (WARN_ON(not)) return -EINVAL; - mutex_lock(&trace_probe_ops.regex_lock); + mutex_lock(&trace_probe_ops.func_hash->regex_lock); hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); if (!hash) { @@ -3428,7 +3432,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, out_unlock: mutex_unlock(&ftrace_lock); out: - mutex_unlock(&trace_probe_ops.regex_lock); + mutex_unlock(&trace_probe_ops.func_hash->regex_lock); free_ftrace_hash(hash); return count; @@ -3446,7 +3450,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, struct ftrace_func_entry *rec_entry; struct ftrace_func_probe *entry; struct ftrace_func_probe *p; - struct ftrace_hash **orig_hash = &trace_probe_ops.filter_hash; + struct ftrace_hash **orig_hash = &trace_probe_ops.func_hash->filter_hash; struct list_head free_list; struct ftrace_hash *hash; struct hlist_node *tmp; @@ -3468,7 +3472,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, return; } - mutex_lock(&trace_probe_ops.regex_lock); + mutex_lock(&trace_probe_ops.func_hash->regex_lock); hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); if (!hash) @@ -3521,7 +3525,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, mutex_unlock(&ftrace_lock); out_unlock: - mutex_unlock(&trace_probe_ops.regex_lock); + mutex_unlock(&trace_probe_ops.func_hash->regex_lock); free_ftrace_hash(hash); } @@ -3717,12 +3721,12 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, if (unlikely(ftrace_disabled)) return -ENODEV; - mutex_lock(&ops->regex_lock); + mutex_lock(&ops->func_hash->regex_lock); if (enable) - orig_hash = &ops->filter_hash; + orig_hash = &ops->func_hash->filter_hash; else - orig_hash = &ops->notrace_hash; + orig_hash = &ops->func_hash->notrace_hash; if (reset) hash = alloc_ftrace_hash(FTRACE_HASH_DEFAULT_BITS); @@ -3752,7 +3756,7 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, mutex_unlock(&ftrace_lock); out_regex_unlock: - mutex_unlock(&ops->regex_lock); + mutex_unlock(&ops->func_hash->regex_lock); free_ftrace_hash(hash); return ret; @@ -3975,15 +3979,15 @@ int ftrace_regex_release(struct inode *inode, struct file *file) trace_parser_put(parser); - mutex_lock(&iter->ops->regex_lock); + mutex_lock(&iter->ops->func_hash->regex_lock); if (file->f_mode & FMODE_WRITE) { filter_hash = !!(iter->flags & FTRACE_ITER_FILTER); if (filter_hash) - orig_hash = &iter->ops->filter_hash; + orig_hash = &iter->ops->func_hash->filter_hash; else - orig_hash = &iter->ops->notrace_hash; + orig_hash = &iter->ops->func_hash->notrace_hash; mutex_lock(&ftrace_lock); ret = ftrace_hash_move(iter->ops, filter_hash, @@ -3994,7 +3998,7 @@ int ftrace_regex_release(struct inode *inode, struct file *file) mutex_unlock(&ftrace_lock); } - mutex_unlock(&iter->ops->regex_lock); + mutex_unlock(&iter->ops->func_hash->regex_lock); free_ftrace_hash(iter->hash); kfree(iter); @@ -4611,7 +4615,7 @@ void __init ftrace_init(void) static struct ftrace_ops global_ops = { .func = ftrace_stub, .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, - INIT_REGEX_LOCK(global_ops) + INIT_OPS_HASH(global_ops) }; static int __init ftrace_nodyn_init(void) @@ -4713,7 +4717,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, static struct ftrace_ops control_ops = { .func = ftrace_ops_control_func, .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, - INIT_REGEX_LOCK(control_ops) + INIT_OPS_HASH(control_ops) }; static inline void -- cgit v1.2.3 From b5b822050ca3c4fc1f475100cc197cc00ba2d492 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 Aug 2014 16:17:38 +0800 Subject: f2fs: use macro for code readability This patch introduces DEF_NIDS_PER_INODE/GET_ORPHAN_BLOCKS/F2FS_CP_PACKS macro instead of numbers in code for readability. change log from v1: o fix typo pointed out by Jaegeuk Kim. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 21 ++++++++++----------- include/linux/f2fs_fs.h | 13 ++++++++++--- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index c9c08d52ecfd..ec3b7a5381fa 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -443,8 +443,8 @@ static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) struct f2fs_orphan_block *orphan_blk = NULL; unsigned int nentries = 0; unsigned short index; - unsigned short orphan_blocks = (unsigned short)((sbi->n_orphans + - (F2FS_ORPHANS_PER_BLOCK - 1)) / F2FS_ORPHANS_PER_BLOCK); + unsigned short orphan_blocks = + (unsigned short)GET_ORPHAN_BLOCKS(sbi->n_orphans); struct page *page = NULL; struct ino_entry *orphan = NULL; @@ -837,7 +837,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi)); ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi)); ckpt->free_segment_count = cpu_to_le32(free_segments(sbi)); - for (i = 0; i < 3; i++) { + for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { ckpt->cur_node_segno[i] = cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_NODE)); ckpt->cur_node_blkoff[i] = @@ -845,7 +845,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) ckpt->alloc_type[i + CURSEG_HOT_NODE] = curseg_alloc_type(sbi, i + CURSEG_HOT_NODE); } - for (i = 0; i < 3; i++) { + for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { ckpt->cur_data_segno[i] = cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_DATA)); ckpt->cur_data_blkoff[i] = @@ -860,24 +860,23 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) /* 2 cp + n data seg summary + orphan inode blocks */ data_sum_blocks = npages_for_summary_flush(sbi); - if (data_sum_blocks < 3) + if (data_sum_blocks < NR_CURSEG_DATA_TYPE) set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); else clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); - orphan_blocks = (sbi->n_orphans + F2FS_ORPHANS_PER_BLOCK - 1) - / F2FS_ORPHANS_PER_BLOCK; + orphan_blocks = GET_ORPHAN_BLOCKS(sbi->n_orphans); ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + orphan_blocks); if (is_umount) { set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - ckpt->cp_pack_total_block_count = cpu_to_le32(2 + + ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS+ cp_payload_blks + data_sum_blocks + orphan_blocks + NR_CURSEG_NODE_TYPE); } else { clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - ckpt->cp_pack_total_block_count = cpu_to_le32(2 + + ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS + cp_payload_blks + data_sum_blocks + orphan_blocks); } @@ -1022,8 +1021,8 @@ void init_ino_entry_info(struct f2fs_sb_info *sbi) * for cp pack we can have max 1020*504 orphan entries */ sbi->n_orphans = 0; - sbi->max_orphans = (sbi->blocks_per_seg - 2 - NR_CURSEG_TYPE) - * F2FS_ORPHANS_PER_BLOCK; + sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS - + NR_CURSEG_TYPE) * F2FS_ORPHANS_PER_BLOCK; } int __init create_checkpoint_caches(void) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 0ed77f32c9ac..08ed2b0a96e6 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -90,6 +90,8 @@ struct f2fs_super_block { #define CP_ORPHAN_PRESENT_FLAG 0x00000002 #define CP_UMOUNT_FLAG 0x00000001 +#define F2FS_CP_PACKS 2 /* # of checkpoint packs */ + struct f2fs_checkpoint { __le64 checkpoint_ver; /* checkpoint block version number */ __le64 user_block_count; /* # of user blocks */ @@ -126,6 +128,9 @@ struct f2fs_checkpoint { */ #define F2FS_ORPHANS_PER_BLOCK 1020 +#define GET_ORPHAN_BLOCKS(n) ((n + F2FS_ORPHANS_PER_BLOCK - 1) / \ + F2FS_ORPHANS_PER_BLOCK) + struct f2fs_orphan_block { __le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */ __le32 reserved; /* reserved */ @@ -147,6 +152,7 @@ struct f2fs_extent { #define F2FS_NAME_LEN 255 #define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ +#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ #define ADDRS_PER_INODE(fi) addrs_per_inode(fi) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ @@ -166,8 +172,9 @@ struct f2fs_extent { #define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ F2FS_INLINE_XATTR_ADDRS - 1)) -#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) \ - - sizeof(__le32) * (DEF_ADDRS_PER_INODE + 5 - 1)) +#define INLINE_DATA_OFFSET (PAGE_CACHE_SIZE - sizeof(struct node_footer) -\ + sizeof(__le32) * (DEF_ADDRS_PER_INODE + \ + DEF_NIDS_PER_INODE - 1)) struct f2fs_inode { __le16 i_mode; /* file mode */ @@ -197,7 +204,7 @@ struct f2fs_inode { __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ - __le32 i_nid[5]; /* direct(2), indirect(2), + __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), double_indirect(1) node id */ } __packed; -- cgit v1.2.3 From 7c3af975257383ece54b83c0505d3e0656cb7daf Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Fri, 8 Aug 2014 11:00:57 -0400 Subject: nfs: don't sleep with inode lock in lock_and_join_requests This handles the 'nonblock=false' case in nfs_lock_and_join_requests. If the group is already locked and blocking is allowed, drop the inode lock and wait for the group lock to be cleared before trying it all again. This should fix warnings found in peterz's tree (sched/wait branch), where might_sleep() checks are added to wait.[ch]. Reported-by: Fengguang Wu Signed-off-by: Weston Andros Adamson Reviewed-by: Peng Tao Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 17 +++++++++++++++++ fs/nfs/write.c | 12 +++++++++++- include/linux/nfs_page.h | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 30c9626f96b0..4ec67f8d70aa 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -167,6 +167,23 @@ nfs_page_group_lock(struct nfs_page *req, bool nonblock) return -EAGAIN; } +/* + * nfs_page_group_lock_wait - wait for the lock to clear, but don't grab it + * @req - a request in the group + * + * This is a blocking call to wait for the group lock to be cleared. + */ +void +nfs_page_group_lock_wait(struct nfs_page *req) +{ + struct nfs_page *head = req->wb_head; + + WARN_ON_ONCE(head != head->wb_head); + + wait_on_bit(&head->wb_flags, PG_HEADLOCK, + TASK_UNINTERRUPTIBLE); +} + /* * nfs_page_group_unlock - unlock the head of the page group * @req - request in group that is to be unlocked diff --git a/fs/nfs/write.c b/fs/nfs/write.c index e056f617adf2..175d5d073ccf 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -478,13 +478,23 @@ try_again: return NULL; } - /* lock each request in the page group */ + /* holding inode lock, so always make a non-blocking call to try the + * page group lock */ ret = nfs_page_group_lock(head, true); if (ret < 0) { spin_unlock(&inode->i_lock); + + if (!nonblock && ret == -EAGAIN) { + nfs_page_group_lock_wait(head); + nfs_release_request(head); + goto try_again; + } + nfs_release_request(head); return ERR_PTR(ret); } + + /* lock each request in the page group */ subreq = head; do { /* diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 6ad2bbcad405..6c3e06ee2fb7 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -123,6 +123,7 @@ extern int nfs_wait_on_request(struct nfs_page *); extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_and_release_request(struct nfs_page *); extern int nfs_page_group_lock(struct nfs_page *, bool); +extern void nfs_page_group_lock_wait(struct nfs_page *); extern void nfs_page_group_unlock(struct nfs_page *); extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int); -- cgit v1.2.3 From d1c85c2ebe7ffe1f1b27846bd1ba0944c513d822 Mon Sep 17 00:00:00 2001 From: Zhouyi Zhou Date: Fri, 22 Aug 2014 10:40:15 +0800 Subject: netfilter: HAVE_JUMP_LABEL instead of CONFIG_JUMP_LABEL Use HAVE_JUMP_LABEL as elsewhere in the kernel to ensure that the toolchain has the required support in addition to CONFIG_JUMP_LABEL being set. Signed-off-by: Zhouyi Zhou Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 5 +++-- net/netfilter/core.c | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 2077489f9887..2517ece98820 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #ifdef CONFIG_NETFILTER static inline int NF_DROP_GETERR(int verdict) @@ -99,9 +100,9 @@ void nf_unregister_sockopt(struct nf_sockopt_ops *reg); extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; -#if defined(CONFIG_JUMP_LABEL) -#include +#ifdef HAVE_JUMP_LABEL extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; + static inline bool nf_hooks_active(u_int8_t pf, unsigned int hook) { if (__builtin_constant_p(pf) && diff --git a/net/netfilter/core.c b/net/netfilter/core.c index a93c97f106d4..024a2e25c8a4 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -54,7 +54,7 @@ EXPORT_SYMBOL_GPL(nf_unregister_afinfo); struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly; EXPORT_SYMBOL(nf_hooks); -#if defined(CONFIG_JUMP_LABEL) +#ifdef HAVE_JUMP_LABEL struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; EXPORT_SYMBOL(nf_hooks_needed); #endif @@ -72,7 +72,7 @@ int nf_register_hook(struct nf_hook_ops *reg) } list_add_rcu(®->list, elem->list.prev); mutex_unlock(&nf_hook_mutex); -#if defined(CONFIG_JUMP_LABEL) +#ifdef HAVE_JUMP_LABEL static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif return 0; @@ -84,7 +84,7 @@ void nf_unregister_hook(struct nf_hook_ops *reg) mutex_lock(&nf_hook_mutex); list_del_rcu(®->list); mutex_unlock(&nf_hook_mutex); -#if defined(CONFIG_JUMP_LABEL) +#ifdef HAVE_JUMP_LABEL static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif synchronize_net(); -- cgit v1.2.3 From 96c2737716d586a218bc795fcb79d2e2b6003081 Mon Sep 17 00:00:00 2001 From: Valentina Manea Date: Wed, 20 Aug 2014 07:31:00 +0300 Subject: usbip: move usbip kernel code out of staging At this point, USB/IP kernel code is fully functional and can be moved out of staging. Signed-off-by: Valentina Manea Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/usbip/Kconfig | 41 -- drivers/staging/usbip/Makefile | 10 - drivers/staging/usbip/README | 7 - drivers/staging/usbip/stub.h | 113 --- drivers/staging/usbip/stub_dev.c | 525 -------------- drivers/staging/usbip/stub_main.c | 335 --------- drivers/staging/usbip/stub_rx.c | 594 --------------- drivers/staging/usbip/stub_tx.c | 398 ---------- drivers/staging/usbip/uapi/usbip.h | 26 - drivers/staging/usbip/usbip_common.c | 776 -------------------- drivers/staging/usbip/usbip_common.h | 335 --------- drivers/staging/usbip/usbip_event.c | 128 ---- drivers/staging/usbip/usbip_protocol.txt | 358 --------- drivers/staging/usbip/vhci.h | 129 ---- drivers/staging/usbip/vhci_hcd.c | 1171 ------------------------------ drivers/staging/usbip/vhci_rx.c | 268 ------- drivers/staging/usbip/vhci_sysfs.c | 252 ------- drivers/staging/usbip/vhci_tx.c | 224 ------ drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 2 + drivers/usb/usbip/Kconfig | 41 ++ drivers/usb/usbip/Makefile | 10 + drivers/usb/usbip/README | 7 + drivers/usb/usbip/stub.h | 113 +++ drivers/usb/usbip/stub_dev.c | 525 ++++++++++++++ drivers/usb/usbip/stub_main.c | 335 +++++++++ drivers/usb/usbip/stub_rx.c | 594 +++++++++++++++ drivers/usb/usbip/stub_tx.c | 398 ++++++++++ drivers/usb/usbip/usbip_common.c | 776 ++++++++++++++++++++ drivers/usb/usbip/usbip_common.h | 335 +++++++++ drivers/usb/usbip/usbip_event.c | 128 ++++ drivers/usb/usbip/usbip_protocol.txt | 358 +++++++++ drivers/usb/usbip/vhci.h | 129 ++++ drivers/usb/usbip/vhci_hcd.c | 1171 ++++++++++++++++++++++++++++++ drivers/usb/usbip/vhci_rx.c | 268 +++++++ drivers/usb/usbip/vhci_sysfs.c | 252 +++++++ drivers/usb/usbip/vhci_tx.c | 224 ++++++ include/uapi/linux/usbip.h | 26 + 40 files changed, 5694 insertions(+), 5693 deletions(-) delete mode 100644 drivers/staging/usbip/Kconfig delete mode 100644 drivers/staging/usbip/Makefile delete mode 100644 drivers/staging/usbip/README delete mode 100644 drivers/staging/usbip/stub.h delete mode 100644 drivers/staging/usbip/stub_dev.c delete mode 100644 drivers/staging/usbip/stub_main.c delete mode 100644 drivers/staging/usbip/stub_rx.c delete mode 100644 drivers/staging/usbip/stub_tx.c delete mode 100644 drivers/staging/usbip/uapi/usbip.h delete mode 100644 drivers/staging/usbip/usbip_common.c delete mode 100644 drivers/staging/usbip/usbip_common.h delete mode 100644 drivers/staging/usbip/usbip_event.c delete mode 100644 drivers/staging/usbip/usbip_protocol.txt delete mode 100644 drivers/staging/usbip/vhci.h delete mode 100644 drivers/staging/usbip/vhci_hcd.c delete mode 100644 drivers/staging/usbip/vhci_rx.c delete mode 100644 drivers/staging/usbip/vhci_sysfs.c delete mode 100644 drivers/staging/usbip/vhci_tx.c create mode 100644 drivers/usb/usbip/Kconfig create mode 100644 drivers/usb/usbip/Makefile create mode 100644 drivers/usb/usbip/README create mode 100644 drivers/usb/usbip/stub.h create mode 100644 drivers/usb/usbip/stub_dev.c create mode 100644 drivers/usb/usbip/stub_main.c create mode 100644 drivers/usb/usbip/stub_rx.c create mode 100644 drivers/usb/usbip/stub_tx.c create mode 100644 drivers/usb/usbip/usbip_common.c create mode 100644 drivers/usb/usbip/usbip_common.h create mode 100644 drivers/usb/usbip/usbip_event.c create mode 100644 drivers/usb/usbip/usbip_protocol.txt create mode 100644 drivers/usb/usbip/vhci.h create mode 100644 drivers/usb/usbip/vhci_hcd.c create mode 100644 drivers/usb/usbip/vhci_rx.c create mode 100644 drivers/usb/usbip/vhci_sysfs.c create mode 100644 drivers/usb/usbip/vhci_tx.c create mode 100644 include/uapi/linux/usbip.h (limited to 'include') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 2c486ea6236b..35b494f5667f 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -28,8 +28,6 @@ source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" -source "drivers/staging/usbip/Kconfig" - source "drivers/staging/wlan-ng/Kconfig" source "drivers/staging/comedi/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 1e1a3a10faf7..e66a5dbd9b02 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_STAGING) += staging.o obj-y += media/ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ -obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig deleted file mode 100644 index bd99e9e47e50..000000000000 --- a/drivers/staging/usbip/Kconfig +++ /dev/null @@ -1,41 +0,0 @@ -config USBIP_CORE - tristate "USB/IP support" - depends on USB && NET - ---help--- - This enables pushing USB packets over IP to allow remote - machines direct access to USB devices. It provides the - USB/IP core that is required by both drivers. - - For more details, and to get the userspace utility - programs, please see . - - To compile this as a module, choose M here: the module will - be called usbip-core. - - If unsure, say N. - -config USBIP_VHCI_HCD - tristate "VHCI hcd" - depends on USBIP_CORE - ---help--- - This enables the USB/IP virtual host controller driver, - which is run on the remote machine. - - To compile this driver as a module, choose M here: the - module will be called vhci-hcd. - -config USBIP_HOST - tristate "Host driver" - depends on USBIP_CORE - ---help--- - This enables the USB/IP host driver, which is run on the - machine that is sharing the USB devices. - - To compile this driver as a module, choose M here: the - module will be called usbip-host. - -config USBIP_DEBUG - bool "Debug messages for USB/IP" - depends on USBIP_CORE - ---help--- - This enables the debug messages from the USB/IP drivers. diff --git a/drivers/staging/usbip/Makefile b/drivers/staging/usbip/Makefile deleted file mode 100644 index 9ecd61545be1..000000000000 --- a/drivers/staging/usbip/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG - -obj-$(CONFIG_USBIP_CORE) += usbip-core.o -usbip-core-y := usbip_common.o usbip_event.o - -obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o -vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o - -obj-$(CONFIG_USBIP_HOST) += usbip-host.o -usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o diff --git a/drivers/staging/usbip/README b/drivers/staging/usbip/README deleted file mode 100644 index 41a2cf2e77a6..000000000000 --- a/drivers/staging/usbip/README +++ /dev/null @@ -1,7 +0,0 @@ -TODO: - - more discussion about the protocol - - testing - - review of the userspace interface - - document the protocol - -Please send patches for this code to Greg Kroah-Hartman diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h deleted file mode 100644 index 266e2b0ce9a8..000000000000 --- a/drivers/staging/usbip/stub.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 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. - */ - -#ifndef __USBIP_STUB_H -#define __USBIP_STUB_H - -#include -#include -#include -#include -#include -#include - -#define STUB_BUSID_OTHER 0 -#define STUB_BUSID_REMOV 1 -#define STUB_BUSID_ADDED 2 -#define STUB_BUSID_ALLOC 3 - -struct stub_device { - struct usb_interface *interface; - struct usb_device *udev; - - struct usbip_device ud; - __u32 devid; - - /* - * stub_priv preserves private data of each urb. - * It is allocated as stub_priv_cache and assigned to urb->context. - * - * stub_priv is always linked to any one of 3 lists; - * priv_init: linked to this until the comletion of a urb. - * priv_tx : linked to this after the completion of a urb. - * priv_free: linked to this after the sending of the result. - * - * Any of these list operations should be locked by priv_lock. - */ - spinlock_t priv_lock; - struct list_head priv_init; - struct list_head priv_tx; - struct list_head priv_free; - - /* see comments for unlinking in stub_rx.c */ - struct list_head unlink_tx; - struct list_head unlink_free; - - wait_queue_head_t tx_waitq; -}; - -/* private data into urb->priv */ -struct stub_priv { - unsigned long seqnum; - struct list_head list; - struct stub_device *sdev; - struct urb *urb; - - int unlinking; -}; - -struct stub_unlink { - unsigned long seqnum; - struct list_head list; - __u32 status; -}; - -/* same as SYSFS_BUS_ID_SIZE */ -#define BUSID_SIZE 32 - -struct bus_id_priv { - char name[BUSID_SIZE]; - char status; - int interf_count; - struct stub_device *sdev; - struct usb_device *udev; - char shutdown_busid; -}; - -/* stub_priv is allocated from stub_priv_cache */ -extern struct kmem_cache *stub_priv_cache; - -/* stub_dev.c */ -extern struct usb_device_driver stub_driver; - -/* stub_main.c */ -struct bus_id_priv *get_busid_priv(const char *busid); -int del_match_busid(char *busid); -void stub_device_cleanup_urbs(struct stub_device *sdev); - -/* stub_rx.c */ -int stub_rx_loop(void *data); - -/* stub_tx.c */ -void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, - __u32 status); -void stub_complete(struct urb *urb); -int stub_tx_loop(void *data); - -#endif /* __USBIP_STUB_H */ diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c deleted file mode 100644 index 51d0c7188738..000000000000 --- a/drivers/staging/usbip/stub_dev.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include -#include - -#include "usbip_common.h" -#include "stub.h" - -/* - * Define device IDs here if you want to explicitly limit exportable devices. - * In most cases, wildcard matching will be okay because driver binding can be - * changed dynamically by a userland program. - */ -static struct usb_device_id stub_table[] = { -#if 0 - /* just an example */ - { USB_DEVICE(0x05ac, 0x0301) }, /* Mac 1 button mouse */ - { USB_DEVICE(0x0430, 0x0009) }, /* Plat Home Keyboard */ - { USB_DEVICE(0x059b, 0x0001) }, /* Iomega USB Zip 100 */ - { USB_DEVICE(0x04b3, 0x4427) }, /* IBM USB CD-ROM */ - { USB_DEVICE(0x05a9, 0xa511) }, /* LifeView USB cam */ - { USB_DEVICE(0x55aa, 0x0201) }, /* Imation card reader */ - { USB_DEVICE(0x046d, 0x0870) }, /* Qcam Express(QV-30) */ - { USB_DEVICE(0x04bb, 0x0101) }, /* IO-DATA HD 120GB */ - { USB_DEVICE(0x04bb, 0x0904) }, /* IO-DATA USB-ET/TX */ - { USB_DEVICE(0x04bb, 0x0201) }, /* IO-DATA USB-ET/TX */ - { USB_DEVICE(0x08bb, 0x2702) }, /* ONKYO USB Speaker */ - { USB_DEVICE(0x046d, 0x08b2) }, /* Logicool Qcam 4000 Pro */ -#endif - /* magic for wild card */ - { .driver_info = 1 }, - { 0, } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, stub_table); - -/* - * usbip_status shows the status of usbip-host as long as this driver is bound - * to the target device. - */ -static ssize_t usbip_status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct stub_device *sdev = dev_get_drvdata(dev); - int status; - - if (!sdev) { - dev_err(dev, "sdev is null\n"); - return -ENODEV; - } - - spin_lock_irq(&sdev->ud.lock); - status = sdev->ud.status; - spin_unlock_irq(&sdev->ud.lock); - - return snprintf(buf, PAGE_SIZE, "%d\n", status); -} -static DEVICE_ATTR_RO(usbip_status); - -/* - * usbip_sockfd gets a socket descriptor of an established TCP connection that - * is used to transfer usbip requests by kernel threads. -1 is a magic number - * by which usbip connection is finished. - */ -static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct stub_device *sdev = dev_get_drvdata(dev); - int sockfd = 0; - struct socket *socket; - int rv; - - if (!sdev) { - dev_err(dev, "sdev is null\n"); - return -ENODEV; - } - - rv = sscanf(buf, "%d", &sockfd); - if (rv != 1) - return -EINVAL; - - if (sockfd != -1) { - int err; - - dev_info(dev, "stub up\n"); - - spin_lock_irq(&sdev->ud.lock); - - if (sdev->ud.status != SDEV_ST_AVAILABLE) { - dev_err(dev, "not ready\n"); - goto err; - } - - socket = sockfd_lookup(sockfd, &err); - if (!socket) - goto err; - - sdev->ud.tcp_socket = socket; - - spin_unlock_irq(&sdev->ud.lock); - - sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, - "stub_rx"); - sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, - "stub_tx"); - - spin_lock_irq(&sdev->ud.lock); - sdev->ud.status = SDEV_ST_USED; - spin_unlock_irq(&sdev->ud.lock); - - } else { - dev_info(dev, "stub down\n"); - - spin_lock_irq(&sdev->ud.lock); - if (sdev->ud.status != SDEV_ST_USED) - goto err; - - spin_unlock_irq(&sdev->ud.lock); - - usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); - } - - return count; - -err: - spin_unlock_irq(&sdev->ud.lock); - return -EINVAL; -} -static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); - -static int stub_add_files(struct device *dev) -{ - int err = 0; - - err = device_create_file(dev, &dev_attr_usbip_status); - if (err) - goto err_status; - - err = device_create_file(dev, &dev_attr_usbip_sockfd); - if (err) - goto err_sockfd; - - err = device_create_file(dev, &dev_attr_usbip_debug); - if (err) - goto err_debug; - - return 0; - -err_debug: - device_remove_file(dev, &dev_attr_usbip_sockfd); -err_sockfd: - device_remove_file(dev, &dev_attr_usbip_status); -err_status: - return err; -} - -static void stub_remove_files(struct device *dev) -{ - device_remove_file(dev, &dev_attr_usbip_status); - device_remove_file(dev, &dev_attr_usbip_sockfd); - device_remove_file(dev, &dev_attr_usbip_debug); -} - -static void stub_shutdown_connection(struct usbip_device *ud) -{ - struct stub_device *sdev = container_of(ud, struct stub_device, ud); - - /* - * When removing an exported device, kernel panic sometimes occurred - * and then EIP was sk_wait_data of stub_rx thread. Is this because - * sk_wait_data returned though stub_rx thread was already finished by - * step 1? - */ - if (ud->tcp_socket) { - dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", - ud->tcp_socket); - kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); - } - - /* 1. stop threads */ - if (ud->tcp_rx) { - kthread_stop_put(ud->tcp_rx); - ud->tcp_rx = NULL; - } - if (ud->tcp_tx) { - kthread_stop_put(ud->tcp_tx); - ud->tcp_tx = NULL; - } - - /* - * 2. close the socket - * - * tcp_socket is freed after threads are killed so that usbip_xmit does - * not touch NULL socket. - */ - if (ud->tcp_socket) { - sockfd_put(ud->tcp_socket); - ud->tcp_socket = NULL; - } - - /* 3. free used data */ - stub_device_cleanup_urbs(sdev); - - /* 4. free stub_unlink */ - { - unsigned long flags; - struct stub_unlink *unlink, *tmp; - - spin_lock_irqsave(&sdev->priv_lock, flags); - list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { - list_del(&unlink->list); - kfree(unlink); - } - list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, - list) { - list_del(&unlink->list); - kfree(unlink); - } - spin_unlock_irqrestore(&sdev->priv_lock, flags); - } -} - -static void stub_device_reset(struct usbip_device *ud) -{ - struct stub_device *sdev = container_of(ud, struct stub_device, ud); - struct usb_device *udev = sdev->udev; - int ret; - - dev_dbg(&udev->dev, "device reset"); - - ret = usb_lock_device_for_reset(udev, sdev->interface); - if (ret < 0) { - dev_err(&udev->dev, "lock for reset\n"); - spin_lock_irq(&ud->lock); - ud->status = SDEV_ST_ERROR; - spin_unlock_irq(&ud->lock); - return; - } - - /* try to reset the device */ - ret = usb_reset_device(udev); - usb_unlock_device(udev); - - spin_lock_irq(&ud->lock); - if (ret) { - dev_err(&udev->dev, "device reset\n"); - ud->status = SDEV_ST_ERROR; - } else { - dev_info(&udev->dev, "device reset\n"); - ud->status = SDEV_ST_AVAILABLE; - } - spin_unlock_irq(&ud->lock); -} - -static void stub_device_unusable(struct usbip_device *ud) -{ - spin_lock_irq(&ud->lock); - ud->status = SDEV_ST_ERROR; - spin_unlock_irq(&ud->lock); -} - -/** - * stub_device_alloc - allocate a new stub_device struct - * @interface: usb_interface of a new device - * - * Allocates and initializes a new stub_device struct. - */ -static struct stub_device *stub_device_alloc(struct usb_device *udev) -{ - struct stub_device *sdev; - int busnum = udev->bus->busnum; - int devnum = udev->devnum; - - dev_dbg(&udev->dev, "allocating stub device"); - - /* yes, it's a new device */ - sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); - if (!sdev) - return NULL; - - sdev->udev = usb_get_dev(udev); - - /* - * devid is defined with devnum when this driver is first allocated. - * devnum may change later if a device is reset. However, devid never - * changes during a usbip connection. - */ - sdev->devid = (busnum << 16) | devnum; - sdev->ud.side = USBIP_STUB; - sdev->ud.status = SDEV_ST_AVAILABLE; - spin_lock_init(&sdev->ud.lock); - sdev->ud.tcp_socket = NULL; - - INIT_LIST_HEAD(&sdev->priv_init); - INIT_LIST_HEAD(&sdev->priv_tx); - INIT_LIST_HEAD(&sdev->priv_free); - INIT_LIST_HEAD(&sdev->unlink_free); - INIT_LIST_HEAD(&sdev->unlink_tx); - spin_lock_init(&sdev->priv_lock); - - init_waitqueue_head(&sdev->tx_waitq); - - sdev->ud.eh_ops.shutdown = stub_shutdown_connection; - sdev->ud.eh_ops.reset = stub_device_reset; - sdev->ud.eh_ops.unusable = stub_device_unusable; - - usbip_start_eh(&sdev->ud); - - dev_dbg(&udev->dev, "register new device\n"); - - return sdev; -} - -static void stub_device_free(struct stub_device *sdev) -{ - kfree(sdev); -} - -static int stub_probe(struct usb_device *udev) -{ - struct stub_device *sdev = NULL; - const char *udev_busid = dev_name(&udev->dev); - int err = 0; - struct bus_id_priv *busid_priv; - int rc; - - dev_dbg(&udev->dev, "Enter\n"); - - /* check we should claim or not by busid_table */ - busid_priv = get_busid_priv(udev_busid); - if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || - (busid_priv->status == STUB_BUSID_OTHER)) { - dev_info(&udev->dev, - "%s is not in match_busid table... skip!\n", - udev_busid); - - /* - * Return value should be ENODEV or ENOXIO to continue trying - * other matched drivers by the driver core. - * See driver_probe_device() in driver/base/dd.c - */ - return -ENODEV; - } - - if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { - dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", - udev_busid); - return -ENODEV; - } - - if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { - dev_dbg(&udev->dev, - "%s is attached on vhci_hcd... skip!\n", - udev_busid); - - return -ENODEV; - } - - /* ok, this is my device */ - sdev = stub_device_alloc(udev); - if (!sdev) - return -ENOMEM; - - dev_info(&udev->dev, - "usbip-host: register new device (bus %u dev %u)\n", - udev->bus->busnum, udev->devnum); - - busid_priv->shutdown_busid = 0; - - /* set private data to usb_device */ - dev_set_drvdata(&udev->dev, sdev); - busid_priv->sdev = sdev; - busid_priv->udev = udev; - - /* - * Claim this hub port. - * It doesn't matter what value we pass as owner - * (struct dev_state) as long as it is unique. - */ - rc = usb_hub_claim_port(udev->parent, udev->portnum, - (struct usb_dev_state *) udev); - if (rc) { - dev_dbg(&udev->dev, "unable to claim port\n"); - return rc; - } - - err = stub_add_files(&udev->dev); - if (err) { - dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); - dev_set_drvdata(&udev->dev, NULL); - usb_put_dev(udev); - kthread_stop_put(sdev->ud.eh); - - busid_priv->sdev = NULL; - stub_device_free(sdev); - return err; - } - busid_priv->status = STUB_BUSID_ALLOC; - - return 0; -} - -static void shutdown_busid(struct bus_id_priv *busid_priv) -{ - if (busid_priv->sdev && !busid_priv->shutdown_busid) { - busid_priv->shutdown_busid = 1; - usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); - - /* wait for the stop of the event handler */ - usbip_stop_eh(&busid_priv->sdev->ud); - } -} - -/* - * called in usb_disconnect() or usb_deregister() - * but only if actconfig(active configuration) exists - */ -static void stub_disconnect(struct usb_device *udev) -{ - struct stub_device *sdev; - const char *udev_busid = dev_name(&udev->dev); - struct bus_id_priv *busid_priv; - int rc; - - dev_dbg(&udev->dev, "Enter\n"); - - busid_priv = get_busid_priv(udev_busid); - if (!busid_priv) { - BUG(); - return; - } - - sdev = dev_get_drvdata(&udev->dev); - - /* get stub_device */ - if (!sdev) { - dev_err(&udev->dev, "could not get device"); - return; - } - - dev_set_drvdata(&udev->dev, NULL); - - /* - * NOTE: rx/tx threads are invoked for each usb_device. - */ - stub_remove_files(&udev->dev); - - /* release port */ - rc = usb_hub_release_port(udev->parent, udev->portnum, - (struct usb_dev_state *) udev); - if (rc) { - dev_dbg(&udev->dev, "unable to release port\n"); - return; - } - - /* If usb reset is called from event handler */ - if (busid_priv->sdev->ud.eh == current) - return; - - /* shutdown the current connection */ - shutdown_busid(busid_priv); - - usb_put_dev(sdev->udev); - - /* free sdev */ - busid_priv->sdev = NULL; - stub_device_free(sdev); - - if (busid_priv->status == STUB_BUSID_ALLOC) { - busid_priv->status = STUB_BUSID_ADDED; - } else { - busid_priv->status = STUB_BUSID_OTHER; - del_match_busid((char *)udev_busid); - } -} - -#ifdef CONFIG_PM - -/* These functions need usb_port_suspend and usb_port_resume, - * which reside in drivers/usb/core/usb.h. Skip for now. */ - -static int stub_suspend(struct usb_device *udev, pm_message_t message) -{ - dev_dbg(&udev->dev, "stub_suspend\n"); - - return 0; -} - -static int stub_resume(struct usb_device *udev, pm_message_t message) -{ - dev_dbg(&udev->dev, "stub_resume\n"); - - return 0; -} - -#endif /* CONFIG_PM */ - -struct usb_device_driver stub_driver = { - .name = "usbip-host", - .probe = stub_probe, - .disconnect = stub_disconnect, -#ifdef CONFIG_PM - .suspend = stub_suspend, - .resume = stub_resume, -#endif - .supports_autosuspend = 0, -}; diff --git a/drivers/staging/usbip/stub_main.c b/drivers/staging/usbip/stub_main.c deleted file mode 100644 index 44ab43fc4fcc..000000000000 --- a/drivers/staging/usbip/stub_main.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include - -#include "usbip_common.h" -#include "stub.h" - -#define DRIVER_AUTHOR "Takahiro Hirofuchi" -#define DRIVER_DESC "USB/IP Host Driver" - -struct kmem_cache *stub_priv_cache; -/* - * busid_tables defines matching busids that usbip can grab. A user can change - * dynamically what device is locally used and what device is exported to a - * remote host. - */ -#define MAX_BUSID 16 -static struct bus_id_priv busid_table[MAX_BUSID]; -static spinlock_t busid_table_lock; - -static void init_busid_table(void) -{ - /* - * This also sets the bus_table[i].status to - * STUB_BUSID_OTHER, which is 0. - */ - memset(busid_table, 0, sizeof(busid_table)); - - spin_lock_init(&busid_table_lock); -} - -/* - * Find the index of the busid by name. - * Must be called with busid_table_lock held. - */ -static int get_busid_idx(const char *busid) -{ - int i; - int idx = -1; - - for (i = 0; i < MAX_BUSID; i++) - if (busid_table[i].name[0]) - if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { - idx = i; - break; - } - return idx; -} - -struct bus_id_priv *get_busid_priv(const char *busid) -{ - int idx; - struct bus_id_priv *bid = NULL; - - spin_lock(&busid_table_lock); - idx = get_busid_idx(busid); - if (idx >= 0) - bid = &(busid_table[idx]); - spin_unlock(&busid_table_lock); - - return bid; -} - -static int add_match_busid(char *busid) -{ - int i; - int ret = -1; - - spin_lock(&busid_table_lock); - /* already registered? */ - if (get_busid_idx(busid) >= 0) { - ret = 0; - goto out; - } - - for (i = 0; i < MAX_BUSID; i++) - if (!busid_table[i].name[0]) { - strlcpy(busid_table[i].name, busid, BUSID_SIZE); - if ((busid_table[i].status != STUB_BUSID_ALLOC) && - (busid_table[i].status != STUB_BUSID_REMOV)) - busid_table[i].status = STUB_BUSID_ADDED; - ret = 0; - break; - } - -out: - spin_unlock(&busid_table_lock); - - return ret; -} - -int del_match_busid(char *busid) -{ - int idx; - int ret = -1; - - spin_lock(&busid_table_lock); - idx = get_busid_idx(busid); - if (idx < 0) - goto out; - - /* found */ - ret = 0; - - if (busid_table[idx].status == STUB_BUSID_OTHER) - memset(busid_table[idx].name, 0, BUSID_SIZE); - - if ((busid_table[idx].status != STUB_BUSID_OTHER) && - (busid_table[idx].status != STUB_BUSID_ADDED)) - busid_table[idx].status = STUB_BUSID_REMOV; - -out: - spin_unlock(&busid_table_lock); - - return ret; -} - -static ssize_t show_match_busid(struct device_driver *drv, char *buf) -{ - int i; - char *out = buf; - - spin_lock(&busid_table_lock); - for (i = 0; i < MAX_BUSID; i++) - if (busid_table[i].name[0]) - out += sprintf(out, "%s ", busid_table[i].name); - spin_unlock(&busid_table_lock); - out += sprintf(out, "\n"); - - return out - buf; -} - -static ssize_t store_match_busid(struct device_driver *dev, const char *buf, - size_t count) -{ - int len; - char busid[BUSID_SIZE]; - - if (count < 5) - return -EINVAL; - - /* busid needs to include \0 termination */ - len = strlcpy(busid, buf + 4, BUSID_SIZE); - if (sizeof(busid) <= len) - return -EINVAL; - - if (!strncmp(buf, "add ", 4)) { - if (add_match_busid(busid) < 0) - return -ENOMEM; - - pr_debug("add busid %s\n", busid); - return count; - } - - if (!strncmp(buf, "del ", 4)) { - if (del_match_busid(busid) < 0) - return -ENODEV; - - pr_debug("del busid %s\n", busid); - return count; - } - - return -EINVAL; -} -static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, - store_match_busid); - -static ssize_t rebind_store(struct device_driver *dev, const char *buf, - size_t count) -{ - int ret; - int len; - struct bus_id_priv *bid; - - /* buf length should be less that BUSID_SIZE */ - len = strnlen(buf, BUSID_SIZE); - - if (!(len < BUSID_SIZE)) - return -EINVAL; - - bid = get_busid_priv(buf); - if (!bid) - return -ENODEV; - - ret = device_attach(&bid->udev->dev); - if (ret < 0) { - dev_err(&bid->udev->dev, "rebind failed\n"); - return ret; - } - - return count; -} - -static DRIVER_ATTR_WO(rebind); - -static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) -{ - struct stub_priv *priv, *tmp; - - list_for_each_entry_safe(priv, tmp, listhead, list) { - list_del(&priv->list); - return priv; - } - - return NULL; -} - -static struct stub_priv *stub_priv_pop(struct stub_device *sdev) -{ - unsigned long flags; - struct stub_priv *priv; - - spin_lock_irqsave(&sdev->priv_lock, flags); - - priv = stub_priv_pop_from_listhead(&sdev->priv_init); - if (priv) - goto done; - - priv = stub_priv_pop_from_listhead(&sdev->priv_tx); - if (priv) - goto done; - - priv = stub_priv_pop_from_listhead(&sdev->priv_free); - -done: - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return priv; -} - -void stub_device_cleanup_urbs(struct stub_device *sdev) -{ - struct stub_priv *priv; - struct urb *urb; - - dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); - - while ((priv = stub_priv_pop(sdev))) { - urb = priv->urb; - dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); - usb_kill_urb(urb); - - kmem_cache_free(stub_priv_cache, priv); - - kfree(urb->transfer_buffer); - kfree(urb->setup_packet); - usb_free_urb(urb); - } -} - -static int __init usbip_host_init(void) -{ - int ret; - - init_busid_table(); - - stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); - if (!stub_priv_cache) { - pr_err("kmem_cache_create failed\n"); - return -ENOMEM; - } - - ret = usb_register_device_driver(&stub_driver, THIS_MODULE); - if (ret) { - pr_err("usb_register failed %d\n", ret); - goto err_usb_register; - } - - ret = driver_create_file(&stub_driver.drvwrap.driver, - &driver_attr_match_busid); - if (ret) { - pr_err("driver_create_file failed\n"); - goto err_create_file; - } - - ret = driver_create_file(&stub_driver.drvwrap.driver, - &driver_attr_rebind); - if (ret) { - pr_err("driver_create_file failed\n"); - goto err_create_file; - } - - pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); - return ret; - -err_create_file: - usb_deregister_device_driver(&stub_driver); -err_usb_register: - kmem_cache_destroy(stub_priv_cache); - return ret; -} - -static void __exit usbip_host_exit(void) -{ - driver_remove_file(&stub_driver.drvwrap.driver, - &driver_attr_match_busid); - - driver_remove_file(&stub_driver.drvwrap.driver, - &driver_attr_rebind); - - /* - * deregister() calls stub_disconnect() for all devices. Device - * specific data is cleared in stub_disconnect(). - */ - usb_deregister_device_driver(&stub_driver); - - kmem_cache_destroy(stub_priv_cache); -} - -module_init(usbip_host_init); -module_exit(usbip_host_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c deleted file mode 100644 index 00e475c51a12..000000000000 --- a/drivers/staging/usbip/stub_rx.c +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include -#include - -#include "usbip_common.h" -#include "stub.h" - -static int is_clear_halt_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - - return (req->bRequest == USB_REQ_CLEAR_FEATURE) && - (req->bRequestType == USB_RECIP_ENDPOINT) && - (req->wValue == USB_ENDPOINT_HALT); -} - -static int is_set_interface_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - - return (req->bRequest == USB_REQ_SET_INTERFACE) && - (req->bRequestType == USB_RECIP_INTERFACE); -} - -static int is_set_configuration_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - - return (req->bRequest == USB_REQ_SET_CONFIGURATION) && - (req->bRequestType == USB_RECIP_DEVICE); -} - -static int is_reset_device_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - __u16 value; - __u16 index; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - value = le16_to_cpu(req->wValue); - index = le16_to_cpu(req->wIndex); - - if ((req->bRequest == USB_REQ_SET_FEATURE) && - (req->bRequestType == USB_RT_PORT) && - (value == USB_PORT_FEAT_RESET)) { - usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index); - return 1; - } else - return 0; -} - -static int tweak_clear_halt_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - int target_endp; - int target_dir; - int target_pipe; - int ret; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - - /* - * The stalled endpoint is specified in the wIndex value. The endpoint - * of the urb is the target of this clear_halt request (i.e., control - * endpoint). - */ - target_endp = le16_to_cpu(req->wIndex) & 0x000f; - - /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */ - target_dir = le16_to_cpu(req->wIndex) & 0x0080; - - if (target_dir) - target_pipe = usb_rcvctrlpipe(urb->dev, target_endp); - else - target_pipe = usb_sndctrlpipe(urb->dev, target_endp); - - ret = usb_clear_halt(urb->dev, target_pipe); - if (ret < 0) - dev_err(&urb->dev->dev, - "usb_clear_halt error: devnum %d endp %d ret %d\n", - urb->dev->devnum, target_endp, ret); - else - dev_info(&urb->dev->dev, - "usb_clear_halt done: devnum %d endp %d\n", - urb->dev->devnum, target_endp); - - return ret; -} - -static int tweak_set_interface_cmd(struct urb *urb) -{ - struct usb_ctrlrequest *req; - __u16 alternate; - __u16 interface; - int ret; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - alternate = le16_to_cpu(req->wValue); - interface = le16_to_cpu(req->wIndex); - - usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", - interface, alternate); - - ret = usb_set_interface(urb->dev, interface, alternate); - if (ret < 0) - dev_err(&urb->dev->dev, - "usb_set_interface error: inf %u alt %u ret %d\n", - interface, alternate, ret); - else - dev_info(&urb->dev->dev, - "usb_set_interface done: inf %u alt %u\n", - interface, alternate); - - return ret; -} - -static int tweak_set_configuration_cmd(struct urb *urb) -{ - struct stub_priv *priv = (struct stub_priv *) urb->context; - struct stub_device *sdev = priv->sdev; - struct usb_ctrlrequest *req; - __u16 config; - int err; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - config = le16_to_cpu(req->wValue); - - err = usb_set_configuration(sdev->udev, config); - if (err && err != -ENODEV) - dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", - config, err); - return 0; -} - -static int tweak_reset_device_cmd(struct urb *urb) -{ - struct stub_priv *priv = (struct stub_priv *) urb->context; - struct stub_device *sdev = priv->sdev; - - dev_info(&urb->dev->dev, "usb_queue_reset_device\n"); - - /* - * With the implementation of pre_reset and post_reset the driver no - * longer unbinds. This allows the use of synchronous reset. - */ - - if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) { - dev_err(&urb->dev->dev, "could not obtain lock to reset device\n"); - return 0; - } - usb_reset_device(sdev->udev); - usb_unlock_device(sdev->udev); - - return 0; -} - -/* - * clear_halt, set_interface, and set_configuration require special tricks. - */ -static void tweak_special_requests(struct urb *urb) -{ - if (!urb || !urb->setup_packet) - return; - - if (usb_pipetype(urb->pipe) != PIPE_CONTROL) - return; - - if (is_clear_halt_cmd(urb)) - /* tweak clear_halt */ - tweak_clear_halt_cmd(urb); - - else if (is_set_interface_cmd(urb)) - /* tweak set_interface */ - tweak_set_interface_cmd(urb); - - else if (is_set_configuration_cmd(urb)) - /* tweak set_configuration */ - tweak_set_configuration_cmd(urb); - - else if (is_reset_device_cmd(urb)) - tweak_reset_device_cmd(urb); - else - usbip_dbg_stub_rx("no need to tweak\n"); -} - -/* - * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb(). - * By unlinking the urb asynchronously, stub_rx can continuously - * process coming urbs. Even if the urb is unlinked, its completion - * handler will be called and stub_tx will send a return pdu. - * - * See also comments about unlinking strategy in vhci_hcd.c. - */ -static int stub_recv_cmd_unlink(struct stub_device *sdev, - struct usbip_header *pdu) -{ - int ret; - unsigned long flags; - struct stub_priv *priv; - - spin_lock_irqsave(&sdev->priv_lock, flags); - - list_for_each_entry(priv, &sdev->priv_init, list) { - if (priv->seqnum != pdu->u.cmd_unlink.seqnum) - continue; - - dev_info(&priv->urb->dev->dev, "unlink urb %p\n", - priv->urb); - - /* - * This matched urb is not completed yet (i.e., be in - * flight in usb hcd hardware/driver). Now we are - * cancelling it. The unlinking flag means that we are - * now not going to return the normal result pdu of a - * submission request, but going to return a result pdu - * of the unlink request. - */ - priv->unlinking = 1; - - /* - * In the case that unlinking flag is on, prev->seqnum - * is changed from the seqnum of the cancelling urb to - * the seqnum of the unlink request. This will be used - * to make the result pdu of the unlink request. - */ - priv->seqnum = pdu->base.seqnum; - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - /* - * usb_unlink_urb() is now out of spinlocking to avoid - * spinlock recursion since stub_complete() is - * sometimes called in this context but not in the - * interrupt context. If stub_complete() is executed - * before we call usb_unlink_urb(), usb_unlink_urb() - * will return an error value. In this case, stub_tx - * will return the result pdu of this unlink request - * though submission is completed and actual unlinking - * is not executed. OK? - */ - /* In the above case, urb->status is not -ECONNRESET, - * so a driver in a client host will know the failure - * of the unlink request ? - */ - ret = usb_unlink_urb(priv->urb); - if (ret != -EINPROGRESS) - dev_err(&priv->urb->dev->dev, - "failed to unlink a urb %p, ret %d\n", - priv->urb, ret); - - return 0; - } - - usbip_dbg_stub_rx("seqnum %d is not pending\n", - pdu->u.cmd_unlink.seqnum); - - /* - * The urb of the unlink target is not found in priv_init queue. It was - * already completed and its results is/was going to be sent by a - * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only - * return the completeness of this unlink request to vhci_hcd. - */ - stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0); - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return 0; -} - -static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) -{ - struct usbip_device *ud = &sdev->ud; - int valid = 0; - - if (pdu->base.devid == sdev->devid) { - spin_lock_irq(&ud->lock); - if (ud->status == SDEV_ST_USED) { - /* A request is valid. */ - valid = 1; - } - spin_unlock_irq(&ud->lock); - } - - return valid; -} - -static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, - struct usbip_header *pdu) -{ - struct stub_priv *priv; - struct usbip_device *ud = &sdev->ud; - unsigned long flags; - - spin_lock_irqsave(&sdev->priv_lock, flags); - - priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC); - if (!priv) { - dev_err(&sdev->interface->dev, "alloc stub_priv\n"); - spin_unlock_irqrestore(&sdev->priv_lock, flags); - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return NULL; - } - - priv->seqnum = pdu->base.seqnum; - priv->sdev = sdev; - - /* - * After a stub_priv is linked to a list_head, - * our error handler can free allocated data. - */ - list_add_tail(&priv->list, &sdev->priv_init); - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return priv; -} - -static int get_pipe(struct stub_device *sdev, int epnum, int dir) -{ - struct usb_device *udev = sdev->udev; - struct usb_host_endpoint *ep; - struct usb_endpoint_descriptor *epd = NULL; - - if (dir == USBIP_DIR_IN) - ep = udev->ep_in[epnum & 0x7f]; - else - ep = udev->ep_out[epnum & 0x7f]; - if (!ep) { - dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", - epnum); - BUG(); - } - - epd = &ep->desc; - if (usb_endpoint_xfer_control(epd)) { - if (dir == USBIP_DIR_OUT) - return usb_sndctrlpipe(udev, epnum); - else - return usb_rcvctrlpipe(udev, epnum); - } - - if (usb_endpoint_xfer_bulk(epd)) { - if (dir == USBIP_DIR_OUT) - return usb_sndbulkpipe(udev, epnum); - else - return usb_rcvbulkpipe(udev, epnum); - } - - if (usb_endpoint_xfer_int(epd)) { - if (dir == USBIP_DIR_OUT) - return usb_sndintpipe(udev, epnum); - else - return usb_rcvintpipe(udev, epnum); - } - - if (usb_endpoint_xfer_isoc(epd)) { - if (dir == USBIP_DIR_OUT) - return usb_sndisocpipe(udev, epnum); - else - return usb_rcvisocpipe(udev, epnum); - } - - /* NOT REACHED */ - dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); - return 0; -} - -static void masking_bogus_flags(struct urb *urb) -{ - int xfertype; - struct usb_device *dev; - struct usb_host_endpoint *ep; - int is_out; - unsigned int allowed; - - if (!urb || urb->hcpriv || !urb->complete) - return; - dev = urb->dev; - if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) - return; - - ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) - [usb_pipeendpoint(urb->pipe)]; - if (!ep) - return; - - xfertype = usb_endpoint_type(&ep->desc); - if (xfertype == USB_ENDPOINT_XFER_CONTROL) { - struct usb_ctrlrequest *setup = - (struct usb_ctrlrequest *) urb->setup_packet; - - if (!setup) - return; - is_out = !(setup->bRequestType & USB_DIR_IN) || - !setup->wLength; - } else { - is_out = usb_endpoint_dir_out(&ep->desc); - } - - /* enforce simple/standard policy */ - allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | - URB_DIR_MASK | URB_FREE_BUFFER); - switch (xfertype) { - case USB_ENDPOINT_XFER_BULK: - if (is_out) - allowed |= URB_ZERO_PACKET; - /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_CONTROL: - allowed |= URB_NO_FSBR; /* only affects UHCI */ - /* FALLTHROUGH */ - default: /* all non-iso endpoints */ - if (!is_out) - allowed |= URB_SHORT_NOT_OK; - break; - case USB_ENDPOINT_XFER_ISOC: - allowed |= URB_ISO_ASAP; - break; - } - urb->transfer_flags &= allowed; -} - -static void stub_recv_cmd_submit(struct stub_device *sdev, - struct usbip_header *pdu) -{ - int ret; - struct stub_priv *priv; - struct usbip_device *ud = &sdev->ud; - struct usb_device *udev = sdev->udev; - int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); - - priv = stub_priv_alloc(sdev, pdu); - if (!priv) - return; - - /* setup a urb */ - if (usb_pipeisoc(pipe)) - priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, - GFP_KERNEL); - else - priv->urb = usb_alloc_urb(0, GFP_KERNEL); - - if (!priv->urb) { - dev_err(&sdev->interface->dev, "malloc urb\n"); - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; - } - - /* allocate urb transfer buffer, if needed */ - if (pdu->u.cmd_submit.transfer_buffer_length > 0) { - priv->urb->transfer_buffer = - kzalloc(pdu->u.cmd_submit.transfer_buffer_length, - GFP_KERNEL); - if (!priv->urb->transfer_buffer) { - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; - } - } - - /* copy urb setup packet */ - priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, - GFP_KERNEL); - if (!priv->urb->setup_packet) { - dev_err(&sdev->interface->dev, "allocate setup_packet\n"); - usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); - return; - } - - /* set other members from the base header of pdu */ - priv->urb->context = (void *) priv; - priv->urb->dev = udev; - priv->urb->pipe = pipe; - priv->urb->complete = stub_complete; - - usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); - - - if (usbip_recv_xbuff(ud, priv->urb) < 0) - return; - - if (usbip_recv_iso(ud, priv->urb) < 0) - return; - - /* no need to submit an intercepted request, but harmless? */ - tweak_special_requests(priv->urb); - - masking_bogus_flags(priv->urb); - /* urb is now ready to submit */ - ret = usb_submit_urb(priv->urb, GFP_KERNEL); - - if (ret == 0) - usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", - pdu->base.seqnum); - else { - dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret); - usbip_dump_header(pdu); - usbip_dump_urb(priv->urb); - - /* - * Pessimistic. - * This connection will be discarded. - */ - usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); - } - - usbip_dbg_stub_rx("Leave\n"); -} - -/* recv a pdu */ -static void stub_rx_pdu(struct usbip_device *ud) -{ - int ret; - struct usbip_header pdu; - struct stub_device *sdev = container_of(ud, struct stub_device, ud); - struct device *dev = &sdev->udev->dev; - - usbip_dbg_stub_rx("Enter\n"); - - memset(&pdu, 0, sizeof(pdu)); - - /* receive a pdu header */ - ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); - if (ret != sizeof(pdu)) { - dev_err(dev, "recv a header, %d\n", ret); - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - return; - } - - usbip_header_correct_endian(&pdu, 0); - - if (usbip_dbg_flag_stub_rx) - usbip_dump_header(&pdu); - - if (!valid_request(sdev, &pdu)) { - dev_err(dev, "recv invalid request\n"); - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - return; - } - - switch (pdu.base.command) { - case USBIP_CMD_UNLINK: - stub_recv_cmd_unlink(sdev, &pdu); - break; - - case USBIP_CMD_SUBMIT: - stub_recv_cmd_submit(sdev, &pdu); - break; - - default: - /* NOTREACHED */ - dev_err(dev, "unknown pdu\n"); - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - break; - } -} - -int stub_rx_loop(void *data) -{ - struct usbip_device *ud = data; - - while (!kthread_should_stop()) { - if (usbip_event_happened(ud)) - break; - - stub_rx_pdu(ud); - } - - return 0; -} diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c deleted file mode 100644 index dbcabc9dbe0d..000000000000 --- a/drivers/staging/usbip/stub_tx.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include - -#include "usbip_common.h" -#include "stub.h" - -static void stub_free_priv_and_urb(struct stub_priv *priv) -{ - struct urb *urb = priv->urb; - - kfree(urb->setup_packet); - kfree(urb->transfer_buffer); - list_del(&priv->list); - kmem_cache_free(stub_priv_cache, priv); - usb_free_urb(urb); -} - -/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ -void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, - __u32 status) -{ - struct stub_unlink *unlink; - - unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); - if (!unlink) { - usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); - return; - } - - unlink->seqnum = seqnum; - unlink->status = status; - - list_add_tail(&unlink->list, &sdev->unlink_tx); -} - -/** - * stub_complete - completion handler of a usbip urb - * @urb: pointer to the urb completed - * - * When a urb has completed, the USB core driver calls this function mostly in - * the interrupt context. To return the result of a urb, the completed urb is - * linked to the pending list of returning. - * - */ -void stub_complete(struct urb *urb) -{ - struct stub_priv *priv = (struct stub_priv *) urb->context; - struct stub_device *sdev = priv->sdev; - unsigned long flags; - - usbip_dbg_stub_tx("complete! status %d\n", urb->status); - - switch (urb->status) { - case 0: - /* OK */ - break; - case -ENOENT: - dev_info(&urb->dev->dev, - "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); - return; - case -ECONNRESET: - dev_info(&urb->dev->dev, - "unlinked by a call to usb_unlink_urb()\n"); - break; - case -EPIPE: - dev_info(&urb->dev->dev, "endpoint %d is stalled\n", - usb_pipeendpoint(urb->pipe)); - break; - case -ESHUTDOWN: - dev_info(&urb->dev->dev, "device removed?\n"); - break; - default: - dev_info(&urb->dev->dev, - "urb completion with non-zero status %d\n", - urb->status); - break; - } - - /* link a urb to the queue of tx. */ - spin_lock_irqsave(&sdev->priv_lock, flags); - if (priv->unlinking) { - stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); - stub_free_priv_and_urb(priv); - } else { - list_move_tail(&priv->list, &sdev->priv_tx); - } - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - /* wake up tx_thread */ - wake_up(&sdev->tx_waitq); -} - -static inline void setup_base_pdu(struct usbip_header_basic *base, - __u32 command, __u32 seqnum) -{ - base->command = command; - base->seqnum = seqnum; - base->devid = 0; - base->ep = 0; - base->direction = 0; -} - -static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb) -{ - struct stub_priv *priv = (struct stub_priv *) urb->context; - - setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum); - usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1); -} - -static void setup_ret_unlink_pdu(struct usbip_header *rpdu, - struct stub_unlink *unlink) -{ - setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum); - rpdu->u.ret_unlink.status = unlink->status; -} - -static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev) -{ - unsigned long flags; - struct stub_priv *priv, *tmp; - - spin_lock_irqsave(&sdev->priv_lock, flags); - - list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) { - list_move_tail(&priv->list, &sdev->priv_free); - spin_unlock_irqrestore(&sdev->priv_lock, flags); - return priv; - } - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return NULL; -} - -static int stub_send_ret_submit(struct stub_device *sdev) -{ - unsigned long flags; - struct stub_priv *priv, *tmp; - - struct msghdr msg; - size_t txsize; - - size_t total_size = 0; - - while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { - int ret; - struct urb *urb = priv->urb; - struct usbip_header pdu_header; - struct usbip_iso_packet_descriptor *iso_buffer = NULL; - struct kvec *iov = NULL; - int iovnum = 0; - - txsize = 0; - memset(&pdu_header, 0, sizeof(pdu_header)); - memset(&msg, 0, sizeof(msg)); - - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) - iovnum = 2 + urb->number_of_packets; - else - iovnum = 2; - - iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); - - if (!iov) { - usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); - return -1; - } - - iovnum = 0; - - /* 1. setup usbip_header */ - setup_ret_submit_pdu(&pdu_header, urb); - usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", - pdu_header.base.seqnum, urb); - usbip_header_correct_endian(&pdu_header, 1); - - iov[iovnum].iov_base = &pdu_header; - iov[iovnum].iov_len = sizeof(pdu_header); - iovnum++; - txsize += sizeof(pdu_header); - - /* 2. setup transfer buffer */ - if (usb_pipein(urb->pipe) && - usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && - urb->actual_length > 0) { - iov[iovnum].iov_base = urb->transfer_buffer; - iov[iovnum].iov_len = urb->actual_length; - iovnum++; - txsize += urb->actual_length; - } else if (usb_pipein(urb->pipe) && - usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - /* - * For isochronous packets: actual length is the sum of - * the actual length of the individual, packets, but as - * the packet offsets are not changed there will be - * padding between the packets. To optimally use the - * bandwidth the padding is not transmitted. - */ - - int i; - - for (i = 0; i < urb->number_of_packets; i++) { - iov[iovnum].iov_base = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - iov[iovnum].iov_len = - urb->iso_frame_desc[i].actual_length; - iovnum++; - txsize += urb->iso_frame_desc[i].actual_length; - } - - if (txsize != sizeof(pdu_header) + urb->actual_length) { - dev_err(&sdev->interface->dev, - "actual length of urb %d does not match iso packet sizes %zu\n", - urb->actual_length, - txsize-sizeof(pdu_header)); - kfree(iov); - usbip_event_add(&sdev->ud, - SDEV_EVENT_ERROR_TCP); - return -1; - } - } - - /* 3. setup iso_packet_descriptor */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - ssize_t len = 0; - - iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); - if (!iso_buffer) { - usbip_event_add(&sdev->ud, - SDEV_EVENT_ERROR_MALLOC); - kfree(iov); - return -1; - } - - iov[iovnum].iov_base = iso_buffer; - iov[iovnum].iov_len = len; - txsize += len; - iovnum++; - } - - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, - iov, iovnum, txsize); - if (ret != txsize) { - dev_err(&sdev->interface->dev, - "sendmsg failed!, retval %d for %zd\n", - ret, txsize); - kfree(iov); - kfree(iso_buffer); - usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); - return -1; - } - - kfree(iov); - kfree(iso_buffer); - - total_size += txsize; - } - - spin_lock_irqsave(&sdev->priv_lock, flags); - list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { - stub_free_priv_and_urb(priv); - } - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return total_size; -} - -static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev) -{ - unsigned long flags; - struct stub_unlink *unlink, *tmp; - - spin_lock_irqsave(&sdev->priv_lock, flags); - - list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { - list_move_tail(&unlink->list, &sdev->unlink_free); - spin_unlock_irqrestore(&sdev->priv_lock, flags); - return unlink; - } - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return NULL; -} - -static int stub_send_ret_unlink(struct stub_device *sdev) -{ - unsigned long flags; - struct stub_unlink *unlink, *tmp; - - struct msghdr msg; - struct kvec iov[1]; - size_t txsize; - - size_t total_size = 0; - - while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) { - int ret; - struct usbip_header pdu_header; - - txsize = 0; - memset(&pdu_header, 0, sizeof(pdu_header)); - memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - - usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum); - - /* 1. setup usbip_header */ - setup_ret_unlink_pdu(&pdu_header, unlink); - usbip_header_correct_endian(&pdu_header, 1); - - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); - txsize += sizeof(pdu_header); - - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, - 1, txsize); - if (ret != txsize) { - dev_err(&sdev->interface->dev, - "sendmsg failed!, retval %d for %zd\n", - ret, txsize); - usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); - return -1; - } - - usbip_dbg_stub_tx("send txdata\n"); - total_size += txsize; - } - - spin_lock_irqsave(&sdev->priv_lock, flags); - - list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) { - list_del(&unlink->list); - kfree(unlink); - } - - spin_unlock_irqrestore(&sdev->priv_lock, flags); - - return total_size; -} - -int stub_tx_loop(void *data) -{ - struct usbip_device *ud = data; - struct stub_device *sdev = container_of(ud, struct stub_device, ud); - - while (!kthread_should_stop()) { - if (usbip_event_happened(ud)) - break; - - /* - * send_ret_submit comes earlier than send_ret_unlink. stub_rx - * looks at only priv_init queue. If the completion of a URB is - * earlier than the receive of CMD_UNLINK, priv is moved to - * priv_tx queue and stub_rx does not find the target priv. In - * this case, vhci_rx receives the result of the submit request - * and then receives the result of the unlink request. The - * result of the submit is given back to the usbcore as the - * completion of the unlink request. The request of the - * unlink is ignored. This is ok because a driver who calls - * usb_unlink_urb() understands the unlink was too late by - * getting the status of the given-backed URB which has the - * status of usb_submit_urb(). - */ - if (stub_send_ret_submit(sdev) < 0) - break; - - if (stub_send_ret_unlink(sdev) < 0) - break; - - wait_event_interruptible(sdev->tx_waitq, - (!list_empty(&sdev->priv_tx) || - !list_empty(&sdev->unlink_tx) || - kthread_should_stop())); - } - - return 0; -} diff --git a/drivers/staging/usbip/uapi/usbip.h b/drivers/staging/usbip/uapi/usbip.h deleted file mode 100644 index fa5db30ede36..000000000000 --- a/drivers/staging/usbip/uapi/usbip.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * usbip.h - * - * USBIP uapi defines and function prototypes etc. -*/ - -#ifndef _UAPI_LINUX_USBIP_H -#define _UAPI_LINUX_USBIP_H - -/* usbip device status - exported in usbip device sysfs status */ -enum usbip_device_status { - /* sdev is available. */ - SDEV_ST_AVAILABLE = 0x01, - /* sdev is now used. */ - SDEV_ST_USED, - /* sdev is unusable because of a fatal error. */ - SDEV_ST_ERROR, - - /* vdev does not connect a remote device. */ - VDEV_ST_NULL, - /* vdev is used, but the USB address is not assigned yet */ - VDEV_ST_NOTASSIGNED, - VDEV_ST_USED, - VDEV_ST_ERROR -}; -#endif /* _UAPI_LINUX_USBIP_H */ diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c deleted file mode 100644 index facaaf003f19..000000000000 --- a/drivers/staging/usbip/usbip_common.c +++ /dev/null @@ -1,776 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "usbip_common.h" - -#define DRIVER_AUTHOR "Takahiro Hirofuchi " -#define DRIVER_DESC "USB/IP Core" - -#ifdef CONFIG_USBIP_DEBUG -unsigned long usbip_debug_flag = 0xffffffff; -#else -unsigned long usbip_debug_flag; -#endif -EXPORT_SYMBOL_GPL(usbip_debug_flag); -module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)"); - -/* FIXME */ -struct device_attribute dev_attr_usbip_debug; -EXPORT_SYMBOL_GPL(dev_attr_usbip_debug); - -static ssize_t usbip_debug_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%lx\n", usbip_debug_flag); -} - -static ssize_t usbip_debug_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - if (sscanf(buf, "%lx", &usbip_debug_flag) != 1) - return -EINVAL; - return count; -} -DEVICE_ATTR_RW(usbip_debug); - -static void usbip_dump_buffer(char *buff, int bufflen) -{ - print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4, - buff, bufflen, false); -} - -static void usbip_dump_pipe(unsigned int p) -{ - unsigned char type = usb_pipetype(p); - unsigned char ep = usb_pipeendpoint(p); - unsigned char dev = usb_pipedevice(p); - unsigned char dir = usb_pipein(p); - - pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT"); - - switch (type) { - case PIPE_ISOCHRONOUS: - pr_debug("ISO\n"); - break; - case PIPE_INTERRUPT: - pr_debug("INT\n"); - break; - case PIPE_CONTROL: - pr_debug("CTRL\n"); - break; - case PIPE_BULK: - pr_debug("BULK\n"); - break; - default: - pr_debug("ERR\n"); - break; - } -} - -static void usbip_dump_usb_device(struct usb_device *udev) -{ - struct device *dev = &udev->dev; - int i; - - dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", - udev->devnum, udev->devpath, usb_speed_string(udev->speed)); - - pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); - - dev_dbg(dev, " "); - for (i = 0; i < 16; i++) - pr_debug(" %2u", i); - pr_debug("\n"); - - dev_dbg(dev, " toggle0(IN) :"); - for (i = 0; i < 16; i++) - pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0); - pr_debug("\n"); - - dev_dbg(dev, " toggle1(OUT):"); - for (i = 0; i < 16; i++) - pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0); - pr_debug("\n"); - - dev_dbg(dev, " epmaxp_in :"); - for (i = 0; i < 16; i++) { - if (udev->ep_in[i]) - pr_debug(" %2u", - le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize)); - } - pr_debug("\n"); - - dev_dbg(dev, " epmaxp_out :"); - for (i = 0; i < 16; i++) { - if (udev->ep_out[i]) - pr_debug(" %2u", - le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize)); - } - pr_debug("\n"); - - dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); - - dev_dbg(dev, - "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", - &udev->descriptor, udev->config, - udev->actconfig, udev->rawdescriptors); - - dev_dbg(dev, "have_langid %d, string_langid %d\n", - udev->have_langid, udev->string_langid); - - dev_dbg(dev, "maxchild %d\n", udev->maxchild); -} - -static void usbip_dump_request_type(__u8 rt) -{ - switch (rt & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - pr_debug("DEVICE"); - break; - case USB_RECIP_INTERFACE: - pr_debug("INTERF"); - break; - case USB_RECIP_ENDPOINT: - pr_debug("ENDPOI"); - break; - case USB_RECIP_OTHER: - pr_debug("OTHER "); - break; - default: - pr_debug("------"); - break; - } -} - -static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd) -{ - if (!cmd) { - pr_debug(" : null pointer\n"); - return; - } - - pr_debug(" "); - pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ", - cmd->bRequestType, cmd->bRequest, - cmd->wValue, cmd->wIndex, cmd->wLength); - pr_debug("\n "); - - if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - pr_debug("STANDARD "); - switch (cmd->bRequest) { - case USB_REQ_GET_STATUS: - pr_debug("GET_STATUS\n"); - break; - case USB_REQ_CLEAR_FEATURE: - pr_debug("CLEAR_FEAT\n"); - break; - case USB_REQ_SET_FEATURE: - pr_debug("SET_FEAT\n"); - break; - case USB_REQ_SET_ADDRESS: - pr_debug("SET_ADDRRS\n"); - break; - case USB_REQ_GET_DESCRIPTOR: - pr_debug("GET_DESCRI\n"); - break; - case USB_REQ_SET_DESCRIPTOR: - pr_debug("SET_DESCRI\n"); - break; - case USB_REQ_GET_CONFIGURATION: - pr_debug("GET_CONFIG\n"); - break; - case USB_REQ_SET_CONFIGURATION: - pr_debug("SET_CONFIG\n"); - break; - case USB_REQ_GET_INTERFACE: - pr_debug("GET_INTERF\n"); - break; - case USB_REQ_SET_INTERFACE: - pr_debug("SET_INTERF\n"); - break; - case USB_REQ_SYNCH_FRAME: - pr_debug("SYNC_FRAME\n"); - break; - default: - pr_debug("REQ(%02X)\n", cmd->bRequest); - break; - } - usbip_dump_request_type(cmd->bRequestType); - } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { - pr_debug("CLASS\n"); - } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { - pr_debug("VENDOR\n"); - } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) { - pr_debug("RESERVED\n"); - } -} - -void usbip_dump_urb(struct urb *urb) -{ - struct device *dev; - - if (!urb) { - pr_debug("urb: null pointer!!\n"); - return; - } - - if (!urb->dev) { - pr_debug("urb->dev: null pointer!!\n"); - return; - } - - dev = &urb->dev->dev; - - dev_dbg(dev, " urb :%p\n", urb); - dev_dbg(dev, " dev :%p\n", urb->dev); - - usbip_dump_usb_device(urb->dev); - - dev_dbg(dev, " pipe :%08x ", urb->pipe); - - usbip_dump_pipe(urb->pipe); - - dev_dbg(dev, " status :%d\n", urb->status); - dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); - dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); - dev_dbg(dev, " transfer_buffer_length:%d\n", - urb->transfer_buffer_length); - dev_dbg(dev, " actual_length :%d\n", urb->actual_length); - dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); - - if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) - usbip_dump_usb_ctrlrequest( - (struct usb_ctrlrequest *)urb->setup_packet); - - dev_dbg(dev, " start_frame :%d\n", urb->start_frame); - dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); - dev_dbg(dev, " interval :%d\n", urb->interval); - dev_dbg(dev, " error_count :%d\n", urb->error_count); - dev_dbg(dev, " context :%p\n", urb->context); - dev_dbg(dev, " complete :%p\n", urb->complete); -} -EXPORT_SYMBOL_GPL(usbip_dump_urb); - -void usbip_dump_header(struct usbip_header *pdu) -{ - pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n", - pdu->base.command, - pdu->base.seqnum, - pdu->base.devid, - pdu->base.direction, - pdu->base.ep); - - switch (pdu->base.command) { - case USBIP_CMD_SUBMIT: - pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n", - pdu->u.cmd_submit.transfer_flags, - pdu->u.cmd_submit.transfer_buffer_length, - pdu->u.cmd_submit.start_frame, - pdu->u.cmd_submit.number_of_packets, - pdu->u.cmd_submit.interval); - break; - case USBIP_CMD_UNLINK: - pr_debug("USBIP_CMD_UNLINK: seq %u\n", - pdu->u.cmd_unlink.seqnum); - break; - case USBIP_RET_SUBMIT: - pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", - pdu->u.ret_submit.status, - pdu->u.ret_submit.actual_length, - pdu->u.ret_submit.start_frame, - pdu->u.ret_submit.number_of_packets, - pdu->u.ret_submit.error_count); - break; - case USBIP_RET_UNLINK: - pr_debug("USBIP_RET_UNLINK: status %d\n", - pdu->u.ret_unlink.status); - break; - default: - /* NOT REACHED */ - pr_err("unknown command\n"); - break; - } -} -EXPORT_SYMBOL_GPL(usbip_dump_header); - -/* Receive data over TCP/IP. */ -int usbip_recv(struct socket *sock, void *buf, int size) -{ - int result; - struct msghdr msg; - struct kvec iov; - int total = 0; - - /* for blocks of if (usbip_dbg_flag_xmit) */ - char *bp = buf; - int osize = size; - - usbip_dbg_xmit("enter\n"); - - if (!sock || !buf || !size) { - pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, - size); - return -EINVAL; - } - - do { - sock->sk->sk_allocation = GFP_NOIO; - iov.iov_base = buf; - iov.iov_len = size; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = MSG_NOSIGNAL; - - result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); - if (result <= 0) { - pr_debug("receive sock %p buf %p size %u ret %d total %d\n", - sock, buf, size, result, total); - goto err; - } - - size -= result; - buf += result; - total += result; - } while (size > 0); - - if (usbip_dbg_flag_xmit) { - if (!in_interrupt()) - pr_debug("%-10s:", current->comm); - else - pr_debug("interrupt :"); - - pr_debug("receiving....\n"); - usbip_dump_buffer(bp, osize); - pr_debug("received, osize %d ret %d size %d total %d\n", - osize, result, size, total); - } - - return total; - -err: - return result; -} -EXPORT_SYMBOL_GPL(usbip_recv); - -/* there may be more cases to tweak the flags. */ -static unsigned int tweak_transfer_flags(unsigned int flags) -{ - flags &= ~URB_NO_TRANSFER_DMA_MAP; - return flags; -} - -static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb, - int pack) -{ - struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit; - - /* - * Some members are not still implemented in usbip. I hope this issue - * will be discussed when usbip is ported to other operating systems. - */ - if (pack) { - spdu->transfer_flags = - tweak_transfer_flags(urb->transfer_flags); - spdu->transfer_buffer_length = urb->transfer_buffer_length; - spdu->start_frame = urb->start_frame; - spdu->number_of_packets = urb->number_of_packets; - spdu->interval = urb->interval; - } else { - urb->transfer_flags = spdu->transfer_flags; - urb->transfer_buffer_length = spdu->transfer_buffer_length; - urb->start_frame = spdu->start_frame; - urb->number_of_packets = spdu->number_of_packets; - urb->interval = spdu->interval; - } -} - -static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, - int pack) -{ - struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit; - - if (pack) { - rpdu->status = urb->status; - rpdu->actual_length = urb->actual_length; - rpdu->start_frame = urb->start_frame; - rpdu->number_of_packets = urb->number_of_packets; - rpdu->error_count = urb->error_count; - } else { - urb->status = rpdu->status; - urb->actual_length = rpdu->actual_length; - urb->start_frame = rpdu->start_frame; - urb->number_of_packets = rpdu->number_of_packets; - urb->error_count = rpdu->error_count; - } -} - -void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, - int pack) -{ - switch (cmd) { - case USBIP_CMD_SUBMIT: - usbip_pack_cmd_submit(pdu, urb, pack); - break; - case USBIP_RET_SUBMIT: - usbip_pack_ret_submit(pdu, urb, pack); - break; - default: - /* NOT REACHED */ - pr_err("unknown command\n"); - break; - } -} -EXPORT_SYMBOL_GPL(usbip_pack_pdu); - -static void correct_endian_basic(struct usbip_header_basic *base, int send) -{ - if (send) { - base->command = cpu_to_be32(base->command); - base->seqnum = cpu_to_be32(base->seqnum); - base->devid = cpu_to_be32(base->devid); - base->direction = cpu_to_be32(base->direction); - base->ep = cpu_to_be32(base->ep); - } else { - base->command = be32_to_cpu(base->command); - base->seqnum = be32_to_cpu(base->seqnum); - base->devid = be32_to_cpu(base->devid); - base->direction = be32_to_cpu(base->direction); - base->ep = be32_to_cpu(base->ep); - } -} - -static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu, - int send) -{ - if (send) { - pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags); - - cpu_to_be32s(&pdu->transfer_buffer_length); - cpu_to_be32s(&pdu->start_frame); - cpu_to_be32s(&pdu->number_of_packets); - cpu_to_be32s(&pdu->interval); - } else { - pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags); - - be32_to_cpus(&pdu->transfer_buffer_length); - be32_to_cpus(&pdu->start_frame); - be32_to_cpus(&pdu->number_of_packets); - be32_to_cpus(&pdu->interval); - } -} - -static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, - int send) -{ - if (send) { - cpu_to_be32s(&pdu->status); - cpu_to_be32s(&pdu->actual_length); - cpu_to_be32s(&pdu->start_frame); - cpu_to_be32s(&pdu->number_of_packets); - cpu_to_be32s(&pdu->error_count); - } else { - be32_to_cpus(&pdu->status); - be32_to_cpus(&pdu->actual_length); - be32_to_cpus(&pdu->start_frame); - be32_to_cpus(&pdu->number_of_packets); - be32_to_cpus(&pdu->error_count); - } -} - -static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu, - int send) -{ - if (send) - pdu->seqnum = cpu_to_be32(pdu->seqnum); - else - pdu->seqnum = be32_to_cpu(pdu->seqnum); -} - -static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu, - int send) -{ - if (send) - cpu_to_be32s(&pdu->status); - else - be32_to_cpus(&pdu->status); -} - -void usbip_header_correct_endian(struct usbip_header *pdu, int send) -{ - __u32 cmd = 0; - - if (send) - cmd = pdu->base.command; - - correct_endian_basic(&pdu->base, send); - - if (!send) - cmd = pdu->base.command; - - switch (cmd) { - case USBIP_CMD_SUBMIT: - correct_endian_cmd_submit(&pdu->u.cmd_submit, send); - break; - case USBIP_RET_SUBMIT: - correct_endian_ret_submit(&pdu->u.ret_submit, send); - break; - case USBIP_CMD_UNLINK: - correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send); - break; - case USBIP_RET_UNLINK: - correct_endian_ret_unlink(&pdu->u.ret_unlink, send); - break; - default: - /* NOT REACHED */ - pr_err("unknown command\n"); - break; - } -} -EXPORT_SYMBOL_GPL(usbip_header_correct_endian); - -static void usbip_iso_packet_correct_endian( - struct usbip_iso_packet_descriptor *iso, int send) -{ - /* does not need all members. but copy all simply. */ - if (send) { - iso->offset = cpu_to_be32(iso->offset); - iso->length = cpu_to_be32(iso->length); - iso->status = cpu_to_be32(iso->status); - iso->actual_length = cpu_to_be32(iso->actual_length); - } else { - iso->offset = be32_to_cpu(iso->offset); - iso->length = be32_to_cpu(iso->length); - iso->status = be32_to_cpu(iso->status); - iso->actual_length = be32_to_cpu(iso->actual_length); - } -} - -static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso, - struct usb_iso_packet_descriptor *uiso, int pack) -{ - if (pack) { - iso->offset = uiso->offset; - iso->length = uiso->length; - iso->status = uiso->status; - iso->actual_length = uiso->actual_length; - } else { - uiso->offset = iso->offset; - uiso->length = iso->length; - uiso->status = iso->status; - uiso->actual_length = iso->actual_length; - } -} - -/* must free buffer */ -struct usbip_iso_packet_descriptor* -usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen) -{ - struct usbip_iso_packet_descriptor *iso; - int np = urb->number_of_packets; - ssize_t size = np * sizeof(*iso); - int i; - - iso = kzalloc(size, GFP_KERNEL); - if (!iso) - return NULL; - - for (i = 0; i < np; i++) { - usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1); - usbip_iso_packet_correct_endian(&iso[i], 1); - } - - *bufflen = size; - - return iso; -} -EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu); - -/* some members of urb must be substituted before. */ -int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) -{ - void *buff; - struct usbip_iso_packet_descriptor *iso; - int np = urb->number_of_packets; - int size = np * sizeof(*iso); - int i; - int ret; - int total_length = 0; - - if (!usb_pipeisoc(urb->pipe)) - return 0; - - /* my Bluetooth dongle gets ISO URBs which are np = 0 */ - if (np == 0) - return 0; - - buff = kzalloc(size, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - ret = usbip_recv(ud->tcp_socket, buff, size); - if (ret != size) { - dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n", - ret); - kfree(buff); - - if (ud->side == USBIP_STUB) - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - else - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - - return -EPIPE; - } - - iso = (struct usbip_iso_packet_descriptor *) buff; - for (i = 0; i < np; i++) { - usbip_iso_packet_correct_endian(&iso[i], 0); - usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); - total_length += urb->iso_frame_desc[i].actual_length; - } - - kfree(buff); - - if (total_length != urb->actual_length) { - dev_err(&urb->dev->dev, - "total length of iso packets %d not equal to actual length of buffer %d\n", - total_length, urb->actual_length); - - if (ud->side == USBIP_STUB) - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - else - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - - return -EPIPE; - } - - return ret; -} -EXPORT_SYMBOL_GPL(usbip_recv_iso); - -/* - * This functions restores the padding which was removed for optimizing - * the bandwidth during transfer over tcp/ip - * - * buffer and iso packets need to be stored and be in propeper endian in urb - * before calling this function - */ -void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) -{ - int np = urb->number_of_packets; - int i; - int actualoffset = urb->actual_length; - - if (!usb_pipeisoc(urb->pipe)) - return; - - /* if no packets or length of data is 0, then nothing to unpack */ - if (np == 0 || urb->actual_length == 0) - return; - - /* - * if actual_length is transfer_buffer_length then no padding is - * present. - */ - if (urb->actual_length == urb->transfer_buffer_length) - return; - - /* - * loop over all packets from last to first (to prevent overwritting - * memory when padding) and move them into the proper place - */ - for (i = np-1; i > 0; i--) { - actualoffset -= urb->iso_frame_desc[i].actual_length; - memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, - urb->transfer_buffer + actualoffset, - urb->iso_frame_desc[i].actual_length); - } -} -EXPORT_SYMBOL_GPL(usbip_pad_iso); - -/* some members of urb must be substituted before. */ -int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) -{ - int ret; - int size; - - if (ud->side == USBIP_STUB) { - /* the direction of urb must be OUT. */ - if (usb_pipein(urb->pipe)) - return 0; - - size = urb->transfer_buffer_length; - } else { - /* the direction of urb must be IN. */ - if (usb_pipeout(urb->pipe)) - return 0; - - size = urb->actual_length; - } - - /* no need to recv xbuff */ - if (!(size > 0)) - return 0; - - ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); - if (ret != size) { - dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); - if (ud->side == USBIP_STUB) { - usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); - } else { - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return -EPIPE; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(usbip_recv_xbuff); - -static int __init usbip_core_init(void) -{ - pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); - return 0; -} - -static void __exit usbip_core_exit(void) -{ - return; -} - -module_init(usbip_core_init); -module_exit(usbip_core_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h deleted file mode 100644 index 4da3866a037d..000000000000 --- a/drivers/staging/usbip/usbip_common.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 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. - */ - -#ifndef __USBIP_COMMON_H -#define __USBIP_COMMON_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "uapi/usbip.h" - -#define USBIP_VERSION "1.0.0" - -#undef pr_fmt - -#ifdef DEBUG -#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__ -#else -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#endif - -enum { - usbip_debug_xmit = (1 << 0), - usbip_debug_sysfs = (1 << 1), - usbip_debug_urb = (1 << 2), - usbip_debug_eh = (1 << 3), - - usbip_debug_stub_cmp = (1 << 8), - usbip_debug_stub_dev = (1 << 9), - usbip_debug_stub_rx = (1 << 10), - usbip_debug_stub_tx = (1 << 11), - - usbip_debug_vhci_rh = (1 << 8), - usbip_debug_vhci_hc = (1 << 9), - usbip_debug_vhci_rx = (1 << 10), - usbip_debug_vhci_tx = (1 << 11), - usbip_debug_vhci_sysfs = (1 << 12) -}; - -#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit) -#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh) -#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc) -#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx) -#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx) -#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx) -#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx) -#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs) - -extern unsigned long usbip_debug_flag; -extern struct device_attribute dev_attr_usbip_debug; - -#define usbip_dbg_with_flag(flag, fmt, args...) \ - do { \ - if (flag & usbip_debug_flag) \ - pr_debug(fmt, ##args); \ - } while (0) - -#define usbip_dbg_sysfs(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args) -#define usbip_dbg_xmit(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args) -#define usbip_dbg_urb(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args) -#define usbip_dbg_eh(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args) - -#define usbip_dbg_vhci_rh(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args) -#define usbip_dbg_vhci_hc(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args) -#define usbip_dbg_vhci_rx(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args) -#define usbip_dbg_vhci_tx(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args) -#define usbip_dbg_vhci_sysfs(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args) - -#define usbip_dbg_stub_cmp(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args) -#define usbip_dbg_stub_rx(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args) -#define usbip_dbg_stub_tx(fmt, args...) \ - usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args) - -/* - * USB/IP request headers - * - * Each request is transferred across the network to its counterpart, which - * facilitates the normal USB communication. The values contained in the headers - * are basically the same as in a URB. Currently, four request types are - * defined: - * - * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb() - * (client to server) - * - * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT - * (server to client) - * - * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT, - * corresponds to usb_unlink_urb() - * (client to server) - * - * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK - * (server to client) - * - */ -#define USBIP_CMD_SUBMIT 0x0001 -#define USBIP_CMD_UNLINK 0x0002 -#define USBIP_RET_SUBMIT 0x0003 -#define USBIP_RET_UNLINK 0x0004 - -#define USBIP_DIR_OUT 0x00 -#define USBIP_DIR_IN 0x01 - -/** - * struct usbip_header_basic - data pertinent to every request - * @command: the usbip request type - * @seqnum: sequential number that identifies requests; incremented per - * connection - * @devid: specifies a remote USB device uniquely instead of busnum and devnum; - * in the stub driver, this value is ((busnum << 16) | devnum) - * @direction: direction of the transfer - * @ep: endpoint number - */ -struct usbip_header_basic { - __u32 command; - __u32 seqnum; - __u32 devid; - __u32 direction; - __u32 ep; -} __packed; - -/** - * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header - * @transfer_flags: URB flags - * @transfer_buffer_length: the data size for (in) or (out) transfer - * @start_frame: initial frame for isochronous or interrupt transfers - * @number_of_packets: number of isochronous packets - * @interval: maximum time for the request on the server-side host controller - * @setup: setup data for a control request - */ -struct usbip_header_cmd_submit { - __u32 transfer_flags; - __s32 transfer_buffer_length; - - /* it is difficult for usbip to sync frames (reserved only?) */ - __s32 start_frame; - __s32 number_of_packets; - __s32 interval; - - unsigned char setup[8]; -} __packed; - -/** - * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header - * @status: return status of a non-iso request - * @actual_length: number of bytes transferred - * @start_frame: initial frame for isochronous or interrupt transfers - * @number_of_packets: number of isochronous packets - * @error_count: number of errors for isochronous transfers - */ -struct usbip_header_ret_submit { - __s32 status; - __s32 actual_length; - __s32 start_frame; - __s32 number_of_packets; - __s32 error_count; -} __packed; - -/** - * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header - * @seqnum: the URB seqnum to unlink - */ -struct usbip_header_cmd_unlink { - __u32 seqnum; -} __packed; - -/** - * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header - * @status: return status of the request - */ -struct usbip_header_ret_unlink { - __s32 status; -} __packed; - -/** - * struct usbip_header - common header for all usbip packets - * @base: the basic header - * @u: packet type dependent header - */ -struct usbip_header { - struct usbip_header_basic base; - - union { - struct usbip_header_cmd_submit cmd_submit; - struct usbip_header_ret_submit ret_submit; - struct usbip_header_cmd_unlink cmd_unlink; - struct usbip_header_ret_unlink ret_unlink; - } u; -} __packed; - -/* - * This is the same as usb_iso_packet_descriptor but packed for pdu. - */ -struct usbip_iso_packet_descriptor { - __u32 offset; - __u32 length; /* expected length */ - __u32 actual_length; - __u32 status; -} __packed; - -enum usbip_side { - USBIP_VHCI, - USBIP_STUB, -}; - -/* event handler */ -#define USBIP_EH_SHUTDOWN (1 << 0) -#define USBIP_EH_BYE (1 << 1) -#define USBIP_EH_RESET (1 << 2) -#define USBIP_EH_UNUSABLE (1 << 3) - -#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) -#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) -#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) -#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) -#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) - -#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) -#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) -#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) -#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) - -/* a common structure for stub_device and vhci_device */ -struct usbip_device { - enum usbip_side side; - enum usbip_device_status status; - - /* lock for status */ - spinlock_t lock; - - struct socket *tcp_socket; - - struct task_struct *tcp_rx; - struct task_struct *tcp_tx; - - unsigned long event; - struct task_struct *eh; - wait_queue_head_t eh_waitq; - - struct eh_ops { - void (*shutdown)(struct usbip_device *); - void (*reset)(struct usbip_device *); - void (*unusable)(struct usbip_device *); - } eh_ops; -}; - -#define kthread_get_run(threadfn, data, namefmt, ...) \ -({ \ - struct task_struct *__k \ - = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ - if (!IS_ERR(__k)) { \ - get_task_struct(__k); \ - wake_up_process(__k); \ - } \ - __k; \ -}) - -#define kthread_stop_put(k) \ - do { \ - kthread_stop(k); \ - put_task_struct(k); \ - } while (0) - -/* usbip_common.c */ -void usbip_dump_urb(struct urb *purb); -void usbip_dump_header(struct usbip_header *pdu); - -int usbip_recv(struct socket *sock, void *buf, int size); - -void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, - int pack); -void usbip_header_correct_endian(struct usbip_header *pdu, int send); - -struct usbip_iso_packet_descriptor* -usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); - -/* some members of urb must be substituted before. */ -int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); -void usbip_pad_iso(struct usbip_device *ud, struct urb *urb); -int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); - -/* usbip_event.c */ -int usbip_start_eh(struct usbip_device *ud); -void usbip_stop_eh(struct usbip_device *ud); -void usbip_event_add(struct usbip_device *ud, unsigned long event); -int usbip_event_happened(struct usbip_device *ud); - -static inline int interface_to_busnum(struct usb_interface *interface) -{ - struct usb_device *udev = interface_to_usbdev(interface); - - return udev->bus->busnum; -} - -static inline int interface_to_devnum(struct usb_interface *interface) -{ - struct usb_device *udev = interface_to_usbdev(interface); - - return udev->devnum; -} - -#endif /* __USBIP_COMMON_H */ diff --git a/drivers/staging/usbip/usbip_event.c b/drivers/staging/usbip/usbip_event.c deleted file mode 100644 index 64933b993d7a..000000000000 --- a/drivers/staging/usbip/usbip_event.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include - -#include "usbip_common.h" - -static int event_handler(struct usbip_device *ud) -{ - usbip_dbg_eh("enter\n"); - - /* - * Events are handled by only this thread. - */ - while (usbip_event_happened(ud)) { - usbip_dbg_eh("pending event %lx\n", ud->event); - - /* - * NOTE: shutdown must come first. - * Shutdown the device. - */ - if (ud->event & USBIP_EH_SHUTDOWN) { - ud->eh_ops.shutdown(ud); - ud->event &= ~USBIP_EH_SHUTDOWN; - } - - /* Reset the device. */ - if (ud->event & USBIP_EH_RESET) { - ud->eh_ops.reset(ud); - ud->event &= ~USBIP_EH_RESET; - } - - /* Mark the device as unusable. */ - if (ud->event & USBIP_EH_UNUSABLE) { - ud->eh_ops.unusable(ud); - ud->event &= ~USBIP_EH_UNUSABLE; - } - - /* Stop the error handler. */ - if (ud->event & USBIP_EH_BYE) - return -1; - } - - return 0; -} - -static int event_handler_loop(void *data) -{ - struct usbip_device *ud = data; - - while (!kthread_should_stop()) { - wait_event_interruptible(ud->eh_waitq, - usbip_event_happened(ud) || - kthread_should_stop()); - usbip_dbg_eh("wakeup\n"); - - if (event_handler(ud) < 0) - break; - } - - return 0; -} - -int usbip_start_eh(struct usbip_device *ud) -{ - init_waitqueue_head(&ud->eh_waitq); - ud->event = 0; - - ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); - if (IS_ERR(ud->eh)) { - pr_warn("Unable to start control thread\n"); - return PTR_ERR(ud->eh); - } - - return 0; -} -EXPORT_SYMBOL_GPL(usbip_start_eh); - -void usbip_stop_eh(struct usbip_device *ud) -{ - if (ud->eh == current) - return; /* do not wait for myself */ - - kthread_stop(ud->eh); - usbip_dbg_eh("usbip_eh has finished\n"); -} -EXPORT_SYMBOL_GPL(usbip_stop_eh); - -void usbip_event_add(struct usbip_device *ud, unsigned long event) -{ - unsigned long flags; - - spin_lock_irqsave(&ud->lock, flags); - ud->event |= event; - wake_up(&ud->eh_waitq); - spin_unlock_irqrestore(&ud->lock, flags); -} -EXPORT_SYMBOL_GPL(usbip_event_add); - -int usbip_event_happened(struct usbip_device *ud) -{ - int happened = 0; - - spin_lock(&ud->lock); - if (ud->event != 0) - happened = 1; - spin_unlock(&ud->lock); - - return happened; -} -EXPORT_SYMBOL_GPL(usbip_event_happened); diff --git a/drivers/staging/usbip/usbip_protocol.txt b/drivers/staging/usbip/usbip_protocol.txt deleted file mode 100644 index 16b6fe27284c..000000000000 --- a/drivers/staging/usbip/usbip_protocol.txt +++ /dev/null @@ -1,358 +0,0 @@ -PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! -28 Jun 2011 - -The USB/IP protocol follows a server/client architecture. The server exports the -USB devices and the clients imports them. The device driver for the exported -USB device runs on the client machine. - -The client may ask for the list of the exported USB devices. To get the list the -client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST -packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent -in one or more pieces at the low level transport layer). The server sends back -the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the -TCP/IP connection is closed. - - virtual host controller usb host - "client" "server" - (imports USB devices) (exports USB devices) - | | - | OP_REQ_DEVLIST | - | ----------------------------------------------> | - | | - | OP_REP_DEVLIST | - | <---------------------------------------------- | - | | - -Once the client knows the list of exported USB devices it may decide to use one -of them. First the client opens a TCP/IP connection towards the server and -sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the -import was successful the TCP/IP connection remains open and will be used -to transfer the URB traffic between the client and the server. The client may -send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and -USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the -server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. - - virtual host controller usb host - "client" "server" - (imports USB devices) (exports USB devices) - | | - | OP_REQ_IMPORT | - | ----------------------------------------------> | - | | - | OP_REP_IMPORT | - | <---------------------------------------------- | - | | - | | - | USBIP_CMD_SUBMIT(seqnum = n) | - | ----------------------------------------------> | - | | - | USBIP_RET_SUBMIT(seqnum = n) | - | <---------------------------------------------- | - | . | - | : | - | | - | USBIP_CMD_SUBMIT(seqnum = m) | - | ----------------------------------------------> | - | | - | USBIP_CMD_SUBMIT(seqnum = m+1) | - | ----------------------------------------------> | - | | - | USBIP_CMD_SUBMIT(seqnum = m+2) | - | ----------------------------------------------> | - | | - | USBIP_RET_SUBMIT(seqnum = m) | - | <---------------------------------------------- | - | | - | USBIP_CMD_SUBMIT(seqnum = m+3) | - | ----------------------------------------------> | - | | - | USBIP_RET_SUBMIT(seqnum = m+1) | - | <---------------------------------------------- | - | | - | USBIP_CMD_SUBMIT(seqnum = m+4) | - | ----------------------------------------------> | - | | - | USBIP_RET_SUBMIT(seqnum = m+2) | - | <---------------------------------------------- | - | . | - | : | - | | - | USBIP_CMD_UNLINK | - | ----------------------------------------------> | - | | - | USBIP_RET_UNLINK | - | <---------------------------------------------- | - | | - -The fields are in network (big endian) byte order meaning that the most significant -byte (MSB) is stored at the lowest address. - - -OP_REQ_DEVLIST: Retrieve the list of exported USB devices. - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 ------------+--------+------------+--------------------------------------------------- - 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB - | | | devices. ------------+--------+------------+--------------------------------------------------- - 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 - -OP_REP_DEVLIST: Reply with the list of exported USB devices. - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0. ------------+--------+------------+--------------------------------------------------- - 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. ------------+--------+------------+--------------------------------------------------- - 4 | 4 | 0x00000000 | Status: 0 for OK ------------+--------+------------+--------------------------------------------------- - 8 | 4 | n | Number of exported devices: 0 means no exported - | | | devices. ------------+--------+------------+--------------------------------------------------- - 0x0C | | | From now on the exported n devices are described, - | | | if any. If no devices are exported the message - | | | ends with the previous "number of exported - | | | devices" field. ------------+--------+------------+--------------------------------------------------- - | 256 | | path: Path of the device on the host exporting the - | | | USB device, string closed with zero byte, e.g. - | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" - | | | The unused bytes shall be filled with zero - | | | bytes. ------------+--------+------------+--------------------------------------------------- - 0x10C | 32 | | busid: Bus ID of the exported device, string - | | | closed with zero byte, e.g. "3-2". The unused - | | | bytes shall be filled with zero bytes. ------------+--------+------------+--------------------------------------------------- - 0x12C | 4 | | busnum ------------+--------+------------+--------------------------------------------------- - 0x130 | 4 | | devnum ------------+--------+------------+--------------------------------------------------- - 0x134 | 4 | | speed ------------+--------+------------+--------------------------------------------------- - 0x138 | 2 | | idVendor ------------+--------+------------+--------------------------------------------------- - 0x13A | 2 | | idProduct ------------+--------+------------+--------------------------------------------------- - 0x13C | 2 | | bcdDevice ------------+--------+------------+--------------------------------------------------- - 0x13E | 1 | | bDeviceClass ------------+--------+------------+--------------------------------------------------- - 0x13F | 1 | | bDeviceSubClass ------------+--------+------------+--------------------------------------------------- - 0x140 | 1 | | bDeviceProtocol ------------+--------+------------+--------------------------------------------------- - 0x141 | 1 | | bConfigurationValue ------------+--------+------------+--------------------------------------------------- - 0x142 | 1 | | bNumConfigurations ------------+--------+------------+--------------------------------------------------- - 0x143 | 1 | | bNumInterfaces ------------+--------+------------+--------------------------------------------------- - 0x144 | | m_0 | From now on each interface is described, all - | | | together bNumInterfaces times, with the - | | | the following 4 fields: ------------+--------+------------+--------------------------------------------------- - | 1 | | bInterfaceClass ------------+--------+------------+--------------------------------------------------- - 0x145 | 1 | | bInterfaceSubClass ------------+--------+------------+--------------------------------------------------- - 0x146 | 1 | | bInterfaceProtocol ------------+--------+------------+--------------------------------------------------- - 0x147 | 1 | | padding byte for alignment, shall be set to zero ------------+--------+------------+--------------------------------------------------- - 0xC + | | | The second exported USB device starts at i=1 - i*0x138 + | | | with the busid field. - m_(i-1)*4 | | | - -OP_REQ_IMPORT: Request to import (attach) a remote USB device. - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 ------------+--------+------------+--------------------------------------------------- - 2 | 2 | 0x8003 | Command code: import a remote USB device. ------------+--------+------------+--------------------------------------------------- - 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 ------------+--------+------------+--------------------------------------------------- - 8 | 32 | | busid: the busid of the exported device on the - | | | remote host. The possible values are taken - | | | from the message field OP_REP_DEVLIST.busid. - | | | A string closed with zero, the unused bytes - | | | shall be filled with zeros. ------------+--------+------------+--------------------------------------------------- - -OP_REP_IMPORT: Reply to import (attach) a remote USB device. - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 ------------+--------+------------+--------------------------------------------------- - 2 | 2 | 0x0003 | Reply code: Reply to import. ------------+--------+------------+--------------------------------------------------- - 4 | 4 | 0x00000000 | Status: 0 for OK - | | | 1 for error ------------+--------+------------+--------------------------------------------------- - 8 | | | From now on comes the details of the imported - | | | device, if the previous status field was OK (0), - | | | otherwise the reply ends with the status field. ------------+--------+------------+--------------------------------------------------- - | 256 | | path: Path of the device on the host exporting the - | | | USB device, string closed with zero byte, e.g. - | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" - | | | The unused bytes shall be filled with zero - | | | bytes. ------------+--------+------------+--------------------------------------------------- - 0x108 | 32 | | busid: Bus ID of the exported device, string - | | | closed with zero byte, e.g. "3-2". The unused - | | | bytes shall be filled with zero bytes. ------------+--------+------------+--------------------------------------------------- - 0x128 | 4 | | busnum ------------+--------+------------+--------------------------------------------------- - 0x12C | 4 | | devnum ------------+--------+------------+--------------------------------------------------- - 0x130 | 4 | | speed ------------+--------+------------+--------------------------------------------------- - 0x134 | 2 | | idVendor ------------+--------+------------+--------------------------------------------------- - 0x136 | 2 | | idProduct ------------+--------+------------+--------------------------------------------------- - 0x138 | 2 | | bcdDevice ------------+--------+------------+--------------------------------------------------- - 0x139 | 1 | | bDeviceClass ------------+--------+------------+--------------------------------------------------- - 0x13A | 1 | | bDeviceSubClass ------------+--------+------------+--------------------------------------------------- - 0x13B | 1 | | bDeviceProtocol ------------+--------+------------+--------------------------------------------------- - 0x13C | 1 | | bConfigurationValue ------------+--------+------------+--------------------------------------------------- - 0x13D | 1 | | bNumConfigurations ------------+--------+------------+--------------------------------------------------- - 0x13E | 1 | | bNumInterfaces - -USBIP_CMD_SUBMIT: Submit an URB - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 4 | 0x00000001 | command: Submit an URB ------------+--------+------------+--------------------------------------------------- - 4 | 4 | | seqnum: the sequence number of the URB to submit ------------+--------+------------+--------------------------------------------------- - 8 | 4 | | devid ------------+--------+------------+--------------------------------------------------- - 0xC | 4 | | direction: 0: USBIP_DIR_OUT - | | | 1: USBIP_DIR_IN ------------+--------+------------+--------------------------------------------------- - 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 ------------+--------+------------+--------------------------------------------------- - 0x14 | 4 | | transfer_flags: possible values depend on the - | | | URB transfer type, see below ------------+--------+------------+--------------------------------------------------- - 0x18 | 4 | | transfer_buffer_length ------------+--------+------------+--------------------------------------------------- - 0x1C | 4 | | start_frame: specify the selected frame to - | | | transmit an ISO frame, ignored if URB_ISO_ASAP - | | | is specified at transfer_flags ------------+--------+------------+--------------------------------------------------- - 0x20 | 4 | | number_of_packets: number of ISO packets ------------+--------+------------+--------------------------------------------------- - 0x24 | 4 | | interval: maximum time for the request on the - | | | server-side host controller ------------+--------+------------+--------------------------------------------------- - 0x28 | 8 | | setup: data bytes for USB setup, filled with - | | | zeros if not used ------------+--------+------------+--------------------------------------------------- - 0x30 | | | URB data. For ISO transfers the padding between - | | | each ISO packets is not transmitted. - - - Allowed transfer_flags | value | control | interrupt | bulk | isochronous - -------------------------+------------+---------+-----------+----------+------------- - URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no - URB_ISO_ASAP | 0x00000002 | no | no | no | yes - URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes - URB_NO_FSBR | 0x00000020 | yes | no | no | no - URB_ZERO_PACKET | 0x00000040 | no | no | only out | no - URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes - URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes - URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes - - -USBIP_RET_SUBMIT: Reply for submitting an URB - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 4 | 0x00000003 | command ------------+--------+------------+--------------------------------------------------- - 4 | 4 | | seqnum: URB sequence number ------------+--------+------------+--------------------------------------------------- - 8 | 4 | | devid ------------+--------+------------+--------------------------------------------------- - 0xC | 4 | | direction: 0: USBIP_DIR_OUT - | | | 1: USBIP_DIR_IN ------------+--------+------------+--------------------------------------------------- - 0x10 | 4 | | ep: endpoint number ------------+--------+------------+--------------------------------------------------- - 0x14 | 4 | | status: zero for successful URB transaction, - | | | otherwise some kind of error happened. ------------+--------+------------+--------------------------------------------------- - 0x18 | 4 | n | actual_length: number of URB data bytes ------------+--------+------------+--------------------------------------------------- - 0x1C | 4 | | start_frame: for an ISO frame the actually - | | | selected frame for transmit. ------------+--------+------------+--------------------------------------------------- - 0x20 | 4 | | number_of_packets ------------+--------+------------+--------------------------------------------------- - 0x24 | 4 | | error_count ------------+--------+------------+--------------------------------------------------- - 0x28 | 8 | | setup: data bytes for USB setup, filled with - | | | zeros if not used ------------+--------+------------+--------------------------------------------------- - 0x30 | n | | URB data bytes. For ISO transfers the padding - | | | between each ISO packets is not transmitted. - -USBIP_CMD_UNLINK: Unlink an URB - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 4 | 0x00000002 | command: URB unlink command ------------+--------+------------+--------------------------------------------------- - 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so? ------------+--------+------------+--------------------------------------------------- - 8 | 4 | | devid ------------+--------+------------+--------------------------------------------------- - 0xC | 4 | | direction: 0: USBIP_DIR_OUT - | | | 1: USBIP_DIR_IN ------------+--------+------------+--------------------------------------------------- - 0x10 | 4 | | ep: endpoint number: zero ------------+--------+------------+--------------------------------------------------- - 0x14 | 4 | | seqnum: the URB sequence number given previously - | | | at USBIP_CMD_SUBMIT.seqnum field ------------+--------+------------+--------------------------------------------------- - 0x30 | n | | URB data bytes. For ISO transfers the padding - | | | between each ISO packets is not transmitted. - -USBIP_RET_UNLINK: Reply for URB unlink - - Offset | Length | Value | Description ------------+--------+------------+--------------------------------------------------- - 0 | 4 | 0x00000004 | command: reply for the URB unlink command ------------+--------+------------+--------------------------------------------------- - 4 | 4 | | seqnum: the unlinked URB sequence number ------------+--------+------------+--------------------------------------------------- - 8 | 4 | | devid ------------+--------+------------+--------------------------------------------------- - 0xC | 4 | | direction: 0: USBIP_DIR_OUT - | | | 1: USBIP_DIR_IN ------------+--------+------------+--------------------------------------------------- - 0x10 | 4 | | ep: endpoint number ------------+--------+------------+--------------------------------------------------- - 0x14 | 4 | | status: This is the value contained in the - | | | urb->status in the URB completition handler. - | | | FIXME: a better explanation needed. ------------+--------+------------+--------------------------------------------------- - 0x30 | n | | URB data bytes. For ISO transfers the padding - | | | between each ISO packets is not transmitted. diff --git a/drivers/staging/usbip/vhci.h b/drivers/staging/usbip/vhci.h deleted file mode 100644 index a863a98a91ce..000000000000 --- a/drivers/staging/usbip/vhci.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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. - * - */ - -#ifndef __USBIP_VHCI_H -#define __USBIP_VHCI_H - -#include -#include -#include -#include -#include -#include -#include -#include - -struct vhci_device { - struct usb_device *udev; - - /* - * devid specifies a remote usb device uniquely instead - * of combination of busnum and devnum. - */ - __u32 devid; - - /* speed of a remote device */ - enum usb_device_speed speed; - - /* vhci root-hub port to which this device is attached */ - __u32 rhport; - - struct usbip_device ud; - - /* lock for the below link lists */ - spinlock_t priv_lock; - - /* vhci_priv is linked to one of them. */ - struct list_head priv_tx; - struct list_head priv_rx; - - /* vhci_unlink is linked to one of them */ - struct list_head unlink_tx; - struct list_head unlink_rx; - - /* vhci_tx thread sleeps for this queue */ - wait_queue_head_t waitq_tx; -}; - -/* urb->hcpriv, use container_of() */ -struct vhci_priv { - unsigned long seqnum; - struct list_head list; - - struct vhci_device *vdev; - struct urb *urb; -}; - -struct vhci_unlink { - /* seqnum of this request */ - unsigned long seqnum; - - struct list_head list; - - /* seqnum of the unlink target */ - unsigned long unlink_seqnum; -}; - -/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */ -#define VHCI_NPORTS 8 - -/* for usb_bus.hcpriv */ -struct vhci_hcd { - spinlock_t lock; - - u32 port_status[VHCI_NPORTS]; - - unsigned resuming:1; - unsigned long re_timeout; - - atomic_t seqnum; - - /* - * NOTE: - * wIndex shows the port number and begins from 1. - * But, the index of this array begins from 0. - */ - struct vhci_device vdev[VHCI_NPORTS]; -}; - -extern struct vhci_hcd *the_controller; -extern const struct attribute_group dev_attr_group; - -/* vhci_hcd.c */ -void rh_port_connect(int rhport, enum usb_device_speed speed); - -/* vhci_rx.c */ -struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum); -int vhci_rx_loop(void *data); - -/* vhci_tx.c */ -int vhci_tx_loop(void *data); - -static inline struct vhci_device *port_to_vdev(__u32 port) -{ - return &the_controller->vdev[port]; -} - -static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd) -{ - return (struct vhci_hcd *) (hcd->hcd_priv); -} - -static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci) -{ - return container_of((void *) vhci, struct usb_hcd, hcd_priv); -} - -static inline struct device *vhci_dev(struct vhci_hcd *vhci) -{ - return vhci_to_hcd(vhci)->self.controller; -} - -#endif /* __USBIP_VHCI_H */ diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c deleted file mode 100644 index c02374b6049c..000000000000 --- a/drivers/staging/usbip/vhci_hcd.c +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "usbip_common.h" -#include "vhci.h" - -#define DRIVER_AUTHOR "Takahiro Hirofuchi" -#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" - -/* - * TODO - * - update root hub emulation - * - move the emulation code to userland ? - * porting to other operating systems - * minimize kernel code - * - add suspend/resume code - * - clean up everything - */ - -/* See usb gadget dummy hcd */ - -static int vhci_hub_status(struct usb_hcd *hcd, char *buff); -static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, - u16 wIndex, char *buff, u16 wLength); -static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, - gfp_t mem_flags); -static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); -static int vhci_start(struct usb_hcd *vhci_hcd); -static void vhci_stop(struct usb_hcd *hcd); -static int vhci_get_frame_number(struct usb_hcd *hcd); - -static const char driver_name[] = "vhci_hcd"; -static const char driver_desc[] = "USB/IP Virtual Host Controller"; - -struct vhci_hcd *the_controller; - -static const char * const bit_desc[] = { - "CONNECTION", /*0*/ - "ENABLE", /*1*/ - "SUSPEND", /*2*/ - "OVER_CURRENT", /*3*/ - "RESET", /*4*/ - "R5", /*5*/ - "R6", /*6*/ - "R7", /*7*/ - "POWER", /*8*/ - "LOWSPEED", /*9*/ - "HIGHSPEED", /*10*/ - "PORT_TEST", /*11*/ - "INDICATOR", /*12*/ - "R13", /*13*/ - "R14", /*14*/ - "R15", /*15*/ - "C_CONNECTION", /*16*/ - "C_ENABLE", /*17*/ - "C_SUSPEND", /*18*/ - "C_OVER_CURRENT", /*19*/ - "C_RESET", /*20*/ - "R21", /*21*/ - "R22", /*22*/ - "R23", /*23*/ - "R24", /*24*/ - "R25", /*25*/ - "R26", /*26*/ - "R27", /*27*/ - "R28", /*28*/ - "R29", /*29*/ - "R30", /*30*/ - "R31", /*31*/ -}; - -static void dump_port_status_diff(u32 prev_status, u32 new_status) -{ - int i = 0; - u32 bit = 1; - - pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); - while (bit) { - u32 prev = prev_status & bit; - u32 new = new_status & bit; - char change; - - if (!prev && new) - change = '+'; - else if (prev && !new) - change = '-'; - else - change = ' '; - - if (prev || new) - pr_debug(" %c%s\n", change, bit_desc[i]); - bit <<= 1; - i++; - } - pr_debug("\n"); -} - -void rh_port_connect(int rhport, enum usb_device_speed speed) -{ - usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); - - spin_lock(&the_controller->lock); - - the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION - | (1 << USB_PORT_FEAT_C_CONNECTION); - - switch (speed) { - case USB_SPEED_HIGH: - the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; - break; - case USB_SPEED_LOW: - the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; - break; - default: - break; - } - - spin_unlock(&the_controller->lock); - - usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); -} - -static void rh_port_disconnect(int rhport) -{ - usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); - - spin_lock(&the_controller->lock); - - the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; - the_controller->port_status[rhport] |= - (1 << USB_PORT_FEAT_C_CONNECTION); - - spin_unlock(&the_controller->lock); - usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); -} - -#define PORT_C_MASK \ - ((USB_PORT_STAT_C_CONNECTION \ - | USB_PORT_STAT_C_ENABLE \ - | USB_PORT_STAT_C_SUSPEND \ - | USB_PORT_STAT_C_OVERCURRENT \ - | USB_PORT_STAT_C_RESET) << 16) - -/* - * Returns 0 if the status hasn't changed, or the number of bytes in buf. - * Ports are 0-indexed from the HCD point of view, - * and 1-indexed from the USB core pointer of view. - * - * @buf: a bitmap to show which port status has been changed. - * bit 0: reserved - * bit 1: the status of port 0 has been changed. - * bit 2: the status of port 1 has been changed. - * ... - */ -static int vhci_hub_status(struct usb_hcd *hcd, char *buf) -{ - struct vhci_hcd *vhci; - int retval; - int rhport; - int changed = 0; - - retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); - memset(buf, 0, retval); - - vhci = hcd_to_vhci(hcd); - - spin_lock(&vhci->lock); - if (!HCD_HW_ACCESSIBLE(hcd)) { - usbip_dbg_vhci_rh("hw accessible flag not on?\n"); - goto done; - } - - /* check pseudo status register for each port */ - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { - if ((vhci->port_status[rhport] & PORT_C_MASK)) { - /* The status of a port has been changed, */ - usbip_dbg_vhci_rh("port %d status changed\n", rhport); - - buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; - changed = 1; - } - } - - if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) - usb_hcd_resume_root_hub(hcd); - -done: - spin_unlock(&vhci->lock); - return changed ? retval : 0; -} - -static inline void hub_descriptor(struct usb_hub_descriptor *desc) -{ - memset(desc, 0, sizeof(*desc)); - desc->bDescriptorType = 0x29; - desc->bDescLength = 9; - desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001)); - desc->bNbrPorts = VHCI_NPORTS; - desc->u.hs.DeviceRemovable[0] = 0xff; - desc->u.hs.DeviceRemovable[1] = 0xff; -} - -static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, - u16 wIndex, char *buf, u16 wLength) -{ - struct vhci_hcd *dum; - int retval = 0; - int rhport; - - u32 prev_port_status[VHCI_NPORTS]; - - if (!HCD_HW_ACCESSIBLE(hcd)) - return -ETIMEDOUT; - - /* - * NOTE: - * wIndex shows the port number and begins from 1. - */ - usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, - wIndex); - if (wIndex > VHCI_NPORTS) - pr_err("invalid port number %d\n", wIndex); - rhport = ((__u8)(wIndex & 0x00ff)) - 1; - - dum = hcd_to_vhci(hcd); - - spin_lock(&dum->lock); - - /* store old status and compare now and old later */ - if (usbip_dbg_flag_vhci_rh) { - memcpy(prev_port_status, dum->port_status, - sizeof(prev_port_status)); - } - - switch (typeReq) { - case ClearHubFeature: - usbip_dbg_vhci_rh(" ClearHubFeature\n"); - break; - case ClearPortFeature: - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) { - /* 20msec signaling */ - dum->resuming = 1; - dum->re_timeout = - jiffies + msecs_to_jiffies(20); - } - break; - case USB_PORT_FEAT_POWER: - usbip_dbg_vhci_rh( - " ClearPortFeature: USB_PORT_FEAT_POWER\n"); - dum->port_status[rhport] = 0; - dum->resuming = 0; - break; - case USB_PORT_FEAT_C_RESET: - usbip_dbg_vhci_rh( - " ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); - switch (dum->vdev[rhport].speed) { - case USB_SPEED_HIGH: - dum->port_status[rhport] |= - USB_PORT_STAT_HIGH_SPEED; - break; - case USB_SPEED_LOW: - dum->port_status[rhport] |= - USB_PORT_STAT_LOW_SPEED; - break; - default: - break; - } - default: - usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", - wValue); - dum->port_status[rhport] &= ~(1 << wValue); - break; - } - break; - case GetHubDescriptor: - usbip_dbg_vhci_rh(" GetHubDescriptor\n"); - hub_descriptor((struct usb_hub_descriptor *) buf); - break; - case GetHubStatus: - usbip_dbg_vhci_rh(" GetHubStatus\n"); - *(__le32 *) buf = cpu_to_le32(0); - break; - case GetPortStatus: - usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); - if (wIndex > VHCI_NPORTS || wIndex < 1) { - pr_err("invalid port number %d\n", wIndex); - retval = -EPIPE; - } - - /* we do not care about resume. */ - - /* whoever resets or resumes must GetPortStatus to - * complete it!! - */ - if (dum->resuming && time_after(jiffies, dum->re_timeout)) { - dum->port_status[rhport] |= - (1 << USB_PORT_FEAT_C_SUSPEND); - dum->port_status[rhport] &= - ~(1 << USB_PORT_FEAT_SUSPEND); - dum->resuming = 0; - dum->re_timeout = 0; - } - - if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != - 0 && time_after(jiffies, dum->re_timeout)) { - dum->port_status[rhport] |= - (1 << USB_PORT_FEAT_C_RESET); - dum->port_status[rhport] &= - ~(1 << USB_PORT_FEAT_RESET); - dum->re_timeout = 0; - - if (dum->vdev[rhport].ud.status == - VDEV_ST_NOTASSIGNED) { - usbip_dbg_vhci_rh( - " enable rhport %d (status %u)\n", - rhport, - dum->vdev[rhport].ud.status); - dum->port_status[rhport] |= - USB_PORT_STAT_ENABLE; - } - } - ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]); - ((__le16 *) buf)[1] = - cpu_to_le16(dum->port_status[rhport] >> 16); - - usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], - ((u16 *)buf)[1]); - break; - case SetHubFeature: - usbip_dbg_vhci_rh(" SetHubFeature\n"); - retval = -EPIPE; - break; - case SetPortFeature: - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - usbip_dbg_vhci_rh( - " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); - break; - case USB_PORT_FEAT_RESET: - usbip_dbg_vhci_rh( - " SetPortFeature: USB_PORT_FEAT_RESET\n"); - /* if it's already running, disconnect first */ - if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { - dum->port_status[rhport] &= - ~(USB_PORT_STAT_ENABLE | - USB_PORT_STAT_LOW_SPEED | - USB_PORT_STAT_HIGH_SPEED); - /* FIXME test that code path! */ - } - /* 50msec reset signaling */ - dum->re_timeout = jiffies + msecs_to_jiffies(50); - - /* FALLTHROUGH */ - default: - usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", - wValue); - dum->port_status[rhport] |= (1 << wValue); - break; - } - break; - - default: - pr_err("default: no such request\n"); - - /* "protocol stall" on error */ - retval = -EPIPE; - } - - if (usbip_dbg_flag_vhci_rh) { - pr_debug("port %d\n", rhport); - /* Only dump valid port status */ - if (rhport >= 0) { - dump_port_status_diff(prev_port_status[rhport], - dum->port_status[rhport]); - } - } - usbip_dbg_vhci_rh(" bye\n"); - - spin_unlock(&dum->lock); - - return retval; -} - -static struct vhci_device *get_vdev(struct usb_device *udev) -{ - int i; - - if (!udev) - return NULL; - - for (i = 0; i < VHCI_NPORTS; i++) - if (the_controller->vdev[i].udev == udev) - return port_to_vdev(i); - - return NULL; -} - -static void vhci_tx_urb(struct urb *urb) -{ - struct vhci_device *vdev = get_vdev(urb->dev); - struct vhci_priv *priv; - - if (!vdev) { - pr_err("could not get virtual device"); - return; - } - - priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); - if (!priv) { - usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); - return; - } - - spin_lock(&vdev->priv_lock); - - priv->seqnum = atomic_inc_return(&the_controller->seqnum); - if (priv->seqnum == 0xffff) - dev_info(&urb->dev->dev, "seqnum max\n"); - - priv->vdev = vdev; - priv->urb = urb; - - urb->hcpriv = (void *) priv; - - list_add_tail(&priv->list, &vdev->priv_tx); - - wake_up(&vdev->waitq_tx); - spin_unlock(&vdev->priv_lock); -} - -static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, - gfp_t mem_flags) -{ - struct device *dev = &urb->dev->dev; - int ret = 0; - struct vhci_device *vdev; - - usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", - hcd, urb, mem_flags); - - /* patch to usb_sg_init() is in 2.5.60 */ - BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); - - spin_lock(&the_controller->lock); - - if (urb->status != -EINPROGRESS) { - dev_err(dev, "URB already unlinked!, status %d\n", urb->status); - spin_unlock(&the_controller->lock); - return urb->status; - } - - vdev = port_to_vdev(urb->dev->portnum-1); - - /* refuse enqueue for dead connection */ - spin_lock(&vdev->ud.lock); - if (vdev->ud.status == VDEV_ST_NULL || - vdev->ud.status == VDEV_ST_ERROR) { - dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); - spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); - return -ENODEV; - } - spin_unlock(&vdev->ud.lock); - - ret = usb_hcd_link_urb_to_ep(hcd, urb); - if (ret) - goto no_need_unlink; - - /* - * The enumeration process is as follows; - * - * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) - * to get max packet length of default pipe - * - * 2. Set_Address request to DevAddr(0) EndPoint(0) - * - */ - if (usb_pipedevice(urb->pipe) == 0) { - __u8 type = usb_pipetype(urb->pipe); - struct usb_ctrlrequest *ctrlreq = - (struct usb_ctrlrequest *) urb->setup_packet; - - if (type != PIPE_CONTROL || !ctrlreq) { - dev_err(dev, "invalid request to devnum 0\n"); - ret = -EINVAL; - goto no_need_xmit; - } - - switch (ctrlreq->bRequest) { - case USB_REQ_SET_ADDRESS: - /* set_address may come when a device is reset */ - dev_info(dev, "SetAddress Request (%d) to port %d\n", - ctrlreq->wValue, vdev->rhport); - - if (vdev->udev) - usb_put_dev(vdev->udev); - vdev->udev = usb_get_dev(urb->dev); - - spin_lock(&vdev->ud.lock); - vdev->ud.status = VDEV_ST_USED; - spin_unlock(&vdev->ud.lock); - - if (urb->status == -EINPROGRESS) { - /* This request is successfully completed. */ - /* If not -EINPROGRESS, possibly unlinked. */ - urb->status = 0; - } - - goto no_need_xmit; - - case USB_REQ_GET_DESCRIPTOR: - if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8)) - usbip_dbg_vhci_hc( - "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); - - if (vdev->udev) - usb_put_dev(vdev->udev); - vdev->udev = usb_get_dev(urb->dev); - goto out; - - default: - /* NOT REACHED */ - dev_err(dev, - "invalid request to devnum 0 bRequest %u, wValue %u\n", - ctrlreq->bRequest, - ctrlreq->wValue); - ret = -EINVAL; - goto no_need_xmit; - } - - } - -out: - vhci_tx_urb(urb); - spin_unlock(&the_controller->lock); - - return 0; - -no_need_xmit: - usb_hcd_unlink_urb_from_ep(hcd, urb); -no_need_unlink: - spin_unlock(&the_controller->lock); - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - return ret; -} - -/* - * vhci_rx gives back the urb after receiving the reply of the urb. If an - * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives - * back its urb. For the driver unlinking the urb, the content of the urb is - * not important, but the calling to its completion handler is important; the - * completion of unlinking is notified by the completion handler. - * - * - * CLIENT SIDE - * - * - When vhci_hcd receives RET_SUBMIT, - * - * - case 1a). the urb of the pdu is not unlinking. - * - normal case - * => just give back the urb - * - * - case 1b). the urb of the pdu is unlinking. - * - usbip.ko will return a reply of the unlinking request. - * => give back the urb now and go to case 2b). - * - * - When vhci_hcd receives RET_UNLINK, - * - * - case 2a). a submit request is still pending in vhci_hcd. - * - urb was really pending in usbip.ko and urb_unlink_urb() was - * completed there. - * => free a pending submit request - * => notify unlink completeness by giving back the urb - * - * - case 2b). a submit request is *not* pending in vhci_hcd. - * - urb was already given back to the core driver. - * => do not give back the urb - * - * - * SERVER SIDE - * - * - When usbip receives CMD_UNLINK, - * - * - case 3a). the urb of the unlink request is now in submission. - * => do usb_unlink_urb(). - * => after the unlink is completed, send RET_UNLINK. - * - * - case 3b). the urb of the unlink request is not in submission. - * - may be already completed or never be received - * => send RET_UNLINK - * - */ -static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct vhci_priv *priv; - struct vhci_device *vdev; - - pr_info("dequeue a urb %p\n", urb); - - spin_lock(&the_controller->lock); - - priv = urb->hcpriv; - if (!priv) { - /* URB was never linked! or will be soon given back by - * vhci_rx. */ - spin_unlock(&the_controller->lock); - return 0; - } - - { - int ret = 0; - - ret = usb_hcd_check_unlink_urb(hcd, urb, status); - if (ret) { - spin_unlock(&the_controller->lock); - return ret; - } - } - - /* send unlink request here? */ - vdev = priv->vdev; - - if (!vdev->ud.tcp_socket) { - /* tcp connection is closed */ - spin_lock(&vdev->priv_lock); - - pr_info("device %p seems to be disconnected\n", vdev); - list_del(&priv->list); - kfree(priv); - urb->hcpriv = NULL; - - spin_unlock(&vdev->priv_lock); - - /* - * If tcp connection is alive, we have sent CMD_UNLINK. - * vhci_rx will receive RET_UNLINK and give back the URB. - * Otherwise, we give back it here. - */ - pr_info("gives back urb %p\n", urb); - - usb_hcd_unlink_urb_from_ep(hcd, urb); - - spin_unlock(&the_controller->lock); - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, - urb->status); - spin_lock(&the_controller->lock); - - } else { - /* tcp connection is alive */ - struct vhci_unlink *unlink; - - spin_lock(&vdev->priv_lock); - - /* setup CMD_UNLINK pdu */ - unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); - if (!unlink) { - spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); - usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); - return -ENOMEM; - } - - unlink->seqnum = atomic_inc_return(&the_controller->seqnum); - if (unlink->seqnum == 0xffff) - pr_info("seqnum max\n"); - - unlink->unlink_seqnum = priv->seqnum; - - pr_info("device %p seems to be still connected\n", vdev); - - /* send cmd_unlink and try to cancel the pending URB in the - * peer */ - list_add_tail(&unlink->list, &vdev->unlink_tx); - wake_up(&vdev->waitq_tx); - - spin_unlock(&vdev->priv_lock); - } - - spin_unlock(&the_controller->lock); - - usbip_dbg_vhci_hc("leave\n"); - return 0; -} - -static void vhci_device_unlink_cleanup(struct vhci_device *vdev) -{ - struct vhci_unlink *unlink, *tmp; - - spin_lock(&the_controller->lock); - spin_lock(&vdev->priv_lock); - - list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { - pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); - list_del(&unlink->list); - kfree(unlink); - } - - while (!list_empty(&vdev->unlink_rx)) { - struct urb *urb; - - unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, - list); - - /* give back URB of unanswered unlink request */ - pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); - - urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); - if (!urb) { - pr_info("the urb (seqnum %lu) was already given back\n", - unlink->unlink_seqnum); - list_del(&unlink->list); - kfree(unlink); - continue; - } - - urb->status = -ENODEV; - - usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - - list_del(&unlink->list); - - spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); - - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, - urb->status); - - spin_lock(&the_controller->lock); - spin_lock(&vdev->priv_lock); - - kfree(unlink); - } - - spin_unlock(&vdev->priv_lock); - spin_unlock(&the_controller->lock); -} - -/* - * The important thing is that only one context begins cleanup. - * This is why error handling and cleanup become simple. - * We do not want to consider race condition as possible. - */ -static void vhci_shutdown_connection(struct usbip_device *ud) -{ - struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); - - /* need this? see stub_dev.c */ - if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); - kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); - } - - /* kill threads related to this sdev */ - if (vdev->ud.tcp_rx) { - kthread_stop_put(vdev->ud.tcp_rx); - vdev->ud.tcp_rx = NULL; - } - if (vdev->ud.tcp_tx) { - kthread_stop_put(vdev->ud.tcp_tx); - vdev->ud.tcp_tx = NULL; - } - pr_info("stop threads\n"); - - /* active connection is closed */ - if (vdev->ud.tcp_socket) { - sockfd_put(vdev->ud.tcp_socket); - vdev->ud.tcp_socket = NULL; - } - pr_info("release socket\n"); - - vhci_device_unlink_cleanup(vdev); - - /* - * rh_port_disconnect() is a trigger of ... - * usb_disable_device(): - * disable all the endpoints for a USB device. - * usb_disable_endpoint(): - * disable endpoints. pending urbs are unlinked(dequeued). - * - * NOTE: After calling rh_port_disconnect(), the USB device drivers of a - * detached device should release used urbs in a cleanup function (i.e. - * xxx_disconnect()). Therefore, vhci_hcd does not need to release - * pushed urbs and their private data in this function. - * - * NOTE: vhci_dequeue() must be considered carefully. When shutting down - * a connection, vhci_shutdown_connection() expects vhci_dequeue() - * gives back pushed urbs and frees their private data by request of - * the cleanup function of a USB driver. When unlinking a urb with an - * active connection, vhci_dequeue() does not give back the urb which - * is actually given back by vhci_rx after receiving its return pdu. - * - */ - rh_port_disconnect(vdev->rhport); - - pr_info("disconnect device\n"); -} - - -static void vhci_device_reset(struct usbip_device *ud) -{ - struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); - - spin_lock(&ud->lock); - - vdev->speed = 0; - vdev->devid = 0; - - if (vdev->udev) - usb_put_dev(vdev->udev); - vdev->udev = NULL; - - if (ud->tcp_socket) { - sockfd_put(ud->tcp_socket); - ud->tcp_socket = NULL; - } - ud->status = VDEV_ST_NULL; - - spin_unlock(&ud->lock); -} - -static void vhci_device_unusable(struct usbip_device *ud) -{ - spin_lock(&ud->lock); - ud->status = VDEV_ST_ERROR; - spin_unlock(&ud->lock); -} - -static void vhci_device_init(struct vhci_device *vdev) -{ - memset(vdev, 0, sizeof(*vdev)); - - vdev->ud.side = USBIP_VHCI; - vdev->ud.status = VDEV_ST_NULL; - spin_lock_init(&vdev->ud.lock); - - INIT_LIST_HEAD(&vdev->priv_rx); - INIT_LIST_HEAD(&vdev->priv_tx); - INIT_LIST_HEAD(&vdev->unlink_tx); - INIT_LIST_HEAD(&vdev->unlink_rx); - spin_lock_init(&vdev->priv_lock); - - init_waitqueue_head(&vdev->waitq_tx); - - vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; - vdev->ud.eh_ops.reset = vhci_device_reset; - vdev->ud.eh_ops.unusable = vhci_device_unusable; - - usbip_start_eh(&vdev->ud); -} - -static int vhci_start(struct usb_hcd *hcd) -{ - struct vhci_hcd *vhci = hcd_to_vhci(hcd); - int rhport; - int err = 0; - - usbip_dbg_vhci_hc("enter vhci_start\n"); - - /* initialize private data of usb_hcd */ - - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { - struct vhci_device *vdev = &vhci->vdev[rhport]; - - vhci_device_init(vdev); - vdev->rhport = rhport; - } - - atomic_set(&vhci->seqnum, 0); - spin_lock_init(&vhci->lock); - - hcd->power_budget = 0; /* no limit */ - hcd->uses_new_polling = 1; - - /* vhci_hcd is now ready to be controlled through sysfs */ - err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group); - if (err) { - pr_err("create sysfs files\n"); - return err; - } - - return 0; -} - -static void vhci_stop(struct usb_hcd *hcd) -{ - struct vhci_hcd *vhci = hcd_to_vhci(hcd); - int rhport = 0; - - usbip_dbg_vhci_hc("stop VHCI controller\n"); - - /* 1. remove the userland interface of vhci_hcd */ - sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group); - - /* 2. shutdown all the ports of vhci_hcd */ - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { - struct vhci_device *vdev = &vhci->vdev[rhport]; - - usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); - usbip_stop_eh(&vdev->ud); - } -} - -static int vhci_get_frame_number(struct usb_hcd *hcd) -{ - pr_err("Not yet implemented\n"); - return 0; -} - -#ifdef CONFIG_PM - -/* FIXME: suspend/resume */ -static int vhci_bus_suspend(struct usb_hcd *hcd) -{ - struct vhci_hcd *vhci = hcd_to_vhci(hcd); - - dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - - spin_lock(&vhci->lock); - hcd->state = HC_STATE_SUSPENDED; - spin_unlock(&vhci->lock); - - return 0; -} - -static int vhci_bus_resume(struct usb_hcd *hcd) -{ - struct vhci_hcd *vhci = hcd_to_vhci(hcd); - int rc = 0; - - dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); - - spin_lock(&vhci->lock); - if (!HCD_HW_ACCESSIBLE(hcd)) - rc = -ESHUTDOWN; - else - hcd->state = HC_STATE_RUNNING; - spin_unlock(&vhci->lock); - - return rc; -} - -#else - -#define vhci_bus_suspend NULL -#define vhci_bus_resume NULL -#endif - -static struct hc_driver vhci_hc_driver = { - .description = driver_name, - .product_desc = driver_desc, - .hcd_priv_size = sizeof(struct vhci_hcd), - - .flags = HCD_USB2, - - .start = vhci_start, - .stop = vhci_stop, - - .urb_enqueue = vhci_urb_enqueue, - .urb_dequeue = vhci_urb_dequeue, - - .get_frame_number = vhci_get_frame_number, - - .hub_status_data = vhci_hub_status, - .hub_control = vhci_hub_control, - .bus_suspend = vhci_bus_suspend, - .bus_resume = vhci_bus_resume, -}; - -static int vhci_hcd_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - int ret; - - usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); - - /* - * Allocate and initialize hcd. - * Our private data is also allocated automatically. - */ - hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) { - pr_err("create hcd failed\n"); - return -ENOMEM; - } - hcd->has_tt = 1; - - /* this is private data for vhci_hcd */ - the_controller = hcd_to_vhci(hcd); - - /* - * Finish generic HCD structure initialization and register. - * Call the driver's reset() and start() routines. - */ - ret = usb_add_hcd(hcd, 0, 0); - if (ret != 0) { - pr_err("usb_add_hcd failed %d\n", ret); - usb_put_hcd(hcd); - the_controller = NULL; - return ret; - } - - usbip_dbg_vhci_hc("bye\n"); - return 0; -} - -static int vhci_hcd_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - - hcd = platform_get_drvdata(pdev); - if (!hcd) - return 0; - - /* - * Disconnects the root hub, - * then reverses the effects of usb_add_hcd(), - * invoking the HCD's stop() methods. - */ - usb_remove_hcd(hcd); - usb_put_hcd(hcd); - the_controller = NULL; - - return 0; -} - -#ifdef CONFIG_PM - -/* what should happen for USB/IP under suspend/resume? */ -static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct usb_hcd *hcd; - int rhport = 0; - int connected = 0; - int ret = 0; - - hcd = platform_get_drvdata(pdev); - - spin_lock(&the_controller->lock); - - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) - if (the_controller->port_status[rhport] & - USB_PORT_STAT_CONNECTION) - connected += 1; - - spin_unlock(&the_controller->lock); - - if (connected > 0) { - dev_info(&pdev->dev, - "We have %d active connection%s. Do not suspend.\n", - connected, (connected == 1 ? "" : "s")); - ret = -EBUSY; - } else { - dev_info(&pdev->dev, "suspend vhci_hcd"); - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - } - - return ret; -} - -static int vhci_hcd_resume(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - - dev_dbg(&pdev->dev, "%s\n", __func__); - - hcd = platform_get_drvdata(pdev); - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - usb_hcd_poll_rh_status(hcd); - - return 0; -} - -#else - -#define vhci_hcd_suspend NULL -#define vhci_hcd_resume NULL - -#endif - -static struct platform_driver vhci_driver = { - .probe = vhci_hcd_probe, - .remove = vhci_hcd_remove, - .suspend = vhci_hcd_suspend, - .resume = vhci_hcd_resume, - .driver = { - .name = driver_name, - .owner = THIS_MODULE, - }, -}; - -/* - * The VHCI 'device' is 'virtual'; not a real plug&play hardware. - * We need to add this virtual device as a platform device arbitrarily: - * 1. platform_device_register() - */ -static void the_pdev_release(struct device *dev) -{ -} - -static struct platform_device the_pdev = { - /* should be the same name as driver_name */ - .name = driver_name, - .id = -1, - .dev = { - .release = the_pdev_release, - }, -}; - -static int __init vhci_hcd_init(void) -{ - int ret; - - if (usb_disabled()) - return -ENODEV; - - ret = platform_driver_register(&vhci_driver); - if (ret) - goto err_driver_register; - - ret = platform_device_register(&the_pdev); - if (ret) - goto err_platform_device_register; - - pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); - return ret; - -err_platform_device_register: - platform_driver_unregister(&vhci_driver); -err_driver_register: - return ret; -} - -static void __exit vhci_hcd_exit(void) -{ - platform_device_unregister(&the_pdev); - platform_driver_unregister(&vhci_driver); -} - -module_init(vhci_hcd_init); -module_exit(vhci_hcd_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c deleted file mode 100644 index 00e4a54308e4..000000000000 --- a/drivers/staging/usbip/vhci_rx.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include - -#include "usbip_common.h" -#include "vhci.h" - -/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ -struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) -{ - struct vhci_priv *priv, *tmp; - struct urb *urb = NULL; - int status; - - list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { - if (priv->seqnum != seqnum) - continue; - - urb = priv->urb; - status = urb->status; - - usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", - urb, priv, seqnum); - - switch (status) { - case -ENOENT: - /* fall through */ - case -ECONNRESET: - dev_info(&urb->dev->dev, - "urb %p was unlinked %ssynchronuously.\n", urb, - status == -ENOENT ? "" : "a"); - break; - case -EINPROGRESS: - /* no info output */ - break; - default: - dev_info(&urb->dev->dev, - "urb %p may be in a error, status %d\n", urb, - status); - } - - list_del(&priv->list); - kfree(priv); - urb->hcpriv = NULL; - - break; - } - - return urb; -} - -static void vhci_recv_ret_submit(struct vhci_device *vdev, - struct usbip_header *pdu) -{ - struct usbip_device *ud = &vdev->ud; - struct urb *urb; - - spin_lock(&vdev->priv_lock); - urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); - spin_unlock(&vdev->priv_lock); - - if (!urb) { - pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); - pr_info("max seqnum %d\n", - atomic_read(&the_controller->seqnum)); - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return; - } - - /* unpack the pdu to a urb */ - usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); - - /* recv transfer buffer */ - if (usbip_recv_xbuff(ud, urb) < 0) - return; - - /* recv iso_packet_descriptor */ - if (usbip_recv_iso(ud, urb) < 0) - return; - - /* restore the padding in iso packets */ - usbip_pad_iso(ud, urb); - - if (usbip_dbg_flag_vhci_rx) - usbip_dump_urb(urb); - - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); - - spin_lock(&the_controller->lock); - usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); - - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); - - usbip_dbg_vhci_rx("Leave\n"); -} - -static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, - struct usbip_header *pdu) -{ - struct vhci_unlink *unlink, *tmp; - - spin_lock(&vdev->priv_lock); - - list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { - pr_info("unlink->seqnum %lu\n", unlink->seqnum); - if (unlink->seqnum == pdu->base.seqnum) { - usbip_dbg_vhci_rx("found pending unlink, %lu\n", - unlink->seqnum); - list_del(&unlink->list); - - spin_unlock(&vdev->priv_lock); - return unlink; - } - } - - spin_unlock(&vdev->priv_lock); - - return NULL; -} - -static void vhci_recv_ret_unlink(struct vhci_device *vdev, - struct usbip_header *pdu) -{ - struct vhci_unlink *unlink; - struct urb *urb; - - usbip_dump_header(pdu); - - unlink = dequeue_pending_unlink(vdev, pdu); - if (!unlink) { - pr_info("cannot find the pending unlink %u\n", - pdu->base.seqnum); - return; - } - - spin_lock(&vdev->priv_lock); - urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); - spin_unlock(&vdev->priv_lock); - - if (!urb) { - /* - * I get the result of a unlink request. But, it seems that I - * already received the result of its submit result and gave - * back the URB. - */ - pr_info("the urb (seqnum %d) was already given back\n", - pdu->base.seqnum); - } else { - usbip_dbg_vhci_rx("now giveback urb %p\n", urb); - - /* If unlink is successful, status is -ECONNRESET */ - urb->status = pdu->u.ret_unlink.status; - pr_info("urb->status %d\n", urb->status); - - spin_lock(&the_controller->lock); - usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); - spin_unlock(&the_controller->lock); - - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, - urb->status); - } - - kfree(unlink); -} - -static int vhci_priv_tx_empty(struct vhci_device *vdev) -{ - int empty = 0; - - spin_lock(&vdev->priv_lock); - empty = list_empty(&vdev->priv_rx); - spin_unlock(&vdev->priv_lock); - - return empty; -} - -/* recv a pdu */ -static void vhci_rx_pdu(struct usbip_device *ud) -{ - int ret; - struct usbip_header pdu; - struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); - - usbip_dbg_vhci_rx("Enter\n"); - - memset(&pdu, 0, sizeof(pdu)); - - /* receive a pdu header */ - ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); - if (ret < 0) { - if (ret == -ECONNRESET) - pr_info("connection reset by peer\n"); - else if (ret == -EAGAIN) { - /* ignore if connection was idle */ - if (vhci_priv_tx_empty(vdev)) - return; - pr_info("connection timed out with pending urbs\n"); - } else if (ret != -ERESTARTSYS) - pr_info("xmit failed %d\n", ret); - - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return; - } - if (ret == 0) { - pr_info("connection closed"); - usbip_event_add(ud, VDEV_EVENT_DOWN); - return; - } - if (ret != sizeof(pdu)) { - pr_err("received pdu size is %d, should be %d\n", ret, - (unsigned int)sizeof(pdu)); - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - return; - } - - usbip_header_correct_endian(&pdu, 0); - - if (usbip_dbg_flag_vhci_rx) - usbip_dump_header(&pdu); - - switch (pdu.base.command) { - case USBIP_RET_SUBMIT: - vhci_recv_ret_submit(vdev, &pdu); - break; - case USBIP_RET_UNLINK: - vhci_recv_ret_unlink(vdev, &pdu); - break; - default: - /* NOT REACHED */ - pr_err("unknown pdu %u\n", pdu.base.command); - usbip_dump_header(&pdu); - usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); - break; - } -} - -int vhci_rx_loop(void *data) -{ - struct usbip_device *ud = data; - - while (!kthread_should_stop()) { - if (usbip_event_happened(ud)) - break; - - vhci_rx_pdu(ud); - } - - return 0; -} diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c deleted file mode 100644 index 211f43f67ea2..000000000000 --- a/drivers/staging/usbip/vhci_sysfs.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include - -#include "usbip_common.h" -#include "vhci.h" - -/* TODO: refine locking ?*/ - -/* Sysfs entry to show port status */ -static ssize_t status_show(struct device *dev, struct device_attribute *attr, - char *out) -{ - char *s = out; - int i = 0; - - BUG_ON(!the_controller || !out); - - spin_lock(&the_controller->lock); - - /* - * output example: - * prt sta spd dev socket local_busid - * 000 004 000 000 c5a7bb80 1-2.3 - * 001 004 000 000 d8cee980 2-3.4 - * - * IP address can be retrieved from a socket pointer address by looking - * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a - * port number and its peer IP address. - */ - out += sprintf(out, - "prt sta spd bus dev socket local_busid\n"); - - for (i = 0; i < VHCI_NPORTS; i++) { - struct vhci_device *vdev = port_to_vdev(i); - - spin_lock(&vdev->ud.lock); - out += sprintf(out, "%03u %03u ", i, vdev->ud.status); - - if (vdev->ud.status == VDEV_ST_USED) { - out += sprintf(out, "%03u %08x ", - vdev->speed, vdev->devid); - out += sprintf(out, "%16p ", vdev->ud.tcp_socket); - out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); - - } else { - out += sprintf(out, "000 000 000 0000000000000000 0-0"); - } - - out += sprintf(out, "\n"); - spin_unlock(&vdev->ud.lock); - } - - spin_unlock(&the_controller->lock); - - return out - s; -} -static DEVICE_ATTR_RO(status); - -/* Sysfs entry to shutdown a virtual connection */ -static int vhci_port_disconnect(__u32 rhport) -{ - struct vhci_device *vdev; - - usbip_dbg_vhci_sysfs("enter\n"); - - /* lock */ - spin_lock(&the_controller->lock); - - vdev = port_to_vdev(rhport); - - spin_lock(&vdev->ud.lock); - if (vdev->ud.status == VDEV_ST_NULL) { - pr_err("not connected %d\n", vdev->ud.status); - - /* unlock */ - spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); - - return -EINVAL; - } - - /* unlock */ - spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); - - usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); - - return 0; -} - -static ssize_t store_detach(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int err; - __u32 rhport = 0; - - if (sscanf(buf, "%u", &rhport) != 1) - return -EINVAL; - - /* check rhport */ - if (rhport >= VHCI_NPORTS) { - dev_err(dev, "invalid port %u\n", rhport); - return -EINVAL; - } - - err = vhci_port_disconnect(rhport); - if (err < 0) - return -EINVAL; - - usbip_dbg_vhci_sysfs("Leave\n"); - - return count; -} -static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); - -/* Sysfs entry to establish a virtual connection */ -static int valid_args(__u32 rhport, enum usb_device_speed speed) -{ - /* check rhport */ - if (rhport >= VHCI_NPORTS) { - pr_err("port %u\n", rhport); - return -EINVAL; - } - - /* check speed */ - switch (speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - case USB_SPEED_WIRELESS: - break; - default: - pr_err("Failed attach request for unsupported USB speed: %s\n", - usb_speed_string(speed)); - return -EINVAL; - } - - return 0; -} - -/* - * To start a new USB/IP attachment, a userland program needs to setup a TCP - * connection and then write its socket descriptor with remote device - * information into this sysfs file. - * - * A remote device is virtually attached to the root-hub port of @rhport with - * @speed. @devid is embedded into a request to specify the remote device in a - * server host. - * - * write() returns 0 on success, else negative errno. - */ -static ssize_t store_attach(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct vhci_device *vdev; - struct socket *socket; - int sockfd = 0; - __u32 rhport = 0, devid = 0, speed = 0; - int err; - - /* - * @rhport: port number of vhci_hcd - * @sockfd: socket descriptor of an established TCP connection - * @devid: unique device identifier in a remote host - * @speed: usb device speed in a remote host - */ - if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) - return -EINVAL; - - usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", - rhport, sockfd, devid, speed); - - /* check received parameters */ - if (valid_args(rhport, speed) < 0) - return -EINVAL; - - /* Extract socket from fd. */ - socket = sockfd_lookup(sockfd, &err); - if (!socket) - return -EINVAL; - - /* now need lock until setting vdev status as used */ - - /* begin a lock */ - spin_lock(&the_controller->lock); - vdev = port_to_vdev(rhport); - spin_lock(&vdev->ud.lock); - - if (vdev->ud.status != VDEV_ST_NULL) { - /* end of the lock */ - spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); - - sockfd_put(socket); - - dev_err(dev, "port %d already used\n", rhport); - return -EINVAL; - } - - dev_info(dev, - "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", - rhport, sockfd, devid, speed, usb_speed_string(speed)); - - vdev->devid = devid; - vdev->speed = speed; - vdev->ud.tcp_socket = socket; - vdev->ud.status = VDEV_ST_NOTASSIGNED; - - spin_unlock(&vdev->ud.lock); - spin_unlock(&the_controller->lock); - /* end the lock */ - - vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); - vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); - - rh_port_connect(rhport, speed); - - return count; -} -static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); - -static struct attribute *dev_attrs[] = { - &dev_attr_status.attr, - &dev_attr_detach.attr, - &dev_attr_attach.attr, - &dev_attr_usbip_debug.attr, - NULL, -}; - -const struct attribute_group dev_attr_group = { - .attrs = dev_attrs, -}; diff --git a/drivers/staging/usbip/vhci_tx.c b/drivers/staging/usbip/vhci_tx.c deleted file mode 100644 index 409fd99f3257..000000000000 --- a/drivers/staging/usbip/vhci_tx.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2003-2008 Takahiro Hirofuchi - * - * This 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 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include - -#include "usbip_common.h" -#include "vhci.h" - -static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) -{ - struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); - struct vhci_device *vdev = priv->vdev; - - usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", - usb_pipedevice(urb->pipe), vdev->devid); - - pdup->base.command = USBIP_CMD_SUBMIT; - pdup->base.seqnum = priv->seqnum; - pdup->base.devid = vdev->devid; - pdup->base.direction = usb_pipein(urb->pipe) ? - USBIP_DIR_IN : USBIP_DIR_OUT; - pdup->base.ep = usb_pipeendpoint(urb->pipe); - - usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); - - if (urb->setup_packet) - memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); -} - -static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) -{ - struct vhci_priv *priv, *tmp; - - spin_lock(&vdev->priv_lock); - - list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { - list_move_tail(&priv->list, &vdev->priv_rx); - spin_unlock(&vdev->priv_lock); - return priv; - } - - spin_unlock(&vdev->priv_lock); - - return NULL; -} - -static int vhci_send_cmd_submit(struct vhci_device *vdev) -{ - struct vhci_priv *priv = NULL; - - struct msghdr msg; - struct kvec iov[3]; - size_t txsize; - - size_t total_size = 0; - - while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { - int ret; - struct urb *urb = priv->urb; - struct usbip_header pdu_header; - struct usbip_iso_packet_descriptor *iso_buffer = NULL; - - txsize = 0; - memset(&pdu_header, 0, sizeof(pdu_header)); - memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - - usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); - - /* 1. setup usbip_header */ - setup_cmd_submit_pdu(&pdu_header, urb); - usbip_header_correct_endian(&pdu_header, 1); - - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); - txsize += sizeof(pdu_header); - - /* 2. setup transfer buffer */ - if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { - iov[1].iov_base = urb->transfer_buffer; - iov[1].iov_len = urb->transfer_buffer_length; - txsize += urb->transfer_buffer_length; - } - - /* 3. setup iso_packet_descriptor */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - ssize_t len = 0; - - iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); - if (!iso_buffer) { - usbip_event_add(&vdev->ud, - SDEV_EVENT_ERROR_MALLOC); - return -1; - } - - iov[2].iov_base = iso_buffer; - iov[2].iov_len = len; - txsize += len; - } - - ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); - if (ret != txsize) { - pr_err("sendmsg failed!, ret=%d for %zd\n", ret, - txsize); - kfree(iso_buffer); - usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); - return -1; - } - - kfree(iso_buffer); - usbip_dbg_vhci_tx("send txdata\n"); - - total_size += txsize; - } - - return total_size; -} - -static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) -{ - struct vhci_unlink *unlink, *tmp; - - spin_lock(&vdev->priv_lock); - - list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { - list_move_tail(&unlink->list, &vdev->unlink_rx); - spin_unlock(&vdev->priv_lock); - return unlink; - } - - spin_unlock(&vdev->priv_lock); - - return NULL; -} - -static int vhci_send_cmd_unlink(struct vhci_device *vdev) -{ - struct vhci_unlink *unlink = NULL; - - struct msghdr msg; - struct kvec iov[3]; - size_t txsize; - - size_t total_size = 0; - - while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { - int ret; - struct usbip_header pdu_header; - - txsize = 0; - memset(&pdu_header, 0, sizeof(pdu_header)); - memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - - usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); - - /* 1. setup usbip_header */ - pdu_header.base.command = USBIP_CMD_UNLINK; - pdu_header.base.seqnum = unlink->seqnum; - pdu_header.base.devid = vdev->devid; - pdu_header.base.ep = 0; - pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; - - usbip_header_correct_endian(&pdu_header, 1); - - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); - txsize += sizeof(pdu_header); - - ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); - if (ret != txsize) { - pr_err("sendmsg failed!, ret=%d for %zd\n", ret, - txsize); - usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); - return -1; - } - - usbip_dbg_vhci_tx("send txdata\n"); - - total_size += txsize; - } - - return total_size; -} - -int vhci_tx_loop(void *data) -{ - struct usbip_device *ud = data; - struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); - - while (!kthread_should_stop()) { - if (vhci_send_cmd_submit(vdev) < 0) - break; - - if (vhci_send_cmd_unlink(vdev) < 0) - break; - - wait_event_interruptible(vdev->waitq_tx, - (!list_empty(&vdev->priv_tx) || - !list_empty(&vdev->unlink_tx) || - kthread_should_stop())); - - usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); - } - - return 0; -} diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e0cad4418085..cf1b19bca306 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -92,6 +92,8 @@ source "drivers/usb/storage/Kconfig" source "drivers/usb/image/Kconfig" +source "drivers/usb/usbip/Kconfig" + endif source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 3cba892b83a2..d7be71778059 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -60,3 +60,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_COMMON) += common/ + +obj-$(CONFIG_USBIP_CORE) += usbip/ diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig new file mode 100644 index 000000000000..bd99e9e47e50 --- /dev/null +++ b/drivers/usb/usbip/Kconfig @@ -0,0 +1,41 @@ +config USBIP_CORE + tristate "USB/IP support" + depends on USB && NET + ---help--- + This enables pushing USB packets over IP to allow remote + machines direct access to USB devices. It provides the + USB/IP core that is required by both drivers. + + For more details, and to get the userspace utility + programs, please see . + + To compile this as a module, choose M here: the module will + be called usbip-core. + + If unsure, say N. + +config USBIP_VHCI_HCD + tristate "VHCI hcd" + depends on USBIP_CORE + ---help--- + This enables the USB/IP virtual host controller driver, + which is run on the remote machine. + + To compile this driver as a module, choose M here: the + module will be called vhci-hcd. + +config USBIP_HOST + tristate "Host driver" + depends on USBIP_CORE + ---help--- + This enables the USB/IP host driver, which is run on the + machine that is sharing the USB devices. + + To compile this driver as a module, choose M here: the + module will be called usbip-host. + +config USBIP_DEBUG + bool "Debug messages for USB/IP" + depends on USBIP_CORE + ---help--- + This enables the debug messages from the USB/IP drivers. diff --git a/drivers/usb/usbip/Makefile b/drivers/usb/usbip/Makefile new file mode 100644 index 000000000000..9ecd61545be1 --- /dev/null +++ b/drivers/usb/usbip/Makefile @@ -0,0 +1,10 @@ +ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG + +obj-$(CONFIG_USBIP_CORE) += usbip-core.o +usbip-core-y := usbip_common.o usbip_event.o + +obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o +vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o + +obj-$(CONFIG_USBIP_HOST) += usbip-host.o +usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o diff --git a/drivers/usb/usbip/README b/drivers/usb/usbip/README new file mode 100644 index 000000000000..41a2cf2e77a6 --- /dev/null +++ b/drivers/usb/usbip/README @@ -0,0 +1,7 @@ +TODO: + - more discussion about the protocol + - testing + - review of the userspace interface + - document the protocol + +Please send patches for this code to Greg Kroah-Hartman diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h new file mode 100644 index 000000000000..266e2b0ce9a8 --- /dev/null +++ b/drivers/usb/usbip/stub.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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. + */ + +#ifndef __USBIP_STUB_H +#define __USBIP_STUB_H + +#include +#include +#include +#include +#include +#include + +#define STUB_BUSID_OTHER 0 +#define STUB_BUSID_REMOV 1 +#define STUB_BUSID_ADDED 2 +#define STUB_BUSID_ALLOC 3 + +struct stub_device { + struct usb_interface *interface; + struct usb_device *udev; + + struct usbip_device ud; + __u32 devid; + + /* + * stub_priv preserves private data of each urb. + * It is allocated as stub_priv_cache and assigned to urb->context. + * + * stub_priv is always linked to any one of 3 lists; + * priv_init: linked to this until the comletion of a urb. + * priv_tx : linked to this after the completion of a urb. + * priv_free: linked to this after the sending of the result. + * + * Any of these list operations should be locked by priv_lock. + */ + spinlock_t priv_lock; + struct list_head priv_init; + struct list_head priv_tx; + struct list_head priv_free; + + /* see comments for unlinking in stub_rx.c */ + struct list_head unlink_tx; + struct list_head unlink_free; + + wait_queue_head_t tx_waitq; +}; + +/* private data into urb->priv */ +struct stub_priv { + unsigned long seqnum; + struct list_head list; + struct stub_device *sdev; + struct urb *urb; + + int unlinking; +}; + +struct stub_unlink { + unsigned long seqnum; + struct list_head list; + __u32 status; +}; + +/* same as SYSFS_BUS_ID_SIZE */ +#define BUSID_SIZE 32 + +struct bus_id_priv { + char name[BUSID_SIZE]; + char status; + int interf_count; + struct stub_device *sdev; + struct usb_device *udev; + char shutdown_busid; +}; + +/* stub_priv is allocated from stub_priv_cache */ +extern struct kmem_cache *stub_priv_cache; + +/* stub_dev.c */ +extern struct usb_device_driver stub_driver; + +/* stub_main.c */ +struct bus_id_priv *get_busid_priv(const char *busid); +int del_match_busid(char *busid); +void stub_device_cleanup_urbs(struct stub_device *sdev); + +/* stub_rx.c */ +int stub_rx_loop(void *data); + +/* stub_tx.c */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status); +void stub_complete(struct urb *urb); +int stub_tx_loop(void *data); + +#endif /* __USBIP_STUB_H */ diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c new file mode 100644 index 000000000000..51d0c7188738 --- /dev/null +++ b/drivers/usb/usbip/stub_dev.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include + +#include "usbip_common.h" +#include "stub.h" + +/* + * Define device IDs here if you want to explicitly limit exportable devices. + * In most cases, wildcard matching will be okay because driver binding can be + * changed dynamically by a userland program. + */ +static struct usb_device_id stub_table[] = { +#if 0 + /* just an example */ + { USB_DEVICE(0x05ac, 0x0301) }, /* Mac 1 button mouse */ + { USB_DEVICE(0x0430, 0x0009) }, /* Plat Home Keyboard */ + { USB_DEVICE(0x059b, 0x0001) }, /* Iomega USB Zip 100 */ + { USB_DEVICE(0x04b3, 0x4427) }, /* IBM USB CD-ROM */ + { USB_DEVICE(0x05a9, 0xa511) }, /* LifeView USB cam */ + { USB_DEVICE(0x55aa, 0x0201) }, /* Imation card reader */ + { USB_DEVICE(0x046d, 0x0870) }, /* Qcam Express(QV-30) */ + { USB_DEVICE(0x04bb, 0x0101) }, /* IO-DATA HD 120GB */ + { USB_DEVICE(0x04bb, 0x0904) }, /* IO-DATA USB-ET/TX */ + { USB_DEVICE(0x04bb, 0x0201) }, /* IO-DATA USB-ET/TX */ + { USB_DEVICE(0x08bb, 0x2702) }, /* ONKYO USB Speaker */ + { USB_DEVICE(0x046d, 0x08b2) }, /* Logicool Qcam 4000 Pro */ +#endif + /* magic for wild card */ + { .driver_info = 1 }, + { 0, } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, stub_table); + +/* + * usbip_status shows the status of usbip-host as long as this driver is bound + * to the target device. + */ +static ssize_t usbip_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int status; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + spin_lock_irq(&sdev->ud.lock); + status = sdev->ud.status; + spin_unlock_irq(&sdev->ud.lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} +static DEVICE_ATTR_RO(usbip_status); + +/* + * usbip_sockfd gets a socket descriptor of an established TCP connection that + * is used to transfer usbip requests by kernel threads. -1 is a magic number + * by which usbip connection is finished. + */ +static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int sockfd = 0; + struct socket *socket; + int rv; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + rv = sscanf(buf, "%d", &sockfd); + if (rv != 1) + return -EINVAL; + + if (sockfd != -1) { + int err; + + dev_info(dev, "stub up\n"); + + spin_lock_irq(&sdev->ud.lock); + + if (sdev->ud.status != SDEV_ST_AVAILABLE) { + dev_err(dev, "not ready\n"); + goto err; + } + + socket = sockfd_lookup(sockfd, &err); + if (!socket) + goto err; + + sdev->ud.tcp_socket = socket; + + spin_unlock_irq(&sdev->ud.lock); + + sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, + "stub_rx"); + sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, + "stub_tx"); + + spin_lock_irq(&sdev->ud.lock); + sdev->ud.status = SDEV_ST_USED; + spin_unlock_irq(&sdev->ud.lock); + + } else { + dev_info(dev, "stub down\n"); + + spin_lock_irq(&sdev->ud.lock); + if (sdev->ud.status != SDEV_ST_USED) + goto err; + + spin_unlock_irq(&sdev->ud.lock); + + usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); + } + + return count; + +err: + spin_unlock_irq(&sdev->ud.lock); + return -EINVAL; +} +static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); + +static int stub_add_files(struct device *dev) +{ + int err = 0; + + err = device_create_file(dev, &dev_attr_usbip_status); + if (err) + goto err_status; + + err = device_create_file(dev, &dev_attr_usbip_sockfd); + if (err) + goto err_sockfd; + + err = device_create_file(dev, &dev_attr_usbip_debug); + if (err) + goto err_debug; + + return 0; + +err_debug: + device_remove_file(dev, &dev_attr_usbip_sockfd); +err_sockfd: + device_remove_file(dev, &dev_attr_usbip_status); +err_status: + return err; +} + +static void stub_remove_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_usbip_status); + device_remove_file(dev, &dev_attr_usbip_sockfd); + device_remove_file(dev, &dev_attr_usbip_debug); +} + +static void stub_shutdown_connection(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + /* + * When removing an exported device, kernel panic sometimes occurred + * and then EIP was sk_wait_data of stub_rx thread. Is this because + * sk_wait_data returned though stub_rx thread was already finished by + * step 1? + */ + if (ud->tcp_socket) { + dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", + ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* 1. stop threads */ + if (ud->tcp_rx) { + kthread_stop_put(ud->tcp_rx); + ud->tcp_rx = NULL; + } + if (ud->tcp_tx) { + kthread_stop_put(ud->tcp_tx); + ud->tcp_tx = NULL; + } + + /* + * 2. close the socket + * + * tcp_socket is freed after threads are killed so that usbip_xmit does + * not touch NULL socket. + */ + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + + /* 3. free used data */ + stub_device_cleanup_urbs(sdev); + + /* 4. free stub_unlink */ + { + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_del(&unlink->list); + kfree(unlink); + } + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, + list) { + list_del(&unlink->list); + kfree(unlink); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + } +} + +static void stub_device_reset(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct usb_device *udev = sdev->udev; + int ret; + + dev_dbg(&udev->dev, "device reset"); + + ret = usb_lock_device_for_reset(udev, sdev->interface); + if (ret < 0) { + dev_err(&udev->dev, "lock for reset\n"); + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); + return; + } + + /* try to reset the device */ + ret = usb_reset_device(udev); + usb_unlock_device(udev); + + spin_lock_irq(&ud->lock); + if (ret) { + dev_err(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_ERROR; + } else { + dev_info(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_AVAILABLE; + } + spin_unlock_irq(&ud->lock); +} + +static void stub_device_unusable(struct usbip_device *ud) +{ + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); +} + +/** + * stub_device_alloc - allocate a new stub_device struct + * @interface: usb_interface of a new device + * + * Allocates and initializes a new stub_device struct. + */ +static struct stub_device *stub_device_alloc(struct usb_device *udev) +{ + struct stub_device *sdev; + int busnum = udev->bus->busnum; + int devnum = udev->devnum; + + dev_dbg(&udev->dev, "allocating stub device"); + + /* yes, it's a new device */ + sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); + if (!sdev) + return NULL; + + sdev->udev = usb_get_dev(udev); + + /* + * devid is defined with devnum when this driver is first allocated. + * devnum may change later if a device is reset. However, devid never + * changes during a usbip connection. + */ + sdev->devid = (busnum << 16) | devnum; + sdev->ud.side = USBIP_STUB; + sdev->ud.status = SDEV_ST_AVAILABLE; + spin_lock_init(&sdev->ud.lock); + sdev->ud.tcp_socket = NULL; + + INIT_LIST_HEAD(&sdev->priv_init); + INIT_LIST_HEAD(&sdev->priv_tx); + INIT_LIST_HEAD(&sdev->priv_free); + INIT_LIST_HEAD(&sdev->unlink_free); + INIT_LIST_HEAD(&sdev->unlink_tx); + spin_lock_init(&sdev->priv_lock); + + init_waitqueue_head(&sdev->tx_waitq); + + sdev->ud.eh_ops.shutdown = stub_shutdown_connection; + sdev->ud.eh_ops.reset = stub_device_reset; + sdev->ud.eh_ops.unusable = stub_device_unusable; + + usbip_start_eh(&sdev->ud); + + dev_dbg(&udev->dev, "register new device\n"); + + return sdev; +} + +static void stub_device_free(struct stub_device *sdev) +{ + kfree(sdev); +} + +static int stub_probe(struct usb_device *udev) +{ + struct stub_device *sdev = NULL; + const char *udev_busid = dev_name(&udev->dev); + int err = 0; + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + /* check we should claim or not by busid_table */ + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || + (busid_priv->status == STUB_BUSID_OTHER)) { + dev_info(&udev->dev, + "%s is not in match_busid table... skip!\n", + udev_busid); + + /* + * Return value should be ENODEV or ENOXIO to continue trying + * other matched drivers by the driver core. + * See driver_probe_device() in driver/base/dd.c + */ + return -ENODEV; + } + + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { + dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", + udev_busid); + return -ENODEV; + } + + if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { + dev_dbg(&udev->dev, + "%s is attached on vhci_hcd... skip!\n", + udev_busid); + + return -ENODEV; + } + + /* ok, this is my device */ + sdev = stub_device_alloc(udev); + if (!sdev) + return -ENOMEM; + + dev_info(&udev->dev, + "usbip-host: register new device (bus %u dev %u)\n", + udev->bus->busnum, udev->devnum); + + busid_priv->shutdown_busid = 0; + + /* set private data to usb_device */ + dev_set_drvdata(&udev->dev, sdev); + busid_priv->sdev = sdev; + busid_priv->udev = udev; + + /* + * Claim this hub port. + * It doesn't matter what value we pass as owner + * (struct dev_state) as long as it is unique. + */ + rc = usb_hub_claim_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to claim port\n"); + return rc; + } + + err = stub_add_files(&udev->dev); + if (err) { + dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(udev); + kthread_stop_put(sdev->ud.eh); + + busid_priv->sdev = NULL; + stub_device_free(sdev); + return err; + } + busid_priv->status = STUB_BUSID_ALLOC; + + return 0; +} + +static void shutdown_busid(struct bus_id_priv *busid_priv) +{ + if (busid_priv->sdev && !busid_priv->shutdown_busid) { + busid_priv->shutdown_busid = 1; + usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); + + /* wait for the stop of the event handler */ + usbip_stop_eh(&busid_priv->sdev->ud); + } +} + +/* + * called in usb_disconnect() or usb_deregister() + * but only if actconfig(active configuration) exists + */ +static void stub_disconnect(struct usb_device *udev) +{ + struct stub_device *sdev; + const char *udev_busid = dev_name(&udev->dev); + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv) { + BUG(); + return; + } + + sdev = dev_get_drvdata(&udev->dev); + + /* get stub_device */ + if (!sdev) { + dev_err(&udev->dev, "could not get device"); + return; + } + + dev_set_drvdata(&udev->dev, NULL); + + /* + * NOTE: rx/tx threads are invoked for each usb_device. + */ + stub_remove_files(&udev->dev); + + /* release port */ + rc = usb_hub_release_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to release port\n"); + return; + } + + /* If usb reset is called from event handler */ + if (busid_priv->sdev->ud.eh == current) + return; + + /* shutdown the current connection */ + shutdown_busid(busid_priv); + + usb_put_dev(sdev->udev); + + /* free sdev */ + busid_priv->sdev = NULL; + stub_device_free(sdev); + + if (busid_priv->status == STUB_BUSID_ALLOC) { + busid_priv->status = STUB_BUSID_ADDED; + } else { + busid_priv->status = STUB_BUSID_OTHER; + del_match_busid((char *)udev_busid); + } +} + +#ifdef CONFIG_PM + +/* These functions need usb_port_suspend and usb_port_resume, + * which reside in drivers/usb/core/usb.h. Skip for now. */ + +static int stub_suspend(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_suspend\n"); + + return 0; +} + +static int stub_resume(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_resume\n"); + + return 0; +} + +#endif /* CONFIG_PM */ + +struct usb_device_driver stub_driver = { + .name = "usbip-host", + .probe = stub_probe, + .disconnect = stub_disconnect, +#ifdef CONFIG_PM + .suspend = stub_suspend, + .resume = stub_resume, +#endif + .supports_autosuspend = 0, +}; diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c new file mode 100644 index 000000000000..44ab43fc4fcc --- /dev/null +++ b/drivers/usb/usbip/stub_main.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#include "usbip_common.h" +#include "stub.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP Host Driver" + +struct kmem_cache *stub_priv_cache; +/* + * busid_tables defines matching busids that usbip can grab. A user can change + * dynamically what device is locally used and what device is exported to a + * remote host. + */ +#define MAX_BUSID 16 +static struct bus_id_priv busid_table[MAX_BUSID]; +static spinlock_t busid_table_lock; + +static void init_busid_table(void) +{ + /* + * This also sets the bus_table[i].status to + * STUB_BUSID_OTHER, which is 0. + */ + memset(busid_table, 0, sizeof(busid_table)); + + spin_lock_init(&busid_table_lock); +} + +/* + * Find the index of the busid by name. + * Must be called with busid_table_lock held. + */ +static int get_busid_idx(const char *busid) +{ + int i; + int idx = -1; + + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { + idx = i; + break; + } + return idx; +} + +struct bus_id_priv *get_busid_priv(const char *busid) +{ + int idx; + struct bus_id_priv *bid = NULL; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx >= 0) + bid = &(busid_table[idx]); + spin_unlock(&busid_table_lock); + + return bid; +} + +static int add_match_busid(char *busid) +{ + int i; + int ret = -1; + + spin_lock(&busid_table_lock); + /* already registered? */ + if (get_busid_idx(busid) >= 0) { + ret = 0; + goto out; + } + + for (i = 0; i < MAX_BUSID; i++) + if (!busid_table[i].name[0]) { + strlcpy(busid_table[i].name, busid, BUSID_SIZE); + if ((busid_table[i].status != STUB_BUSID_ALLOC) && + (busid_table[i].status != STUB_BUSID_REMOV)) + busid_table[i].status = STUB_BUSID_ADDED; + ret = 0; + break; + } + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +int del_match_busid(char *busid) +{ + int idx; + int ret = -1; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx < 0) + goto out; + + /* found */ + ret = 0; + + if (busid_table[idx].status == STUB_BUSID_OTHER) + memset(busid_table[idx].name, 0, BUSID_SIZE); + + if ((busid_table[idx].status != STUB_BUSID_OTHER) && + (busid_table[idx].status != STUB_BUSID_ADDED)) + busid_table[idx].status = STUB_BUSID_REMOV; + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +static ssize_t show_match_busid(struct device_driver *drv, char *buf) +{ + int i; + char *out = buf; + + spin_lock(&busid_table_lock); + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + out += sprintf(out, "%s ", busid_table[i].name); + spin_unlock(&busid_table_lock); + out += sprintf(out, "\n"); + + return out - buf; +} + +static ssize_t store_match_busid(struct device_driver *dev, const char *buf, + size_t count) +{ + int len; + char busid[BUSID_SIZE]; + + if (count < 5) + return -EINVAL; + + /* busid needs to include \0 termination */ + len = strlcpy(busid, buf + 4, BUSID_SIZE); + if (sizeof(busid) <= len) + return -EINVAL; + + if (!strncmp(buf, "add ", 4)) { + if (add_match_busid(busid) < 0) + return -ENOMEM; + + pr_debug("add busid %s\n", busid); + return count; + } + + if (!strncmp(buf, "del ", 4)) { + if (del_match_busid(busid) < 0) + return -ENODEV; + + pr_debug("del busid %s\n", busid); + return count; + } + + return -EINVAL; +} +static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, + store_match_busid); + +static ssize_t rebind_store(struct device_driver *dev, const char *buf, + size_t count) +{ + int ret; + int len; + struct bus_id_priv *bid; + + /* buf length should be less that BUSID_SIZE */ + len = strnlen(buf, BUSID_SIZE); + + if (!(len < BUSID_SIZE)) + return -EINVAL; + + bid = get_busid_priv(buf); + if (!bid) + return -ENODEV; + + ret = device_attach(&bid->udev->dev); + if (ret < 0) { + dev_err(&bid->udev->dev, "rebind failed\n"); + return ret; + } + + return count; +} + +static DRIVER_ATTR_WO(rebind); + +static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) +{ + struct stub_priv *priv, *tmp; + + list_for_each_entry_safe(priv, tmp, listhead, list) { + list_del(&priv->list); + return priv; + } + + return NULL; +} + +static struct stub_priv *stub_priv_pop(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = stub_priv_pop_from_listhead(&sdev->priv_init); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_tx); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_free); + +done: + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +void stub_device_cleanup_urbs(struct stub_device *sdev) +{ + struct stub_priv *priv; + struct urb *urb; + + dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + + while ((priv = stub_priv_pop(sdev))) { + urb = priv->urb; + dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + usb_kill_urb(urb); + + kmem_cache_free(stub_priv_cache, priv); + + kfree(urb->transfer_buffer); + kfree(urb->setup_packet); + usb_free_urb(urb); + } +} + +static int __init usbip_host_init(void) +{ + int ret; + + init_busid_table(); + + stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + if (!stub_priv_cache) { + pr_err("kmem_cache_create failed\n"); + return -ENOMEM; + } + + ret = usb_register_device_driver(&stub_driver, THIS_MODULE); + if (ret) { + pr_err("usb_register failed %d\n", ret); + goto err_usb_register; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_create_file: + usb_deregister_device_driver(&stub_driver); +err_usb_register: + kmem_cache_destroy(stub_priv_cache); + return ret; +} + +static void __exit usbip_host_exit(void) +{ + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + + /* + * deregister() calls stub_disconnect() for all devices. Device + * specific data is cleared in stub_disconnect(). + */ + usb_deregister_device_driver(&stub_driver); + + kmem_cache_destroy(stub_priv_cache); +} + +module_init(usbip_host_init); +module_exit(usbip_host_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c new file mode 100644 index 000000000000..00e475c51a12 --- /dev/null +++ b/drivers/usb/usbip/stub_rx.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include + +#include "usbip_common.h" +#include "stub.h" + +static int is_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_CLEAR_FEATURE) && + (req->bRequestType == USB_RECIP_ENDPOINT) && + (req->wValue == USB_ENDPOINT_HALT); +} + +static int is_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_INTERFACE) && + (req->bRequestType == USB_RECIP_INTERFACE); +} + +static int is_set_configuration_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_CONFIGURATION) && + (req->bRequestType == USB_RECIP_DEVICE); +} + +static int is_reset_device_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 value; + __u16 index; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + value = le16_to_cpu(req->wValue); + index = le16_to_cpu(req->wIndex); + + if ((req->bRequest == USB_REQ_SET_FEATURE) && + (req->bRequestType == USB_RT_PORT) && + (value == USB_PORT_FEAT_RESET)) { + usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index); + return 1; + } else + return 0; +} + +static int tweak_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + int target_endp; + int target_dir; + int target_pipe; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + /* + * The stalled endpoint is specified in the wIndex value. The endpoint + * of the urb is the target of this clear_halt request (i.e., control + * endpoint). + */ + target_endp = le16_to_cpu(req->wIndex) & 0x000f; + + /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */ + target_dir = le16_to_cpu(req->wIndex) & 0x0080; + + if (target_dir) + target_pipe = usb_rcvctrlpipe(urb->dev, target_endp); + else + target_pipe = usb_sndctrlpipe(urb->dev, target_endp); + + ret = usb_clear_halt(urb->dev, target_pipe); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_clear_halt error: devnum %d endp %d ret %d\n", + urb->dev->devnum, target_endp, ret); + else + dev_info(&urb->dev->dev, + "usb_clear_halt done: devnum %d endp %d\n", + urb->dev->devnum, target_endp); + + return ret; +} + +static int tweak_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 alternate; + __u16 interface; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + alternate = le16_to_cpu(req->wValue); + interface = le16_to_cpu(req->wIndex); + + usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", + interface, alternate); + + ret = usb_set_interface(urb->dev, interface, alternate); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_set_interface error: inf %u alt %u ret %d\n", + interface, alternate, ret); + else + dev_info(&urb->dev->dev, + "usb_set_interface done: inf %u alt %u\n", + interface, alternate); + + return ret; +} + +static int tweak_set_configuration_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + struct usb_ctrlrequest *req; + __u16 config; + int err; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + config = le16_to_cpu(req->wValue); + + err = usb_set_configuration(sdev->udev, config); + if (err && err != -ENODEV) + dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", + config, err); + return 0; +} + +static int tweak_reset_device_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + + dev_info(&urb->dev->dev, "usb_queue_reset_device\n"); + + /* + * With the implementation of pre_reset and post_reset the driver no + * longer unbinds. This allows the use of synchronous reset. + */ + + if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) { + dev_err(&urb->dev->dev, "could not obtain lock to reset device\n"); + return 0; + } + usb_reset_device(sdev->udev); + usb_unlock_device(sdev->udev); + + return 0; +} + +/* + * clear_halt, set_interface, and set_configuration require special tricks. + */ +static void tweak_special_requests(struct urb *urb) +{ + if (!urb || !urb->setup_packet) + return; + + if (usb_pipetype(urb->pipe) != PIPE_CONTROL) + return; + + if (is_clear_halt_cmd(urb)) + /* tweak clear_halt */ + tweak_clear_halt_cmd(urb); + + else if (is_set_interface_cmd(urb)) + /* tweak set_interface */ + tweak_set_interface_cmd(urb); + + else if (is_set_configuration_cmd(urb)) + /* tweak set_configuration */ + tweak_set_configuration_cmd(urb); + + else if (is_reset_device_cmd(urb)) + tweak_reset_device_cmd(urb); + else + usbip_dbg_stub_rx("no need to tweak\n"); +} + +/* + * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb(). + * By unlinking the urb asynchronously, stub_rx can continuously + * process coming urbs. Even if the urb is unlinked, its completion + * handler will be called and stub_tx will send a return pdu. + * + * See also comments about unlinking strategy in vhci_hcd.c. + */ +static int stub_recv_cmd_unlink(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry(priv, &sdev->priv_init, list) { + if (priv->seqnum != pdu->u.cmd_unlink.seqnum) + continue; + + dev_info(&priv->urb->dev->dev, "unlink urb %p\n", + priv->urb); + + /* + * This matched urb is not completed yet (i.e., be in + * flight in usb hcd hardware/driver). Now we are + * cancelling it. The unlinking flag means that we are + * now not going to return the normal result pdu of a + * submission request, but going to return a result pdu + * of the unlink request. + */ + priv->unlinking = 1; + + /* + * In the case that unlinking flag is on, prev->seqnum + * is changed from the seqnum of the cancelling urb to + * the seqnum of the unlink request. This will be used + * to make the result pdu of the unlink request. + */ + priv->seqnum = pdu->base.seqnum; + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* + * usb_unlink_urb() is now out of spinlocking to avoid + * spinlock recursion since stub_complete() is + * sometimes called in this context but not in the + * interrupt context. If stub_complete() is executed + * before we call usb_unlink_urb(), usb_unlink_urb() + * will return an error value. In this case, stub_tx + * will return the result pdu of this unlink request + * though submission is completed and actual unlinking + * is not executed. OK? + */ + /* In the above case, urb->status is not -ECONNRESET, + * so a driver in a client host will know the failure + * of the unlink request ? + */ + ret = usb_unlink_urb(priv->urb); + if (ret != -EINPROGRESS) + dev_err(&priv->urb->dev->dev, + "failed to unlink a urb %p, ret %d\n", + priv->urb, ret); + + return 0; + } + + usbip_dbg_stub_rx("seqnum %d is not pending\n", + pdu->u.cmd_unlink.seqnum); + + /* + * The urb of the unlink target is not found in priv_init queue. It was + * already completed and its results is/was going to be sent by a + * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only + * return the completeness of this unlink request to vhci_hcd. + */ + stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return 0; +} + +static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) +{ + struct usbip_device *ud = &sdev->ud; + int valid = 0; + + if (pdu->base.devid == sdev->devid) { + spin_lock_irq(&ud->lock); + if (ud->status == SDEV_ST_USED) { + /* A request is valid. */ + valid = 1; + } + spin_unlock_irq(&ud->lock); + } + + return valid; +} + +static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, + struct usbip_header *pdu) +{ + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + unsigned long flags; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC); + if (!priv) { + dev_err(&sdev->interface->dev, "alloc stub_priv\n"); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return NULL; + } + + priv->seqnum = pdu->base.seqnum; + priv->sdev = sdev; + + /* + * After a stub_priv is linked to a list_head, + * our error handler can free allocated data. + */ + list_add_tail(&priv->list, &sdev->priv_init); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +static int get_pipe(struct stub_device *sdev, int epnum, int dir) +{ + struct usb_device *udev = sdev->udev; + struct usb_host_endpoint *ep; + struct usb_endpoint_descriptor *epd = NULL; + + if (dir == USBIP_DIR_IN) + ep = udev->ep_in[epnum & 0x7f]; + else + ep = udev->ep_out[epnum & 0x7f]; + if (!ep) { + dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", + epnum); + BUG(); + } + + epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndctrlpipe(udev, epnum); + else + return usb_rcvctrlpipe(udev, epnum); + } + + if (usb_endpoint_xfer_bulk(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndbulkpipe(udev, epnum); + else + return usb_rcvbulkpipe(udev, epnum); + } + + if (usb_endpoint_xfer_int(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndintpipe(udev, epnum); + else + return usb_rcvintpipe(udev, epnum); + } + + if (usb_endpoint_xfer_isoc(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndisocpipe(udev, epnum); + else + return usb_rcvisocpipe(udev, epnum); + } + + /* NOT REACHED */ + dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + return 0; +} + +static void masking_bogus_flags(struct urb *urb) +{ + int xfertype; + struct usb_device *dev; + struct usb_host_endpoint *ep; + int is_out; + unsigned int allowed; + + if (!urb || urb->hcpriv || !urb->complete) + return; + dev = urb->dev; + if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) + return; + + ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) + return; + + xfertype = usb_endpoint_type(&ep->desc); + if (xfertype == USB_ENDPOINT_XFER_CONTROL) { + struct usb_ctrlrequest *setup = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (!setup) + return; + is_out = !(setup->bRequestType & USB_DIR_IN) || + !setup->wLength; + } else { + is_out = usb_endpoint_dir_out(&ep->desc); + } + + /* enforce simple/standard policy */ + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | + URB_DIR_MASK | URB_FREE_BUFFER); + switch (xfertype) { + case USB_ENDPOINT_XFER_BULK: + if (is_out) + allowed |= URB_ZERO_PACKET; + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_CONTROL: + allowed |= URB_NO_FSBR; /* only affects UHCI */ + /* FALLTHROUGH */ + default: /* all non-iso endpoints */ + if (!is_out) + allowed |= URB_SHORT_NOT_OK; + break; + case USB_ENDPOINT_XFER_ISOC: + allowed |= URB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; +} + +static void stub_recv_cmd_submit(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + struct usb_device *udev = sdev->udev; + int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + + priv = stub_priv_alloc(sdev, pdu); + if (!priv) + return; + + /* setup a urb */ + if (usb_pipeisoc(pipe)) + priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, + GFP_KERNEL); + else + priv->urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!priv->urb) { + dev_err(&sdev->interface->dev, "malloc urb\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* allocate urb transfer buffer, if needed */ + if (pdu->u.cmd_submit.transfer_buffer_length > 0) { + priv->urb->transfer_buffer = + kzalloc(pdu->u.cmd_submit.transfer_buffer_length, + GFP_KERNEL); + if (!priv->urb->transfer_buffer) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + } + + /* copy urb setup packet */ + priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, + GFP_KERNEL); + if (!priv->urb->setup_packet) { + dev_err(&sdev->interface->dev, "allocate setup_packet\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* set other members from the base header of pdu */ + priv->urb->context = (void *) priv; + priv->urb->dev = udev; + priv->urb->pipe = pipe; + priv->urb->complete = stub_complete; + + usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); + + + if (usbip_recv_xbuff(ud, priv->urb) < 0) + return; + + if (usbip_recv_iso(ud, priv->urb) < 0) + return; + + /* no need to submit an intercepted request, but harmless? */ + tweak_special_requests(priv->urb); + + masking_bogus_flags(priv->urb); + /* urb is now ready to submit */ + ret = usb_submit_urb(priv->urb, GFP_KERNEL); + + if (ret == 0) + usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", + pdu->base.seqnum); + else { + dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret); + usbip_dump_header(pdu); + usbip_dump_urb(priv->urb); + + /* + * Pessimistic. + * This connection will be discarded. + */ + usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + } + + usbip_dbg_stub_rx("Leave\n"); +} + +/* recv a pdu */ +static void stub_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct device *dev = &sdev->udev->dev; + + usbip_dbg_stub_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret != sizeof(pdu)) { + dev_err(dev, "recv a header, %d\n", ret); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_stub_rx) + usbip_dump_header(&pdu); + + if (!valid_request(sdev, &pdu)) { + dev_err(dev, "recv invalid request\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + switch (pdu.base.command) { + case USBIP_CMD_UNLINK: + stub_recv_cmd_unlink(sdev, &pdu); + break; + + case USBIP_CMD_SUBMIT: + stub_recv_cmd_submit(sdev, &pdu); + break; + + default: + /* NOTREACHED */ + dev_err(dev, "unknown pdu\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + break; + } +} + +int stub_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + stub_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c new file mode 100644 index 000000000000..dbcabc9dbe0d --- /dev/null +++ b/drivers/usb/usbip/stub_tx.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include + +#include "usbip_common.h" +#include "stub.h" + +static void stub_free_priv_and_urb(struct stub_priv *priv) +{ + struct urb *urb = priv->urb; + + kfree(urb->setup_packet); + kfree(urb->transfer_buffer); + list_del(&priv->list); + kmem_cache_free(stub_priv_cache, priv); + usb_free_urb(urb); +} + +/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status) +{ + struct stub_unlink *unlink; + + unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); + if (!unlink) { + usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + unlink->seqnum = seqnum; + unlink->status = status; + + list_add_tail(&unlink->list, &sdev->unlink_tx); +} + +/** + * stub_complete - completion handler of a usbip urb + * @urb: pointer to the urb completed + * + * When a urb has completed, the USB core driver calls this function mostly in + * the interrupt context. To return the result of a urb, the completed urb is + * linked to the pending list of returning. + * + */ +void stub_complete(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + unsigned long flags; + + usbip_dbg_stub_tx("complete! status %d\n", urb->status); + + switch (urb->status) { + case 0: + /* OK */ + break; + case -ENOENT: + dev_info(&urb->dev->dev, + "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); + return; + case -ECONNRESET: + dev_info(&urb->dev->dev, + "unlinked by a call to usb_unlink_urb()\n"); + break; + case -EPIPE: + dev_info(&urb->dev->dev, "endpoint %d is stalled\n", + usb_pipeendpoint(urb->pipe)); + break; + case -ESHUTDOWN: + dev_info(&urb->dev->dev, "device removed?\n"); + break; + default: + dev_info(&urb->dev->dev, + "urb completion with non-zero status %d\n", + urb->status); + break; + } + + /* link a urb to the queue of tx. */ + spin_lock_irqsave(&sdev->priv_lock, flags); + if (priv->unlinking) { + stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); + stub_free_priv_and_urb(priv); + } else { + list_move_tail(&priv->list, &sdev->priv_tx); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* wake up tx_thread */ + wake_up(&sdev->tx_waitq); +} + +static inline void setup_base_pdu(struct usbip_header_basic *base, + __u32 command, __u32 seqnum) +{ + base->command = command; + base->seqnum = seqnum; + base->devid = 0; + base->ep = 0; + base->direction = 0; +} + +static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + + setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum); + usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1); +} + +static void setup_ret_unlink_pdu(struct usbip_header *rpdu, + struct stub_unlink *unlink) +{ + setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum); + rpdu->u.ret_unlink.status = unlink->status; +} + +static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) { + list_move_tail(&priv->list, &sdev->priv_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return priv; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_submit(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + struct msghdr msg; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); + + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; + + /* 1. setup usbip_header */ + setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; + txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = + urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb %d does not match iso packet sizes %zu\n", + urb->actual_length, + txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_TCP); + return -1; + } + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_MALLOC); + kfree(iov); + return -1; + } + + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; + txsize += len; + iovnum++; + } + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + kfree(iov); + kfree(iso_buffer); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iov); + kfree(iso_buffer); + + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { + stub_free_priv_and_urb(priv); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_move_tail(&unlink->list, &sdev->unlink_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return unlink; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_unlink(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + struct msghdr msg; + struct kvec iov[1]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + setup_ret_unlink_pdu(&pdu_header, unlink); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, + 1, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_stub_tx("send txdata\n"); + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) { + list_del(&unlink->list); + kfree(unlink); + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +int stub_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + /* + * send_ret_submit comes earlier than send_ret_unlink. stub_rx + * looks at only priv_init queue. If the completion of a URB is + * earlier than the receive of CMD_UNLINK, priv is moved to + * priv_tx queue and stub_rx does not find the target priv. In + * this case, vhci_rx receives the result of the submit request + * and then receives the result of the unlink request. The + * result of the submit is given back to the usbcore as the + * completion of the unlink request. The request of the + * unlink is ignored. This is ok because a driver who calls + * usb_unlink_urb() understands the unlink was too late by + * getting the status of the given-backed URB which has the + * status of usb_submit_urb(). + */ + if (stub_send_ret_submit(sdev) < 0) + break; + + if (stub_send_ret_unlink(sdev) < 0) + break; + + wait_event_interruptible(sdev->tx_waitq, + (!list_empty(&sdev->priv_tx) || + !list_empty(&sdev->unlink_tx) || + kthread_should_stop())); + } + + return 0; +} diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c new file mode 100644 index 000000000000..facaaf003f19 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbip_common.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi " +#define DRIVER_DESC "USB/IP Core" + +#ifdef CONFIG_USBIP_DEBUG +unsigned long usbip_debug_flag = 0xffffffff; +#else +unsigned long usbip_debug_flag; +#endif +EXPORT_SYMBOL_GPL(usbip_debug_flag); +module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)"); + +/* FIXME */ +struct device_attribute dev_attr_usbip_debug; +EXPORT_SYMBOL_GPL(dev_attr_usbip_debug); + +static ssize_t usbip_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", usbip_debug_flag); +} + +static ssize_t usbip_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + if (sscanf(buf, "%lx", &usbip_debug_flag) != 1) + return -EINVAL; + return count; +} +DEVICE_ATTR_RW(usbip_debug); + +static void usbip_dump_buffer(char *buff, int bufflen) +{ + print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4, + buff, bufflen, false); +} + +static void usbip_dump_pipe(unsigned int p) +{ + unsigned char type = usb_pipetype(p); + unsigned char ep = usb_pipeendpoint(p); + unsigned char dev = usb_pipedevice(p); + unsigned char dir = usb_pipein(p); + + pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT"); + + switch (type) { + case PIPE_ISOCHRONOUS: + pr_debug("ISO\n"); + break; + case PIPE_INTERRUPT: + pr_debug("INT\n"); + break; + case PIPE_CONTROL: + pr_debug("CTRL\n"); + break; + case PIPE_BULK: + pr_debug("BULK\n"); + break; + default: + pr_debug("ERR\n"); + break; + } +} + +static void usbip_dump_usb_device(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + int i; + + dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", + udev->devnum, udev->devpath, usb_speed_string(udev->speed)); + + pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); + + dev_dbg(dev, " "); + for (i = 0; i < 16; i++) + pr_debug(" %2u", i); + pr_debug("\n"); + + dev_dbg(dev, " toggle0(IN) :"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " toggle1(OUT):"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_in :"); + for (i = 0; i < 16; i++) { + if (udev->ep_in[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_out :"); + for (i = 0; i < 16; i++) { + if (udev->ep_out[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); + + dev_dbg(dev, + "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", + &udev->descriptor, udev->config, + udev->actconfig, udev->rawdescriptors); + + dev_dbg(dev, "have_langid %d, string_langid %d\n", + udev->have_langid, udev->string_langid); + + dev_dbg(dev, "maxchild %d\n", udev->maxchild); +} + +static void usbip_dump_request_type(__u8 rt) +{ + switch (rt & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + pr_debug("DEVICE"); + break; + case USB_RECIP_INTERFACE: + pr_debug("INTERF"); + break; + case USB_RECIP_ENDPOINT: + pr_debug("ENDPOI"); + break; + case USB_RECIP_OTHER: + pr_debug("OTHER "); + break; + default: + pr_debug("------"); + break; + } +} + +static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd) +{ + if (!cmd) { + pr_debug(" : null pointer\n"); + return; + } + + pr_debug(" "); + pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ", + cmd->bRequestType, cmd->bRequest, + cmd->wValue, cmd->wIndex, cmd->wLength); + pr_debug("\n "); + + if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + pr_debug("STANDARD "); + switch (cmd->bRequest) { + case USB_REQ_GET_STATUS: + pr_debug("GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + pr_debug("CLEAR_FEAT\n"); + break; + case USB_REQ_SET_FEATURE: + pr_debug("SET_FEAT\n"); + break; + case USB_REQ_SET_ADDRESS: + pr_debug("SET_ADDRRS\n"); + break; + case USB_REQ_GET_DESCRIPTOR: + pr_debug("GET_DESCRI\n"); + break; + case USB_REQ_SET_DESCRIPTOR: + pr_debug("SET_DESCRI\n"); + break; + case USB_REQ_GET_CONFIGURATION: + pr_debug("GET_CONFIG\n"); + break; + case USB_REQ_SET_CONFIGURATION: + pr_debug("SET_CONFIG\n"); + break; + case USB_REQ_GET_INTERFACE: + pr_debug("GET_INTERF\n"); + break; + case USB_REQ_SET_INTERFACE: + pr_debug("SET_INTERF\n"); + break; + case USB_REQ_SYNCH_FRAME: + pr_debug("SYNC_FRAME\n"); + break; + default: + pr_debug("REQ(%02X)\n", cmd->bRequest); + break; + } + usbip_dump_request_type(cmd->bRequestType); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + pr_debug("CLASS\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + pr_debug("VENDOR\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) { + pr_debug("RESERVED\n"); + } +} + +void usbip_dump_urb(struct urb *urb) +{ + struct device *dev; + + if (!urb) { + pr_debug("urb: null pointer!!\n"); + return; + } + + if (!urb->dev) { + pr_debug("urb->dev: null pointer!!\n"); + return; + } + + dev = &urb->dev->dev; + + dev_dbg(dev, " urb :%p\n", urb); + dev_dbg(dev, " dev :%p\n", urb->dev); + + usbip_dump_usb_device(urb->dev); + + dev_dbg(dev, " pipe :%08x ", urb->pipe); + + usbip_dump_pipe(urb->pipe); + + dev_dbg(dev, " status :%d\n", urb->status); + dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); + dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); + dev_dbg(dev, " transfer_buffer_length:%d\n", + urb->transfer_buffer_length); + dev_dbg(dev, " actual_length :%d\n", urb->actual_length); + dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); + + if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) + usbip_dump_usb_ctrlrequest( + (struct usb_ctrlrequest *)urb->setup_packet); + + dev_dbg(dev, " start_frame :%d\n", urb->start_frame); + dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); + dev_dbg(dev, " interval :%d\n", urb->interval); + dev_dbg(dev, " error_count :%d\n", urb->error_count); + dev_dbg(dev, " context :%p\n", urb->context); + dev_dbg(dev, " complete :%p\n", urb->complete); +} +EXPORT_SYMBOL_GPL(usbip_dump_urb); + +void usbip_dump_header(struct usbip_header *pdu) +{ + pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n", + pdu->base.command, + pdu->base.seqnum, + pdu->base.devid, + pdu->base.direction, + pdu->base.ep); + + switch (pdu->base.command) { + case USBIP_CMD_SUBMIT: + pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n", + pdu->u.cmd_submit.transfer_flags, + pdu->u.cmd_submit.transfer_buffer_length, + pdu->u.cmd_submit.start_frame, + pdu->u.cmd_submit.number_of_packets, + pdu->u.cmd_submit.interval); + break; + case USBIP_CMD_UNLINK: + pr_debug("USBIP_CMD_UNLINK: seq %u\n", + pdu->u.cmd_unlink.seqnum); + break; + case USBIP_RET_SUBMIT: + pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", + pdu->u.ret_submit.status, + pdu->u.ret_submit.actual_length, + pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, + pdu->u.ret_submit.error_count); + break; + case USBIP_RET_UNLINK: + pr_debug("USBIP_RET_UNLINK: status %d\n", + pdu->u.ret_unlink.status); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_dump_header); + +/* Receive data over TCP/IP. */ +int usbip_recv(struct socket *sock, void *buf, int size) +{ + int result; + struct msghdr msg; + struct kvec iov; + int total = 0; + + /* for blocks of if (usbip_dbg_flag_xmit) */ + char *bp = buf; + int osize = size; + + usbip_dbg_xmit("enter\n"); + + if (!sock || !buf || !size) { + pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, + size); + return -EINVAL; + } + + do { + sock->sk->sk_allocation = GFP_NOIO; + iov.iov_base = buf; + iov.iov_len = size; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_NOSIGNAL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); + if (result <= 0) { + pr_debug("receive sock %p buf %p size %u ret %d total %d\n", + sock, buf, size, result, total); + goto err; + } + + size -= result; + buf += result; + total += result; + } while (size > 0); + + if (usbip_dbg_flag_xmit) { + if (!in_interrupt()) + pr_debug("%-10s:", current->comm); + else + pr_debug("interrupt :"); + + pr_debug("receiving....\n"); + usbip_dump_buffer(bp, osize); + pr_debug("received, osize %d ret %d size %d total %d\n", + osize, result, size, total); + } + + return total; + +err: + return result; +} +EXPORT_SYMBOL_GPL(usbip_recv); + +/* there may be more cases to tweak the flags. */ +static unsigned int tweak_transfer_flags(unsigned int flags) +{ + flags &= ~URB_NO_TRANSFER_DMA_MAP; + return flags; +} + +static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit; + + /* + * Some members are not still implemented in usbip. I hope this issue + * will be discussed when usbip is ported to other operating systems. + */ + if (pack) { + spdu->transfer_flags = + tweak_transfer_flags(urb->transfer_flags); + spdu->transfer_buffer_length = urb->transfer_buffer_length; + spdu->start_frame = urb->start_frame; + spdu->number_of_packets = urb->number_of_packets; + spdu->interval = urb->interval; + } else { + urb->transfer_flags = spdu->transfer_flags; + urb->transfer_buffer_length = spdu->transfer_buffer_length; + urb->start_frame = spdu->start_frame; + urb->number_of_packets = spdu->number_of_packets; + urb->interval = spdu->interval; + } +} + +static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit; + + if (pack) { + rpdu->status = urb->status; + rpdu->actual_length = urb->actual_length; + rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; + rpdu->error_count = urb->error_count; + } else { + urb->status = rpdu->status; + urb->actual_length = rpdu->actual_length; + urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; + urb->error_count = rpdu->error_count; + } +} + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack) +{ + switch (cmd) { + case USBIP_CMD_SUBMIT: + usbip_pack_cmd_submit(pdu, urb, pack); + break; + case USBIP_RET_SUBMIT: + usbip_pack_ret_submit(pdu, urb, pack); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_pack_pdu); + +static void correct_endian_basic(struct usbip_header_basic *base, int send) +{ + if (send) { + base->command = cpu_to_be32(base->command); + base->seqnum = cpu_to_be32(base->seqnum); + base->devid = cpu_to_be32(base->devid); + base->direction = cpu_to_be32(base->direction); + base->ep = cpu_to_be32(base->ep); + } else { + base->command = be32_to_cpu(base->command); + base->seqnum = be32_to_cpu(base->seqnum); + base->devid = be32_to_cpu(base->devid); + base->direction = be32_to_cpu(base->direction); + base->ep = be32_to_cpu(base->ep); + } +} + +static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu, + int send) +{ + if (send) { + pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags); + + cpu_to_be32s(&pdu->transfer_buffer_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->interval); + } else { + pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags); + + be32_to_cpus(&pdu->transfer_buffer_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->interval); + } +} + +static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, + int send) +{ + if (send) { + cpu_to_be32s(&pdu->status); + cpu_to_be32s(&pdu->actual_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->error_count); + } else { + be32_to_cpus(&pdu->status); + be32_to_cpus(&pdu->actual_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->error_count); + } +} + +static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu, + int send) +{ + if (send) + pdu->seqnum = cpu_to_be32(pdu->seqnum); + else + pdu->seqnum = be32_to_cpu(pdu->seqnum); +} + +static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu, + int send) +{ + if (send) + cpu_to_be32s(&pdu->status); + else + be32_to_cpus(&pdu->status); +} + +void usbip_header_correct_endian(struct usbip_header *pdu, int send) +{ + __u32 cmd = 0; + + if (send) + cmd = pdu->base.command; + + correct_endian_basic(&pdu->base, send); + + if (!send) + cmd = pdu->base.command; + + switch (cmd) { + case USBIP_CMD_SUBMIT: + correct_endian_cmd_submit(&pdu->u.cmd_submit, send); + break; + case USBIP_RET_SUBMIT: + correct_endian_ret_submit(&pdu->u.ret_submit, send); + break; + case USBIP_CMD_UNLINK: + correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send); + break; + case USBIP_RET_UNLINK: + correct_endian_ret_unlink(&pdu->u.ret_unlink, send); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_header_correct_endian); + +static void usbip_iso_packet_correct_endian( + struct usbip_iso_packet_descriptor *iso, int send) +{ + /* does not need all members. but copy all simply. */ + if (send) { + iso->offset = cpu_to_be32(iso->offset); + iso->length = cpu_to_be32(iso->length); + iso->status = cpu_to_be32(iso->status); + iso->actual_length = cpu_to_be32(iso->actual_length); + } else { + iso->offset = be32_to_cpu(iso->offset); + iso->length = be32_to_cpu(iso->length); + iso->status = be32_to_cpu(iso->status); + iso->actual_length = be32_to_cpu(iso->actual_length); + } +} + +static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso, + struct usb_iso_packet_descriptor *uiso, int pack) +{ + if (pack) { + iso->offset = uiso->offset; + iso->length = uiso->length; + iso->status = uiso->status; + iso->actual_length = uiso->actual_length; + } else { + uiso->offset = iso->offset; + uiso->length = iso->length; + uiso->status = iso->status; + uiso->actual_length = iso->actual_length; + } +} + +/* must free buffer */ +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen) +{ + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + ssize_t size = np * sizeof(*iso); + int i; + + iso = kzalloc(size, GFP_KERNEL); + if (!iso) + return NULL; + + for (i = 0; i < np; i++) { + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1); + usbip_iso_packet_correct_endian(&iso[i], 1); + } + + *bufflen = size; + + return iso; +} +EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) +{ + void *buff; + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + int size = np * sizeof(*iso); + int i; + int ret; + int total_length = 0; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* my Bluetooth dongle gets ISO URBs which are np = 0 */ + if (np == 0) + return 0; + + buff = kzalloc(size, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + ret = usbip_recv(ud->tcp_socket, buff, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n", + ret); + kfree(buff); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + iso = (struct usbip_iso_packet_descriptor *) buff; + for (i = 0; i < np; i++) { + usbip_iso_packet_correct_endian(&iso[i], 0); + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; + } + + kfree(buff); + + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets %d not equal to actual length of buffer %d\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_iso); + +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); + +/* some members of urb must be substituted before. */ +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) +{ + int ret; + int size; + + if (ud->side == USBIP_STUB) { + /* the direction of urb must be OUT. */ + if (usb_pipein(urb->pipe)) + return 0; + + size = urb->transfer_buffer_length; + } else { + /* the direction of urb must be IN. */ + if (usb_pipeout(urb->pipe)) + return 0; + + size = urb->actual_length; + } + + /* no need to recv xbuff */ + if (!(size > 0)) + return 0; + + ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); + if (ud->side == USBIP_STUB) { + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + } else { + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return -EPIPE; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_xbuff); + +static int __init usbip_core_init(void) +{ + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return 0; +} + +static void __exit usbip_core_exit(void) +{ + return; +} + +module_init(usbip_core_init); +module_exit(usbip_core_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h new file mode 100644 index 000000000000..86b08475c254 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 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. + */ + +#ifndef __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USBIP_VERSION "1.0.0" + +#undef pr_fmt + +#ifdef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__ +#else +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +enum { + usbip_debug_xmit = (1 << 0), + usbip_debug_sysfs = (1 << 1), + usbip_debug_urb = (1 << 2), + usbip_debug_eh = (1 << 3), + + usbip_debug_stub_cmp = (1 << 8), + usbip_debug_stub_dev = (1 << 9), + usbip_debug_stub_rx = (1 << 10), + usbip_debug_stub_tx = (1 << 11), + + usbip_debug_vhci_rh = (1 << 8), + usbip_debug_vhci_hc = (1 << 9), + usbip_debug_vhci_rx = (1 << 10), + usbip_debug_vhci_tx = (1 << 11), + usbip_debug_vhci_sysfs = (1 << 12) +}; + +#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit) +#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh) +#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc) +#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx) +#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx) +#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx) +#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx) +#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs) + +extern unsigned long usbip_debug_flag; +extern struct device_attribute dev_attr_usbip_debug; + +#define usbip_dbg_with_flag(flag, fmt, args...) \ + do { \ + if (flag & usbip_debug_flag) \ + pr_debug(fmt, ##args); \ + } while (0) + +#define usbip_dbg_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args) +#define usbip_dbg_xmit(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args) +#define usbip_dbg_urb(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args) +#define usbip_dbg_eh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args) + +#define usbip_dbg_vhci_rh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args) +#define usbip_dbg_vhci_hc(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args) +#define usbip_dbg_vhci_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args) +#define usbip_dbg_vhci_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args) +#define usbip_dbg_vhci_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args) + +#define usbip_dbg_stub_cmp(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args) +#define usbip_dbg_stub_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args) +#define usbip_dbg_stub_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args) + +/* + * USB/IP request headers + * + * Each request is transferred across the network to its counterpart, which + * facilitates the normal USB communication. The values contained in the headers + * are basically the same as in a URB. Currently, four request types are + * defined: + * + * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb() + * (client to server) + * + * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT + * (server to client) + * + * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT, + * corresponds to usb_unlink_urb() + * (client to server) + * + * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK + * (server to client) + * + */ +#define USBIP_CMD_SUBMIT 0x0001 +#define USBIP_CMD_UNLINK 0x0002 +#define USBIP_RET_SUBMIT 0x0003 +#define USBIP_RET_UNLINK 0x0004 + +#define USBIP_DIR_OUT 0x00 +#define USBIP_DIR_IN 0x01 + +/** + * struct usbip_header_basic - data pertinent to every request + * @command: the usbip request type + * @seqnum: sequential number that identifies requests; incremented per + * connection + * @devid: specifies a remote USB device uniquely instead of busnum and devnum; + * in the stub driver, this value is ((busnum << 16) | devnum) + * @direction: direction of the transfer + * @ep: endpoint number + */ +struct usbip_header_basic { + __u32 command; + __u32 seqnum; + __u32 devid; + __u32 direction; + __u32 ep; +} __packed; + +/** + * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header + * @transfer_flags: URB flags + * @transfer_buffer_length: the data size for (in) or (out) transfer + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @interval: maximum time for the request on the server-side host controller + * @setup: setup data for a control request + */ +struct usbip_header_cmd_submit { + __u32 transfer_flags; + __s32 transfer_buffer_length; + + /* it is difficult for usbip to sync frames (reserved only?) */ + __s32 start_frame; + __s32 number_of_packets; + __s32 interval; + + unsigned char setup[8]; +} __packed; + +/** + * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header + * @status: return status of a non-iso request + * @actual_length: number of bytes transferred + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @error_count: number of errors for isochronous transfers + */ +struct usbip_header_ret_submit { + __s32 status; + __s32 actual_length; + __s32 start_frame; + __s32 number_of_packets; + __s32 error_count; +} __packed; + +/** + * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header + * @seqnum: the URB seqnum to unlink + */ +struct usbip_header_cmd_unlink { + __u32 seqnum; +} __packed; + +/** + * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header + * @status: return status of the request + */ +struct usbip_header_ret_unlink { + __s32 status; +} __packed; + +/** + * struct usbip_header - common header for all usbip packets + * @base: the basic header + * @u: packet type dependent header + */ +struct usbip_header { + struct usbip_header_basic base; + + union { + struct usbip_header_cmd_submit cmd_submit; + struct usbip_header_ret_submit ret_submit; + struct usbip_header_cmd_unlink cmd_unlink; + struct usbip_header_ret_unlink ret_unlink; + } u; +} __packed; + +/* + * This is the same as usb_iso_packet_descriptor but packed for pdu. + */ +struct usbip_iso_packet_descriptor { + __u32 offset; + __u32 length; /* expected length */ + __u32 actual_length; + __u32 status; +} __packed; + +enum usbip_side { + USBIP_VHCI, + USBIP_STUB, +}; + +/* event handler */ +#define USBIP_EH_SHUTDOWN (1 << 0) +#define USBIP_EH_BYE (1 << 1) +#define USBIP_EH_RESET (1 << 2) +#define USBIP_EH_UNUSABLE (1 << 3) + +#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) +#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) +#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +/* a common structure for stub_device and vhci_device */ +struct usbip_device { + enum usbip_side side; + enum usbip_device_status status; + + /* lock for status */ + spinlock_t lock; + + struct socket *tcp_socket; + + struct task_struct *tcp_rx; + struct task_struct *tcp_tx; + + unsigned long event; + struct task_struct *eh; + wait_queue_head_t eh_waitq; + + struct eh_ops { + void (*shutdown)(struct usbip_device *); + void (*reset)(struct usbip_device *); + void (*unusable)(struct usbip_device *); + } eh_ops; +}; + +#define kthread_get_run(threadfn, data, namefmt, ...) \ +({ \ + struct task_struct *__k \ + = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ + if (!IS_ERR(__k)) { \ + get_task_struct(__k); \ + wake_up_process(__k); \ + } \ + __k; \ +}) + +#define kthread_stop_put(k) \ + do { \ + kthread_stop(k); \ + put_task_struct(k); \ + } while (0) + +/* usbip_common.c */ +void usbip_dump_urb(struct urb *purb); +void usbip_dump_header(struct usbip_header *pdu); + +int usbip_recv(struct socket *sock, void *buf, int size); + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack); +void usbip_header_correct_endian(struct usbip_header *pdu, int send); + +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb); +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); + +/* usbip_event.c */ +int usbip_start_eh(struct usbip_device *ud); +void usbip_stop_eh(struct usbip_device *ud); +void usbip_event_add(struct usbip_device *ud, unsigned long event); +int usbip_event_happened(struct usbip_device *ud); + +static inline int interface_to_busnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->bus->busnum; +} + +static inline int interface_to_devnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->devnum; +} + +#endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c new file mode 100644 index 000000000000..64933b993d7a --- /dev/null +++ b/drivers/usb/usbip/usbip_event.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include + +#include "usbip_common.h" + +static int event_handler(struct usbip_device *ud) +{ + usbip_dbg_eh("enter\n"); + + /* + * Events are handled by only this thread. + */ + while (usbip_event_happened(ud)) { + usbip_dbg_eh("pending event %lx\n", ud->event); + + /* + * NOTE: shutdown must come first. + * Shutdown the device. + */ + if (ud->event & USBIP_EH_SHUTDOWN) { + ud->eh_ops.shutdown(ud); + ud->event &= ~USBIP_EH_SHUTDOWN; + } + + /* Reset the device. */ + if (ud->event & USBIP_EH_RESET) { + ud->eh_ops.reset(ud); + ud->event &= ~USBIP_EH_RESET; + } + + /* Mark the device as unusable. */ + if (ud->event & USBIP_EH_UNUSABLE) { + ud->eh_ops.unusable(ud); + ud->event &= ~USBIP_EH_UNUSABLE; + } + + /* Stop the error handler. */ + if (ud->event & USBIP_EH_BYE) + return -1; + } + + return 0; +} + +static int event_handler_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(ud->eh_waitq, + usbip_event_happened(ud) || + kthread_should_stop()); + usbip_dbg_eh("wakeup\n"); + + if (event_handler(ud) < 0) + break; + } + + return 0; +} + +int usbip_start_eh(struct usbip_device *ud) +{ + init_waitqueue_head(&ud->eh_waitq); + ud->event = 0; + + ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); + if (IS_ERR(ud->eh)) { + pr_warn("Unable to start control thread\n"); + return PTR_ERR(ud->eh); + } + + return 0; +} +EXPORT_SYMBOL_GPL(usbip_start_eh); + +void usbip_stop_eh(struct usbip_device *ud) +{ + if (ud->eh == current) + return; /* do not wait for myself */ + + kthread_stop(ud->eh); + usbip_dbg_eh("usbip_eh has finished\n"); +} +EXPORT_SYMBOL_GPL(usbip_stop_eh); + +void usbip_event_add(struct usbip_device *ud, unsigned long event) +{ + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); + ud->event |= event; + wake_up(&ud->eh_waitq); + spin_unlock_irqrestore(&ud->lock, flags); +} +EXPORT_SYMBOL_GPL(usbip_event_add); + +int usbip_event_happened(struct usbip_device *ud) +{ + int happened = 0; + + spin_lock(&ud->lock); + if (ud->event != 0) + happened = 1; + spin_unlock(&ud->lock); + + return happened; +} +EXPORT_SYMBOL_GPL(usbip_event_happened); diff --git a/drivers/usb/usbip/usbip_protocol.txt b/drivers/usb/usbip/usbip_protocol.txt new file mode 100644 index 000000000000..16b6fe27284c --- /dev/null +++ b/drivers/usb/usbip/usbip_protocol.txt @@ -0,0 +1,358 @@ +PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! +28 Jun 2011 + +The USB/IP protocol follows a server/client architecture. The server exports the +USB devices and the clients imports them. The device driver for the exported +USB device runs on the client machine. + +The client may ask for the list of the exported USB devices. To get the list the +client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST +packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent +in one or more pieces at the low level transport layer). The server sends back +the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the +TCP/IP connection is closed. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_DEVLIST | + | ----------------------------------------------> | + | | + | OP_REP_DEVLIST | + | <---------------------------------------------- | + | | + +Once the client knows the list of exported USB devices it may decide to use one +of them. First the client opens a TCP/IP connection towards the server and +sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the +import was successful the TCP/IP connection remains open and will be used +to transfer the URB traffic between the client and the server. The client may +send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and +USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the +server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_IMPORT | + | ----------------------------------------------> | + | | + | OP_REP_IMPORT | + | <---------------------------------------------- | + | | + | | + | USBIP_CMD_SUBMIT(seqnum = n) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = n) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_SUBMIT(seqnum = m) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+1) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+2) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+3) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+1) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+4) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+2) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_UNLINK | + | ----------------------------------------------> | + | | + | USBIP_RET_UNLINK | + | <---------------------------------------------- | + | | + +The fields are in network (big endian) byte order meaning that the most significant +byte (MSB) is stored at the lowest address. + + +OP_REQ_DEVLIST: Retrieve the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 + +OP_REP_DEVLIST: Reply with the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0. +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | n | Number of exported devices: 0 means no exported + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 0x0C | | | From now on the exported n devices are described, + | | | if any. If no devices are exported the message + | | | ends with the previous "number of exported + | | | devices" field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x10C | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x134 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x13A | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x13C | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13F | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x140 | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x141 | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x142 | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x143 | 1 | | bNumInterfaces +-----------+--------+------------+--------------------------------------------------- + 0x144 | | m_0 | From now on each interface is described, all + | | | together bNumInterfaces times, with the + | | | the following 4 fields: +-----------+--------+------------+--------------------------------------------------- + | 1 | | bInterfaceClass +-----------+--------+------------+--------------------------------------------------- + 0x145 | 1 | | bInterfaceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x146 | 1 | | bInterfaceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x147 | 1 | | padding byte for alignment, shall be set to zero +-----------+--------+------------+--------------------------------------------------- + 0xC + | | | The second exported USB device starts at i=1 + i*0x138 + | | | with the busid field. + m_(i-1)*4 | | | + +OP_REQ_IMPORT: Request to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8003 | Command code: import a remote USB device. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 +-----------+--------+------------+--------------------------------------------------- + 8 | 32 | | busid: the busid of the exported device on the + | | | remote host. The possible values are taken + | | | from the message field OP_REP_DEVLIST.busid. + | | | A string closed with zero, the unused bytes + | | | shall be filled with zeros. +-----------+--------+------------+--------------------------------------------------- + +OP_REP_IMPORT: Reply to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0003 | Reply code: Reply to import. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK + | | | 1 for error +-----------+--------+------------+--------------------------------------------------- + 8 | | | From now on comes the details of the imported + | | | device, if the previous status field was OK (0), + | | | otherwise the reply ends with the status field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x108 | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x128 | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x134 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x136 | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x139 | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13A | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x13B | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x13C | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x13D | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bNumInterfaces + +USBIP_CMD_SUBMIT: Submit an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000001 | command: Submit an URB +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the sequence number of the URB to submit +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | transfer_flags: possible values depend on the + | | | URB transfer type, see below +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | | transfer_buffer_length +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: specify the selected frame to + | | | transmit an ISO frame, ignored if URB_ISO_ASAP + | | | is specified at transfer_flags +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets: number of ISO packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | interval: maximum time for the request on the + | | | server-side host controller +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | | | URB data. For ISO transfers the padding between + | | | each ISO packets is not transmitted. + + + Allowed transfer_flags | value | control | interrupt | bulk | isochronous + -------------------------+------------+---------+-----------+----------+------------- + URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no + URB_ISO_ASAP | 0x00000002 | no | no | no | yes + URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes + URB_NO_FSBR | 0x00000020 | yes | no | no | no + URB_ZERO_PACKET | 0x00000040 | no | no | only out | no + URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes + URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes + URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes + + +USBIP_RET_SUBMIT: Reply for submitting an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000003 | command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: zero for successful URB transaction, + | | | otherwise some kind of error happened. +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | n | actual_length: number of URB data bytes +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: for an ISO frame the actually + | | | selected frame for transmit. +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | error_count +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_CMD_UNLINK: Unlink an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000002 | command: URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so? +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number: zero +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | seqnum: the URB sequence number given previously + | | | at USBIP_CMD_SUBMIT.seqnum field +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_RET_UNLINK: Reply for URB unlink + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000004 | command: reply for the URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the unlinked URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: This is the value contained in the + | | | urb->status in the URB completition handler. + | | | FIXME: a better explanation needed. +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h new file mode 100644 index 000000000000..a863a98a91ce --- /dev/null +++ b/drivers/usb/usbip/vhci.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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. + * + */ + +#ifndef __USBIP_VHCI_H +#define __USBIP_VHCI_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vhci_device { + struct usb_device *udev; + + /* + * devid specifies a remote usb device uniquely instead + * of combination of busnum and devnum. + */ + __u32 devid; + + /* speed of a remote device */ + enum usb_device_speed speed; + + /* vhci root-hub port to which this device is attached */ + __u32 rhport; + + struct usbip_device ud; + + /* lock for the below link lists */ + spinlock_t priv_lock; + + /* vhci_priv is linked to one of them. */ + struct list_head priv_tx; + struct list_head priv_rx; + + /* vhci_unlink is linked to one of them */ + struct list_head unlink_tx; + struct list_head unlink_rx; + + /* vhci_tx thread sleeps for this queue */ + wait_queue_head_t waitq_tx; +}; + +/* urb->hcpriv, use container_of() */ +struct vhci_priv { + unsigned long seqnum; + struct list_head list; + + struct vhci_device *vdev; + struct urb *urb; +}; + +struct vhci_unlink { + /* seqnum of this request */ + unsigned long seqnum; + + struct list_head list; + + /* seqnum of the unlink target */ + unsigned long unlink_seqnum; +}; + +/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */ +#define VHCI_NPORTS 8 + +/* for usb_bus.hcpriv */ +struct vhci_hcd { + spinlock_t lock; + + u32 port_status[VHCI_NPORTS]; + + unsigned resuming:1; + unsigned long re_timeout; + + atomic_t seqnum; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + * But, the index of this array begins from 0. + */ + struct vhci_device vdev[VHCI_NPORTS]; +}; + +extern struct vhci_hcd *the_controller; +extern const struct attribute_group dev_attr_group; + +/* vhci_hcd.c */ +void rh_port_connect(int rhport, enum usb_device_speed speed); + +/* vhci_rx.c */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum); +int vhci_rx_loop(void *data); + +/* vhci_tx.c */ +int vhci_tx_loop(void *data); + +static inline struct vhci_device *port_to_vdev(__u32 port) +{ + return &the_controller->vdev[port]; +} + +static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd) +{ + return (struct vhci_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci) +{ + return container_of((void *) vhci, struct usb_hcd, hcd_priv); +} + +static inline struct device *vhci_dev(struct vhci_hcd *vhci) +{ + return vhci_to_hcd(vhci)->self.controller; +} + +#endif /* __USBIP_VHCI_H */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c new file mode 100644 index 000000000000..c02374b6049c --- /dev/null +++ b/drivers/usb/usbip/vhci_hcd.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "usbip_common.h" +#include "vhci.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" + +/* + * TODO + * - update root hub emulation + * - move the emulation code to userland ? + * porting to other operating systems + * minimize kernel code + * - add suspend/resume code + * - clean up everything + */ + +/* See usb gadget dummy hcd */ + +static int vhci_hub_status(struct usb_hcd *hcd, char *buff); +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buff, u16 wLength); +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +static int vhci_start(struct usb_hcd *vhci_hcd); +static void vhci_stop(struct usb_hcd *hcd); +static int vhci_get_frame_number(struct usb_hcd *hcd); + +static const char driver_name[] = "vhci_hcd"; +static const char driver_desc[] = "USB/IP Virtual Host Controller"; + +struct vhci_hcd *the_controller; + +static const char * const bit_desc[] = { + "CONNECTION", /*0*/ + "ENABLE", /*1*/ + "SUSPEND", /*2*/ + "OVER_CURRENT", /*3*/ + "RESET", /*4*/ + "R5", /*5*/ + "R6", /*6*/ + "R7", /*7*/ + "POWER", /*8*/ + "LOWSPEED", /*9*/ + "HIGHSPEED", /*10*/ + "PORT_TEST", /*11*/ + "INDICATOR", /*12*/ + "R13", /*13*/ + "R14", /*14*/ + "R15", /*15*/ + "C_CONNECTION", /*16*/ + "C_ENABLE", /*17*/ + "C_SUSPEND", /*18*/ + "C_OVER_CURRENT", /*19*/ + "C_RESET", /*20*/ + "R21", /*21*/ + "R22", /*22*/ + "R23", /*23*/ + "R24", /*24*/ + "R25", /*25*/ + "R26", /*26*/ + "R27", /*27*/ + "R28", /*28*/ + "R29", /*29*/ + "R30", /*30*/ + "R31", /*31*/ +}; + +static void dump_port_status_diff(u32 prev_status, u32 new_status) +{ + int i = 0; + u32 bit = 1; + + pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); + while (bit) { + u32 prev = prev_status & bit; + u32 new = new_status & bit; + char change; + + if (!prev && new) + change = '+'; + else if (prev && !new) + change = '-'; + else + change = ' '; + + if (prev || new) + pr_debug(" %c%s\n", change, bit_desc[i]); + bit <<= 1; + i++; + } + pr_debug("\n"); +} + +void rh_port_connect(int rhport, enum usb_device_speed speed) +{ + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION + | (1 << USB_PORT_FEAT_C_CONNECTION); + + switch (speed) { + case USB_SPEED_HIGH: + the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + + spin_unlock(&the_controller->lock); + + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +static void rh_port_disconnect(int rhport) +{ + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; + the_controller->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_CONNECTION); + + spin_unlock(&the_controller->lock); + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +#define PORT_C_MASK \ + ((USB_PORT_STAT_C_CONNECTION \ + | USB_PORT_STAT_C_ENABLE \ + | USB_PORT_STAT_C_SUSPEND \ + | USB_PORT_STAT_C_OVERCURRENT \ + | USB_PORT_STAT_C_RESET) << 16) + +/* + * Returns 0 if the status hasn't changed, or the number of bytes in buf. + * Ports are 0-indexed from the HCD point of view, + * and 1-indexed from the USB core pointer of view. + * + * @buf: a bitmap to show which port status has been changed. + * bit 0: reserved + * bit 1: the status of port 0 has been changed. + * bit 2: the status of port 1 has been changed. + * ... + */ +static int vhci_hub_status(struct usb_hcd *hcd, char *buf) +{ + struct vhci_hcd *vhci; + int retval; + int rhport; + int changed = 0; + + retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); + memset(buf, 0, retval); + + vhci = hcd_to_vhci(hcd); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) { + usbip_dbg_vhci_rh("hw accessible flag not on?\n"); + goto done; + } + + /* check pseudo status register for each port */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + if ((vhci->port_status[rhport] & PORT_C_MASK)) { + /* The status of a port has been changed, */ + usbip_dbg_vhci_rh("port %d status changed\n", rhport); + + buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; + changed = 1; + } + } + + if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) + usb_hcd_resume_root_hub(hcd); + +done: + spin_unlock(&vhci->lock); + return changed ? retval : 0; +} + +static inline void hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof(*desc)); + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001)); + desc->bNbrPorts = VHCI_NPORTS; + desc->u.hs.DeviceRemovable[0] = 0xff; + desc->u.hs.DeviceRemovable[1] = 0xff; +} + +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct vhci_hcd *dum; + int retval = 0; + int rhport; + + u32 prev_port_status[VHCI_NPORTS]; + + if (!HCD_HW_ACCESSIBLE(hcd)) + return -ETIMEDOUT; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + */ + usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, + wIndex); + if (wIndex > VHCI_NPORTS) + pr_err("invalid port number %d\n", wIndex); + rhport = ((__u8)(wIndex & 0x00ff)) - 1; + + dum = hcd_to_vhci(hcd); + + spin_lock(&dum->lock); + + /* store old status and compare now and old later */ + if (usbip_dbg_flag_vhci_rh) { + memcpy(prev_port_status, dum->port_status, + sizeof(prev_port_status)); + } + + switch (typeReq) { + case ClearHubFeature: + usbip_dbg_vhci_rh(" ClearHubFeature\n"); + break; + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) { + /* 20msec signaling */ + dum->resuming = 1; + dum->re_timeout = + jiffies + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_POWER: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_POWER\n"); + dum->port_status[rhport] = 0; + dum->resuming = 0; + break; + case USB_PORT_FEAT_C_RESET: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); + switch (dum->vdev[rhport].speed) { + case USB_SPEED_HIGH: + dum->port_status[rhport] |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum->port_status[rhport] |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + default: + usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", + wValue); + dum->port_status[rhport] &= ~(1 << wValue); + break; + } + break; + case GetHubDescriptor: + usbip_dbg_vhci_rh(" GetHubDescriptor\n"); + hub_descriptor((struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + usbip_dbg_vhci_rh(" GetHubStatus\n"); + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); + if (wIndex > VHCI_NPORTS || wIndex < 1) { + pr_err("invalid port number %d\n", wIndex); + retval = -EPIPE; + } + + /* we do not care about resume. */ + + /* whoever resets or resumes must GetPortStatus to + * complete it!! + */ + if (dum->resuming && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_SUSPEND); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_SUSPEND); + dum->resuming = 0; + dum->re_timeout = 0; + } + + if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != + 0 && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_RESET); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_RESET); + dum->re_timeout = 0; + + if (dum->vdev[rhport].ud.status == + VDEV_ST_NOTASSIGNED) { + usbip_dbg_vhci_rh( + " enable rhport %d (status %u)\n", + rhport, + dum->vdev[rhport].ud.status); + dum->port_status[rhport] |= + USB_PORT_STAT_ENABLE; + } + } + ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]); + ((__le16 *) buf)[1] = + cpu_to_le16(dum->port_status[rhport] >> 16); + + usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0], + ((u16 *)buf)[1]); + break; + case SetHubFeature: + usbip_dbg_vhci_rh(" SetHubFeature\n"); + retval = -EPIPE; + break; + case SetPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); + break; + case USB_PORT_FEAT_RESET: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_RESET\n"); + /* if it's already running, disconnect first */ + if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { + dum->port_status[rhport] &= + ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + /* FIXME test that code path! */ + } + /* 50msec reset signaling */ + dum->re_timeout = jiffies + msecs_to_jiffies(50); + + /* FALLTHROUGH */ + default: + usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", + wValue); + dum->port_status[rhport] |= (1 << wValue); + break; + } + break; + + default: + pr_err("default: no such request\n"); + + /* "protocol stall" on error */ + retval = -EPIPE; + } + + if (usbip_dbg_flag_vhci_rh) { + pr_debug("port %d\n", rhport); + /* Only dump valid port status */ + if (rhport >= 0) { + dump_port_status_diff(prev_port_status[rhport], + dum->port_status[rhport]); + } + } + usbip_dbg_vhci_rh(" bye\n"); + + spin_unlock(&dum->lock); + + return retval; +} + +static struct vhci_device *get_vdev(struct usb_device *udev) +{ + int i; + + if (!udev) + return NULL; + + for (i = 0; i < VHCI_NPORTS; i++) + if (the_controller->vdev[i].udev == udev) + return port_to_vdev(i); + + return NULL; +} + +static void vhci_tx_urb(struct urb *urb) +{ + struct vhci_device *vdev = get_vdev(urb->dev); + struct vhci_priv *priv; + + if (!vdev) { + pr_err("could not get virtual device"); + return; + } + + priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); + if (!priv) { + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + spin_lock(&vdev->priv_lock); + + priv->seqnum = atomic_inc_return(&the_controller->seqnum); + if (priv->seqnum == 0xffff) + dev_info(&urb->dev->dev, "seqnum max\n"); + + priv->vdev = vdev; + priv->urb = urb; + + urb->hcpriv = (void *) priv; + + list_add_tail(&priv->list, &vdev->priv_tx); + + wake_up(&vdev->waitq_tx); + spin_unlock(&vdev->priv_lock); +} + +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct device *dev = &urb->dev->dev; + int ret = 0; + struct vhci_device *vdev; + + usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", + hcd, urb, mem_flags); + + /* patch to usb_sg_init() is in 2.5.60 */ + BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); + + spin_lock(&the_controller->lock); + + if (urb->status != -EINPROGRESS) { + dev_err(dev, "URB already unlinked!, status %d\n", urb->status); + spin_unlock(&the_controller->lock); + return urb->status; + } + + vdev = port_to_vdev(urb->dev->portnum-1); + + /* refuse enqueue for dead connection */ + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL || + vdev->ud.status == VDEV_ST_ERROR) { + dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport); + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + return -ENODEV; + } + spin_unlock(&vdev->ud.lock); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto no_need_unlink; + + /* + * The enumeration process is as follows; + * + * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) + * to get max packet length of default pipe + * + * 2. Set_Address request to DevAddr(0) EndPoint(0) + * + */ + if (usb_pipedevice(urb->pipe) == 0) { + __u8 type = usb_pipetype(urb->pipe); + struct usb_ctrlrequest *ctrlreq = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (type != PIPE_CONTROL || !ctrlreq) { + dev_err(dev, "invalid request to devnum 0\n"); + ret = -EINVAL; + goto no_need_xmit; + } + + switch (ctrlreq->bRequest) { + case USB_REQ_SET_ADDRESS: + /* set_address may come when a device is reset */ + dev_info(dev, "SetAddress Request (%d) to port %d\n", + ctrlreq->wValue, vdev->rhport); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + + spin_lock(&vdev->ud.lock); + vdev->ud.status = VDEV_ST_USED; + spin_unlock(&vdev->ud.lock); + + if (urb->status == -EINPROGRESS) { + /* This request is successfully completed. */ + /* If not -EINPROGRESS, possibly unlinked. */ + urb->status = 0; + } + + goto no_need_xmit; + + case USB_REQ_GET_DESCRIPTOR: + if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8)) + usbip_dbg_vhci_hc( + "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + goto out; + + default: + /* NOT REACHED */ + dev_err(dev, + "invalid request to devnum 0 bRequest %u, wValue %u\n", + ctrlreq->bRequest, + ctrlreq->wValue); + ret = -EINVAL; + goto no_need_xmit; + } + + } + +out: + vhci_tx_urb(urb); + spin_unlock(&the_controller->lock); + + return 0; + +no_need_xmit: + usb_hcd_unlink_urb_from_ep(hcd, urb); +no_need_unlink: + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + return ret; +} + +/* + * vhci_rx gives back the urb after receiving the reply of the urb. If an + * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives + * back its urb. For the driver unlinking the urb, the content of the urb is + * not important, but the calling to its completion handler is important; the + * completion of unlinking is notified by the completion handler. + * + * + * CLIENT SIDE + * + * - When vhci_hcd receives RET_SUBMIT, + * + * - case 1a). the urb of the pdu is not unlinking. + * - normal case + * => just give back the urb + * + * - case 1b). the urb of the pdu is unlinking. + * - usbip.ko will return a reply of the unlinking request. + * => give back the urb now and go to case 2b). + * + * - When vhci_hcd receives RET_UNLINK, + * + * - case 2a). a submit request is still pending in vhci_hcd. + * - urb was really pending in usbip.ko and urb_unlink_urb() was + * completed there. + * => free a pending submit request + * => notify unlink completeness by giving back the urb + * + * - case 2b). a submit request is *not* pending in vhci_hcd. + * - urb was already given back to the core driver. + * => do not give back the urb + * + * + * SERVER SIDE + * + * - When usbip receives CMD_UNLINK, + * + * - case 3a). the urb of the unlink request is now in submission. + * => do usb_unlink_urb(). + * => after the unlink is completed, send RET_UNLINK. + * + * - case 3b). the urb of the unlink request is not in submission. + * - may be already completed or never be received + * => send RET_UNLINK + * + */ +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct vhci_priv *priv; + struct vhci_device *vdev; + + pr_info("dequeue a urb %p\n", urb); + + spin_lock(&the_controller->lock); + + priv = urb->hcpriv; + if (!priv) { + /* URB was never linked! or will be soon given back by + * vhci_rx. */ + spin_unlock(&the_controller->lock); + return 0; + } + + { + int ret = 0; + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) { + spin_unlock(&the_controller->lock); + return ret; + } + } + + /* send unlink request here? */ + vdev = priv->vdev; + + if (!vdev->ud.tcp_socket) { + /* tcp connection is closed */ + spin_lock(&vdev->priv_lock); + + pr_info("device %p seems to be disconnected\n", vdev); + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + spin_unlock(&vdev->priv_lock); + + /* + * If tcp connection is alive, we have sent CMD_UNLINK. + * vhci_rx will receive RET_UNLINK and give back the URB. + * Otherwise, we give back it here. + */ + pr_info("gives back urb %p\n", urb); + + usb_hcd_unlink_urb_from_ep(hcd, urb); + + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + spin_lock(&the_controller->lock); + + } else { + /* tcp connection is alive */ + struct vhci_unlink *unlink; + + spin_lock(&vdev->priv_lock); + + /* setup CMD_UNLINK pdu */ + unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); + if (!unlink) { + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return -ENOMEM; + } + + unlink->seqnum = atomic_inc_return(&the_controller->seqnum); + if (unlink->seqnum == 0xffff) + pr_info("seqnum max\n"); + + unlink->unlink_seqnum = priv->seqnum; + + pr_info("device %p seems to be still connected\n", vdev); + + /* send cmd_unlink and try to cancel the pending URB in the + * peer */ + list_add_tail(&unlink->list, &vdev->unlink_tx); + wake_up(&vdev->waitq_tx); + + spin_unlock(&vdev->priv_lock); + } + + spin_unlock(&the_controller->lock); + + usbip_dbg_vhci_hc("leave\n"); + return 0; +} + +static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + } + + while (!list_empty(&vdev->unlink_rx)) { + struct urb *urb; + + unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, + list); + + /* give back URB of unanswered unlink request */ + pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); + + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + if (!urb) { + pr_info("the urb (seqnum %lu) was already given back\n", + unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + continue; + } + + urb->status = -ENODEV; + + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + kfree(unlink); + } + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); +} + +/* + * The important thing is that only one context begins cleanup. + * This is why error handling and cleanup become simple. + * We do not want to consider race condition as possible. + */ +static void vhci_shutdown_connection(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + /* need this? see stub_dev.c */ + if (ud->tcp_socket) { + pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* kill threads related to this sdev */ + if (vdev->ud.tcp_rx) { + kthread_stop_put(vdev->ud.tcp_rx); + vdev->ud.tcp_rx = NULL; + } + if (vdev->ud.tcp_tx) { + kthread_stop_put(vdev->ud.tcp_tx); + vdev->ud.tcp_tx = NULL; + } + pr_info("stop threads\n"); + + /* active connection is closed */ + if (vdev->ud.tcp_socket) { + sockfd_put(vdev->ud.tcp_socket); + vdev->ud.tcp_socket = NULL; + } + pr_info("release socket\n"); + + vhci_device_unlink_cleanup(vdev); + + /* + * rh_port_disconnect() is a trigger of ... + * usb_disable_device(): + * disable all the endpoints for a USB device. + * usb_disable_endpoint(): + * disable endpoints. pending urbs are unlinked(dequeued). + * + * NOTE: After calling rh_port_disconnect(), the USB device drivers of a + * detached device should release used urbs in a cleanup function (i.e. + * xxx_disconnect()). Therefore, vhci_hcd does not need to release + * pushed urbs and their private data in this function. + * + * NOTE: vhci_dequeue() must be considered carefully. When shutting down + * a connection, vhci_shutdown_connection() expects vhci_dequeue() + * gives back pushed urbs and frees their private data by request of + * the cleanup function of a USB driver. When unlinking a urb with an + * active connection, vhci_dequeue() does not give back the urb which + * is actually given back by vhci_rx after receiving its return pdu. + * + */ + rh_port_disconnect(vdev->rhport); + + pr_info("disconnect device\n"); +} + + +static void vhci_device_reset(struct usbip_device *ud) +{ + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + spin_lock(&ud->lock); + + vdev->speed = 0; + vdev->devid = 0; + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = NULL; + + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + ud->status = VDEV_ST_NULL; + + spin_unlock(&ud->lock); +} + +static void vhci_device_unusable(struct usbip_device *ud) +{ + spin_lock(&ud->lock); + ud->status = VDEV_ST_ERROR; + spin_unlock(&ud->lock); +} + +static void vhci_device_init(struct vhci_device *vdev) +{ + memset(vdev, 0, sizeof(*vdev)); + + vdev->ud.side = USBIP_VHCI; + vdev->ud.status = VDEV_ST_NULL; + spin_lock_init(&vdev->ud.lock); + + INIT_LIST_HEAD(&vdev->priv_rx); + INIT_LIST_HEAD(&vdev->priv_tx); + INIT_LIST_HEAD(&vdev->unlink_tx); + INIT_LIST_HEAD(&vdev->unlink_rx); + spin_lock_init(&vdev->priv_lock); + + init_waitqueue_head(&vdev->waitq_tx); + + vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; + vdev->ud.eh_ops.reset = vhci_device_reset; + vdev->ud.eh_ops.unusable = vhci_device_unusable; + + usbip_start_eh(&vdev->ud); +} + +static int vhci_start(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport; + int err = 0; + + usbip_dbg_vhci_hc("enter vhci_start\n"); + + /* initialize private data of usb_hcd */ + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + vhci_device_init(vdev); + vdev->rhport = rhport; + } + + atomic_set(&vhci->seqnum, 0); + spin_lock_init(&vhci->lock); + + hcd->power_budget = 0; /* no limit */ + hcd->uses_new_polling = 1; + + /* vhci_hcd is now ready to be controlled through sysfs */ + err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + if (err) { + pr_err("create sysfs files\n"); + return err; + } + + return 0; +} + +static void vhci_stop(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rhport = 0; + + usbip_dbg_vhci_hc("stop VHCI controller\n"); + + /* 1. remove the userland interface of vhci_hcd */ + sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group); + + /* 2. shutdown all the ports of vhci_hcd */ + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + struct vhci_device *vdev = &vhci->vdev[rhport]; + + usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); + usbip_stop_eh(&vdev->ud); + } +} + +static int vhci_get_frame_number(struct usb_hcd *hcd) +{ + pr_err("Not yet implemented\n"); + return 0; +} + +#ifdef CONFIG_PM + +/* FIXME: suspend/resume */ +static int vhci_bus_suspend(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + hcd->state = HC_STATE_SUSPENDED; + spin_unlock(&vhci->lock); + + return 0; +} + +static int vhci_bus_resume(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rc = 0; + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) + rc = -ESHUTDOWN; + else + hcd->state = HC_STATE_RUNNING; + spin_unlock(&vhci->lock); + + return rc; +} + +#else + +#define vhci_bus_suspend NULL +#define vhci_bus_resume NULL +#endif + +static struct hc_driver vhci_hc_driver = { + .description = driver_name, + .product_desc = driver_desc, + .hcd_priv_size = sizeof(struct vhci_hcd), + + .flags = HCD_USB2, + + .start = vhci_start, + .stop = vhci_stop, + + .urb_enqueue = vhci_urb_enqueue, + .urb_dequeue = vhci_urb_dequeue, + + .get_frame_number = vhci_get_frame_number, + + .hub_status_data = vhci_hub_status, + .hub_control = vhci_hub_control, + .bus_suspend = vhci_bus_suspend, + .bus_resume = vhci_bus_resume, +}; + +static int vhci_hcd_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + int ret; + + usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); + + /* + * Allocate and initialize hcd. + * Our private data is also allocated automatically. + */ + hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + pr_err("create hcd failed\n"); + return -ENOMEM; + } + hcd->has_tt = 1; + + /* this is private data for vhci_hcd */ + the_controller = hcd_to_vhci(hcd); + + /* + * Finish generic HCD structure initialization and register. + * Call the driver's reset() and start() routines. + */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret != 0) { + pr_err("usb_add_hcd failed %d\n", ret); + usb_put_hcd(hcd); + the_controller = NULL; + return ret; + } + + usbip_dbg_vhci_hc("bye\n"); + return 0; +} + +static int vhci_hcd_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + hcd = platform_get_drvdata(pdev); + if (!hcd) + return 0; + + /* + * Disconnects the root hub, + * then reverses the effects of usb_add_hcd(), + * invoking the HCD's stop() methods. + */ + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + the_controller = NULL; + + return 0; +} + +#ifdef CONFIG_PM + +/* what should happen for USB/IP under suspend/resume? */ +static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct usb_hcd *hcd; + int rhport = 0; + int connected = 0; + int ret = 0; + + hcd = platform_get_drvdata(pdev); + + spin_lock(&the_controller->lock); + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) + if (the_controller->port_status[rhport] & + USB_PORT_STAT_CONNECTION) + connected += 1; + + spin_unlock(&the_controller->lock); + + if (connected > 0) { + dev_info(&pdev->dev, + "We have %d active connection%s. Do not suspend.\n", + connected, (connected == 1 ? "" : "s")); + ret = -EBUSY; + } else { + dev_info(&pdev->dev, "suspend vhci_hcd"); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } + + return ret; +} + +static int vhci_hcd_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + hcd = platform_get_drvdata(pdev); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + + return 0; +} + +#else + +#define vhci_hcd_suspend NULL +#define vhci_hcd_resume NULL + +#endif + +static struct platform_driver vhci_driver = { + .probe = vhci_hcd_probe, + .remove = vhci_hcd_remove, + .suspend = vhci_hcd_suspend, + .resume = vhci_hcd_resume, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +/* + * The VHCI 'device' is 'virtual'; not a real plug&play hardware. + * We need to add this virtual device as a platform device arbitrarily: + * 1. platform_device_register() + */ +static void the_pdev_release(struct device *dev) +{ +} + +static struct platform_device the_pdev = { + /* should be the same name as driver_name */ + .name = driver_name, + .id = -1, + .dev = { + .release = the_pdev_release, + }, +}; + +static int __init vhci_hcd_init(void) +{ + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = platform_driver_register(&vhci_driver); + if (ret) + goto err_driver_register; + + ret = platform_device_register(&the_pdev); + if (ret) + goto err_platform_device_register; + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_platform_device_register: + platform_driver_unregister(&vhci_driver); +err_driver_register: + return ret; +} + +static void __exit vhci_hcd_exit(void) +{ + platform_device_unregister(&the_pdev); + platform_driver_unregister(&vhci_driver); +} + +module_init(vhci_hcd_init); +module_exit(vhci_hcd_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c new file mode 100644 index 000000000000..00e4a54308e4 --- /dev/null +++ b/drivers/usb/usbip/vhci_rx.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include + +#include "usbip_common.h" +#include "vhci.h" + +/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) +{ + struct vhci_priv *priv, *tmp; + struct urb *urb = NULL; + int status; + + list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { + if (priv->seqnum != seqnum) + continue; + + urb = priv->urb; + status = urb->status; + + usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", + urb, priv, seqnum); + + switch (status) { + case -ENOENT: + /* fall through */ + case -ECONNRESET: + dev_info(&urb->dev->dev, + "urb %p was unlinked %ssynchronuously.\n", urb, + status == -ENOENT ? "" : "a"); + break; + case -EINPROGRESS: + /* no info output */ + break; + default: + dev_info(&urb->dev->dev, + "urb %p may be in a error, status %d\n", urb, + status); + } + + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + break; + } + + return urb; +} + +static void vhci_recv_ret_submit(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct usbip_device *ud = &vdev->ud; + struct urb *urb; + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); + pr_info("max seqnum %d\n", + atomic_read(&the_controller->seqnum)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + /* unpack the pdu to a urb */ + usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); + + /* recv transfer buffer */ + if (usbip_recv_xbuff(ud, urb) < 0) + return; + + /* recv iso_packet_descriptor */ + if (usbip_recv_iso(ud, urb) < 0) + return; + + /* restore the padding in iso packets */ + usbip_pad_iso(ud, urb); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_urb(urb); + + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + + usbip_dbg_vhci_rx("Leave\n"); +} + +static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { + pr_info("unlink->seqnum %lu\n", unlink->seqnum); + if (unlink->seqnum == pdu->base.seqnum) { + usbip_dbg_vhci_rx("found pending unlink, %lu\n", + unlink->seqnum); + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + return unlink; + } + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static void vhci_recv_ret_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink; + struct urb *urb; + + usbip_dump_header(pdu); + + unlink = dequeue_pending_unlink(vdev, pdu); + if (!unlink) { + pr_info("cannot find the pending unlink %u\n", + pdu->base.seqnum); + return; + } + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + /* + * I get the result of a unlink request. But, it seems that I + * already received the result of its submit result and gave + * back the URB. + */ + pr_info("the urb (seqnum %d) was already given back\n", + pdu->base.seqnum); + } else { + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + /* If unlink is successful, status is -ECONNRESET */ + urb->status = pdu->u.ret_unlink.status; + pr_info("urb->status %d\n", urb->status); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + } + + kfree(unlink); +} + +static int vhci_priv_tx_empty(struct vhci_device *vdev) +{ + int empty = 0; + + spin_lock(&vdev->priv_lock); + empty = list_empty(&vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + + return empty; +} + +/* recv a pdu */ +static void vhci_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + usbip_dbg_vhci_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret < 0) { + if (ret == -ECONNRESET) + pr_info("connection reset by peer\n"); + else if (ret == -EAGAIN) { + /* ignore if connection was idle */ + if (vhci_priv_tx_empty(vdev)) + return; + pr_info("connection timed out with pending urbs\n"); + } else if (ret != -ERESTARTSYS) + pr_info("xmit failed %d\n", ret); + + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + if (ret == 0) { + pr_info("connection closed"); + usbip_event_add(ud, VDEV_EVENT_DOWN); + return; + } + if (ret != sizeof(pdu)) { + pr_err("received pdu size is %d, should be %d\n", ret, + (unsigned int)sizeof(pdu)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_header(&pdu); + + switch (pdu.base.command) { + case USBIP_RET_SUBMIT: + vhci_recv_ret_submit(vdev, &pdu); + break; + case USBIP_RET_UNLINK: + vhci_recv_ret_unlink(vdev, &pdu); + break; + default: + /* NOT REACHED */ + pr_err("unknown pdu %u\n", pdu.base.command); + usbip_dump_header(&pdu); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + break; + } +} + +int vhci_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + vhci_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c new file mode 100644 index 000000000000..211f43f67ea2 --- /dev/null +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#include "usbip_common.h" +#include "vhci.h" + +/* TODO: refine locking ?*/ + +/* Sysfs entry to show port status */ +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *out) +{ + char *s = out; + int i = 0; + + BUG_ON(!the_controller || !out); + + spin_lock(&the_controller->lock); + + /* + * output example: + * prt sta spd dev socket local_busid + * 000 004 000 000 c5a7bb80 1-2.3 + * 001 004 000 000 d8cee980 2-3.4 + * + * IP address can be retrieved from a socket pointer address by looking + * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a + * port number and its peer IP address. + */ + out += sprintf(out, + "prt sta spd bus dev socket local_busid\n"); + + for (i = 0; i < VHCI_NPORTS; i++) { + struct vhci_device *vdev = port_to_vdev(i); + + spin_lock(&vdev->ud.lock); + out += sprintf(out, "%03u %03u ", i, vdev->ud.status); + + if (vdev->ud.status == VDEV_ST_USED) { + out += sprintf(out, "%03u %08x ", + vdev->speed, vdev->devid); + out += sprintf(out, "%16p ", vdev->ud.tcp_socket); + out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); + + } else { + out += sprintf(out, "000 000 000 0000000000000000 0-0"); + } + + out += sprintf(out, "\n"); + spin_unlock(&vdev->ud.lock); + } + + spin_unlock(&the_controller->lock); + + return out - s; +} +static DEVICE_ATTR_RO(status); + +/* Sysfs entry to shutdown a virtual connection */ +static int vhci_port_disconnect(__u32 rhport) +{ + struct vhci_device *vdev; + + usbip_dbg_vhci_sysfs("enter\n"); + + /* lock */ + spin_lock(&the_controller->lock); + + vdev = port_to_vdev(rhport); + + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL) { + pr_err("not connected %d\n", vdev->ud.status); + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + return -EINVAL; + } + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); + + return 0; +} + +static ssize_t store_detach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + __u32 rhport = 0; + + if (sscanf(buf, "%u", &rhport) != 1) + return -EINVAL; + + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + dev_err(dev, "invalid port %u\n", rhport); + return -EINVAL; + } + + err = vhci_port_disconnect(rhport); + if (err < 0) + return -EINVAL; + + usbip_dbg_vhci_sysfs("Leave\n"); + + return count; +} +static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); + +/* Sysfs entry to establish a virtual connection */ +static int valid_args(__u32 rhport, enum usb_device_speed speed) +{ + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + pr_err("port %u\n", rhport); + return -EINVAL; + } + + /* check speed */ + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: + break; + default: + pr_err("Failed attach request for unsupported USB speed: %s\n", + usb_speed_string(speed)); + return -EINVAL; + } + + return 0; +} + +/* + * To start a new USB/IP attachment, a userland program needs to setup a TCP + * connection and then write its socket descriptor with remote device + * information into this sysfs file. + * + * A remote device is virtually attached to the root-hub port of @rhport with + * @speed. @devid is embedded into a request to specify the remote device in a + * server host. + * + * write() returns 0 on success, else negative errno. + */ +static ssize_t store_attach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vhci_device *vdev; + struct socket *socket; + int sockfd = 0; + __u32 rhport = 0, devid = 0, speed = 0; + int err; + + /* + * @rhport: port number of vhci_hcd + * @sockfd: socket descriptor of an established TCP connection + * @devid: unique device identifier in a remote host + * @speed: usb device speed in a remote host + */ + if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) + return -EINVAL; + + usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", + rhport, sockfd, devid, speed); + + /* check received parameters */ + if (valid_args(rhport, speed) < 0) + return -EINVAL; + + /* Extract socket from fd. */ + socket = sockfd_lookup(sockfd, &err); + if (!socket) + return -EINVAL; + + /* now need lock until setting vdev status as used */ + + /* begin a lock */ + spin_lock(&the_controller->lock); + vdev = port_to_vdev(rhport); + spin_lock(&vdev->ud.lock); + + if (vdev->ud.status != VDEV_ST_NULL) { + /* end of the lock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + sockfd_put(socket); + + dev_err(dev, "port %d already used\n", rhport); + return -EINVAL; + } + + dev_info(dev, + "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", + rhport, sockfd, devid, speed, usb_speed_string(speed)); + + vdev->devid = devid; + vdev->speed = speed; + vdev->ud.tcp_socket = socket; + vdev->ud.status = VDEV_ST_NOTASSIGNED; + + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + /* end the lock */ + + vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); + vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); + + rh_port_connect(rhport, speed); + + return count; +} +static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); + +static struct attribute *dev_attrs[] = { + &dev_attr_status.attr, + &dev_attr_detach.attr, + &dev_attr_attach.attr, + &dev_attr_usbip_debug.attr, + NULL, +}; + +const struct attribute_group dev_attr_group = { + .attrs = dev_attrs, +}; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c new file mode 100644 index 000000000000..409fd99f3257 --- /dev/null +++ b/drivers/usb/usbip/vhci_tx.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This 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 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include + +#include "usbip_common.h" +#include "vhci.h" + +static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) +{ + struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); + struct vhci_device *vdev = priv->vdev; + + usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", + usb_pipedevice(urb->pipe), vdev->devid); + + pdup->base.command = USBIP_CMD_SUBMIT; + pdup->base.seqnum = priv->seqnum; + pdup->base.devid = vdev->devid; + pdup->base.direction = usb_pipein(urb->pipe) ? + USBIP_DIR_IN : USBIP_DIR_OUT; + pdup->base.ep = usb_pipeendpoint(urb->pipe); + + usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); + + if (urb->setup_packet) + memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); +} + +static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) +{ + struct vhci_priv *priv, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { + list_move_tail(&priv->list, &vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + return priv; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_submit(struct vhci_device *vdev) +{ + struct vhci_priv *priv = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + + /* 1. setup usbip_header */ + setup_cmd_submit_pdu(&pdu_header, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { + iov[1].iov_base = urb->transfer_buffer; + iov[1].iov_len = urb->transfer_buffer_length; + txsize += urb->transfer_buffer_length; + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&vdev->ud, + SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iov[2].iov_base = iso_buffer; + iov[2].iov_len = len; + txsize += len; + } + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + kfree(iso_buffer); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iso_buffer); + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + list_move_tail(&unlink->list, &vdev->unlink_rx); + spin_unlock(&vdev->priv_lock); + return unlink; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_unlink(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + pdu_header.base.command = USBIP_CMD_UNLINK; + pdu_header.base.seqnum = unlink->seqnum; + pdu_header.base.devid = vdev->devid; + pdu_header.base.ep = 0; + pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; + + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +int vhci_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + while (!kthread_should_stop()) { + if (vhci_send_cmd_submit(vdev) < 0) + break; + + if (vhci_send_cmd_unlink(vdev) < 0) + break; + + wait_event_interruptible(vdev->waitq_tx, + (!list_empty(&vdev->priv_tx) || + !list_empty(&vdev->unlink_tx) || + kthread_should_stop())); + + usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); + } + + return 0; +} diff --git a/include/uapi/linux/usbip.h b/include/uapi/linux/usbip.h new file mode 100644 index 000000000000..fa5db30ede36 --- /dev/null +++ b/include/uapi/linux/usbip.h @@ -0,0 +1,26 @@ +/* + * usbip.h + * + * USBIP uapi defines and function prototypes etc. +*/ + +#ifndef _UAPI_LINUX_USBIP_H +#define _UAPI_LINUX_USBIP_H + +/* usbip device status - exported in usbip device sysfs status */ +enum usbip_device_status { + /* sdev is available. */ + SDEV_ST_AVAILABLE = 0x01, + /* sdev is now used. */ + SDEV_ST_USED, + /* sdev is unusable because of a fatal error. */ + SDEV_ST_ERROR, + + /* vdev does not connect a remote device. */ + VDEV_ST_NULL, + /* vdev is used, but the USB address is not assigned yet */ + VDEV_ST_NOTASSIGNED, + VDEV_ST_USED, + VDEV_ST_ERROR +}; +#endif /* _UAPI_LINUX_USBIP_H */ -- cgit v1.2.3 From f153566570fb9e32c2f59182883f4f66048788fb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 22 Aug 2014 21:48:00 +0100 Subject: iio:trigger: modify return value for iio_trigger_get Instead of a void function, return the trigger pointer. Whilst not in of itself a fix, this makes the following set of 7 fixes cleaner than they would otherwise be. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron Cc: Stable@vger.kernel.org --- include/linux/iio/trigger.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 4b79ffe7b188..fa76c79a52a1 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -84,10 +84,12 @@ static inline void iio_trigger_put(struct iio_trigger *trig) put_device(&trig->dev); } -static inline void iio_trigger_get(struct iio_trigger *trig) +static inline struct iio_trigger *iio_trigger_get(struct iio_trigger *trig) { get_device(&trig->dev); __module_get(trig->ops->owner); + + return trig; } /** -- cgit v1.2.3 From 7d5929c1f34304ca5a970cfde8044053e56aa8c9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 25 Aug 2014 16:15:32 -0700 Subject: mtd: nand: omap: Revert to using software ECC by default For v3.12 and prior, 1-bit Hamming code ECC via software was the default choice. Commit c66d039197e4 in v3.13 changed the behaviour to use 1-bit Hamming code via Hardware using a different ECC layout i.e. (ROM code layout) than what is used by software ECC. This ECC layout change causes NAND filesystems created in v3.12 and prior to be unusable in v3.13 and later. So revert back to using software ECC by default if an ECC scheme is not explicitely specified. This defect can be observed on the following boards during legacy boot -omap3beagle -omap3touchbook -overo -am3517crane -devkit8000 -ldp -3430sdp Signed-off-by: Roger Quadros Tested-by: Grazvydas Ignotas Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-flash.c | 2 +- arch/arm/mach-omap2/gpmc-nand.c | 3 ++- drivers/mtd/nand/omap2.c | 14 +++++++++++--- include/linux/platform_data/mtd-nand-omap2.h | 13 +++++++++++-- 4 files changed, 25 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c index e87f2a83d6bf..2d245c2e641c 100644 --- a/arch/arm/mach-omap2/board-flash.c +++ b/arch/arm/mach-omap2/board-flash.c @@ -142,7 +142,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, u8 nr_parts, u8 cs, board_nand_data.nr_parts = nr_parts; board_nand_data.devsize = nand_type; - board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_HW; + board_nand_data.ecc_opt = OMAP_ECC_HAM1_CODE_SW; gpmc_nand_init(&board_nand_data, gpmc_t); } #endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */ diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index 8897ad7035fd..cb7764314f17 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -49,7 +49,8 @@ static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt) return 0; /* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */ - if (ecc_opt == OMAP_ECC_HAM1_CODE_HW) + if (ecc_opt == OMAP_ECC_HAM1_CODE_HW || + ecc_opt == OMAP_ECC_HAM1_CODE_SW) return 1; else return 0; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index f0ed92e210a1..4dd617897eee 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1794,9 +1794,12 @@ static int omap_nand_probe(struct platform_device *pdev) } /* populate MTD interface based on ECC scheme */ - nand_chip->ecc.layout = &omap_oobinfo; ecclayout = &omap_oobinfo; switch (info->ecc_opt) { + case OMAP_ECC_HAM1_CODE_SW: + nand_chip->ecc.mode = NAND_ECC_SOFT; + break; + case OMAP_ECC_HAM1_CODE_HW: pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); nand_chip->ecc.mode = NAND_ECC_HW; @@ -1848,7 +1851,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.priv = nand_bch_init(mtd, nand_chip->ecc.size, nand_chip->ecc.bytes, - &nand_chip->ecc.layout); + &ecclayout); if (!nand_chip->ecc.priv) { pr_err("nand: error: unable to use s/w BCH library\n"); err = -EINVAL; @@ -1923,7 +1926,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.priv = nand_bch_init(mtd, nand_chip->ecc.size, nand_chip->ecc.bytes, - &nand_chip->ecc.layout); + &ecclayout); if (!nand_chip->ecc.priv) { pr_err("nand: error: unable to use s/w BCH library\n"); err = -EINVAL; @@ -2012,6 +2015,9 @@ static int omap_nand_probe(struct platform_device *pdev) goto return_error; } + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) + goto scan_tail; + /* all OOB bytes from oobfree->offset till end off OOB are free */ ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; /* check if NAND device's OOB is enough to store ECC signatures */ @@ -2021,7 +2027,9 @@ static int omap_nand_probe(struct platform_device *pdev) err = -EINVAL; goto return_error; } + nand_chip->ecc.layout = ecclayout; +scan_tail: /* second phase scan */ if (nand_scan_tail(mtd)) { err = -ENXIO; diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 660c029d694f..16ec262dfcc8 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -21,8 +21,17 @@ enum nand_io { }; enum omap_ecc { - /* 1-bit ECC calculation by GPMC, Error detection by Software */ - OMAP_ECC_HAM1_CODE_HW = 0, + /* + * 1-bit ECC: calculation and correction by SW + * ECC stored at end of spare area + */ + OMAP_ECC_HAM1_CODE_SW = 0, + + /* + * 1-bit ECC: calculation by GPMC, Error detection by Software + * ECC layout compatible with ROM code layout + */ + OMAP_ECC_HAM1_CODE_HW, /* 4-bit ECC calculation by GPMC, Error detection by Software */ OMAP_ECC_BCH4_CODE_HW_DETECTION_SW, /* 4-bit ECC calculation by GPMC, Error detection by ELM */ -- cgit v1.2.3 From 1f58d9465c568eb47cab939bbc4f30ae51863295 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Aug 2014 13:06:30 +0200 Subject: dma-buf/fence: Fix one more kerneldoc warning The seqno_fence_init() function's cond argument isn't described in the kerneldoc comment. Fix that to silence a warning when building DocBook documentation. Signed-off-by: Thierry Reding Signed-off-by: Sumit Semwal --- include/linux/seqno-fence.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/seqno-fence.h b/include/linux/seqno-fence.h index 3d6003de4b0d..a1ba6a5ccdd6 100644 --- a/include/linux/seqno-fence.h +++ b/include/linux/seqno-fence.h @@ -62,6 +62,7 @@ to_seqno_fence(struct fence *fence) * @context: the execution context this fence is a part of * @seqno_ofs: the offset within @sync_buf * @seqno: the sequence # to signal on + * @cond: fence wait condition * @ops: the fence_ops for operations on this seqno fence * * This function initializes a struct seqno_fence with passed parameters, -- cgit v1.2.3 From a8dbfeedfe47a19a4712749eb2444b1d7ea1150e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 27 Aug 2014 14:31:24 -0700 Subject: regulator: fix kernel-doc warnings in header files Fix kernel-doc warnings in regulator header files: Warning(..//include/linux/regulator/machine.h:140): No description found for parameter 'ramp_disable' Warning(..//include/linux/regulator/driver.h:279): No description found for parameter 'linear_ranges' Warning(..//include/linux/regulator/driver.h:279): No description found for parameter 'n_linear_ranges' Signed-off-by: Randy Dunlap Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 2 ++ include/linux/regulator/machine.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bbe03a1924c0..4efa1ed8a2b0 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -218,6 +218,8 @@ enum regulator_type { * @linear_min_sel: Minimal selector for starting linear mapping * @fixed_uV: Fixed voltage of rails. * @ramp_delay: Time to settle down after voltage change (unit: uV/us) + * @linear_ranges: A constant table of possible voltage ranges. + * @n_linear_ranges: Number of entries in the @linear_ranges table. * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 730e638c5589..0b08d05d470b 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -85,6 +85,7 @@ struct regulator_state { * bootloader then it will be enabled when the constraints are * applied. * @apply_uV: Apply the voltage constraint when initialising. + * @ramp_disable: Disable ramp delay when initialising or when setting voltage. * * @input_uV: Input voltage for regulator when supplied by another regulator. * -- cgit v1.2.3 From db9ee220361de03ee86388f9ea5e529eaad5323c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 27 Aug 2014 18:40:07 -0400 Subject: jbd2: fix descriptor block size handling errors with journal_csum It turns out that there are some serious problems with the on-disk format of journal checksum v2. The foremost is that the function to calculate descriptor tag size returns sizes that are too big. This causes alignment issues on some architectures and is compounded by the fact that some parts of jbd2 use the structure size (incorrectly) to determine the presence of a 64bit journal instead of checking the feature flags. Therefore, introduce journal checksum v3, which enlarges the descriptor block tag format to allow for full 32-bit checksums of journal blocks, fix the journal tag function to return the correct sizes, and fix the jbd2 recovery code to use feature flags to determine 64bitness. Add a few function helpers so we don't have to open-code quite so many pieces. Switching to a 16-byte block size was found to increase journal size overhead by a maximum of 0.1%, to convert a 32-bit journal with no checksumming to a 32-bit journal with checksum v3 enabled. Signed-off-by: Darrick J. Wong Reported-by: TR Reardon Signed-off-by: Theodore Ts'o Cc: stable@vger.kernel.org --- fs/ext4/super.c | 5 +++-- fs/jbd2/commit.c | 21 +++++++++++--------- fs/jbd2/journal.c | 56 ++++++++++++++++++++++++++++++++++------------------ fs/jbd2/recovery.c | 26 +++++++++++++----------- fs/jbd2/revoke.c | 6 +++--- include/linux/jbd2.h | 30 +++++++++++++++++++++++----- 6 files changed, 95 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 32b43ad154b9..0b28b36e7915 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3181,9 +3181,9 @@ static int set_journal_csum_feature_set(struct super_block *sb) if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { - /* journal checksum v2 */ + /* journal checksum v3 */ compat = 0; - incompat = JBD2_FEATURE_INCOMPAT_CSUM_V2; + incompat = JBD2_FEATURE_INCOMPAT_CSUM_V3; } else { /* journal checksum v1 */ compat = JBD2_FEATURE_COMPAT_CHECKSUM; @@ -3205,6 +3205,7 @@ static int set_journal_csum_feature_set(struct super_block *sb) jbd2_journal_clear_features(sbi->s_journal, JBD2_FEATURE_COMPAT_CHECKSUM, 0, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT | + JBD2_FEATURE_INCOMPAT_CSUM_V3 | JBD2_FEATURE_INCOMPAT_CSUM_V2); } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 6fac74349856..b73e0215baa7 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -97,7 +97,7 @@ static void jbd2_commit_block_csum_set(journal_t *j, struct buffer_head *bh) struct commit_header *h; __u32 csum; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return; h = (struct commit_header *)(bh->b_data); @@ -313,11 +313,11 @@ static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh) return checksum; } -static void write_tag_block(int tag_bytes, journal_block_tag_t *tag, +static void write_tag_block(journal_t *j, journal_block_tag_t *tag, unsigned long long block) { tag->t_blocknr = cpu_to_be32(block & (u32)~0); - if (tag_bytes > JBD2_TAG_SIZE32) + if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_64BIT)) tag->t_blocknr_high = cpu_to_be32((block >> 31) >> 1); } @@ -327,7 +327,7 @@ static void jbd2_descr_block_csum_set(journal_t *j, struct jbd2_journal_block_tail *tail; __u32 csum; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return; tail = (struct jbd2_journal_block_tail *)(bh->b_data + j->j_blocksize - @@ -340,12 +340,13 @@ static void jbd2_descr_block_csum_set(journal_t *j, static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, struct buffer_head *bh, __u32 sequence) { + journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; struct page *page = bh->b_page; __u8 *addr; __u32 csum32; __be32 seq; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return; seq = cpu_to_be32(sequence); @@ -355,8 +356,10 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, bh->b_size); kunmap_atomic(addr); - /* We only have space to store the lower 16 bits of the crc32c. */ - tag->t_checksum = cpu_to_be16(csum32); + if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V3)) + tag3->t_checksum = cpu_to_be32(csum32); + else + tag->t_checksum = cpu_to_be16(csum32); } /* * jbd2_journal_commit_transaction @@ -396,7 +399,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) LIST_HEAD(io_bufs); LIST_HEAD(log_bufs); - if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) csum_size = sizeof(struct jbd2_journal_block_tail); /* @@ -690,7 +693,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) tag_flag |= JBD2_FLAG_SAME_UUID; tag = (journal_block_tag_t *) tagp; - write_tag_block(tag_bytes, tag, jh2bh(jh)->b_blocknr); + write_tag_block(journal, tag, jh2bh(jh)->b_blocknr); tag->t_flags = cpu_to_be16(tag_flag); jbd2_block_tag_csum_set(journal, tag, wbuf[bufs], commit_transaction->t_tid); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 67b8e303946c..19d74d86d99c 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -124,7 +124,7 @@ EXPORT_SYMBOL(__jbd2_debug); /* Checksumming functions */ static int jbd2_verify_csum_type(journal_t *j, journal_superblock_t *sb) { - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; return sb->s_checksum_type == JBD2_CRC32C_CHKSUM; @@ -145,7 +145,7 @@ static __be32 jbd2_superblock_csum(journal_t *j, journal_superblock_t *sb) static int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb) { - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; return sb->s_checksum == jbd2_superblock_csum(j, sb); @@ -153,7 +153,7 @@ static int jbd2_superblock_csum_verify(journal_t *j, journal_superblock_t *sb) static void jbd2_superblock_csum_set(journal_t *j, journal_superblock_t *sb) { - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return; sb->s_checksum = jbd2_superblock_csum(j, sb); @@ -1522,21 +1522,29 @@ static int journal_get_superblock(journal_t *journal) goto out; } - if (JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM) && - JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) { + if (jbd2_journal_has_csum_v2or3(journal) && + JBD2_HAS_COMPAT_FEATURE(journal, JBD2_FEATURE_COMPAT_CHECKSUM)) { /* Can't have checksum v1 and v2 on at the same time! */ printk(KERN_ERR "JBD2: Can't enable checksumming v1 and v2 " "at the same time!\n"); goto out; } + if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2) && + JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3)) { + /* Can't have checksum v2 and v3 at the same time! */ + printk(KERN_ERR "JBD2: Can't enable checksumming v2 and v3 " + "at the same time!\n"); + goto out; + } + if (!jbd2_verify_csum_type(journal, sb)) { printk(KERN_ERR "JBD2: Unknown checksum type\n"); goto out; } /* Load the checksum driver */ - if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) { + if (jbd2_journal_has_csum_v2or3(journal)) { journal->j_chksum_driver = crypto_alloc_shash("crc32c", 0, 0); if (IS_ERR(journal->j_chksum_driver)) { printk(KERN_ERR "JBD2: Cannot load crc32c driver.\n"); @@ -1553,7 +1561,7 @@ static int journal_get_superblock(journal_t *journal) } /* Precompute checksum seed for all metadata */ - if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, sizeof(sb->s_uuid)); @@ -1813,8 +1821,14 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, if (!jbd2_journal_check_available_features(journal, compat, ro, incompat)) return 0; - /* Asking for checksumming v2 and v1? Only give them v2. */ - if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2 && + /* If enabling v2 checksums, turn on v3 instead */ + if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V2) { + incompat &= ~JBD2_FEATURE_INCOMPAT_CSUM_V2; + incompat |= JBD2_FEATURE_INCOMPAT_CSUM_V3; + } + + /* Asking for checksumming v3 and v1? Only give them v3. */ + if (incompat & JBD2_FEATURE_INCOMPAT_CSUM_V3 && compat & JBD2_FEATURE_COMPAT_CHECKSUM) compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM; @@ -1823,8 +1837,8 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, sb = journal->j_superblock; - /* If enabling v2 checksums, update superblock */ - if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V2)) { + /* If enabling v3 checksums, update superblock */ + if (INCOMPAT_FEATURE_ON(JBD2_FEATURE_INCOMPAT_CSUM_V3)) { sb->s_checksum_type = JBD2_CRC32C_CHKSUM; sb->s_feature_compat &= ~cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM); @@ -1842,8 +1856,7 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, } /* Precompute checksum seed for all metadata */ - if (JBD2_HAS_INCOMPAT_FEATURE(journal, - JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) journal->j_csum_seed = jbd2_chksum(journal, ~0, sb->s_uuid, sizeof(sb->s_uuid)); @@ -1852,7 +1865,8 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat, /* If enabling v1 checksums, downgrade superblock */ if (COMPAT_FEATURE_ON(JBD2_FEATURE_COMPAT_CHECKSUM)) sb->s_feature_incompat &= - ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2); + ~cpu_to_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2 | + JBD2_FEATURE_INCOMPAT_CSUM_V3); sb->s_feature_compat |= cpu_to_be32(compat); sb->s_feature_ro_compat |= cpu_to_be32(ro); @@ -2165,16 +2179,20 @@ int jbd2_journal_blocks_per_page(struct inode *inode) */ size_t journal_tag_bytes(journal_t *journal) { - journal_block_tag_t tag; - size_t x = 0; + size_t sz; + + if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3)) + return sizeof(journal_block_tag3_t); + + sz = sizeof(journal_block_tag_t); if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) - x += sizeof(tag.t_checksum); + sz += sizeof(__u16); if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) - return x + JBD2_TAG_SIZE64; + return sz; else - return x + JBD2_TAG_SIZE32; + return sz - sizeof(__u32); } /* diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 00e9703d7dc6..9b329b55ffe3 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -181,7 +181,7 @@ static int jbd2_descr_block_csum_verify(journal_t *j, __be32 provided; __u32 calculated; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; tail = (struct jbd2_journal_block_tail *)(buf + j->j_blocksize - @@ -205,7 +205,7 @@ static int count_tags(journal_t *journal, struct buffer_head *bh) int nr = 0, size = journal->j_blocksize; int tag_bytes = journal_tag_bytes(journal); - if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) size -= sizeof(struct jbd2_journal_block_tail); tagp = &bh->b_data[sizeof(journal_header_t)]; @@ -338,10 +338,11 @@ int jbd2_journal_skip_recovery(journal_t *journal) return err; } -static inline unsigned long long read_tag_block(int tag_bytes, journal_block_tag_t *tag) +static inline unsigned long long read_tag_block(journal_t *journal, + journal_block_tag_t *tag) { unsigned long long block = be32_to_cpu(tag->t_blocknr); - if (tag_bytes > JBD2_TAG_SIZE32) + if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) block |= (u64)be32_to_cpu(tag->t_blocknr_high) << 32; return block; } @@ -384,7 +385,7 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf) __be32 provided; __u32 calculated; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; h = buf; @@ -399,17 +400,21 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf) static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag, void *buf, __u32 sequence) { + journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; __u32 csum32; __be32 seq; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; seq = cpu_to_be32(sequence); csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); csum32 = jbd2_chksum(j, csum32, buf, j->j_blocksize); - return tag->t_checksum == cpu_to_be16(csum32); + if (JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V3)) + return tag3->t_checksum == cpu_to_be32(csum32); + else + return tag->t_checksum == cpu_to_be16(csum32); } static int do_one_pass(journal_t *journal, @@ -513,8 +518,7 @@ static int do_one_pass(journal_t *journal, switch(blocktype) { case JBD2_DESCRIPTOR_BLOCK: /* Verify checksum first */ - if (JBD2_HAS_INCOMPAT_FEATURE(journal, - JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) descr_csum_size = sizeof(struct jbd2_journal_block_tail); if (descr_csum_size > 0 && @@ -575,7 +579,7 @@ static int do_one_pass(journal_t *journal, unsigned long long blocknr; J_ASSERT(obh != NULL); - blocknr = read_tag_block(tag_bytes, + blocknr = read_tag_block(journal, tag); /* If the block has been @@ -814,7 +818,7 @@ static int jbd2_revoke_block_csum_verify(journal_t *j, __be32 provided; __u32 calculated; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return 1; tail = (struct jbd2_journal_revoke_tail *)(buf + j->j_blocksize - diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 198c9c10276d..d5e95a175c92 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -91,8 +91,8 @@ #include #include #include -#endif #include +#endif static struct kmem_cache *jbd2_revoke_record_cache; static struct kmem_cache *jbd2_revoke_table_cache; @@ -597,7 +597,7 @@ static void write_one_revoke_record(journal_t *journal, offset = *offsetp; /* Do we need to leave space at the end for a checksum? */ - if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (jbd2_journal_has_csum_v2or3(journal)) csum_size = sizeof(struct jbd2_journal_revoke_tail); /* Make sure we have a descriptor with space left for the record */ @@ -644,7 +644,7 @@ static void jbd2_revoke_csum_set(journal_t *j, struct buffer_head *bh) struct jbd2_journal_revoke_tail *tail; __u32 csum; - if (!JBD2_HAS_INCOMPAT_FEATURE(j, JBD2_FEATURE_INCOMPAT_CSUM_V2)) + if (!jbd2_journal_has_csum_v2or3(j)) return; tail = (struct jbd2_journal_revoke_tail *)(bh->b_data + j->j_blocksize - diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index d5b50a19463c..0dae71e9971c 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -159,7 +159,11 @@ typedef struct journal_header_s * journal_block_tag (in the descriptor). The other h_chksum* fields are * not used. * - * Checksum v1 and v2 are mutually exclusive features. + * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses + * journal_block_tag3_t to store a full 32-bit checksum. Everything else + * is the same as v2. + * + * Checksum v1, v2, and v3 are mutually exclusive features. */ struct commit_header { __be32 h_magic; @@ -179,6 +183,14 @@ struct commit_header { * raw struct shouldn't be used for pointer math or sizeof() - use * journal_tag_bytes(journal) instead to compute this. */ +typedef struct journal_block_tag3_s +{ + __be32 t_blocknr; /* The on-disk block number */ + __be32 t_flags; /* See below */ + __be32 t_blocknr_high; /* most-significant high 32bits. */ + __be32 t_checksum; /* crc32c(uuid+seq+block) */ +} journal_block_tag3_t; + typedef struct journal_block_tag_s { __be32 t_blocknr; /* The on-disk block number */ @@ -187,9 +199,6 @@ typedef struct journal_block_tag_s __be32 t_blocknr_high; /* most-significant high 32bits. */ } journal_block_tag_t; -#define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high)) -#define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t)) - /* Tail of descriptor block, for checksumming */ struct jbd2_journal_block_tail { __be32 t_checksum; /* crc32c(uuid+descr_block) */ @@ -284,6 +293,7 @@ typedef struct journal_superblock_s #define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002 #define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 #define JBD2_FEATURE_INCOMPAT_CSUM_V2 0x00000008 +#define JBD2_FEATURE_INCOMPAT_CSUM_V3 0x00000010 /* Features known to this kernel version: */ #define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM @@ -291,7 +301,8 @@ typedef struct journal_superblock_s #define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE | \ JBD2_FEATURE_INCOMPAT_64BIT | \ JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT | \ - JBD2_FEATURE_INCOMPAT_CSUM_V2) + JBD2_FEATURE_INCOMPAT_CSUM_V2 | \ + JBD2_FEATURE_INCOMPAT_CSUM_V3) #ifdef __KERNEL__ @@ -1296,6 +1307,15 @@ static inline int tid_geq(tid_t x, tid_t y) extern int jbd2_journal_blocks_per_page(struct inode *inode); extern size_t journal_tag_bytes(journal_t *journal); +static inline int jbd2_journal_has_csum_v2or3(journal_t *journal) +{ + if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V2) || + JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_CSUM_V3)) + return 1; + + return 0; +} + /* * We reserve t_outstanding_credits >> JBD2_CONTROL_BLOCKS_SHIFT for * transaction control blocks. -- cgit v1.2.3 From bfcfd44cce2774f19daeb59fb4e43fc9aa80e7b8 Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Fri, 29 Aug 2014 15:18:51 -0700 Subject: xattr: fix check for simultaneous glibc header inclusion The guard was introduced in commit ea1a8217b06b ("xattr: guard against simultaneous glibc header inclusion") but it is using #ifdef to check for a define that is either set to 1 or 0. Fix it to use #if instead. * Without this patch: $ { echo "#include "; echo "#include "; } | gcc -E -Iinclude/uapi - >/dev/null include/uapi/linux/xattr.h:19:0: warning: "XATTR_CREATE" redefined [enabled by default] #define XATTR_CREATE 0x1 /* set value, fail if attr already exists */ ^ /usr/include/x86_64-linux-gnu/sys/xattr.h:32:0: note: this is the location of the previous definition #define XATTR_CREATE XATTR_CREATE ^ * With this patch: $ { echo "#include "; echo "#include "; } | gcc -E -Iinclude/uapi - >/dev/null (no warnings) Signed-off-by: Filipe Brandenburger Acked-by: Serge E. Hallyn Cc: Allan McRae Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/xattr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h index c38355c1f3c9..1590c49cae57 100644 --- a/include/uapi/linux/xattr.h +++ b/include/uapi/linux/xattr.h @@ -13,7 +13,7 @@ #ifndef _UAPI_LINUX_XATTR_H #define _UAPI_LINUX_XATTR_H -#ifdef __UAPI_DEF_XATTR +#if __UAPI_DEF_XATTR #define __USE_KERNEL_XATTR_DEFS #define XATTR_CREATE 0x1 /* set value, fail if attr already exists */ -- cgit v1.2.3 From b95089d00c04712a9d4655d5c638930ac24b7bd3 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 27 Aug 2014 16:47:48 +0300 Subject: net/mlx4: Move the tunnel steering helper function to mlx4_core Move the function which we use to set VXLAN DMFS (flow-steering) rules from mlx4_en to mlx4_core. This refactoring will allow the mlx4_ib driver to call the helper for the use case of user-space RAW Ethernet QPs, such that they can serve VXLAN traffic too. Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 31 ++------------------- drivers/net/ethernet/mellanox/mlx4/mcg.c | 38 ++++++++++++++++++++++++++ include/linux/mlx4/device.h | 3 ++ 3 files changed, 43 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index bb536aa613f4..abddcf8c40aa 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -474,39 +474,12 @@ static int mlx4_en_tunnel_steer_add(struct mlx4_en_priv *priv, unsigned char *ad int qpn, u64 *reg_id) { int err; - struct mlx4_spec_list spec_eth_outer = { {NULL} }; - struct mlx4_spec_list spec_vxlan = { {NULL} }; - struct mlx4_spec_list spec_eth_inner = { {NULL} }; - - struct mlx4_net_trans_rule rule = { - .queue_mode = MLX4_NET_TRANS_Q_FIFO, - .exclusive = 0, - .allow_loopback = 1, - .promisc_mode = MLX4_FS_REGULAR, - .priority = MLX4_DOMAIN_NIC, - }; - - __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) return 0; /* do nothing */ - rule.port = priv->port; - rule.qpn = qpn; - INIT_LIST_HEAD(&rule.list); - - spec_eth_outer.id = MLX4_NET_TRANS_RULE_ID_ETH; - memcpy(spec_eth_outer.eth.dst_mac, addr, ETH_ALEN); - memcpy(spec_eth_outer.eth.dst_mac_msk, &mac_mask, ETH_ALEN); - - spec_vxlan.id = MLX4_NET_TRANS_RULE_ID_VXLAN; /* any vxlan header */ - spec_eth_inner.id = MLX4_NET_TRANS_RULE_ID_ETH; /* any inner eth header */ - - list_add_tail(&spec_eth_outer.list, &rule.list); - list_add_tail(&spec_vxlan.list, &rule.list); - list_add_tail(&spec_eth_inner.list, &rule.list); - - err = mlx4_flow_attach(priv->mdev->dev, &rule, reg_id); + err = mlx4_tunnel_steer_add(priv->mdev->dev, addr, priv->port, qpn, + MLX4_DOMAIN_NIC, reg_id); if (err) { en_err(priv, "failed to add vxlan steering rule, err %d\n", err); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index d80e7a6fac74..ca0f98c95105 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1020,6 +1020,44 @@ int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id) } EXPORT_SYMBOL_GPL(mlx4_flow_detach); +int mlx4_tunnel_steer_add(struct mlx4_dev *dev, unsigned char *addr, + int port, int qpn, u16 prio, u64 *reg_id) +{ + int err; + struct mlx4_spec_list spec_eth_outer = { {NULL} }; + struct mlx4_spec_list spec_vxlan = { {NULL} }; + struct mlx4_spec_list spec_eth_inner = { {NULL} }; + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .allow_loopback = 1, + .promisc_mode = MLX4_FS_REGULAR, + }; + + __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + + rule.port = port; + rule.qpn = qpn; + rule.priority = prio; + INIT_LIST_HEAD(&rule.list); + + spec_eth_outer.id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec_eth_outer.eth.dst_mac, addr, ETH_ALEN); + memcpy(spec_eth_outer.eth.dst_mac_msk, &mac_mask, ETH_ALEN); + + spec_vxlan.id = MLX4_NET_TRANS_RULE_ID_VXLAN; /* any vxlan header */ + spec_eth_inner.id = MLX4_NET_TRANS_RULE_ID_ETH; /* any inner eth header */ + + list_add_tail(&spec_eth_outer.list, &rule.list); + list_add_tail(&spec_vxlan.list, &rule.list); + list_add_tail(&spec_eth_inner.list, &rule.list); + + err = mlx4_flow_attach(dev, &rule, reg_id); + return err; +} +EXPORT_SYMBOL(mlx4_tunnel_steer_add); + int mlx4_FLOW_STEERING_IB_UC_QP_RANGE(struct mlx4_dev *dev, u32 min_range_qpn, u32 max_range_qpn) { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 071f6b234604..511c6e0d21a9 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -1196,6 +1196,9 @@ int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev, enum mlx4_net_trans_rule_id id); int mlx4_hw_rule_sz(struct mlx4_dev *dev, enum mlx4_net_trans_rule_id id); +int mlx4_tunnel_steer_add(struct mlx4_dev *dev, unsigned char *addr, + int port, int qpn, u16 prio, u64 *reg_id); + void mlx4_sync_pkey_table(struct mlx4_dev *dev, int slave, int port, int i, int val); -- cgit v1.2.3 From 38ab1fa981d543e1b00f4ffbce4ddb480cd2effe Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 28 Aug 2014 15:28:26 +0200 Subject: net: sctp: fix ABI mismatch through sctp_assoc_to_state helper Since SCTP day 1, that is, 19b55a2af145 ("Initial commit") from lksctp tree, the official header carries a copy of enum sctp_sstat_state that looks like (compared to the current in-kernel enumeration): User definition: Kernel definition: enum sctp_sstat_state { typedef enum { SCTP_EMPTY = 0, SCTP_CLOSED = 1, SCTP_STATE_CLOSED = 0, SCTP_COOKIE_WAIT = 2, SCTP_STATE_COOKIE_WAIT = 1, SCTP_COOKIE_ECHOED = 3, SCTP_STATE_COOKIE_ECHOED = 2, SCTP_ESTABLISHED = 4, SCTP_STATE_ESTABLISHED = 3, SCTP_SHUTDOWN_PENDING = 5, SCTP_STATE_SHUTDOWN_PENDING = 4, SCTP_SHUTDOWN_SENT = 6, SCTP_STATE_SHUTDOWN_SENT = 5, SCTP_SHUTDOWN_RECEIVED = 7, SCTP_STATE_SHUTDOWN_RECEIVED = 6, SCTP_SHUTDOWN_ACK_SENT = 8, SCTP_STATE_SHUTDOWN_ACK_SENT = 7, }; } sctp_state_t; This header was later on also placed into the uapi, so that user space programs can compile without having , but the shipped with instead. While RFC6458 under 8.2.1.Association Status (SCTP_STATUS) says that sstat_state can range from SCTP_CLOSED to SCTP_SHUTDOWN_ACK_SENT, we nevertheless have a what it appears to be dummy SCTP_EMPTY state from the very early days. While it seems to do just nothing, commit 0b8f9e25b0aa ("sctp: remove completely unsed EMPTY state") did the right thing and removed this dead code. That however, causes an off-by-one when the user asks the SCTP stack via SCTP_STATUS API and checks for the current socket state thus yielding possibly undefined behaviour in applications as they expect the kernel to tell the right thing. The enumeration had to be changed however as based on the current socket state, we access a function pointer lookup-table through this. Therefore, I think the best way to deal with this is just to add a helper function sctp_assoc_to_state() to encapsulate the off-by-one quirk. Reported-by: Tristan Su Fixes: 0b8f9e25b0aa ("sctp: remove completely unsed EMPTY state") Signed-off-by: Daniel Borkmann Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 13 +++++++++++++ net/sctp/socket.c | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index f6e7397e799d..9fbd856e6713 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -320,6 +320,19 @@ static inline sctp_assoc_t sctp_assoc2id(const struct sctp_association *asoc) return asoc ? asoc->assoc_id : 0; } +static inline enum sctp_sstat_state +sctp_assoc_to_state(const struct sctp_association *asoc) +{ + /* SCTP's uapi always had SCTP_EMPTY(=0) as a dummy state, but we + * got rid of it in kernel space. Therefore SCTP_CLOSED et al + * start at =1 in user space, but actually as =0 in kernel space. + * Now that we can not break user space and SCTP_EMPTY is exposed + * there, we need to fix it up with an ugly offset not to break + * applications. :( + */ + return asoc->state + 1; +} + /* Look up the association by its id. */ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index eb71d49e7653..634a2abb5f3a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4243,7 +4243,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, transport = asoc->peer.primary_path; status.sstat_assoc_id = sctp_assoc2id(asoc); - status.sstat_state = asoc->state; + status.sstat_state = sctp_assoc_to_state(asoc); status.sstat_rwnd = asoc->peer.rwnd; status.sstat_unackdata = asoc->unack_data; -- cgit v1.2.3 From 0dbc8b7afef6e4fddcfebcbacbeb269a0a3b06d5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 1 Sep 2014 15:15:40 +0200 Subject: gpio: move varargs hack outside #ifdef GPIOLIB commit 39b2bbe3d715cf5013b5c48695ccdd25bd3bf120 "gpio: add flags argument to gpiod_get*() functions" added a dynamic flags argument to all the GPIOD getter functions, however this did not cover the stubs so when people used gpiod stubs to compile out descriptor code, compilation failed. Solve this by: - Also rename all the stub functions __gpiod_* - Moving the vararg hack outside of #ifdef CONFIG_GPIOLIB so these will always be available. Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- include/linux/gpio/consumer.h | 105 ++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index c7e17de732f3..12f146fa6604 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -38,60 +38,32 @@ enum gpiod_flags { struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags); -#define __gpiod_get(dev, con_id, flags, ...) __gpiod_get(dev, con_id, flags) -#define gpiod_get(varargs...) __gpiod_get(varargs, 0) struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags); -#define __gpiod_get_index(dev, con_id, index, flags, ...) \ - __gpiod_get_index(dev, con_id, index, flags) -#define gpiod_get_index(varargs...) __gpiod_get_index(varargs, 0) struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags); -#define __gpiod_get_optional(dev, con_id, flags, ...) \ - __gpiod_get_optional(dev, con_id, flags) -#define gpiod_get_optional(varargs...) __gpiod_get_optional(varargs, 0) struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags); -#define __gpiod_get_index_optional(dev, con_id, index, flags, ...) \ - __gpiod_get_index_optional(dev, con_id, index, flags) -#define gpiod_get_index_optional(varargs...) \ - __gpiod_get_index_optional(varargs, 0) - void gpiod_put(struct gpio_desc *desc); struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags); -#define __devm_gpiod_get(dev, con_id, flags, ...) \ - __devm_gpiod_get(dev, con_id, flags) -#define devm_gpiod_get(varargs...) __devm_gpiod_get(varargs, 0) struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags); -#define __devm_gpiod_get_index(dev, con_id, index, flags, ...) \ - __devm_gpiod_get_index(dev, con_id, index, flags) -#define devm_gpiod_get_index(varargs...) __devm_gpiod_get_index(varargs, 0) struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags); -#define __devm_gpiod_get_optional(dev, con_id, flags, ...) \ - __devm_gpiod_get_optional(dev, con_id, flags) -#define devm_gpiod_get_optional(varargs...) \ - __devm_gpiod_get_optional(varargs, 0) struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags); -#define __devm_gpiod_get_index_optional(dev, con_id, index, flags, ...) \ - __devm_gpiod_get_index_optional(dev, con_id, index, flags) -#define devm_gpiod_get_index_optional(varargs...) \ - __devm_gpiod_get_index_optional(varargs, 0) - void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); int gpiod_get_direction(const struct gpio_desc *desc); @@ -124,27 +96,31 @@ int desc_to_gpio(const struct gpio_desc *desc); #else /* CONFIG_GPIOLIB */ -static inline struct gpio_desc *__must_check gpiod_get(struct device *dev, - const char *con_id) +static inline struct gpio_desc *__must_check __gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } -static inline struct gpio_desc *__must_check gpiod_get_index(struct device *dev, - const char *con_id, - unsigned int idx) +static inline struct gpio_desc *__must_check +__gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } static inline struct gpio_desc *__must_check -gpiod_get_optional(struct device *dev, const char *con_id) +__gpiod_get_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } static inline struct gpio_desc *__must_check -gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index) +__gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index, enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } @@ -157,28 +133,33 @@ static inline void gpiod_put(struct gpio_desc *desc) WARN_ON(1); } -static inline struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, - const char *con_id) +static inline struct gpio_desc *__must_check +__devm_gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } static inline -struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, - const char *con_id, - unsigned int idx) +struct gpio_desc *__must_check +__devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } static inline struct gpio_desc *__must_check -devm_gpiod_get_optional(struct device *dev, const char *con_id) +__devm_gpiod_get_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } static inline struct gpio_desc *__must_check -devm_gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index) +__devm_gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index, enum gpiod_flags flags) { return ERR_PTR(-ENOSYS); } @@ -303,9 +284,43 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) return -EINVAL; } - #endif /* CONFIG_GPIOLIB */ +/* + * Vararg-hacks! This is done to transition the kernel to always pass + * the options flags argument to the below functions. During a transition + * phase these vararg macros make both old-and-newstyle code compile, + * but when all calls to the elder API are removed, these should go away + * and the __gpiod_get() etc functions above be renamed just gpiod_get() + * etc. + */ +#define __gpiod_get(dev, con_id, flags, ...) __gpiod_get(dev, con_id, flags) +#define gpiod_get(varargs...) __gpiod_get(varargs, 0) +#define __gpiod_get_index(dev, con_id, index, flags, ...) \ + __gpiod_get_index(dev, con_id, index, flags) +#define gpiod_get_index(varargs...) __gpiod_get_index(varargs, 0) +#define __gpiod_get_optional(dev, con_id, flags, ...) \ + __gpiod_get_optional(dev, con_id, flags) +#define gpiod_get_optional(varargs...) __gpiod_get_optional(varargs, 0) +#define __gpiod_get_index_optional(dev, con_id, index, flags, ...) \ + __gpiod_get_index_optional(dev, con_id, index, flags) +#define gpiod_get_index_optional(varargs...) \ + __gpiod_get_index_optional(varargs, 0) +#define __devm_gpiod_get(dev, con_id, flags, ...) \ + __devm_gpiod_get(dev, con_id, flags) +#define devm_gpiod_get(varargs...) __devm_gpiod_get(varargs, 0) +#define __devm_gpiod_get_index(dev, con_id, index, flags, ...) \ + __devm_gpiod_get_index(dev, con_id, index, flags) +#define devm_gpiod_get_index(varargs...) __devm_gpiod_get_index(varargs, 0) +#define __devm_gpiod_get_optional(dev, con_id, flags, ...) \ + __devm_gpiod_get_optional(dev, con_id, flags) +#define devm_gpiod_get_optional(varargs...) \ + __devm_gpiod_get_optional(varargs, 0) +#define __devm_gpiod_get_index_optional(dev, con_id, index, flags, ...) \ + __devm_gpiod_get_index_optional(dev, con_id, index, flags) +#define devm_gpiod_get_index_optional(varargs...) \ + __devm_gpiod_get_index_optional(varargs, 0) + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) int gpiod_export(struct gpio_desc *desc, bool direction_may_change); -- cgit v1.2.3 From 9067359faf890b3a18ab38c792d458fba77b32b4 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 2 Sep 2014 02:03:12 -0700 Subject: Revert "leds: convert blink timer to workqueue" This reverts commit 8b37e1bef5a6b60e949e28a4db3006e4b00bd758. It's broken as it changes led_blink_set() in a way that it can now sleep (while synchronously waiting for workqueue to be cancelled). That's a problem, because it's possible that this function gets called from atomic context (tpt_trig_timer() takes a readlock and thus disables preemption). This has been brought up 3 weeks ago already [1] but no proper fix has materialized, and I keep seeing the problem since 3.17-rc1. [1] https://lkml.org/lkml/2014/8/16/128 BUG: sleeping function called from invalid context at kernel/workqueue.c:2650 in_atomic(): 1, irqs_disabled(): 0, pid: 2335, name: wpa_supplicant 5 locks held by wpa_supplicant/2335: #0: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x12/0x20 #1: (&wdev->mtx){+.+.+.}, at: [] cfg80211_mgd_wext_siwessid+0x5c/0x180 [cfg80211] #2: (&local->mtx){+.+.+.}, at: [] ieee80211_prep_connection+0x17a/0x9a0 [mac80211] #3: (&local->chanctx_mtx){+.+.+.}, at: [] ieee80211_vif_use_channel+0x5d/0x2a0 [mac80211] #4: (&trig->leddev_list_lock){.+.+..}, at: [] tpt_trig_timer+0xec/0x170 [mac80211] CPU: 0 PID: 2335 Comm: wpa_supplicant Not tainted 3.17.0-rc3 #1 Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008 ffff8800360b5a50 ffff8800751f76d8 ffffffff8159e97f ffff8800360b5a30 ffff8800751f76e8 ffffffff810739a5 ffff8800751f77b0 ffffffff8106862f ffffffff810685d0 0aa2209200000000 ffff880000000004 ffff8800361c59d0 Call Trace: [] dump_stack+0x4d/0x66 [] __might_sleep+0xe5/0x120 [] flush_work+0x5f/0x270 [] ? mod_delayed_work_on+0x80/0x80 [] ? mark_held_locks+0x6a/0x90 [] ? __cancel_work_timer+0x6f/0x100 [] ? trace_hardirqs_on_caller+0xfd/0x1c0 [] __cancel_work_timer+0x7b/0x100 [] cancel_delayed_work_sync+0xe/0x10 [] led_blink_set+0x1b/0x40 [] tpt_trig_timer+0x110/0x170 [mac80211] [] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211] [] __ieee80211_recalc_idle+0x98/0x140 [mac80211] [] ieee80211_idle_off+0xe/0x10 [mac80211] [] ieee80211_add_chanctx+0x3b/0x220 [mac80211] [] ieee80211_new_chanctx+0x44/0xf0 [mac80211] [] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211] [] ieee80211_prep_connection+0x188/0x9a0 [mac80211] [] ieee80211_mgd_auth+0x256/0x2e0 [mac80211] [] ieee80211_auth+0x13/0x20 [mac80211] [] cfg80211_mlme_auth+0x106/0x270 [cfg80211] [] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211] [] cfg80211_connect+0x3f0/0x540 [cfg80211] [] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211] [] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211] [] ? cfg80211_wext_giwessid+0x50/0x50 [cfg80211] [] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211] [] ioctl_standard_iw_point+0x14c/0x3e0 [] ? trace_hardirqs_on_caller+0xfd/0x1c0 [] ioctl_standard_call+0x8a/0xd0 [] ? ioctl_standard_iw_point+0x3e0/0x3e0 [] wireless_process_ioctl.constprop.10+0xb6/0x100 [] wext_handle_ioctl+0x5d/0xb0 [] dev_ioctl+0x329/0x620 [] ? trace_hardirqs_on_caller+0xfd/0x1c0 [] sock_ioctl+0x142/0x2e0 [] do_vfs_ioctl+0x300/0x520 [] ? sysret_check+0x1b/0x56 [] ? trace_hardirqs_on_caller+0xfd/0x1c0 [] SyS_ioctl+0x81/0xa0 [] system_call_fastpath+0x1a/0x1f wlan0: send auth to 00:0b:6b:3c:8c:e4 (try 1/3) wlan0: authenticated wlan0: associate with 00:0b:6b:3c:8c:e4 (try 1/3) wlan0: RX AssocResp from 00:0b:6b:3c:8c:e4 (capab=0x431 status=0 aid=2) wlan0: associated IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready cfg80211: Calling CRDA for country: NA wlan0: Limiting TX power to 27 (27 - 0) dBm as advertised by 00:0b:6b:3c:8c:e4 ================================= [ INFO: inconsistent lock state ] 3.17.0-rc3 #1 Not tainted --------------------------------- inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes: ((&(&led_cdev->blink_work)->work)){+.?...}, at: [] flush_work+0x0/0x270 {SOFTIRQ-ON-W} state was registered at: [] __lock_acquire+0x30e/0x1a30 [] lock_acquire+0x91/0x110 [] flush_work+0x38/0x270 [] __cancel_work_timer+0x7b/0x100 [] cancel_delayed_work_sync+0xe/0x10 [] led_blink_set+0x1b/0x40 [] tpt_trig_timer+0x110/0x170 [mac80211] [] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211] [] __ieee80211_recalc_idle+0x98/0x140 [mac80211] [] ieee80211_idle_off+0xe/0x10 [mac80211] [] ieee80211_add_chanctx+0x3b/0x220 [mac80211] [] ieee80211_new_chanctx+0x44/0xf0 [mac80211] [] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211] [] ieee80211_prep_connection+0x188/0x9a0 [mac80211] [] ieee80211_mgd_auth+0x256/0x2e0 [mac80211] [] ieee80211_auth+0x13/0x20 [mac80211] [] cfg80211_mlme_auth+0x106/0x270 [cfg80211] [] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211] [] cfg80211_connect+0x3f0/0x540 [cfg80211] [] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211] [] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211] [] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211] [] ioctl_standard_iw_point+0x14c/0x3e0 [] ioctl_standard_call+0x8a/0xd0 [] wireless_process_ioctl.constprop.10+0xb6/0x100 [] wext_handle_ioctl+0x5d/0xb0 [] dev_ioctl+0x329/0x620 [] sock_ioctl+0x142/0x2e0 [] do_vfs_ioctl+0x300/0x520 [] SyS_ioctl+0x81/0xa0 [] system_call_fastpath+0x1a/0x1f irq event stamp: 493416 hardirqs last enabled at (493416): [] __cancel_work_timer+0x6f/0x100 hardirqs last disabled at (493415): [] try_to_grab_pending+0x1f/0x160 softirqs last enabled at (493408): [] _local_bh_enable+0x1d/0x50 softirqs last disabled at (493409): [] irq_exit+0xa5/0xb0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock((&(&led_cdev->blink_work)->work)); lock((&(&led_cdev->blink_work)->work)); *** DEADLOCK *** 2 locks held by swapper/0/0: #0: (((&tpt_trig->timer))){+.-...}, at: [] call_timer_fn+0x0/0x180 #1: (&trig->leddev_list_lock){.+.?..}, at: [] tpt_trig_timer+0xec/0x170 [mac80211] stack backtrace: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.17.0-rc3 #1 Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008 ffffffff8246eb30 ffff88007c203b00 ffffffff8159e97f ffffffff81a194c0 ffff88007c203b50 ffffffff81599c29 0000000000000001 ffffffff00000001 ffff880000000000 0000000000000006 ffffffff81a194c0 ffffffff81093ad0 Call Trace: [] dump_stack+0x4d/0x66 [] print_usage_bug+0x1f4/0x205 [] ? check_usage_backwards+0x140/0x140 [] mark_lock+0x223/0x2b0 [] __lock_acquire+0x2b0/0x1a30 [] lock_acquire+0x91/0x110 [] ? mod_delayed_work_on+0x80/0x80 [] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211] [] flush_work+0x38/0x270 [] ? mod_delayed_work_on+0x80/0x80 [] ? mark_held_locks+0x6a/0x90 [] ? __cancel_work_timer+0x6f/0x100 [] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211] [] ? trace_hardirqs_on_caller+0xad/0x1c0 [] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211] [] __cancel_work_timer+0x7b/0x100 [] cancel_delayed_work_sync+0xe/0x10 [] led_blink_set+0x1b/0x40 [] tpt_trig_timer+0x110/0x170 [mac80211] [] call_timer_fn+0x75/0x180 [] ? process_timeout+0x10/0x10 [] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211] [] run_timer_softirq+0x1fc/0x2f0 [] __do_softirq+0x115/0x2e0 [] irq_exit+0xa5/0xb0 [] do_IRQ+0x53/0xf0 [] common_interrupt+0x6f/0x6f [] ? cpuidle_enter_state+0x6e/0x180 [] cpuidle_enter+0x12/0x20 [] cpu_startup_entry+0x330/0x360 [] rest_init+0xc1/0xd0 [] ? csum_partial_copy_generic+0x170/0x170 [] start_kernel+0x44f/0x45a [] ? set_init_arg+0x53/0x53 [] x86_64_start_reservations+0x2a/0x2c [] x86_64_start_kernel+0xf1/0xf4 Cc: Vincent Donnefort Cc: Hugh Dickins Cc: Tejun Heo Signed-off-by: Jiri Kosina Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 14 +++++++------- drivers/leds/led-core.c | 11 +++++------ include/linux/leds.h | 3 ++- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 129729d35478..aa29198fca3e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -15,10 +15,10 @@ #include #include #include +#include #include #include #include -#include #include "leds.h" static struct class *leds_class; @@ -97,10 +97,9 @@ static const struct attribute_group *led_groups[] = { NULL, }; -static void led_work_function(struct work_struct *ws) +static void led_timer_function(unsigned long data) { - struct led_classdev *led_cdev = - container_of(ws, struct led_classdev, blink_work.work); + struct led_classdev *led_cdev = (void *)data; unsigned long brightness; unsigned long delay; @@ -144,8 +143,7 @@ static void led_work_function(struct work_struct *ws) } } - queue_delayed_work(system_wq, &led_cdev->blink_work, - msecs_to_jiffies(delay)); + mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); } static void set_brightness_delayed(struct work_struct *ws) @@ -233,7 +231,9 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); - INIT_DELAYED_WORK(&led_cdev->blink_work, led_work_function); + init_timer(&led_cdev->blink_timer); + led_cdev->blink_timer.function = led_timer_function; + led_cdev->blink_timer.data = (unsigned long)led_cdev; #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 4bb116867b88..71b40d3bf776 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "leds.h" DECLARE_RWSEM(leds_list_lock); @@ -52,7 +51,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, return; } - queue_delayed_work(system_wq, &led_cdev->blink_work, 1); + mod_timer(&led_cdev->blink_timer, jiffies + 1); } @@ -76,7 +75,7 @@ void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { - cancel_delayed_work_sync(&led_cdev->blink_work); + del_timer_sync(&led_cdev->blink_timer); led_cdev->flags &= ~LED_BLINK_ONESHOT; led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; @@ -91,7 +90,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, int invert) { if ((led_cdev->flags & LED_BLINK_ONESHOT) && - delayed_work_pending(&led_cdev->blink_work)) + timer_pending(&led_cdev->blink_timer)) return; led_cdev->flags |= LED_BLINK_ONESHOT; @@ -108,7 +107,7 @@ EXPORT_SYMBOL(led_blink_set_oneshot); void led_stop_software_blink(struct led_classdev *led_cdev) { - cancel_delayed_work_sync(&led_cdev->blink_work); + del_timer_sync(&led_cdev->blink_timer); led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; } @@ -117,7 +116,7 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink); void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { - /* delay brightness setting if need to stop soft-blink work */ + /* delay brightness setting if need to stop soft-blink timer */ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { led_cdev->delayed_set_value = brightness; schedule_work(&led_cdev->set_brightness_work); diff --git a/include/linux/leds.h b/include/linux/leds.h index 6a599dce7f9d..e43686472197 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -15,6 +15,7 @@ #include #include #include +#include #include struct device; @@ -68,7 +69,7 @@ struct led_classdev { const char *default_trigger; /* Trigger to use */ unsigned long blink_delay_on, blink_delay_off; - struct delayed_work blink_work; + struct timer_list blink_timer; int blink_brightness; struct work_struct set_brightness_work; -- cgit v1.2.3 From d07e9c178f188f76851aca8ab49a78da13dda006 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 Aug 2014 15:13:21 +0200 Subject: PM / domains: Make generic_pm_domain.name const Signed-off-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki --- include/linux/pm_domain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 7c1d252b20c0..ebc4c76ffb73 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -60,7 +60,7 @@ struct generic_pm_domain { struct mutex lock; struct dev_power_governor *gov; struct work_struct power_off_work; - char *name; + const char *name; unsigned int in_progress; /* Number of devices being suspended now */ atomic_t sd_count; /* Number of subdomains with power "on" */ enum gpd_status status; /* Current state of the domain */ -- cgit v1.2.3 From a383b68d9fe9864c4d3b86f67ad6488f58136435 Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Wed, 3 Sep 2014 13:39:13 +0900 Subject: ACPI / scan: not cache _SUN value in struct acpi_device_pnp The _SUN device indentification object is not guaranteed to return the same value every time it is executed, so we should not cache its return value, but rather execute it every time as needed. If it is cached, an incorrect stale value may be used in some situations. This issue was exposed by commit 202317a573b2 (ACPI / scan: Add acpi_device objects for all device nodes in the namespace). Fix it by avoiding to cache the return value of _SUN. Fixes: 202317a573b2 (ACPI / scan: Add acpi_device objects for all device nodes in the namespace) Signed-off-by: Yasuaki Ishimatsu Cc: 3.14+ # 3.14+ [ rjw: Changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 15 ++++++++------- include/acpi/acpi_bus.h | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9a9298994e26..3bf7764659a4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -667,8 +667,14 @@ static ssize_t acpi_device_sun_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_status status; + unsigned long long sun; + + status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun); + if (ACPI_FAILURE(status)) + return -ENODEV; - return sprintf(buf, "%lu\n", acpi_dev->pnp.sun); + return sprintf(buf, "%llu\n", sun); } static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL); @@ -690,7 +696,6 @@ static int acpi_device_setup_files(struct acpi_device *dev) { struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; - unsigned long long sun; int result = 0; /* @@ -731,14 +736,10 @@ static int acpi_device_setup_files(struct acpi_device *dev) if (dev->pnp.unique_id) result = device_create_file(&dev->dev, &dev_attr_uid); - status = acpi_evaluate_integer(dev->handle, "_SUN", NULL, &sun); - if (ACPI_SUCCESS(status)) { - dev->pnp.sun = (unsigned long)sun; + if (acpi_has_method(dev->handle, "_SUN")) { result = device_create_file(&dev->dev, &dev_attr_sun); if (result) goto end; - } else { - dev->pnp.sun = (unsigned long)-1; } if (acpi_has_method(dev->handle, "_STA")) { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bcfd808b1098..c1c9de19edbe 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -246,7 +246,6 @@ struct acpi_device_pnp { acpi_device_name device_name; /* Driver-determined */ acpi_device_class device_class; /* " */ union acpi_object *str_obj; /* unicode string for _STR method */ - unsigned long sun; /* _SUN */ }; #define acpi_device_bid(d) ((d)->pnp.bus_id) -- cgit v1.2.3 From 40bea039593dfc7f3f9814dab844f6db43ae580b Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 13 Aug 2014 18:50:16 +0200 Subject: nohz: Restore NMI safe local irq work for local nohz kick The local nohz kick is currently used by perf which needs it to be NMI-safe. Recent commit though (7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9) changed its implementation to fire the local kick using the remote kick API. It was convenient to make the code more generic but the remote kick isn't NMI-safe. As a result: WARNING: CPU: 3 PID: 18062 at kernel/irq_work.c:72 irq_work_queue_on+0x11e/0x140() CPU: 3 PID: 18062 Comm: trinity-subchil Not tainted 3.16.0+ #34 0000000000000009 00000000903774d1 ffff880244e06c00 ffffffff9a7f1e37 0000000000000000 ffff880244e06c38 ffffffff9a0791dd ffff880244fce180 0000000000000003 ffff880244e06d58 ffff880244e06ef8 0000000000000000 Call Trace: [] dump_stack+0x4e/0x7a [] warn_slowpath_common+0x7d/0xa0 [] warn_slowpath_null+0x1a/0x20 [] irq_work_queue_on+0x11e/0x140 [] tick_nohz_full_kick_cpu+0x57/0x90 [] __perf_event_overflow+0x275/0x350 [] ? perf_event_task_disable+0xa0/0xa0 [] ? x86_perf_event_set_period+0xbf/0x150 [] perf_event_overflow+0x14/0x20 [] intel_pmu_handle_irq+0x206/0x410 [] ? arch_vtime_task_switch+0x63/0x130 [] perf_event_nmi_handler+0x2b/0x50 [] nmi_handle+0xd2/0x390 [] ? nmi_handle+0x5/0x390 [] ? lock_release+0xab/0x330 [] default_do_nmi+0x72/0x1c0 [] ? cpuacct_account_field+0xcf/0x200 [] do_nmi+0xb8/0x100 Lets fix this by restoring the use of local irq work for the nohz local kick. Reported-by: Catalin Iacob Reported-and-tested-by: Dave Jones Cc: Peter Zijlstra Cc: Thomas Gleixner Signed-off-by: Frederic Weisbecker --- include/linux/tick.h | 7 +------ kernel/time/tick-sched.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/tick.h b/include/linux/tick.h index 059052306831..9a82c7dc3fdd 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -183,13 +183,8 @@ static inline bool tick_nohz_full_cpu(int cpu) extern void tick_nohz_init(void); extern void __tick_nohz_full_check(void); +extern void tick_nohz_full_kick(void); extern void tick_nohz_full_kick_cpu(int cpu); - -static inline void tick_nohz_full_kick(void) -{ - tick_nohz_full_kick_cpu(smp_processor_id()); -} - extern void tick_nohz_full_kick_all(void); extern void __tick_nohz_task_switch(struct task_struct *tsk); #else diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 99aa6ee3908f..f654a8a298fa 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -224,6 +224,20 @@ static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { .func = nohz_full_kick_work_func, }; +/* + * Kick this CPU if it's full dynticks in order to force it to + * re-evaluate its dependency on the tick and restart it if necessary. + * This kick, unlike tick_nohz_full_kick_cpu() and tick_nohz_full_kick_all(), + * is NMI safe. + */ +void tick_nohz_full_kick(void) +{ + if (!tick_nohz_full_cpu(smp_processor_id())) + return; + + irq_work_queue(&__get_cpu_var(nohz_full_kick_work)); +} + /* * Kick the CPU if it's full dynticks in order to force it to * re-evaluate its dependency on the tick and restart it if necessary. -- cgit v1.2.3 From 6fa9e1be7f28dd407d710c3ab367b1e5bc34c2bf Mon Sep 17 00:00:00 2001 From: Piotr Król Date: Fri, 5 Sep 2014 00:58:02 +0200 Subject: usb: usbip: fix usbip.h path in userspace tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 588b48caf65c ("usbip: move usbip userspace code out of staging") which introduced build failure by not changing uapi/usbip.h include path according to new location. Signed-off-by: Piotr Król Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/Kbuild | 1 + tools/usb/usbip/libsrc/usbip_common.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 24e9033f8b3f..9955c3b0ef93 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -395,6 +395,7 @@ header-y += un.h header-y += unistd.h header-y += unix_diag.h header-y += usbdevice_fs.h +header-y += usbip.h header-y += utime.h header-y += utsname.h header-y += uuid.h diff --git a/tools/usb/usbip/libsrc/usbip_common.h b/tools/usb/usbip/libsrc/usbip_common.h index 5a0e95edf4df..15fe792e1e96 100644 --- a/tools/usb/usbip/libsrc/usbip_common.h +++ b/tools/usb/usbip/libsrc/usbip_common.h @@ -15,7 +15,7 @@ #include #include #include -#include "../../uapi/usbip.h" +#include #ifndef USBIDS_FILE #define USBIDS_FILE "/usr/share/hwdata/usb.ids" -- cgit v1.2.3 From fb38ab4cd05e11184fd2c3ef916fa106ecc505fc Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 5 Sep 2014 15:52:28 +0800 Subject: crypto: drbg - backport "fix maximum value checks on 32 bit systems" This is a backport of commit b9347aff91ce4789619168539f08202d8d6a1177. This backport is needed as without it the code will crash on 32-bit systems. The maximum values for additional input string or generated blocks is larger than 1<<32. To ensure a sensible value on 32 bit systems, return SIZE_MAX on 32 bit systems. This value is lower than the maximum allowed values defined in SP800-90A. The standard allow lower maximum values, but not larger values. SIZE_MAX - 1 is used for drbg_max_addtl to allow drbg_healthcheck_sanity to check the enforcement of the variable without wrapping. Reported-by: Stephen Rothwell Reported-by: kbuild test robot Signed-off-by: Herbert Xu --- include/crypto/drbg.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index 831d786976c5..882675e7c055 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -162,12 +162,25 @@ static inline size_t drbg_max_request_bytes(struct drbg_state *drbg) static inline size_t drbg_max_addtl(struct drbg_state *drbg) { +#if (__BITS_PER_LONG == 32) + /* + * SP800-90A allows smaller maximum numbers to be returned -- we + * return SIZE_MAX - 1 to allow the verification of the enforcement + * of this value in drbg_healthcheck_sanity. + */ + return (SIZE_MAX - 1); +#else return (1UL<<(drbg->core->max_addtllen)); +#endif } static inline size_t drbg_max_requests(struct drbg_state *drbg) { +#if (__BITS_PER_LONG == 32) + return SIZE_MAX; +#else return (1UL<<(drbg->core->max_req)); +#endif } /* -- cgit v1.2.3 From c199105d154e029cd8c94cccd35bd073e64acc45 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 3 Sep 2014 12:01:18 -0400 Subject: net-timestamp: only report sw timestamp if reporting bit is set The timestamping API has separate bits for generating and reporting timestamps. A software timestamp should only be reported for a packet when the packet has the relevant generation flag (SKBTX_..) set and the socket has reporting bit SOF_TIMESTAMPING_SOFTWARE set. The second check was accidentally removed. Reinstitute the original behavior. Tested: Without this patch, Documentation/networking/txtimestamp reports timestamps regardless of whether SOF_TIMESTAMPING_SOFTWARE is set. After the patch, it only reports them when the flag is set. Fixes: f24b9be5957b ("net-timestamp: extend SCM_TIMESTAMPING ancillary data struct") Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/net/sock.h | 4 +--- net/socket.c | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 7f2ab72f321a..b9a5bd0ed9f3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2165,9 +2165,7 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) */ if (sock_flag(sk, SOCK_RCVTSTAMP) || (sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) || - (kt.tv64 && - (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE || - skb_shinfo(skb)->tx_flags & SKBTX_ANY_SW_TSTAMP)) || + (kt.tv64 && sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) || (hwtstamps->hwtstamp.tv64 && (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE))) __sock_recv_timestamp(msg, sk, skb); diff --git a/net/socket.c b/net/socket.c index 95ee7d8682e7..4eb09b34b2d3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -734,8 +734,7 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, } memset(&tss, 0, sizeof(tss)); - if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE || - skb_shinfo(skb)->tx_flags & SKBTX_ANY_SW_TSTAMP) && + if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) && ktime_to_timespec_cond(skb->tstamp, tss.ts + 0)) empty = 0; if (shhwtstamps && -- cgit v1.2.3 From e793c0f70e9bdf4a2e71c151a1a3cf85c4db92ad Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Thu, 4 Sep 2014 23:44:36 +0900 Subject: net: treewide: Fix typo found in DocBook/networking.xml This patch fix spelling typo found in DocBook/networking.xml. It is because the neworking.xml is generated from comments in the source, I have to fix typo in comments within the source. Signed-off-by: Masanari Iida Acked-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- include/net/wimax.h | 2 +- include/trace/events/irq.h | 4 ++-- net/core/datagram.c | 2 +- net/core/gen_estimator.c | 2 +- net/core/gen_stats.c | 2 +- net/core/skbuff.c | 4 ++-- net/core/sock.c | 4 ++-- net/socket.c | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 38377392d082..c8e388e5fccc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3176,7 +3176,7 @@ static inline int __dev_uc_sync(struct net_device *dev, } /** - * __dev_uc_unsync - Remove synchonized addresses from device + * __dev_uc_unsync - Remove synchronized addresses from device * @dev: device to sync * @unsync: function to call if address should be removed * @@ -3220,7 +3220,7 @@ static inline int __dev_mc_sync(struct net_device *dev, } /** - * __dev_mc_unsync - Remove synchonized addresses from device + * __dev_mc_unsync - Remove synchronized addresses from device * @dev: device to sync * @unsync: function to call if address should be removed * diff --git a/include/net/wimax.h b/include/net/wimax.h index e52ef5357e08..c52b68577cb0 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h @@ -290,7 +290,7 @@ struct wimax_dev; * This operation has to be synchronous, and return only when the * reset is complete. In case of having had to resort to bus/cold * reset implying a device disconnection, the call is allowed to - * return inmediately. + * return immediately. * NOTE: wimax_dev->mutex is NOT locked when this op is being * called; however, wimax_dev->mutex_reset IS locked to ensure * serialization of calls to wimax_reset(). diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h index 1c09820df585..3608bebd3d9c 100644 --- a/include/trace/events/irq.h +++ b/include/trace/events/irq.h @@ -107,7 +107,7 @@ DECLARE_EVENT_CLASS(softirq, * @vec_nr: softirq vector number * * When used in combination with the softirq_exit tracepoint - * we can determine the softirq handler runtine. + * we can determine the softirq handler routine. */ DEFINE_EVENT(softirq, softirq_entry, @@ -121,7 +121,7 @@ DEFINE_EVENT(softirq, softirq_entry, * @vec_nr: softirq vector number * * When used in combination with the softirq_entry tracepoint - * we can determine the softirq handler runtine. + * we can determine the softirq handler routine. */ DEFINE_EVENT(softirq, softirq_exit, diff --git a/net/core/datagram.c b/net/core/datagram.c index 488dd1a825c0..fdbc9a81d4c2 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -775,7 +775,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb) EXPORT_SYMBOL(__skb_checksum_complete); /** - * skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec. + * skb_copy_and_csum_datagram_iovec - Copy and checksum skb to user iovec. * @skb: skbuff * @hlen: hardware length * @iov: io vector diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index 6b5b6e7013ca..9d33dfffca19 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -197,7 +197,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats * as destination. A new timer with the interval specified in the * configuration TLV is created. Upon each interval, the latest statistics * will be read from &bstats and the estimated rate will be stored in - * &rate_est with the statistics lock grabed during this period. + * &rate_est with the statistics lock grabbed during this period. * * Returns 0 on success or a negative error code. * diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index 9d3d9e78397b..2ddbce4cce14 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -206,7 +206,7 @@ EXPORT_SYMBOL(gnet_stats_copy_queue); * @st: application specific statistics data * @len: length of data * - * Appends the application sepecific statistics to the top level TLV created by + * Appends the application specific statistics to the top level TLV created by * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping * handle is in backward compatibility mode. * diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 163b673f9e62..da1378a3e2c7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2647,7 +2647,7 @@ EXPORT_SYMBOL(skb_prepare_seq_read); * skb_seq_read() will return the remaining part of the block. * * Note 1: The size of each block of data returned can be arbitrary, - * this limitation is the cost for zerocopy seqeuental + * this limitation is the cost for zerocopy sequential * reads of potentially non linear data. * * Note 2: Fragment lists within fragments are not implemented @@ -2781,7 +2781,7 @@ EXPORT_SYMBOL(skb_find_text); /** * skb_append_datato_frags - append the user data to a skb * @sk: sock structure - * @skb: skb structure to be appened with user data. + * @skb: skb structure to be appended with user data. * @getfrag: call back function to be used for getting the user data * @from: pointer to user message iov * @length: length of the iov message diff --git a/net/core/sock.c b/net/core/sock.c index 29870571c42f..d372b4bd3f99 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -166,7 +166,7 @@ EXPORT_SYMBOL(sk_ns_capable); /** * sk_capable - Socket global capability test * @sk: Socket to use a capability on or through - * @cap: The global capbility to use + * @cap: The global capability to use * * Test to see if the opener of the socket had when the socket was * created and the current process has the capability @cap in all user @@ -183,7 +183,7 @@ EXPORT_SYMBOL(sk_capable); * @sk: Socket to use a capability on or through * @cap: The capability to use * - * Test to see if the opener of the socket had when the socke was created + * Test to see if the opener of the socket had when the socket was created * and the current process has the capability @cap over the network namespace * the socket is a member of. */ diff --git a/net/socket.c b/net/socket.c index 4eb09b34b2d3..2e2586e2dee1 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2601,7 +2601,7 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) * * This function is called by a protocol handler that wants to * advertise its address family, and have it linked into the - * socket interface. The value ops->family coresponds to the + * socket interface. The value ops->family corresponds to the * socket system call protocol family. */ int sock_register(const struct net_proto_family *ops) -- cgit v1.2.3 From 7611392fe8ff95ecae528b01a815ae3d72ca6b95 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 8 Sep 2014 14:42:12 -0700 Subject: Input: add INPUT_PROP_POINTING_STICK property It is useful for userspace to know that there not dealing with a regular mouse but rather with a pointing stick (e.g. a trackpoint) so that userspace can e.g. automatically enable middle button scrollwheel emulation. It is impossible to tell the difference from the evdev info without resorting to putting a list of device / driver names in userspace, this is undesirable. Add a property which allows userspace to see if a device is a pointing stick, and set it on all the pointing stick drivers. Signed-off-by: Hans de Goede Acked-by: Benjamin Tissoires Acked-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 3 +++ drivers/input/mouse/elantech.c | 3 +++ drivers/input/mouse/synaptics_usb.c | 1 + drivers/input/mouse/trackpoint.c | 2 ++ include/uapi/linux/input.h | 1 + 5 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index a956b980ee73..9d9e5076d402 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -2373,6 +2373,9 @@ int alps_init(struct psmouse *psmouse) dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + if (priv->flags & ALPS_DUALPOINT) + __set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit); + if (input_register_device(priv->dev2)) goto init_fail; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index d71bd3635de1..0cbf6281899c 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1614,6 +1614,9 @@ int elantech_init(struct psmouse *psmouse) tp_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + + __set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit); + error = input_register_device(etd->tp_dev); if (error < 0) goto init_fail_tp_reg; diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c index e122bda16aab..db3973d78207 100644 --- a/drivers/input/mouse/synaptics_usb.c +++ b/drivers/input/mouse/synaptics_usb.c @@ -387,6 +387,7 @@ static int synusb_probe(struct usb_interface *intf, __set_bit(EV_REL, input_dev->evbit); __set_bit(REL_X, input_dev->relbit); __set_bit(REL_Y, input_dev->relbit); + __set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit); input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); } else { input_set_abs_params(input_dev, ABS_X, diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index ca843b6cf6bd..b377462748a2 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -393,6 +393,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) if ((button_info & 0x0f) >= 3) __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit); + trackpoint_defaults(psmouse->private); error = trackpoint_power_on_reset(&psmouse->ps2dev); diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 19df18c9b8be..1874ebe9ac1e 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -165,6 +165,7 @@ struct input_keymap_entry { #define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ #define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ #define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ +#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ #define INPUT_PROP_MAX 0x1f #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) -- cgit v1.2.3 From b440bde74f043c8ec31081cb59c9a53ade954701 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Sep 2014 13:45:01 -0600 Subject: PCI: Add pci_ignore_hotplug() to ignore hotplug events for a device Powering off a hot-pluggable device, e.g., with pci_set_power_state(D3cold), normally generates a hot-remove event that unbinds the driver. Some drivers expect to remain bound to a device even while they power it off and back on again. This can be dangerous, because if the device is removed or replaced while it is powered off, the driver doesn't know that anything changed. But some drivers accept that risk. Add pci_ignore_hotplug() for use by drivers that know their device cannot be removed. Using pci_ignore_hotplug() tells the PCI core that hot-plug events for the device should be ignored. The radeon and nouveau drivers use this to switch between a low-power, integrated GPU and a higher-power, higher-performance discrete GPU. They power off the unused GPU, but they want to remain bound to it. This is a reimplementation of f244d8b623da ("ACPIPHP / radeon / nouveau: Fix VGA switcheroo problem related to hotplug") but extends it to work with both acpiphp and pciehp. This fixes a problem where systems with dual GPUs using the radeon drivers become unusable, freezing every few seconds (see bugzillas below). The resume of the radeon device may also fail, e.g., This fixes problems on dual GPU systems where the radeon driver becomes unusable because of problems while suspending the device, as in bug 79701: [drm] radeon: finishing device. radeon 0000:01:00.0: Userspace still has active objects ! radeon 0000:01:00.0: ffff8800cb4ec288 ffff8800cb4ec000 16384 4294967297 force free ... WARNING: CPU: 0 PID: 67 at /home/apw/COD/linux/drivers/gpu/drm/radeon/radeon_gart.c:234 radeon_gart_unbind+0xd2/0xe0 [radeon]() trying to unbind memory from uninitialized GART ! or while resuming it, as in bug 77261: radeon 0000:01:00.0: ring 0 stalled for more than 10158msec radeon 0000:01:00.0: GPU lockup ... radeon 0000:01:00.0: GPU pci config reset pciehp 0000:00:01.0:pcie04: Card not present on Slot(1-1) radeon 0000:01:00.0: GPU reset succeeded, trying to resume *ERROR* radeon: dpm resume failed radeon 0000:01:00.0: Wait for MC idle timedout ! Link: https://bugzilla.kernel.org/show_bug.cgi?id=77261 Link: https://bugzilla.kernel.org/show_bug.cgi?id=79701 Reported-by: Shawn Starr Reported-by: Jose P. Signed-off-by: Bjorn Helgaas Acked-by: Alex Deucher Acked-by: Rajat Jain Acked-by: Rafael J. Wysocki Acked-by: Dave Airlie CC: stable@vger.kernel.org # v3.15+ --- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 + drivers/gpu/drm/radeon/radeon_drv.c | 1 + drivers/pci/hotplug/acpiphp_glue.c | 16 ++++++---------- drivers/pci/hotplug/pciehp_hpc.c | 12 ++++++++++++ include/linux/pci.h | 6 ++++++ 5 files changed, 26 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 250a5e88c751..9c3af96a7153 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -627,6 +627,7 @@ int nouveau_pmops_suspend(struct device *dev) pci_save_state(pdev); pci_disable_device(pdev); + pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3hot); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8df888908833..abbd87adfd75 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -440,6 +440,7 @@ static int radeon_pmops_runtime_suspend(struct device *dev) ret = radeon_suspend_kms(drm_dev, false, false); pci_save_state(pdev); pci_disable_device(pdev); + pci_ignore_hotplug(pdev); pci_set_power_state(pdev, PCI_D3cold); drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 70741c8c46a0..6cd5160fc057 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -560,19 +560,15 @@ static void disable_slot(struct acpiphp_slot *slot) slot->flags &= (~SLOT_ENABLED); } -static bool acpiphp_no_hotplug(struct acpi_device *adev) -{ - return adev && adev->flags.no_hotplug; -} - static bool slot_no_hotplug(struct acpiphp_slot *slot) { - struct acpiphp_func *func; + struct pci_bus *bus = slot->bus; + struct pci_dev *dev; - list_for_each_entry(func, &slot->funcs, sibling) - if (acpiphp_no_hotplug(func_to_acpi_device(func))) + list_for_each_entry(dev, &bus->devices, bus_list) { + if (PCI_SLOT(dev->devfn) == slot->device && dev->ignore_hotplug) return true; - + } return false; } @@ -645,7 +641,7 @@ static void trim_stale_devices(struct pci_dev *dev) status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); alive = (ACPI_SUCCESS(status) && device_status_valid(sta)) - || acpiphp_no_hotplug(adev); + || dev->ignore_hotplug; } if (!alive) alive = pci_device_is_present(dev); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 9da84b8b27d8..5e01ae39ec46 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -506,6 +506,8 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_bus *subordinate = pdev->subordinate; + struct pci_dev *dev; struct slot *slot = ctrl->slot; u16 detected, intr_loc; @@ -539,6 +541,16 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) wake_up(&ctrl->queue); } + if (subordinate) { + list_for_each_entry(dev, &subordinate->devices, bus_list) { + if (dev->ignore_hotplug) { + ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n", + intr_loc, pci_name(dev)); + return IRQ_HANDLED; + } + } + } + if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) return IRQ_HANDLED; diff --git a/include/linux/pci.h b/include/linux/pci.h index 61978a460841..96453f9bc8ba 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -303,6 +303,7 @@ struct pci_dev { D3cold, not set for devices powered on/off by the corresponding bridge */ + unsigned int ignore_hotplug:1; /* Ignore hotplug events */ unsigned int d3_delay; /* D3->D0 transition time in ms */ unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ @@ -1021,6 +1022,11 @@ bool pci_dev_run_wake(struct pci_dev *dev); bool pci_check_pme_status(struct pci_dev *dev); void pci_pme_wakeup_bus(struct pci_bus *bus); +static inline void pci_ignore_hotplug(struct pci_dev *dev) +{ + dev->ignore_hotplug = 1; +} + static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) { -- cgit v1.2.3 From b01d072065b6f36550f486fe77f05b092225ba1b Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Tue, 9 Sep 2014 14:50:57 -0700 Subject: shm: add memfd.h to UAPI export list The new header file memfd.h from commit 9183df25fe7b ("shm: add memfd_create() syscall") should be exported. Signed-off-by: David Drysdale Reviewed-by: David Herrmann Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 24e9033f8b3f..939df6973102 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -240,6 +240,7 @@ header-y += matroxfb.h header-y += mdio.h header-y += media.h header-y += mei.h +header-y += memfd.h header-y += mempolicy.h header-y += meye.h header-y += mic_common.h -- cgit v1.2.3 From 5ebc77de83c7b74543de774afa7395b3b790e65e Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 10 Sep 2014 22:49:30 +0000 Subject: xen/arm: introduce XENFEAT_grant_map_identity The flag tells us that the hypervisor maps a grant page to guest physical address == machine address of the page in addition to the normal grant mapping address. It is needed to properly issue cache maintenance operation at the completion of a DMA operation involving a foreign grant. Signed-off-by: Stefano Stabellini Tested-by: Denis Schneider --- arch/arm/xen/enlighten.c | 6 ++++++ include/xen/interface/features.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 98544c5f86e9..0e15f011f9c8 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -260,6 +260,12 @@ static int __init xen_guest_init(void) xen_domain_type = XEN_HVM_DOMAIN; xen_setup_features(); + + if (!xen_feature(XENFEAT_grant_map_identity)) { + pr_warn("Please upgrade your Xen.\n" + "If your platform has any non-coherent DMA devices, they won't work properly.\n"); + } + if (xen_feature(XENFEAT_dom0)) xen_start_info->flags |= SIF_INITDOMAIN|SIF_PRIVILEGED; else diff --git a/include/xen/interface/features.h b/include/xen/interface/features.h index 131a6ccdba25..14334d0161d5 100644 --- a/include/xen/interface/features.h +++ b/include/xen/interface/features.h @@ -53,6 +53,9 @@ /* operation as Dom0 is supported */ #define XENFEAT_dom0 11 +/* Xen also maps grant references at pfn = mfn */ +#define XENFEAT_grant_map_identity 12 + #define XENFEAT_NR_SUBMAPS 1 #endif /* __XEN_PUBLIC_FEATURES_H__ */ -- cgit v1.2.3 From d78c9300c51d6ceed9f6d078d4e9366f259de28c Mon Sep 17 00:00:00 2001 From: Andrew Hunter Date: Thu, 4 Sep 2014 14:17:16 -0700 Subject: jiffies: Fix timeval conversion to jiffies timeval_to_jiffies tried to round a timeval up to an integral number of jiffies, but the logic for doing so was incorrect: intervals corresponding to exactly N jiffies would become N+1. This manifested itself particularly repeatedly stopping/starting an itimer: setitimer(ITIMER_PROF, &val, NULL); setitimer(ITIMER_PROF, NULL, &val); would add a full tick to val, _even if it was exactly representable in terms of jiffies_ (say, the result of a previous rounding.) Doing this repeatedly would cause unbounded growth in val. So fix the math. Here's what was wrong with the conversion: we essentially computed (eliding seconds) jiffies = usec * (NSEC_PER_USEC/TICK_NSEC) by using scaling arithmetic, which took the best approximation of NSEC_PER_USEC/TICK_NSEC with denominator of 2^USEC_JIFFIE_SC = x/(2^USEC_JIFFIE_SC), and computed: jiffies = (usec * x) >> USEC_JIFFIE_SC and rounded this calculation up in the intermediate form (since we can't necessarily exactly represent TICK_NSEC in usec.) But the scaling arithmetic is a (very slight) *over*approximation of the true value; that is, instead of dividing by (1 usec/ 1 jiffie), we effectively divided by (1 usec/1 jiffie)-epsilon (rounding down). This would normally be fine, but we want to round timeouts up, and we did so by adding 2^USEC_JIFFIE_SC - 1 before the shift; this would be fine if our division was exact, but dividing this by the slightly smaller factor was equivalent to adding just _over_ 1 to the final result (instead of just _under_ 1, as desired.) In particular, with HZ=1000, we consistently computed that 10000 usec was 11 jiffies; the same was true for any exact multiple of TICK_NSEC. We could possibly still round in the intermediate form, adding something less than 2^USEC_JIFFIE_SC - 1, but easier still is to convert usec->nsec, round in nanoseconds, and then convert using time*spec*_to_jiffies. This adds one constant multiplication, and is not observably slower in microbenchmarks on recent x86 hardware. Tested: the following program: int main() { struct itimerval zero = {{0, 0}, {0, 0}}; /* Initially set to 10 ms. */ struct itimerval initial = zero; initial.it_interval.tv_usec = 10000; setitimer(ITIMER_PROF, &initial, NULL); /* Save and restore several times. */ for (size_t i = 0; i < 10; ++i) { struct itimerval prev; setitimer(ITIMER_PROF, &zero, &prev); /* on old kernels, this goes up by TICK_USEC every iteration */ printf("previous value: %ld %ld %ld %ld\n", prev.it_interval.tv_sec, prev.it_interval.tv_usec, prev.it_value.tv_sec, prev.it_value.tv_usec); setitimer(ITIMER_PROF, &prev, NULL); } return 0; } Cc: stable@vger.kernel.org Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Paul Turner Cc: Richard Cochran Cc: Prarit Bhargava Reviewed-by: Paul Turner Reported-by: Aaron Jacobs Signed-off-by: Andrew Hunter [jstultz: Tweaked to apply to 3.17-rc] Signed-off-by: John Stultz --- include/linux/jiffies.h | 12 ----------- kernel/time/time.c | 56 +++++++++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 1f44466c1e9d..c367cbdf73ab 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -258,23 +258,11 @@ extern unsigned long preset_lpj; #define SEC_JIFFIE_SC (32 - SHIFT_HZ) #endif #define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 29) -#define USEC_JIFFIE_SC (SEC_JIFFIE_SC + 19) #define SEC_CONVERSION ((unsigned long)((((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) +\ TICK_NSEC -1) / (u64)TICK_NSEC)) #define NSEC_CONVERSION ((unsigned long)((((u64)1 << NSEC_JIFFIE_SC) +\ TICK_NSEC -1) / (u64)TICK_NSEC)) -#define USEC_CONVERSION \ - ((unsigned long)((((u64)NSEC_PER_USEC << USEC_JIFFIE_SC) +\ - TICK_NSEC -1) / (u64)TICK_NSEC)) -/* - * USEC_ROUND is used in the timeval to jiffie conversion. See there - * for more details. It is the scaled resolution rounding value. Note - * that it is a 64-bit value. Since, when it is applied, we are already - * in jiffies (albit scaled), it is nothing but the bits we will shift - * off. - */ -#define USEC_ROUND (u64)(((u64)1 << USEC_JIFFIE_SC) - 1) /* * The maximum jiffie value is (MAX_INT >> 1). Here we translate that * into seconds. The 64-bit case will overflow if we are not careful, diff --git a/kernel/time/time.c b/kernel/time/time.c index f0294ba14634..a9ae20fb0b11 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -559,17 +559,20 @@ EXPORT_SYMBOL(usecs_to_jiffies); * that a remainder subtract here would not do the right thing as the * resolution values don't fall on second boundries. I.e. the line: * nsec -= nsec % TICK_NSEC; is NOT a correct resolution rounding. + * Note that due to the small error in the multiplier here, this + * rounding is incorrect for sufficiently large values of tv_nsec, but + * well formed timespecs should have tv_nsec < NSEC_PER_SEC, so we're + * OK. * * Rather, we just shift the bits off the right. * * The >> (NSEC_JIFFIE_SC - SEC_JIFFIE_SC) converts the scaled nsec * value to a scaled second value. */ -unsigned long -timespec_to_jiffies(const struct timespec *value) +static unsigned long +__timespec_to_jiffies(unsigned long sec, long nsec) { - unsigned long sec = value->tv_sec; - long nsec = value->tv_nsec + TICK_NSEC - 1; + nsec = nsec + TICK_NSEC - 1; if (sec >= MAX_SEC_IN_JIFFIES){ sec = MAX_SEC_IN_JIFFIES; @@ -580,6 +583,13 @@ timespec_to_jiffies(const struct timespec *value) (NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC; } + +unsigned long +timespec_to_jiffies(const struct timespec *value) +{ + return __timespec_to_jiffies(value->tv_sec, value->tv_nsec); +} + EXPORT_SYMBOL(timespec_to_jiffies); void @@ -596,31 +606,27 @@ jiffies_to_timespec(const unsigned long jiffies, struct timespec *value) } EXPORT_SYMBOL(jiffies_to_timespec); -/* Same for "timeval" - * - * Well, almost. The problem here is that the real system resolution is - * in nanoseconds and the value being converted is in micro seconds. - * Also for some machines (those that use HZ = 1024, in-particular), - * there is a LARGE error in the tick size in microseconds. - - * The solution we use is to do the rounding AFTER we convert the - * microsecond part. Thus the USEC_ROUND, the bits to be shifted off. - * Instruction wise, this should cost only an additional add with carry - * instruction above the way it was done above. +/* + * We could use a similar algorithm to timespec_to_jiffies (with a + * different multiplier for usec instead of nsec). But this has a + * problem with rounding: we can't exactly add TICK_NSEC - 1 to the + * usec value, since it's not necessarily integral. + * + * We could instead round in the intermediate scaled representation + * (i.e. in units of 1/2^(large scale) jiffies) but that's also + * perilous: the scaling introduces a small positive error, which + * combined with a division-rounding-upward (i.e. adding 2^(scale) - 1 + * units to the intermediate before shifting) leads to accidental + * overflow and overestimates. + * + * At the cost of one additional multiplication by a constant, just + * use the timespec implementation. */ unsigned long timeval_to_jiffies(const struct timeval *value) { - unsigned long sec = value->tv_sec; - long usec = value->tv_usec; - - if (sec >= MAX_SEC_IN_JIFFIES){ - sec = MAX_SEC_IN_JIFFIES; - usec = 0; - } - return (((u64)sec * SEC_CONVERSION) + - (((u64)usec * USEC_CONVERSION + USEC_ROUND) >> - (USEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC; + return __timespec_to_jiffies(value->tv_sec, + value->tv_usec * NSEC_PER_USEC); } EXPORT_SYMBOL(timeval_to_jiffies); -- cgit v1.2.3 From 23d0db76ffa13ffb95229946e4648568c3c29db5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 13 Sep 2014 11:24:03 -0700 Subject: Make hash_64() use a 64-bit multiply when appropriate The hash_64() function historically does the multiply by the GOLDEN_RATIO_PRIME_64 number with explicit shifts and adds, because unlike the 32-bit case, gcc seems unable to turn the constant multiply into the more appropriate shift and adds when required. However, that means that we generate those shifts and adds even when the architecture has a fast multiplier, and could just do it better in hardware. Use the now-cleaned-up CONFIG_ARCH_HAS_FAST_MULTIPLIER (together with "is it a 64-bit architecture") to decide whether to use an integer multiply or the explicit sequence of shift/add instructions. Signed-off-by: Linus Torvalds --- include/linux/hash.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/hash.h b/include/linux/hash.h index bd1754c7ecef..d0494c399392 100644 --- a/include/linux/hash.h +++ b/include/linux/hash.h @@ -37,6 +37,9 @@ static __always_inline u64 hash_64(u64 val, unsigned int bits) { u64 hash = val; +#if defined(CONFIG_ARCH_HAS_FAST_MULTIPLIER) && BITS_PER_LONG == 64 + hash = hash * GOLDEN_RATIO_PRIME_64; +#else /* Sigh, gcc can't optimise this alone like it does for 32 bits. */ u64 n = hash; n <<= 18; @@ -51,6 +54,7 @@ static __always_inline u64 hash_64(u64 val, unsigned int bits) hash += n; n <<= 2; hash += n; +#endif /* High bits are more random, so use them. */ return hash >> (64 - bits); -- cgit v1.2.3 From 9226b5b440f2b4fbb3b797f3cb74a9a627220660 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 14 Sep 2014 17:28:32 -0700 Subject: vfs: avoid non-forwarding large load after small store in path lookup The performance regression that Josef Bacik reported in the pathname lookup (see commit 99d263d4c5b2 "vfs: fix bad hashing of dentries") made me look at performance stability of the dcache code, just to verify that the problem was actually fixed. That turned up a few other problems in this area. There are a few cases where we exit RCU lookup mode and go to the slow serializing case when we shouldn't, Al has fixed those and they'll come in with the next VFS pull. But my performance verification also shows that link_path_walk() turns out to have a very unfortunate 32-bit store of the length and hash of the name we look up, followed by a 64-bit read of the combined hash_len field. That screws up the processor store to load forwarding, causing an unnecessary hickup in this critical routine. It's caused by the ugly calling convention for the "hash_name()" function, and easily fixed by just making hash_name() fill in the whole 'struct qstr' rather than passing it a pointer to just the hash value. With that, the profile for this function looks much smoother. Signed-off-by: Linus Torvalds --- fs/namei.c | 19 ++++++++++--------- include/linux/dcache.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/namei.c b/fs/namei.c index 229235862e50..2be5120b81b3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1669,13 +1669,14 @@ EXPORT_SYMBOL(full_name_hash); /* * Calculate the length and hash of the path component, and - * return the length of the component; + * fill in the qstr. return the "len" as the result. */ -static inline unsigned long hash_name(const char *name, unsigned int *hashp) +static inline unsigned long hash_name(const char *name, struct qstr *res) { unsigned long a, b, adata, bdata, mask, hash, len; const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; + res->name = name; hash = a = 0; len = -sizeof(unsigned long); do { @@ -1691,9 +1692,10 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp) mask = create_zero_mask(adata | bdata); hash += a & zero_bytemask(mask); - *hashp = fold_hash(hash); + len += find_zero(mask); + res->hash_len = hashlen_create(fold_hash(hash), len); - return len + find_zero(mask); + return len; } #else @@ -1711,18 +1713,19 @@ EXPORT_SYMBOL(full_name_hash); * We know there's a real path component here of at least * one character. */ -static inline unsigned long hash_name(const char *name, unsigned int *hashp) +static inline long hash_name(const char *name, struct qstr *res) { unsigned long hash = init_name_hash(); unsigned long len = 0, c; + res->name = name; c = (unsigned char)*name; do { len++; hash = partial_name_hash(c, hash); c = (unsigned char)name[len]; } while (c && c != '/'); - *hashp = end_name_hash(hash); + res->hash_len = hashlen_create(end_name_hash(hash), len); return len; } @@ -1756,9 +1759,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (err) break; - len = hash_name(name, &this.hash); - this.name = name; - this.len = len; + len = hash_name(name, &this); type = LAST_NORM; if (name[0] == '.') switch (len) { diff --git a/include/linux/dcache.h b/include/linux/dcache.h index e4ae2ad48d07..75a227cc7ce2 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -55,6 +55,7 @@ struct qstr { #define QSTR_INIT(n,l) { { { .len = l } }, .name = n } #define hashlen_hash(hashlen) ((u32) (hashlen)) #define hashlen_len(hashlen) ((u32)((hashlen) >> 32)) +#define hashlen_create(hash,len) (((u64)(len)<<32)|(u32)(hash)) struct dentry_stat_t { long nr_dentry; -- cgit v1.2.3 From f91ce35e471ae17552ce7bfe355cfd997e3ad781 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Sep 2014 15:30:08 -0600 Subject: ACPIPHP / radeon / nouveau: Remove acpi_bus_no_hotplug() Revert parts of f244d8b623da ("ACPIPHP / radeon / nouveau: Fix VGA switcheroo problem related to hotplug"). A previous commit 5493b31f0b55 ("PCI: Add pci_ignore_hotplug() to ignore hotplug events for a device") added equivalent functionality implemented in a different way for both acpiphp and pciehp. Signed-off-by: Bjorn Helgaas Acked-by: Alex Deucher Acked-by: Rafael J. Wysocki Acked-by: Dave Airlie Acked-by: Rajat Jain --- drivers/acpi/bus.c | 10 ---------- drivers/gpu/drm/nouveau/nouveau_acpi.c | 16 ++-------------- drivers/gpu/drm/radeon/radeon_atpx_handler.c | 16 ++-------------- include/acpi/acpi_bus.h | 4 +--- 4 files changed, 5 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8581f5b84f48..8b67bd0f6bb5 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -177,16 +177,6 @@ void acpi_bus_detach_private_data(acpi_handle handle) } EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); -void acpi_bus_no_hotplug(acpi_handle handle) -{ - struct acpi_device *adev = NULL; - - acpi_bus_get_device(handle, &adev); - if (adev) - adev->flags.no_hotplug = true; -} -EXPORT_SYMBOL_GPL(acpi_bus_no_hotplug); - static void acpi_print_osc_error(acpi_handle handle, struct acpi_osc_context *context, char *error) { diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 279206997e5c..622424692b3b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -46,7 +46,6 @@ static struct nouveau_dsm_priv { bool dsm_detected; bool optimus_detected; acpi_handle dhandle; - acpi_handle other_handle; acpi_handle rom_handle; } nouveau_dsm_priv; @@ -222,10 +221,9 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev) if (!dhandle) return false; - if (!acpi_has_method(dhandle, "_DSM")) { - nouveau_dsm_priv.other_handle = dhandle; + if (!acpi_has_method(dhandle, "_DSM")) return false; - } + if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, 1 << NOUVEAU_DSM_POWER)) retval |= NOUVEAU_DSM_HAS_MUX; @@ -301,16 +299,6 @@ static bool nouveau_dsm_detect(void) printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", acpi_method_name); nouveau_dsm_priv.dsm_detected = true; - /* - * On some systems hotplug events are generated for the device - * being switched off when _DSM is executed. They cause ACPI - * hotplug to trigger and attempt to remove the device from - * the system, which causes it to break down. Prevent that from - * happening by setting the no_hotplug flag for the involved - * ACPI device objects. - */ - acpi_bus_no_hotplug(nouveau_dsm_priv.dhandle); - acpi_bus_no_hotplug(nouveau_dsm_priv.other_handle); ret = true; } diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c index a9fb0d016d38..8bc7d0bbd3c8 100644 --- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c @@ -33,7 +33,6 @@ static struct radeon_atpx_priv { bool atpx_detected; /* handle for device - and atpx */ acpi_handle dhandle; - acpi_handle other_handle; struct radeon_atpx atpx; } radeon_atpx_priv; @@ -453,10 +452,9 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) return false; status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); - if (ACPI_FAILURE(status)) { - radeon_atpx_priv.other_handle = dhandle; + if (ACPI_FAILURE(status)) return false; - } + radeon_atpx_priv.dhandle = dhandle; radeon_atpx_priv.atpx.handle = atpx_handle; return true; @@ -540,16 +538,6 @@ static bool radeon_atpx_detect(void) printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n", acpi_method_name); radeon_atpx_priv.atpx_detected = true; - /* - * On some systems hotplug events are generated for the device - * being switched off when ATPX is executed. They cause ACPI - * hotplug to trigger and attempt to remove the device from - * the system, which causes it to break down. Prevent that from - * happening by setting the no_hotplug flag for the involved - * ACPI device objects. - */ - acpi_bus_no_hotplug(radeon_atpx_priv.dhandle); - acpi_bus_no_hotplug(radeon_atpx_priv.other_handle); return true; } return false; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bcfd808b1098..95c506961a13 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -204,10 +204,9 @@ struct acpi_device_flags { u32 match_driver:1; u32 initialized:1; u32 visited:1; - u32 no_hotplug:1; u32 hotplug_notify:1; u32 is_dock_station:1; - u32 reserved:22; + u32 reserved:23; }; /* File System */ @@ -412,7 +411,6 @@ void acpi_bus_private_data_handler(acpi_handle, void *); int acpi_bus_get_private_data(acpi_handle, void **); int acpi_bus_attach_private_data(acpi_handle, void *); void acpi_bus_detach_private_data(acpi_handle); -void acpi_bus_no_hotplug(acpi_handle handle); extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); -- cgit v1.2.3 From ce6eacb07e287c0c3c8b5c316a9a7f9cecf69e95 Mon Sep 17 00:00:00 2001 From: Bruno Prémont Date: Sun, 24 Aug 2014 23:13:15 +0200 Subject: vgaarb: Drop obsolete #ifndef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 20cde694027e ("x86, ia64: Move EFI_FB vga_default_device() initialization to pci_vga_fixup()") moved boot video device detection from efifb to x86 and ia64 pci/fixup.c. Remove the left-over #ifndef check that will always match since the corresponding arch-specific define is gone with above patch. Signed-off-by: Bruno Prémont Signed-off-by: Bjorn Helgaas CC: Matthew Garrett --- drivers/gpu/vga/vgaarb.c | 8 -------- include/linux/vgaarb.h | 2 -- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index 24ac52e6cd41..77711623b973 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -113,10 +113,8 @@ both: return 1; } -#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE /* this is only used a cookie - it should not be dereferenced */ static struct pci_dev *vga_default; -#endif static void vga_arb_device_card_gone(struct pci_dev *pdev); @@ -132,7 +130,6 @@ static struct vga_device *vgadev_find(struct pci_dev *pdev) } /* Returns the default VGA device (vgacon's babe) */ -#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE struct pci_dev *vga_default_device(void) { return vga_default; @@ -148,7 +145,6 @@ void vga_set_default_device(struct pci_dev *pdev) pci_dev_put(vga_default); vga_default = pci_dev_get(pdev); } -#endif static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) { @@ -584,14 +580,12 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) /* Deal with VGA default device. Use first enabled one * by default if arch doesn't have it's own hook */ -#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE if (vga_default == NULL && ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) { pr_info("vgaarb: setting as boot device: PCI:%s\n", pci_name(pdev)); vga_set_default_device(pdev); } -#endif vga_arbiter_check_bridge_sharing(vgadev); @@ -625,10 +619,8 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev) goto bail; } -#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE if (vga_default == pdev) vga_set_default_device(NULL); -#endif if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) vga_decode_count--; diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index 2c02f3a8d2ba..c37bd4d06739 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h @@ -182,7 +182,6 @@ extern void vga_put(struct pci_dev *pdev, unsigned int rsrc); * vga_get()... */ -#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE #ifdef CONFIG_VGA_ARB extern struct pci_dev *vga_default_device(void); extern void vga_set_default_device(struct pci_dev *pdev); @@ -190,7 +189,6 @@ extern void vga_set_default_device(struct pci_dev *pdev); static inline struct pci_dev *vga_default_device(void) { return NULL; }; static inline void vga_set_default_device(struct pci_dev *pdev) { }; #endif -#endif /** * vga_conflicts -- cgit v1.2.3 From 766a53d059d1500c9755c8af017bd411bd8f1b20 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 12 Sep 2014 17:51:29 -0400 Subject: vgaswitcheroo: add vga_switcheroo_fini_domain_pm_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers should call this on unload to unregister pmops. Bug: https://bugzilla.kernel.org/show_bug.cgi?id=84431 Reviewed-by: Ben Skeggs Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Signed-off-by: Pali Rohár Cc: Ben Skeggs --- drivers/gpu/vga/vga_switcheroo.c | 6 ++++++ include/linux/vga_switcheroo.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 6866448083b2..37ac7b5dbd06 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -660,6 +660,12 @@ int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain * } EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops); +void vga_switcheroo_fini_domain_pm_ops(struct device *dev) +{ + dev->pm_domain = NULL; +} +EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops); + static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index 502073a53dd3..b483abd34493 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -64,6 +64,7 @@ int vga_switcheroo_get_client_state(struct pci_dev *dev); void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic); int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain); +void vga_switcheroo_fini_domain_pm_ops(struct device *dev); int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain); #else @@ -82,6 +83,7 @@ static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {} static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } +static inline void vga_switcheroo_fini_domain_pm_ops(struct device *dev) {} static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } #endif -- cgit v1.2.3 From e8be1cf58d5b4ddbf8089700df8458f432ed5934 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 12 Sep 2014 16:00:32 -0700 Subject: [SCSI] fix regression that accidentally disabled block-based tcq The scsi blk-mq support accidentally flipped a conditional, which lead to never enabling block based tcq when using the legacy request path. Fixes: d285203cf647d7c9 scsi: add support for a blk-mq based I/O path. Reported-by: Hans de Goede Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- include/scsi/scsi_tcq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/scsi/scsi_tcq.h b/include/scsi/scsi_tcq.h index cdcc90b07ecb..e64583560701 100644 --- a/include/scsi/scsi_tcq.h +++ b/include/scsi/scsi_tcq.h @@ -68,7 +68,7 @@ static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth) return; if (!shost_use_blk_mq(sdev->host) && - blk_queue_tagged(sdev->request_queue)) + !blk_queue_tagged(sdev->request_queue)) blk_queue_init_tags(sdev->request_queue, depth, sdev->host->bqt); -- cgit v1.2.3