diff options
Diffstat (limited to 'drivers')
418 files changed, 19269 insertions, 6865 deletions
diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index db06f34419cf..1c052127548c 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -3416,27 +3416,28 @@ init_card(struct atm_dev *dev) size = sizeof(struct vc_map *) * card->tct_size; IPRINTK("%s: allocate %d byte for VC map.\n", card->name, size); - if (NULL == (card->vcs = vmalloc(size))) { + card->vcs = vzalloc(size); + if (!card->vcs) { printk("%s: memory allocation failure.\n", card->name); deinit_card(card); return -1; } - memset(card->vcs, 0, size); size = sizeof(struct vc_map *) * card->scd_size; IPRINTK("%s: allocate %d byte for SCD to VC mapping.\n", card->name, size); - if (NULL == (card->scd2vc = vmalloc(size))) { + card->scd2vc = vzalloc(size); + if (!card->scd2vc) { printk("%s: memory allocation failure.\n", card->name); deinit_card(card); return -1; } - memset(card->scd2vc, 0, size); size = sizeof(struct tst_info) * (card->tst_size - 2); IPRINTK("%s: allocate %d byte for TST to VC mapping.\n", card->name, size); - if (NULL == (card->soft_tst = vmalloc(size))) { + card->soft_tst = vmalloc(size); + if (!card->soft_tst) { printk("%s: memory allocation failure.\n", card->name); deinit_card(card); return -1; diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index e828c5487493..f5569699f31c 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -1457,10 +1457,9 @@ static int __devinit vcc_table_allocate(struct lanai_dev *lanai) return (lanai->vccs == NULL) ? -ENOMEM : 0; #else int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); - lanai->vccs = (struct lanai_vcc **) vmalloc(bytes); + lanai->vccs = vzalloc(bytes); if (unlikely(lanai->vccs == NULL)) return -ENOMEM; - memset(lanai->vccs, 0, bytes); return 0; #endif } diff --git a/drivers/base/core.c b/drivers/base/core.c index bc8729d603a7..82c865452c70 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1764,8 +1764,8 @@ void device_shutdown(void) #ifdef CONFIG_PRINTK -static int __dev_printk(const char *level, const struct device *dev, - struct va_format *vaf) +int __dev_printk(const char *level, const struct device *dev, + struct va_format *vaf) { if (!dev) return printk("%s(NULL device *): %pV", level, vaf); @@ -1773,6 +1773,7 @@ static int __dev_printk(const char *level, const struct device *dev, return printk("%s%s %s: %pV", level, dev_driver_string(dev), dev_name(dev), vaf); } +EXPORT_SYMBOL(__dev_printk); int dev_printk(const char *level, const struct device *dev, const char *fmt, ...) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6658da743c3a..142e3d600f14 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -147,6 +147,9 @@ probe_failed: printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev_name(dev), ret); + } else { + pr_debug("%s: probe of %s rejects match %d\n", + drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2840ed4668c1..8272d92d22c0 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -224,13 +224,48 @@ int memory_isolate_notify(unsigned long val, void *v) } /* + * The probe routines leave the pages reserved, just as the bootmem code does. + * Make sure they're still that way. + */ +static bool pages_correctly_reserved(unsigned long start_pfn, + unsigned long nr_pages) +{ + int i, j; + struct page *page; + unsigned long pfn = start_pfn; + + /* + * memmap between sections is not contiguous except with + * SPARSEMEM_VMEMMAP. We lookup the page once per section + * and assume memmap is contiguous within each section + */ + for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) { + if (WARN_ON_ONCE(!pfn_valid(pfn))) + return false; + page = pfn_to_page(pfn); + + for (j = 0; j < PAGES_PER_SECTION; j++) { + if (PageReserved(page + j)) + continue; + + printk(KERN_WARNING "section number %ld page number %d " + "not reserved, was it already online?\n", + pfn_to_section_nr(pfn), j); + + return false; + } + } + + return true; +} + +/* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int memory_block_action(unsigned long phys_index, unsigned long action) { - int i; unsigned long start_pfn, start_paddr; unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; struct page *first_page; @@ -238,26 +273,13 @@ memory_block_action(unsigned long phys_index, unsigned long action) first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT); - /* - * The probe routines leave the pages reserved, just - * as the bootmem code does. Make sure they're still - * that way. - */ - if (action == MEM_ONLINE) { - for (i = 0; i < nr_pages; i++) { - if (PageReserved(first_page+i)) - continue; - - printk(KERN_WARNING "section number %ld page number %d " - "not reserved, was it already online?\n", - phys_index, i); - return -EBUSY; - } - } - switch (action) { case MEM_ONLINE: start_pfn = page_to_pfn(first_page); + + if (!pages_correctly_reserved(start_pfn, nr_pages)) + return -EBUSY; + ret = online_pages(start_pfn, nr_pages); break; case MEM_OFFLINE: @@ -380,9 +402,13 @@ memory_probe_store(struct class *class, struct class_attribute *attr, u64 phys_addr; int nid; int i, ret; + unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block; phys_addr = simple_strtoull(buf, NULL, 0); + if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1)) + return -EINVAL; + for (i = 0; i < sections_per_block; i++) { nid = memory_add_physaddr_to_nid(phys_addr); ret = add_memory(nid, phys_addr, diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 99a5272d7c2f..7a24895543e7 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -375,52 +375,64 @@ void platform_device_unregister(struct platform_device *pdev) EXPORT_SYMBOL_GPL(platform_device_unregister); /** - * platform_device_register_resndata - add a platform-level device with + * platform_device_register_full - add a platform-level device with * resources and platform-specific data * - * @parent: parent device for the device we're adding - * @name: base name of the device we're adding - * @id: instance id - * @res: set of resources that needs to be allocated for the device - * @num: number of resources - * @data: platform specific data for this platform device - * @size: size of platform specific data + * @pdevinfo: data used to create device * * Returns &struct platform_device pointer on success, or ERR_PTR() on error. */ -struct platform_device *platform_device_register_resndata( - struct device *parent, - const char *name, int id, - const struct resource *res, unsigned int num, - const void *data, size_t size) +struct platform_device *platform_device_register_full( + struct platform_device_info *pdevinfo) { int ret = -ENOMEM; struct platform_device *pdev; - pdev = platform_device_alloc(name, id); + pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id); if (!pdev) - goto err; - - pdev->dev.parent = parent; + goto err_alloc; + + pdev->dev.parent = pdevinfo->parent; + + if (pdevinfo->dma_mask) { + /* + * This memory isn't freed when the device is put, + * I don't have a nice idea for that though. Conceptually + * dma_mask in struct device should not be a pointer. + * See http://thread.gmane.org/gmane.linux.kernel.pci/9081 + */ + pdev->dev.dma_mask = + kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); + if (!pdev->dev.dma_mask) + goto err; + + *pdev->dev.dma_mask = pdevinfo->dma_mask; + pdev->dev.coherent_dma_mask = pdevinfo->dma_mask; + } - ret = platform_device_add_resources(pdev, res, num); + ret = platform_device_add_resources(pdev, + pdevinfo->res, pdevinfo->num_res); if (ret) goto err; - ret = platform_device_add_data(pdev, data, size); + ret = platform_device_add_data(pdev, + pdevinfo->data, pdevinfo->size_data); if (ret) goto err; ret = platform_device_add(pdev); if (ret) { err: + kfree(pdev->dev.dma_mask); + +err_alloc: platform_device_put(pdev); return ERR_PTR(ret); } return pdev; } -EXPORT_SYMBOL_GPL(platform_device_register_resndata); +EXPORT_SYMBOL_GPL(platform_device_register_full); static int platform_drv_probe(struct device *_dev) { @@ -614,7 +626,7 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) return rc; add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, - (pdev->id_entry) ? pdev->id_entry->name : pdev->name); + pdev->name); return 0; } diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index 7b976296b564..912f585a760f 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -378,15 +378,14 @@ static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want) * thread. As we have no disk yet, we are not in the IO path, * not even the IO path of the peer. */ bytes = sizeof(struct page *)*want; - new_pages = kmalloc(bytes, GFP_KERNEL); + new_pages = kzalloc(bytes, GFP_KERNEL); if (!new_pages) { - new_pages = vmalloc(bytes); + new_pages = vzalloc(bytes); if (!new_pages) return NULL; vmalloced = 1; } - memset(new_pages, 0, bytes); if (want >= have) { for (i = 0; i < have; i++) new_pages[i] = old_pages[i]; diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index ef2ceed3be4b..1706d60b8c99 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -28,7 +28,6 @@ #include <linux/compiler.h> #include <linux/types.h> -#include <linux/version.h> #include <linux/list.h> #include <linux/sched.h> #include <linux/bitops.h> @@ -928,7 +927,7 @@ struct drbd_md { #define NL_INT64(pn,pr,member) __u64 member; #define NL_BIT(pn,pr,member) unsigned member:1; #define NL_STRING(pn,pr,member,len) unsigned char member[len]; int member ## _len; -#include "linux/drbd_nl.h" +#include <linux/drbd_nl.h> struct drbd_backing_dev { struct block_device *backing_bdev; diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 0feab261e295..af2a25049bce 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -94,7 +94,7 @@ static int name ## _from_tags(struct drbd_conf *mdev, \ arg->member ## _len = dlen; \ memcpy(arg->member, tags, min_t(size_t, dlen, len)); \ break; -#include "linux/drbd_nl.h" +#include <linux/drbd_nl.h> /* Generate the struct to tag_list functions */ #define NL_PACKET(name, number, fields) \ @@ -129,7 +129,7 @@ name ## _to_tags(struct drbd_conf *mdev, \ put_unaligned(arg->member ## _len, tags++); \ memcpy(tags, arg->member, arg->member ## _len); \ tags = (unsigned short *)((char *)tags + arg->member ## _len); -#include "linux/drbd_nl.h" +#include <linux/drbd_nl.h> void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name); void drbd_nl_send_reply(struct cn_msg *, int); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 15f65b5f3fc7..fe3c3249cec4 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -192,7 +192,7 @@ static ssize_t rbd_snap_add(struct device *dev, const char *buf, size_t count); static void __rbd_remove_snap_dev(struct rbd_device *rbd_dev, - struct rbd_snap *snap);; + struct rbd_snap *snap); static struct rbd_device *dev_to_rbd(struct device *dev) diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 2330a9ad5e95..1540792b1e54 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -396,7 +396,7 @@ static int xen_blkbk_map(struct blkif_request *req, continue; ret = m2p_add_override(PFN_DOWN(map[i].dev_bus_addr), - blkbk->pending_page(pending_req, i), false); + blkbk->pending_page(pending_req, i), NULL); if (ret) { pr_alert(DRV_PFX "Failed to install M2P override for %lx (ret: %d)\n", (unsigned long)map[i].dev_bus_addr, ret); diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 00c57c90e2d6..c4bd34063ecc 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -27,7 +27,6 @@ #ifndef __XEN_BLKIF__BACKEND__COMMON_H__ #define __XEN_BLKIF__BACKEND__COMMON_H__ -#include <linux/version.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/slab.h> diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 7f521d4ac657..c827d737ccee 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -81,7 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = { .io_port_2 = 0x7a, }; -static const struct btmrvl_sdio_device btmrvl_sdio_sd6888 = { +static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "sd8688_helper.bin", .firmware = "sd8688.bin", .reg = &btmrvl_reg_8688, @@ -98,7 +98,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8688 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105), - .driver_data = (unsigned long) &btmrvl_sdio_sd6888 }, + .driver_data = (unsigned long) &btmrvl_sdio_sd8688 }, /* Marvell SD8787 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index f27d0d0816d3..4b71647782d0 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -171,7 +171,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } got_gatt = 1; - bridge->key_list = vmalloc(PAGE_SIZE * 4); + bridge->key_list = vzalloc(PAGE_SIZE * 4); if (bridge->key_list == NULL) { dev_err(&bridge->dev->dev, "can't allocate memory for key lists\n"); @@ -181,7 +181,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) got_keylist = 1; /* FIXME vmalloc'd memory not guaranteed contiguous */ - memset(bridge->key_list, 0, PAGE_SIZE * 4); if (bridge->driver->configure()) { dev_err(&bridge->dev->dev, "error configuring host chipset\n"); diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index a7346ab97a3c..f4837a893dfa 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -40,10 +40,7 @@ #define APM_MINOR_DEV 134 /* - * See Documentation/Config.help for the configuration options. - * - * Various options can be changed at boot time as follows: - * (We allow underscores for compatibility with the modules code) + * One option can be changed at boot time as follows: * apm=on/off enable/disable APM */ @@ -300,17 +297,13 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg) /* * Wait for the suspend/resume to complete. If there * are pending acknowledges, we wait here for them. + * wait_event_freezable() is interruptible and pending + * signal can cause busy looping. We aren't doing + * anything critical, chill a bit on each iteration. */ - freezer_do_not_count(); - - wait_event(apm_suspend_waitqueue, - as->suspend_state == SUSPEND_DONE); - - /* - * Since we are waiting until the suspend is done, the - * try_to_freeze() in freezer_count() will not trigger - */ - freezer_count(); + while (wait_event_freezable(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE)) + msleep(10); break; case SUSPEND_ACKTO: as->suspend_result = -ETIMEDOUT; diff --git a/drivers/char/raw.c b/drivers/char/raw.c index b33e8ea314ed..b6de2c047145 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -324,13 +324,12 @@ static int __init raw_init(void) max_raw_minors = MAX_RAW_MINORS; } - raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors); + raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors); if (!raw_devices) { printk(KERN_ERR "Not enough memory for raw device structures\n"); ret = -ENOMEM; goto error; } - memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors); ret = register_chrdev_region(dev, max_raw_minors, "raw"); if (ret) diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index dfa8b3062fda..ccd124ab7ca7 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -80,6 +80,7 @@ #include <linux/bcd.h> #include <linux/delay.h> #include <linux/uaccess.h> +#include <linux/ratelimit.h> #include <asm/current.h> #include <asm/system.h> @@ -1195,10 +1196,8 @@ static void rtc_dropped_irq(unsigned long data) spin_unlock_irq(&rtc_lock); - if (printk_ratelimit()) { - printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", - freq); - } + printk_ratelimited(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", + freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 9ca5c021d0b6..361a1dff8f77 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -966,6 +966,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr, { struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip->vendor.duration[TPM_LONG] == 0) + return 0; + return sprintf(buf, "%d %d %d [%s]\n", jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 891360edecdd..629b3ec698e2 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -725,7 +725,7 @@ static int __init cpufreq_gov_dbs_init(void) dbs_tuners_ins.down_differential = MICRO_FREQUENCY_DOWN_DIFFERENTIAL; /* - * In no_hz/micro accounting case we set the minimum frequency + * In nohz/micro accounting case we set the minimum frequency * not depending on HZ, but fixed (very low). The deferred * timer might skip some samples if idle/sleeping as needed. */ diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 7fd4e3e5ad5f..3475a09f946b 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -48,7 +48,7 @@ static void radeon_fence_write(struct radeon_device *rdev, u32 seq) scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; else scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; - rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq);; + rdev->wb.wb[scratch_index/4] = cpu_to_le32(seq); } else WREG32(rdev->fence_drv.scratch_reg, seq); } diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1130a8987125..22a4a051f221 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -69,7 +69,7 @@ config HID_ACRUX Say Y here if you want to enable support for ACRUX game controllers. config HID_ACRUX_FF - tristate "ACRUX force feedback support" + bool "ACRUX force feedback support" depends on HID_ACRUX select INPUT_FF_MEMLESS ---help--- @@ -245,6 +245,15 @@ config HID_LOGITECH ---help--- Support for Logitech devices that are not fully compliant with HID standard. +config HID_LOGITECH_DJ + tristate "Logitech Unifying receivers full support" + depends on HID_LOGITECH + default m + ---help--- + Say Y if you want support for Logitech Unifying receivers and devices. + Unifying receivers are capable of pairing up to 6 Logitech compliant + devices to the same receiver. + config LOGITECH_FF bool "Logitech force feedback support" depends on HID_LOGITECH @@ -278,13 +287,21 @@ config LOGIG940_FF Say Y here if you want to enable force feedback support for Logitech Flight System G940 devices. -config LOGIWII_FF - bool "Logitech Speed Force Wireless force feedback support" +config LOGIWHEELS_FF + bool "Logitech wheels configuration and force feedback support" depends on HID_LOGITECH select INPUT_FF_MEMLESS + default LOGITECH_FF help - Say Y here if you want to enable force feedback support for Logitech - Speed Force Wireless (Wii) devices. + Say Y here if you want to enable force feedback and range setting + support for following Logitech wheels: + - Logitech Driving Force + - Logitech Driving Force Pro + - Logitech Driving Force GT + - Logitech G25 + - Logitech G27 + - Logitech MOMO/MOMO 2 + - Logitech Formula Force EX config HID_MAGICMOUSE tristate "Apple MagicMouse multi-touch support" @@ -328,6 +345,7 @@ config HID_MULTITOUCH - Hanvon dual touch panels - Ilitek dual touch panels - IrTouch Infrared USB panels + - LG Display panels (Dell ST2220Tc) - Lumio CrystalTouch panels - MosArt dual-touch panels - PenMount dual touch panels @@ -441,6 +459,13 @@ config HID_PICOLCD_LEDS ---help--- Provide access to PicoLCD's GPO pins via leds class. +config HID_PRIMAX + tristate "Primax non-fully HID-compliant devices" + depends on USB_HID + ---help--- + Support for Primax devices that are not fully compliant with the + HID standard. + config HID_QUANTA tristate "Quanta Optical Touch panels" depends on USB_HID @@ -539,7 +564,11 @@ config HID_SMARTJOYPLUS tristate "SmartJoy PLUS PS2/USB adapter support" depends on USB_HID ---help--- - Support for SmartJoy PLUS PS2/USB adapter. + Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box, + Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro. + + Note that DDR (Dance Dance Revolution) mode is not supported, nor + is pressure sensitive buttons on the pro models. config SMARTJOYPLUS_FF bool "SmartJoy PLUS PS2/USB adapter force feedback support" @@ -590,6 +619,7 @@ config HID_WIIMOTE tristate "Nintendo Wii Remote support" depends on BT_HIDP depends on LEDS_CLASS + select POWER_SUPPLY ---help--- Support for the Nintendo Wii Remote bluetooth device. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0a0a38e9fd28..1e0d2a638b28 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -21,7 +21,7 @@ endif ifdef CONFIG_LOGIG940_FF hid-logitech-y += hid-lg3ff.o endif -ifdef CONFIG_LOGIWII_FF +ifdef CONFIG_LOGIWHEELS_FF hid-logitech-y += hid-lg4ff.o endif @@ -43,6 +43,7 @@ obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o @@ -54,6 +55,7 @@ obj-$(CONFIG_HID_QUANTA) += hid-quanta.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o +obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 18b3bc646bf3..9bc7b03269df 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -183,6 +183,9 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) table = macbookair_fn_keys; + else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI && + hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) + table = macbookair_fn_keys; else if (hid->product < 0x21d || hid->product >= 0x300) table = powerbook_fn_keys; else @@ -493,6 +496,18 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 121514149e0b..3bdb4500f95e 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -6,7 +6,7 @@ * Xbox 360 controller. * * 1a34:0802 "ACRUX USB GAMEPAD 8116" - * - tested with a EXEQ EQ-PCU-02090 game controller. + * - tested with an EXEQ EQ-PCU-02090 game controller. * * Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru> */ @@ -45,7 +45,10 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect { struct hid_device *hid = input_get_drvdata(dev); struct axff_device *axff = data; + struct hid_report *report = axff->report; + int field_count = 0; int left, right; + int i, j; left = effect->u.rumble.strong_magnitude; right = effect->u.rumble.weak_magnitude; @@ -55,10 +58,14 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect left = left * 0xff / 0xffff; right = right * 0xff / 0xffff; - axff->report->field[0]->value[0] = left; - axff->report->field[1]->value[0] = right; - axff->report->field[2]->value[0] = left; - axff->report->field[3]->value[0] = right; + for (i = 0; i < report->maxfield; i++) { + for (j = 0; j < report->field[i]->report_count; j++) { + report->field[i]->value[j] = + field_count % 2 ? right : left; + field_count++; + } + } + dbg_hid("running with 0x%02x 0x%02x", left, right); usbhid_submit_report(hid, axff->report, USB_DIR_OUT); @@ -72,6 +79,8 @@ static int axff_init(struct hid_device *hid) struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list); struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; + int field_count = 0; + int i, j; int error; if (list_empty(report_list)) { @@ -80,9 +89,16 @@ static int axff_init(struct hid_device *hid) } report = list_first_entry(report_list, struct hid_report, list); + for (i = 0; i < report->maxfield; i++) { + for (j = 0; j < report->field[i]->report_count; j++) { + report->field[i]->value[j] = 0x00; + field_count++; + } + } - if (report->maxfield < 4) { - hid_err(hid, "no fields in the report: %d\n", report->maxfield); + if (field_count < 4) { + hid_err(hid, "not enough fields in the report: %d\n", + field_count); return -ENODEV; } @@ -97,13 +113,9 @@ static int axff_init(struct hid_device *hid) goto err_free_mem; axff->report = report; - axff->report->field[0]->value[0] = 0x00; - axff->report->field[1]->value[0] = 0x00; - axff->report->field[2]->value[0] = 0x00; - axff->report->field[3]->value[0] = 0x00; usbhid_submit_report(hid, axff->report, USB_DIR_OUT); - hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>\n"); + hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n"); return 0; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 242353df3dc4..91adcc5bad28 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -29,6 +29,7 @@ #include <linux/wait.h> #include <linux/vmalloc.h> #include <linux/sched.h> +#include <linux/semaphore.h> #include <linux/hid.h> #include <linux/hiddev.h> @@ -1085,16 +1086,25 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i struct hid_report *report; char *buf; unsigned int i; - int ret; + int ret = 0; - if (!hid || !hid->driver) + if (!hid) return -ENODEV; + + if (down_trylock(&hid->driver_lock)) + return -EBUSY; + + if (!hid->driver) { + ret = -ENODEV; + goto unlock; + } report_enum = hid->report_enum + type; hdrv = hid->driver; if (!size) { dbg_hid("empty report\n"); - return -1; + ret = -1; + goto unlock; } buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); @@ -1118,18 +1128,24 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i nomem: report = hid_get_report(report_enum, data); - if (!report) - return -1; + if (!report) { + ret = -1; + goto unlock; + } if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { ret = hdrv->raw_event(hid, report, data, size); - if (ret != 0) - return ret < 0 ? ret : 0; + if (ret != 0) { + ret = ret < 0 ? ret : 0; + goto unlock; + } } hid_report_raw_event(hid, type, data, size, interrupt); - return 0; +unlock: + up(&hid->driver_lock); + return ret; } EXPORT_SYMBOL_GPL(hid_input_report); @@ -1212,6 +1228,12 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, connect_mask & HID_CONNECT_HIDINPUT_FORCE)) hdev->claimed |= HID_CLAIMED_INPUT; + if (hdev->quirks & HID_QUIRK_MULTITOUCH) { + /* this device should be handled by hid-multitouch, skip it */ + hdev->quirks &= ~HID_QUIRK_MULTITOUCH; + return -ENODEV; + } + if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && !hdev->hiddev_connect(hdev, connect_mask & HID_CONNECT_HIDDEV_FORCE)) @@ -1343,6 +1365,12 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, @@ -1391,6 +1419,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, @@ -1399,6 +1428,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, @@ -1420,8 +1450,11 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, @@ -1461,6 +1494,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, @@ -1501,6 +1535,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, { HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, @@ -1620,10 +1658,15 @@ static int hid_device_probe(struct device *dev) const struct hid_device_id *id; int ret = 0; + if (down_interruptible(&hdev->driver_lock)) + return -EINTR; + if (!hdev->driver) { id = hid_match_device(hdev, hdrv); - if (id == NULL) - return -ENODEV; + if (id == NULL) { + ret = -ENODEV; + goto unlock; + } hdev->driver = hdrv; if (hdrv->probe) { @@ -1636,14 +1679,20 @@ static int hid_device_probe(struct device *dev) if (ret) hdev->driver = NULL; } +unlock: + up(&hdev->driver_lock); return ret; } static int hid_device_remove(struct device *dev) { struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct hid_driver *hdrv = hdev->driver; + struct hid_driver *hdrv; + + if (down_interruptible(&hdev->driver_lock)) + return -EINTR; + hdrv = hdev->driver; if (hdrv) { if (hdrv->remove) hdrv->remove(hdev); @@ -1652,6 +1701,7 @@ static int hid_device_remove(struct device *dev) hdev->driver = NULL; } + up(&hdev->driver_lock); return 0; } @@ -1892,6 +1942,12 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } @@ -1999,6 +2055,7 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); + sema_init(&hdev->driver_lock, 1); return hdev; err: diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index bae48745bb42..9a243ca96e6d 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -450,6 +450,11 @@ void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) { seq_printf(f, "Logical("); hid_resolv_usage(field->logical, f); seq_printf(f, ")\n"); } + if (field->application) { + tab(n, f); + seq_printf(f, "Application("); + hid_resolv_usage(field->application, f); seq_printf(f, ")\n"); + } tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage); for (j = 0; j < field->maxusage; j++) { tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7484e1b67249..1680e99b4816 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -112,6 +112,12 @@ #define USB_DEVICE_ID_APPLE_ALU_REVB_ANSI 0x024f #define USB_DEVICE_ID_APPLE_ALU_REVB_ISO 0x0250 #define USB_DEVICE_ID_APPLE_ALU_REVB_JIS 0x0251 +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI 0x0249 +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO 0x024a +#define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS 0x024b +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI 0x024c +#define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO 0x024d +#define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS 0x024e #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b @@ -351,6 +357,9 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_IDEACOM 0x1cb6 +#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 + #define USB_VENDOR_ID_ILITEK 0x222a #define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 @@ -423,6 +432,9 @@ #define USB_DEVICE_ID_LD_HYBRID 0x2090 #define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 +#define USB_VENDOR_ID_LG 0x1fd2 +#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 + #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 @@ -440,6 +452,7 @@ #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 +#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a #define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b #define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a @@ -447,6 +460,8 @@ #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532 #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 @@ -678,6 +693,9 @@ #define USB_VENDOR_ID_WISEGROUP_LTD 0x6666 #define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677 #define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 +#define USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO 0x8801 +#define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802 +#define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804 #define USB_VENDOR_ID_X_TENSIONS 0x1ae7 #define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001 @@ -693,4 +711,7 @@ #define USB_VENDOR_ID_ZYDACRON 0x13EC #define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 +#define USB_VENDOR_ID_PRIMAX 0x0461 +#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 + #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 6559e2e3364e..f333139d1a48 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -474,6 +474,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel map_key_clear(BTN_STYLUS2); break; + case 0x51: /* ContactID */ + device->quirks |= HID_QUIRK_MULTITOUCH; + goto unknown; + default: goto unknown; } break; @@ -978,6 +982,13 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } + if (hid->quirks & HID_QUIRK_MULTITOUCH) { + /* generic hid does not know how to handle multitouch devices */ + if (hidinput) + goto out_cleanup; + goto out_unwind; + } + if (hidinput && input_register_device(hidinput->input)) goto out_cleanup; diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index a7f916e8fc32..e7a7bd1eb34a 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -363,7 +363,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & (LG_FF | LG_FF2 | LG_FF3)) + if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4)) connect_mask &= ~HID_CONNECT_FF; ret = hid_hw_start(hdev, connect_mask); @@ -372,7 +372,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & LG_FF4) { + /* Setup wireless link with Logitech Wii wheel */ + if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); @@ -405,6 +406,15 @@ err_free: return ret; } +static void lg_remove(struct hid_device *hdev) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + if(quirks & LG_FF4) + lg4ff_deinit(hdev); + + hid_hw_stop(hdev); +} + static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), .driver_data = LG_RDESC | LG_WIRELESS }, @@ -431,7 +441,7 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), .driver_data = LG_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), - .driver_data = LG_NOGET | LG_FF }, + .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), .driver_data = LG_FF2 }, @@ -444,15 +454,17 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), - .driver_data = LG_FF }, + .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), - .driver_data = LG_FF }, + .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), - .driver_data = LG_FF }, + .driver_data = LG_FF4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL), + .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL), - .driver_data = LG_FF }, + .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL), - .driver_data = LG_NOGET | LG_FF }, + .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), @@ -478,6 +490,7 @@ static struct hid_driver lg_driver = { .input_mapped = lg_input_mapped, .event = lg_event, .probe = lg_probe, + .remove = lg_remove, }; static int __init lg_init(void) diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index b0100ba2ae0b..4b097286dc78 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -19,10 +19,12 @@ int lg3ff_init(struct hid_device *hdev); static inline int lg3ff_init(struct hid_device *hdev) { return -1; } #endif -#ifdef CONFIG_LOGIWII_FF +#ifdef CONFIG_LOGIWHEELS_FF int lg4ff_init(struct hid_device *hdev); +int lg4ff_deinit(struct hid_device *hdev); #else static inline int lg4ff_init(struct hid_device *hdev) { return -1; } +static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } #endif #endif diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index fa550c8e1d1b..103f30d93f76 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -29,19 +29,108 @@ #include "usbhid/usbhid.h" #include "hid-lg.h" +#include "hid-ids.h" -struct lg4ff_device { - struct hid_report *report; +#define DFGT_REV_MAJ 0x13 +#define DFGT_REV_MIN 0x22 +#define DFP_REV_MAJ 0x11 +#define DFP_REV_MIN 0x06 +#define FFEX_REV_MAJ 0x21 +#define FFEX_REV_MIN 0x00 +#define G25_REV_MAJ 0x12 +#define G25_REV_MIN 0x22 +#define G27_REV_MAJ 0x12 +#define G27_REV_MIN 0x38 + +#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) + +static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); +static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range); +static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); + +static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); + +static bool list_inited; + +struct lg4ff_device_entry { + char *device_id; /* Use name in respective kobject structure's address as the ID */ + __u16 range; + __u16 min_range; + __u16 max_range; + __u8 leds; + struct list_head list; + void (*set_range)(struct hid_device *hid, u16 range); }; -static const signed short ff4_wheel_ac[] = { +static struct lg4ff_device_entry device_list; + +static const signed short lg4ff_wheel_effects[] = { FF_CONSTANT, FF_AUTOCENTER, -1 }; -static int hid_lg4ff_play(struct input_dev *dev, void *data, - struct ff_effect *effect) +struct lg4ff_wheel { + const __u32 product_id; + const signed short *ff_effects; + const __u16 min_range; + const __u16 max_range; + void (*set_range)(struct hid_device *hid, u16 range); +}; + +static const struct lg4ff_wheel lg4ff_devices[] = { + {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, + {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, + {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp}, + {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25}, + {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25}, + {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25}, + {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL}, + {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} +}; + +struct lg4ff_native_cmd { + const __u8 cmd_num; /* Number of commands to send */ + const __u8 cmd[]; +}; + +struct lg4ff_usb_revision { + const __u16 rev_maj; + const __u16 rev_min; + const struct lg4ff_native_cmd *command; +}; + +static const struct lg4ff_native_cmd native_dfp = { + 1, + {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +static const struct lg4ff_native_cmd native_dfgt = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ + 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ +}; + +static const struct lg4ff_native_cmd native_g25 = { + 1, + {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +static const struct lg4ff_native_cmd native_g27 = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ + 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ +}; + +static const struct lg4ff_usb_revision lg4ff_revs[] = { + {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */ + {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ + {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ + {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ +}; + +static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { struct hid_device *hid = input_get_drvdata(dev); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; @@ -55,13 +144,12 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ CLAMP(x); report->field[0]->value[0] = 0x11; /* Slot 1 */ - report->field[0]->value[1] = 0x10; + report->field[0]->value[1] = 0x08; report->field[0]->value[2] = x; - report->field[0]->value[3] = 0x00; + report->field[0]->value[3] = 0x80; report->field[0]->value[4] = 0x00; - report->field[0]->value[5] = 0x08; + report->field[0]->value[5] = 0x00; report->field[0]->value[6] = 0x00; - dbg_hid("Autocenter, x=0x%02X\n", x); usbhid_submit_report(hid, report, USB_DIR_OUT); break; @@ -69,24 +157,184 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, return 0; } -static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude) +/* Sends default autocentering command compatible with + * all wheels except Formula Force EX */ +static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude) { struct hid_device *hid = input_get_drvdata(dev); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct hid_report *report = list_entry(report_list->next, struct hid_report, list); - __s32 *value = report->field[0]->value; - *value++ = 0xfe; - *value++ = 0x0d; - *value++ = 0x07; - *value++ = 0x07; - *value++ = (magnitude >> 8) & 0xff; - *value++ = 0x00; - *value = 0x00; + report->field[0]->value[0] = 0xfe; + report->field[0]->value[1] = 0x0d; + report->field[0]->value[2] = magnitude >> 13; + report->field[0]->value[3] = magnitude >> 13; + report->field[0]->value[4] = magnitude >> 8; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + +/* Sends autocentering command compatible with Formula Force EX */ +static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + magnitude = magnitude * 90 / 65535; + + + report->field[0]->value[0] = 0xfe; + report->field[0]->value[1] = 0x03; + report->field[0]->value[2] = magnitude >> 14; + report->field[0]->value[3] = magnitude >> 14; + report->field[0]->value[4] = magnitude; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + +/* Sends command to set range compatible with G25/G27/Driving Force GT */ +static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range) +{ + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + dbg_hid("G25/G27/DFGT: setting range to %u\n", range); + + report->field[0]->value[0] = 0xf8; + report->field[0]->value[1] = 0x81; + report->field[0]->value[2] = range & 0x00ff; + report->field[0]->value[3] = (range & 0xff00) >> 8; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + +/* Sends commands to set range compatible with Driving Force Pro wheel */ +static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) +{ + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int start_left, start_right, full_range; + dbg_hid("Driving Force Pro: setting range to %u\n", range); + + /* Prepare "coarse" limit command */ + report->field[0]->value[0] = 0xf8; + report->field[0]->value[1] = 0x00; /* Set later */ + report->field[0]->value[2] = 0x00; + report->field[0]->value[3] = 0x00; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + if (range > 200) { + report->field[0]->value[1] = 0x03; + full_range = 900; + } else { + report->field[0]->value[1] = 0x02; + full_range = 200; + } + usbhid_submit_report(hid, report, USB_DIR_OUT); + + /* Prepare "fine" limit command */ + report->field[0]->value[0] = 0x81; + report->field[0]->value[1] = 0x0b; + report->field[0]->value[2] = 0x00; + report->field[0]->value[3] = 0x00; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + + if (range == 200 || range == 900) { /* Do not apply any fine limit */ + usbhid_submit_report(hid, report, USB_DIR_OUT); + return; + } + + /* Construct fine limit command */ + start_left = (((full_range - range + 1) * 2047) / full_range); + start_right = 0xfff - start_left; + + report->field[0]->value[2] = start_left >> 4; + report->field[0]->value[3] = start_right >> 4; + report->field[0]->value[4] = 0xff; + report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); + report->field[0]->value[6] = 0xff; usbhid_submit_report(hid, report, USB_DIR_OUT); } +static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd) +{ + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + __u8 i, j; + + j = 0; + while (j < 7*cmd->cmd_num) { + for (i = 0; i < 7; i++) + report->field[0]->value[i] = cmd->cmd[j++]; + + usbhid_submit_report(hid, report, USB_DIR_OUT); + } +} + +/* Read current range and display it in terminal */ +static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lg4ff_device_entry *uninitialized_var(entry); + struct list_head *h; + struct hid_device *hid = to_hid_device(dev); + size_t count; + + list_for_each(h, &device_list.list) { + entry = list_entry(h, struct lg4ff_device_entry, list); + if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) + break; + } + if (h == &device_list.list) { + dbg_hid("Device not found!"); + return 0; + } + + count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range); + return count; +} + +/* Set range to user specified value, call appropriate function + * according to the type of the wheel */ +static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct lg4ff_device_entry *uninitialized_var(entry); + struct list_head *h; + struct hid_device *hid = to_hid_device(dev); + __u16 range = simple_strtoul(buf, NULL, 10); + + list_for_each(h, &device_list.list) { + entry = list_entry(h, struct lg4ff_device_entry, list); + if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) + break; + } + if (h == &device_list.list) { + dbg_hid("Device not found!"); + return count; + } + + if (range == 0) + range = entry->max_range; + + /* Check if the wheel supports range setting + * and that the range is within limits for the wheel */ + if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) { + entry->set_range(hid, range); + entry->range = range; + } + + return count; +} int lg4ff_init(struct hid_device *hid) { @@ -95,9 +343,10 @@ int lg4ff_init(struct hid_device *hid) struct input_dev *dev = hidinput->input; struct hid_report *report; struct hid_field *field; - const signed short *ff_bits = ff4_wheel_ac; - int error; - int i; + struct lg4ff_device_entry *entry; + struct usb_device_descriptor *udesc; + int error, i, j; + __u16 bcdDevice, rev_maj, rev_min; /* Find the report to use */ if (list_empty(report_list)) { @@ -118,18 +367,122 @@ int lg4ff_init(struct hid_device *hid) return -1; } - for (i = 0; ff_bits[i] >= 0; i++) - set_bit(ff_bits[i], dev->ffbit); + /* Check what wheel has been connected */ + for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { + if (hid->product == lg4ff_devices[i].product_id) { + dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id); + break; + } + } + + if (i == ARRAY_SIZE(lg4ff_devices)) { + hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to" + "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n"); + return -1; + } + + /* Attempt to switch wheel to native mode when applicable */ + udesc = &(hid_to_usb_dev(hid)->descriptor); + if (!udesc) { + hid_err(hid, "NULL USB device descriptor\n"); + return -1; + } + bcdDevice = le16_to_cpu(udesc->bcdDevice); + rev_maj = bcdDevice >> 8; + rev_min = bcdDevice & 0xff; + + if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) { + dbg_hid("Generic wheel detected, can it do native?\n"); + dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min); + + for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) { + if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) { + hid_lg4ff_switch_native(hid, lg4ff_revs[j].command); + hid_info(hid, "Switched to native mode\n"); + } + } + } + + /* Set supported force feedback capabilities */ + for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++) + set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit); error = input_ff_create_memless(dev, NULL, hid_lg4ff_play); if (error) return error; - if (test_bit(FF_AUTOCENTER, dev->ffbit)) - dev->ff->set_autocenter = hid_lg4ff_set_autocenter; + /* Check if autocentering is available and + * set the centering force to zero by default */ + if (test_bit(FF_AUTOCENTER, dev->ffbit)) { + if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ + dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; + else + dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; + + dev->ff->set_autocenter(dev, 0); + } + + /* Initialize device_list if this is the first device to handle by lg4ff */ + if (!list_inited) { + INIT_LIST_HEAD(&device_list.list); + list_inited = 1; + } + + /* Add the device to device_list */ + entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); + if (!entry) { + hid_err(hid, "Cannot add device, insufficient memory.\n"); + return -ENOMEM; + } + entry->device_id = kstrdup((&hid->dev)->kobj.name, GFP_KERNEL); + if (!entry->device_id) { + hid_err(hid, "Cannot set device_id, insufficient memory.\n"); + kfree(entry); + return -ENOMEM; + } + entry->min_range = lg4ff_devices[i].min_range; + entry->max_range = lg4ff_devices[i].max_range; + entry->set_range = lg4ff_devices[i].set_range; + list_add(&entry->list, &device_list.list); + + /* Create sysfs interface */ + error = device_create_file(&hid->dev, &dev_attr_range); + if (error) + return error; + dbg_hid("sysfs interface created\n"); + + /* Set the maximum range to start with */ + entry->range = entry->max_range; + if (entry->set_range != NULL) + entry->set_range(hid, entry->range); hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n"); return 0; } +int lg4ff_deinit(struct hid_device *hid) +{ + bool found = 0; + struct lg4ff_device_entry *entry; + struct list_head *h, *g; + list_for_each_safe(h, g, &device_list.list) { + entry = list_entry(h, struct lg4ff_device_entry, list); + if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) { + list_del(h); + kfree(entry->device_id); + kfree(entry); + found = 1; + break; + } + } + + if (!found) { + dbg_hid("Device entry not found!\n"); + return -1; + } + + device_remove_file(&hid->dev, &dev_attr_range); + dbg_hid("Device successfully unregistered\n"); + return 0; +} diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 088f85049290..27bc54f92f44 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -58,12 +58,6 @@ static const signed short ff_joystick_ac[] = { -1 }; -static const signed short ff_wheel[] = { - FF_CONSTANT, - FF_AUTOCENTER, - -1 -}; - static const struct dev_type devices[] = { { 0x046d, 0xc211, ff_rumble }, { 0x046d, 0xc219, ff_rumble }, @@ -71,14 +65,7 @@ static const struct dev_type devices[] = { { 0x046d, 0xc286, ff_joystick_ac }, { 0x046d, 0xc287, ff_joystick_ac }, { 0x046d, 0xc293, ff_joystick }, - { 0x046d, 0xc294, ff_wheel }, - { 0x046d, 0xc298, ff_wheel }, - { 0x046d, 0xc299, ff_wheel }, - { 0x046d, 0xc29b, ff_wheel }, { 0x046d, 0xc295, ff_joystick }, - { 0x046d, 0xc298, ff_wheel }, - { 0x046d, 0xc299, ff_wheel }, - { 0x046d, 0xca03, ff_wheel }, }; static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c new file mode 100644 index 000000000000..38b12e45780c --- /dev/null +++ b/drivers/hid/hid-logitech-dj.c @@ -0,0 +1,922 @@ +/* + * HID driver for Logitech Unifying receivers + * + * Copyright (c) 2011 Logitech + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> +#include "usbhid/usbhid.h" +#include "hid-ids.h" +#include "hid-logitech-dj.h" + +/* Keyboard descriptor (1) */ +static const char kbd_descriptor[] = { + 0x05, 0x01, /* USAGE_PAGE (generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x01, /* REPORT_ID (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */ + 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x05, /* REPORT COUNT (5) */ + 0x05, 0x08, /* USAGE PAGE (LED page) */ + 0x19, 0x01, /* USAGE MINIMUM (1) */ + 0x29, 0x05, /* USAGE MAXIMUM (5) */ + 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ + 0x95, 0x01, /* REPORT COUNT (1) */ + 0x75, 0x03, /* REPORT SIZE (3) */ + 0x91, 0x01, /* OUTPUT (Constant) */ + 0x95, 0x06, /* REPORT_COUNT (6) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (no event) */ + 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0xC0 +}; + +/* Mouse descriptor (2) */ +static const char mse_descriptor[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x02, /* REPORT_ID = 2 */ + 0x09, 0x01, /* USAGE (pointer) */ + 0xA1, 0x00, /* COLLECTION (physical) */ + 0x05, 0x09, /* USAGE_PAGE (buttons) */ + 0x19, 0x01, /* USAGE_MIN (1) */ + 0x29, 0x10, /* USAGE_MAX (16) */ + 0x15, 0x00, /* LOGICAL_MIN (0) */ + 0x25, 0x01, /* LOGICAL_MAX (1) */ + 0x95, 0x10, /* REPORT_COUNT (16) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x81, 0x02, /* INPUT (data var abs) */ + 0x05, 0x01, /* USAGE_PAGE (generic desktop) */ + 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */ + 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */ + 0x75, 0x0C, /* REPORT_SIZE (12) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x81, 0x06, /* INPUT */ + 0x15, 0x81, /* LOGICAL_MIN (-127) */ + 0x25, 0x7F, /* LOGICAL_MAX (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x09, 0x38, /* USAGE (wheel) */ + 0x81, 0x06, /* INPUT */ + 0x05, 0x0C, /* USAGE_PAGE(consumer) */ + 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x06, /* INPUT */ + 0xC0, /* END_COLLECTION */ + 0xC0, /* END_COLLECTION */ +}; + +/* Consumer Control descriptor (3) */ +static const char consumer_descriptor[] = { + 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */ + 0x09, 0x01, /* USAGE (Consumer Control) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x03, /* REPORT_ID = 3 */ + 0x75, 0x10, /* REPORT_SIZE (16) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x15, 0x01, /* LOGICAL_MIN (1) */ + 0x26, 0x8C, 0x02, /* LOGICAL_MAX (652) */ + 0x19, 0x01, /* USAGE_MIN (1) */ + 0x2A, 0x8C, 0x02, /* USAGE_MAX (652) */ + 0x81, 0x00, /* INPUT (Data Ary Abs) */ + 0xC0, /* END_COLLECTION */ +}; /* */ + +/* System control descriptor (4) */ +static const char syscontrol_descriptor[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x80, /* USAGE (System Control) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x04, /* REPORT_ID = 4 */ + 0x75, 0x02, /* REPORT_SIZE (2) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x15, 0x01, /* LOGICAL_MIN (1) */ + 0x25, 0x03, /* LOGICAL_MAX (3) */ + 0x09, 0x82, /* USAGE (System Sleep) */ + 0x09, 0x81, /* USAGE (System Power Down) */ + 0x09, 0x83, /* USAGE (System Wake Up) */ + 0x81, 0x60, /* INPUT (Data Ary Abs NPrf Null) */ + 0x75, 0x06, /* REPORT_SIZE (6) */ + 0x81, 0x03, /* INPUT (Cnst Var Abs) */ + 0xC0, /* END_COLLECTION */ +}; + +/* Media descriptor (8) */ +static const char media_descriptor[] = { + 0x06, 0xbc, 0xff, /* Usage Page 0xffbc */ + 0x09, 0x88, /* Usage 0x0088 */ + 0xa1, 0x01, /* BeginCollection */ + 0x85, 0x08, /* Report ID 8 */ + 0x19, 0x01, /* Usage Min 0x0001 */ + 0x29, 0xff, /* Usage Max 0x00ff */ + 0x15, 0x01, /* Logical Min 1 */ + 0x26, 0xff, 0x00, /* Logical Max 255 */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x00, /* Input */ + 0xc0, /* EndCollection */ +}; /* */ + +/* Maximum size of all defined hid reports in bytes (including report id) */ +#define MAX_REPORT_SIZE 8 + +/* Number of possible hid report types that can be created by this driver. + * + * Right now, RF report types have the same report types (or report id's) + * than the hid report created from those RF reports. In the future + * this doesnt have to be true. + * + * For instance, RF report type 0x01 which has a size of 8 bytes, corresponds + * to hid report id 0x01, this is standard keyboard. Same thing applies to mice + * reports and consumer control, etc. If a new RF report is created, it doesn't + * has to have the same report id as its corresponding hid report, so an + * translation may have to take place for future report types. + */ +#define NUMBER_OF_HID_REPORTS 32 +static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { + [1] = 8, /* Standard keyboard */ + [2] = 8, /* Standard mouse */ + [3] = 5, /* Consumer control */ + [4] = 2, /* System control */ + [8] = 2, /* Media Center */ +}; + + +#define LOGITECH_DJ_INTERFACE_NUMBER 0x02 + +static struct hid_ll_driver logi_dj_ll_driver; + +static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, + size_t count, + unsigned char report_type); + +static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + /* Called in delayed work context */ + struct dj_device *dj_dev; + unsigned long flags; + + spin_lock_irqsave(&djrcv_dev->lock, flags); + dj_dev = djrcv_dev->paired_dj_devices[dj_report->device_index]; + djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL; + spin_unlock_irqrestore(&djrcv_dev->lock, flags); + + if (dj_dev != NULL) { + hid_destroy_device(dj_dev->hdev); + kfree(dj_dev); + } else { + dev_err(&djrcv_dev->hdev->dev, "%s: can't destroy a NULL device\n", + __func__); + } +} + +static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + /* Called in delayed work context */ + struct hid_device *djrcv_hdev = djrcv_dev->hdev; + struct usb_interface *intf = to_usb_interface(djrcv_hdev->dev.parent); + struct usb_device *usbdev = interface_to_usbdev(intf); + struct hid_device *dj_hiddev; + struct dj_device *dj_dev; + + /* Device index goes from 1 to 6, we need 3 bytes to store the + * semicolon, the index, and a null terminator + */ + unsigned char tmpstr[3]; + + if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] & + SPFUNCTION_DEVICE_LIST_EMPTY) { + dbg_hid("%s: device list is empty\n", __func__); + return; + } + + if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || + (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { + dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n", + __func__, dj_report->device_index); + return; + } + + dj_hiddev = hid_allocate_device(); + if (IS_ERR(dj_hiddev)) { + dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n", + __func__); + return; + } + + dj_hiddev->ll_driver = &logi_dj_ll_driver; + dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report; + + dj_hiddev->dev.parent = &djrcv_hdev->dev; + dj_hiddev->bus = BUS_USB; + dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor); + dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct); + snprintf(dj_hiddev->name, sizeof(dj_hiddev->name), + "Logitech Unifying Device. Wireless PID:%02x%02x", + dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB], + dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]); + + usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys)); + snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index); + strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys)); + + dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL); + + if (!dj_dev) { + dev_err(&djrcv_hdev->dev, "%s: failed allocating dj_device\n", + __func__); + goto dj_device_allocate_fail; + } + + dj_dev->reports_supported = le32_to_cpu( + dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]); + dj_dev->hdev = dj_hiddev; + dj_dev->dj_receiver_dev = djrcv_dev; + dj_dev->device_index = dj_report->device_index; + dj_hiddev->driver_data = dj_dev; + + djrcv_dev->paired_dj_devices[dj_report->device_index] = dj_dev; + + if (hid_add_device(dj_hiddev)) { + dev_err(&djrcv_hdev->dev, "%s: failed adding dj_device\n", + __func__); + goto hid_add_device_fail; + } + + return; + +hid_add_device_fail: + djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL; + kfree(dj_dev); +dj_device_allocate_fail: + hid_destroy_device(dj_hiddev); +} + +static void delayedwork_callback(struct work_struct *work) +{ + struct dj_receiver_dev *djrcv_dev = + container_of(work, struct dj_receiver_dev, work); + + struct dj_report dj_report; + unsigned long flags; + int count; + + dbg_hid("%s\n", __func__); + + spin_lock_irqsave(&djrcv_dev->lock, flags); + + count = kfifo_out(&djrcv_dev->notif_fifo, &dj_report, + sizeof(struct dj_report)); + + if (count != sizeof(struct dj_report)) { + dev_err(&djrcv_dev->hdev->dev, "%s: workitem triggered without " + "notifications available\n", __func__); + spin_unlock_irqrestore(&djrcv_dev->lock, flags); + return; + } + + if (!kfifo_is_empty(&djrcv_dev->notif_fifo)) { + if (schedule_work(&djrcv_dev->work) == 0) { + dbg_hid("%s: did not schedule the work item, was " + "already queued\n", __func__); + } + } + + spin_unlock_irqrestore(&djrcv_dev->lock, flags); + + switch (dj_report.report_type) { + case REPORT_TYPE_NOTIF_DEVICE_PAIRED: + logi_dj_recv_add_djhid_device(djrcv_dev, &dj_report); + break; + case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED: + logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report); + break; + default: + dbg_hid("%s: unexpected report type\n", __func__); + } +} + +static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + /* We are called from atomic context (tasklet && djrcv->lock held) */ + + kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); + + if (schedule_work(&djrcv_dev->work) == 0) { + dbg_hid("%s: did not schedule the work item, was already " + "queued\n", __func__); + } +} + +static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + /* We are called from atomic context (tasklet && djrcv->lock held) */ + unsigned int i; + u8 reportbuffer[MAX_REPORT_SIZE]; + struct dj_device *djdev; + + djdev = djrcv_dev->paired_dj_devices[dj_report->device_index]; + + if (!djdev) { + dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" + " is NULL, index %d\n", dj_report->device_index); + return; + } + + memset(reportbuffer, 0, sizeof(reportbuffer)); + + for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) { + if (djdev->reports_supported & (1 << i)) { + reportbuffer[0] = i; + if (hid_input_report(djdev->hdev, + HID_INPUT_REPORT, + reportbuffer, + hid_reportid_size_map[i], 1)) { + dbg_hid("hid_input_report error sending null " + "report\n"); + } + } + } +} + +static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + /* We are called from atomic context (tasklet && djrcv->lock held) */ + struct dj_device *dj_device; + + dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index]; + + if (dj_device == NULL) { + dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" + " is NULL, index %d\n", dj_report->device_index); + return; + } + + if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) || + (hid_reportid_size_map[dj_report->report_type] == 0)) { + dbg_hid("invalid report type:%x\n", dj_report->report_type); + return; + } + + if (hid_input_report(dj_device->hdev, + HID_INPUT_REPORT, &dj_report->report_type, + hid_reportid_size_map[dj_report->report_type], 1)) { + dbg_hid("hid_input_report error\n"); + } +} + + +static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, + struct dj_report *dj_report) +{ + struct hid_device *hdev = djrcv_dev->hdev; + int sent_bytes; + + if (!hdev->hid_output_raw_report) { + dev_err(&hdev->dev, "%s:" + "hid_output_raw_report is null\n", __func__); + return -ENODEV; + } + + sent_bytes = hdev->hid_output_raw_report(hdev, (u8 *) dj_report, + sizeof(struct dj_report), + HID_OUTPUT_REPORT); + + return (sent_bytes < 0) ? sent_bytes : 0; +} + +static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) +{ + struct dj_report dj_report; + + memset(&dj_report, 0, sizeof(dj_report)); + dj_report.report_id = REPORT_ID_DJ_SHORT; + dj_report.device_index = 0xFF; + dj_report.report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES; + return logi_dj_recv_send_report(djrcv_dev, &dj_report); +} + +static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, + unsigned timeout) +{ + struct dj_report dj_report; + + memset(&dj_report, 0, sizeof(dj_report)); + dj_report.report_id = REPORT_ID_DJ_SHORT; + dj_report.device_index = 0xFF; + dj_report.report_type = REPORT_TYPE_CMD_SWITCH; + dj_report.report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x1F; + dj_report.report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout; + return logi_dj_recv_send_report(djrcv_dev, &dj_report); +} + + +static int logi_dj_ll_open(struct hid_device *hid) +{ + dbg_hid("%s:%s\n", __func__, hid->phys); + return 0; + +} + +static void logi_dj_ll_close(struct hid_device *hid) +{ + dbg_hid("%s:%s\n", __func__, hid->phys); +} + +static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, + size_t count, + unsigned char report_type) +{ + /* Called by hid raw to send data */ + dbg_hid("%s\n", __func__); + + return 0; +} + +static int logi_dj_ll_parse(struct hid_device *hid) +{ + struct dj_device *djdev = hid->driver_data; + int retval; + + dbg_hid("%s\n", __func__); + + djdev->hdev->version = 0x0111; + djdev->hdev->country = 0x00; + + if (djdev->reports_supported & STD_KEYBOARD) { + dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n", + __func__, djdev->reports_supported); + retval = hid_parse_report(hid, + (u8 *) kbd_descriptor, + sizeof(kbd_descriptor)); + if (retval) { + dbg_hid("%s: sending a kbd descriptor, hid_parse failed" + " error: %d\n", __func__, retval); + return retval; + } + } + + if (djdev->reports_supported & STD_MOUSE) { + dbg_hid("%s: sending a mouse descriptor, reports_supported: " + "%x\n", __func__, djdev->reports_supported); + retval = hid_parse_report(hid, + (u8 *) mse_descriptor, + sizeof(mse_descriptor)); + if (retval) { + dbg_hid("%s: sending a mouse descriptor, hid_parse " + "failed error: %d\n", __func__, retval); + return retval; + } + } + + if (djdev->reports_supported & MULTIMEDIA) { + dbg_hid("%s: sending a multimedia report descriptor: %x\n", + __func__, djdev->reports_supported); + retval = hid_parse_report(hid, + (u8 *) consumer_descriptor, + sizeof(consumer_descriptor)); + if (retval) { + dbg_hid("%s: sending a consumer_descriptor, hid_parse " + "failed error: %d\n", __func__, retval); + return retval; + } + } + + if (djdev->reports_supported & POWER_KEYS) { + dbg_hid("%s: sending a power keys report descriptor: %x\n", + __func__, djdev->reports_supported); + retval = hid_parse_report(hid, + (u8 *) syscontrol_descriptor, + sizeof(syscontrol_descriptor)); + if (retval) { + dbg_hid("%s: sending a syscontrol_descriptor, " + "hid_parse failed error: %d\n", + __func__, retval); + return retval; + } + } + + if (djdev->reports_supported & MEDIA_CENTER) { + dbg_hid("%s: sending a media center report descriptor: %x\n", + __func__, djdev->reports_supported); + retval = hid_parse_report(hid, + (u8 *) media_descriptor, + sizeof(media_descriptor)); + if (retval) { + dbg_hid("%s: sending a media_descriptor, hid_parse " + "failed error: %d\n", __func__, retval); + return retval; + } + } + + if (djdev->reports_supported & KBD_LEDS) { + dbg_hid("%s: need to send kbd leds report descriptor: %x\n", + __func__, djdev->reports_supported); + } + + return 0; +} + +static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + /* Sent by the input layer to handle leds and Force Feedback */ + struct hid_device *dj_hiddev = input_get_drvdata(dev); + struct dj_device *dj_dev = dj_hiddev->driver_data; + + struct dj_receiver_dev *djrcv_dev = + dev_get_drvdata(dj_hiddev->dev.parent); + struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev; + struct hid_report_enum *output_report_enum; + + struct hid_field *field; + struct hid_report *report; + unsigned char data[8]; + int offset; + + dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", + __func__, dev->phys, type, code, value); + + if (type != EV_LED) + return -1; + + offset = hidinput_find_field(dj_hiddev, type, code, &field); + + if (offset == -1) { + dev_warn(&dev->dev, "event field not found\n"); + return -1; + } + hid_set_field(field, offset, value); + hid_output_report(field->report, &data[0]); + + output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; + report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; + hid_set_field(report->field[0], 0, dj_dev->device_index); + hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); + hid_set_field(report->field[0], 2, data[1]); + + usbhid_submit_report(dj_rcv_hiddev, report, USB_DIR_OUT); + + return 0; + +} + +static int logi_dj_ll_start(struct hid_device *hid) +{ + dbg_hid("%s\n", __func__); + return 0; +} + +static void logi_dj_ll_stop(struct hid_device *hid) +{ + dbg_hid("%s\n", __func__); +} + + +static struct hid_ll_driver logi_dj_ll_driver = { + .parse = logi_dj_ll_parse, + .start = logi_dj_ll_start, + .stop = logi_dj_ll_stop, + .open = logi_dj_ll_open, + .close = logi_dj_ll_close, + .hidinput_input_event = logi_dj_ll_input_event, +}; + + +static int logi_dj_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, + int size) +{ + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); + struct dj_report *dj_report = (struct dj_report *) data; + unsigned long flags; + bool report_processed = false; + + dbg_hid("%s, size:%d\n", __func__, size); + + /* Here we receive all data coming from iface 2, there are 4 cases: + * + * 1) Data should continue its normal processing i.e. data does not + * come from the DJ collection, in which case we do nothing and + * return 0, so hid-core can continue normal processing (will forward + * to associated hidraw device) + * + * 2) Data is from DJ collection, and is intended for this driver i. e. + * data contains arrival, departure, etc notifications, in which case + * we queue them for delayed processing by the work queue. We return 1 + * to hid-core as no further processing is required from it. + * + * 3) Data is from DJ collection, and informs a connection change, + * if the change means rf link loss, then we must send a null report + * to the upper layer to discard potentially pressed keys that may be + * repeated forever by the input layer. Return 1 to hid-core as no + * further processing is required. + * + * 4) Data is from DJ collection and is an actual input event from + * a paired DJ device in which case we forward it to the correct hid + * device (via hid_input_report() ) and return 1 so hid-core does not do + * anything else with it. + */ + + spin_lock_irqsave(&djrcv_dev->lock, flags); + if (dj_report->report_id == REPORT_ID_DJ_SHORT) { + switch (dj_report->report_type) { + case REPORT_TYPE_NOTIF_DEVICE_PAIRED: + case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED: + logi_dj_recv_queue_notification(djrcv_dev, dj_report); + break; + case REPORT_TYPE_NOTIF_CONNECTION_STATUS: + if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] == + STATUS_LINKLOSS) { + logi_dj_recv_forward_null_report(djrcv_dev, dj_report); + } + break; + default: + logi_dj_recv_forward_report(djrcv_dev, dj_report); + } + report_processed = true; + } + spin_unlock_irqrestore(&djrcv_dev->lock, flags); + + return report_processed; +} + +static int logi_dj_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct dj_receiver_dev *djrcv_dev; + int retval; + + if (is_dj_device((struct dj_device *)hdev->driver_data)) + return -ENODEV; + + dbg_hid("%s called for ifnum %d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* Ignore interfaces 0 and 1, they will not carry any data, dont create + * any hid_device for them */ + if (intf->cur_altsetting->desc.bInterfaceNumber != + LOGITECH_DJ_INTERFACE_NUMBER) { + dbg_hid("%s: ignoring ifnum %d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + return -ENODEV; + } + + /* Treat interface 2 */ + + djrcv_dev = kzalloc(sizeof(struct dj_receiver_dev), GFP_KERNEL); + if (!djrcv_dev) { + dev_err(&hdev->dev, + "%s:failed allocating dj_receiver_dev\n", __func__); + return -ENOMEM; + } + djrcv_dev->hdev = hdev; + INIT_WORK(&djrcv_dev->work, delayedwork_callback); + spin_lock_init(&djrcv_dev->lock); + if (kfifo_alloc(&djrcv_dev->notif_fifo, + DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report), + GFP_KERNEL)) { + dev_err(&hdev->dev, + "%s:failed allocating notif_fifo\n", __func__); + kfree(djrcv_dev); + return -ENOMEM; + } + hid_set_drvdata(hdev, djrcv_dev); + + /* Call to usbhid to fetch the HID descriptors of interface 2 and + * subsequently call to the hid/hid-core to parse the fetched + * descriptors, this will in turn create the hidraw and hiddev nodes + * for interface 2 of the receiver */ + retval = hid_parse(hdev); + if (retval) { + dev_err(&hdev->dev, + "%s:parse of interface 2 failed\n", __func__); + goto hid_parse_fail; + } + + /* Starts the usb device and connects to upper interfaces hiddev and + * hidraw */ + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + dev_err(&hdev->dev, + "%s:hid_hw_start returned error\n", __func__); + goto hid_hw_start_fail; + } + + retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); + if (retval < 0) { + dev_err(&hdev->dev, + "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n", + __func__, retval); + goto switch_to_dj_mode_fail; + } + + /* This is enabling the polling urb on the IN endpoint */ + retval = hdev->ll_driver->open(hdev); + if (retval < 0) { + dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned " + "error:%d\n", __func__, retval); + goto llopen_failed; + } + + retval = logi_dj_recv_query_paired_devices(djrcv_dev); + if (retval < 0) { + dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices " + "error:%d\n", __func__, retval); + goto logi_dj_recv_query_paired_devices_failed; + } + + return retval; + +logi_dj_recv_query_paired_devices_failed: + hdev->ll_driver->close(hdev); + +llopen_failed: +switch_to_dj_mode_fail: + hid_hw_stop(hdev); + +hid_hw_start_fail: +hid_parse_fail: + kfifo_free(&djrcv_dev->notif_fifo); + kfree(djrcv_dev); + hid_set_drvdata(hdev, NULL); + return retval; + +} + +#ifdef CONFIG_PM +static int logi_dj_reset_resume(struct hid_device *hdev) +{ + int retval; + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); + + retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0); + if (retval < 0) { + dev_err(&hdev->dev, + "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n", + __func__, retval); + } + + return 0; +} +#endif + +static void logi_dj_remove(struct hid_device *hdev) +{ + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); + struct dj_device *dj_dev; + int i; + + dbg_hid("%s\n", __func__); + + cancel_work_sync(&djrcv_dev->work); + + hdev->ll_driver->close(hdev); + hid_hw_stop(hdev); + + /* I suppose that at this point the only context that can access + * the djrecv_data is this thread as the work item is guaranteed to + * have finished and no more raw_event callbacks should arrive after + * the remove callback was triggered so no locks are put around the + * code below */ + for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) { + dj_dev = djrcv_dev->paired_dj_devices[i]; + if (dj_dev != NULL) { + hid_destroy_device(dj_dev->hdev); + kfree(dj_dev); + djrcv_dev->paired_dj_devices[i] = NULL; + } + } + + kfifo_free(&djrcv_dev->notif_fifo); + kfree(djrcv_dev); + hid_set_drvdata(hdev, NULL); +} + +static int logi_djdevice_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct dj_device *dj_dev = hdev->driver_data; + + if (!is_dj_device(dj_dev)) + return -ENODEV; + + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + return ret; +} + +static const struct hid_device_id logi_dj_receivers[] = { + {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, + {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)}, + {} +}; + +MODULE_DEVICE_TABLE(hid, logi_dj_receivers); + +static struct hid_driver logi_djreceiver_driver = { + .name = "logitech-djreceiver", + .id_table = logi_dj_receivers, + .probe = logi_dj_probe, + .remove = logi_dj_remove, + .raw_event = logi_dj_raw_event, +#ifdef CONFIG_PM + .reset_resume = logi_dj_reset_resume, +#endif +}; + + +static const struct hid_device_id logi_dj_devices[] = { + {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, + {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)}, + {} +}; + +static struct hid_driver logi_djdevice_driver = { + .name = "logitech-djdevice", + .id_table = logi_dj_devices, + .probe = logi_djdevice_probe, +}; + + +static int __init logi_dj_init(void) +{ + int retval; + + dbg_hid("Logitech-DJ:%s\n", __func__); + + retval = hid_register_driver(&logi_djreceiver_driver); + if (retval) + return retval; + + retval = hid_register_driver(&logi_djdevice_driver); + if (retval) + hid_unregister_driver(&logi_djreceiver_driver); + + return retval; + +} + +static void __exit logi_dj_exit(void) +{ + dbg_hid("Logitech-DJ:%s\n", __func__); + + hid_unregister_driver(&logi_djdevice_driver); + hid_unregister_driver(&logi_djreceiver_driver); + +} + +module_init(logi_dj_init); +module_exit(logi_dj_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Logitech"); +MODULE_AUTHOR("Nestor Lopez Casado"); +MODULE_AUTHOR("nlopezcasad@logitech.com"); diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h new file mode 100644 index 000000000000..fd28a5e0ca3b --- /dev/null +++ b/drivers/hid/hid-logitech-dj.h @@ -0,0 +1,123 @@ +#ifndef __HID_LOGITECH_DJ_H +#define __HID_LOGITECH_DJ_H + +/* + * HID driver for Logitech Unifying receivers + * + * Copyright (c) 2011 Logitech + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kfifo.h> + +#define DJ_MAX_PAIRED_DEVICES 6 +#define DJ_MAX_NUMBER_NOTIFICATIONS 8 +#define DJ_DEVICE_INDEX_MIN 1 +#define DJ_DEVICE_INDEX_MAX 6 + +#define DJREPORT_SHORT_LENGTH 15 +#define DJREPORT_LONG_LENGTH 32 + +#define REPORT_ID_DJ_SHORT 0x20 +#define REPORT_ID_DJ_LONG 0x21 + +#define REPORT_TYPE_RFREPORT_FIRST 0x01 +#define REPORT_TYPE_RFREPORT_LAST 0x1F + +/* Command Switch to DJ mode */ +#define REPORT_TYPE_CMD_SWITCH 0x80 +#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00 +#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01 +#define TIMEOUT_NO_KEEPALIVE 0x00 + +/* Command to Get the list of Paired devices */ +#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81 + +/* Device Paired Notification */ +#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41 +#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01 +#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02 +#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00 +#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01 +#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02 +#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03 + +/* Device Un-Paired Notification */ +#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40 + + +/* Connection Status Notification */ +#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42 +#define CONNECTION_STATUS_PARAM_STATUS 0x00 +#define STATUS_LINKLOSS 0x01 + +/* Error Notification */ +#define REPORT_TYPE_NOTIF_ERROR 0x7F +#define NOTIF_ERROR_PARAM_ETYPE 0x00 +#define ETYPE_KEEPALIVE_TIMEOUT 0x01 + +/* supported DJ HID && RF report types */ +#define REPORT_TYPE_KEYBOARD 0x01 +#define REPORT_TYPE_MOUSE 0x02 +#define REPORT_TYPE_CONSUMER_CONTROL 0x03 +#define REPORT_TYPE_SYSTEM_CONTROL 0x04 +#define REPORT_TYPE_MEDIA_CENTER 0x08 +#define REPORT_TYPE_LEDS 0x0E + +/* RF Report types bitfield */ +#define STD_KEYBOARD 0x00000002 +#define STD_MOUSE 0x00000004 +#define MULTIMEDIA 0x00000008 +#define POWER_KEYS 0x00000010 +#define MEDIA_CENTER 0x00000100 +#define KBD_LEDS 0x00004000 + +struct dj_report { + u8 report_id; + u8 device_index; + u8 report_type; + u8 report_params[DJREPORT_SHORT_LENGTH - 3]; +}; + +struct dj_receiver_dev { + struct hid_device *hdev; + struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES + + DJ_DEVICE_INDEX_MIN]; + struct work_struct work; + struct kfifo notif_fifo; + spinlock_t lock; +}; + +struct dj_device { + struct hid_device *hdev; + struct dj_receiver_dev *dj_receiver_dev; + u32 reports_supported; + u8 device_index; +}; + +/** + * is_dj_device - know if the given dj_device is not the receiver. + * @dj_dev: the dj device to test + * + * This macro tests if a struct dj_device pointer is a device created + * by the bus enumarator. + */ +#define is_dj_device(dj_dev) \ + (&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent) + +#endif diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index f0fbd7bd239e..2ab71758e2e2 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -405,6 +405,13 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(REL_HWHEEL, input->relbit); } } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + /* input->keybit is initialized with incorrect button info + * for Magic Trackpad. There really is only one physical + * button (BTN_LEFT == BTN_MOUSE). Make sure we don't + * advertise buttons that don't exist... + */ + __clear_bit(BTN_RIGHT, input->keybit); + __clear_bit(BTN_MIDDLE, input->keybit); __set_bit(BTN_MOUSE, input->keybit); __set_bit(BTN_TOOL_FINGER, input->keybit); __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 58d0e7aaf088..fa5d7a1ffa9e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -47,10 +47,11 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) #define MT_QUIRK_CYPRESS (1 << 2) #define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) -#define MT_QUIRK_VALID_IS_INRANGE (1 << 4) -#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5) -#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6) -#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 7) +#define MT_QUIRK_ALWAYS_VALID (1 << 4) +#define MT_QUIRK_VALID_IS_INRANGE (1 << 5) +#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) +#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7) +#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) struct mt_slot { __s32 x, y, p, w, h; @@ -86,11 +87,12 @@ struct mt_class { /* classes of device behavior */ #define MT_CLS_DEFAULT 0x0001 -#define MT_CLS_CONFIDENCE 0x0002 -#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0003 -#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0004 -#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0005 -#define MT_CLS_DUAL_NSMU_CONTACTID 0x0006 +#define MT_CLS_SERIAL 0x0002 +#define MT_CLS_CONFIDENCE 0x0003 +#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004 +#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006 +#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -134,6 +136,8 @@ static int find_slot_from_contactid(struct mt_device *td) struct mt_class mt_classes[] = { { .name = MT_CLS_DEFAULT, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, + { .name = MT_CLS_SERIAL, + .quirks = MT_QUIRK_ALWAYS_VALID}, { .name = MT_CLS_CONFIDENCE, .quirks = MT_QUIRK_VALID_IS_CONFIDENCE }, { .name = MT_CLS_CONFIDENCE_MINUS_ONE, @@ -213,6 +217,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct mt_class *cls = td->mtclass; __s32 quirks = cls->quirks; + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application == HID_DG_TOUCHSCREEN) + set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + else if (field->application == HID_DG_TOUCHPAD) + set_bit(INPUT_PROP_POINTER, hi->input->propbit); + else + return 0; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: @@ -277,6 +291,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, td->last_slot_field = usage->hid; td->last_field_index = field->index; td->last_mt_collection = usage->collection_index; + hdev->quirks &= ~HID_QUIRK_MULTITOUCH; return 1; case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, @@ -435,7 +450,9 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { switch (usage->hid) { case HID_DG_INRANGE: - if (quirks & MT_QUIRK_VALID_IS_INRANGE) + if (quirks & MT_QUIRK_ALWAYS_VALID) + td->curvalid = true; + else if (quirks & MT_QUIRK_VALID_IS_INRANGE) td->curvalid = value; break; case HID_DG_TIPSWITCH: @@ -513,12 +530,44 @@ static void mt_set_input_mode(struct hid_device *hdev) } } +/* a list of devices for which there is a specialized multitouch driver */ +static const struct hid_device_id mt_have_special_driver[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0006) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, + { } +}; + +static bool mt_match_one_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + return id->bus == hdev->bus && + (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && + (id->product == HID_ANY_ID || id->product == hdev->product); +} + +static const struct hid_device_id *mt_match_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + for (; id->bus; id++) + if (mt_match_one_id(hdev, id)) + return id; + + return NULL; +} + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; struct mt_device *td; struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ + if (mt_match_id(hdev, mt_have_special_driver)) + return -ENODEV; + for (i = 0; mt_classes[i].name ; i++) { if (id->driver_data == mt_classes[i].name) { mtclass = &(mt_classes[i]); @@ -526,10 +575,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) } } - /* This allows the driver to correctly support devices - * that emit events over several HID messages. - */ - hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); if (!td) { @@ -545,10 +590,16 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret != 0) goto fail; + hdev->quirks |= HID_QUIRK_MULTITOUCH; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) goto fail; + /* This allows the driver to correctly support devices + * that emit events over several HID messages. + */ + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot), GFP_KERNEL); if (!td->slots) { @@ -662,6 +713,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, + /* Ideacom panel */ + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, + USB_DEVICE_ID_IDEACOM_IDC6650) }, + /* Ilitek dual touch panel */ { .driver_data = MT_CLS_DEFAULT, HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, @@ -672,6 +728,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, + /* LG Display panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_LG, + USB_DEVICE_ID_LG_MULTITOUCH) }, + /* Lumio panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, @@ -732,6 +793,10 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, + /* Rest of the world */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } }; MODULE_DEVICE_TABLE(hid, mt_devices); diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c new file mode 100644 index 000000000000..4d3c60d88318 --- /dev/null +++ b/drivers/hid/hid-primax.c @@ -0,0 +1,117 @@ +/* + * HID driver for primax and similar keyboards with in-band modifiers + * + * Copyright 2011 Google Inc. All Rights Reserved + * + * Author: + * Terry Lambert <tlambert@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static int px_raw_event(struct hid_device *hid, struct hid_report *report, + u8 *data, int size) +{ + int idx = size; + + switch (report->id) { + case 0: /* keyboard input */ + /* + * Convert in-band modifier key values into out of band + * modifier bits and pull the key strokes from the report. + * Thus a report data set which looked like: + * + * [00][00][E0][30][00][00][00][00] + * (no modifier bits + "Left Shift" key + "1" key) + * + * Would be converted to: + * + * [01][00][00][30][00][00][00][00] + * (Left Shift modifier bit + "1" key) + * + * As long as it's in the size range, the upper level + * drivers don't particularly care if there are in-band + * 0-valued keys, so they don't stop parsing. + */ + while (--idx > 1) { + if (data[idx] < 0xE0 || data[idx] > 0xE7) + continue; + data[0] |= (1 << (data[idx] - 0xE0)); + data[idx] = 0; + } + hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0); + return 1; + + default: /* unknown report */ + /* Unknown report type; pass upstream */ + hid_info(hid, "unknown report type %d\n", report->id); + break; + } + + return 0; +} + +static int px_probe(struct hid_device *hid, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hid); + if (ret) { + hid_err(hid, "parse failed\n"); + goto fail; + } + + ret = hid_hw_start(hid, HID_CONNECT_DEFAULT); + if (ret) + hid_err(hid, "hw start failed\n"); + +fail: + return ret; +} + +static void px_remove(struct hid_device *hid) +{ + hid_hw_stop(hid); +} + +static const struct hid_device_id px_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, + { } +}; +MODULE_DEVICE_TABLE(hid, px_devices); + +static struct hid_driver px_driver = { + .name = "primax", + .id_table = px_devices, + .raw_event = px_raw_event, + .probe = px_probe, + .remove = px_remove, +}; + +static int __init px_init(void) +{ + return hid_register_driver(&px_driver); +} + +static void __exit px_exit(void) +{ + hid_unregister_driver(&px_driver); +} + +module_init(px_init); +module_exit(px_exit); +MODULE_AUTHOR("Terry Lambert <tlambert@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 158b389d0fb7..f779009104eb 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -816,7 +816,7 @@ static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id) if (pm == NULL) { hid_err(hdev, "can't alloc descriptor\n"); ret = -ENOMEM; - goto err_free; + goto err_free_pk; } pm->pk = pk; @@ -849,10 +849,10 @@ static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id) err_stop: hid_hw_stop(hdev); err_free: - if (pm != NULL) - kfree(pm); - + kfree(pm); +err_free_pk: kfree(pk); + return ret; } diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 2b8f3a31ffb3..e2072afb34bb 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -37,6 +37,21 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4}; +static void kone_profile_activated(struct kone_device *kone, uint new_profile) +{ + kone->actual_profile = new_profile; + kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi; +} + +static void kone_profile_report(struct kone_device *kone, uint new_profile) +{ + struct kone_roccat_report roccat_report; + roccat_report.event = kone_mouse_event_switch_profile; + roccat_report.value = new_profile; + roccat_report.key = 0; + roccat_report_event(kone->chrdev_minor, (uint8_t *)&roccat_report); +} + static int kone_receive(struct usb_device *usb_dev, uint usb_command, void *data, uint size) { @@ -283,7 +298,7 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj, container_of(kobj, struct device, kobj)->parent->parent; struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); - int retval = 0, difference; + int retval = 0, difference, old_profile; /* I need to get my data in one piece */ if (off != 0 || count != sizeof(struct kone_settings)) @@ -294,21 +309,20 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj, if (difference) { retval = kone_set_settings(usb_dev, (struct kone_settings const *)buf); - if (!retval) - memcpy(&kone->settings, buf, - sizeof(struct kone_settings)); - } - mutex_unlock(&kone->kone_lock); + if (retval) { + mutex_unlock(&kone->kone_lock); + return retval; + } - if (retval) - return retval; + old_profile = kone->settings.startup_profile; + memcpy(&kone->settings, buf, sizeof(struct kone_settings)); - /* - * If we get here, treat settings as okay and update actual values - * according to startup_profile - */ - kone->actual_profile = kone->settings.startup_profile; - kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi; + kone_profile_activated(kone, kone->settings.startup_profile); + + if (kone->settings.startup_profile != old_profile) + kone_profile_report(kone, kone->settings.startup_profile); + } + mutex_unlock(&kone->kone_lock); return sizeof(struct kone_settings); } @@ -501,6 +515,8 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev, goto exit_no_settings; goto exit_unlock; } + /* calibration resets profile */ + kone_profile_activated(kone, kone->settings.startup_profile); } retval = size; @@ -544,16 +560,16 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev, kone_set_settings_checksum(&kone->settings); retval = kone_set_settings(usb_dev, &kone->settings); - - mutex_unlock(&kone->kone_lock); - - if (retval) + if (retval) { + mutex_unlock(&kone->kone_lock); return retval; + } /* changing the startup profile immediately activates this profile */ - kone->actual_profile = new_startup_profile; - kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi; + kone_profile_activated(kone, new_startup_profile); + kone_profile_report(kone, new_startup_profile); + mutex_unlock(&kone->kone_lock); return size; } @@ -665,8 +681,7 @@ static int kone_init_kone_device_struct(struct usb_device *usb_dev, if (retval) return retval; - kone->actual_profile = kone->settings.startup_profile; - kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi; + kone_profile_activated(kone, kone->settings.startup_profile); return 0; } @@ -776,10 +791,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone, { switch (event->event) { case kone_mouse_event_switch_profile: + kone->actual_dpi = kone->profiles[event->value - 1]. + startup_dpi; case kone_mouse_event_osd_profile: kone->actual_profile = event->value; - kone->actual_dpi = kone->profiles[kone->actual_profile - 1]. - startup_dpi; break; case kone_mouse_event_switch_dpi: case kone_mouse_event_osd_dpi: diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1f8336e3f584..112d934132c8 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -323,6 +323,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, struct usb_device *usb_dev; unsigned long profile; int retval; + struct kovaplus_roccat_report roccat_report; dev = dev->parent->parent; kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); @@ -337,10 +338,22 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, mutex_lock(&kovaplus->kovaplus_lock); retval = kovaplus_set_actual_profile(usb_dev, profile); + if (retval) { + mutex_unlock(&kovaplus->kovaplus_lock); + return retval; + } + kovaplus_profile_activated(kovaplus, profile); + + roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1; + roccat_report.profile = profile + 1; + roccat_report.button = 0; + roccat_report.data1 = profile + 1; + roccat_report.data2 = 0; + roccat_report_event(kovaplus->chrdev_minor, + (uint8_t const *)&roccat_report); + mutex_unlock(&kovaplus->kovaplus_lock); - if (retval) - return retval; return size; } diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 8140776bd8c5..df05c1b1064f 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -298,6 +298,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval = 0; int difference; + struct pyra_roccat_report roccat_report; if (off != 0 || count != sizeof(struct pyra_settings)) return -EINVAL; @@ -307,17 +308,23 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, if (difference) { retval = pyra_set_settings(usb_dev, (struct pyra_settings const *)buf); - if (!retval) - memcpy(&pyra->settings, buf, - sizeof(struct pyra_settings)); - } - mutex_unlock(&pyra->pyra_lock); + if (retval) { + mutex_unlock(&pyra->pyra_lock); + return retval; + } - if (retval) - return retval; + memcpy(&pyra->settings, buf, + sizeof(struct pyra_settings)); - profile_activated(pyra, pyra->settings.startup_profile); + profile_activated(pyra, pyra->settings.startup_profile); + roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; + roccat_report.value = pyra->settings.startup_profile + 1; + roccat_report.key = 0; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report); + } + mutex_unlock(&pyra->pyra_lock); return sizeof(struct pyra_settings); } diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 16f7cafc9695..670da9109f86 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -65,8 +65,7 @@ static int sjoyff_init(struct hid_device *hid) { struct sjoyff_device *sjoyff; struct hid_report *report; - struct hid_input *hidinput = list_entry(hid->inputs.next, - struct hid_input, list); + struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct list_head *report_ptr = report_list; @@ -78,43 +77,45 @@ static int sjoyff_init(struct hid_device *hid) return -ENODEV; } - report_ptr = report_ptr->next; + list_for_each_entry(hidinput, &hid->inputs, list) { + report_ptr = report_ptr->next; - if (report_ptr == report_list) { - hid_err(hid, "required output report is missing\n"); - return -ENODEV; - } + if (report_ptr == report_list) { + hid_err(hid, "required output report is missing\n"); + return -ENODEV; + } - report = list_entry(report_ptr, struct hid_report, list); - if (report->maxfield < 1) { - hid_err(hid, "no fields in the report\n"); - return -ENODEV; - } + report = list_entry(report_ptr, struct hid_report, list); + if (report->maxfield < 1) { + hid_err(hid, "no fields in the report\n"); + return -ENODEV; + } - if (report->field[0]->report_count < 3) { - hid_err(hid, "not enough values in the field\n"); - return -ENODEV; - } + if (report->field[0]->report_count < 3) { + hid_err(hid, "not enough values in the field\n"); + return -ENODEV; + } - sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL); - if (!sjoyff) - return -ENOMEM; + sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL); + if (!sjoyff) + return -ENOMEM; - dev = hidinput->input; + dev = hidinput->input; - set_bit(FF_RUMBLE, dev->ffbit); + set_bit(FF_RUMBLE, dev->ffbit); - error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); - if (error) { - kfree(sjoyff); - return error; - } + error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); + if (error) { + kfree(sjoyff); + return error; + } - sjoyff->report = report; - sjoyff->report->field[0]->value[0] = 0x01; - sjoyff->report->field[0]->value[1] = 0x00; - sjoyff->report->field[0]->value[2] = 0x00; - usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + sjoyff->report = report; + sjoyff->report->field[0]->value[0] = 0x01; + sjoyff->report->field[0]->value[1] = 0x00; + sjoyff->report->field[0]->value[2] = 0x00; + usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT); + } hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); @@ -131,6 +132,8 @@ static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; + hdev->quirks |= id->driver_data; + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); @@ -151,7 +154,17 @@ err: } static const struct hid_device_id sjoy_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO), + .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | + HID_QUIRK_SKIP_OUTPUT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO), + .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | + HID_QUIRK_SKIP_OUTPUT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD), + .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | + HID_QUIRK_SKIP_OUTPUT_REPORTS }, { } }; MODULE_DEVICE_TABLE(hid, sjoy_devices); diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 72ca689b6474..17bb88f782b6 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -304,11 +304,51 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, return 1; } +static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, unsigned long **bit, + int *max) +{ + struct input_dev *input = hi->input; + + __set_bit(INPUT_PROP_POINTER, input->propbit); + + /* Basics */ + input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL); + + __set_bit(REL_WHEEL, input->relbit); + + __set_bit(BTN_TOOL_PEN, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_STYLUS, input->keybit); + __set_bit(BTN_STYLUS2, input->keybit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + __set_bit(BTN_MIDDLE, input->keybit); + + /* Pad */ + input->evbit[0] |= BIT(EV_MSC); + + __set_bit(MSC_SERIAL, input->mscbit); + + __set_bit(BTN_0, input->keybit); + __set_bit(BTN_1, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + /* Distance, rubber and mouse */ + __set_bit(BTN_TOOL_RUBBER, input->keybit); + __set_bit(BTN_TOOL_MOUSE, input->keybit); + + input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); + + return 0; +} + static int wacom_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct hid_input *hidinput; - struct input_dev *input; struct wacom_data *wdata; int ret; @@ -370,42 +410,6 @@ static int wacom_probe(struct hid_device *hdev, goto err_ac; } #endif - hidinput = list_entry(hdev->inputs.next, struct hid_input, list); - input = hidinput->input; - - __set_bit(INPUT_PROP_POINTER, input->propbit); - - /* Basics */ - input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL); - - __set_bit(REL_WHEEL, input->relbit); - - __set_bit(BTN_TOOL_PEN, input->keybit); - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_STYLUS, input->keybit); - __set_bit(BTN_STYLUS2, input->keybit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_RIGHT, input->keybit); - __set_bit(BTN_MIDDLE, input->keybit); - - /* Pad */ - input->evbit[0] |= BIT(EV_MSC); - - __set_bit(MSC_SERIAL, input->mscbit); - - __set_bit(BTN_0, input->keybit); - __set_bit(BTN_1, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - /* Distance, rubber and mouse */ - __set_bit(BTN_TOOL_RUBBER, input->keybit); - __set_bit(BTN_TOOL_MOUSE, input->keybit); - - input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); - input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); - input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); - return 0; #ifdef CONFIG_HID_WACOM_POWER_SUPPLY @@ -448,6 +452,7 @@ static struct hid_driver wacom_driver = { .probe = wacom_probe, .remove = wacom_remove, .raw_event = wacom_raw_event, + .input_mapped = wacom_input_mapped, }; static int __init wacom_init(void) diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c index 85a02e5f9fe8..76739c07fa3c 100644 --- a/drivers/hid/hid-wiimote.c +++ b/drivers/hid/hid-wiimote.c @@ -10,15 +10,18 @@ * any later version. */ +#include <linux/completion.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/input.h> #include <linux/leds.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/power_supply.h> #include <linux/spinlock.h> #include "hid-ids.h" -#define WIIMOTE_VERSION "0.1" +#define WIIMOTE_VERSION "0.2" #define WIIMOTE_NAME "Nintendo Wii Remote" #define WIIMOTE_BUFSIZE 32 @@ -30,12 +33,26 @@ struct wiimote_buf { struct wiimote_state { spinlock_t lock; __u8 flags; + __u8 accel_split[2]; + + /* synchronous cmd requests */ + struct mutex sync; + struct completion ready; + int cmd; + __u32 opt; + + /* results of synchronous requests */ + __u8 cmd_battery; + __u8 cmd_err; }; struct wiimote_data { struct hid_device *hdev; struct input_dev *input; struct led_classdev *leds[4]; + struct input_dev *accel; + struct input_dev *ir; + struct power_supply battery; spinlock_t qlock; __u8 head; @@ -46,23 +63,47 @@ struct wiimote_data { struct wiimote_state state; }; -#define WIIPROTO_FLAG_LED1 0x01 -#define WIIPROTO_FLAG_LED2 0x02 -#define WIIPROTO_FLAG_LED3 0x04 -#define WIIPROTO_FLAG_LED4 0x08 +#define WIIPROTO_FLAG_LED1 0x01 +#define WIIPROTO_FLAG_LED2 0x02 +#define WIIPROTO_FLAG_LED3 0x04 +#define WIIPROTO_FLAG_LED4 0x08 +#define WIIPROTO_FLAG_RUMBLE 0x10 +#define WIIPROTO_FLAG_ACCEL 0x20 +#define WIIPROTO_FLAG_IR_BASIC 0x40 +#define WIIPROTO_FLAG_IR_EXT 0x80 +#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */ #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \ WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) +#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \ + WIIPROTO_FLAG_IR_FULL) /* return flag for led \num */ #define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1)) enum wiiproto_reqs { WIIPROTO_REQ_NULL = 0x0, + WIIPROTO_REQ_RUMBLE = 0x10, WIIPROTO_REQ_LED = 0x11, WIIPROTO_REQ_DRM = 0x12, + WIIPROTO_REQ_IR1 = 0x13, + WIIPROTO_REQ_SREQ = 0x15, + WIIPROTO_REQ_WMEM = 0x16, + WIIPROTO_REQ_RMEM = 0x17, + WIIPROTO_REQ_IR2 = 0x1a, WIIPROTO_REQ_STATUS = 0x20, + WIIPROTO_REQ_DATA = 0x21, WIIPROTO_REQ_RETURN = 0x22, WIIPROTO_REQ_DRM_K = 0x30, + WIIPROTO_REQ_DRM_KA = 0x31, + WIIPROTO_REQ_DRM_KE = 0x32, + WIIPROTO_REQ_DRM_KAI = 0x33, + WIIPROTO_REQ_DRM_KEE = 0x34, + WIIPROTO_REQ_DRM_KAE = 0x35, + WIIPROTO_REQ_DRM_KIE = 0x36, + WIIPROTO_REQ_DRM_KAIE = 0x37, + WIIPROTO_REQ_DRM_E = 0x3d, + WIIPROTO_REQ_DRM_SKAI1 = 0x3e, + WIIPROTO_REQ_DRM_SKAI2 = 0x3f, }; enum wiiproto_keys { @@ -94,6 +135,56 @@ static __u16 wiiproto_keymap[] = { BTN_MODE, /* WIIPROTO_KEY_HOME */ }; +static enum power_supply_property wiimote_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY +}; + +/* requires the state.lock spinlock to be held */ +static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + return wdata->state.cmd == cmd && wdata->state.opt == opt; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_complete(struct wiimote_data *wdata) +{ + wdata->state.cmd = WIIPROTO_REQ_NULL; + complete(&wdata->state.ready); +} + +static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) +{ + return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; +} + +/* requires the state.lock spinlock to be held */ +static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd, + __u32 opt) +{ + INIT_COMPLETION(wdata->state.ready); + wdata->state.cmd = cmd; + wdata->state.opt = opt; +} + +static inline void wiimote_cmd_release(struct wiimote_data *wdata) +{ + mutex_unlock(&wdata->state.sync); +} + +static inline int wiimote_cmd_wait(struct wiimote_data *wdata) +{ + int ret; + + ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); + if (ret < 0) + return -ERESTARTSYS; + else if (ret == 0) + return -EIO; + else + return 0; +} + static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, size_t count) { @@ -172,6 +263,39 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer, spin_unlock_irqrestore(&wdata->qlock, flags); } +/* + * This sets the rumble bit on the given output report if rumble is + * currently enabled. + * \cmd1 must point to the second byte in the output report => &cmd[1] + * This must be called on nearly every output report before passing it + * into the output queue! + */ +static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1) +{ + if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE) + *cmd1 |= 0x01; +} + +static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble) +{ + __u8 cmd[2]; + + rumble = !!rumble; + if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE)) + return; + + if (rumble) + wdata->state.flags |= WIIPROTO_FLAG_RUMBLE; + else + wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE; + + cmd[0] = WIIPROTO_REQ_RUMBLE; + cmd[1] = 0; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) { __u8 cmd[2]; @@ -193,6 +317,7 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) if (leds & WIIPROTO_FLAG_LED4) cmd[1] |= 0x80; + wiiproto_keep_rumble(wdata, &cmd[1]); wiimote_queue(wdata, cmd, sizeof(cmd)); } @@ -203,7 +328,23 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds) */ static __u8 select_drm(struct wiimote_data *wdata) { - return WIIPROTO_REQ_DRM_K; + __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR; + + if (ir == WIIPROTO_FLAG_IR_BASIC) { + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) + return WIIPROTO_REQ_DRM_KAIE; + else + return WIIPROTO_REQ_DRM_KIE; + } else if (ir == WIIPROTO_FLAG_IR_EXT) { + return WIIPROTO_REQ_DRM_KAI; + } else if (ir == WIIPROTO_FLAG_IR_FULL) { + return WIIPROTO_REQ_DRM_SKAI1; + } else { + if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) + return WIIPROTO_REQ_DRM_KA; + else + return WIIPROTO_REQ_DRM_K; + } } static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) @@ -217,9 +358,256 @@ static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm) cmd[1] = 0; cmd[2] = drm; + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_status(struct wiimote_data *wdata) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_SREQ; + cmd[1] = 0; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel) +{ + accel = !!accel; + if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) + return; + + if (accel) + wdata->state.flags |= WIIPROTO_FLAG_ACCEL; + else + wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL; + + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); +} + +static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_IR1; + cmd[1] = flags; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags) +{ + __u8 cmd[2]; + + cmd[0] = WIIPROTO_REQ_IR2; + cmd[1] = flags; + + wiiproto_keep_rumble(wdata, &cmd[1]); + wiimote_queue(wdata, cmd, sizeof(cmd)); +} + +#define wiiproto_req_wreg(wdata, os, buf, sz) \ + wiiproto_req_wmem((wdata), false, (os), (buf), (sz)) + +#define wiiproto_req_weeprom(wdata, os, buf, sz) \ + wiiproto_req_wmem((wdata), true, (os), (buf), (sz)) + +static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom, + __u32 offset, const __u8 *buf, __u8 size) +{ + __u8 cmd[22]; + + if (size > 16 || size == 0) { + hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size); + return; + } + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = WIIPROTO_REQ_WMEM; + cmd[2] = (offset >> 16) & 0xff; + cmd[3] = (offset >> 8) & 0xff; + cmd[4] = offset & 0xff; + cmd[5] = size; + memcpy(&cmd[6], buf, size); + + if (!eeprom) + cmd[1] |= 0x04; + + wiiproto_keep_rumble(wdata, &cmd[1]); wiimote_queue(wdata, cmd, sizeof(cmd)); } +/* requries the cmd-mutex to be held */ +static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset, + const __u8 *wmem, __u8 size) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0); + wiiproto_req_wreg(wdata, offset, wmem, size); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (!ret && wdata->state.cmd_err) + ret = -EIO; + + return ret; +} + +static int wiimote_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wiimote_data *wdata = container_of(psy, + struct wiimote_data, battery); + int ret = 0, state; + unsigned long flags; + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0); + wiiproto_req_status(wdata); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + state = wdata->state.cmd_battery; + wiimote_cmd_release(wdata); + + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = state * 100 / 255; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode) +{ + int ret; + unsigned long flags; + __u8 format = 0; + static const __u8 data_enable[] = { 0x01 }; + static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01, + 0x00, 0xaa, 0x00, 0x64 }; + static const __u8 data_sens2[] = { 0x63, 0x03 }; + static const __u8 data_fin[] = { 0x08 }; + + spin_lock_irqsave(&wdata->state.lock, flags); + + if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) { + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + if (mode == 0) { + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wiiproto_req_ir1(wdata, 0); + wiiproto_req_ir2(wdata, 0); + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; + } + + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_acquire(wdata); + if (ret) + return ret; + + /* send PIXEL CLOCK ENABLE cmd first */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0); + wiiproto_req_ir1(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR LOGIC */ + spin_lock_irqsave(&wdata->state.lock, flags); + wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0); + wiiproto_req_ir2(wdata, 0x06); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + ret = wiimote_cmd_wait(wdata); + if (ret) + goto unlock; + if (wdata->state.cmd_err) { + ret = -EIO; + goto unlock; + } + + /* enable IR cam but do not make it send data, yet */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_enable, + sizeof(data_enable)); + if (ret) + goto unlock; + + /* write first sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1, + sizeof(data_sens1)); + if (ret) + goto unlock; + + /* write second sensitivity block */ + ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2, + sizeof(data_sens2)); + if (ret) + goto unlock; + + /* put IR cam into desired state */ + switch (mode) { + case WIIPROTO_FLAG_IR_FULL: + format = 5; + break; + case WIIPROTO_FLAG_IR_EXT: + format = 3; + break; + case WIIPROTO_FLAG_IR_BASIC: + format = 1; + break; + } + ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format)); + if (ret) + goto unlock; + + /* make IR cam send data */ + ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin)); + if (ret) + goto unlock; + + /* request new DRM mode compatible to IR mode */ + spin_lock_irqsave(&wdata->state.lock, flags); + wdata->state.flags &= ~WIIPROTO_FLAGS_IR; + wdata->state.flags |= mode & WIIPROTO_FLAGS_IR; + wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + spin_unlock_irqrestore(&wdata->state.lock, flags); + +unlock: + wiimote_cmd_release(wdata); + return ret; +} + static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev) { struct wiimote_data *wdata; @@ -268,9 +656,28 @@ static void wiimote_leds_set(struct led_classdev *led_dev, } } -static int wiimote_input_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) +static int wiimote_ff_play(struct input_dev *dev, void *data, + struct ff_effect *eff) { + struct wiimote_data *wdata = input_get_drvdata(dev); + __u8 value; + unsigned long flags; + + /* + * The wiimote supports only a single rumble motor so if any magnitude + * is set to non-zero then we start the rumble motor. If both are set to + * zero, we stop the rumble motor. + */ + + if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude) + value = 1; + else + value = 0; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_rumble(wdata, value); + spin_unlock_irqrestore(&wdata->state.lock, flags); + return 0; } @@ -288,6 +695,61 @@ static void wiimote_input_close(struct input_dev *dev) hid_hw_close(wdata->hdev); } +static int wiimote_accel_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + int ret; + unsigned long flags; + + ret = hid_hw_open(wdata->hdev); + if (ret) + return ret; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, true); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + return 0; +} + +static void wiimote_accel_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&wdata->state.lock, flags); + wiiproto_req_accel(wdata, false); + spin_unlock_irqrestore(&wdata->state.lock, flags); + + hid_hw_close(wdata->hdev); +} + +static int wiimote_ir_open(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + int ret; + + ret = hid_hw_open(wdata->hdev); + if (ret) + return ret; + + ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC); + if (ret) { + hid_hw_close(wdata->hdev); + return ret; + } + + return 0; +} + +static void wiimote_ir_close(struct input_dev *dev) +{ + struct wiimote_data *wdata = input_get_drvdata(dev); + + wiimote_init_ir(wdata, 0); + hid_hw_close(wdata->hdev); +} + static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) { input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT], @@ -315,12 +777,100 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload) input_sync(wdata->input); } +static void handler_accel(struct wiimote_data *wdata, const __u8 *payload) +{ + __u16 x, y, z; + + if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL)) + return; + + /* + * payload is: BB BB XX YY ZZ + * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ + * contain the upper 8 bits of each value. The lower 2 bits are + * contained in the buttons data BB BB. + * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the + * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y + * accel value and bit 6 is the second bit of the Z value. + * The first bit of Y and Z values is not available and always set to 0. + * 0x200 is returned on no movement. + */ + + x = payload[2] << 2; + y = payload[3] << 2; + z = payload[4] << 2; + + x |= (payload[0] >> 5) & 0x3; + y |= (payload[1] >> 4) & 0x2; + z |= (payload[1] >> 5) & 0x2; + + input_report_abs(wdata->accel, ABS_RX, x - 0x200); + input_report_abs(wdata->accel, ABS_RY, y - 0x200); + input_report_abs(wdata->accel, ABS_RZ, z - 0x200); + input_sync(wdata->accel); +} + +#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT0X, ABS_HAT0Y) +#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT1X, ABS_HAT1Y) +#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT2X, ABS_HAT2Y) +#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \ + ABS_HAT3X, ABS_HAT3Y) + +static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, + bool packed, __u8 xid, __u8 yid) +{ + __u16 x, y; + + if (!(wdata->state.flags & WIIPROTO_FLAGS_IR)) + return; + + /* + * Basic IR data is encoded into 3 bytes. The first two bytes are the + * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits + * of both. + * If data is packed, then the 3rd byte is put first and slightly + * reordered. This allows to interleave packed and non-packed data to + * have two IR sets in 5 bytes instead of 6. + * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev. + */ + + if (packed) { + x = ir[1] << 2; + y = ir[2] << 2; + + x |= ir[0] & 0x3; + y |= (ir[0] >> 2) & 0x3; + } else { + x = ir[0] << 2; + y = ir[1] << 2; + + x |= (ir[2] >> 4) & 0x3; + y |= (ir[2] >> 6) & 0x3; + } + + input_report_abs(wdata->ir, xid, x); + input_report_abs(wdata->ir, yid, y); +} + static void handler_status(struct wiimote_data *wdata, const __u8 *payload) { handler_keys(wdata, payload); /* on status reports the drm is reset so we need to resend the drm */ wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL); + + if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) { + wdata->state.cmd_battery = payload[5]; + wiimote_cmd_complete(wdata); + } +} + +static void handler_data(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); } static void handler_return(struct wiimote_data *wdata, const __u8 *payload) @@ -330,9 +880,105 @@ static void handler_return(struct wiimote_data *wdata, const __u8 *payload) handler_keys(wdata, payload); - if (err) + if (wiimote_cmd_pending(wdata, cmd, 0)) { + wdata->state.cmd_err = err; + wiimote_cmd_complete(wdata); + } else if (err) { hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err, cmd); + } +} + +static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); +} + +static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + +static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); + ir_to_input0(wdata, &payload[5], false); + ir_to_input1(wdata, &payload[8], false); + ir_to_input2(wdata, &payload[11], false); + ir_to_input3(wdata, &payload[14], false); + input_sync(wdata->ir); +} + +static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); +} + +static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + ir_to_input0(wdata, &payload[2], false); + ir_to_input1(wdata, &payload[4], true); + ir_to_input2(wdata, &payload[7], false); + ir_to_input3(wdata, &payload[9], true); + input_sync(wdata->ir); +} + +static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); +} + +static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + handler_accel(wdata, payload); + ir_to_input0(wdata, &payload[5], false); + ir_to_input1(wdata, &payload[7], true); + ir_to_input2(wdata, &payload[10], false); + ir_to_input3(wdata, &payload[12], true); + input_sync(wdata->ir); +} + +static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload) +{ +} + +static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload) +{ + handler_keys(wdata, payload); + + wdata->state.accel_split[0] = payload[2]; + wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20); + wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80); + + ir_to_input0(wdata, &payload[3], false); + ir_to_input1(wdata, &payload[12], false); + input_sync(wdata->ir); +} + +static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload) +{ + __u8 buf[5]; + + handler_keys(wdata, payload); + + wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02); + wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08); + + buf[0] = 0; + buf[1] = 0; + buf[2] = wdata->state.accel_split[0]; + buf[3] = payload[2]; + buf[4] = wdata->state.accel_split[1]; + handler_accel(wdata, buf); + + ir_to_input2(wdata, &payload[3], false); + ir_to_input3(wdata, &payload[12], false); + input_sync(wdata->ir); } struct wiiproto_handler { @@ -343,8 +989,19 @@ struct wiiproto_handler { static struct wiiproto_handler handlers[] = { { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status }, + { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data }, { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return }, { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys }, + { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA }, + { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE }, + { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI }, + { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE }, + { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE }, + { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE }, + { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE }, + { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E }, + { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 }, + { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 }, { .id = 0 } }; @@ -355,6 +1012,7 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, struct wiiproto_handler *h; int i; unsigned long flags; + bool handled = false; if (size < 1) return -EINVAL; @@ -363,10 +1021,16 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report, for (i = 0; handlers[i].id; ++i) { h = &handlers[i]; - if (h->id == raw_data[0] && h->size < size) + if (h->id == raw_data[0] && h->size < size) { h->func(wdata, &raw_data[1]); + handled = true; + } } + if (!handled) + hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0], + size); + spin_unlock_irqrestore(&wdata->state.lock, flags); return 0; @@ -434,16 +1098,13 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) return NULL; wdata->input = input_allocate_device(); - if (!wdata->input) { - kfree(wdata); - return NULL; - } + if (!wdata->input) + goto err; wdata->hdev = hdev; hid_set_drvdata(hdev, wdata); input_set_drvdata(wdata->input, wdata); - wdata->input->event = wiimote_input_event; wdata->input->open = wiimote_input_open; wdata->input->close = wiimote_input_close; wdata->input->dev.parent = &wdata->hdev->dev; @@ -457,18 +1118,89 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev) for (i = 0; i < WIIPROTO_KEY_COUNT; ++i) set_bit(wiiproto_keymap[i], wdata->input->keybit); + set_bit(FF_RUMBLE, wdata->input->ffbit); + if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play)) + goto err_input; + + wdata->accel = input_allocate_device(); + if (!wdata->accel) + goto err_input; + + input_set_drvdata(wdata->accel, wdata); + wdata->accel->open = wiimote_accel_open; + wdata->accel->close = wiimote_accel_close; + wdata->accel->dev.parent = &wdata->hdev->dev; + wdata->accel->id.bustype = wdata->hdev->bus; + wdata->accel->id.vendor = wdata->hdev->vendor; + wdata->accel->id.product = wdata->hdev->product; + wdata->accel->id.version = wdata->hdev->version; + wdata->accel->name = WIIMOTE_NAME " Accelerometer"; + + set_bit(EV_ABS, wdata->accel->evbit); + set_bit(ABS_RX, wdata->accel->absbit); + set_bit(ABS_RY, wdata->accel->absbit); + set_bit(ABS_RZ, wdata->accel->absbit); + input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4); + input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4); + + wdata->ir = input_allocate_device(); + if (!wdata->ir) + goto err_ir; + + input_set_drvdata(wdata->ir, wdata); + wdata->ir->open = wiimote_ir_open; + wdata->ir->close = wiimote_ir_close; + wdata->ir->dev.parent = &wdata->hdev->dev; + wdata->ir->id.bustype = wdata->hdev->bus; + wdata->ir->id.vendor = wdata->hdev->vendor; + wdata->ir->id.product = wdata->hdev->product; + wdata->ir->id.version = wdata->hdev->version; + wdata->ir->name = WIIMOTE_NAME " IR"; + + set_bit(EV_ABS, wdata->ir->evbit); + set_bit(ABS_HAT0X, wdata->ir->absbit); + set_bit(ABS_HAT0Y, wdata->ir->absbit); + set_bit(ABS_HAT1X, wdata->ir->absbit); + set_bit(ABS_HAT1Y, wdata->ir->absbit); + set_bit(ABS_HAT2X, wdata->ir->absbit); + set_bit(ABS_HAT2Y, wdata->ir->absbit); + set_bit(ABS_HAT3X, wdata->ir->absbit); + set_bit(ABS_HAT3Y, wdata->ir->absbit); + input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4); + input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4); + spin_lock_init(&wdata->qlock); INIT_WORK(&wdata->worker, wiimote_worker); spin_lock_init(&wdata->state.lock); + init_completion(&wdata->state.ready); + mutex_init(&wdata->state.sync); return wdata; + +err_ir: + input_free_device(wdata->accel); +err_input: + input_free_device(wdata->input); +err: + kfree(wdata); + return NULL; } static void wiimote_destroy(struct wiimote_data *wdata) { wiimote_leds_destroy(wdata); + power_supply_unregister(&wdata->battery); + input_unregister_device(wdata->accel); + input_unregister_device(wdata->ir); input_unregister_device(wdata->input); cancel_work_sync(&wdata->worker); hid_hw_stop(wdata->hdev); @@ -500,12 +1232,37 @@ static int wiimote_hid_probe(struct hid_device *hdev, goto err; } - ret = input_register_device(wdata->input); + ret = input_register_device(wdata->accel); if (ret) { hid_err(hdev, "Cannot register input device\n"); goto err_stop; } + ret = input_register_device(wdata->ir); + if (ret) { + hid_err(hdev, "Cannot register input device\n"); + goto err_ir; + } + + ret = input_register_device(wdata->input); + if (ret) { + hid_err(hdev, "Cannot register input device\n"); + goto err_input; + } + + wdata->battery.properties = wiimote_battery_props; + wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props); + wdata->battery.get_property = wiimote_battery_get_property; + wdata->battery.name = "wiimote_battery"; + wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wdata->battery.use_for_apm = 0; + + ret = power_supply_register(&wdata->hdev->dev, &wdata->battery); + if (ret) { + hid_err(hdev, "Cannot register battery device\n"); + goto err_battery; + } + ret = wiimote_leds_create(wdata); if (ret) goto err_free; @@ -523,9 +1280,20 @@ err_free: wiimote_destroy(wdata); return ret; +err_battery: + input_unregister_device(wdata->input); + wdata->input = NULL; +err_input: + input_unregister_device(wdata->ir); + wdata->ir = NULL; +err_ir: + input_unregister_device(wdata->accel); + wdata->accel = NULL; err_stop: hid_hw_stop(hdev); err: + input_free_device(wdata->ir); + input_free_device(wdata->accel); input_free_device(wdata->input); kfree(wdata); return ret; diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c index e90371508fd2..1ad85f2257b4 100644 --- a/drivers/hid/hid-zydacron.c +++ b/drivers/hid/hid-zydacron.c @@ -201,9 +201,7 @@ static void zc_remove(struct hid_device *hdev) struct zc_device *zc = hid_get_drvdata(hdev); hid_hw_stop(hdev); - - if (NULL != zc) - kfree(zc); + kfree(zc); } static const struct hid_device_id zc_devices[] = { diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index c79578b5a788..cf7d6d58e79f 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -259,7 +259,6 @@ static int hidraw_open(struct inode *inode, struct file *file) mutex_lock(&minors_lock); if (!hidraw_table[minor]) { - kfree(list); err = -ENODEV; goto out_unlock; } @@ -272,8 +271,10 @@ static int hidraw_open(struct inode *inode, struct file *file) dev = hidraw_table[minor]; if (!dev->open++) { err = hid_hw_power(dev->hid, PM_HINT_FULLON); - if (err < 0) + if (err < 0) { + dev->open--; goto out_unlock; + } err = hid_hw_open(dev->hid); if (err < 0) { @@ -285,6 +286,8 @@ static int hidraw_open(struct inode *inode, struct file *file) out_unlock: mutex_unlock(&minors_lock); out: + if (err < 0) + kfree(list); return err; } @@ -510,13 +513,12 @@ void hidraw_disconnect(struct hid_device *hid) { struct hidraw *hidraw = hid->hidraw; + mutex_lock(&minors_lock); hidraw->exist = 0; device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - mutex_lock(&minors_lock); hidraw_table[hidraw->minor] = NULL; - mutex_unlock(&minors_lock); if (hidraw->open) { hid_hw_close(hid); @@ -524,6 +526,7 @@ void hidraw_disconnect(struct hid_device *hid) } else { kfree(hidraw); } + mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ad978f5748d3..77e705c2209c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1270,7 +1270,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) static void hid_cease_io(struct usbhid_device *usbhid) { - del_timer(&usbhid->io_retry); + del_timer_sync(&usbhid->io_retry); usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbctrl); usb_kill_urb(usbhid->urbout); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 3146fdcda272..4ea464151c3b 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -80,10 +80,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE }, diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 7c1188b53c3e..4ef02b269a71 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -641,6 +641,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct usb_device *dev = hid_to_usb_dev(hid); struct usbhid_device *usbhid = hid->driver_data; + memset(&dinfo, 0, sizeof(dinfo)); + dinfo.bustype = BUS_USB; dinfo.busnum = dev->bus->busnum; dinfo.devnum = dev->devnum; diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 7b0260dc76fb..36d7f270b14d 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -390,7 +390,7 @@ temp_from_reg(u16 reg, s16 regval) { if (is_word_sized(reg)) return LM75_TEMP_FROM_REG(regval); - return regval * 1000; + return ((s8)regval) * 1000; } static inline u16 @@ -398,7 +398,8 @@ temp_to_reg(u16 reg, long temp) { if (is_word_sized(reg)) return LM75_TEMP_TO_REG(temp); - return DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), 1000); + return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), + 1000); } /* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c index b7a51c43b185..1b42b50b5992 100644 --- a/drivers/i2c/busses/i2c-designware.c +++ b/drivers/i2c/busses/i2c-designware.c @@ -390,7 +390,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) int tx_limit, rx_limit; u32 addr = msgs[dev->msg_write_idx].addr; u32 buf_len = dev->tx_buf_len; - u8 *buf = dev->tx_buf;; + u8 *buf = dev->tx_buf; intr_mask = DW_IC_INTR_DEFAULT_MASK; diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 811dbbd9306c..76b6d98bd295 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -681,7 +681,7 @@ config BLK_DEV_IDE_AU1XXX select IDE_XFER_MODE choice prompt "IDE Mode for AMD Alchemy Au1200" - default CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA + default BLK_DEV_IDE_AU1XXX_PIO_DBDMA depends on SOC_AU1200 && BLK_DEV_IDE_AU1XXX config BLK_DEV_IDE_AU1XXX_PIO_DBDMA diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 2c8b84dd9dac..2be21694fac1 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -7,7 +7,7 @@ * state machine code inspired by code from Tim Ruetz * * A generic driver for rotary encoders connected to GPIO lines. - * See file:Documentation/input/rotary_encoder.txt for more information + * See file:Documentation/input/rotary-encoder.txt for more information * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c621c98c99da..a88f3cbb100b 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -306,6 +306,11 @@ static inline bool dma_pte_present(struct dma_pte *pte) return (pte->val & 3) != 0; } +static inline bool dma_pte_superpage(struct dma_pte *pte) +{ + return (pte->val & (1 << 7)); +} + static inline int first_pte_in_page(struct dma_pte *pte) { return !((unsigned long)pte & ~VTD_PAGE_MASK); @@ -404,6 +409,9 @@ static int dmar_forcedac; static int intel_iommu_strict; static int intel_iommu_superpage = 1; +int intel_iommu_gfx_mapped; +EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); + #define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1)) static DEFINE_SPINLOCK(device_domain_lock); static LIST_HEAD(device_domain_list); @@ -577,17 +585,18 @@ static void domain_update_iommu_snooping(struct dmar_domain *domain) static void domain_update_iommu_superpage(struct dmar_domain *domain) { - int i, mask = 0xf; + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu = NULL; + int mask = 0xf; if (!intel_iommu_superpage) { domain->iommu_superpage = 0; return; } - domain->iommu_superpage = 4; /* 1TiB */ - - for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) { - mask |= cap_super_page_val(g_iommus[i]->cap); + /* set iommu_superpage to the smallest common denominator */ + for_each_active_iommu(iommu, drhd) { + mask &= cap_super_page_val(iommu->cap); if (!mask) { break; } @@ -730,29 +739,23 @@ out: } static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, - unsigned long pfn, int large_level) + unsigned long pfn, int target_level) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; struct dma_pte *parent, *pte = NULL; int level = agaw_to_level(domain->agaw); - int offset, target_level; + int offset; BUG_ON(!domain->pgd); BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width); parent = domain->pgd; - /* Search pte */ - if (!large_level) - target_level = 1; - else - target_level = large_level; - while (level > 0) { void *tmp_page; offset = pfn_level_offset(pfn, level); pte = &parent[offset]; - if (!large_level && (pte->val & DMA_PTE_LARGE_PAGE)) + if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte))) break; if (level == target_level) break; @@ -816,13 +819,14 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain, } /* clear last level pte, a tlb flush should be followed */ -static void dma_pte_clear_range(struct dmar_domain *domain, +static int dma_pte_clear_range(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; unsigned int large_page = 1; struct dma_pte *first_pte, *pte; + int order; BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); @@ -846,6 +850,9 @@ static void dma_pte_clear_range(struct dmar_domain *domain, (void *)pte - (void *)first_pte); } while (start_pfn && start_pfn <= last_pfn); + + order = (large_page - 1) * 9; + return order; } /* free page table pages. last level pte should already be cleared */ @@ -3226,9 +3233,6 @@ static void __init init_no_remapping_devices(void) } } - if (dmar_map_gfx) - return; - for_each_drhd_unit(drhd) { int i; if (drhd->ignored || drhd->include_all) @@ -3236,18 +3240,23 @@ static void __init init_no_remapping_devices(void) for (i = 0; i < drhd->devices_cnt; i++) if (drhd->devices[i] && - !IS_GFX_DEVICE(drhd->devices[i])) + !IS_GFX_DEVICE(drhd->devices[i])) break; if (i < drhd->devices_cnt) continue; - /* bypass IOMMU if it is just for gfx devices */ - drhd->ignored = 1; - for (i = 0; i < drhd->devices_cnt; i++) { - if (!drhd->devices[i]) - continue; - drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO; + /* This IOMMU has *only* gfx devices. Either bypass it or + set the gfx_mapped flag, as appropriate */ + if (dmar_map_gfx) { + intel_iommu_gfx_mapped = 1; + } else { + drhd->ignored = 1; + for (i = 0; i < drhd->devices_cnt; i++) { + if (!drhd->devices[i]) + continue; + drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO; + } } } } @@ -3568,6 +3577,8 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, found = 1; } + spin_unlock_irqrestore(&device_domain_lock, flags); + if (found == 0) { unsigned long tmp_flags; spin_lock_irqsave(&domain->iommu_lock, tmp_flags); @@ -3584,8 +3595,6 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, spin_unlock_irqrestore(&iommu->lock, tmp_flags); } } - - spin_unlock_irqrestore(&device_domain_lock, flags); } static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) @@ -3739,6 +3748,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain) vm_domain_exit(dmar_domain); return -ENOMEM; } + domain_update_iommu_cap(dmar_domain); domain->priv = dmar_domain; return 0; @@ -3864,14 +3874,15 @@ static int intel_iommu_unmap(struct iommu_domain *domain, { struct dmar_domain *dmar_domain = domain->priv; size_t size = PAGE_SIZE << gfp_order; + int order; - dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT, + order = dma_pte_clear_range(dmar_domain, iova >> VTD_PAGE_SHIFT, (iova + size - 1) >> VTD_PAGE_SHIFT); if (dmar_domain->max_addr == iova + size) dmar_domain->max_addr = iova; - return gfp_order; + return order; } static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, @@ -3950,7 +3961,11 @@ static void __devinit quirk_calpella_no_shadow_gtt(struct pci_dev *dev) if (!(ggc & GGC_MEMORY_VT_ENABLED)) { printk(KERN_INFO "DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); dmar_map_gfx = 0; - } + } else if (dmar_map_gfx) { + /* we have to ensure the gfx device is idle before we flush */ + printk(KERN_INFO "DMAR: Disabling batched IOTLB flush on Ironlake\n"); + intel_iommu_strict = 1; + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0040, quirk_calpella_no_shadow_gtt); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_calpella_no_shadow_gtt); diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 6ed82add6ffa..6ddb795e31c5 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -2308,11 +2308,11 @@ static int __init isdn_init(void) int i; char tmprev[50]; - if (!(dev = vmalloc(sizeof(isdn_dev)))) { + dev = vzalloc(sizeof(isdn_dev)); + if (!dev) { printk(KERN_WARNING "isdn: Could not allocate device-struct.\n"); return -EIO; } - memset((char *) dev, 0, sizeof(isdn_dev)); init_timer(&dev->timer); dev->timer.function = isdn_timer_funct; spin_lock_init(&dev->lock); diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 2877291a9ed8..0c41553ce684 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -1052,12 +1052,11 @@ dspcreate(struct channel_req *crq) if (crq->protocol != ISDN_P_B_L2DSP && crq->protocol != ISDN_P_B_L2DSPHDLC) return -EPROTONOSUPPORT; - ndsp = vmalloc(sizeof(struct dsp)); + ndsp = vzalloc(sizeof(struct dsp)); if (!ndsp) { printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); return -ENOMEM; } - memset(ndsp, 0, sizeof(struct dsp)); if (dsp_debug & DEBUG_DSP_CTRL) printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c index bbfd1b863ed3..5a89972624d8 100644 --- a/drivers/isdn/mISDN/l1oip_codec.c +++ b/drivers/isdn/mISDN/l1oip_codec.c @@ -330,14 +330,12 @@ l1oip_4bit_alloc(int ulaw) return 0; /* alloc conversion tables */ - table_com = vmalloc(65536); - table_dec = vmalloc(512); + table_com = vzalloc(65536); + table_dec = vzalloc(512); if (!table_com || !table_dec) { l1oip_4bit_free(); return -ENOMEM; } - memset(table_com, 0, 65536); - memset(table_dec, 0, 512); /* generate compression table */ i1 = 0; while (i1 < 256) { diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b591e726a6fa..807c875f1c2e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -400,7 +400,7 @@ config LEDS_TRIGGER_TIMER This allows LEDs to be controlled by a programmable timer via sysfs. Some LED hardware can be programmed to start blinking the LED without any further software interaction. - For more details read Documentation/leds-class.txt. + For more details read Documentation/leds/leds-class.txt. If unsure, say Y. diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index f82147029636..32ac70861d66 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -628,6 +628,7 @@ void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, job->kc = kc; job->fn = fn; job->context = context; + job->master_job = job; atomic_inc(&kc->nr_jobs); diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c index c3bc64ed405c..7e3961d0db6b 100644 --- a/drivers/media/dvb/dvb-usb/af9005-remote.c +++ b/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -21,7 +21,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * see Documentation/dvb/REDME.dvb-usb for more information + * see Documentation/dvb/README.dvb-usb for more information */ #include "af9005.h" /* debug */ diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c index 51f6439dcfd5..0351c0e52dd2 100644 --- a/drivers/media/dvb/dvb-usb/af9005.c +++ b/drivers/media/dvb/dvb-usb/af9005.c @@ -19,7 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * see Documentation/dvb/REDME.dvb-usb for more information + * see Documentation/dvb/README.dvb-usb for more information */ #include "af9005.h" diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h index ba917359fa65..404f63a6f26b 100644 --- a/drivers/media/dvb/frontends/dib3000.h +++ b/drivers/media/dvb/frontends/dib3000.h @@ -17,7 +17,7 @@ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * see Documentation/dvb/README.dibusb for more information + * see Documentation/dvb/README.dvb-usb for more information * */ diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index e80c59796368..437904cbf3e6 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -17,7 +17,7 @@ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * see Documentation/dvb/README.dibusb for more information + * see Documentation/dvb/README.dvb-usb for more information * */ diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 8c0e19276970..ec1d52f38904 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -402,7 +402,7 @@ static int fm_v4l2_vidioc_s_hw_freq_seek(struct file *file, void *priv, static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, struct v4l2_modulator *mod) { - struct fmdev *fmdev = video_drvdata(file);; + struct fmdev *fmdev = video_drvdata(file); if (mod->index != 0) return -EINVAL; diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 05fe6bdbe062..b63fdfaac49e 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -69,7 +69,7 @@ struct cx18_mailbox { /* Each command can have up to 6 arguments */ u32 args[MAX_MB_ARGUMENTS]; /* The return code can be one of the codes in the file cx23418.h. If the - command is completed successfuly, the error will be ERR_SYS_SUCCESS. + command is completed successfully, the error will be ERR_SYS_SUCCESS. If it is pending, the code is ERR_SYS_PENDING. If it failed, the error code would indicate the task from which the error originated and will be one of the errors in cx23418.h. In that case, the following diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 0fd7e77bee29..dee33addcaeb 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -90,6 +90,7 @@ #define PCH_PHUB_INTPIN_REG_WPERMIT_REG3 0x002C #define PCH_PHUB_INT_REDUCE_CONTROL_REG_BASE 0x0040 #define CLKCFG_REG_OFFSET 0x500 +#define FUNCSEL_REG_OFFSET 0x508 #define PCH_PHUB_OROM_SIZE 15360 @@ -108,6 +109,7 @@ * @intpin_reg_wpermit_reg3: INTPIN_REG_WPERMIT register 3 val * @int_reduce_control_reg: INT_REDUCE_CONTROL registers val * @clkcfg_reg: CLK CFG register val + * @funcsel_reg: Function select register value * @pch_phub_base_address: Register base address * @pch_phub_extrom_base_address: external rom base address * @pch_mac_start_address: MAC address area start address @@ -128,6 +130,7 @@ struct pch_phub_reg { u32 intpin_reg_wpermit_reg3; u32 int_reduce_control_reg[MAX_NUM_INT_REDUCE_CONTROL_REG]; u32 clkcfg_reg; + u32 funcsel_reg; void __iomem *pch_phub_base_address; void __iomem *pch_phub_extrom_base_address; u32 pch_mac_start_address; @@ -211,6 +214,8 @@ static void pch_phub_save_reg_conf(struct pci_dev *pdev) __func__, i, chip->int_reduce_control_reg[i]); } chip->clkcfg_reg = ioread32(p + CLKCFG_REG_OFFSET); + if ((chip->ioh_type == 2) || (chip->ioh_type == 4)) + chip->funcsel_reg = ioread32(p + FUNCSEL_REG_OFFSET); } /* pch_phub_restore_reg_conf - restore register configuration */ @@ -271,6 +276,8 @@ static void pch_phub_restore_reg_conf(struct pci_dev *pdev) } iowrite32(chip->clkcfg_reg, p + CLKCFG_REG_OFFSET); + if ((chip->ioh_type == 2) || (chip->ioh_type == 4)) + iowrite32(chip->funcsel_reg, p + FUNCSEL_REG_OFFSET); } /** @@ -594,8 +601,7 @@ static ssize_t show_pch_mac(struct device *dev, struct device_attribute *attr, pch_phub_read_gbe_mac_addr(chip, mac); - return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return sprintf(buf, "%pM\n", mac); } static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 4be8373d43e5..66b616ebe536 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -142,7 +142,7 @@ config MTD_OF_PARTS help This provides a partition parsing function which derives the partition map from the children of the flash node, - as described in Documentation/powerpc/booting-without-of.txt. + as described in Documentation/devicetree/booting-without-of.txt. config MTD_AR7_PARTS tristate "TI AR7 partitioning support" diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index cee6ba2b8b58..c3dd9d09be57 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -29,7 +29,7 @@ * nxp,external-clock-frequency = <16000000>; * }; * - * See "Documentation/powerpc/dts-bindings/can/sja1000.txt" for further + * See "Documentation/devicetree/bindings/net/can/sja1000.txt" for further * information. */ diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 0405261efb5c..33a4e35f5ee8 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -858,7 +858,7 @@ static s32 atl1_init_hw(struct atl1_hw *hw) atl1_init_flash_opcode(hw); if (!hw->phy_configured) { - /* enable GPHY LinkChange Interrrupt */ + /* enable GPHY LinkChange Interrupt */ ret_val = atl1_write_phy_reg(hw, 18, 0xC00); if (ret_val) return ret_val; diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index fc50d4267df8..99d31a7d6aaa 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -5617,7 +5617,7 @@ struct l2_fhdr { #define BNX2_TXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_TXP_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_TXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_TXP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_TXP_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_TXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_TXP_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_TXP_CPU_STATE_BLOCKED_READ (1L<<31) @@ -5712,7 +5712,7 @@ struct l2_fhdr { #define BNX2_TPAT_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_TPAT_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_TPAT_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_TPAT_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_TPAT_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_TPAT_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_TPAT_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_TPAT_CPU_STATE_BLOCKED_READ (1L<<31) @@ -5807,7 +5807,7 @@ struct l2_fhdr { #define BNX2_RXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_RXP_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_RXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_RXP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_RXP_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_RXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_RXP_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_RXP_CPU_STATE_BLOCKED_READ (1L<<31) @@ -5953,7 +5953,7 @@ struct l2_fhdr { #define BNX2_COM_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_COM_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_COM_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_COM_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_COM_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_COM_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_COM_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_COM_CPU_STATE_BLOCKED_READ (1L<<31) @@ -6119,7 +6119,7 @@ struct l2_fhdr { #define BNX2_CP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_CP_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_CP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_CP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_CP_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_CP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_CP_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_CP_CPU_STATE_BLOCKED_READ (1L<<31) @@ -6291,7 +6291,7 @@ struct l2_fhdr { #define BNX2_MCP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) #define BNX2_MCP_CPU_STATE_SOFT_HALTED (1L<<10) #define BNX2_MCP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) -#define BNX2_MCP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_MCP_CPU_STATE_INTERRUPT (1L<<12) #define BNX2_MCP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) #define BNX2_MCP_CPU_STATE_INST_FETCH_STALL (1L<<15) #define BNX2_MCP_CPU_STATE_BLOCKED_READ (1L<<31) diff --git a/drivers/net/ethernet/dec/tulip/21142.c b/drivers/net/ethernet/dec/tulip/21142.c index 092c3faa882a..25b8deedbef8 100644 --- a/drivers/net/ethernet/dec/tulip/21142.c +++ b/drivers/net/ethernet/dec/tulip/21142.c @@ -7,9 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - DC21143 manual "21143 PCI/CardBus 10/100Mb/s Ethernet LAN Controller Hardware Reference Manual" is currently available at : http://developer.intel.com/design/network/manuals/278074.htm diff --git a/drivers/net/ethernet/dec/tulip/eeprom.c b/drivers/net/ethernet/dec/tulip/eeprom.c index fa5eee925f25..14d5b611783d 100644 --- a/drivers/net/ethernet/dec/tulip/eeprom.c +++ b/drivers/net/ethernet/dec/tulip/eeprom.c @@ -7,8 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. Please submit bug reports to http://bugzilla.kernel.org/. */ diff --git a/drivers/net/ethernet/dec/tulip/interrupt.c b/drivers/net/ethernet/dec/tulip/interrupt.c index 5350d753e0ff..4fb8c8c0a420 100644 --- a/drivers/net/ethernet/dec/tulip/interrupt.c +++ b/drivers/net/ethernet/dec/tulip/interrupt.c @@ -7,10 +7,7 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. Please submit bugs to http://bugzilla.kernel.org/ . - */ #include <linux/pci.h> diff --git a/drivers/net/ethernet/dec/tulip/media.c b/drivers/net/ethernet/dec/tulip/media.c index 4bd13922875d..beeb17b52ad4 100644 --- a/drivers/net/ethernet/dec/tulip/media.c +++ b/drivers/net/ethernet/dec/tulip/media.c @@ -7,9 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/dec/tulip/pnic.c b/drivers/net/ethernet/dec/tulip/pnic.c index 52d898bdbeb4..9c16e4ad02a6 100644 --- a/drivers/net/ethernet/dec/tulip/pnic.c +++ b/drivers/net/ethernet/dec/tulip/pnic.c @@ -7,9 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/dec/tulip/pnic2.c b/drivers/net/ethernet/dec/tulip/pnic2.c index 93358ee4d830..04a7e477eaff 100644 --- a/drivers/net/ethernet/dec/tulip/pnic2.c +++ b/drivers/net/ethernet/dec/tulip/pnic2.c @@ -8,9 +8,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/dec/tulip/timer.c b/drivers/net/ethernet/dec/tulip/timer.c index 2017faf2d0e6..19078d28ffb9 100644 --- a/drivers/net/ethernet/dec/tulip/timer.c +++ b/drivers/net/ethernet/dec/tulip/timer.c @@ -7,9 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/dec/tulip/tulip.h b/drivers/net/ethernet/dec/tulip/tulip.h index 9db528967da9..fb3887c18dc6 100644 --- a/drivers/net/ethernet/dec/tulip/tulip.h +++ b/drivers/net/ethernet/dec/tulip/tulip.h @@ -7,9 +7,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 011f67c7ca47..9656dd0647d9 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -6,9 +6,6 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. - Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} - for more information on this driver. - Please submit bugs to http://bugzilla.kernel.org/ . */ diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 74f2f11ac290..469d95eaa154 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -34,7 +34,7 @@ * @size: Length of buffer * @mbx_id: id of mailbox to read * - * returns SUCCESS if it successfuly read message from buffer + * returns SUCCESS if it successfully read message from buffer **/ s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) { diff --git a/drivers/net/ethernet/intel/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c index 3d6f4cc3998a..048aae248d06 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.c +++ b/drivers/net/ethernet/intel/igbvf/mbx.c @@ -288,7 +288,7 @@ out_no_write: * @msg: The message buffer * @size: Length of buffer * - * returns SUCCESS if it successfuly read message from buffer + * returns SUCCESS if it successfully read message from buffer **/ static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c index 1ff0eefcfd0a..3f725d48336d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c @@ -38,7 +38,7 @@ * @size: Length of buffer * @mbx_id: id of mailbox to read * - * returns SUCCESS if it successfuly read message from buffer + * returns SUCCESS if it successfully read message from buffer **/ s32 ixgbe_read_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size, u16 mbx_id) { diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.c b/drivers/net/ethernet/intel/ixgbevf/mbx.c index 7a8833125770..930fa83f2568 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.c +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.c @@ -276,7 +276,7 @@ out_no_write: * @msg: The message buffer * @size: Length of buffer * - * returns 0 if it successfuly read message from buffer + * returns 0 if it successfully read message from buffer **/ static s32 ixgbevf_read_mbx_vf(struct ixgbe_hw *hw, u32 *msg, u16 size) { diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 7b083c438a14..cbd026f3bc57 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -2082,7 +2082,7 @@ static void sky2_hw_down(struct sky2_port *sky2) sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); - /* Force any delayed status interrrupt and NAPI */ + /* Force any delayed status interrupt and NAPI */ sky2_write32(hw, STAT_LEV_TIMER_CNT, 0); sky2_write32(hw, STAT_TX_TIMER_CNT, 0); sky2_write32(hw, STAT_ISR_TIMER_CNT, 0); diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c index 70788401d699..ab81c0dc96e2 100644 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ b/drivers/net/ethernet/micrel/ks8695net.c @@ -414,7 +414,7 @@ ks8695_tx_irq(int irq, void *dev_id) * Interrupt Status Register (Offset 0xF208) * Bit29: WAN MAC Receive Status * Bit16: LAN MAC Receive Status - * So, this Rx interrrupt enable/status bit number is equal + * So, this Rx interrupt enable/status bit number is equal * as Rx IRQ number. */ static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp) diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 1854c88dfb92..5a689af516e9 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -106,8 +106,7 @@ config SMSC911X Say Y here if you want support for SMSC LAN911x and LAN921x families of ethernet controllers. - To compile this driver as a module, choose M here and read - <file:Documentation/networking/net-modules.txt>. The module + To compile this driver as a module, choose M here. The module will be called smsc911x. config SMSC911X_ARCH_HOOKS diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 78e3fb226cce..10826d8a2a2d 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -177,7 +177,7 @@ struct tile_net_cpu { struct tile_net_stats_t stats; /* True iff NAPI is enabled. */ bool napi_enabled; - /* True if this tile has succcessfully registered with the IPP. */ + /* True if this tile has successfully registered with the IPP. */ bool registered; /* True if the link was down last time we tried to register. */ bool link_down; diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index 25bb2a015e18..a40fab44b9ae 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -183,7 +183,7 @@ config OLD_BELKIN_DONGLE Say Y here if you want to build support for the Adaptec Airport 1000 and 2000 dongles. If you want to compile it as a module, choose M here. Some information is contained in the comments - at the top of <file:drivers/net/irda/old_belkin.c>. + at the top of <file:drivers/net/irda/old_belkin-sir.c>. config ACT200L_DONGLE tristate "ACTiSYS IR-200L dongle" diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index d84c4224dd12..e8be47d6d7d0 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -553,7 +553,7 @@ static int bcm5481_config_aneg(struct phy_device *phydev) /* * There is no BCM5481 specification available, so down * here is everything we know about "register 0x18". This - * at least helps BCM5481 to successfuly receive packets + * at least helps BCM5481 to successfully receive packets * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com> * says: "This sets delay between the RXD and RXC signals * instead of using trace lengths to achieve timing". diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c index 01c88a71abe1..e8c039879b05 100644 --- a/drivers/net/wireless/ipw2x00/libipw_tx.c +++ b/drivers/net/wireless/ipw2x00/libipw_tx.c @@ -395,7 +395,7 @@ netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) bytes_per_frag -= LIBIPW_FCS_LEN; - /* Each fragment may need to have room for encryptiong + /* Each fragment may need to have room for encryption * pre/postfix */ if (host_encrypt) bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/libertas_tf/deb_defs.h index ae753962d8b5..4bd3dc5adf7c 100644 --- a/drivers/net/wireless/libertas_tf/deb_defs.h +++ b/drivers/net/wireless/libertas_tf/deb_defs.h @@ -3,7 +3,7 @@ * global variable declaration. */ #ifndef _LBS_DEB_DEFS_H_ -#define _LBS_DEB_EFS_H_ +#define _LBS_DEB_DEFS_H_ #ifndef DRV_NAME #define DRV_NAME "libertas_tf" diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 5380f3b040ac..177a8e669241 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -475,7 +475,7 @@ static void _rtl_pci_tx_chk_waitq(struct ieee80211_hw *hw) struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct sk_buff *skb = NULL; struct ieee80211_tx_info *info = NULL; - int tid; /* should be int */ + int tid; if (!rtlpriv->rtlhal.earlymode_enable) return; @@ -1525,7 +1525,7 @@ static int rtl_pci_start(struct ieee80211_hw *hw) rtl_init_rx_config(hw); - /*should after adapter start and interrupt enable. */ + /*should be after adapter start and interrupt enable. */ set_hal_start(rtlhal); RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); @@ -1546,7 +1546,7 @@ static void rtl_pci_stop(struct ieee80211_hw *hw) u8 RFInProgressTimeOut = 0; /* - *should before disable interrrupt&adapter + *should be before disable interrupt&adapter *and will do it immediately. */ set_hal_stop(rtlhal); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 57a6d19eba4c..a6f762188bc3 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -668,7 +668,7 @@ sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) * @dev: instance of PCI owned by the driver that's asking * @mask: number of address bits this PCI device can handle * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static int sba_dma_supported( struct device *dev, u64 mask) { @@ -680,7 +680,7 @@ static int sba_dma_supported( struct device *dev, u64 mask) return(0); } - /* Documentation/PCI/PCI-DMA-mapping.txt tells drivers to try 64-bit + /* Documentation/DMA-API-HOWTO.txt tells drivers to try 64-bit * first, then fall back to 32-bit if that fails. * We are just "encouraging" 32-bit DMA masks here since we can * never allow IOMMU bypass unless we add special support for ZX1. @@ -706,7 +706,7 @@ static int sba_dma_supported( struct device *dev, u64 mask) * @size: number of bytes to map in driver buffer. * @direction: R/W or both. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static dma_addr_t sba_map_single(struct device *dev, void *addr, size_t size, @@ -785,7 +785,7 @@ sba_map_single(struct device *dev, void *addr, size_t size, * @size: number of bytes mapped in driver buffer. * @direction: R/W or both. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static void sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, @@ -861,7 +861,7 @@ sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, * @size: number of bytes mapped in driver buffer. * @dma_handle: IOVA of new buffer. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static void *sba_alloc_consistent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) @@ -892,7 +892,7 @@ static void *sba_alloc_consistent(struct device *hwdev, size_t size, * @vaddr: virtual address IOVA of "consistent" buffer. * @dma_handler: IO virtual address of "consistent" buffer. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static void sba_free_consistent(struct device *hwdev, size_t size, void *vaddr, @@ -927,7 +927,7 @@ int dump_run_sg = 0; * @nents: number of entries in list * @direction: R/W or both. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static int sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, @@ -1011,7 +1011,7 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, * @nents: number of entries in list * @direction: R/W or both. * - * See Documentation/PCI/PCI-DMA-mapping.txt + * See Documentation/DMA-API-HOWTO.txt */ static void sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 6fa215a38615..90832a955991 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -400,9 +400,8 @@ static int pcifront_claim_resource(struct pci_dev *dev, void *data) dev_info(&pdev->xdev->dev, "claiming resource %s/%d\n", pci_name(dev), i); if (pci_claim_resource(dev, i)) { - dev_err(&pdev->xdev->dev, "Could not claim " - "resource %s/%d! Device offline. Try " - "giving less than 4GB to domain.\n", + dev_err(&pdev->xdev->dev, "Could not claim resource %s/%d! " + "Device offline. Try using e820_host=1 in the guest config.\n", pci_name(dev), i); } } diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c index c998f7aaadbc..0fac9658b020 100644 --- a/drivers/pcmcia/sa1100_simpad.c +++ b/drivers/pcmcia/sa1100_simpad.c @@ -15,10 +15,6 @@ #include <mach/simpad.h> #include "sa1100_generic.h" -extern long get_cs3_shadow(void); -extern void set_cs3_bit(int value); -extern void clear_cs3_bit(int value); - static struct pcmcia_irqs irqs[] = { { 1, IRQ_GPIO_CF_CD, "CF_CD" }, }; @@ -26,7 +22,7 @@ static struct pcmcia_irqs irqs[] = { static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { - clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); + simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); skt->socket.pci_irq = IRQ_GPIO_CF_IRQ; @@ -38,8 +34,8 @@ static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); /* Disable CF bus: */ - //set_cs3_bit(PCMCIA_BUFF_DIS); - clear_cs3_bit(PCMCIA_RESET); + /*simpad_set_cs3_bit(PCMCIA_BUFF_DIS);*/ + simpad_clear_cs3_bit(PCMCIA_RESET); } static void @@ -47,15 +43,16 @@ simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) { unsigned long levels = GPLR; - long cs3reg = get_cs3_shadow(); + long cs3reg = simpad_get_cs3_ro(); state->detect=((levels & GPIO_CF_CD)==0)?1:0; state->ready=(levels & GPIO_CF_IRQ)?1:0; - state->bvd1=1; /* Not available on Simpad. */ - state->bvd2=1; /* Not available on Simpad. */ + state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */ + state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */ state->wrprot=0; /* Not available on Simpad. */ - - if((cs3reg & 0x0c) == 0x0c) { + + if ((cs3reg & (PCMCIA_VS1|PCMCIA_VS2)) == + (PCMCIA_VS1|PCMCIA_VS2)) { state->vs_3v=0; state->vs_Xv=0; } else { @@ -75,23 +72,23 @@ simpad_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, /* Murphy: see table of MIC2562a-1 */ switch (state->Vcc) { case 0: - clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); + simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); break; case 33: - clear_cs3_bit(VCC_3V_EN|EN1); - set_cs3_bit(VCC_5V_EN|EN0); + simpad_clear_cs3_bit(VCC_3V_EN|EN1); + simpad_set_cs3_bit(VCC_5V_EN|EN0); break; case 50: - clear_cs3_bit(VCC_5V_EN|EN1); - set_cs3_bit(VCC_3V_EN|EN0); + simpad_clear_cs3_bit(VCC_5V_EN|EN1); + simpad_set_cs3_bit(VCC_3V_EN|EN0); break; default: printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __func__, state->Vcc); - clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); + simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); local_irq_restore(flags); return -1; } @@ -110,7 +107,7 @@ static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt) static void simpad_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) { soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); - set_cs3_bit(PCMCIA_RESET); + simpad_set_cs3_bit(PCMCIA_RESET); } static struct pcmcia_low_level simpad_pcmcia_ops = { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1e88d4785321..10cf2500522b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -31,9 +31,6 @@ config ACER_WMI wireless radio and bluetooth control, and on some laptops, exposes the mail LED and LCD backlight. - For more information about this driver see - <file:Documentation/laptops/acer-wmi.txt> - If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M here. @@ -164,7 +161,7 @@ config HP_ACCEL Support for a led indicating disk protection will be provided as hp::hddprotect. For more information on the feature, refer to - Documentation/hwmon/lis3lv02d. + Documentation/misc-devices/lis3lv02d. To compile this driver as a module, choose M here: the module will be called hp_accel. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7bd829f247eb..7b828680b21d 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -4007,7 +4007,7 @@ static void bluetooth_shutdown(void) pr_notice("failed to save bluetooth state to NVRAM\n"); else vdbg_printk(TPACPI_DBG_RFKILL, - "bluestooth state saved to NVRAM\n"); + "bluetooth state saved to NVRAM\n"); } static void bluetooth_exit(void) diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 76058a5166ed..08c66035dd19 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -335,10 +335,9 @@ cio_ignore_write(struct file *file, const char __user *user_buf, return -EINVAL; if (user_len > 65536) user_len = 65536; - buf = vmalloc (user_len + 1); /* maybe better use the stack? */ + buf = vzalloc(user_len + 1); /* maybe better use the stack? */ if (buf == NULL) return -ENOMEM; - memset(buf, 0, user_len + 1); if (strncpy_from_user (buf, user_buf, user_len) < 0) { rc = -EFAULT; diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c index c20494660603..957595a7a45c 100644 --- a/drivers/scsi/aacraid/src.c +++ b/drivers/scsi/aacraid/src.c @@ -37,7 +37,6 @@ #include <linux/slab.h> #include <linux/blkdev.h> #include <linux/delay.h> -#include <linux/version.h> #include <linux/completion.h> #include <linux/time.h> #include <linux/interrupt.h> diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx index 5e6620f8dabc..6739069477de 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic79xx +++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx @@ -31,8 +31,7 @@ config AIC79XX_CMDS_PER_DEVICE on some devices. The upper bound is 253. 0 disables tagged queueing. Per device tag depth can be controlled via the kernel command line - "tag_info" option. See drivers/scsi/aic7xxx/README.aic79xx - for details. + "tag_info" option. See Documentation/scsi/aic79xx.txt for details. config AIC79XX_RESET_DELAY_MS int "Initial bus reset delay in milli-seconds" diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx index 88da670a7915..55ac55ee6068 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic7xxx +++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx @@ -36,8 +36,7 @@ config AIC7XXX_CMDS_PER_DEVICE on some devices. The upper bound is 253. 0 disables tagged queueing. Per device tag depth can be controlled via the kernel command line - "tag_info" option. See drivers/scsi/aic7xxx/README.aic7xxx - for details. + "tag_info" option. See Documentation/scsi/aic7xxx.txt for details. config AIC7XXX_RESET_DELAY_MS int "Initial bus reset delay in milli-seconds" diff --git a/drivers/scsi/aic94xx/aic94xx_dump.c b/drivers/scsi/aic94xx/aic94xx_dump.c index 67eeba3bdb06..a16a77c8b9cf 100644 --- a/drivers/scsi/aic94xx/aic94xx_dump.c +++ b/drivers/scsi/aic94xx/aic94xx_dump.c @@ -29,7 +29,7 @@ * */ -#include "linux/pci.h" +#include <linux/pci.h> #include "aic94xx.h" #include "aic94xx_reg.h" #include "aic94xx_reg_def.h" diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h index bda999ad9f52..5e19a5f820ec 100644 --- a/drivers/scsi/bfa/bfad_drv.h +++ b/drivers/scsi/bfa/bfad_drv.h @@ -27,7 +27,6 @@ #define __BFAD_DRV_H__ #include <linux/types.h> -#include <linux/version.h> #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/idr.h> diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index f58644850333..1242c7c04a01 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -14,7 +14,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ -#include <linux/version.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <scsi/scsi_host.h> diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index ae13c4993aa3..31c79bde6976 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -13,7 +13,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ -#include <linux/version.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <scsi/scsi_host.h> diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9d3d81778af1..a1c0ddd53aa9 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -18,7 +18,6 @@ */ #include <linux/module.h> -#include <linux/version.h> #include <linux/spinlock.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 430fc8ff014a..09e61134037f 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -708,7 +708,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) __func__, event_code); - return SCI_FAILURE;; + return SCI_FAILURE; } return SCI_SUCCESS; case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 2e6619eff3ea..8883ca36f932 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -67,7 +67,7 @@ * * NEC MegaRAID PCI Express ROMB 1000 0408 1033 8287 * - * For history of changes, see Documentation/ChangeLog.megaraid + * For history of changes, see Documentation/scsi/ChangeLog.megaraid */ #include <linux/slab.h> diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 83035bd1c489..6825772cfd6a 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -42,7 +42,6 @@ * USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index 6861244249a3..2b1101076cfe 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -41,7 +41,6 @@ * USA. */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 38ed0260959d..246d5fbc6e5a 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -42,7 +42,6 @@ * USA. */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 6abd2fcc43e2..5202de3f3d3f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -41,7 +41,6 @@ * USA. */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 44d7885a4a1d..44b474513223 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -43,7 +43,6 @@ #include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/sas_ata.h> -#include <linux/version.h> #include "mv_defs.h" #define DRV_NAME "mvsas" diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index d079f9a3c6b3..b86db84d6f32 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -39,7 +39,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/hdreg.h> -#include <linux/version.h> #include <linux/io.h> #include <linux/slab.h> #include <asm/irq.h> diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index f920baf3ff24..ca496c7474e3 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -24,7 +24,6 @@ #ifndef _PMCRAID_H #define _PMCRAID_H -#include <linux/version.h> #include <linux/types.h> #include <linux/completion.h> #include <linux/list.h> diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 909ed9ed24c0..441a1c5b8974 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -50,6 +50,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #include <linux/delay.h> #include <linux/blktrace_api.h> #include <linux/mutex.h> +#include <linux/ratelimit.h> #include "scsi.h" #include <scsi/scsi_dbg.h> @@ -626,14 +627,15 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) */ if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) { static char cmd[TASK_COMM_LEN]; - if (strcmp(current->comm, cmd) && printk_ratelimit()) { - printk(KERN_WARNING - "sg_write: data in/out %d/%d bytes for SCSI command 0x%x--" - "guessing data in;\n " - "program %s not setting count and/or reply_len properly\n", - old_hdr.reply_len - (int)SZ_SG_HEADER, - input_size, (unsigned int) cmnd[0], - current->comm); + if (strcmp(current->comm, cmd)) { + printk_ratelimited(KERN_WARNING + "sg_write: data in/out %d/%d bytes " + "for SCSI command 0x%x-- guessing " + "data in;\n program %s not setting " + "count and/or reply_len properly\n", + old_hdr.reply_len - (int)SZ_SG_HEADER, + input_size, (unsigned int) cmnd[0], + current->comm); strcpy(cmd, current->comm); } } diff --git a/drivers/staging/cxt1e1/Kconfig b/drivers/staging/cxt1e1/Kconfig index 73430ef6ae2b..947f42a65c59 100644 --- a/drivers/staging/cxt1e1/Kconfig +++ b/drivers/staging/cxt1e1/Kconfig @@ -6,8 +6,7 @@ config CXT1E1 channelized stream WAN adapter card which contains a HDLC/Transparent mode controller. - If you want to compile this driver as a module - say M here and read <file:Documentation/modules.txt>. + If you want to compile this driver as a module say M here. The module will be called 'cxt1e1'. If unsure, say N. diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c index 52d1ea349635..1c86cf11ab94 100644 --- a/drivers/staging/iio/addac/adt7316-i2c.c +++ b/drivers/staging/iio/addac/adt7316-i2c.c @@ -109,7 +109,7 @@ static int __devinit adt7316_i2c_probe(struct i2c_client *client, static int __devexit adt7316_i2c_remove(struct i2c_client *client) { - return adt7316_remove(&client->dev);; + return adt7316_remove(&client->dev); } static const struct i2c_device_id adt7316_i2c_id[] = { diff --git a/drivers/staging/iio/dds/ad9832.c b/drivers/staging/iio/dds/ad9832.c index e3e61a469bb8..6f0efe6580e7 100644 --- a/drivers/staging/iio/dds/ad9832.c +++ b/drivers/staging/iio/dds/ad9832.c @@ -52,7 +52,7 @@ static int ad9832_write_frequency(struct ad9832_state *st, ((addr - 3) << ADD_SHIFT) | ((regval >> 0) & 0xFF)); - return spi_sync(st->spi, &st->freq_msg);; + return spi_sync(st->spi, &st->freq_msg); } static int ad9832_write_phase(struct ad9832_state *st, diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 1060c7b7f803..62e54053bcd8 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -6,7 +6,6 @@ target_core_mod-y := target_core_configfs.o \ target_core_hba.o \ target_core_pr.o \ target_core_alua.o \ - target_core_scdb.o \ target_core_tmr.o \ target_core_tpg.o \ target_core_transport.o \ diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 6a4ea29c2f36..4d01768fcd90 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -765,7 +765,7 @@ static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) u32 iov_count = (cmd->se_cmd.t_data_nents == 0) ? 1 : cmd->se_cmd.t_data_nents; - iov_count += TRANSPORT_IOV_DATA_BUFFER; + iov_count += ISCSI_IOV_DATA_BUFFER; cmd->iov_data = kzalloc(iov_count * sizeof(struct kvec), GFP_KERNEL); if (!cmd->iov_data) { @@ -3538,16 +3538,8 @@ get_immediate: spin_lock_bh(&conn->cmd_lock); list_del(&cmd->i_list); spin_unlock_bh(&conn->cmd_lock); - /* - * Determine if a struct se_cmd is assoicated with - * this struct iscsi_cmd. - */ - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) && - !(cmd->tmr_req)) - iscsit_release_cmd(cmd); - else - transport_generic_free_cmd(&cmd->se_cmd, - 1, 0); + + iscsit_free_cmd(cmd); goto get_immediate; case ISTATE_SEND_NOPIN_WANT_RESPONSE: spin_unlock_bh(&cmd->istate_lock); @@ -3940,7 +3932,6 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) { struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL; struct iscsi_session *sess = conn->sess; - struct se_cmd *se_cmd; /* * We expect this function to only ever be called from either RX or TX * thread context via iscsit_close_connection() once the other context @@ -3948,35 +3939,13 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) */ spin_lock_bh(&conn->cmd_lock); list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) { - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD)) { - list_del(&cmd->i_list); - spin_unlock_bh(&conn->cmd_lock); - iscsit_increment_maxcmdsn(cmd, sess); - se_cmd = &cmd->se_cmd; - /* - * Special cases for active iSCSI TMR, and - * transport_lookup_cmd_lun() failing from - * iscsit_get_lun_for_cmd() in iscsit_handle_scsi_cmd(). - */ - if (cmd->tmr_req && se_cmd->transport_wait_for_tasks) - se_cmd->transport_wait_for_tasks(se_cmd, 1, 1); - else if (cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) - transport_release_cmd(se_cmd); - else - iscsit_release_cmd(cmd); - - spin_lock_bh(&conn->cmd_lock); - continue; - } list_del(&cmd->i_list); spin_unlock_bh(&conn->cmd_lock); iscsit_increment_maxcmdsn(cmd, sess); - se_cmd = &cmd->se_cmd; - if (se_cmd->transport_wait_for_tasks) - se_cmd->transport_wait_for_tasks(se_cmd, 1, 1); + iscsit_free_cmd(cmd); spin_lock_bh(&conn->cmd_lock); } diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 11fd74307811..beb39469e7f1 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -18,6 +18,7 @@ * GNU General Public License for more details. ******************************************************************************/ +#include <linux/kernel.h> #include <linux/string.h> #include <linux/crypto.h> #include <linux/err.h> @@ -27,40 +28,11 @@ #include "iscsi_target_nego.h" #include "iscsi_target_auth.h" -static unsigned char chap_asciihex_to_binaryhex(unsigned char val[2]) -{ - unsigned char result = 0; - /* - * MSB - */ - if ((val[0] >= 'a') && (val[0] <= 'f')) - result = ((val[0] - 'a' + 10) & 0xf) << 4; - else - if ((val[0] >= 'A') && (val[0] <= 'F')) - result = ((val[0] - 'A' + 10) & 0xf) << 4; - else /* digit */ - result = ((val[0] - '0') & 0xf) << 4; - /* - * LSB - */ - if ((val[1] >= 'a') && (val[1] <= 'f')) - result |= ((val[1] - 'a' + 10) & 0xf); - else - if ((val[1] >= 'A') && (val[1] <= 'F')) - result |= ((val[1] - 'A' + 10) & 0xf); - else /* digit */ - result |= ((val[1] - '0') & 0xf); - - return result; -} - static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len) { - int i, j = 0; + int j = DIV_ROUND_UP(len, 2); - for (i = 0; i < len; i += 2) { - dst[j++] = (unsigned char) chap_asciihex_to_binaryhex(&src[i]); - } + hex2bin(dst, src, j); dst[j] = '\0'; return j; diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 470ed551eeb5..3723d90d5ae5 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -57,6 +57,9 @@ #define TA_PROD_MODE_WRITE_PROTECT 0 #define TA_CACHE_CORE_NPS 0 + +#define ISCSI_IOV_DATA_BUFFER 5 + enum tpg_np_network_transport_table { ISCSI_TCP = 0, ISCSI_SCTP_TCP = 1, @@ -425,7 +428,6 @@ struct iscsi_cmd { /* Number of times struct iscsi_cmd is present in immediate queue */ atomic_t immed_queue_count; atomic_t response_queue_count; - atomic_t transport_sent; spinlock_t datain_lock; spinlock_t dataout_timeout_lock; /* spinlock for protecting struct iscsi_cmd->i_state */ diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c index 91a4d170bda4..0b8404c30125 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.c +++ b/drivers/target/iscsi/iscsi_target_erl2.c @@ -143,12 +143,7 @@ void iscsit_free_connection_recovery_entires(struct iscsi_session *sess) list_del(&cmd->i_list); cmd->conn = NULL; spin_unlock(&cr->conn_recovery_cmd_lock); - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 1); + iscsit_free_cmd(cmd); spin_lock(&cr->conn_recovery_cmd_lock); } spin_unlock(&cr->conn_recovery_cmd_lock); @@ -170,12 +165,7 @@ void iscsit_free_connection_recovery_entires(struct iscsi_session *sess) list_del(&cmd->i_list); cmd->conn = NULL; spin_unlock(&cr->conn_recovery_cmd_lock); - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 1); + iscsit_free_cmd(cmd); spin_lock(&cr->conn_recovery_cmd_lock); } spin_unlock(&cr->conn_recovery_cmd_lock); @@ -260,12 +250,7 @@ void iscsit_discard_cr_cmds_by_expstatsn( iscsit_remove_cmd_from_connection_recovery(cmd, sess); spin_unlock(&cr->conn_recovery_cmd_lock); - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 0); + iscsit_free_cmd(cmd); spin_lock(&cr->conn_recovery_cmd_lock); } spin_unlock(&cr->conn_recovery_cmd_lock); @@ -319,12 +304,7 @@ int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn) list_del(&cmd->i_list); spin_unlock_bh(&conn->cmd_lock); - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 1); + iscsit_free_cmd(cmd); spin_lock_bh(&conn->cmd_lock); } spin_unlock_bh(&conn->cmd_lock); @@ -377,13 +357,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) list_del(&cmd->i_list); spin_unlock_bh(&conn->cmd_lock); - - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 0); + iscsit_free_cmd(cmd); spin_lock_bh(&conn->cmd_lock); continue; } @@ -403,13 +377,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) (cmd->cmd_sn >= conn->sess->exp_cmd_sn)) { list_del(&cmd->i_list); spin_unlock_bh(&conn->cmd_lock); - - if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || - !(cmd->se_cmd.transport_wait_for_tasks)) - iscsit_release_cmd(cmd); - else - cmd->se_cmd.transport_wait_for_tasks( - &cmd->se_cmd, 1, 1); + iscsit_free_cmd(cmd); spin_lock_bh(&conn->cmd_lock); continue; } @@ -434,10 +402,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) iscsit_free_all_datain_reqs(cmd); - if ((cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) && - cmd->se_cmd.transport_wait_for_tasks) - cmd->se_cmd.transport_wait_for_tasks(&cmd->se_cmd, - 0, 0); + transport_wait_for_tasks(&cmd->se_cmd); /* * Add the struct iscsi_cmd to the connection recovery cmd list */ diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 4d087ac11067..426cd4bf6a9a 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -504,7 +504,7 @@ static int iscsi_target_do_authentication( break; case 1: pr_debug("iSCSI security negotiation" - " completed sucessfully.\n"); + " completed successfully.\n"); login->auth_complete = 1; if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c index db1fe1ec84df..490207eacde9 100644 --- a/drivers/target/iscsi/iscsi_target_tmr.c +++ b/drivers/target/iscsi/iscsi_target_tmr.c @@ -250,7 +250,7 @@ static int iscsit_task_reassign_complete_write( * so if we have received all DataOUT we can safety ignore Initiator. */ if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { - if (!atomic_read(&cmd->transport_sent)) { + if (!atomic_read(&cmd->se_cmd.t_transport_sent)) { pr_debug("WRITE ITT: 0x%08x: t_state: %d" " never sent to transport\n", cmd->init_task_tag, cmd->se_cmd.t_state); @@ -314,11 +314,11 @@ static int iscsit_task_reassign_complete_read( cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); } - if (!atomic_read(&cmd->transport_sent)) { + if (!atomic_read(&cmd->se_cmd.t_transport_sent)) { pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" " transport\n", cmd->init_task_tag, cmd->se_cmd.t_state); - transport_generic_handle_cdb(se_cmd); + transport_handle_cdb_direct(se_cmd); return 0; } diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index f00137f377b2..02348f727bd4 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -289,7 +289,8 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr( } se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, - (void *)cmd->tmr_req, tcm_function); + (void *)cmd->tmr_req, tcm_function, + GFP_KERNEL); if (!se_cmd->se_tmr_req) goto out; @@ -839,6 +840,23 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd) kmem_cache_free(lio_cmd_cache, cmd); } +void iscsit_free_cmd(struct iscsi_cmd *cmd) +{ + /* + * Determine if a struct se_cmd is assoicated with + * this struct iscsi_cmd. + */ + switch (cmd->iscsi_opcode) { + case ISCSI_OP_SCSI_CMD: + case ISCSI_OP_SCSI_TMFUNC: + transport_generic_free_cmd(&cmd->se_cmd, 1); + break; + default: + iscsit_release_cmd(cmd); + break; + } +} + int iscsit_check_session_usage_count(struct iscsi_session *sess) { spin_lock_bh(&sess->session_usage_lock); diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h index 2cd49d607bda..835bf7de0281 100644 --- a/drivers/target/iscsi/iscsi_target_util.h +++ b/drivers/target/iscsi/iscsi_target_util.h @@ -30,6 +30,7 @@ extern struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_c extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_conn *); extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *); extern void iscsit_release_cmd(struct iscsi_cmd *); +extern void iscsit_free_cmd(struct iscsi_cmd *); extern int iscsit_check_session_usage_count(struct iscsi_session *); extern void iscsit_dec_session_usage_count(struct iscsi_session *); extern void iscsit_inc_session_usage_count(struct iscsi_session *); diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index aa2d67997235..b15d8cbf630b 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -200,7 +200,7 @@ static void tcm_loop_check_stop_free(struct se_cmd *se_cmd) * Release the struct se_cmd, which will make a callback to release * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() */ - transport_generic_free_cmd(se_cmd, 0, 0); + transport_generic_free_cmd(se_cmd, 0); } static void tcm_loop_release_cmd(struct se_cmd *se_cmd) @@ -290,6 +290,15 @@ static int tcm_loop_queuecommand( */ tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; + /* + * Ensure that this tl_tpg reference from the incoming sc->device->id + * has already been configured via tcm_loop_make_naa_tpg(). + */ + if (!tl_tpg->tl_hba) { + set_host_byte(sc, DID_NO_CONNECT); + sc->scsi_done(sc); + return 0; + } se_tpg = &tl_tpg->tl_se_tpg; /* * Determine the SAM Task Attribute and allocate tl_cmd and @@ -366,7 +375,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) * Allocate the LUN_RESET TMR */ se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, tl_tmr, - TMR_LUN_RESET); + TMR_LUN_RESET, GFP_KERNEL); if (IS_ERR(se_cmd->se_tmr_req)) goto release; /* @@ -388,7 +397,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc) SUCCESS : FAILED; release: if (se_cmd) - transport_generic_free_cmd(se_cmd, 1, 0); + transport_generic_free_cmd(se_cmd, 1); else kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); kfree(tl_tmr); @@ -1245,6 +1254,9 @@ void tcm_loop_drop_naa_tpg( */ core_tpg_deregister(se_tpg); + tl_tpg->tl_hba = NULL; + tl_tpg->tl_tpgt = 0; + pr_debug("TCM_Loop_ConfigFS: Deallocated Emulated %s" " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), config_item_name(&wwn->wwn_group.cg_item), tpgt); diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 98c98a3a0250..8f4447749c71 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -24,7 +24,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/configfs.h> @@ -68,6 +67,15 @@ int core_emulate_report_target_port_groups(struct se_cmd *cmd) unsigned char *buf; u32 rd_len = 0, off = 4; /* Skip over RESERVED area to first Target port group descriptor */ + /* + * Need at least 4 bytes of response data or else we can't + * even fit the return data length. + */ + if (cmd->data_length < 4) { + pr_warn("REPORT TARGET PORT GROUPS allocation length %u" + " too small\n", cmd->data_length); + return -EINVAL; + } buf = transport_kmap_first_data_page(cmd); @@ -75,6 +83,17 @@ int core_emulate_report_target_port_groups(struct se_cmd *cmd) list_for_each_entry(tg_pt_gp, &su_dev->t10_alua.tg_pt_gps_list, tg_pt_gp_list) { /* + * Check if the Target port group and Target port descriptor list + * based on tg_pt_gp_members count will fit into the response payload. + * Otherwise, bump rd_len to let the initiator know we have exceeded + * the allocation length and the response is truncated. + */ + if ((off + 8 + (tg_pt_gp->tg_pt_gp_members * 4)) > + cmd->data_length) { + rd_len += 8 + (tg_pt_gp->tg_pt_gp_members * 4); + continue; + } + /* * PREF: Preferred target port bit, determine if this * bit should be set for port group. */ diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index f04d4ef99dca..38535eb13929 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -24,7 +24,7 @@ */ #include <linux/kernel.h> -#include <linux/ctype.h> +#include <linux/module.h> #include <asm/unaligned.h> #include <scsi/scsi.h> @@ -156,11 +156,12 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf) } static void -target_parse_naa_6h_vendor_specific(struct se_device *dev, unsigned char *buf_off) +target_parse_naa_6h_vendor_specific(struct se_device *dev, unsigned char *buf) { unsigned char *p = &dev->se_sub_dev->t10_wwn.unit_serial[0]; - unsigned char *buf = buf_off; - int cnt = 0, next = 1; + int cnt; + bool next = true; + /* * Generate up to 36 bits of VENDOR SPECIFIC IDENTIFIER starting on * byte 3 bit 3-0 for NAA IEEE Registered Extended DESIGNATOR field @@ -169,19 +170,18 @@ target_parse_naa_6h_vendor_specific(struct se_device *dev, unsigned char *buf_of * NUMBER set via vpd_unit_serial in target_core_configfs.c to ensure * per device uniqeness. */ - while (*p != '\0') { - if (cnt >= 13) - break; - if (!isxdigit(*p)) { - p++; + for (cnt = 0; *p && cnt < 13; p++) { + int val = hex_to_bin(*p); + + if (val < 0) continue; - } - if (next != 0) { - buf[cnt++] |= hex_to_bin(*p++); - next = 0; + + if (next) { + next = false; + buf[cnt++] |= val; } else { - buf[cnt] = hex_to_bin(*p++) << 4; - next = 1; + next = true; + buf[cnt] = val << 4; } } } @@ -1266,3 +1266,52 @@ transport_emulate_control_cdb(struct se_task *task) return PYX_TRANSPORT_SENT_TO_TRANSPORT; } + +/* + * Write a CDB into @cdb that is based on the one the intiator sent us, + * but updated to only cover the sectors that the current task handles. + */ +void target_get_task_cdb(struct se_task *task, unsigned char *cdb) +{ + struct se_cmd *cmd = task->task_se_cmd; + unsigned int cdb_len = scsi_command_size(cmd->t_task_cdb); + + memcpy(cdb, cmd->t_task_cdb, cdb_len); + if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { + unsigned long long lba = task->task_lba; + u32 sectors = task->task_sectors; + + switch (cdb_len) { + case 6: + /* 21-bit LBA and 8-bit sectors */ + cdb[1] = (lba >> 16) & 0x1f; + cdb[2] = (lba >> 8) & 0xff; + cdb[3] = lba & 0xff; + cdb[4] = sectors & 0xff; + break; + case 10: + /* 32-bit LBA and 16-bit sectors */ + put_unaligned_be32(lba, &cdb[2]); + put_unaligned_be16(sectors, &cdb[7]); + break; + case 12: + /* 32-bit LBA and 32-bit sectors */ + put_unaligned_be32(lba, &cdb[2]); + put_unaligned_be32(sectors, &cdb[6]); + break; + case 16: + /* 64-bit LBA and 32-bit sectors */ + put_unaligned_be64(lba, &cdb[2]); + put_unaligned_be32(sectors, &cdb[10]); + break; + case 32: + /* 64-bit LBA and 32-bit sectors, extended CDB */ + put_unaligned_be64(lba, &cdb[12]); + put_unaligned_be32(sectors, &cdb[28]); + break; + default: + BUG(); + } + } +} +EXPORT_SYMBOL(target_get_task_cdb); diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index b2575d8568cc..e0c1e8a8dd4e 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> @@ -133,14 +132,6 @@ static struct config_group *target_core_register_fabric( pr_debug("Target_Core_ConfigFS: REGISTER -> group: %p name:" " %s\n", group, name); /* - * Ensure that TCM subsystem plugins are loaded at this point for - * using the RAMDISK_DR virtual LUN 0 and all other struct se_port - * LUN symlinks. - */ - if (transport_subsystem_check_init() < 0) - return ERR_PTR(-EINVAL); - - /* * Below are some hardcoded request_module() calls to automatically * local fabric modules when the following is called: * @@ -725,9 +716,6 @@ SE_DEV_ATTR_RO(hw_queue_depth); DEF_DEV_ATTRIB(queue_depth); SE_DEV_ATTR(queue_depth, S_IRUGO | S_IWUSR); -DEF_DEV_ATTRIB(task_timeout); -SE_DEV_ATTR(task_timeout, S_IRUGO | S_IWUSR); - DEF_DEV_ATTRIB(max_unmap_lba_count); SE_DEV_ATTR(max_unmap_lba_count, S_IRUGO | S_IWUSR); @@ -761,7 +749,6 @@ static struct configfs_attribute *target_core_dev_attrib_attrs[] = { &target_core_dev_attrib_optimal_sectors.attr, &target_core_dev_attrib_hw_queue_depth.attr, &target_core_dev_attrib_queue_depth.attr, - &target_core_dev_attrib_task_timeout.attr, &target_core_dev_attrib_max_unmap_lba_count.attr, &target_core_dev_attrib_max_unmap_block_desc_count.attr, &target_core_dev_attrib_unmap_granularity.attr, @@ -3080,8 +3067,7 @@ static struct config_group *target_core_call_addhbatotarget( /* * Load up TCM subsystem plugins if they have not already been loaded. */ - if (transport_subsystem_check_init() < 0) - return ERR_PTR(-EINVAL); + transport_subsystem_check_init(); hba = core_alloc_hba(se_plugin_str, plugin_dep_id, 0); if (IS_ERR(hba)) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index ca6e4a4df134..f870c3bcfd82 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -914,21 +914,6 @@ void se_dev_set_default_attribs( dev->se_sub_dev->se_dev_attrib.queue_depth = dev_limits->queue_depth; } -int se_dev_set_task_timeout(struct se_device *dev, u32 task_timeout) -{ - if (task_timeout > DA_TASK_TIMEOUT_MAX) { - pr_err("dev[%p]: Passed task_timeout: %u larger then" - " DA_TASK_TIMEOUT_MAX\n", dev, task_timeout); - return -EINVAL; - } else { - dev->se_sub_dev->se_dev_attrib.task_timeout = task_timeout; - pr_debug("dev[%p]: Set SE Device task_timeout: %u\n", - dev, task_timeout); - } - - return 0; -} - int se_dev_set_max_unmap_lba_count( struct se_device *dev, u32 max_unmap_lba_count) @@ -972,36 +957,24 @@ int se_dev_set_unmap_granularity_alignment( int se_dev_set_emulate_dpo(struct se_device *dev, int flag) { - if ((flag != 0) && (flag != 1)) { + if (flag != 0 && flag != 1) { pr_err("Illegal value %d\n", flag); return -EINVAL; } - if (dev->transport->dpo_emulated == NULL) { - pr_err("dev->transport->dpo_emulated is NULL\n"); - return -EINVAL; - } - if (dev->transport->dpo_emulated(dev) == 0) { - pr_err("dev->transport->dpo_emulated not supported\n"); - return -EINVAL; - } - dev->se_sub_dev->se_dev_attrib.emulate_dpo = flag; - pr_debug("dev[%p]: SE Device Page Out (DPO) Emulation" - " bit: %d\n", dev, dev->se_sub_dev->se_dev_attrib.emulate_dpo); - return 0; + + pr_err("dpo_emulated not supported\n"); + return -EINVAL; } int se_dev_set_emulate_fua_write(struct se_device *dev, int flag) { - if ((flag != 0) && (flag != 1)) { + if (flag != 0 && flag != 1) { pr_err("Illegal value %d\n", flag); return -EINVAL; } - if (dev->transport->fua_write_emulated == NULL) { - pr_err("dev->transport->fua_write_emulated is NULL\n"); - return -EINVAL; - } - if (dev->transport->fua_write_emulated(dev) == 0) { - pr_err("dev->transport->fua_write_emulated not supported\n"); + + if (dev->transport->fua_write_emulated == 0) { + pr_err("fua_write_emulated not supported\n"); return -EINVAL; } dev->se_sub_dev->se_dev_attrib.emulate_fua_write = flag; @@ -1012,36 +985,23 @@ int se_dev_set_emulate_fua_write(struct se_device *dev, int flag) int se_dev_set_emulate_fua_read(struct se_device *dev, int flag) { - if ((flag != 0) && (flag != 1)) { + if (flag != 0 && flag != 1) { pr_err("Illegal value %d\n", flag); return -EINVAL; } - if (dev->transport->fua_read_emulated == NULL) { - pr_err("dev->transport->fua_read_emulated is NULL\n"); - return -EINVAL; - } - if (dev->transport->fua_read_emulated(dev) == 0) { - pr_err("dev->transport->fua_read_emulated not supported\n"); - return -EINVAL; - } - dev->se_sub_dev->se_dev_attrib.emulate_fua_read = flag; - pr_debug("dev[%p]: SE Device Forced Unit Access READs: %d\n", - dev, dev->se_sub_dev->se_dev_attrib.emulate_fua_read); - return 0; + + pr_err("ua read emulated not supported\n"); + return -EINVAL; } int se_dev_set_emulate_write_cache(struct se_device *dev, int flag) { - if ((flag != 0) && (flag != 1)) { + if (flag != 0 && flag != 1) { pr_err("Illegal value %d\n", flag); return -EINVAL; } - if (dev->transport->write_cache_emulated == NULL) { - pr_err("dev->transport->write_cache_emulated is NULL\n"); - return -EINVAL; - } - if (dev->transport->write_cache_emulated(dev) == 0) { - pr_err("dev->transport->write_cache_emulated not supported\n"); + if (dev->transport->write_cache_emulated == 0) { + pr_err("write_cache_emulated not supported\n"); return -EINVAL; } dev->se_sub_dev->se_dev_attrib.emulate_write_cache = flag; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 55bbe0847a6d..09b6f8729f91 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index c4ea3a9a555b..39f021b855ef 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -63,6 +63,7 @@ u32 sas_get_pr_transport_id( unsigned char *buf) { unsigned char *ptr; + int ret; /* * Set PROTOCOL IDENTIFIER to 6h for SAS @@ -74,7 +75,9 @@ u32 sas_get_pr_transport_id( */ ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */ - hex2bin(&buf[4], ptr, 8); + ret = hex2bin(&buf[4], ptr, 8); + if (ret < 0) + pr_debug("sas transport_id: invalid hex string\n"); /* * The SAS Transport ID is a hardcoded 24-byte length @@ -156,8 +159,9 @@ u32 fc_get_pr_transport_id( unsigned char *buf) { unsigned char *ptr; - int i; + int i, ret; u32 off = 8; + /* * PROTOCOL IDENTIFIER is 0h for FCP-2 * @@ -174,7 +178,9 @@ u32 fc_get_pr_transport_id( i++; continue; } - hex2bin(&buf[off++], &ptr[i], 1); + ret = hex2bin(&buf[off++], &ptr[i], 1); + if (ret < 0) + pr_debug("fc transport_id: invalid hex string\n"); i += 2; } /* diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index bc1b33639b8d..19a0be9c6570 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -26,7 +26,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/string.h> #include <linux/parser.h> #include <linux/timer.h> @@ -273,13 +272,14 @@ fd_alloc_task(unsigned char *cdb) static int fd_do_readv(struct se_task *task) { struct fd_request *req = FILE_REQ(task); - struct fd_dev *dev = req->fd_task.se_dev->dev_ptr; + struct se_device *se_dev = req->fd_task.task_se_cmd->se_dev; + struct fd_dev *dev = se_dev->dev_ptr; struct file *fd = dev->fd_file; struct scatterlist *sg = task->task_sg; struct iovec *iov; mm_segment_t old_fs; loff_t pos = (task->task_lba * - task->se_dev->se_sub_dev->se_dev_attrib.block_size); + se_dev->se_sub_dev->se_dev_attrib.block_size); int ret = 0, i; iov = kzalloc(sizeof(struct iovec) * task->task_sg_nents, GFP_KERNEL); @@ -325,13 +325,14 @@ static int fd_do_readv(struct se_task *task) static int fd_do_writev(struct se_task *task) { struct fd_request *req = FILE_REQ(task); - struct fd_dev *dev = req->fd_task.se_dev->dev_ptr; + struct se_device *se_dev = req->fd_task.task_se_cmd->se_dev; + struct fd_dev *dev = se_dev->dev_ptr; struct file *fd = dev->fd_file; struct scatterlist *sg = task->task_sg; struct iovec *iov; mm_segment_t old_fs; loff_t pos = (task->task_lba * - task->se_dev->se_sub_dev->se_dev_attrib.block_size); + se_dev->se_sub_dev->se_dev_attrib.block_size); int ret, i = 0; iov = kzalloc(sizeof(struct iovec) * task->task_sg_nents, GFP_KERNEL); @@ -399,33 +400,6 @@ static void fd_emulate_sync_cache(struct se_task *task) } /* - * Tell TCM Core that we are capable of WriteCache emulation for - * an underlying struct se_device. - */ -static int fd_emulated_write_cache(struct se_device *dev) -{ - return 1; -} - -static int fd_emulated_dpo(struct se_device *dev) -{ - return 0; -} -/* - * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs - * for TYPE_DISK. - */ -static int fd_emulated_fua_write(struct se_device *dev) -{ - return 1; -} - -static int fd_emulated_fua_read(struct se_device *dev) -{ - return 0; -} - -/* * WRITE Force Unit Access (FUA) emulation on a per struct se_task * LBA range basis.. */ @@ -608,17 +582,6 @@ static ssize_t fd_show_configfs_dev_params( return bl; } -/* fd_get_cdb(): (Part of se_subsystem_api_t template) - * - * - */ -static unsigned char *fd_get_cdb(struct se_task *task) -{ - struct fd_request *req = FILE_REQ(task); - - return req->fd_scsi_cdb; -} - /* fd_get_device_rev(): (Part of se_subsystem_api_t template) * * @@ -650,15 +613,13 @@ static struct se_subsystem_api fileio_template = { .name = "fileio", .owner = THIS_MODULE, .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV, + .write_cache_emulated = 1, + .fua_write_emulated = 1, .attach_hba = fd_attach_hba, .detach_hba = fd_detach_hba, .allocate_virtdevice = fd_allocate_virtdevice, .create_virtdevice = fd_create_virtdevice, .free_device = fd_free_device, - .dpo_emulated = fd_emulated_dpo, - .fua_write_emulated = fd_emulated_fua_write, - .fua_read_emulated = fd_emulated_fua_read, - .write_cache_emulated = fd_emulated_write_cache, .alloc_task = fd_alloc_task, .do_task = fd_do_task, .do_sync_cache = fd_emulate_sync_cache, @@ -666,7 +627,6 @@ static struct se_subsystem_api fileio_template = { .check_configfs_dev_params = fd_check_configfs_dev_params, .set_configfs_dev_params = fd_set_configfs_dev_params, .show_configfs_dev_params = fd_show_configfs_dev_params, - .get_cdb = fd_get_cdb, .get_device_rev = fd_get_device_rev, .get_device_type = fd_get_device_type, .get_blocks = fd_get_blocks, diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h index daebd710b893..59e6e73106c2 100644 --- a/drivers/target/target_core_file.h +++ b/drivers/target/target_core_file.h @@ -14,9 +14,7 @@ struct fd_request { struct se_task fd_task; - /* SCSI CDB from iSCSI Command PDU */ - unsigned char fd_scsi_cdb[TCM_MAX_COMMAND_SIZE]; -} ____cacheline_aligned; +}; #define FBDF_HAS_PATH 0x01 #define FBDF_HAS_SIZE 0x02 diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 7e1234105442..41ad02b5fb87 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -27,7 +27,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/string.h> #include <linux/parser.h> #include <linux/timer.h> @@ -314,104 +313,42 @@ static unsigned long long iblock_emulate_read_cap_with_block_size( return blocks_long; } +static void iblock_end_io_flush(struct bio *bio, int err) +{ + struct se_cmd *cmd = bio->bi_private; + + if (err) + pr_err("IBLOCK: cache flush failed: %d\n", err); + + if (cmd) + transport_complete_sync_cache(cmd, err == 0); + bio_put(bio); +} + /* - * Emulate SYCHRONIZE_CACHE_* + * Implement SYCHRONIZE CACHE. Note that we can't handle lba ranges and must + * always flush the whole cache. */ static void iblock_emulate_sync_cache(struct se_task *task) { struct se_cmd *cmd = task->task_se_cmd; struct iblock_dev *ib_dev = cmd->se_dev->dev_ptr; int immed = (cmd->t_task_cdb[1] & 0x2); - sector_t error_sector; - int ret; + struct bio *bio; /* * If the Immediate bit is set, queue up the GOOD response - * for this SYNCHRONIZE_CACHE op + * for this SYNCHRONIZE_CACHE op. */ if (immed) transport_complete_sync_cache(cmd, 1); - /* - * blkdev_issue_flush() does not support a specifying a range, so - * we have to flush the entire cache. - */ - ret = blkdev_issue_flush(ib_dev->ibd_bd, GFP_KERNEL, &error_sector); - if (ret != 0) { - pr_err("IBLOCK: block_issue_flush() failed: %d " - " error_sector: %llu\n", ret, - (unsigned long long)error_sector); - } - + bio = bio_alloc(GFP_KERNEL, 0); + bio->bi_end_io = iblock_end_io_flush; + bio->bi_bdev = ib_dev->ibd_bd; if (!immed) - transport_complete_sync_cache(cmd, ret == 0); -} - -/* - * Tell TCM Core that we are capable of WriteCache emulation for - * an underlying struct se_device. - */ -static int iblock_emulated_write_cache(struct se_device *dev) -{ - return 1; -} - -static int iblock_emulated_dpo(struct se_device *dev) -{ - return 0; -} - -/* - * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs - * for TYPE_DISK. - */ -static int iblock_emulated_fua_write(struct se_device *dev) -{ - return 1; -} - -static int iblock_emulated_fua_read(struct se_device *dev) -{ - return 0; -} - -static int iblock_do_task(struct se_task *task) -{ - struct se_device *dev = task->task_se_cmd->se_dev; - struct iblock_req *req = IBLOCK_REQ(task); - struct bio *bio = req->ib_bio, *nbio = NULL; - struct blk_plug plug; - int rw; - - if (task->task_data_direction == DMA_TO_DEVICE) { - /* - * Force data to disk if we pretend to not have a volatile - * write cache, or the initiator set the Force Unit Access bit. - */ - if (dev->se_sub_dev->se_dev_attrib.emulate_write_cache == 0 || - (dev->se_sub_dev->se_dev_attrib.emulate_fua_write > 0 && - task->task_se_cmd->t_tasks_fua)) - rw = WRITE_FUA; - else - rw = WRITE; - } else { - rw = READ; - } - - blk_start_plug(&plug); - while (bio) { - nbio = bio->bi_next; - bio->bi_next = NULL; - pr_debug("Calling submit_bio() task: %p bio: %p" - " bio->bi_sector: %llu\n", task, bio, - (unsigned long long)bio->bi_sector); - - submit_bio(rw, bio); - bio = nbio; - } - blk_finish_plug(&plug); - - return PYX_TRANSPORT_SENT_TO_TRANSPORT; + bio->bi_private = cmd; + submit_bio(WRITE_FLUSH, bio); } static int iblock_do_discard(struct se_device *dev, sector_t lba, u32 range) @@ -425,20 +362,7 @@ static int iblock_do_discard(struct se_device *dev, sector_t lba, u32 range) static void iblock_free_task(struct se_task *task) { - struct iblock_req *req = IBLOCK_REQ(task); - struct bio *bio, *hbio = req->ib_bio; - /* - * We only release the bio(s) here if iblock_bio_done() has not called - * bio_put() -> iblock_bio_destructor(). - */ - while (hbio != NULL) { - bio = hbio; - hbio = hbio->bi_next; - bio->bi_next = NULL; - bio_put(bio); - } - - kfree(req); + kfree(IBLOCK_REQ(task)); } enum { @@ -552,25 +476,21 @@ static ssize_t iblock_show_configfs_dev_params( static void iblock_bio_destructor(struct bio *bio) { struct se_task *task = bio->bi_private; - struct iblock_dev *ib_dev = task->se_dev->dev_ptr; + struct iblock_dev *ib_dev = task->task_se_cmd->se_dev->dev_ptr; bio_free(bio, ib_dev->ibd_bio_set); } -static struct bio *iblock_get_bio( - struct se_task *task, - struct iblock_req *ib_req, - struct iblock_dev *ib_dev, - int *ret, - sector_t lba, - u32 sg_num) +static struct bio * +iblock_get_bio(struct se_task *task, sector_t lba, u32 sg_num) { + struct iblock_dev *ib_dev = task->task_se_cmd->se_dev->dev_ptr; + struct iblock_req *ib_req = IBLOCK_REQ(task); struct bio *bio; bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set); if (!bio) { pr_err("Unable to allocate memory for bio\n"); - *ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; return NULL; } @@ -591,17 +511,33 @@ static struct bio *iblock_get_bio( return bio; } -static int iblock_map_data_SG(struct se_task *task) +static int iblock_do_task(struct se_task *task) { struct se_cmd *cmd = task->task_se_cmd; struct se_device *dev = cmd->se_dev; - struct iblock_dev *ib_dev = task->se_dev->dev_ptr; - struct iblock_req *ib_req = IBLOCK_REQ(task); - struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; + struct bio *bio; + struct bio_list list; struct scatterlist *sg; - int ret = 0; u32 i, sg_num = task->task_sg_nents; sector_t block_lba; + struct blk_plug plug; + int rw; + + if (task->task_data_direction == DMA_TO_DEVICE) { + /* + * Force data to disk if we pretend to not have a volatile + * write cache, or the initiator set the Force Unit Access bit. + */ + if (dev->se_sub_dev->se_dev_attrib.emulate_write_cache == 0 || + (dev->se_sub_dev->se_dev_attrib.emulate_fua_write > 0 && + task->task_se_cmd->t_tasks_fua)) + rw = WRITE_FUA; + else + rw = WRITE; + } else { + rw = READ; + } + /* * Do starting conversion up from non 512-byte blocksize with * struct se_task SCSI blocksize into Linux/Block 512 units for BIO. @@ -620,68 +556,43 @@ static int iblock_map_data_SG(struct se_task *task) return PYX_TRANSPORT_LU_COMM_FAILURE; } - bio = iblock_get_bio(task, ib_req, ib_dev, &ret, block_lba, sg_num); + bio = iblock_get_bio(task, block_lba, sg_num); if (!bio) - return ret; + return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; + + bio_list_init(&list); + bio_list_add(&list, bio); - ib_req->ib_bio = bio; - hbio = tbio = bio; - /* - * Use fs/bio.c:bio_add_pages() to setup the bio_vec maplist - * from task->task_sg -> struct scatterlist memory. - */ for_each_sg(task->task_sg, sg, task->task_sg_nents, i) { - pr_debug("task: %p bio: %p Calling bio_add_page(): page:" - " %p len: %u offset: %u\n", task, bio, sg_page(sg), - sg->length, sg->offset); -again: - ret = bio_add_page(bio, sg_page(sg), sg->length, sg->offset); - if (ret != sg->length) { - - pr_debug("*** Set bio->bi_sector: %llu\n", - (unsigned long long)bio->bi_sector); - pr_debug("** task->task_size: %u\n", - task->task_size); - pr_debug("*** bio->bi_max_vecs: %u\n", - bio->bi_max_vecs); - pr_debug("*** bio->bi_vcnt: %u\n", - bio->bi_vcnt); - - bio = iblock_get_bio(task, ib_req, ib_dev, &ret, - block_lba, sg_num); + /* + * XXX: if the length the device accepts is shorter than the + * length of the S/G list entry this will cause and + * endless loop. Better hope no driver uses huge pages. + */ + while (bio_add_page(bio, sg_page(sg), sg->length, sg->offset) + != sg->length) { + bio = iblock_get_bio(task, block_lba, sg_num); if (!bio) goto fail; - - tbio = tbio->bi_next = bio; - pr_debug("-----------------> Added +1 bio: %p to" - " list, Going to again\n", bio); - goto again; + bio_list_add(&list, bio); } + /* Always in 512 byte units for Linux/Block */ block_lba += sg->length >> IBLOCK_LBA_SHIFT; sg_num--; - pr_debug("task: %p bio-add_page() passed!, decremented" - " sg_num to %u\n", task, sg_num); - pr_debug("task: %p bio_add_page() passed!, increased lba" - " to %llu\n", task, (unsigned long long)block_lba); - pr_debug("task: %p bio_add_page() passed!, bio->bi_vcnt:" - " %u\n", task, bio->bi_vcnt); } - return 0; + blk_start_plug(&plug); + while ((bio = bio_list_pop(&list))) + submit_bio(rw, bio); + blk_finish_plug(&plug); + + return PYX_TRANSPORT_SENT_TO_TRANSPORT; + fail: - while (hbio) { - bio = hbio; - hbio = hbio->bi_next; - bio->bi_next = NULL; + while ((bio = bio_list_pop(&list))) bio_put(bio); - } - return ret; -} - -static unsigned char *iblock_get_cdb(struct se_task *task) -{ - return IBLOCK_REQ(task)->ib_scsi_cdb; + return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; } static u32 iblock_get_device_rev(struct se_device *dev) @@ -707,6 +618,7 @@ static void iblock_bio_done(struct bio *bio, int err) { struct se_task *task = bio->bi_private; struct iblock_req *ibr = IBLOCK_REQ(task); + /* * Set -EIO if !BIO_UPTODATE and the passed is still err=0 */ @@ -721,50 +633,31 @@ static void iblock_bio_done(struct bio *bio, int err) */ atomic_inc(&ibr->ib_bio_err_cnt); smp_mb__after_atomic_inc(); - bio_put(bio); - /* - * Wait to complete the task until the last bio as completed. - */ - if (!atomic_dec_and_test(&ibr->ib_bio_cnt)) - return; - - ibr->ib_bio = NULL; - transport_complete_task(task, 0); - return; } - pr_debug("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n", - task, bio, task->task_lba, (unsigned long long)bio->bi_sector, err); - /* - * bio_put() will call iblock_bio_destructor() to release the bio back - * to ibr->ib_bio_set. - */ + bio_put(bio); - /* - * Wait to complete the task until the last bio as completed. - */ + if (!atomic_dec_and_test(&ibr->ib_bio_cnt)) return; - /* - * Return GOOD status for task if zero ib_bio_err_cnt exists. - */ - ibr->ib_bio = NULL; - transport_complete_task(task, (!atomic_read(&ibr->ib_bio_err_cnt))); + + pr_debug("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n", + task, bio, task->task_lba, + (unsigned long long)bio->bi_sector, err); + + transport_complete_task(task, !atomic_read(&ibr->ib_bio_err_cnt)); } static struct se_subsystem_api iblock_template = { .name = "iblock", .owner = THIS_MODULE, .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV, - .map_data_SG = iblock_map_data_SG, + .write_cache_emulated = 1, + .fua_write_emulated = 1, .attach_hba = iblock_attach_hba, .detach_hba = iblock_detach_hba, .allocate_virtdevice = iblock_allocate_virtdevice, .create_virtdevice = iblock_create_virtdevice, .free_device = iblock_free_device, - .dpo_emulated = iblock_emulated_dpo, - .fua_write_emulated = iblock_emulated_fua_write, - .fua_read_emulated = iblock_emulated_fua_read, - .write_cache_emulated = iblock_emulated_write_cache, .alloc_task = iblock_alloc_task, .do_task = iblock_do_task, .do_discard = iblock_do_discard, @@ -773,7 +666,6 @@ static struct se_subsystem_api iblock_template = { .check_configfs_dev_params = iblock_check_configfs_dev_params, .set_configfs_dev_params = iblock_set_configfs_dev_params, .show_configfs_dev_params = iblock_show_configfs_dev_params, - .get_cdb = iblock_get_cdb, .get_device_rev = iblock_get_device_rev, .get_device_type = iblock_get_device_type, .get_blocks = iblock_get_blocks, diff --git a/drivers/target/target_core_iblock.h b/drivers/target/target_core_iblock.h index a121cd1b6575..5cf1860c10d0 100644 --- a/drivers/target/target_core_iblock.h +++ b/drivers/target/target_core_iblock.h @@ -8,10 +8,8 @@ struct iblock_req { struct se_task ib_task; - unsigned char ib_scsi_cdb[TCM_MAX_COMMAND_SIZE]; atomic_t ib_bio_cnt; atomic_t ib_bio_err_cnt; - struct bio *ib_bio; } ____cacheline_aligned; #define IBDF_HAS_UDEV_PATH 0x01 diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 7fd3a161f7cc..0c4f783f924c 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -25,7 +25,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/list.h> diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 2b7b0da9146d..dad671dee9e9 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -26,7 +26,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/string.h> #include <linux/parser.h> #include <linux/timer.h> @@ -567,7 +566,7 @@ static struct se_device *pscsi_create_virtdevice( if (IS_ERR(sh)) { pr_err("pSCSI: Unable to locate" " pdv_host_id: %d\n", pdv->pdv_host_id); - return (struct se_device *) sh; + return ERR_CAST(sh); } } } else { @@ -677,7 +676,7 @@ static inline struct pscsi_plugin_task *PSCSI_TASK(struct se_task *task) */ static int pscsi_transport_complete(struct se_task *task) { - struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr; + struct pscsi_dev_virt *pdv = task->task_se_cmd->se_dev->dev_ptr; struct scsi_device *sd = pdv->pdv_sd; int result; struct pscsi_plugin_task *pt = PSCSI_TASK(task); @@ -777,95 +776,6 @@ pscsi_alloc_task(unsigned char *cdb) return &pt->pscsi_task; } -static inline void pscsi_blk_init_request( - struct se_task *task, - struct pscsi_plugin_task *pt, - struct request *req, - int bidi_read) -{ - /* - * Defined as "scsi command" in include/linux/blkdev.h. - */ - req->cmd_type = REQ_TYPE_BLOCK_PC; - /* - * For the extra BIDI-COMMAND READ struct request we do not - * need to setup the remaining structure members - */ - if (bidi_read) - return; - /* - * Setup the done function pointer for struct request, - * also set the end_io_data pointer.to struct se_task. - */ - req->end_io = pscsi_req_done; - req->end_io_data = task; - /* - * Load the referenced struct se_task's SCSI CDB into - * include/linux/blkdev.h:struct request->cmd - */ - req->cmd_len = scsi_command_size(pt->pscsi_cdb); - req->cmd = &pt->pscsi_cdb[0]; - /* - * Setup pointer for outgoing sense data. - */ - req->sense = &pt->pscsi_sense[0]; - req->sense_len = 0; -} - -/* - * Used for pSCSI data payloads for all *NON* SCF_SCSI_DATA_SG_IO_CDB -*/ -static int pscsi_blk_get_request(struct se_task *task) -{ - struct pscsi_plugin_task *pt = PSCSI_TASK(task); - struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr; - - pt->pscsi_req = blk_get_request(pdv->pdv_sd->request_queue, - (task->task_data_direction == DMA_TO_DEVICE), - GFP_KERNEL); - if (!pt->pscsi_req || IS_ERR(pt->pscsi_req)) { - pr_err("PSCSI: blk_get_request() failed: %ld\n", - IS_ERR(pt->pscsi_req)); - return PYX_TRANSPORT_LU_COMM_FAILURE; - } - /* - * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC, - * and setup rq callback, CDB and sense. - */ - pscsi_blk_init_request(task, pt, pt->pscsi_req, 0); - return 0; -} - -/* pscsi_do_task(): (Part of se_subsystem_api_t template) - * - * - */ -static int pscsi_do_task(struct se_task *task) -{ - struct pscsi_plugin_task *pt = PSCSI_TASK(task); - struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr; - /* - * Set the struct request->timeout value based on peripheral - * device type from SCSI. - */ - if (pdv->pdv_sd->type == TYPE_DISK) - pt->pscsi_req->timeout = PS_TIMEOUT_DISK; - else - pt->pscsi_req->timeout = PS_TIMEOUT_OTHER; - - pt->pscsi_req->retries = PS_RETRY; - /* - * Queue the struct request into the struct scsi_device->request_queue. - * Also check for HEAD_OF_QUEUE SAM TASK attr from received se_cmd - * descriptor - */ - blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, pt->pscsi_req, - (task->task_se_cmd->sam_task_attr == MSG_HEAD_TAG), - pscsi_req_done); - - return PYX_TRANSPORT_SENT_TO_TRANSPORT; -} - static void pscsi_free_task(struct se_task *task) { struct pscsi_plugin_task *pt = PSCSI_TASK(task); @@ -1049,15 +959,12 @@ static inline struct bio *pscsi_get_bio(int sg_num) return bio; } -static int __pscsi_map_SG( - struct se_task *task, - struct scatterlist *task_sg, - u32 task_sg_num, - int bidi_read) +static int pscsi_map_sg(struct se_task *task, struct scatterlist *task_sg, + struct bio **hbio) { - struct pscsi_plugin_task *pt = PSCSI_TASK(task); - struct pscsi_dev_virt *pdv = task->se_dev->dev_ptr; - struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; + struct pscsi_dev_virt *pdv = task->task_se_cmd->se_dev->dev_ptr; + u32 task_sg_num = task->task_sg_nents; + struct bio *bio = NULL, *tbio = NULL; struct page *page; struct scatterlist *sg; u32 data_len = task->task_size, i, len, bytes, off; @@ -1066,19 +973,8 @@ static int __pscsi_map_SG( int nr_vecs = 0, rc, ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; int rw = (task->task_data_direction == DMA_TO_DEVICE); - if (!task->task_size) - return 0; - /* - * For SCF_SCSI_DATA_SG_IO_CDB, Use fs/bio.c:bio_add_page() to setup - * the bio_vec maplist from task->task_sg -> - * struct scatterlist memory. The struct se_task->task_sg[] currently needs - * to be attached to struct bios for submission to Linux/SCSI using - * struct request to struct scsi_device->request_queue. - * - * Note that this will be changing post v2.6.28 as Target_Core_Mod/pSCSI - * is ported to upstream SCSI passthrough functionality that accepts - * struct scatterlist->page_link or struct page as a paraemeter. - */ + *hbio = NULL; + pr_debug("PSCSI: nr_pages: %d\n", nr_pages); for_each_sg(task_sg, sg, task_sg_num, i) { @@ -1115,8 +1011,8 @@ static int __pscsi_map_SG( * bios need to be added to complete a given * struct se_task */ - if (!hbio) - hbio = tbio = bio; + if (!*hbio) + *hbio = tbio = bio; else tbio = tbio->bi_next = bio; } @@ -1152,92 +1048,82 @@ static int __pscsi_map_SG( off = 0; } } - /* - * Setup the primary pt->pscsi_req used for non BIDI and BIDI-COMMAND - * primary SCSI WRITE poayload mapped for struct se_task->task_sg[] - */ - if (!bidi_read) { - /* - * Starting with v2.6.31, call blk_make_request() passing in *hbio to - * allocate the pSCSI task a struct request. - */ - pt->pscsi_req = blk_make_request(pdv->pdv_sd->request_queue, - hbio, GFP_KERNEL); - if (!pt->pscsi_req) { - pr_err("pSCSI: blk_make_request() failed\n"); - goto fail; - } - /* - * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC, - * and setup rq callback, CDB and sense. - */ - pscsi_blk_init_request(task, pt, pt->pscsi_req, 0); - - return task->task_sg_nents; - } - /* - * Setup the secondary pt->pscsi_req->next_rq used for the extra BIDI-COMMAND - * SCSI READ paylaod mapped for struct se_task->task_sg_bidi[] - */ - pt->pscsi_req->next_rq = blk_make_request(pdv->pdv_sd->request_queue, - hbio, GFP_KERNEL); - if (!pt->pscsi_req->next_rq) { - pr_err("pSCSI: blk_make_request() failed for BIDI\n"); - goto fail; - } - pscsi_blk_init_request(task, pt, pt->pscsi_req->next_rq, 1); return task->task_sg_nents; fail: - while (hbio) { - bio = hbio; - hbio = hbio->bi_next; + while (*hbio) { + bio = *hbio; + *hbio = (*hbio)->bi_next; bio->bi_next = NULL; - bio_endio(bio, 0); + bio_endio(bio, 0); /* XXX: should be error */ } return ret; } -/* - * pSCSI maps both ->map_control_SG() and ->map_data_SG() to a single call. - */ -static int pscsi_map_SG(struct se_task *task) +static int pscsi_do_task(struct se_task *task) { + struct pscsi_dev_virt *pdv = task->task_se_cmd->se_dev->dev_ptr; + struct pscsi_plugin_task *pt = PSCSI_TASK(task); + struct request *req; + struct bio *hbio; int ret; - /* - * Setup the main struct request for the task->task_sg[] payload - */ + target_get_task_cdb(task, pt->pscsi_cdb); + + if (task->task_se_cmd->se_cmd_flags & SCF_SCSI_NON_DATA_CDB) { + req = blk_get_request(pdv->pdv_sd->request_queue, + (task->task_data_direction == DMA_TO_DEVICE), + GFP_KERNEL); + if (!req || IS_ERR(req)) { + pr_err("PSCSI: blk_get_request() failed: %ld\n", + req ? IS_ERR(req) : -ENOMEM); + return PYX_TRANSPORT_LU_COMM_FAILURE; + } + } else { + BUG_ON(!task->task_size); - ret = __pscsi_map_SG(task, task->task_sg, task->task_sg_nents, 0); - if (ret >= 0 && task->task_sg_bidi) { /* - * If present, set up the extra BIDI-COMMAND SCSI READ - * struct request and payload. + * Setup the main struct request for the task->task_sg[] payload */ - ret = __pscsi_map_SG(task, task->task_sg_bidi, - task->task_sg_nents, 1); + ret = pscsi_map_sg(task, task->task_sg, &hbio); + if (ret < 0) + return PYX_TRANSPORT_LU_COMM_FAILURE; + + req = blk_make_request(pdv->pdv_sd->request_queue, hbio, + GFP_KERNEL); + if (!req) { + pr_err("pSCSI: blk_make_request() failed\n"); + goto fail; + } } - if (ret < 0) - return PYX_TRANSPORT_LU_COMM_FAILURE; - return 0; -} + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->end_io = pscsi_req_done; + req->end_io_data = task; + req->cmd_len = scsi_command_size(pt->pscsi_cdb); + req->cmd = &pt->pscsi_cdb[0]; + req->sense = &pt->pscsi_sense[0]; + req->sense_len = 0; + if (pdv->pdv_sd->type == TYPE_DISK) + req->timeout = PS_TIMEOUT_DISK; + else + req->timeout = PS_TIMEOUT_OTHER; + req->retries = PS_RETRY; -static int pscsi_CDB_none(struct se_task *task) -{ - return pscsi_blk_get_request(task); -} + blk_execute_rq_nowait(pdv->pdv_sd->request_queue, NULL, req, + (task->task_se_cmd->sam_task_attr == MSG_HEAD_TAG), + pscsi_req_done); -/* pscsi_get_cdb(): - * - * - */ -static unsigned char *pscsi_get_cdb(struct se_task *task) -{ - struct pscsi_plugin_task *pt = PSCSI_TASK(task); + return PYX_TRANSPORT_SENT_TO_TRANSPORT; - return pt->pscsi_cdb; +fail: + while (hbio) { + struct bio *bio = hbio; + hbio = hbio->bi_next; + bio->bi_next = NULL; + bio_endio(bio, 0); /* XXX: should be error */ + } + return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES; } /* pscsi_get_sense_buffer(): @@ -1328,23 +1214,13 @@ static void pscsi_req_done(struct request *req, int uptodate) pt->pscsi_resid = req->resid_len; pscsi_process_SAM_status(task, pt); - /* - * Release BIDI-READ if present - */ - if (req->next_rq != NULL) - __blk_put_request(req->q, req->next_rq); - __blk_put_request(req->q, req); - pt->pscsi_req = NULL; } static struct se_subsystem_api pscsi_template = { .name = "pscsi", .owner = THIS_MODULE, .transport_type = TRANSPORT_PLUGIN_PHBA_PDEV, - .cdb_none = pscsi_CDB_none, - .map_control_SG = pscsi_map_SG, - .map_data_SG = pscsi_map_SG, .attach_hba = pscsi_attach_hba, .detach_hba = pscsi_detach_hba, .pmode_enable_hba = pscsi_pmode_enable_hba, @@ -1358,7 +1234,6 @@ static struct se_subsystem_api pscsi_template = { .check_configfs_dev_params = pscsi_check_configfs_dev_params, .set_configfs_dev_params = pscsi_set_configfs_dev_params, .show_configfs_dev_params = pscsi_show_configfs_dev_params, - .get_cdb = pscsi_get_cdb, .get_sense_buffer = pscsi_get_sense_buffer, .get_device_rev = pscsi_get_device_rev, .get_device_type = pscsi_get_device_type, diff --git a/drivers/target/target_core_pscsi.h b/drivers/target/target_core_pscsi.h index ebf4f1ae2c83..fdc17b6aefb3 100644 --- a/drivers/target/target_core_pscsi.h +++ b/drivers/target/target_core_pscsi.h @@ -27,7 +27,6 @@ struct pscsi_plugin_task { int pscsi_direction; int pscsi_result; u32 pscsi_resid; - struct request *pscsi_req; unsigned char pscsi_cdb[0]; } ____cacheline_aligned; diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index e567e129c697..5158d3846f19 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -27,7 +27,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/string.h> #include <linux/parser.h> #include <linux/timer.h> @@ -351,7 +350,7 @@ static struct rd_dev_sg_table *rd_get_sg_table(struct rd_dev *rd_dev, u32 page) static int rd_MEMCPY_read(struct rd_request *req) { struct se_task *task = &req->rd_task; - struct rd_dev *dev = req->rd_task.se_dev->dev_ptr; + struct rd_dev *dev = req->rd_task.task_se_cmd->se_dev->dev_ptr; struct rd_dev_sg_table *table; struct scatterlist *sg_d, *sg_s; void *dst, *src; @@ -467,7 +466,7 @@ static int rd_MEMCPY_read(struct rd_request *req) static int rd_MEMCPY_write(struct rd_request *req) { struct se_task *task = &req->rd_task; - struct rd_dev *dev = req->rd_task.se_dev->dev_ptr; + struct rd_dev *dev = req->rd_task.task_se_cmd->se_dev->dev_ptr; struct rd_dev_sg_table *table; struct scatterlist *sg_d, *sg_s; void *dst, *src; @@ -582,7 +581,7 @@ static int rd_MEMCPY_write(struct rd_request *req) */ static int rd_MEMCPY_do_task(struct se_task *task) { - struct se_device *dev = task->se_dev; + struct se_device *dev = task->task_se_cmd->se_dev; struct rd_request *req = RD_REQ(task); unsigned long long lba; int ret; @@ -692,17 +691,6 @@ static ssize_t rd_show_configfs_dev_params( return bl; } -/* rd_get_cdb(): (Part of se_subsystem_api_t template) - * - * - */ -static unsigned char *rd_get_cdb(struct se_task *task) -{ - struct rd_request *req = RD_REQ(task); - - return req->rd_scsi_cdb; -} - static u32 rd_get_device_rev(struct se_device *dev) { return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */ @@ -736,7 +724,6 @@ static struct se_subsystem_api rd_mcp_template = { .check_configfs_dev_params = rd_check_configfs_dev_params, .set_configfs_dev_params = rd_set_configfs_dev_params, .show_configfs_dev_params = rd_show_configfs_dev_params, - .get_cdb = rd_get_cdb, .get_device_rev = rd_get_device_rev, .get_device_type = rd_get_device_type, .get_blocks = rd_get_blocks, diff --git a/drivers/target/target_core_rd.h b/drivers/target/target_core_rd.h index 0d027732cd00..784e56a04100 100644 --- a/drivers/target/target_core_rd.h +++ b/drivers/target/target_core_rd.h @@ -22,8 +22,6 @@ void rd_module_exit(void); struct rd_request { struct se_task rd_task; - /* SCSI CDB from iSCSI Command PDU */ - unsigned char rd_scsi_cdb[TCM_MAX_COMMAND_SIZE]; /* Offset from start of page */ u32 rd_offset; /* Starting page in Ramdisk for request */ diff --git a/drivers/target/target_core_scdb.c b/drivers/target/target_core_scdb.c deleted file mode 100644 index 72843441d4fa..000000000000 --- a/drivers/target/target_core_scdb.c +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Filename: target_core_scdb.c - * - * This file contains the generic target engine Split CDB related functions. - * - * Copyright (c) 2004-2005 PyX Technologies, Inc. - * Copyright (c) 2005, 2006, 2007 SBE, Inc. - * Copyright (c) 2007-2010 Rising Tide Systems - * Copyright (c) 2008-2010 Linux-iSCSI.org - * - * Nicholas A. Bellinger <nab@kernel.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - ******************************************************************************/ - -#include <linux/net.h> -#include <linux/string.h> -#include <scsi/scsi.h> -#include <asm/unaligned.h> - -#include <target/target_core_base.h> -#include <target/target_core_transport.h> - -#include "target_core_scdb.h" - -/* split_cdb_XX_6(): - * - * 21-bit LBA w/ 8-bit SECTORS - */ -void split_cdb_XX_6( - unsigned long long lba, - u32 sectors, - unsigned char *cdb) -{ - cdb[1] = (lba >> 16) & 0x1f; - cdb[2] = (lba >> 8) & 0xff; - cdb[3] = lba & 0xff; - cdb[4] = sectors & 0xff; -} - -/* split_cdb_XX_10(): - * - * 32-bit LBA w/ 16-bit SECTORS - */ -void split_cdb_XX_10( - unsigned long long lba, - u32 sectors, - unsigned char *cdb) -{ - put_unaligned_be32(lba, &cdb[2]); - put_unaligned_be16(sectors, &cdb[7]); -} - -/* split_cdb_XX_12(): - * - * 32-bit LBA w/ 32-bit SECTORS - */ -void split_cdb_XX_12( - unsigned long long lba, - u32 sectors, - unsigned char *cdb) -{ - put_unaligned_be32(lba, &cdb[2]); - put_unaligned_be32(sectors, &cdb[6]); -} - -/* split_cdb_XX_16(): - * - * 64-bit LBA w/ 32-bit SECTORS - */ -void split_cdb_XX_16( - unsigned long long lba, - u32 sectors, - unsigned char *cdb) -{ - put_unaligned_be64(lba, &cdb[2]); - put_unaligned_be32(sectors, &cdb[10]); -} - -/* - * split_cdb_XX_32(): - * - * 64-bit LBA w/ 32-bit SECTORS such as READ_32, WRITE_32 and emulated XDWRITEREAD_32 - */ -void split_cdb_XX_32( - unsigned long long lba, - u32 sectors, - unsigned char *cdb) -{ - put_unaligned_be64(lba, &cdb[12]); - put_unaligned_be32(sectors, &cdb[28]); -} diff --git a/drivers/target/target_core_scdb.h b/drivers/target/target_core_scdb.h deleted file mode 100644 index 48e9ccc9585e..000000000000 --- a/drivers/target/target_core_scdb.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef TARGET_CORE_SCDB_H -#define TARGET_CORE_SCDB_H - -extern void split_cdb_XX_6(unsigned long long, u32, unsigned char *); -extern void split_cdb_XX_10(unsigned long long, u32, unsigned char *); -extern void split_cdb_XX_12(unsigned long long, u32, unsigned char *); -extern void split_cdb_XX_16(unsigned long long, u32, unsigned char *); -extern void split_cdb_XX_32(unsigned long long, u32, unsigned char *); - -#endif /* TARGET_CORE_SCDB_H */ diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index a8d6e1dee938..874152aed94a 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -32,7 +32,6 @@ #include <linux/delay.h> #include <linux/timer.h> #include <linux/string.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/proc_fs.h> diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 27d4925e51c3..570b144a1edb 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -24,7 +24,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/list.h> @@ -44,12 +43,12 @@ struct se_tmr_req *core_tmr_alloc_req( struct se_cmd *se_cmd, void *fabric_tmr_ptr, - u8 function) + u8 function, + gfp_t gfp_flags) { struct se_tmr_req *tmr; - tmr = kmem_cache_zalloc(se_tmr_req_cache, (in_interrupt()) ? - GFP_ATOMIC : GFP_KERNEL); + tmr = kmem_cache_zalloc(se_tmr_req_cache, gfp_flags); if (!tmr) { pr_err("Unable to allocate struct se_tmr_req\n"); return ERR_PTR(-ENOMEM); @@ -67,15 +66,16 @@ void core_tmr_release_req( struct se_tmr_req *tmr) { struct se_device *dev = tmr->tmr_dev; + unsigned long flags; if (!dev) { kmem_cache_free(se_tmr_req_cache, tmr); return; } - spin_lock_irq(&dev->se_tmr_lock); + spin_lock_irqsave(&dev->se_tmr_lock, flags); list_del(&tmr->tmr_list); - spin_unlock_irq(&dev->se_tmr_lock); + spin_unlock_irqrestore(&dev->se_tmr_lock, flags); kmem_cache_free(se_tmr_req_cache, tmr); } @@ -100,54 +100,20 @@ static void core_tmr_handle_tas_abort( transport_cmd_finish_abort(cmd, 0); } -int core_tmr_lun_reset( +static void core_tmr_drain_tmr_list( struct se_device *dev, struct se_tmr_req *tmr, - struct list_head *preempt_and_abort_list, - struct se_cmd *prout_cmd) + struct list_head *preempt_and_abort_list) { - struct se_cmd *cmd, *tcmd; - struct se_node_acl *tmr_nacl = NULL; - struct se_portal_group *tmr_tpg = NULL; - struct se_queue_obj *qobj = &dev->dev_queue_obj; + LIST_HEAD(drain_tmr_list); struct se_tmr_req *tmr_p, *tmr_pp; - struct se_task *task, *task_tmp; + struct se_cmd *cmd; unsigned long flags; - int fe_count, tas; - /* - * TASK_ABORTED status bit, this is configurable via ConfigFS - * struct se_device attributes. spc4r17 section 7.4.6 Control mode page - * - * A task aborted status (TAS) bit set to zero specifies that aborted - * tasks shall be terminated by the device server without any response - * to the application client. A TAS bit set to one specifies that tasks - * aborted by the actions of an I_T nexus other than the I_T nexus on - * which the command was received shall be completed with TASK ABORTED - * status (see SAM-4). - */ - tas = dev->se_sub_dev->se_dev_attrib.emulate_tas; - /* - * Determine if this se_tmr is coming from a $FABRIC_MOD - * or struct se_device passthrough.. - */ - if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) { - tmr_nacl = tmr->task_cmd->se_sess->se_node_acl; - tmr_tpg = tmr->task_cmd->se_sess->se_tpg; - if (tmr_nacl && tmr_tpg) { - pr_debug("LUN_RESET: TMR caller fabric: %s" - " initiator port %s\n", - tmr_tpg->se_tpg_tfo->get_fabric_name(), - tmr_nacl->initiatorname); - } - } - pr_debug("LUN_RESET: %s starting for [%s], tas: %d\n", - (preempt_and_abort_list) ? "Preempt" : "TMR", - dev->transport->name, tas); /* * Release all pending and outgoing TMRs aside from the received * LUN_RESET tmr.. */ - spin_lock_irq(&dev->se_tmr_lock); + spin_lock_irqsave(&dev->se_tmr_lock, flags); list_for_each_entry_safe(tmr_p, tmr_pp, &dev->dev_tmr_list, tmr_list) { /* * Allow the received TMR to return with FUNCTION_COMPLETE. @@ -169,29 +135,48 @@ int core_tmr_lun_reset( (core_scsi3_check_cdb_abort_and_preempt( preempt_and_abort_list, cmd) != 0)) continue; - spin_unlock_irq(&dev->se_tmr_lock); - spin_lock_irqsave(&cmd->t_state_lock, flags); + spin_lock(&cmd->t_state_lock); if (!atomic_read(&cmd->t_transport_active)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - spin_lock_irq(&dev->se_tmr_lock); + spin_unlock(&cmd->t_state_lock); continue; } if (cmd->t_state == TRANSPORT_ISTATE_PROCESSING) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - spin_lock_irq(&dev->se_tmr_lock); + spin_unlock(&cmd->t_state_lock); continue; } + spin_unlock(&cmd->t_state_lock); + + list_move_tail(&tmr->tmr_list, &drain_tmr_list); + } + spin_unlock_irqrestore(&dev->se_tmr_lock, flags); + + while (!list_empty(&drain_tmr_list)) { + tmr = list_entry(drain_tmr_list.next, struct se_tmr_req, tmr_list); + list_del(&tmr->tmr_list); + cmd = tmr_p->task_cmd; + pr_debug("LUN_RESET: %s releasing TMR %p Function: 0x%02x," " Response: 0x%02x, t_state: %d\n", - (preempt_and_abort_list) ? "Preempt" : "", tmr_p, - tmr_p->function, tmr_p->response, cmd->t_state); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); + (preempt_and_abort_list) ? "Preempt" : "", tmr, + tmr->function, tmr->response, cmd->t_state); - transport_cmd_finish_abort_tmr(cmd); - spin_lock_irq(&dev->se_tmr_lock); + transport_cmd_finish_abort(cmd, 1); } - spin_unlock_irq(&dev->se_tmr_lock); +} + +static void core_tmr_drain_task_list( + struct se_device *dev, + struct se_cmd *prout_cmd, + struct se_node_acl *tmr_nacl, + int tas, + struct list_head *preempt_and_abort_list) +{ + LIST_HEAD(drain_task_list); + struct se_cmd *cmd; + struct se_task *task, *task_tmp; + unsigned long flags; + int fe_count; /* * Complete outstanding struct se_task CDBs with TASK_ABORTED SAM status. * This is following sam4r17, section 5.6 Aborting commands, Table 38 @@ -236,18 +221,28 @@ int core_tmr_lun_reset( if (prout_cmd == cmd) continue; - list_del(&task->t_state_list); + list_move_tail(&task->t_state_list, &drain_task_list); atomic_set(&task->task_state_active, 0); - spin_unlock_irqrestore(&dev->execute_task_lock, flags); + /* + * Remove from task execute list before processing drain_task_list + */ + if (!list_empty(&task->t_execute_list)) + __transport_remove_task_from_execute_queue(task, dev); + } + spin_unlock_irqrestore(&dev->execute_task_lock, flags); + + while (!list_empty(&drain_task_list)) { + task = list_entry(drain_task_list.next, struct se_task, t_state_list); + list_del(&task->t_state_list); + cmd = task->task_se_cmd; - spin_lock_irqsave(&cmd->t_state_lock, flags); pr_debug("LUN_RESET: %s cmd: %p task: %p" - " ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state/" - "def_t_state: %d/%d cdb: 0x%02x\n", + " ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state: %d" + "cdb: 0x%02x\n", (preempt_and_abort_list) ? "Preempt" : "", cmd, task, cmd->se_tfo->get_task_tag(cmd), 0, cmd->se_tfo->get_cmd_state(cmd), cmd->t_state, - cmd->deferred_t_state, cmd->t_task_cdb[0]); + cmd->t_task_cdb[0]); pr_debug("LUN_RESET: ITT[0x%08x] - pr_res_key: 0x%016Lx" " t_task_cdbs: %d t_task_cdbs_left: %d" " t_task_cdbs_sent: %d -- t_transport_active: %d" @@ -260,35 +255,24 @@ int core_tmr_lun_reset( atomic_read(&cmd->t_transport_stop), atomic_read(&cmd->t_transport_sent)); - if (atomic_read(&task->task_active)) { - atomic_set(&task->task_stop, 1); - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - - pr_debug("LUN_RESET: Waiting for task: %p to shutdown" - " for dev: %p\n", task, dev); - wait_for_completion(&task->task_stop_comp); - pr_debug("LUN_RESET Completed task: %p shutdown for" - " dev: %p\n", task, dev); - spin_lock_irqsave(&cmd->t_state_lock, flags); - atomic_dec(&cmd->t_task_cdbs_left); - - atomic_set(&task->task_active, 0); - atomic_set(&task->task_stop, 0); - } else { - if (atomic_read(&task->task_execute_queue) != 0) - transport_remove_task_from_execute_queue(task, dev); - } - __transport_stop_task_timer(task, &flags); + /* + * If the command may be queued onto a workqueue cancel it now. + * + * This is equivalent to removal from the execute queue in the + * loop above, but we do it down here given that + * cancel_work_sync may block. + */ + if (cmd->t_state == TRANSPORT_COMPLETE) + cancel_work_sync(&cmd->work); + + spin_lock_irqsave(&cmd->t_state_lock, flags); + target_stop_task(task, &flags); if (!atomic_dec_and_test(&cmd->t_task_cdbs_ex_left)) { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); pr_debug("LUN_RESET: Skipping task: %p, dev: %p for" " t_task_cdbs_ex_left: %d\n", task, dev, atomic_read(&cmd->t_task_cdbs_ex_left)); - - spin_lock_irqsave(&dev->execute_task_lock, flags); continue; } fe_count = atomic_read(&cmd->t_fe_count); @@ -298,22 +282,31 @@ int core_tmr_lun_reset( " task: %p, t_fe_count: %d dev: %p\n", task, fe_count, dev); atomic_set(&cmd->t_transport_aborted, 1); - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); - spin_lock_irqsave(&dev->execute_task_lock, flags); + core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); continue; } pr_debug("LUN_RESET: Got t_transport_active = 0 for task: %p," " t_fe_count: %d dev: %p\n", task, fe_count, dev); atomic_set(&cmd->t_transport_aborted, 1); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); - spin_lock_irqsave(&dev->execute_task_lock, flags); + core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); } - spin_unlock_irqrestore(&dev->execute_task_lock, flags); +} + +static void core_tmr_drain_cmd_list( + struct se_device *dev, + struct se_cmd *prout_cmd, + struct se_node_acl *tmr_nacl, + int tas, + struct list_head *preempt_and_abort_list) +{ + LIST_HEAD(drain_cmd_list); + struct se_queue_obj *qobj = &dev->dev_queue_obj; + struct se_cmd *cmd, *tcmd; + unsigned long flags; /* * Release all commands remaining in the struct se_device cmd queue. * @@ -337,11 +330,26 @@ int core_tmr_lun_reset( */ if (prout_cmd == cmd) continue; + /* + * Skip direct processing of TRANSPORT_FREE_CMD_INTR for + * HW target mode fabrics. + */ + spin_lock(&cmd->t_state_lock); + if (cmd->t_state == TRANSPORT_FREE_CMD_INTR) { + spin_unlock(&cmd->t_state_lock); + continue; + } + spin_unlock(&cmd->t_state_lock); - atomic_dec(&cmd->t_transport_queue_active); + atomic_set(&cmd->t_transport_queue_active, 0); atomic_dec(&qobj->queue_cnt); - list_del(&cmd->se_queue_node); - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); + list_move_tail(&cmd->se_queue_node, &drain_cmd_list); + } + spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); + + while (!list_empty(&drain_cmd_list)) { + cmd = list_entry(drain_cmd_list.next, struct se_cmd, se_queue_node); + list_del_init(&cmd->se_queue_node); pr_debug("LUN_RESET: %s from Device Queue: cmd: %p t_state:" " %d t_fe_count: %d\n", (preempt_and_abort_list) ? @@ -354,9 +362,53 @@ int core_tmr_lun_reset( core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, atomic_read(&cmd->t_fe_count)); - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); } - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); +} + +int core_tmr_lun_reset( + struct se_device *dev, + struct se_tmr_req *tmr, + struct list_head *preempt_and_abort_list, + struct se_cmd *prout_cmd) +{ + struct se_node_acl *tmr_nacl = NULL; + struct se_portal_group *tmr_tpg = NULL; + int tas; + /* + * TASK_ABORTED status bit, this is configurable via ConfigFS + * struct se_device attributes. spc4r17 section 7.4.6 Control mode page + * + * A task aborted status (TAS) bit set to zero specifies that aborted + * tasks shall be terminated by the device server without any response + * to the application client. A TAS bit set to one specifies that tasks + * aborted by the actions of an I_T nexus other than the I_T nexus on + * which the command was received shall be completed with TASK ABORTED + * status (see SAM-4). + */ + tas = dev->se_sub_dev->se_dev_attrib.emulate_tas; + /* + * Determine if this se_tmr is coming from a $FABRIC_MOD + * or struct se_device passthrough.. + */ + if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) { + tmr_nacl = tmr->task_cmd->se_sess->se_node_acl; + tmr_tpg = tmr->task_cmd->se_sess->se_tpg; + if (tmr_nacl && tmr_tpg) { + pr_debug("LUN_RESET: TMR caller fabric: %s" + " initiator port %s\n", + tmr_tpg->se_tpg_tfo->get_fabric_name(), + tmr_nacl->initiatorname); + } + } + pr_debug("LUN_RESET: %s starting for [%s], tas: %d\n", + (preempt_and_abort_list) ? "Preempt" : "TMR", + dev->transport->name, tas); + + core_tmr_drain_tmr_list(dev, tmr, preempt_and_abort_list); + core_tmr_drain_task_list(dev, prout_cmd, tmr_nacl, tas, + preempt_and_abort_list); + core_tmr_drain_cmd_list(dev, prout_cmd, tmr_nacl, tas, + preempt_and_abort_list); /* * Clear any legacy SPC-2 reservation when called during * LOGICAL UNIT RESET @@ -379,3 +431,4 @@ int core_tmr_lun_reset( dev->transport->name); return 0; } + diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 162b736c7342..49fd0a9b0a56 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -593,7 +593,7 @@ int core_tpg_set_initiator_node_queue_depth( if (init_sess) tpg->se_tpg_tfo->close_session(init_sess); - pr_debug("Successfuly changed queue depth to: %d for Initiator" + pr_debug("Successfully changed queue depth to: %d for Initiator" " Node: %s on %s Target Portal Group: %u\n", queue_depth, initiatorname, tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg)); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index a4b0a8d27f25..d75255804481 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -26,7 +26,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/net.h> #include <linux/delay.h> #include <linux/string.h> @@ -55,11 +54,11 @@ #include "target_core_alua.h" #include "target_core_hba.h" #include "target_core_pr.h" -#include "target_core_scdb.h" #include "target_core_ua.h" static int sub_api_initialized; +static struct workqueue_struct *target_completion_wq; static struct kmem_cache *se_cmd_cache; static struct kmem_cache *se_sess_cache; struct kmem_cache *se_tmr_req_cache; @@ -70,30 +69,19 @@ struct kmem_cache *t10_alua_lu_gp_mem_cache; struct kmem_cache *t10_alua_tg_pt_gp_cache; struct kmem_cache *t10_alua_tg_pt_gp_mem_cache; -/* Used for transport_dev_get_map_*() */ -typedef int (*map_func_t)(struct se_task *, u32); - static int transport_generic_write_pending(struct se_cmd *); static int transport_processing_thread(void *param); static int __transport_execute_tasks(struct se_device *dev); static void transport_complete_task_attr(struct se_cmd *cmd); -static int transport_complete_qf(struct se_cmd *cmd); static void transport_handle_queue_full(struct se_cmd *cmd, - struct se_device *dev, int (*qf_callback)(struct se_cmd *)); -static void transport_direct_request_timeout(struct se_cmd *cmd); + struct se_device *dev); static void transport_free_dev_tasks(struct se_cmd *cmd); -static u32 transport_allocate_tasks(struct se_cmd *cmd, - unsigned long long starting_lba, - enum dma_data_direction data_direction, - struct scatterlist *sgl, unsigned int nents); static int transport_generic_get_mem(struct se_cmd *cmd); -static int transport_generic_remove(struct se_cmd *cmd, - int session_reinstatement); -static void transport_release_fe_cmd(struct se_cmd *cmd); -static void transport_remove_cmd_from_queue(struct se_cmd *cmd, - struct se_queue_obj *qobj); +static void transport_put_cmd(struct se_cmd *cmd); +static void transport_remove_cmd_from_queue(struct se_cmd *cmd); static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq); -static void transport_stop_all_task_timers(struct se_cmd *cmd); +static void transport_generic_request_failure(struct se_cmd *, int, int); +static void target_complete_ok_work(struct work_struct *work); int init_se_kmem_caches(void) { @@ -109,7 +97,7 @@ int init_se_kmem_caches(void) if (!se_tmr_req_cache) { pr_err("kmem_cache_create() for struct se_tmr_req" " failed\n"); - goto out; + goto out_free_cmd_cache; } se_sess_cache = kmem_cache_create("se_sess_cache", sizeof(struct se_session), __alignof__(struct se_session), @@ -117,14 +105,14 @@ int init_se_kmem_caches(void) if (!se_sess_cache) { pr_err("kmem_cache_create() for struct se_session" " failed\n"); - goto out; + goto out_free_tmr_req_cache; } se_ua_cache = kmem_cache_create("se_ua_cache", sizeof(struct se_ua), __alignof__(struct se_ua), 0, NULL); if (!se_ua_cache) { pr_err("kmem_cache_create() for struct se_ua failed\n"); - goto out; + goto out_free_sess_cache; } t10_pr_reg_cache = kmem_cache_create("t10_pr_reg_cache", sizeof(struct t10_pr_registration), @@ -132,7 +120,7 @@ int init_se_kmem_caches(void) if (!t10_pr_reg_cache) { pr_err("kmem_cache_create() for struct t10_pr_registration" " failed\n"); - goto out; + goto out_free_ua_cache; } t10_alua_lu_gp_cache = kmem_cache_create("t10_alua_lu_gp_cache", sizeof(struct t10_alua_lu_gp), __alignof__(struct t10_alua_lu_gp), @@ -140,7 +128,7 @@ int init_se_kmem_caches(void) if (!t10_alua_lu_gp_cache) { pr_err("kmem_cache_create() for t10_alua_lu_gp_cache" " failed\n"); - goto out; + goto out_free_pr_reg_cache; } t10_alua_lu_gp_mem_cache = kmem_cache_create("t10_alua_lu_gp_mem_cache", sizeof(struct t10_alua_lu_gp_member), @@ -148,7 +136,7 @@ int init_se_kmem_caches(void) if (!t10_alua_lu_gp_mem_cache) { pr_err("kmem_cache_create() for t10_alua_lu_gp_mem_" "cache failed\n"); - goto out; + goto out_free_lu_gp_cache; } t10_alua_tg_pt_gp_cache = kmem_cache_create("t10_alua_tg_pt_gp_cache", sizeof(struct t10_alua_tg_pt_gp), @@ -156,7 +144,7 @@ int init_se_kmem_caches(void) if (!t10_alua_tg_pt_gp_cache) { pr_err("kmem_cache_create() for t10_alua_tg_pt_gp_" "cache failed\n"); - goto out; + goto out_free_lu_gp_mem_cache; } t10_alua_tg_pt_gp_mem_cache = kmem_cache_create( "t10_alua_tg_pt_gp_mem_cache", @@ -166,34 +154,41 @@ int init_se_kmem_caches(void) if (!t10_alua_tg_pt_gp_mem_cache) { pr_err("kmem_cache_create() for t10_alua_tg_pt_gp_" "mem_t failed\n"); - goto out; + goto out_free_tg_pt_gp_cache; } + target_completion_wq = alloc_workqueue("target_completion", + WQ_MEM_RECLAIM, 0); + if (!target_completion_wq) + goto out_free_tg_pt_gp_mem_cache; + return 0; + +out_free_tg_pt_gp_mem_cache: + kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache); +out_free_tg_pt_gp_cache: + kmem_cache_destroy(t10_alua_tg_pt_gp_cache); +out_free_lu_gp_mem_cache: + kmem_cache_destroy(t10_alua_lu_gp_mem_cache); +out_free_lu_gp_cache: + kmem_cache_destroy(t10_alua_lu_gp_cache); +out_free_pr_reg_cache: + kmem_cache_destroy(t10_pr_reg_cache); +out_free_ua_cache: + kmem_cache_destroy(se_ua_cache); +out_free_sess_cache: + kmem_cache_destroy(se_sess_cache); +out_free_tmr_req_cache: + kmem_cache_destroy(se_tmr_req_cache); +out_free_cmd_cache: + kmem_cache_destroy(se_cmd_cache); out: - if (se_cmd_cache) - kmem_cache_destroy(se_cmd_cache); - if (se_tmr_req_cache) - kmem_cache_destroy(se_tmr_req_cache); - if (se_sess_cache) - kmem_cache_destroy(se_sess_cache); - if (se_ua_cache) - kmem_cache_destroy(se_ua_cache); - if (t10_pr_reg_cache) - kmem_cache_destroy(t10_pr_reg_cache); - if (t10_alua_lu_gp_cache) - kmem_cache_destroy(t10_alua_lu_gp_cache); - if (t10_alua_lu_gp_mem_cache) - kmem_cache_destroy(t10_alua_lu_gp_mem_cache); - if (t10_alua_tg_pt_gp_cache) - kmem_cache_destroy(t10_alua_tg_pt_gp_cache); - if (t10_alua_tg_pt_gp_mem_cache) - kmem_cache_destroy(t10_alua_tg_pt_gp_mem_cache); return -ENOMEM; } void release_se_kmem_caches(void) { + destroy_workqueue(target_completion_wq); kmem_cache_destroy(se_cmd_cache); kmem_cache_destroy(se_tmr_req_cache); kmem_cache_destroy(se_sess_cache); @@ -234,10 +229,13 @@ void transport_init_queue_obj(struct se_queue_obj *qobj) } EXPORT_SYMBOL(transport_init_queue_obj); -static int transport_subsystem_reqmods(void) +void transport_subsystem_check_init(void) { int ret; + if (sub_api_initialized) + return; + ret = request_module("target_core_iblock"); if (ret != 0) pr_err("Unable to load target_core_iblock\n"); @@ -254,24 +252,8 @@ static int transport_subsystem_reqmods(void) if (ret != 0) pr_err("Unable to load target_core_stgt\n"); - return 0; -} - -int transport_subsystem_check_init(void) -{ - int ret; - - if (sub_api_initialized) - return 0; - /* - * Request the loading of known TCM subsystem plugins.. - */ - ret = transport_subsystem_reqmods(); - if (ret < 0) - return ret; - sub_api_initialized = 1; - return 0; + return; } struct se_session *transport_init_session(void) @@ -438,16 +420,15 @@ EXPORT_SYMBOL(transport_deregister_session); */ static void transport_all_task_dev_remove_state(struct se_cmd *cmd) { - struct se_device *dev; + struct se_device *dev = cmd->se_dev; struct se_task *task; unsigned long flags; - list_for_each_entry(task, &cmd->t_task_list, t_list) { - dev = task->se_dev; - if (!dev) - continue; + if (!dev) + return; - if (atomic_read(&task->task_active)) + list_for_each_entry(task, &cmd->t_task_list, t_list) { + if (task->task_flags & TF_ACTIVE) continue; if (!atomic_read(&task->task_state_active)) @@ -489,8 +470,6 @@ static int transport_cmd_check_stop( " == TRUE for ITT: 0x%08x\n", __func__, __LINE__, cmd->se_tfo->get_task_tag(cmd)); - cmd->deferred_t_state = cmd->t_state; - cmd->t_state = TRANSPORT_DEFERRED_CMD; atomic_set(&cmd->t_transport_active, 0); if (transport_off == 2) transport_all_task_dev_remove_state(cmd); @@ -508,8 +487,6 @@ static int transport_cmd_check_stop( " TRUE for ITT: 0x%08x\n", __func__, __LINE__, cmd->se_tfo->get_task_tag(cmd)); - cmd->deferred_t_state = cmd->t_state; - cmd->t_state = TRANSPORT_DEFERRED_CMD; if (transport_off == 2) transport_all_task_dev_remove_state(cmd); @@ -594,35 +571,24 @@ check_lun: void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { - transport_remove_cmd_from_queue(cmd, &cmd->se_dev->dev_queue_obj); - transport_lun_remove_cmd(cmd); + if (!cmd->se_tmr_req) + transport_lun_remove_cmd(cmd); if (transport_cmd_check_stop_to_fabric(cmd)) return; - if (remove) - transport_generic_remove(cmd, 0); -} - -void transport_cmd_finish_abort_tmr(struct se_cmd *cmd) -{ - transport_remove_cmd_from_queue(cmd, &cmd->se_dev->dev_queue_obj); - - if (transport_cmd_check_stop_to_fabric(cmd)) - return; - - transport_generic_remove(cmd, 0); + if (remove) { + transport_remove_cmd_from_queue(cmd); + transport_put_cmd(cmd); + } } -static void transport_add_cmd_to_queue( - struct se_cmd *cmd, - int t_state) +static void transport_add_cmd_to_queue(struct se_cmd *cmd, int t_state, + bool at_head) { struct se_device *dev = cmd->se_dev; struct se_queue_obj *qobj = &dev->dev_queue_obj; unsigned long flags; - INIT_LIST_HEAD(&cmd->se_queue_node); - if (t_state) { spin_lock_irqsave(&cmd->t_state_lock, flags); cmd->t_state = t_state; @@ -631,15 +597,20 @@ static void transport_add_cmd_to_queue( } spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - if (cmd->se_cmd_flags & SCF_EMULATE_QUEUE_FULL) { - cmd->se_cmd_flags &= ~SCF_EMULATE_QUEUE_FULL; + + /* If the cmd is already on the list, remove it before we add it */ + if (!list_empty(&cmd->se_queue_node)) + list_del(&cmd->se_queue_node); + else + atomic_inc(&qobj->queue_cnt); + + if (at_head) list_add(&cmd->se_queue_node, &qobj->qobj_list); - } else + else list_add_tail(&cmd->se_queue_node, &qobj->qobj_list); - atomic_inc(&cmd->t_transport_queue_active); + atomic_set(&cmd->t_transport_queue_active, 1); spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - atomic_inc(&qobj->queue_cnt); wake_up_interruptible(&qobj->thread_wq); } @@ -656,19 +627,18 @@ transport_get_cmd_from_queue(struct se_queue_obj *qobj) } cmd = list_first_entry(&qobj->qobj_list, struct se_cmd, se_queue_node); - atomic_dec(&cmd->t_transport_queue_active); + atomic_set(&cmd->t_transport_queue_active, 0); - list_del(&cmd->se_queue_node); + list_del_init(&cmd->se_queue_node); atomic_dec(&qobj->queue_cnt); spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); return cmd; } -static void transport_remove_cmd_from_queue(struct se_cmd *cmd, - struct se_queue_obj *qobj) +static void transport_remove_cmd_from_queue(struct se_cmd *cmd) { - struct se_cmd *t; + struct se_queue_obj *qobj = &cmd->se_dev->dev_queue_obj; unsigned long flags; spin_lock_irqsave(&qobj->cmd_queue_lock, flags); @@ -676,14 +646,9 @@ static void transport_remove_cmd_from_queue(struct se_cmd *cmd, spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); return; } - - list_for_each_entry(t, &qobj->qobj_list, se_queue_node) - if (t == cmd) { - atomic_dec(&cmd->t_transport_queue_active); - atomic_dec(&qobj->queue_cnt); - list_del(&cmd->se_queue_node); - break; - } + atomic_set(&cmd->t_transport_queue_active, 0); + atomic_dec(&qobj->queue_cnt); + list_del_init(&cmd->se_queue_node); spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); if (atomic_read(&cmd->t_transport_queue_active)) { @@ -716,6 +681,13 @@ void transport_complete_sync_cache(struct se_cmd *cmd, int good) } EXPORT_SYMBOL(transport_complete_sync_cache); +static void target_complete_failure_work(struct work_struct *work) +{ + struct se_cmd *cmd = container_of(work, struct se_cmd, work); + + transport_generic_request_failure(cmd, 1, 1); +} + /* transport_complete_task(): * * Called from interrupt and non interrupt context depending @@ -724,8 +696,7 @@ EXPORT_SYMBOL(transport_complete_sync_cache); void transport_complete_task(struct se_task *task, int success) { struct se_cmd *cmd = task->task_se_cmd; - struct se_device *dev = task->se_dev; - int t_state; + struct se_device *dev = cmd->se_dev; unsigned long flags; #if 0 pr_debug("task: %p CDB: 0x%02x obj_ptr: %p\n", task, @@ -735,7 +706,7 @@ void transport_complete_task(struct se_task *task, int success) atomic_inc(&dev->depth_left); spin_lock_irqsave(&cmd->t_state_lock, flags); - atomic_set(&task->task_active, 0); + task->task_flags &= ~TF_ACTIVE; /* * See if any sense data exists, if so set the TASK_SENSE flag. @@ -754,68 +725,39 @@ void transport_complete_task(struct se_task *task, int success) * See if we are waiting for outstanding struct se_task * to complete for an exception condition */ - if (atomic_read(&task->task_stop)) { - /* - * Decrement cmd->t_se_count if this task had - * previously thrown its timeout exception handler. - */ - if (atomic_read(&task->task_timeout)) { - atomic_dec(&cmd->t_se_count); - atomic_set(&task->task_timeout, 0); - } + if (task->task_flags & TF_REQUEST_STOP) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); - complete(&task->task_stop_comp); return; } /* - * If the task's timeout handler has fired, use the t_task_cdbs_timeout - * left counter to determine when the struct se_cmd is ready to be queued to - * the processing thread. - */ - if (atomic_read(&task->task_timeout)) { - if (!atomic_dec_and_test( - &cmd->t_task_cdbs_timeout_left)) { - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - return; - } - t_state = TRANSPORT_COMPLETE_TIMEOUT; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - transport_add_cmd_to_queue(cmd, t_state); - return; - } - atomic_dec(&cmd->t_task_cdbs_timeout_left); - - /* * Decrement the outstanding t_task_cdbs_left count. The last * struct se_task from struct se_cmd will complete itself into the * device queue depending upon int success. */ if (!atomic_dec_and_test(&cmd->t_task_cdbs_left)) { - if (!success) - cmd->t_tasks_failed = 1; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); return; } if (!success || cmd->t_tasks_failed) { - t_state = TRANSPORT_COMPLETE_FAILURE; if (!task->task_error_status) { task->task_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; cmd->transport_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; } + INIT_WORK(&cmd->work, target_complete_failure_work); } else { atomic_set(&cmd->t_transport_complete, 1); - t_state = TRANSPORT_COMPLETE_OK; + INIT_WORK(&cmd->work, target_complete_ok_work); } + + cmd->t_state = TRANSPORT_COMPLETE; + atomic_set(&cmd->t_transport_active, 1); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - transport_add_cmd_to_queue(cmd, t_state); + queue_work(target_completion_wq, &cmd->work); } EXPORT_SYMBOL(transport_complete_task); @@ -902,14 +844,12 @@ static void __transport_add_task_to_execute_queue( static void transport_add_tasks_to_state_queue(struct se_cmd *cmd) { - struct se_device *dev; + struct se_device *dev = cmd->se_dev; struct se_task *task; unsigned long flags; spin_lock_irqsave(&cmd->t_state_lock, flags); list_for_each_entry(task, &cmd->t_task_list, t_list) { - dev = task->se_dev; - if (atomic_read(&task->task_state_active)) continue; @@ -934,38 +874,36 @@ static void transport_add_tasks_from_cmd(struct se_cmd *cmd) spin_lock_irqsave(&dev->execute_task_lock, flags); list_for_each_entry(task, &cmd->t_task_list, t_list) { - if (atomic_read(&task->task_execute_queue)) + if (!list_empty(&task->t_execute_list)) continue; /* * __transport_add_task_to_execute_queue() handles the * SAM Task Attribute emulation if enabled */ __transport_add_task_to_execute_queue(task, task_prev, dev); - atomic_set(&task->task_execute_queue, 1); task_prev = task; } spin_unlock_irqrestore(&dev->execute_task_lock, flags); } -/* transport_remove_task_from_execute_queue(): - * - * - */ +void __transport_remove_task_from_execute_queue(struct se_task *task, + struct se_device *dev) +{ + list_del_init(&task->t_execute_list); + atomic_dec(&dev->execute_tasks); +} + void transport_remove_task_from_execute_queue( struct se_task *task, struct se_device *dev) { unsigned long flags; - if (atomic_read(&task->task_execute_queue) == 0) { - dump_stack(); + if (WARN_ON(list_empty(&task->t_execute_list))) return; - } spin_lock_irqsave(&dev->execute_task_lock, flags); - list_del(&task->t_execute_list); - atomic_set(&task->task_execute_queue, 0); - atomic_dec(&dev->execute_tasks); + __transport_remove_task_from_execute_queue(task, dev); spin_unlock_irqrestore(&dev->execute_task_lock, flags); } @@ -991,14 +929,11 @@ static void target_qf_do_work(struct work_struct *work) pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, - (cmd->t_state == TRANSPORT_COMPLETE_OK) ? "COMPLETE_OK" : + (cmd->t_state == TRANSPORT_COMPLETE_QF_OK) ? "COMPLETE_OK" : (cmd->t_state == TRANSPORT_COMPLETE_QF_WP) ? "WRITE_PENDING" : "UNKNOWN"); - /* - * The SCF_EMULATE_QUEUE_FULL flag will be cleared once se_cmd - * has been added to head of queue - */ - transport_add_cmd_to_queue(cmd, cmd->t_state); + + transport_add_cmd_to_queue(cmd, cmd->t_state, true); } } @@ -1053,41 +988,6 @@ void transport_dump_dev_state( *bl += sprintf(b + *bl, " "); } -/* transport_release_all_cmds(): - * - * - */ -static void transport_release_all_cmds(struct se_device *dev) -{ - struct se_cmd *cmd, *tcmd; - int bug_out = 0, t_state; - unsigned long flags; - - spin_lock_irqsave(&dev->dev_queue_obj.cmd_queue_lock, flags); - list_for_each_entry_safe(cmd, tcmd, &dev->dev_queue_obj.qobj_list, - se_queue_node) { - t_state = cmd->t_state; - list_del(&cmd->se_queue_node); - spin_unlock_irqrestore(&dev->dev_queue_obj.cmd_queue_lock, - flags); - - pr_err("Releasing ITT: 0x%08x, i_state: %u," - " t_state: %u directly\n", - cmd->se_tfo->get_task_tag(cmd), - cmd->se_tfo->get_cmd_state(cmd), t_state); - - transport_release_fe_cmd(cmd); - bug_out = 1; - - spin_lock_irqsave(&dev->dev_queue_obj.cmd_queue_lock, flags); - } - spin_unlock_irqrestore(&dev->dev_queue_obj.cmd_queue_lock, flags); -#if 0 - if (bug_out) - BUG(); -#endif -} - void transport_dump_vpd_proto_id( struct t10_vpd *vpd, unsigned char *p_buf, @@ -1573,7 +1473,6 @@ transport_generic_get_task(struct se_cmd *cmd, INIT_LIST_HEAD(&task->t_state_list); init_completion(&task->task_stop_comp); task->task_se_cmd = cmd; - task->se_dev = dev; task->task_data_direction = data_direction; return task; @@ -1598,6 +1497,7 @@ void transport_init_se_cmd( INIT_LIST_HEAD(&cmd->se_delayed_node); INIT_LIST_HEAD(&cmd->se_ordered_node); INIT_LIST_HEAD(&cmd->se_qf_node); + INIT_LIST_HEAD(&cmd->se_queue_node); INIT_LIST_HEAD(&cmd->t_task_list); init_completion(&cmd->transport_lun_fe_stop_comp); @@ -1641,21 +1541,6 @@ static int transport_check_alloc_task_attr(struct se_cmd *cmd) return 0; } -void transport_free_se_cmd( - struct se_cmd *se_cmd) -{ - if (se_cmd->se_tmr_req) - core_tmr_release_req(se_cmd->se_tmr_req); - /* - * Check and free any extended CDB buffer that was allocated - */ - if (se_cmd->t_task_cdb != se_cmd->__t_task_cdb) - kfree(se_cmd->t_task_cdb); -} -EXPORT_SYMBOL(transport_free_se_cmd); - -static void transport_generic_wait_for_tasks(struct se_cmd *, int, int); - /* transport_generic_allocate_tasks(): * * Called from fabric RX Thread. @@ -1667,12 +1552,6 @@ int transport_generic_allocate_tasks( int ret; transport_generic_prepare_cdb(cdb); - - /* - * This is needed for early exceptions. - */ - cmd->transport_wait_for_tasks = &transport_generic_wait_for_tasks; - /* * Ensure that the received CDB is less than the max (252 + 8) bytes * for VARIABLE_LENGTH_CMD @@ -1730,26 +1609,6 @@ int transport_generic_allocate_tasks( EXPORT_SYMBOL(transport_generic_allocate_tasks); /* - * Used by fabric module frontends not defining a TFO->new_cmd_map() - * to queue up a newly setup se_cmd w/ TRANSPORT_NEW_CMD statis - */ -int transport_generic_handle_cdb( - struct se_cmd *cmd) -{ - if (!cmd->se_lun) { - dump_stack(); - pr_err("cmd->se_lun is NULL\n"); - return -EINVAL; - } - - transport_add_cmd_to_queue(cmd, TRANSPORT_NEW_CMD); - return 0; -} -EXPORT_SYMBOL(transport_generic_handle_cdb); - -static void transport_generic_request_failure(struct se_cmd *, - struct se_device *, int, int); -/* * Used by fabric module frontends to queue tasks directly. * Many only be used from process context only */ @@ -1773,7 +1632,7 @@ int transport_handle_cdb_direct( * Set TRANSPORT_NEW_CMD state and cmd->t_transport_active=1 following * transport_generic_handle_cdb*() -> transport_add_cmd_to_queue() * in existing usage to ensure that outstanding descriptors are handled - * correctly during shutdown via transport_generic_wait_for_tasks() + * correctly during shutdown via transport_wait_for_tasks() * * Also, we don't take cmd->t_state_lock here as we only expect * this to be called for initial descriptor submission. @@ -1790,7 +1649,7 @@ int transport_handle_cdb_direct( return 0; else if (ret < 0) { cmd->transport_error_status = ret; - transport_generic_request_failure(cmd, NULL, 0, + transport_generic_request_failure(cmd, 0, (cmd->data_direction != DMA_TO_DEVICE)); } return 0; @@ -1811,7 +1670,7 @@ int transport_generic_handle_cdb_map( return -EINVAL; } - transport_add_cmd_to_queue(cmd, TRANSPORT_NEW_CMD_MAP); + transport_add_cmd_to_queue(cmd, TRANSPORT_NEW_CMD_MAP, false); return 0; } EXPORT_SYMBOL(transport_generic_handle_cdb_map); @@ -1841,7 +1700,7 @@ int transport_generic_handle_data( if (transport_check_aborted_status(cmd, 1) != 0) return 0; - transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_WRITE); + transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_WRITE, false); return 0; } EXPORT_SYMBOL(transport_generic_handle_data); @@ -1853,12 +1712,7 @@ EXPORT_SYMBOL(transport_generic_handle_data); int transport_generic_handle_tmr( struct se_cmd *cmd) { - /* - * This is needed for early exceptions. - */ - cmd->transport_wait_for_tasks = &transport_generic_wait_for_tasks; - - transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_TMR); + transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_TMR, false); return 0; } EXPORT_SYMBOL(transport_generic_handle_tmr); @@ -1866,10 +1720,36 @@ EXPORT_SYMBOL(transport_generic_handle_tmr); void transport_generic_free_cmd_intr( struct se_cmd *cmd) { - transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR); + transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR, false); } EXPORT_SYMBOL(transport_generic_free_cmd_intr); +/* + * If the task is active, request it to be stopped and sleep until it + * has completed. + */ +bool target_stop_task(struct se_task *task, unsigned long *flags) +{ + struct se_cmd *cmd = task->task_se_cmd; + bool was_active = false; + + if (task->task_flags & TF_ACTIVE) { + task->task_flags |= TF_REQUEST_STOP; + spin_unlock_irqrestore(&cmd->t_state_lock, *flags); + + pr_debug("Task %p waiting to complete\n", task); + wait_for_completion(&task->task_stop_comp); + pr_debug("Task %p stopped successfully\n", task); + + spin_lock_irqsave(&cmd->t_state_lock, *flags); + atomic_dec(&cmd->t_task_cdbs_left); + task->task_flags &= ~(TF_ACTIVE | TF_REQUEST_STOP); + was_active = true; + } + + return was_active; +} + static int transport_stop_tasks_for_cmd(struct se_cmd *cmd) { struct se_task *task, *task_tmp; @@ -1885,51 +1765,26 @@ static int transport_stop_tasks_for_cmd(struct se_cmd *cmd) spin_lock_irqsave(&cmd->t_state_lock, flags); list_for_each_entry_safe(task, task_tmp, &cmd->t_task_list, t_list) { - pr_debug("task_no[%d] - Processing task %p\n", - task->task_no, task); + pr_debug("Processing task %p\n", task); /* * If the struct se_task has not been sent and is not active, * remove the struct se_task from the execution queue. */ - if (!atomic_read(&task->task_sent) && - !atomic_read(&task->task_active)) { + if (!(task->task_flags & (TF_ACTIVE | TF_SENT))) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); transport_remove_task_from_execute_queue(task, - task->se_dev); + cmd->se_dev); - pr_debug("task_no[%d] - Removed from execute queue\n", - task->task_no); + pr_debug("Task %p removed from execute queue\n", task); spin_lock_irqsave(&cmd->t_state_lock, flags); continue; } - /* - * If the struct se_task is active, sleep until it is returned - * from the plugin. - */ - if (atomic_read(&task->task_active)) { - atomic_set(&task->task_stop, 1); - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - - pr_debug("task_no[%d] - Waiting to complete\n", - task->task_no); - wait_for_completion(&task->task_stop_comp); - pr_debug("task_no[%d] - Stopped successfully\n", - task->task_no); - - spin_lock_irqsave(&cmd->t_state_lock, flags); - atomic_dec(&cmd->t_task_cdbs_left); - - atomic_set(&task->task_active, 0); - atomic_set(&task->task_stop, 0); - } else { - pr_debug("task_no[%d] - Did nothing\n", task->task_no); + if (!target_stop_task(task, &flags)) { + pr_debug("Task %p - did nothing\n", task); ret++; } - - __transport_stop_task_timer(task, &flags); } spin_unlock_irqrestore(&cmd->t_state_lock, flags); @@ -1941,7 +1796,6 @@ static int transport_stop_tasks_for_cmd(struct se_cmd *cmd) */ static void transport_generic_request_failure( struct se_cmd *cmd, - struct se_device *dev, int complete, int sc) { @@ -1950,10 +1804,9 @@ static void transport_generic_request_failure( pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08x" " CDB: 0x%02x\n", cmd, cmd->se_tfo->get_task_tag(cmd), cmd->t_task_cdb[0]); - pr_debug("-----[ i_state: %d t_state/def_t_state:" - " %d/%d transport_error_status: %d\n", + pr_debug("-----[ i_state: %d t_state: %d transport_error_status: %d\n", cmd->se_tfo->get_cmd_state(cmd), - cmd->t_state, cmd->deferred_t_state, + cmd->t_state, cmd->transport_error_status); pr_debug("-----[ t_tasks: %d t_task_cdbs_left: %d" " t_task_cdbs_sent: %d t_task_cdbs_ex_left: %d --" @@ -1966,10 +1819,6 @@ static void transport_generic_request_failure( atomic_read(&cmd->t_transport_stop), atomic_read(&cmd->t_transport_sent)); - transport_stop_all_task_timers(cmd); - - if (dev) - atomic_inc(&dev->depth_left); /* * For SAM Task Attribute emulation for failed struct se_cmd */ @@ -1977,7 +1826,6 @@ static void transport_generic_request_failure( transport_complete_task_attr(cmd); if (complete) { - transport_direct_request_timeout(cmd); cmd->transport_error_status = PYX_TRANSPORT_LU_COMM_FAILURE; } @@ -2076,46 +1924,8 @@ check_stop: return; queue_full: - cmd->t_state = TRANSPORT_COMPLETE_OK; - transport_handle_queue_full(cmd, cmd->se_dev, transport_complete_qf); -} - -static void transport_direct_request_timeout(struct se_cmd *cmd) -{ - unsigned long flags; - - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (!atomic_read(&cmd->t_transport_timeout)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } - if (atomic_read(&cmd->t_task_cdbs_timeout_left)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } - - atomic_sub(atomic_read(&cmd->t_transport_timeout), - &cmd->t_se_count); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); -} - -static void transport_generic_request_timeout(struct se_cmd *cmd) -{ - unsigned long flags; - - /* - * Reset cmd->t_se_count to allow transport_generic_remove() - * to allow last call to free memory resources. - */ - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (atomic_read(&cmd->t_transport_timeout) > 1) { - int tmp = (atomic_read(&cmd->t_transport_timeout) - 1); - - atomic_sub(tmp, &cmd->t_se_count); - } - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - transport_generic_remove(cmd, 0); + cmd->t_state = TRANSPORT_COMPLETE_QF_OK; + transport_handle_queue_full(cmd, cmd->se_dev); } static inline u32 transport_lba_21(unsigned char *cdb) @@ -2160,127 +1970,6 @@ static void transport_set_supported_SAM_opcode(struct se_cmd *se_cmd) spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); } -/* - * Called from interrupt context. - */ -static void transport_task_timeout_handler(unsigned long data) -{ - struct se_task *task = (struct se_task *)data; - struct se_cmd *cmd = task->task_se_cmd; - unsigned long flags; - - pr_debug("transport task timeout fired! task: %p cmd: %p\n", task, cmd); - - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (task->task_flags & TF_STOP) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } - task->task_flags &= ~TF_RUNNING; - - /* - * Determine if transport_complete_task() has already been called. - */ - if (!atomic_read(&task->task_active)) { - pr_debug("transport task: %p cmd: %p timeout task_active" - " == 0\n", task, cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } - - atomic_inc(&cmd->t_se_count); - atomic_inc(&cmd->t_transport_timeout); - cmd->t_tasks_failed = 1; - - atomic_set(&task->task_timeout, 1); - task->task_error_status = PYX_TRANSPORT_TASK_TIMEOUT; - task->task_scsi_status = 1; - - if (atomic_read(&task->task_stop)) { - pr_debug("transport task: %p cmd: %p timeout task_stop" - " == 1\n", task, cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - complete(&task->task_stop_comp); - return; - } - - if (!atomic_dec_and_test(&cmd->t_task_cdbs_left)) { - pr_debug("transport task: %p cmd: %p timeout non zero" - " t_task_cdbs_left\n", task, cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return; - } - pr_debug("transport task: %p cmd: %p timeout ZERO t_task_cdbs_left\n", - task, cmd); - - cmd->t_state = TRANSPORT_COMPLETE_FAILURE; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - transport_add_cmd_to_queue(cmd, TRANSPORT_COMPLETE_FAILURE); -} - -/* - * Called with cmd->t_state_lock held. - */ -static void transport_start_task_timer(struct se_task *task) -{ - struct se_device *dev = task->se_dev; - int timeout; - - if (task->task_flags & TF_RUNNING) - return; - /* - * If the task_timeout is disabled, exit now. - */ - timeout = dev->se_sub_dev->se_dev_attrib.task_timeout; - if (!timeout) - return; - - init_timer(&task->task_timer); - task->task_timer.expires = (get_jiffies_64() + timeout * HZ); - task->task_timer.data = (unsigned long) task; - task->task_timer.function = transport_task_timeout_handler; - - task->task_flags |= TF_RUNNING; - add_timer(&task->task_timer); -#if 0 - pr_debug("Starting task timer for cmd: %p task: %p seconds:" - " %d\n", task->task_se_cmd, task, timeout); -#endif -} - -/* - * Called with spin_lock_irq(&cmd->t_state_lock) held. - */ -void __transport_stop_task_timer(struct se_task *task, unsigned long *flags) -{ - struct se_cmd *cmd = task->task_se_cmd; - - if (!task->task_flags & TF_RUNNING) - return; - - task->task_flags |= TF_STOP; - spin_unlock_irqrestore(&cmd->t_state_lock, *flags); - - del_timer_sync(&task->task_timer); - - spin_lock_irqsave(&cmd->t_state_lock, *flags); - task->task_flags &= ~TF_RUNNING; - task->task_flags &= ~TF_STOP; -} - -static void transport_stop_all_task_timers(struct se_cmd *cmd) -{ - struct se_task *task = NULL, *task_tmp; - unsigned long flags; - - spin_lock_irqsave(&cmd->t_state_lock, flags); - list_for_each_entry_safe(task, task_tmp, - &cmd->t_task_list, t_list) - __transport_stop_task_timer(task, &flags); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); -} - static inline int transport_tcq_window_closed(struct se_device *dev) { if (dev->dev_tcq_window_closed++ < @@ -2385,7 +2074,7 @@ static int transport_execute_tasks(struct se_cmd *cmd) if (se_dev_check_online(cmd->se_orig_obj_ptr) != 0) { cmd->transport_error_status = PYX_TRANSPORT_LU_COMM_FAILURE; - transport_generic_request_failure(cmd, NULL, 0, 1); + transport_generic_request_failure(cmd, 0, 1); return 0; } @@ -2448,9 +2137,7 @@ check_depth: } task = list_first_entry(&dev->execute_task_list, struct se_task, t_execute_list); - list_del(&task->t_execute_list); - atomic_set(&task->task_execute_queue, 0); - atomic_dec(&dev->execute_tasks); + __transport_remove_task_from_execute_queue(task, dev); spin_unlock_irq(&dev->execute_task_lock); atomic_dec(&dev->depth_left); @@ -2458,15 +2145,13 @@ check_depth: cmd = task->task_se_cmd; spin_lock_irqsave(&cmd->t_state_lock, flags); - atomic_set(&task->task_active, 1); - atomic_set(&task->task_sent, 1); + task->task_flags |= (TF_ACTIVE | TF_SENT); atomic_inc(&cmd->t_task_cdbs_sent); if (atomic_read(&cmd->t_task_cdbs_sent) == cmd->t_task_list_num) - atomic_set(&cmd->transport_sent, 1); + atomic_set(&cmd->t_transport_sent, 1); - transport_start_task_timer(task); spin_unlock_irqrestore(&cmd->t_state_lock, flags); /* * The struct se_cmd->transport_emulate_cdb() function pointer is used @@ -2477,10 +2162,13 @@ check_depth: error = cmd->transport_emulate_cdb(cmd); if (error != 0) { cmd->transport_error_status = error; - atomic_set(&task->task_active, 0); - atomic_set(&cmd->transport_sent, 0); + spin_lock_irqsave(&cmd->t_state_lock, flags); + task->task_flags &= ~TF_ACTIVE; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + atomic_set(&cmd->t_transport_sent, 0); transport_stop_tasks_for_cmd(cmd); - transport_generic_request_failure(cmd, dev, 0, 1); + atomic_inc(&dev->depth_left); + transport_generic_request_failure(cmd, 0, 1); goto check_depth; } /* @@ -2513,10 +2201,13 @@ check_depth: if (error != 0) { cmd->transport_error_status = error; - atomic_set(&task->task_active, 0); - atomic_set(&cmd->transport_sent, 0); + spin_lock_irqsave(&cmd->t_state_lock, flags); + task->task_flags &= ~TF_ACTIVE; + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + atomic_set(&cmd->t_transport_sent, 0); transport_stop_tasks_for_cmd(cmd); - transport_generic_request_failure(cmd, dev, 0, 1); + atomic_inc(&dev->depth_left); + transport_generic_request_failure(cmd, 0, 1); } } @@ -2538,8 +2229,6 @@ void transport_new_cmd_failure(struct se_cmd *se_cmd) spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); } -static void transport_nop_wait_for_tasks(struct se_cmd *, int, int); - static inline u32 transport_get_sectors_6( unsigned char *cdb, struct se_cmd *cmd, @@ -2752,13 +2441,16 @@ out: static int transport_get_sense_data(struct se_cmd *cmd) { unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL; - struct se_device *dev; + struct se_device *dev = cmd->se_dev; struct se_task *task = NULL, *task_tmp; unsigned long flags; u32 offset = 0; WARN_ON(!cmd->se_lun); + if (!dev) + return 0; + spin_lock_irqsave(&cmd->t_state_lock, flags); if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); @@ -2767,14 +2459,9 @@ static int transport_get_sense_data(struct se_cmd *cmd) list_for_each_entry_safe(task, task_tmp, &cmd->t_task_list, t_list) { - if (!task->task_sense) continue; - dev = task->se_dev; - if (!dev) - continue; - if (!dev->transport->get_sense_buffer) { pr_err("dev->transport->get_sense_buffer" " is NULL\n"); @@ -2783,9 +2470,9 @@ static int transport_get_sense_data(struct se_cmd *cmd) sense_buffer = dev->transport->get_sense_buffer(task); if (!sense_buffer) { - pr_err("ITT[0x%08x]_TASK[%d]: Unable to locate" + pr_err("ITT[0x%08x]_TASK[%p]: Unable to locate" " sense buffer for task with sense\n", - cmd->se_tfo->get_task_tag(cmd), task->task_no); + cmd->se_tfo->get_task_tag(cmd), task); continue; } spin_unlock_irqrestore(&cmd->t_state_lock, flags); @@ -2814,7 +2501,6 @@ static int transport_get_sense_data(struct se_cmd *cmd) static int transport_handle_reservation_conflict(struct se_cmd *cmd) { - cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks; cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; cmd->se_cmd_flags |= SCF_SCSI_RESERVATION_CONFLICT; cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT; @@ -2915,8 +2601,6 @@ static int transport_generic_cmd_sequencer( * Check for an existing UNIT ATTENTION condition */ if (core_scsi3_ua_check(cmd, cdb) < 0) { - cmd->transport_wait_for_tasks = - &transport_nop_wait_for_tasks; cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; cmd->scsi_sense_reason = TCM_CHECK_CONDITION_UNIT_ATTENTION; return -EINVAL; @@ -2926,7 +2610,6 @@ static int transport_generic_cmd_sequencer( */ ret = su_dev->t10_alua.alua_state_check(cmd, cdb, &alua_ascq); if (ret != 0) { - cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks; /* * Set SCSI additional sense code (ASC) to 'LUN Not Accessible'; * The ALUA additional sense code qualifier (ASCQ) is determined @@ -2965,7 +2648,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_6; cmd->t_task_lba = transport_lba_21(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; break; @@ -2974,7 +2656,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_10; cmd->t_task_lba = transport_lba_32(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; break; @@ -2983,7 +2664,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_12; cmd->t_task_lba = transport_lba_32(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; break; @@ -2992,7 +2672,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_16; cmd->t_task_lba = transport_lba_64(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; break; @@ -3001,7 +2680,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_6; cmd->t_task_lba = transport_lba_21(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; break; @@ -3010,7 +2688,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_10; cmd->t_task_lba = transport_lba_32(cdb); cmd->t_tasks_fua = (cdb[1] & 0x8); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; @@ -3020,7 +2697,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_12; cmd->t_task_lba = transport_lba_32(cdb); cmd->t_tasks_fua = (cdb[1] & 0x8); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; @@ -3030,7 +2706,6 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_16; cmd->t_task_lba = transport_lba_64(cdb); cmd->t_tasks_fua = (cdb[1] & 0x8); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; @@ -3043,18 +2718,14 @@ static int transport_generic_cmd_sequencer( if (sector_ret) goto out_unsupported_cdb; size = transport_get_size(sectors, cdb, cmd); - cmd->transport_split_cdb = &split_cdb_XX_10; cmd->t_task_lba = transport_lba_32(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; - passthrough = (dev->transport->transport_type == - TRANSPORT_PLUGIN_PHBA_PDEV); - /* - * Skip the remaining assignments for TCM/PSCSI passthrough - */ - if (passthrough) - break; + + if (dev->transport->transport_type == + TRANSPORT_PLUGIN_PHBA_PDEV) + goto out_unsupported_cdb; /* - * Setup BIDI XOR callback to be run during transport_generic_complete_ok() + * Setup BIDI XOR callback to be run after I/O completion. */ cmd->transport_complete_callback = &transport_xor_callback; cmd->t_tasks_fua = (cdb[1] & 0x8); @@ -3078,19 +2749,14 @@ static int transport_generic_cmd_sequencer( * Use WRITE_32 and READ_32 opcodes for the emulated * XDWRITE_READ_32 logic. */ - cmd->transport_split_cdb = &split_cdb_XX_32; cmd->t_task_lba = transport_lba_64_ext(cdb); cmd->se_cmd_flags |= SCF_SCSI_DATA_SG_IO_CDB; - /* - * Skip the remaining assignments for TCM/PSCSI passthrough - */ if (passthrough) - break; - + goto out_unsupported_cdb; /* - * Setup BIDI XOR callback to be run during - * transport_generic_complete_ok() + * Setup BIDI XOR callback to be run during after I/O + * completion. */ cmd->transport_complete_callback = &transport_xor_callback; cmd->t_tasks_fua = (cdb[10] & 0x8); @@ -3430,7 +3096,6 @@ static int transport_generic_cmd_sequencer( pr_warn("TARGET_CORE[%s]: Unsupported SCSI Opcode" " 0x%02x, sending CHECK_CONDITION.\n", cmd->se_tfo->get_fabric_name(), cdb[0]); - cmd->transport_wait_for_tasks = &transport_nop_wait_for_tasks; goto out_unsupported_cdb; } @@ -3488,8 +3153,7 @@ out_invalid_cdb_field: } /* - * Called from transport_generic_complete_ok() and - * transport_generic_request_failure() to determine which dormant/delayed + * Called from I/O completion to determine which dormant/delayed * and ordered cmds need to have their tasks added to the execution queue. */ static void transport_complete_task_attr(struct se_cmd *cmd) @@ -3557,12 +3221,18 @@ static void transport_complete_task_attr(struct se_cmd *cmd) wake_up_interruptible(&dev->dev_queue_obj.thread_wq); } -static int transport_complete_qf(struct se_cmd *cmd) +static void transport_complete_qf(struct se_cmd *cmd) { int ret = 0; - if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) - return cmd->se_tfo->queue_status(cmd); + if (cmd->se_dev->dev_task_attr_type == SAM_TASK_ATTR_EMULATED) + transport_complete_task_attr(cmd); + + if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { + ret = cmd->se_tfo->queue_status(cmd); + if (ret) + goto out; + } switch (cmd->data_direction) { case DMA_FROM_DEVICE: @@ -3572,7 +3242,7 @@ static int transport_complete_qf(struct se_cmd *cmd) if (cmd->t_bidi_data_sg) { ret = cmd->se_tfo->queue_data_in(cmd); if (ret < 0) - return ret; + break; } /* Fall through for DMA_TO_DEVICE */ case DMA_NONE: @@ -3582,17 +3252,20 @@ static int transport_complete_qf(struct se_cmd *cmd) break; } - return ret; +out: + if (ret < 0) { + transport_handle_queue_full(cmd, cmd->se_dev); + return; + } + transport_lun_remove_cmd(cmd); + transport_cmd_check_stop_to_fabric(cmd); } static void transport_handle_queue_full( struct se_cmd *cmd, - struct se_device *dev, - int (*qf_callback)(struct se_cmd *)) + struct se_device *dev) { spin_lock_irq(&dev->qf_cmd_lock); - cmd->se_cmd_flags |= SCF_EMULATE_QUEUE_FULL; - cmd->transport_qf_callback = qf_callback; list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); atomic_inc(&dev->dev_qf_count); smp_mb__after_atomic_inc(); @@ -3601,9 +3274,11 @@ static void transport_handle_queue_full( schedule_work(&cmd->se_dev->qf_work_queue); } -static void transport_generic_complete_ok(struct se_cmd *cmd) +static void target_complete_ok_work(struct work_struct *work) { + struct se_cmd *cmd = container_of(work, struct se_cmd, work); int reason = 0, ret; + /* * Check if we need to move delayed/dormant tasks from cmds on the * delayed execution list after a HEAD_OF_QUEUE or ORDERED Task @@ -3618,14 +3293,6 @@ static void transport_generic_complete_ok(struct se_cmd *cmd) if (atomic_read(&cmd->se_dev->dev_qf_count) != 0) schedule_work(&cmd->se_dev->qf_work_queue); - if (cmd->transport_qf_callback) { - ret = cmd->transport_qf_callback(cmd); - if (ret < 0) - goto queue_full; - - cmd->transport_qf_callback = NULL; - goto done; - } /* * Check if we need to retrieve a sense buffer from * the struct se_cmd in question. @@ -3701,7 +3368,6 @@ static void transport_generic_complete_ok(struct se_cmd *cmd) break; } -done: transport_lun_remove_cmd(cmd); transport_cmd_check_stop_to_fabric(cmd); return; @@ -3709,34 +3375,35 @@ done: queue_full: pr_debug("Handling complete_ok QUEUE_FULL: se_cmd: %p," " data_direction: %d\n", cmd, cmd->data_direction); - transport_handle_queue_full(cmd, cmd->se_dev, transport_complete_qf); + cmd->t_state = TRANSPORT_COMPLETE_QF_OK; + transport_handle_queue_full(cmd, cmd->se_dev); } static void transport_free_dev_tasks(struct se_cmd *cmd) { struct se_task *task, *task_tmp; unsigned long flags; + LIST_HEAD(dispose_list); spin_lock_irqsave(&cmd->t_state_lock, flags); list_for_each_entry_safe(task, task_tmp, &cmd->t_task_list, t_list) { - if (atomic_read(&task->task_active)) - continue; + if (!(task->task_flags & TF_ACTIVE)) + list_move_tail(&task->t_list, &dispose_list); + } + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + while (!list_empty(&dispose_list)) { + task = list_first_entry(&dispose_list, struct se_task, t_list); - kfree(task->task_sg_bidi); - kfree(task->task_sg); + if (task->task_sg != cmd->t_data_sg && + task->task_sg != cmd->t_bidi_data_sg) + kfree(task->task_sg); list_del(&task->t_list); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (task->se_dev) - task->se_dev->transport->free_task(task); - else - pr_err("task[%u] - task->se_dev is NULL\n", - task->task_no); - spin_lock_irqsave(&cmd->t_state_lock, flags); + cmd->se_dev->transport->free_task(task); } - spin_unlock_irqrestore(&cmd->t_state_lock, flags); } static inline void transport_free_sgl(struct scatterlist *sgl, int nents) @@ -3764,89 +3431,43 @@ static inline void transport_free_pages(struct se_cmd *cmd) cmd->t_bidi_data_nents = 0; } -static inline void transport_release_tasks(struct se_cmd *cmd) -{ - transport_free_dev_tasks(cmd); -} - -static inline int transport_dec_and_check(struct se_cmd *cmd) +/** + * transport_put_cmd - release a reference to a command + * @cmd: command to release + * + * This routine releases our reference to the command and frees it if possible. + */ +static void transport_put_cmd(struct se_cmd *cmd) { unsigned long flags; + int free_tasks = 0; spin_lock_irqsave(&cmd->t_state_lock, flags); if (atomic_read(&cmd->t_fe_count)) { - if (!atomic_dec_and_test(&cmd->t_fe_count)) { - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - return 1; - } + if (!atomic_dec_and_test(&cmd->t_fe_count)) + goto out_busy; } if (atomic_read(&cmd->t_se_count)) { - if (!atomic_dec_and_test(&cmd->t_se_count)) { - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - return 1; - } + if (!atomic_dec_and_test(&cmd->t_se_count)) + goto out_busy; } - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - return 0; -} - -static void transport_release_fe_cmd(struct se_cmd *cmd) -{ - unsigned long flags; - - if (transport_dec_and_check(cmd)) - return; - - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (!atomic_read(&cmd->transport_dev_active)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - goto free_pages; - } - atomic_set(&cmd->transport_dev_active, 0); - transport_all_task_dev_remove_state(cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - transport_release_tasks(cmd); -free_pages: - transport_free_pages(cmd); - transport_free_se_cmd(cmd); - cmd->se_tfo->release_cmd(cmd); -} - -static int -transport_generic_remove(struct se_cmd *cmd, int session_reinstatement) -{ - unsigned long flags; - - if (transport_dec_and_check(cmd)) { - if (session_reinstatement) { - spin_lock_irqsave(&cmd->t_state_lock, flags); - transport_all_task_dev_remove_state(cmd); - spin_unlock_irqrestore(&cmd->t_state_lock, - flags); - } - return 1; - } - - spin_lock_irqsave(&cmd->t_state_lock, flags); - if (!atomic_read(&cmd->transport_dev_active)) { - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - goto free_pages; + if (atomic_read(&cmd->transport_dev_active)) { + atomic_set(&cmd->transport_dev_active, 0); + transport_all_task_dev_remove_state(cmd); + free_tasks = 1; } - atomic_set(&cmd->transport_dev_active, 0); - transport_all_task_dev_remove_state(cmd); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - transport_release_tasks(cmd); + if (free_tasks != 0) + transport_free_dev_tasks(cmd); -free_pages: transport_free_pages(cmd); transport_release_cmd(cmd); - return 0; + return; +out_busy: + spin_unlock_irqrestore(&cmd->t_state_lock, flags); } /* @@ -3888,62 +3509,6 @@ int transport_generic_map_mem_to_cmd( } EXPORT_SYMBOL(transport_generic_map_mem_to_cmd); -static int transport_new_cmd_obj(struct se_cmd *cmd) -{ - struct se_device *dev = cmd->se_dev; - int set_counts = 1, rc, task_cdbs; - - /* - * Setup any BIDI READ tasks and memory from - * cmd->t_mem_bidi_list so the READ struct se_tasks - * are queued first for the non pSCSI passthrough case. - */ - if (cmd->t_bidi_data_sg && - (dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV)) { - rc = transport_allocate_tasks(cmd, - cmd->t_task_lba, - DMA_FROM_DEVICE, - cmd->t_bidi_data_sg, - cmd->t_bidi_data_nents); - if (rc <= 0) { - cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; - cmd->scsi_sense_reason = - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - return -EINVAL; - } - atomic_inc(&cmd->t_fe_count); - atomic_inc(&cmd->t_se_count); - set_counts = 0; - } - /* - * Setup the tasks and memory from cmd->t_mem_list - * Note for BIDI transfers this will contain the WRITE payload - */ - task_cdbs = transport_allocate_tasks(cmd, - cmd->t_task_lba, - cmd->data_direction, - cmd->t_data_sg, - cmd->t_data_nents); - if (task_cdbs <= 0) { - cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; - cmd->scsi_sense_reason = - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - return -EINVAL; - } - - if (set_counts) { - atomic_inc(&cmd->t_fe_count); - atomic_inc(&cmd->t_se_count); - } - - cmd->t_task_list_num = task_cdbs; - - atomic_set(&cmd->t_task_cdbs_left, task_cdbs); - atomic_set(&cmd->t_task_cdbs_ex_left, task_cdbs); - atomic_set(&cmd->t_task_cdbs_timeout_left, task_cdbs); - return 0; -} - void *transport_kmap_first_data_page(struct se_cmd *cmd) { struct scatterlist *sg = cmd->t_data_sg; @@ -4054,15 +3619,13 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) /* * For the padded tasks, use the extra SGL vector allocated * in transport_allocate_data_tasks() for the sg_prev_nents - * offset into sg_chain() above.. The last task of a - * multi-task list, or a single task will not have - * task->task_sg_padded set.. + * offset into sg_chain() above. + * + * We do not need the padding for the last task (or a single + * task), but in that case we will never use the sg_prev_nents + * value below which would be incorrect. */ - if (task->task_padded_sg) - sg_prev_nents = (task->task_sg_nents + 1); - else - sg_prev_nents = task->task_sg_nents; - + sg_prev_nents = (task->task_sg_nents + 1); sg_prev = task->task_sg; } /* @@ -4092,30 +3655,60 @@ EXPORT_SYMBOL(transport_do_task_sg_chain); /* * Break up cmd into chunks transport can handle */ -static int transport_allocate_data_tasks( - struct se_cmd *cmd, - unsigned long long lba, +static int +transport_allocate_data_tasks(struct se_cmd *cmd, enum dma_data_direction data_direction, - struct scatterlist *sgl, - unsigned int sgl_nents) + struct scatterlist *cmd_sg, unsigned int sgl_nents) { - unsigned char *cdb = NULL; - struct se_task *task; struct se_device *dev = cmd->se_dev; - unsigned long flags; - int task_count, i, ret; - sector_t sectors, dev_max_sectors = dev->se_sub_dev->se_dev_attrib.max_sectors; - u32 sector_size = dev->se_sub_dev->se_dev_attrib.block_size; - struct scatterlist *sg; - struct scatterlist *cmd_sg; + int task_count, i; + unsigned long long lba; + sector_t sectors, dev_max_sectors; + u32 sector_size; + + if (transport_cmd_get_valid_sectors(cmd) < 0) + return -EINVAL; + + dev_max_sectors = dev->se_sub_dev->se_dev_attrib.max_sectors; + sector_size = dev->se_sub_dev->se_dev_attrib.block_size; WARN_ON(cmd->data_length % sector_size); + + lba = cmd->t_task_lba; sectors = DIV_ROUND_UP(cmd->data_length, sector_size); task_count = DIV_ROUND_UP_SECTOR_T(sectors, dev_max_sectors); - - cmd_sg = sgl; + + /* + * If we need just a single task reuse the SG list in the command + * and avoid a lot of work. + */ + if (task_count == 1) { + struct se_task *task; + unsigned long flags; + + task = transport_generic_get_task(cmd, data_direction); + if (!task) + return -ENOMEM; + + task->task_sg = cmd_sg; + task->task_sg_nents = sgl_nents; + + task->task_lba = lba; + task->task_sectors = sectors; + task->task_size = task->task_sectors * sector_size; + + spin_lock_irqsave(&cmd->t_state_lock, flags); + list_add_tail(&task->t_list, &cmd->t_task_list); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + + return task_count; + } + for (i = 0; i < task_count; i++) { + struct se_task *task; unsigned int task_size, task_sg_nents_padded; + struct scatterlist *sg; + unsigned long flags; int count; task = transport_generic_get_task(cmd, data_direction); @@ -4126,14 +3719,6 @@ static int transport_allocate_data_tasks( task->task_sectors = min(sectors, dev_max_sectors); task->task_size = task->task_sectors * sector_size; - cdb = dev->transport->get_cdb(task); - BUG_ON(!cdb); - - memcpy(cdb, cmd->t_task_cdb, - scsi_command_size(cmd->t_task_cdb)); - - /* Update new cdb with updated lba/sectors */ - cmd->transport_split_cdb(task->task_lba, task->task_sectors, cdb); /* * This now assumes that passed sg_ents are in PAGE_SIZE chunks * in order to calculate the number per task SGL entries @@ -4149,7 +3734,6 @@ static int transport_allocate_data_tasks( */ if (cmd->se_tfo->task_sg_chaining && (i < (task_count - 1))) { task_sg_nents_padded = (task->task_sg_nents + 1); - task->task_padded_sg = 1; } else task_sg_nents_padded = task->task_sg_nents; @@ -4181,20 +3765,6 @@ static int transport_allocate_data_tasks( list_add_tail(&task->t_list, &cmd->t_task_list); spin_unlock_irqrestore(&cmd->t_state_lock, flags); } - /* - * Now perform the memory map of task->task_sg[] into backend - * subsystem memory.. - */ - list_for_each_entry(task, &cmd->t_task_list, t_list) { - if (atomic_read(&task->task_sent)) - continue; - if (!dev->transport->map_data_SG) - continue; - - ret = dev->transport->map_data_SG(task); - if (ret < 0) - return 0; - } return task_count; } @@ -4202,30 +3772,14 @@ static int transport_allocate_data_tasks( static int transport_allocate_control_task(struct se_cmd *cmd) { - struct se_device *dev = cmd->se_dev; - unsigned char *cdb; struct se_task *task; unsigned long flags; - int ret = 0; task = transport_generic_get_task(cmd, cmd->data_direction); if (!task) return -ENOMEM; - cdb = dev->transport->get_cdb(task); - BUG_ON(!cdb); - memcpy(cdb, cmd->t_task_cdb, - scsi_command_size(cmd->t_task_cdb)); - - task->task_sg = kmalloc(sizeof(struct scatterlist) * cmd->t_data_nents, - GFP_KERNEL); - if (!task->task_sg) { - cmd->se_dev->transport->free_task(task); - return -ENOMEM; - } - - memcpy(task->task_sg, cmd->t_data_sg, - sizeof(struct scatterlist) * cmd->t_data_nents); + task->task_sg = cmd->t_data_sg; task->task_size = cmd->data_length; task->task_sg_nents = cmd->t_data_nents; @@ -4233,53 +3787,20 @@ transport_allocate_control_task(struct se_cmd *cmd) list_add_tail(&task->t_list, &cmd->t_task_list); spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB) { - if (dev->transport->map_control_SG) - ret = dev->transport->map_control_SG(task); - } else if (cmd->se_cmd_flags & SCF_SCSI_NON_DATA_CDB) { - if (dev->transport->cdb_none) - ret = dev->transport->cdb_none(task); - } else { - pr_err("target: Unknown control cmd type!\n"); - BUG(); - } - /* Success! Return number of tasks allocated */ - if (ret == 0) - return 1; - return ret; -} - -static u32 transport_allocate_tasks( - struct se_cmd *cmd, - unsigned long long lba, - enum dma_data_direction data_direction, - struct scatterlist *sgl, - unsigned int sgl_nents) -{ - if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { - if (transport_cmd_get_valid_sectors(cmd) < 0) - return -EINVAL; - - return transport_allocate_data_tasks(cmd, lba, data_direction, - sgl, sgl_nents); - } else - return transport_allocate_control_task(cmd); - + return 1; } - -/* transport_generic_new_cmd(): Called from transport_processing_thread() - * - * Allocate storage transport resources from a set of values predefined - * by transport_generic_cmd_sequencer() from the iSCSI Target RX process. - * Any non zero return here is treated as an "out of resource' op here. +/* + * Allocate any required ressources to execute the command, and either place + * it on the execution queue if possible. For writes we might not have the + * payload yet, thus notify the fabric via a call to ->write_pending instead. */ - /* - * Generate struct se_task(s) and/or their payloads for this CDB. - */ int transport_generic_new_cmd(struct se_cmd *cmd) { + struct se_device *dev = cmd->se_dev; + int task_cdbs, task_cdbs_bidi = 0; + int set_counts = 1; int ret = 0; /* @@ -4293,16 +3814,45 @@ int transport_generic_new_cmd(struct se_cmd *cmd) if (ret < 0) return ret; } + /* - * Call transport_new_cmd_obj() to invoke transport_allocate_tasks() for - * control or data CDB types, and perform the map to backend subsystem - * code from SGL memory allocated here by transport_generic_get_mem(), or - * via pre-existing SGL memory setup explictly by fabric module code with - * transport_generic_map_mem_to_cmd(). + * For BIDI command set up the read tasks first. */ - ret = transport_new_cmd_obj(cmd); - if (ret < 0) - return ret; + if (cmd->t_bidi_data_sg && + dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) { + BUG_ON(!(cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)); + + task_cdbs_bidi = transport_allocate_data_tasks(cmd, + DMA_FROM_DEVICE, cmd->t_bidi_data_sg, + cmd->t_bidi_data_nents); + if (task_cdbs_bidi <= 0) + goto out_fail; + + atomic_inc(&cmd->t_fe_count); + atomic_inc(&cmd->t_se_count); + set_counts = 0; + } + + if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) { + task_cdbs = transport_allocate_data_tasks(cmd, + cmd->data_direction, cmd->t_data_sg, + cmd->t_data_nents); + } else { + task_cdbs = transport_allocate_control_task(cmd); + } + + if (task_cdbs <= 0) + goto out_fail; + + if (set_counts) { + atomic_inc(&cmd->t_fe_count); + atomic_inc(&cmd->t_se_count); + } + + cmd->t_task_list_num = (task_cdbs + task_cdbs_bidi); + atomic_set(&cmd->t_task_cdbs_left, cmd->t_task_list_num); + atomic_set(&cmd->t_task_cdbs_ex_left, cmd->t_task_list_num); + /* * For WRITEs, let the fabric know its buffer is ready.. * This WRITE struct se_cmd (and all of its associated struct se_task's) @@ -4320,6 +3870,11 @@ int transport_generic_new_cmd(struct se_cmd *cmd) */ transport_execute_tasks(cmd); return 0; + +out_fail: + cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; + cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + return -EINVAL; } EXPORT_SYMBOL(transport_generic_new_cmd); @@ -4333,15 +3888,15 @@ void transport_generic_process_write(struct se_cmd *cmd) } EXPORT_SYMBOL(transport_generic_process_write); -static int transport_write_pending_qf(struct se_cmd *cmd) +static void transport_write_pending_qf(struct se_cmd *cmd) { - return cmd->se_tfo->write_pending(cmd); + if (cmd->se_tfo->write_pending(cmd) == -EAGAIN) { + pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", + cmd); + transport_handle_queue_full(cmd, cmd->se_dev); + } } -/* transport_generic_write_pending(): - * - * - */ static int transport_generic_write_pending(struct se_cmd *cmd) { unsigned long flags; @@ -4351,17 +3906,6 @@ static int transport_generic_write_pending(struct se_cmd *cmd) cmd->t_state = TRANSPORT_WRITE_PENDING; spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (cmd->transport_qf_callback) { - ret = cmd->transport_qf_callback(cmd); - if (ret == -EAGAIN) - goto queue_full; - else if (ret < 0) - return ret; - - cmd->transport_qf_callback = NULL; - return 0; - } - /* * Clear the se_cmd for WRITE_PENDING status in order to set * cmd->t_transport_active=0 so that transport_generic_handle_data @@ -4386,61 +3930,52 @@ static int transport_generic_write_pending(struct se_cmd *cmd) queue_full: pr_debug("Handling write_pending QUEUE__FULL: se_cmd: %p\n", cmd); cmd->t_state = TRANSPORT_COMPLETE_QF_WP; - transport_handle_queue_full(cmd, cmd->se_dev, - transport_write_pending_qf); + transport_handle_queue_full(cmd, cmd->se_dev); return ret; } +/** + * transport_release_cmd - free a command + * @cmd: command to free + * + * This routine unconditionally frees a command, and reference counting + * or list removal must be done in the caller. + */ void transport_release_cmd(struct se_cmd *cmd) { BUG_ON(!cmd->se_tfo); - transport_free_se_cmd(cmd); + if (cmd->se_tmr_req) + core_tmr_release_req(cmd->se_tmr_req); + if (cmd->t_task_cdb != cmd->__t_task_cdb) + kfree(cmd->t_task_cdb); cmd->se_tfo->release_cmd(cmd); } EXPORT_SYMBOL(transport_release_cmd); -/* transport_generic_free_cmd(): - * - * Called from processing frontend to release storage engine resources - */ -void transport_generic_free_cmd( - struct se_cmd *cmd, - int wait_for_tasks, - int session_reinstatement) +void transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) { - if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) + if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) { + if (wait_for_tasks && cmd->se_tmr_req) + transport_wait_for_tasks(cmd); + transport_release_cmd(cmd); - else { + } else { + if (wait_for_tasks) + transport_wait_for_tasks(cmd); + core_dec_lacl_count(cmd->se_sess->se_node_acl, cmd); - if (cmd->se_lun) { -#if 0 - pr_debug("cmd: %p ITT: 0x%08x contains" - " cmd->se_lun\n", cmd, - cmd->se_tfo->get_task_tag(cmd)); -#endif + if (cmd->se_lun) transport_lun_remove_cmd(cmd); - } - - if (wait_for_tasks && cmd->transport_wait_for_tasks) - cmd->transport_wait_for_tasks(cmd, 0, 0); transport_free_dev_tasks(cmd); - transport_generic_remove(cmd, session_reinstatement); + transport_put_cmd(cmd); } } EXPORT_SYMBOL(transport_generic_free_cmd); -static void transport_nop_wait_for_tasks( - struct se_cmd *cmd, - int remove_cmd, - int session_reinstatement) -{ - return; -} - /* transport_lun_wait_for_tasks(): * * Called from ConfigFS context to stop the passed struct se_cmd to allow @@ -4479,7 +4014,7 @@ static int transport_lun_wait_for_tasks(struct se_cmd *cmd, struct se_lun *lun) pr_debug("ConfigFS: ITT[0x%08x] - stopped cmd....\n", cmd->se_tfo->get_task_tag(cmd)); } - transport_remove_cmd_from_queue(cmd, &cmd->se_dev->dev_queue_obj); + transport_remove_cmd_from_queue(cmd); return 0; } @@ -4610,22 +4145,30 @@ int transport_clear_lun_from_sessions(struct se_lun *lun) return 0; } -/* transport_generic_wait_for_tasks(): +/** + * transport_wait_for_tasks - wait for completion to occur + * @cmd: command to wait * - * Called from frontend or passthrough context to wait for storage engine - * to pause and/or release frontend generated struct se_cmd. + * Called from frontend fabric context to wait for storage engine + * to pause and/or release frontend generated struct se_cmd. */ -static void transport_generic_wait_for_tasks( - struct se_cmd *cmd, - int remove_cmd, - int session_reinstatement) +void transport_wait_for_tasks(struct se_cmd *cmd) { unsigned long flags; - if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) && !(cmd->se_tmr_req)) - return; - spin_lock_irqsave(&cmd->t_state_lock, flags); + if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) && !(cmd->se_tmr_req)) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + return; + } + /* + * Only perform a possible wait_for_tasks if SCF_SUPPORTED_SAM_OPCODE + * has been set in transport_set_supported_SAM_opcode(). + */ + if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) && !cmd->se_tmr_req) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + return; + } /* * If we are already stopped due to an external event (ie: LUN shutdown) * sleep until the connection can have the passed struct se_cmd back. @@ -4665,16 +4208,17 @@ static void transport_generic_wait_for_tasks( atomic_set(&cmd->transport_lun_stop, 0); } if (!atomic_read(&cmd->t_transport_active) || - atomic_read(&cmd->t_transport_aborted)) - goto remove; + atomic_read(&cmd->t_transport_aborted)) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + return; + } atomic_set(&cmd->t_transport_stop, 1); pr_debug("wait_for_tasks: Stopping %p ITT: 0x%08x" - " i_state: %d, t_state/def_t_state: %d/%d, t_transport_stop" - " = TRUE\n", cmd, cmd->se_tfo->get_task_tag(cmd), - cmd->se_tfo->get_cmd_state(cmd), cmd->t_state, - cmd->deferred_t_state); + " i_state: %d, t_state: %d, t_transport_stop = TRUE\n", + cmd, cmd->se_tfo->get_task_tag(cmd), + cmd->se_tfo->get_cmd_state(cmd), cmd->t_state); spin_unlock_irqrestore(&cmd->t_state_lock, flags); @@ -4689,13 +4233,10 @@ static void transport_generic_wait_for_tasks( pr_debug("wait_for_tasks: Stopped wait_for_compltion(" "&cmd->t_transport_stop_comp) for ITT: 0x%08x\n", cmd->se_tfo->get_task_tag(cmd)); -remove: - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - if (!remove_cmd) - return; - transport_generic_free_cmd(cmd, 0, session_reinstatement); + spin_unlock_irqrestore(&cmd->t_state_lock, flags); } +EXPORT_SYMBOL(transport_wait_for_tasks); static int transport_get_sense_codes( struct se_cmd *cmd, @@ -4920,6 +4461,15 @@ EXPORT_SYMBOL(transport_check_aborted_status); void transport_send_task_abort(struct se_cmd *cmd) { + unsigned long flags; + + spin_lock_irqsave(&cmd->t_state_lock, flags); + if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + return; + } + spin_unlock_irqrestore(&cmd->t_state_lock, flags); + /* * If there are still expected incoming fabric WRITEs, we wait * until until they have completed before sending a TASK_ABORTED @@ -4984,184 +4534,10 @@ int transport_generic_do_tmr(struct se_cmd *cmd) cmd->t_state = TRANSPORT_ISTATE_PROCESSING; cmd->se_tfo->queue_tm_rsp(cmd); - transport_cmd_check_stop(cmd, 2, 0); + transport_cmd_check_stop_to_fabric(cmd); return 0; } -/* - * Called with spin_lock_irq(&dev->execute_task_lock); held - * - */ -static struct se_task * -transport_get_task_from_state_list(struct se_device *dev) -{ - struct se_task *task; - - if (list_empty(&dev->state_task_list)) - return NULL; - - list_for_each_entry(task, &dev->state_task_list, t_state_list) - break; - - list_del(&task->t_state_list); - atomic_set(&task->task_state_active, 0); - - return task; -} - -static void transport_processing_shutdown(struct se_device *dev) -{ - struct se_cmd *cmd; - struct se_task *task; - unsigned long flags; - /* - * Empty the struct se_device's struct se_task state list. - */ - spin_lock_irqsave(&dev->execute_task_lock, flags); - while ((task = transport_get_task_from_state_list(dev))) { - if (!task->task_se_cmd) { - pr_err("task->task_se_cmd is NULL!\n"); - continue; - } - cmd = task->task_se_cmd; - - spin_unlock_irqrestore(&dev->execute_task_lock, flags); - - spin_lock_irqsave(&cmd->t_state_lock, flags); - - pr_debug("PT: cmd: %p task: %p ITT: 0x%08x," - " i_state: %d, t_state/def_t_state:" - " %d/%d cdb: 0x%02x\n", cmd, task, - cmd->se_tfo->get_task_tag(cmd), - cmd->se_tfo->get_cmd_state(cmd), - cmd->t_state, cmd->deferred_t_state, - cmd->t_task_cdb[0]); - pr_debug("PT: ITT[0x%08x] - t_tasks: %d t_task_cdbs_left:" - " %d t_task_cdbs_sent: %d -- t_transport_active: %d" - " t_transport_stop: %d t_transport_sent: %d\n", - cmd->se_tfo->get_task_tag(cmd), - cmd->t_task_list_num, - atomic_read(&cmd->t_task_cdbs_left), - atomic_read(&cmd->t_task_cdbs_sent), - atomic_read(&cmd->t_transport_active), - atomic_read(&cmd->t_transport_stop), - atomic_read(&cmd->t_transport_sent)); - - if (atomic_read(&task->task_active)) { - atomic_set(&task->task_stop, 1); - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - - pr_debug("Waiting for task: %p to shutdown for dev:" - " %p\n", task, dev); - wait_for_completion(&task->task_stop_comp); - pr_debug("Completed task: %p shutdown for dev: %p\n", - task, dev); - - spin_lock_irqsave(&cmd->t_state_lock, flags); - atomic_dec(&cmd->t_task_cdbs_left); - - atomic_set(&task->task_active, 0); - atomic_set(&task->task_stop, 0); - } else { - if (atomic_read(&task->task_execute_queue) != 0) - transport_remove_task_from_execute_queue(task, dev); - } - __transport_stop_task_timer(task, &flags); - - if (!atomic_dec_and_test(&cmd->t_task_cdbs_ex_left)) { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - - pr_debug("Skipping task: %p, dev: %p for" - " t_task_cdbs_ex_left: %d\n", task, dev, - atomic_read(&cmd->t_task_cdbs_ex_left)); - - spin_lock_irqsave(&dev->execute_task_lock, flags); - continue; - } - - if (atomic_read(&cmd->t_transport_active)) { - pr_debug("got t_transport_active = 1 for task: %p, dev:" - " %p\n", task, dev); - - if (atomic_read(&cmd->t_fe_count)) { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - transport_send_check_condition_and_sense( - cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, - 0); - transport_remove_cmd_from_queue(cmd, - &cmd->se_dev->dev_queue_obj); - - transport_lun_remove_cmd(cmd); - transport_cmd_check_stop(cmd, 1, 0); - } else { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - - transport_remove_cmd_from_queue(cmd, - &cmd->se_dev->dev_queue_obj); - - transport_lun_remove_cmd(cmd); - - if (transport_cmd_check_stop(cmd, 1, 0)) - transport_generic_remove(cmd, 0); - } - - spin_lock_irqsave(&dev->execute_task_lock, flags); - continue; - } - pr_debug("Got t_transport_active = 0 for task: %p, dev: %p\n", - task, dev); - - if (atomic_read(&cmd->t_fe_count)) { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - transport_send_check_condition_and_sense(cmd, - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); - transport_remove_cmd_from_queue(cmd, - &cmd->se_dev->dev_queue_obj); - - transport_lun_remove_cmd(cmd); - transport_cmd_check_stop(cmd, 1, 0); - } else { - spin_unlock_irqrestore( - &cmd->t_state_lock, flags); - - transport_remove_cmd_from_queue(cmd, - &cmd->se_dev->dev_queue_obj); - transport_lun_remove_cmd(cmd); - - if (transport_cmd_check_stop(cmd, 1, 0)) - transport_generic_remove(cmd, 0); - } - - spin_lock_irqsave(&dev->execute_task_lock, flags); - } - spin_unlock_irqrestore(&dev->execute_task_lock, flags); - /* - * Empty the struct se_device's struct se_cmd list. - */ - while ((cmd = transport_get_cmd_from_queue(&dev->dev_queue_obj))) { - - pr_debug("From Device Queue: cmd: %p t_state: %d\n", - cmd, cmd->t_state); - - if (atomic_read(&cmd->t_fe_count)) { - transport_send_check_condition_and_sense(cmd, - TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); - - transport_lun_remove_cmd(cmd); - transport_cmd_check_stop(cmd, 1, 0); - } else { - transport_lun_remove_cmd(cmd); - if (transport_cmd_check_stop(cmd, 1, 0)) - transport_generic_remove(cmd, 0); - } - } -} - /* transport_processing_thread(): * * @@ -5181,14 +4557,6 @@ static int transport_processing_thread(void *param) if (ret < 0) goto out; - spin_lock_irq(&dev->dev_status_lock); - if (dev->dev_status & TRANSPORT_DEVICE_SHUTDOWN) { - spin_unlock_irq(&dev->dev_status_lock); - transport_processing_shutdown(dev); - continue; - } - spin_unlock_irq(&dev->dev_status_lock); - get_cmd: __transport_execute_tasks(dev); @@ -5197,6 +4565,9 @@ get_cmd: continue; switch (cmd->t_state) { + case TRANSPORT_NEW_CMD: + BUG(); + break; case TRANSPORT_NEW_CMD_MAP: if (!cmd->se_tfo->new_cmd_map) { pr_err("cmd->se_tfo->new_cmd_map is" @@ -5206,19 +4577,17 @@ get_cmd: ret = cmd->se_tfo->new_cmd_map(cmd); if (ret < 0) { cmd->transport_error_status = ret; - transport_generic_request_failure(cmd, NULL, + transport_generic_request_failure(cmd, 0, (cmd->data_direction != DMA_TO_DEVICE)); break; } - /* Fall through */ - case TRANSPORT_NEW_CMD: ret = transport_generic_new_cmd(cmd); if (ret == -EAGAIN) break; else if (ret < 0) { cmd->transport_error_status = ret; - transport_generic_request_failure(cmd, NULL, + transport_generic_request_failure(cmd, 0, (cmd->data_direction != DMA_TO_DEVICE)); } @@ -5226,33 +4595,22 @@ get_cmd: case TRANSPORT_PROCESS_WRITE: transport_generic_process_write(cmd); break; - case TRANSPORT_COMPLETE_OK: - transport_stop_all_task_timers(cmd); - transport_generic_complete_ok(cmd); - break; - case TRANSPORT_REMOVE: - transport_generic_remove(cmd, 0); - break; case TRANSPORT_FREE_CMD_INTR: - transport_generic_free_cmd(cmd, 0, 0); + transport_generic_free_cmd(cmd, 0); break; case TRANSPORT_PROCESS_TMR: transport_generic_do_tmr(cmd); break; - case TRANSPORT_COMPLETE_FAILURE: - transport_generic_request_failure(cmd, NULL, 1, 1); - break; - case TRANSPORT_COMPLETE_TIMEOUT: - transport_stop_all_task_timers(cmd); - transport_generic_request_timeout(cmd); - break; case TRANSPORT_COMPLETE_QF_WP: - transport_generic_write_pending(cmd); + transport_write_pending_qf(cmd); + break; + case TRANSPORT_COMPLETE_QF_OK: + transport_complete_qf(cmd); break; default: - pr_err("Unknown t_state: %d deferred_t_state:" - " %d for ITT: 0x%08x i_state: %d on SE LUN:" - " %u\n", cmd->t_state, cmd->deferred_t_state, + pr_err("Unknown t_state: %d for ITT: 0x%08x " + "i_state: %d on SE LUN: %u\n", + cmd->t_state, cmd->se_tfo->get_task_tag(cmd), cmd->se_tfo->get_cmd_state(cmd), cmd->se_lun->unpacked_lun); @@ -5263,7 +4621,8 @@ get_cmd: } out: - transport_release_all_cmds(dev); + WARN_ON(!list_empty(&dev->state_task_list)); + WARN_ON(!list_empty(&dev->dev_queue_obj.qobj_list)); dev->process_thread = NULL; return 0; } diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 31e3c652527e..50a480db7a66 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -24,7 +24,6 @@ * ******************************************************************************/ -#include <linux/version.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <scsi/scsi.h> diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 80fbcde00cb6..6195026cc7b0 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> @@ -115,7 +114,7 @@ void ft_release_cmd(struct se_cmd *se_cmd) void ft_check_stop_free(struct se_cmd *se_cmd) { - transport_generic_free_cmd(se_cmd, 0, 0); + transport_generic_free_cmd(se_cmd, 0); } /* @@ -268,9 +267,8 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) if (IS_ERR(fp)) { /* XXX need to find cmd if queued */ - cmd->se_cmd.t_state = TRANSPORT_REMOVE; cmd->seq = NULL; - transport_generic_free_cmd(&cmd->se_cmd, 0, 0); + transport_generic_free_cmd(&cmd->se_cmd, 0); return; } @@ -288,7 +286,7 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg) __func__, fh->fh_r_ctl); ft_invl_hw_context(cmd); fc_frame_free(fp); - transport_generic_free_cmd(&cmd->se_cmd, 0, 0); + transport_generic_free_cmd(&cmd->se_cmd, 0); break; } } @@ -397,7 +395,7 @@ static void ft_send_tm(struct ft_cmd *cmd) } pr_debug("alloc tm cmd fn %d\n", tm_func); - tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func); + tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func, GFP_KERNEL); if (!tmr) { pr_debug("alloc failed\n"); ft_send_resp_code_and_free(cmd, FCP_TMF_FAILED); @@ -421,7 +419,7 @@ static void ft_send_tm(struct ft_cmd *cmd) sess = cmd->sess; transport_send_check_condition_and_sense(&cmd->se_cmd, cmd->se_cmd.scsi_sense_reason, 0); - transport_generic_free_cmd(&cmd->se_cmd, 0, 0); + transport_generic_free_cmd(&cmd->se_cmd, 0); ft_sess_put(sess); return; } @@ -628,7 +626,7 @@ static void ft_send_work(struct work_struct *work) if (ret == -ENOMEM) { transport_send_check_condition_and_sense(se_cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); - transport_generic_free_cmd(se_cmd, 0, 0); + transport_generic_free_cmd(se_cmd, 0); return; } if (ret == -EINVAL) { @@ -637,10 +635,10 @@ static void ft_send_work(struct work_struct *work) else transport_send_check_condition_and_sense(se_cmd, se_cmd->scsi_sense_reason, 0); - transport_generic_free_cmd(se_cmd, 0, 0); + transport_generic_free_cmd(se_cmd, 0); return; } - transport_generic_handle_cdb(se_cmd); + transport_handle_cdb_direct(se_cmd); return; err: diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 8fa39b74f22c..5f770412ca40 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> @@ -32,6 +31,7 @@ #include <linux/types.h> #include <linux/string.h> #include <linux/configfs.h> +#include <linux/kernel.h> #include <linux/ctype.h> #include <asm/unaligned.h> #include <scsi/scsi.h> @@ -71,10 +71,10 @@ static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) { const char *cp; char c; - u32 nibble; u32 byte = 0; u32 pos = 0; u32 err; + int val; *wwn = 0; for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { @@ -95,13 +95,10 @@ static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) return cp - name; } err = 3; - if (isdigit(c)) - nibble = c - '0'; - else if (isxdigit(c) && (islower(c) || !strict)) - nibble = tolower(c) - 'a' + 10; - else + val = hex_to_bin(c); + if (val < 0 || (strict && isupper(c))) goto fail; - *wwn = (*wwn << 4) | nibble; + *wwn = (*wwn << 4) | val; } err = 4; fail: diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index d35ea5a3d56c..1369b1cb103d 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index dbb5eaeee399..326921385aff 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <generated/utsrelease.h> #include <linux/utsname.h> #include <linux/init.h> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index bd7cc0527999..f462fa5f937c 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -62,7 +62,7 @@ config VT_CONSOLE config HW_CONSOLE bool - depends on VT && !S390 && !UML + depends on VT && !UML default y config VT_HW_CONSOLE_BINDING diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 23bc743f2a22..492c14d63e99 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -23,7 +23,7 @@ * 1 word. If SPI master controller doesn't support sclk frequency change, * then the char need be sent out one by one with some delay * - * 2. Currently only RX available interrrupt is used, no need for waiting TXE + * 2. Currently only RX available interrupt is used, no need for waiting TXE * interrupt for a low speed UART device */ diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index d2efe823c20d..a783d533a1a6 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -69,7 +69,7 @@ static ssize_t map_name_show(struct uio_mem *mem, char *buf) static ssize_t map_addr_show(struct uio_mem *mem, char *buf) { - return sprintf(buf, "0x%lx\n", mem->addr); + return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr); } static ssize_t map_size_show(struct uio_mem *mem, char *buf) @@ -79,7 +79,7 @@ static ssize_t map_size_show(struct uio_mem *mem, char *buf) static ssize_t map_offset_show(struct uio_mem *mem, char *buf) { - return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK); + return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr & ~PAGE_MASK); } struct map_sysfs_entry { @@ -634,8 +634,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) page = virt_to_page(idev->info->mem[mi].addr + offset); else - page = vmalloc_to_page((void *)idev->info->mem[mi].addr - + offset); + page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset); get_page(page); vmf->page = page; return 0; @@ -750,14 +749,13 @@ static int uio_major_init(void) uio_major = MAJOR(uio_dev); uio_cdev = cdev; - result = 0; -out: - return result; + return 0; out_put: kobject_put(&cdev->kobj); out_unregister: unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES); - goto out; +out: + return result; } static void uio_major_cleanup(void) diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c index fc22e1e6f215..02bd47bdee1c 100644 --- a/drivers/uio/uio_pci_generic.c +++ b/drivers/uio/uio_pci_generic.c @@ -24,7 +24,6 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/uio_driver.h> -#include <linux/spinlock.h> #define DRIVER_VERSION "0.01.0" #define DRIVER_AUTHOR "Michael S. Tsirkin <mst@redhat.com>" @@ -33,7 +32,6 @@ struct uio_pci_generic_dev { struct uio_info info; struct pci_dev *pdev; - spinlock_t lock; /* guards command register accesses */ }; static inline struct uio_pci_generic_dev * @@ -57,7 +55,6 @@ static irqreturn_t irqhandler(int irq, struct uio_info *info) BUILD_BUG_ON(PCI_COMMAND % 4); BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS); - spin_lock_irq(&gdev->lock); pci_block_user_cfg_access(pdev); /* Read both command and status registers in a single 32-bit operation. @@ -83,7 +80,6 @@ static irqreturn_t irqhandler(int irq, struct uio_info *info) done: pci_unblock_user_cfg_access(pdev); - spin_unlock_irq(&gdev->lock); return ret; } @@ -158,7 +154,6 @@ static int __devinit probe(struct pci_dev *pdev, gdev->info.irq_flags = IRQF_SHARED; gdev->info.handler = irqhandler; gdev->pdev = pdev; - spin_lock_init(&gdev->lock); if (uio_register_device(&pdev->dev, &gdev->info)) goto err_register; diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index bae96d246760..0b2ed71e3bfa 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -253,7 +253,7 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { }; #ifdef CONFIG_OF -static const struct of_device_id __devinitconst uio_of_genirq_match[] = { +static const struct of_device_id uio_of_genirq_match[] = { { /* empty for now */ }, }; MODULE_DEVICE_TABLE(of, uio_of_genirq_match); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 48f1781352f1..4ac2750491de 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -12,6 +12,11 @@ menuconfig USB_SUPPORT if USB_SUPPORT +config USB_COMMON + tristate + default y + depends on USB || USB_GADGET + # Host-side USB depends on having a host controller # NOTE: dummy_hcd is always an option, but it's ignored here ... # NOTE: SL-811 option should be board-specific ... @@ -19,6 +24,7 @@ config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI default y if USB_ARCH_HAS_EHCI + default y if USB_ARCH_HAS_XHCI default y if PCMCIA && !M32R # sl811_cs default y if ARM # SL-811 default y if BLACKFIN # SL-811 @@ -54,7 +60,7 @@ config USB_ARCH_HAS_OHCI # some non-PCI hcds implement EHCI config USB_ARCH_HAS_EHCI boolean - default y if PPC_83xx + default y if FSL_SOC default y if PPC_MPC512x default y if SOC_AU1200 default y if ARCH_IXP4XX @@ -69,6 +75,12 @@ config USB_ARCH_HAS_EHCI default y if ARCH_MSM default y if MICROBLAZE default y if SPARC_LEON + default y if ARCH_MMP + default PCI + +# some non-PCI HCDs implement xHCI +config USB_ARCH_HAS_XHCI + boolean default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. @@ -110,6 +122,8 @@ config USB source "drivers/usb/core/Kconfig" +source "drivers/usb/dwc3/Kconfig" + source "drivers/usb/mon/Kconfig" source "drivers/usb/wusbcore/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 30ddf8dc4f72..75eca7645227 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_USB) += core/ +obj-$(CONFIG_USB_DWC3) += dwc3/ + obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ @@ -51,3 +53,5 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb/ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ obj-$(CONFIG_USB_GADGET) += gadget/ + +obj-$(CONFIG_USB_COMMON) += usb-common.o diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index dac7676ce21b..3ec6699ab725 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1058,11 +1058,11 @@ made_compressed_probe: goto alloc_fail; } - ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); - readsize = le16_to_cpu(epread->wMaxPacketSize) * + ctrlsize = usb_endpoint_maxp(epctrl); + readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); acm->combined_interfaces = combined_interfaces; - acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; + acm->writesize = usb_endpoint_maxp(epwrite) * 20; acm->control = control_interface; acm->data = data_interface; acm->minor = minor; @@ -1534,6 +1534,9 @@ static const struct usb_device_id acm_ids[] = { { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */ { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ + /* Support for Owen devices */ + { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */ + /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ /* Support Lego NXT using pbLua firmware */ diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 2b9ff518b509..1d26a7135dd9 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -682,7 +682,7 @@ next_desc: if (!ep || !usb_endpoint_is_int_in(ep)) goto err; - desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); + desc->wMaxPacketSize = usb_endpoint_maxp(ep); desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 3f94ac34dce3..12cf5e7395a8 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -186,8 +186,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) if (current_setting->endpoint[n].desc.bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(current_setting->endpoint[n]. - desc.wMaxPacketSize); + max_size = usb_endpoint_maxp(¤t_setting->endpoint[n].desc); if (max_size == 0) { dev_err(dev, "Couldn't get wMaxPacketSize\n"); @@ -636,7 +635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { desc = ¤t_setting->endpoint[n].desc; if (desc->bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(desc->wMaxPacketSize); + max_size = usb_endpoint_maxp(desc); } if (max_size == 0) { diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 26678cadfb21..f4bdd0ce8d56 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, if (usb_endpoint_xfer_isoc(&ep->desc)) max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) * - le16_to_cpu(ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&ep->desc); else if (usb_endpoint_xfer_int(&ep->desc)) - max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) * + max_tx = usb_endpoint_maxp(&ep->desc) * (desc->bMaxBurst + 1); else max_tx = 999999; @@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress); endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; - if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) + if (usb_endpoint_maxp(&endpoint->desc) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } @@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, && usb_endpoint_xfer_bulk(d)) { unsigned maxp; - maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff; + maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", @@ -755,3 +755,106 @@ err2: dev_err(ddev, "out of memory\n"); return result; } + +void usb_release_bos_descriptor(struct usb_device *dev) +{ + if (dev->bos) { + kfree(dev->bos->desc); + kfree(dev->bos); + dev->bos = NULL; + } +} + +/* Get BOS descriptor set */ +int usb_get_bos_descriptor(struct usb_device *dev) +{ + struct device *ddev = &dev->dev; + struct usb_bos_descriptor *bos; + struct usb_dev_cap_header *cap; + unsigned char *buffer; + int length, total_len, num, i; + int ret; + + bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL); + if (!bos) + return -ENOMEM; + + /* Get BOS descriptor */ + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE); + if (ret < USB_DT_BOS_SIZE) { + dev_err(ddev, "unable to get BOS descriptor\n"); + if (ret >= 0) + ret = -ENOMSG; + kfree(bos); + return ret; + } + + length = bos->bLength; + total_len = le16_to_cpu(bos->wTotalLength); + num = bos->bNumDeviceCaps; + kfree(bos); + if (total_len < length) + return -EINVAL; + + dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL); + if (!dev->bos) + return -ENOMEM; + + /* Now let's get the whole BOS descriptor set */ + buffer = kzalloc(total_len, GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto err; + } + dev->bos->desc = (struct usb_bos_descriptor *)buffer; + + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len); + if (ret < total_len) { + dev_err(ddev, "unable to get BOS descriptor set\n"); + if (ret >= 0) + ret = -ENOMSG; + goto err; + } + total_len -= length; + + for (i = 0; i < num; i++) { + buffer += length; + cap = (struct usb_dev_cap_header *)buffer; + length = cap->bLength; + + if (total_len < length) + break; + total_len -= length; + + if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { + dev_warn(ddev, "descriptor type invalid, skip\n"); + continue; + } + + switch (cap->bDevCapabilityType) { + case USB_CAP_TYPE_WIRELESS_USB: + /* Wireless USB cap descriptor is handled by wusb */ + break; + case USB_CAP_TYPE_EXT: + dev->bos->ext_cap = + (struct usb_ext_cap_descriptor *)buffer; + break; + case USB_SS_CAP_TYPE: + dev->bos->ss_cap = + (struct usb_ss_cap_descriptor *)buffer; + break; + case CONTAINER_ID_TYPE: + dev->bos->ss_id = + (struct usb_ss_container_id_descriptor *)buffer; + break; + default: + break; + } + } + + return 0; + +err: + usb_release_bos_descriptor(dev); + return ret; +} diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 0149c0976e9c..d95696584762 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, dir = usb_endpoint_dir_in(desc) ? 'I' : 'O'; if (speed == USB_SPEED_HIGH) { - switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) { + switch (usb_endpoint_maxp(desc) & (0x03 << 11)) { case 1 << 11: bandwidth = 2; break; case 2 << 11: @@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, desc->bmAttributes, type, - (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * + (usb_endpoint_maxp(desc) & 0x07ff) * bandwidth, interval, unit); return start; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 37518dfdeb98..e3beaf229ee3 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -46,6 +46,7 @@ #include <linux/cdev.h> #include <linux/notifier.h> #include <linux/security.h> +#include <linux/user_namespace.h> #include <asm/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> @@ -68,7 +69,7 @@ struct dev_state { wait_queue_head_t wait; /* wake up if a request completed */ unsigned int discsignr; struct pid *disc_pid; - uid_t disc_uid, disc_euid; + const struct cred *cred; void __user *disccontext; unsigned long ifclaimed; u32 secid; @@ -79,7 +80,7 @@ struct async { struct list_head asynclist; struct dev_state *ps; struct pid *pid; - uid_t uid, euid; + const struct cred *cred; unsigned int signr; unsigned int ifnum; void __user *userbuffer; @@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { put_pid(as->pid); + put_cred(as->cred); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -393,9 +395,8 @@ static void async_completed(struct urb *urb) struct dev_state *ps = as->ps; struct siginfo sinfo; struct pid *pid = NULL; - uid_t uid = 0; - uid_t euid = 0; u32 secid = 0; + const struct cred *cred = NULL; int signr; spin_lock(&ps->lock); @@ -407,9 +408,8 @@ static void async_completed(struct urb *urb) sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - pid = as->pid; - uid = as->uid; - euid = as->euid; + pid = get_pid(as->pid); + cred = get_cred(as->cred); secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); @@ -422,9 +422,11 @@ static void async_completed(struct urb *urb) cancel_bulk_urbs(ps, as->bulk_addr); spin_unlock(&ps->lock); - if (signr) - kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, - euid, secid); + if (signr) { + kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); + put_pid(pid); + put_cred(cred); + } wake_up(&ps->wait); } @@ -607,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep) } static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, - unsigned int index) + unsigned int request, unsigned int index) { int ret = 0; + struct usb_host_interface *alt_setting; if (ps->dev->state != USB_STATE_UNAUTHENTICATED && ps->dev->state != USB_STATE_ADDRESS @@ -618,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; + /* + * check for the special corner case 'get_device_id' in the printer + * class specification, where wIndex is (interface << 8 | altsetting) + * instead of just interface + */ + if (requesttype == 0xa1 && request == 0) { + alt_setting = usb_find_alt_setting(ps->dev->actconfig, + index >> 8, index & 0xff); + if (alt_setting + && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) + index >>= 8; + } + index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: @@ -656,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file) { struct usb_device *dev = NULL; struct dev_state *ps; - const struct cred *cred = current_cred(); int ret; ret = -ENOMEM; @@ -706,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file) init_waitqueue_head(&ps->wait); ps->discsignr = 0; ps->disc_pid = get_pid(task_pid(current)); - ps->disc_uid = cred->uid; - ps->disc_euid = cred->euid; + ps->cred = get_current_cred(); ps->disccontext = NULL; ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); @@ -749,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_unlock_device(dev); usb_put_dev(dev); put_pid(ps->disc_pid); + put_cred(ps->cred); as = async_getcompleted(ps); while (as) { @@ -770,7 +785,8 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex); + ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest, + ctrl.wIndex); if (ret) return ret; wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */ @@ -1048,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usb_host_endpoint *ep; struct async *as; struct usb_ctrlrequest *dr = NULL; - const struct cred *cred = current_cred(); unsigned int u, totlen, isofrmlen; int ret, ifnum = -1; int is_in; @@ -1100,7 +1115,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(dr); return -EINVAL; } - ret = check_ctrlrecip(ps, dr->bRequestType, + ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest, le16_to_cpup(&dr->wIndex)); if (ret) { kfree(dr); @@ -1262,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->signr = uurb->signr; as->ifnum = ifnum; as->pid = get_pid(task_pid(current)); - as->uid = cred->uid; - as->euid = cred->euid; + as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); if (!is_in && uurb->buffer_length > 0) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, @@ -1981,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev) sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = ps->disccontext; - kill_pid_info_as_uid(ps->discsignr, &sinfo, - ps->disc_pid, ps->disc_uid, - ps->disc_euid, ps->secid); + kill_pid_info_as_cred(ps->discsignr, &sinfo, + ps->disc_pid, ps->cred, ps->secid); } } } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 34e3da5aa72a..adf5ca8a2396 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1583,7 +1583,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); - if (status > 0) + if (status > 0 || status == -EINPROGRESS) status = 0; return status; } @@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) return 0; } +int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret = -EPERM; + + if (hcd->driver->set_usb2_hw_lpm) { + ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); + if (!ret) + udev->usb2_hw_lpm_enabled = enable; + } + + return ret; +} + #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index df502a98d0df..db7fe50c23d4 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev, { struct ep_device *ep = to_ep_device(dev); return sprintf(buf, "%04x\n", - le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff); + usb_endpoint_maxp(ep->desc) & 0x07ff); } static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ce22f4a84ed0..a004db35f6d0 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -242,7 +242,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_master(dev); - retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (retval != 0) goto unmap_registers; set_hs_companion(dev, hcd); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 73cbbd85219f..b3b7d062906d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - u8 tbuf [sizeof (struct usb_hub_descriptor)] + /* + * tbuf should be as big as the BOS descriptor and + * the USB hub descriptor. + */ + u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE] __attribute__((aligned(4))); const u8 *bufp = tbuf; unsigned len = 0; @@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) else /* unsupported IDs --> "protocol stall" */ goto error; break; + case USB_DT_BOS << 8: + goto nongeneric; default: goto error; } @@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) /* CLASS REQUESTS (and errors) */ default: +nongeneric: /* non-generic request */ switch (typeReq) { case GetHubStatus: @@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case GetHubDescriptor: len = sizeof (struct usb_hub_descriptor); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + /* len is returned by hub_control */ + break; } status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, @@ -615,7 +625,7 @@ error: status = -EPIPE; } - if (status) { + if (status < 0) { len = 0; if (status != -EPIPE) { dev_dbg (hcd->self.controller, @@ -624,6 +634,10 @@ error: typeReq, wValue, wIndex, wLength, status); } + } else if (status > 0) { + /* hub_control may return the length of data copied. */ + len = status; + status = 0; } if (len) { if (urb->transfer_buffer_length < len) @@ -2429,7 +2443,7 @@ int usb_add_hcd(struct usb_hcd *hcd, * but drivers can override it in reset() if needed, along with * recording the overall controller's system wakeup capability. */ - device_init_wakeup(&rhdev->dev, 1); + device_set_wakeup_capable(&rhdev->dev, 1); /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is * registered. But since the controller can die at any time, @@ -2478,6 +2492,13 @@ int usb_add_hcd(struct usb_hcd *hcd, } if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); + + /* + * Host controllers don't generate their own wakeup requests; + * they only forward requests from the root hub. Therefore + * controllers should always be enabled for remote wakeup. + */ + device_wakeup_enable(hcd->self.controller); return retval; error_create_attr_group: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a428aa080a36..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev) int i; struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!udev) { - pr_debug ("%s nodev\n", __func__); - return; - } - /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. * this quiesces everything except pending urbs. @@ -2030,11 +2025,23 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 +#define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 +static int hub_port_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay, bool warm); + +/* Is a USB 3.0 port in the Inactive state? */ +static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +{ + return hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE; +} + static int hub_port_wait_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int delay_time, ret; u16 portstatus; @@ -2051,28 +2058,71 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; - /* Device went away? */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return -ENOTCONN; - - /* bomb out completely if the connection bounced */ - if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; - - /* if we`ve finished resetting, then break out of the loop */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { - if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_WIRELESS; - else if (hub_is_superspeed(hub->hdev)) - udev->speed = USB_SPEED_SUPER; - else if (portstatus & USB_PORT_STAT_HIGH_SPEED) - udev->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) - udev->speed = USB_SPEED_LOW; - else - udev->speed = USB_SPEED_FULL; - return 0; + /* + * Some buggy devices require a warm reset to be issued even + * when the port appears not to be connected. + */ + if (!warm) { + /* + * Some buggy devices can cause an NEC host controller + * to transition to the "Error" state after a hot port + * reset. This will show up as the port state in + * "Inactive", and the port may also report a + * disconnect. Forcing a warm port reset seems to make + * the device work. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 + */ + if (hub_port_inactive(hub, portstatus)) { + int ret; + + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); + dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", + port1); + ret = hub_port_reset(hub, port1, + udev, HUB_BH_RESET_TIME, + true); + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + return ret; + } + /* Device went away? */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + + /* bomb out completely if the connection bounced */ + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + return -ENOTCONN; + + /* if we`ve finished resetting, then break out of + * the loop + */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_ENABLE)) { + if (hub_is_wusb(hub)) + udev->speed = USB_SPEED_WIRELESS; + else if (hub_is_superspeed(hub->hdev)) + udev->speed = USB_SPEED_SUPER; + else if (portstatus & USB_PORT_STAT_HIGH_SPEED) + udev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + udev->speed = USB_SPEED_LOW; + else + udev->speed = USB_SPEED_FULL; + return 0; + } + } else { + if (portchange & USB_PORT_STAT_C_BH_RESET) + return 0; } /* switch to the long delay after two short delay failures */ @@ -2080,35 +2130,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; dev_dbg (hub->intfdev, - "port %d not reset yet, waiting %dms\n", - port1, delay); + "port %d not %sreset yet, waiting %dms\n", + port1, warm ? "warm " : "", delay); } return -EBUSY; } +static void hub_port_finish_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, int *status, bool warm) +{ + switch (*status) { + case 0: + if (!warm) { + struct usb_hcd *hcd; + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + update_devnum(udev, 0); + hcd = bus_to_hcd(udev->bus); + if (hcd->driver->reset_device) { + *status = hcd->driver->reset_device(hcd, udev); + if (*status < 0) { + dev_err(&udev->dev, "Cannot reset " + "HCD device state\n"); + break; + } + } + } + /* FALL THROUGH */ + case -ENOTCONN: + case -ENODEV: + clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_C_RESET); + /* FIXME need disconnect() for NOTATTACHED device */ + if (warm) { + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + usb_set_device_state(udev, *status + ? USB_STATE_NOTATTACHED + : USB_STATE_DEFAULT); + } + break; + } +} + +/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int i, status; - struct usb_hcd *hcd; - hcd = bus_to_hcd(udev->bus); - /* Block EHCI CF initialization during the port reset. - * Some companion controllers don't like it when they mix. - */ - down_read(&ehci_cf_port_reset_rwsem); + if (!warm) { + /* Block EHCI CF initialization during the port reset. + * Some companion controllers don't like it when they mix. + */ + down_read(&ehci_cf_port_reset_rwsem); + } else { + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support " + "warm reset\n"); + return -EINVAL; + } + } /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { - status = set_port_feature(hub->hdev, - port1, USB_PORT_FEAT_RESET); - if (status) + status = set_port_feature(hub->hdev, port1, (warm ? + USB_PORT_FEAT_BH_PORT_RESET : + USB_PORT_FEAT_RESET)); + if (status) { dev_err(hub->intfdev, - "cannot reset port %d (err = %d)\n", - port1, status); - else { - status = hub_port_wait_reset(hub, port1, udev, delay); + "cannot %sreset port %d (err = %d)\n", + warm ? "warm " : "", port1, status); + } else { + status = hub_port_wait_reset(hub, port1, udev, delay, + warm); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", @@ -2116,34 +2215,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1, } /* return on disconnect or reset */ - switch (status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - update_devnum(udev, 0); - if (hcd->driver->reset_device) { - status = hcd->driver->reset_device(hcd, udev); - if (status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - /* FIXME need disconnect() for NOTATTACHED device */ - usb_set_device_state(udev, status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); + if (status == 0 || status == -ENOTCONN || status == -ENODEV) { + hub_port_finish_reset(hub, port1, udev, &status, warm); goto done; } dev_dbg (hub->intfdev, - "port %d not enabled, trying reset again...\n", - port1); + "port %d not enabled, trying %sreset again...\n", + port1, warm ? "warm " : ""); delay = HUB_LONG_RESET_TIME; } @@ -2151,45 +2230,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); - done: - up_read(&ehci_cf_port_reset_rwsem); - return status; -} - -/* Warm reset a USB3 protocol port */ -static int hub_port_warm_reset(struct usb_hub *hub, int port) -{ - int ret; - u16 portstatus, portchange; - - if (!hub_is_superspeed(hub->hdev)) { - dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); - return -EINVAL; - } - - /* Warm reset the port */ - ret = set_port_feature(hub->hdev, - port, USB_PORT_FEAT_BH_PORT_RESET); - if (ret) { - dev_err(hub->intfdev, "cannot warm reset port %d\n", port); - return ret; - } - - msleep(20); - ret = hub_port_status(hub, port, &portstatus, &portchange); - - if (portchange & USB_PORT_STAT_C_RESET) - clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); - - if (portchange & USB_PORT_STAT_C_BH_RESET) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_BH_PORT_RESET); - - if (portchange & USB_PORT_STAT_C_LINK_STATE) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_PORT_LINK_STATE); +done: + if (!warm) + up_read(&ehci_cf_port_reset_rwsem); - return ret; + return status; } /* Check if a port is power on */ @@ -2347,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2558,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } @@ -2798,7 +2852,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; - char *speed, *type; + const char *speed; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period @@ -2819,7 +2873,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ @@ -2858,25 +2912,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, default: goto fail; } - - type = ""; - switch (udev->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - case USB_SPEED_SUPER: - speed = "super"; - break; - case USB_SPEED_WIRELESS: - speed = "variable"; - type = "Wireless "; - break; - default: speed = "?"; break; - } + + if (udev->speed == USB_SPEED_WIRELESS) + speed = "variable speed Wireless"; + else + speed = usb_speed_string(udev->speed); + if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, - "%s %s speed %sUSB device number %d using %s\n", - (udev->config) ? "reset" : "new", speed, type, + "%s %s USB device number %d using %s\n", + (udev->config) ? "reset" : "new", speed, devnum, udev->bus->controller->driver->name); /* Set up TT records, if needed */ @@ -2949,7 +2994,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, buf->bMaxPacketSize0; kfree(buf); - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { @@ -3023,7 +3068,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, i = 512; else i = udev->descriptor.bMaxPacketSize0; - if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { + if (usb_endpoint_maxp(&udev->ep0.desc) != i) { if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); @@ -3047,6 +3092,15 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { + retval = usb_get_bos_descriptor(udev); + if (!retval) { + if (udev->bos->ext_cap && (USB_LPM_SUPPORT & + le32_to_cpu(udev->bos->ext_cap->bmAttributes))) + udev->lpm_capable = 1; + } + } + retval = 0; /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) @@ -3570,7 +3624,8 @@ static void hub_events(void) (portstatus & USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_SS_INACTIVE) { dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_warm_reset(hub, i); + hub_port_reset(hub, i, NULL, + HUB_BH_RESET_TIME, true); } if (connect_change) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0b5ec234c787..b3bdfede45e6 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -435,7 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, len = sg->length; if (length) { - len = min_t(unsigned, len, length); + len = min_t(size_t, len, length); length -= len; if (length == 0) io->entries = i + 1; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 81ce6a8e1d94..d6a8d8269bfb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -38,6 +38,27 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Webcam C200 */ + { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C250 */ + { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C300 */ + { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam B/C500 */ + { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam Pro 9000 */ + { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C310 */ + { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C270 */ + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Harmony 700-series */ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -69,6 +90,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Guillemot Webcam Hercules Dualpix Exchange*/ + { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cf05b97693ea..662c0cf3a3e1 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); +static ssize_t +show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p; + + if (udev->usb2_hw_lpm_enabled == 1) + p = "enabled"; + else + p = "disabled"; + + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + bool value; + int ret; + + usb_lock_device(udev); + + ret = strtobool(buf, &value); + + if (!ret) + ret = usb_set_usb2_hardware_lpm(udev, value); + + usb_unlock_device(udev); + + if (!ret) + return count; + + return ret; +} + +static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, + set_usb2_hardware_lpm); + +static struct attribute *usb2_hardware_lpm_attr[] = { + &dev_attr_usb2_hardware_lpm.attr, + NULL, +}; +static struct attribute_group usb2_hardware_lpm_attr_group = { + .name = power_group_name, + .attrs = usb2_hardware_lpm_attr, +}; + static struct attribute *power_attrs[] = { &dev_attr_autosuspend.attr, &dev_attr_level.attr, @@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); rc = sysfs_merge_group(&dev->kobj, &power_attr_group); + if (udev->usb2_hw_lpm_capable == 1) + rc = sysfs_merge_group(&dev->kobj, + &usb2_hardware_lpm_attr_group); + } + return rc; } static void remove_power_attributes(struct device *dev) { + sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group); sysfs_unmerge_group(&dev->kobj, &power_attr_group); } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index ae334b067c13..909625b91eb3 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) dev->state < USB_STATE_CONFIGURED) return -ENODEV; - max = le16_to_cpu(ep->desc.wMaxPacketSize); + max = usb_endpoint_maxp(&ep->desc); if (max <= 0) { dev_dbg(&dev->dev, "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8706fc97e60f..73cd90012ec5 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev) hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); + usb_release_bos_descriptor(udev); usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d44d4b7bbf17..3888778582c4 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_get_bos_descriptor(struct usb_device *dev); +extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); @@ -80,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); +extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); #else @@ -94,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) return 0; } +static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + return 0; +} #endif extern struct bus_type usb_bus_type; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig new file mode 100644 index 000000000000..3c1d67d324fd --- /dev/null +++ b/drivers/usb/dwc3/Kconfig @@ -0,0 +1,25 @@ +config USB_DWC3 + tristate "DesignWare USB3 DRD Core Support" + depends on (USB || USB_GADGET) + select USB_OTG_UTILS + help + Say Y or M here if your system has a Dual Role SuperSpeed + USB controller based on the DesignWare USB3 IP Core. + + If you choose to build this driver is a dynamically linked + module, the module will be called dwc3.ko. + +if USB_DWC3 + +config USB_DWC3_DEBUG + bool "Enable Debugging Messages" + help + Say Y here to enable debugging messages on DWC3 Driver. + +config USB_DWC3_VERBOSE + bool "Enable Verbose Debugging Messages" + depends on USB_DWC3_DEBUG + help + Say Y here to enable verbose debugging messages on DWC3 Driver. + +endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 000000000000..593d1dbc465b --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,36 @@ +ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG + +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +ifneq ($(CONFIG_USB_GADGET_DWC3),) + dwc3-y += gadget.o ep0.o +endif + +ifneq ($(CONFIG_DEBUG_FS),) + dwc3-y += debugfs.o +endif + +## +# Platform-specific glue layers go here +# +# NOTICE: Make sure your glue layer doesn't depend on anything +# which is arch-specific and that it compiles on all situations. +# +# We want to keep this requirement in order to be able to compile +# the entire driver (with all its glue layers) on several architectures +# and make sure it compiles fine. This will also help with allmodconfig +# and allyesconfig builds. +# +# The only exception is the PCI glue layer, but that's only because +# PCI doesn't provide nops if CONFIG_PCI isn't enabled. +## + +obj-$(CONFIG_USB_DWC3) += dwc3-omap.o + +ifneq ($(CONFIG_PCI),) + obj-$(CONFIG_USB_DWC3) += dwc3-pci.o +endif + diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 000000000000..717ebc9ff941 --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,484 @@ +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/module.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "debug.h" + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static void dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + + /* Before Resetting PHY, put Core in Reset */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* Assert USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Assert USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); + + /* Clear USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Clear USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + /* After PHYs are stable we can take Core out of reset state */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +/** + * dwc3_free_one_event_buffer - Frees one event buffer + * @dwc: Pointer to our controller context structure + * @evt: Pointer to event buffer to be freed + */ +static void dwc3_free_one_event_buffer(struct dwc3 *dwc, + struct dwc3_event_buffer *evt) +{ + dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); + kfree(evt); +} + +/** + * dwc3_alloc_one_event_buffer - Allocated one event buffer structure + * @dwc: Pointer to our controller context structure + * @length: size of the event buffer + * + * Returns a pointer to the allocated event buffer structure on succes + * otherwise ERR_PTR(errno). + */ +static struct dwc3_event_buffer *__devinit +dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = kzalloc(sizeof(*evt), GFP_KERNEL); + if (!evt) + return ERR_PTR(-ENOMEM); + + evt->dwc = dwc; + evt->length = length; + evt->buf = dma_alloc_coherent(dwc->dev, length, + &evt->dma, GFP_KERNEL); + if (!evt->buf) { + kfree(evt); + return ERR_PTR(-ENOMEM); + } + + return evt; +} + +/** + * dwc3_free_event_buffers - frees all allocated event buffers + * @dwc: Pointer to our controller context structure + */ +static void dwc3_free_event_buffers(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int i; + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + evt = dwc->ev_buffs[i]; + if (evt) { + dwc3_free_one_event_buffer(dwc, evt); + dwc->ev_buffs[i] = NULL; + } + } +} + +/** + * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * @dwc: Pointer to out controller context structure + * @num: number of event buffers to allocate + * @length: size of event buffer + * + * Returns 0 on success otherwise negative errno. In error the case, dwc + * may contain some buffers allocated but not all which were requested. + */ +static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num, + unsigned length) +{ + int i; + + for (i = 0; i < num; i++) { + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buffs[i] = evt; + } + + return 0; +} + +/** + * dwc3_event_buffers_setup - setup our allocated event buffers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, + evt->length); + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + evt->length & 0xffff); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } + + return 0; +} + +static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } +} + +static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_core_init(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + int ret; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + dwc->revision = reg & DWC3_GSNPSREV_MASK; + + dwc3_core_soft_reset(dwc); + + /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); + + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM, + DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + ret = -ENOMEM; + goto err1; + } + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err1; + } + + dwc3_cache_hwparams(dwc); + + return 0; + +err1: + dwc3_free_event_buffers(dwc); + +err0: + return ret; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); +} + +#define DWC3_ALIGN_MASK (16 - 1) + +static int __devinit dwc3_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct resource *res; + struct dwc3 *dwc; + void __iomem *regs; + unsigned int features = id->driver_data; + int ret = -ENOMEM; + int irq; + void *mem; + + mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); + if (!mem) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); + dwc->mem = mem; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing resource\n"); + goto err1; + } + + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err2; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ\n"); + goto err3; + } + + spin_lock_init(&dwc->lock); + platform_set_drvdata(pdev, dwc); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + dwc->dev = &pdev->dev; + dwc->irq = irq; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + + ret = dwc3_core_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize core\n"); + goto err3; + } + + if (features & DWC3_HAS_PERIPHERAL) { + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialized gadget\n"); + goto err4; + } + } + + ret = dwc3_debugfs_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize debugfs\n"); + goto err5; + } + + pm_runtime_allow(&pdev->dev); + + return 0; + +err5: + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + +err4: + dwc3_core_exit(dwc); + +err3: + iounmap(regs); + +err2: + release_mem_region(res->start, resource_size(res)); + +err1: + kfree(dwc->mem); + +err0: + return ret; +} + +static int __devexit dwc3_remove(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct resource *res; + unsigned int features = id->driver_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + dwc3_debugfs_exit(dwc); + + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + + dwc3_core_exit(dwc); + release_mem_region(res->start, resource_size(res)); + iounmap(dwc->regs); + kfree(dwc->mem); + + return 0; +} + +static const struct platform_device_id dwc3_id_table[] __devinitconst = { + { + .name = "dwc3-omap", + .driver_data = (DWC3_HAS_PERIPHERAL + | DWC3_HAS_XHCI + | DWC3_HAS_OTG), + }, + { + .name = "dwc3-pci", + .driver_data = DWC3_HAS_PERIPHERAL, + }, + { }, /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(platform, dwc3_id_table); + +static struct platform_driver dwc3_driver = { + .probe = dwc3_probe, + .remove = __devexit_p(dwc3_remove), + .driver = { + .name = "dwc3", + }, + .id_table = dwc3_id_table, +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); + +static int __devinit dwc3_init(void) +{ + return platform_driver_register(&dwc3_driver); +} +module_init(dwc3_init); + +static void __exit dwc3_exit(void) +{ + platform_driver_unregister(&dwc3_driver); +} +module_exit(dwc3_exit); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 000000000000..29a8e1679e12 --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,768 @@ +/** + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <linux/mm.h> +#include <linux/debugfs.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* Global constants */ +#define DWC3_ENDPOINTS_NUM 32 + +#define DWC3_EVENT_BUFFERS_NUM 2 +#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 +#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) +#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) +#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) +#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVTEN 0xcc08 +#define DWC3_OSTS 0xcc0C + +/* Bit fields */ + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) (n << 19) +#define DWC3_GCTL_U2RSTECN (1 << 16) +#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAPDIR(n) (n << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SCALEDOWN(n) (n << 4) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) + +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED2 (1 << 0) +#define DWC3_DCFG_LOWSPEED (2 << 0) +#define DWC3_DCFG_FULLSPEED1 (3 << 0) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP (1 << 31) +#define DWC3_DCTL_CSFTRST (1 << 30) +#define DWC3_DCTL_LSFTRST (1 << 29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) + +#define DWC3_DCTL_APPL1RES (1 << 23) + +#define DWC3_DCTL_INITU2ENA (1 << 12) +#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) +#define DWC3_DCTL_INITU1ENA (1 << 10) +#define DWC3_DCTL_ACCEPTU1ENA (1 << 9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) +#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) +#define DWC3_DEVTEN_ERRTICERREN (1 << 9) +#define DWC3_DEVTEN_SOFEN (1 << 7) +#define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) +#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) +#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) +#define DWC3_DEVTEN_USBRSTEN (1 << 1) +#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) + +/* Device Status Register */ +#define DWC3_DSTS_PWRUPREQ (1 << 24) +#define DWC3_DSTS_COREIDLE (1 << 23) +#define DWC3_DSTS_DEVCTRLHLT (1 << 22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) + +#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED2 (1 << 0) +#define DWC3_DSTS_LOWSPEED (2 << 0) +#define DWC3_DSTS_FULLSPEED1 (3 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12) +#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12) +#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) +#define DWC3_DEPCMD_CMDACT (1 << 10) +#define DWC3_DEPCMD_CMDIOC (1 << 8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) (1 << n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +/* Structures */ + +struct dwc3_trb_hw; + +/** + * struct dwc3_event_buffer - Software event buffer representation + * @list: a list of event buffers + * @buf: _THE_ buffer + * @length: size of this buffer + * @dma: dma_addr_t + * @dwc: pointer to DWC controller + */ +struct dwc3_event_buffer { + void *buf; + unsigned length; + unsigned int lpos; + + dma_addr_t dma; + + struct dwc3 *dwc; +}; + +#define DWC3_EP_FLAG_STALLED (1 << 0) +#define DWC3_EP_FLAG_WEDGED (1 << 1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 32 +#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) + +/** + * struct dwc3_ep - device side endpoint representation + * @endpoint: usb endpoint + * @request_list: list of requests for this endpoint + * @req_queued: list of requests on this ep which have TRBs setup + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @free_slot: next slot which is going to be used + * @busy_slot: first slot which is owned by HW + * @desc: usb_endpoint_descriptor pointer + * @dwc: pointer to DWC controller + * @flags: endpoint flags (wedged, stalled, ...) + * @current_trb: index of current used trb + * @number: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @res_trans_idx: Resource transfer index + * @interval: the intervall on which the ISOC transfer is started + * @name: a human readable name e.g. ep1out-bulk + * @direction: true for TX, false for RX + * @stream_capable: true when streams are enabled + */ +struct dwc3_ep { + struct usb_ep endpoint; + struct list_head request_list; + struct list_head req_queued; + + struct dwc3_trb_hw *trb_pool; + dma_addr_t trb_pool_dma; + u32 free_slot; + u32 busy_slot; + const struct usb_endpoint_descriptor *desc; + struct dwc3 *dwc; + + unsigned flags; +#define DWC3_EP_ENABLED (1 << 0) +#define DWC3_EP_STALL (1 << 1) +#define DWC3_EP_WEDGE (1 << 2) +#define DWC3_EP_BUSY (1 << 4) +#define DWC3_EP_PENDING_REQUEST (1 << 5) + + /* This last one is specific to EP0 */ +#define DWC3_EP0_DIR_IN (1 << 31) + + unsigned current_trb; + + u8 number; + u8 type; + u8 res_trans_idx; + u32 interval; + + char name[20]; + + unsigned direction:1; + unsigned stream_capable:1; +}; + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_SETUP, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +enum dwc3_device_state { + DWC3_DEFAULT_STATE, + DWC3_ADDRESS_STATE, + DWC3_CONFIGURED_STATE, +}; + +/** + * struct dwc3_trb - transfer request block + * @bpl: lower 32bit of the buffer + * @bph: higher 32bit of the buffer + * @length: buffer size (up to 16mb - 1) + * @pcm1: packet count m1 + * @trbsts: trb status + * 0 = ok + * 1 = missed isoc + * 2 = setup pending + * @hwo: hardware owner of descriptor + * @lst: last trb + * @chn: chain buffers + * @csp: continue on short packets (only supported on isoc eps) + * @trbctl: trb control + * 1 = normal + * 2 = control-setup + * 3 = control-status-2 + * 4 = control-status-3 + * 5 = control-data (first trb of data stage) + * 6 = isochronous-first (first trb of service interval) + * 7 = isochronous + * 8 = link trb + * others = reserved + * @isp_imi: interrupt on short packet / interrupt on missed isoc + * @ioc: interrupt on complete + * @sid_sofn: Stream ID / SOF Number + */ +struct dwc3_trb { + u64 bplh; + + union { + struct { + u32 length:24; + u32 pcm1:2; + u32 reserved27_26:2; + u32 trbsts:4; +#define DWC3_TRB_STS_OKAY 0 +#define DWC3_TRB_STS_MISSED_ISOC 1 +#define DWC3_TRB_STS_SETUP_PENDING 2 + }; + u32 len_pcm; + }; + + union { + struct { + u32 hwo:1; + u32 lst:1; + u32 chn:1; + u32 csp:1; + u32 trbctl:6; + u32 isp_imi:1; + u32 ioc:1; + u32 reserved13_12:2; + u32 sid_sofn:16; + u32 reserved31_30:2; + }; + u32 control; + }; +} __packed; + +/** + * struct dwc3_trb_hw - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @trl: DWC-F + */ +struct dwc3_trb_hw { + __le32 bpl; + __le32 bph; + __le32 size; + __le32 ctrl; +} __packed; + +static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw) +{ + hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh)); + hw->bph = cpu_to_le32(upper_32_bits(nat->bplh)); + hw->size = cpu_to_le32p(&nat->len_pcm); + /* HWO is written last */ + hw->ctrl = cpu_to_le32p(&nat->control); +} + +static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) +{ + u64 bplh; + + bplh = le32_to_cpup(&hw->bpl); + bplh |= (u64) le32_to_cpup(&hw->bph) << 32; + nat->bplh = bplh; + + nat->len_pcm = le32_to_cpup(&hw->size); + nat->control = le32_to_cpup(&hw->ctrl); +} + +/** + * dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0 - GHWPARAMS0 + * @hwparams1 - GHWPARAMS1 + * @hwparams2 - GHWPARAMS2 + * @hwparams3 - GHWPARAMS3 + * @hwparams4 - GHWPARAMS4 + * @hwparams5 - GHWPARAMS5 + * @hwparams6 - GHWPARAMS6 + * @hwparams7 - GHWPARAMS7 + * @hwparams8 - GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/** + * struct dwc3 - representation of our controller + * @ctrl_req: usb control request which is used for ep0 + * @ep0_trb: trb which is used for the ctrl_req + * @ep0_bounce: bounce buffer for ep0 + * @setup_buf: used while precessing STD USB requests + * @ctrl_req_addr: dma address of ctrl_req + * @ep0_trb: dma address of ep0_trb + * @ep0_usb_req: dummy req used while handling STD USB requests + * @setup_buf_addr: dma address of setup_buf + * @ep0_bounce_addr: dma address of ep0_bounce + * @lock: for synchronizing + * @dev: pointer to our struct device + * @event_buffer_list: a list of event buffers + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @regs: base address for our registers + * @regs_size: address space size + * @irq: IRQ number + * @revision: revision register contents + * @is_selfpowered: true when we are selfpowered + * @three_stage_setup: set if we perform a three phase setup + * @ep0_status_pending: ep0 status response without a req is pending + * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer + * @start_config_issued: true when StartConfig command has been issued + * @ep0_next_event: hold the next expected event + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @mem: points to start of memory which is used for this struct. + * @hwparams: copy of hwparams registers + * @root: debugfs root folder pointer + */ +struct dwc3 { + struct usb_ctrlrequest *ctrl_req; + struct dwc3_trb_hw *ep0_trb; + void *ep0_bounce; + u8 *setup_buf; + dma_addr_t ctrl_req_addr; + dma_addr_t ep0_trb_addr; + dma_addr_t setup_buf_addr; + dma_addr_t ep0_bounce_addr; + struct usb_request ep0_usb_req; + /* device lock */ + spinlock_t lock; + struct device *dev; + + struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM]; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + void __iomem *regs; + size_t regs_size; + + int irq; + + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a + + unsigned is_selfpowered:1; + unsigned three_stage_setup:1; + unsigned ep0_status_pending:1; + unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; + unsigned start_config_issued:1; + + enum dwc3_ep0_next ep0_next_event; + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; + enum dwc3_device_state dev_state; + + u8 speed; + void *mem; + + struct dwc3_hwparams hwparams; + struct dentry *root; +}; + +/* -------------------------------------------------------------------------- */ + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 + +#define DWC3_TRBCTL_NORMAL 1 +#define DWC3_TRBCTL_CONTROL_SETUP 2 +#define DWC3_TRBCTL_CONTROL_STATUS2 3 +#define DWC3_TRBCTL_CONTROL_STATUS3 4 +#define DWC3_TRBCTL_CONTROL_DATA 5 +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6 +#define DWC3_TRBCTL_ISOCHRONOUS 7 +#define DWC3_TRBCTL_LINK_TRB 8 + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:6; + u32 reserved8_31:25; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; +#define DEPEVT_STATUS_BUSERR (1 << 0) +#define DEPEVT_STATUS_SHORT (1 << 1) +#define DEPEVT_STATUS_IOC (1 << 2) +#define DEPEVT_STATUS_LST (1 << 3) + +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_SETUP 0 +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 + + u32 parameters:16; +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_24: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:8; + u32 reserved31_24:8; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 000000000000..5894ee8222af --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,50 @@ +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline int dwc3_debugfs_init(struct dwc3 *d) +{ return 0; } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif + diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c new file mode 100644 index 000000000000..da1ad77d8d51 --- /dev/null +++ b/drivers/usb/dwc3/debugfs.c @@ -0,0 +1,441 @@ +/** + * debugfs.c - DesignWare USB3 DRD Controller DebugFS file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/ptrace.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +struct dwc3_register { + const char *name; + u32 offset; +}; + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = DWC3_ ##nm, \ +} + +static const struct dwc3_register dwc3_regs[] = { + dump_register(GSBUSCFG0), + dump_register(GSBUSCFG1), + dump_register(GTXTHRCFG), + dump_register(GRXTHRCFG), + dump_register(GCTL), + dump_register(GEVTEN), + dump_register(GSTS), + dump_register(GSNPSID), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GUCTL), + dump_register(GBUSERRADDR0), + dump_register(GBUSERRADDR1), + dump_register(GPRTBIMAP0), + dump_register(GPRTBIMAP1), + dump_register(GHWPARAMS0), + dump_register(GHWPARAMS1), + dump_register(GHWPARAMS2), + dump_register(GHWPARAMS3), + dump_register(GHWPARAMS4), + dump_register(GHWPARAMS5), + dump_register(GHWPARAMS6), + dump_register(GHWPARAMS7), + dump_register(GDBGFIFOSPACE), + dump_register(GDBGLTSSM), + dump_register(GPRTBIMAP_HS0), + dump_register(GPRTBIMAP_HS1), + dump_register(GPRTBIMAP_FS0), + dump_register(GPRTBIMAP_FS1), + + dump_register(GUSB2PHYCFG(0)), + dump_register(GUSB2PHYCFG(1)), + dump_register(GUSB2PHYCFG(2)), + dump_register(GUSB2PHYCFG(3)), + dump_register(GUSB2PHYCFG(4)), + dump_register(GUSB2PHYCFG(5)), + dump_register(GUSB2PHYCFG(6)), + dump_register(GUSB2PHYCFG(7)), + dump_register(GUSB2PHYCFG(8)), + dump_register(GUSB2PHYCFG(9)), + dump_register(GUSB2PHYCFG(10)), + dump_register(GUSB2PHYCFG(11)), + dump_register(GUSB2PHYCFG(12)), + dump_register(GUSB2PHYCFG(13)), + dump_register(GUSB2PHYCFG(14)), + dump_register(GUSB2PHYCFG(15)), + + dump_register(GUSB2I2CCTL(0)), + dump_register(GUSB2I2CCTL(1)), + dump_register(GUSB2I2CCTL(2)), + dump_register(GUSB2I2CCTL(3)), + dump_register(GUSB2I2CCTL(4)), + dump_register(GUSB2I2CCTL(5)), + dump_register(GUSB2I2CCTL(6)), + dump_register(GUSB2I2CCTL(7)), + dump_register(GUSB2I2CCTL(8)), + dump_register(GUSB2I2CCTL(9)), + dump_register(GUSB2I2CCTL(10)), + dump_register(GUSB2I2CCTL(11)), + dump_register(GUSB2I2CCTL(12)), + dump_register(GUSB2I2CCTL(13)), + dump_register(GUSB2I2CCTL(14)), + dump_register(GUSB2I2CCTL(15)), + + dump_register(GUSB2PHYACC(0)), + dump_register(GUSB2PHYACC(1)), + dump_register(GUSB2PHYACC(2)), + dump_register(GUSB2PHYACC(3)), + dump_register(GUSB2PHYACC(4)), + dump_register(GUSB2PHYACC(5)), + dump_register(GUSB2PHYACC(6)), + dump_register(GUSB2PHYACC(7)), + dump_register(GUSB2PHYACC(8)), + dump_register(GUSB2PHYACC(9)), + dump_register(GUSB2PHYACC(10)), + dump_register(GUSB2PHYACC(11)), + dump_register(GUSB2PHYACC(12)), + dump_register(GUSB2PHYACC(13)), + dump_register(GUSB2PHYACC(14)), + dump_register(GUSB2PHYACC(15)), + + dump_register(GUSB3PIPECTL(0)), + dump_register(GUSB3PIPECTL(1)), + dump_register(GUSB3PIPECTL(2)), + dump_register(GUSB3PIPECTL(3)), + dump_register(GUSB3PIPECTL(4)), + dump_register(GUSB3PIPECTL(5)), + dump_register(GUSB3PIPECTL(6)), + dump_register(GUSB3PIPECTL(7)), + dump_register(GUSB3PIPECTL(8)), + dump_register(GUSB3PIPECTL(9)), + dump_register(GUSB3PIPECTL(10)), + dump_register(GUSB3PIPECTL(11)), + dump_register(GUSB3PIPECTL(12)), + dump_register(GUSB3PIPECTL(13)), + dump_register(GUSB3PIPECTL(14)), + dump_register(GUSB3PIPECTL(15)), + + dump_register(GTXFIFOSIZ(0)), + dump_register(GTXFIFOSIZ(1)), + dump_register(GTXFIFOSIZ(2)), + dump_register(GTXFIFOSIZ(3)), + dump_register(GTXFIFOSIZ(4)), + dump_register(GTXFIFOSIZ(5)), + dump_register(GTXFIFOSIZ(6)), + dump_register(GTXFIFOSIZ(7)), + dump_register(GTXFIFOSIZ(8)), + dump_register(GTXFIFOSIZ(9)), + dump_register(GTXFIFOSIZ(10)), + dump_register(GTXFIFOSIZ(11)), + dump_register(GTXFIFOSIZ(12)), + dump_register(GTXFIFOSIZ(13)), + dump_register(GTXFIFOSIZ(14)), + dump_register(GTXFIFOSIZ(15)), + dump_register(GTXFIFOSIZ(16)), + dump_register(GTXFIFOSIZ(17)), + dump_register(GTXFIFOSIZ(18)), + dump_register(GTXFIFOSIZ(19)), + dump_register(GTXFIFOSIZ(20)), + dump_register(GTXFIFOSIZ(21)), + dump_register(GTXFIFOSIZ(22)), + dump_register(GTXFIFOSIZ(23)), + dump_register(GTXFIFOSIZ(24)), + dump_register(GTXFIFOSIZ(25)), + dump_register(GTXFIFOSIZ(26)), + dump_register(GTXFIFOSIZ(27)), + dump_register(GTXFIFOSIZ(28)), + dump_register(GTXFIFOSIZ(29)), + dump_register(GTXFIFOSIZ(30)), + dump_register(GTXFIFOSIZ(31)), + + dump_register(GRXFIFOSIZ(0)), + dump_register(GRXFIFOSIZ(1)), + dump_register(GRXFIFOSIZ(2)), + dump_register(GRXFIFOSIZ(3)), + dump_register(GRXFIFOSIZ(4)), + dump_register(GRXFIFOSIZ(5)), + dump_register(GRXFIFOSIZ(6)), + dump_register(GRXFIFOSIZ(7)), + dump_register(GRXFIFOSIZ(8)), + dump_register(GRXFIFOSIZ(9)), + dump_register(GRXFIFOSIZ(10)), + dump_register(GRXFIFOSIZ(11)), + dump_register(GRXFIFOSIZ(12)), + dump_register(GRXFIFOSIZ(13)), + dump_register(GRXFIFOSIZ(14)), + dump_register(GRXFIFOSIZ(15)), + dump_register(GRXFIFOSIZ(16)), + dump_register(GRXFIFOSIZ(17)), + dump_register(GRXFIFOSIZ(18)), + dump_register(GRXFIFOSIZ(19)), + dump_register(GRXFIFOSIZ(20)), + dump_register(GRXFIFOSIZ(21)), + dump_register(GRXFIFOSIZ(22)), + dump_register(GRXFIFOSIZ(23)), + dump_register(GRXFIFOSIZ(24)), + dump_register(GRXFIFOSIZ(25)), + dump_register(GRXFIFOSIZ(26)), + dump_register(GRXFIFOSIZ(27)), + dump_register(GRXFIFOSIZ(28)), + dump_register(GRXFIFOSIZ(29)), + dump_register(GRXFIFOSIZ(30)), + dump_register(GRXFIFOSIZ(31)), + + dump_register(GEVNTADRLO(0)), + dump_register(GEVNTADRHI(0)), + dump_register(GEVNTSIZ(0)), + dump_register(GEVNTCOUNT(0)), + + dump_register(GHWPARAMS8), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DEVTEN), + dump_register(DSTS), + dump_register(DGCMDPAR), + dump_register(DGCMD), + dump_register(DALEPENA), + + dump_register(DEPCMDPAR2(0)), + dump_register(DEPCMDPAR2(1)), + dump_register(DEPCMDPAR2(2)), + dump_register(DEPCMDPAR2(3)), + dump_register(DEPCMDPAR2(4)), + dump_register(DEPCMDPAR2(5)), + dump_register(DEPCMDPAR2(6)), + dump_register(DEPCMDPAR2(7)), + dump_register(DEPCMDPAR2(8)), + dump_register(DEPCMDPAR2(9)), + dump_register(DEPCMDPAR2(10)), + dump_register(DEPCMDPAR2(11)), + dump_register(DEPCMDPAR2(12)), + dump_register(DEPCMDPAR2(13)), + dump_register(DEPCMDPAR2(14)), + dump_register(DEPCMDPAR2(15)), + dump_register(DEPCMDPAR2(16)), + dump_register(DEPCMDPAR2(17)), + dump_register(DEPCMDPAR2(18)), + dump_register(DEPCMDPAR2(19)), + dump_register(DEPCMDPAR2(20)), + dump_register(DEPCMDPAR2(21)), + dump_register(DEPCMDPAR2(22)), + dump_register(DEPCMDPAR2(23)), + dump_register(DEPCMDPAR2(24)), + dump_register(DEPCMDPAR2(25)), + dump_register(DEPCMDPAR2(26)), + dump_register(DEPCMDPAR2(27)), + dump_register(DEPCMDPAR2(28)), + dump_register(DEPCMDPAR2(29)), + dump_register(DEPCMDPAR2(30)), + dump_register(DEPCMDPAR2(31)), + + dump_register(DEPCMDPAR1(0)), + dump_register(DEPCMDPAR1(1)), + dump_register(DEPCMDPAR1(2)), + dump_register(DEPCMDPAR1(3)), + dump_register(DEPCMDPAR1(4)), + dump_register(DEPCMDPAR1(5)), + dump_register(DEPCMDPAR1(6)), + dump_register(DEPCMDPAR1(7)), + dump_register(DEPCMDPAR1(8)), + dump_register(DEPCMDPAR1(9)), + dump_register(DEPCMDPAR1(10)), + dump_register(DEPCMDPAR1(11)), + dump_register(DEPCMDPAR1(12)), + dump_register(DEPCMDPAR1(13)), + dump_register(DEPCMDPAR1(14)), + dump_register(DEPCMDPAR1(15)), + dump_register(DEPCMDPAR1(16)), + dump_register(DEPCMDPAR1(17)), + dump_register(DEPCMDPAR1(18)), + dump_register(DEPCMDPAR1(19)), + dump_register(DEPCMDPAR1(20)), + dump_register(DEPCMDPAR1(21)), + dump_register(DEPCMDPAR1(22)), + dump_register(DEPCMDPAR1(23)), + dump_register(DEPCMDPAR1(24)), + dump_register(DEPCMDPAR1(25)), + dump_register(DEPCMDPAR1(26)), + dump_register(DEPCMDPAR1(27)), + dump_register(DEPCMDPAR1(28)), + dump_register(DEPCMDPAR1(29)), + dump_register(DEPCMDPAR1(30)), + dump_register(DEPCMDPAR1(31)), + + dump_register(DEPCMDPAR0(0)), + dump_register(DEPCMDPAR0(1)), + dump_register(DEPCMDPAR0(2)), + dump_register(DEPCMDPAR0(3)), + dump_register(DEPCMDPAR0(4)), + dump_register(DEPCMDPAR0(5)), + dump_register(DEPCMDPAR0(6)), + dump_register(DEPCMDPAR0(7)), + dump_register(DEPCMDPAR0(8)), + dump_register(DEPCMDPAR0(9)), + dump_register(DEPCMDPAR0(10)), + dump_register(DEPCMDPAR0(11)), + dump_register(DEPCMDPAR0(12)), + dump_register(DEPCMDPAR0(13)), + dump_register(DEPCMDPAR0(14)), + dump_register(DEPCMDPAR0(15)), + dump_register(DEPCMDPAR0(16)), + dump_register(DEPCMDPAR0(17)), + dump_register(DEPCMDPAR0(18)), + dump_register(DEPCMDPAR0(19)), + dump_register(DEPCMDPAR0(20)), + dump_register(DEPCMDPAR0(21)), + dump_register(DEPCMDPAR0(22)), + dump_register(DEPCMDPAR0(23)), + dump_register(DEPCMDPAR0(24)), + dump_register(DEPCMDPAR0(25)), + dump_register(DEPCMDPAR0(26)), + dump_register(DEPCMDPAR0(27)), + dump_register(DEPCMDPAR0(28)), + dump_register(DEPCMDPAR0(29)), + dump_register(DEPCMDPAR0(30)), + dump_register(DEPCMDPAR0(31)), + + dump_register(DEPCMD(0)), + dump_register(DEPCMD(1)), + dump_register(DEPCMD(2)), + dump_register(DEPCMD(3)), + dump_register(DEPCMD(4)), + dump_register(DEPCMD(5)), + dump_register(DEPCMD(6)), + dump_register(DEPCMD(7)), + dump_register(DEPCMD(8)), + dump_register(DEPCMD(9)), + dump_register(DEPCMD(10)), + dump_register(DEPCMD(11)), + dump_register(DEPCMD(12)), + dump_register(DEPCMD(13)), + dump_register(DEPCMD(14)), + dump_register(DEPCMD(15)), + dump_register(DEPCMD(16)), + dump_register(DEPCMD(17)), + dump_register(DEPCMD(18)), + dump_register(DEPCMD(19)), + dump_register(DEPCMD(20)), + dump_register(DEPCMD(21)), + dump_register(DEPCMD(22)), + dump_register(DEPCMD(23)), + dump_register(DEPCMD(24)), + dump_register(DEPCMD(25)), + dump_register(DEPCMD(26)), + dump_register(DEPCMD(27)), + dump_register(DEPCMD(28)), + dump_register(DEPCMD(29)), + dump_register(DEPCMD(30)), + dump_register(DEPCMD(31)), + + dump_register(OCFG), + dump_register(OCTL), + dump_register(OEVTEN), + dump_register(OSTS), +}; + +static int dwc3_regdump_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + int i; + + seq_printf(s, "DesignWare USB3 Core Register Dump\n"); + + for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) { + seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name, + dwc3_readl(dwc->regs, dwc3_regs[i].offset)); + } + + return 0; +} + +static int dwc3_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_regdump_show, inode->i_private); +} + +static const struct file_operations dwc3_regdump_fops = { + .open = dwc3_regdump_open, + .read = seq_read, + .release = single_release, +}; + +int __devinit dwc3_debugfs_init(struct dwc3 *dwc) +{ + struct dentry *root; + struct dentry *file; + int ret; + + root = debugfs_create_dir(dev_name(dwc->dev), NULL); + if (IS_ERR(root)){ + ret = PTR_ERR(root); + goto err0; + } + + dwc->root = root; + + file = debugfs_create_file("regdump", S_IRUGO, root, dwc, + &dwc3_regdump_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + return 0; + +err1: + debugfs_remove_recursive(root); + +err0: + return ret; +} + +void __devexit dwc3_debugfs_exit(struct dwc3 *dwc) +{ + debugfs_remove_recursive(dwc->root); + dwc->root = NULL; +} diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c new file mode 100644 index 000000000000..062552b5fc8a --- /dev/null +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -0,0 +1,401 @@ +/** + * dwc3-omap.c - OMAP Specific Glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/platform_data/dwc3-omap.h> +#include <linux/dma-mapping.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/module.h> + +#include "io.h" + +/* + * All these registers belong to OMAP's Wrapper around the + * DesignWare USB3 Core. + */ + +#define USBOTGSS_REVISION 0x0000 +#define USBOTGSS_SYSCONFIG 0x0010 +#define USBOTGSS_IRQ_EOI 0x0020 +#define USBOTGSS_IRQSTATUS_RAW_0 0x0024 +#define USBOTGSS_IRQSTATUS_0 0x0028 +#define USBOTGSS_IRQENABLE_SET_0 0x002c +#define USBOTGSS_IRQENABLE_CLR_0 0x0030 +#define USBOTGSS_IRQSTATUS_RAW_1 0x0034 +#define USBOTGSS_IRQSTATUS_1 0x0038 +#define USBOTGSS_IRQENABLE_SET_1 0x003c +#define USBOTGSS_IRQENABLE_CLR_1 0x0040 +#define USBOTGSS_UTMI_OTG_CTRL 0x0080 +#define USBOTGSS_UTMI_OTG_STATUS 0x0084 +#define USBOTGSS_MMRAM_OFFSET 0x0100 +#define USBOTGSS_FLADJ 0x0104 +#define USBOTGSS_DEBUG_CFG 0x0108 +#define USBOTGSS_DEBUG_DATA 0x010c + +/* SYSCONFIG REGISTER */ +#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) +#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) + +#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0 +#define USBOTGSS_STANDBYMODE_NO_STANDBY 1 +#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2 +#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3 + +#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4) + +#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) + +#define USBOTGSS_IDLEMODE_FORCE_IDLE 0 +#define USBOTGSS_IDLEMODE_NO_IDLE 1 +#define USBOTGSS_IDLEMODE_SMART_IDLE 2 +#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3 + +#define USBOTGSS_IDLEMODE_MASK (0x03 << 2) + +/* IRQ_EOI REGISTER */ +#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) + +/* IRQS0 BITS */ +#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0) + +/* IRQ1 BITS */ +#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17) +#define USBOTGSS_IRQ1_OEVT (1 << 16) +#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13) +#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12) +#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11) +#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8) +#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5) +#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4) +#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3) +#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0) + +/* UTMI_OTG_CTRL REGISTER */ +#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5) +#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4) +#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3) +#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0) + +/* UTMI_OTG_STATUS REGISTER */ +#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31) +#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9) +#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8) +#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4) +#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3) +#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2) +#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) + +struct dwc3_omap { + /* device lock */ + spinlock_t lock; + + struct platform_device *dwc3; + struct device *dev; + + int irq; + void __iomem *base; + + void *context; + u32 resource_size; + + u32 dma_status:1; +}; + +static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) +{ + struct dwc3_omap *omap = _omap; + u32 reg; + + spin_lock(&omap->lock); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1); + + if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { + dev_dbg(omap->dev, "DMA Disable was Cleared\n"); + omap->dma_status = false; + } + + if (reg & USBOTGSS_IRQ1_OEVT) + dev_dbg(omap->dev, "OTG Event\n"); + + if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) + dev_dbg(omap->dev, "DRVVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) + dev_dbg(omap->dev, "CHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) + dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); + + if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) + dev_dbg(omap->dev, "IDPULLUP Rise\n"); + + if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) + dev_dbg(omap->dev, "DRVVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) + dev_dbg(omap->dev, "CHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) + dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); + + if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) + dev_dbg(omap->dev, "IDPULLUP Fall\n"); + + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0); + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg); + + spin_unlock(&omap->lock); + + return IRQ_HANDLED; +} + +static int __devinit dwc3_omap_probe(struct platform_device *pdev) +{ + struct dwc3_omap_data *pdata = pdev->dev.platform_data; + struct platform_device *dwc3; + struct dwc3_omap *omap; + struct resource *res; + + int ret = -ENOMEM; + int irq; + + u32 reg; + + void __iomem *base; + void *context; + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + + platform_set_drvdata(pdev, omap); + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ resource\n"); + ret = -EINVAL; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "missing memory base resource\n"); + ret = -EINVAL; + goto err1; + } + + base = ioremap_nocache(res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err1; + } + + dwc3 = platform_device_alloc("dwc3-omap", -1); + if (!dwc3) { + dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); + goto err2; + } + + context = kzalloc(resource_size(res), GFP_KERNEL); + if (!context) { + dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); + goto err3; + } + + spin_lock_init(&omap->lock); + dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); + + dwc3->dev.parent = &pdev->dev; + dwc3->dev.dma_mask = pdev->dev.dma_mask; + dwc3->dev.dma_parms = pdev->dev.dma_parms; + omap->resource_size = resource_size(res); + omap->context = context; + omap->dev = &pdev->dev; + omap->irq = irq; + omap->base = base; + omap->dwc3 = dwc3; + + reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform data\n"); + } else { + switch (pdata->utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n", + pdata->utmi_mode); + } + } + + dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); + + /* check the DMA Status */ + reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); + omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); + + /* Set No-Idle and No-Standby */ + reg &= ~(USBOTGSS_STANDBYMODE_MASK + | USBOTGSS_IDLEMODE_MASK); + + reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY) + | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE)); + + dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); + + ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, + "dwc3-omap", omap); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", + omap->irq, ret); + goto err4; + } + + /* enable all IRQs */ + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); + + reg = (USBOTGSS_IRQ1_OEVT | + USBOTGSS_IRQ1_DRVVBUS_RISE | + USBOTGSS_IRQ1_CHRGVBUS_RISE | + USBOTGSS_IRQ1_DISCHRGVBUS_RISE | + USBOTGSS_IRQ1_IDPULLUP_RISE | + USBOTGSS_IRQ1_DRVVBUS_FALL | + USBOTGSS_IRQ1_CHRGVBUS_FALL | + USBOTGSS_IRQ1_DISCHRGVBUS_FALL | + USBOTGSS_IRQ1_IDPULLUP_FALL); + + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); + + ret = platform_device_add_resources(dwc3, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); + goto err5; + } + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pdev->dev, "failed to register dwc3 device\n"); + goto err5; + } + + return 0; + +err5: + free_irq(omap->irq, omap); + +err4: + kfree(omap->context); + +err3: + platform_device_put(dwc3); + +err2: + iounmap(base); + +err1: + kfree(omap); + +err0: + return ret; +} + +static int __devexit dwc3_omap_remove(struct platform_device *pdev) +{ + struct dwc3_omap *omap = platform_get_drvdata(pdev); + + platform_device_unregister(omap->dwc3); + + free_irq(omap->irq, omap); + iounmap(omap->base); + + kfree(omap->context); + kfree(omap); + + return 0; +} + +static const struct of_device_id of_dwc3_matach[] = { + { + "ti,dwc3", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_dwc3_matach); + +static struct platform_driver dwc3_omap_driver = { + .probe = dwc3_omap_probe, + .remove = __devexit_p(dwc3_omap_remove), + .driver = { + .name = "omap-dwc3", + .of_match_table = of_dwc3_matach, + }, +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); + +static int __devinit dwc3_omap_init(void) +{ + return platform_driver_register(&dwc3_omap_driver); +} +module_init(dwc3_omap_init); + +static void __exit dwc3_omap_exit(void) +{ + platform_driver_unregister(&dwc3_omap_driver); +} +module_exit(dwc3_omap_exit); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c new file mode 100644 index 000000000000..f77c00042685 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -0,0 +1,219 @@ +/** + * dwc3-pci.c - PCI Specific glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +/* FIXME define these in <linux/pci_ids.h> */ +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd + +#define DWC3_PCI_DEVS_POSSIBLE 32 + +struct dwc3_pci { + struct device *dev; + struct platform_device *dwc3; +}; + +static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + +static int dwc3_pci_get_device_id(struct dwc3_pci *glue) +{ + int id; + +again: + id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + if (id < DWC3_PCI_DEVS_POSSIBLE) { + int old; + + old = test_and_set_bit(id, dwc3_pci_devs); + if (old) + goto again; + } else { + dev_err(glue->dev, "no space for new device\n"); + id = -ENOMEM; + } + + return 0; +} + +static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id) +{ + int ret; + + if (id < 0) + return; + + ret = test_bit(id, dwc3_pci_devs); + WARN(!ret, "Device: %s\nID %d not in use\n", + dev_driver_string(glue->dev), id); + clear_bit(id, dwc3_pci_devs); +} + +static int __devinit dwc3_pci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + struct resource res[2]; + struct platform_device *dwc3; + struct dwc3_pci *glue; + int ret = -ENOMEM; + int devid; + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pci->dev, "not enough memory\n"); + goto err0; + } + + glue->dev = &pci->dev; + + ret = pci_enable_device(pci); + if (ret) { + dev_err(&pci->dev, "failed to enable pci device\n"); + goto err1; + } + + pci_set_power_state(pci, PCI_D0); + pci_set_master(pci); + + devid = dwc3_pci_get_device_id(glue); + if (devid < 0) + goto err2; + + dwc3 = platform_device_alloc("dwc3-pci", devid); + if (!dwc3) { + dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); + goto err3; + } + + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); + + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc_usb3"; + res[0].flags = IORESOURCE_MEM; + + res[1].start = pci->irq; + res[1].name = "dwc_usb3"; + res[1].flags = IORESOURCE_IRQ; + + ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(&pci->dev, "couldn't add resources to dwc3 device\n"); + goto err4; + } + + pci_set_drvdata(pci, glue); + + dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); + + dwc3->dev.dma_mask = pci->dev.dma_mask; + dwc3->dev.dma_parms = pci->dev.dma_parms; + dwc3->dev.parent = &pci->dev; + glue->dwc3 = dwc3; + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pci->dev, "failed to register dwc3 device\n"); + goto err4; + } + + return 0; + +err4: + pci_set_drvdata(pci, NULL); + platform_device_put(dwc3); + +err3: + dwc3_pci_put_device_id(glue, devid); + +err2: + pci_disable_device(pci); + +err1: + kfree(pci); + +err0: + return ret; +} + +static void __devexit dwc3_pci_remove(struct pci_dev *pci) +{ + struct dwc3_pci *glue = pci_get_drvdata(pci); + + dwc3_pci_put_device_id(glue, glue->dwc3->id); + platform_device_unregister(glue->dwc3); + pci_set_drvdata(pci, NULL); + pci_disable_device(pci); + kfree(glue); +} + +static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), + }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); + +static struct pci_driver dwc3_pci_driver = { + .name = "pci-dwc3", + .id_table = dwc3_pci_id_table, + .probe = dwc3_pci_probe, + .remove = __devexit_p(dwc3_pci_remove), +}; + +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); + +static int __devinit dwc3_pci_init(void) +{ + return pci_register_driver(&dwc3_pci_driver); +} +module_init(dwc3_pci_init); + +static void __exit dwc3_pci_exit(void) +{ + pci_unregister_driver(&dwc3_pci_driver); +} +module_exit(dwc3_pci_exit); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c new file mode 100644 index 000000000000..69a4e43ddf59 --- /dev/null +++ b/drivers/usb/dwc3/ep0.c @@ -0,0 +1,804 @@ +/** + * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); + +static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; + default: + return "UNKNOWN"; + } +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, + u32 len, u32 type) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + struct dwc3_ep *dep; + + int ret; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); + return 0; + } + + trb_hw = dwc->ep0_trb; + memset(&trb, 0, sizeof(trb)); + + trb.trbctl = type; + trb.bplh = buf_dma; + trb.length = len; + + trb.hwo = 1; + trb.lst = 1; + trb.ioc = 1; + trb.isp_imi = 1; + + dwc3_trb_to_hw(&trb, trb_hw); + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(dwc->ep0_trb_addr); + params.param1 = lower_32_bits(dwc->ep0_trb_addr); + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_STARTTRANSFER, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + + return 0; +} + +static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + int ret = 0; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->epnum = dep->number; + + list_add_tail(&req->list, &dep->request_list); + + /* + * Gadget driver might not be quick enough to queue a request + * before we get a Transfer Not Ready event on this endpoint. + * + * In that case, we will set DWC3_EP_PENDING_REQUEST. When that + * flag is set, it's telling us that as soon as Gadget queues the + * required request, we should kick the transfer here because the + * IRQ we were waiting for is long gone. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + struct dwc3 *dwc = dep->dwc; + unsigned direction; + u32 type; + + direction = !!(dep->flags & DWC3_EP0_DIR_IN); + + if (dwc->ep0state == EP0_STATUS_PHASE) { + type = dwc->three_stage_setup + ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + } else if (dwc->ep0state == EP0_DATA_PHASE) { + type = DWC3_TRBCTL_CONTROL_DATA; + } else { + /* should never happen */ + WARN_ON(1); + return 0; + } + + ret = dwc3_ep0_start_trans(dwc, direction, + req->request.dma, req->request.length, type); + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | + DWC3_EP0_DIR_IN); + } + + return ret; +} + +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, dep->name); + ret = -ESHUTDOWN; + goto out; + } + + /* we share one TRB for ep0/1 */ + if (!list_empty(&dwc->eps[0]->request_list) || + !list_empty(&dwc->eps[1]->request_list) || + dwc->ep0_status_pending) { + ret = -EBUSY; + goto out; + } + + dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); + + ret = __dwc3_gadget_ep0_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +{ + struct dwc3_ep *dep = dwc->eps[0]; + + /* stall is always issued on EP0 */ + __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); + dwc->eps[0]->flags = DWC3_EP_ENABLED; + + if (!list_empty(&dep->request_list)) { + struct dwc3_request *req; + + req = next_request(&dep->request_list); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +void dwc3_ep0_out_start(struct dwc3 *dwc) +{ + int ret; + + ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + DWC3_TRBCTL_CONTROL_SETUP); + WARN_ON(ret < 0); +} + +static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) +{ + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; + + epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; + if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + epnum |= 1; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_ENABLED) + return dep; + + return NULL; +} + +static void dwc3_ep0_send_status_response(struct dwc3 *dwc) +{ + dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr, + dwc->ep0_usb_req.length, + DWC3_TRBCTL_CONTROL_DATA); +} + +/* + * ch 9.4.5 + */ +static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + u32 recip; + u16 usb_status = 0; + __le16 *response_pkt; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + /* + * We are self-powered. U1/U2/LTM will be set later + * once we handle this states. RemoteWakeup is 0 on SS + */ + usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + break; + + case USB_RECIP_INTERFACE: + /* + * Function Remote Wake Capable D0 + * Function Remote Wakeup D1 + */ + break; + + case USB_RECIP_ENDPOINT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (dep->flags & DWC3_EP_STALL) + usb_status = 1 << USB_ENDPOINT_HALT; + break; + default: + return -EINVAL; + }; + + response_pkt = (__le16 *) dwc->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + dwc->ep0_usb_req.length = sizeof(*response_pkt); + dwc->ep0_status_pending = 1; + + return 0; +} + +static int dwc3_ep0_handle_feature(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + struct dwc3_ep *dep; + u32 recip; + u32 wValue; + u32 wIndex; + u32 reg; + int ret; + u32 mode; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + + /* + * 9.4.1 says only only for SS, in AddressState only for + * default control pipe + */ + switch (wValue) { + case USB_DEVICE_U1_ENABLE: + case USB_DEVICE_U2_ENABLE: + case USB_DEVICE_LTM_ENABLE: + if (dwc->dev_state != DWC3_CONFIGURED_STATE) + return -EINVAL; + if (dwc->speed != DWC3_DSTS_SUPERSPEED) + return -EINVAL; + } + + /* XXX add U[12] & LTM */ + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + case USB_DEVICE_U1_ENABLE: + break; + case USB_DEVICE_U2_ENABLE: + break; + case USB_DEVICE_LTM_ENABLE: + break; + + case USB_DEVICE_TEST_MODE: + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + mode = wIndex >> 8; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + reg |= mode << 1; + break; + default: + return -EINVAL; + } + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_INTERFACE: + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + if (wIndex & USB_INTRF_FUNC_SUSPEND_LP) + /* XXX enable Low power suspend */ + ; + if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) + /* XXX enable remote wakeup */ + ; + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_ENDPOINT: + switch (wValue) { + case USB_ENDPOINT_HALT: + + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + ret = __dwc3_gadget_ep_set_halt(dep, set); + if (ret) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + }; + + return 0; +} + +static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u32 addr; + u32 reg; + + addr = le16_to_cpu(ctrl->wValue); + if (addr > 127) + return -EINVAL; + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + if (addr) + dwc->dev_state = DWC3_ADDRESS_STATE; + else + dwc->dev_state = DWC3_DEFAULT_STATE; + + return 0; +} + +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + spin_lock(&dwc->lock); + return ret; +} + +static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u32 cfg; + int ret; + + dwc->start_config_issued = false; + cfg = le16_to_cpu(ctrl->wValue); + + switch (dwc->dev_state) { + case DWC3_DEFAULT_STATE: + return -EINVAL; + break; + + case DWC3_ADDRESS_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + /* if the cfg matches and the cfg is non zero */ + if (!ret && cfg) + dwc->dev_state = DWC3_CONFIGURED_STATE; + break; + + case DWC3_CONFIGURED_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (!cfg) + dwc->dev_state = DWC3_ADDRESS_STATE; + break; + } + return 0; +} + +static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + ret = dwc3_ep0_handle_status(dwc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); + break; + case USB_REQ_SET_ADDRESS: + dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = dwc3_ep0_set_address(dwc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + ret = dwc3_ep0_set_config(dwc, ctrl); + break; + default: + dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + ret = dwc3_ep0_delegate_req(dwc, ctrl); + break; + }; + + return ret; +} + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct usb_ctrlrequest *ctrl = dwc->ctrl_req; + int ret; + u32 len; + + if (!dwc->gadget_driver) + goto err; + + len = le16_to_cpu(ctrl->wLength); + if (!len) { + dwc->three_stage_setup = false; + dwc->ep0_expect_in = false; + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + } else { + dwc->three_stage_setup = true; + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); + dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = dwc3_ep0_std_request(dwc, ctrl); + else + ret = dwc3_ep0_delegate_req(dwc, ctrl); + + if (ret >= 0) + return; + +err: + dwc3_ep0_stall_and_restart(dwc); +} + +static void dwc3_ep0_complete_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r = NULL; + struct usb_request *ur; + struct dwc3_trb trb; + struct dwc3_ep *dep; + u32 transferred; + u8 epnum; + + epnum = event->endpoint_number; + dep = dwc->eps[epnum]; + + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + + if (!dwc->ep0_status_pending) { + r = next_request(&dwc->eps[0]->request_list); + ur = &r->request; + } else { + ur = &dwc->ep0_usb_req; + dwc->ep0_status_pending = 0; + } + + dwc3_trb_to_nat(dwc->ep0_trb, &trb); + + if (dwc->ep0_bounced) { + struct dwc3_ep *ep0 = dwc->eps[0]; + + transferred = min_t(u32, ur->length, + ep0->endpoint.maxpacket - trb.length); + memcpy(ur->buf, dwc->ep0_bounce, transferred); + dwc->ep0_bounced = false; + } else { + transferred = ur->length - trb.length; + ur->actual += transferred; + } + + if ((epnum & 1) && ur->actual < ur->length) { + /* for some reason we did not get everything out */ + + dwc3_ep0_stall_and_restart(dwc); + } else { + /* + * handle the case where we have to send a zero packet. This + * seems to be case when req.length > maxpacket. Could it be? + */ + if (r) + dwc3_gadget_giveback(dep, r, 0); + } +} + +static void dwc3_ep0_complete_req(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r; + struct dwc3_ep *dep; + + dep = dwc->eps[0]; + + if (!list_empty(&dep->request_list)) { + r = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, r, 0); + } + + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + dep->flags &= ~DWC3_EP_BUSY; + + switch (dwc->ep0state) { + case EP0_SETUP_PHASE: + dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); + dwc3_ep0_inspect_setup(dwc, event); + break; + + case EP0_DATA_PHASE: + dev_vdbg(dwc->dev, "Data Phase\n"); + dwc3_ep0_complete_data(dwc, event); + break; + + case EP0_STATUS_PHASE: + dev_vdbg(dwc->dev, "Status Phase\n"); + dwc3_ep0_complete_req(dwc, event); + break; + default: + WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); + } +} + +static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_do_control_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + struct dwc3_request *req; + int ret; + + dep = dwc->eps[0]; + dwc->ep0state = EP0_DATA_PHASE; + + if (dwc->ep0_status_pending) { + dwc3_ep0_send_status_response(dwc); + return; + } + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); + dep->flags |= DWC3_EP_PENDING_REQUEST; + + if (event->endpoint_number) + dep->flags |= DWC3_EP0_DIR_IN; + return; + } + + req = next_request(&dep->request_list); + req->direction = !!event->endpoint_number; + + dwc->ep0state = EP0_DATA_PHASE; + if (req->request.length == 0) { + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA); + } else if ((req->request.length % dep->endpoint.maxpacket) + && (event->endpoint_number == 0)) { + dwc3_map_buffer_to_dma(req); + + WARN_ON(req->request.length > dep->endpoint.maxpacket); + + dwc->ep0_bounced = true; + + /* + * REVISIT in case request length is bigger than EP0 + * wMaxPacketSize, we will need two chained TRBs to handle + * the transfer. + */ + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ep0_bounce_addr, dep->endpoint.maxpacket, + DWC3_TRBCTL_CONTROL_DATA); + } else { + dwc3_map_buffer_to_dma(req); + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + req->request.dma, req->request.length, + DWC3_TRBCTL_CONTROL_DATA); + } + + WARN_ON(ret < 0); +} + +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + u32 type; + int ret; + + dwc->ep0state = EP0_STATUS_PHASE; + + type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, type); + + WARN_ON(ret < 0); +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (event->status) { + case DEPEVT_STATUS_CONTROL_SETUP: + dev_vdbg(dwc->dev, "Control Setup\n"); + dwc3_ep0_do_control_setup(dwc, event); + break; + + case DEPEVT_STATUS_CONTROL_DATA: + dev_vdbg(dwc->dev, "Control Data\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + dwc->ep0_next_event, + DWC3_EP0_NRDY_DATA); + + dwc3_ep0_stall_and_restart(dwc); + return; + } + + /* + * One of the possible error cases is when Host _does_ + * request for Data Phase, but it does so on the wrong + * direction. + * + * Here, we already know ep0_next_event is DATA (see above), + * so we only need to check for direction. + */ + if (dwc->ep0_expect_in != event->endpoint_number) { + dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_ep0_stall_and_restart(dwc); + return; + } + + dwc3_ep0_do_control_data(dwc, event); + break; + + case DEPEVT_STATUS_CONTROL_STATUS: + dev_vdbg(dwc->dev, "Control Status\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + dwc->ep0_next_event, + DWC3_EP0_NRDY_STATUS); + + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc3_ep0_do_control_status(dwc, event); + } +} + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const const struct dwc3_event_depevt *event) +{ + u8 epnum = event->endpoint_number; + + dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_ep_event_string(event->endpoint_event), + epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_ep0_xfer_complete(dwc, event); + break; + + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_ep0_xfernotready(dwc, event); + break; + + case DWC3_DEPEVT_XFERINPROGRESS: + case DWC3_DEPEVT_RXTXFIFOEVT: + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_EPCMDCMPLT: + break; + } +} diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c new file mode 100644 index 000000000000..fa824cfdd2eb --- /dev/null +++ b/drivers/usb/dwc3/gadget.c @@ -0,0 +1,2104 @@ +/** + * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +void dwc3_map_buffer_to_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->request.length == 0) { + /* req->request.dma = dwc->setup_buf_addr; */ + return; + } + + if (req->request.dma == DMA_ADDR_INVALID) { + req->request.dma = dma_map_single(dwc->dev, req->request.buf, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = true; + } +} + +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->request.length == 0) { + req->request.dma = DMA_ADDR_INVALID; + return; + } + + if (req->mapped) { + dma_unmap_single(dwc->dev, req->request.dma, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 0; + req->request.dma = DMA_ADDR_INVALID; + } +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + if (req->queued) { + dep->busy_slot++; + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we just + * completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + dep->busy_slot++; + } + list_del(&req->list); + + if (req->request.status == -EINPROGRESS) + req->request.status = status; + + dwc3_unmap_buffer_from_dma(req); + + dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", + req, dep->name, req->request.actual, + req->request.length, status); + + spin_unlock(&dwc->lock); + req->request.complete(&req->dep->endpoint, &req->request); + spin_lock(&dwc->lock); +} + +static const char *dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETSEQNUMBER: + return "Get Data Sequence Number"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + struct dwc3_ep *dep = dwc->eps[ep]; + u32 timeout = 500; + u32 reg; + + dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", + dep->name, + dwc3_gadget_ep_cmd_string(cmd), params->param0, + params->param1, params->param2); + + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); + + dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); + do { + reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); + if (!(reg & DWC3_DEPCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DEPCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it is also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + + udelay(1); + } while (1); +} + +static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, + struct dwc3_trb_hw *trb) +{ + u32 offset = (char *) trb - (char *) dep->trb_pool; + + return dep->trb_pool_dma + offset; +} + +static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + if (dep->trb_pool) + return 0; + + if (dep->number == 0 || dep->number == 1) + return 0; + + dep->trb_pool = dma_alloc_coherent(dwc->dev, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma, GFP_KERNEL); + if (!dep->trb_pool) { + dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", + dep->name); + return -ENOMEM; + } + + return 0; +} + +static void dwc3_free_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + dep->trb_pool, dep->trb_pool_dma); + + dep->trb_pool = NULL; + dep->trb_pool_dma = 0; +} + +static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + + memset(¶ms, 0x00, sizeof(params)); + + if (dep->number != 1) { + cmd = DWC3_DEPCMD_DEPSTARTCFG; + /* XferRscIdx == 0 for ep0 and 2 for the remaining */ + if (dep->number > 1) { + if (dwc->start_config_issued) + return 0; + dwc->start_config_issued = true; + cmd |= DWC3_DEPCMD_PARAM(2); + } + + return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); + } + + return 0; +} + +static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) + | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) + | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); + + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN + | DWC3_DEPCFG_XFER_NOT_READY_EN; + + if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { + params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_STREAM_EVENT_EN; + dep->stream_capable = true; + } + + if (usb_endpoint_xfer_isoc(desc)) + params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; + + /* + * We are doing 1:1 mapping for endpoints, meaning + * Physical Endpoints 2 maps to Logical Endpoint 2 and + * so on. We consider the direction bit as part of the physical + * endpoint number. So USB endpoint 0x81 is 0x03. + */ + params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); + + /* + * We must use the lower 16 TX FIFOs even though + * HW might have more + */ + if (dep->direction) + params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); + + if (desc->bInterval) { + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); + dep->interval = 1 << (desc->bInterval - 1); + } + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETEPCONFIG, ¶ms); +} + +static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); +} + +/** + * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * @dep: endpoint to be initialized + * @desc: USB Endpoint Descriptor + * + * Caller should take care of locking + */ +static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + int ret = -ENOMEM; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_start_config(dwc, dep); + if (ret) + return ret; + } + + ret = dwc3_gadget_set_ep_config(dwc, dep, desc); + if (ret) + return ret; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + struct dwc3_trb_hw *trb_st_hw; + struct dwc3_trb_hw *trb_link_hw; + struct dwc3_trb trb_link; + + ret = dwc3_gadget_set_xfer_resource(dwc, dep); + if (ret) + return ret; + + dep->desc = desc; + dep->type = usb_endpoint_type(desc); + dep->flags |= DWC3_EP_ENABLED; + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg |= DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + if (!usb_endpoint_xfer_isoc(desc)) + return 0; + + memset(&trb_link, 0, sizeof(trb_link)); + + /* Link TRB for ISOC. The HWO but is never reset */ + trb_st_hw = &dep->trb_pool[0]; + + trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw); + trb_link.trbctl = DWC3_TRBCTL_LINK_TRB; + trb_link.hwo = true; + + trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1]; + dwc3_trb_to_hw(&trb_link, trb_link_hw); + } + + return 0; +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_request *req; + + if (!list_empty(&dep->req_queued)) + dwc3_stop_active_transfer(dwc, dep->number); + + while (!list_empty(&dep->request_list)) { + req = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } +} + +/** + * __dwc3_gadget_ep_disable - Disables a HW endpoint + * @dep: the endpoint to disable + * + * This function also removes requests which are currently processed ny the + * hardware and those which are not yet scheduled. + * Caller should take care of locking. + */ +static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dwc3_remove_requests(dwc, dep); + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg &= ~DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + dep->stream_capable = false; + dep->desc = NULL; + dep->type = 0; + dep->flags = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int dwc3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + pr_debug("dwc3: missing wMaxPacketSize\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + strncat(dep->name, "-control", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_ISOC: + strncat(dep->name, "-isoc", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_BULK: + strncat(dep->name, "-bulk", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_INT: + strncat(dep->name, "-int", sizeof(dep->name)); + break; + default: + dev_err(dwc->dev, "invalid endpoint transfer type\n"); + } + + if (dep->flags & DWC3_EP_ENABLED) { + dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", + dep->name); + return 0; + } + + dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_enable(dep, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_disable(struct usb_ep *ep) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", + dep->name); + return 0; + } + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", + dep->number >> 1, + (dep->number & 1) ? "in" : "out"); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_disable(dep); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) { + dev_err(dwc->dev, "not enough memory\n"); + return NULL; + } + + req->epnum = dep->number; + req->dep = dep; + req->request.dma = DMA_ADDR_INVALID; + + return &req->request; +} + +static void dwc3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + + kfree(req); +} + +/* + * dwc3_prepare_trbs - setup TRBs from requests + * @dep: endpoint for which requests are being prepared + * @starting: true if the endpoint is idle and no requests are queued. + * + * The functions goes through the requests list and setups TRBs for the + * transfers. The functions returns once there are not more TRBs available or + * it run out of requests. + */ +static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, + bool starting) +{ + struct dwc3_request *req, *n, *ret = NULL; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + u32 trbs_left; + + BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + + /* the first request must not be queued */ + trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; + /* + * if busy & slot are equal than it is either full or empty. If we are + * starting to proceed requests then we are empty. Otherwise we ar + * full and don't do anything + */ + if (!trbs_left) { + if (!starting) + return NULL; + trbs_left = DWC3_TRB_NUM; + /* + * In case we start from scratch, we queue the ISOC requests + * starting from slot 1. This is done because we use ring + * buffer and have no LST bit to stop us. Instead, we place + * IOC bit TRB_NUM/4. We try to avoid to having an interrupt + * after the first request so we start at slot 1 and have + * 7 requests proceed before we hit the first IOC. + * Other transfer types don't use the ring buffer and are + * processed from the first TRB until the last one. Since we + * don't wrap around we have to start at the beginning. + */ + if (usb_endpoint_xfer_isoc(dep->desc)) { + dep->busy_slot = 1; + dep->free_slot = 1; + } else { + dep->busy_slot = 0; + dep->free_slot = 0; + } + } + + /* The last TRB is a link TRB, not used for xfer */ + if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc)) + return NULL; + + list_for_each_entry_safe(req, n, &dep->request_list, list) { + unsigned int last_one = 0; + unsigned int cur_slot; + + trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + cur_slot = dep->free_slot; + dep->free_slot++; + + /* Skip the LINK-TRB on ISOC */ + if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + continue; + + dwc3_gadget_move_request_queued(req); + memset(&trb, 0, sizeof(trb)); + trbs_left--; + + /* Is our TRB pool empty? */ + if (!trbs_left) + last_one = 1; + /* Is this the last request? */ + if (list_empty(&dep->request_list)) + last_one = 1; + + /* + * FIXME we shouldn't need to set LST bit always but we are + * facing some weird problem with the Hardware where it doesn't + * complete even though it has been previously started. + * + * While we're debugging the problem, as a workaround to + * multiple TRBs handling, use only one TRB at a time. + */ + last_one = 1; + + req->trb = trb_hw; + if (!ret) + ret = req; + + trb.bplh = req->request.dma; + + if (usb_endpoint_xfer_isoc(dep->desc)) { + trb.isp_imi = true; + trb.csp = true; + } else { + trb.lst = last_one; + } + + if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) + trb.sid_sofn = req->request.stream_id; + + switch (usb_endpoint_type(dep->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + + /* IOC every DWC3_TRB_NUM / 4 so we can refill */ + if (!(cur_slot % (DWC3_TRB_NUM / 4))) + trb.ioc = last_one; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb.trbctl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + BUG(); + } + + trb.length = req->request.length; + trb.hwo = true; + + dwc3_trb_to_hw(&trb, trb_hw); + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + + if (last_one) + break; + } + + return ret; +} + +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, + int start_new) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + int ret; + u32 cmd; + + if (start_new && (dep->flags & DWC3_EP_BUSY)) { + dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); + return -EBUSY; + } + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + /* + * If we are getting here after a short-out-packet we don't enqueue any + * new requests as we try to set the IOC bit only on the last request. + */ + if (start_new) { + if (list_empty(&dep->req_queued)) + dwc3_prepare_trbs(dep, start_new); + + /* req points to the first request which will be sent */ + req = next_request(&dep->req_queued); + } else { + /* + * req points to the first request where HWO changed + * from 0 to 1 + */ + req = dwc3_prepare_trbs(dep, start_new); + } + if (!req) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return 0; + } + + memset(¶ms, 0, sizeof(params)); + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); + + if (start_new) + cmd = DWC3_DEPCMD_STARTTRANSFER; + else + cmd = DWC3_DEPCMD_UPDATETRANSFER; + + cmd |= DWC3_DEPCMD_PARAM(cmd_param); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + + /* + * FIXME we need to iterate over the list of requests + * here and stop, unmap, free and del each of the linked + * requests instead of we do now. + */ + dwc3_unmap_buffer_from_dma(req); + list_del(&req->list); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + if (!dep->res_trans_idx) + printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__); + return 0; +} + +static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) +{ + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->direction = dep->direction; + req->epnum = dep->number; + + /* + * We only add to our list of requests now and + * start consuming the list once we get XferNotReady + * IRQ. + * + * That way, we avoid doing anything that we don't need + * to do now and defer it until the point we receive a + * particular token from the Host side. + * + * This will also avoid Host cancelling URBs due to too + * many NACKs. + */ + dwc3_map_buffer_to_dma(req); + list_add_tail(&req->list, &dep->request_list); + + /* + * There is one special case: XferNotReady with + * empty list of requests. We need to kick the + * transfer here in that situation, otherwise + * we will be NAKing forever. + * + * If we get XferNotReady before gadget driver + * has a chance to queue a request, we will ACK + * the IRQ but won't be able to receive the data + * until the next request is queued. The following + * code is handling exactly that. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + int ret; + int start_trans; + + start_trans = 1; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + dep->flags & DWC3_EP_BUSY) + start_trans = 0; + + ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + }; + + return 0; +} + +static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, ep->name); + return -ESHUTDOWN; + } + + dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", + request, ep->name, request->length); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dwc->lock, flags); + + list_for_each_entry(r, &dep->request_list, list) { + if (r == req) + break; + } + + if (r != req) { + list_for_each_entry(r, &dep->req_queued, list) { + if (r == req) + break; + } + if (r == req) { + /* wait until it is processed */ + dwc3_stop_active_transfer(dwc, dep->number); + goto out0; + } + dev_err(dwc->dev, "request %p was not queued to %s\n", + request, ep->name); + ret = -EINVAL; + goto out0; + } + + /* giveback the request */ + dwc3_gadget_giveback(dep, req, -ECONNRESET); + +out0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + int ret; + + memset(¶ms, 0x00, sizeof(params)); + + if (value) { + if (dep->number == 0 || dep->number == 1) { + /* + * Whenever EP0 is stalled, we will restart + * the state machine, thus moving back to + * Setup Phase + */ + dwc->ep0state = EP0_SETUP_PHASE; + } + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags |= DWC3_EP_STALL; + } else { + if (dep->flags & DWC3_EP_WEDGE) + return 0; + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags &= ~DWC3_EP_STALL; + } + + return ret; +} + +static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + ret = -EINVAL; + goto out; + } + + ret = __dwc3_gadget_ep_set_halt(dep, value); +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + + dep->flags |= DWC3_EP_WEDGE; + + return dwc3_gadget_ep_set_halt(ep, 1); +} + +/* -------------------------------------------------------------------------- */ + +static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static const struct usb_ep_ops dwc3_gadget_ep0_ops = { + .enable = dwc3_gadget_ep0_enable, + .disable = dwc3_gadget_ep0_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep0_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops dwc3_gadget_ep_ops = { + .enable = dwc3_gadget_ep_enable, + .disable = dwc3_gadget_ep_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_get_frame(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + return DWC3_DSTS_SOFFN(reg); +} + +static int dwc3_gadget_wakeup(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + unsigned long timeout; + unsigned long flags; + + u32 reg; + + int ret = 0; + + u8 link_state; + u8 speed; + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * According to the Databook Remote wakeup request should + * be issued only when the device is in early suspend state. + * + * We can check that via USB Link State bits in DSTS register. + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + speed = reg & DWC3_DSTS_CONNECTSPD; + if (speed == DWC3_DSTS_SUPERSPEED) { + dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); + ret = -EINVAL; + goto out; + } + + link_state = DWC3_DSTS_USBLNKST(reg); + + switch (link_state) { + case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ + case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + break; + default: + dev_dbg(dwc->dev, "can't wakeup from link state %d\n", + link_state); + ret = -EINVAL; + goto out; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + + /* + * Switch link state to Recovery. In HS/FS/LS this means + * RemoteWakeup Request + */ + reg |= DWC3_DCTL_ULSTCHNG_RECOVERY; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* wait for at least 2000us */ + usleep_range(2000, 2500); + + /* write zeroes to Link Change Request */ + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* pool until Link State change to ON */ + timeout = jiffies + msecs_to_jiffies(100); + + while (!(time_after(jiffies, timeout))) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) + break; + } + + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { + dev_err(dwc->dev, "failed to send remote wakeup\n"); + ret = -EINVAL; + } + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, + int is_selfpowered) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + dwc->is_selfpowered = !!is_selfpowered; + + return 0; +} + +static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +{ + u32 reg; + u32 timeout = 500; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (is_on) + reg |= DWC3_DCTL_RUN_STOP; + else + reg &= ~DWC3_DCTL_RUN_STOP; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (is_on) { + if (!(reg & DWC3_DSTS_DEVCTRLHLT)) + break; + } else { + if (reg & DWC3_DSTS_DEVCTRLHLT) + break; + } + timeout--; + if (!timeout) + break; + udelay(1); + } while (1); + + dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", + dwc->gadget_driver + ? dwc->gadget_driver->function : "no-function", + is_on ? "connect" : "disconnect"); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + is_on = !!is_on; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_run_stop(dwc, is_on); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_ep *dep; + unsigned long flags; + int ret = 0; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err0; + } + + dwc->gadget_driver = driver; + dwc->gadget.dev.driver = &driver->driver; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + + reg &= ~DWC3_GCTL_SCALEDOWN(3); + reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG); + reg &= ~DWC3_GCTL_DISSCRAMBLE; + reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); + + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + dev_dbg(dwc->dev, "No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * when The device fails to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter in a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + reg |= DWC3_DCFG_SUPERSPEED; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + dwc->start_config_issued = false; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err0; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err1; + } + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->gadget_driver = NULL; + dwc->gadget.dev.driver = NULL; + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} +static const struct usb_gadget_ops dwc3_gadget_ops = { + .get_frame = dwc3_gadget_get_frame, + .wakeup = dwc3_gadget_wakeup, + .set_selfpowered = dwc3_gadget_set_selfpowered, + .pullup = dwc3_gadget_pullup, + .udc_start = dwc3_gadget_start, + .udc_stop = dwc3_gadget_stop, +}; + +/* -------------------------------------------------------------------------- */ + +static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) { + dev_err(dwc->dev, "can't allocate endpoint %d\n", + epnum); + return -ENOMEM; + } + + dep->dwc = dwc; + dep->number = epnum; + dwc->eps[epnum] = dep; + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, + (epnum & 1) ? "in" : "out"); + dep->endpoint.name = dep->name; + dep->direction = (epnum & 1); + + if (epnum == 0 || epnum == 1) { + dep->endpoint.maxpacket = 512; + dep->endpoint.ops = &dwc3_gadget_ep0_ops; + if (!epnum) + dwc->gadget.ep0 = &dep->endpoint; + } else { + int ret; + + dep->endpoint.maxpacket = 1024; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + ret = dwc3_alloc_trb_pool(dep); + if (ret) { + dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name); + return ret; + } + } + INIT_LIST_HEAD(&dep->request_list); + INIT_LIST_HEAD(&dep->req_queued); + } + + return 0; +} + +static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = dwc->eps[epnum]; + dwc3_free_trb_pool(dep); + + if (epnum != 0 && epnum != 1) + list_del(&dep->endpoint.ep_list); + + kfree(dep); + } +} + +static void dwc3_gadget_release(struct device *dev) +{ + dev_dbg(dev, "%s\n", __func__); +} + +/* -------------------------------------------------------------------------- */ +static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) +{ + struct dwc3_request *req; + struct dwc3_trb trb; + unsigned int count; + unsigned int s_pkt = 0; + + do { + req = next_request(&dep->req_queued); + if (!req) + break; + + dwc3_trb_to_nat(req->trb, &trb); + + if (trb.hwo && status != -ESHUTDOWN) + /* + * We continue despite the error. There is not much we + * can do. If we don't clean in up we loop for ever. If + * we skip the TRB than it gets overwritten reused after + * a while since we use them in a ring buffer. a BUG() + * would help. Lets hope that if this occures, someone + * fixes the root cause instead of looking away :) + */ + dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", + dep->name, req->trb); + count = trb.length; + + if (dep->direction) { + if (count) { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } + } else { + if (count && (event->status & DEPEVT_STATUS_SHORT)) + s_pkt = 1; + } + + /* + * We assume here we will always receive the entire data block + * which we should receive. Meaning, if we program RX to + * receive 4K but we receive only 2K, we assume that's all we + * should receive and we simply bounce the request back to the + * gadget driver for further processing. + */ + req->request.actual += req->request.length - count; + dwc3_gadget_giveback(dep, req, status); + if (s_pkt) + break; + if ((event->status & DEPEVT_STATUS_LST) && trb.lst) + break; + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + break; + } while (1); + + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + return 0; + return 1; +} + +static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event, + int start_new) +{ + unsigned status = 0; + int clean_busy; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); + if (clean_busy) { + dep->flags &= ~DWC3_EP_BUSY; + dep->res_trans_idx = 0; + } +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + u32 uf; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", + dep->name); + return; + } + + if (event->parameters) { + u32 mask; + + mask = ~(dep->interval - 1); + uf = event->parameters & mask; + /* 4 micro frames in the future */ + uf += dep->interval * 4; + } else { + uf = 0; + } + + __dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + struct dwc3_event_depevt mod_ev = *event; + + /* + * We were asked to remove one requests. It is possible that this + * request and a few other were started together and have the same + * transfer index. Since we stopped the complete endpoint we don't + * know how many requests were already completed (and not yet) + * reported and how could be done (later). We purge them all until + * the end of the list. + */ + mod_ev.status = DEPEVT_STATUS_LST; + dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); + dep->flags &= ~DWC3_EP_BUSY; + /* pending requets are ignored and are queued on XferNotReady */ +} + +static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u32 param = event->parameters; + u32 cmd_type = (param >> 8) & ((1 << 5) - 1); + + switch (cmd_type) { + case DWC3_DEPCMD_ENDTRANSFER: + dwc3_process_ep_cmd_complete(dep, event); + break; + case DWC3_DEPCMD_STARTTRANSFER: + dep->res_trans_idx = param & 0x7f; + break; + default: + printk(KERN_ERR "%s() unknown /unexpected type: %d\n", + __func__, cmd_type); + break; + }; +} + +static void dwc3_endpoint_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; + + dep = dwc->eps[epnum]; + + dev_vdbg(dwc->dev, "%s: %s\n", dep->name, + dwc3_ep_event_string(event->endpoint_event)); + + if (epnum == 0 || epnum == 1) { + dwc3_ep0_interrupt(dwc, event); + return; + } + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + if (!usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + break; + case DWC3_DEPEVT_XFERNOTREADY: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dwc3_gadget_start_isoc(dwc, dep, event); + } else { + int ret; + + dev_vdbg(dwc->dev, "%s: reason %s\n", + dep->name, event->status + ? "Transfer Active" + : "Transfer Not Active"); + + ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + if (!ret || ret == -EBUSY) + return; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + + break; + case DWC3_DEPEVT_STREAMEVT: + if (!usb_endpoint_xfer_bulk(dep->desc)) { + dev_err(dwc->dev, "Stream event for non-Bulk %s\n", + dep->name); + return; + } + + switch (event->status) { + case DEPEVT_STREAMEVT_FOUND: + dev_vdbg(dwc->dev, "Stream %d found and started\n", + event->parameters); + + break; + case DEPEVT_STREAMEVT_NOTFOUND: + /* FALLTHROUGH */ + default: + dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); + } + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); + break; + case DWC3_DEPEVT_EPCMDCMPLT: + dwc3_ep_cmd_compl(dep, event); + break; + } +} + +static void dwc3_disconnect_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +{ + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + dep = dwc->eps[epnum]; + + WARN_ON(!dep->res_trans_idx); + if (dep->res_trans_idx) { + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + dep->res_trans_idx = 0; + } +} + +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + dwc3_remove_requests(dwc, dep); + } +} + +static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + int ret; + + dep = dwc->eps[epnum]; + + if (!(dep->flags & DWC3_EP_STALL)) + continue; + + dep->flags &= ~DWC3_EP_STALL; + + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + WARN_ON_ONCE(ret); + } +} + +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); +#if 0 + XXX + U1/U2 is powersave optimization. Skip it for now. Anyway we need to + enable it before we can disable it. + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); +#endif + + dwc3_stop_active_transfers(dwc); + dwc3_disconnect_gadget(dwc); + dwc->start_config_issued = false; + + dwc->gadget.speed = USB_SPEED_UNKNOWN; +} + +static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + if (on) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + else + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +} + +static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + if (on) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + else + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +} + +static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) +{ + u32 reg; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* Enable PHYs */ + dwc3_gadget_usb2_phy_power(dwc, true); + dwc3_gadget_usb3_phy_power(dwc, true); + + if (dwc->gadget.speed != USB_SPEED_UNKNOWN) + dwc3_disconnect_gadget(dwc); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_stop_active_transfers(dwc); + dwc3_clear_stall_all_ep(dwc); + dwc->start_config_issued = false; + + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + +static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) +{ + u32 reg; + u32 usb30_clock = DWC3_GCTL_CLK_BUS; + + /* + * We change the clock only at SS but I dunno why I would want to do + * this. Maybe it becomes part of the power saving plan. + */ + + if (speed != DWC3_DSTS_SUPERSPEED) + return; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + */ + if (!usb30_clock) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +{ + switch (speed) { + case USB_SPEED_SUPER: + dwc3_gadget_usb2_phy_power(dwc, false); + break; + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + case USB_SPEED_LOW: + dwc3_gadget_usb3_phy_power(dwc, false); + break; + } +} + +static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 speed; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + memset(¶ms, 0x00, sizeof(params)); + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + dwc3_update_ram_clk_sel(dwc, speed); + + switch (speed) { + case DWC3_DCFG_SUPERSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER; + break; + case DWC3_DCFG_HIGHSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_HIGH; + break; + case DWC3_DCFG_FULLSPEED2: + case DWC3_DCFG_FULLSPEED1: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_FULL; + break; + case DWC3_DCFG_LOWSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); + dwc->gadget.ep0->maxpacket = 8; + dwc->gadget.speed = USB_SPEED_LOW; + break; + } + + /* Disable unneded PHY */ + dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + /* + * Configure PHY via GUSB3PIPECTLn if required. + * + * Update GTXFIFOSIZn + * + * In both cases reset values should be sufficient. + */ +} + +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* + * TODO take core out of low power mode when that's + * implemented. + */ + + dwc->gadget_driver->resume(&dwc->gadget); +} + +static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + /* The fith bit says SuperSpeed yes or no. */ + dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; + + dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); +} + +static void dwc3_gadget_interrupt(struct dwc3 *dwc, + const struct dwc3_event_devt *event) +{ + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + dwc3_gadget_disconnect_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_RESET: + dwc3_gadget_reset_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + dwc3_gadget_conndone_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + dwc3_gadget_wakeup_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_EOPF: + dev_vdbg(dwc->dev, "End of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_SOF: + dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + dev_vdbg(dwc->dev, "Erratic Error\n"); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + dev_vdbg(dwc->dev, "Command Complete\n"); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + dev_vdbg(dwc->dev, "Overflow\n"); + break; + default: + dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + } +} + +static void dwc3_process_event_entry(struct dwc3 *dwc, + const union dwc3_event *event) +{ + /* Endpoint IRQ, handle it and return early */ + if (event->type.is_devspec == 0) { + /* depevt */ + return dwc3_endpoint_interrupt(dwc, &event->depevt); + } + + switch (event->type.type) { + case DWC3_EVENT_TYPE_DEV: + dwc3_gadget_interrupt(dwc, &event->devt); + break; + /* REVISIT what to do with Carkit and I2C events ? */ + default: + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); + } +} + +static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) +{ + struct dwc3_event_buffer *evt; + int left; + u32 count; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return IRQ_NONE; + + evt = dwc->ev_buffs[buf]; + left = count; + + while (left > 0) { + union dwc3_event event; + + memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw)); + dwc3_process_event_entry(dwc, &event); + /* + * XXX we wrap around correctly to the next entry as almost all + * entries are 4 bytes in size. There is one entry which has 12 + * bytes which is a regular entry followed by 8 bytes data. ATM + * I don't know how things are organized if were get next to the + * a boundary so I worry about that once we try to handle that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dwc3_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + int i; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&dwc->lock); + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + irqreturn_t status; + + status = dwc3_process_event_buf(dwc, i); + if (status == IRQ_HANDLED) + ret = status; + } + + spin_unlock(&dwc->lock); + + return ret; +} + +/** + * dwc3_gadget_init - Initializes gadget related registers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +int __devinit dwc3_gadget_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + int irq; + + dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + &dwc->ctrl_req_addr, GFP_KERNEL); + if (!dwc->ctrl_req) { + dev_err(dwc->dev, "failed to allocate ctrl request\n"); + ret = -ENOMEM; + goto err0; + } + + dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + &dwc->ep0_trb_addr, GFP_KERNEL); + if (!dwc->ep0_trb) { + dev_err(dwc->dev, "failed to allocate ep0 trb\n"); + ret = -ENOMEM; + goto err1; + } + + dwc->setup_buf = dma_alloc_coherent(dwc->dev, + sizeof(*dwc->setup_buf) * 2, + &dwc->setup_buf_addr, GFP_KERNEL); + if (!dwc->setup_buf) { + dev_err(dwc->dev, "failed to allocate setup buffer\n"); + ret = -ENOMEM; + goto err2; + } + + dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, + 512, &dwc->ep0_bounce_addr, GFP_KERNEL); + if (!dwc->ep0_bounce) { + dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err3; + } + + dev_set_name(&dwc->gadget.dev, "gadget"); + + dwc->gadget.ops = &dwc3_gadget_ops; + dwc->gadget.is_dualspeed = true; + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.dev.parent = dwc->dev; + + dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask); + + dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; + dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; + dwc->gadget.dev.release = dwc3_gadget_release; + dwc->gadget.name = "dwc3-gadget"; + + /* + * REVISIT: Here we should clear all pending IRQs to be + * sure we're starting from a well known location. + */ + + ret = dwc3_gadget_init_endpoints(dwc); + if (ret) + goto err4; + + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, + "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err5; + } + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + + ret = device_register(&dwc->gadget.dev); + if (ret) { + dev_err(dwc->dev, "failed to register gadget device\n"); + put_device(&dwc->gadget.dev); + goto err6; + } + + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); + if (ret) { + dev_err(dwc->dev, "failed to register udc\n"); + goto err7; + } + + return 0; + +err7: + device_unregister(&dwc->gadget.dev); + +err6: + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + +err5: + dwc3_gadget_free_endpoints(dwc); + +err4: + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + +err3: + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + +err2: + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + +err1: + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + +err0: + return ret; +} + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + int irq; + int i; + + usb_del_gadget_udc(&dwc->gadget); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + + for (i = 0; i < ARRAY_SIZE(dwc->eps); i++) + __dwc3_gadget_ep_disable(dwc->eps[i]); + + dwc3_gadget_free_endpoints(dwc); + + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + + device_unregister(&dwc->gadget.dev); +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h new file mode 100644 index 000000000000..71145a449d99 --- /dev/null +++ b/drivers/usb/dwc3/gadget.h @@ -0,0 +1,211 @@ +/** + * gadget.h - DesignWare USB3 DRD Gadget Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_GADGET_H +#define __DRIVERS_USB_DWC3_GADGET_H + +#include <linux/list.h> +#include <linux/usb/gadget.h> +#include "io.h" + +struct dwc3; +#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) +#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) + +/* DEPCFG parameter 1 */ +#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) +#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) +#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_BULK_BASED (1 << 30) +#define DWC3_DEPCFG_FIFO_BASED (1 << 31) + +/* DEPCFG parameter 0 */ +#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) + +/* DEPXFERCFG parameter 0 */ +#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) + +struct dwc3_gadget_ep_cmd_params { + u32 param2; + u32 param1; + u32 param0; +}; + +/* -------------------------------------------------------------------------- */ + +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + + u8 epnum; + struct dwc3_trb_hw *trb; + dma_addr_t trb_dma; + + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; +#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) + +static inline struct dwc3_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + + return list_first_entry(list, struct dwc3_request, list); +} + +static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->queued = true; + list_move_tail(&req->list, &dep->req_queued); +} + +#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE) +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + return 0; +} +#endif + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status); + +void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); +void dwc3_ep0_out_start(struct dwc3 *dwc); +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +void dwc3_map_buffer_to_dma(struct dwc3_request *req); +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); + +/** + * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW + * @dwc: DesignWare USB3 Pointer + * @number: DWC endpoint number + * + * Caller should take care of locking + */ +static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) +{ + u32 res_id; + + res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); + + return DWC3_DEPCMD_GET_RSC_IDX(res_id); +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +#endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 000000000000..bc957db1ea4b --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,54 @@ +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi <balbi@ti.com>, + * Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include <asm/io.h> + +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +#endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5a084b9cfa3c..b21cd376c11a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -96,6 +96,22 @@ config USB_GADGET_VBUS_DRAW This value will be used except for system-specific gadget drivers that have more specific information. +config USB_GADGET_STORAGE_NUM_BUFFERS + int "Number of storage pipeline buffers" + range 2 4 + default 2 + help + Usually 2 buffers are enough to establish a good buffering + pipeline. The number may be increased in order to compensate + for a bursty VFS behaviour. For instance there may be CPU wake up + latencies that makes the VFS to appear bursty in a system with + an CPU on-demand governor. Especially if DMA is doing IO to + offload the CPU. In this case the CPU will go into power + save often and spin up occasionally to move data within VFS. + If selecting USB_GADGET_DEBUG_FILES this value may be set by + a module parameter as well. + If unsure, say 2. + # # USB Peripheral Controller Support # @@ -255,12 +271,11 @@ config USB_S3C_HSOTG integrated into the S3C64XX series SoC. config USB_IMX - tristate "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 + tristate "Freescale i.MX1 USB Peripheral Controller" + depends on ARCH_MXC help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. + Freescale's i.MX1 includes an integrated full speed + USB 1.1 device controller. It has Six fixed-function endpoints, as well as endpoint zero (for control transfers). @@ -303,6 +318,18 @@ config USB_PXA_U2O PXA9xx Processor series include a high speed USB2.0 device controller, which support high speed and full speed USB peripheral. +config USB_GADGET_DWC3 + tristate "DesignWare USB3.0 (DRD) Controller" + depends on USB_DWC3 + select USB_GADGET_DUALSPEED + select USB_GADGET_SUPERSPEED + help + DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller + which can be configured for peripheral-only, host-only, hub-only + and Dual-Role operation. This Controller was first integrated into + the OMAP5 series of processors. More information about the OMAP5 + version of this controller, refer to http://www.ti.com/omap5. + # # Controllers available in both integrated and discrete versions # @@ -846,6 +873,16 @@ config USB_G_NOKIA It's only really useful for N900 hardware. If you're building a kernel for N900, say Y or M here. If unsure, say N. +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + config USB_G_MULTI tristate "Multifunction Composite Gadget (EXPERIMENTAL)" depends on BLOCK && NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ba725af4a08..b54ac6190890 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o -mv_udc-y := mv_udc_core.o mv_udc_phy.o +mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o @@ -51,6 +51,7 @@ g_dbgp-y := dbgp.o g_nokia-y := nokia.o g_webcam-y := webcam.o g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -69,3 +70,4 @@ obj-$(CONFIG_USB_G_MULTI) += g_multi.o obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c new file mode 100644 index 000000000000..fdb7aec3bd0c --- /dev/null +++ b/drivers/usb/gadget/acm_ms.c @@ -0,0 +1,256 @@ +/* + * acm_ms.c -- Composite driver, with ACM and mass storage support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Author: David Brownell + * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de> + * + * Heavily based on multi.c and cdc2.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/utsname.h> + +#include "u_serial.h" + +#define DRIVER_DESC "Composite Gadget (ACM + MS)" +#define DRIVER_VERSION "2011/10/10" + +/*-------------------------------------------------------------------------*/ + +/* + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ + +/*-------------------------------------------------------------------------*/ + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "u_serial.c" +#include "f_acm.c" +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM), + .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + /*.bNumConfigurations = DYNAMIC*/ +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +static struct fsg_common fsg_common; + +/*-------------------------------------------------------------------------*/ + +/* + * We _always_ have both ACM and mass storage functions. + */ +static int __init acm_ms_do_config(struct usb_configuration *c) +{ + int status; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + + status = acm_bind_config(c, 0); + if (status < 0) + return status; + + status = fsg_bind_config(c->cdev, c, &fsg_common); + if (status < 0) + return status; + + return 0; +} + +static struct usb_configuration acm_ms_config_driver = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init acm_ms_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; + void *retp; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + return status; + + /* set up mass storage function */ + retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); + if (IS_ERR(retp)) { + status = PTR_ERR(retp); + goto fail0; + } + + /* set bcdDevice */ + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) { + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + } else { + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + acm_ms_config_driver.label); + device_desc.bcdDevice = + cpu_to_le16(0x0300 | 0x0099); + } + + /* + * Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + /* register our configuration */ + status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); + if (status < 0) + goto fail1; + + dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", + DRIVER_DESC); + fsg_common_put(&fsg_common); + return 0; + + /* error recovery */ +fail1: + fsg_common_put(&fsg_common); +fail0: + gserial_cleanup(); + return status; +} + +static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + + return 0; +} + +static struct usb_composite_driver acm_ms_driver = { + .name = "g_acm_ms", + .dev = &device_desc, + .strings = dev_strings, + .unbind = __exit_p(acm_ms_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); +MODULE_LICENSE("GPL v2"); + +static int __init init(void) +{ + return usb_composite_probe(&acm_ms_driver, acm_ms_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&acm_ms_driver); +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 70f2b376c86d..4730016d7cd4 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* @@ -354,7 +345,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) writel(tmp, &dev->ep[ep->num].regs->ctl); /* set max packet size */ - maxpacket = le16_to_cpu(desc->wMaxPacketSize); + maxpacket = usb_endpoint_maxp(desc); tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE); ep->ep.maxpacket = maxpacket; @@ -3014,13 +3005,8 @@ __acquires(dev->lock) /* link up all endpoints */ udc_setup_endpoints(dev); - if (dev->gadget.speed == USB_SPEED_HIGH) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "high"); - } else if (dev->gadget.speed == USB_SPEED_FULL) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "full"); - } + dev_info(&dev->pdev->dev, "Connect: %s\n", + usb_speed_string(dev->gadget.speed)); /* init ep 0 */ activate_control_endpoints(dev); diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index 1d1c7543468e..f87e29c65325 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AMD5536UDC_H diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index ddb118a76807..8efe0fa9228d 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -9,16 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. */ #undef VERBOSE_DEBUG @@ -460,7 +450,7 @@ static void nuke(struct at91_ep *ep, int status) { struct at91_request *req; - // terminer chaque requete dans la queue + /* terminate any request in the queue */ ep->stopped = 1; if (list_empty(&ep->queue)) return; @@ -487,7 +477,7 @@ static int at91_ep_enable(struct usb_ep *_ep, || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT - || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 + || (maxpacket = usb_endpoint_maxp(desc)) == 0 || maxpacket > ep->maxpacket) { DBG("bad ep or descriptor\n"); return -EINVAL; @@ -788,7 +778,7 @@ static const struct usb_ep_ops at91_ep_ops = { .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, - // there's only imprecise fifo status reporting + /* there's only imprecise fifo status reporting */ }; /*-------------------------------------------------------------------------*/ @@ -846,7 +836,7 @@ static void udc_reinit(struct at91_udc *udc) ep->fifo_bank = 0; ep->ep.maxpacket = ep->maxpacket; ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); - // initialiser une queue par endpoint + /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); } } @@ -952,7 +942,7 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - // VDBG("vbus %s\n", is_active ? "on" : "off"); + /* VDBG("vbus %s\n", is_active ? "on" : "off"); */ spin_lock_irqsave(&udc->lock, flags); udc->vbus = (is_active != 0); if (udc->driver) @@ -1003,7 +993,7 @@ static const struct usb_gadget_ops at91_udc_ops = { * VBUS-powered devices may also also want to support bigger * power budgets after an appropriate SET_CONFIGURATION. */ - // .vbus_power = at91_vbus_power, + /* .vbus_power = at91_vbus_power, */ }; /*-------------------------------------------------------------------------*/ @@ -1072,7 +1062,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) ep->is_in = 0; } } else { - // REVISIT this happens sometimes under load; why?? + /* REVISIT this happens sometimes under load; why?? */ ERR("SETUP len %d, csr %08x\n", rxcount, csr); status = -EINVAL; } @@ -1451,7 +1441,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP); - // VDBG("bus suspend\n"); + /* VDBG("bus suspend\n"); */ if (udc->suspended) continue; udc->suspended = 1; @@ -1473,7 +1463,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); - // VDBG("bus resume\n"); + /* VDBG("bus resume\n"); */ if (!udc->suspended) continue; udc->suspended = 0; @@ -1820,7 +1810,7 @@ static int __init at91udc_probe(struct platform_device *pdev) /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); retval = request_irq(udc->udp_irq, at91_udc_irq, - IRQF_DISABLED, driver_name, udc); + 0, driver_name, udc); if (retval < 0) { DBG("request irq %d failed\n", udc->udp_irq); goto fail1; @@ -1848,7 +1838,7 @@ static int __init at91udc_probe(struct platform_device *pdev) jiffies + VBUS_POLL_TIMEOUT); } else { if (request_irq(udc->board.vbus_pin, at91_vbus_irq, - IRQF_DISABLED, driver_name, udc)) { + 0, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); retval = -EBUSY; diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index 108ca54f9092..3c0315b86ace 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -7,16 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef AT91_UDC_H diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 5b1665eb1bef..271a9d873608 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -527,7 +527,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); - maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index) || ep->index == 0 @@ -571,7 +571,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * Bits 11:12 specify number of _additional_ * transactions per microframe. */ - nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1; + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; if (nr_trans > 3) return -EINVAL; @@ -1718,13 +1718,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) spin_lock(&udc->lock); } - if (status & USBA_HIGH_SPEED) { - DBG(DBG_BUS, "High-speed bus reset detected\n"); + if (status & USBA_HIGH_SPEED) udc->gadget.speed = USB_SPEED_HIGH; - } else { - DBG(DBG_BUS, "Full-speed bus reset detected\n"); + else udc->gadget.speed = USB_SPEED_FULL; - } + DBG(DBG_BUS, "%s bus reset detected\n", + usb_speed_string(udc->gadget.speed)); ep0 = &usba_ep[0]; ep0->desc = &usba_ep0_desc; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index b1c1afbb8750..672674c2fb3d 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c index 470981ad6f77..4eedfe557154 100644 --- a/drivers/usb/gadget/ci13xxx_msm.c +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -3,17 +3,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #include <linux/module.h> diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 1265a8502ea0..83428f56253b 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2101,7 +2101,7 @@ static int ep_enable(struct usb_ep *ep, mEp->num = usb_endpoint_num(desc); mEp->type = usb_endpoint_type(desc); - mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); + mEp->ep.maxpacket = usb_endpoint_maxp(desc); dbg_event(_usb_addr(mEp), "ENABLE", 0); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index aef47414f5d5..8a5529d214fb 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ @@ -164,7 +155,7 @@ int config_ep_by_speed(struct usb_gadget *g, ep_found: /* commit results */ - _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); + _ep->maxpacket = usb_endpoint_maxp(chosen_desc); _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; @@ -551,9 +542,9 @@ static int bos_desc(struct usb_composite_dev *cdev) if (cdev->gadget->ops->get_config_params) cdev->gadget->ops->get_config_params(&dcd_config_params); else { - dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT; dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT); + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; @@ -626,25 +617,9 @@ static int set_config(struct usb_composite_dev *cdev, result = 0; } - INFO(cdev, "%s speed config #%d: %s\n", - ({ char *speed; - switch (gadget->speed) { - case USB_SPEED_LOW: - speed = "low"; - break; - case USB_SPEED_FULL: - speed = "full"; - break; - case USB_SPEED_HIGH: - speed = "high"; - break; - case USB_SPEED_SUPER: - speed = "super"; - break; - default: - speed = "?"; - break; - } ; speed; }), number, c ? c->label : "unconfigured"); + INFO(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b2c001334876..7542a72ce51a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/errno.h> diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 8beefdd36787..f855ecf7a637 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -4,7 +4,6 @@ * Copyright (C) 2010 Stephane Duverger * * Released under the GPLv2. - * */ /* verbose messages */ diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index e755a9d267fc..ab8f1b488d54 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -439,7 +430,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * maximum packet size. * For SS devices the wMaxPacketSize is limited by 1024. */ - max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + max = usb_endpoint_maxp(desc) & 0x7ff; /* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints @@ -1277,7 +1268,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) int tmp; /* high bandwidth mode */ - tmp = le16_to_cpu(ep->desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(ep->desc); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 7a7e6b7e1fd6..596a0b464e61 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -7,16 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/kernel.h> @@ -158,7 +148,7 @@ ep_matches ( * where it's an output parameter representing the full speed limit. * the usb spec fixes high speed bulk maxpacket at 512 bytes. */ - max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); + max = 0x7ff & usb_endpoint_maxp(desc); switch (type) { case USB_ENDPOINT_XFER_INT: /* INT: limit 64 bytes full speed, 1024 high/super speed */ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index aafc84f33e26..0cd764d59351 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index a9a4eade7e80..ec7ffcd0d0cd 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -460,7 +460,7 @@ static int audio_set_endpoint_req(struct usb_function *f, switch (ctrl->bRequest) { case UAC_SET_CUR: - value = 0; + value = len; break; case UAC_SET_MIN: @@ -499,7 +499,7 @@ static int audio_get_endpoint_req(struct usb_function *f, case UAC_GET_MIN: case UAC_GET_MAX: case UAC_GET_RES: - value = 3; + value = len; break; case UAC_GET_MEM: break; @@ -681,17 +681,18 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) status = -ENOMEM; - /* supcard all relevant hardware speeds... we expect that when + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(f_audio_desc); + + /* + * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - - /* copy descriptors, and track endpoint copies */ if (gadget_is_dualspeed(c->cdev->gadget)) { c->highspeed = true; f->hs_descriptors = usb_copy_descriptors(f_audio_desc); - } else - f->descriptors = usb_copy_descriptors(f_audio_desc); + } return 0; diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 3691a0cb9465..11c07cb7d337 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 046c6d0e6960..1a7b2dd7d408 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index c161a9aaeb7e..6b1c20b6c9b2 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -12,15 +12,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 83a266bdb40e..b2113420b806 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index ca660d40b11a..6d87f288df4e 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5b9339582007..52583a235330 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -112,8 +112,7 @@ * is not loaded (an empty string as "filename" in the fsg_config * structure causes error). The CD-ROM emulation includes a single * data track and no audio tracks; hence there need be only one - * backing file per LUN. Note also that the CD-ROM block length is - * set to 512 rather than the more common value 2048. + * backing file per LUN. * * * MSF includes support for module parameters. If gadget using it @@ -363,7 +362,7 @@ struct fsg_common { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + struct fsg_buffhd *buffhds; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; @@ -745,7 +744,6 @@ static int do_read(struct fsg_common *common) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* @@ -771,7 +769,7 @@ static int do_read(struct fsg_common *common) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = common->data_size_from_cmnd; @@ -784,18 +782,10 @@ static int do_read(struct fsg_common *common) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int)PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; @@ -812,7 +802,8 @@ static int do_read(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -835,18 +826,25 @@ static int do_read(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int)nread, amount); - nread -= (nread & 511); /* Round down to a block */ + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; common->residue -= nread; + + /* + * Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -877,7 +875,6 @@ static int do_write(struct fsg_common *common) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -921,7 +918,7 @@ static int do_write(struct fsg_common *common) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = common->data_size_from_cmnd; amount_left_to_write = common->data_size_from_cmnd; @@ -933,41 +930,21 @@ static int do_write(struct fsg_common *common) /* * Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. + * Try to get the remaining amount, + * but not more than the buffer size. */ amount = min(amount_left_to_req, FSG_BUFLEN); - amount = min((loff_t)amount, - curlun->file_length - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int)PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = + usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= amount & 511; - if (amount == 0) { - - /* - * Why were we were asked to transfer a - * partial block? - */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -977,12 +954,11 @@ static int do_write(struct fsg_common *common) get_some_more = 0; /* - * amount is always divisible by 512, hence by - * the bulk-out maxpacket size + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -1002,7 +978,8 @@ static int do_write(struct fsg_common *common) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1016,6 +993,16 @@ static int do_write(struct fsg_common *common) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1033,8 +1020,7 @@ static int do_write(struct fsg_common *common) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int)nwritten, amount); - nwritten -= (nwritten & 511); - /* Round down to a block */ + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1043,13 +1029,15 @@ static int do_write(struct fsg_common *common) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } + empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { common->short_packet_received = 1; break; } @@ -1129,8 +1117,8 @@ static int do_verify(struct fsg_common *common) return -EIO; /* No default reply */ /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1148,8 +1136,6 @@ static int do_verify(struct fsg_common *common) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, @@ -1157,7 +1143,8 @@ static int do_verify(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1179,11 +1166,12 @@ static int do_verify(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int)nread, amount); - nread -= nread & 511; /* Round down to a sector */ + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1289,7 +1277,7 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ return 8; } @@ -1527,7 +1515,7 @@ static int do_read_format_capacities(struct fsg_common *common, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -1607,7 +1595,7 @@ static int throw_away_data(struct fsg_common *common) common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(common, FSG_STATE_ABORT_BULK_OUT); @@ -1623,12 +1611,11 @@ static int throw_away_data(struct fsg_common *common) amount = min(common->usb_amount_left, FSG_BUFLEN); /* - * amount is always divisible by 512, hence by + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -2022,7 +2009,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)"); @@ -2032,7 +2020,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)"); @@ -2042,7 +2031,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)"); @@ -2142,7 +2132,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)"); @@ -2152,7 +2143,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)"); @@ -2162,7 +2154,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)"); @@ -2297,7 +2290,6 @@ static int get_next_command(struct fsg_common *common) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Don't know what to do if common->fsg is NULL */ return -EIO; @@ -2348,7 +2340,7 @@ reset: if (common->fsg) { fsg = common->fsg; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; if (bh->inreq) { @@ -2401,12 +2393,11 @@ reset: goto reset; fsg->bulk_out->driver_data = common; fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = - le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize); + common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; rc = alloc_request(common, fsg->bulk_in, &bh->inreq); @@ -2475,7 +2466,7 @@ static void handle_exception(struct fsg_common *common) /* Cancel all the pending transfers */ if (likely(common->fsg)) { - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); @@ -2487,7 +2478,7 @@ static void handle_exception(struct fsg_common *common) /* Wait until everything is idle */ for (;;) { int num_active = 0; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2510,7 +2501,7 @@ static void handle_exception(struct fsg_common *common) */ spin_lock_irq(&common->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -2719,6 +2710,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, int nluns, i, rc; char *pathbuf; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return ERR_PTR(rc); + /* Find out how many LUNs there should be */ nluns = cfg->nluns; if (nluns < 1 || nluns > FSG_MAX_LUNS) { @@ -2737,6 +2732,14 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->free_storage_on_release = 0; } + common->buffhds = kcalloc(fsg_num_buffers, + sizeof *(common->buffhds), GFP_KERNEL); + if (!common->buffhds) { + if (common->free_storage_on_release) + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->ops = cfg->ops; common->private_data = cfg->private_data; @@ -2814,7 +2817,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, /* Data buffers cyclic list */ bh = common->buffhds; - i = FSG_NUM_BUFFERS; + i = fsg_num_buffers; goto buffhds_first_it; do { bh->next = bh + 1; @@ -2940,12 +2943,13 @@ static void fsg_common_release(struct kref *ref) { struct fsg_buffhd *bh = common->buffhds; - unsigned i = FSG_NUM_BUFFERS; + unsigned i = fsg_num_buffers; do { kfree(bh->buf); } while (++bh, --i); } + kfree(common->buffhds); if (common->free_storage_on_release) kfree(common); } @@ -3019,6 +3023,28 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) } } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + f->ss_descriptors = usb_copy_descriptors(fsg_ss_function); + if (unlikely(!f->ss_descriptors)) { + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + return -ENOMEM; + } + } + return 0; autoconf_fail: diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c new file mode 100644 index 000000000000..67b222908cf9 --- /dev/null +++ b/drivers/usb/gadget/f_midi.c @@ -0,0 +1,998 @@ +/* + * f_midi.c -- USB MIDI class function driver + * + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Developed for Thumtronics by Grey Innovation + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Rewritten for the composite framework + * Copyright (C) 2011 Daniel Mack <zonque@gmail.com> + * + * Based on drivers/usb/gadget/f_audio.c, + * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> + * Copyright (C) 2008 Analog Devices, Inc + * + * and drivers/usb/gadget/midi.c, + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Ben Williamson <ben.williamson@greyinnovation.com> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/utsname.h> +#include <linux/device.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/audio.h> +#include <linux/usb/midi.h> + +MODULE_AUTHOR("Ben Williamson"); +MODULE_LICENSE("GPL v2"); + +static const char f_midi_shortname[] = "f_midi"; +static const char f_midi_longname[] = "MIDI Gadget"; + +/* + * We can only handle 16 cables on one single endpoint, as cable numbers are + * stored in 4-bit fields. And as the interface currently only holds one + * single endpoint, this is the maximum number of ports we can allow. + */ +#define MAX_PORTS 16 + +/* + * This is a gadget, and the IN/OUT naming is from the host's perspective. + * USB -> OUT endpoint -> rawmidi + * USB <- IN endpoint <- rawmidi + */ +struct gmidi_in_port { + struct f_midi *midi; + int active; + uint8_t cable; + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; +}; + +struct f_midi { + struct usb_function func; + struct usb_gadget *gadget; + struct usb_ep *in_ep, *out_ep; + struct snd_card *card; + struct snd_rawmidi *rmidi; + + struct snd_rawmidi_substream *in_substream[MAX_PORTS]; + struct snd_rawmidi_substream *out_substream[MAX_PORTS]; + struct gmidi_in_port *in_port[MAX_PORTS]; + + unsigned long out_triggered; + struct tasklet_struct tasklet; + unsigned int in_ports; + unsigned int out_ports; + int index; + char *id; + unsigned int buflen, qlen; +}; + +static inline struct f_midi *func_to_midi(struct usb_function *f) +{ + return container_of(f, struct f_midi, func); +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(16); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + /* .bNumEndpoints = DYNAMIC */ + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + /* .iInterface = DYNAMIC */ +}; + +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdADC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), + .bInCollection = 1, + /* .baInterfaceNr = DYNAMIC */ +}; + +/* B.4.1 Standard MS Interface Descriptor */ +static struct usb_interface_descriptor ms_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + /* .iInterface = DYNAMIC */ +}; + +/* B.4.2 Class-Specific MS Interface Descriptor */ +static struct usb_ms_header_descriptor ms_header_desc __initdata = { + .bLength = USB_DT_MS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = cpu_to_le16(0x0100), + /* .wTotalLength = DYNAMIC */ +}; + +/* B.4.3 Embedded MIDI IN Jack Descriptor */ +static struct usb_midi_in_jack_descriptor jack_in_emb_desc = { + .bLength = USB_DT_MIDI_IN_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EMBEDDED, + /* .bJackID = DYNAMIC */ +}; + +/* B.4.4 Embedded MIDI OUT Jack Descriptor */ +static struct usb_midi_out_jack_descriptor_16 jack_out_emb_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EMBEDDED, + /* .bJackID = DYNAMIC */ + /* .bNrInputPins = DYNAMIC */ + /* .pins = DYNAMIC */ +}; + +/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* B.6.1 Standard Bulk IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ +static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { + /* .bLength = DYNAMIC */ + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ +}; + +/* string IDs are assigned dynamically */ + +#define STRING_FUNC_IDX 0 + +static struct usb_string midi_string_defs[] = { + [STRING_FUNC_IDX].s = "MIDI function", + { } /* end of list */ +}; + +static struct usb_gadget_strings midi_stringtab = { + .language = 0x0409, /* en-us */ + .strings = midi_string_defs, +}; + +static struct usb_gadget_strings *midi_strings[] = { + &midi_stringtab, + NULL, +}; + +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = kmalloc(length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static const uint8_t f_midi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Receives a chunk of MIDI data. + */ +static void f_midi_read_data(struct usb_ep *ep, int cable, + uint8_t *data, int length) +{ + struct f_midi *midi = ep->driver_data; + struct snd_rawmidi_substream *substream = midi->out_substream[cable]; + + if (!substream) + /* Nobody is listening - throw it on the floor. */ + return; + + if (!test_bit(cable, &midi->out_triggered)) + return; + + snd_rawmidi_receive(substream, data, length); +} + +static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int i; + u8 *buf = req->buf; + + for (i = 0; i + 3 < req->actual; i += 4) + if (buf[i] != 0) { + int cable = buf[i] >> 4; + int length = f_midi_cin_length[buf[i] & 0x0f]; + f_midi_read_data(ep, cable, &buf[i + 1], length); + } +} + +static void +f_midi_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_midi *midi = ep->driver_data; + struct usb_composite_dev *cdev = midi->func.config->cdev; + int status = req->status; + + switch (status) { + case 0: /* normal completion */ + if (ep == midi->out_ep) { + /* We received stuff. req is queued again, below */ + f_midi_handle_out_data(ep, req); + } else if (ep == midi->in_ep) { + /* Our transmit completed. See if there's more to go. + * f_midi_transmit eats req, don't queue it again. */ + f_midi_transmit(midi, req); + return; + } + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == midi->out_ep) + f_midi_handle_out_data(ep, req); + + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough buffer. + */ + default: + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + break; + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int f_midi_start_ep(struct f_midi *midi, + struct usb_function *f, + struct usb_ep *ep) +{ + int err; + struct usb_composite_dev *cdev = f->config->cdev; + + if (ep->driver_data) + usb_ep_disable(ep); + + err = config_ep_by_speed(midi->gadget, f, ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", ep->name, err); + return err; + } + + err = usb_ep_enable(ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", ep->name, err); + return err; + } + + ep->driver_data = midi; + + return 0; +} + +static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + unsigned i; + int err; + + err = f_midi_start_ep(midi, f, midi->in_ep); + if (err) + return err; + + err = f_midi_start_ep(midi, f, midi->out_ep); + if (err) + return err; + + if (midi->out_ep->driver_data) + usb_ep_disable(midi->out_ep); + + err = config_ep_by_speed(midi->gadget, f, midi->out_ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", + midi->out_ep->name, err); + return err; + } + + err = usb_ep_enable(midi->out_ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", + midi->out_ep->name, err); + return err; + } + + midi->out_ep->driver_data = midi; + + /* allocate a bunch of read buffers and queue them all at once. */ + for (i = 0; i < midi->qlen && err == 0; i++) { + struct usb_request *req = + alloc_ep_req(midi->out_ep, midi->buflen); + if (req == NULL) + return -ENOMEM; + + req->complete = f_midi_complete; + err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); + if (err) { + ERROR(midi, "%s queue req: %d\n", + midi->out_ep->name, err); + } + } + + return 0; +} + +static void f_midi_disable(struct usb_function *f) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "disable\n"); + + /* + * just disable endpoints, forcing completion of pending i/o. + * all our completion handlers free their requests in this case. + */ + usb_ep_disable(midi->in_ep); + usb_ep_disable(midi->out_ep); +} + +static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_midi *midi = func_to_midi(f); + struct snd_card *card; + + DBG(cdev, "unbind\n"); + + /* just to be sure */ + f_midi_disable(f); + + card = midi->card; + midi->card = NULL; + if (card) + snd_card_free(card); + + kfree(midi->id); + midi->id = NULL; + + usb_free_descriptors(f->descriptors); + kfree(midi); +} + +static int f_midi_snd_free(struct snd_device *device) +{ + return 0; +} + +static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + unsigned length = req->length; + u8 *buf = (u8 *)req->buf + length; + + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + req->length = length + 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void f_midi_transmit_byte(struct usb_request *req, + struct gmidi_in_port *port, uint8_t b) +{ + uint8_t p0 = port->cable << 4; + + if (b >= 0xf8) { + f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + f_midi_transmit_packet(req, + p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + f_midi_transmit_packet(req, + p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x07, port->data[0], + port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, + p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) +{ + struct usb_ep *ep = midi->in_ep; + int i; + + if (!ep) + return; + + if (!req) + req = alloc_ep_req(ep, midi->buflen); + + if (!req) { + ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + return; + } + req->length = 0; + req->complete = f_midi_complete; + + for (i = 0; i < MAX_PORTS; i++) { + struct gmidi_in_port *port = midi->in_port[i]; + struct snd_rawmidi_substream *substream = midi->in_substream[i]; + + if (!port || !port->active || !substream) + continue; + + while (req->length + 3 < midi->buflen) { + uint8_t b; + if (snd_rawmidi_transmit(substream, &b, 1) != 1) { + port->active = 0; + break; + } + f_midi_transmit_byte(req, port, b); + } + } + + if (req->length > 0) + usb_ep_queue(ep, req, GFP_ATOMIC); + else + free_ep_req(ep, req); +} + +static void f_midi_in_tasklet(unsigned long data) +{ + struct f_midi *midi = (struct f_midi *) data; + f_midi_transmit(midi, NULL); +} + +static int f_midi_in_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->in_substream[substream->number] = substream; + midi->in_port[substream->number]->state = STATE_UNKNOWN; + return 0; +} + +static int f_midi_in_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (!midi->in_port[substream->number]) + return; + + VDBG(midi, "%s() %d\n", __func__, up); + midi->in_port[substream->number]->active = up; + if (up) + tasklet_hi_schedule(&midi->tasklet); +} + +static int f_midi_out_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + if (substream->number >= MAX_PORTS) + return -EINVAL; + + VDBG(midi, "%s()\n", __func__); + midi->out_substream[substream->number] = substream; + return 0; +} + +static int f_midi_out_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + + if (up) + set_bit(substream->number, &midi->out_triggered); + else + clear_bit(substream->number, &midi->out_triggered); +} + +static struct snd_rawmidi_ops gmidi_in_ops = { + .open = f_midi_in_open, + .close = f_midi_in_close, + .trigger = f_midi_in_trigger, +}; + +static struct snd_rawmidi_ops gmidi_out_ops = { + .open = f_midi_out_open, + .close = f_midi_out_close, + .trigger = f_midi_out_trigger +}; + +/* register as a sound "card" */ +static int f_midi_register_card(struct f_midi *midi) +{ + struct snd_card *card; + struct snd_rawmidi *rmidi; + int err; + static struct snd_device_ops ops = { + .dev_free = f_midi_snd_free, + }; + + err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + if (err < 0) { + ERROR(midi, "snd_card_create() failed\n"); + goto fail; + } + midi->card = card; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops); + if (err < 0) { + ERROR(midi, "snd_device_new() failed: error %d\n", err); + goto fail; + } + + strcpy(card->driver, f_midi_longname); + strcpy(card->longname, f_midi_longname); + strcpy(card->shortname, f_midi_shortname); + + /* Set up rawmidi */ + snd_component_add(card, "MIDI"); + err = snd_rawmidi_new(card, card->longname, 0, + midi->out_ports, midi->in_ports, &rmidi); + if (err < 0) { + ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err); + goto fail; + } + midi->rmidi = rmidi; + strcpy(rmidi->name, card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + + /* + * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. + * It's an upside-down world being a gadget. + */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); + + snd_card_set_dev(card, &midi->gadget->dev); + + /* register it - we're ready to go */ + err = snd_card_register(card); + if (err < 0) { + ERROR(midi, "snd_card_register() failed\n"); + goto fail; + } + + VDBG(midi, "%s() finished ok\n", __func__); + return 0; + +fail: + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } + return err; +} + +/* MIDI function driver setup/binding */ + +static int __init +f_midi_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_descriptor_header *midi_function[(MAX_PORTS * 2) + 12]; + struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; + struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; + struct usb_composite_dev *cdev = c->cdev; + struct f_midi *midi = func_to_midi(f); + int status, n, jack = 1, i = 0; + + /* maybe allocate device-global string ID */ + if (midi_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + goto fail; + midi_string_defs[0].id = status; + } + + /* We have two interfaces, AudioControl and MIDIStreaming */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ms_interface_desc.bInterfaceNumber = status; + ac_header_desc.baInterfaceNr[0] = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); + if (!midi->in_ep) + goto fail; + midi->in_ep->driver_data = cdev; /* claim */ + + midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc); + if (!midi->out_ep) + goto fail; + midi->out_ep->driver_data = cdev; /* claim */ + + /* + * construct the function's descriptor set. As the number of + * input and output MIDI ports is configurable, we have to do + * it that way. + */ + + /* add the headers - these are always the same */ + midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc; + + /* calculate the header's wTotalLength */ + n = USB_DT_MS_HEADER_SIZE + + (1 + midi->in_ports) * USB_DT_MIDI_IN_SIZE + + (1 + midi->out_ports) * USB_DT_MIDI_OUT_SIZE(1); + ms_header_desc.wTotalLength = cpu_to_le16(n); + + midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; + + /* we have one embedded IN jack */ + jack_in_emb_desc.bJackID = jack++; + midi_function[i++] = (struct usb_descriptor_header *) &jack_in_emb_desc; + + /* and a dynamic amount of external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + struct usb_midi_in_jack_descriptor *ext = &jack_in_ext_desc[n]; + + ext->bLength = USB_DT_MIDI_IN_SIZE; + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->iJack = 0; + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* one embedded OUT jack ... */ + jack_out_emb_desc.bLength = USB_DT_MIDI_OUT_SIZE(midi->in_ports); + jack_out_emb_desc.bJackID = jack++; + jack_out_emb_desc.bNrInputPins = midi->in_ports; + /* ... which referencess all external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + jack_out_emb_desc.pins[n].baSourceID = jack_in_ext_desc[n].bJackID; + jack_out_emb_desc.pins[n].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) &jack_out_emb_desc; + + /* and multiple external OUT jacks ... */ + for (n = 0; n < midi->out_ports; n++) { + struct usb_midi_out_jack_descriptor_1 *ext = &jack_out_ext_desc[n]; + int m; + + ext->bLength = USB_DT_MIDI_OUT_SIZE(1); + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->bNrInputPins = 1; + ext->iJack = 0; + /* ... which all reference the same embedded IN jack */ + for (m = 0; m < midi->out_ports; m++) { + ext->pins[m].baSourceID = jack_in_emb_desc.bJackID; + ext->pins[m].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* configure the endpoint descriptors ... */ + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + for (n = 0; n < midi->in_ports; n++) + ms_out_desc.baAssocJackID[n] = jack_in_emb_desc.bJackID; + + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + for (n = 0; n < midi->out_ports; n++) + ms_in_desc.baAssocJackID[n] = jack_out_emb_desc.bJackID; + + /* ... and add them to the list */ + midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc; + midi_function[i++] = NULL; + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + /* copy descriptors, and track endpoint copies */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + c->highspeed = true; + bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); + f->hs_descriptors = usb_copy_descriptors(midi_function); + } else { + f->descriptors = usb_copy_descriptors(midi_function); + } + + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (midi->out_ep) + midi->out_ep->driver_data = NULL; + if (midi->in_ep) + midi->in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +/** + * f_midi_bind_config - add USB MIDI function to a configuration + * @c: the configuration to supcard the USB audio function + * @index: the soundcard index to use for the ALSA device creation + * @id: the soundcard id to use for the ALSA device creation + * @buflen: the buffer length to use + * @qlen the number of read requests to pre-allocate + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +int __init f_midi_bind_config(struct usb_configuration *c, + int index, char *id, + unsigned int in_ports, + unsigned int out_ports, + unsigned int buflen, + unsigned int qlen) +{ + struct f_midi *midi; + int status, i; + + /* sanity check */ + if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) + return -EINVAL; + + /* allocate and initialize one new instance */ + midi = kzalloc(sizeof *midi, GFP_KERNEL); + if (!midi) { + status = -ENOMEM; + goto fail; + } + + for (i = 0; i < in_ports; i++) { + struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + status = -ENOMEM; + goto setup_fail; + } + + port->midi = midi; + port->active = 0; + port->cable = i; + midi->in_port[i] = port; + } + + midi->gadget = c->cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + + /* set up ALSA midi devices */ + midi->in_ports = in_ports; + midi->out_ports = out_ports; + status = f_midi_register_card(midi); + if (status < 0) + goto setup_fail; + + midi->func.name = "gmidi function"; + midi->func.strings = midi_strings; + midi->func.bind = f_midi_bind; + midi->func.unbind = f_midi_unbind; + midi->func.set_alt = f_midi_set_alt; + midi->func.disable = f_midi_disable; + + midi->id = kstrdup(id, GFP_KERNEL); + midi->index = index; + midi->buflen = buflen; + midi->qlen = qlen; + + status = usb_add_function(c, &midi->func); + if (status) + goto setup_fail; + + return 0; + +setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); + kfree(midi); +fail: + return status; +} + diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index ae69ed7e6b99..aab8eded045b 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -13,15 +13,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 394502abeb96..e3f74bf5da2d 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 8f3eab1af885..349077033338 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include <linux/mm.h> diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 3ea4666be3d0..704d1d94f72a 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -11,15 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index e18b4f520951..168906d2b5d4 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 3dc53754ab60..c1540648125a 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/slab.h> diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 7a8b9aa4aea5..2022fe492148 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -8,7 +8,6 @@ * 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. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index e18a6636c283..abf832935134 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -8,7 +8,6 @@ * 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 _F_UVC_H_ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 639e14a2fd15..3ac4f51cd0bb 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -69,8 +69,7 @@ * each LUN would be settable independently as a disk drive or a CD-ROM * drive, but currently all LUNs have to be the same type. The CD-ROM * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. Note also that the CD-ROM block - * length is set to 512 rather than the more common value 2048. + * need be only one backing file per LUN. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are * needed (an interrupt-out endpoint is also needed for CBI). The memory @@ -461,7 +460,6 @@ struct fsg_dev { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; int thread_wakeup_needed; struct completion thread_notifier; @@ -488,6 +486,8 @@ struct fsg_dev { unsigned int nluns; struct fsg_lun *luns; struct fsg_lun *curlun; + /* Must be the last entry */ + struct fsg_buffhd buffhds[]; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -586,7 +586,19 @@ dev_qualifier = { .bNumConfigurations = 1, }; +static int populate_bos(struct fsg_dev *fsg, u8 *buf) +{ + memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE); + buf += USB_DT_BOS_SIZE; + + memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE); + buf += USB_DT_USB_EXT_CAP_SIZE; + memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE); + + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE + + USB_DT_USB_EXT_CAP_SIZE; +} /* * Config descriptors must agree with the code that sets configurations @@ -935,7 +947,8 @@ static int standard_setup_req(struct fsg_dev *fsg, break; case USB_DT_DEVICE_QUALIFIER: VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; /* * Assume ep0 uses the same maxpacket value for both @@ -948,7 +961,8 @@ static int standard_setup_req(struct fsg_dev *fsg, case USB_DT_OTHER_SPEED_CONFIG: VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; goto get_config; case USB_DT_CONFIG: @@ -967,7 +981,15 @@ get_config: value = usb_gadget_get_string(&fsg_stringtab, w_value & 0xff, req->buf); break; + + case USB_DT_BOS: + VDBG(fsg, "get bos descriptor\n"); + + if (gadget_is_superspeed(fsg->gadget)) + value = populate_bos(fsg, req->buf); + break; } + break; /* One config, two speeds */ @@ -1136,7 +1158,6 @@ static int do_read(struct fsg_dev *fsg) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* Get the starting Logical Block Address and check that it's @@ -1158,7 +1179,7 @@ static int do_read(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; @@ -1171,17 +1192,10 @@ static int do_read(struct fsg_dev *fsg) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; @@ -1196,7 +1210,7 @@ static int do_read(struct fsg_dev *fsg) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -1221,18 +1235,23 @@ static int do_read(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a block + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; fsg->residue -= nread; + + /* Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1262,7 +1281,6 @@ static int do_write(struct fsg_dev *fsg) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -1303,7 +1321,7 @@ static int do_write(struct fsg_dev *fsg) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; while (amount_left_to_write > 0) { @@ -1313,38 +1331,20 @@ static int do_write(struct fsg_dev *fsg) if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. */ + * Try to get the remaining amount, + * but not more than the buffer size. + */ amount = min(amount_left_to_req, mod_data.buflen); - amount = min((loff_t) amount, curlun->file_length - - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int) PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= (amount & 511); - if (amount == 0) { - - /* Why were we were asked to transfer a - * partial block? */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -1353,11 +1353,11 @@ static int do_write(struct fsg_dev *fsg) if (amount_left_to_req == 0) get_some_more = 0; - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -1376,7 +1376,7 @@ static int do_write(struct fsg_dev *fsg) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1390,6 +1390,16 @@ static int do_write(struct fsg_dev *fsg) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1408,8 +1418,7 @@ static int do_write(struct fsg_dev *fsg) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); - nwritten -= (nwritten & 511); - // Round down to a block + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1418,13 +1427,14 @@ static int do_write(struct fsg_dev *fsg) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } + empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { fsg->short_packet_received = 1; break; } @@ -1500,8 +1510,8 @@ static int do_verify(struct fsg_dev *fsg) return -EIO; // No default reply /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1519,15 +1529,14 @@ static int do_verify(struct fsg_dev *fsg) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1550,11 +1559,11 @@ static int do_verify(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a sector + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1668,7 +1677,7 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ return 8; } @@ -1890,7 +1899,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -1969,7 +1978,7 @@ static int throw_away_data(struct fsg_dev *fsg) fsg->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); return -EINTR; @@ -1983,11 +1992,11 @@ static int throw_away_data(struct fsg_dev *fsg) amount = min(fsg->usb_amount_left, (u32) mod_data.buflen); - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -2415,7 +2424,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)")) == 0) @@ -2424,7 +2433,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)")) == 0) @@ -2433,7 +2442,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)")) == 0) @@ -2519,7 +2528,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)")) == 0) @@ -2528,7 +2537,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)")) == 0) @@ -2537,7 +2546,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)")) == 0) @@ -2666,7 +2675,6 @@ static int get_next_command(struct fsg_dev *fsg) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); @@ -2752,7 +2760,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting) reset: /* Deallocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if (bh->inreq) { @@ -2791,29 +2799,32 @@ reset: /* Enable the endpoints */ d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc, + &fsg_ss_bulk_in_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) goto reset; fsg->bulk_in_enabled = 1; d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc, + &fsg_ss_bulk_out_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + fsg->bulk_out_maxpacket = usb_endpoint_maxp(d); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); if (transport_is_cbi()) { d = fsg_ep_desc(fsg->gadget, - &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc, + &fsg_ss_intr_in_desc); if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) goto reset; fsg->intr_in_enabled = 1; } /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) @@ -2862,17 +2873,10 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) fsg->config = new_config; if ((rc = do_set_interface(fsg, 0)) != 0) fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } + else + INFO(fsg, "%s config #%d\n", + usb_speed_string(fsg->gadget->speed), + fsg->config); } return rc; } @@ -2909,7 +2913,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Cancel all the pending transfers */ if (fsg->intreq_busy) usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(fsg->bulk_in, bh->inreq); @@ -2920,7 +2924,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { num_active = fsg->intreq_busy; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2942,7 +2946,7 @@ static void handle_exception(struct fsg_dev *fsg) * state, and the exception. Then invoke the handler. */ spin_lock_irq(&fsg->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -3149,6 +3153,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); + /* If the thread isn't already dead, tell it to exit now */ + if (fsg->state != FSG_STATE_TERMINATED) { + raise_exception(fsg, FSG_STATE_EXIT); + wait_for_completion(&fsg->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&fsg->thread_notifier); + } + /* Unregister the sysfs attribute files and the LUNs */ for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; @@ -3162,17 +3175,8 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } } - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - /* Free the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) + for (i = 0; i < fsg_num_buffers; ++i) kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ @@ -3445,6 +3449,24 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg_fs_intr_in_desc.bEndpointAddress; } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, mod_data.buflen / 1024, 15); + + /* Assume endpoint addresses are the same for both speeds */ + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + } + if (gadget_is_otg(gadget)) fsg_otg_desc.bmAttributes |= USB_OTG_HNP; @@ -3460,7 +3482,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) req->complete = ep0_complete; /* Allocate the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; /* Allocate for the bulk-in endpoint. We assume that @@ -3471,7 +3493,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; bh->next = bh + 1; } - fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0]; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); @@ -3561,11 +3583,7 @@ static void fsg_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif + .speed = USB_SPEED_SUPER, .function = (char *) fsg_string_product, .unbind = fsg_unbind, .disconnect = fsg_disconnect, @@ -3587,7 +3605,9 @@ static int __init fsg_alloc(void) { struct fsg_dev *fsg; - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + fsg = kzalloc(sizeof *fsg + + fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL); + if (!fsg) return -ENOMEM; spin_lock_init(&fsg->lock); @@ -3605,6 +3625,10 @@ static int __init fsg_init(void) int rc; struct fsg_dev *fsg; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return rc; + if ((rc = fsg_alloc()) != 0) return rc; fsg = the_fsg; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 3bf872e1ad39..2a03e4de11c1 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -540,7 +540,7 @@ static int qe_ep_init(struct qe_udc *udc, int reval = 0; u16 max = 0; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* check the max package size validate for this endpoint */ /* Refer to USB2.0 spec table 9-13, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index de24a4233c25..b2c44e1d5813 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -559,7 +559,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* Disable automatic zlp generation. Driver is responsible to indicate * explicitly through req->req.zero. This is needed to enable multi-td @@ -1715,34 +1715,31 @@ static void dtd_complete_irq(struct fsl_udc *udc) } } +static inline enum usb_device_speed portscx_device_speed(u32 reg) +{ + switch (speed & PORTSCX_PORT_SPEED_MASK) { + case PORTSCX_PORT_SPEED_HIGH: + return USB_SPEED_HIGH; + case PORTSCX_PORT_SPEED_FULL: + return USB_SPEED_FULL; + case PORTSCX_PORT_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* Process a port change interrupt */ static void port_change_irq(struct fsl_udc *udc) { - u32 speed; - if (udc->bus_reset) udc->bus_reset = 0; /* Bus resetting is finished */ - if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) /* Get the speed */ - speed = (fsl_readl(&dr_regs->portsc1) - & PORTSCX_PORT_SPEED_MASK); - switch (speed) { - case PORTSCX_PORT_SPEED_HIGH: - udc->gadget.speed = USB_SPEED_HIGH; - break; - case PORTSCX_PORT_SPEED_FULL: - udc->gadget.speed = USB_SPEED_FULL; - break; - case PORTSCX_PORT_SPEED_LOW: - udc->gadget.speed = USB_SPEED_LOW; - break; - default: - udc->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - } + udc->gadget.speed = + portscx_device_speed(fsl_readl(&dr_regs->portsc1)); /* Update USB state */ if (!udc->resume_state) @@ -2167,20 +2164,8 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, default: s = "None"; break; } - s;} ), ( { - char *s; - switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { - case PORTSCX_PORT_SPEED_FULL: - s = "Full Speed"; break; - case PORTSCX_PORT_SPEED_LOW: - s = "Low Speed"; break; - case PORTSCX_PORT_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Undefined"; break; - } - s; - } ), + s;} ), + usb_speed_string(portscx_device_speed(tmp_reg)), (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? "Normal PHY mode" : "Low power mode", (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 4ec888f90002..e593f2849fa9 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/dma-mapping.h> #include <linux/err.h> @@ -220,7 +210,7 @@ static int config_ep(struct fusb300_ep *ep, info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if ((info.type == USB_ENDPOINT_XFER_INT) || @@ -1479,7 +1469,7 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.name = udc_name; fusb300->reg = reg; - ret = request_irq(ires->start, fusb300_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); @@ -1487,7 +1477,7 @@ static int __init fusb300_probe(struct platform_device *pdev) } ret = request_irq(ires1->start, fusb300_irq, - IRQF_DISABLED | IRQF_SHARED, udc_name, fusb300); + IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq1 error (%d)\n", ret); goto clean_up; diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h index f51aa2ef1f90..92745bd03064 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/fusb300_udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 704c2800ac00..0519d77915ec 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define pr_fmt(fmt) "g_ffs: " fmt diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f3a83cd0ef50..a8855d0b7f3b 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -31,6 +31,7 @@ #define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name)) #define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) #define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name)) +#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name)) #define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name)) #define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name)) #define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) @@ -115,6 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x30; else if (gadget_is_net2272(gadget)) return 0x31; + else if (gadget_is_dwc3(gadget)) + return 0x32; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 8b9220e128a7..8fcde37aa6d4 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -36,134 +36,43 @@ #include "gadget_chips.h" - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ +#include "composite.c" #include "usbstring.c" #include "config.c" #include "epautoconf.c" +#include "f_midi.c" /*-------------------------------------------------------------------------*/ - MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); -#define DRIVER_VERSION "25 Jul 2006" - static const char shortname[] = "g_midi"; static const char longname[] = "MIDI Gadget"; static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; - -module_param(index, int, 0444); +module_param(index, int, S_IRUGO); MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); -module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); - -/* Some systems will want different product identifiers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); - -/* - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; +static char *id = SNDRV_DEFAULT_STR1; +module_param(id, charp, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - - -/* This is a gadget, and the IN/OUT naming is from the host's perspective. - USB -> OUT endpoint -> rawmidi - USB <- IN endpoint <- rawmidi */ -struct gmidi_in_port { - struct gmidi_device* dev; - int active; - uint8_t cable; /* cable number << 4 */ - uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 - uint8_t data[2]; -}; - -struct gmidi_device { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - u8 config; - struct usb_ep *in_ep, *out_ep; - struct snd_card *card; - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *in_substream; - struct snd_rawmidi_substream *out_substream; - - /* For the moment we only support one port in - each direction, but in_port is kept as a - separate struct so we can have more later. */ - struct gmidi_in_port in_port; - unsigned long out_triggered; - struct tasklet_struct tasklet; -}; - -static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); - - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - - -static unsigned buflen = 256; -static unsigned qlen = 32; - +static unsigned int buflen = 256; module_param(buflen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "MIDI buffer length"); + +static unsigned int qlen = 32; module_param(qlen, uint, S_IRUGO); +MODULE_PARM_DESC(qlen, "USB read request queue length"); +static unsigned int in_ports = 1; +module_param(in_ports, uint, S_IRUGO); +MODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); + +static unsigned int out_ports = 1; +module_param(out_ports, uint, S_IRUGO); +MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); /* Thanks to Grey Innovation for donating this product ID. * @@ -173,1149 +82,124 @@ module_param(qlen, uint, S_IRUGO); #define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */ #define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */ +/* string IDs are assigned dynamically */ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_MIDI_GADGET 250 +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_DESCRIPTION_IDX 2 -/* We only have the one configuration, it's number 1. */ -#define GMIDI_CONFIG 1 - -/* We have two interfaces- AudioControl and MIDIStreaming */ -#define GMIDI_AC_INTERFACE 0 -#define GMIDI_MS_INTERFACE 1 -#define GMIDI_NUM_INTERFACES 2 - -DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); -DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); - -/* B.1 Device Descriptor */ static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(0x0200), + .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_PER_INTERFACE, - .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, }; -/* B.2 Configuration Descriptor */ -static struct usb_config_descriptor config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* compute wTotalLength on the fly */ - .bNumInterfaces = GMIDI_NUM_INTERFACES, - .bConfigurationValue = GMIDI_CONFIG, - .iConfiguration = STRING_MIDI_GADGET, - /* - * FIXME: When embedding this driver in a device, - * these need to be set to reflect the actual - * power properties of the device. Is it selfpowered? - */ - .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - -/* B.3.1 Standard AC Interface Descriptor */ -static const struct usb_interface_descriptor ac_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_AC_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct uac1_ac_header_descriptor_1 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), - .bInCollection = 1, - .baInterfaceNr = { - [0] = GMIDI_MS_INTERFACE, - } -}; - -/* B.4.1 Standard MS Interface Descriptor */ -static const struct usb_interface_descriptor ms_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_MS_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, - .iInterface = STRING_MIDI_GADGET, +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = "Grey Innovation", + [STRING_PRODUCT_IDX].s = "MIDI Gadget", + [STRING_DESCRIPTION_IDX].s = "MIDI", + { } /* end of list */ }; -/* B.4.2 Class-Specific MS Interface Descriptor */ -static const struct usb_ms_header_descriptor ms_header_desc = { - .bLength = USB_DT_MS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdMSC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE - + 2*USB_DT_MIDI_IN_SIZE - + 2*USB_DT_MIDI_OUT_SIZE(1)), -}; - -#define JACK_IN_EMB 1 -#define JACK_IN_EXT 2 -#define JACK_OUT_EMB 3 -#define JACK_OUT_EXT 4 - -/* B.4.3 MIDI IN Jack Descriptors */ -static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_IN_EMB, -}; - -static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_IN_EXT, -}; - -/* B.4.4 MIDI OUT Jack Descriptors */ -static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_OUT_EMB, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EXT, - .baSourcePin = 1, - } - } -}; - -static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_OUT_EXT, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EMB, - .baSourcePin = 1, - } - } -}; - -/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_IN_EMB, - } -}; - -/* B.6.1 Standard Bulk IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_OUT_EMB, - } -}; - -static const struct usb_descriptor_header *gmidi_function [] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&ms_interface_desc, - - (struct usb_descriptor_header *)&ms_header_desc, - (struct usb_descriptor_header *)&jack_in_emb_desc, - (struct usb_descriptor_header *)&jack_in_ext_desc, - (struct usb_descriptor_header *)&jack_out_emb_desc, - (struct usb_descriptor_header *)&jack_out_ext_desc, - /* If you add more jacks, update ms_header_desc.wTotalLength */ - - (struct usb_descriptor_header *)&bulk_out_desc, - (struct usb_descriptor_header *)&ms_out_desc, - (struct usb_descriptor_header *)&bulk_in_desc, - (struct usb_descriptor_header *)&ms_in_desc, - NULL, -}; - -static char manufacturer[50]; -static char product_desc[40] = "MIDI Gadget"; -static char serial_number[20]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIAL, serial_number, }, - { STRING_MIDI_GADGET, longname, }, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int len; - - /* only one configuration */ - if (index != 0) { - return -EINVAL; - } - len = usb_gadget_config_buf(&config_desc, - buf, USB_BUFSIZ, gmidi_function); - if (len < 0) { - return len; - } - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - return len; -} - -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; -} - -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static const uint8_t gmidi_cin_length[] = { - 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -/* - * Receives a chunk of MIDI data. - */ -static void gmidi_read_data(struct usb_ep *ep, int cable, - uint8_t *data, int length) -{ - struct gmidi_device *dev = ep->driver_data; - /* cable is ignored, because for now we only have one. */ - - if (!dev->out_substream) { - /* Nobody is listening - throw it on the floor. */ - return; - } - if (!test_bit(dev->out_substream->number, &dev->out_triggered)) { - return; - } - snd_rawmidi_receive(dev->out_substream, data, length); -} - -static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req) -{ - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i + 3 < req->actual; i += 4) { - if (buf[i] != 0) { - int cable = buf[i] >> 4; - int length = gmidi_cin_length[buf[i] & 0x0f]; - gmidi_read_data(ep, cable, &buf[i + 1], length); - } - } -} - -static void gmidi_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gmidi_device *dev = ep->driver_data; - int status = req->status; - - switch (status) { - case 0: /* normal completion */ - if (ep == dev->out_ep) { - /* we received stuff. - req is queued again, below */ - gmidi_handle_out_data(ep, req); - } else if (ep == dev->in_ep) { - /* our transmit completed. - see if there's more to go. - gmidi_transmit eats req, don't queue it again. */ - gmidi_transmit(dev, req); - return; - } - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) { - gmidi_handle_out_data(ep, req); - } - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - break; - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags) -{ - int err = 0; - struct usb_request *req; - struct usb_ep *ep; - unsigned i; - - dev->in_ep->desc = &bulk_in_desc; - err = usb_ep_enable(dev->in_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err); - goto fail; - } - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = &bulk_out_desc; - err = usb_ep_enable(dev->out_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err); - goto fail; - } - dev->out_ep->driver_data = dev; - - /* allocate a bunch of read buffers and queue them all at once. */ - ep = dev->out_ep; - for (i = 0; i < qlen && err == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = gmidi_complete; - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) { - DBG(dev, "%s queue req: %d\n", ep->name, err); - } - } else { - err = -ENOMEM; - } - } -fail: - /* caller is responsible for cleanup on error */ - return err; -} - - -static void gmidi_reset_config(struct gmidi_device *dev) -{ - if (dev->config == 0) { - return; - } - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - usb_ep_disable(dev->in_ep); - usb_ep_disable(dev->out_ep); - dev->config = 0; -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int -gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if 0 - /* FIXME */ - /* Hacking this bit out fixes a bug where on receipt of two - USB_REQ_SET_CONFIGURATION messages, we end up with no - buffered OUT requests waiting for data. This is clearly - hiding a bug elsewhere, because if the config didn't - change then we really shouldn't do anything. */ - /* Having said that, when we do "change" from config 1 - to config 1, we at least gmidi_reset_config() which - clears out any requests on endpoints, so it's not like - we leak or anything. */ - if (number == dev->config) { - return 0; - } -#endif - - gmidi_reset_config(dev); - - switch (number) { - case GMIDI_CONFIG: - result = set_gmidi_config(dev, gfp_flags); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) { - result = -ENODEV; - } - if (result) { - gmidi_reset_config(dev); - } else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - - dev->config = number; - INFO(dev, "%s speed\n", speed); - } - return result; -} - - -static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req) -{ - if (req->status || req->actual != req->length) { - DBG((struct gmidi_device *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - } -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int gmidi_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - switch (w_value >> 8) { - - case USB_DT_DEVICE: - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - value = min(w_length, (u16) sizeof(device_desc)); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) { - goto unknown; - } - if (gadget->a_hnp_support) { - DBG(dev, "HNP available\n"); - } else if (gadget->a_alt_hnp_support) { - DBG(dev, "HNP needs a different root port\n"); - } else { - VDBG(dev, "HNP inactive\n"); - } - spin_lock(&dev->lock); - value = gmidi_set_config(dev, w_value, GFP_ATOMIC); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16)1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) { - goto unknown; - } - spin_lock(&dev->lock); - if (dev->config && w_index < GMIDI_NUM_INTERFACES - && w_value == 0) - { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - gmidi_reset_config(dev); - gmidi_set_config(dev, config, GFP_ATOMIC); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) { - goto unknown; - } - if (!dev->config) { - break; - } - if (w_index >= GMIDI_NUM_INTERFACES) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16)1); - break; - - default: -unknown: - VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - gmidi_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void gmidi_disconnect(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - gmidi_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct snd_card *card; - - DBG(dev, "unbind\n"); - - card = dev->card; - dev->card = NULL; - if (card) { - snd_card_free(card); - } - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int gmidi_snd_free(struct snd_device *device) +static int __exit midi_unbind(struct usb_composite_dev *dev) { return 0; } -static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - -/* - * Converts MIDI commands to USB MIDI packets. - */ -static void gmidi_transmit_byte(struct usb_request *req, - struct gmidi_in_port *port, uint8_t b) -{ - uint8_t p0 = port->cable; - - if (b >= 0xf8) { - gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { - switch (b) { - case 0xf0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case 0xf1: - case 0xf3: - port->data[0] = b; - port->state = STATE_1PARAM; - break; - case 0xf2: - port->data[0] = b; - port->state = STATE_2PARAM_1; - break; - case 0xf4: - case 0xf5: - port->state = STATE_UNKNOWN; - break; - case 0xf6: - gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - gmidi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - gmidi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; - break; - } - } else if (b >= 0x80) { - port->data[0] = b; - if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; - else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ - switch (port->state) { - case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, p0, port->data[0], b, 0); - break; - case STATE_2PARAM_1: - port->data[1] = b; - port->state = STATE_2PARAM_2; - break; - case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, - p0, port->data[0], port->data[1], b); - break; - case STATE_SYSEX_0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case STATE_SYSEX_1: - port->data[1] = b; - port->state = STATE_SYSEX_2; - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; - break; - } - } -} - -static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req) -{ - struct usb_ep *ep = dev->in_ep; - struct gmidi_in_port *port = &dev->in_port; - - if (!ep) { - return; - } - if (!req) { - req = alloc_ep_req(ep, buflen); - } - if (!req) { - ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n"); - return; - } - req->length = 0; - req->complete = gmidi_complete; - - if (port->active) { - while (req->length + 3 < buflen) { - uint8_t b; - if (snd_rawmidi_transmit(dev->in_substream, &b, 1) - != 1) - { - port->active = 0; - break; - } - gmidi_transmit_byte(req, port, b); - } - } - if (req->length > 0) { - usb_ep_queue(ep, req, GFP_ATOMIC); - } else { - free_ep_req(ep, req); - } -} - -static void gmidi_in_tasklet(unsigned long data) -{ - struct gmidi_device *dev = (struct gmidi_device *)data; - - gmidi_transmit(dev, NULL); -} - -static int gmidi_in_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_open\n"); - dev->in_substream = substream; - dev->in_port.state = STATE_UNKNOWN; - return 0; -} - -static int gmidi_in_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_close\n"); - return 0; -} - -static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_trigger %d\n", up); - dev->in_port.active = up; - if (up) { - tasklet_hi_schedule(&dev->tasklet); - } -} - -static int gmidi_out_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_open\n"); - dev->out_substream = substream; - return 0; -} - -static int gmidi_out_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_close\n"); - return 0; -} - -static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_trigger %d\n", up); - if (up) { - set_bit(substream->number, &dev->out_triggered); - } else { - clear_bit(substream->number, &dev->out_triggered); - } -} - -static struct snd_rawmidi_ops gmidi_in_ops = { - .open = gmidi_in_open, - .close = gmidi_in_close, - .trigger = gmidi_in_trigger, +static struct usb_configuration midi_config = { + .label = "MIDI Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; -static struct snd_rawmidi_ops gmidi_out_ops = { - .open = gmidi_out_open, - .close = gmidi_out_close, - .trigger = gmidi_out_trigger -}; - -/* register as a sound "card" */ -static int gmidi_register_card(struct gmidi_device *dev) +static int __init midi_bind_config(struct usb_configuration *c) { - struct snd_card *card; - struct snd_rawmidi *rmidi; - int err; - int out_ports = 1; - int in_ports = 1; - static struct snd_device_ops ops = { - .dev_free = gmidi_snd_free, - }; - - err = snd_card_create(index, id, THIS_MODULE, 0, &card); - if (err < 0) { - ERROR(dev, "snd_card_create failed\n"); - goto fail; - } - dev->card = card; - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops); - if (err < 0) { - ERROR(dev, "snd_device_new failed: error %d\n", err); - goto fail; - } - - strcpy(card->driver, longname); - strcpy(card->longname, longname); - strcpy(card->shortname, shortname); - - /* Set up rawmidi */ - dev->in_port.dev = dev; - dev->in_port.active = 0; - snd_component_add(card, "MIDI"); - err = snd_rawmidi_new(card, "USB MIDI Gadget", 0, - out_ports, in_ports, &rmidi); - if (err < 0) { - ERROR(dev, "snd_rawmidi_new failed: error %d\n", err); - goto fail; - } - dev->rmidi = rmidi; - strcpy(rmidi->name, card->shortname); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->private_data = dev; - - /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. - It's an upside-down world being a gadget. */ - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - - snd_card_set_dev(card, &dev->gadget->dev); - - /* register it - we're ready to go */ - err = snd_card_register(card); - if (err < 0) { - ERROR(dev, "snd_card_register failed\n"); - goto fail; - } - - VDBG(dev, "gmidi_register_card finished ok\n"); - return 0; - -fail: - if (dev->card) { - snd_card_free(dev->card); - dev->card = NULL; - } - return err; + return f_midi_bind_config(c, index, id, + in_ports, out_ports, + buflen, qlen); } -/* - * Creates an output endpoint, and initializes output ports. - */ -static int __init gmidi_bind(struct usb_gadget *gadget) +static int __init midi_bind(struct usb_composite_dev *cdev) { - struct gmidi_device *dev; - struct usb_ep *in_ep, *out_ep; - int gcnum, err = 0; + struct usb_gadget *gadget = cdev->gadget; + int gcnum, status; - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - pr_err("idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) { - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - } - if (iManufacturer) { - strlcpy(manufacturer, iManufacturer, sizeof(manufacturer)); - } else { - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - } - if (iProduct) { - strlcpy(product_desc, iProduct, sizeof(product_desc)); - } - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIAL, - strlcpy(serial_number, iSerialNumber, sizeof(serial_number)); - } + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. - */ - usb_ep_autoconfig_reset(gadget); - in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc); - if (!in_ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; - } - EP_IN_NAME = in_ep->name; - in_ep->driver_data = in_ep; /* claim */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; - out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc); - if (!out_ep) { - goto autoconf_fail; - } - EP_OUT_NAME = out_ep->name; - out_ep->driver_data = out_ep; /* claim */ + /* config description */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_DESCRIPTION_IDX].id = status; + + midi_config.iConfiguration = status; gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { + if (gcnum < 0) { /* gmidi is so simple (no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. * so warn about unrecognized controllers, don't panic. */ pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); + __func__, gadget->name); device_desc.bcdDevice = cpu_to_le16(0x9999); + } else { + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); } + status = usb_add_config(cdev, &midi_config, midi_bind_config); + if (status < 0) + return status; - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - return -ENOMEM; - } - spin_lock_init(&dev->lock); - dev->gadget = gadget; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - set_gadget_data(gadget, dev); - tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); - - /* preallocate control response and buffer */ - dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); - if (!dev->req) { - err = -ENOMEM; - goto fail; - } - - dev->req->complete = gmidi_setup_complete; - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); - - /* register as an ALSA sound card */ - err = gmidi_register_card(dev); - if (err < 0) { - goto fail; - } - - VDBG(dev, "gmidi_bind finished ok\n"); + pr_info("%s\n", longname); return 0; - -fail: - gmidi_unbind(gadget); - return err; -} - - -static void gmidi_suspend(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) { - return; - } - - DBG(dev, "suspend\n"); -} - -static void gmidi_resume(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); } - -static struct usb_gadget_driver gmidi_driver = { - .speed = USB_SPEED_FULL, - .function = (char *)longname, - .unbind = gmidi_unbind, - - .setup = gmidi_setup, - .disconnect = gmidi_disconnect, - - .suspend = gmidi_suspend, - .resume = gmidi_resume, - - .driver = { - .name = (char *)shortname, - .owner = THIS_MODULE, - }, +static struct usb_composite_driver midi_driver = { + .name = (char *) longname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .unbind = __exit_p(midi_unbind), }; -static int __init gmidi_init(void) +static int __init midi_init(void) { - return usb_gadget_probe_driver(&gmidi_driver, gmidi_bind); + return usb_composite_probe(&midi_driver, midi_bind); } -module_init(gmidi_init); +module_init(midi_init); -static void __exit gmidi_cleanup(void) +static void __exit midi_cleanup(void) { - usb_gadget_unregister_driver(&gmidi_driver); + usb_composite_unregister(&midi_driver); } -module_exit(gmidi_cleanup); +module_exit(midi_cleanup); diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 9fb575034a0e..f888c3ede860 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 692fd9b2248b..2d978c0e7ced 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -689,7 +689,7 @@ static int imx_ep_enable(struct usb_ep *usb_ep, return -EINVAL; } - if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) { + if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { D_ERR(imx_usb->dev, "<%s> bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; @@ -1478,7 +1478,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) for (i = 0; i < IMX_USB_NB_EP + 1; i++) { ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), - IRQF_DISABLED, driver_name, imx_usb); + 0, driver_name, imx_usb); if (ret) { dev_err(&pdev->dev, "can't get irq %i, err %d\n", imx_usb->usbd_int[i], ret); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h index 7136c242b4ec..d118fb777840 100644 --- a/drivers/usb/gadget/imx_udc.h +++ b/drivers/usb/gadget/imx_udc.h @@ -8,11 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_USB_GADGET_IMX_H diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 1b240990448f..a392ec0d2d51 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index a06e2c27b435..c9fa3bf5b377 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -5,16 +5,6 @@ * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ @@ -60,9 +50,6 @@ static const char driver_name[] = "langwell_udc"; static const char driver_desc[] = DRIVER_DESC; -/* controller device global variable */ -static struct langwell_udc *the_controller; - /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor langwell_ep0_desc = { @@ -283,7 +270,7 @@ static int langwell_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select @@ -1321,9 +1308,12 @@ static int langwell_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int langwell_stop(struct usb_gadget_driver *driver); +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); + +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); + /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops langwell_ops = { @@ -1345,8 +1335,8 @@ static const struct usb_gadget_ops langwell_ops = { /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = langwell_pullup, - .start = langwell_start, - .stop = langwell_stop, + .udc_start = langwell_start, + .udc_stop = langwell_stop, }; @@ -1561,7 +1551,7 @@ static void stop_activity(struct langwell_udc *dev, static ssize_t show_function(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); if (!dev->driver || !dev->driver->function || strlen(dev->driver->function) > PAGE_SIZE) @@ -1572,11 +1562,25 @@ static ssize_t show_function(struct device *_dev, static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); +static inline enum usb_device_speed lpm_device_speed(u32 reg) +{ + switch (LPM_PSPD(reg)) { + case LPM_SPEED_HIGH: + return USB_SPEED_HIGH; + case LPM_SPEED_FULL: + return USB_SPEED_FULL; + case LPM_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* device "langwell_udc" sysfs attribute file */ static ssize_t show_langwell_udc(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); struct langwell_request *req; struct langwell_ep *ep = NULL; char *next; @@ -1700,20 +1704,7 @@ static ssize_t show_langwell_udc(struct device *_dev, "BmAttributes: %d\n\n", LPM_PTS(tmp_reg), (tmp_reg & LPM_STS) ? 1 : 0, - ({ - char *s; - switch (LPM_PSPD(tmp_reg)) { - case LPM_SPEED_FULL: - s = "Full Speed"; break; - case LPM_SPEED_LOW: - s = "Low Speed"; break; - case LPM_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Unknown Speed"; break; - } - s; - }), + usb_speed_string(lpm_device_speed(tmp_reg)), (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force", (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled", LPM_BA(tmp_reg)); @@ -1821,7 +1812,7 @@ static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); static ssize_t store_remote_wakeup(struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); unsigned long flags; ssize_t rc = count; @@ -1857,21 +1848,15 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); * the driver might get unbound. */ -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; int retval; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (dev->driver) - return -EBUSY; - spin_lock_irqsave(&dev->lock, flags); /* hook up the driver ... */ @@ -1881,18 +1866,9 @@ static int langwell_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&dev->lock, flags); - retval = bind(&dev->gadget); - if (retval) { - dev_dbg(&dev->pdev->dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = device_create_file(&dev->pdev->dev, &dev_attr_function); if (retval) - goto err_unbind; + goto err; dev->usb_state = USB_STATE_ATTACHED; dev->ep0_state = WAIT_FOR_SETUP; @@ -1909,31 +1885,27 @@ static int langwell_start(struct usb_gadget_driver *driver, dev_info(&dev->pdev->dev, "register driver: %s\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; -err_unbind: - driver->unbind(&dev->gadget); +err: dev->gadget.dev.driver = NULL; dev->driver = NULL; dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return retval; } /* unregister gadget driver */ -static int langwell_stop(struct usb_gadget_driver *driver) +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (unlikely(!driver || !driver->unbind)) - return -EINVAL; - /* exit PHY low power suspend */ if (dev->pdev->device != 0x0829) langwell_phy_low_power(dev, 0); @@ -1956,8 +1928,6 @@ static int langwell_stop(struct usb_gadget_driver *driver) stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - /* unbind gadget driver */ - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -1966,6 +1936,7 @@ static int langwell_stop(struct usb_gadget_driver *driver) dev_info(&dev->pdev->dev, "unregistered driver '%s'\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; } @@ -2657,12 +2628,10 @@ done: dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } - /* port change detect interrupt handler */ static void handle_port_change(struct langwell_udc *dev) { u32 portsc1, devlc; - u32 speed; dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -2677,24 +2646,9 @@ static void handle_port_change(struct langwell_udc *dev) /* bus reset is finished */ if (!(portsc1 & PORTS_PR)) { /* get the speed */ - speed = LPM_PSPD(devlc); - switch (speed) { - case LPM_SPEED_HIGH: - dev->gadget.speed = USB_SPEED_HIGH; - break; - case LPM_SPEED_FULL: - dev->gadget.speed = USB_SPEED_FULL; - break; - case LPM_SPEED_LOW: - dev->gadget.speed = USB_SPEED_LOW; - break; - default: - dev->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - dev_vdbg(&dev->pdev->dev, - "speed = %d, dev->gadget.speed = %d\n", - speed, dev->gadget.speed); + dev->gadget.speed = lpm_device_speed(devlc); + dev_vdbg(&dev->pdev->dev, "dev->gadget.speed = %d\n", + dev->gadget.speed); } /* LPM L0 to L1 */ @@ -2969,7 +2923,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev) handle_port_change(dev); } - /* suspend interrrupt */ + /* suspend interrupt */ if (irq_sts & STS_SLI) { dev_vdbg(&dev->pdev->dev, "suspend interrupt\n"); handle_bus_suspend(dev); @@ -2999,7 +2953,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev) /* release device structure */ static void gadget_release(struct device *_dev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3057,7 +3011,7 @@ static void sram_deinit(struct langwell_udc *dev) /* tear down the binding between this driver and the pci device */ static void langwell_udc_remove(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); DECLARE_COMPLETION(done); @@ -3124,8 +3078,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) /* free dev, wait for the release() finished */ wait_for_completion(&done); - - the_controller = NULL; } @@ -3144,11 +3096,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, size_t size; int retval; - if (the_controller) { - dev_warn(&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc(sizeof *dev, GFP_KERNEL); if (dev == NULL) { @@ -3368,8 +3315,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, "After langwell_udc_probe(), print all registers:\n"); print_all_registers(dev); - the_controller = dev; - retval = device_register(&dev->gadget.dev); if (retval) goto error; @@ -3404,7 +3349,7 @@ error: /* device controller suspend */ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3452,7 +3397,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) /* device controller resume */ static int langwell_udc_resume(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); size_t size; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3534,7 +3479,7 @@ static int langwell_udc_resume(struct pci_dev *pdev) /* pci driver shutdown */ static void langwell_udc_shutdown(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); u32 usbmode; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index f1d9c1bb04f3..ef79e242b7b0 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -5,16 +5,6 @@ * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <linux/usb/langwell_udc.h> @@ -231,3 +221,5 @@ struct langwell_udc { u16 dev_status; }; +#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget) + diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 491f825ed5c9..91d0af2a24a8 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -370,7 +360,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); m66592->pipenum2ep[pipenum] = ep; m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; INIT_LIST_HEAD(&ep->queue); @@ -447,7 +437,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; @@ -1674,7 +1664,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->timer.data = (unsigned long)m66592; m66592->reg = reg; - ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, udc_name, m66592); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 7b93d579af37..9d9f7e39f037 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __M66592_UDC_H__ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index d3eb27427c58..e24f72f82a47 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -169,7 +160,7 @@ static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, .iProduct = DRIVER_DESC, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .needs_serial = 1, }; diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 8c7b74717d85..7e7f515b8b19 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 65f1f7c3bd4e..daa75c12f336 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -1,3 +1,11 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ #ifndef __MV_UDC_H #define __MV_UDC_H @@ -194,14 +202,25 @@ struct mv_udc { unsigned int ep0_dir; unsigned int dev_addr; + unsigned int test_mode; int errors; unsigned softconnect:1, vbus_active:1, remote_wakeup:1, softconnected:1, - force_fs:1; - struct clk *clk; + force_fs:1, + clock_gating:1, + active:1; + + struct work_struct vbus_work; + struct workqueue_struct *qwork; + + struct mv_usb_platform_data *pdata; + + /* some SOC has mutiple clock sources for USB*/ + unsigned int clknum; + struct clk *clk[0]; }; /* endpoint data structure */ @@ -225,6 +244,7 @@ struct mv_req { struct mv_dtd *dtd, *head, *tail; struct mv_ep *ep; struct list_head queue; + unsigned int test_mode; unsigned dtd_count; unsigned mapped:1; }; @@ -289,6 +309,4 @@ struct mv_dtd { struct mv_dtd *next_dtd_virt; }; -extern int mv_udc_phy_init(unsigned int base); - #endif diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index ce1ac2bcb314..892412103dd8 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1,3 +1,14 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie <chao.xie@marvell.com> + * Neil Zhang <zhangwm@marvell.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + #include <linux/module.h> #include <linux/pci.h> #include <linux/dma-mapping.h> @@ -22,6 +33,7 @@ #include <linux/irq.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/platform_data/mv_usb.h> #include <asm/system.h> #include <asm/unaligned.h> @@ -45,6 +57,8 @@ #define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) #define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) +static DECLARE_COMPLETION(release_done); + static const char driver_name[] = "mv_udc"; static const char driver_desc[] = DRIVER_DESC; @@ -53,6 +67,7 @@ static struct mv_udc *the_controller; int mv_usb_otgsc; static void nuke(struct mv_ep *ep, int status); +static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor mv_ep0_desc = { @@ -82,14 +97,16 @@ static void ep0_reset(struct mv_udc *udc) (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; + epctrlx = readl(&udc->op_regs->epctrlx[0]); if (i) { /* TX */ - epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_TX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_TX_EP_TYPE_SHIFT); } else { /* RX */ - epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_RX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_RX_EP_TYPE_SHIFT); } @@ -122,6 +139,7 @@ static int process_ep_req(struct mv_udc *udc, int index, int i, direction; int retval = 0; u32 errors; + u32 bit_pos; curr_dqh = &udc->ep_dqh[index]; direction = index % 2; @@ -139,10 +157,20 @@ static int process_ep_req(struct mv_udc *udc, int index, errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; if (!errors) { - remaining_length += + remaining_length = (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; + + if (remaining_length) { + if (direction) { + dev_dbg(&udc->dev->dev, + "TX dTD remains data\n"); + retval = -EPROTO; + break; + } else + break; + } } else { dev_info(&udc->dev->dev, "complete_tr error: ep=%d %s: error = 0x%x\n", @@ -164,6 +192,20 @@ static int process_ep_req(struct mv_udc *udc, int index, if (retval) return retval; + if (direction == EP_DIR_OUT) + bit_pos = 1 << curr_req->ep->ep_num; + else + bit_pos = 1 << (16 + curr_req->ep->ep_num); + + while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) { + if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { + while (readl(&udc->op_regs->epstatus) & bit_pos) + udelay(1); + break; + } + udelay(1); + } + curr_req->req.actual = actual; return 0; @@ -335,7 +377,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req) } else { /* Write dQH next pointer and terminate bit to 0 */ dqh->next_dtd_ptr = req->head->td_dma - & EP_QUEUE_HEAD_NEXT_POINTER_MASK;; + & EP_QUEUE_HEAD_NEXT_POINTER_MASK; dqh->size_ioc_int_sts = 0; /* Ensure that updates to the QH will occur before priming. */ @@ -376,7 +418,7 @@ static int queue_dtd(struct mv_ep *ep, struct mv_req *req) } } done: - return retval;; + return retval; } static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length, @@ -481,6 +523,7 @@ static int mv_ep_enable(struct usb_ep *_ep, u16 max = 0; u32 bit_pos, epctrlx, direction; unsigned char zlt = 0, ios = 0, mult = 0; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); udc = ep->udc; @@ -493,7 +536,7 @@ static int mv_ep_enable(struct usb_ep *_ep, return -ESHUTDOWN; direction = ep_dir(ep); - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select @@ -501,9 +544,6 @@ static int mv_ep_enable(struct usb_ep *_ep, */ zlt = 1; - /* Get the endpoint queue head address */ - dqh = (struct mv_dqh *)ep->dqh; - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); /* Check if the Endpoint is Primed */ @@ -532,7 +572,7 @@ static int mv_ep_enable(struct usb_ep *_ep, case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); - max = max & 0x8ff; /* bit 0~10 */ + max = max & 0x7ff; /* bit 0~10 */ /* 3 transactions at most */ if (mult > 3) goto en_done; @@ -540,6 +580,10 @@ static int mv_ep_enable(struct usb_ep *_ep, default: goto en_done; } + + spin_lock_irqsave(&udc->lock, flags); + /* Get the endpoint queue head address */ + dqh = ep->dqh; dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | (mult << EP_QUEUE_HEAD_MULT_POS) | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) @@ -572,18 +616,20 @@ static int mv_ep_enable(struct usb_ep *_ep, */ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_RX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_RX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_TX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_TX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } + spin_unlock_irqrestore(&udc->lock, flags); + return 0; en_done: return -EINVAL; @@ -595,6 +641,7 @@ static int mv_ep_disable(struct usb_ep *_ep) struct mv_ep *ep; struct mv_dqh *dqh; u32 bit_pos, epctrlx, direction; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); if ((_ep == NULL) || !ep->desc) @@ -605,6 +652,8 @@ static int mv_ep_disable(struct usb_ep *_ep) /* Get the endpoint queue head address */ dqh = ep->dqh; + spin_lock_irqsave(&udc->lock, flags); + direction = ep_dir(ep); bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); @@ -623,6 +672,9 @@ static int mv_ep_disable(struct usb_ep *_ep) ep->desc = NULL; ep->stopped = 1; + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; } @@ -655,37 +707,28 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) { struct mv_udc *udc; u32 bit_pos, direction; - struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); + struct mv_ep *ep; unsigned int loops; + if (!_ep) + return; + + ep = container_of(_ep, struct mv_ep, ep); + if (!ep->desc) + return; + udc = ep->udc; direction = ep_dir(ep); - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); - /* - * Flushing will halt the pipe - * Write 1 to the Flush register - */ - writel(bit_pos, &udc->op_regs->epflush); - /* Wait until flushing completed */ - loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { - /* - * ENDPTFLUSH bit should be cleared to indicate this - * operation is complete - */ - if (loops == 0) { - dev_err(&udc->dev->dev, - "TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x\n", - (unsigned)readl(&udc->op_regs->epflush), - (unsigned)bit_pos); - return; - } - loops--; - udelay(LOOPS_USEC); - } + if (ep->ep_num == 0) + bit_pos = (1 << 16) | 1; + else if (direction == EP_DIR_OUT) + bit_pos = 1 << ep->ep_num; + else + bit_pos = 1 << (16 + ep->ep_num); + loops = LOOPS(EPSTATUS_TIMEOUT); - while (readl(&udc->op_regs->epstatus) & bit_pos) { + do { unsigned int inter_loops; if (loops == 0) { @@ -700,7 +743,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flushing completed */ inter_loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { + while (readl(&udc->op_regs->epflush)) { /* * ENDPTFLUSH bit should be cleared to indicate this * operation is complete @@ -717,7 +760,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) udelay(LOOPS_USEC); } loops--; - } + } while (readl(&udc->op_regs->epstatus) & bit_pos); } /* queues (submits) an I/O request to an endpoint */ @@ -987,6 +1030,22 @@ static struct usb_ep_ops mv_ep_ops = { .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ }; +static void udc_clock_enable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_enable(udc->clk[i]); +} + +static void udc_clock_disable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_disable(udc->clk[i]); +} + static void udc_stop(struct mv_udc *udc) { u32 tmp; @@ -1075,6 +1134,40 @@ static int udc_reset(struct mv_udc *udc) return 0; } +static int mv_udc_enable(struct mv_udc *udc) +{ + int retval; + + if (udc->clock_gating == 0 || udc->active) + return 0; + + dev_dbg(&udc->dev->dev, "enable udc\n"); + udc_clock_enable(udc); + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d\n", retval); + udc_clock_disable(udc); + return retval; + } + } + udc->active = 1; + + return 0; +} + +static void mv_udc_disable(struct mv_udc *udc) +{ + if (udc->clock_gating && udc->active) { + dev_dbg(&udc->dev->dev, "disable udc\n"); + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + udc->active = 0; + } +} + static int mv_udc_get_frame(struct usb_gadget *gadget) { struct mv_udc *udc; @@ -1110,22 +1203,68 @@ static int mv_udc_wakeup(struct usb_gadget *gadget) return 0; } +static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_udc *udc; + unsigned long flags; + int retval = 0; + + udc = container_of(gadget, struct mv_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + + udc->vbus_active = (is_active != 0); + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->softconnect) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); + udc_stop(udc); + mv_udc_disable(udc); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return retval; +} + static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) { struct mv_udc *udc; unsigned long flags; + int retval = 0; udc = container_of(gadget, struct mv_udc, gadget); spin_lock_irqsave(&udc->lock, flags); + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + udc->softconnect = (is_on != 0); - if (udc->driver && udc->softconnect) - udc_start(udc); - else + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->vbus_active) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); udc_stop(udc); + mv_udc_disable(udc); + } spin_unlock_irqrestore(&udc->lock, flags); - return 0; + return retval; } static int mv_udc_start(struct usb_gadget_driver *driver, @@ -1140,17 +1279,15 @@ static const struct usb_gadget_ops mv_ops = { /* tries to wake up the host connected to this gadget */ .wakeup = mv_udc_wakeup, + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_udc_vbus_session, + /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = mv_udc_pullup, .start = mv_udc_start, .stop = mv_udc_stop, }; -static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter) -{ - dev_info(&udc->dev->dev, "Test Mode is not support yet\n"); -} - static int eps_init(struct mv_udc *udc) { struct mv_ep *ep; @@ -1257,7 +1394,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = USB_DIR_OUT; + udc->ep0_dir = EP_DIR_OUT; spin_unlock_irqrestore(&udc->lock, flags); @@ -1269,9 +1406,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->gadget.dev.driver = NULL; return retval; } - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); + + /* pullup is always on */ + mv_udc_pullup(&udc->gadget, 1); + + /* When boot with cable attached, there will be no vbus irq occurred */ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); return 0; } @@ -1284,13 +1425,16 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) if (!udc) return -ENODEV; - udc_stop(udc); - spin_lock_irqsave(&udc->lock, flags); + mv_udc_enable(udc); + udc_stop(udc); + /* stop all usb activities */ udc->gadget.speed = USB_SPEED_UNKNOWN; stop_activity(udc, driver); + mv_udc_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); /* unbind gadget driver */ @@ -1301,6 +1445,31 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) return 0; } +static void mv_set_ptc(struct mv_udc *udc, u32 mode) +{ + u32 portsc; + + portsc = readl(&udc->op_regs->portsc[0]); + portsc |= mode << 16; + writel(portsc, &udc->op_regs->portsc[0]); +} + +static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) +{ + struct mv_udc *udc = the_controller; + struct mv_req *req = container_of(_req, struct mv_req, req); + unsigned long flags; + + dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode); + + spin_lock_irqsave(&udc->lock, flags); + if (req->test_mode) { + mv_set_ptc(udc, req->test_mode); + req->test_mode = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); +} + static int udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) { @@ -1310,6 +1479,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) ep = &udc->eps[0]; udc->ep0_dir = direction; + udc->ep0_state = WAIT_FOR_OUT_STATUS; req = udc->status_req; @@ -1323,9 +1493,21 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req->ep = ep; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + if (udc->test_mode) { + req->req.complete = prime_status_complete; + req->test_mode = udc->test_mode; + udc->test_mode = 0; + } else + req->req.complete = NULL; req->dtd_count = 0; + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, req->req.length, + ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 1; + } + /* prime the data phase */ if (!req_to_dtd(req)) retval = queue_dtd(ep, req); @@ -1346,6 +1528,17 @@ out: return retval; } +static void mv_udc_testmode(struct mv_udc *udc, u16 index) +{ + if (index <= TEST_FORCE_EN) { + udc->test_mode = index; + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); + } else + dev_err(&udc->dev->dev, + "This test mode(%d) is not supported\n", index); +} + static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) { udc->dev_addr = (u8)setup->wValue; @@ -1360,7 +1553,7 @@ static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) static void ch9getstatus(struct mv_udc *udc, u8 ep_num, struct usb_ctrlrequest *setup) { - u16 status; + u16 status = 0; int retval; if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) @@ -1388,6 +1581,8 @@ static void ch9getstatus(struct mv_udc *udc, u8 ep_num, retval = udc_prime_status(udc, EP_DIR_IN, status, false); if (retval) ep0_stall(udc); + else + udc->ep0_state = DATA_STATE_XMIT; } static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) @@ -1402,9 +1597,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) case USB_DEVICE_REMOTE_WAKEUP: udc->remote_wakeup = 0; break; - case USB_DEVICE_TEST_MODE: - mv_udc_testmode(udc, 0, false); - break; default: goto out; } @@ -1433,8 +1625,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) if (udc_prime_status(udc, EP_DIR_IN, 0, true)) ep0_stall(udc); - else - udc->ep0_state = DATA_STATE_XMIT; out: return; } @@ -1452,16 +1642,16 @@ static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) break; case USB_DEVICE_TEST_MODE: if (setup->wIndex & 0xFF - && udc->gadget.speed != USB_SPEED_HIGH) - goto out; - if (udc->usb_state == USB_STATE_CONFIGURED - || udc->usb_state == USB_STATE_ADDRESS - || udc->usb_state == USB_STATE_DEFAULT) - mv_udc_testmode(udc, - setup->wIndex & 0xFF00, true); - else - goto out; - break; + || udc->gadget.speed != USB_SPEED_HIGH) + ep0_stall(udc); + + if (udc->usb_state != USB_STATE_CONFIGURED + && udc->usb_state != USB_STATE_ADDRESS + && udc->usb_state != USB_STATE_DEFAULT) + ep0_stall(udc); + + mv_udc_testmode(udc, (setup->wIndex >> 8)); + goto out; default: goto out; } @@ -1599,8 +1789,7 @@ static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr) dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT]; /* Clear bit in ENDPTSETUPSTAT */ - temp = readl(&udc->op_regs->epsetupstat); - writel(temp | (1 << ep_num), &udc->op_regs->epsetupstat); + writel((1 << ep_num), &udc->op_regs->epsetupstat); /* while a hazard exists when setup package arrives */ do { @@ -1871,23 +2060,57 @@ static irqreturn_t mv_udc_irq(int irq, void *dev) return IRQ_HANDLED; } +static irqreturn_t mv_udc_vbus_irq(int irq, void *dev) +{ + struct mv_udc *udc = (struct mv_udc *)dev; + + /* polling VBUS and init phy may cause too much time*/ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); + + return IRQ_HANDLED; +} + +static void mv_udc_vbus_work(struct work_struct *work) +{ + struct mv_udc *udc; + unsigned int vbus; + + udc = container_of(work, struct mv_udc, vbus_work); + if (!udc->pdata->vbus) + return; + + vbus = udc->pdata->vbus->poll(); + dev_info(&udc->dev->dev, "vbus is %d\n", vbus); + + if (vbus == VBUS_HIGH) + mv_udc_vbus_session(&udc->gadget, 1); + else if (vbus == VBUS_LOW) + mv_udc_vbus_session(&udc->gadget, 0); +} + /* release device structure */ static void gadget_release(struct device *_dev) { struct mv_udc *udc = the_controller; complete(udc->done); - kfree(udc); } -static int mv_udc_remove(struct platform_device *dev) +static int __devexit mv_udc_remove(struct platform_device *dev) { struct mv_udc *udc = the_controller; - DECLARE_COMPLETION(done); + int clk_i; usb_del_gadget_udc(&udc->gadget); - udc->done = &done; + if (udc->qwork) { + flush_workqueue(udc->qwork); + destroy_workqueue(udc->qwork); + } + + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(udc->pdata->vbus->irq, &dev->dev); /* free memory allocated in probe */ if (udc->dtd_pool) @@ -1902,6 +2125,8 @@ static int mv_udc_remove(struct platform_device *dev) if (udc->irq) free_irq(udc->irq, &dev->dev); + mv_udc_disable(udc); + if (udc->cap_regs) iounmap(udc->cap_regs); udc->cap_regs = NULL; @@ -1915,45 +2140,62 @@ static int mv_udc_remove(struct platform_device *dev) kfree(udc->status_req); } + for (clk_i = 0; clk_i <= udc->clknum; clk_i++) + clk_put(udc->clk[clk_i]); + device_unregister(&udc->gadget.dev); /* free dev, wait for the release() finished */ - wait_for_completion(&done); + wait_for_completion(udc->done); + kfree(udc); the_controller = NULL; return 0; } -int mv_udc_probe(struct platform_device *dev) +static int __devinit mv_udc_probe(struct platform_device *dev) { + struct mv_usb_platform_data *pdata = dev->dev.platform_data; struct mv_udc *udc; int retval = 0; + int clk_i = 0; struct resource *r; size_t size; - udc = kzalloc(sizeof *udc, GFP_KERNEL); + if (pdata == NULL) { + dev_err(&dev->dev, "missing platform_data\n"); + return -ENODEV; + } + + size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum; + udc = kzalloc(size, GFP_KERNEL); if (udc == NULL) { dev_err(&dev->dev, "failed to allocate memory for udc\n"); - retval = -ENOMEM; - goto error; + return -ENOMEM; } + the_controller = udc; + udc->done = &release_done; + udc->pdata = dev->dev.platform_data; spin_lock_init(&udc->lock); udc->dev = dev; - udc->clk = clk_get(&dev->dev, "U2OCLK"); - if (IS_ERR(udc->clk)) { - retval = PTR_ERR(udc->clk); - goto error; + udc->clknum = pdata->clknum; + for (clk_i = 0; clk_i < udc->clknum; clk_i++) { + udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]); + if (IS_ERR(udc->clk[clk_i])) { + retval = PTR_ERR(udc->clk[clk_i]); + goto err_put_clk; + } } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_put_clk; } udc->cap_regs = (struct mv_cap_regs __iomem *) @@ -1961,29 +2203,31 @@ int mv_udc_probe(struct platform_device *dev) if (udc->cap_regs == NULL) { dev_err(&dev->dev, "failed to map I/O memory\n"); retval = -EBUSY; - goto error; + goto err_put_clk; } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); if (r == NULL) { dev_err(&dev->dev, "no phy I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_iounmap_capreg; } udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r)); if (udc->phy_regs == 0) { dev_err(&dev->dev, "failed to map phy I/O memory\n"); retval = -EBUSY; - goto error; + goto err_iounmap_capreg; } /* we will acces controller register, so enable the clk */ - clk_enable(udc->clk); - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(&dev->dev, "phy initialization error %d\n", retval); - goto error; + udc_clock_enable(udc); + if (pdata->phy_init) { + retval = pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&dev->dev, "phy init error %d\n", retval); + goto err_iounmap_phyreg; + } } udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs @@ -1991,6 +2235,13 @@ int mv_udc_probe(struct platform_device *dev) & CAPLENGTH_MASK)); udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK; + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop udc here. + */ + udc_stop(udc); + writel(0xFFFFFFFF, &udc->op_regs->usbsts); + size = udc->max_eps * sizeof(struct mv_dqh) *2; size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); udc->ep_dqh = dma_alloc_coherent(&dev->dev, size, @@ -1999,7 +2250,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->ep_dqh == NULL) { dev_err(&dev->dev, "allocate dQH memory failed\n"); retval = -ENOMEM; - goto error; + goto err_disable_clock; } udc->ep_dqh_size = size; @@ -2012,7 +2263,7 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->dtd_pool) { retval = -ENOMEM; - goto error; + goto err_free_dma; } size = udc->max_eps * sizeof(struct mv_ep) *2; @@ -2020,7 +2271,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->eps == NULL) { dev_err(&dev->dev, "allocate ep memory failed\n"); retval = -ENOMEM; - goto error; + goto err_destroy_dma; } /* initialize ep0 status request structure */ @@ -2028,13 +2279,13 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->status_req) { dev_err(&dev->dev, "allocate status_req memory failed\n"); retval = -ENOMEM; - goto error; + goto err_free_eps; } INIT_LIST_HEAD(&udc->status_req->queue); /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kzalloc(8, GFP_KERNEL); - udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + udc->status_req->req.dma = DMA_ADDR_INVALID; udc->resume_state = USB_STATE_NOTATTACHED; udc->usb_state = USB_STATE_POWERED; @@ -2045,15 +2296,15 @@ int mv_udc_probe(struct platform_device *dev) if (r == NULL) { dev_err(&dev->dev, "no IRQ resource defined\n"); retval = -ENODEV; - goto error; + goto err_free_status_req; } udc->irq = r->start; if (request_irq(udc->irq, mv_udc_irq, - IRQF_DISABLED | IRQF_SHARED, driver_name, udc)) { + IRQF_SHARED, driver_name, udc)) { dev_err(&dev->dev, "Request irq %d for UDC failed\n", udc->irq); retval = -ENODEV; - goto error; + goto err_free_status_req; } /* initialize gadget structure */ @@ -2072,18 +2323,82 @@ int mv_udc_probe(struct platform_device *dev) retval = device_register(&udc->gadget.dev); if (retval) - goto error; + goto err_free_irq; eps_init(udc); - the_controller = udc; + /* VBUS detect: we can disable/enable clock on demand.*/ + if (pdata->vbus) { + udc->clock_gating = 1; + retval = request_threaded_irq(pdata->vbus->irq, NULL, + mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); + if (retval) { + dev_info(&dev->dev, + "Can not request irq for VBUS, " + "disable clock gating\n"); + udc->clock_gating = 0; + } + + udc->qwork = create_singlethread_workqueue("mv_udc_queue"); + if (!udc->qwork) { + dev_err(&dev->dev, "cannot create workqueue\n"); + retval = -ENOMEM; + goto err_unregister; + } + + INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); + } + + /* + * When clock gating is supported, we can disable clk and phy. + * If not, it means that VBUS detection is not supported, we + * have to enable vbus active all the time to let controller work. + */ + if (udc->clock_gating) { + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + } else + udc->vbus_active = 1; retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); - if (!retval) - return retval; -error: - if (udc) - mv_udc_remove(udc->dev); + if (retval) + goto err_unregister; + + dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n", + udc->clock_gating ? "with" : "without"); + + return 0; + +err_unregister: + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(pdata->vbus->irq, &dev->dev); + device_unregister(&udc->gadget.dev); +err_free_irq: + free_irq(udc->irq, &dev->dev); +err_free_status_req: + kfree(udc->status_req->req.buf); + kfree(udc->status_req); +err_free_eps: + kfree(udc->eps); +err_destroy_dma: + dma_pool_destroy(udc->dtd_pool); +err_free_dma: + dma_free_coherent(&dev->dev, udc->ep_dqh_size, + udc->ep_dqh, udc->ep_dqh_dma); +err_disable_clock: + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); +err_iounmap_phyreg: + iounmap((void *)udc->phy_regs); +err_iounmap_capreg: + iounmap(udc->cap_regs); +err_put_clk: + for (clk_i--; clk_i >= 0; clk_i--) + clk_put(udc->clk[clk_i]); + the_controller = NULL; + kfree(udc); return retval; } @@ -2102,11 +2417,16 @@ static int mv_udc_resume(struct device *_dev) struct mv_udc *udc = the_controller; int retval; - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(_dev, "phy initialization error %d\n", retval); - return retval; + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d when resume back\n", + retval); + return retval; + } } + udc_reset(udc); ep0_reset(udc); udc_start(udc); @@ -2120,9 +2440,21 @@ static const struct dev_pm_ops mv_udc_pm_ops = { }; #endif +static void mv_udc_shutdown(struct platform_device *dev) +{ + struct mv_udc *udc = the_controller; + u32 mode; + + /* reset controller mode to IDLE */ + mode = readl(&udc->op_regs->usbmode); + mode &= ~3; + writel(mode, &udc->op_regs->usbmode); +} + static struct platform_driver udc_driver = { .probe = mv_udc_probe, .remove = __exit_p(mv_udc_remove), + .shutdown = mv_udc_shutdown, .driver = { .owner = THIS_MODULE, .name = "pxa-u2o", diff --git a/drivers/usb/gadget/mv_udc_phy.c b/drivers/usb/gadget/mv_udc_phy.c deleted file mode 100644 index d4dea97e38a5..000000000000 --- a/drivers/usb/gadget/mv_udc_phy.c +++ /dev/null @@ -1,214 +0,0 @@ -#include <linux/delay.h> -#include <linux/timer.h> -#include <linux/io.h> -#include <linux/errno.h> - -#include <mach/cputype.h> - -#ifdef CONFIG_ARCH_MMP - -#define UTMI_REVISION 0x0 -#define UTMI_CTRL 0x4 -#define UTMI_PLL 0x8 -#define UTMI_TX 0xc -#define UTMI_RX 0x10 -#define UTMI_IVREF 0x14 -#define UTMI_T0 0x18 -#define UTMI_T1 0x1c -#define UTMI_T2 0x20 -#define UTMI_T3 0x24 -#define UTMI_T4 0x28 -#define UTMI_T5 0x2c -#define UTMI_RESERVE 0x30 -#define UTMI_USB_INT 0x34 -#define UTMI_DBG_CTL 0x38 -#define UTMI_OTG_ADDON 0x3c - -/* For UTMICTRL Register */ -#define UTMI_CTRL_USB_CLK_EN (1 << 31) -/* pxa168 */ -#define UTMI_CTRL_SUSPEND_SET1 (1 << 30) -#define UTMI_CTRL_SUSPEND_SET2 (1 << 29) -#define UTMI_CTRL_RXBUF_PDWN (1 << 24) -#define UTMI_CTRL_TXBUF_PDWN (1 << 11) - -#define UTMI_CTRL_INPKT_DELAY_SHIFT 30 -#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28 -#define UTMI_CTRL_PU_REF_SHIFT 20 -#define UTMI_CTRL_ARC_PULLDN_SHIFT 12 -#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1 -#define UTMI_CTRL_PWR_UP_SHIFT 0 -/* For UTMI_PLL Register */ -#define UTMI_PLL_CLK_BLK_EN_SHIFT 24 -#define UTMI_PLL_FBDIV_SHIFT 4 -#define UTMI_PLL_REFDIV_SHIFT 0 -#define UTMI_PLL_FBDIV_MASK 0x00000FF0 -#define UTMI_PLL_REFDIV_MASK 0x0000000F -#define UTMI_PLL_ICP_MASK 0x00007000 -#define UTMI_PLL_KVCO_MASK 0x00031000 -#define UTMI_PLL_PLLCALI12_SHIFT 29 -#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29) -#define UTMI_PLL_PLLVDD18_SHIFT 27 -#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27) -#define UTMI_PLL_PLLVDD12_SHIFT 25 -#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25) -#define UTMI_PLL_KVCO_SHIFT 15 -#define UTMI_PLL_ICP_SHIFT 12 -/* For UTMI_TX Register */ -#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27 -#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27) -#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26 -#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26) -#define UTMI_TX_LOW_VDD_EN_SHIFT 11 -#define UTMI_TX_IMPCAL_VTH_SHIFT 14 -#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14) -#define UTMI_TX_CK60_PHSEL_SHIFT 17 -#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17) -#define UTMI_TX_TXVDD12_SHIFT 22 -#define UTMI_TX_TXVDD12_MASK (0x3 << 22) -#define UTMI_TX_AMP_SHIFT 0 -#define UTMI_TX_AMP_MASK (0x7 << 0) -/* For UTMI_RX Register */ -#define UTMI_RX_SQ_THRESH_SHIFT 4 -#define UTMI_RX_SQ_THRESH_MASK (0xf << 4) -#define UTMI_REG_SQ_LENGTH_SHIFT 15 -#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15) - -#define REG_RCAL_START 0x00001000 -#define VCOCAL_START 0x00200000 -#define KVCO_EXT 0x00400000 -#define PLL_READY 0x00800000 -#define CLK_BLK_EN 0x01000000 -#endif - -static unsigned int u2o_read(unsigned int base, unsigned int offset) -{ - return readl(base + offset); -} - -static void u2o_set(unsigned int base, unsigned int offset, unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg |= value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_clear(unsigned int base, unsigned int offset, - unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg &= ~value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_write(unsigned int base, unsigned int offset, - unsigned int value) -{ - writel(value, base + offset); - readl(base + offset); -} - -#ifdef CONFIG_ARCH_MMP -int mv_udc_phy_init(unsigned int base) -{ - unsigned long timeout; - - /* Initialize the USB PHY power */ - if (cpu_is_pxa910()) { - u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT) - | (1 << UTMI_CTRL_PU_REF_SHIFT)); - } - - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT); - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT); - - /* UTMI_PLL settings */ - u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK - | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK - | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK - | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK); - - u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT) - | (0xb << UTMI_PLL_REFDIV_SHIFT) - | (3 << UTMI_PLL_PLLVDD18_SHIFT) - | (3 << UTMI_PLL_PLLVDD12_SHIFT) - | (3 << UTMI_PLL_PLLCALI12_SHIFT) - | (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT)); - - /* UTMI_TX */ - u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK - | UTMI_TX_TXVDD12_MASK - | UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK - | UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK); - u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT) - | (4 << UTMI_TX_CK60_PHSEL_SHIFT) - | (4 << UTMI_TX_IMPCAL_VTH_SHIFT) - | (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT) - | (3 << UTMI_TX_AMP_SHIFT)); - - /* UTMI_RX */ - u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK - | UTMI_REG_SQ_LENGTH_MASK); - if (cpu_is_pxa168()) - u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - else - u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - - /* UTMI_IVREF */ - if (cpu_is_pxa168()) - /* - * fixing Microsoft Altair board interface with NEC hub issue - - * Set UTMI_IVREF from 0x4a3 to 0x4bf - */ - u2o_write(base, UTMI_IVREF, 0x4bf); - - /* calibrate */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - /* toggle VCOCAL_START bit of UTMI_PLL */ - udelay(200); - u2o_set(base, UTMI_PLL, VCOCAL_START); - udelay(40); - u2o_clear(base, UTMI_PLL, VCOCAL_START); - - /* toggle REG_RCAL_START bit of UTMI_TX */ - udelay(200); - u2o_set(base, UTMI_TX, REG_RCAL_START); - udelay(40); - u2o_clear(base, UTMI_TX, REG_RCAL_START); - udelay(200); - - /* make sure phy is ready */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - if (cpu_is_pxa168()) { - u2o_set(base, UTMI_RESERVE, 1 << 5); - /* Turn on UTMI PHY OTG extension */ - u2o_write(base, UTMI_OTG_ADDON, 1); - } - return 0; -} -#else -int mv_udc_phy_init(unsigned int base) -{ - return 0; -} -#endif diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 62ee5087dcac..89530034dff1 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -14,15 +14,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define DEBUG */ diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index df886cec5ef4..b0e52fc277b4 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -10,12 +10,6 @@ * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * */ #ifndef _LINUX_NDIS_H diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index ab98ea926a11..d1b76368472f 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -204,7 +204,7 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max & 0x7fff; @@ -1172,17 +1172,18 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2272_stop(struct usb_gadget_driver *driver); +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2272_ops = { - .get_frame = net2272_get_frame, - .wakeup = net2272_wakeup, + .get_frame = net2272_get_frame, + .wakeup = net2272_wakeup, .set_selfpowered = net2272_set_selfpowered, - .pullup = net2272_pullup, - .start = net2272_start, - .stop = net2272_stop, + .pullup = net2272_pullup, + .udc_start = net2272_start, + .udc_stop = net2272_stop, }; /*---------------------------------------------------------------------------*/ @@ -1356,8 +1357,6 @@ net2272_set_fifo_mode(struct net2272 *dev, int mode) /*---------------------------------------------------------------------------*/ -static struct net2272 *the_controller; - static void net2272_usb_reset(struct net2272 *dev) { @@ -1453,20 +1452,17 @@ net2272_ep0_start(struct net2272 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; - int ret; + struct net2272 *dev; unsigned i; - if (!driver || !bind || !driver->unbind || !driver->setup || + if (!driver || !driver->unbind || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of(_gadget, struct net2272, gadget); for (i = 0; i < 4; ++i) dev->ep[i].irqs = 0; @@ -1475,14 +1471,6 @@ static int net2272_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - ret = bind(&dev->gadget); - if (ret) { - dev_dbg(dev->dev, "bind to driver %s --> %d\n", - driver->driver.name, ret); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return ret; - } /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1510,33 +1498,21 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 4; ++i) net2272_dequeue_all(&dev->ep[i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - - } net2272_usb_reinit(dev); } -static int net2272_stop(struct usb_gadget_driver *driver) +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; + struct net2272 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver) - return -EINVAL; + dev = container_of(_gadget, struct net2272, gadget); spin_lock_irqsave(&dev->lock, flags); stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - net2272_pullup(&dev->gadget, 0); - - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -1764,8 +1740,8 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) dev->gadget.speed = USB_SPEED_HIGH; else dev->gadget.speed = USB_SPEED_FULL; - dev_dbg(dev->dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full"); + dev_dbg(dev->dev, "%s\n", + usb_speed_string(dev->gadget.speed)); } ep = &dev->ep[0]; @@ -2238,7 +2214,6 @@ net2272_remove(struct net2272 *dev) device_remove_file(dev->dev, &dev_attr_registers); dev_info(dev->dev, "unbind\n"); - the_controller = NULL; } static struct net2272 * __devinit @@ -2246,11 +2221,6 @@ net2272_probe_init(struct device *dev, unsigned int irq) { struct net2272 *ret; - if (the_controller) { - dev_warn(dev, "ignoring\n"); - return ERR_PTR(-EBUSY); - } - if (!irq) { dev_dbg(dev, "No IRQ!\n"); return ERR_PTR(-ENODEV); @@ -2307,8 +2277,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) dma_mode_string()); dev_info(dev->dev, "version: %s\n", driver_vers); - the_controller = dev; - ret = device_register(&dev->gadget.dev); if (ret) goto err_irq; @@ -2684,8 +2652,6 @@ net2272_plat_probe(struct platform_device *pdev) dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); - the_controller = dev; - return 0; err_io: diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 3dd40b4e675c..7f1bc9a73cda 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -33,15 +33,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG /* messages on error and most fault paths */ @@ -169,7 +160,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EDOM; /* sanity check ep-e/ep-f since their fifos are small */ - max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp (desc) & 0x1fff; if (ep->num > 4 && max > 64) return -ERANGE; @@ -1410,17 +1401,18 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2280_stop(struct usb_gadget_driver *driver); +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, - .start = net2280_start, - .stop = net2280_stop, + .udc_start = net2280_start, + .udc_stop = net2280_stop, }; /*-------------------------------------------------------------------------*/ @@ -1640,7 +1632,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) default: val = "iso"; break; }; val; }), - le16_to_cpu (d->wMaxPacketSize) & 0x1fff, + usb_endpoint_maxp (d) & 0x1fff, ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ @@ -1753,8 +1745,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode) * perhaps to bind specific drivers to specific devices. */ -static struct net2280 *the_controller; - static void usb_reset (struct net2280 *dev) { u32 tmp; @@ -1880,10 +1870,10 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; int retval; unsigned i; @@ -1891,14 +1881,11 @@ static int net2280_start(struct usb_gadget_driver *driver, * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) * "must not be used in normal operation" */ - if (!driver - || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) + if (!driver || driver->speed != USB_SPEED_HIGH + || !driver->setup) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of (_gadget, struct net2280, gadget); for (i = 0; i < 7; i++) dev->ep [i].irqs = 0; @@ -1908,14 +1895,6 @@ static int net2280_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget); - if (retval) { - DEBUG (dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } retval = device_create_file (&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; @@ -1961,33 +1940,21 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 7; i++) nuke (&dev->ep [i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock (&dev->lock); - driver->disconnect (&dev->gadget); - spin_lock (&dev->lock); - } - usb_reinit (dev); } -static int net2280_stop(struct usb_gadget_driver *driver) +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; + dev = container_of (_gadget, struct net2280, gadget); spin_lock_irqsave (&dev->lock, flags); stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - net2280_pullup (&dev->gadget, 0); - - driver->unbind (&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2266,9 +2233,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) else dev->gadget.speed = USB_SPEED_FULL; net2280_led_speed (dev, dev->gadget.speed); - DEBUG (dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) - ? "high" : "full"); + DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } ep = &dev->ep [0]; @@ -2481,7 +2446,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. - * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT * only indicates a change in the reset state). */ @@ -2709,8 +2674,6 @@ static void net2280_remove (struct pci_dev *pdev) pci_set_drvdata (pdev, NULL); INFO (dev, "unbind\n"); - - the_controller = NULL; } /* wrap this driver around the specified device, but @@ -2724,14 +2687,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval, i; - /* if you want to support more than one controller in a system, - * usb_gadget_driver_{register,unregister}() must change. - */ - if (the_controller) { - dev_warn (&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc (sizeof *dev, GFP_KERNEL); if (dev == NULL){ @@ -2858,8 +2813,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - the_controller = dev; - retval = device_register (&dev->gadget.dev); if (retval) goto done; retval = device_create_file (&pdev->dev, &dev_attr_registers); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index c36852263d93..a844be0d683a 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -11,15 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/usb/net2280.h> diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 740c7daed279..788989a10223 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG @@ -166,15 +157,14 @@ static int omap_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->maxpacket < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->maxpacket < usb_endpoint_maxp(desc)) { DBG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } - maxp = le16_to_cpu (desc->wMaxPacketSize); + maxp = usb_endpoint_maxp(desc); if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && maxp != ep->maxpacket) - || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket + || usb_endpoint_maxp(desc) > ep->maxpacket || !desc->wMaxPacketSize) { DBG("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; @@ -2968,7 +2958,7 @@ known: } #ifdef USE_ISO status = request_irq(pdev->resource[3].start, omap_udc_iso_irq, - IRQF_DISABLED, "omap_udc iso", udc); + 0, "omap_udc iso", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[3].start, status); diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index f96615ab6b77..550d6dcdf104 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -4,15 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> @@ -947,7 +938,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, else buff_size = UDC_EPOUT_BUFF_SIZE; pch_udc_ep_set_bufsz(ep, buff_size, ep->in); - pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize)); + pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc)); pch_udc_ep_set_nak(ep); pch_udc_ep_fifo_flush(ep, ep->in); /* Configure the endpoint */ @@ -957,7 +948,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) | (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) | (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) | - le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT; + usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT; if (ep->in) pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num)); @@ -1466,7 +1457,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep, ep->desc = desc; ep->halted = 0; pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc); - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); spin_unlock_irqrestore(&dev->lock, iflags); return 0; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index a341dde6f9c3..65a8834f274b 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> @@ -971,23 +962,15 @@ printer_set_config(struct printer_dev *dev, unsigned number) usb_gadget_vbus_draw(dev->gadget, dev->gadget->is_otg ? 8 : 100); } else { - char *speed; unsigned power; power = 2 * config_desc.bMaxPower; usb_gadget_vbus_draw(dev->gadget, power); - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - dev->config = number; - INFO(dev, "%s speed config #%d: %d mA, %s\n", - speed, number, power, driver_desc); + INFO(dev, "%s config #%d: %d mA, %s\n", + usb_speed_string(gadget->speed), + number, power, driver_desc); } return result; } @@ -1611,7 +1594,7 @@ cleanup(void) if (status) ERROR(dev, "usb_gadget_unregister_driver %x\n", status); - unregister_chrdev_region(g_printer_devno, 2); + unregister_chrdev_region(g_printer_devno, 1); class_destroy(usb_gadget_class); mutex_unlock(&usb_printer_gadget.lock_printer_io); } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index e4e59b4de25d..c090a7e3ecf8 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -11,16 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ /* #define VERBOSE_DEBUG */ @@ -232,8 +222,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->fifo_size < usb_endpoint_maxp (desc)) { DMSG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } @@ -248,7 +237,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu (desc->wMaxPacketSize) + && usb_endpoint_maxp (desc) != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); @@ -264,7 +253,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, ep->desc = desc; ep->stopped = 0; ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp (desc); /* flush fifo (mostly for OUT buffers) */ pxa25x_ep_fifo_flush (_ep); @@ -401,7 +390,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) { unsigned max; - max = le16_to_cpu(ep->desc->wMaxPacketSize); + max = usb_endpoint_maxp(ep->desc); do { unsigned count; int is_last, is_short; @@ -671,8 +660,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * we can report per-packet status. that also helps with dma. */ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - && req->req.length > le16_to_cpu - (ep->desc->wMaxPacketSize))) + && req->req.length > usb_endpoint_maxp (ep->desc))) return -EMSGSIZE; DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", @@ -1105,7 +1093,7 @@ udc_seq_show(struct seq_file *m, void *_d) tmp = *dev->ep [i].reg_udccs; seq_printf(m, "%s max %d %s udccs %02x irqs %lu\n", - ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), + ep->ep.name, usb_endpoint_maxp(desc), "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ @@ -2202,7 +2190,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, - IRQF_DISABLED, driver_name, dev); + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); @@ -2214,7 +2202,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) if (machine_is_lubbock()) { retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", @@ -2223,7 +2211,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) } retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index f572c5617462..8eaf4e43726b 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA25X_H @@ -161,8 +152,6 @@ static struct pxa25x_udc *the_controller; #ifdef DEBUG -static int is_vbus_present(void); - static const char *state_name[] = { "EP0_IDLE", "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", @@ -214,8 +203,7 @@ dump_state(struct pxa25x_udc *dev) u32 tmp; unsigned i; - DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - is_vbus_present() ? "host " : "disconnected", + DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", state_name[dev->ep0state], UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); dump_udccr("udccr"); @@ -232,9 +220,6 @@ dump_state(struct pxa25x_udc *dev) } else DMSG("ep0 driver '%s'\n", dev->driver->driver.name); - if (!is_vbus_present()) - return; - dump_udccs0 ("udccs0"); DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", dev->stats.write.bytes, dev->stats.write.ops, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 85b68c75dc9d..18b6b091f2a6 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -8,16 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/module.h> #include <linux/kernel.h> @@ -1439,7 +1429,7 @@ static int pxa_ep_enable(struct usb_ep *_ep, return -EINVAL; } - if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + if (ep->fifo_size < usb_endpoint_maxp(desc)) { ep_err(ep, "bad maxpacket\n"); return -ERANGE; } diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index b01696eab068..7f4e8f424e80 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA27X_H diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 50991e5bd5e8..68a826a1b866 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> @@ -28,13 +18,14 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/dma-mapping.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include "r8a66597-udc.h" -#define DRIVER_VERSION "2009-08-18" +#define DRIVER_VERSION "2011-09-26" static const char udc_name[] = "r8a66597_udc"; static const char *r8a66597_ep_name[] = { @@ -115,13 +106,15 @@ static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum) u16 pid = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { pid = r8a66597_read(r8a66597, DCPCTR) & PID; - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); pid = r8a66597_read(r8a66597, offset) & PID; - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return pid; } @@ -131,13 +124,15 @@ static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum, { unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { r8a66597_mdfy(r8a66597, pid, PID, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_mdfy(r8a66597, pid, PID, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } } static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum) @@ -160,13 +155,15 @@ static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum) u16 ret = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { ret = r8a66597_read(r8a66597, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); ret = r8a66597_read(r8a66597, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return ret; } @@ -177,13 +174,63 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) pipe_stop(r8a66597, pipenum); - if (pipenum == 0) + if (pipenum == 0) { r8a66597_bset(r8a66597, SQCLR, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_bset(r8a66597, SQCLR, offset); - } else - printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } +} + +static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) { + r8a66597_bset(r8a66597, SQSET, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQSET, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } +} + +static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + if (pipenum == 0) { + return r8a66597_read(r8a66597, DCPCTR) & SQMON; + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + return r8a66597_read(r8a66597, offset) & SQMON; + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } + + return 0; +} + +static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum) +{ + return control_reg_sqmon(r8a66597, pipenum); +} + +static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum, + u16 toggle) +{ + if (toggle) + control_reg_sqset(r8a66597, pipenum); + else + control_reg_sqclr(r8a66597, pipenum); } static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) @@ -222,18 +269,51 @@ static inline unsigned short mbw_value(struct r8a66597 *r8a66597) return MBW_16; } +static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum, + u16 isel, u16 fifosel) +{ + u16 tmp, mask, loop; + int i = 0; + + if (!pipenum) { + mask = ISEL | CURPIPE; + loop = isel; + } else { + mask = CURPIPE; + loop = pipenum; + } + r8a66597_mdfy(r8a66597, loop, mask, fifosel); + + do { + tmp = r8a66597_read(r8a66597, fifosel); + if (i++ > 1000000) { + dev_err(r8a66597_to_dev(r8a66597), + "r8a66597: register%x, loop %x " + "is timeout\n", fifosel, loop); + break; + } + ndelay(1); + } while ((tmp & mask) != loop); +} + static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum) { struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; if (ep->use_dma) - return; + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel); ndelay(450); - r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + if (r8a66597_is_sudmac(r8a66597) && ep->use_dma) + r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel); + else + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + + if (ep->use_dma) + r8a66597_bset(r8a66597, DREQE, ep->fifosel); } static int pipe_buffer_setting(struct r8a66597 *r8a66597, @@ -297,17 +377,18 @@ static void pipe_buffer_release(struct r8a66597 *r8a66597, if (info->pipe == 0) return; - if (is_bulk_pipe(info->pipe)) + if (is_bulk_pipe(info->pipe)) { r8a66597->bulk--; - else if (is_interrupt_pipe(info->pipe)) + } else if (is_interrupt_pipe(info->pipe)) { r8a66597->interrupt--; - else if (is_isoc_pipe(info->pipe)) { + } else if (is_isoc_pipe(info->pipe)) { r8a66597->isochronous--; if (info->type == R8A66597_BULK) r8a66597->bulk--; - } else - printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", - info->pipe); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "ep_release: unexpect pipenum (%d)\n", info->pipe); + } } static void pipe_initialize(struct r8a66597_ep *ep) @@ -337,11 +418,17 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->fifoaddr = CFIFO; ep->fifosel = CFIFOSEL; ep->fifoctr = CFIFOCTR; - ep->fifotrn = 0; ep->pipectr = get_pipectr_addr(pipenum); + if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) { + ep->pipetre = get_pipetre_addr(pipenum); + ep->pipetrn = get_pipetrn_addr(pipenum); + } else { + ep->pipetre = 0; + ep->pipetrn = 0; + } ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] = ep; @@ -381,7 +468,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, case USB_ENDPOINT_XFER_BULK: if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "bulk pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "bulk pipe is insufficient\n"); return -ENODEV; } else { info.pipe = R8A66597_BASE_PIPENUM_ISOC @@ -397,7 +485,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_INT: if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) { - printk(KERN_ERR "interrupt pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "interrupt pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt; @@ -406,7 +495,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_ISOC: if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "isochronous pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "isochronous pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous; @@ -414,13 +504,13 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, counter = &r8a66597->isochronous; break; default: - printk(KERN_ERR "unexpect xfer type\n"); + dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n"); return -EINVAL; } ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; @@ -429,7 +519,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, ret = pipe_buffer_setting(r8a66597, &info); if (ret < 0) { - printk(KERN_ERR "pipe_buffer_setting fail\n"); + dev_err(r8a66597_to_dev(r8a66597), + "pipe_buffer_setting fail\n"); return ret; } @@ -495,6 +586,124 @@ static void start_ep0_write(struct r8a66597_ep *ep, } } +static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum, + u16 fifosel) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE; + if (tmp == pipenum) + r8a66597_change_curpipe(r8a66597, 0, 0, fifosel); +} + +static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum, + int enable) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + u16 tmp, toggle; + + /* check current BFRE bit */ + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE; + if ((enable && tmp) || (!enable && !tmp)) + return; + + /* change BFRE bit */ + pipe_stop(r8a66597, pipenum); + disable_fifosel(r8a66597, pipenum, CFIFOSEL); + disable_fifosel(r8a66597, pipenum, D0FIFOSEL); + disable_fifosel(r8a66597, pipenum, D1FIFOSEL); + + toggle = save_usb_toggle(r8a66597, pipenum); + + r8a66597_write(r8a66597, pipenum, PIPESEL); + if (enable) + r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG); + else + r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG); + + /* initialize for internal BFRE flag */ + r8a66597_bset(r8a66597, ACLRM, ep->pipectr); + r8a66597_bclr(r8a66597, ACLRM, ep->pipectr); + + restore_usb_toggle(r8a66597, pipenum, toggle); +} + +static int sudmac_alloc_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597_dma *dma; + + if (!r8a66597_is_sudmac(r8a66597)) + return -ENODEV; + + /* Check transfer type */ + if (!is_bulk_pipe(ep->pipenum)) + return -EIO; + + if (r8a66597->dma.used) + return -EBUSY; + + /* set SUDMAC parameters */ + dma = &r8a66597->dma; + dma->used = 1; + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + dma->dir = 1; + } else { + dma->dir = 0; + change_bfre_mode(r8a66597, ep->pipenum, 1); + } + + /* set r8a66597_ep paramters */ + ep->use_dma = 1; + ep->dma = dma; + ep->fifoaddr = D0FIFO; + ep->fifosel = D0FIFOSEL; + ep->fifoctr = D0FIFOCTR; + + /* dma mapping */ + req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597), + req->req.buf, req->req.length, + dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + return 0; +} + +static void sudmac_free_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + if (!r8a66597_is_sudmac(r8a66597)) + return; + + dma_unmap_single(r8a66597_to_dev(ep->r8a66597), + req->req.dma, req->req.length, + ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); + r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel); + + ep->dma->used = 0; + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; +} + +static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + BUG_ON(req->req.length == 0); + + r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG); + r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA); + r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC); + r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL); + + r8a66597_sudmac_write(r8a66597, DEN, CH0DEN); +} + static void start_packet_write(struct r8a66597_ep *ep, struct r8a66597_request *req) { @@ -505,11 +714,29 @@ static void start_packet_write(struct r8a66597_ep *ep, disable_irq_empty(r8a66597, ep->pipenum); pipe_start(r8a66597, ep->pipenum); - tmp = r8a66597_read(r8a66597, ep->fifoctr); - if (unlikely((tmp & FRDY) == 0)) - pipe_irq_enable(r8a66597, ep->pipenum); - else - irq_packet_write(ep, req); + if (req->req.length == 0) { + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS); + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, ep->pipenum); + else + irq_packet_write(ep, req); + } else { + /* DMA mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_nrdy(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + enable_irq_nrdy(r8a66597, ep->pipenum); + sudmac_start(r8a66597, ep, req); + } + } } static void start_packet_read(struct r8a66597_ep *ep, @@ -524,17 +751,26 @@ static void start_packet_read(struct r8a66597_ep *ep, pipe_start(r8a66597, pipenum); pipe_irq_enable(r8a66597, pipenum); } else { - if (ep->use_dma) { - r8a66597_bset(r8a66597, TRCLR, ep->fifosel); - pipe_change(r8a66597, pipenum); - r8a66597_bset(r8a66597, TRENB, ep->fifosel); + pipe_stop(r8a66597, pipenum); + if (ep->pipetre) { + enable_irq_nrdy(r8a66597, pipenum); + r8a66597_write(r8a66597, TRCLR, ep->pipetre); r8a66597_write(r8a66597, - (req->req.length + ep->ep.maxpacket - 1) - / ep->ep.maxpacket, - ep->fifotrn); + DIV_ROUND_UP(req->req.length, ep->ep.maxpacket), + ep->pipetrn); + r8a66597_bset(r8a66597, TRENB, ep->pipetre); + } + + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + change_bfre_mode(r8a66597, ep->pipenum, 0); + pipe_start(r8a66597, pipenum); /* trigger once */ + pipe_irq_enable(r8a66597, pipenum); + } else { + pipe_change(r8a66597, pipenum); + sudmac_start(r8a66597, ep, req); + pipe_start(r8a66597, pipenum); /* trigger once */ } - pipe_start(r8a66597, pipenum); /* trigger once */ - pipe_irq_enable(r8a66597, pipenum); } } @@ -564,7 +800,8 @@ static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req) control_end(ep->r8a66597, 0); break; default: - printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(ep->r8a66597), + "start_ep0: unexpect ctsq(%x)\n", ctsq); break; } } @@ -690,6 +927,9 @@ __acquires(r8a66597->lock) if (!list_empty(&ep->queue)) restart = 1; + if (ep->use_dma) + sudmac_free_channel(ep->r8a66597, ep, req); + spin_unlock(&ep->r8a66597->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->r8a66597->lock); @@ -718,7 +958,8 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) do { tmp = r8a66597_read(r8a66597, ep->fifoctr); if (i++ > 100000) { - printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + dev_err(r8a66597_to_dev(r8a66597), + "pipe0 is busy. maybe cpu i/o bus " "conflict. please power off this controller."); return; } @@ -733,7 +974,7 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) /* write fifo */ if (req->req.buf) { if (size > 0) - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) r8a66597_bset(r8a66597, BVAL, ep->fifoctr); } @@ -769,7 +1010,8 @@ static void irq_packet_write(struct r8a66597_ep *ep, if (unlikely((tmp & FRDY) == 0)) { pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + dev_err(r8a66597_to_dev(r8a66597), + "write fifo not ready. pipnum=%d\n", pipenum); return; } @@ -780,7 +1022,7 @@ static void irq_packet_write(struct r8a66597_ep *ep, /* write fifo */ if (req->req.buf) { - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || ((bufsize != ep->ep.maxpacket) @@ -819,7 +1061,7 @@ static void irq_packet_read(struct r8a66597_ep *ep, req->req.status = -EPIPE; pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "read fifo not ready"); + dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready"); return; } @@ -1095,7 +1337,7 @@ static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597) break; default: r8a66597->gadget.speed = USB_SPEED_UNKNOWN; - printk(KERN_ERR "USB speed unknown\n"); + dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n"); } } @@ -1158,11 +1400,71 @@ __acquires(r8a66597->lock) control_end(r8a66597, 0); break; default: - printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(r8a66597), + "ctrl_stage: unexpect ctsq(%x)\n", ctsq); break; } } +static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep) +{ + u16 pipenum; + struct r8a66597_request *req; + u32 len; + int i = 0; + + pipenum = ep->pipenum; + pipe_change(r8a66597, pipenum); + + while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) { + udelay(1); + if (unlikely(i++ >= 10000)) { /* timeout = 10 msec */ + dev_err(r8a66597_to_dev(r8a66597), + "%s: FRDY was not set (%d)\n", + __func__, pipenum); + return; + } + } + + r8a66597_bset(r8a66597, BCLR, ep->fifoctr); + req = get_request_from_ep(ep); + + /* prepare parameters */ + len = r8a66597_sudmac_read(r8a66597, CH0CBC); + req->req.actual += len; + + /* clear */ + r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR); + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (len % ep->ep.maxpacket)) { + if (ep->dma->dir) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + /* Clear the interrupt flag for next transfer */ + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + transfer_complete(ep, req, 0); + } + } +} + +static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597) +{ + u32 irqsts; + struct r8a66597_ep *ep; + u16 pipenum; + + irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS); + if (irqsts & CH0ENDS) { + r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR); + pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE); + ep = r8a66597->pipenum2ep[pipenum]; + sudmac_finish(r8a66597, ep); + } +} + static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) { struct r8a66597 *r8a66597 = _r8a66597; @@ -1173,6 +1475,9 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) u16 savepipe; u16 mask0; + if (r8a66597_is_sudmac(r8a66597)) + r8a66597_sudmac_irq(r8a66597); + spin_lock(&r8a66597->lock); intsts0 = r8a66597_read(r8a66597, INTSTS0); @@ -1433,23 +1738,18 @@ static struct usb_ep_ops r8a66597_ep_ops = { }; /*-------------------------------------------------------------------------*/ -static struct r8a66597 *the_controller; - -static int r8a66597_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int r8a66597_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); int retval; if (!driver || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) return -EINVAL; if (!r8a66597) return -ENODEV; - if (r8a66597->driver) - return -EBUSY; /* hook up the driver */ driver->driver.bus = NULL; @@ -1458,14 +1758,8 @@ static int r8a66597_start(struct usb_gadget_driver *driver, retval = device_add(&r8a66597->gadget.dev); if (retval) { - printk(KERN_ERR "device_add error (%d)\n", retval); - goto error; - } - - retval = bind(&r8a66597->gadget); - if (retval) { - printk(KERN_ERR "bind to driver error (%d)\n", retval); - device_del(&r8a66597->gadget.dev); + dev_err(r8a66597_to_dev(r8a66597), "device_add error (%d)\n", + retval); goto error; } @@ -1489,23 +1783,17 @@ error: return retval; } -static int r8a66597_stop(struct usb_gadget_driver *driver) +static int r8a66597_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); unsigned long flags; - if (driver != r8a66597->driver || !driver->unbind) - return -EINVAL; - spin_lock_irqsave(&r8a66597->lock, flags); - if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN) - r8a66597_usb_disconnect(r8a66597); r8a66597_bclr(r8a66597, VBSE, INTENB0); disable_controller(r8a66597); spin_unlock_irqrestore(&r8a66597->lock, flags); - driver->unbind(&r8a66597->gadget); - device_del(&r8a66597->gadget.dev); r8a66597->driver = NULL; return 0; @@ -1535,8 +1823,8 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on) static struct usb_gadget_ops r8a66597_gadget_ops = { .get_frame = r8a66597_get_frame, - .start = r8a66597_start, - .stop = r8a66597_stop, + .udc_start = r8a66597_start, + .udc_stop = r8a66597_stop, .pullup = r8a66597_pullup, }; @@ -1547,6 +1835,8 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); iounmap(r8a66597->reg); + if (r8a66597->pdata->sudmac) + iounmap(r8a66597->sudmac_reg); free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); #ifdef CONFIG_HAVE_CLK @@ -1563,6 +1853,26 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } +static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, + struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); + if (!res) { + dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); + return -ENODEV; + } + + r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); + if (r8a66597->sudmac_reg == NULL) { + dev_err(&pdev->dev, "ioremap error(sudmac).\n"); + return -ENOMEM; + } + + return 0; +} + static int __init r8a66597_probe(struct platform_device *pdev) { #ifdef CONFIG_HAVE_CLK @@ -1579,7 +1889,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - printk(KERN_ERR "platform_get_resource error.\n"); + dev_err(&pdev->dev, "platform_get_resource error.\n"); goto clean_up; } @@ -1589,14 +1899,14 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (irq < 0) { ret = -ENODEV; - printk(KERN_ERR "platform_get_irq error.\n"); + dev_err(&pdev->dev, "platform_get_irq error.\n"); goto clean_up; } reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; - printk(KERN_ERR "ioremap error.\n"); + dev_err(&pdev->dev, "ioremap error.\n"); goto clean_up; } @@ -1604,7 +1914,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); if (r8a66597 == NULL) { ret = -ENOMEM; - printk(KERN_ERR "kzalloc error\n"); + dev_err(&pdev->dev, "kzalloc error\n"); goto clean_up; } @@ -1640,13 +1950,18 @@ static int __init r8a66597_probe(struct platform_device *pdev) clk_enable(r8a66597->clk); } #endif + if (r8a66597->pdata->sudmac) { + ret = r8a66597_sudmac_ioremap(r8a66597, pdev); + if (ret < 0) + goto clean_up2; + } disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, udc_name, r8a66597); if (ret < 0) { - printk(KERN_ERR "request_irq error (%d)\n", ret); + dev_err(&pdev->dev, "request_irq error (%d)\n", ret); goto clean_up2; } @@ -1672,13 +1987,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->ep[0].fifoaddr = CFIFO; r8a66597->ep[0].fifosel = CFIFOSEL; r8a66597->ep[0].fifoctr = CFIFOCTR; - r8a66597->ep[0].fifotrn = 0; r8a66597->ep[0].pipectr = get_pipectr_addr(0); r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; - the_controller = r8a66597; - r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, GFP_KERNEL); if (r8a66597->ep0_req == NULL) @@ -1705,6 +2017,8 @@ clean_up2: #endif clean_up: if (r8a66597) { + if (r8a66597->sudmac_reg) + iounmap(r8a66597->sudmac_reg); if (r8a66597->ep0_req) r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 503f766c23a7..8e3de61cd4b8 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __R8A66597_H__ @@ -53,6 +43,7 @@ ((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \ (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) +#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac) struct r8a66597_pipe_info { u16 pipe; u16 epnum; @@ -70,6 +61,7 @@ struct r8a66597_request { struct r8a66597_ep { struct usb_ep ep; struct r8a66597 *r8a66597; + struct r8a66597_dma *dma; struct list_head queue; unsigned busy:1; @@ -85,13 +77,20 @@ struct r8a66597_ep { unsigned char fifoaddr; unsigned char fifosel; unsigned char fifoctr; - unsigned char fifotrn; unsigned char pipectr; + unsigned char pipetre; + unsigned char pipetrn; +}; + +struct r8a66597_dma { + unsigned used:1; + unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */ }; struct r8a66597 { spinlock_t lock; void __iomem *reg; + void __iomem *sudmac_reg; #ifdef CONFIG_HAVE_CLK struct clk *clk; @@ -104,6 +103,7 @@ struct r8a66597 { struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *epaddr2ep[16]; + struct r8a66597_dma dma; struct timer_list timer; struct usb_request *ep0_req; /* for internal request */ @@ -124,6 +124,7 @@ struct r8a66597 { #define gadget_to_r8a66597(_gadget) \ container_of(_gadget, struct r8a66597, gadget) #define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget) +#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent) static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) { @@ -182,12 +183,27 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, iowrite16(val, r8a66597->reg + offset); } +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, + struct r8a66597_ep *ep, unsigned char *buf, int len) { - void __iomem *fifoaddr = r8a66597->reg + offset; + void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr; int adj = 0; int i; @@ -215,18 +231,12 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, adj = 0x01; /* 16-bit wide */ } + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); for (i = 0; i < len; i++) iowrite8(buf[i], fifoaddr + adj - (i & adj)); -} - -static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, - u16 val, u16 pat, unsigned long offset) -{ - u16 tmp; - tmp = r8a66597_read(r8a66597, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - r8a66597_write(r8a66597, tmp, offset); + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); } static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) @@ -251,12 +261,21 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) return clock; } -#define r8a66597_bclr(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, 0, val, offset) -#define r8a66597_bset(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, val, 0, offset) +static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597, + unsigned long offset) +{ + return ioread32(r8a66597->sudmac_reg + offset); +} + +static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val, + unsigned long offset) +{ + iowrite32(val, r8a66597->sudmac_reg + offset); +} #define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) #define enable_irq_ready(r8a66597, pipenum) \ enable_pipe_irq(r8a66597, pipenum, BRDYENB) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 8bdee67ce09a..a552453dc946 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1951,30 +1951,26 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) case S3C_DSTS_EnumSpd_FS: case S3C_DSTS_EnumSpd_FS48: hsotg->gadget.speed = USB_SPEED_FULL; - dev_info(hsotg->dev, "new device is full-speed\n"); - ep0_mps = EP0_MPS_LIMIT; ep_mps = 64; break; case S3C_DSTS_EnumSpd_HS: - dev_info(hsotg->dev, "new device is high-speed\n"); hsotg->gadget.speed = USB_SPEED_HIGH; - ep0_mps = EP0_MPS_LIMIT; ep_mps = 512; break; case S3C_DSTS_EnumSpd_LS: hsotg->gadget.speed = USB_SPEED_LOW; - dev_info(hsotg->dev, "new device is low-speed\n"); - /* note, we don't actually support LS in this driver at the * moment, and the documentation seems to imply that it isn't * supported by the PHYs on some of the devices. */ break; } + dev_info(hsotg->dev, "new device is %s\n", + usb_speed_string(hsotg->gadget.speed)); /* we should now know the maximum packet size for an * endpoint, so set the endpoints to a default value. */ @@ -2297,7 +2293,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, return -EINVAL; } - mps = le16_to_cpu(desc->wMaxPacketSize); + mps = usb_endpoint_maxp(desc); /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 3fa717c5f4bc..8d54f893cefe 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -26,6 +26,7 @@ #include <linux/clk.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/usb/otg.h> #include <linux/prefetch.h> #include <mach/regs-s3c2443-clock.h> @@ -137,6 +138,7 @@ struct s3c_hsudc { struct usb_gadget_driver *driver; struct device *dev; struct s3c24xx_hsudc_platdata *pd; + struct otg_transceiver *transceiver; spinlock_t lock; void __iomem *regs; struct resource *mem_rsrc; @@ -759,11 +761,11 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || hsep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || hsep->bEndpointAddress != desc->bEndpointAddress - || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) + || ep_maxpacket(hsep) < usb_endpoint_maxp(desc)) return -EINVAL; if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) + && usb_endpoint_maxp(desc) != ep_maxpacket(hsep)) || !desc->wMaxPacketSize) return -ERANGE; @@ -779,7 +781,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, hsep->stopped = hsep->wedge = 0; hsep->desc = desc; - hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + hsep->ep.maxpacket = usb_endpoint_maxp(desc); s3c_hsudc_set_halt(_ep, 0); __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); @@ -1171,6 +1173,22 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, return ret; } + /* connect to bus through transceiver */ + if (hsudc->transceiver) { + ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: can't bind to transceiver\n", + hsudc->gadget.name); + driver->unbind(&hsudc->gadget); + + device_del(&hsudc->gadget.dev); + + hsudc->driver = NULL; + hsudc->gadget.dev.driver = NULL; + return ret; + } + } + enable_irq(hsudc->irq); dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); @@ -1201,6 +1219,9 @@ static int s3c_hsudc_stop(struct usb_gadget_driver *driver) s3c_hsudc_stop_activity(hsudc, driver); spin_unlock_irqrestore(&hsudc->lock, flags); + if (hsudc->transceiver) + (void) otg_set_peripheral(hsudc->transceiver, NULL); + driver->unbind(&hsudc->gadget); device_del(&hsudc->gadget.dev); disable_irq(hsudc->irq); @@ -1220,10 +1241,24 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) return s3c_hsudc_read_frameno(to_hsudc(gadget)); } +static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct s3c_hsudc *hsudc = the_controller; + + if (!hsudc) + return -ENODEV; + + if (hsudc->transceiver) + return otg_set_power(hsudc->transceiver, mA); + + return -EOPNOTSUPP; +} + static struct usb_gadget_ops s3c_hsudc_gadget_ops = { .get_frame = s3c_hsudc_gadget_getframe, .start = s3c_hsudc_start, .stop = s3c_hsudc_stop, + .vbus_draw = s3c_hsudc_vbus_draw, }; static int s3c_hsudc_probe(struct platform_device *pdev) @@ -1247,6 +1282,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; + hsudc->transceiver = otg_get_transceiver(); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "unable to obtain driver resource data\n"); @@ -1269,19 +1306,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_remap; } - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "unable to obtain IRQ number\n"); - goto err_irq; - } - hsudc->irq = ret; - - ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); - if (ret < 0) { - dev_err(dev, "irq request failed\n"); - goto err_irq; - } - spin_lock_init(&hsudc->lock); device_initialize(&hsudc->gadget.dev); @@ -1299,6 +1323,19 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_irq; + } + hsudc->irq = ret; + + ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_irq; + } + hsudc->uclk = clk_get(&pdev->dev, "usb-device"); if (IS_ERR(hsudc->uclk)) { dev_err(dev, "failed to find usb-device clock source\n"); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 8d31848aab09..b8643771fa80 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -10,16 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/module.h> @@ -1082,7 +1072,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; local_irq_save (flags); _ep->maxpacket = max & 0x7ff; @@ -1903,7 +1893,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + 0, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1927,7 +1917,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } retval = request_irq(irq, s3c2410_udc_vbus_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h index 9e0bece4f241..a48f619cb1cc 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -9,16 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _S3C2410_UDC_H diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index d3dd227a2bfc..c7f291a331df 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -52,6 +43,12 @@ * characters rather then a pointer to void. */ +/* + * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers + * sets the number of pipeline buffers (length of the fsg_buffhd array). + * The valid range of num_buffers is: num >= 2 && num <= 4. + */ + #include <linux/usb/storage.h> #include <scsi/scsi.h> @@ -247,6 +244,8 @@ struct fsg_lun { u32 sense_data_info; u32 unit_attention_data; + unsigned int blkbits; /* Bits of logical block size of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ struct device dev; }; @@ -262,8 +261,31 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define FSG_NUM_BUFFERS 2 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; +module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); +MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_DEBUG */ + +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(void) +{ + if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) + return 0; + pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", + fsg_num_buffers, 2 ,4); + return -EINVAL; +} /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) @@ -493,12 +515,128 @@ static struct usb_descriptor_header *fsg_hs_function[] = { NULL, }; +static struct usb_endpoint_descriptor +fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +static struct usb_endpoint_descriptor +fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_ss_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .wBytesPerInterval = cpu_to_le16(2), +}; + +#ifndef FSG_NO_OTG +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = { + .bLength = USB_DT_USB_EXT_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_CAP_TYPE_EXT, + + .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT), +}; + +static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { + .bLength = USB_DT_USB_SS_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_SS_CAP_TYPE, + + /* .bmAttributes = LTM is not supported yet */ + + .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION + | USB_FULL_SPEED_OPERATION + | USB_HIGH_SPEED_OPERATION + | USB_5GBPS_OPERATION), + .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, + .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, + .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT, +}; + +static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + + .wTotalLength = USB_DT_BOS_SIZE + + USB_DT_USB_EXT_CAP_SIZE + + USB_DT_USB_SS_CAP_SIZE, + + .bNumDeviceCaps = 2, +}; + +static struct usb_descriptor_header *fsg_ss_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_ss_intr_in_desc, + (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc, +#endif + NULL, +}; + /* Maxpacket and other transfer characteristics vary by speed. */ static __maybe_unused struct usb_endpoint_descriptor * fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return ss; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return hs; return fs; } @@ -580,13 +718,24 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; /* File size in 512-byte blocks */ + + if (curlun->cdrom) { + curlun->blksize = 2048; + curlun->blkbits = 11; + } else if (inode->i_bdev) { + curlun->blksize = bdev_logical_block_size(inode->i_bdev); + curlun->blkbits = blksize_bits(curlun->blksize); + } else { + curlun->blksize = 512; + curlun->blkbits = 9; + } + + num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */ min_sectors = 1; if (curlun->cdrom) { - num_sectors &= ~3; /* Reduce to a multiple of 2048 */ - min_sectors = 300*4; /* Smallest track is 300 frames */ - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; + min_sectors = 300; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75) { + num_sectors = 256*60*75 - 1; LINFO(curlun, "file too big: %s\n", filename); LINFO(curlun, "using only first %d blocks\n", (int) num_sectors); diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index dfed4c1d96c0..29c854bbca44 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index c966440ddd70..8012357e98aa 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __U_ETHER_H diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index a8aa46962d81..3a4a664bab44 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -552,9 +552,8 @@ recycle: /* Push from tty to ldisc; without low_latency set this is handled by * a workqueue, so we won't get callbacks and can hold port_lock */ - if (tty && do_push) { + if (tty && do_push) tty_flip_buffer_push(tty); - } /* We want our data queue to become empty ASAP, keeping data diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 05ba47214361..022baeca7c94 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -356,7 +356,7 @@ static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); static ssize_t usb_udc_softconn_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { - struct usb_udc *udc = dev_get_drvdata(dev); + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); if (sysfs_streq(buf, "connect")) { usb_gadget_connect(udc->gadget); @@ -375,23 +375,8 @@ static ssize_t usb_udc_speed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget *gadget = udc->gadget; - - switch (gadget->speed) { - case USB_SPEED_LOW: - return snprintf(buf, PAGE_SIZE, "low-speed\n"); - case USB_SPEED_FULL: - return snprintf(buf, PAGE_SIZE, "full-speed\n"); - case USB_SPEED_HIGH: - return snprintf(buf, PAGE_SIZE, "high-speed\n"); - case USB_SPEED_WIRELESS: - return snprintf(buf, PAGE_SIZE, "wireless\n"); - case USB_SPEED_SUPER: - return snprintf(buf, PAGE_SIZE, "super-speed\n"); - case USB_SPEED_UNKNOWN: /* FALLTHROUGH */ - default: - return snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); - } + return snprintf(buf, PAGE_SIZE, "%s\n", + usb_speed_string(udc->gadget->speed)); } static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL); diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 5b7919460fd2..bc78c606c12b 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -8,7 +8,6 @@ * 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 _UVC_GADGET_H_ @@ -56,6 +55,7 @@ struct uvc_event #include <linux/usb.h> /* For usb_endpoint_* */ #include <linux/usb/gadget.h> #include <linux/videodev2.h> +#include <linux/version.h> #include <media/v4l2-fh.h> #include "uvc_queue.h" diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index aa0ad34e0f1f..d776adb2da67 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -8,7 +8,6 @@ * 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. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 52f8f9e513af..f6e083b50191 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -8,7 +8,6 @@ * 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. - * */ #include <linux/kernel.h> @@ -16,7 +15,6 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/mutex.h> -#include <linux/version.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index b08f35438d70..b0e53a8ea4f7 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -8,7 +8,6 @@ * 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. - * */ #include <linux/kernel.h> diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index df6882de50bf..668fe128f2ef 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -8,8 +8,8 @@ * 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. - * */ + #include <linux/kernel.h> #include <linux/device.h> #include <linux/usb/video.h> diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 00e2fd2d4791..20697cc132d1 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f12d570..060e0e2b1ae6 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -19,7 +19,7 @@ config USB_C67X00_HCD config USB_XHCI_HCD tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)" - depends on USB && PCI && EXPERIMENTAL + depends on USB && USB_ARCH_HAS_XHCI && EXPERIMENTAL ---help--- The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0 "SuperSpeed" host controller hardware. @@ -515,6 +515,19 @@ config USB_R8A66597_HCD To compile this driver as a module, choose M here: the module will be called r8a66597-hcd. +config USB_RENESAS_USBHS_HCD + tristate "Renesas USBHS HCD support" + depends on USB + depends on USB_RENESAS_USBHS + help + The Renesas USBHS is a USB 2.0 host and peripheral controller. + + Enable this option if your board has this chip, and you want + to use it as a host controller. If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called renesas-usbhs. + config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -544,11 +557,11 @@ config USB_HWA_HCD will be called "hwa-hc". config USB_IMX21_HCD - tristate "iMX21 HCD support" - depends on USB && ARM && MACH_MX21 + tristate "i.MX21 HCD support" + depends on USB && ARM && ARCH_MXC help This driver enables support for the on-chip USB host in the - iMX21 processor. + i.MX21 processor. To compile this driver as a module, choose M here: the module will be called "imx21-hcd". @@ -578,3 +591,10 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +config USB_PXA168_EHCI + bool "Marvell PXA168 on-chip EHCI HCD support" + depends on USB_EHCI_HCD && ARCH_MMP + help + Enable support for Marvell PXA168 SoC's on-chip EHCI + host controller diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 624a362f2fee..ed48a5d79e16 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -11,8 +11,9 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o -xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o +xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o +xhci-hcd-$(CONFIG_PCI) += xhci-pci.o obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 4d2e88d04dab..afb6743cf094 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -163,7 +163,7 @@ static int ehci_ath79_probe(struct platform_device *pdev) goto err_release_region; } - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_iounmap; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 42ae57409908..65719e8d24e4 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -181,7 +181,7 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ehci->hcs_params = readl(&ehci->caps->hcs_params); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; @@ -293,7 +293,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 40a844c1dbb4..d6d74d2e09f4 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -697,6 +697,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) } #undef DBG_SCHED_LIMIT +static const char *rh_state_string(struct ehci_hcd *ehci) +{ + switch (ehci->rh_state) { + case EHCI_RH_HALTED: + return "halted"; + case EHCI_RH_SUSPENDED: + return "suspended"; + case EHCI_RH_RUNNING: + return "running"; + } + return "?"; +} + static ssize_t fill_registers_buffer(struct debug_buffer *buf) { struct usb_hcd *hcd; @@ -730,11 +743,11 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) temp = scnprintf (next, size, "bus %s, device %s\n" "%s\n" - "EHCI %x.%02x, hcd state %d\n", + "EHCI %x.%02x, rh state %s\n", hcd->self.controller->bus->name, dev_name(hcd->self.controller), hcd->product_desc, - i >> 8, i & 0x0ff, hcd->state); + i >> 8, i & 0x0ff, rh_state_string(ehci)); size -= temp; next += temp; @@ -808,7 +821,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; temp = scnprintf (next, size, "uframe %04x\n", - ehci_readl(ehci, &ehci->regs->frame_index)); + ehci_read_frame_index(ehci)); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 34a3140d1e5f..e90344a17631 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -134,7 +134,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, /* Don't need to set host mode here. It will be done by tdi_reset() */ - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; @@ -392,7 +392,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) dev_dbg(dev, "suspending...\n"); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; dev->power.power_state = PMSG_SUSPEND; /* ignore non-host interrupts */ @@ -481,7 +481,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev) ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; dev->power.power_state = PMSG_ON; tmp = ehci_readl(ehci, &ehci->regs->command); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f72ae0b6ee7f..59e81615e09c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -95,7 +95,7 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) - /* 200-ms async qh unlink delay */ + /* 5-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -238,7 +238,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, error = handshake(ehci, ptr, mask, done, usec); if (error) { ehci_halt(ehci); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", ptr, mask, done, error); } @@ -278,7 +278,7 @@ static int ehci_reset (struct ehci_hcd *ehci) command |= CMD_RESET; dbg_cmd (ehci, "reset", command); ehci_writel(ehci, command, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci->next_statechange = jiffies; retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); @@ -307,7 +307,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) u32 temp; #ifdef DEBUG - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) BUG (); #endif @@ -356,7 +356,7 @@ static void ehci_iaa_watchdog(unsigned long param) */ if (ehci->reclaim && !timer_pending(&ehci->iaa_watchdog) - && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + && ehci->rh_state == EHCI_RH_RUNNING) { u32 cmd, status; /* If we get here, IAA is *REALLY* late. It's barely @@ -496,7 +496,7 @@ static void ehci_work (struct ehci_hcd *ehci) * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && + if (ehci->rh_state == EHCI_RH_RUNNING && (ehci->async->qh_next.ptr != NULL || ehci->periodic_sched != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); @@ -516,7 +516,7 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); - if (HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); ehci_silence_controller(ehci); @@ -741,7 +741,7 @@ static int ehci_run (struct usb_hcd *hcd) * be started before the port switching actions could complete. */ down_write(&ehci_cf_port_reset_rwsem); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); @@ -768,6 +768,35 @@ static int ehci_run (struct usb_hcd *hcd) return 0; } +static int __maybe_unused ehci_setup (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci->regs = (void __iomem *)ehci->caps + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + ehci->sbrn = HCD_USB2; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + + return 0; +} + /*-------------------------------------------------------------------------*/ static irqreturn_t ehci_irq (struct usb_hcd *hcd) @@ -788,7 +817,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { + if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -952,7 +981,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) + if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1079,7 +1108,7 @@ rescan: goto idle_timeout; } - if (!HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: @@ -1166,8 +1195,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) static int ehci_get_frame (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) % - ehci->periodic_size; + return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size; } /*-------------------------------------------------------------------------*/ @@ -1291,6 +1319,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_grlib_driver #endif +#ifdef CONFIG_USB_PXA168_EHCI +#include "ehci-pxa168.c" +#define PLATFORM_DRIVER ehci_pxa168_driver +#endif + +#ifdef CONFIG_NLM_XLR +#include "ehci-xls.c" +#define PLATFORM_DRIVER ehci_xls_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4c32cb19b405..77bbb2357e47 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -236,10 +236,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* stop schedules, clean any completed work */ - if (HC_IS_RUNNING(hcd->state)) { + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); - hcd->state = HC_STATE_QUIESCING; - } ehci->command = ehci_readl(ehci, &ehci->regs->command); ehci_work(ehci); @@ -313,7 +311,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; if (ehci->reclaim) end_unlink_async(ehci); @@ -382,6 +380,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci->rh_state = EHCI_RH_RUNNING; /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ @@ -451,7 +450,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } ehci->next_statechange = jiffies + msecs_to_jiffies(5); - hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); @@ -563,7 +561,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) u32 ppcd = 0; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (!HC_IS_RUNNING(hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) return 0; /* init status to no-changes */ diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 555a73c864b5..55978fcfa4b2 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -236,7 +236,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) priv->hcd = hcd; platform_set_drvdata(pdev, priv); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_add; diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index c3ba3ed5f3a6..ba1f51361134 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -155,7 +155,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 45240321ca09..e39b0297bad1 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -228,7 +228,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 395bdb0248d5..a68a2a5c4b83 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -277,7 +277,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n"); } - err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err4; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 1102ce65a3a9..f4b627d343ac 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_dev_put(p_smbus); } break; + case PCI_VENDOR_ID_NETMOS: + /* MosChip frame-index-register bug */ + ehci_info(ehci, "applying MosChip frame-index workaround\n"); + ehci->frame_index_bug = 1; + break; } /* optional debug port, normally in the first BAR */ @@ -439,7 +444,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } #endif diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 64626a777d61..2dc32da75cfc 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -167,7 +167,7 @@ static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ehci-pxa168.c b/drivers/usb/host/ehci-pxa168.c new file mode 100644 index 000000000000..ac0c16e8f539 --- /dev/null +++ b/drivers/usb/host/ehci-pxa168.c @@ -0,0 +1,363 @@ +/* + * drivers/usb/host/ehci-pxa168.c + * + * Tanmay Upadhyay <tanmay.upadhyay@einfochips.com> + * + * Based on drivers/usb/host/ehci-orion.c + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <mach/pxa168.h> + +#define USB_PHY_CTRL_REG 0x4 +#define USB_PHY_PLL_REG 0x8 +#define USB_PHY_TX_REG 0xc + +#define FBDIV_SHIFT 4 + +#define ICP_SHIFT 12 +#define ICP_15 2 +#define ICP_20 3 +#define ICP_25 4 + +#define KVCO_SHIFT 15 + +#define PLLCALI12_SHIFT 25 +#define CALI12_VDD 0 +#define CALI12_09 1 +#define CALI12_10 2 +#define CALI12_11 3 + +#define PLLVDD12_SHIFT 27 +#define VDD12_VDD 0 +#define VDD12_10 1 +#define VDD12_11 2 +#define VDD12_12 3 + +#define PLLVDD18_SHIFT 29 +#define VDD18_19 0 +#define VDD18_20 1 +#define VDD18_21 2 +#define VDD18_22 3 + + +#define PLL_READY (1 << 23) +#define VCOCAL_START (1 << 21) +#define REG_RCAL_START (1 << 12) + +struct pxa168_usb_drv_data { + struct ehci_hcd ehci; + struct clk *pxa168_usb_clk; + struct resource *usb_phy_res; + void __iomem *usb_phy_reg_base; +}; + +static int ehci_pxa168_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci_reset(ehci); + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_pxa168_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell PXA168 EHCI", + .hcd_priv_size = sizeof(struct pxa168_usb_drv_data), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_pxa168_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int pxa168_usb_phy_init(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *usb_phy_reg_base; + struct pxa168_usb_pdata *pdata; + struct pxa168_usb_drv_data *drv_data; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + unsigned long reg_val; + int pll_retry_cont = 10000, err = 0; + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + pdata = (struct pxa168_usb_pdata *)pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no PHY register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + return -EBUSY; + } + + usb_phy_reg_base = ioremap(res->start, resource_size(res)); + if (usb_phy_reg_base == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err1; + } + drv_data->usb_phy_reg_base = usb_phy_reg_base; + drv_data->usb_phy_res = res; + + /* If someone wants to init USB phy in board specific way */ + if (pdata && pdata->phy_init) + return pdata->phy_init(usb_phy_reg_base); + + /* Power up the PHY and PLL */ + writel(readl(usb_phy_reg_base + USB_PHY_CTRL_REG) | 0x3, + usb_phy_reg_base + USB_PHY_CTRL_REG); + + /* Configure PHY PLL */ + reg_val = readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~(0x7e03ffff); + reg_val |= (VDD18_22 << PLLVDD18_SHIFT | VDD12_12 << PLLVDD12_SHIFT | + CALI12_11 << PLLCALI12_SHIFT | 3 << KVCO_SHIFT | + ICP_15 << ICP_SHIFT | 0xee << FBDIV_SHIFT | 0xb); + writel(reg_val, usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Make sure PHY PLL is ready */ + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + /* Toggle VCOCAL_START bit of U2PLL for PLL calibration */ + udelay(200); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) | VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Toggle REG_RCAL_START bit of U2PTX for impedance calibration */ + udelay(400); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) | REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) & ~REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + + /* Make sure PHY PLL is ready again */ + pll_retry_cont = 0; + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + return 0; +err2: + iounmap(usb_phy_reg_base); +err1: + release_mem_region(res->start, resource_size(res)); + return err; +} + +static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct pxa168_usb_drv_data *drv_data; + void __iomem *regs; + int irq, err = 0; + + if (usb_disabled()) + return -ENODEV; + + pr_debug("Initializing pxa168-SoC USB Host Controller\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + err = -EBUSY; + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err2; + } + + hcd = usb_create_hcd(&ehci_pxa168_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + err = -ENOMEM; + goto err3; + } + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + /* Enable USB clock */ + drv_data->pxa168_usb_clk = clk_get(&pdev->dev, "PXA168-USBCLK"); + if (IS_ERR(drv_data->pxa168_usb_clk)) { + dev_err(&pdev->dev, "Couldn't get USB clock\n"); + err = PTR_ERR(drv_data->pxa168_usb_clk); + goto err4; + } + clk_enable(drv_data->pxa168_usb_clk); + + err = pxa168_usb_phy_init(pdev); + if (err) { + dev_err(&pdev->dev, "USB PHY initialization failed\n"); + goto err5; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = regs; + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + hcd->has_tt = 1; + ehci->sbrn = 0x20; + + err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (err) + goto err5; + + return 0; + +err5: + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); +err4: + usb_put_hcd(hcd); +err3: + iounmap(regs); +err2: + release_mem_region(res->start, resource_size(res)); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", + dev_name(&pdev->dev), err); + + return err; +} + +static int __exit ehci_pxa168_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct pxa168_usb_drv_data *drv_data = + (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + usb_remove_hcd(hcd); + + /* Power down PHY & PLL */ + writel(readl(drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG) & (~0x3), + drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG); + + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + iounmap(drv_data->usb_phy_reg_base); + release_mem_region(drv_data->usb_phy_res->start, + resource_size(drv_data->usb_phy_res)); + + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("platform:pxa168-ehci"); + +static struct platform_driver ehci_pxa168_driver = { + .probe = ehci_pxa168_drv_probe, + .remove = __exit_p(ehci_pxa168_drv_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver.name = "pxa168-ehci", +}; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 0917e3a32465..4e4066c35a09 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -111,8 +111,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) } } - /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ - wmb (); hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); } @@ -153,7 +151,7 @@ static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, spin_lock_irqsave(&ehci->lock, flags); qh->clearing_tt = 0; if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) - && HC_IS_RUNNING(hcd->state)) + && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async(ehci, qh); spin_unlock_irqrestore(&ehci->lock, flags); } @@ -425,7 +423,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { + && ehci->rh_state == EHCI_RH_RUNNING)) { break; /* scan the whole queue for unlinks whenever it stops */ @@ -433,7 +431,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) last_status = -ESHUTDOWN; /* this qtd is active; skip it unless a previous qtd @@ -724,7 +722,8 @@ qh_urb_transaction ( /* * control requests may need a terminating data "status" ack; - * bulk ones may need a terminating short packet (zero length). + * other OUT ones may need a terminating short packet + * (zero length). */ if (likely (urb->transfer_buffer_length != 0)) { int one_more = 0; @@ -733,7 +732,7 @@ qh_urb_transaction ( one_more = 1; token ^= 0x0100; /* "in" <--> "out" */ token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipebulk (urb->pipe) + } else if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && !(urb->transfer_buffer_length % maxpacket)) { one_more = 1; @@ -977,9 +976,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* in case a clear of CMD_ASE didn't take yet */ (void)handshake(ehci, &ehci->regs->status, STS_ASS, 0, 150); - cmd |= CMD_ASE | CMD_RUN; + cmd |= CMD_ASE; ehci_writel(ehci, cmd, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* posted write need not be known to HC yet ... */ } } @@ -1058,7 +1056,7 @@ static struct ehci_qh *qh_append_tds ( */ token = qtd->hw_token; qtd->hw_token = HALT_BIT(ehci); - wmb (); + dummy = qh->dummy; dma = dummy->qtd_dma; @@ -1168,14 +1166,13 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh_completions (ehci, qh); - if (!list_empty (&qh->qtd_list) - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { qh_link_async (ehci, qh); - else { + } else { /* it's not free to turn the async schedule on/off; leave it * active but idle for a while once it empties. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state == EHCI_RH_RUNNING && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } @@ -1211,7 +1208,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop async schedule right now? */ if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ - if (ehci_to_hcd(ehci)->state != HC_STATE_HALT + if (ehci->rh_state != EHCI_RH_HALTED && !ehci->reclaim) { /* ... and CMD_IAAD clear */ ehci_writel(ehci, cmd & ~CMD_ASE, @@ -1237,7 +1234,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) wmb (); /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) { + if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { /* if (unlikely (qh->reclaim != 0)) * this will recurse, probably not much */ @@ -1260,7 +1257,7 @@ static void scan_async (struct ehci_hcd *ehci) enum ehci_timer_action action = TIMER_IO_WATCHDOG; timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); + stopped = (ehci->rh_state != EHCI_RH_RUNNING); ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 9e77f1c8bdbd..024b65c4990d 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -136,7 +136,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; @@ -270,7 +270,7 @@ static int s5p_ehci_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 2abf8543f083..2e829fae6482 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -36,6 +36,27 @@ static int ehci_get_frame (struct usb_hcd *hcd); +#ifdef CONFIG_PCI + +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + unsigned uf; + + /* + * The MosChip MCS9990 controller updates its microframe counter + * a little before the frame counter, and occasionally we will read + * the invalid intermediate value. Avoid problems by checking the + * microframe number (the low-order 3 bits); if they are 0 then + * re-read the register to get the correct value. + */ + uf = ehci_readl(ehci, &ehci->regs->frame_index); + if (unlikely(ehci->frame_index_bug && ((uf & 7) == 0))) + uf = ehci_readl(ehci, &ehci->regs->frame_index); + return uf; +} + +#endif + /*-------------------------------------------------------------------------*/ /* @@ -479,10 +500,9 @@ static int enable_periodic (struct ehci_hcd *ehci) cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); /* posted write ... PSS happens later */ - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) + ehci->next_uframe = ehci_read_frame_index(ehci) % (ehci->periodic_size << 3); if (unlikely(ehci->broken_periodic)) ehci->last_periodic_enable = ktime_get_real(); @@ -677,7 +697,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) /* reschedule QH iff another request is queued */ if (!list_empty(&qh->qtd_list) && - HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + ehci->rh_state == EHCI_RH_RUNNING) { rc = qh_schedule(ehci, qh); /* An error here likely indicates handshake failure @@ -1409,7 +1429,7 @@ iso_stream_schedule ( goto fail; } - now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind @@ -2275,8 +2295,8 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { - clock = ehci_readl(ehci, &ehci->regs->frame_index); + if (ehci->rh_state == EHCI_RH_RUNNING) { + clock = ehci_read_frame_index(ehci); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { clock = now_uframe + mod - 1; @@ -2310,7 +2330,7 @@ restart: union ehci_shadow temp; int live; - live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state); + live = (ehci->rh_state == EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ @@ -2435,7 +2455,7 @@ restart: * We can't advance our scan without collecting the ISO * transfers that are still pending in this frame. */ - if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) { ehci->next_uframe = now_uframe; break; } @@ -2451,12 +2471,11 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state != EHCI_RH_RUNNING || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; - now = ehci_readl(ehci, &ehci->regs->frame_index) & - (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); if (now_uframe == now) break; diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 86a95bb80a61..9d9cf47d80da 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -168,7 +168,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) clk_enable(priv->fclk); clk_enable(priv->iclk); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd"); goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index dbf1e4ef3c17..b115b0b76e33 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -154,7 +154,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) ehci->clk = usbh_clk; spear_start_ehci(ehci); - retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 02b2bfd49a10..db9d1b4bfbdc 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -674,7 +674,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) } #endif - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 47d749631bc7..54d1ab8aec49 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -133,7 +133,7 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci_port_power(ehci, 1); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c new file mode 100644 index 000000000000..fe74bd676018 --- /dev/null +++ b/drivers/usb/host/ehci-xls.c @@ -0,0 +1,161 @@ +/* + * EHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on various ehci-*.c drivers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/platform_device.h> + +static int ehci_xls_setup(struct usb_hcd *hcd) +{ + int retval; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + + return retval; +} + +int ehci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Error: MMIO Handle %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), + retval); + return retval; +} + +static struct hc_driver ehci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + .reset = ehci_xls_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .get_frame_number = ehci_get_frame, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_xls_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return ehci_xls_probe_internal(&ehci_xls_hc_driver, pdev); +} + +static int ehci_xls_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +MODULE_ALIAS("ehci-xls"); + +static struct platform_driver ehci_xls_driver = { + .probe = ehci_xls_probe, + .remove = ehci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ehci-xls", + }, +}; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc7d337ec355..0a5fda73b3f2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -62,6 +62,12 @@ struct ehci_stats { #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ +enum ehci_rh_state { + EHCI_RH_HALTED, + EHCI_RH_SUSPENDED, + EHCI_RH_RUNNING +}; + struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -70,6 +76,7 @@ struct ehci_hcd { /* one per controller */ __u32 hcs_params; /* cached register copy */ spinlock_t lock; + enum ehci_rh_state rh_state; /* async schedule support */ struct ehci_qh *async; @@ -139,6 +146,7 @@ struct ehci_hcd { /* one per controller */ unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ + unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -740,6 +748,22 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PCI + +/* For working around the MosChip frame-index-register bug */ +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci); + +#else + +static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + return ehci_readl(ehci, &ehci->regs->frame_index); +} + +#endif + +/*-------------------------------------------------------------------------*/ + #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 572ea53b0226..4ed6d19f2a54 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -621,12 +621,15 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) goto err_pram; } - pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE); + pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64); if (IS_ERR_VALUE(pram_addr)) { dev_err(dev, "failed to allocate usb pram\n"); ret = -ENOMEM; goto err_pram; } + + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB, + QE_CR_PROTOCOL_UNSPECIFIED, pram_addr); fhci->pram = cpm_muram_addr(pram_addr); /* GPIOs and pins */ @@ -686,7 +689,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) } ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq, - IRQF_DISABLED, "qe timer (usb)", hcd); + 0, "qe timer (usb)", hcd); if (ret) { dev_err(dev, "failed to request timer irq"); goto err_timer_irq; @@ -745,7 +748,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) out_be16(&fhci->regs->usb_event, 0xffff); out_be16(&fhci->regs->usb_mask, 0); - ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, usb_irq, 0); if (ret < 0) goto err_add_hcd; diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a42ef380e917..2df851b4bc7c 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -1,7 +1,7 @@ /* * Freescale QUICC Engine USB Host Controller Driver * - * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Copyright (c) Freescale Semicondutor, Inc. 2006, 2011. * Shlomi Gridish <gridish@freescale.com> * Jerry Huang <Chang-Ming.Huang@freescale.com> * Copyright (c) Logic Product Development, Inc. 2007 @@ -810,9 +810,11 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) ed->dev_addr = usb_pipedevice(urb->pipe); ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + /* setup stage */ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); + /* data stage */ if (data_len > 0) { td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, usb_pipeout(urb->pipe) ? FHCI_TA_OUT : @@ -820,9 +822,18 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) USB_TD_TOGGLE_DATA1, data, data_len, 0, 0, true); } - td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, - usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT, - USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + + /* status stage */ + if (data_len > 0) + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + (usb_pipeout(urb->pipe) ? FHCI_TA_IN : + FHCI_TA_OUT), + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + else + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + FHCI_TA_IN, + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + urb_state = US_CTRL_SETUP; break; case FHCI_TF_ISO: diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index af05718bdc73..2ee18cfa1efe 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1891,7 +1891,7 @@ static int imx21_probe(struct platform_device *pdev) dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n", (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret != 0) { dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); goto failed_add_hcd; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index baae4ccd16ac..d91e5f211a76 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1639,7 +1639,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev) goto err6; } - ret = usb_add_hcd(hcd, irq, irqflags | IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err6; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 9c37dad3e816..e5fd8aa57af1 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2358,7 +2358,7 @@ static int isp1362_hc_reset(struct usb_hcd *hcd) unsigned long flags; int clkrdy = 0; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); if (isp1362_hcd->board && isp1362_hcd->board->reset) { isp1362_hcd->board->reset(hcd->self.controller, 1); @@ -2395,7 +2395,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd) unsigned long flags; u32 tmp; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); del_timer_sync(&hcd->rh_timer); @@ -2523,7 +2523,7 @@ static int isp1362_hc_start(struct usb_hcd *hcd) u16 chipid; unsigned long flags; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); spin_lock_irqsave(&isp1362_hcd->lock, flags); chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); @@ -2773,7 +2773,7 @@ static int __devinit isp1362_probe(struct platform_device *pdev) if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) irq_flags |= IRQF_TRIGGER_LOW; - retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); if (retval != 0) goto err6; pr_info("%s, irq %d\n", hcd->product_desc, irq); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 840beda66dd9..27dfab80ed8f 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -21,8 +21,10 @@ #include <linux/uaccess.h> #include <linux/io.h> #include <linux/mm.h> +#include <linux/timer.h> #include <asm/unaligned.h> #include <asm/cacheflush.h> +#include <linux/gpio.h> #include "isp1760-hcd.h" @@ -39,7 +41,6 @@ struct isp1760_hcd { int int_done_map; struct memory_chunk memory_pool[BLOCKS]; struct list_head controlqhs, bulkqhs, interruptqhs; - int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -48,6 +49,8 @@ struct isp1760_hcd { unsigned long reset_done; unsigned long next_statechange; unsigned int devflags; + + int rst_gpio; }; static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) @@ -114,6 +117,7 @@ struct isp1760_qh { u32 toggle; u32 ping; int slot; + int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */ }; struct urb_listitem { @@ -432,6 +436,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) int result; u32 scratch, hwmode; + /* low-level chip reset */ + if (gpio_is_valid(priv->rst_gpio)) { + unsigned int rst_lvl; + + rst_lvl = (priv->devflags & + ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0; + + gpio_set_value(priv->rst_gpio, rst_lvl); + mdelay(50); + gpio_set_value(priv->rst_gpio, !rst_lvl); + } + /* Setup HW Mode Control: This assumes a level active-low interrupt */ hwmode = HW_DATA_BUS_32BIT; @@ -489,10 +505,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); - /* This is weird: at the first plug-in of a device there seems to be - one packet queued that never gets returned? */ - priv->active_ptds = -1; - /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); @@ -514,83 +526,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) return priv_init(hcd); } -static void isp1760_init_maps(struct usb_hcd *hcd) -{ - /*set last maps, for iso its only 1, else 32 tds bitmap*/ - reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); - - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); - - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - ATL_BUF_FILL | INT_BUF_FILL); -} - -static void isp1760_enable_interrupts(struct usb_hcd *hcd) -{ - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); - /* step 23 passed */ -} - -static int isp1760_run(struct usb_hcd *hcd) -{ - int retval; - u32 temp; - u32 command; - u32 chipid; - - hcd->uses_new_polling = 1; - - hcd->state = HC_STATE_RUNNING; - isp1760_enable_interrupts(hcd); - temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); - reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); - - command = reg_read32(hcd->regs, HC_USBCMD); - command &= ~(CMD_LRESET|CMD_RESET); - command |= CMD_RUN; - reg_write32(hcd->regs, HC_USBCMD, command); - - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); - if (retval) - return retval; - - /* - * XXX - * Spec says to write FLAG_CF as last config action, priv code grabs - * the semaphore while doing so. - */ - down_write(&ehci_cf_port_reset_rwsem); - reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); - - retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); - up_write(&ehci_cf_port_reset_rwsem); - if (retval) - return retval; - - chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); - dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", - chipid & 0xffff, chipid >> 16); - - /* PTD Register Init Part 2, Step 28 */ - /* enable INTs */ - isp1760_init_maps(hcd); - - /* GRR this is run-once init(), being done every time the HC starts. - * So long as they're part of class devices, we can't do it init() - * since the class device isn't created that early. - */ - return 0; -} - static u32 base_to_chip(u32 base) { return ((base - 0x400) >> 3); @@ -813,28 +748,29 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, WARN_ON(slots[slot].qh); WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); - slots[slot].qtd = qtd; - slots[slot].qh = qh; - qh->slot = slot; - qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since - interrupt routine may preempt and expects this value. */ - ptd_write(hcd->regs, ptd_offset, slot, ptd); - priv->active_ptds++; - /* Make sure done map has not triggered from some unlinked transfer */ if (ptd_offset == ATL_PTD_OFFSET) { priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->atl_done_map &= ~(1 << qh->slot); + priv->atl_done_map &= ~(1 << slot); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << slot); + } + + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; + slots[slot].timestamp = jiffies; + slots[slot].qtd = qtd; + slots[slot].qh = qh; + ptd_write(hcd->regs, ptd_offset, slot, ptd); + if (ptd_offset == ATL_PTD_OFFSET) { skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); } else { - priv->int_done_map |= reg_read32(hcd->regs, - HC_INT_PTD_DONEMAP_REG); - priv->int_done_map &= ~(1 << qh->slot); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); @@ -858,10 +794,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, if (qtd->status < QTD_XFER_COMPLETE) break; - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - last_qtd = 1; - else - last_qtd = qtd->urb != qtd_next->urb; + last_qtd = last_qtd_of_urb(qtd, qh); if ((!last_qtd) && (qtd->status == QTD_RETIRE)) qtd_next->status = QTD_RETIRE; @@ -902,7 +835,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, GFP_ATOMIC); if (unlikely(!urb_listitem)) - break; + break; /* Try again on next call */ urb_listitem->urb = qtd->urb; list_add_tail(&urb_listitem->urb_list, urb_list); } @@ -928,6 +861,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) return; } + /* Make sure this endpoint's TT buffer is clean before queueing ptds */ + if (qh->tt_buffer_dirty) + return; + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, qtd_list)->urb->pipe)) { ptd_offset = INT_PTD_OFFSET; @@ -1168,11 +1105,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, return PTD_STATE_QTD_DONE; } -static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +static void handle_done_ptds(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; - irqreturn_t irqret = IRQ_NONE; struct ptd ptd; struct isp1760_qh *qh; int slot; @@ -1181,27 +1116,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) u32 ptd_offset; struct isp1760_qtd *qtd; int modified; - static int last_active_ptds; - int int_skip_map, atl_skip_map; - - spin_lock(&priv->lock); - - if (!(hcd->state & HC_STATE_RUNNING)) - goto leave; - - imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); - if (unlikely(!imask)) - goto leave; - reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + int skip_map; - int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->int_done_map &= ~int_skip_map; - priv->atl_done_map &= ~atl_skip_map; + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + priv->int_done_map &= ~skip_map; + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + priv->atl_done_map &= ~skip_map; - modified = priv->int_done_map | priv->atl_done_map; + modified = priv->int_done_map || priv->atl_done_map; while (priv->int_done_map || priv->atl_done_map) { if (priv->int_done_map) { @@ -1240,7 +1162,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) slots[slot].qtd = NULL; qh = slots[slot].qh; slots[slot].qh = NULL; - priv->active_ptds--; qh->slot = -1; WARN_ON(qtd->status != QTD_XFER_STARTED); @@ -1281,6 +1202,15 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) case PTD_STATE_URB_RETIRE: qtd->status = QTD_RETIRE; + if ((qtd->urb->dev->speed != USB_SPEED_HIGH) && + (qtd->urb->status != -EPIPE) && + (qtd->urb->status != -EREMOTEIO)) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(qtd->urb)) + /* Clear failed; let's hope things work + anyway */ + qh->tt_buffer_dirty = 0; + } qtd = NULL; qh->toggle = 0; qh->ping = 0; @@ -1311,22 +1241,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) if (modified) schedule_ptds(hcd); +} - /* ISP1760 Errata 2 explains that interrupts may be missed (or not - happen?) if two USB devices are running simultaneously. Perhaps - this happens when a PTD is finished during interrupt handling; - enable SOF interrupts if PTDs are still scheduled when exiting this - interrupt handler, just to be safe. */ +static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 imask; + irqreturn_t irqret = IRQ_NONE; - if (priv->active_ptds != last_active_ptds) { - if (priv->active_ptds > 0) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - else - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_MASK); - last_active_ptds = priv->active_ptds; - } + spin_lock(&priv->lock); + + if (!(hcd->state & HC_STATE_RUNNING)) + goto leave; + + imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); + if (unlikely(!imask)) + goto leave; + reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + + priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + + handle_done_ptds(hcd); irqret = IRQ_HANDLED; leave: @@ -1335,6 +1271,138 @@ leave: return irqret; } +/* + * Workaround for problem described in chip errata 2: + * + * Sometimes interrupts are not generated when ATL (not INT?) completion occurs. + * One solution suggested in the errata is to use SOF interrupts _instead_of_ + * ATL done interrupts (the "instead of" might be important since it seems + * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget" + * to set the PTD's done bit in addition to not generating an interrupt!). + * + * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their + * done bit is not being set. This is bad - it blocks the endpoint until reboot. + * + * If we use SOF interrupts only, we get latency between ptd completion and the + * actual handling. This is very noticeable in testusb runs which takes several + * minutes longer without ATL interrupts. + * + * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it + * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the + * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered + * completed and its done map bit is set. + * + * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen + * not to cause too much lag when this HW bug occurs, while still hopefully + * ensuring that the check does not falsely trigger. + */ +#define SLOT_TIMEOUT 300 +#define SLOT_CHECK_PERIOD 200 +static struct timer_list errata2_timer; + +void errata2_function(unsigned long data) +{ + struct usb_hcd *hcd = (struct usb_hcd *) data; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int slot; + struct ptd ptd; + unsigned long spinflags; + + spin_lock_irqsave(&priv->lock, spinflags); + + for (slot = 0; slot < 32; slot++) + if (priv->atl_slots[slot].qh && time_after(jiffies, + priv->atl_slots[slot].timestamp + + SLOT_TIMEOUT * HZ / 1000)) { + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + if (!FROM_DW0_VALID(ptd.dw0) && + !FROM_DW3_ACTIVE(ptd.dw3)) + priv->atl_done_map |= 1 << slot; + } + + if (priv->atl_done_map) + handle_done_ptds(hcd); + + spin_unlock_irqrestore(&priv->lock, spinflags); + + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); +} + +static int isp1760_run(struct usb_hcd *hcd) +{ + int retval; + u32 temp; + u32 command; + u32 chipid; + + hcd->uses_new_polling = 1; + + hcd->state = HC_STATE_RUNNING; + + /* Set PTD interrupt AND & OR maps */ + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); + /* step 23 passed */ + + temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); + + command = reg_read32(hcd->regs, HC_USBCMD); + command &= ~(CMD_LRESET|CMD_RESET); + command |= CMD_RUN; + reg_write32(hcd->regs, HC_USBCMD, command); + + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); + if (retval) + return retval; + + /* + * XXX + * Spec says to write FLAG_CF as last config action, priv code grabs + * the semaphore while doing so. + */ + down_write(&ehci_cf_port_reset_rwsem); + reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); + + retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); + up_write(&ehci_cf_port_reset_rwsem); + if (retval) + return retval; + + init_timer(&errata2_timer); + errata2_timer.function = errata2_function; + errata2_timer.data = (unsigned long) hcd; + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); + + chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); + dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", + chipid & 0xffff, chipid >> 16); + + /* PTD Register Init Part 2, Step 28 */ + + /* Setup registers controlling PTD checking */ + reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + return 0; +} + static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) { qtd->data_buffer = databuffer; @@ -1503,7 +1571,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, packetize_urb(hcd, urb, &new_qtds, mem_flags); if (list_empty(&new_qtds)) return -ENOMEM; - urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */ retval = 0; spin_lock_irqsave(&priv->lock, spinflags); @@ -1531,6 +1598,7 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, qh = qh_alloc(GFP_ATOMIC); if (!qh) { retval = -ENOMEM; + usb_hcd_unlink_urb_from_ep(hcd, urb); goto out; } list_add_tail(&qh->qh_list, ep_queue); @@ -1570,7 +1638,41 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, } qh->slot = -1; - priv->active_ptds--; +} + +/* + * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing + * any active transfer belonging to the urb in the process. + */ +static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd) +{ + struct urb *urb; + int urb_was_running; + + urb = qtd->urb; + urb_was_running = 0; + list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb != urb) + break; + + if (qtd->status >= QTD_XFER_STARTED) + urb_was_running = 1; + if (last_qtd_of_urb(qtd, qh) && + (qtd->status >= QTD_XFER_COMPLETE)) + urb_was_running = 0; + + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, urb, qh); + qtd->status = QTD_RETIRE; + } + + if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(urb)) + /* Clear failed; let's hope things work anyway */ + qh->tt_buffer_dirty = 0; + } } static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, @@ -1595,9 +1697,8 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, list_for_each_entry(qtd, &qh->qtd_list, qtd_list) if (qtd->urb == urb) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, urb, qh); - qtd->status = QTD_RETIRE; + dequeue_urb_from_qtd(hcd, qh, qtd); + break; } urb->status = status; @@ -1622,12 +1723,11 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, if (!qh) goto out; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, qtd->urb, qh); - qtd->status = QTD_RETIRE; - qtd->urb->status = -ECONNRESET; - } + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) + if (qtd->status != QTD_RETIRE) { + dequeue_urb_from_qtd(hcd, qh, qtd); + qtd->urb->status = -ECONNRESET; + } ep->hcpriv = NULL; /* Cannot free qh here since it will be parsed by schedule_ptds() */ @@ -2021,6 +2121,8 @@ static void isp1760_stop(struct usb_hcd *hcd) struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 temp; + del_timer(&errata2_timer); + isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, NULL, 0); mdelay(20); @@ -2048,6 +2150,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_USBCMD, command); } +static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct isp1760_qh *qh = ep->hcpriv; + unsigned long spinflags; + + if (!qh) + return; + + spin_lock_irqsave(&priv->lock, spinflags); + qh->tt_buffer_dirty = 0; + schedule_ptds(hcd); + spin_unlock_irqrestore(&priv->lock, spinflags); +} + + static const struct hc_driver isp1760_hc_driver = { .description = "isp1760-hcd", .product_desc = "NXP ISP1760 USB Host Controller", @@ -2064,6 +2183,7 @@ static const struct hc_driver isp1760_hc_driver = { .get_frame_number = isp1760_get_frame, .hub_status_data = isp1760_hub_status_data, .hub_control = isp1760_hub_control, + .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete, }; int __init init_kmem_once(void) @@ -2102,6 +2222,7 @@ void deinit_kmem_cache(void) struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags) { @@ -2121,6 +2242,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, priv = hcd_to_priv(hcd); priv->devflags = devflags; + priv->rst_gpio = rst_gpio; init_memory(priv); hcd->regs = ioremap(res_start, res_len); if (!hcd->regs) { diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 014a7dfadf91..33dc79ccaa6b 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -4,6 +4,7 @@ /* exports for if */ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags); int init_kmem_once(void); @@ -73,7 +74,6 @@ void deinit_kmem_cache(void); #define HC_EOT_INT (1 << 3) #define HC_SOT_INT (1 << 1) #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) -#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) #define HC_ISO_IRQ_MASK_OR_REG 0x318 #define HC_INT_IRQ_MASK_OR_REG 0x31C @@ -107,6 +107,7 @@ struct ptd { struct slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; + unsigned long timestamp; }; @@ -126,6 +127,7 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ #define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ #define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ +#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */ /* chip memory management */ struct memory_chunk { @@ -188,6 +190,7 @@ struct memory_chunk { #define DW3_BABBLE_BIT (1 << 29) #define DW3_HALT_BIT (1 << 30) #define DW3_ACTIVE_BIT (1 << 31) +#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) #define INT_UNDERRUN (1 << 2) #define INT_BABBLE (1 << 1) diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 7ee30056f373..2c7fc830c9e4 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -17,19 +17,28 @@ #include "isp1760-hcd.h" -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF +#include <linux/slab.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> #endif #ifdef CONFIG_PCI #include <linux/pci.h> #endif -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF +struct isp1760 { + struct usb_hcd *hcd; + int rst_gpio; +}; + static int of_isp1760_probe(struct platform_device *dev) { - struct usb_hcd *hcd; + struct isp1760 *drvdata; struct device_node *dp = dev->dev.of_node; struct resource *res; struct resource memory; @@ -39,6 +48,11 @@ static int of_isp1760_probe(struct platform_device *dev) int ret; const unsigned int *prop; unsigned int devflags = 0; + enum of_gpio_flags gpio_flags; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; ret = of_address_to_resource(dp, 0, &memory); if (ret) @@ -78,32 +92,57 @@ static int of_isp1760_probe(struct platform_device *dev) if (of_get_property(dp, "dreq-polarity", NULL) != NULL) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), - devflags); - if (IS_ERR(hcd)) { - ret = PTR_ERR(hcd); - goto release_reg; + drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags); + if (gpio_is_valid(drvdata->rst_gpio)) { + ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev)); + if (!ret) { + if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) { + devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH; + gpio_direction_output(drvdata->rst_gpio, 0); + } else { + gpio_direction_output(drvdata->rst_gpio, 1); + } + } else { + drvdata->rst_gpio = ret; + } } - dev_set_drvdata(&dev->dev, hcd); + drvdata->hcd = isp1760_register(memory.start, res_len, virq, + IRQF_SHARED, drvdata->rst_gpio, + &dev->dev, dev_name(&dev->dev), + devflags); + if (IS_ERR(drvdata->hcd)) { + ret = PTR_ERR(drvdata->hcd); + goto free_gpio; + } + + dev_set_drvdata(&dev->dev, drvdata); return ret; +free_gpio: + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); release_reg: release_mem_region(memory.start, res_len); + kfree(drvdata); return ret; } static int of_isp1760_remove(struct platform_device *dev) { - struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); + struct isp1760 *drvdata = dev_get_drvdata(&dev->dev); dev_set_drvdata(&dev->dev, NULL); - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + usb_remove_hcd(drvdata->hcd); + iounmap(drvdata->hcd->regs); + release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len); + usb_put_hcd(drvdata->hcd); + + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); + + kfree(drvdata); return 0; } @@ -240,7 +279,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev), devflags); if (IS_ERR(hcd)) { ret_status = -ENODEV; @@ -313,7 +352,7 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) resource_size_t mem_size; struct isp1760_platform_data *priv = pdev->dev.platform_data; unsigned int devflags = 0; - unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; + unsigned long irqflags = IRQF_SHARED; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { @@ -351,7 +390,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) } hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); + irqflags, -ENOENT, + &pdev->dev, dev_name(&pdev->dev), devflags); if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); ret = -ENODEV; @@ -396,7 +436,7 @@ static int __init isp1760_init(void) ret = platform_driver_register(&isp1760_plat_driver); if (!ret) any_ret = 0; -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF ret = platform_driver_register(&isp1760_of_driver); if (!ret) any_ret = 0; @@ -416,7 +456,7 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { platform_driver_unregister(&isp1760_plat_driver); -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF platform_driver_unregister(&isp1760_of_driver); #endif #ifdef CONFIG_PCI diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c index c620c50f6770..18d574d6958b 100644 --- a/drivers/usb/host/ohci-ath79.c +++ b/drivers/usb/host/ohci-ath79.c @@ -111,7 +111,7 @@ static int ohci_ath79_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_stop_hcd; diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 958d985f2951..6b7bc50dfeaa 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -218,7 +218,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 6aca2c4453f7..843509778a33 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -344,7 +344,7 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, error = -ENODEV; goto err4; } - error = usb_add_hcd(hcd, irq, IRQF_DISABLED); + error = usb_add_hcd(hcd, irq, 0); if (error) goto err4; diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 4e681613e7ae..dc45d489d00e 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -81,7 +81,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, pdev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f04b742..34efd479e068 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_ath79_driver #endif +#ifdef CONFIG_NLM_XLR +#include "ohci-xls.c" +#define PLATFORM_DRIVER ohci_xls_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 9154615292db..2f00040fc408 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -356,10 +356,7 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) msleep(20); } - /* Does the root hub have a port wakeup pending? */ - if (ohci_readl(ohci, &ohci->regs->intrstatus) & - (OHCI_INTR_RD | OHCI_INTR_RHSC)) - usb_hcd_resume_root_hub(hcd); + usb_hcd_resume_root_hub(hcd); } /* Carry out polling-, autostop-, and autoresume-related state changes */ diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c index d8b45647d1dc..d469bf9b9e54 100644 --- a/drivers/usb/host/ohci-octeon.c +++ b/drivers/usb/host/ohci-octeon.c @@ -164,7 +164,7 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 5645f70b9214..e4b8782cc6e2 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -14,7 +14,7 @@ * This file is licenced under the GPL. */ -#include <linux/signal.h> /* IRQF_DISABLED */ +#include <linux/signal.h> #include <linux/jiffies.h> #include <linux/platform_device.h> #include <linux/clk.h> @@ -363,7 +363,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, retval = -ENXIO; goto err3; } - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval) goto err3; diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 6048f2f64f73..516ebc4d6cc2 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -149,7 +149,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci"); - if (!ret) { + if (!res) { dev_err(dev, "UHH OHCI get resource failed\n"); return -ENOMEM; } @@ -180,7 +180,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) { dev_dbg(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 653d6a60edb5..9ad8bee22c15 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -398,7 +398,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) ohci_hcd_init(ohci); dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret == 0) return ret; diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c index 28467e288a93..f13d08f94d6b 100644 --- a/drivers/usb/host/ohci-pnx8550.c +++ b/drivers/usb/host/ohci-pnx8550.c @@ -107,7 +107,7 @@ int usb_hcd_pnx8550_probe (const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 0c12f4e14dcd..d24cc89de16f 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -143,7 +143,7 @@ static int __devinit ohci_hcd_ppc_of_probe(struct platform_device *op) ohci_hcd_init(ohci); - rv = usb_add_hcd(hcd, irq, IRQF_DISABLED); + rv = usb_add_hcd(hcd, irq, 0); if (rv == 0) return 0; diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index c0f595c44487..1514b7067470 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -80,7 +80,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, #endif ohci_hcd_init(ohci); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 700950455f4d..6fd4fa1f19bb 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -164,7 +164,7 @@ static int __devinit ps3_ohci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 80be5472783a..29dfefe1c726 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -359,7 +359,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index dd24fc115e48..15dc51ded61a 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -428,7 +428,7 @@ static struct ed *ed_get ( ed->type = usb_pipetype(pipe); info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7; - info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16; + info |= usb_endpoint_maxp(&ep->desc) << 16; if (udev->speed == USB_SPEED_LOW) info |= ED_LOWSPEED; /* only control transfers store pids in tds */ @@ -444,7 +444,7 @@ static struct ed *ed_get ( ed->load = usb_calc_bus_time ( udev->speed, !is_out, ed->type == PIPE_ISOCHRONOUS, - le16_to_cpu(ep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&ep->desc)) / 1000; } } diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 7c9a4d55526b..a1877c47601e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -384,7 +384,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval != 0) goto err_ioremap; diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 4204d9720d23..4bde4f9821ba 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -143,7 +143,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, sa1111_start_hc(dev); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->irq[1], IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->irq[1], 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index 14cecb52a9fe..afc4eb6bb9d0 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev) hcd->regs = (void __iomem *)res->start; hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { err("Failed to add hcd"); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 78918ca0da23..968cea2b6d4e 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -165,7 +165,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err5; diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 4fd4bea9ac7a..69874654f3b5 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -152,7 +152,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) spear_start_ohci(ohci_p); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index c4aea3b8315e..5ba18595d6f7 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -169,7 +169,7 @@ static int ssb_ohci_attach(struct ssb_device *dev) hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) goto err_put_hcd; - err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (err) goto err_iounmap; diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 57ad1271fc9b..06331d931171 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -244,7 +244,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_add_hcd; diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c new file mode 100644 index 000000000000..a3a9c6f45b91 --- /dev/null +++ b/drivers/usb/host/ohci-xls.c @@ -0,0 +1,151 @@ +/* + * OHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on ohci-au1xxx.c, and other Linux OHCI drivers. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/platform_device.h> +#include <linux/signal.h> + +static int ohci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *dev) +{ + struct resource *res; + struct usb_hcd *hcd; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "Found HC with no IRQ\n"); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&dev->dev, "MMIO Handle incorrect!\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(driver, &dev->dev, "XLS"); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&dev->dev, "Controller already in use\n"); + retval = -EBUSY; + goto err2; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&dev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&dev->dev, "init fail, %d\n", retval); + return retval; +} + +static int ohci_xls_reset(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + return ohci_init(ohci); +} + +static int __devinit ohci_xls_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci; + int ret; + + ohci = hcd_to_ohci(hcd); + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + return 0; +} + +static struct hc_driver ohci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS OHCI Host Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + .reset = ohci_xls_reset, + .start = ohci_xls_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_xls_probe(struct platform_device *dev) +{ + int ret; + + pr_debug("In ohci_xls_probe"); + if (usb_disabled()) + return -ENODEV; + ret = ohci_xls_probe_internal(&ohci_xls_hc_driver, dev); + return ret; +} + +static int ohci_xls_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +static struct platform_driver ohci_xls_driver = { + .probe = ohci_xls_probe, + .remove = ohci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ohci-xls-0", + .owner = THIS_MODULE, + }, +}; diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 40a0d8b03ad7..e84ca1928dbf 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -959,7 +959,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, info.pipenum = get_empty_pipenum(r8a66597, ep); info.address = get_urb_to_r8a66597_addr(r8a66597, urb); info.epnum = usb_endpoint_num(ep); - info.maxpacket = le16_to_cpu(ep->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(ep); info.type = get_r8a66597_type(usb_endpoint_type(ep)); info.bufnum = get_bufnum(info.pipenum); info.buf_bsize = get_buf_bsize(info.pipenum); @@ -2519,7 +2519,7 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->has_tt = 1; - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); + ret = usb_add_hcd(hcd, irq, irq_trigger); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd\n"); goto clean_up3; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 1a996245ab98..961d6638d8f9 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1729,7 +1729,7 @@ sl811h_probe(struct platform_device *dev) * Use resource IRQ flags if set by platform device setup. */ irqflags |= IRQF_SHARED; - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags); + retval = usb_add_hcd(hcd, irq, irqflags); if (retval != 0) goto err6; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fba99b120588..c8ae199cfbb8 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -294,50 +294,50 @@ __acquires(uhci->lock) * and that remote wakeups should be enabled. */ egsm_enable = USBCMD_EGSM; - uhci->RD_enable = 1; int_enable = USBINTR_RESUME; wakeup_enable = 1; - /* In auto-stop mode wakeups must always be detected, but - * Resume-Detect interrupts may be prohibited. (In the absence - * of CONFIG_PM, they are always disallowed.) + /* + * In auto-stop mode, we must be able to detect new connections. + * The user can force us to poll by disabling remote wakeup; + * otherwise we will use the EGSM/RD mechanism. */ if (auto_stop) { if (!device_may_wakeup(&rhdev->dev)) - int_enable = 0; + egsm_enable = int_enable = 0; + } - /* In bus-suspend mode wakeups may be disabled, but if they are - * allowed then so are Resume-Detect interrupts. - */ - } else { #ifdef CONFIG_PM + /* + * In bus-suspend mode, we use the wakeup setting specified + * for the root hub. + */ + else { if (!rhdev->do_remote_wakeup) wakeup_enable = 0; -#endif } +#endif - /* EGSM causes the root hub to echo a 'K' signal (resume) out any - * port which requests a remote wakeup. According to the USB spec, - * every hub is supposed to do this. But if we are ignoring - * remote-wakeup requests anyway then there's no point to it. - * We also shouldn't enable EGSM if it's broken. - */ - if (!wakeup_enable || global_suspend_mode_is_broken(uhci)) - egsm_enable = 0; - - /* If we're ignoring wakeup events then there's no reason to - * enable Resume-Detect interrupts. We also shouldn't enable - * them if they are broken or disallowed. + /* + * UHCI doesn't distinguish between wakeup requests from downstream + * devices and local connect/disconnect events. There's no way to + * enable one without the other; both are controlled by EGSM. Thus + * if wakeups are disallowed then EGSM must be turned off -- in which + * case remote wakeup requests from downstream during system sleep + * will be lost. + * + * In addition, if EGSM is broken then we can't use it. Likewise, + * if Resume-Detect interrupts are broken then we can't use them. * - * This logic may lead us to enabling RD but not EGSM. The UHCI - * spec foolishly says that RD works only when EGSM is on, but - * there's no harm in enabling it anyway -- perhaps some chips - * will implement it! + * Finally, neither EGSM nor RD is useful by itself. Without EGSM, + * the RD status bit will never get set. Without RD, the controller + * won't generate interrupts to tell the system about wakeup events. */ - if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) || - !int_enable) - uhci->RD_enable = int_enable = 0; + if (!wakeup_enable || global_suspend_mode_is_broken(uhci) || + resume_detect_interrupts_are_broken(uhci)) + egsm_enable = int_enable = 0; + uhci->RD_enable = !!int_enable; uhci_writew(uhci, int_enable, USBINTR); uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD); mb(); @@ -364,10 +364,12 @@ __acquires(uhci->lock) uhci->rh_state = new_state; uhci->is_stopped = UHCI_IS_STOPPED; - /* If interrupts don't work and remote wakeup is enabled then - * the suspended root hub needs to be polled. + /* + * If remote wakeup is enabled but either EGSM or RD interrupts + * doesn't work, then we won't get an interrupt when a wakeup event + * occurs. Thus the suspended root hub needs to be polled. */ - if (!int_enable && wakeup_enable) + if (wakeup_enable && (!int_enable || !egsm_enable)) set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); else clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 84ed28b34f93..f6ca80ee4cec 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -280,7 +280,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, qh->load = usb_calc_bus_time(udev->speed, usb_endpoint_dir_in(&hep->desc), qh->type == USB_ENDPOINT_XFER_ISOC, - le16_to_cpu(hep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&hep->desc)) / 1000 + 1; } else { /* Skeleton QH */ @@ -792,7 +792,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; __hc32 *plink; @@ -918,7 +918,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; int this_sg_len; dma_addr_t data; diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index ce5c9e51748e..c7f33123d4c0 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -65,6 +65,12 @@ /* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ #define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C (1 << 16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC (1 << 19) + /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ #define XHCI_CMD_RUN (1 << 0) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 723f8231193d..431efe72b1f7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -28,6 +28,25 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) +/* usb 1.1 root hub device descriptor */ +static u8 usb_bos_descriptor [] = { + USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ + USB_DT_BOS, /* __u8 bDescriptorType */ + 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ + 0x1, /* __u8 bNumDeviceCaps */ + /* First device capability */ + USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ + USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ + 0x00, /* bmAttributes, LTM off by default */ + USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ + 0x03, /* bFunctionalitySupport, + USB 3.0 speed only */ + 0x00, /* bU1DevExitLat, set later. */ + 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ +}; + + static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc, int ports) { @@ -232,7 +251,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, continue; speed = xhci->devs[i]->udev->speed; if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) - && xhci->devs[i]->port == port) { + && xhci->devs[i]->fake_port == port) { slot_id = i; break; } @@ -392,13 +411,39 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) return max_ports; } +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | link_state; + xhci_writel(xhci, temp, port_array[port_id]); +} + +/* Test and clear port RWC bit */ +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + if (temp & port_bit) { + temp = xhci_port_state_to_neutral(temp); + temp |= port_bit; + xhci_writel(xhci, temp, port_array[port_id]); + } +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; - u32 temp, temp1, status; + u32 temp, status; int retval = 0; __le32 __iomem **port_array; int slot_id; @@ -429,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_hub_descriptor(hcd, xhci, (struct usb_hub_descriptor *) buf); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + if ((wValue & 0xff00) != (USB_DT_BOS << 8)) + goto error; + + if (hcd->speed != HCD_USB3) + goto error; + + memcpy(buf, &usb_bos_descriptor, + USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); + temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + + spin_unlock_irqrestore(&xhci->lock, flags); + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; @@ -472,11 +532,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); bus_state->resume_done[wIndex] = 0; - temp1 = xhci_port_state_to_neutral(temp); - temp1 &= ~PORT_PLS_MASK; - temp1 |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp1, port_array[wIndex]); - + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); xhci_dbg(xhci, "set port %d resume\n", wIndex + 1); slot_id = xhci_find_slot_id_by_port(hcd, xhci, @@ -551,10 +608,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); + if ((temp & PORT_PLS_MASK) != XDEV_U0) { + /* Resume the port to U0 first */ + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ + temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " @@ -573,10 +639,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U3; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ @@ -610,10 +673,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } } - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | link_state; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + link_state); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); /* wait device to enter */ @@ -677,24 +738,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if ((temp & PORT_PE) == 0) goto error; - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, - port_array[wIndex]); - - spin_unlock_irqrestore(&xhci->lock, - flags); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_RESUME); + spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - - temp = xhci_readl(xhci, - port_array[wIndex]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); } bus_state->port_c_suspend |= 1 << wIndex; @@ -910,25 +960,18 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { if (DEV_SUPERSPEED(temp)) { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } else { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[port_index]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } /* wait for the port to enter U0 and report port link * state change. @@ -938,12 +981,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) spin_lock_irqsave(&xhci->lock, flags); /* Clear PLC */ - temp = xhci_readl(xhci, port_array[port_index]); - if (temp & PORT_PLC) { - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[port_index]); - } + xhci_test_and_clear_bit(xhci, port_array, port_index, + PORT_PLC); slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d446886b22b0..42a22b8e6922 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -61,8 +61,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) { - if (!seg) - return; if (seg->trbs) { xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n", seg->trbs, (unsigned long long)seg->dma); @@ -81,7 +79,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) * related flags, such as End TRB, Toggle Cycle, and no snoop. */ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, - struct xhci_segment *next, bool link_trbs) + struct xhci_segment *next, bool link_trbs, bool isoc) { u32 val; @@ -97,7 +95,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); /* Always set the chain bit with 0.95 hardware */ - if (xhci_link_trb_quirk(xhci)) + /* Set chain bit for isoc rings on AMD 0.96 host */ + if (xhci_link_trb_quirk(xhci) || + (isoc && (xhci->quirks & XHCI_AMD_0x96_HOST))) val |= TRB_CHAIN; prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } @@ -112,18 +112,20 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) struct xhci_segment *seg; struct xhci_segment *first_seg; - if (!ring || !ring->first_seg) + if (!ring) return; - first_seg = ring->first_seg; - seg = first_seg->next; - xhci_dbg(xhci, "Freeing ring at %p\n", ring); - while (seg != first_seg) { - struct xhci_segment *next = seg->next; - xhci_segment_free(xhci, seg); - seg = next; + if (ring->first_seg) { + first_seg = ring->first_seg; + seg = first_seg->next; + xhci_dbg(xhci, "Freeing ring at %p\n", ring); + while (seg != first_seg) { + struct xhci_segment *next = seg->next; + xhci_segment_free(xhci, seg); + seg = next; + } + xhci_segment_free(xhci, first_seg); + ring->first_seg = NULL; } - xhci_segment_free(xhci, first_seg); - ring->first_seg = NULL; kfree(ring); } @@ -152,7 +154,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring) * See section 4.9.1 and figures 15 and 16. */ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, bool link_trbs, gfp_t flags) + unsigned int num_segs, bool link_trbs, bool isoc, gfp_t flags) { struct xhci_ring *ring; struct xhci_segment *prev; @@ -178,12 +180,12 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, next = xhci_segment_alloc(xhci, flags); if (!next) goto fail; - xhci_link_segments(xhci, prev, next, link_trbs); + xhci_link_segments(xhci, prev, next, link_trbs, isoc); prev = next; num_segs--; } - xhci_link_segments(xhci, prev, ring->first_seg, link_trbs); + xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc); if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ @@ -229,14 +231,14 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, * pointers to the beginning of the ring. */ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci, - struct xhci_ring *ring) + struct xhci_ring *ring, bool isoc) { struct xhci_segment *seg = ring->first_seg; do { memset(seg->trbs, 0, sizeof(union xhci_trb)*TRBS_PER_SEGMENT); /* All endpoint rings have link TRBs */ - xhci_link_segments(xhci, seg, seg->next, 1); + xhci_link_segments(xhci, seg, seg->next, 1, isoc); seg = seg->next; } while (seg != ring->first_seg); xhci_initialize_ring_info(ring); @@ -315,7 +317,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, stream_ctx, dma); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) @@ -343,9 +345,9 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return pci_alloc_consistent(pdev, + return dma_alloc_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, - dma); + dma, mem_flags); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_alloc(xhci->small_streams_pool, mem_flags, dma); @@ -540,7 +542,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, */ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -687,11 +689,103 @@ static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, ep->xhci = xhci; } -/* All the xhci_tds in the ring's TD list should be freed at this point */ +static void xhci_free_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int slot_id) +{ + struct list_head *tt; + struct list_head *tt_list_head; + struct list_head *tt_next; + struct xhci_tt_bw_info *tt_info; + + /* If the device never made it past the Set Address stage, + * it may not have the real_port set correctly. + */ + if (virt_dev->real_port == 0 || + virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "Bad real port.\n"); + return; + } + + tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts); + if (list_empty(tt_list_head)) + return; + + list_for_each(tt, tt_list_head) { + tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list); + if (tt_info->slot_id == slot_id) + break; + } + /* Cautionary measure in case the hub was disconnected before we + * stored the TT information. + */ + if (tt_info->slot_id != slot_id) + return; + + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + /* Multi-TT hubs will have more than one entry */ + do { + list_del(tt); + kfree(tt_info); + tt = tt_next; + if (list_empty(tt_list_head)) + break; + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + } while (tt_info->slot_id == slot_id); +} + +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags) +{ + struct xhci_tt_bw_info *tt_info; + unsigned int num_ports; + int i, j; + + if (!tt->multi) + num_ports = 1; + else + num_ports = hdev->maxchild; + + for (i = 0; i < num_ports; i++, tt_info++) { + struct xhci_interval_bw_table *bw_table; + + tt_info = kzalloc(sizeof(*tt_info), mem_flags); + if (!tt_info) + goto free_tts; + INIT_LIST_HEAD(&tt_info->tt_list); + list_add(&tt_info->tt_list, + &xhci->rh_bw[virt_dev->real_port - 1].tts); + tt_info->slot_id = virt_dev->udev->slot_id; + if (tt->multi) + tt_info->ttport = i+1; + bw_table = &tt_info->bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } + return 0; + +free_tts: + xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id); + return -ENOMEM; +} + + +/* All the xhci_tds in the ring's TD list should be freed at this point. + * Should be called with xhci->lock held if there is any chance the TT lists + * will be manipulated by the configure endpoint, allocate device, or update + * hub functions while this function is removing the TT entries from the list. + */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *dev; int i; + int old_active_eps = 0; /* Slot ID 0 is reserved */ if (slot_id == 0 || !xhci->devs[slot_id]) @@ -702,13 +796,29 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) if (!dev) return; + if (dev->tt_info) + old_active_eps = dev->tt_info->active_eps; + for (i = 0; i < 31; ++i) { if (dev->eps[i].ring) xhci_ring_free(xhci, dev->eps[i].ring); if (dev->eps[i].stream_info) xhci_free_stream_info(xhci, dev->eps[i].stream_info); + /* Endpoints on the TT/root port lists should have been removed + * when usb_disable_device() was called for the device. + * We can't drop them anyway, because the udev might have gone + * away by this point, and we can't tell what speed it was. + */ + if (!list_empty(&dev->eps[i].bw_endpoint_list)) + xhci_warn(xhci, "Slot %u endpoint %u " + "not removed from BW list!\n", + slot_id, i); } + /* If this is a hub, free the TT(s) from the TT list */ + xhci_free_tt_info(xhci, dev, slot_id); + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, dev, old_active_eps); if (dev->ring_cache) { for (i = 0; i < dev->num_rings_cached; i++) @@ -762,10 +872,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, for (i = 0; i < 31; i++) { xhci_init_endpoint_timer(xhci, &dev->eps[i]); INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!dev->eps[0].ring) goto fail; @@ -921,9 +1032,40 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) /* Found device below root hub */; - dev->port = top_dev->portnum; + dev->fake_port = top_dev->portnum; + dev->real_port = port_num; xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); - xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port); + xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port); + + /* Find the right bandwidth table that this device will be a part of. + * If this is a full speed device attached directly to a root port (or a + * decendent of one), it counts as a primary bandwidth domain, not a + * secondary bandwidth domain under a TT. An xhci_tt_info structure + * will never be created for the HS root hub. + */ + if (!udev->tt || !udev->tt->hub->parent) { + dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table; + } else { + struct xhci_root_port_bw_info *rh_bw; + struct xhci_tt_bw_info *tt_bw; + + rh_bw = &xhci->rh_bw[port_num - 1]; + /* Find the right TT. */ + list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) { + if (tt_bw->slot_id != udev->tt->hub->slot_id) + continue; + + if (!dev->udev->tt->multi || + (udev->tt->multi && + tt_bw->ttport == dev->udev->ttport)) { + dev->bw_table = &tt_bw->bw_table; + dev->tt_info = tt_bw; + break; + } + } + if (!dev->tt_info) + xhci_warn(xhci, "WARN: Didn't find a matching TT\n"); + } /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { @@ -1141,8 +1283,8 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, if (udev->speed == USB_SPEED_SUPER) return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * (max_burst + 1); } @@ -1175,10 +1317,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc)) virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 8, true, mem_flags); + xhci_ring_alloc(xhci, 8, true, true, mem_flags); else virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); if (!virt_dev->eps[ep_index].new_ring) { /* Attempt to use the ring cache */ if (virt_dev->num_rings_cached == 0) @@ -1187,7 +1329,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->ring_cache[virt_dev->num_rings_cached]; virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; virt_dev->num_rings_cached--; - xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); + xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, + usb_endpoint_xfer_isoc(&ep->desc) ? true : false); } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; @@ -1211,7 +1354,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set the max packet size and max burst */ switch (udev->speed) { case USB_SPEED_SUPER: - max_packet = le16_to_cpu(ep->desc.wMaxPacketSize); + max_packet = usb_endpoint_maxp(&ep->desc); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); /* dig out max burst from ep companion desc */ max_packet = ep->ss_ep_comp.bMaxBurst; @@ -1223,14 +1366,14 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst)); } /* Fall through */ case USB_SPEED_FULL: case USB_SPEED_LOW: - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); break; default: @@ -1286,6 +1429,70 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, */ } +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info) +{ + bw_info->ep_interval = 0; + bw_info->mult = 0; + bw_info->num_packets = 0; + bw_info->max_packet_size = 0; + bw_info->type = 0; + bw_info->max_esit_payload = 0; +} + +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev) +{ + struct xhci_bw_info *bw_info; + struct xhci_ep_ctx *ep_ctx; + unsigned int ep_type; + int i; + + for (i = 1; i < 31; ++i) { + bw_info = &virt_dev->eps[i].bw_info; + + /* We can't tell what endpoint type is being dropped, but + * unconditionally clearing the bandwidth info for non-periodic + * endpoints should be harmless because the info will never be + * set in the first place. + */ + if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) { + /* Dropped endpoint */ + xhci_clear_endpoint_bw_info(bw_info); + continue; + } + + if (EP_IS_ADDED(ctrl_ctx, i)) { + ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, i); + ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + /* Ignore non-periodic endpoints */ + if (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP) + continue; + + /* Added or changed endpoint */ + bw_info->ep_interval = CTX_TO_EP_INTERVAL( + le32_to_cpu(ep_ctx->ep_info)); + /* Number of packets and mult are zero-based in the + * input context, but we want one-based for the + * interval table. + */ + bw_info->mult = CTX_TO_EP_MULT( + le32_to_cpu(ep_ctx->ep_info)) + 1; + bw_info->num_packets = CTX_TO_MAX_BURST( + le32_to_cpu(ep_ctx->ep_info2)) + 1; + bw_info->max_packet_size = MAX_PACKET_DECODED( + le32_to_cpu(ep_ctx->ep_info2)); + bw_info->type = ep_type; + bw_info->max_esit_payload = CTX_TO_MAX_ESIT_PAYLOAD( + le32_to_cpu(ep_ctx->tx_info)); + } + } +} + /* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. * Useful when you want to change one particular aspect of the endpoint and then * issue a configure endpoint command. @@ -1344,10 +1551,9 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad) goto fail_sp; - xhci->scratchpad->sp_array = - pci_alloc_consistent(to_pci_dev(dev), + xhci->scratchpad->sp_array = dma_alloc_coherent(dev, num_sp * sizeof(u64), - &xhci->scratchpad->sp_dma); + &xhci->scratchpad->sp_dma, flags); if (!xhci->scratchpad->sp_array) goto fail_sp2; @@ -1364,8 +1570,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; - void *buf = pci_alloc_consistent(to_pci_dev(dev), - xhci->page_size, &dma); + void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, + flags); if (!buf) goto fail_sp5; @@ -1378,7 +1584,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) fail_sp5: for (i = i - 1; i >= 0; i--) { - pci_free_consistent(to_pci_dev(dev), xhci->page_size, + dma_free_coherent(dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } @@ -1388,7 +1594,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) kfree(xhci->scratchpad->sp_buffers); fail_sp3: - pci_free_consistent(to_pci_dev(dev), num_sp * sizeof(u64), + dma_free_coherent(dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); @@ -1412,13 +1618,13 @@ static void scratchpad_free(struct xhci_hcd *xhci) num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); for (i = 0; i < num_sp; i++) { - pci_free_consistent(pdev, xhci->page_size, + dma_free_coherent(&pdev->dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } kfree(xhci->scratchpad->sp_dma_buffers); kfree(xhci->scratchpad->sp_buffers); - pci_free_consistent(pdev, num_sp * sizeof(u64), + dma_free_coherent(&pdev->dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); kfree(xhci->scratchpad); @@ -1463,18 +1669,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv) { - int last; - - if (!urb_priv) - return; - - last = urb_priv->length - 1; - if (last >= 0) { - int i; - for (i = 0; i <= last; i++) - kfree(urb_priv->td[i]); + if (urb_priv) { + kfree(urb_priv->td[0]); + kfree(urb_priv); } - kfree(urb_priv); } void xhci_free_command(struct xhci_hcd *xhci, @@ -1489,6 +1687,8 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct dev_info *dev_info, *next; + unsigned long flags; int size; int i; @@ -1500,7 +1700,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) - pci_free_consistent(pdev, size, + dma_free_coherent(&pdev->dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; xhci_dbg(xhci, "Freed ERST\n"); @@ -1540,17 +1740,25 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); if (xhci->dcbaa) - pci_free_consistent(pdev, sizeof(*xhci->dcbaa), + dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa = NULL; scratchpad_free(xhci); + spin_lock_irqsave(&xhci->lock, flags); + list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) { + list_del(&dev_info->list); + kfree(dev_info); + } + spin_unlock_irqrestore(&xhci->lock, flags); + xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; kfree(xhci->usb2_ports); kfree(xhci->usb3_ports); kfree(xhci->port_array); + kfree(xhci->rh_bw); xhci->page_size = 0; xhci->page_shift = 0; @@ -1762,6 +1970,23 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) /* WTF? "Valid values are ‘1’ to MaxPorts" */ return; + + /* Check the host's USB2 LPM capability */ + if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && + (temp & XHCI_L1C)) { + xhci_dbg(xhci, "xHCI 0.96: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + } + + if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + if (temp & XHCI_HLC) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 hardware lpm\n"); + xhci->hw_lpm_support = 1; + } + } + port_offset--; for (i = port_offset; i < (port_offset + port_count); i++) { /* Duplicate entry. Ignore the port if the revisions differ. */ @@ -1806,7 +2031,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) __le32 __iomem *addr; u32 offset; unsigned int num_ports; - int i, port_index; + int i, j, port_index; addr = &xhci->cap_regs->hcc_params; offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); @@ -1821,6 +2046,18 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->port_array) return -ENOMEM; + xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags); + if (!xhci->rh_bw) + return -ENOMEM; + for (i = 0; i < num_ports; i++) { + struct xhci_interval_bw_table *bw_table; + + INIT_LIST_HEAD(&xhci->rh_bw[i].tts); + bw_table = &xhci->rh_bw[i].bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } + /* * For whatever reason, the first capability offset is from the * capability register base, not from the HCCPARAMS register. @@ -1959,8 +2196,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * Section 5.4.8 - doorbell array must be * "physically contiguous and 64-byte (cache line) aligned". */ - xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev), - sizeof(*xhci->dcbaa), &dma); + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, + GFP_KERNEL); if (!xhci->dcbaa) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); @@ -1994,14 +2231,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) dma_pool_create("xHCI 1KB stream ctx arrays", dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE - * will be allocated with pci_alloc_consistent() + * will be allocated with dma_alloc_coherent() */ if (!xhci->small_streams_pool || !xhci->medium_streams_pool) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring); @@ -2032,14 +2269,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * the event ring segment table (ERST). Section 4.9.3. */ xhci_dbg(xhci, "// Allocating event ring\n"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); + xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, false, + flags); if (!xhci->event_ring) goto fail; if (xhci_check_trb_in_td_math(xhci, flags) < 0) goto fail; - xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), - sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); + xhci->erst.entries = dma_alloc_coherent(dev, + sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, + GFP_KERNEL); if (!xhci->erst.entries) goto fail; xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n", @@ -2102,6 +2341,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_setup_port_arrays(xhci, flags)) goto fail; + INIT_LIST_HEAD(&xhci->lpm_failed_devs); + return 0; fail: diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cb16de213f64..9f51f88cc0f5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,61 +51,9 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) return 0; } -/* called during probe() after chip reset completes */ -static int xhci_pci_setup(struct usb_hcd *hcd) +static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) { - struct xhci_hcd *xhci; - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - int retval; - u32 temp; - - hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; - - if (usb_hcd_is_primary_hcd(hcd)) { - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; - xhci->main_hcd = hcd; - /* Mark the first roothub as being USB 2.0. - * The xHCI driver will register the USB 3.0 roothub. - */ - hcd->speed = HCD_USB2; - hcd->self.root_hub->speed = USB_SPEED_HIGH; - /* - * USB 2.0 roothub under xHCI has an integrated TT, - * (rate matching hub) as opposed to having an OHCI/UHCI - * companion controller. - */ - hcd->has_tt = 1; - } else { - /* xHCI private pointer was set in xhci_pci_probe for the second - * registered roothub. - */ - xhci = hcd_to_xhci(hcd); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } - return 0; - } - - xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + - HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); - xhci->run_regs = hcd->regs + - (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); - /* Cache read-only capability registers */ - xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); - xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); - xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - xhci->hci_version = HC_VERSION(xhci->hcc_params); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - xhci_print_registers(xhci); + struct pci_dev *pdev = to_pci_dev(dev); /* Look for vendor-specific quirks */ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC && @@ -128,6 +76,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (pdev->vendor == PCI_VENDOR_ID_NEC) xhci->quirks |= XHCI_NEC_HOST; + if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96) + xhci->quirks |= XHCI_AMD_0x96_HOST; + /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; @@ -136,39 +87,29 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; xhci->quirks |= XHCI_EP_LIMIT_QUIRK; xhci->limit_active_eps = 64; + xhci->quirks |= XHCI_SW_BW_CHECKING; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); } +} - /* Make sure the HC is halted. */ - retval = xhci_halt(xhci); - if (retval) - goto error; +/* called during probe() after chip reset completes */ +static int xhci_pci_setup(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci; + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + int retval; - xhci_dbg(xhci, "Resetting HCD\n"); - /* Reset the internal HC memory state and registers. */ - retval = xhci_reset(xhci); + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } + return retval; - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); - if (retval) - goto error; - xhci_dbg(xhci, "Called HCD init\n"); + xhci = hcd_to_xhci(hcd); + if (!usb_hcd_is_primary_hcd(hcd)) + return 0; pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); @@ -178,7 +119,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!retval) return retval; -error: kfree(xhci); return retval; } @@ -222,7 +162,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (retval) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ @@ -344,6 +284,11 @@ static const struct hc_driver xhci_pci_hc_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + /* + * call back when device connected and addressed + */ + .update_device = xhci_update_device, + .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, }; /*-------------------------------------------------------------------------*/ @@ -375,12 +320,12 @@ static struct pci_driver xhci_pci_driver = { #endif }; -int xhci_register_pci(void) +int __init xhci_register_pci(void) { return pci_register_driver(&xhci_pci_driver); } -void xhci_unregister_pci(void) +void __exit xhci_unregister_pci(void) { pci_unregister_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 952e2ded61af..940321b3ec68 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -185,7 +185,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer * prepare_transfer()? */ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming) + bool consumer, bool more_trbs_coming, bool isoc) { u32 chain; union xhci_trb *next; @@ -212,11 +212,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, if (!chain && !more_trbs_coming) break; - /* If we're not dealing with 0.95 hardware, + /* If we're not dealing with 0.95 hardware or + * isoc rings on AMD 0.96 host, * carry over the chain bit of the previous TRB * (which may mean the chain bit is cleared). */ - if (!xhci_link_trb_quirk(xhci)) { + if (!(isoc && (xhci->quirks & XHCI_AMD_0x96_HOST)) + && !xhci_link_trb_quirk(xhci)) { next->link.control &= cpu_to_le32(~TRB_CHAIN); next->link.control |= @@ -1329,10 +1331,8 @@ static void handle_port_status(struct xhci_hcd *xhci, if (DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "resume SS port %d\n", port_id); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_set_link_state(xhci, port_array, faked_port_index, + XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, faked_port_index); if (!slot_id) { @@ -1342,10 +1342,8 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_ring_device(xhci, slot_id); xhci_dbg(xhci, "resume SS port %d finished\n", port_id); /* Clear PORT_PLC */ - temp = xhci_readl(xhci, port_array[faked_port_index]); - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); bus_state->resume_done[faked_port_index] = jiffies + @@ -1356,6 +1354,10 @@ static void handle_port_status(struct xhci_hcd *xhci, } } + if (hcd->speed != HCD_USB3) + xhci_test_and_clear_bit(xhci, port_array, faked_port_index, + PORT_PLC); + cleanup: /* Update event ring dequeue pointer before dropping the lock */ inc_deq(xhci, xhci->event_ring, true); @@ -2192,7 +2194,8 @@ cleanup: if ((urb->actual_length != urb->transfer_buffer_length && (urb->transfer_flags & URB_SHORT_NOT_OK)) || - status != 0) + (status != 0 && + !usb_endpoint_xfer_isoc(&urb->ep->desc))) xhci_dbg(xhci, "Giveback URB %p, len = %d, " "expected = %x, status = %d\n", urb, urb->actual_length, @@ -2409,7 +2412,7 @@ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) * prepare_transfer()? */ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming, + bool consumer, bool more_trbs_coming, bool isoc, u32 field1, u32 field2, u32 field3, u32 field4) { struct xhci_generic_trb *trb; @@ -2419,7 +2422,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, trb->field[1] = cpu_to_le32(field2); trb->field[2] = cpu_to_le32(field3); trb->field[3] = cpu_to_le32(field4); - inc_enq(xhci, ring, consumer, more_trbs_coming); + inc_enq(xhci, ring, consumer, more_trbs_coming, isoc); } /* @@ -2427,7 +2430,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, * FIXME allocate segments if the ring is full. */ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - u32 ep_state, unsigned int num_trbs, gfp_t mem_flags) + u32 ep_state, unsigned int num_trbs, bool isoc, gfp_t mem_flags) { /* Make sure the endpoint has been added to xHC schedule */ switch (ep_state) { @@ -2469,10 +2472,11 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next = ring->enqueue; while (last_trb(xhci, ring, ring->enq_seg, next)) { - /* If we're not dealing with 0.95 hardware, - * clear the chain bit. + /* If we're not dealing with 0.95 hardware or isoc rings + * on AMD 0.96 host, clear the chain bit. */ - if (!xhci_link_trb_quirk(xhci)) + if (!xhci_link_trb_quirk(xhci) && !(isoc && + (xhci->quirks & XHCI_AMD_0x96_HOST))) next->link.control &= cpu_to_le32(~TRB_CHAIN); else next->link.control |= cpu_to_le32(TRB_CHAIN); @@ -2505,6 +2509,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, unsigned int num_trbs, struct urb *urb, unsigned int td_index, + bool isoc, gfp_t mem_flags) { int ret; @@ -2522,7 +2527,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, isoc, mem_flags); if (ret) return ret; @@ -2711,7 +2716,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, * running_total. */ packets_transferred = (running_total + trb_buff_len) / - le16_to_cpu(urb->ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&urb->ep->desc); return xhci_td_remainder(total_packet_count - packets_transferred); } @@ -2741,11 +2746,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (trb_buff_len < 0) return trb_buff_len; @@ -2840,7 +2845,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -2931,7 +2936,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -2948,7 +2953,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, running_total = 0; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -3003,7 +3008,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3063,7 +3068,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs++; ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -3096,7 +3101,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } } - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, TRB_LEN(8) | TRB_INTR_TARGET(0), @@ -3116,7 +3121,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), length_field, @@ -3132,7 +3137,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field = 0; else field = TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, false, + queue_trb(xhci, ep_ring, false, false, false, 0, 0, TRB_INTR_TARGET(0), @@ -3269,7 +3274,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; total_packet_count = roundup(td_len, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); /* A zero-length transfer still involves at least one packet. */ if (total_packet_count == 0) total_packet_count++; @@ -3281,7 +3286,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, - urb->stream_id, trbs_per_td, urb, i, mem_flags); + urb->stream_id, trbs_per_td, urb, i, true, + mem_flags); if (ret < 0) { if (i == 0) return ret; @@ -3351,7 +3357,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, remainder | TRB_INTR_TARGET(0); - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, true, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3433,7 +3439,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Do not insert any td of the urb to the ring if the check failed. */ ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, true, mem_flags); if (ret) return ret; @@ -3492,7 +3498,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, reserved_trbs++; ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING, - reserved_trbs, GFP_ATOMIC); + reserved_trbs, false, GFP_ATOMIC); if (ret < 0) { xhci_err(xhci, "ERR: No room for command on command ring\n"); if (command_must_succeed) @@ -3500,8 +3506,8 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, "unfailable commands failed.\n"); return ret; } - queue_trb(xhci, xhci->cmd_ring, false, false, field1, field2, field3, - field4 | xhci->cmd_ring->cycle_state); + queue_trb(xhci, xhci->cmd_ring, false, false, false, field1, field2, + field3, field4 | xhci->cmd_ring->cycle_state); return 0; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3a0f695138f4..1ff95a0df576 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -175,28 +175,19 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); } -/* - * Free IRQs - * free all IRQs request - */ -static void xhci_free_irq(struct xhci_hcd *xhci) +#ifdef CONFIG_PCI +static int xhci_free_msi(struct xhci_hcd *xhci) { int i; - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - /* return if using legacy interrupt */ - if (xhci_to_hcd(xhci)->irq >= 0) - return; - - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - if (xhci->msix_entries[i].vector) - free_irq(xhci->msix_entries[i].vector, - xhci_to_hcd(xhci)); - } else if (pdev->irq >= 0) - free_irq(pdev->irq, xhci_to_hcd(xhci)); + if (!xhci->msix_entries) + return -EINVAL; - return; + for (i = 0; i < xhci->msix_count; i++) + if (xhci->msix_entries[i].vector) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + return 0; } /* @@ -224,6 +215,28 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) } /* + * Free IRQs + * free all IRQs request + */ +static void xhci_free_irq(struct xhci_hcd *xhci) +{ + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* return if using legacy interrupt */ + if (xhci_to_hcd(xhci)->irq >= 0) + return; + + ret = xhci_free_msi(xhci); + if (!ret) + return; + if (pdev->irq >= 0) + free_irq(pdev->irq, xhci_to_hcd(xhci)); + + return; +} + +/* * Set up MSI-X */ static int xhci_setup_msix(struct xhci_hcd *xhci) @@ -302,6 +315,72 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) return; } +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ + int i; + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + synchronize_irq(xhci->msix_entries[i].vector); + } +} + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* + * Some Fresco Logic host controllers advertise MSI, but fail to + * generate interrupts. Don't even try to enable MSI. + */ + if (xhci->quirks & XHCI_BROKEN_MSI) + return 0; + + /* unregister the legacy interrupt */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + + ret = xhci_setup_msix(xhci); + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + + if (!ret) + /* hcd->irq is -1, we have MSI */ + return 0; + + /* fall back to legacy interrupt*/ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (ret) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return ret; + } + hcd->irq = pdev->irq; + return 0; +} + +#else + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + return 0; +} + +static void xhci_cleanup_msix(struct xhci_hcd *xhci) +{ +} + +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ +} + +#endif + /* * Initialize memory for HCD and xHC (one-time init). * @@ -316,7 +395,7 @@ int xhci_init(struct usb_hcd *hcd) xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); - if (link_quirk) { + if (xhci->hci_version == 0x95 && link_quirk) { xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n"); xhci->quirks |= XHCI_LINK_TRB_QUIRK; } else { @@ -413,9 +492,8 @@ int xhci_run(struct usb_hcd *hcd) { u32 temp; u64 temp_64; - u32 ret; + int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. @@ -426,34 +504,10 @@ int xhci_run(struct usb_hcd *hcd) return xhci_run_finished(xhci); xhci_dbg(xhci, "xhci_run\n"); - /* unregister the legacy interrupt */ - if (hcd->irq) - free_irq(hcd->irq, hcd); - hcd->irq = -1; - - /* Some Fresco Logic host controllers advertise MSI, but fail to - * generate interrupts. Don't even try to enable MSI. - */ - if (xhci->quirks & XHCI_BROKEN_MSI) - goto legacy_irq; - ret = xhci_setup_msix(xhci); + ret = xhci_try_enable_msi(hcd); if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); - - if (ret) { -legacy_irq: - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); - if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); - return ret; - } - hcd->irq = pdev->irq; - } + return ret; #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING init_timer(&xhci->event_ring_timer); @@ -694,7 +748,6 @@ int xhci_suspend(struct xhci_hcd *xhci) int rc = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; - int i; spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); @@ -730,10 +783,7 @@ int xhci_suspend(struct xhci_hcd *xhci) /* step 5: remove core well power */ /* synchronize irq when using MSI-X */ - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(xhci->msix_entries[i].vector); - } + xhci_msix_sync_irqs(xhci); return rc; } @@ -945,8 +995,7 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; if (check_virt_dev) { - if (!udev->slot_id || !xhci->devs - || !xhci->devs[udev->slot_id]) { + if (!udev->slot_id || !xhci->devs[udev->slot_id]) { printk(KERN_DEBUG "xHCI %s called with unaddressed " "device\n", func); return -EINVAL; @@ -987,7 +1036,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, out_ctx = xhci->devs[slot_id]->out_ctx; ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize); + max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); if (hw_max_packet_size != max_packet_size) { xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); xhci_dbg(xhci, "Max packet size in usb_device = %d\n", @@ -1035,6 +1084,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_td *buffer; unsigned long flags; int ret = 0; unsigned int slot_id, ep_index; @@ -1065,13 +1115,15 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) if (!urb_priv) return -ENOMEM; + buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags); + if (!buffer) { + kfree(urb_priv); + return -ENOMEM; + } + for (i = 0; i < size; i++) { - urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags); - if (!urb_priv->td[i]) { - urb_priv->length = i; - xhci_urb_free_priv(xhci, urb_priv); - return -ENOMEM; - } + urb_priv->td[i] = buffer; + buffer++; } urb_priv->length = size; @@ -1747,6 +1799,564 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci, xhci->num_active_eps); } +unsigned int xhci_get_block_size(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return FS_BLOCK; + case USB_SPEED_HIGH: + return HS_BLOCK; + case USB_SPEED_SUPER: + return SS_BLOCK; + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + default: + /* Should never happen */ + return 1; + } +} + +unsigned int xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw) +{ + if (interval_bw->overhead[LS_OVERHEAD_TYPE]) + return LS_OVERHEAD; + if (interval_bw->overhead[FS_OVERHEAD_TYPE]) + return FS_OVERHEAD; + return HS_OVERHEAD; +} + +/* If we are changing a LS/FS device under a HS hub, + * make sure (if we are activating a new TT) that the HS bus has enough + * bandwidth for this new TT. + */ +static int xhci_check_tt_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; + + /* Find the bandwidth table for the root port this TT is attached to. */ + bw_table = &xhci->rh_bw[virt_dev->real_port - 1].bw_table; + tt_info = virt_dev->tt_info; + /* If this TT already had active endpoints, the bandwidth for this TT + * has already been added. Removing all periodic endpoints (and thus + * making the TT enactive) will only decrease the bandwidth used. + */ + if (old_active_eps) + return 0; + if (old_active_eps == 0 && tt_info->active_eps != 0) { + if (bw_table->bw_used + TT_HS_OVERHEAD > HS_BW_LIMIT) + return -ENOMEM; + return 0; + } + /* Not sure why we would have no new active endpoints... + * + * Maybe because of an Evaluate Context change for a hub update or a + * control endpoint 0 max packet size change? + * FIXME: skip the bandwidth calculation in that case. + */ + return 0; +} + +static int xhci_check_ss_bw(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev) +{ + unsigned int bw_reserved; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100); + if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved)) + return -ENOMEM; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100); + if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved)) + return -ENOMEM; + + return 0; +} + +/* + * This algorithm is a very conservative estimate of the worst-case scheduling + * scenario for any one interval. The hardware dynamically schedules the + * packets, so we can't tell which microframe could be the limiting factor in + * the bandwidth scheduling. This only takes into account periodic endpoints. + * + * Obviously, we can't solve an NP complete problem to find the minimum worst + * case scenario. Instead, we come up with an estimate that is no less than + * the worst case bandwidth used for any one microframe, but may be an + * over-estimate. + * + * We walk the requirements for each endpoint by interval, starting with the + * smallest interval, and place packets in the schedule where there is only one + * possible way to schedule packets for that interval. In order to simplify + * this algorithm, we record the largest max packet size for each interval, and + * assume all packets will be that size. + * + * For interval 0, we obviously must schedule all packets for each interval. + * The bandwidth for interval 0 is just the amount of data to be transmitted + * (the sum of all max ESIT payload sizes, plus any overhead per packet times + * the number of packets). + * + * For interval 1, we have two possible microframes to schedule those packets + * in. For this algorithm, if we can schedule the same number of packets for + * each possible scheduling opportunity (each microframe), we will do so. The + * remaining number of packets will be saved to be transmitted in the gaps in + * the next interval's scheduling sequence. + * + * As we move those remaining packets to be scheduled with interval 2 packets, + * we have to double the number of remaining packets to transmit. This is + * because the intervals are actually powers of 2, and we would be transmitting + * the previous interval's packets twice in this interval. We also have to be + * sure that when we look at the largest max packet size for this interval, we + * also look at the largest max packet size for the remaining packets and take + * the greater of the two. + * + * The algorithm continues to evenly distribute packets in each scheduling + * opportunity, and push the remaining packets out, until we get to the last + * interval. Then those packets and their associated overhead are just added + * to the bandwidth used. + */ +static int xhci_check_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + unsigned int bw_reserved; + unsigned int max_bandwidth; + unsigned int bw_used; + unsigned int block_size; + struct xhci_interval_bw_table *bw_table; + unsigned int packet_size = 0; + unsigned int overhead = 0; + unsigned int packets_transmitted = 0; + unsigned int packets_remaining = 0; + unsigned int i; + + if (virt_dev->udev->speed == USB_SPEED_SUPER) + return xhci_check_ss_bw(xhci, virt_dev); + + if (virt_dev->udev->speed == USB_SPEED_HIGH) { + max_bandwidth = HS_BW_LIMIT; + /* Convert percent of bus BW reserved to blocks reserved */ + bw_reserved = DIV_ROUND_UP(HS_BW_RESERVED * max_bandwidth, 100); + } else { + max_bandwidth = FS_BW_LIMIT; + bw_reserved = DIV_ROUND_UP(FS_BW_RESERVED * max_bandwidth, 100); + } + + bw_table = virt_dev->bw_table; + /* We need to translate the max packet size and max ESIT payloads into + * the units the hardware uses. + */ + block_size = xhci_get_block_size(virt_dev->udev); + + /* If we are manipulating a LS/FS device under a HS hub, double check + * that the HS bus has enough bandwidth if we are activing a new TT. + */ + if (virt_dev->tt_info) { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) { + xhci_warn(xhci, "Not enough bandwidth on HS bus for " + "newly activated TT.\n"); + return -ENOMEM; + } + xhci_dbg(xhci, "Recalculating BW for TT slot %u port %u\n", + virt_dev->tt_info->slot_id, + virt_dev->tt_info->ttport); + } else { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + } + + /* Add in how much bandwidth will be used for interval zero, or the + * rounded max ESIT payload + number of packets * largest overhead. + */ + bw_used = DIV_ROUND_UP(bw_table->interval0_esit_payload, block_size) + + bw_table->interval_bw[0].num_packets * + xhci_get_largest_overhead(&bw_table->interval_bw[0]); + + for (i = 1; i < XHCI_MAX_INTERVAL; i++) { + unsigned int bw_added; + unsigned int largest_mps; + unsigned int interval_overhead; + + /* + * How many packets could we transmit in this interval? + * If packets didn't fit in the previous interval, we will need + * to transmit that many packets twice within this interval. + */ + packets_remaining = 2 * packets_remaining + + bw_table->interval_bw[i].num_packets; + + /* Find the largest max packet size of this or the previous + * interval. + */ + if (list_empty(&bw_table->interval_bw[i].endpoints)) + largest_mps = 0; + else { + struct xhci_virt_ep *virt_ep; + struct list_head *ep_entry; + + ep_entry = bw_table->interval_bw[i].endpoints.next; + virt_ep = list_entry(ep_entry, + struct xhci_virt_ep, bw_endpoint_list); + /* Convert to blocks, rounding up */ + largest_mps = DIV_ROUND_UP( + virt_ep->bw_info.max_packet_size, + block_size); + } + if (largest_mps > packet_size) + packet_size = largest_mps; + + /* Use the larger overhead of this or the previous interval. */ + interval_overhead = xhci_get_largest_overhead( + &bw_table->interval_bw[i]); + if (interval_overhead > overhead) + overhead = interval_overhead; + + /* How many packets can we evenly distribute across + * (1 << (i + 1)) possible scheduling opportunities? + */ + packets_transmitted = packets_remaining >> (i + 1); + + /* Add in the bandwidth used for those scheduled packets */ + bw_added = packets_transmitted * (overhead + packet_size); + + /* How many packets do we have remaining to transmit? */ + packets_remaining = packets_remaining % (1 << (i + 1)); + + /* What largest max packet size should those packets have? */ + /* If we've transmitted all packets, don't carry over the + * largest packet size. + */ + if (packets_remaining == 0) { + packet_size = 0; + overhead = 0; + } else if (packets_transmitted > 0) { + /* Otherwise if we do have remaining packets, and we've + * scheduled some packets in this interval, take the + * largest max packet size from endpoints with this + * interval. + */ + packet_size = largest_mps; + overhead = interval_overhead; + } + /* Otherwise carry over packet_size and overhead from the last + * time we had a remainder. + */ + bw_used += bw_added; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. " + "Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + } + /* + * Ok, we know we have some packets left over after even-handedly + * scheduling interval 15. We don't know which microframes they will + * fit into, so we over-schedule and say they will be scheduled every + * microframe. + */ + if (packets_remaining > 0) + bw_used += overhead + packet_size; + + if (!virt_dev->tt_info && virt_dev->udev->speed == USB_SPEED_HIGH) { + unsigned int port_index = virt_dev->real_port - 1; + + /* OK, we're manipulating a HS device attached to a + * root port bandwidth domain. Include the number of active TTs + * in the bandwidth used. + */ + bw_used += TT_HS_OVERHEAD * + xhci->rh_bw[port_index].num_active_tts; + } + + xhci_dbg(xhci, "Final bandwidth: %u, Limit: %u, Reserved: %u, " + "Available: %u " "percent\n", + bw_used, max_bandwidth, bw_reserved, + (max_bandwidth - bw_used - bw_reserved) * 100 / + max_bandwidth); + + bw_used += bw_reserved; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + + bw_table->bw_used = bw_used; + return 0; +} + +static bool xhci_is_async_ep(unsigned int ep_type) +{ + return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP); +} + +static bool xhci_is_sync_in_ep(unsigned int ep_type) +{ + return (ep_type == ISOC_IN_EP || ep_type != INT_IN_EP); +} + +static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw) +{ + unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK); + + if (ep_bw->ep_interval == 0) + return SS_OVERHEAD_BURST + + (ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps)); + return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps + SS_OVERHEAD_BURST), + 1 << ep_bw->ep_interval); + +} + +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type)) + return; + + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in -= + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out -= + xhci_get_ss_bw_consumed(ep_bw); + return; + } + + /* SuperSpeed endpoints never get added to intervals in the table, so + * this check is only valid for HS/FS/LS devices. + */ + if (list_empty(&virt_ep->bw_endpoint_list)) + return; + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload -= ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets -= ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + if (tt_info) + tt_info->active_eps -= 1; + list_del_init(&virt_ep->bw_endpoint_list); +} + +static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + struct xhci_virt_ep *smaller_ep; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type)) + return; + + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in += + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out += + xhci_get_ss_bw_consumed(ep_bw); + return; + } + + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload += ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets += ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + + if (tt_info) + tt_info->active_eps += 1; + /* Insert the endpoint into the list, largest max packet size first. */ + list_for_each_entry(smaller_ep, &interval_bw->endpoints, + bw_endpoint_list) { + if (ep_bw->max_packet_size >= + smaller_ep->bw_info.max_packet_size) { + /* Add the new ep before the smaller endpoint */ + list_add_tail(&virt_ep->bw_endpoint_list, + &smaller_ep->bw_endpoint_list); + return; + } + } + /* Add the new endpoint at the end of the list. */ + list_add_tail(&virt_ep->bw_endpoint_list, + &interval_bw->endpoints); +} + +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_root_port_bw_info *rh_bw_info; + if (!virt_dev->tt_info) + return; + + rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1]; + if (old_active_eps == 0 && + virt_dev->tt_info->active_eps != 0) { + rh_bw_info->num_active_tts += 1; + rh_bw_info->bw_table.bw_used += TT_HS_OVERHEAD; + } else if (old_active_eps != 0 && + virt_dev->tt_info->active_eps == 0) { + rh_bw_info->num_active_tts -= 1; + rh_bw_info->bw_table.bw_used -= TT_HS_OVERHEAD; + } +} + +static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct xhci_container_ctx *in_ctx) +{ + struct xhci_bw_info ep_bw_info[31]; + int i; + struct xhci_input_control_ctx *ctrl_ctx; + int old_active_eps = 0; + + if (virt_dev->tt_info) + old_active_eps = virt_dev->tt_info->active_eps; + + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); + + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Make a copy of the BW info in case we need to revert this */ + memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info, + sizeof(ep_bw_info[i])); + /* Drop the endpoint from the interval table if the endpoint is + * being dropped or changed. + */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Overwrite the information stored in the endpoints' bw_info */ + xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev); + for (i = 0; i < 31; i++) { + /* Add any changed or added endpoints to the interval table */ + if (EP_IS_ADDED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + + if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) { + /* Ok, this fits in the bandwidth we have. + * Update the number of active TTs. + */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + return 0; + } + + /* We don't have enough bandwidth for this, revert the stored info. */ + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Drop the new copies of any added or changed endpoints from + * the interval table. + */ + if (EP_IS_ADDED(ctrl_ctx, i)) { + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Revert the endpoint back to its old information */ + memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i], + sizeof(ep_bw_info[i])); + /* Add any changed or dropped endpoints back into the table */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + return -ENOMEM; +} + + /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. */ @@ -1765,17 +2375,30 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); virt_dev = xhci->devs[udev->slot_id]; - if (command) { + + if (command) in_ctx = command->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } + else + in_ctx = virt_dev->in_ctx; + + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && + xhci_reserve_host_resources(xhci, in_ctx)) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough host resources, " + "active endpoint contexts = %u\n", + xhci->num_active_eps); + return -ENOMEM; + } + if ((xhci->quirks & XHCI_SW_BW_CHECKING) && + xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) { + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) + xhci_free_host_resources(xhci, in_ctx); + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough bandwidth\n"); + return -ENOMEM; + } + if (command) { cmd_completion = command->completion; cmd_status = &command->status; command->command_trb = xhci->cmd_ring->enqueue; @@ -1789,15 +2412,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, list_add_tail(&command->cmd_list, &virt_dev->cmd_list); } else { - in_ctx = virt_dev->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } cmd_completion = &virt_dev->cmd_completion; cmd_status = &virt_dev->cmd_status; } @@ -1888,6 +2502,12 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG); ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + + /* Don't issue the command if there's no endpoints to update. */ + if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) && + ctrl_ctx->drop_flags == 0) + return 0; + xhci_dbg(xhci, "New Input Control Context:\n"); slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); xhci_dbg_ctx(xhci, virt_dev->in_ctx, @@ -2525,6 +3145,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) int timeleft; int last_freed_endpoint; struct xhci_slot_ctx *slot_ctx; + int old_active_eps = 0; ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); if (ret <= 0) @@ -2666,7 +3287,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); last_freed_endpoint = i; } - } + if (!list_empty(&virt_dev->eps[i].bw_endpoint_list)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + udev, + &virt_dev->eps[i], + virt_dev->tt_info); + xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info); + } + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); ret = 0; @@ -2704,6 +3336,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } + if (udev->usb2_hw_lpm_enabled) { + xhci_set_usb2_hardware_lpm(hcd, udev, 0); + udev->usb2_hw_lpm_enabled = 0; + } + spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); @@ -2889,7 +3526,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * command on a timeout. */ if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for a slot\n", + xhci_warn(xhci, "%s while waiting for address device command\n", timeleft == 0 ? "Timeout" : "Signal"); /* FIXME cancel the address device command */ return -ETIME; @@ -2957,6 +3594,254 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +#ifdef CONFIG_USB_SUSPEND + +/* BESL to HIRD Encoding array for USB2 LPM */ +static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, + 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; + +/* Calculate HIRD/BESL for USB2 PORTPMSC*/ +static int xhci_calculate_hird_besl(int u2del, bool use_besl) +{ + int hird; + + if (use_besl) { + for (hird = 0; hird < 16; hird++) { + if (xhci_besl_encoding[hird] >= u2del) + break; + } + } else { + if (u2del <= 50) + hird = 0; + else + hird = (u2del - 51) / 75 + 1; + + if (hird > 15) + hird = 15; + } + + return hird; +} + +static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, + struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct dev_info *dev_info; + __le32 __iomem **port_array; + __le32 __iomem *addr, *pm_addr; + u32 temp, dev_id; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + int ret; + + if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + !udev->lpm_capable) + return -EINVAL; + + /* we only support lpm for non-hub device connected to root hub yet */ + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EINVAL; + + spin_lock_irqsave(&xhci->lock, flags); + + /* Look for devices in lpm_failed_devs list */ + dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | + le16_to_cpu(udev->descriptor.idProduct); + list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { + if (dev_info->dev_id == dev_id) { + ret = -EINVAL; + goto finish; + } + } + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + + if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); + ret = -EINVAL; + goto finish; + } + + /* + * Test USB 2.0 software LPM. + * FIXME: some xHCI 1.0 hosts may implement a new register to set up + * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 + * in the June 2011 errata release. + */ + xhci_dbg(xhci, "test port %d software LPM\n", port_num); + /* + * Set L1 Device Slot and HIRD/BESL. + * Check device's USB 2.0 extension descriptor to determine whether + * HIRD or BESL shoule be used. See USB2.0 LPM errata. + */ + pm_addr = port_array[port_num] + 1; + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); + xhci_writel(xhci, temp, pm_addr); + + /* Set port link state to U2(L1) */ + addr = port_array[port_num]; + xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); + + /* wait for ACK */ + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Check L1 Status */ + ret = handshake(xhci, pm_addr, PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); + if (ret != -ETIMEDOUT) { + /* enter L1 successfully */ + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", + port_num, temp); + ret = 0; + } else { + temp = xhci_readl(xhci, pm_addr); + xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", + port_num, temp & PORT_L1S_MASK); + ret = -EINVAL; + } + + /* Resume the port */ + xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); + + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Clear PLC */ + xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); + + /* Check PORTSC to make sure the device is in the right state */ + if (!ret) { + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); + if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || + (temp & PORT_PLS_MASK) != XDEV_U0) { + xhci_dbg(xhci, "port L1 resume fail\n"); + ret = -EINVAL; + } + } + + if (ret) { + /* Insert dev to lpm_failed_devs list */ + xhci_warn(xhci, "device LPM test failed, may disconnect and " + "re-enumerate\n"); + dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); + if (!dev_info) { + ret = -ENOMEM; + goto finish; + } + dev_info->dev_id = dev_id; + INIT_LIST_HEAD(&dev_info->list); + list_add(&dev_info->list, &xhci->lpm_failed_devs); + } else { + xhci_ring_device(xhci, udev->slot_id); + } + +finish: + spin_unlock_irqrestore(&xhci->lock, flags); + return ret; +} + +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + __le32 __iomem *pm_addr; + u32 temp; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + + if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || + !udev->lpm_capable) + return -EPERM; + + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (udev->usb2_hw_lpm_capable != 1) + return -EPERM; + + spin_lock_irqsave(&xhci->lock, flags); + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + pm_addr = port_array[port_num] + 1; + temp = xhci_readl(xhci, pm_addr); + + xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", + enable ? "enable" : "disable", port_num); + + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + if (enable) { + temp &= ~PORT_HIRD_MASK; + temp |= PORT_HIRD(hird) | PORT_RWE; + xhci_writel(xhci, temp, pm_addr); + temp = xhci_readl(xhci, pm_addr); + temp |= PORT_HLE; + xhci_writel(xhci, temp, pm_addr); + } else { + temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + xhci_writel(xhci, temp, pm_addr); + } + + spin_unlock_irqrestore(&xhci->lock, flags); + return 0; +} + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; + + ret = xhci_usb2_software_lpm_test(hcd, udev); + if (!ret) { + xhci_dbg(xhci, "software LPM test succeed\n"); + if (xhci->hw_lpm_support == 1) { + udev->usb2_hw_lpm_capable = 1; + ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); + if (!ret) + udev->usb2_hw_lpm_enabled = 1; + } + } + + return 0; +} + +#else + +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + return 0; +} + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return 0; +} + +#endif /* CONFIG_USB_SUSPEND */ + /* Once a hub descriptor is fetched for a device, we need to update the xHC's * internal data structures for the device. */ @@ -2988,6 +3873,14 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, } spin_lock_irqsave(&xhci->lock, flags); + if (hdev->speed == USB_SPEED_HIGH && + xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) { + xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n"); + xhci_free_command(xhci, config_cmd); + spin_unlock_irqrestore(&xhci->lock, flags); + return -ENOMEM; + } + xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); @@ -3051,22 +3944,108 @@ int xhci_get_frame(struct usb_hcd *hcd) return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3; } +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) +{ + struct xhci_hcd *xhci; + struct device *dev = hcd->self.controller; + int retval; + u32 temp; + + hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; + + if (usb_hcd_is_primary_hcd(hcd)) { + xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); + if (!xhci) + return -ENOMEM; + *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + xhci->main_hcd = hcd; + /* Mark the first roothub as being USB 2.0. + * The xHCI driver will register the USB 3.0 roothub. + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + /* + * USB 2.0 roothub under xHCI has an integrated TT, + * (rate matching hub) as opposed to having an OHCI/UHCI + * companion controller. + */ + hcd->has_tt = 1; + } else { + /* xHCI private pointer was set in xhci_pci_probe for the second + * registered roothub. + */ + xhci = hcd_to_xhci(hcd); + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + return 0; + } + + xhci->cap_regs = hcd->regs; + xhci->op_regs = hcd->regs + + HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); + xhci->run_regs = hcd->regs + + (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + xhci->hci_version = HC_VERSION(xhci->hcc_params); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + xhci_print_registers(xhci); + + get_quirks(dev, xhci); + + /* Make sure the HC is halted. */ + retval = xhci_halt(xhci); + if (retval) + goto error; + + xhci_dbg(xhci, "Resetting HCD\n"); + /* Reset the internal HC memory state and registers. */ + retval = xhci_reset(xhci); + if (retval) + goto error; + xhci_dbg(xhci, "Reset complete\n"); + + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + + xhci_dbg(xhci, "Calling HCD init\n"); + /* Initialize HCD and host controller data structures. */ + retval = xhci_init(hcd); + if (retval) + goto error; + xhci_dbg(xhci, "Called HCD init\n"); + return 0; +error: + kfree(xhci); + return retval; +} + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { -#ifdef CONFIG_PCI - int retval = 0; + int retval; retval = xhci_register_pci(); - if (retval < 0) { printk(KERN_DEBUG "Problem registering PCI driver."); return retval; } -#endif /* * Check the compiler generated sizes of structures that must be laid * out in specific ways for hardware access. @@ -3091,8 +4070,6 @@ module_init(xhci_hcd_init); static void __exit xhci_hcd_cleanup(void) { -#ifdef CONFIG_PCI xhci_unregister_pci(); -#endif } module_exit(xhci_hcd_cleanup); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cae8e23308bf..3c8fbd2772ea 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -272,6 +272,7 @@ struct xhci_op_regs { */ #define PORT_PLS_MASK (0xf << 5) #define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) #define XDEV_U3 (0x3 << 5) #define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ @@ -362,7 +363,13 @@ struct xhci_op_regs { /* Bits 24:31 for port testing */ /* USB2 Protocol PORTSPMSC */ -#define PORT_RWE (1 << 0x3) +#define PORT_L1S_MASK 7 +#define PORT_L1S_SUCCESS 1 +#define PORT_RWE (1 << 3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) /** * struct xhci_intr_reg - Interrupt Register Set @@ -611,11 +618,13 @@ struct xhci_ep_ctx { #define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ #define EP_MULT(p) (((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ #define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) #define EP_MAXPSTREAMS_MASK (0x1f << 10) #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ @@ -640,6 +649,7 @@ struct xhci_ep_ctx { /* bit 6 reserved */ /* bit 7 is Host Initiate Disable - for disabling stream selection */ #define MAX_BURST(p) (((p)&0xff) << 8) +#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) #define MAX_PACKET(p) (((p)&0xffff) << 16) #define MAX_PACKET_MASK (0xffff << 16) #define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) @@ -652,6 +662,7 @@ struct xhci_ep_ctx { /* tx_info bitmasks */ #define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) @@ -670,6 +681,11 @@ struct xhci_input_control_ctx { __le32 rsvd2[6]; }; +#define EP_IS_ADDED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))) +#define EP_IS_DROPPED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) + /* Represents everything that is needed to issue a command on the command ring. * It's useful to pre-allocate these for commands that cannot fail due to * out-of-memory errors, like freeing streams. @@ -731,6 +747,67 @@ struct xhci_stream_info { #define SMALL_STREAM_ARRAY_SIZE 256 #define MEDIUM_STREAM_ARRAY_SIZE 1024 +/* Some Intel xHCI host controllers need software to keep track of the bus + * bandwidth. Keep track of endpoint info here. Each root port is allocated + * the full bus bandwidth. We must also treat TTs (including each port under a + * multi-TT hub) as a separate bandwidth domain. The direct memory interface + * (DMI) also limits the total bandwidth (across all domains) that can be used. + */ +struct xhci_bw_info { + /* ep_interval is zero-based */ + unsigned int ep_interval; + /* mult and num_packets are one-based */ + unsigned int mult; + unsigned int num_packets; + unsigned int max_packet_size; + unsigned int max_esit_payload; + unsigned int type; +}; + +/* "Block" sizes in bytes the hardware uses for different device speeds. + * The logic in this part of the hardware limits the number of bits the hardware + * can use, so must represent bandwidth in a less precise manner to mimic what + * the scheduler hardware computes. + */ +#define FS_BLOCK 1 +#define HS_BLOCK 4 +#define SS_BLOCK 16 +#define DMI_BLOCK 32 + +/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated + * with each byte transferred. SuperSpeed devices have an initial overhead to + * set up bursts. These are in blocks, see above. LS overhead has already been + * translated into FS blocks. + */ +#define DMI_OVERHEAD 8 +#define DMI_OVERHEAD_BURST 4 +#define SS_OVERHEAD 8 +#define SS_OVERHEAD_BURST 32 +#define HS_OVERHEAD 26 +#define FS_OVERHEAD 20 +#define LS_OVERHEAD 128 +/* The TTs need to claim roughly twice as much bandwidth (94 bytes per + * microframe ~= 24Mbps) of the HS bus as the devices can actually use because + * of overhead associated with split transfers crossing microframe boundaries. + * 31 blocks is pure protocol overhead. + */ +#define TT_HS_OVERHEAD (31 + 94) +#define TT_DMI_OVERHEAD (25 + 12) + +/* Bandwidth limits in blocks */ +#define FS_BW_LIMIT 1285 +#define TT_BW_LIMIT 1320 +#define HS_BW_LIMIT 1607 +#define SS_BW_LIMIT_IN 3906 +#define DMI_BW_LIMIT_IN 3906 +#define SS_BW_LIMIT_OUT 3906 +#define DMI_BW_LIMIT_OUT 3906 + +/* Percentage of bus bandwidth reserved for non-periodic transfers */ +#define FS_BW_RESERVED 10 +#define HS_BW_RESERVED 20 +#define SS_BW_RESERVED 10 + struct xhci_virt_ep { struct xhci_ring *ring; /* Related to endpoints that are configured to use stream IDs only */ @@ -772,8 +849,39 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + /* Bandwidth checking storage */ + struct xhci_bw_info bw_info; + struct list_head bw_endpoint_list; +}; + +enum xhci_overhead_type { + LS_OVERHEAD_TYPE = 0, + FS_OVERHEAD_TYPE, + HS_OVERHEAD_TYPE, +}; + +struct xhci_interval_bw { + unsigned int num_packets; + /* Sorted by max packet size. + * Head of the list is the greatest max packet size. + */ + struct list_head endpoints; + /* How many endpoints of each speed are present. */ + unsigned int overhead[3]; +}; + +#define XHCI_MAX_INTERVAL 16 + +struct xhci_interval_bw_table { + unsigned int interval0_esit_payload; + struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL]; + /* Includes reserved bandwidth for async endpoints */ + unsigned int bw_used; + unsigned int ss_bw_in; + unsigned int ss_bw_out; }; + struct xhci_virt_device { struct usb_device *udev; /* @@ -798,7 +906,32 @@ struct xhci_virt_device { /* Status of the last command issued for this device */ u32 cmd_status; struct list_head cmd_list; - u8 port; + u8 fake_port; + u8 real_port; + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; +}; + +/* + * For each roothub, keep track of the bandwidth information for each periodic + * interval. + * + * If a high speed hub is attached to the roothub, each TT associated with that + * hub is a separate bandwidth domain. The interval information for the + * endpoints on the devices under that TT will appear in the TT structure. + */ +struct xhci_root_port_bw_info { + struct list_head tts; + unsigned int num_active_tts; + struct xhci_interval_bw_table bw_table; +}; + +struct xhci_tt_bw_info { + struct list_head tt_list; + int slot_id; + int ttport; + struct xhci_interval_bw_table bw_table; + int active_eps; }; @@ -1198,6 +1331,12 @@ struct s3_save { u64 erst_dequeue; }; +/* Use for lpm */ +struct dev_info { + u32 dev_id; + struct list_head list; +}; + struct xhci_bus_state { unsigned long bus_suspended; unsigned long next_statechange; @@ -1261,12 +1400,16 @@ struct xhci_hcd { struct xhci_erst erst; /* Scratchpad */ struct xhci_scratchpad *scratchpad; + /* Store LPM test failed devices' information */ + struct list_head lpm_failed_devs; /* slot enabling and address device helpers */ struct completion addr_dev; int slot_id; /* Internal mirror of the HW's dcbaa */ struct xhci_virt_device *devs[MAX_HC_SLOTS]; + /* For keeping track of bandwidth domains per roothub. */ + struct xhci_root_port_bw_info *rh_bw; /* DMA pools */ struct dma_pool *device_pool; @@ -1318,6 +1461,8 @@ struct xhci_hcd { #define XHCI_EP_LIMIT_QUIRK (1 << 5) #define XHCI_BROKEN_MSI (1 << 6) #define XHCI_RESET_ON_RESUME (1 << 7) +#define XHCI_SW_BW_CHECKING (1 << 8) +#define XHCI_AMD_0x96_HOST (1 << 9) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1330,6 +1475,10 @@ struct xhci_hcd { /* Array of pointers to USB 2.0 PORTSC registers */ __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; + /* support xHCI 0.96 spec USB2 software LPM */ + unsigned sw_lpm_support:1; + /* support xHCI 1.0 spec USB2 hardware LPM */ + unsigned hw_lpm_support:1; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -1401,9 +1550,7 @@ static inline void xhci_write_64(struct xhci_hcd *xhci, static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) { - u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - return ((HC_VERSION(temp) == 0x95) && - (xhci->quirks & XHCI_LINK_TRB_QUIRK)); + return xhci->quirks & XHCI_LINK_TRB_QUIRK; } /* xHCI debugging */ @@ -1438,6 +1585,20 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc); unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index); unsigned int xhci_last_valid_endpoint(u32 added_ctxs); void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep); +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info); +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps); +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev); void xhci_endpoint_copy(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_container_ctx *out_ctx, @@ -1483,9 +1644,13 @@ void xhci_free_command(struct xhci_hcd *xhci, /* xHCI PCI glue */ int xhci_register_pci(void); void xhci_unregister_pci(void); +#else +static inline int xhci_register_pci(void) { return 0; } +static inline void xhci_unregister_pci(void) {} #endif /* xHCI host controller glue */ +typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); @@ -1493,6 +1658,7 @@ int xhci_init(struct usb_hcd *hcd); int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci); @@ -1507,6 +1673,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags); @@ -1514,6 +1684,9 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); @@ -1572,6 +1745,10 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); /* xHCI roothub code */ +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state); +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit); int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index a6afd15f6a46..fe858711651c 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -213,7 +213,7 @@ static void adu_interrupt_in_callback(struct urb *urb) if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { if (dev->read_buffer_length < - (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) - + (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) - (urb->actual_length)) { memcpy (dev->read_buffer_primary + dev->read_buffer_length, @@ -315,7 +315,7 @@ static int adu_open(struct inode *inode, struct file *file) usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); dev->read_urb_finished = 0; @@ -483,7 +483,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -536,7 +536,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -622,7 +622,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, dbg(4," %s : sending, count = %Zd", __func__, count); /* write the data into interrupt_out_buffer from userspace */ - buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); bytes_to_write = count > buffer_size ? buffer_size : count; dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); @@ -752,8 +752,8 @@ static int adu_probe(struct usb_interface *interface, goto error; } - in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); - out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); + out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); if (!dev->read_buffer_primary) { diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 2f41089cd854..2dbe600fbc11 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2777,7 +2777,7 @@ static int ftdi_elan_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (!ftdi->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); ftdi->bulk_in_size = buffer_size; ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress; ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index c6184b4d1695..515b67fffab1 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -359,7 +359,7 @@ static int idmouse_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[0].desc; if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->orig_bi_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = 0x200; /* works _much_ faster */ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index a2190b983f52..81457904d6ba 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -803,7 +803,7 @@ static int iowarrior_probe(struct usb_interface *interface, dev->int_out_endpoint = endpoint; } /* we have to check the report_size often, so remember it in the endianess suitable for our machine */ - dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize); + dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56)) /* IOWarrior56 has wMaxPacketSize different from report size */ diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index cb4096201e29..48c166f0d764 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -721,7 +721,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * if (dev->interrupt_out_endpoint == NULL) dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n"); - dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); + dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL); if (!dev->ring_buffer) { dev_err(&intf->dev, "Couldn't allocate ring_buffer\n"); @@ -737,7 +737,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); goto error; } - dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) : + dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) : udev->descriptor.bMaxPacketSize0; dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6482c6e2e6bd..a989356f693e 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -409,7 +409,7 @@ static int tower_open (struct inode *inode, struct file *file) dev->udev, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), tower_interrupt_in_callback, dev, dev->interrupt_in_interval); @@ -928,7 +928,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device err("Couldn't allocate read_buffer"); goto error; } - dev->interrupt_in_buffer = kmalloc (le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL); if (!dev->interrupt_in_buffer) { err("Couldn't allocate interrupt_in_buffer"); goto error; diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 51648154bb44..1871cdf10da3 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -18,7 +18,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/usb.h> #define DRIVER_VERSION "USBLCD Driver Version 1.05" @@ -34,22 +34,29 @@ static const struct usb_device_id id_table[] = { { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static DEFINE_MUTEX(open_disc_mutex); struct usb_lcd { - struct usb_device * udev; /* init: probe_lcd */ - struct usb_interface * interface; /* the interface for this device */ - unsigned char * bulk_in_buffer; /* the buffer to receive data */ - size_t bulk_in_size; /* the size of the receive buffer */ - __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ - __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + struct usb_device *udev; /* init: probe_lcd */ + struct usb_interface *interface; /* the interface for + this device */ + unsigned char *bulk_in_buffer; /* the buffer to receive + data */ + size_t bulk_in_size; /* the size of the + receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the + bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the + bulk out endpoint */ struct kref kref; - struct semaphore limit_sem; /* to stop writes at full throttle from - * using up all RAM */ - struct usb_anchor submitted; /* URBs to wait for before suspend */ + struct semaphore limit_sem; /* to stop writes at + full throttle from + using up all RAM */ + struct usb_anchor submitted; /* URBs to wait for + before suspend */ }; #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) @@ -63,8 +70,8 @@ static void lcd_delete(struct kref *kref) struct usb_lcd *dev = to_lcd_dev(kref); usb_put_dev(dev->udev); - kfree (dev->bulk_in_buffer); - kfree (dev); + kfree(dev->bulk_in_buffer); + kfree(dev); } @@ -80,7 +87,7 @@ static int lcd_open(struct inode *inode, struct file *file) interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { mutex_unlock(&lcd_mutex); - err ("USBLCD: %s - error, can't find device for minor %d", + err("USBLCD: %s - error, can't find device for minor %d", __func__, subminor); return -ENODEV; } @@ -126,7 +133,8 @@ static int lcd_release(struct inode *inode, struct file *file) return 0; } -static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t lcd_read(struct file *file, char __user * buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; int retval = 0; @@ -135,8 +143,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l dev = file->private_data; /* do a blocking bulk read to get data from the device */ - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &bytes_read, 10000); @@ -161,23 +170,23 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev = file->private_data; if (dev == NULL) return -ENODEV; - + switch (cmd) { case IOCTL_GET_HARD_VERSION: mutex_lock(&lcd_mutex); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); - sprintf(buf,"%1d%1d.%1d%1d", + sprintf(buf, "%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); mutex_unlock(&lcd_mutex); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; case IOCTL_GET_DRV_VERSION: - sprintf(buf,DRIVER_VERSION); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + sprintf(buf, DRIVER_VERSION); + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; default: @@ -199,7 +208,7 @@ static void lcd_write_bulk_callback(struct urb *urb) if (status && !(status == -ENOENT || status == -ECONNRESET || - status == -ESHUTDOWN)) { + status == -ESHUTDOWN)) { dbg("USBLCD: %s - nonzero write bulk status received: %d", __func__, status); } @@ -210,15 +219,16 @@ static void lcd_write_bulk_callback(struct urb *urb) up(&dev->limit_sem); } -static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos) +static ssize_t lcd_write(struct file *file, const char __user * user_buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; - int retval = 0, r; + int retval = 0, r; struct urb *urb = NULL; char *buf = NULL; - + dev = file->private_data; - + /* verify that we actually have some data to write */ if (count == 0) goto exit; @@ -233,34 +243,38 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz retval = -ENOMEM; goto err_no_buf; } - - buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + + buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, + &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } - + if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } - + /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, - usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + usb_sndbulkpipe(dev->udev, + dev->bulk_out_endpointAddr), buf, count, lcd_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); - + /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { - err("USBLCD: %s - failed submitting write urb, error %d", __func__, retval); + err("USBLCD: %s - failed submitting write urb, error %d", + __func__, retval); goto error_unanchor; } - - /* release our reference to this urb, the USB core will eventually free it entirely */ + + /* release our reference to this urb, + the USB core will eventually free it entirely */ usb_free_urb(urb); exit: @@ -276,13 +290,13 @@ err_no_buf: } static const struct file_operations lcd_fops = { - .owner = THIS_MODULE, - .read = lcd_read, - .write = lcd_write, - .open = lcd_open, + .owner = THIS_MODULE, + .read = lcd_read, + .write = lcd_write, + .open = lcd_open, .unlocked_ioctl = lcd_ioctl, - .release = lcd_release, - .llseek = noop_llseek, + .release = lcd_release, + .llseek = noop_llseek, }; /* @@ -290,12 +304,13 @@ static const struct file_operations lcd_fops = { * and to have the device registered with the driver core */ static struct usb_class_driver lcd_class = { - .name = "lcd%d", - .fops = &lcd_fops, - .minor_base = USBLCD_MINOR, + .name = "lcd%d", + .fops = &lcd_fops, + .minor_base = USBLCD_MINOR, }; -static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int lcd_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_lcd *dev = NULL; struct usb_host_interface *iface_desc; @@ -322,7 +337,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id retval = -ENODEV; goto error; } - + /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; @@ -332,7 +347,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -369,7 +384,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, - (i & 0xF0)>>4,(i & 0xF), dev->udev->devnum); + (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum); /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", @@ -401,7 +416,7 @@ static int lcd_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int lcd_resume (struct usb_interface *intf) +static int lcd_resume(struct usb_interface *intf) { return 0; } @@ -409,16 +424,16 @@ static int lcd_resume (struct usb_interface *intf) static void lcd_disconnect(struct usb_interface *interface) { struct usb_lcd *dev; - int minor = interface->minor; + int minor = interface->minor; mutex_lock(&open_disc_mutex); - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); mutex_unlock(&open_disc_mutex); - /* give back our minor */ - usb_deregister_dev(interface, &lcd_class); - + /* give back our minor */ + usb_deregister_dev(interface, &lcd_class); + /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); @@ -438,7 +453,7 @@ static struct usb_driver lcd_driver = { static int __init usb_lcd_init(void) { int result; - + result = usb_register(&lcd_driver); if (result) err("usb_register failed. Error number %d", result); diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 1616ad1793a4..43f84e50d514 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -33,10 +33,10 @@ static const struct usb_device_id id_table[] = { .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); struct usb_led { - struct usb_device * udev; + struct usb_device *udev; unsigned char blue; unsigned char red; unsigned char green; @@ -113,14 +113,16 @@ static void change_color(struct usb_led *led) } #define show_set(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\ + char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ \ return sprintf(buf, "%d\n", led->value); \ } \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\ + const char *buf, size_t count) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ @@ -135,7 +137,8 @@ show_set(blue); show_set(red); show_set(green); -static int led_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int led_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct usb_led *dev = NULL; @@ -150,7 +153,7 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id dev->udev = usb_get_dev(udev); dev->type = id->driver_info; - usb_set_intfdata (interface, dev); + usb_set_intfdata(interface, dev); retval = device_create_file(&interface->dev, &dev_attr_blue); if (retval) @@ -194,7 +197,7 @@ error: device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); error_mem: @@ -205,14 +208,14 @@ static void led_disconnect(struct usb_interface *interface) { struct usb_led *dev; - dev = usb_get_intfdata (interface); + dev = usb_get_intfdata(interface); device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); /* first remove the files, then set the pointer to NULL */ - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); @@ -243,8 +246,8 @@ static void __exit usb_led_exit(void) usb_deregister(&led_driver); } -module_init (usb_led_init); -module_exit (usb_led_exit); +module_init(usb_led_init); +module_exit(usb_led_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index bb10846affc3..bd6d00802eab 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -359,8 +359,10 @@ static int simple_io( urb->context = &completion; while (retval == 0 && iterations-- > 0) { init_completion(&completion); - if (usb_pipeout(urb->pipe)) + if (usb_pipeout(urb->pipe)) { simple_fill_buf(urb); + urb->transfer_flags |= URB_ZERO_PACKET; + } retval = usb_submit_urb(urb, GFP_KERNEL); if (retval != 0) break; @@ -1583,8 +1585,8 @@ static struct urb *iso_alloc_urb( if (bytes < 0 || !desc) return NULL; - maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); - maxp *= 1 + (0x3 & (le16_to_cpu(desc->wMaxPacketSize) >> 11)); + maxp = 0x7ff & usb_endpoint_maxp(desc); + maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)); packets = DIV_ROUND_UP(bytes, maxp); urb = usb_alloc_urb(packets, GFP_KERNEL); @@ -1654,7 +1656,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, "... iso period %d %sframes, wMaxPacket %04x\n", 1 << (desc->bInterval - 1), (udev->speed == USB_SPEED_HIGH) ? "micro" : "", - le16_to_cpu(desc->wMaxPacketSize)); + usb_endpoint_maxp(desc)); for (i = 0; i < param->sglen; i++) { urbs[i] = iso_alloc_urb(udev, pipe, desc, @@ -2298,25 +2300,8 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, dev); dev_info(&intf->dev, "%s\n", info->name); - dev_info(&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n", - ({ char *tmp; - switch (udev->speed) { - case USB_SPEED_LOW: - tmp = "low"; - break; - case USB_SPEED_FULL: - tmp = "full"; - break; - case USB_SPEED_HIGH: - tmp = "high"; - break; - case USB_SPEED_SUPER: - tmp = "super"; - break; - default: - tmp = "unknown"; - break; - }; tmp; }), + dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n", + usb_speed_string(udev->speed), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index a09dbd243eb3..a04b2ff9dd83 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1101,7 +1101,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg nevents = mon_bin_queued(rp); sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) + if (put_user(ndropped, &sp->dropped)) return -EFAULT; if (put_user(nevents, &sp->queued)) return -EFAULT; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index e81820370d6f..ae4a20acef6c 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -634,6 +634,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) u16 len; u16 csr = musb_readw(epio, MUSB_RXCSR); struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + u8 use_mode_1; if (hw_ep->is_shared_fifo) musb_ep = &hw_ep->ep_in; @@ -683,6 +684,18 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (csr & MUSB_RXCSR_RXPKTRDY) { len = musb_readw(epio, MUSB_RXCOUNT); + + /* + * Enable Mode 1 on RX transfers only when short_not_ok flag + * is set. Currently short_not_ok flag is set only from + * file_storage and f_mass_storage drivers + */ + + if (request->short_not_ok && len == musb_ep->packet_sz) + use_mode_1 = 1; + else + use_mode_1 = 0; + if (request->actual < request->length) { #ifdef CONFIG_USB_INVENTRA_DMA if (is_buffer_mapped(req)) { @@ -704,7 +717,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) * most these gadgets, end of is signified either by a short packet, * or filling the last byte of the buffer. (Sending extra data in * that last pckate should trigger an overflow fault.) But in mode 1, - * we don't get DMA completion interrrupt for short packets. + * we don't get DMA completion interrupt for short packets. * * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1), * to get endpoint interrupt on every DMA req, but that didn't seem @@ -714,37 +727,41 @@ static void rxstate(struct musb *musb, struct musb_request *req) * then becomes usable as a runtime "use mode 1" hint... */ - csr |= MUSB_RXCSR_DMAENAB; -#ifdef USE_MODE1 - csr |= MUSB_RXCSR_AUTOCLEAR; - /* csr |= MUSB_RXCSR_DMAMODE; */ - - /* this special sequence (enabling and then - * disabling MUSB_RXCSR_DMAMODE) is required - * to get DMAReq to activate - */ - musb_writew(epio, MUSB_RXCSR, - csr | MUSB_RXCSR_DMAMODE); -#else - if (!musb_ep->hb_mult && - musb_ep->hw_ep->rx_double_buffered) + /* Experimental: Mode1 works with mass storage use cases */ + if (use_mode_1) { csr |= MUSB_RXCSR_AUTOCLEAR; -#endif - musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + /* + * this special sequence (enabling and then + * disabling MUSB_RXCSR_DMAMODE) is required + * to get DMAReq to activate + */ + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + + } else { + if (!musb_ep->hb_mult && + musb_ep->hw_ep->rx_double_buffered) + csr |= MUSB_RXCSR_AUTOCLEAR; + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + } if (request->actual < request->length) { int transfer_size = 0; -#ifdef USE_MODE1 - transfer_size = min(request->length - request->actual, - channel->max_len); -#else - transfer_size = min(request->length - request->actual, - (unsigned)len); -#endif - if (transfer_size <= musb_ep->packet_sz) - musb_ep->dma->desired_mode = 0; - else + if (use_mode_1) { + transfer_size = min(request->length - request->actual, + channel->max_len); musb_ep->dma->desired_mode = 1; + } else { + transfer_size = min(request->length - request->actual, + (unsigned)len); + musb_ep->dma->desired_mode = 0; + } use_dma = c->channel_program( channel, @@ -1020,7 +1037,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; /* REVISIT this rules out high bandwidth periodic transfers */ - tmp = le16_to_cpu(desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(desc); if (tmp & ~0x07ff) { int ok; diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 9378b359c1f0..6a0d0467ec74 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -679,6 +679,14 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) musb_readb(mbase, MUSB_FADDR), decode_ep0stage(musb->ep0_state)); + if (csr & MUSB_CSR0_P_DATAEND) { + /* + * If DATAEND is set we should not call the callback, + * hence the status stage is not complete. + */ + return IRQ_HANDLED; + } + /* I sent a stall.. need to acknowledge it now.. */ if (csr & MUSB_CSR0_P_SENTSTALL) { musb_writew(regs, MUSB_CSR0, diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8b2473fa0f47..60ddba8066ea 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1932,7 +1932,7 @@ static int musb_urb_enqueue( INIT_LIST_HEAD(&qh->ring); qh->is_ready = 1; - qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize); + qh->maxpacket = usb_endpoint_maxp(epd); qh->type = usb_endpoint_type(epd); /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier. diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index f70c5a577736..57a608584e16 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -408,7 +408,7 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; - if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, + if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index ca9b690a7e40..8c86787c2f09 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -899,7 +899,7 @@ static int otg_bind(struct isp1301 *isp) if (otg_dev) status = request_irq(otg_dev->resource[1].start, omap_otg_irq, - IRQF_DISABLED, DRIVER_NAME, isp); + 0, DRIVER_NAME, isp); else status = -ENODEV; diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index b4d2c0972b3d..ed2b26cfe814 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -137,22 +137,6 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) return ret; } -/*-------------------------------------------------------------------------*/ -static int twl6030_set_phy_clk(struct otg_transceiver *x, int on) -{ - struct twl6030_usb *twl; - struct device *dev; - struct twl4030_usb_data *pdata; - - twl = xceiv_to_twl(x); - dev = twl->dev; - pdata = dev->platform_data; - - pdata->phy_set_clock(twl->dev, on); - - return 0; -} - static int twl6030_phy_init(struct otg_transceiver *x) { struct twl6030_usb *twl; diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 286cbf1ca7de..6f4afa436381 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -4,7 +4,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' - depends on SUPERH || ARCH_SHMOBILE + depends on USB && USB_GADGET default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index ce08345fa15a..bc8aef4311a1 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -6,4 +6,10 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o renesas_usbhs-y := common.o mod.o pipe.o fifo.o -renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o +ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) + renesas_usbhs-y += mod_host.o +endif + +ifneq ($(CONFIG_USB_RENESAS_USBHS_UDC),) + renesas_usbhs-y += mod_gadget.o +endif diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d8239e5efa66..d2e2efaba658 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -61,8 +61,8 @@ */ #define usbhs_platform_call(priv, func, args...)\ (!(priv) ? -ENODEV : \ - !((priv)->pfunc->func) ? 0 : \ - (priv)->pfunc->func(args)) + !((priv)->pfunc.func) ? 0 : \ + (priv)->pfunc.func(args)) /* * common functions @@ -114,6 +114,10 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) { u16 mask = DCFM | DRPD | DPRPU; u16 val = DCFM | DRPD; + int has_otg = usbhs_get_dparam(priv, has_otg); + + if (has_otg) + usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN)); /* * if enable @@ -147,19 +151,133 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) } /* + * usb request functions + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + u16 val; + + val = usbhs_read(priv, USBREQ); + req->bRequest = (val >> 8) & 0xFF; + req->bRequestType = (val >> 0) & 0xFF; + + req->wValue = usbhs_read(priv, USBVAL); + req->wIndex = usbhs_read(priv, USBINDX); + req->wLength = usbhs_read(priv, USBLENG); +} + +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); + usbhs_write(priv, USBVAL, req->wValue); + usbhs_write(priv, USBINDX, req->wIndex); + usbhs_write(priv, USBLENG, req->wLength); + + usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); +} + +/* + * bus/vbus functions + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv) +{ + u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT); + + if (status != USBRST) { + struct device *dev = usbhs_priv_to_dev(priv); + dev_err(dev, "usbhs should be reset\n"); + } + + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); +} + +void usbhs_bus_send_reset(struct usbhs_priv *priv) +{ + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); +} + +int usbhs_bus_get_speed(struct usbhs_priv *priv) +{ + u16 dvstctr = usbhs_read(priv, DVSTCTR); + + switch (RHST & dvstctr) { + case RHST_LOW_SPEED: + return USB_SPEED_LOW; + case RHST_FULL_SPEED: + return USB_SPEED_FULL; + case RHST_HIGH_SPEED: + return USB_SPEED_HIGH; + } + + return USB_SPEED_UNKNOWN; +} + +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) +{ + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + return usbhs_platform_call(priv, set_vbus, pdev, enable); +} + +static void usbhsc_bus_init(struct usbhs_priv *priv) +{ + usbhs_write(priv, DVSTCTR, 0); + + usbhs_vbus_ctrl(priv, 0); +} + +/* + * device configuration + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, + u16 upphub, u16 hubport, u16 speed) +{ + struct device *dev = usbhs_priv_to_dev(priv); + u16 usbspd = 0; + u32 reg = DEVADD0 + (2 * devnum); + + if (devnum > 10) { + dev_err(dev, "cannot set speed to unknown device %d\n", devnum); + return -EIO; + } + + if (upphub > 0xA) { + dev_err(dev, "unsupported hub number %d\n", upphub); + return -EIO; + } + + switch (speed) { + case USB_SPEED_LOW: + usbspd = USBSPD_SPEED_LOW; + break; + case USB_SPEED_FULL: + usbspd = USBSPD_SPEED_FULL; + break; + case USB_SPEED_HIGH: + usbspd = USBSPD_SPEED_HIGH; + break; + default: + dev_err(dev, "unsupported speed %d\n", speed); + return -EIO; + } + + usbhs_write(priv, reg, UPPHUB(upphub) | + HUBPORT(hubport)| + USBSPD(usbspd)); + + return 0; +} + +/* * local functions */ -static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable) +static void usbhsc_set_buswait(struct usbhs_priv *priv) { int wait = usbhs_get_dparam(priv, buswait_bwait); - u16 data = 0; - if (enable) { - /* set bus wait if platform have */ - if (wait) - usbhs_bset(priv, BUSWAIT, 0x000F, wait); - } - usbhs_write(priv, DVSTCTR, data); + /* set bus wait if platform have */ + if (wait) + usbhs_bset(priv, BUSWAIT, 0x000F, wait); } /* @@ -191,10 +309,8 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* USB on */ usbhs_sys_clock_ctrl(priv, enable); - usbhsc_bus_ctrl(priv, enable); } else { /* USB off */ - usbhsc_bus_ctrl(priv, enable); usbhs_sys_clock_ctrl(priv, enable); /* disable PM */ @@ -203,13 +319,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) } /* - * notify hotplug + * hotplug */ -static void usbhsc_notify_hotplug(struct work_struct *work) +static void usbhsc_hotplug(struct usbhs_priv *priv) { - struct usbhs_priv *priv = container_of(work, - struct usbhs_priv, - notify_hotplug_work.work); struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; @@ -237,6 +350,10 @@ static void usbhsc_notify_hotplug(struct work_struct *work) if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); + /* bus init */ + usbhsc_set_buswait(priv); + usbhsc_bus_init(priv); + /* module start */ usbhs_mod_call(priv, start, priv); @@ -246,6 +363,9 @@ static void usbhsc_notify_hotplug(struct work_struct *work) /* module stop */ usbhs_mod_call(priv, stop, priv); + /* bus init */ + usbhsc_bus_init(priv); + /* power off */ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); @@ -257,6 +377,17 @@ static void usbhsc_notify_hotplug(struct work_struct *work) } } +/* + * notify hotplug + */ +static void usbhsc_notify_hotplug(struct work_struct *work) +{ + struct usbhs_priv *priv = container_of(work, + struct usbhs_priv, + notify_hotplug_work.work); + usbhsc_hotplug(priv); +} + int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); @@ -315,24 +446,28 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* * care platform info */ - priv->pfunc = &info->platform_callback; - priv->dparam = &info->driver_param; + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, + &info->driver_param, + sizeof(struct renesas_usbhs_driver_param)); /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; /* set default param if platform doesn't have */ - if (!priv->dparam->pipe_type) { - priv->dparam->pipe_type = usbhsc_default_pipe_type; - priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_default_pipe_type; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); } - if (!priv->dparam->pio_dma_border) - priv->dparam->pio_dma_border = 64; /* 64byte */ + if (!priv->dparam.pio_dma_border) + priv->dparam.pio_dma_border = 64; /* 64byte */ /* FIXME */ /* runtime power control ? */ - if (priv->pfunc->get_vbus) + if (priv->pfunc.get_vbus) usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); /* @@ -443,9 +578,60 @@ static int __devexit usbhs_remove(struct platform_device *pdev) return 0; } +static int usbhsc_suspend(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + + if (mod) { + usbhs_mod_call(priv, stop, priv); + usbhs_mod_change(priv, -1); + } + + if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 0); + + return 0; +} + +static int usbhsc_resume(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + usbhs_platform_call(priv, phy_reset, pdev); + + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 1); + + usbhsc_hotplug(priv); + + return 0; +} + +static int usbhsc_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static const struct dev_pm_ops usbhsc_pm_ops = { + .suspend = usbhsc_suspend, + .resume = usbhsc_resume, + .runtime_suspend = usbhsc_runtime_nop, + .runtime_resume = usbhsc_runtime_nop, +}; + static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", + .pm = &usbhsc_pm_ops, }, .probe = usbhs_probe, .remove = __devexit_p(usbhs_remove), diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index b410463a1212..8729da5c3be6 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -90,6 +90,17 @@ struct usbhs_priv; #define PIPE9TRN 0x00BA #define PIPEATRE 0x00BC #define PIPEATRN 0x00BE +#define DEVADD0 0x00D0 /* Device address n configuration */ +#define DEVADD1 0x00D2 +#define DEVADD2 0x00D4 +#define DEVADD3 0x00D6 +#define DEVADD4 0x00D8 +#define DEVADD5 0x00DA +#define DEVADD6 0x00DC +#define DEVADD7 0x00DE +#define DEVADD8 0x00E0 +#define DEVADD9 0x00E2 +#define DEVADDA 0x00E4 /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -102,6 +113,8 @@ struct usbhs_priv; /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ #define PWEN (1 << 9) /* Controls the PWEN pin output state */ +#define USBRST (1 << 6) /* Bus Reset Output */ +#define UACT (1 << 4) /* USB Bus Enable */ #define RHST (0x7) /* Reset Handshake */ #define RHST_LOW_SPEED 1 /* Low-speed connection */ #define RHST_FULL_SPEED 2 /* Full-speed connection */ @@ -159,6 +172,15 @@ struct usbhs_priv; #define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */ #define SEQUENCE_ERROR 6 /* Control transfer sequence error */ +/* INTSTS1 */ +#define OVRCR (1 << 15) /* OVRCR Interrupt Status */ +#define BCHG (1 << 14) /* USB Bus Change Interrupt Status */ +#define DTCH (1 << 12) /* USB Disconnection Detect Interrupt Status */ +#define ATTCH (1 << 11) /* ATTCH Interrupt Status */ +#define EOFERR (1 << 6) /* EOF Error Detect Interrupt Status */ +#define SIGN (1 << 5) /* Setup Transaction Error Interrupt Status */ +#define SACK (1 << 4) /* Setup Transaction ACK Response Interrupt Status */ + /* PIPECFG */ /* DCPCFG */ #define TYPE_NONE (0 << 14) /* Transfer Type */ @@ -183,9 +205,11 @@ struct usbhs_priv; /* PIPEnCTR */ /* DCPCTR */ #define BSTS (1 << 15) /* Buffer Status */ +#define SUREQ (1 << 14) /* Sending SETUP Token */ #define CSSTS (1 << 12) /* CSSTS Status */ -#define SQCLR (1 << 8) /* Toggle Bit Clear */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ +#define SQCLR (1 << 8) /* Toggle Bit Clear */ +#define SQSET (1 << 7) /* Toggle Bit Set */ #define PBUSY (1 << 5) /* Pipe Busy */ #define PID_MASK (0x3) /* Response PID */ #define PID_NAK 0 @@ -202,6 +226,14 @@ struct usbhs_priv; /* FRMNUM */ #define FRNM_MASK (0x7FF) +/* DEVADDn */ +#define UPPHUB(x) (((x) & 0xF) << 11) /* HUB Register */ +#define HUBPORT(x) (((x) & 0x7) << 8) /* HUB Port for Target Device */ +#define USBSPD(x) (((x) & 0x3) << 6) /* Device Transfer Rate */ +#define USBSPD_SPEED_LOW 0x1 +#define USBSPD_SPEED_FULL 0x2 +#define USBSPD_SPEED_HIGH 0x3 + /* * struct */ @@ -210,8 +242,8 @@ struct usbhs_priv { void __iomem *base; unsigned int irq; - struct renesas_usbhs_platform_callback *pfunc; - struct renesas_usbhs_driver_param *dparam; + struct renesas_usbhs_platform_callback pfunc; + struct renesas_usbhs_driver_param dparam; struct delayed_work notify_hotplug_work; struct platform_device *pdev; @@ -258,15 +290,35 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); /* + * usb request + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); + +/* + * bus + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv); +void usbhs_bus_send_reset(struct usbhs_priv *priv); +int usbhs_bus_get_speed(struct usbhs_priv *priv); +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable); + +/* * frame */ int usbhs_frame_get_num(struct usbhs_priv *priv); /* + * device config + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub, + u16 hubport, u16 speed); + +/* * data */ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); -#define usbhs_get_dparam(priv, param) (priv->dparam->param) +#define usbhs_get_dparam(priv, param) (priv->dparam.param) #define usbhs_priv_to_pdev(priv) (priv->pdev) #define usbhs_priv_to_dev(priv) (&priv->pdev->dev) #define usbhs_priv_to_lock(priv) (&priv->lock) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index a34430f55fb7..8da685e796d1 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -54,35 +54,45 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { }; void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; + if (!done) { + dev_err(dev, "no done function\n"); + return; + } + /******************** spin lock ********************/ usbhs_lock(priv, flags); - if (!handler) { + if (!pipe->handler) { dev_err(dev, "no handler function\n"); - handler = &usbhsf_null_handler; + pipe->handler = &usbhsf_null_handler; } list_del_init(&pkt->node); list_add_tail(&pkt->node, &pipe->list); + /* + * each pkt must hold own handler. + * because handler might be changed by its situation. + * dma handler -> pio handler. + */ pkt->pipe = pipe; pkt->buf = buf; - pkt->handler = handler; + pkt->handler = pipe->handler; pkt->length = len; pkt->zero = zero; pkt->actual = 0; + pkt->done = done; usbhs_unlock(priv, flags); /******************** spin unlock ******************/ - - usbhs_pkt_start(pipe); } static void __usbhsf_pkt_del(struct usbhs_pkt *pkt) @@ -118,10 +128,15 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) return pkt; } -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type) +enum { + USBHSF_PKT_PREPARE, + USBHSF_PKT_TRY_RUN, + USBHSF_PKT_DMA_DONE, +}; + +static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct usbhs_pkt *pkt; struct device *dev = usbhs_priv_to_dev(priv); int (*func)(struct usbhs_pkt *pkt, int *is_done); @@ -161,13 +176,18 @@ __usbhs_pkt_handler_end: /******************** spin unlock ******************/ if (is_done) { - info->done(pkt); + pkt->done(priv, pkt); usbhs_pkt_start(pipe); } return ret; } +void usbhs_pkt_start(struct usbhs_pipe *pipe) +{ + usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE); +} + /* * irq enable/disable function */ @@ -276,9 +296,13 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, usbhsf_fifo_is_busy(fifo)) return -EBUSY; - if (usbhs_pipe_is_dcp(pipe)) + if (usbhs_pipe_is_dcp(pipe)) { base |= (1 == write) << 5; /* ISEL */ + if (usbhs_mod_is_host(priv)) + usbhs_dcp_dir_for_host(pipe, write); + } + /* "base" will be used below */ usbhs_write(priv, fifo->sel, base | MBW_32); @@ -297,6 +321,151 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, } /* + * DCP status stage + */ +static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 1); + if (ret < 0) { + dev_err(dev, "%s() faile\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_clear(pipe, fifo); + usbhsf_send_terminator(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_tx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; +} + +static int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) { + dev_err(dev, "%s() fail\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + usbhsf_fifo_clear(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_rx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; + +} + +static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + if (pkt->handler == &usbhs_dcp_status_stage_in_handler) + usbhsf_tx_irq_ctrl(pipe, 0); + else + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->actual = pkt->length; + *is_done = 1; + + return 0; +} + +struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = { + .prepare = usbhs_dcp_dir_switch_to_write, + .try_run = usbhs_dcp_dir_switch_done, +}; + +struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = { + .prepare = usbhs_dcp_dir_switch_to_read, + .try_run = usbhs_dcp_dir_switch_done, +}; + +/* + * DCP data stage (push) + */ +static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + /* + * change handler to PIO push + */ + pkt->handler = &usbhs_fifo_pio_push_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = { + .prepare = usbhsf_dcp_data_stage_try_push, +}; + +/* + * DCP data stage (pop) + */ +static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* + * prepare pop for DCP should + * - change DCP direction, + * - clear fifo + * - DATA1 + */ + usbhs_pipe_disable(pipe); + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_select(pipe, fifo, 0); + usbhsf_fifo_clear(pipe, fifo); + usbhsf_fifo_unselect(pipe, fifo); + + /* + * change handler to PIO pop + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = { + .prepare = usbhsf_dcp_data_stage_prepare_pop, +}; + +/* * PIO push handler */ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) @@ -452,6 +621,20 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) total_len = len; /* + * update actual length first here to decide disable pipe. + * if this pipe keeps BUF status and all data were popped, + * then, next interrupt/token will be issued again + */ + pkt->actual += total_len; + + if ((pkt->actual == pkt->length) || /* receive all data */ + (total_len < maxp)) { /* short packet */ + *is_done = 1; + usbhsf_rx_irq_ctrl(pipe, 0); + usbhs_pipe_disable(pipe); /* disable pipe first */ + } + + /* * Buffer clear if Zero-Length packet * * see @@ -481,16 +664,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; } - pkt->actual += total_len; - usbhs_fifo_read_end: - if ((pkt->actual == pkt->length) || /* receive all data */ - (total_len < maxp)) { /* short packet */ - *is_done = 1; - usbhsf_rx_irq_ctrl(pipe, 0); - usbhs_pipe_disable(pipe); - } - dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", usbhs_pipe_number(pipe), pkt->length, pkt->actual, *is_done, pkt->zero); @@ -646,7 +820,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) if (len % 4) /* 32bit alignment */ goto usbhsf_pio_prepare_push; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_push; /* get enable DMA fifo */ @@ -723,7 +897,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) if (!fifo) goto usbhsf_pio_prepare_pop; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_pop; ret = usbhsf_fifo_select(pipe, fifo, 0); @@ -884,7 +1058,7 @@ static int usbhsf_irq_empty(struct usbhs_priv *priv, if (!(irq_state->bempsts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_empty run_error %d : %d\n", i, ret); } @@ -914,7 +1088,7 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, if (!(irq_state->brdysts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_ready run_error %d : %d\n", i, ret); } @@ -929,7 +1103,7 @@ static void usbhsf_dma_complete(void *arg) struct device *dev = usbhs_priv_to_dev(priv); int ret; - ret = usbhs_pkt_dmadone(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE); if (ret < 0) dev_err(dev, "dma_complete run_error %d : %d\n", usbhs_pipe_number(pipe), ret); diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index ed6d8e56c13c..32a7b246b28d 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -51,6 +51,8 @@ struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; struct usbhs_pkt_handle *handler; + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt); dma_addr_t dma; void *buf; int length; @@ -76,12 +78,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv); /* * packet info */ -enum { - USBHSF_PKT_PREPARE, - USBHSF_PKT_TRY_RUN, - USBHSF_PKT_DMA_DONE, -}; - extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; @@ -89,16 +85,18 @@ extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler; + +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type); - -#define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE) -#define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN) -#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE) +void usbhs_pkt_start(struct usbhs_pipe *pipe); #endif /* RENESAS_USB_FIFO_H */ diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index a577f8f4064c..053f86d70009 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -58,7 +58,7 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); info->irq_vbus = usbhsm_autonomy_irq_vbus; - priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; + priv->pfunc.get_vbus = usbhsm_autonomy_get_vbus; usbhs_irq_callback_update(priv, NULL); } @@ -93,8 +93,9 @@ struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) return ret; } -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod) +int usbhs_mod_is_host(struct usbhs_priv *priv) { + struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); if (!mod) @@ -139,13 +140,17 @@ int usbhs_mod_probe(struct usbhs_priv *priv) /* * install host/gadget driver */ - ret = usbhs_mod_gadget_probe(priv); + ret = usbhs_mod_host_probe(priv); if (ret < 0) return ret; + ret = usbhs_mod_gadget_probe(priv); + if (ret < 0) + goto mod_init_host_err; + /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, - IRQF_DISABLED, dev_name(dev), priv); + 0, dev_name(dev), priv); if (ret) { dev_err(dev, "irq request err\n"); goto mod_init_gadget_err; @@ -155,12 +160,15 @@ int usbhs_mod_probe(struct usbhs_priv *priv) mod_init_gadget_err: usbhs_mod_gadget_remove(priv); +mod_init_host_err: + usbhs_mod_host_remove(priv); return ret; } void usbhs_mod_remove(struct usbhs_priv *priv) { + usbhs_mod_host_remove(priv); usbhs_mod_gadget_remove(priv); free_irq(priv->irq, priv); } @@ -168,20 +176,6 @@ void usbhs_mod_remove(struct usbhs_priv *priv) /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state) -{ - switch (irq_state->dvstctr & RHST) { - case RHST_LOW_SPEED: - return USB_SPEED_LOW; - case RHST_FULL_SPEED: - return USB_SPEED_FULL; - case RHST_HIGH_SPEED: - return USB_SPEED_HIGH; - } - - return USB_SPEED_UNKNOWN; -} - int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) { int state = irq_state->intsts0 & DVSQ_MASK; @@ -221,8 +215,6 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv, state->intsts0 = usbhs_read(priv, INTSTS0); state->intsts1 = usbhs_read(priv, INTSTS1); - state->dvstctr = usbhs_read(priv, DVSTCTR); - /* mask */ if (mod) { state->brdysts = usbhs_read(priv, BRDYSTS); @@ -269,6 +261,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * see also * usbhs_irq_setting_update */ + + /* INTSTS0 */ if (irq_state.intsts0 & VBINT) usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); @@ -284,15 +278,38 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); + + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + return IRQ_HANDLED; } void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) { u16 intenb0 = 0; + u16 intenb1 = 0; struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + /* + * BEMPENB/BRDYENB are picky. + * below method is required + * + * - clear INTSTS0 + * - update BEMPENB/BRDYENB + * - update INTSTS0 + */ usbhs_write(priv, INTENB0, 0); + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -310,6 +327,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= VBSE; if (mod) { + /* + * INTSTS0 + */ if (mod->irq_ctrl_stage) intenb0 |= CTRE; @@ -322,7 +342,26 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) usbhs_write(priv, BRDYENB, mod->irq_brdysts); intenb0 |= BRDYE; } + + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; + + if (mod->irq_attch) + intenb1 |= DTCHE; + + if (mod->irq_sign) + intenb1 |= SIGNE; + + if (mod->irq_sack) + intenb1 |= SACKE; } - usbhs_write(priv, INTENB0, intenb0); + if (intenb0) + usbhs_write(priv, INTENB0, intenb0); + + if (intenb1) + usbhs_write(priv, INTENB1, intenb1); } diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 5c845a28a21c..8ae3733031cd 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -30,7 +30,6 @@ struct usbhs_irq_state { u16 brdysts; u16 nrdysts; u16 bempsts; - u16 dvstctr; }; struct usbhs_mod { @@ -42,26 +41,48 @@ struct usbhs_mod { int (*start)(struct usbhs_priv *priv); int (*stop)(struct usbhs_priv *priv); - /* INTSTS0 :: DVST (DVSQ) */ + /* + * INTSTS0 + */ + + /* DVST (DVSQ) */ int (*irq_dev_state)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: CTRT (CTSQ) */ + /* CTRT (CTSQ) */ int (*irq_ctrl_stage)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: BEMP */ - /* BEMPSTS */ + /* BEMP / BEMPSTS */ int (*irq_empty)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_bempsts; - /* INTSTS0 :: BRDY */ - /* BRDYSTS */ + /* BRDY / BRDYSTS */ int (*irq_ready)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_brdysts; + /* + * INTSTS1 + */ + + /* ATTCHE */ + int (*irq_attch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* DTCHE */ + int (*irq_dtch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SIGN */ + int (*irq_sign)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SACK */ + int (*irq_sack)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + struct usbhs_priv *priv; }; @@ -89,7 +110,7 @@ struct usbhs_mod_info { struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id); struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv); void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id); -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod); +int usbhs_mod_is_host(struct usbhs_priv *priv); int usbhs_mod_change(struct usbhs_priv *priv, int id); int usbhs_mod_probe(struct usbhs_priv *priv); void usbhs_mod_remove(struct usbhs_priv *priv); @@ -99,7 +120,6 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv); /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state); int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state); int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state); @@ -119,9 +139,24 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); }) /* - * gadget control + * host / gadget control */ -#ifdef CONFIG_USB_RENESAS_USBHS_UDC +#if defined(CONFIG_USB_RENESAS_USBHS_HCD) || \ + defined(CONFIG_USB_RENESAS_USBHS_HCD_MODULE) +extern int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv); +extern int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv); +#else +static inline int usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + return 0; +} +static inline void usbhs_mod_host_remove(struct usbhs_priv *priv) +{ +} +#endif + +#if defined(CONFIG_USB_RENESAS_USBHS_UDC) || \ + defined(CONFIG_USB_RENESAS_USBHS_UDC_MODULE) extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv); extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv); #else diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index cb2d451d511e..4cc7ee0babc6 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -39,7 +39,6 @@ struct usbhsg_uep { char ep_name[EP_NAME_SIZE]; struct usbhsg_gpriv *gpriv; - struct usbhs_pkt_handle *handler; }; struct usbhsg_gpriv { @@ -128,25 +127,6 @@ LIST_HEAD(the_controller_link); /* * queue push/pop */ -static void usbhsg_queue_push(struct usbhsg_uep *uep, - struct usbhsg_request *ureq) -{ - struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); - struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); - struct usb_request *req = &ureq->req; - - req->actual = 0; - req->status = -EINPROGRESS; - usbhs_pkt_push(pipe, pkt, uep->handler, - req->buf, req->length, req->zero); - - dev_dbg(dev, "pipe %d : queue push (%d)\n", - usbhs_pipe_number(pipe), - req->length); -} - static void usbhsg_queue_pop(struct usbhsg_uep *uep, struct usbhsg_request *ureq, int status) @@ -161,7 +141,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep, ureq->req.complete(&uep->ep, &ureq->req); } -static void usbhsg_queue_done(struct usbhs_pkt *pkt) +static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); @@ -172,6 +152,26 @@ static void usbhsg_queue_done(struct usbhs_pkt *pkt) usbhsg_queue_pop(uep, ureq, 0); } +static void usbhsg_queue_push(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); + struct usb_request *req = &ureq->req; + + req->actual = 0; + req->status = -EINPROGRESS; + usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, + req->buf, req->length, req->zero); + usbhs_pkt_start(pipe); + + dev_dbg(dev, "pipe %d : queue push (%d)\n", + usbhs_pipe_number(pipe), + req->length); +} + /* * dma map/unmap */ @@ -265,7 +265,7 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) { usbhs_pipe_disable(pipe); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); usbhs_pipe_enable(pipe); } @@ -355,7 +355,7 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv, struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); - gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state); + gpriv->gadget.speed = usbhs_bus_get_speed(priv); dev_dbg(dev, "state = %x : speed : %d\n", usbhs_status_get_device_state(irq_state), @@ -389,13 +389,13 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, switch (stage) { case READ_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_push_handler; + pipe->handler = &usbhs_fifo_pio_push_handler; break; case WRITE_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_pop_handler; + pipe->handler = &usbhs_fifo_pio_pop_handler; break; case NODATA_STATUS_STAGE: - dcp->handler = &usbhs_ctrl_stage_end_handler; + pipe->handler = &usbhs_ctrl_stage_end_handler; break; default: return ret; @@ -479,24 +479,31 @@ static int usbhsg_ep_enable(struct usb_ep *ep, */ if (uep->pipe) { usbhs_pipe_clear(uep->pipe); - usbhs_pipe_clear_sequence(uep->pipe); + usbhs_pipe_sequence_data0(uep->pipe); return 0; } - pipe = usbhs_pipe_malloc(priv, desc); + pipe = usbhs_pipe_malloc(priv, + usb_endpoint_type(desc), + usb_endpoint_dir_in(desc)); if (pipe) { uep->pipe = pipe; pipe->mod_private = uep; + /* set epnum / maxp */ + usbhs_pipe_config_update(pipe, 0, + usb_endpoint_num(desc), + usb_endpoint_maxp(desc)); + /* * usbhs_fifo_dma_push/pop_handler try to * use dmaengine if possible. * It will use pio handler if impossible. */ if (usb_endpoint_dir_in(desc)) - uep->handler = &usbhs_fifo_dma_push_handler; + pipe->handler = &usbhs_fifo_dma_push_handler; else - uep->handler = &usbhs_fifo_dma_pop_handler; + pipe->handler = &usbhs_fifo_dma_pop_handler; ret = 0; } @@ -659,7 +666,6 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv, - usbhsg_queue_done, usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); usbhsg_uep_init(gpriv); @@ -667,6 +673,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) /* dcp init */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; + usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); /* * system config enble @@ -730,10 +737,6 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhsg_pipe_disable(dcp); - if (gpriv->driver && - gpriv->driver->disconnect) - gpriv->driver->disconnect(&gpriv->gadget); - dev_dbg(dev, "stop gadget\n"); return 0; @@ -744,31 +747,19 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) * linux usb function * */ -static int usbhsg_gadget_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int usbhsg_gadget_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; int ret; - if (!bind || - !driver || + if (!driver || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - /* - * find unused controller - */ - usbhsg_for_each_controller(gpriv) { - if (!gpriv->driver) - goto find_unused_controller; - } - return -ENODEV; - -find_unused_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -782,19 +773,8 @@ find_unused_controller: goto add_fail; } - ret = bind(&gpriv->gadget); - if (ret) { - dev_err(dev, "bind to driver %s error %d\n", - driver->driver.name, ret); - goto bind_fail; - } - - dev_dbg(dev, "bind %s\n", driver->driver.name); - return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); -bind_fail: - device_del(&gpriv->gadget.dev); add_fail: gpriv->driver = NULL; gpriv->gadget.dev.driver = NULL; @@ -802,9 +782,10 @@ add_fail: return ret; } -static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) +static int usbhsg_gadget_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; @@ -812,17 +793,6 @@ static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) !driver->unbind) return -EINVAL; - /* - * find controller - */ - usbhsg_for_each_controller(gpriv) { - if (gpriv->driver == driver) - goto find_matching_controller; - } - return -ENODEV; - -find_matching_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -830,12 +800,6 @@ find_matching_controller: device_del(&gpriv->gadget.dev); gpriv->driver = NULL; - if (driver->disconnect) - driver->disconnect(&gpriv->gadget); - - driver->unbind(&gpriv->gadget); - dev_dbg(dev, "unbind %s\n", driver->driver.name); - return 0; } @@ -852,8 +816,8 @@ static int usbhsg_get_frame(struct usb_gadget *gadget) static struct usb_gadget_ops usbhsg_gadget_ops = { .get_frame = usbhsg_get_frame, - .start = usbhsg_gadget_start, - .stop = usbhsg_gadget_stop, + .udc_start = usbhsg_gadget_start, + .udc_stop = usbhsg_gadget_stop, }; static int usbhsg_start(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c new file mode 100644 index 000000000000..1a7208a50afc --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -0,0 +1,1312 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include "common.h" + +/* + *** HARDWARE LIMITATION *** + * + * 1) renesas_usbhs has a limited number of controllable devices. + * it can control only 9 devices in generally. + * see DEVADDn / DCPMAXP / PIPEMAXP. + * + * 2) renesas_usbhs pipe number is limited. + * the pipe will be re-used for each devices. + * so, software should control DATA0/1 sequence of each devices. + */ + + +/* + * image of mod_host + * + * +--------+ + * | udev 0 | --> it is used when set address + * +--------+ + * + * +--------+ pipes are reused for each uep. + * | udev 1 |-+- [uep 0 (dcp) ] --+ pipe will be switched when + * +--------+ | | target device was changed + * +- [uep 1 (bulk)] --|---+ +--------------+ + * | +--------------> | pipe0 (dcp) | + * +- [uep 2 (bulk)] --|---|---+ +--------------+ + * | | | | pipe1 (isoc) | + * +--------+ | | | +--------------+ + * | udev 2 |-+- [uep 0 (dcp) ] --+ +-- |------> | pipe2 (bulk) | + * +--------+ | | | | +--------------+ + * +- [uep 1 (int) ] --|-+ | +------> | pipe3 (bulk) | + * | | | | +--------------+ + * +--------+ | +-|---|------> | pipe4 (int) | + * | udev 3 |-+- [uep 0 (dcp) ] --+ | | +--------------+ + * +--------+ | | | | .... | + * +- [uep 1 (bulk)] ------+ | | .... | + * | | + * +- [uep 2 (bulk)]-----------+ + */ + + +/* + * struct + */ +struct usbhsh_pipe_info { + unsigned int usr_cnt; /* see usbhsh_endpoint_alloc() */ +}; + +struct usbhsh_request { + struct urb *urb; + struct usbhs_pkt pkt; + struct list_head ureq_link; /* see hpriv :: ureq_link_xxx */ +}; + +struct usbhsh_device { + struct usb_device *usbv; + struct list_head ep_list_head; /* list of usbhsh_ep */ +}; + +struct usbhsh_ep { + struct usbhs_pipe *pipe; + struct usbhsh_device *udev; /* attached udev */ + struct list_head ep_list; /* list to usbhsh_device */ + + int maxp; +}; + +#define USBHSH_DEVICE_MAX 10 /* see DEVADDn / DCPMAXP / PIPEMAXP */ +#define USBHSH_PORT_MAX 7 /* see DEVADDn :: HUBPORT */ +struct usbhsh_hpriv { + struct usbhs_mod mod; + struct usbhs_pipe *dcp; + + struct usbhsh_device udev[USBHSH_DEVICE_MAX]; + + struct usbhsh_pipe_info *pipe_info; + int pipe_size; + + u32 port_stat; /* USB_PORT_STAT_xxx */ + + struct completion *done; + + /* see usbhsh_req_alloc/free */ + struct list_head ureq_link_active; + struct list_head ureq_link_free; +}; + + +static const char usbhsh_hcd_name[] = "renesas_usbhs host"; + +/* + * macro + */ +#define usbhsh_priv_to_hpriv(priv) \ + container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod) + +#define __usbhsh_for_each_hpipe(start, pos, h, i) \ + for (i = start, pos = (h)->hpipe + i; \ + i < (h)->hpipe_size; \ + i++, pos = (h)->hpipe + i) + +#define usbhsh_for_each_hpipe(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(1, pos, hpriv, i) + +#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(0, pos, hpriv, i) + +#define __usbhsh_for_each_udev(start, pos, h, i) \ + for (i = start, pos = (h)->udev + i; \ + i < USBHSH_DEVICE_MAX; \ + i++, pos = (h)->udev + i) + +#define usbhsh_for_each_udev(pos, hpriv, i) \ + __usbhsh_for_each_udev(1, pos, hpriv, i) + +#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i) \ + __usbhsh_for_each_udev(0, pos, hpriv, i) + +#define usbhsh_hcd_to_hpriv(h) (struct usbhsh_hpriv *)((h)->hcd_priv) +#define usbhsh_hcd_to_dev(h) ((h)->self.controller) + +#define usbhsh_hpriv_to_priv(h) ((h)->mod.priv) +#define usbhsh_hpriv_to_dcp(h) ((h)->dcp) +#define usbhsh_hpriv_to_hcd(h) \ + container_of((void *)h, struct usb_hcd, hcd_priv) + +#define usbhsh_ep_to_uep(u) ((u)->hcpriv) +#define usbhsh_uep_to_pipe(u) ((u)->pipe) +#define usbhsh_uep_to_udev(u) ((u)->udev) +#define usbhsh_urb_to_ureq(u) ((u)->hcpriv) +#define usbhsh_urb_to_usbv(u) ((u)->dev) + +#define usbhsh_usbv_to_udev(d) dev_get_drvdata(&(d)->dev) + +#define usbhsh_udev_to_usbv(h) ((h)->usbv) + +#define usbhsh_pipe_info(p) ((p)->mod_private) + +#define usbhsh_device_number(h, d) ((int)((d) - (h)->udev)) +#define usbhsh_device_nth(h, d) ((h)->udev + d) +#define usbhsh_device0(h) usbhsh_device_nth(h, 0) + +#define usbhsh_port_stat_init(h) ((h)->port_stat = 0) +#define usbhsh_port_stat_set(h, s) ((h)->port_stat |= (s)) +#define usbhsh_port_stat_clear(h, s) ((h)->port_stat &= ~(s)) +#define usbhsh_port_stat_get(h) ((h)->port_stat) + +#define usbhsh_pkt_to_req(p) \ + container_of((void *)p, struct usbhsh_request, pkt) + +/* + * req alloc/free + */ +static void usbhsh_req_list_init(struct usbhsh_hpriv *hpriv) +{ + INIT_LIST_HEAD(&hpriv->ureq_link_active); + INIT_LIST_HEAD(&hpriv->ureq_link_free); +} + +static void usbhsh_req_list_quit(struct usbhsh_hpriv *hpriv) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usbhsh_request *ureq, *next; + + /* kfree all active ureq */ + list_for_each_entry_safe(ureq, next, + &hpriv->ureq_link_active, + ureq_link) { + dev_err(dev, "active ureq (%p) is force freed\n", ureq); + kfree(ureq); + } + + /* kfree all free ureq */ + list_for_each_entry_safe(ureq, next, &hpriv->ureq_link_free, ureq_link) + kfree(ureq); +} + +static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_request *ureq; + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + if (list_empty(&hpriv->ureq_link_free)) { + /* + * create new one if there is no free ureq + */ + ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); + if (ureq) + INIT_LIST_HEAD(&ureq->ureq_link); + } else { + /* + * reuse "free" ureq if exist + */ + ureq = list_entry(hpriv->ureq_link_free.next, + struct usbhsh_request, + ureq_link); + if (ureq) + list_del_init(&ureq->ureq_link); + } + + if (!ureq) { + dev_err(dev, "ureq alloc fail\n"); + return NULL; + } + + usbhs_pkt_init(&ureq->pkt); + + /* + * push it to "active" list + */ + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_active); + ureq->urb = urb; + + return ureq; +} + +static void usbhsh_req_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_request *ureq) +{ + struct usbhs_pkt *pkt = &ureq->pkt; + + usbhs_pkt_init(pkt); + + /* + * removed from "active" list, + * and push it to "free" list + */ + ureq->urb = NULL; + list_del_init(&ureq->ureq_link); + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_free); +} + +/* + * device control + */ + +static int usbhsh_device_has_endpoint(struct usbhsh_device *udev) +{ + return !list_empty(&udev->ep_list_head); +} + +static struct usbhsh_device *usbhsh_device_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb) +{ + struct usbhsh_device *udev = NULL; + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + int i; + + /* + * device 0 + */ + if (0 == usb_pipedevice(urb->pipe)) { + udev = usbhsh_device0(hpriv); + goto usbhsh_device_find; + } + + /* + * find unused device + */ + usbhsh_for_each_udev(udev, hpriv, i) { + if (usbhsh_udev_to_usbv(udev)) + continue; + goto usbhsh_device_find; + } + + dev_err(dev, "no free usbhsh_device\n"); + + return NULL; + +usbhsh_device_find: + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev have old endpoint\n"); + + /* uep will be attached */ + INIT_LIST_HEAD(&udev->ep_list_head); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be enable + */ + dev_set_drvdata(&usbv->dev, udev); + udev->usbv = usbv; + + /* set device config */ + usbhs_set_device_speed(priv, + usbhsh_device_number(hpriv, udev), + usbhsh_device_number(hpriv, udev), + 0, /* FIXME no parent */ + usbv->speed); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + return udev; +} + +static void usbhsh_device_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_udev_to_usbv(udev); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev still have endpoint\n"); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be disable + */ + dev_set_drvdata(&usbv->dev, NULL); + udev->usbv = NULL; +} + +/* + * end-point control + */ +struct usbhsh_ep *usbhsh_endpoint_alloc(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev, + struct usb_host_endpoint *ep, + gfp_t mem_flags) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhsh_ep *uep; + struct usbhsh_pipe_info *info; + struct usbhs_pipe *pipe, *best_pipe; + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_endpoint_descriptor *desc = &ep->desc; + int type, i; + unsigned int min_usr; + + uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags); + if (!uep) { + dev_err(dev, "usbhsh_ep alloc fail\n"); + return NULL; + } + type = usb_endpoint_type(desc); + + /* + * find best pipe for endpoint + * see + * HARDWARE LIMITATION + */ + min_usr = ~0; + best_pipe = NULL; + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (!usbhs_pipe_type_is(pipe, type)) + continue; + + info = usbhsh_pipe_info(pipe); + + if (min_usr > info->usr_cnt) { + min_usr = info->usr_cnt; + best_pipe = pipe; + } + } + + if (unlikely(!best_pipe)) { + dev_err(dev, "couldn't find best pipe\n"); + kfree(uep); + return NULL; + } + + /* + * init uep + */ + uep->pipe = best_pipe; + uep->maxp = usb_endpoint_maxp(desc); + usbhsh_uep_to_udev(uep) = udev; + usbhsh_ep_to_uep(ep) = uep; + + /* + * update pipe user count + */ + info = usbhsh_pipe_info(best_pipe); + info->usr_cnt++; + + /* init this endpoint, and attach it to udev */ + INIT_LIST_HEAD(&uep->ep_list); + list_add_tail(&uep->ep_list, &udev->ep_list_head); + + /* + * usbhs_pipe_config_update() should be called after + * usbhs_device_config() + * see + * DCPMAXP/PIPEMAXP + */ + usbhs_pipe_config_update(uep->pipe, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(desc), + uep->maxp); + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), + usbhs_pipe_name(pipe), uep); + + return uep; +} + +void usbhsh_endpoint_free(struct usbhsh_hpriv *hpriv, + struct usb_host_endpoint *ep) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_pipe_info *info; + + if (!uep) + return; + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)), + usbhs_pipe_name(uep->pipe), uep); + + info = usbhsh_pipe_info(uep->pipe); + info->usr_cnt--; + + /* remove this endpoint from udev */ + list_del_init(&uep->ep_list); + + usbhsh_uep_to_udev(uep) = NULL; + usbhsh_ep_to_uep(ep) = NULL; + + kfree(uep); +} + +/* + * queue push/pop + */ +static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct urb *urb = ureq->urb; + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + if (!urb) { + dev_warn(dev, "pkt doesn't have urb\n"); + return; + } + + urb->actual_length = pkt->actual; + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, 0); +} + +static int usbhsh_queue_push(struct usb_hcd *hcd, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + struct usbhs_pkt *pkt = &ureq->pkt; + struct device *dev = usbhsh_hcd_to_dev(hcd); + void *buf; + int len; + + if (usb_pipeisoc(urb->pipe)) { + dev_err(dev, "pipe iso is not supported now\n"); + return -EIO; + } + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_fifo_pio_pop_handler; + else + pipe->handler = &usbhs_fifo_pio_push_handler; + + buf = (void *)(urb->transfer_buffer + urb->actual_length); + len = urb->transfer_buffer_length - urb->actual_length; + + dev_dbg(dev, "%s\n", __func__); + usbhs_pkt_push(pipe, pkt, usbhsh_queue_done, + buf, len, (urb->transfer_flags & URB_ZERO_PACKET)); + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * DCP setup stage + */ +static int usbhsh_is_request_address(struct urb *urb) +{ + struct usb_ctrlrequest *cmd; + + cmd = (struct usb_ctrlrequest *)urb->setup_packet; + + if ((DeviceOutRequest == cmd->bRequestType << 8) && + (USB_REQ_SET_ADDRESS == cmd->bRequest)) + return 1; + else + return 0; +} + +static void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_ctrlrequest req; + struct device *dev = usbhs_priv_to_dev(priv); + + /* + * wait setup packet ACK + * see + * usbhsh_irq_setup_ack() + * usbhsh_irq_setup_err() + */ + DECLARE_COMPLETION(done); + hpriv->done = &done; + + /* copy original request */ + memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest)); + + /* + * renesas_usbhs can not use original usb address. + * see HARDWARE LIMITATION. + * modify usb address here. + */ + if (usbhsh_is_request_address(urb)) { + /* FIXME */ + req.wValue = 1; + dev_dbg(dev, "create new address - %d\n", req.wValue); + } + + /* set request */ + usbhs_usbreq_set_val(priv, &req); + + /* + * wait setup packet ACK + */ + wait_for_completion(&done); + hpriv->done = NULL; + + dev_dbg(dev, "%s done\n", __func__); +} + +/* + * DCP data stage + */ +static void usbhsh_data_stage_packet_done(struct usbhs_priv *priv, + struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct urb *urb = ureq->urb; + + /* this ureq was connected to urb when usbhsh_urb_enqueue() */ + + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; +} + +static void usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * data stage uses ureq which is connected to urb + * see usbhsh_urb_enqueue() :: alloc new request. + * it will be freed in usbhsh_data_stage_packet_done() + */ + ureq = usbhsh_urb_to_ureq(urb); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_data_stage_in_handler; + else + pipe->handler = &usbhs_dcp_data_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_data_stage_packet_done, + urb->transfer_buffer, + urb->transfer_buffer_length, + (urb->transfer_flags & URB_ZERO_PACKET)); +} + +/* + * DCP status stage + */ +static void usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * status stage uses allocated ureq. + * it will be freed on usbhsh_queue_done() + */ + ureq = usbhsh_req_alloc(hpriv, urb, GFP_KERNEL); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_status_stage_in_handler; + else + pipe->handler = &usbhs_dcp_status_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_queue_done, + NULL, + urb->transfer_buffer_length, + 0); +} + +static int usbhsh_dcp_queue_push(struct usb_hcd *hcd, + struct usbhsh_hpriv *hpriv, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct device *dev = usbhsh_hcd_to_dev(hcd); + + dev_dbg(dev, "%s\n", __func__); + + /* + * setup stage + * + * usbhsh_send_setup_stage_packet() wait SACK/SIGN + */ + usbhsh_setup_stage_packet_push(hpriv, urb, pipe); + + /* + * data stage + * + * It is pushed only when urb has buffer. + */ + if (urb->transfer_buffer_length) + usbhsh_data_stage_packet_push(hpriv, urb, pipe); + + /* + * status stage + */ + usbhsh_status_stage_packet_push(hpriv, urb, pipe); + + /* + * start pushed packets + */ + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * dma map functions + */ +static int usbhsh_dma_map_ctrl(struct usbhs_pkt *pkt, int map) +{ + return 0; +} + +/* + * for hc_driver + */ +static int usbhsh_host_start(struct usb_hcd *hcd) +{ + return 0; +} + +static void usbhsh_host_stop(struct usb_hcd *hcd) +{ +} + +static int usbhsh_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usb_host_endpoint *ep = urb->ep; + struct usbhsh_request *ureq; + struct usbhsh_device *udev, *new_udev = NULL; + struct usbhs_pipe *pipe; + struct usbhsh_ep *uep; + + int ret; + + dev_dbg(dev, "%s (%s)\n", + __func__, usb_pipein(urb->pipe) ? "in" : "out"); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto usbhsh_urb_enqueue_error_not_linked; + + /* + * get udev + */ + udev = usbhsh_usbv_to_udev(usbv); + if (!udev) { + new_udev = usbhsh_device_alloc(hpriv, urb); + if (!new_udev) + goto usbhsh_urb_enqueue_error_not_linked; + + udev = new_udev; + } + + /* + * get uep + */ + uep = usbhsh_ep_to_uep(ep); + if (!uep) { + uep = usbhsh_endpoint_alloc(hpriv, udev, ep, mem_flags); + if (!uep) + goto usbhsh_urb_enqueue_error_free_device; + } + pipe = usbhsh_uep_to_pipe(uep); + + /* + * alloc new request + */ + ureq = usbhsh_req_alloc(hpriv, urb, mem_flags); + if (unlikely(!ureq)) { + ret = -ENOMEM; + goto usbhsh_urb_enqueue_error_free_endpoint; + } + usbhsh_urb_to_ureq(urb) = ureq; + + /* + * push packet + */ + if (usb_pipecontrol(urb->pipe)) + usbhsh_dcp_queue_push(hcd, hpriv, pipe, urb); + else + usbhsh_queue_push(hcd, pipe, urb); + + return 0; + +usbhsh_urb_enqueue_error_free_endpoint: + usbhsh_endpoint_free(hpriv, ep); +usbhsh_urb_enqueue_error_free_device: + if (new_udev) + usbhsh_device_free(hpriv, new_udev); +usbhsh_urb_enqueue_error_not_linked: + + dev_dbg(dev, "%s error\n", __func__); + + return ret; +} + +static int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + + if (ureq) { + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + } + + return 0; +} + +static void usbhsh_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_device *udev; + struct usbhsh_hpriv *hpriv; + + /* + * this function might be called manytimes by same hcd/ep + * in-endpoitn == out-endpoint if ep == dcp. + */ + if (!uep) + return; + + udev = usbhsh_uep_to_udev(uep); + hpriv = usbhsh_hcd_to_hpriv(hcd); + + usbhsh_endpoint_free(hpriv, ep); + ep->hcpriv = NULL; + + /* + * if there is no endpoint, + * free device + */ + if (!usbhsh_device_has_endpoint(udev)) + usbhsh_device_free(hpriv, udev); +} + +static int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + /* + * does port stat was changed ? + * check USB_PORT_STAT_C_xxx << 16 + */ + if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000) + *buf = (1 << roothub_id); + else + *buf = 0; + + dev_dbg(dev, "%s (%02x)\n", __func__, *buf); + + return !!(*buf); +} + +static int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + dev_dbg(dev, "%s :: C_HUB_xx\n", __func__); + return 0; + } + + return -EPIPE; +} + +static int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int enable = (typeReq == SetPortFeature); + int speed, i, timeout = 128; + int roothub_id = 1; /* only 1 root hub */ + + /* common error */ + if (wIndex > roothub_id || wLength != 0) + return -EPIPE; + + /* check wValue */ + switch (wValue) { + case USB_PORT_FEAT_POWER: + usbhs_vbus_ctrl(priv, enable); + dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__); + break; + + case USB_PORT_FEAT_ENABLE: + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__); + break; + + case USB_PORT_FEAT_RESET: + if (!enable) + break; + + usbhsh_port_stat_clear(hpriv, + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_LOW_SPEED); + + usbhs_bus_send_reset(priv); + msleep(20); + usbhs_bus_send_sof_enable(priv); + + for (i = 0; i < timeout ; i++) { + switch (usbhs_bus_get_speed(priv)) { + case USB_SPEED_LOW: + speed = USB_PORT_STAT_LOW_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_HIGH: + speed = USB_PORT_STAT_HIGH_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_FULL: + speed = 0; + goto got_usb_bus_speed; + } + + msleep(20); + } + return -EPIPE; + +got_usb_bus_speed: + usbhsh_port_stat_set(hpriv, speed); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE); + + dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n", + __func__, speed); + + /* status change is not needed */ + return 0; + + default: + return -EPIPE; + } + + /* set/clear status */ + if (enable) + usbhsh_port_stat_set(hpriv, (1 << wValue)); + else + usbhsh_port_stat_clear(hpriv, (1 << wValue)); + + return 0; +} + +static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf; + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + switch (typeReq) { + case GetHubStatus: + dev_dbg(dev, "%s :: GetHubStatus\n", __func__); + + *buf = 0x00; + break; + + case GetPortStatus: + if (wIndex != roothub_id) + return -EPIPE; + + dev_dbg(dev, "%s :: GetPortStatus\n", __func__); + *(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv)); + break; + + case GetHubDescriptor: + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = roothub_id; + desc->bDescLength = 9; + desc->bPwrOn2PwrGood = 0; + desc->wHubCharacteristics = cpu_to_le16(0x0011); + desc->u.hs.DeviceRemovable[0] = (roothub_id << 1); + desc->u.hs.DeviceRemovable[1] = ~0; + dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__); + break; + } + + return 0; +} + +static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret = -EPIPE; + + switch (typeReq) { + + /* Hub Feature */ + case ClearHubFeature: + case SetHubFeature: + ret = __usbhsh_hub_hub_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Port Feature */ + case SetPortFeature: + case ClearPortFeature: + ret = __usbhsh_hub_port_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Get status */ + case GetHubStatus: + case GetPortStatus: + case GetHubDescriptor: + ret = __usbhsh_hub_get_status(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + } + + dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n", + typeReq, ret, usbhsh_port_stat_get(hpriv)); + + return ret; +} + +static struct hc_driver usbhsh_driver = { + .description = usbhsh_hcd_name, + .hcd_priv_size = sizeof(struct usbhsh_hpriv), + + /* + * generic hardware linkage + */ + .flags = HCD_USB2, + + .start = usbhsh_host_start, + .stop = usbhsh_host_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = usbhsh_urb_enqueue, + .urb_dequeue = usbhsh_urb_dequeue, + .endpoint_disable = usbhsh_endpoint_disable, + + /* + * root hub + */ + .hub_status_data = usbhsh_hub_status_data, + .hub_control = usbhsh_hub_control, +}; + +/* + * interrupt functions + */ +static int usbhsh_irq_attch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device attached\n"); + + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_dtch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device detached\n"); + + usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_setup_ack(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet OK\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup ack happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +static int usbhsh_irq_setup_err(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet Err\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup err happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +/* + * module start/stop + */ +static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usbhsh_pipe_info *pipe_info = hpriv->pipe_info; + struct usbhs_pipe *pipe; + u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int old_type, dir_in, i; + + /* init all pipe */ + old_type = USB_ENDPOINT_XFER_CONTROL; + for (i = 0; i < pipe_size; i++) { + pipe_info[i].usr_cnt = 0; + + /* + * data "output" will be finished as soon as possible, + * but there is no guaranty at data "input" case. + * + * "input" needs "standby" pipe. + * So, "input" direction pipe > "output" direction pipe + * is good idea. + * + * 1st USB_ENDPOINT_XFER_xxx will be output direction, + * and the other will be input direction here. + * + * ex) + * ... + * USB_ENDPOINT_XFER_ISOC -> dir out + * USB_ENDPOINT_XFER_ISOC -> dir in + * USB_ENDPOINT_XFER_BULK -> dir out + * USB_ENDPOINT_XFER_BULK -> dir in + * USB_ENDPOINT_XFER_BULK -> dir in + * ... + */ + dir_in = (pipe_type[i] == old_type); + old_type = pipe_type[i]; + + if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) { + pipe = usbhs_dcp_malloc(priv); + usbhsh_hpriv_to_dcp(hpriv) = pipe; + } else { + pipe = usbhs_pipe_malloc(priv, + pipe_type[i], + dir_in); + } + + pipe->mod_private = pipe_info + i; + } +} + +static int usbhsh_start(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + /* add hcd */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret < 0) + return 0; + + /* + * pipe initialize and enable DCP + */ + usbhs_pipe_init(priv, + usbhsh_dma_map_ctrl); + usbhs_fifo_init(priv); + usbhsh_pipe_init_for_host(priv); + + /* + * system config enble + * - HI speed + * - host + * - usb module + */ + usbhs_sys_hispeed_ctrl(priv, 1); + usbhs_sys_host_ctrl(priv, 1); + usbhs_sys_usb_ctrl(priv, 1); + + /* + * enable irq callback + */ + mod->irq_attch = usbhsh_irq_attch; + mod->irq_dtch = usbhsh_irq_dtch; + mod->irq_sack = usbhsh_irq_setup_ack; + mod->irq_sign = usbhsh_irq_setup_err; + usbhs_irq_callback_update(priv, mod); + + dev_dbg(dev, "start host\n"); + + return ret; +} + +static int usbhsh_stop(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + usb_remove_hcd(hcd); + + /* disable sys */ + usbhs_sys_hispeed_ctrl(priv, 0); + usbhs_sys_host_ctrl(priv, 0); + usbhs_sys_usb_ctrl(priv, 0); + + dev_dbg(dev, "quit host\n"); + + return 0; +} + +int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv; + struct usb_hcd *hcd; + struct usbhsh_pipe_info *pipe_info; + struct usbhsh_device *udev; + struct device *dev = usbhs_priv_to_dev(priv); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int i; + + /* initialize hcd */ + hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name); + if (!hcd) { + dev_err(dev, "Failed to create hcd\n"); + return -ENOMEM; + } + + pipe_info = kzalloc(sizeof(*pipe_info) * pipe_size, GFP_KERNEL); + if (!pipe_info) { + dev_err(dev, "Could not allocate pipe_info\n"); + goto usbhs_mod_host_probe_err; + } + + /* + * CAUTION + * + * There is no guarantee that it is possible to access usb module here. + * Don't accesses to it. + * The accesse will be enable after "usbhsh_start" + */ + + hpriv = usbhsh_hcd_to_hpriv(hcd); + + /* + * register itself + */ + usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST); + + /* init hpriv */ + hpriv->mod.name = "host"; + hpriv->mod.start = usbhsh_start; + hpriv->mod.stop = usbhsh_stop; + hpriv->pipe_info = pipe_info; + hpriv->pipe_size = pipe_size; + hpriv->done = NULL; + usbhsh_req_list_init(hpriv); + usbhsh_port_stat_init(hpriv); + + /* init all device */ + usbhsh_for_each_udev_with_dev0(udev, hpriv, i) { + udev->usbv = NULL; + INIT_LIST_HEAD(&udev->ep_list_head); + } + + dev_info(dev, "host probed\n"); + + return 0; + +usbhs_mod_host_probe_err: + usb_put_hcd(hcd); + + return -ENOMEM; +} + +int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + + usbhsh_req_list_quit(hpriv); + + usb_put_hcd(hcd); + + return 0; +} diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1b14cae45704..c74389ce2177 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -29,9 +29,6 @@ #define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f) #define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0) -#define usbhsp_type(p) ((p)->pipe_type) -#define usbhsp_type_is(p, t) ((p)->pipe_type == t) - /* * for debug */ @@ -42,28 +39,9 @@ static char *usbhsp_pipe_name[] = { [USB_ENDPOINT_XFER_ISOC] = "ISO", }; -/* - * usb request functions - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +char *usbhs_pipe_name(struct usbhs_pipe *pipe) { - u16 val; - - val = usbhs_read(priv, USBREQ); - req->bRequest = (val >> 8) & 0xFF; - req->bRequestType = (val >> 0) & 0xFF; - - req->wValue = usbhs_read(priv, USBVAL); - req->wIndex = usbhs_read(priv, USBINDX); - req->wLength = usbhs_read(priv, USBLENG); -} - -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) -{ - usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); - usbhs_write(priv, USBVAL, req->wValue); - usbhs_write(priv, USBINDX, req->wIndex); - usbhs_write(priv, USBLENG, req->wLength); + return usbhsp_pipe_name[usbhs_pipe_type(pipe)]; } /* @@ -106,17 +84,6 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, usbhs_bset(priv, pipe_reg, mask, val); } -static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, - u16 dcp_reg, u16 pipe_reg) -{ - struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - - if (usbhs_pipe_is_dcp(pipe)) - return usbhs_read(priv, dcp_reg); - else - return usbhs_read(priv, pipe_reg); -} - /* * DCPCFG/PIPECFG functions */ @@ -144,11 +111,6 @@ static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val) __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val); } -static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe) -{ - return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP); -} - /* * pipe control functions */ @@ -303,16 +265,16 @@ static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) /* * only ISO / BULK pipe can use double buffer */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) return 1; return 0; } static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) + int is_host, + int dir_in) { u16 type = 0; u16 bfre = 0; @@ -341,40 +303,40 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, */ /* TYPE */ - type = type_array[usbhsp_type(pipe)]; + type = type_array[usbhs_pipe_type(pipe)]; /* BFRE */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) bfre = 0; /* FIXME */ /* DBLB */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) dblb = (is_double) ? DBLB : 0; /* CNTMD */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) cntmd = 0; /* FIXME */ /* DIR */ - if (usb_endpoint_dir_in(desc)) + if (dir_in) usbhsp_flags_set(pipe, IS_DIR_HOST); - if ((is_host && usb_endpoint_dir_out(desc)) || - (!is_host && usb_endpoint_dir_in(desc))) + if ((is_host && !dir_in) || + (!is_host && dir_in)) dir |= DIR_OUT; if (!dir) usbhsp_flags_set(pipe, IS_DIR_IN); /* SHTNAK */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) && + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) && !dir) shtnak = SHTNAK; /* EPNUM */ - epnum = 0xF & usb_endpoint_num(desc); + epnum = 0; /* see usbhs_pipe_config_update() */ return type | bfre | @@ -385,19 +347,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, epnum; } -static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) -{ - /* host should set DEVSEL */ - - /* reutn MXPS */ - return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize); -} - -static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) +static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); @@ -441,9 +391,9 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, * INT : 64 byte * ISOC: 512 byte */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) buff_size = 256; - else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) buff_size = 64; else buff_size = 512; @@ -453,7 +403,7 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, /* BUFNMB has been reserved for INT pipe * see above */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) { + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) { bufnmb = pipe_num - 2; } else { bufnmb = info->bufnmb_last; @@ -473,16 +423,42 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, (0xff & bufnmb) << 0; } +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp) +{ + if (devsel > 0xA) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_err(dev, "devsel error %d\n", devsel); + + devsel = 0; + } + + usbhsp_pipe_barrier(pipe); + + pipe->maxp = maxp; + + usbhsp_pipe_select(pipe); + usbhsp_pipe_maxp_set(pipe, 0xFFFF, + (devsel << 12) | + maxp); + + if (!usbhs_pipe_is_dcp(pipe)) + usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); +} + /* * pipe control */ int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) { - u16 mask = usbhs_pipe_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK; - - usbhsp_pipe_select(pipe); - - return (int)(usbhsp_pipe_maxp_get(pipe) & mask); + /* + * see + * usbhs_pipe_config_update() + * usbhs_dcp_malloc() + */ + return pipe->maxp; } int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) @@ -495,9 +471,12 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) return usbhsp_flags_has(pipe, IS_DIR_HOST); } -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data) { - usbhsp_pipectrl_set(pipe, SQCLR, SQCLR); + u16 mask = (SQCLR | SQSET); + u16 val = (data) ? SQSET : SQCLR; + + usbhsp_pipectrl_set(pipe, mask, val); } void usbhs_pipe_clear(struct usbhs_pipe *pipe) @@ -516,7 +495,7 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) */ pipe = NULL; usbhs_for_each_pipe_with_dcp(pos, priv, i) { - if (!usbhsp_type_is(pos, type)) + if (!usbhs_pipe_type_is(pos, type)) continue; if (usbhsp_flags_has(pos, IS_USED)) continue; @@ -538,19 +517,12 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) } void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); - struct device *dev = usbhs_priv_to_dev(priv); struct usbhs_pipe *pipe; int i; - if (!done) { - dev_err(dev, "no done function\n"); - return; - } - /* * FIXME * @@ -565,7 +537,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv, */ info->bufnmb_last = 4; usbhs_for_each_pipe_with_dcp(pipe, priv, i) { - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) info->bufnmb_last++; usbhsp_flags_init(pipe); @@ -577,24 +549,23 @@ void usbhs_pipe_init(struct usbhs_priv *priv, usbhs_pipe_clear(pipe); } - info->done = done; info->dma_map_ctrl = dma_map_ctrl; } struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc) + int endpoint_type, + int dir_in) { struct device *dev = usbhs_priv_to_dev(priv); - struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_pipe *pipe; - int is_host = usbhs_mod_is_host(priv, mod); + int is_host = usbhs_mod_is_host(priv); int ret; - u16 pipecfg, pipebuf, pipemaxp; + u16 pipecfg, pipebuf; - pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc)); + pipe = usbhsp_get_pipe(priv, endpoint_type); if (!pipe) { dev_err(dev, "can't get pipe (%s)\n", - usbhsp_pipe_name[usb_endpoint_type(desc)]); + usbhsp_pipe_name[endpoint_type]); return NULL; } @@ -609,22 +580,25 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return NULL; } - pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host); - pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host); - pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host); + pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in); + pipebuf = usbhsp_setup_pipebuff(pipe); usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); - usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); dev_dbg(dev, "enable pipe %d : %s (%s)\n", usbhs_pipe_number(pipe), - usbhsp_pipe_name[usb_endpoint_type(desc)], + usbhs_pipe_name(pipe), usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); + /* + * epnum / maxp are still not set to this pipe. + * call usbhs_pipe_config_update() after this function !! + */ + return pipe; } @@ -651,25 +625,31 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) if (!pipe) return NULL; + INIT_LIST_HEAD(&pipe->list); + /* - * dcpcfg : default - * dcpmaxp : default - * pipebuf : nothing to do + * call usbhs_pipe_config_update() after this function !! */ - usbhsp_pipe_select(pipe); - usbhs_pipe_clear_sequence(pipe); - INIT_LIST_HEAD(&pipe->list); - return pipe; } void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + WARN_ON(!usbhs_pipe_is_dcp(pipe)); usbhs_pipe_enable(pipe); - usbhsp_pipectrl_set(pipe, CCPL, CCPL); + + if (!usbhs_mod_is_host(priv)) /* funconly */ + usbhsp_pipectrl_set(pipe, CCPL, CCPL); +} + +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) +{ + usbhsp_pipe_cfg_set(pipe, DIR_OUT, + dir_out ? DIR_OUT : 0); } /* @@ -703,7 +683,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv) */ usbhs_for_each_pipe_with_dcp(pipe, priv, i) { pipe->priv = priv; - usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; + + usbhs_pipe_type(pipe) = + pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; dev_dbg(dev, "pipe %x\t: %s\n", i, usbhsp_pipe_name[pipe_type[i]]); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 41534cb0e734..6334fc644cc0 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -30,11 +30,15 @@ struct usbhs_pipe { struct usbhs_fifo *fifo; struct list_head list; + int maxp; + u32 flags; #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) + struct usbhs_pkt_handle *handler; + void *mod_private; }; @@ -43,7 +47,6 @@ struct usbhs_pipe_info { int size; /* array size of "pipe" */ int bufnmb_last; /* FIXME : driver needs good allocator */ - void (*done)(struct usbhs_pkt *pkt); int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map); }; @@ -67,32 +70,30 @@ struct usbhs_pipe_info { #define usbhs_priv_to_pipeinfo(pr) (&(pr)->pipe_info) /* - * usb request - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); - -/* * pipe control */ +char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe -*usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc); +*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); void usbhs_pipe_clear(struct usbhs_pipe *pipe); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp); + +#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0) +#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data); #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) @@ -100,10 +101,14 @@ void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); #define usbhs_pipe_to_fifo(p) ((p)->fifo) #define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p) +#define usbhs_pipe_type(p) ((p)->pipe_type) +#define usbhs_pipe_type_is(p, t) ((p)->pipe_type == t) + /* * dcp control */ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv); void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe); +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out); #endif /* RENESAS_USB_PIPE_H */ diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index b71e309116a3..677f577c0243 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -252,6 +252,7 @@ config USB_SERIAL_GARMIN config USB_SERIAL_IPW tristate "USB IPWireless (3G UMTS TDD) Driver" + select USB_SERIAL_WWAN help Say Y here if you want to use a IPWireless USB modem such as the ones supplied by Axity3G/Sentech South Africa. diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 86fbba6336c9..e92cbefc0f88 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -227,7 +227,7 @@ * - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to * recheck the condition they are sleeping on. This is defensive, * in case a wake up is lost. -* - Following Documentation/DocBook/kernel-locking.pdf no spin locks +* - Following Documentation/DocBook/kernel-locking.tmpl no spin locks * are held when calling copy_to/from_user or printk. */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5fc13e717911..8fe034d2d3e7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -73,6 +73,7 @@ struct ftdi_private { */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ + struct async_icount icount; wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ @@ -207,6 +208,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, @@ -745,6 +748,8 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, @@ -885,6 +890,8 @@ static void ftdi_set_termios(struct tty_struct *tty, static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -919,6 +926,7 @@ static struct usb_serial_driver ftdi_sio_device = { .prepare_write_buffer = ftdi_prepare_write_buffer, .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, + .get_icount = ftdi_get_icount, .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, @@ -1486,7 +1494,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port) } /* set max packet size based on descriptor */ - priv->max_packet_size = le16_to_cpu(ep_desc->wMaxPacketSize); + priv->max_packet_size = usb_endpoint_maxp(ep_desc); dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } @@ -1646,6 +1654,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) kref_init(&priv->kref); mutex_init(&priv->cfg_lock); + memset(&priv->icount, 0x00, sizeof(priv->icount)); init_waitqueue_head(&priv->delta_msr_wait); priv->flags = ASYNC_LOW_LATENCY; @@ -1909,6 +1918,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); if (!c) break; + priv->icount.tx += c; buffer[i] = (c << 2) + 1; count += c + 1; } @@ -1916,6 +1926,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, } else { count = kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); + priv->icount.tx += count; } return count; @@ -1943,6 +1954,14 @@ static int ftdi_process_packet(struct tty_struct *tty, N.B. packet may be processed more than once, but differences are only processed once. */ status = packet[0] & FTDI_STATUS_B0_MASK; + if (status & FTDI_RS0_CTS) + priv->icount.cts++; + if (status & FTDI_RS0_DSR) + priv->icount.dsr++; + if (status & FTDI_RS0_RI) + priv->icount.rng++; + if (status & FTDI_RS0_RLSD) + priv->icount.dcd++; if (status != priv->prev_status) { priv->diff_status |= status ^ priv->prev_status; wake_up_interruptible(&priv->delta_msr_wait); @@ -1955,15 +1974,20 @@ static int ftdi_process_packet(struct tty_struct *tty, * over framing errors */ if (packet[1] & FTDI_RS_BI) { flag = TTY_BREAK; + priv->icount.brk++; usb_serial_handle_break(port); } else if (packet[1] & FTDI_RS_PE) { flag = TTY_PARITY; + priv->icount.parity++; } else if (packet[1] & FTDI_RS_FE) { flag = TTY_FRAME; + priv->icount.frame++; } /* Overrun is special, not associated with a char */ - if (packet[1] & FTDI_RS_OE) + if (packet[1] & FTDI_RS_OE) { + priv->icount.overrun++; tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } } /* save if the transmitter is empty or not */ @@ -1975,6 +1999,7 @@ static int ftdi_process_packet(struct tty_struct *tty, len -= 2; if (!len) return 0; /* status only */ + priv->icount.rx += len; ch = packet + 2; if (port->port.console && port->sysrq) { @@ -2277,11 +2302,34 @@ static int ftdi_tiocmset(struct tty_struct *tty, return update_mctrl(port, set, clear); } +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct usb_serial_port *port = tty->driver_data; + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount *ic = &priv->icount; + + icount->cts = ic->cts; + icount->dsr = ic->dsr; + icount->rng = ic->rng; + icount->dcd = ic->dcd; + icount->tx = ic->tx; + icount->rx = ic->rx; + icount->frame = ic->frame; + icount->parity = ic->parity; + icount->overrun = ic->overrun; + icount->brk = ic->brk; + icount->buf_overrun = ic->buf_overrun; + return 0; +} + static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount cnow; + struct async_icount cprev; dbg("%s cmd 0x%04x", __func__, cmd); @@ -2301,41 +2349,30 @@ static int ftdi_ioctl(struct tty_struct *tty, * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was. - * (except that the driver doesn't support it !) * * This code is borrowed from linux/drivers/char/serial.c */ case TIOCMIWAIT: - while (priv != NULL) { + cprev = priv->icount; + while (1) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - else { - char diff = priv->diff_status; - - if (diff == 0) - return -EIO; /* no change => error */ - - /* Consume all events */ - priv->diff_status = 0; - - /* Return 0 if caller wanted to know about - these bits */ - if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || - ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || - ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || - ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) { - return 0; - } - /* - * Otherwise caller can't care less about what - * happened,and so we continue to wait for more - * events. - */ + cnow = priv->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + return 0; } + cprev = cnow; } - return 0; + /* not reached */ + break; case TIOCSERGETLSR: return get_lsr_info(port, (struct serial_struct __user *)arg); break; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index bf5227ad3ef7..571fa96b49c7 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -54,6 +54,7 @@ /* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ #define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 #define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 +#define LMI_LM3S_ICDI_BOARD_PID 0xbcda #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ @@ -420,9 +421,11 @@ #define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ /* - * DSS-20 Sync Station for Sony Ericsson P800 + * Sony Ericsson product ids */ -#define FTDI_DSS20_PID 0xFC82 +#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ +#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ +#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ /* www.irtrans.de device */ #define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index abf095be5753..2ee807523f53 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3042,7 +3042,7 @@ static int edge_startup(struct usb_serial *serial) endpoint = &serial->interface->altsetting[0]. endpoint[i].desc; - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { /* we found a interrupt in endpoint */ @@ -3107,7 +3107,7 @@ static int edge_startup(struct usb_serial *serial) usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), edge_serial->bulk_in_buffer, - le16_to_cpu(endpoint->wMaxPacketSize), + usb_endpoint_maxp(endpoint), edge_bulk_in_callback, edge_serial); bulk_in_found = true; diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index ca77e88836bd..5170799d6e94 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -47,6 +47,7 @@ #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/uaccess.h> +#include "usb-wwan.h" /* * Version Information @@ -185,7 +186,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) /*--2: Start reading from the device */ dbg("%s: setting up bulk read callback", __func__); - usb_serial_generic_open(tty, port); + usb_wwan_open(tty, port); /*--3: Tell the modem to open the floodgates on the rx bulk channel */ dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__); @@ -219,6 +220,29 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } +/* fake probe - only to allocate data structures */ +static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id) +{ + struct usb_wwan_intf_private *data; + + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->susp_lock); + usb_set_serial_data(serial, data); + return 0; +} + +static void ipw_release(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *data = usb_get_serial_data(serial); + + usb_wwan_release(serial); + usb_set_serial_data(serial, NULL); + kfree(data); +} + static void ipw_dtr_rts(struct usb_serial_port *port, int on) { struct usb_device *dev = port->serial->dev; @@ -285,7 +309,7 @@ static void ipw_close(struct usb_serial_port *port) dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)\n", result); - usb_serial_generic_close(port); + usb_wwan_close(port); } static struct usb_serial_driver ipw_device = { @@ -297,9 +321,14 @@ static struct usb_serial_driver ipw_device = { .usb_driver = &usb_ipw_driver, .id_table = usb_ipw_ids, .num_ports = 1, + .disconnect = usb_wwan_disconnect, .open = ipw_open, .close = ipw_close, + .probe = ipw_probe, + .attach = usb_wwan_startup, + .release = ipw_release, .dtr_rts = ipw_dtr_rts, + .write = usb_wwan_write, }; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 40abedbc5943..3524a105d042 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -2006,7 +2006,6 @@ static int mos7720_ioctl(struct tty_struct *tty, dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(tty, mos7720_port, (unsigned int __user *)arg); - return 0; /* FIXME: These should be using the mode methods */ case TIOCMBIS: diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 7b50aa122752..c72abd524983 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2263,7 +2263,6 @@ static int mos7840_ioctl(struct tty_struct *tty, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return mos7840_get_lsr_info(tty, argp); - return 0; case TIOCGSERIAL: dbg("%s (%d) TIOCGSERIAL", __func__, port->number); diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 96423f3c8ef3..c248a9147439 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -523,7 +523,7 @@ static int opticon_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->bulk_in_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fe22e90bc879..89ae1f65e1b1 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -475,31 +475,54 @@ enum option_blacklist_reason { OPTION_BLACKLIST_RESERVED_IF = 2 }; +#define MAX_BL_NUM 8 struct option_blacklist_info { - const u32 infolen; /* number of interface numbers on blacklist */ - const u8 *ifaceinfo; /* pointer to the array holding the numbers */ - enum option_blacklist_reason reason; + /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */ + const unsigned long sendsetup; + /* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */ + const unsigned long reserved; }; -static const u8 four_g_w14_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info four_g_w14_blacklist = { - .infolen = ARRAY_SIZE(four_g_w14_no_sendsetup), - .ifaceinfo = four_g_w14_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), }; -static const u8 alcatel_x200_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info alcatel_x200_blacklist = { - .infolen = ARRAY_SIZE(alcatel_x200_no_sendsetup), - .ifaceinfo = alcatel_x200_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), +}; + +static const struct option_blacklist_info zte_0037_blacklist = { + .sendsetup = BIT(0) | BIT(1), }; -static const u8 zte_k3765_z_no_sendsetup[] = { 0, 1, 2 }; static const struct option_blacklist_info zte_k3765_z_blacklist = { - .infolen = ARRAY_SIZE(zte_k3765_z_no_sendsetup), - .ifaceinfo = zte_k3765_z_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1) | BIT(2), + .reserved = BIT(4), +}; + +static const struct option_blacklist_info huawei_cdc12_blacklist = { + .reserved = BIT(1) | BIT(2), +}; + +static const struct option_blacklist_info net_intf1_blacklist = { + .reserved = BIT(1), +}; + +static const struct option_blacklist_info net_intf3_blacklist = { + .reserved = BIT(3), +}; + +static const struct option_blacklist_info net_intf4_blacklist = { + .reserved = BIT(4), +}; + +static const struct option_blacklist_info net_intf5_blacklist = { + .reserved = BIT(5), +}; + +static const struct option_blacklist_info zte_mf626_blacklist = { + .sendsetup = BIT(0) | BIT(1), + .reserved = BIT(4), }; static const struct usb_device_id option_ids[] = { @@ -599,12 +622,15 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) }, @@ -705,7 +731,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) }, @@ -720,51 +747,62 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_0037_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, @@ -779,11 +817,13 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) }, @@ -1214,10 +1254,35 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); +static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, + const struct option_blacklist_info *blacklist) +{ + unsigned long num; + const unsigned long *intf_list; + + if (blacklist) { + if (reason == OPTION_BLACKLIST_SENDSETUP) + intf_list = &blacklist->sendsetup; + else if (reason == OPTION_BLACKLIST_RESERVED_IF) + intf_list = &blacklist->reserved; + else { + BUG_ON(reason); + return false; + } + + for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) { + if (num == ifnum) + return true; + } + } + return false; +} + static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct usb_wwan_intf_private *data; + /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && @@ -1230,14 +1295,14 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; - /* Don't bind network interfaces on Huawei K3765, K4505 & K4605 */ - if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID && - (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4605) && - (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1 || - serial->interface->cur_altsetting->desc.bInterfaceNumber == 2)) - return -ENODEV; + /* Don't bind reserved interfaces (like network ones) which often have + * the same class/subclass/protocol as the serial interfaces. Look at + * the Windows driver .INF files for reserved interface numbers. + */ + if (is_blacklisted( + serial->interface->cur_altsetting->desc.bInterfaceNumber, + OPTION_BLACKLIST_RESERVED_IF, + (const struct option_blacklist_info *) id->driver_info)) /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && @@ -1246,7 +1311,6 @@ static int option_probe(struct usb_serial *serial, return -ENODEV; data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); - if (!data) return -ENOMEM; data->send_setup = option_send_setup; @@ -1255,23 +1319,6 @@ static int option_probe(struct usb_serial *serial, return 0; } -static enum option_blacklist_reason is_blacklisted(const u8 ifnum, - const struct option_blacklist_info *blacklist) -{ - const u8 *info; - int i; - - if (blacklist) { - info = blacklist->ifaceinfo; - - for (i = 0; i < blacklist->infolen; i++) { - if (info[i] == ifnum) - return blacklist->reason; - } - } - return OPTION_BLACKLIST_NONE; -} - static void option_instat_callback(struct urb *urb) { int err; @@ -1343,9 +1390,8 @@ static int option_send_setup(struct usb_serial_port *port) int val = 0; dbg("%s", __func__); - if (is_blacklisted(ifNum, - (struct option_blacklist_info *) intfdata->private) - == OPTION_BLACKLIST_SENDSETUP) { + if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP, + (struct option_blacklist_info *) intfdata->private)) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1d33260de014..9083d1e616b4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -92,6 +92,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) }, + { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -360,9 +361,6 @@ static void pl2303_set_termios(struct tty_struct *tty, tmp >>= 2; buf[1] <<= 1; } - if (tmp > 256) { - tmp %= 256; - } buf[0] = tmp; } } diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index ca0d237683b3..3d10d7f02072 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -148,3 +148,8 @@ /* WinChipHead USB->RS 232 adapter */ #define WINCHIPHEAD_VENDOR_ID 0x4348 #define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523 + +/* SMART USB Serial Adapter */ +#define SMART_VENDOR_ID 0x0b8c +#define SMART_PRODUCT_ID 0x2303 + diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index aeccc7f0a93c..b9bb24729c99 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -28,6 +28,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */ + {USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ {USB_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ {USB_DEVICE(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */ {USB_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ @@ -84,6 +85,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index d9457bd4fe10..7096f799b071 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -226,7 +226,7 @@ static int symbol_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->int_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 1c031309ab25..cc274fdf2627 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -912,7 +912,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; } buffer_size = max_t(int, serial->type->bulk_in_size, - le16_to_cpu(endpoint->wMaxPacketSize)); + usb_endpoint_maxp(endpoint)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -942,7 +942,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; buffer_size = serial->type->bulk_out_size; if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -990,7 +990,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; port->interrupt_in_buffer = kmalloc(buffer_size, @@ -1021,7 +1021,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_out_size = buffer_size; port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index bedc4b9f2ac4..fe2d803a6347 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -42,7 +42,7 @@ config USB_STORAGE_REALTEK config REALTEK_AUTOPM bool "Realtek Card Reader autosuspend support" - depends on USB_STORAGE_REALTEK && CONFIG_PM_RUNTIME + depends on USB_STORAGE_REALTEK && PM_RUNTIME default y config USB_STORAGE_DATAFAB diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 34adc4b42ceb..0ce5f79197e7 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -25,7 +25,6 @@ #include <linux/kthread.h> #include <linux/sched.h> #include <linux/kernel.h> -#include <linux/version.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -293,6 +292,52 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, return USB_STOR_TRANSPORT_ERROR; } +static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, + u8 *cmd, int cmd_len, u8 *buf, int buf_len, + enum dma_data_direction dir, int *act_len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + int result; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(buf_len); + bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; + bcb->Tag = ++us->tag; + bcb->Lun = lun; + bcb->Length = cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, cmd, bcb->Length); + + /* send it to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + if (buf && buf_len) { + unsigned int pipe = (dir == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_transfer_buf(us, pipe, + buf, buf_len, NULL); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + } + + /* get CSW for device status */ + result = usb_bulk_msg(us->pusb_dev, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &cswlen, 250); + return result; +} + /* Determine what the maximum LUN supported is */ static int rts51x_get_max_lun(struct us_data *us) { @@ -320,6 +365,11 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -331,10 +381,14 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_FROM_DEVICE, NULL); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(data, buf, len); + kfree(buf); return 0; } @@ -342,6 +396,12 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + memcpy(buf, data, len); US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -353,7 +413,8 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_TO_DEVICE, NULL); + buf, len, DMA_TO_DEVICE, NULL); + kfree(buf); if (retval != USB_STOR_TRANSPORT_GOOD) return -EIO; @@ -365,6 +426,11 @@ static int rts51x_read_status(struct us_data *us, { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, lun = %d\n", __func__, lun); @@ -372,10 +438,14 @@ static int rts51x_read_status(struct us_data *us, cmnd[1] = 0x09; retval = rts51x_bulk_transport(us, lun, cmnd, 12, - status, len, DMA_FROM_DEVICE, actlen); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, actlen); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(status, buf, len); + kfree(buf); return 0; } @@ -434,6 +504,29 @@ static int enable_oscillator(struct us_data *us) return 0; } +static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len) +{ + int retval; + u16 addr = 0xFE47; + u8 cmnd[12] = {0}; + + US_DEBUGP("%s, addr = 0x%x, len = %d\n", __FUNCTION__, addr, len); + + cmnd[0] = 0xF0; + cmnd[1] = 0x0E; + cmnd[2] = (u8)(addr >> 8); + cmnd[3] = (u8)addr; + cmnd[4] = (u8)(len >> 8); + cmnd[5] = (u8)len; + + retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, data, len, DMA_TO_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + return -EIO; + } + + return 0; +} + static int do_config_autodelink(struct us_data *us, int enable, int force) { int retval; @@ -454,7 +547,8 @@ static int do_config_autodelink(struct us_data *us, int enable, int force) US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -486,7 +580,8 @@ static int config_autodelink_after_power_on(struct us_data *us) SET_BIT(value, 7); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -507,7 +602,8 @@ static int config_autodelink_after_power_on(struct us_data *us) CLR_BIT(value, 7); } - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -584,7 +680,8 @@ static int config_autodelink_before_power_down(struct us_data *us) if (CHECK_ID(chip, 0x0138, 0x3882)) SET_BIT(value, 2); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e8ae21b2d387..ff32390d61e5 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -691,6 +691,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) int temp_result; struct scsi_eh_save ses; int sense_size = US_SENSE_SIZE; + struct scsi_sense_hdr sshdr; + const u8 *scdd; + u8 fm_ili; /* device supports and needs bigger sense buffer */ if (us->fflags & US_FL_SANE_SENSE) @@ -774,32 +777,30 @@ Retry_Sense: srb->sense_buffer[7] = (US_SENSE_SIZE - 8); } + scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE, + &sshdr); + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", - srb->sense_buffer[0], - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + sshdr.response_code, sshdr.sense_key, + sshdr.asc, sshdr.ascq); #ifdef CONFIG_USB_STORAGE_DEBUG - usb_stor_show_sense( - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + usb_stor_show_sense(sshdr.sense_key, sshdr.asc, sshdr.ascq); #endif /* set the result so the higher layers expect this data */ srb->result = SAM_STAT_CHECK_CONDITION; + scdd = scsi_sense_desc_find(srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, 4); + fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0; + /* We often get empty sense data. This could indicate that * everything worked or that there was an unspecified * problem. We have to decide which. */ - if ( /* Filemark 0, ignore EOM, ILI 0, no sense */ - (srb->sense_buffer[2] & 0xaf) == 0 && - /* No ASC or ASCQ */ - srb->sense_buffer[12] == 0 && - srb->sense_buffer[13] == 0) { - + if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 && + fm_ili == 0) { /* If things are really okay, then let's show that. * Zero out the sense buffer so the higher layers * won't realize we did an unsolicited auto-sense. @@ -814,7 +815,10 @@ Retry_Sense: */ } else { srb->result = DID_ERROR << 16; - srb->sense_buffer[2] = HARDWARE_ERROR; + if ((sshdr.response_code & 0x72) == 0x72) + srb->sense_buffer[1] = HARDWARE_ERROR; + else + srb->sense_buffer[2] = HARDWARE_ERROR; } } } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 0ca095820f3e..c325e69415a1 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -831,12 +831,22 @@ static int usb_stor_scan_thread(void * __us) dev_dbg(dev, "device found\n"); - set_freezable(); - /* Wait for the timeout to expire or for a disconnect */ + set_freezable_with_signal(); + /* + * Wait for the timeout to expire or for a disconnect + * + * We can't freeze in this thread or we risk causing khubd to + * fail to freeze, but we can't be non-freezable either. Nor can + * khubd freeze while waiting for scanning to complete as it may + * hold the device lock, causing a hang when suspending devices. + * So we request a fake signal when freezing and use + * interruptible sleep to kick us out of our wait early when + * freezing happens. + */ if (delay_use > 0) { dev_dbg(dev, "waiting for device to settle " "before scanning\n"); - wait_event_freezable_timeout(us->delay_wait, + wait_event_interruptible_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); } diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c new file mode 100644 index 000000000000..d29503e954ab --- /dev/null +++ b/drivers/usb/usb-common.c @@ -0,0 +1,35 @@ +/* + * Provides code common for host and device side USB. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * If either host side (ie. CONFIG_USB=y) or device side USB stack + * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is + * compiled-in as well. Otherwise, if either of the two stacks is + * compiled as module, this file is compiled as module as well. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb/ch9.h> + +const char *usb_speed_string(enum usb_device_speed speed) +{ + static const char *const names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", + }; + + if (speed < 0 || speed >= ARRAY_SIZE(names)) + speed = USB_SPEED_UNKNOWN; + return names[speed]; +} +EXPORT_SYMBOL_GPL(usb_speed_string); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index e24ce3123071..32d6fc953904 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -555,7 +555,7 @@ static int skel_probe(struct usb_interface *interface, if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 59a748a0e5da..0d1863c9edde 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -43,7 +43,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface) /* Fill up Data Transfer EP pointers */ wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; - wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize); + wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd); wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL); if (wa->xfer_result == NULL) goto error_xfer_result_alloc; diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index 157485c862c0..a7494bf10081 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -28,7 +28,6 @@ #ifndef __UWB_INTERNAL_H__ #define __UWB_INTERNAL_H__ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/uwb.h> diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index d885c770eb84..2d97752f79a5 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -428,7 +428,7 @@ static int __init igafb_init(void) * * IGS2000 has its I/O memory mapped and we want * to generate memory cycles on PCI, e.g. do ioremap(), - * then readb/writeb() as in Documentation/IO-mapping.txt. + * then readb/writeb() as in Documentation/io-mapping.txt. * * IGS1682 is more traditional, it responds to PCI I/O * cycles, so we want to access it with inb()/outb(). diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index e97b0499bd0d..97b8184614ae 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -40,7 +40,7 @@ * mknod /dev/watchdog c 10 130 * * For an example userspace keep-alive daemon, see: - * Documentation/watchdog/watchdog.txt + * Documentation/watchdog/wdt.txt */ #include <linux/module.h> diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 5f7ff8e2fc14..8795480c2350 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -137,16 +137,6 @@ config XEN_GRANT_DEV_ALLOC to other domains. This can be used to implement frontend drivers or as part of an inter-domain shared memory channel. -config XEN_PLATFORM_PCI - tristate "xen platform pci device driver" - depends on XEN_PVHVM && PCI - default m - help - Driver for the Xen PCI Platform device: it is responsible for - initializing xenbus and grant_table when running in a Xen HVM - domain. As a consequence this driver is required to run any Xen PV - frontend on Xen HVM. - config SWIOTLB_XEN def_bool y depends on PCI diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 72bbb27d7a68..974fffdf22b2 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -14,7 +14,7 @@ obj-$(CONFIG_XEN_GNTDEV) += xen-gntdev.o obj-$(CONFIG_XEN_GRANT_DEV_ALLOC) += xen-gntalloc.o obj-$(CONFIG_XENFS) += xenfs/ obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o -obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o +obj-$(CONFIG_XEN_PVHVM) += platform-pci.o obj-$(CONFIG_XEN_TMEM) += tmem.o obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o obj-$(CONFIG_XEN_DOM0) += pci.o @@ -23,5 +23,3 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/ xen-evtchn-y := evtchn.o xen-gntdev-y := gntdev.o xen-gntalloc-y := gntalloc.o - -xen-platform-pci-y := platform-pci.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 5dfd8f8ff07f..5876e1ae6c2d 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -501,20 +501,24 @@ EXPORT_SYMBOL_GPL(balloon_set_new_target); * alloc_xenballooned_pages - get pages that have been ballooned out * @nr_pages: Number of pages to get * @pages: pages returned + * @highmem: highmem or lowmem pages * @return 0 on success, error otherwise */ -int alloc_xenballooned_pages(int nr_pages, struct page** pages) +int alloc_xenballooned_pages(int nr_pages, struct page **pages, bool highmem) { int pgno = 0; struct page* page; mutex_lock(&balloon_mutex); while (pgno < nr_pages) { - page = balloon_retrieve(true); - if (page) { + page = balloon_retrieve(highmem); + if (page && PageHighMem(page) == highmem) { pages[pgno++] = page; } else { enum bp_state st; - st = decrease_reservation(nr_pages - pgno, GFP_HIGHUSER); + if (page) + balloon_append(page); + st = decrease_reservation(nr_pages - pgno, + highmem ? GFP_HIGHUSER : GFP_USER); if (st != BP_DONE) goto out_undo; } @@ -555,17 +559,40 @@ void free_xenballooned_pages(int nr_pages, struct page** pages) } EXPORT_SYMBOL(free_xenballooned_pages); -static int __init balloon_init(void) +static void __init balloon_add_region(unsigned long start_pfn, + unsigned long pages) { unsigned long pfn, extra_pfn_end; struct page *page; + /* + * If the amount of usable memory has been limited (e.g., with + * the 'mem' command line parameter), don't add pages beyond + * this limit. + */ + extra_pfn_end = min(max_pfn, start_pfn + pages); + + for (pfn = start_pfn; pfn < extra_pfn_end; pfn++) { + page = pfn_to_page(pfn); + /* totalram_pages and totalhigh_pages do not + include the boot-time balloon extension, so + don't subtract from it. */ + __balloon_append(page); + } +} + +static int __init balloon_init(void) +{ + int i; + if (!xen_domain()) return -ENODEV; pr_info("xen/balloon: Initialising balloon driver.\n"); - balloon_stats.current_pages = xen_pv_domain() ? min(xen_start_info->nr_pages, max_pfn) : max_pfn; + balloon_stats.current_pages = xen_pv_domain() + ? min(xen_start_info->nr_pages - xen_released_pages, max_pfn) + : max_pfn; balloon_stats.target_pages = balloon_stats.current_pages; balloon_stats.balloon_low = 0; balloon_stats.balloon_high = 0; @@ -584,24 +611,13 @@ static int __init balloon_init(void) #endif /* - * Initialise the balloon with excess memory space. We need - * to make sure we don't add memory which doesn't exist or - * logically exist. The E820 map can be trimmed to be smaller - * than the amount of physical memory due to the mem= command - * line parameter. And if this is a 32-bit non-HIGHMEM kernel - * on a system with memory which requires highmem to access, - * don't try to use it. + * Initialize the balloon with pages from the extra memory + * regions (see arch/x86/xen/setup.c). */ - extra_pfn_end = min(min(max_pfn, e820_end_of_ram_pfn()), - (unsigned long)PFN_DOWN(xen_extra_mem_start + xen_extra_mem_size)); - for (pfn = PFN_UP(xen_extra_mem_start); - pfn < extra_pfn_end; - pfn++) { - page = pfn_to_page(pfn); - /* totalram_pages and totalhigh_pages do not include the boot-time - balloon extension, so don't subtract from it. */ - __balloon_append(page); - } + for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) + if (xen_extra_mem[i].size) + balloon_add_region(PFN_UP(xen_extra_mem[i].start), + PFN_DOWN(xen_extra_mem[i].size)); return 0; } diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 7523719bf8a4..7a55b292bf39 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -432,7 +432,8 @@ static int __must_check xen_allocate_irq_dynamic(void) irq = irq_alloc_desc_from(first, -1); - xen_irq_init(irq); + if (irq >= 0) + xen_irq_init(irq); return irq; } @@ -713,7 +714,7 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, mutex_lock(&irq_mapping_update_lock); irq = xen_allocate_irq_dynamic(); - if (irq == -1) + if (irq < 0) goto out; irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_edge_irq, @@ -729,7 +730,7 @@ out: error_irq: mutex_unlock(&irq_mapping_update_lock); xen_free_irq(irq); - return -1; + return ret; } #endif @@ -779,7 +780,7 @@ int xen_irq_from_pirq(unsigned pirq) mutex_lock(&irq_mapping_update_lock); list_for_each_entry(info, &xen_irq_list_head, list) { - if (info == NULL || info->type != IRQT_PIRQ) + if (info->type != IRQT_PIRQ) continue; irq = info->irq; if (info->u.pirq.pirq == pirq) @@ -872,11 +873,32 @@ static int bind_interdomain_evtchn_to_irq(unsigned int remote_domain, return err ? : bind_evtchn_to_irq(bind_interdomain.local_port); } +static int find_virq(unsigned int virq, unsigned int cpu) +{ + struct evtchn_status status; + int port, rc = -ENOENT; + + memset(&status, 0, sizeof(status)); + for (port = 0; port <= NR_EVENT_CHANNELS; port++) { + status.dom = DOMID_SELF; + status.port = port; + rc = HYPERVISOR_event_channel_op(EVTCHNOP_status, &status); + if (rc < 0) + continue; + if (status.status != EVTCHNSTAT_virq) + continue; + if (status.u.virq == virq && status.vcpu == cpu) { + rc = port; + break; + } + } + return rc; +} int bind_virq_to_irq(unsigned int virq, unsigned int cpu) { struct evtchn_bind_virq bind_virq; - int evtchn, irq; + int evtchn, irq, ret; mutex_lock(&irq_mapping_update_lock); @@ -892,10 +914,16 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu) bind_virq.virq = virq; bind_virq.vcpu = cpu; - if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, - &bind_virq) != 0) - BUG(); - evtchn = bind_virq.port; + ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, + &bind_virq); + if (ret == 0) + evtchn = bind_virq.port; + else { + if (ret == -EEXIST) + ret = find_virq(virq, cpu); + BUG_ON(ret < 0); + evtchn = ret; + } xen_irq_info_virq_init(cpu, irq, evtchn, virq); @@ -1670,6 +1698,7 @@ void __init xen_init_IRQ(void) evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq), GFP_KERNEL); + BUG_ON(!evtchn_to_irq); for (i = 0; i < NR_EVENT_CHANNELS; i++) evtchn_to_irq[i] = -1; diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index f914b26cf0c2..880798aae2f2 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -83,6 +83,7 @@ struct grant_map { struct ioctl_gntdev_grant_ref *grants; struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; + struct gnttab_map_grant_ref *kmap_ops; struct page **pages; }; @@ -116,19 +117,22 @@ static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count) add->grants = kzalloc(sizeof(add->grants[0]) * count, GFP_KERNEL); add->map_ops = kzalloc(sizeof(add->map_ops[0]) * count, GFP_KERNEL); add->unmap_ops = kzalloc(sizeof(add->unmap_ops[0]) * count, GFP_KERNEL); + add->kmap_ops = kzalloc(sizeof(add->kmap_ops[0]) * count, GFP_KERNEL); add->pages = kzalloc(sizeof(add->pages[0]) * count, GFP_KERNEL); if (NULL == add->grants || NULL == add->map_ops || NULL == add->unmap_ops || + NULL == add->kmap_ops || NULL == add->pages) goto err; - if (alloc_xenballooned_pages(count, add->pages)) + if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */)) goto err; for (i = 0; i < count; i++) { add->map_ops[i].handle = -1; add->unmap_ops[i].handle = -1; + add->kmap_ops[i].handle = -1; } add->index = 0; @@ -142,6 +146,7 @@ err: kfree(add->grants); kfree(add->map_ops); kfree(add->unmap_ops); + kfree(add->kmap_ops); kfree(add); return NULL; } @@ -243,10 +248,35 @@ static int map_grant_pages(struct grant_map *map) gnttab_set_unmap_op(&map->unmap_ops[i], addr, map->flags, -1 /* handle */); } + } else { + /* + * Setup the map_ops corresponding to the pte entries pointing + * to the kernel linear addresses of the struct pages. + * These ptes are completely different from the user ptes dealt + * with find_grant_ptes. + */ + for (i = 0; i < map->count; i++) { + unsigned level; + unsigned long address = (unsigned long) + pfn_to_kaddr(page_to_pfn(map->pages[i])); + pte_t *ptep; + u64 pte_maddr = 0; + BUG_ON(PageHighMem(map->pages[i])); + + ptep = lookup_address(address, &level); + pte_maddr = arbitrary_virt_to_machine(ptep).maddr; + gnttab_set_map_op(&map->kmap_ops[i], pte_maddr, + map->flags | + GNTMAP_host_map | + GNTMAP_contains_pte, + map->grants[i].ref, + map->grants[i].domid); + } } pr_debug("map %d+%d\n", map->index, map->count); - err = gnttab_map_refs(map->map_ops, map->pages, map->count); + err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL, + map->pages, map->count); if (err) return err; @@ -462,13 +492,11 @@ static int gntdev_release(struct inode *inode, struct file *flip) pr_debug("priv %p\n", priv); - spin_lock(&priv->lock); while (!list_empty(&priv->maps)) { map = list_entry(priv->maps.next, struct grant_map, next); list_del(&map->next); gntdev_put_map(map); } - spin_unlock(&priv->lock); if (use_ptemod) mmu_notifier_unregister(&priv->mn, priv->mm); @@ -532,10 +560,11 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); if (map) { list_del(&map->next); - gntdev_put_map(map); err = 0; } spin_unlock(&priv->lock); + if (map) + gntdev_put_map(map); return err; } diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 4f44b347b24a..8c71ab801756 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -448,7 +448,8 @@ unsigned int gnttab_max_grant_frames(void) EXPORT_SYMBOL_GPL(gnttab_max_grant_frames); int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, - struct page **pages, unsigned int count) + struct gnttab_map_grant_ref *kmap_ops, + struct page **pages, unsigned int count) { int i, ret; pte_t *pte; @@ -488,8 +489,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, */ return -EOPNOTSUPP; } - ret = m2p_add_override(mfn, pages[i], - map_ops[i].flags & GNTMAP_contains_pte); + ret = m2p_add_override(mfn, pages[i], &kmap_ops[i]); if (ret) return ret; } diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index cef4bafc07dc..66057075d6e2 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -18,6 +18,7 @@ */ #include <linux/pci.h> +#include <linux/acpi.h> #include <xen/xen.h> #include <xen/interface/physdev.h> #include <xen/interface/xen.h> @@ -26,26 +27,85 @@ #include <asm/xen/hypercall.h> #include "../pci/pci.h" +static bool __read_mostly pci_seg_supported = true; + static int xen_add_device(struct device *dev) { int r; struct pci_dev *pci_dev = to_pci_dev(dev); +#ifdef CONFIG_PCI_IOV + struct pci_dev *physfn = pci_dev->physfn; +#endif + + if (pci_seg_supported) { + struct physdev_pci_device_add add = { + .seg = pci_domain_nr(pci_dev->bus), + .bus = pci_dev->bus->number, + .devfn = pci_dev->devfn + }; +#ifdef CONFIG_ACPI + acpi_handle handle; +#endif + +#ifdef CONFIG_PCI_IOV + if (pci_dev->is_virtfn) { + add.flags = XEN_PCI_DEV_VIRTFN; + add.physfn.bus = physfn->bus->number; + add.physfn.devfn = physfn->devfn; + } else +#endif + if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) + add.flags = XEN_PCI_DEV_EXTFN; + +#ifdef CONFIG_ACPI + handle = DEVICE_ACPI_HANDLE(&pci_dev->dev); + if (!handle) + handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge); +#ifdef CONFIG_PCI_IOV + if (!handle && pci_dev->is_virtfn) + handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge); +#endif + if (handle) { + acpi_status status; + + do { + unsigned long long pxm; + + status = acpi_evaluate_integer(handle, "_PXM", + NULL, &pxm); + if (ACPI_SUCCESS(status)) { + add.optarr[0] = pxm; + add.flags |= XEN_PCI_DEV_PXM; + break; + } + status = acpi_get_parent(handle, &handle); + } while (ACPI_SUCCESS(status)); + } +#endif /* CONFIG_ACPI */ + + r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add); + if (r != -ENOSYS) + return r; + pci_seg_supported = false; + } + if (pci_domain_nr(pci_dev->bus)) + r = -ENOSYS; #ifdef CONFIG_PCI_IOV - if (pci_dev->is_virtfn) { + else if (pci_dev->is_virtfn) { struct physdev_manage_pci_ext manage_pci_ext = { .bus = pci_dev->bus->number, .devfn = pci_dev->devfn, .is_virtfn = 1, - .physfn.bus = pci_dev->physfn->bus->number, - .physfn.devfn = pci_dev->physfn->devfn, + .physfn.bus = physfn->bus->number, + .physfn.devfn = physfn->devfn, }; r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, &manage_pci_ext); - } else + } #endif - if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { + else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { struct physdev_manage_pci_ext manage_pci_ext = { .bus = pci_dev->bus->number, .devfn = pci_dev->devfn, @@ -71,13 +131,27 @@ static int xen_remove_device(struct device *dev) { int r; struct pci_dev *pci_dev = to_pci_dev(dev); - struct physdev_manage_pci manage_pci; - manage_pci.bus = pci_dev->bus->number; - manage_pci.devfn = pci_dev->devfn; + if (pci_seg_supported) { + struct physdev_pci_device device = { + .seg = pci_domain_nr(pci_dev->bus), + .bus = pci_dev->bus->number, + .devfn = pci_dev->devfn + }; - r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, - &manage_pci); + r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove, + &device); + } else if (pci_domain_nr(pci_dev->bus)) + r = -ENOSYS; + else { + struct physdev_manage_pci manage_pci = { + .bus = pci_dev->bus->number, + .devfn = pci_dev->devfn + }; + + r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, + &manage_pci); + } return r; } @@ -96,13 +170,16 @@ static int xen_pci_notifier(struct notifier_block *nb, r = xen_remove_device(dev); break; default: - break; + return NOTIFY_DONE; } - - return r; + if (r) + dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n", + action == BUS_NOTIFY_ADD_DEVICE ? "add" : + (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?")); + return NOTIFY_OK; } -struct notifier_block device_nb = { +static struct notifier_block device_nb = { .notifier_call = xen_pci_notifier, }; diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 6e8c15a23201..c984768d98ca 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -38,6 +38,7 @@ #include <xen/swiotlb-xen.h> #include <xen/page.h> #include <xen/xen-ops.h> +#include <xen/hvc-console.h> /* * Used to do a quick range check in swiotlb_tbl_unmap_single and * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this @@ -146,8 +147,10 @@ xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs) void __init xen_swiotlb_init(int verbose) { unsigned long bytes; - int rc; + int rc = -ENOMEM; unsigned long nr_tbl; + char *m = NULL; + unsigned int repeat = 3; nr_tbl = swioltb_nr_tbl(); if (nr_tbl) @@ -156,16 +159,17 @@ void __init xen_swiotlb_init(int verbose) xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT); xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE); } - +retry: bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT; /* * Get IO TLB memory from any location. */ xen_io_tlb_start = alloc_bootmem(bytes); - if (!xen_io_tlb_start) - panic("Cannot allocate SWIOTLB buffer"); - + if (!xen_io_tlb_start) { + m = "Cannot allocate Xen-SWIOTLB buffer!\n"; + goto error; + } xen_io_tlb_end = xen_io_tlb_start + bytes; /* * And replace that memory with pages under 4GB. @@ -173,17 +177,28 @@ void __init xen_swiotlb_init(int verbose) rc = xen_swiotlb_fixup(xen_io_tlb_start, bytes, xen_io_tlb_nslabs); - if (rc) + if (rc) { + free_bootmem(__pa(xen_io_tlb_start), bytes); + m = "Failed to get contiguous memory for DMA from Xen!\n"\ + "You either: don't have the permissions, do not have"\ + " enough free memory under 4GB, or the hypervisor memory"\ + "is too fragmented!"; goto error; - + } start_dma_addr = xen_virt_to_bus(xen_io_tlb_start); swiotlb_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs, verbose); return; error: - panic("DMA(%d): Failed to exchange pages allocated for DMA with Xen! "\ - "We either don't have the permission or you do not have enough"\ - "free memory under 4GB!\n", rc); + if (repeat--) { + xen_io_tlb_nslabs = max(1024UL, /* Min is 2MB */ + (xen_io_tlb_nslabs >> 1)); + printk(KERN_INFO "Xen-SWIOTLB: Lowering to %luMB\n", + (xen_io_tlb_nslabs << IO_TLB_SHIFT) >> 20); + goto retry; + } + xen_raw_printk("%s (rc:%d)", m, rc); + panic("%s (rc:%d)", m, rc); } void * @@ -194,6 +209,8 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, int order = get_order(size); u64 dma_mask = DMA_BIT_MASK(32); unsigned long vstart; + phys_addr_t phys; + dma_addr_t dev_addr; /* * Ignore region specifiers - the kernel's ideas of @@ -209,18 +226,26 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size, vstart = __get_free_pages(flags, order); ret = (void *)vstart; + if (!ret) + return ret; + if (hwdev && hwdev->coherent_dma_mask) - dma_mask = dma_alloc_coherent_mask(hwdev, flags); + dma_mask = hwdev->coherent_dma_mask; - if (ret) { + phys = virt_to_phys(ret); + dev_addr = xen_phys_to_bus(phys); + if (((dev_addr + size - 1 <= dma_mask)) && + !range_straddles_page_boundary(phys, size)) + *dma_handle = dev_addr; + else { if (xen_create_contiguous_region(vstart, order, fls64(dma_mask)) != 0) { free_pages(vstart, order); return NULL; } - memset(ret, 0, size); *dma_handle = virt_to_machine(ret).maddr; } + memset(ret, 0, size); return ret; } EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent); @@ -230,11 +255,21 @@ xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dev_addr) { int order = get_order(size); + phys_addr_t phys; + u64 dma_mask = DMA_BIT_MASK(32); if (dma_release_from_coherent(hwdev, order, vaddr)) return; - xen_destroy_contiguous_region((unsigned long)vaddr, order); + if (hwdev && hwdev->coherent_dma_mask) + dma_mask = hwdev->coherent_dma_mask; + + phys = virt_to_phys(vaddr); + + if (((dev_addr + size - 1 > dma_mask)) || + range_straddles_page_boundary(phys, size)) + xen_destroy_contiguous_region((unsigned long)vaddr, order); + free_pages((unsigned long)vaddr, order); } EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent); @@ -278,9 +313,10 @@ dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, /* * Ensure that the address returned is DMA'ble */ - if (!dma_capable(dev, dev_addr, size)) - panic("map_single: bounce buffer is not DMA'ble"); - + if (!dma_capable(dev, dev_addr, size)) { + swiotlb_tbl_unmap_single(dev, map, size, dir); + dev_addr = 0; + } return dev_addr; } EXPORT_SYMBOL_GPL(xen_swiotlb_map_page); diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index a8031445d94e..444345afbd5c 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -15,7 +15,6 @@ #include "conf_space.h" #include "conf_space_quirks.h" -#define DRV_NAME "xen-pciback" static int permissive; module_param(permissive, bool, 0644); diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index da3cbdfcb5dc..3daf862d739d 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c @@ -15,7 +15,6 @@ struct pci_bar_info { int which; }; -#define DRV_NAME "xen-pciback" #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) @@ -25,7 +24,7 @@ static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) int ret; ret = xen_pcibk_read_config_word(dev, offset, value, data); - if (!atomic_read(&dev->enable_cnt)) + if (!pci_is_enabled(dev)) return ret; for (i = 0; i < PCI_ROM_RESOURCE; i++) { @@ -187,7 +186,7 @@ static inline void read_dev_bar(struct pci_dev *dev, bar_info->val = res[pos].start | (res[pos].flags & PCI_REGION_FLAG_MASK); - bar_info->len_val = res[pos].end - res[pos].start + 1; + bar_info->len_val = resource_size(&res[pos]); } static void *bar_init(struct pci_dev *dev, int offset) diff --git a/drivers/xen/xen-pciback/conf_space_quirks.c b/drivers/xen/xen-pciback/conf_space_quirks.c index 921a889e65eb..7476791cab40 100644 --- a/drivers/xen/xen-pciback/conf_space_quirks.c +++ b/drivers/xen/xen-pciback/conf_space_quirks.c @@ -12,7 +12,6 @@ #include "conf_space_quirks.h" LIST_HEAD(xen_pcibk_quirks); -#define DRV_NAME "xen-pciback" static inline const struct pci_device_id * match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) { @@ -36,7 +35,7 @@ static struct xen_pcibk_config_quirk *xen_pcibk_find_quirk(struct pci_dev *dev) goto out; tmp_quirk = NULL; printk(KERN_DEBUG DRV_NAME - ":quirk didn't match any device xen_pciback knows about\n"); + ": quirk didn't match any device known\n"); out: return tmp_quirk; } diff --git a/drivers/xen/xen-pciback/passthrough.c b/drivers/xen/xen-pciback/passthrough.c index 1d32a9a42c01..828dddc360df 100644 --- a/drivers/xen/xen-pciback/passthrough.c +++ b/drivers/xen/xen-pciback/passthrough.c @@ -7,13 +7,13 @@ #include <linux/list.h> #include <linux/pci.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include "pciback.h" struct passthrough_dev_data { /* Access to dev_list must be protected by lock */ struct list_head dev_list; - spinlock_t lock; + struct mutex lock; }; static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, @@ -24,9 +24,8 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, struct passthrough_dev_data *dev_data = pdev->pci_dev_data; struct pci_dev_entry *dev_entry; struct pci_dev *dev = NULL; - unsigned long flags; - spin_lock_irqsave(&dev_data->lock, flags); + mutex_lock(&dev_data->lock); list_for_each_entry(dev_entry, &dev_data->dev_list, list) { if (domain == (unsigned int)pci_domain_nr(dev_entry->dev->bus) @@ -37,7 +36,7 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, } } - spin_unlock_irqrestore(&dev_data->lock, flags); + mutex_unlock(&dev_data->lock); return dev; } @@ -48,7 +47,6 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, { struct passthrough_dev_data *dev_data = pdev->pci_dev_data; struct pci_dev_entry *dev_entry; - unsigned long flags; unsigned int domain, bus, devfn; int err; @@ -57,9 +55,9 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, return -ENOMEM; dev_entry->dev = dev; - spin_lock_irqsave(&dev_data->lock, flags); + mutex_lock(&dev_data->lock); list_add_tail(&dev_entry->list, &dev_data->dev_list); - spin_unlock_irqrestore(&dev_data->lock, flags); + mutex_unlock(&dev_data->lock); /* Publish this device. */ domain = (unsigned int)pci_domain_nr(dev->bus); @@ -76,9 +74,8 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, struct passthrough_dev_data *dev_data = pdev->pci_dev_data; struct pci_dev_entry *dev_entry, *t; struct pci_dev *found_dev = NULL; - unsigned long flags; - spin_lock_irqsave(&dev_data->lock, flags); + mutex_lock(&dev_data->lock); list_for_each_entry_safe(dev_entry, t, &dev_data->dev_list, list) { if (dev_entry->dev == dev) { @@ -88,7 +85,7 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, } } - spin_unlock_irqrestore(&dev_data->lock, flags); + mutex_unlock(&dev_data->lock); if (found_dev) pcistub_put_pci_dev(found_dev); @@ -102,7 +99,7 @@ static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev) if (!dev_data) return -ENOMEM; - spin_lock_init(&dev_data->lock); + mutex_init(&dev_data->lock); INIT_LIST_HEAD(&dev_data->dev_list); @@ -116,14 +113,14 @@ static int __xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev, { int err = 0; struct passthrough_dev_data *dev_data = pdev->pci_dev_data; - struct pci_dev_entry *dev_entry, *e, *tmp; + struct pci_dev_entry *dev_entry, *e; struct pci_dev *dev; int found; unsigned int domain, bus; - spin_lock(&dev_data->lock); + mutex_lock(&dev_data->lock); - list_for_each_entry_safe(dev_entry, tmp, &dev_data->dev_list, list) { + list_for_each_entry(dev_entry, &dev_data->dev_list, list) { /* Only publish this device as a root if none of its * parent bridges are exported */ @@ -142,16 +139,13 @@ static int __xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev, bus = (unsigned int)dev_entry->dev->bus->number; if (!found) { - spin_unlock(&dev_data->lock); err = publish_root_cb(pdev, domain, bus); if (err) break; - spin_lock(&dev_data->lock); } } - if (!err) - spin_unlock(&dev_data->lock); + mutex_unlock(&dev_data->lock); return err; } @@ -182,7 +176,7 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev, return 1; } -struct xen_pcibk_backend xen_pcibk_passthrough_backend = { +const struct xen_pcibk_backend xen_pcibk_passthrough_backend = { .name = "passthrough", .init = __xen_pcibk_init_devices, .free = __xen_pcibk_release_devices, diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index aec214ac0a14..8f06e1ed028c 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -21,8 +21,6 @@ #include "conf_space.h" #include "conf_space_quirks.h" -#define DRV_NAME "xen-pciback" - static char *pci_devs_to_hide; wait_queue_head_t xen_pcibk_aer_wait_queue; /*Add sem for sync AER handling and xen_pcibk remove/reconfigue ops, @@ -222,6 +220,8 @@ void pcistub_put_pci_dev(struct pci_dev *dev) } spin_unlock_irqrestore(&pcistub_devices_lock, flags); + if (WARN_ON(!found_psdev)) + return; /*hold this lock for avoiding breaking link between * pcistub and xen_pcibk when AER is in processing @@ -514,12 +514,9 @@ static void kill_domain_by_device(struct pcistub_device *psdev) int err; char nodename[PCI_NODENAME_MAX]; - if (!psdev) - dev_err(&psdev->dev->dev, - "device is NULL when do AER recovery/kill_domain\n"); + BUG_ON(!psdev); snprintf(nodename, PCI_NODENAME_MAX, "/local/domain/0/backend/pci/%d/0", psdev->pdev->xdev->otherend_id); - nodename[strlen(nodename)] = '\0'; again: err = xenbus_transaction_start(&xbt); @@ -605,7 +602,7 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev, if (test_bit(_XEN_PCIF_active, (unsigned long *)&psdev->pdev->sh_info->flags)) { dev_dbg(&psdev->dev->dev, - "schedule pci_conf service in xen_pcibk\n"); + "schedule pci_conf service in " DRV_NAME "\n"); xen_pcibk_test_and_schedule_op(psdev->pdev); } @@ -995,8 +992,7 @@ out: err = count; return err; } - -DRIVER_ATTR(new_slot, S_IWUSR, NULL, pcistub_slot_add); +static DRIVER_ATTR(new_slot, S_IWUSR, NULL, pcistub_slot_add); static ssize_t pcistub_slot_remove(struct device_driver *drv, const char *buf, size_t count) @@ -1015,8 +1011,7 @@ out: err = count; return err; } - -DRIVER_ATTR(remove_slot, S_IWUSR, NULL, pcistub_slot_remove); +static DRIVER_ATTR(remove_slot, S_IWUSR, NULL, pcistub_slot_remove); static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf) { @@ -1039,8 +1034,7 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf) return count; } - -DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL); +static DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL); static ssize_t pcistub_irq_handler_show(struct device_driver *drv, char *buf) { @@ -1069,8 +1063,7 @@ static ssize_t pcistub_irq_handler_show(struct device_driver *drv, char *buf) spin_unlock_irqrestore(&pcistub_devices_lock, flags); return count; } - -DRIVER_ATTR(irq_handlers, S_IRUSR, pcistub_irq_handler_show, NULL); +static DRIVER_ATTR(irq_handlers, S_IRUSR, pcistub_irq_handler_show, NULL); static ssize_t pcistub_irq_handler_switch(struct device_driver *drv, const char *buf, @@ -1106,7 +1099,8 @@ out: err = count; return err; } -DRIVER_ATTR(irq_handler_state, S_IWUSR, NULL, pcistub_irq_handler_switch); +static DRIVER_ATTR(irq_handler_state, S_IWUSR, NULL, + pcistub_irq_handler_switch); static ssize_t pcistub_quirk_add(struct device_driver *drv, const char *buf, size_t count) @@ -1170,8 +1164,8 @@ out: return count; } - -DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show, pcistub_quirk_add); +static DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show, + pcistub_quirk_add); static ssize_t permissive_add(struct device_driver *drv, const char *buf, size_t count) @@ -1236,8 +1230,8 @@ static ssize_t permissive_show(struct device_driver *drv, char *buf) spin_unlock_irqrestore(&pcistub_devices_lock, flags); return count; } - -DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show, permissive_add); +static DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show, + permissive_add); static void pcistub_exit(void) { @@ -1374,3 +1368,4 @@ module_init(xen_pcibk_init); module_exit(xen_pcibk_cleanup); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("xen-backend:pci"); diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h index a0e131a81503..e9b4011c5f9a 100644 --- a/drivers/xen/xen-pciback/pciback.h +++ b/drivers/xen/xen-pciback/pciback.h @@ -15,6 +15,8 @@ #include <linux/atomic.h> #include <xen/interface/io/pciif.h> +#define DRV_NAME "xen-pciback" + struct pci_dev_entry { struct list_head list; struct pci_dev *dev; @@ -27,7 +29,7 @@ struct pci_dev_entry { struct xen_pcibk_device { void *pci_dev_data; - spinlock_t dev_lock; + struct mutex dev_lock; struct xenbus_device *xdev; struct xenbus_watch be_watch; u8 be_watching; @@ -89,7 +91,7 @@ typedef int (*publish_pci_root_cb) (struct xen_pcibk_device *pdev, * passthrough - BDFs are exactly like in the host. */ struct xen_pcibk_backend { - char *name; + const char *name; int (*init)(struct xen_pcibk_device *pdev); void (*free)(struct xen_pcibk_device *pdev); int (*find)(struct pci_dev *pcidev, struct xen_pcibk_device *pdev, @@ -104,9 +106,9 @@ struct xen_pcibk_backend { unsigned int devfn); }; -extern struct xen_pcibk_backend xen_pcibk_vpci_backend; -extern struct xen_pcibk_backend xen_pcibk_passthrough_backend; -extern struct xen_pcibk_backend *xen_pcibk_backend; +extern const struct xen_pcibk_backend xen_pcibk_vpci_backend; +extern const struct xen_pcibk_backend xen_pcibk_passthrough_backend; +extern const struct xen_pcibk_backend *xen_pcibk_backend; static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev *dev, @@ -116,13 +118,14 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, if (xen_pcibk_backend && xen_pcibk_backend->add) return xen_pcibk_backend->add(pdev, dev, devid, publish_cb); return -1; -}; +} + static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev *dev) { if (xen_pcibk_backend && xen_pcibk_backend->free) return xen_pcibk_backend->release(pdev, dev); -}; +} static inline struct pci_dev * xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, unsigned int domain, @@ -131,7 +134,8 @@ xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, unsigned int domain, if (xen_pcibk_backend && xen_pcibk_backend->get) return xen_pcibk_backend->get(pdev, domain, bus, devfn); return NULL; -}; +} + /** * Add for domain0 PCIE-AER handling. Get guest domain/bus/devfn in xen_pcibk * before sending aer request to pcifront, so that guest could identify @@ -148,25 +152,29 @@ static inline int xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev, return xen_pcibk_backend->find(pcidev, pdev, domain, bus, devfn); return -1; -}; +} + static inline int xen_pcibk_init_devices(struct xen_pcibk_device *pdev) { if (xen_pcibk_backend && xen_pcibk_backend->init) return xen_pcibk_backend->init(pdev); return -1; -}; +} + static inline int xen_pcibk_publish_pci_roots(struct xen_pcibk_device *pdev, publish_pci_root_cb cb) { if (xen_pcibk_backend && xen_pcibk_backend->publish) return xen_pcibk_backend->publish(pdev, cb); return -1; -}; +} + static inline void xen_pcibk_release_devices(struct xen_pcibk_device *pdev) { if (xen_pcibk_backend && xen_pcibk_backend->free) return xen_pcibk_backend->free(pdev); -}; +} + /* Handles events from front-end */ irqreturn_t xen_pcibk_handle_event(int irq, void *dev_id); void xen_pcibk_do_op(struct work_struct *data); diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index 8c95c3415b75..63616d7453e6 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -10,7 +10,6 @@ #include <linux/sched.h> #include "pciback.h" -#define DRV_NAME "xen-pciback" int verbose_request; module_param(verbose_request, int, 0644); diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c index 4a42cfb0959d..46d140baebd8 100644 --- a/drivers/xen/xen-pciback/vpci.c +++ b/drivers/xen/xen-pciback/vpci.c @@ -8,16 +8,15 @@ #include <linux/list.h> #include <linux/slab.h> #include <linux/pci.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include "pciback.h" #define PCI_SLOT_MAX 32 -#define DRV_NAME "xen-pciback" struct vpci_dev_data { /* Access to dev_list must be protected by lock */ struct list_head dev_list[PCI_SLOT_MAX]; - spinlock_t lock; + struct mutex lock; }; static inline struct list_head *list_first(struct list_head *head) @@ -33,13 +32,12 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev_entry *entry; struct pci_dev *dev = NULL; struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; - unsigned long flags; if (domain != 0 || bus != 0) return NULL; if (PCI_SLOT(devfn) < PCI_SLOT_MAX) { - spin_lock_irqsave(&vpci_dev->lock, flags); + mutex_lock(&vpci_dev->lock); list_for_each_entry(entry, &vpci_dev->dev_list[PCI_SLOT(devfn)], @@ -50,7 +48,7 @@ static struct pci_dev *__xen_pcibk_get_pci_dev(struct xen_pcibk_device *pdev, } } - spin_unlock_irqrestore(&vpci_dev->lock, flags); + mutex_unlock(&vpci_dev->lock); } return dev; } @@ -71,7 +69,6 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, int err = 0, slot, func = -1; struct pci_dev_entry *t, *dev_entry; struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; - unsigned long flags; if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) { err = -EFAULT; @@ -90,7 +87,7 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, dev_entry->dev = dev; - spin_lock_irqsave(&vpci_dev->lock, flags); + mutex_lock(&vpci_dev->lock); /* Keep multi-function devices together on the virtual PCI bus */ for (slot = 0; slot < PCI_SLOT_MAX; slot++) { @@ -129,7 +126,7 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, "No more space on root virtual PCI bus"); unlock: - spin_unlock_irqrestore(&vpci_dev->lock, flags); + mutex_unlock(&vpci_dev->lock); /* Publish this device. */ if (!err) @@ -145,14 +142,13 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, int slot; struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; struct pci_dev *found_dev = NULL; - unsigned long flags; - spin_lock_irqsave(&vpci_dev->lock, flags); + mutex_lock(&vpci_dev->lock); for (slot = 0; slot < PCI_SLOT_MAX; slot++) { - struct pci_dev_entry *e, *tmp; - list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], - list) { + struct pci_dev_entry *e; + + list_for_each_entry(e, &vpci_dev->dev_list[slot], list) { if (e->dev == dev) { list_del(&e->list); found_dev = e->dev; @@ -163,7 +159,7 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, } out: - spin_unlock_irqrestore(&vpci_dev->lock, flags); + mutex_unlock(&vpci_dev->lock); if (found_dev) pcistub_put_pci_dev(found_dev); @@ -178,7 +174,7 @@ static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev) if (!vpci_dev) return -ENOMEM; - spin_lock_init(&vpci_dev->lock); + mutex_init(&vpci_dev->lock); for (slot = 0; slot < PCI_SLOT_MAX; slot++) INIT_LIST_HEAD(&vpci_dev->dev_list[slot]); @@ -222,10 +218,9 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev, struct pci_dev_entry *entry; struct pci_dev *dev = NULL; struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; - unsigned long flags; int found = 0, slot; - spin_lock_irqsave(&vpci_dev->lock, flags); + mutex_lock(&vpci_dev->lock); for (slot = 0; slot < PCI_SLOT_MAX; slot++) { list_for_each_entry(entry, &vpci_dev->dev_list[slot], @@ -243,11 +238,11 @@ static int __xen_pcibk_get_pcifront_dev(struct pci_dev *pcidev, } } } - spin_unlock_irqrestore(&vpci_dev->lock, flags); + mutex_unlock(&vpci_dev->lock); return found; } -struct xen_pcibk_backend xen_pcibk_vpci_backend = { +const struct xen_pcibk_backend xen_pcibk_vpci_backend = { .name = "vpci", .init = __xen_pcibk_init_devices, .free = __xen_pcibk_release_devices, diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index 18db31f13a4c..075525945e36 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -13,7 +13,6 @@ #include <asm/xen/pci.h> #include "pciback.h" -#define DRV_NAME "xen-pciback" #define INVALID_EVTCHN_IRQ (-1) struct workqueue_struct *xen_pcibk_wq; @@ -44,7 +43,7 @@ static struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev) pdev->xdev = xdev; dev_set_drvdata(&xdev->dev, pdev); - spin_lock_init(&pdev->dev_lock); + mutex_init(&pdev->dev_lock); pdev->sh_info = NULL; pdev->evtchn_irq = INVALID_EVTCHN_IRQ; @@ -62,14 +61,12 @@ out: static void xen_pcibk_disconnect(struct xen_pcibk_device *pdev) { - spin_lock(&pdev->dev_lock); - + mutex_lock(&pdev->dev_lock); /* Ensure the guest can't trigger our handler before removing devices */ if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) { unbind_from_irqhandler(pdev->evtchn_irq, pdev); pdev->evtchn_irq = INVALID_EVTCHN_IRQ; } - spin_unlock(&pdev->dev_lock); /* If the driver domain started an op, make sure we complete it * before releasing the shared memory */ @@ -77,13 +74,11 @@ static void xen_pcibk_disconnect(struct xen_pcibk_device *pdev) /* Note, the workqueue does not use spinlocks at all.*/ flush_workqueue(xen_pcibk_wq); - spin_lock(&pdev->dev_lock); if (pdev->sh_info != NULL) { xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_info); pdev->sh_info = NULL; } - spin_unlock(&pdev->dev_lock); - + mutex_unlock(&pdev->dev_lock); } static void free_pdev(struct xen_pcibk_device *pdev) @@ -120,9 +115,7 @@ static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref, goto out; } - spin_lock(&pdev->dev_lock); pdev->sh_info = vaddr; - spin_unlock(&pdev->dev_lock); err = bind_interdomain_evtchn_to_irqhandler( pdev->xdev->otherend_id, remote_evtchn, xen_pcibk_handle_event, @@ -132,10 +125,7 @@ static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref, "Error binding event channel to IRQ"); goto out; } - - spin_lock(&pdev->dev_lock); pdev->evtchn_irq = err; - spin_unlock(&pdev->dev_lock); err = 0; dev_dbg(&pdev->xdev->dev, "Attached!\n"); @@ -150,6 +140,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev) char *magic = NULL; + mutex_lock(&pdev->dev_lock); /* Make sure we only do this setup once */ if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateInitialised) @@ -176,7 +167,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev) if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) { xenbus_dev_fatal(pdev->xdev, -EFAULT, "version mismatch (%s/%s) with pcifront - " - "halting xen_pcibk", + "halting " DRV_NAME, magic, XEN_PCI_MAGIC); goto out; } @@ -194,6 +185,7 @@ static int xen_pcibk_attach(struct xen_pcibk_device *pdev) dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err); out: + mutex_unlock(&pdev->dev_lock); kfree(magic); @@ -371,6 +363,7 @@ static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev) dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n"); + mutex_lock(&pdev->dev_lock); /* Make sure we only reconfigure once */ if (xenbus_read_driver_state(pdev->xdev->nodename) != XenbusStateReconfiguring) @@ -508,6 +501,7 @@ static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev) } out: + mutex_unlock(&pdev->dev_lock); return 0; } @@ -564,6 +558,7 @@ static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) char dev_str[64]; char state_str[64]; + mutex_lock(&pdev->dev_lock); /* It's possible we could get the call to setup twice, so make sure * we're not already connected. */ @@ -644,10 +639,10 @@ static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) "Error switching to initialised state!"); out: + mutex_unlock(&pdev->dev_lock); if (!err) /* see if pcifront is already configured (if not, we'll wait) */ xen_pcibk_attach(pdev); - return err; } @@ -726,7 +721,7 @@ static struct xenbus_driver xenbus_xen_pcibk_driver = { .otherend_changed = xen_pcibk_frontend_changed, }; -struct xen_pcibk_backend *xen_pcibk_backend; +const struct xen_pcibk_backend *__read_mostly xen_pcibk_backend; int __init xen_pcibk_xenbus_register(void) { diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c index 6ea852e25162..d93c70857e03 100644 --- a/drivers/xen/xen-selfballoon.c +++ b/drivers/xen/xen-selfballoon.c @@ -68,6 +68,8 @@ */ #include <linux/kernel.h> +#include <linux/bootmem.h> +#include <linux/swap.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/module.h> @@ -93,6 +95,15 @@ static unsigned int selfballoon_uphysteresis __read_mostly = 1; /* In HZ, controls frequency of worker invocation. */ static unsigned int selfballoon_interval __read_mostly = 5; +/* + * Minimum usable RAM in MB for selfballooning target for balloon. + * If non-zero, it is added to totalreserve_pages and self-ballooning + * will not balloon below the sum. If zero, a piecewise linear function + * is calculated as a minimum and added to totalreserve_pages. Note that + * setting this value indiscriminately may cause OOMs and crashes. + */ +static unsigned int selfballoon_min_usable_mb; + static void selfballoon_process(struct work_struct *work); static DECLARE_DELAYED_WORK(selfballoon_worker, selfballoon_process); @@ -189,20 +200,23 @@ static int __init xen_selfballooning_setup(char *s) __setup("selfballooning", xen_selfballooning_setup); #endif /* CONFIG_FRONTSWAP */ +#define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) + /* * Use current balloon size, the goal (vm_committed_as), and hysteresis * parameters to set a new target balloon size */ static void selfballoon_process(struct work_struct *work) { - unsigned long cur_pages, goal_pages, tgt_pages; + unsigned long cur_pages, goal_pages, tgt_pages, floor_pages; + unsigned long useful_pages; bool reset_timer = false; if (xen_selfballooning_enabled) { - cur_pages = balloon_stats.current_pages; + cur_pages = totalram_pages; tgt_pages = cur_pages; /* default is no change */ goal_pages = percpu_counter_read_positive(&vm_committed_as) + - balloon_stats.current_pages - totalram_pages; + totalreserve_pages; #ifdef CONFIG_FRONTSWAP /* allow space for frontswap pages to be repatriated */ if (frontswap_selfshrinking && frontswap_enabled) @@ -217,7 +231,26 @@ static void selfballoon_process(struct work_struct *work) ((goal_pages - cur_pages) / selfballoon_uphysteresis); /* else if cur_pages == goal_pages, no change */ - balloon_set_new_target(tgt_pages); + useful_pages = max_pfn - totalreserve_pages; + if (selfballoon_min_usable_mb != 0) + floor_pages = totalreserve_pages + + MB2PAGES(selfballoon_min_usable_mb); + /* piecewise linear function ending in ~3% slope */ + else if (useful_pages < MB2PAGES(16)) + floor_pages = max_pfn; /* not worth ballooning */ + else if (useful_pages < MB2PAGES(64)) + floor_pages = totalreserve_pages + MB2PAGES(16) + + ((useful_pages - MB2PAGES(16)) >> 1); + else if (useful_pages < MB2PAGES(512)) + floor_pages = totalreserve_pages + MB2PAGES(40) + + ((useful_pages - MB2PAGES(40)) >> 3); + else /* useful_pages >= MB2PAGES(512) */ + floor_pages = totalreserve_pages + MB2PAGES(99) + + ((useful_pages - MB2PAGES(99)) >> 5); + if (tgt_pages < floor_pages) + tgt_pages = floor_pages; + balloon_set_new_target(tgt_pages + + balloon_stats.current_pages - totalram_pages); reset_timer = true; } #ifdef CONFIG_FRONTSWAP @@ -340,6 +373,31 @@ static ssize_t store_selfballoon_uphys(struct sys_device *dev, static SYSDEV_ATTR(selfballoon_uphysteresis, S_IRUGO | S_IWUSR, show_selfballoon_uphys, store_selfballoon_uphys); +SELFBALLOON_SHOW(selfballoon_min_usable_mb, "%d\n", + selfballoon_min_usable_mb); + +static ssize_t store_selfballoon_min_usable_mb(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long val; + int err; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + err = strict_strtoul(buf, 10, &val); + if (err || val == 0) + return -EINVAL; + selfballoon_min_usable_mb = val; + return count; +} + +static SYSDEV_ATTR(selfballoon_min_usable_mb, S_IRUGO | S_IWUSR, + show_selfballoon_min_usable_mb, + store_selfballoon_min_usable_mb); + + #ifdef CONFIG_FRONTSWAP SELFBALLOON_SHOW(frontswap_selfshrinking, "%d\n", frontswap_selfshrinking); @@ -421,6 +479,7 @@ static struct attribute *selfballoon_attrs[] = { &attr_selfballoon_interval.attr, &attr_selfballoon_downhysteresis.attr, &attr_selfballoon_uphysteresis.attr, + &attr_selfballoon_min_usable_mb.attr, #ifdef CONFIG_FRONTSWAP &attr_frontswap_selfshrinking.attr, &attr_frontswap_hysteresis.attr, diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index 090c61ee8fd0..2eff7a6aaa20 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -212,7 +212,9 @@ int xb_init_comms(void) printk(KERN_WARNING "XENBUS response ring is not quiescent " "(%08x:%08x): fixing up\n", intf->rsp_cons, intf->rsp_prod); - intf->rsp_cons = intf->rsp_prod; + /* breaks kdump */ + if (!reset_devices) + intf->rsp_cons = intf->rsp_prod; } if (xenbus_irq) { diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index bd2f90c9ac8b..cef9b0bf63d5 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -684,64 +684,74 @@ static int __init xenbus_probe_initcall(void) device_initcall(xenbus_probe_initcall); -static int __init xenbus_init(void) +/* Set up event channel for xenstored which is run as a local process + * (this is normally used only in dom0) + */ +static int __init xenstored_local_init(void) { int err = 0; unsigned long page = 0; + struct evtchn_alloc_unbound alloc_unbound; - DPRINTK(""); + /* Allocate Xenstore page */ + page = get_zeroed_page(GFP_KERNEL); + if (!page) + goto out_err; - err = -ENODEV; - if (!xen_domain()) - return err; + xen_store_mfn = xen_start_info->store_mfn = + pfn_to_mfn(virt_to_phys((void *)page) >> + PAGE_SHIFT); - /* - * Domain0 doesn't have a store_evtchn or store_mfn yet. - */ - if (xen_initial_domain()) { - struct evtchn_alloc_unbound alloc_unbound; + /* Next allocate a local port which xenstored can bind to */ + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = DOMID_SELF; - /* Allocate Xenstore page */ - page = get_zeroed_page(GFP_KERNEL); - if (!page) - goto out_error; + err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, + &alloc_unbound); + if (err == -ENOSYS) + goto out_err; - xen_store_mfn = xen_start_info->store_mfn = - pfn_to_mfn(virt_to_phys((void *)page) >> - PAGE_SHIFT); + BUG_ON(err); + xen_store_evtchn = xen_start_info->store_evtchn = + alloc_unbound.port; - /* Next allocate a local port which xenstored can bind to */ - alloc_unbound.dom = DOMID_SELF; - alloc_unbound.remote_dom = 0; + return 0; - err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, - &alloc_unbound); - if (err == -ENOSYS) - goto out_error; + out_err: + if (page != 0) + free_page(page); + return err; +} - BUG_ON(err); - xen_store_evtchn = xen_start_info->store_evtchn = - alloc_unbound.port; +static int __init xenbus_init(void) +{ + int err = 0; - xen_store_interface = mfn_to_virt(xen_store_mfn); + if (!xen_domain()) + return -ENODEV; + + if (xen_hvm_domain()) { + uint64_t v = 0; + err = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v); + if (err) + goto out_error; + xen_store_evtchn = (int)v; + err = hvm_get_parameter(HVM_PARAM_STORE_PFN, &v); + if (err) + goto out_error; + xen_store_mfn = (unsigned long)v; + xen_store_interface = ioremap(xen_store_mfn << PAGE_SHIFT, PAGE_SIZE); } else { - if (xen_hvm_domain()) { - uint64_t v = 0; - err = hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v); - if (err) - goto out_error; - xen_store_evtchn = (int)v; - err = hvm_get_parameter(HVM_PARAM_STORE_PFN, &v); + xen_store_evtchn = xen_start_info->store_evtchn; + xen_store_mfn = xen_start_info->store_mfn; + if (xen_store_evtchn) + xenstored_ready = 1; + else { + err = xenstored_local_init(); if (err) goto out_error; - xen_store_mfn = (unsigned long)v; - xen_store_interface = ioremap(xen_store_mfn << PAGE_SHIFT, PAGE_SIZE); - } else { - xen_store_evtchn = xen_start_info->store_evtchn; - xen_store_mfn = xen_start_info->store_mfn; - xen_store_interface = mfn_to_virt(xen_store_mfn); - xenstored_ready = 1; } + xen_store_interface = mfn_to_virt(xen_store_mfn); } /* Initialize the interface to xenstore. */ @@ -760,12 +770,7 @@ static int __init xenbus_init(void) proc_mkdir("xen", NULL); #endif - return 0; - - out_error: - if (page != 0) - free_page(page); - + out_error: return err; } diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 60adf919d78d..32417b5064fd 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -104,8 +104,6 @@ static int xenbus_uevent_backend(struct device *dev, xdev = to_xenbus_device(dev); bus = container_of(xdev->dev.bus, struct xen_bus_type, bus); - if (xdev == NULL) - return -ENODEV; if (add_uevent_var(env, "MODALIAS=xen-backend:%s", xdev->devicetype)) return -ENOMEM; diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index ed2ba474a560..540587e18a94 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -248,10 +248,131 @@ int __xenbus_register_frontend(struct xenbus_driver *drv, } EXPORT_SYMBOL_GPL(__xenbus_register_frontend); +static DECLARE_WAIT_QUEUE_HEAD(backend_state_wq); +static int backend_state; + +static void xenbus_reset_backend_state_changed(struct xenbus_watch *w, + const char **v, unsigned int l) +{ + xenbus_scanf(XBT_NIL, v[XS_WATCH_PATH], "", "%i", &backend_state); + printk(KERN_DEBUG "XENBUS: backend %s %s\n", + v[XS_WATCH_PATH], xenbus_strstate(backend_state)); + wake_up(&backend_state_wq); +} + +static void xenbus_reset_wait_for_backend(char *be, int expected) +{ + long timeout; + timeout = wait_event_interruptible_timeout(backend_state_wq, + backend_state == expected, 5 * HZ); + if (timeout <= 0) + printk(KERN_INFO "XENBUS: backend %s timed out.\n", be); +} + +/* + * Reset frontend if it is in Connected or Closed state. + * Wait for backend to catch up. + * State Connected happens during kdump, Closed after kexec. + */ +static void xenbus_reset_frontend(char *fe, char *be, int be_state) +{ + struct xenbus_watch be_watch; + + printk(KERN_DEBUG "XENBUS: backend %s %s\n", + be, xenbus_strstate(be_state)); + + memset(&be_watch, 0, sizeof(be_watch)); + be_watch.node = kasprintf(GFP_NOIO | __GFP_HIGH, "%s/state", be); + if (!be_watch.node) + return; + + be_watch.callback = xenbus_reset_backend_state_changed; + backend_state = XenbusStateUnknown; + + printk(KERN_INFO "XENBUS: triggering reconnect on %s\n", be); + register_xenbus_watch(&be_watch); + + /* fall through to forward backend to state XenbusStateInitialising */ + switch (be_state) { + case XenbusStateConnected: + xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateClosing); + xenbus_reset_wait_for_backend(be, XenbusStateClosing); + + case XenbusStateClosing: + xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateClosed); + xenbus_reset_wait_for_backend(be, XenbusStateClosed); + + case XenbusStateClosed: + xenbus_printf(XBT_NIL, fe, "state", "%d", XenbusStateInitialising); + xenbus_reset_wait_for_backend(be, XenbusStateInitWait); + } + + unregister_xenbus_watch(&be_watch); + printk(KERN_INFO "XENBUS: reconnect done on %s\n", be); + kfree(be_watch.node); +} + +static void xenbus_check_frontend(char *class, char *dev) +{ + int be_state, fe_state, err; + char *backend, *frontend; + + frontend = kasprintf(GFP_NOIO | __GFP_HIGH, "device/%s/%s", class, dev); + if (!frontend) + return; + + err = xenbus_scanf(XBT_NIL, frontend, "state", "%i", &fe_state); + if (err != 1) + goto out; + + switch (fe_state) { + case XenbusStateConnected: + case XenbusStateClosed: + printk(KERN_DEBUG "XENBUS: frontend %s %s\n", + frontend, xenbus_strstate(fe_state)); + backend = xenbus_read(XBT_NIL, frontend, "backend", NULL); + if (!backend || IS_ERR(backend)) + goto out; + err = xenbus_scanf(XBT_NIL, backend, "state", "%i", &be_state); + if (err == 1) + xenbus_reset_frontend(frontend, backend, be_state); + kfree(backend); + break; + default: + break; + } +out: + kfree(frontend); +} + +static void xenbus_reset_state(void) +{ + char **devclass, **dev; + int devclass_n, dev_n; + int i, j; + + devclass = xenbus_directory(XBT_NIL, "device", "", &devclass_n); + if (IS_ERR(devclass)) + return; + + for (i = 0; i < devclass_n; i++) { + dev = xenbus_directory(XBT_NIL, "device", devclass[i], &dev_n); + if (IS_ERR(dev)) + continue; + for (j = 0; j < dev_n; j++) + xenbus_check_frontend(devclass[i], dev[j]); + kfree(dev); + } + kfree(devclass); +} + static int frontend_probe_and_watch(struct notifier_block *notifier, unsigned long event, void *data) { + /* reset devices in Connected or Closed state */ + if (xen_hvm_domain()) + xenbus_reset_state(); /* Enumerate devices in xenstore and watch for changes. */ xenbus_probe_devices(&xenbus_frontend); register_xenbus_watch(&fe_watch); diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 5534690075af..b3b8f2f3ad10 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -45,6 +45,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <xen/xenbus.h> +#include <xen/xen.h> #include "xenbus_comms.h" struct xs_stored_msg { @@ -620,6 +621,15 @@ static struct xenbus_watch *find_watch(const char *token) return NULL; } +static void xs_reset_watches(void) +{ + int err; + + err = xs_error(xs_single(XBT_NIL, XS_RESET_WATCHES, "", NULL)); + if (err && err != -EEXIST) + printk(KERN_WARNING "xs_reset_watches failed: %d\n", err); +} + /* Register callback to watch this node. */ int register_xenbus_watch(struct xenbus_watch *watch) { @@ -638,8 +648,7 @@ int register_xenbus_watch(struct xenbus_watch *watch) err = xs_watch(watch->node, token); - /* Ignore errors due to multiple registration. */ - if ((err != 0) && (err != -EEXIST)) { + if (err) { spin_lock(&watches_lock); list_del(&watch->list); spin_unlock(&watches_lock); @@ -897,5 +906,9 @@ int xs_init(void) if (IS_ERR(task)) return PTR_ERR(task); + /* shutdown watches for kexec boot */ + if (xen_hvm_domain()) + xs_reset_watches(); + return 0; } diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c index 7ee2b6e71786..229624f867d3 100644 --- a/drivers/zorro/zorro-driver.c +++ b/drivers/zorro/zorro-driver.c @@ -37,6 +37,7 @@ zorro_match_device(const struct zorro_device_id *ids, } return NULL; } +EXPORT_SYMBOL(zorro_match_device); static int zorro_device_probe(struct device *dev) @@ -91,6 +92,7 @@ int zorro_register_driver(struct zorro_driver *drv) /* register with core */ return driver_register(&drv->driver); } +EXPORT_SYMBOL(zorro_register_driver); /** @@ -107,6 +109,7 @@ void zorro_unregister_driver(struct zorro_driver *drv) { driver_unregister(&drv->driver); } +EXPORT_SYMBOL(zorro_unregister_driver); /** @@ -168,6 +171,7 @@ struct bus_type zorro_bus_type = { .probe = zorro_device_probe, .remove = zorro_device_remove, }; +EXPORT_SYMBOL(zorro_bus_type); static int __init zorro_driver_init(void) @@ -177,7 +181,3 @@ static int __init zorro_driver_init(void) postcore_initcall(zorro_driver_init); -EXPORT_SYMBOL(zorro_match_device); -EXPORT_SYMBOL(zorro_register_driver); -EXPORT_SYMBOL(zorro_unregister_driver); -EXPORT_SYMBOL(zorro_bus_type); |