diff options
author | Kevin Hilman <khilman@linaro.org> | 2015-09-10 13:26:56 -0700 |
---|---|---|
committer | Kevin Hilman <khilman@linaro.org> | 2015-09-10 13:26:56 -0700 |
commit | 070565aed13c16e108f35bcedb8ad22782683735 (patch) | |
tree | 8b3de1bf2101c8c1c93822f5583c5838fef8c8cc | |
parent | 829e8333fe5de8b1f43c042d213fe45358e4ee4d (diff) | |
parent | 3ac97f2411ad66f872bd0fe401ad94af5ff4d7d3 (diff) |
Merge branch 'android-3.18' of https://android.googlesource.com/kernel/common into linux-linaro-lsk-v3.18-android
* 'android-3.18' of https://android.googlesource.com/kernel/common: (26 commits)
net: fix crash in tcp_nuke_addr()
net: xt_qtaguid/xt_socket: fix refcount underflow and crash
net: fix iterating over hashtable in tcp_nuke_addr()
nf: IDLETIMER: fix lockdep warning
ANDROID: usb: gadget: create F_midi device
usb: gadget: midi: avoid redundant f_midi_set_alt() call
usb: gadget: f_midi: fix error recovery path
usb: gadget: f_midi: fix segfault when reading empty id
usb: gadget: fix misspelling of current function in string
usb: gadget: midi: f_midi_alloc() can be static
usb: gadget: f_midi: add configfs support
usb: gadget: f_midi: use usb_gstrings_attach
usb: gadget: f_midi: remove compatibility layer
usb: gadget: f_midi: convert to new function interface with backward compatibility
usb: gadget: f_midi: check kstrdup() return value
usb: gadget: f_midi: enable use of the index parameter
usb: gadget: configfs: Fix interfaces array NULL-termination
usb: gadget: Add device attribute to determine gadget state
usb: phy: fix dual role sysfs build if kernel modules are supported
ion: Handle the memory mapping correctly on x86
...
Conflicts:
drivers/usb/gadget/Kconfig
Linaro commit 0fdc146d0ee1 (HACK: usb: gadget: fix android
composite driver build) removed some Kconfig options causing
add/remove conflicts with AOSP commits adding new options.
Resolution: take both sides (removals and new additions)
25 files changed, 1297 insertions, 134 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi b/Documentation/ABI/testing/configfs-usb-gadget-midi new file mode 100644 index 000000000000..6b341df7249c --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-midi @@ -0,0 +1,12 @@ +What: /config/usb-gadget/gadget/functions/midi.name +Date: Nov 2014 +KernelVersion: 3.19 +Description: + The attributes: + + index - index value for the USB MIDI adapter + id - ID string for the USB MIDI adapter + buflen - MIDI buffer length + qlen - USB read request queue length + in_ports - number of MIDI input ports + out_ports - number of MIDI output ports diff --git a/Documentation/ABI/testing/sysfs-class-dual-role-usb b/Documentation/ABI/testing/sysfs-class-dual-role-usb new file mode 100644 index 000000000000..a900fd75430c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-dual-role-usb @@ -0,0 +1,71 @@ +What: /sys/class/dual_role_usb/.../ +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + Provide a generic interface to monitor and change + the state of dual role usb ports. The name here + refers to the name mentioned in the + dual_role_phy_desc that is passed while registering + the dual_role_phy_intstance through + devm_dual_role_instance_register. + +What: /sys/class/dual_role_usb/.../supported_modes +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + This is a static node, once initialized this + is not expected to change during runtime. "dfp" + refers to "downstream facing port" i.e. port can + only act as host. "ufp" refers to "upstream + facing port" i.e. port can only act as device. + "dfp ufp" refers to "dual role port" i.e. the port + can either be a host port or a device port. + +What: /sys/class/dual_role_usb/.../mode +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + The mode node refers to the current mode in which the + port is operating. "dfp" for host ports. "ufp" for device + ports and "none" when cable is not connected. + + On devices where the USB mode is software-controllable, + userspace can change the mode by writing "dfp" or "ufp". + On devices where the USB mode is fixed in hardware, + this attribute is read-only. + +What: /sys/class/dual_role_usb/.../power_role +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + The power_role node mentions whether the port + is "sink"ing or "source"ing power. "none" if + they are not connected. + + On devices implementing USB Power Delivery, + userspace can control the power role by writing "sink" or + "source". On devices without USB-PD, this attribute is + read-only. + +What: /sys/class/dual_role_usb/.../data_role +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + The data_role node mentions whether the port + is acting as "host" or "device" for USB data connection. + "none" if there is no active data link. + + On devices implementing USB Power Delivery, userspace + can control the data role by writing "host" or "device". + On devices without USB-PD, this attribute is read-only + +What: /sys/class/dual_role_usb/.../powers_vconn +Date: June 2015 +Contact: Badhri Jagan Sridharan<badhri@google.com> +Description: + The powers_vconn node mentions whether the port + is supplying power for VCONN pin. + + On devices with software control of VCONN, + userspace can disable the power supply to VCONN by writing "n", + or enable the power supply by writing "y". diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 92886c56db9d..43fe1434f34e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -89,6 +89,10 @@ config MMU config NO_IOPORT_MAP def_bool y if !PCI +config ILLEGAL_POINTER_VALUE + hex + default 0xdead000000000000 + config STACKTRACE_SUPPORT def_bool y diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig index 345234624492..301948cc48bd 100644 --- a/drivers/staging/android/ion/Kconfig +++ b/drivers/staging/android/ion/Kconfig @@ -33,3 +33,10 @@ config ION_TEGRA help Choose this option if you wish to use ion on an nVidia Tegra. +config ION_POOL_CACHE_POLICY + bool "Ion set page pool cache policy" + depends on ION + default y if X86 + help + Choose this option if need to explicity set cache policy of the + pages in the page pool. diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c index 9156d8238c97..e702ce6461fc 100644..100755 --- a/drivers/staging/android/ion/ion_carveout_heap.c +++ b/drivers/staging/android/ion/ion_carveout_heap.c @@ -167,7 +167,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) if (!carveout_heap) return ERR_PTR(-ENOMEM); - carveout_heap->pool = gen_pool_create(12, -1); + carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1); if (!carveout_heap->pool) { kfree(carveout_heap); return ERR_PTR(-ENOMEM); diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index 4b88f11e52d3..b0217488ef92 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -30,6 +30,8 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) if (!page) return NULL; + ion_page_pool_alloc_set_cache_policy(pool, page); + ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order, DMA_BIDIRECTIONAL); return page; @@ -38,6 +40,7 @@ static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool) static void ion_page_pool_free_pages(struct ion_page_pool *pool, struct page *page) { + ion_page_pool_free_set_cache_policy(pool, page); __free_pages(page, pool->order); } @@ -103,6 +106,11 @@ void ion_page_pool_free(struct ion_page_pool *pool, struct page *page) ion_page_pool_free_pages(pool, page); } +void ion_page_pool_free_immediate(struct ion_page_pool *pool, struct page *page) +{ + ion_page_pool_free_pages(pool, page); +} + static int ion_page_pool_total(struct ion_page_pool *pool, bool high) { int count = pool->low_count; diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h index 18a5f93e13b7..0e3b8a6f3bf7 100644 --- a/drivers/staging/android/ion/ion_priv.h +++ b/drivers/staging/android/ion/ion_priv.h @@ -26,6 +26,9 @@ #include <linux/sched.h> #include <linux/shrinker.h> #include <linux/types.h> +#ifdef CONFIG_ION_POOL_CACHE_POLICY +#include <asm/cacheflush.h> +#endif #include "ion.h" @@ -380,6 +383,37 @@ struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order); void ion_page_pool_destroy(struct ion_page_pool *); struct page *ion_page_pool_alloc(struct ion_page_pool *); void ion_page_pool_free(struct ion_page_pool *, struct page *); +void ion_page_pool_free_immediate(struct ion_page_pool *, struct page *); + +#ifdef CONFIG_ION_POOL_CACHE_POLICY +static inline void ion_page_pool_alloc_set_cache_policy + (struct ion_page_pool *pool, + struct page *page){ + void *va = page_address(page); + + if (va) + set_memory_wc((unsigned long)va, 1 << pool->order); +} + +static inline void ion_page_pool_free_set_cache_policy + (struct ion_page_pool *pool, + struct page *page){ + void *va = page_address(page); + + if (va) + set_memory_wb((unsigned long)va, 1 << pool->order); + +} +#else +static inline void ion_page_pool_alloc_set_cache_policy + (struct ion_page_pool *pool, + struct page *page){ } + +static inline void ion_page_pool_free_set_cache_policy + (struct ion_page_pool *pool, + struct page *page){ } +#endif + /** ion_page_pool_shrink - shrinks the size of the memory cached in the pool * @pool: the pool diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index da2a63c0a9ba..1f9feb7480b2 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -85,8 +85,10 @@ static void free_buffer_page(struct ion_system_heap *heap, if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)) { struct ion_page_pool *pool = heap->pools[order_to_index(order)]; - - ion_page_pool_free(pool, page); + if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) + ion_page_pool_free_immediate(pool, page); + else + ion_page_pool_free(pool, page); } else { __free_pages(page, order); } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b3378567cc4d..2d9ee85903e0 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -202,6 +202,9 @@ config USB_F_AUDIO_SRC config USB_F_ACC tristate +config USB_F_MIDI + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -410,6 +413,20 @@ config USB_CONFIGFS_UEVENT state changes. The gadget can be in any of the following three states: "CONNECTED/DISCONNECTED/CONFIGURED" +config USB_CONFIGFS_F_MIDI + boolean "MIDI function" + depends on USB_CONFIGFS + depends on SND + select USB_LIBCOMPOSITE + select SND_RAWMIDI + select USB_F_MIDI + help + The MIDI Function acts as a USB Audio device, with one MIDI + input and one MIDI output. These MIDI jacks appear as + a sound "card" in the ALSA sound system. Other MIDI + connections can then be made on the gadget system, using + ALSA's aconnect utility etc. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 1157a532a34d..a8c90eb085f7 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1646,6 +1646,54 @@ static struct device_attribute *android_usb_attributes[] = { &dev_attr_state, NULL }; + +static int android_device_create(struct gadget_info *gi) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + INIT_WORK(&gi->work, android_work); + android_device = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + return PTR_ERR(android_device); + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + int err; + + err = device_create_file(android_device, attr); + if (err) { + device_destroy(android_device->class, + android_device->devt); + return err; + } + } + + return 0; +} + +static void android_device_destroy(void) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + device_destroy(android_device->class, android_device->devt); +} +#else +static inline int android_device_create(struct gadget_info *gi) +{ + return 0; +} + +static inline void android_device_destroy(void) +{ +} #endif static struct config_group *gadgets_make( @@ -1695,25 +1743,11 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; -#ifdef CONFIG_USB_CONFIGFS_UEVENT - INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) + if (!gi->composite.gadget_driver.function) goto err; - dev_set_drvdata(android_device, gi); - - attrs = android_usb_attributes; - while ((attr = *attrs++)) { - err = device_create_file(android_device, attr); - if (err) - goto err1; - } -#endif - - if (!gi->composite.gadget_driver.function) - goto err1; + if (android_device_create(gi) < 0) + goto err; #ifdef CONFIG_USB_OTG gi->otg.bLength = sizeof(struct usb_otg_descriptor); @@ -1725,15 +1759,6 @@ static struct config_group *gadgets_make( &gadget_root_type); return &gi->group; -err1: -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - - device_destroy(android_device->class, - android_device->devt); -#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1745,13 +1770,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item) struct device_attribute *attr; config_item_put(item); - -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); -#endif + android_device_destroy(); } static struct configfs_group_operations gadgets_ops = { diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index cc428c4d2b9d..5d0a437a74cd 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -48,3 +48,5 @@ usb_f_accessory-y := f_accessory.o obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o +usb_f_midi-y := f_midi.o +obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 59ab62c92b66..6f71a698b13f 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -373,8 +373,9 @@ static int hidg_setup(struct usb_function *f, value = __le16_to_cpu(ctrl->wValue); length = __le16_to_cpu(ctrl->wLength); - VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " - "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); + VDBG(cdev, + "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n", + __func__, ctrl->bRequestType, ctrl->bRequest, value); switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..ee1bfc905fb9 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -20,6 +20,7 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/device.h> @@ -33,6 +34,7 @@ #include <linux/usb/midi.h> #include "u_f.h" +#include "u_midi.h" MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); @@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); /* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { +static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = { }; /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { +static 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, @@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { }; /* B.4.1 Standard MS Interface Descriptor */ -static struct usb_interface_descriptor ms_interface_desc __initdata = { +static struct usb_interface_descriptor ms_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = { }; /* B.4.2 Class-Specific MS Interface Descriptor */ -static struct usb_ms_header_descriptor ms_header_desc __initdata = { +static struct usb_ms_header_descriptor ms_header_desc = { .bLength = USB_DT_MS_HEADER_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, @@ -327,6 +329,10 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) unsigned i; int err; + /* For Control Device interface we do nothing */ + if (intf == 0) + return 0; + err = f_midi_start_ep(midi, f, midi->in_ep); if (err) return err; @@ -387,29 +393,6 @@ static void f_midi_disable(struct usb_function *f) 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_all_descriptors(f); - kfree(midi); -} - static int f_midi_snd_free(struct snd_device *device) { return 0; @@ -541,7 +524,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) req = midi_alloc_ep_req(ep, midi->buflen); if (!req) { - ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + ERROR(midi, "%s: alloc_ep_request failed\n", __func__); return; } req->length = 0; @@ -654,6 +637,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = { .trigger = f_midi_out_trigger }; +static inline void f_midi_unregister_card(struct f_midi *midi) +{ + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } +} + /* register as a sound "card" */ static int f_midi_register_card(struct f_midi *midi) { @@ -715,17 +706,13 @@ static int f_midi_register_card(struct f_midi *midi) return 0; fail: - if (midi->card) { - snd_card_free(midi->card); - midi->card = NULL; - } + f_midi_unregister_card(midi); return err; } /* MIDI function driver setup/binding */ -static int __init -f_midi_bind(struct usb_configuration *c, struct usb_function *f) +static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_descriptor_header **midi_function; struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; @@ -734,15 +721,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS]; struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); + struct usb_string *us; int status, n, jack = 1, i = 0; + midi->gadget = cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + status = f_midi_register_card(midi); + if (status < 0) + goto fail_register; + /* 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; + us = usb_gstrings_attach(c->cdev, midi_strings, + ARRAY_SIZE(midi_string_defs)); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; } + ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id; /* We have two interfaces, AudioControl and MIDIStreaming */ status = usb_interface_id(c, f); @@ -892,6 +887,8 @@ fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); fail: + f_midi_unregister_card(midi); +fail_register: /* we might as well release our claims on endpoints */ if (midi->out_ep) midi->out_ep->driver_data = NULL; @@ -903,42 +900,305 @@ fail: 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) +static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_midi_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_midi_opts); +CONFIGFS_ATTR_OPS(f_midi_opts); + +static void midi_attr_release(struct config_item *item) +{ + struct f_midi_opts *opts = to_f_midi_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations midi_item_ops = { + .release = midi_attr_release, + .show_attribute = f_midi_opts_attr_show, + .store_attribute = f_midi_opts_attr_store, +}; + +#define F_MIDI_OPT(name, test_limit, limit) \ +static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%d\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (test_limit && num > limit) { \ + ret = -EINVAL; \ + goto end; \ + } \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_midi_opts_attribute f_midi_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \ + f_midi_opts_##name##_store) + +F_MIDI_OPT(index, true, SNDRV_CARDS); +F_MIDI_OPT(buflen, false, 0); +F_MIDI_OPT(qlen, false, 0); +F_MIDI_OPT(in_ports, true, MAX_PORTS); +F_MIDI_OPT(out_ports, true, MAX_PORTS); + +static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + if (opts->id) { + result = strlcpy(page, opts->id, PAGE_SIZE); + } else { + page[0] = 0; + result = 0; + } + + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts, + const char *page, size_t len) +{ + int ret; + char *c; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + c = kstrndup(page, len, GFP_KERNEL); + if (!c) { + ret = -ENOMEM; + goto end; + } + if (opts->id_allocated) + kfree(opts->id); + opts->id = c; + opts->id_allocated = true; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_midi_opts_attribute f_midi_opts_id = + __CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show, + f_midi_opts_id_store); + +static struct configfs_attribute *midi_attrs[] = { + &f_midi_opts_index.attr, + &f_midi_opts_buflen.attr, + &f_midi_opts_qlen.attr, + &f_midi_opts_in_ports.attr, + &f_midi_opts_out_ports.attr, + &f_midi_opts_id.attr, + NULL, +}; + +static struct config_item_type midi_func_type = { + .ct_item_ops = &midi_item_ops, + .ct_attrs = midi_attrs, + .ct_owner = THIS_MODULE, +}; + +static void f_midi_free_inst(struct usb_function_instance *f) +{ + struct f_midi_opts *opts; + + opts = container_of(f, struct f_midi_opts, func_inst); + + if (opts->id_allocated) + kfree(opts->id); + + kfree(opts); +} + +#ifdef CONFIG_USB_CONFIGFS_UEVENT +extern struct device *create_function_device(char *name); +static ssize_t alsa_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct usb_function_instance *fi_midi = dev_get_drvdata(dev); struct f_midi *midi; + + if (!fi_midi->f) + dev_warn(dev, "f_midi: function not set\n"); + + if (fi_midi && fi_midi->f) { + midi = func_to_midi(fi_midi->f); + if (midi->rmidi && midi->rmidi->card) + return sprintf(buf, "%d %d\n", + midi->rmidi->card->number, midi->rmidi->device); + } + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", -1, -1); +} + +static DEVICE_ATTR(alsa, S_IRUGO, alsa_show, NULL); + +static struct device_attribute *alsa_function_attributes[] = { + &dev_attr_alsa, + NULL +}; + +static int create_alsa_device(struct usb_function_instance *fi) +{ + struct device *dev; + struct device_attribute **attrs; + struct device_attribute *attr; + int err = 0; + + dev = create_function_device("f_midi"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + attrs = alsa_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + device_destroy(dev->class, dev->devt); + return -EINVAL; + } + } + dev_set_drvdata(dev, fi); + return 0; +} +#else +static int create_alsa_device(struct usb_function_instance *fi) +{ + return 0; +} +#endif + +static struct usb_function_instance *f_midi_alloc_inst(void) +{ + struct f_midi_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = f_midi_free_inst; + opts->index = SNDRV_DEFAULT_IDX1; + opts->id = SNDRV_DEFAULT_STR1; + opts->buflen = 256; + opts->qlen = 32; + opts->in_ports = 1; + opts->out_ports = 1; + + if (create_alsa_device(&opts->func_inst)) { + kfree(opts); + return ERR_PTR(-ENODEV); + } + + config_group_init_type_name(&opts->func_inst.group, "", + &midi_func_type); + + return &opts->func_inst; +} + +static void f_midi_free(struct usb_function *f) +{ + struct f_midi *midi; + struct f_midi_opts *opts; + int i; + + midi = func_to_midi(f); + opts = container_of(f->fi, struct f_midi_opts, func_inst); + kfree(midi->id); + mutex_lock(&opts->lock); + for (i = opts->in_ports - 1; i >= 0; --i) + kfree(midi->in_port[i]); + kfree(midi); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +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); + + usb_free_all_descriptors(f); +} + +static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) +{ + struct f_midi *midi; + struct f_midi_opts *opts; int status, i; + opts = container_of(fi, struct f_midi_opts, func_inst); + + mutex_lock(&opts->lock); /* sanity check */ - if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) - return -EINVAL; + if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) { + mutex_unlock(&opts->lock); + return ERR_PTR(-EINVAL); + } /* allocate and initialize one new instance */ - midi = kzalloc(sizeof *midi, GFP_KERNEL); + midi = kzalloc(sizeof(*midi), GFP_KERNEL); if (!midi) { - status = -ENOMEM; - goto fail; + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); } - for (i = 0; i < in_ports; i++) { + for (i = 0; i < opts->in_ports; i++) { struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { status = -ENOMEM; + mutex_unlock(&opts->lock); goto setup_fail; } @@ -948,39 +1208,36 @@ int __init f_midi_bind_config(struct usb_configuration *c, 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) + midi->id = kstrdup(opts->id, GFP_KERNEL); + if (opts->id && !midi->id) { + status = -ENOMEM; + mutex_unlock(&opts->lock); goto setup_fail; - - return 0; + } + midi->in_ports = opts->in_ports; + midi->out_ports = opts->out_ports; + midi->index = opts->index; + midi->buflen = opts->buflen; + midi->qlen = opts->qlen; + ++opts->refcnt; + mutex_unlock(&opts->lock); + + midi->func.name = "gmidi function"; + 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->func.free_func = f_midi_free; + + fi->f = &midi->func; + return &midi->func; setup_fail: for (--i; i >= 0; i--) kfree(midi->in_port[i]); kfree(midi); -fail: - return status; + return ERR_PTR(status); } +DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc); diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h new file mode 100644 index 000000000000..22510189758e --- /dev/null +++ b/drivers/usb/gadget/function/u_midi.h @@ -0,0 +1,40 @@ +/* + * u_midi.h + * + * Utility definitions for the midi function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * 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. + */ + +#ifndef U_MIDI_H +#define U_MIDI_H + +#include <linux/usb/composite.h> + +struct f_midi_opts { + struct usb_function_instance func_inst; + int index; + char *id; + bool id_allocated; + unsigned int in_ports; + unsigned int out_ports; + unsigned int buflen; + unsigned int qlen; + + /* + * Protect the data form concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_MIDI_H */ + diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index 3d696b86ff76..b7c631d828de 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -37,6 +37,7 @@ #include "gadget_chips.h" +#define USBF_MIDI_INCLUDED #include "f_midi.c" /*-------------------------------------------------------------------------*/ @@ -115,6 +116,9 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function_instance *fi_midi; +static struct usb_function *f_midi; + static int __exit midi_unbind(struct usb_composite_dev *dev) { return 0; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index b03889b901e7..77ef4bb73d41 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -221,4 +221,13 @@ config USB_ULPI_VIEWPORT Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). +config DUAL_ROLE_USB_INTF + bool "Generic DUAL ROLE sysfs interface" + depends on SYSFS && USB_PHY + help + A generic sysfs interface to track and change the state of + dual role usb phys. The usb phy drivers can register to + this interface to expose it capabilities to the userspace + and thereby allowing userspace to change the port mode. + endmenu diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 7553f03dfaf9..5cd78717b13f 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,6 +4,8 @@ obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o +obj-$(CONFIG_DUAL_ROLE_USB_INTF) += class-dual-role.o + # transceiver drivers, keep the list sorted obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c new file mode 100644 index 000000000000..51fcb545a9d5 --- /dev/null +++ b/drivers/usb/phy/class-dual-role.c @@ -0,0 +1,529 @@ +/* + * class-dual-role.c + * + * Copyright (C) 2015 Google, Inc. + * + * 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/ctype.h> +#include <linux/device.h> +#include <linux/usb/class-dual-role.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/types.h> + +#define DUAL_ROLE_NOTIFICATION_TIMEOUT 2000 + +static ssize_t dual_role_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t dual_role_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +#define DUAL_ROLE_ATTR(_name) \ +{ \ + .attr = { .name = #_name }, \ + .show = dual_role_show_property, \ + .store = dual_role_store_property, \ +} + +static struct device_attribute dual_role_attrs[] = { + DUAL_ROLE_ATTR(supported_modes), + DUAL_ROLE_ATTR(mode), + DUAL_ROLE_ATTR(power_role), + DUAL_ROLE_ATTR(data_role), + DUAL_ROLE_ATTR(powers_vconn), +}; + +struct class *dual_role_class; +EXPORT_SYMBOL_GPL(dual_role_class); + +static struct device_type dual_role_dev_type; + +static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper) +{ + char *ret, *ustr; + + ustr = ret = kmalloc(strlen(str) + 1, gfp); + + if (!ret) + return NULL; + + while (*str) + *ustr++ = to_upper ? toupper(*str++) : tolower(*str++); + + *ustr = 0; + + return ret; +} + +static void dual_role_changed_work(struct work_struct *work) +{ + struct dual_role_phy_instance *dual_role = + container_of(work, struct dual_role_phy_instance, + changed_work); + + dev_dbg(&dual_role->dev, "%s\n", __func__); + kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); +} + +void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) +{ + dev_dbg(&dual_role->dev, "%s\n", __func__); + pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT); + schedule_work(&dual_role->changed_work); +} +EXPORT_SYMBOL_GPL(dual_role_instance_changed); + +int dual_role_get_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val) +{ + return dual_role->desc->get_property(dual_role, prop, val); +} +EXPORT_SYMBOL_GPL(dual_role_get_property); + +int dual_role_set_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + if (!dual_role->desc->set_property) + return -ENODEV; + + return dual_role->desc->set_property(dual_role, prop, val); +} +EXPORT_SYMBOL_GPL(dual_role_set_property); + +int dual_role_property_is_writeable(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop) +{ + if (!dual_role->desc->property_is_writeable) + return -ENODEV; + + return dual_role->desc->property_is_writeable(dual_role, prop); +} +EXPORT_SYMBOL_GPL(dual_role_property_is_writeable); + +static void dual_role_dev_release(struct device *dev) +{ + struct dual_role_phy_instance *dual_role = + container_of(dev, struct dual_role_phy_instance, dev); + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); + kfree(dual_role); +} + +static struct dual_role_phy_instance *__must_check +__dual_role_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + struct device *dev; + struct dual_role_phy_instance *dual_role; + int rc; + + dual_role = kzalloc(sizeof(*dual_role), GFP_KERNEL); + if (!dual_role) + return ERR_PTR(-ENOMEM); + + dev = &dual_role->dev; + + device_initialize(dev); + + dev->class = dual_role_class; + dev->type = &dual_role_dev_type; + dev->parent = parent; + dev->release = dual_role_dev_release; + dev_set_drvdata(dev, dual_role); + dual_role->desc = desc; + + rc = dev_set_name(dev, "%s", desc->name); + if (rc) + goto dev_set_name_failed; + + INIT_WORK(&dual_role->changed_work, dual_role_changed_work); + + rc = device_init_wakeup(dev, true); + if (rc) + goto wakeup_init_failed; + + rc = device_add(dev); + if (rc) + goto device_add_failed; + + dual_role_instance_changed(dual_role); + + return dual_role; + +device_add_failed: + device_init_wakeup(dev, false); +wakeup_init_failed: +dev_set_name_failed: + put_device(dev); + kfree(dual_role); + + return ERR_PTR(rc); +} + +static void dual_role_instance_unregister(struct dual_role_phy_instance + *dual_role) +{ + cancel_work_sync(&dual_role->changed_work); + device_init_wakeup(&dual_role->dev, false); + device_unregister(&dual_role->dev); +} + +static void devm_dual_role_release(struct device *dev, void *res) +{ + struct dual_role_phy_instance **dual_role = res; + + dual_role_instance_unregister(*dual_role); +} + +struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + struct dual_role_phy_instance **ptr, *dual_role; + + ptr = devres_alloc(devm_dual_role_release, sizeof(*ptr), GFP_KERNEL); + + if (!ptr) + return ERR_PTR(-ENOMEM); + dual_role = __dual_role_register(parent, desc); + if (IS_ERR(dual_role)) { + devres_free(ptr); + } else { + *ptr = dual_role; + devres_add(parent, ptr); + } + return dual_role; +} +EXPORT_SYMBOL_GPL(devm_dual_role_instance_register); + +static int devm_dual_role_match(struct device *dev, void *res, void *data) +{ + struct dual_role_phy_instance **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role) +{ + int rc; + + rc = devres_release(dev, devm_dual_role_release, + devm_dual_role_match, dual_role); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister); + +void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) +{ + return dual_role->drv_data; +} +EXPORT_SYMBOL_GPL(dual_role_get_drvdata); + +/***************** Device attribute functions **************************/ + +/* port type */ +static char *supported_modes_text[] = { + "ufp dfp", "dfp", "ufp" +}; + +/* current mode */ +static char *mode_text[] = { + "ufp", "dfp", "none" +}; + +/* Power role */ +static char *pr_text[] = { + "source", "sink", "none" +}; + +/* Data role */ +static char *dr_text[] = { + "host", "device", "none" +}; + +/* Vconn supply */ +static char *vconn_supply_text[] = { + "n", "y" +}; + +static ssize_t dual_role_show_property(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + const ptrdiff_t off = attr - dual_role_attrs; + unsigned int value; + + if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { + value = dual_role->desc->supported_modes; + } else { + ret = dual_role_get_property(dual_role, off, &value); + + if (ret < 0) { + if (ret == -ENODATA) + dev_dbg(dev, + "driver has no data for `%s' property\n", + attr->attr.name); + else if (ret != -ENODEV) + dev_err(dev, + "driver failed to report `%s' property: %zd\n", + attr->attr.name, ret); + return ret; + } + } + + if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { + BUILD_BUG_ON(DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL != + ARRAY_SIZE(supported_modes_text)); + if (value < DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + supported_modes_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_MODE) { + BUILD_BUG_ON(DUAL_ROLE_PROP_MODE_TOTAL != + ARRAY_SIZE(mode_text)); + if (value < DUAL_ROLE_PROP_MODE_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + mode_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_PR) { + BUILD_BUG_ON(DUAL_ROLE_PROP_PR_TOTAL != ARRAY_SIZE(pr_text)); + if (value < DUAL_ROLE_PROP_PR_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + pr_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_DR) { + BUILD_BUG_ON(DUAL_ROLE_PROP_DR_TOTAL != ARRAY_SIZE(dr_text)); + if (value < DUAL_ROLE_PROP_DR_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + dr_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_VCONN_SUPPLY) { + BUILD_BUG_ON(DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL != + ARRAY_SIZE(vconn_supply_text)); + if (value < DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + vconn_supply_text[value]); + else + return -EIO; + } else + return -EIO; +} + +static ssize_t dual_role_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + const ptrdiff_t off = attr - dual_role_attrs; + unsigned int value; + int total, i; + char *dup_buf, **text_array; + bool result = false; + + dup_buf = kstrdupcase(buf, GFP_KERNEL, false); + switch (off) { + case DUAL_ROLE_PROP_MODE: + total = DUAL_ROLE_PROP_MODE_TOTAL; + text_array = mode_text; + break; + case DUAL_ROLE_PROP_PR: + total = DUAL_ROLE_PROP_PR_TOTAL; + text_array = pr_text; + break; + case DUAL_ROLE_PROP_DR: + total = DUAL_ROLE_PROP_DR_TOTAL; + text_array = dr_text; + break; + case DUAL_ROLE_PROP_VCONN_SUPPLY: + ret = strtobool(dup_buf, &result); + value = result; + if (!ret) + goto setprop; + default: + ret = -EINVAL; + goto error; + } + + for (i = 0; i <= total; i++) { + if (i == total) { + ret = -ENOTSUPP; + goto error; + } + if (!strncmp(*(text_array + i), dup_buf, + strlen(*(text_array + i)))) { + value = i; + break; + } + } + +setprop: + ret = dual_role->desc->set_property(dual_role, off, &value); + +error: + kfree(dup_buf); + + if (ret < 0) + return ret; + + return count; +} + +static umode_t dual_role_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; + int i; + + if (attrno == DUAL_ROLE_PROP_SUPPORTED_MODES) + return mode; + + for (i = 0; i < dual_role->desc->num_properties; i++) { + int property = dual_role->desc->properties[i]; + + if (property == attrno) { + if (dual_role->desc->property_is_writeable && + dual_role_property_is_writeable(dual_role, property) + > 0) + mode |= S_IWUSR; + + return mode; + } + } + + return 0; +} + +static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1]; + +static struct attribute_group dual_role_attr_group = { + .attrs = __dual_role_attrs, + .is_visible = dual_role_attr_is_visible, +}; + +static const struct attribute_group *dual_role_attr_groups[] = { + &dual_role_attr_group, + NULL, +}; + +void dual_role_init_attrs(struct device_type *dev_type) +{ + int i; + + dev_type->groups = dual_role_attr_groups; + + for (i = 0; i < ARRAY_SIZE(dual_role_attrs); i++) + __dual_role_attrs[i] = &dual_role_attrs[i].attr; +} + +int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + int ret = 0, j; + char *prop_buf; + char *attrname; + + dev_dbg(dev, "uevent\n"); + + if (!dual_role || !dual_role->desc) { + dev_dbg(dev, "No dual_role phy yet\n"); + return ret; + } + + dev_dbg(dev, "DUAL_ROLE_NAME=%s\n", dual_role->desc->name); + + ret = add_uevent_var(env, "DUAL_ROLE_NAME=%s", dual_role->desc->name); + if (ret) + return ret; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!prop_buf) + return -ENOMEM; + + for (j = 0; j < dual_role->desc->num_properties; j++) { + struct device_attribute *attr; + char *line; + + attr = &dual_role_attrs[dual_role->desc->properties[j]]; + + ret = dual_role_show_property(dev, attr, prop_buf); + if (ret == -ENODEV || ret == -ENODATA) { + ret = 0; + continue; + } + + if (ret < 0) + goto out; + line = strnchr(prop_buf, PAGE_SIZE, '\n'); + if (line) + *line = 0; + + attrname = kstrdupcase(attr->attr.name, GFP_KERNEL, true); + if (!attrname) + ret = -ENOMEM; + + dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); + + ret = add_uevent_var(env, "DUAL_ROLE_%s=%s", attrname, + prop_buf); + kfree(attrname); + if (ret) + goto out; + } + +out: + free_page((unsigned long)prop_buf); + + return ret; +} + +/******************* Module Init ***********************************/ + +static int __init dual_role_class_init(void) +{ + dual_role_class = class_create(THIS_MODULE, "dual_role_usb"); + + if (IS_ERR(dual_role_class)) + return PTR_ERR(dual_role_class); + + dual_role_class->dev_uevent = dual_role_uevent; + dual_role_init_attrs(&dual_role_dev_type); + + return 0; +} + +static void __exit dual_role_class_exit(void) +{ + class_destroy(dual_role_class); +} + +subsys_initcall(dual_role_class_init); +module_exit(dual_role_class_exit); diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c index feb5dd2948b4..5a2f05a16c1e 100644 --- a/fs/pstore/pmsg.c +++ b/fs/pstore/pmsg.c @@ -37,6 +37,8 @@ static ssize_t write_pmsg(struct file *file, const char __user *buf, if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE) buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE; buffer = vmalloc(buffer_size); + if (!buffer) + return -ENOMEM; mutex_lock(&pmsg_lock); for (i = 0; i < count; ) { diff --git a/fs/super.c b/fs/super.c index eae088f6aaae..cdf6dec26549 100644 --- a/fs/super.c +++ b/fs/super.c @@ -769,7 +769,7 @@ static void do_emergency_remount(struct work_struct *work) struct super_block *sb, *p = NULL; spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { + list_for_each_entry_reverse(sb, &super_blocks, s_list) { if (hlist_unhashed(&sb->s_instances)) continue; sb->s_count++; diff --git a/include/linux/usb/class-dual-role.h b/include/linux/usb/class-dual-role.h new file mode 100644 index 000000000000..af42ed34944a --- /dev/null +++ b/include/linux/usb/class-dual-role.h @@ -0,0 +1,128 @@ +#ifndef __LINUX_CLASS_DUAL_ROLE_H__ +#define __LINUX_CLASS_DUAL_ROLE_H__ + +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/types.h> + +struct device; + +enum dual_role_supported_modes { + DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP = 0, + DUAL_ROLE_SUPPORTED_MODES_DFP, + DUAL_ROLE_SUPPORTED_MODES_UFP, +/*The following should be the last element*/ + DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_MODE_UFP = 0, + DUAL_ROLE_PROP_MODE_DFP, + DUAL_ROLE_PROP_MODE_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_MODE_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_PR_SRC = 0, + DUAL_ROLE_PROP_PR_SNK, + DUAL_ROLE_PROP_PR_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_PR_TOTAL, + +}; + +enum { + DUAL_ROLE_PROP_DR_HOST = 0, + DUAL_ROLE_PROP_DR_DEVICE, + DUAL_ROLE_PROP_DR_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_DR_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_VCONN_SUPPLY_NO = 0, + DUAL_ROLE_PROP_VCONN_SUPPLY_YES, +/*The following should be the last element*/ + DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL, +}; + +enum dual_role_property { + DUAL_ROLE_PROP_SUPPORTED_MODES = 0, + DUAL_ROLE_PROP_MODE, + DUAL_ROLE_PROP_PR, + DUAL_ROLE_PROP_DR, + DUAL_ROLE_PROP_VCONN_SUPPLY, +}; + +struct dual_role_phy_instance; + +/* Description of typec port */ +struct dual_role_phy_desc { + /* /sys/class/dual_role_usb/<name>/ */ + const char *name; + enum dual_role_supported_modes supported_modes; + enum dual_role_property *properties; + size_t num_properties; + + /* Callback for "cat /sys/class/dual_role_usb/<name>/<property>" */ + int (*get_property)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); + /* Callback for "echo <value> > + * /sys/class/dual_role_usb/<name>/<property>" */ + int (*set_property)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val); + /* Decides whether userspace can change a specific property */ + int (*property_is_writeable)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop); +}; + +struct dual_role_phy_instance { + const struct dual_role_phy_desc *desc; + + /* Driver private data */ + void *drv_data; + + struct device dev; + struct work_struct changed_work; +}; + +#if IS_ENABLED(CONFIG_DUAL_ROLE_USB_INTF) +extern void dual_role_instance_changed(struct dual_role_phy_instance + *dual_role); +extern struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc); +extern void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role); +extern int dual_role_get_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); +extern int dual_role_set_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val); +extern int dual_role_property_is_writeable(struct dual_role_phy_instance + *dual_role, + enum dual_role_property prop); +extern void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role); +#else /* CONFIG_DUAL_ROLE_USB_INTF */ +static void dual_role_instance_changed(struct dual_role_phy_instance + *dual_role){} +static struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + return ERR_PTR(-ENOSYS); +} +static void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role){} +static void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ +#endif /* __LINUX_CLASS_DUAL_ROLE_H__ */ diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 21787ebb332f..252611fad2fe 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -159,7 +159,7 @@ void log_suspend_abort_reason(const char *fmt, ...) suspend_abort = true; va_start(args, fmt); - snprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args); + vsnprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args); va_end(args); spin_unlock(&resume_reason_lock); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 119987f76f3f..8a84ade3a42c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3186,7 +3186,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr) return -EAFNOSUPPORT; } - for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + for (bucket = 0; bucket <= tcp_hashinfo.ehash_mask; bucket++) { struct hlist_nulls_node *node; struct sock *sk; spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); @@ -3196,8 +3196,19 @@ restart: sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { struct inet_sock *inet = inet_sk(sk); + if (sk->sk_state == TCP_TIME_WAIT) { + /* + * Sockets that are in TIME_WAIT state are + * instances of lightweight inet_timewait_sock, + * we should simply skip them (or we'll try to + * access non-existing fields and crash). + */ + continue; + } + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) continue; + if (sock_flag(sk, SOCK_DEAD)) continue; diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 31f76cd19d39..0975c993a94e 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -286,6 +286,7 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) goto out; } + sysfs_attr_init(&info->timer->attr.attr); info->timer->attr.attr.name = kstrdup(info->label, GFP_KERNEL); if (!info->timer->attr.attr.name) { ret = -ENOMEM; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 7eb9d7d534d6..a3b25b227dfe 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -184,7 +184,9 @@ xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par) } #endif - if (!sk) + if (sk) + atomic_inc(&sk->sk_refcnt); + else sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr, sport, dport, par->in); @@ -225,8 +227,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, (sk->sk_state == TCP_TIME_WAIT && inet_twsk(sk)->tw_transparent)); - if (sk != skb->sk) - sock_gen_put(sk); + sock_gen_put(sk); if (wildcard || !transparent) sk = NULL; @@ -361,7 +362,9 @@ xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par) return NULL; } - if (!sk) + if (sk) + atomic_inc(&sk->sk_refcnt); + else sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr, sport, dport, par->in); |