From aae1c430121438ba12e701b242c507c9a8e8135c Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 5 Aug 2015 16:54:53 -0700 Subject: wakeup_reason: use vsnprintf instead of snsprintf for vargs. Bug: 22368519 Signed-off-by: Ruchi Kandoi --- kernel/power/wakeup_reason.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } -- cgit v1.2.3 From e0651910dc7cabfbda651eef05581e69416705ed Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 20 Aug 2015 14:21:34 -0700 Subject: pstore: pmsg: return -ENOMEM on vmalloc failure Signed-off-by: Mark Salyzyn Bug: 23385441 Change-Id: I1c6d98ea23f2fde4e59b6535fcce3a69d862da9b --- fs/pstore/pmsg.c | 2 ++ 1 file changed, 2 insertions(+) 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; ) { -- cgit v1.2.3 From 556724fa019ddfd41ee73d9d5e7abfe91f2c5ed6 Mon Sep 17 00:00:00 2001 From: Christian Poetzsch Date: Fri, 24 Jul 2015 16:42:58 +0100 Subject: Fix for in kernel emergency remount when loop mounts are used adb reboot calls /proc/sysrq-trigger to force an emergency remount (ro) of all mounted disks. This is executed in the order of the time the mount was originally done. Because we have a test system which loop mount images from an extra partition, we see errors cause the loop mounted partitions gets remounted after this physical partition was set to read only already. Fix this by reversing the order of the emergency remount. This will remount the disk first which have been mounted last. So instead of remounting in this order: /dev/sda1 /dev/loop1 /dev/loop2 we now remount in this order: /dev/loop2 /dev/loop1 /dev/sda1 Change-Id: I68fe7e16cc9400ab5278877af70c9ea1d9b57936 Signed-off-by: Christian Poetzsch --- fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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++; -- cgit v1.2.3 From 7e5e463e9e621f6c96cc324a4a18ed88273b6853 Mon Sep 17 00:00:00 2001 From: Rajmal Menariya Date: Wed, 26 Aug 2015 21:02:52 +0800 Subject: ion : In carveout heap, change minimum allocation order from 12 to PAGE_SHIFT, After this change each bit in bitmap (genalloc - General purpose special memory pool) represents one page size memory. Cc:sprd-ind-kernel-group@googlegroups.com Cc:sanjeev.yadav@spreadtrum.com Cc:Colin Cross cc:John Stultz Change-Id: I1172285dac014ecd2f1a965604b697d6739c1726 Signed-off-by: Rajmal Menariya --- drivers/staging/android/ion/ion_carveout_heap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 drivers/staging/android/ion/ion_carveout_heap.c diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c old mode 100644 new mode 100755 index 9156d8238c97..e702ce6461fc --- 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); -- cgit v1.2.3 From e02055c7435e972931640d966fc0ec9562cf0eb6 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Tue, 18 Aug 2015 11:15:53 -0700 Subject: arm64 Kconfig: Move LIST_POISON to a safe value Move the poison pointer offset to 0xdead000000000000, a recognized value that is not mappable by user-space exploits. Change-Id: I558441a26a7c8390aa087f32c4cbe980de8c8ce3 Signed-off-by: Thierry Strudel Signed-off-by: Jeff Vander Stoep --- arch/arm64/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index d564fb7f62b3..70b7d2e420f6 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 -- cgit v1.2.3 From 78917d6a027f545b5f04782ac99163510a729dbf Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 31 Aug 2015 21:36:07 -0700 Subject: usb: phy: Dual role sysfs class definition This CL adds a new class to monitor and change dual role usb ports from userspace. The usb phy drivers can register to the dual_role_usb class and expose the capabilities of the ports. The phy drivers can decide on whether a specific attribute can be changed from userspace by choosing to implement the appropriate callback. Cherry-picked from https://android-review.googlesource.com/#/c/167310/ Signed-off-by: Badhri Jagan Sridharan Bug: 21615151 Change-Id: Id1c4aaa97e898264d7006381a7badd029b5d9789 --- .../ABI/testing/sysfs-class-dual-role-usb | 71 +++ drivers/usb/phy/Kconfig | 9 + drivers/usb/phy/Makefile | 2 + drivers/usb/phy/class-dual-role.c | 529 +++++++++++++++++++++ include/linux/usb/class-dual-role.h | 128 +++++ 5 files changed, 739 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-dual-role-usb create mode 100644 drivers/usb/phy/class-dual-role.c create mode 100644 include/linux/usb/class-dual-role.h 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 +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 +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 +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 +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 +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 +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/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..ce889dd529cb --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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/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 +#include +#include + +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// */ + 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//" */ + int (*get_property)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); + /* Callback for "echo > + * /sys/class/dual_role_usb//" */ + 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__ */ -- cgit v1.2.3 From ecd32842283c64b14243be9b20b60f351aea1b5d Mon Sep 17 00:00:00 2001 From: Vinil Cheeramvelil Date: Wed, 8 Jul 2015 10:35:06 +0800 Subject: ion: Handle the memory mapping correctly on x86 This patch modifies the ion page pool code to address limitation in x86 PAT. When one physical page is mapped to multiple virtual pages, the same cache policy should be used. Add set_memory_wc/uc call to avoid aliases. If not, all mappings will be cached(write back). Change-Id: I98ee8902df0c80135dddfa998c4ca4c2bb44e40e Signed-off-by: Zhebin Jin Signed-off-by: Vinil Cheeramvelil --- drivers/staging/android/ion/Kconfig | 7 ++++++ drivers/staging/android/ion/ion_page_pool.c | 8 +++++++ drivers/staging/android/ion/ion_priv.h | 34 +++++++++++++++++++++++++++ drivers/staging/android/ion/ion_system_heap.c | 6 +++-- 4 files changed, 53 insertions(+), 2 deletions(-) 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_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 #include #include +#ifdef CONFIG_ION_POOL_CACHE_POLICY +#include +#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); } -- cgit v1.2.3 From 9ff158f4c245c2fecb1489a4aae62ca67c0846a1 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 2 Sep 2015 16:38:31 +0530 Subject: usb: phy: fix dual role sysfs build if kernel modules are supported Add a missing ";" after EXPORT_SYMBOL() otherwise we run into following build error if Kernel Modules are supported: ---------- CC drivers/usb/phy/class-dual-role.o drivers/usb/phy/class-dual-role.c:91:1: error: expected ',' or ';' before 'int' int dual_role_get_property(struct dual_role_phy_instance *dual_role, ^ make[3]: *** [drivers/usb/phy/class-dual-role.o] Error 1 ---------- Signed-off-by: Amit Pundir --- drivers/usb/phy/class-dual-role.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c index ce889dd529cb..51fcb545a9d5 100644 --- a/drivers/usb/phy/class-dual-role.c +++ b/drivers/usb/phy/class-dual-role.c @@ -86,7 +86,7 @@ void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT); schedule_work(&dual_role->changed_work); } -EXPORT_SYMBOL_GPL(dual_role_instance_changed) +EXPORT_SYMBOL_GPL(dual_role_instance_changed); int dual_role_get_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, -- cgit v1.2.3 From 492097142eef7da6b84544932c2579d1abb907c3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 9 Aug 2015 15:12:50 -0700 Subject: usb: gadget: Add device attribute to determine gadget state Android frameworks (UsbDeviceManager) relies on gadget state exported through device attributes. This CL adds the device attribute to export USB gadget state. Change-Id: Id0391810d75b58c579610fbec6e37ab22f28886d Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/gadget/configfs.c | 85 ++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index d201f5609bb0..d458b21dbd77 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1647,6 +1647,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( @@ -1696,25 +1744,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); @@ -1726,15 +1760,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); @@ -1746,13 +1771,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 = { -- cgit v1.2.3 From 3f1694ac7562fd9f2aee85c2b6d1f93424f06dcb Mon Sep 17 00:00:00 2001 From: Krzysztof Opasiak Date: Fri, 20 Mar 2015 15:48:56 +0100 Subject: usb: gadget: configfs: Fix interfaces array NULL-termination memset() to 0 interfaces array before reusing usb_configuration structure. This commit fix bug: ln -s functions/acm.1 configs/c.1 ln -s functions/acm.2 configs/c.1 ln -s functions/acm.3 configs/c.1 echo "UDC name" > UDC echo "" > UDC rm configs/c.1/acm.* rmdir functions/* mkdir functions/ecm.usb0 ln -s functions/ecm.usb0 configs/c.1 echo "UDC name" > UDC [ 82.220969] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 82.229009] pgd = c0004000 [ 82.231698] [00000000] *pgd=00000000 [ 82.235260] Internal error: Oops: 17 [#1] PREEMPT SMP ARM [ 82.240638] Modules linked in: [ 82.243681] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.0.0-rc2 #39 [ 82.249926] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 82.256003] task: c07cd2f0 ti: c07c8000 task.ti: c07c8000 [ 82.261393] PC is at composite_setup+0xe3c/0x1674 [ 82.266073] LR is at composite_setup+0xf20/0x1674 [ 82.270760] pc : [] lr : [] psr: 600001d3 [ 82.270760] sp : c07c9df0 ip : c0806448 fp : ed8c9c9c [ 82.282216] r10: 00000001 r9 : 00000000 r8 : edaae918 [ 82.287425] r7 : ed551cc0 r6 : 00007fff r5 : 00000000 r4 : ed799634 [ 82.293934] r3 : 00000003 r2 : 00010002 r1 : edaae918 r0 : 0000002e [ 82.300446] Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel [ 82.307910] Control: 10c5387d Table: 6bc1804a DAC: 00000015 [ 82.313638] Process swapper/0 (pid: 0, stack limit = 0xc07c8210) [ 82.319627] Stack: (0xc07c9df0 to 0xc07ca000) [ 82.323969] 9de0: 00000000 c06e65f4 00000000 c07c9f68 [ 82.332130] 9e00: 00000067 c07c59ac 000003f7 edaae918 ed8c9c98 ed799690 eca2f140 200001d3 [ 82.340289] 9e20: ee79a2d8 c07c9e88 c07c5304 ffff55db 00010002 edaae810 edaae860 eda96d50 [ 82.348448] 9e40: 00000009 ee264510 00000007 c07ca444 edaae860 c0340890 c0827a40 ffff55e0 [ 82.356607] 9e60: c0827a40 eda96e40 ee264510 edaae810 00000000 edaae860 00000007 c07ca444 [ 82.364766] 9e80: edaae860 c0354170 c03407dc c033db4c edaae810 00000000 00000000 00000010 [ 82.372925] 9ea0: 00000032 c0341670 00000000 00000000 00000001 eda96e00 00000000 00000000 [ 82.381084] 9ec0: 00000000 00000032 c0803a23 ee1aa840 00000001 c005d54c 249e2450 00000000 [ 82.389244] 9ee0: 200001d3 ee1aa840 ee1aa8a0 ed84f4c0 00000000 c07c9f68 00000067 c07c59ac [ 82.397403] 9f00: 00000000 c005d688 ee1aa840 ee1aa8a0 c07db4b4 c006009c 00000032 00000000 [ 82.405562] 9f20: 00000001 c005ce20 c07c59ac c005cf34 f002000c c07ca780 c07c9f68 00000057 [ 82.413722] 9f40: f0020000 413fc090 00000001 c00086b4 c000f804 60000053 ffffffff c07c9f9c [ 82.421880] 9f60: c0803a20 c0011fc0 00000000 00000000 c07c9fb8 c001bee0 c07ca4f0 c057004c [ 82.430040] 9f80: c07ca4fc c0803a20 c0803a20 413fc090 00000001 00000000 01000000 c07c9fb0 [ 82.438199] 9fa0: c000f800 c000f804 60000053 ffffffff 00000000 c0050e70 c0803bc0 c0783bd8 [ 82.446358] 9fc0: ffffffff ffffffff c0783664 00000000 00000000 c07b13e8 00000000 c0803e54 [ 82.454517] 9fe0: c07ca480 c07b13e4 c07ce40c 4000406a 00000000 40008074 00000000 00000000 [ 82.462689] [] (composite_setup) from [] (s3c_hsotg_complete_setup+0xb4/0x418) [ 82.471626] [] (s3c_hsotg_complete_setup) from [] (usb_gadget_giveback_request+0xc/0x10) [ 82.481429] [] (usb_gadget_giveback_request) from [] (s3c_hsotg_complete_request+0xcc/0x12c) [ 82.491583] [] (s3c_hsotg_complete_request) from [] (s3c_hsotg_irq+0x4fc/0x558) [ 82.500614] [] (s3c_hsotg_irq) from [] (handle_irq_event_percpu+0x50/0x150) [ 82.509291] [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [ 82.518145] [] (handle_irq_event) from [] (handle_fasteoi_irq+0xd4/0x18c) [ 82.526650] [] (handle_fasteoi_irq) from [] (generic_handle_irq+0x20/0x30) [ 82.535242] [] (generic_handle_irq) from [] (__handle_domain_irq+0x6c/0xdc) [ 82.543923] [] (__handle_domain_irq) from [] (gic_handle_irq+0x2c/0x6c) [ 82.552256] [] (gic_handle_irq) from [] (__irq_svc+0x40/0x74) [ 82.559716] Exception stack(0xc07c9f68 to 0xc07c9fb0) [ 82.564753] 9f60: 00000000 00000000 c07c9fb8 c001bee0 c07ca4f0 c057004c [ 82.572913] 9f80: c07ca4fc c0803a20 c0803a20 413fc090 00000001 00000000 01000000 c07c9fb0 [ 82.581069] 9fa0: c000f800 c000f804 60000053 ffffffff [ 82.586113] [] (__irq_svc) from [] (arch_cpu_idle+0x30/0x3c) [ 82.593491] [] (arch_cpu_idle) from [] (cpu_startup_entry+0x128/0x1a4) [ 82.601740] [] (cpu_startup_entry) from [] (start_kernel+0x350/0x3bc) [ 82.609890] Code: 0a000002 e3530005 05975010 15975008 (e5953000) [ 82.615965] ---[ end trace f57d5f599a5f1bfa ]--- Most of kernel code assume that interface array in struct usb_configuration is NULL terminated. When gadget is composed with configfs configuration structure may be reused for different functions set. This bug happens because purge_configs_funcs() sets only next_interface_id to 0. Interface array still contains pointers to already freed interfaces. If in second try we add less interfaces than earlier we may access unallocated memory when trying to get interface descriptors. Signed-off-by: Krzysztof Opasiak Cc: # 3.10+ Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index d458b21dbd77..52ddfec6e289 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1333,6 +1333,7 @@ static void purge_configs_funcs(struct gadget_info *gi) } } c->next_interface_id = 0; + memset(c->interface, 0, sizeof(c->interface)); c->superspeed = 0; c->highspeed = 0; c->fullspeed = 0; -- cgit v1.2.3 From 4158b96a16aaa6da4c6b588fa267027bbc04ae2c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:25 +0200 Subject: usb: gadget: f_midi: enable use of the index parameter The soundcard index to use for the ALSA device creation is passed as a parameter to f_midi_bind_config(), but is assigned to midi->index only after the call to f_midi_register_card(midi). So no matter what is passed to f_midi_bind_config(), the actual index for snd_card_new() is always 0. This probably works ok if at the moment of f_midi's bind there are no other snd_cards, but if there are, it is not possible to bind f_midi. This patch moves the assignment to a place before the call to f_midi_register_card(midi). BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: Ia2a2c95b165dd3575b03b0928ff2322349b73e24 --- drivers/usb/gadget/function/f_midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 807b31c0edc3..bf329578c957 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -954,6 +954,7 @@ int __init f_midi_bind_config(struct usb_configuration *c, /* set up ALSA midi devices */ midi->in_ports = in_ports; midi->out_ports = out_ports; + midi->index = index; status = f_midi_register_card(midi); if (status < 0) goto setup_fail; @@ -966,7 +967,6 @@ int __init f_midi_bind_config(struct usb_configuration *c, midi->func.disable = f_midi_disable; midi->id = kstrdup(id, GFP_KERNEL); - midi->index = index; midi->buflen = buflen; midi->qlen = qlen; -- cgit v1.2.3 From 519fc4b38fe73b73e878d95be89e49e8e5e0f2ff Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:26 +0200 Subject: usb: gadget: f_midi: check kstrdup() return value kstrdup() might fail, so check its return value and react appropriately. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: Ife54cb138b51fd84ac2b62aa340747789894a320 --- drivers/usb/gadget/function/f_midi.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index bf329578c957..a920eee9e878 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -654,6 +654,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,10 +723,7 @@ 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; } @@ -967,15 +972,23 @@ int __init f_midi_bind_config(struct usb_configuration *c, midi->func.disable = f_midi_disable; midi->id = kstrdup(id, GFP_KERNEL); + if (id && !midi->id) { + status = -ENOMEM; + goto kstrdup_fail; + } midi->buflen = buflen; midi->qlen = qlen; status = usb_add_function(c, &midi->func); if (status) - goto setup_fail; + goto add_fail; return 0; +add_fail: + kfree(midi->id); +kstrdup_fail: + f_midi_unregister_card(midi); setup_fail: for (--i; i >= 0; i--) kfree(midi->in_port[i]); -- cgit v1.2.3 From 8a7a4c03503cc181355ad473acf895136faa251b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:27 +0200 Subject: usb: gadget: f_midi: convert to new function interface with backward compatibility Converting midi to the new function interface requires converting the USB midi's function code and its users. This patch converts the f_midi.c to the new function interface. The file can now be compiled into a separate usb_f_midi.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: I249b56af9549d8a5288bab99ad657ce15cc57e22 --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/function/Makefile | 2 + drivers/usb/gadget/function/f_midi.c | 147 +++++++++++++++++++++++++++++++++-- drivers/usb/gadget/function/u_midi.h | 32 ++++++++ drivers/usb/gadget/legacy/gmidi.c | 1 + 5 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/gadget/function/u_midi.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1542e9a899df..f36e4e50158d 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 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_midi.c b/drivers/usb/gadget/function/f_midi.c index a920eee9e878..f3e7d951f0ff 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -33,6 +34,7 @@ #include #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, @@ -387,6 +389,7 @@ static void f_midi_disable(struct usb_function *f) usb_ep_disable(midi->out_ep); } +#ifdef USBF_MIDI_INCLUDED static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = f->config->cdev; @@ -409,6 +412,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); kfree(midi); } +#endif static int f_midi_snd_free(struct snd_device *device) { @@ -729,8 +733,7 @@ fail: /* 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]; @@ -741,6 +744,14 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct f_midi *midi = func_to_midi(f); int status, n, jack = 1, i = 0; +#ifndef USBF_MIDI_INCLUDED + 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; + +#endif /* maybe allocate device-global string ID */ if (midi_string_defs[0].id == 0) { status = usb_string_id(c->cdev); @@ -897,6 +908,10 @@ fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); fail: +#ifndef USBF_MIDI_INCLUDED + f_midi_unregister_card(midi); +fail_register: +#endif /* we might as well release our claims on endpoints */ if (midi->out_ep) midi->out_ep->driver_data = NULL; @@ -908,6 +923,7 @@ fail: return status; } +#ifdef USBF_MIDI_INCLUDED /** * f_midi_bind_config - add USB MIDI function to a configuration * @c: the configuration to supcard the USB audio function @@ -997,3 +1013,122 @@ fail: return status; } +#else + +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); + + kfree(opts); +} + +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); + opts->func_inst.free_func_inst = f_midi_free_inst; + + 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); + for (i = opts->in_ports - 1; i >= 0; --i) + kfree(midi->in_port[i]); + kfree(midi); +} + +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); +} + +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); + /* sanity check */ + if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) + return ERR_PTR(-EINVAL); + + /* allocate and initialize one new instance */ + midi = kzalloc(sizeof(*midi), GFP_KERNEL); + if (!midi) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < opts->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; + } + + /* set up ALSA midi devices */ + midi->id = kstrdup(opts->id, GFP_KERNEL); + if (opts->id && !midi->id) { + status = -ENOMEM; + goto kstrdup_fail; + } + midi->in_ports = opts->in_ports; + midi->out_ports = opts->out_ports; + midi->index = opts->index; + midi->buflen = opts->buflen; + midi->qlen = opts->qlen; + + 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->func.free_func = f_midi_free; + + return &midi->func; + +kstrdup_fail: + f_midi_unregister_card(midi); +setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); + kfree(midi); + return ERR_PTR(status); +} + +DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc); +#endif diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h new file mode 100644 index 000000000000..76bccc1fdbb4 --- /dev/null +++ b/drivers/usb/gadget/function/u_midi.h @@ -0,0 +1,32 @@ +/* + * u_midi.h + * + * Utility definitions for the midi function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +struct f_midi_opts { + struct usb_function_instance func_inst; + int index; + char *id; + unsigned int in_ports; + unsigned int out_ports; + unsigned int buflen; + unsigned int qlen; +}; + +#endif /* U_MIDI_H */ + diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index 3d696b86ff76..f704c5557d19 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" /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From a0d67b7bed9c08ebffc596847b9ee53fd7fbba61 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:29 +0200 Subject: usb: gadget: f_midi: remove compatibility layer There are no old f_midi interface users left, so remove it. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: I8d4450aa3b630aadf61fc63e6ed7ca105df6a0e6 --- drivers/usb/gadget/function/f_midi.c | 122 ----------------------------------- 1 file changed, 122 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index f3e7d951f0ff..5cd77be95b79 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -389,31 +389,6 @@ static void f_midi_disable(struct usb_function *f) usb_ep_disable(midi->out_ep); } -#ifdef USBF_MIDI_INCLUDED -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); -} -#endif - static int f_midi_snd_free(struct snd_device *device) { return 0; @@ -744,14 +719,12 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct f_midi *midi = func_to_midi(f); int status, n, jack = 1, i = 0; -#ifndef USBF_MIDI_INCLUDED 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; -#endif /* maybe allocate device-global string ID */ if (midi_string_defs[0].id == 0) { status = usb_string_id(c->cdev); @@ -908,10 +881,8 @@ fail_f_midi: kfree(midi_function); usb_free_descriptors(f->hs_descriptors); fail: -#ifndef USBF_MIDI_INCLUDED f_midi_unregister_card(midi); fail_register: -#endif /* we might as well release our claims on endpoints */ if (midi->out_ep) midi->out_ep->driver_data = NULL; @@ -923,98 +894,6 @@ fail_register: return status; } -#ifdef USBF_MIDI_INCLUDED -/** - * 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; - midi->index = index; - 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); - if (id && !midi->id) { - status = -ENOMEM; - goto kstrdup_fail; - } - midi->buflen = buflen; - midi->qlen = qlen; - - status = usb_add_function(c, &midi->func); - if (status) - goto add_fail; - - return 0; - -add_fail: - kfree(midi->id); -kstrdup_fail: - f_midi_unregister_card(midi); -setup_fail: - for (--i; i >= 0; i--) - kfree(midi->in_port[i]); - kfree(midi); -fail: - return status; -} - -#else - static void f_midi_free_inst(struct usb_function_instance *f) { struct f_midi_opts *opts; @@ -1131,4 +1010,3 @@ setup_fail: } DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc); -#endif -- cgit v1.2.3 From 506c9295204d6a581c181fbaafc191586f5750ed Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:30 +0200 Subject: usb: gadget: f_midi: use usb_gstrings_attach In order to add configfs support the usb_gstrings_attach must be used. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: I65b34edbfa8882e8d7e82d0732fa323a2426bf3e --- drivers/usb/gadget/function/f_midi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 5cd77be95b79..ec2a9cee4589 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -717,6 +717,7 @@ static int 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; @@ -726,12 +727,13 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) 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); @@ -991,7 +993,6 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->qlen = opts->qlen; 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; -- cgit v1.2.3 From d03e8022781abd0f6097ba2ed132760f6969d0c9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 16 Oct 2014 13:33:31 +0200 Subject: usb: gadget: f_midi: add configfs support Make the midi function available for gadgets composed with configfs. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: I544e0e270de8cd0e55bf51aae7fa33d7d32a9ad8 --- Documentation/ABI/testing/configfs-usb-gadget-midi | 12 ++ drivers/usb/gadget/Kconfig | 14 ++ drivers/usb/gadget/function/f_midi.c | 161 ++++++++++++++++++++- drivers/usb/gadget/function/u_midi.h | 8 + 4 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-midi 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/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f36e4e50158d..20671d31cfac 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -431,6 +431,20 @@ config USB_ANDROID_RNDIS_DWORD_ALIGNED help Provides dword aligned for DMA controller. +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/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index ec2a9cee4589..1f94dad57307 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -896,12 +896,145 @@ fail_register: return status; } +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); + result = strlcpy(page, opts->id, PAGE_SIZE); + 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); } @@ -912,7 +1045,18 @@ static struct usb_function_instance *f_midi_alloc_inst(void) 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; + + config_group_init_type_name(&opts->func_inst.group, "", + &midi_func_type); return &opts->func_inst; } @@ -926,9 +1070,12 @@ static void f_midi_free(struct usb_function *f) 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) @@ -957,20 +1104,27 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) int status, i; opts = container_of(fi, struct f_midi_opts, func_inst); + + mutex_lock(&opts->lock); /* sanity check */ - if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) + 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); - if (!midi) + if (!midi) { + mutex_unlock(&opts->lock); return ERR_PTR(-ENOMEM); + } 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; } @@ -984,6 +1138,7 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->id = kstrdup(opts->id, GFP_KERNEL); if (opts->id && !midi->id) { status = -ENOMEM; + mutex_unlock(&opts->lock); goto kstrdup_fail; } midi->in_ports = opts->in_ports; @@ -991,6 +1146,8 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi) 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; diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h index 76bccc1fdbb4..22510189758e 100644 --- a/drivers/usb/gadget/function/u_midi.h +++ b/drivers/usb/gadget/function/u_midi.h @@ -22,10 +22,18 @@ 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 */ -- cgit v1.2.3 From d01107520eb572cdbfbe71baf641b4a43a5a1a36 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 12 Nov 2014 21:28:24 +0800 Subject: usb: gadget: midi: f_midi_alloc() can be static drivers/usb/gadget/function/f_midi.c:1072:21: sparse: symbol 'f_midi_alloc' was not declared. Should it be static? drivers/usb/gadget/legacy/gmidi.c:118:30: sparse: symbol 'fi_midi' was not declared. Should it be static? drivers/usb/gadget/legacy/gmidi.c:119:21: sparse: symbol 'f_midi' was not declared. Should it be static? BACKPORT FROM MAINLINE KERNEL Signed-off-by: Fengguang Wu Signed-off-by: Felipe Balbi Change-Id: I7794cd62da8deb408848e86163747d5ce655f33b --- drivers/usb/gadget/function/f_midi.c | 2 +- drivers/usb/gadget/legacy/gmidi.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 1f94dad57307..a90440300735 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1097,7 +1097,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } -struct usb_function *f_midi_alloc(struct usb_function_instance *fi) +static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) { struct f_midi *midi; struct f_midi_opts *opts; diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index f704c5557d19..b7c631d828de 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -116,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; -- cgit v1.2.3 From 745024148169d8d128c185c1d2b2bfab4e307777 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 7 Dec 2014 20:21:00 +0100 Subject: usb: gadget: fix misspelling of current function in string Replace a misspelled function name by %s and then __func__. This was done using Coccinelle, including the use of Levenshtein distance, as proposed by Rasmus Villemoes. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Julia Lawall Signed-off-by: Felipe Balbi Change-Id: I971885f53489cd1fbcf539630e2b8136511ce54d --- drivers/usb/gadget/function/f_hid.c | 5 +++-- drivers/usb/gadget/function/f_midi.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) 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 a90440300735..259b656c0b3e 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -520,7 +520,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; -- cgit v1.2.3 From 61ef7b1f6326cffc724cd59934fc16be2bce7e68 Mon Sep 17 00:00:00 2001 From: Pawel Szewczyk Date: Thu, 14 May 2015 14:14:11 +0200 Subject: usb: gadget: f_midi: fix segfault when reading empty id When midi function is created, 'id' attribute is initialized with SNDRV_DEFAULT_STR1, which is NULL pointer. Trying to read this attribute before filling it ends up with segmentation fault. This commit fix this issue by preventing null pointer dereference. Now f_midi_opts_id_show() returns empty string when id is a null pointer. Reproduction path: $ mkdir functions/midi.0 $ cat functions/midi.0/id [ 53.130132] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 53.132630] pgd = ec6cc000 [ 53.135308] [00000000] *pgd=6b759831, *pte=00000000, *ppte=00000000 [ 53.141530] Internal error: Oops: 17 [#1] PREEMPT SMP ARM [ 53.146904] Modules linked in: usb_f_midi snd_rawmidi libcomposite [ 53.153071] CPU: 1 PID: 2936 Comm: cat Not tainted 3.19.0-00041-gcf4b216 #7 [ 53.160010] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 53.166088] task: ee234c80 ti: ec764000 task.ti: ec764000 [ 53.171482] PC is at strlcpy+0x8/0x60 [ 53.175128] LR is at f_midi_opts_id_show+0x28/0x3c [usb_f_midi] [ 53.181019] pc : [] lr : [] psr: 60000053 [ 53.181019] sp : ec765ef8 ip : 00000141 fp : 00000000 [ 53.192474] r10: 00019000 r9 : ed7546c0 r8 : 00010000 [ 53.197682] r7 : ec765f80 r6 : eb46a000 r5 : eb46a000 r4 : ed754734 [ 53.204192] r3 : ee234c80 r2 : 00001000 r1 : 00000000 r0 : eb46a000 [ 53.210704] Flags: nZCv IRQs on FIQs off Mode SVC_32 ISA ARM Segment user [ 53.217907] Control: 10c5387d Table: 6c6cc04a DAC: 00000015 [ 53.223636] Process cat (pid: 2936, stack limit = 0xec764238) [ 53.229364] Stack: (0xec765ef8 to 0xec766000) [ 53.233706] 5ee0: ed754734 ed7546c0 [ 53.241866] 5f00: eb46a000 bf01bed0 eb753b80 bf01cc44 eb753b98 bf01b0a4 bf01b08c c0125dd0 [ 53.250025] 5f20: 00002f19 00000000 ec432e00 bf01cce8 c0530c00 00019000 00010000 ec765f80 [ 53.258184] 5f40: 00010000 ec764000 00019000 c00cc4ac ec432e00 c00cc55c 00000017 000081a4 [ 53.266343] 5f60: 00000001 00000000 00000000 ec432e00 ec432e00 00010000 00019000 c00cc620 [ 53.274502] 5f80: 00000000 00000000 00000000 00010000 ffff1000 00019000 00000003 c000e9a8 [ 53.282662] 5fa0: 00000000 c000e7e0 00010000 ffff1000 00000003 00019000 00010000 00019000 [ 53.290821] 5fc0: 00010000 ffff1000 00019000 00000003 7fffe000 00000001 00000000 00000000 [ 53.298980] 5fe0: 00000000 be8c68d4 0000b995 b6f0e3e6 40000070 00000003 00000000 00000000 [ 53.307157] [] (strlcpy) from [] (f_midi_opts_id_show+0x28/0x3c [usb_f_midi]) [ 53.316006] [] (f_midi_opts_id_show [usb_f_midi]) from [] (f_midi_opts_attr_show+0x18/0x24 ) [ 53.327209] [] (f_midi_opts_attr_show [usb_f_midi]) from [] (configfs_read_file+0x9c/0xec) [ 53.337180] [] (configfs_read_file) from [] (__vfs_read+0x18/0x4c) [ 53.345073] [] (__vfs_read) from [] (vfs_read+0x7c/0x100) [ 53.352190] [] (vfs_read) from [] (SyS_read+0x40/0x8c) [ 53.359056] [] (SyS_read) from [] (ret_fast_syscall+0x0/0x34) [ 53.366513] Code: ebffe3d3 e8bd8008 e92d4070 e1a05000 (e5d14000) [ 53.372641] ---[ end trace e4f53a4e233d98d0 ]--- BACKPORT FROM MAINLINE KERNEL Signed-off-by: Pawel Szewczyk Signed-off-by: Felipe Balbi Change-Id: I7cb7fef0d8d8336d7801a99941824be2bf04f256 --- drivers/usb/gadget/function/f_midi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 259b656c0b3e..6316aa5b1c49 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -973,7 +973,13 @@ static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = strlcpy(page, opts->id, PAGE_SIZE); + if (opts->id) { + result = strlcpy(page, opts->id, PAGE_SIZE); + } else { + page[0] = 0; + result = 0; + } + mutex_unlock(&opts->lock); return result; -- cgit v1.2.3 From f803e26b70f4539040503994a160e44238ec229f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 3 Jul 2015 14:02:29 +0200 Subject: usb: gadget: f_midi: fix error recovery path In case kstrdup() fails the resources to release are midi->in_port[] and midi. No cards have been registered, so no need to unregister any. BACKPORT FROM MAINLINE KERNEL Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi Change-Id: Idf14ca5aded888fe7c56d8b06763b009800827f5 --- drivers/usb/gadget/function/f_midi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 6316aa5b1c49..ad50a67c1465 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1145,7 +1145,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) if (opts->id && !midi->id) { status = -ENOMEM; mutex_unlock(&opts->lock); - goto kstrdup_fail; + goto setup_fail; } midi->in_ports = opts->in_ports; midi->out_ports = opts->out_ports; @@ -1164,8 +1164,6 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) return &midi->func; -kstrdup_fail: - f_midi_unregister_card(midi); setup_fail: for (--i; i >= 0; i--) kfree(midi->in_port[i]); -- cgit v1.2.3 From 0d87d144f44b6ed2d8e4189489e19f18d9a7c49e Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 13 Jul 2015 11:03:51 +0200 Subject: usb: gadget: midi: avoid redundant f_midi_set_alt() call Function midi registers two interfaces with single set_alt() function which means that f_midi_set_alt() is called twice when configuration is set. That means that endpoint initialization and ep request allocation is done two times. To avoid this problem we do such things only once, for interface number 1 (MIDI Streaming interface). BACKPORT FROM MAINLINE KERNEL Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi Change-Id: I55c015c6a11180a40534671fbf505342a0032ef2 --- drivers/usb/gadget/function/f_midi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index ad50a67c1465..a287a4829273 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -329,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; -- cgit v1.2.3 From 19088ddf7fd9f51395f6a25536e76847d767cd01 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 2 Sep 2015 22:49:10 -0700 Subject: ANDROID: usb: gadget: create F_midi device Android frameworks relies on the alsa config reported by the f_midi device. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0695e00b166fd953f50acea93802245b0d5a5240 --- drivers/usb/gadget/function/f_midi.c | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index a287a4829273..ee1bfc905fb9 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1048,6 +1048,65 @@ static void f_midi_free_inst(struct usb_function_instance *f) 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; @@ -1065,6 +1124,11 @@ static struct usb_function_instance *f_midi_alloc_inst(void) 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); @@ -1166,6 +1230,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->func.disable = f_midi_disable; midi->func.free_func = f_midi_free; + fi->f = &midi->func; return &midi->func; setup_fail: -- cgit v1.2.3 From 415118692305d7a98731abc8068ab59b9c798e9a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Jul 2015 16:41:48 -0700 Subject: nf: IDLETIMER: fix lockdep warning Dynamically allocated sysfs attributes should be initialized with sysfs_attr_init() otherwise lockdep will be angry with us: [ 45.468653] BUG: key ffffffc030fad4e0 not in .data! [ 45.468655] ------------[ cut here ]------------ [ 45.468666] WARNING: CPU: 0 PID: 1176 at /mnt/host/source/src/third_party/kernel/v3.18/kernel/locking/lockdep.c:2991 lockdep_init_map+0x12c/0x490() [ 45.468672] DEBUG_LOCKS_WARN_ON(1) [ 45.468672] CPU: 0 PID: 1176 Comm: iptables Tainted: G U W 3.18.0 #43 [ 45.468674] Hardware name: XXX [ 45.468675] Call trace: [ 45.468680] [] dump_backtrace+0x0/0x10c [ 45.468683] [] show_stack+0x10/0x1c [ 45.468688] [] dump_stack+0x74/0x94 [ 45.468692] [] warn_slowpath_common+0x84/0xb0 [ 45.468694] [] warn_slowpath_fmt+0x4c/0x58 [ 45.468697] [] lockdep_init_map+0x128/0x490 [ 45.468701] [] __kernfs_create_file+0x80/0xe4 [ 45.468704] [] sysfs_add_file_mode_ns+0x104/0x170 [ 45.468706] [] sysfs_create_file_ns+0x58/0x64 [ 45.468711] [] idletimer_tg_checkentry+0x14c/0x324 [ 45.468714] [] xt_check_target+0x170/0x198 [ 45.468717] [] check_target+0x58/0x6c [ 45.468720] [] translate_table+0x30c/0x424 [ 45.468723] [] do_ipt_set_ctl+0x144/0x1d0 [ 45.468728] [] nf_setsockopt+0x50/0x60 [ 45.468732] [] ip_setsockopt+0x8c/0xb4 [ 45.468735] [] raw_setsockopt+0x10/0x50 [ 45.468739] [] sock_common_setsockopt+0x14/0x20 [ 45.468742] [] SyS_setsockopt+0x88/0xb8 [ 45.468744] ---[ end trace 41d156354d18c039 ]--- Change-Id: I1da5cd96fc8e1e1e4209e81eba1165a42d4d45e9 Signed-off-by: Dmitry Torokhov --- net/netfilter/xt_IDLETIMER.c | 1 + 1 file changed, 1 insertion(+) 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; -- cgit v1.2.3 From 9db825899c5c68b03d784efaf42620e0a12da8ee Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Jul 2015 17:17:57 -0700 Subject: net: fix iterating over hashtable in tcp_nuke_addr() The actual size of the tcp hashinfo table is tcp_hashinfo.ehash_mask + 1 so we need to adjust the loop accordingly to get the sockets hashed into the last bucket. Change-Id: I796b3c7b4a1a7fa35fba9e5192a4a403eb6e17de Signed-off-by: Dmitry Torokhov --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3ebfb9aaf188..6824a441172b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3181,7 +3181,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); -- cgit v1.2.3 From 6265ed1db96795ba7f6c3c4a1a32c8ef25712237 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2015 14:48:52 -0700 Subject: net: xt_qtaguid/xt_socket: fix refcount underflow and crash xt_socket_get[4|6]_sk() do not always increment sock refcount, which causes confusion in xt_qtaguid module which is not aware of this fact and drops the reference whether it should have or not. Fix it by changing xt_socket_get[4|6]_sk() to always increment recount of returned sock. This should fix the following crash: [ 111.319523] BUG: failure at /mnt/host/source/src/third_party/kernel/v3.18/net/ipv4/inet_timewait_sock.c:90/__inet_twsk_kill()! [ 111.331192] Kernel panic - not syncing: BUG! [ 111.335468] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G U W 3.18.0-06867-g268df91 #1 [ 111.343810] Hardware name: Google Tegra210 Smaug Rev 1+ (DT) [ 111.349463] Call trace: [ 111.351917] [] dump_backtrace+0x0/0x10c [ 111.357314] [] show_stack+0x10/0x1c [ 111.362367] [] dump_stack+0x74/0x94 [ 111.367414] [] panic+0xec/0x238 [ 111.372116] [] __inet_twsk_kill+0xd0/0xf8 [ 111.377684] [] inet_twdr_do_twkill_work+0x64/0xd0 [ 111.383946] [] inet_twdr_hangman+0x2c/0xa4 [ 111.389602] [] call_timer_fn+0xac/0x160 [ 111.394995] [] run_timer_softirq+0x23c/0x274 [ 111.400824] [] __do_softirq+0x1a4/0x330 [ 111.406218] [] irq_exit+0x70/0xd0 [ 111.411093] [] __handle_domain_irq+0x84/0xa8 [ 111.416922] [] gic_handle_irq+0x4c/0x80 b/22476945 Originally reviewed at: https://chromium-review.googlesource.com/#/c/297414/ Signed-off-by: Dmitry Torokhov --- net/netfilter/xt_socket.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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); -- cgit v1.2.3 From 3ac97f2411ad66f872bd0fe401ad94af5ff4d7d3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2015 13:08:37 -0700 Subject: net: fix crash in tcp_nuke_addr() When iterating through sockets we need to skip sockets in TIME_WAIT state as they use lightweight structure inet_timewait_sock that does not have sk_lock member, and if we try to lock them we'll crash thusly: [ 89.376383] BUG: spinlock lockup suspected on CPU#0, netd/431 [ 89.382139] lock: 0xffffffc039d05070, .magic: 66d30606, .owner: /-1682098992, .owner_cpu: 0 [ 89.390598] CPU: 0 PID: 431 Comm: netd Tainted: G U W 3.18.0 #5 [ 89.397389] Hardware name: Google Tegra210 Smaug Rev 1+ (DT) [ 89.403049] Call trace: [ 89.405501] [] dump_backtrace+0x0/0x10c [ 89.410918] [] show_stack+0x10/0x1c [ 89.415971] [] dump_stack+0x74/0x94 [ 89.421018] [] spin_dump+0x78/0x88 [ 89.425984] [] do_raw_spin_lock+0xfc/0x158 [ 89.431666] [] _raw_spin_lock+0x34/0x44 [ 89.437059] [] tcp_nuke_addr+0x1fc/0x29c [ 89.442548] [] devinet_ioctl+0x288/0x680 [ 89.448053] [] inet_ioctl+0xc4/0xf4 [ 89.453103] [] sock_do_ioctl+0x2c/0x5c [ 89.458408] [] sock_ioctl+0x210/0x230 [ 89.463633] [] do_vfs_ioctl+0x4ac/0x590 [ 89.469049] [] SyS_ioctl+0x5c/0x88 (or with NULL pointer dereference if lockdep is still working). Change-Id: I07c70d9a60b125b1070ff05c4eec27daee1a3e90 Signed-off-by: Dmitry Torokhov --- net/ipv4/tcp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6824a441172b..1d4eef98ac65 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3191,8 +3191,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; -- cgit v1.2.3