diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-04-07 18:30:11 +0300 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2015-04-07 18:30:11 +0300 |
commit | ca147aeafcfbff5796e3234268663248e6ef479f (patch) | |
tree | fccb3733608c054a2c984bb3bbe6fac59163bcb5 | |
parent | 4f5708b1dc29da3a426c9fa99788155e5c462ece (diff) | |
parent | cc756f48f5d2548bafe85ddf8e3f9b9eb4a06213 (diff) |
Merge remote-tracking branch 'android_linaro/linaro-android-llct' into linaro-android-llct-patchedtracking-linaro-android-llct-llct-20150407.0
-rw-r--r-- | drivers/usb/gadget/Kconfig | 20 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 5 | ||||
-rw-r--r-- | drivers/usb/gadget/configfs.c | 35 | ||||
-rw-r--r-- | drivers/usb/gadget/f_accessory.c | 1215 | ||||
-rw-r--r-- | drivers/usb/gadget/f_audio_source.c | 834 | ||||
-rw-r--r-- | drivers/usb/gadget/f_mtp.c | 1457 | ||||
-rw-r--r-- | drivers/usb/gadget/function/Makefile | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_accessory.c | 168 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_audio_source.c | 248 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_mtp.c | 178 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_mtp.h (renamed from drivers/usb/gadget/f_mtp.h) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_ptp.c (renamed from drivers/usb/gadget/f_ptp.c) | 0 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_rndis.c (renamed from drivers/usb/gadget/functionf_rndis.c) | 0 | ||||
-rw-r--r-- | include/linux/atomic.h | 36 | ||||
-rw-r--r-- | include/linux/bitops.h | 20 | ||||
-rw-r--r-- | kernel/sched/core.c | 16 |
16 files changed, 631 insertions, 3609 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c90ef3893838..7bc52ab747d9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -200,6 +200,12 @@ config USB_F_MTP tristate config USB_F_PTP + tristate + +config USB_F_AUDIO_SRC + tristate + +config USB_F_ACC tristate choice @@ -454,6 +460,20 @@ config USB_CONFIGFS_F_PTP help USB gadget PTP support +config USB_CONFIGFS_F_ACC + boolean "Accessory gadget" + depends on USB_CONFIGFS + select USB_F_ACC + help + USB gadget Accessory support + +config USB_CONFIGFS_F_AUDIO_SRC + boolean "Audio Source gadget" + depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC + select USB_F_AUDIO_SRC + help + USB gadget Audio Source support + config USB_CONFIGFS_UEVENT boolean "Uevent notification of Gadget state" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index a0cd4f22ee77..767b2b50ec28 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -13,9 +13,4 @@ g_android-y := android.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ -usb_f_mtp-y := f_mtp.o -obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o -usb_f_ptp-y := f_ptp.o -obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o - obj-$(CONFIG_USB_G_ANDROID) += g_android.o diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 9ca7f8673100..253a3566b3fb 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -13,7 +13,6 @@ #include <linux/platform_device.h> #include <linux/kdev_t.h> #include <linux/usb/ch9.h> -#include "u_fs.h" #ifdef CONFIG_USB_CONFIGFS_F_ACC extern int acc_ctrlrequest(struct usb_composite_dev *cdev, @@ -21,6 +20,18 @@ extern int acc_ctrlrequest(struct usb_composite_dev *cdev, void acc_disconnect(void); #endif static struct class *android_class; +static struct device *android_device; +static int index; + +struct device *create_function_device(char *name) +{ + if (android_device && !IS_ERR(android_device)) + return device_create(android_class, android_device, + MKDEV(0, index++), NULL, name); + else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(create_function_device); #endif int check_user_usb_string(const char *name, @@ -1473,19 +1484,22 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; } @@ -1606,7 +1620,6 @@ static struct config_group *gadgets_make( gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) return ERR_PTR(-ENOMEM); - gi->group.default_groups = gi->default_groups; gi->group.default_groups[0] = &gi->functions_group; gi->group.default_groups[1] = &gi->configs_group; @@ -1644,8 +1657,10 @@ static struct config_group *gadgets_make( #ifdef CONFIG_USB_CONFIGFS_UEVENT INIT_WORK(&gi->work, android_work); - gi->dev = device_create(android_class, NULL, + android_device = device_create(android_class, NULL, MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + goto err; #endif if (!gi->composite.gadget_driver.function) @@ -1668,6 +1683,9 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { config_item_put(item); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + device_destroy(android_device->class, android_device->devt); +#endif } static struct configfs_group_operations gadgets_ops = { @@ -1719,5 +1737,10 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + if (!IS_ERR(android_class)) + class_destroy(android_class); +#endif + } module_exit(gadget_cfs_exit); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c deleted file mode 100644 index 0237f1e059b4..000000000000 --- a/drivers/usb/gadget/f_accessory.c +++ /dev/null @@ -1,1215 +0,0 @@ -/* - * Gadget Function Driver for Android USB accessories - * - * Copyright (C) 2011 Google, Inc. - * Author: Mike Lockwood <lockwood@android.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -/* #define DEBUG */ -/* #define VERBOSE_DEBUG */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/kthread.h> -#include <linux/freezer.h> - -#include <linux/types.h> -#include <linux/file.h> -#include <linux/device.h> -#include <linux/miscdevice.h> - -#include <linux/hid.h> -#include <linux/hiddev.h> -#include <linux/usb.h> -#include <linux/usb/ch9.h> -#include <linux/usb/f_accessory.h> - -#define BULK_BUFFER_SIZE 16384 -#define ACC_STRING_SIZE 256 - -#define PROTOCOL_VERSION 2 - -/* String IDs */ -#define INTERFACE_STRING_INDEX 0 - -/* number of tx and rx requests to allocate */ -#define TX_REQ_MAX 4 -#define RX_REQ_MAX 2 - -struct acc_hid_dev { - struct list_head list; - struct hid_device *hid; - struct acc_dev *dev; - /* accessory defined ID */ - int id; - /* HID report descriptor */ - u8 *report_desc; - /* length of HID report descriptor */ - int report_desc_len; - /* number of bytes of report_desc we have received so far */ - int report_desc_offset; -}; - -struct acc_dev { - struct usb_function function; - struct usb_composite_dev *cdev; - spinlock_t lock; - - struct usb_ep *ep_in; - struct usb_ep *ep_out; - - /* set to 1 when we connect */ - int online:1; - /* Set to 1 when we disconnect. - * Not cleared until our file is closed. - */ - int disconnected:1; - - /* strings sent by the host */ - char manufacturer[ACC_STRING_SIZE]; - char model[ACC_STRING_SIZE]; - char description[ACC_STRING_SIZE]; - char version[ACC_STRING_SIZE]; - char uri[ACC_STRING_SIZE]; - char serial[ACC_STRING_SIZE]; - - /* for acc_complete_set_string */ - int string_index; - - /* set to 1 if we have a pending start request */ - int start_requested; - - int audio_mode; - - /* synchronize access to our device file */ - atomic_t open_excl; - - struct list_head tx_idle; - - wait_queue_head_t read_wq; - wait_queue_head_t write_wq; - struct usb_request *rx_req[RX_REQ_MAX]; - int rx_done; - - /* delayed work for handling ACCESSORY_START */ - struct delayed_work start_work; - - /* worker for registering and unregistering hid devices */ - struct work_struct hid_work; - - /* list of active HID devices */ - struct list_head hid_list; - - /* list of new HID devices to register */ - struct list_head new_hid_list; - - /* list of dead HID devices to unregister */ - struct list_head dead_hid_list; -}; - -static struct usb_interface_descriptor acc_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, - .bInterfaceProtocol = 0, -}; - -static struct usb_endpoint_descriptor acc_highspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor acc_highspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_descriptor_header *fs_acc_descs[] = { - (struct usb_descriptor_header *) &acc_interface_desc, - (struct usb_descriptor_header *) &acc_fullspeed_in_desc, - (struct usb_descriptor_header *) &acc_fullspeed_out_desc, - NULL, -}; - -static struct usb_descriptor_header *hs_acc_descs[] = { - (struct usb_descriptor_header *) &acc_interface_desc, - (struct usb_descriptor_header *) &acc_highspeed_in_desc, - (struct usb_descriptor_header *) &acc_highspeed_out_desc, - NULL, -}; - -static struct usb_string acc_string_defs[] = { - [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", - { }, /* end of list */ -}; - -static struct usb_gadget_strings acc_string_table = { - .language = 0x0409, /* en-US */ - .strings = acc_string_defs, -}; - -static struct usb_gadget_strings *acc_strings[] = { - &acc_string_table, - NULL, -}; - -/* temporary variable used between acc_open() and acc_gadget_bind() */ -static struct acc_dev *_acc_dev; - -static inline struct acc_dev *func_to_dev(struct usb_function *f) -{ - return container_of(f, struct acc_dev, function); -} - -static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) -{ - struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!req) - return NULL; - - /* now allocate buffers for the requests */ - req->buf = kmalloc(buffer_size, GFP_KERNEL); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - return req; -} - -static void acc_request_free(struct usb_request *req, struct usb_ep *ep) -{ - if (req) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/* add a request to the tail of a list */ -static void req_put(struct acc_dev *dev, struct list_head *head, - struct usb_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - list_add_tail(&req->list, head); - spin_unlock_irqrestore(&dev->lock, flags); -} - -/* remove a request from the head of a list */ -static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) -{ - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&dev->lock, flags); - if (list_empty(head)) { - req = 0; - } else { - req = list_first_entry(head, struct usb_request, list); - list_del(&req->list); - } - spin_unlock_irqrestore(&dev->lock, flags); - return req; -} - -static void acc_set_disconnected(struct acc_dev *dev) -{ - dev->online = 0; - dev->disconnected = 1; -} - -static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) -{ - struct acc_dev *dev = _acc_dev; - - if (req->status == -ESHUTDOWN) { - pr_debug("acc_complete_in set disconnected"); - acc_set_disconnected(dev); - } - - req_put(dev, &dev->tx_idle, req); - - wake_up(&dev->write_wq); -} - -static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) -{ - struct acc_dev *dev = _acc_dev; - - dev->rx_done = 1; - if (req->status == -ESHUTDOWN) { - pr_debug("acc_complete_out set disconnected"); - acc_set_disconnected(dev); - } - - wake_up(&dev->read_wq); -} - -static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) -{ - struct acc_dev *dev = ep->driver_data; - char *string_dest = NULL; - int length = req->actual; - - if (req->status != 0) { - pr_err("acc_complete_set_string, err %d\n", req->status); - return; - } - - switch (dev->string_index) { - case ACCESSORY_STRING_MANUFACTURER: - string_dest = dev->manufacturer; - break; - case ACCESSORY_STRING_MODEL: - string_dest = dev->model; - break; - case ACCESSORY_STRING_DESCRIPTION: - string_dest = dev->description; - break; - case ACCESSORY_STRING_VERSION: - string_dest = dev->version; - break; - case ACCESSORY_STRING_URI: - string_dest = dev->uri; - break; - case ACCESSORY_STRING_SERIAL: - string_dest = dev->serial; - break; - } - if (string_dest) { - unsigned long flags; - - if (length >= ACC_STRING_SIZE) - length = ACC_STRING_SIZE - 1; - - spin_lock_irqsave(&dev->lock, flags); - memcpy(string_dest, req->buf, length); - /* ensure zero termination */ - string_dest[length] = 0; - spin_unlock_irqrestore(&dev->lock, flags); - } else { - pr_err("unknown accessory string index %d\n", - dev->string_index); - } -} - -static void acc_complete_set_hid_report_desc(struct usb_ep *ep, - struct usb_request *req) -{ - struct acc_hid_dev *hid = req->context; - struct acc_dev *dev = hid->dev; - int length = req->actual; - - if (req->status != 0) { - pr_err("acc_complete_set_hid_report_desc, err %d\n", - req->status); - return; - } - - memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); - hid->report_desc_offset += length; - if (hid->report_desc_offset == hid->report_desc_len) { - /* After we have received the entire report descriptor - * we schedule work to initialize the HID device - */ - schedule_work(&dev->hid_work); - } -} - -static void acc_complete_send_hid_event(struct usb_ep *ep, - struct usb_request *req) -{ - struct acc_hid_dev *hid = req->context; - int length = req->actual; - - if (req->status != 0) { - pr_err("acc_complete_send_hid_event, err %d\n", req->status); - return; - } - - hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); -} - -static int acc_hid_parse(struct hid_device *hid) -{ - struct acc_hid_dev *hdev = hid->driver_data; - - hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); - return 0; -} - -static int acc_hid_start(struct hid_device *hid) -{ - return 0; -} - -static void acc_hid_stop(struct hid_device *hid) -{ -} - -static int acc_hid_open(struct hid_device *hid) -{ - return 0; -} - -static void acc_hid_close(struct hid_device *hid) -{ -} - -static struct hid_ll_driver acc_hid_ll_driver = { - .parse = acc_hid_parse, - .start = acc_hid_start, - .stop = acc_hid_stop, - .open = acc_hid_open, - .close = acc_hid_close, -}; - -static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, - int id, int desc_len) -{ - struct acc_hid_dev *hdev; - - hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); - if (!hdev) - return NULL; - hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); - if (!hdev->report_desc) { - kfree(hdev); - return NULL; - } - hdev->dev = dev; - hdev->id = id; - hdev->report_desc_len = desc_len; - - return hdev; -} - -static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) -{ - struct acc_hid_dev *hid; - - list_for_each_entry(hid, list, list) { - if (hid->id == id) - return hid; - } - return NULL; -} - -static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) -{ - struct acc_hid_dev *hid; - unsigned long flags; - - /* report descriptor length must be > 0 */ - if (desc_length <= 0) - return -EINVAL; - - spin_lock_irqsave(&dev->lock, flags); - /* replace HID if one already exists with this ID */ - hid = acc_hid_get(&dev->hid_list, id); - if (!hid) - hid = acc_hid_get(&dev->new_hid_list, id); - if (hid) - list_move(&hid->list, &dev->dead_hid_list); - - hid = acc_hid_new(dev, id, desc_length); - if (!hid) { - spin_unlock_irqrestore(&dev->lock, flags); - return -ENOMEM; - } - - list_add(&hid->list, &dev->new_hid_list); - spin_unlock_irqrestore(&dev->lock, flags); - - /* schedule work to register the HID device */ - schedule_work(&dev->hid_work); - return 0; -} - -static int acc_unregister_hid(struct acc_dev *dev, int id) -{ - struct acc_hid_dev *hid; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - hid = acc_hid_get(&dev->hid_list, id); - if (!hid) - hid = acc_hid_get(&dev->new_hid_list, id); - if (!hid) { - spin_unlock_irqrestore(&dev->lock, flags); - return -EINVAL; - } - - list_move(&hid->list, &dev->dead_hid_list); - spin_unlock_irqrestore(&dev->lock, flags); - - schedule_work(&dev->hid_work); - return 0; -} - -static int create_bulk_endpoints(struct acc_dev *dev, - struct usb_endpoint_descriptor *in_desc, - struct usb_endpoint_descriptor *out_desc) -{ - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req; - struct usb_ep *ep; - int i; - - DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); - - ep = usb_ep_autoconfig(cdev->gadget, in_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_in = ep; - - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - - /* now allocate requests for our endpoints */ - for (i = 0; i < TX_REQ_MAX; i++) { - req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = acc_complete_in; - req_put(dev, &dev->tx_idle, req); - } - for (i = 0; i < RX_REQ_MAX; i++) { - req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = acc_complete_out; - dev->rx_req[i] = req; - } - - return 0; - -fail: - pr_err("acc_bind() could not allocate requests\n"); - while ((req = req_get(dev, &dev->tx_idle))) - acc_request_free(req, dev->ep_in); - for (i = 0; i < RX_REQ_MAX; i++) - acc_request_free(dev->rx_req[i], dev->ep_out); - return -1; -} - -static ssize_t acc_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) -{ - struct acc_dev *dev = fp->private_data; - struct usb_request *req; - ssize_t r = count; - unsigned xfer; - int ret = 0; - - pr_debug("acc_read(%zu)\n", count); - - if (dev->disconnected) { - pr_debug("acc_read disconnected"); - return -ENODEV; - } - - if (count > BULK_BUFFER_SIZE) - count = BULK_BUFFER_SIZE; - - /* we will block until we're online */ - pr_debug("acc_read: waiting for online\n"); - ret = wait_event_interruptible(dev->read_wq, dev->online); - if (ret < 0) { - r = ret; - goto done; - } - - if (dev->rx_done) { - // last req cancelled. try to get it. - req = dev->rx_req[0]; - goto copy_data; - } - -requeue_req: - /* queue a request */ - req = dev->rx_req[0]; - req->length = count; - dev->rx_done = 0; - ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); - if (ret < 0) { - r = -EIO; - goto done; - } else { - pr_debug("rx %p queue\n", req); - } - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->read_wq, dev->rx_done); - if (ret < 0) { - r = ret; - ret = usb_ep_dequeue(dev->ep_out, req); - if (ret != 0) { - // cancel failed. There can be a data already received. - // it will be retrieved in the next read. - pr_debug("acc_read: cancelling failed %d", ret); - } - goto done; - } - -copy_data: - dev->rx_done = 0; - if (dev->online) { - /* If we got a 0-len packet, throw it back and try again. */ - if (req->actual == 0) - goto requeue_req; - - pr_debug("rx %p %u\n", req, req->actual); - xfer = (req->actual < count) ? req->actual : count; - r = xfer; - if (copy_to_user(buf, req->buf, xfer)) - r = -EFAULT; - } else - r = -EIO; - -done: - pr_debug("acc_read returning %zd\n", r); - return r; -} - -static ssize_t acc_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct acc_dev *dev = fp->private_data; - struct usb_request *req = 0; - ssize_t r = count; - unsigned xfer; - int ret; - - pr_debug("acc_write(%zu)\n", count); - - if (!dev->online || dev->disconnected) { - pr_debug("acc_write disconnected or not online"); - return -ENODEV; - } - - while (count > 0) { - if (!dev->online) { - pr_debug("acc_write dev->error\n"); - r = -EIO; - break; - } - - /* get an idle tx request to use */ - req = 0; - ret = wait_event_interruptible(dev->write_wq, - ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); - if (!req) { - r = ret; - break; - } - - if (count > BULK_BUFFER_SIZE) { - xfer = BULK_BUFFER_SIZE; - /* ZLP, They will be more TX requests so not yet. */ - req->zero = 0; - } else { - xfer = count; - /* If the data length is a multple of the - * maxpacket size then send a zero length packet(ZLP). - */ - req->zero = ((xfer % dev->ep_in->maxpacket) == 0); - } - if (copy_from_user(req->buf, buf, xfer)) { - r = -EFAULT; - break; - } - - req->length = xfer; - ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); - if (ret < 0) { - pr_debug("acc_write: xfer error %d\n", ret); - r = -EIO; - break; - } - - buf += xfer; - count -= xfer; - - /* zero this so we don't try to free it on error exit */ - req = 0; - } - - if (req) - req_put(dev, &dev->tx_idle, req); - - pr_debug("acc_write returning %zd\n", r); - return r; -} - -static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) -{ - struct acc_dev *dev = fp->private_data; - char *src = NULL; - int ret; - - switch (code) { - case ACCESSORY_GET_STRING_MANUFACTURER: - src = dev->manufacturer; - break; - case ACCESSORY_GET_STRING_MODEL: - src = dev->model; - break; - case ACCESSORY_GET_STRING_DESCRIPTION: - src = dev->description; - break; - case ACCESSORY_GET_STRING_VERSION: - src = dev->version; - break; - case ACCESSORY_GET_STRING_URI: - src = dev->uri; - break; - case ACCESSORY_GET_STRING_SERIAL: - src = dev->serial; - break; - case ACCESSORY_IS_START_REQUESTED: - return dev->start_requested; - case ACCESSORY_GET_AUDIO_MODE: - return dev->audio_mode; - } - if (!src) - return -EINVAL; - - ret = strlen(src) + 1; - if (copy_to_user((void __user *)value, src, ret)) - ret = -EFAULT; - return ret; -} - -static int acc_open(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "acc_open\n"); - if (atomic_xchg(&_acc_dev->open_excl, 1)) - return -EBUSY; - - _acc_dev->disconnected = 0; - fp->private_data = _acc_dev; - return 0; -} - -static int acc_release(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "acc_release\n"); - - WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); - _acc_dev->disconnected = 0; - return 0; -} - -/* file operations for /dev/usb_accessory */ -static const struct file_operations acc_fops = { - .owner = THIS_MODULE, - .read = acc_read, - .write = acc_write, - .unlocked_ioctl = acc_ioctl, - .open = acc_open, - .release = acc_release, -}; - -static int acc_hid_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) - return ret; - return hid_hw_start(hdev, HID_CONNECT_DEFAULT); -} - -static struct miscdevice acc_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "usb_accessory", - .fops = &acc_fops, -}; - -static const struct hid_device_id acc_hid_table[] = { - { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, - { } -}; - -static struct hid_driver acc_hid_driver = { - .name = "USB accessory", - .id_table = acc_hid_table, - .probe = acc_hid_probe, -}; - -static int acc_ctrlrequest(struct usb_composite_dev *cdev, - const struct usb_ctrlrequest *ctrl) -{ - struct acc_dev *dev = _acc_dev; - int value = -EOPNOTSUPP; - struct acc_hid_dev *hid; - int offset; - u8 b_requestType = ctrl->bRequestType; - u8 b_request = ctrl->bRequest; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - unsigned long flags; - -/* - printk(KERN_INFO "acc_ctrlrequest " - "%02x.%02x v%04x i%04x l%u\n", - b_requestType, b_request, - w_value, w_index, w_length); -*/ - - if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { - if (b_request == ACCESSORY_START) { - dev->start_requested = 1; - schedule_delayed_work( - &dev->start_work, msecs_to_jiffies(10)); - value = 0; - } else if (b_request == ACCESSORY_SEND_STRING) { - dev->string_index = w_index; - cdev->gadget->ep0->driver_data = dev; - cdev->req->complete = acc_complete_set_string; - value = w_length; - } else if (b_request == ACCESSORY_SET_AUDIO_MODE && - w_index == 0 && w_length == 0) { - dev->audio_mode = w_value; - value = 0; - } else if (b_request == ACCESSORY_REGISTER_HID) { - value = acc_register_hid(dev, w_value, w_index); - } else if (b_request == ACCESSORY_UNREGISTER_HID) { - value = acc_unregister_hid(dev, w_value); - } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { - spin_lock_irqsave(&dev->lock, flags); - hid = acc_hid_get(&dev->new_hid_list, w_value); - spin_unlock_irqrestore(&dev->lock, flags); - if (!hid) { - value = -EINVAL; - goto err; - } - offset = w_index; - if (offset != hid->report_desc_offset - || offset + w_length > hid->report_desc_len) { - value = -EINVAL; - goto err; - } - cdev->req->context = hid; - cdev->req->complete = acc_complete_set_hid_report_desc; - value = w_length; - } else if (b_request == ACCESSORY_SEND_HID_EVENT) { - spin_lock_irqsave(&dev->lock, flags); - hid = acc_hid_get(&dev->hid_list, w_value); - spin_unlock_irqrestore(&dev->lock, flags); - if (!hid) { - value = -EINVAL; - goto err; - } - cdev->req->context = hid; - cdev->req->complete = acc_complete_send_hid_event; - value = w_length; - } - } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { - if (b_request == ACCESSORY_GET_PROTOCOL) { - *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; - value = sizeof(u16); - - /* clear any string left over from a previous session */ - memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); - memset(dev->model, 0, sizeof(dev->model)); - memset(dev->description, 0, sizeof(dev->description)); - memset(dev->version, 0, sizeof(dev->version)); - memset(dev->uri, 0, sizeof(dev->uri)); - memset(dev->serial, 0, sizeof(dev->serial)); - dev->start_requested = 0; - dev->audio_mode = 0; - } - } - - if (value >= 0) { - cdev->req->zero = 0; - cdev->req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); - if (value < 0) - ERROR(cdev, "%s setup response queue error\n", - __func__); - } - -err: - if (value == -EOPNOTSUPP) - VDBG(cdev, - "unknown class-specific control req " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - return value; -} - -static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct acc_dev *dev = func_to_dev(f); - int id; - int ret; - - DBG(cdev, "acc_function_bind dev: %p\n", dev); - - ret = hid_register_driver(&acc_hid_driver); - if (ret) - return ret; - - dev->start_requested = 0; - - /* allocate interface ID(s) */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - acc_interface_desc.bInterfaceNumber = id; - - /* allocate endpoints */ - ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, - &acc_fullspeed_out_desc); - if (ret) - return ret; - - /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - acc_highspeed_in_desc.bEndpointAddress = - acc_fullspeed_in_desc.bEndpointAddress; - acc_highspeed_out_desc.bEndpointAddress = - acc_fullspeed_out_desc.bEndpointAddress; - } - - DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - f->name, dev->ep_in->name, dev->ep_out->name); - return 0; -} - -static void -kill_all_hid_devices(struct acc_dev *dev) -{ - struct acc_hid_dev *hid; - struct list_head *entry, *temp; - unsigned long flags; - - /* do nothing if usb accessory device doesn't exist */ - if (!dev) - return; - - spin_lock_irqsave(&dev->lock, flags); - list_for_each_safe(entry, temp, &dev->hid_list) { - hid = list_entry(entry, struct acc_hid_dev, list); - list_del(&hid->list); - list_add(&hid->list, &dev->dead_hid_list); - } - list_for_each_safe(entry, temp, &dev->new_hid_list) { - hid = list_entry(entry, struct acc_hid_dev, list); - list_del(&hid->list); - list_add(&hid->list, &dev->dead_hid_list); - } - spin_unlock_irqrestore(&dev->lock, flags); - - schedule_work(&dev->hid_work); -} - -static void -acc_hid_unbind(struct acc_dev *dev) -{ - hid_unregister_driver(&acc_hid_driver); - kill_all_hid_devices(dev); -} - -static void -acc_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct acc_dev *dev = func_to_dev(f); - struct usb_request *req; - int i; - - while ((req = req_get(dev, &dev->tx_idle))) - acc_request_free(req, dev->ep_in); - for (i = 0; i < RX_REQ_MAX; i++) - acc_request_free(dev->rx_req[i], dev->ep_out); - - acc_hid_unbind(dev); -} - -static void acc_start_work(struct work_struct *data) -{ - char *envp[2] = { "ACCESSORY=START", NULL }; - kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); -} - -static int acc_hid_init(struct acc_hid_dev *hdev) -{ - struct hid_device *hid; - int ret; - - hid = hid_allocate_device(); - if (IS_ERR(hid)) - return PTR_ERR(hid); - - hid->ll_driver = &acc_hid_ll_driver; - hid->dev.parent = acc_device.this_device; - - hid->bus = BUS_USB; - hid->vendor = HID_ANY_ID; - hid->product = HID_ANY_ID; - hid->driver_data = hdev; - ret = hid_add_device(hid); - if (ret) { - pr_err("can't add hid device: %d\n", ret); - hid_destroy_device(hid); - return ret; - } - - hdev->hid = hid; - return 0; -} - -static void acc_hid_delete(struct acc_hid_dev *hid) -{ - kfree(hid->report_desc); - kfree(hid); -} - -static void acc_hid_work(struct work_struct *data) -{ - struct acc_dev *dev = _acc_dev; - struct list_head *entry, *temp; - struct acc_hid_dev *hid; - struct list_head new_list, dead_list; - unsigned long flags; - - INIT_LIST_HEAD(&new_list); - - spin_lock_irqsave(&dev->lock, flags); - - /* copy hids that are ready for initialization to new_list */ - list_for_each_safe(entry, temp, &dev->new_hid_list) { - hid = list_entry(entry, struct acc_hid_dev, list); - if (hid->report_desc_offset == hid->report_desc_len) - list_move(&hid->list, &new_list); - } - - if (list_empty(&dev->dead_hid_list)) { - INIT_LIST_HEAD(&dead_list); - } else { - /* move all of dev->dead_hid_list to dead_list */ - dead_list.prev = dev->dead_hid_list.prev; - dead_list.next = dev->dead_hid_list.next; - dead_list.next->prev = &dead_list; - dead_list.prev->next = &dead_list; - INIT_LIST_HEAD(&dev->dead_hid_list); - } - - spin_unlock_irqrestore(&dev->lock, flags); - - /* register new HID devices */ - list_for_each_safe(entry, temp, &new_list) { - hid = list_entry(entry, struct acc_hid_dev, list); - if (acc_hid_init(hid)) { - pr_err("can't add HID device %p\n", hid); - acc_hid_delete(hid); - } else { - spin_lock_irqsave(&dev->lock, flags); - list_move(&hid->list, &dev->hid_list); - spin_unlock_irqrestore(&dev->lock, flags); - } - } - - /* remove dead HID devices */ - list_for_each_safe(entry, temp, &dead_list) { - hid = list_entry(entry, struct acc_hid_dev, list); - list_del(&hid->list); - if (hid->hid) - hid_destroy_device(hid->hid); - acc_hid_delete(hid); - } -} - -static int acc_function_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct acc_dev *dev = func_to_dev(f); - struct usb_composite_dev *cdev = f->config->cdev; - int ret; - - DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_in); - if (ret) - return ret; - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_out); - if (ret) { - usb_ep_disable(dev->ep_in); - return ret; - } - - dev->online = 1; - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - return 0; -} - -static void acc_function_disable(struct usb_function *f) -{ - struct acc_dev *dev = func_to_dev(f); - struct usb_composite_dev *cdev = dev->cdev; - - DBG(cdev, "acc_function_disable\n"); - acc_set_disconnected(dev); - usb_ep_disable(dev->ep_in); - usb_ep_disable(dev->ep_out); - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - - VDBG(cdev, "%s disabled\n", dev->function.name); -} - -static int acc_bind_config(struct usb_configuration *c) -{ - struct acc_dev *dev = _acc_dev; - int ret; - - printk(KERN_INFO "acc_bind_config\n"); - - /* allocate a string ID for our interface */ - if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - acc_string_defs[INTERFACE_STRING_INDEX].id = ret; - acc_interface_desc.iInterface = ret; - } - - dev->cdev = c->cdev; - dev->function.name = "accessory"; - dev->function.strings = acc_strings, - dev->function.fs_descriptors = fs_acc_descs; - dev->function.hs_descriptors = hs_acc_descs; - dev->function.bind = acc_function_bind; - dev->function.unbind = acc_function_unbind; - dev->function.set_alt = acc_function_set_alt; - dev->function.disable = acc_function_disable; - - return usb_add_function(c, &dev->function); -} - -static int acc_setup(void) -{ - struct acc_dev *dev; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock_init(&dev->lock); - init_waitqueue_head(&dev->read_wq); - init_waitqueue_head(&dev->write_wq); - atomic_set(&dev->open_excl, 0); - INIT_LIST_HEAD(&dev->tx_idle); - INIT_LIST_HEAD(&dev->hid_list); - INIT_LIST_HEAD(&dev->new_hid_list); - INIT_LIST_HEAD(&dev->dead_hid_list); - INIT_DELAYED_WORK(&dev->start_work, acc_start_work); - INIT_WORK(&dev->hid_work, acc_hid_work); - - /* _acc_dev must be set before calling usb_gadget_register_driver */ - _acc_dev = dev; - - ret = misc_register(&acc_device); - if (ret) - goto err; - - return 0; - -err: - kfree(dev); - pr_err("USB accessory gadget driver failed to initialize\n"); - return ret; -} - -static void acc_disconnect(void) -{ - /* unregister all HID devices if USB is disconnected */ - kill_all_hid_devices(_acc_dev); -} - -static void acc_cleanup(void) -{ - misc_deregister(&acc_device); - kfree(_acc_dev); - _acc_dev = NULL; -} diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c deleted file mode 100644 index 4b9786b48950..000000000000 --- a/drivers/usb/gadget/f_audio_source.c +++ /dev/null @@ -1,834 +0,0 @@ -/* - * Gadget Function Driver for USB audio source device - * - * Copyright (C) 2012 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/device.h> -#include <linux/usb/audio.h> -#include <linux/wait.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> - -#define SAMPLE_RATE 44100 -#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) - -#define IN_EP_MAX_PACKET_SIZE 256 - -/* Number of requests to allocate */ -#define IN_EP_REQ_COUNT 4 - -#define AUDIO_AC_INTERFACE 0 -#define AUDIO_AS_INTERFACE 1 -#define AUDIO_NUM_INTERFACES 2 - -/* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, -}; - -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); - -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) -/* 1 input terminal, 1 output terminal and 1 feature unit */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ - + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ - + UAC_DT_FEATURE_UNIT_SIZE(0)) -/* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), - .bInCollection = AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - [0] = AUDIO_AC_INTERFACE, - [1] = AUDIO_AS_INTERFACE, - } -}; - -#define INPUT_TERMINAL_ID 1 -static struct uac_input_terminal_descriptor input_terminal_desc = { - .bLength = UAC_DT_INPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = INPUT_TERMINAL_ID, - .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, - .bAssocTerminal = 0, - .wChannelConfig = 0x3, -}; - -DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); - -#define FEATURE_UNIT_ID 2 -static struct uac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FEATURE_UNIT, - .bUnitID = FEATURE_UNIT_ID, - .bSourceID = INPUT_TERMINAL_ID, - .bControlSize = 2, -}; - -#define OUTPUT_TERMINAL_ID 3 -static struct uac1_output_terminal_descriptor output_terminal_desc = { - .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = OUTPUT_TERMINAL_ID, - .wTerminalType = UAC_TERMINAL_STREAMING, - .bAssocTerminal = FEATURE_UNIT_ID, - .bSourceID = FEATURE_UNIT_ID, -}; - -/* B.4.1 Standard AS Interface Descriptor */ -static struct usb_interface_descriptor as_interface_alt_0_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -static struct usb_interface_descriptor as_interface_alt_1_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -/* B.4.2 Class-Specific AS Interface Descriptor */ -static struct uac1_as_header_descriptor as_header_desc = { - .bLength = UAC_DT_AS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_AS_GENERAL, - .bTerminalLink = INPUT_TERMINAL_ID, - .bDelay = 1, - .wFormatTag = UAC_FORMAT_TYPE_I_PCM, -}; - -DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); - -static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { - .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, - .bSubframeSize = 2, - .bBitResolution = 16, - .bSamFreqType = 1, -}; - -/* Standard ISO IN Endpoint Descriptor for highspeed */ -static struct usb_endpoint_descriptor hs_as_in_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_SYNC_SYNC - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), - .bInterval = 4, /* poll 1 per millisecond */ -}; - -/* Standard ISO IN Endpoint Descriptor for highspeed */ -static struct usb_endpoint_descriptor fs_as_in_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_SYNC_SYNC - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), - .bInterval = 1, /* poll 1 per millisecond */ -}; - -/* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct uac_iso_endpoint_descriptor as_iso_in_desc = { - .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = UAC_EP_GENERAL, - .bmAttributes = 1, - .bLockDelayUnits = 1, - .wLockDelay = __constant_cpu_to_le16(1), -}; - -static struct usb_descriptor_header *hs_audio_desc[] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&input_terminal_desc, - (struct usb_descriptor_header *)&output_terminal_desc, - (struct usb_descriptor_header *)&feature_unit_desc, - - (struct usb_descriptor_header *)&as_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_header_desc, - - (struct usb_descriptor_header *)&as_type_i_desc, - - (struct usb_descriptor_header *)&hs_as_in_ep_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -static struct usb_descriptor_header *fs_audio_desc[] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&input_terminal_desc, - (struct usb_descriptor_header *)&output_terminal_desc, - (struct usb_descriptor_header *)&feature_unit_desc, - - (struct usb_descriptor_header *)&as_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_header_desc, - - (struct usb_descriptor_header *)&as_type_i_desc, - - (struct usb_descriptor_header *)&fs_as_in_ep_desc, - (struct usb_descriptor_header *)&as_iso_in_desc, - NULL, -}; - -static struct snd_pcm_hardware audio_hw_info = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 2, - .channels_max = 2, - .rate_min = SAMPLE_RATE, - .rate_max = SAMPLE_RATE, - - .buffer_bytes_max = 1024 * 1024, - .period_bytes_min = 64, - .period_bytes_max = 512 * 1024, - .periods_min = 2, - .periods_max = 1024, -}; - -/*-------------------------------------------------------------------------*/ - -struct audio_source_config { - int card; - int device; -}; - -struct audio_dev { - struct usb_function func; - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm_substream *substream; - - struct list_head idle_reqs; - struct usb_ep *in_ep; - - spinlock_t lock; - - /* beginning, end and current position in our buffer */ - void *buffer_start; - void *buffer_end; - void *buffer_pos; - - /* byte size of a "period" */ - unsigned int period; - /* bytes sent since last call to snd_pcm_period_elapsed */ - unsigned int period_offset; - /* time we started playing */ - ktime_t start_time; - /* number of frames sent since start_time */ - s64 frames_sent; -}; - -static inline struct audio_dev *func_to_audio(struct usb_function *f) -{ - return container_of(f, struct audio_dev, func); -} - -/*-------------------------------------------------------------------------*/ - -static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) -{ - struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!req) - return NULL; - - req->buf = kmalloc(buffer_size, GFP_KERNEL); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - req->length = buffer_size; - return req; -} - -static void audio_request_free(struct usb_request *req, struct usb_ep *ep) -{ - if (req) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -static void audio_req_put(struct audio_dev *audio, struct usb_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&audio->lock, flags); - list_add_tail(&req->list, &audio->idle_reqs); - spin_unlock_irqrestore(&audio->lock, flags); -} - -static struct usb_request *audio_req_get(struct audio_dev *audio) -{ - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&audio->lock, flags); - if (list_empty(&audio->idle_reqs)) { - req = 0; - } else { - req = list_first_entry(&audio->idle_reqs, struct usb_request, - list); - list_del(&req->list); - } - spin_unlock_irqrestore(&audio->lock, flags); - return req; -} - -/* send the appropriate number of packets to match our bitrate */ -static void audio_send(struct audio_dev *audio) -{ - struct snd_pcm_runtime *runtime; - struct usb_request *req; - int length, length1, length2, ret; - s64 msecs; - s64 frames; - ktime_t now; - - /* audio->substream will be null if we have been closed */ - if (!audio->substream) - return; - /* audio->buffer_pos will be null if we have been stopped */ - if (!audio->buffer_pos) - return; - - runtime = audio->substream->runtime; - - /* compute number of frames to send */ - now = ktime_get(); - msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); - do_div(msecs, 1000000); - frames = msecs * SAMPLE_RATE; - do_div(frames, 1000); - - /* Readjust our frames_sent if we fall too far behind. - * If we get too far behind it is better to drop some frames than - * to keep sending data too fast in an attempt to catch up. - */ - if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) - audio->frames_sent = frames - FRAMES_PER_MSEC; - - frames -= audio->frames_sent; - - /* We need to send something to keep the pipeline going */ - if (frames <= 0) - frames = FRAMES_PER_MSEC; - - while (frames > 0) { - req = audio_req_get(audio); - if (!req) - break; - - length = frames_to_bytes(runtime, frames); - if (length > IN_EP_MAX_PACKET_SIZE) - length = IN_EP_MAX_PACKET_SIZE; - - if (audio->buffer_pos + length > audio->buffer_end) - length1 = audio->buffer_end - audio->buffer_pos; - else - length1 = length; - memcpy(req->buf, audio->buffer_pos, length1); - if (length1 < length) { - /* Wrap around and copy remaining length - * at beginning of buffer. - */ - length2 = length - length1; - memcpy(req->buf + length1, audio->buffer_start, - length2); - audio->buffer_pos = audio->buffer_start + length2; - } else { - audio->buffer_pos += length1; - if (audio->buffer_pos >= audio->buffer_end) - audio->buffer_pos = audio->buffer_start; - } - - req->length = length; - ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); - if (ret < 0) { - pr_err("usb_ep_queue failed ret: %d\n", ret); - audio_req_put(audio, req); - break; - } - - frames -= bytes_to_frames(runtime, length); - audio->frames_sent += bytes_to_frames(runtime, length); - } -} - -static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) -{ - /* nothing to do here */ -} - -static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct audio_dev *audio = req->context; - - pr_debug("audio_data_complete req->status %d req->actual %d\n", - req->status, req->actual); - - audio_req_put(audio, req); - - if (!audio->buffer_start || req->status) - return; - - audio->period_offset += req->actual; - if (audio->period_offset >= audio->period) { - snd_pcm_period_elapsed(audio->substream); - audio->period_offset = 0; - } - audio_send(audio); -} - -static int audio_set_endpoint_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - int value = -EOPNOTSUPP; - u16 ep = le16_to_cpu(ctrl->wIndex); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_SET_CUR: - case UAC_SET_MIN: - case UAC_SET_MAX: - case UAC_SET_RES: - value = len; - break; - default: - break; - } - - return value; -} - -static int audio_get_endpoint_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = f->config->cdev; - int value = -EOPNOTSUPP; - u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - u8 *buf = cdev->req->buf; - - pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { - switch (ctrl->bRequest) { - case UAC_GET_CUR: - case UAC_GET_MIN: - case UAC_GET_MAX: - case UAC_GET_RES: - /* return our sample rate */ - buf[0] = (u8)SAMPLE_RATE; - buf[1] = (u8)(SAMPLE_RATE >> 8); - buf[2] = (u8)(SAMPLE_RATE >> 16); - value = 3; - break; - default: - break; - } - } - - return value; -} - -static int -audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* composite driver infrastructure handles everything; interface - * activation uses set_alt(). - */ - switch (ctrl->bRequestType) { - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_set_endpoint_req(f, ctrl); - break; - - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_get_endpoint_req(f, ctrl); - break; - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - req->zero = 0; - req->length = value; - req->complete = audio_control_complete; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - pr_err("audio response on err %d\n", value); - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) -{ - struct audio_dev *audio = func_to_audio(f); - struct usb_composite_dev *cdev = f->config->cdev; - int ret; - - pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); - - ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); - if (ret) - return ret; - - usb_ep_enable(audio->in_ep); - return 0; -} - -static void audio_disable(struct usb_function *f) -{ - struct audio_dev *audio = func_to_audio(f); - - pr_debug("audio_disable\n"); - usb_ep_disable(audio->in_ep); -} - -/*-------------------------------------------------------------------------*/ - -static void audio_build_desc(struct audio_dev *audio) -{ - u8 *sam_freq; - int rate; - - /* Set channel numbers */ - input_terminal_desc.bNrChannels = 2; - as_type_i_desc.bNrChannels = 2; - - /* Set sample rates */ - rate = SAMPLE_RATE; - sam_freq = as_type_i_desc.tSamFreq[0]; - memcpy(sam_freq, &rate, 3); -} - -/* audio function driver setup/binding */ -static int -audio_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct audio_dev *audio = func_to_audio(f); - int status; - struct usb_ep *ep; - struct usb_request *req; - int i; - - audio_build_desc(audio); - - /* allocate instance-specific interface IDs, and patch descriptors */ - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - ac_interface_desc.bInterfaceNumber = status; - - /* AUDIO_AC_INTERFACE */ - ac_header_desc.baInterfaceNr[0] = status; - - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - as_interface_alt_0_desc.bInterfaceNumber = status; - as_interface_alt_1_desc.bInterfaceNumber = status; - - /* AUDIO_AS_INTERFACE */ - ac_header_desc.baInterfaceNr[1] = status; - - status = -ENODEV; - - /* allocate our endpoint */ - ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); - if (!ep) - goto fail; - audio->in_ep = ep; - ep->driver_data = audio; /* claim */ - - if (gadget_is_dualspeed(c->cdev->gadget)) - hs_as_in_ep_desc.bEndpointAddress = - fs_as_in_ep_desc.bEndpointAddress; - - f->fs_descriptors = fs_audio_desc; - f->hs_descriptors = hs_audio_desc; - - for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { - req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); - if (req) { - req->context = audio; - req->complete = audio_data_complete; - audio_req_put(audio, req); - } else - status = -ENOMEM; - } - -fail: - return status; -} - -static void -audio_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct audio_dev *audio = func_to_audio(f); - struct usb_request *req; - - while ((req = audio_req_get(audio))) - audio_request_free(req, audio->in_ep); - - snd_card_free_when_closed(audio->card); - audio->card = NULL; - audio->pcm = NULL; - audio->substream = NULL; - audio->in_ep = NULL; -} - -static void audio_pcm_playback_start(struct audio_dev *audio) -{ - audio->start_time = ktime_get(); - audio->frames_sent = 0; - audio_send(audio); -} - -static void audio_pcm_playback_stop(struct audio_dev *audio) -{ - unsigned long flags; - - spin_lock_irqsave(&audio->lock, flags); - audio->buffer_start = 0; - audio->buffer_end = 0; - audio->buffer_pos = 0; - spin_unlock_irqrestore(&audio->lock, flags); -} - -static int audio_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct audio_dev *audio = substream->private_data; - - runtime->private_data = audio; - runtime->hw = audio_hw_info; - snd_pcm_limit_hw_rates(runtime); - runtime->hw.channels_max = 2; - - audio->substream = substream; - return 0; -} - -static int audio_pcm_close(struct snd_pcm_substream *substream) -{ - struct audio_dev *audio = substream->private_data; - unsigned long flags; - - spin_lock_irqsave(&audio->lock, flags); - audio->substream = NULL; - spin_unlock_irqrestore(&audio->lock, flags); - - return 0; -} - -static int audio_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - unsigned int channels = params_channels(params); - unsigned int rate = params_rate(params); - - if (rate != SAMPLE_RATE) - return -EINVAL; - if (channels != 2) - return -EINVAL; - - return snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(params)); -} - -static int audio_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int audio_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct audio_dev *audio = runtime->private_data; - - audio->period = snd_pcm_lib_period_bytes(substream); - audio->period_offset = 0; - audio->buffer_start = runtime->dma_area; - audio->buffer_end = audio->buffer_start - + snd_pcm_lib_buffer_bytes(substream); - audio->buffer_pos = audio->buffer_start; - - return 0; -} - -static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct audio_dev *audio = runtime->private_data; - ssize_t bytes = audio->buffer_pos - audio->buffer_start; - - /* return offset of next frame to fill in our buffer */ - return bytes_to_frames(runtime, bytes); -} - -static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct audio_dev *audio = substream->runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - audio_pcm_playback_start(audio); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - audio_pcm_playback_stop(audio); - break; - - default: - ret = -EINVAL; - } - - return ret; -} - -static struct audio_dev _audio_dev = { - .func = { - .name = "audio_source", - .bind = audio_bind, - .unbind = audio_unbind, - .set_alt = audio_set_alt, - .setup = audio_setup, - .disable = audio_disable, - }, - .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), - .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), -}; - -static struct snd_pcm_ops audio_playback_ops = { - .open = audio_pcm_open, - .close = audio_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = audio_pcm_hw_params, - .hw_free = audio_pcm_hw_free, - .prepare = audio_pcm_prepare, - .trigger = audio_pcm_playback_trigger, - .pointer = audio_pcm_pointer, -}; - -int audio_source_bind_config(struct usb_configuration *c, - struct audio_source_config *config) -{ - struct audio_dev *audio; - struct snd_card *card; - struct snd_pcm *pcm; - int err; - - config->card = -1; - config->device = -1; - - audio = &_audio_dev; - - err = snd_card_new(&c->cdev->gadget->dev, - SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (err) - return err; - - err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); - if (err) - goto pcm_fail; - - pcm->private_data = audio; - pcm->info_flags = 0; - audio->pcm = pcm; - - strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - NULL, 0, 64 * 1024); - - strlcpy(card->driver, "audio_source", sizeof(card->driver)); - strlcpy(card->shortname, card->driver, sizeof(card->shortname)); - strlcpy(card->longname, "USB accessory audio source", - sizeof(card->longname)); - - err = snd_card_register(card); - if (err) - goto register_fail; - - err = usb_add_function(c, &audio->func); - if (err) - goto add_fail; - - config->card = pcm->card->number; - config->device = pcm->device; - audio->card = card; - return 0; - -add_fail: -register_fail: -pcm_fail: - snd_card_free(audio->card); - return err; -} diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c deleted file mode 100644 index 82f6b2ebaebb..000000000000 --- a/drivers/usb/gadget/f_mtp.c +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * Gadget Function Driver for MTP - * - * Copyright (C) 2010 Google, Inc. - * Author: Mike Lockwood <lockwood@android.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -/* #define DEBUG */ -/* #define VERBOSE_DEBUG */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/err.h> -#include <linux/interrupt.h> - -#include <linux/types.h> -#include <linux/file.h> -#include <linux/device.h> -#include <linux/miscdevice.h> - -#include <linux/usb.h> -#include <linux/usb_usual.h> -#include <linux/usb/ch9.h> -#include <linux/usb/f_mtp.h> -#include <linux/configfs.h> -#include <linux/usb/composite.h> - -#include "configfs.h" - -#define MTP_BULK_BUFFER_SIZE 16384 -#define INTR_BUFFER_SIZE 28 -#define MAX_INST_NAME_LEN 40 - -/* String IDs */ -#define INTERFACE_STRING_INDEX 0 - -/* values for mtp_dev.state */ -#define STATE_OFFLINE 0 /* initial state, disconnected */ -#define STATE_READY 1 /* ready for userspace calls */ -#define STATE_BUSY 2 /* processing userspace calls */ -#define STATE_CANCELED 3 /* transaction canceled by host */ -#define STATE_ERROR 4 /* error from completion routine */ - -/* number of tx and rx requests to allocate */ -#define TX_REQ_MAX 4 -#define RX_REQ_MAX 2 -#define INTR_REQ_MAX 5 - -/* ID for Microsoft MTP OS String */ -#define MTP_OS_STRING_ID 0xEE - -/* MTP class reqeusts */ -#define MTP_REQ_CANCEL 0x64 -#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 -#define MTP_REQ_RESET 0x66 -#define MTP_REQ_GET_DEVICE_STATUS 0x67 - -/* constants for device status */ -#define MTP_RESPONSE_OK 0x2001 -#define MTP_RESPONSE_DEVICE_BUSY 0x2019 -#define DRIVER_NAME "mtp" - -static const char mtp_shortname[] = DRIVER_NAME "_usb"; - -struct mtp_dev { - struct usb_function function; - struct usb_composite_dev *cdev; - spinlock_t lock; - - struct usb_ep *ep_in; - struct usb_ep *ep_out; - struct usb_ep *ep_intr; - - int state; - - /* synchronize access to our device file */ - atomic_t open_excl; - /* to enforce only one ioctl at a time */ - atomic_t ioctl_excl; - - struct list_head tx_idle; - struct list_head intr_idle; - - wait_queue_head_t read_wq; - wait_queue_head_t write_wq; - wait_queue_head_t intr_wq; - struct usb_request *rx_req[RX_REQ_MAX]; - int rx_done; - - /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and - * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue - */ - struct workqueue_struct *wq; - struct work_struct send_file_work; - struct work_struct receive_file_work; - struct file *xfer_file; - loff_t xfer_file_offset; - int64_t xfer_file_length; - unsigned xfer_send_header; - uint16_t xfer_command; - uint32_t xfer_transaction_id; - int xfer_result; -}; - -static struct usb_interface_descriptor mtp_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, - .bInterfaceProtocol = 0, -}; - -static struct usb_interface_descriptor ptp_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_STILL_IMAGE, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 1, -}; - -static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor mtp_intr_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), - .bInterval = 6, -}; - -static struct usb_descriptor_header *fs_mtp_descs[] = { - (struct usb_descriptor_header *) &mtp_interface_desc, - (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, - (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, - (struct usb_descriptor_header *) &mtp_intr_desc, - NULL, -}; - -static struct usb_descriptor_header *hs_mtp_descs[] = { - (struct usb_descriptor_header *) &mtp_interface_desc, - (struct usb_descriptor_header *) &mtp_highspeed_in_desc, - (struct usb_descriptor_header *) &mtp_highspeed_out_desc, - (struct usb_descriptor_header *) &mtp_intr_desc, - NULL, -}; - -static struct usb_descriptor_header *fs_ptp_descs[] = { - (struct usb_descriptor_header *) &ptp_interface_desc, - (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, - (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, - (struct usb_descriptor_header *) &mtp_intr_desc, - NULL, -}; - -static struct usb_descriptor_header *hs_ptp_descs[] = { - (struct usb_descriptor_header *) &ptp_interface_desc, - (struct usb_descriptor_header *) &mtp_highspeed_in_desc, - (struct usb_descriptor_header *) &mtp_highspeed_out_desc, - (struct usb_descriptor_header *) &mtp_intr_desc, - NULL, -}; - -static struct usb_string mtp_string_defs[] = { - /* Naming interface "MTP" so libmtp will recognize us */ - [INTERFACE_STRING_INDEX].s = "MTP", - { }, /* end of list */ -}; - -static struct usb_gadget_strings mtp_string_table = { - .language = 0x0409, /* en-US */ - .strings = mtp_string_defs, -}; - -static struct usb_gadget_strings *mtp_strings[] = { - &mtp_string_table, - NULL, -}; - -/* Microsoft MTP OS String */ -static u8 mtp_os_string[] = { - 18, /* sizeof(mtp_os_string) */ - USB_DT_STRING, - /* Signature field: "MSFT100" */ - 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, - /* vendor code */ - 1, - /* padding */ - 0 -}; - -/* Microsoft Extended Configuration Descriptor Header Section */ -struct mtp_ext_config_desc_header { - __le32 dwLength; - __u16 bcdVersion; - __le16 wIndex; - __u8 bCount; - __u8 reserved[7]; -}; - -/* Microsoft Extended Configuration Descriptor Function Section */ -struct mtp_ext_config_desc_function { - __u8 bFirstInterfaceNumber; - __u8 bInterfaceCount; - __u8 compatibleID[8]; - __u8 subCompatibleID[8]; - __u8 reserved[6]; -}; - -/* MTP Extended Configuration Descriptor */ -struct { - struct mtp_ext_config_desc_header header; - struct mtp_ext_config_desc_function function; -} mtp_ext_config_desc = { - .header = { - .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), - .bcdVersion = __constant_cpu_to_le16(0x0100), - .wIndex = __constant_cpu_to_le16(4), - .bCount = __constant_cpu_to_le16(1), - }, - .function = { - .bFirstInterfaceNumber = 0, - .bInterfaceCount = 1, - .compatibleID = { 'M', 'T', 'P' }, - }, -}; - -struct mtp_device_status { - __le16 wLength; - __le16 wCode; -}; - -struct mtp_data_header { - /* length of packet, including this header */ - __le32 length; - /* container type (2 for data packet) */ - __le16 type; - /* MTP command code */ - __le16 command; - /* MTP transaction ID */ - __le32 transaction_id; -}; - -struct mtp_instance { - struct usb_function_instance func_inst; - const char *name; - struct mtp_dev *dev; -}; - -/* temporary variable used between mtp_open() and mtp_gadget_bind() */ -static struct mtp_dev *_mtp_dev; - -static inline struct mtp_dev *func_to_mtp(struct usb_function *f) -{ - return container_of(f, struct mtp_dev, function); -} - -static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) -{ - struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!req) - return NULL; - - /* now allocate buffers for the requests */ - req->buf = kmalloc(buffer_size, GFP_KERNEL); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - return req; -} - -static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) -{ - if (req) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -static inline int mtp_lock(atomic_t *excl) -{ - if (atomic_inc_return(excl) == 1) { - return 0; - } else { - atomic_dec(excl); - return -1; - } -} - -static inline void mtp_unlock(atomic_t *excl) -{ - atomic_dec(excl); -} - -/* add a request to the tail of a list */ -static void mtp_req_put(struct mtp_dev *dev, struct list_head *head, - struct usb_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - list_add_tail(&req->list, head); - spin_unlock_irqrestore(&dev->lock, flags); -} - -/* remove a request from the head of a list */ -static struct usb_request -*mtp_req_get(struct mtp_dev *dev, struct list_head *head) -{ - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&dev->lock, flags); - if (list_empty(head)) { - req = 0; - } else { - req = list_first_entry(head, struct usb_request, list); - list_del(&req->list); - } - spin_unlock_irqrestore(&dev->lock, flags); - return req; -} - -static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) -{ - struct mtp_dev *dev = _mtp_dev; - - if (req->status != 0) - dev->state = STATE_ERROR; - - mtp_req_put(dev, &dev->tx_idle, req); - - wake_up(&dev->write_wq); -} - -static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) -{ - struct mtp_dev *dev = _mtp_dev; - - dev->rx_done = 1; - if (req->status != 0) - dev->state = STATE_ERROR; - - wake_up(&dev->read_wq); -} - -static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) -{ - struct mtp_dev *dev = _mtp_dev; - - if (req->status != 0) - dev->state = STATE_ERROR; - - mtp_req_put(dev, &dev->intr_idle, req); - - wake_up(&dev->intr_wq); -} - -static int mtp_create_bulk_endpoints(struct mtp_dev *dev, - struct usb_endpoint_descriptor *in_desc, - struct usb_endpoint_descriptor *out_desc, - struct usb_endpoint_descriptor *intr_desc) -{ - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req; - struct usb_ep *ep; - int i; - - DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); - - ep = usb_ep_autoconfig(cdev->gadget, in_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_in = ep; - - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - - ep = usb_ep_autoconfig(cdev->gadget, intr_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_intr = ep; - - /* now allocate requests for our endpoints */ - for (i = 0; i < TX_REQ_MAX; i++) { - req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = mtp_complete_in; - mtp_req_put(dev, &dev->tx_idle, req); - } - for (i = 0; i < RX_REQ_MAX; i++) { - req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = mtp_complete_out; - dev->rx_req[i] = req; - } - for (i = 0; i < INTR_REQ_MAX; i++) { - req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = mtp_complete_intr; - mtp_req_put(dev, &dev->intr_idle, req); - } - - return 0; - -fail: - pr_err("mtp_bind() could not allocate requests\n"); - return -1; -} - -static ssize_t mtp_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) -{ - struct mtp_dev *dev = fp->private_data; - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req; - ssize_t r = count; - unsigned xfer; - int ret = 0; - - DBG(cdev, "mtp_read(%zu)\n", count); - - if (count > MTP_BULK_BUFFER_SIZE) - return -EINVAL; - - /* we will block until we're online */ - DBG(cdev, "mtp_read: waiting for online state\n"); - ret = wait_event_interruptible(dev->read_wq, - dev->state != STATE_OFFLINE); - if (ret < 0) { - r = ret; - goto done; - } - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) { - /* report cancelation to userspace */ - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); - return -ECANCELED; - } - dev->state = STATE_BUSY; - spin_unlock_irq(&dev->lock); - -requeue_req: - /* queue a request */ - req = dev->rx_req[0]; - req->length = count; - dev->rx_done = 0; - ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); - if (ret < 0) { - r = -EIO; - goto done; - } else { - DBG(cdev, "rx %p queue\n", req); - } - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->read_wq, dev->rx_done); - if (ret < 0) { - r = ret; - usb_ep_dequeue(dev->ep_out, req); - goto done; - } - if (dev->state == STATE_BUSY) { - /* If we got a 0-len packet, throw it back and try again. */ - if (req->actual == 0) - goto requeue_req; - - DBG(cdev, "rx %p %d\n", req, req->actual); - xfer = (req->actual < count) ? req->actual : count; - r = xfer; - if (copy_to_user(buf, req->buf, xfer)) - r = -EFAULT; - } else - r = -EIO; - -done: - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) - r = -ECANCELED; - else if (dev->state != STATE_OFFLINE) - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); - - DBG(cdev, "mtp_read returning %zd\n", r); - return r; -} - -static ssize_t mtp_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct mtp_dev *dev = fp->private_data; - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req = 0; - ssize_t r = count; - unsigned xfer; - int sendZLP = 0; - int ret; - - DBG(cdev, "mtp_write(%zu)\n", count); - - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) { - /* report cancelation to userspace */ - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); - return -ECANCELED; - } - if (dev->state == STATE_OFFLINE) { - spin_unlock_irq(&dev->lock); - return -ENODEV; - } - dev->state = STATE_BUSY; - spin_unlock_irq(&dev->lock); - - /* we need to send a zero length packet to signal the end of transfer - * if the transfer size is aligned to a packet boundary. - */ - if ((count & (dev->ep_in->maxpacket - 1)) == 0) - sendZLP = 1; - - while (count > 0 || sendZLP) { - /* so we exit after sending ZLP */ - if (count == 0) - sendZLP = 0; - - if (dev->state != STATE_BUSY) { - DBG(cdev, "mtp_write dev->error\n"); - r = -EIO; - break; - } - - /* get an idle tx request to use */ - req = 0; - ret = wait_event_interruptible(dev->write_wq, - ((req = mtp_req_get(dev, &dev->tx_idle)) - || dev->state != STATE_BUSY)); - if (!req) { - r = ret; - break; - } - - if (count > MTP_BULK_BUFFER_SIZE) - xfer = MTP_BULK_BUFFER_SIZE; - else - xfer = count; - if (xfer && copy_from_user(req->buf, buf, xfer)) { - r = -EFAULT; - break; - } - - req->length = xfer; - ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); - if (ret < 0) { - DBG(cdev, "mtp_write: xfer error %d\n", ret); - r = -EIO; - break; - } - - buf += xfer; - count -= xfer; - - /* zero this so we don't try to free it on error exit */ - req = 0; - } - - if (req) - mtp_req_put(dev, &dev->tx_idle, req); - - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) - r = -ECANCELED; - else if (dev->state != STATE_OFFLINE) - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); - - DBG(cdev, "mtp_write returning %zd\n", r); - return r; -} - -/* read from a local file and write to USB */ -static void send_file_work(struct work_struct *data) -{ - struct mtp_dev *dev = container_of(data, struct mtp_dev, - send_file_work); - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req = 0; - struct mtp_data_header *header; - struct file *filp; - loff_t offset; - int64_t count; - int xfer, ret, hdr_size; - int r = 0; - int sendZLP = 0; - - /* read our parameters */ - smp_rmb(); - filp = dev->xfer_file; - offset = dev->xfer_file_offset; - count = dev->xfer_file_length; - - DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); - - if (dev->xfer_send_header) { - hdr_size = sizeof(struct mtp_data_header); - count += hdr_size; - } else { - hdr_size = 0; - } - - /* we need to send a zero length packet to signal the end of transfer - * if the transfer size is aligned to a packet boundary. - */ - if ((count & (dev->ep_in->maxpacket - 1)) == 0) - sendZLP = 1; - - while (count > 0 || sendZLP) { - /* so we exit after sending ZLP */ - if (count == 0) - sendZLP = 0; - - /* get an idle tx request to use */ - req = 0; - ret = wait_event_interruptible(dev->write_wq, - (req = mtp_req_get(dev, &dev->tx_idle)) - || dev->state != STATE_BUSY); - if (dev->state == STATE_CANCELED) { - r = -ECANCELED; - break; - } - if (!req) { - r = ret; - break; - } - - if (count > MTP_BULK_BUFFER_SIZE) - xfer = MTP_BULK_BUFFER_SIZE; - else - xfer = count; - - if (hdr_size) { - /* prepend MTP data header */ - header = (struct mtp_data_header *)req->buf; - header->length = __cpu_to_le32(count); - header->type = __cpu_to_le16(2); /* data packet */ - header->command = __cpu_to_le16(dev->xfer_command); - header->transaction_id = - __cpu_to_le32(dev->xfer_transaction_id); - } - - ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, - &offset); - if (ret < 0) { - r = ret; - break; - } - xfer = ret + hdr_size; - hdr_size = 0; - - req->length = xfer; - ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); - if (ret < 0) { - DBG(cdev, "send_file_work: xfer error %d\n", ret); - dev->state = STATE_ERROR; - r = -EIO; - break; - } - - count -= xfer; - - /* zero this so we don't try to free it on error exit */ - req = 0; - } - - if (req) - mtp_req_put(dev, &dev->tx_idle, req); - - DBG(cdev, "send_file_work returning %d\n", r); - /* write the result */ - dev->xfer_result = r; - smp_wmb(); -} - -/* read from USB and write to a local file */ -static void receive_file_work(struct work_struct *data) -{ - struct mtp_dev *dev = container_of(data, struct mtp_dev, - receive_file_work); - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *read_req = NULL, *write_req = NULL; - struct file *filp; - loff_t offset; - int64_t count; - int ret, cur_buf = 0; - int r = 0; - - /* read our parameters */ - smp_rmb(); - filp = dev->xfer_file; - offset = dev->xfer_file_offset; - count = dev->xfer_file_length; - - DBG(cdev, "receive_file_work(%lld)\n", count); - - while (count > 0 || write_req) { - if (count > 0) { - /* queue a request */ - read_req = dev->rx_req[cur_buf]; - cur_buf = (cur_buf + 1) % RX_REQ_MAX; - - read_req->length = (count > MTP_BULK_BUFFER_SIZE - ? MTP_BULK_BUFFER_SIZE : count); - dev->rx_done = 0; - ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); - if (ret < 0) { - r = -EIO; - dev->state = STATE_ERROR; - break; - } - } - - if (write_req) { - DBG(cdev, "rx %p %d\n", write_req, write_req->actual); - ret = vfs_write(filp, write_req->buf, write_req->actual, - &offset); - DBG(cdev, "vfs_write %d\n", ret); - if (ret != write_req->actual) { - r = -EIO; - dev->state = STATE_ERROR; - break; - } - write_req = NULL; - } - - if (read_req) { - /* wait for our last read to complete */ - ret = wait_event_interruptible(dev->read_wq, - dev->rx_done || dev->state != STATE_BUSY); - if (dev->state == STATE_CANCELED) { - r = -ECANCELED; - if (!dev->rx_done) - usb_ep_dequeue(dev->ep_out, read_req); - break; - } - /* if xfer_file_length is 0xFFFFFFFF, then we read until - * we get a zero length packet - */ - if (count != 0xFFFFFFFF) - count -= read_req->actual; - if (read_req->actual < read_req->length) { - /* - * short packet is used to signal EOF for - * sizes > 4 gig - */ - DBG(cdev, "got short packet\n"); - count = 0; - } - - write_req = read_req; - read_req = NULL; - } - } - - DBG(cdev, "receive_file_work returning %d\n", r); - /* write the result */ - dev->xfer_result = r; - smp_wmb(); -} - -static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) -{ - struct usb_request *req = NULL; - int ret; - int length = event->length; - - DBG(dev->cdev, "mtp_send_event(%zu)\n", event->length); - - if (length < 0 || length > INTR_BUFFER_SIZE) - return -EINVAL; - if (dev->state == STATE_OFFLINE) - return -ENODEV; - - ret = wait_event_interruptible_timeout(dev->intr_wq, - (req = mtp_req_get(dev, &dev->intr_idle)), - msecs_to_jiffies(1000)); - if (!req) - return -ETIME; - - if (copy_from_user(req->buf, (void __user *)event->data, length)) { - mtp_req_put(dev, &dev->intr_idle, req); - return -EFAULT; - } - req->length = length; - ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); - if (ret) - mtp_req_put(dev, &dev->intr_idle, req); - - return ret; -} - -static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) -{ - struct mtp_dev *dev = fp->private_data; - struct file *filp = NULL; - int ret = -EINVAL; - - if (mtp_lock(&dev->ioctl_excl)) - return -EBUSY; - - switch (code) { - case MTP_SEND_FILE: - case MTP_RECEIVE_FILE: - case MTP_SEND_FILE_WITH_HEADER: - { - struct mtp_file_range mfr; - struct work_struct *work; - - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) { - /* report cancelation to userspace */ - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); - ret = -ECANCELED; - goto out; - } - if (dev->state == STATE_OFFLINE) { - spin_unlock_irq(&dev->lock); - ret = -ENODEV; - goto out; - } - dev->state = STATE_BUSY; - spin_unlock_irq(&dev->lock); - - if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { - ret = -EFAULT; - goto fail; - } - /* hold a reference to the file while we are working with it */ - filp = fget(mfr.fd); - if (!filp) { - ret = -EBADF; - goto fail; - } - - /* write the parameters */ - dev->xfer_file = filp; - dev->xfer_file_offset = mfr.offset; - dev->xfer_file_length = mfr.length; - smp_wmb(); - - if (code == MTP_SEND_FILE_WITH_HEADER) { - work = &dev->send_file_work; - dev->xfer_send_header = 1; - dev->xfer_command = mfr.command; - dev->xfer_transaction_id = mfr.transaction_id; - } else if (code == MTP_SEND_FILE) { - work = &dev->send_file_work; - dev->xfer_send_header = 0; - } else { - work = &dev->receive_file_work; - } - - /* We do the file transfer on a work queue so it will run - * in kernel context, which is necessary for vfs_read and - * vfs_write to use our buffers in the kernel address space. - */ - queue_work(dev->wq, work); - /* wait for operation to complete */ - flush_workqueue(dev->wq); - fput(filp); - - /* read the result */ - smp_rmb(); - ret = dev->xfer_result; - break; - } - case MTP_SEND_EVENT: - { - struct mtp_event event; - /* return here so we don't change dev->state below, - * which would interfere with bulk transfer state. - */ - if (copy_from_user(&event, (void __user *)value, sizeof(event))) - ret = -EFAULT; - else - ret = mtp_send_event(dev, &event); - goto out; - } - } - -fail: - spin_lock_irq(&dev->lock); - if (dev->state == STATE_CANCELED) - ret = -ECANCELED; - else if (dev->state != STATE_OFFLINE) - dev->state = STATE_READY; - spin_unlock_irq(&dev->lock); -out: - mtp_unlock(&dev->ioctl_excl); - DBG(dev->cdev, "ioctl returning %d\n", ret); - return ret; -} - -static int mtp_open(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "mtp_open\n"); - if (mtp_lock(&_mtp_dev->open_excl)) - return -EBUSY; - - /* clear any error condition */ - if (_mtp_dev->state != STATE_OFFLINE) - _mtp_dev->state = STATE_READY; - - fp->private_data = _mtp_dev; - return 0; -} - -static int mtp_release(struct inode *ip, struct file *fp) -{ - printk(KERN_INFO "mtp_release\n"); - - mtp_unlock(&_mtp_dev->open_excl); - return 0; -} - -/* file operations for /dev/mtp_usb */ -static const struct file_operations mtp_fops = { - .owner = THIS_MODULE, - .read = mtp_read, - .write = mtp_write, - .unlocked_ioctl = mtp_ioctl, - .open = mtp_open, - .release = mtp_release, -}; - -static struct miscdevice mtp_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = mtp_shortname, - .fops = &mtp_fops, -}; - -static int mtp_ctrlrequest(struct usb_composite_dev *cdev, - const struct usb_ctrlrequest *ctrl) -{ - struct mtp_dev *dev = _mtp_dev; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - unsigned long flags; - - VDBG(cdev, "mtp_ctrlrequest " - "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - - /* Handle MTP OS string */ - if (ctrl->bRequestType == - (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) - && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR - && (w_value >> 8) == USB_DT_STRING - && (w_value & 0xFF) == MTP_OS_STRING_ID) { - value = (w_length < sizeof(mtp_os_string) - ? w_length : sizeof(mtp_os_string)); - memcpy(cdev->req->buf, mtp_os_string, value); - } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { - /* Handle MTP OS descriptor */ - DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", - ctrl->bRequest, w_index, w_value, w_length); - - if (ctrl->bRequest == 1 - && (ctrl->bRequestType & USB_DIR_IN) - && (w_index == 4 || w_index == 5)) { - value = (w_length < sizeof(mtp_ext_config_desc) ? - w_length : sizeof(mtp_ext_config_desc)); - memcpy(cdev->req->buf, &mtp_ext_config_desc, value); - } - } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { - DBG(cdev, "class request: %d index: %d value: %d length: %d\n", - ctrl->bRequest, w_index, w_value, w_length); - - if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 - && w_value == 0) { - DBG(cdev, "MTP_REQ_CANCEL\n"); - - spin_lock_irqsave(&dev->lock, flags); - if (dev->state == STATE_BUSY) { - dev->state = STATE_CANCELED; - wake_up(&dev->read_wq); - wake_up(&dev->write_wq); - } - spin_unlock_irqrestore(&dev->lock, flags); - - /* We need to queue a request to read the remaining - * bytes, but we don't actually need to look at - * the contents. - */ - value = w_length; - } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS - && w_index == 0 && w_value == 0) { - struct mtp_device_status *status = cdev->req->buf; - status->wLength = - __constant_cpu_to_le16(sizeof(*status)); - - DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); - spin_lock_irqsave(&dev->lock, flags); - /* device status is "busy" until we report - * the cancelation to userspace - */ - if (dev->state == STATE_CANCELED) - status->wCode = - __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); - else - status->wCode = - __cpu_to_le16(MTP_RESPONSE_OK); - spin_unlock_irqrestore(&dev->lock, flags); - value = sizeof(*status); - } - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - int rc; - cdev->req->zero = value < w_length; - cdev->req->length = value; - rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); - if (rc < 0) - ERROR(cdev, "%s: response queue error\n", __func__); - } - return value; -} - -static int -mtp_function_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct mtp_dev *dev = func_to_mtp(f); - int id; - int ret; - - dev->cdev = cdev; - DBG(cdev, "mtp_function_bind dev: %p\n", dev); - - /* allocate interface ID(s) */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - mtp_interface_desc.bInterfaceNumber = id; - - if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; - mtp_interface_desc.iInterface = ret; - } - /* allocate endpoints */ - ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, - &mtp_fullspeed_out_desc, &mtp_intr_desc); - if (ret) - return ret; - - /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - mtp_highspeed_in_desc.bEndpointAddress = - mtp_fullspeed_in_desc.bEndpointAddress; - mtp_highspeed_out_desc.bEndpointAddress = - mtp_fullspeed_out_desc.bEndpointAddress; - } - - DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - f->name, dev->ep_in->name, dev->ep_out->name); - return 0; -} - -static void -mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct mtp_dev *dev = func_to_mtp(f); - struct usb_request *req; - int i; - - mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; - while ((req = mtp_req_get(dev, &dev->tx_idle))) - mtp_request_free(req, dev->ep_in); - for (i = 0; i < RX_REQ_MAX; i++) - mtp_request_free(dev->rx_req[i], dev->ep_out); - while ((req = mtp_req_get(dev, &dev->intr_idle))) - mtp_request_free(req, dev->ep_intr); - dev->state = STATE_OFFLINE; -} - -static int mtp_function_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct mtp_dev *dev = func_to_mtp(f); - struct usb_composite_dev *cdev = f->config->cdev; - int ret; - - DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_in); - if (ret) - return ret; - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_out); - if (ret) { - usb_ep_disable(dev->ep_in); - return ret; - } - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_intr); - if (ret) { - usb_ep_disable(dev->ep_out); - usb_ep_disable(dev->ep_in); - return ret; - } - dev->state = STATE_READY; - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - return 0; -} - -static void mtp_function_disable(struct usb_function *f) -{ - struct mtp_dev *dev = func_to_mtp(f); - struct usb_composite_dev *cdev = dev->cdev; - - DBG(cdev, "mtp_function_disable\n"); - dev->state = STATE_OFFLINE; - usb_ep_disable(dev->ep_in); - usb_ep_disable(dev->ep_out); - usb_ep_disable(dev->ep_intr); - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - - VDBG(cdev, "%s disabled\n", dev->function.name); -} - -static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) -{ - struct mtp_dev *dev = _mtp_dev; - int ret = 0; - - printk(KERN_INFO "mtp_bind_config\n"); - - /* allocate a string ID for our interface */ - if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; - mtp_interface_desc.iInterface = ret; - } - - dev->cdev = c->cdev; - dev->function.name = DRIVER_NAME; - dev->function.strings = mtp_strings; - if (ptp_config) { - dev->function.fs_descriptors = fs_ptp_descs; - dev->function.hs_descriptors = hs_ptp_descs; - } else { - dev->function.fs_descriptors = fs_mtp_descs; - dev->function.hs_descriptors = hs_mtp_descs; - } - dev->function.bind = mtp_function_bind; - dev->function.unbind = mtp_function_unbind; - dev->function.set_alt = mtp_function_set_alt; - dev->function.disable = mtp_function_disable; - - return usb_add_function(c, &dev->function); -} - -static int __mtp_setup(struct mtp_instance *fi_mtp) -{ - struct mtp_dev *dev; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - - if (fi_mtp != NULL) - fi_mtp->dev = dev; - - if (!dev) - return -ENOMEM; - - spin_lock_init(&dev->lock); - init_waitqueue_head(&dev->read_wq); - init_waitqueue_head(&dev->write_wq); - init_waitqueue_head(&dev->intr_wq); - atomic_set(&dev->open_excl, 0); - atomic_set(&dev->ioctl_excl, 0); - INIT_LIST_HEAD(&dev->tx_idle); - INIT_LIST_HEAD(&dev->intr_idle); - - dev->wq = create_singlethread_workqueue("f_mtp"); - if (!dev->wq) { - ret = -ENOMEM; - goto err1; - } - INIT_WORK(&dev->send_file_work, send_file_work); - INIT_WORK(&dev->receive_file_work, receive_file_work); - - _mtp_dev = dev; - - ret = misc_register(&mtp_device); - if (ret) - goto err2; - - return 0; - -err2: - destroy_workqueue(dev->wq); -err1: - _mtp_dev = NULL; - kfree(dev); - printk(KERN_ERR "mtp gadget driver failed to initialize\n"); - return ret; -} - -static int mtp_setup(void) -{ - return __mtp_setup(NULL); -} - -static int mtp_setup_configfs(struct mtp_instance *fi_mtp) -{ - return __mtp_setup(fi_mtp); -} - - -static void mtp_cleanup(void) -{ - struct mtp_dev *dev = _mtp_dev; - - if (!dev) - return; - - misc_deregister(&mtp_device); - destroy_workqueue(dev->wq); - _mtp_dev = NULL; - kfree(dev); -} - -static struct mtp_instance *to_mtp_instance(struct config_item *item) -{ - return container_of(to_config_group(item), struct mtp_instance, - func_inst.group); -} - -static void mtp_attr_release(struct config_item *item) -{ - struct mtp_instance *fi_mtp = to_mtp_instance(item); - usb_put_function_instance(&fi_mtp->func_inst); -} - -static struct configfs_item_operations mtp_item_ops = { - .release = mtp_attr_release, -}; - -static struct config_item_type mtp_func_type = { - .ct_item_ops = &mtp_item_ops, - .ct_owner = THIS_MODULE, -}; - - -static struct mtp_instance *to_fi_mtp(struct usb_function_instance *fi) -{ - return container_of(fi, struct mtp_instance, func_inst); -} - -static int mtp_set_inst_name(struct usb_function_instance *fi, const char *name) -{ - struct mtp_instance *fi_mtp; - char *ptr; - int name_len; - - name_len = strlen(name) + 1; - if (name_len > MAX_INST_NAME_LEN) - return -ENAMETOOLONG; - - ptr = kstrndup(name, name_len, GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - fi_mtp = to_fi_mtp(fi); - fi_mtp->name = ptr; - - return 0; -} - -static void mtp_free_inst(struct usb_function_instance *fi) -{ - struct mtp_instance *fi_mtp; - - fi_mtp = to_fi_mtp(fi); - kfree(fi_mtp->name); - mtp_cleanup(); - kfree(fi_mtp); -} - -struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) -{ - struct mtp_instance *fi_mtp; - int ret = 0; - - fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL); - if (!fi_mtp) - return ERR_PTR(-ENOMEM); - fi_mtp->func_inst.set_inst_name = mtp_set_inst_name; - fi_mtp->func_inst.free_func_inst = mtp_free_inst; - - if (mtp_config) { - ret = mtp_setup_configfs(fi_mtp); - if (ret) { - kfree(fi_mtp); - pr_err("Error setting MTP\n"); - return ERR_PTR(ret); - } - } else - fi_mtp->dev = _mtp_dev; - - config_group_init_type_name(&fi_mtp->func_inst.group, - "", &mtp_func_type); - - return &fi_mtp->func_inst; -} -EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); - -static struct usb_function_instance *mtp_alloc_inst(void) -{ - return alloc_inst_mtp_ptp(true); -} - -static int mtp_ctrlreq_configfs(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - return mtp_ctrlrequest(f->config->cdev, ctrl); -} - -static void mtp_free(struct usb_function *f) -{ - /*NO-OP: no function specific resource allocation in mtp_alloc*/ -} - -struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, - bool mtp_config) -{ - struct mtp_instance *fi_mtp = to_fi_mtp(fi); - struct mtp_dev *dev = fi_mtp->dev; - - dev->function.name = DRIVER_NAME; - dev->function.strings = mtp_strings; - if (mtp_config) { - dev->function.fs_descriptors = fs_mtp_descs; - dev->function.hs_descriptors = hs_mtp_descs; - } else { - dev->function.fs_descriptors = fs_ptp_descs; - dev->function.hs_descriptors = hs_ptp_descs; - } - dev->function.bind = mtp_function_bind; - dev->function.unbind = mtp_function_unbind; - dev->function.set_alt = mtp_function_set_alt; - dev->function.disable = mtp_function_disable; - dev->function.setup = mtp_ctrlreq_configfs; - dev->function.free_func = mtp_free; - - return &dev->function; -} -EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); - -static struct usb_function *mtp_alloc(struct usb_function_instance *fi) -{ - return function_alloc_mtp_ptp(fi, true); -} - -DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index f71b1aaa0edf..7e2bb1b5bee9 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -42,3 +42,11 @@ usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o usb_f_hid-y := f_hid.o obj-$(CONFIG_USB_F_HID) += usb_f_hid.o +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 53e50b5e8612..dfc508d1da35 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -39,6 +39,10 @@ #include <linux/usb/ch9.h> #include <linux/usb/f_accessory.h> +#include <linux/configfs.h> +#include <linux/usb/composite.h> + +#define MAX_INST_NAME_LEN 40 #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 @@ -194,6 +198,11 @@ static struct usb_gadget_strings *acc_strings[] = { /* temporary variable used between acc_open() and acc_gadget_bind() */ static struct acc_dev *_acc_dev; +struct acc_instance { + struct usb_function_instance func_inst; + const char *name; +}; + static inline struct acc_dev *func_to_dev(struct usb_function *f) { return container_of(f, struct acc_dev, function); @@ -662,10 +671,17 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, break; } - if (count > BULK_BUFFER_SIZE) + if (count > BULK_BUFFER_SIZE) { xfer = BULK_BUFFER_SIZE; - else + /* ZLP, They will be more TX requests so not yet. */ + req->zero = 0; + } else { xfer = count; + /* If the data length is a multple of the + * maxpacket size then send a zero length packet(ZLP). + */ + req->zero = ((xfer % dev->ep_in->maxpacket) == 0); + } if (copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; @@ -790,7 +806,7 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; -static int acc_ctrlrequest(struct usb_composite_dev *cdev, +int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; @@ -894,9 +910,11 @@ err: w_value, w_index, w_length); return value; } +EXPORT_SYMBOL_GPL(acc_ctrlrequest); static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) +__acc_function_bind(struct usb_configuration *c, + struct usb_function *f, bool configfs) { struct usb_composite_dev *cdev = c->cdev; struct acc_dev *dev = func_to_dev(f); @@ -905,6 +923,16 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + if (configfs) { + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + dev->cdev = c->cdev; + } ret = hid_register_driver(&acc_hid_driver); if (ret) return ret; @@ -937,6 +965,17 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) { + return __acc_function_bind(c, f, false); +} + +static int +acc_function_bind_configfs(struct usb_configuration *c, + struct usb_function *f) { + return __acc_function_bind(c, f, true); +} + static void kill_all_hid_devices(struct acc_dev *dev) { @@ -944,6 +983,10 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; + /* do nothing if usb accessory device doesn't exist */ + if (!dev) + return; + spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); @@ -1190,11 +1233,12 @@ err: return ret; } -static void acc_disconnect(void) +void acc_disconnect(void) { /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); } +EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { @@ -1202,3 +1246,117 @@ static void acc_cleanup(void) kfree(_acc_dev); _acc_dev = NULL; } +static struct acc_instance *to_acc_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct acc_instance, + func_inst.group); +} + +static void acc_attr_release(struct config_item *item) +{ + struct acc_instance *fi_acc = to_acc_instance(item); + + usb_put_function_instance(&fi_acc->func_inst); +} + +static struct configfs_item_operations acc_item_ops = { + .release = acc_attr_release, +}; + +static struct config_item_type acc_func_type = { + .ct_item_ops = &acc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct acc_instance *to_fi_acc(struct usb_function_instance *fi) +{ + return container_of(fi, struct acc_instance, func_inst); +} + +static int acc_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct acc_instance *fi_acc; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_acc = to_fi_acc(fi); + fi_acc->name = ptr; + return 0; +} + +static void acc_free_inst(struct usb_function_instance *fi) +{ + struct acc_instance *fi_acc; + + fi_acc = to_fi_acc(fi); + kfree(fi_acc->name); + acc_cleanup(); +} + +static struct usb_function_instance *acc_alloc_inst(void) +{ + struct acc_instance *fi_acc; + struct acc_dev *dev; + int err; + + fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); + if (!fi_acc) + return ERR_PTR(-ENOMEM); + fi_acc->func_inst.set_inst_name = acc_set_inst_name; + fi_acc->func_inst.free_func_inst = acc_free_inst; + + err = acc_setup(); + if (err) { + kfree(fi_acc); + pr_err("Error setting ACCESSORY\n"); + return ERR_PTR(err); + } + + config_group_init_type_name(&fi_acc->func_inst.group, + "", &acc_func_type); + dev = _acc_dev; + return &fi_acc->func_inst; +} + +static void acc_free(struct usb_function *f) +{ +/*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +int acc_ctrlrequest_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { + if (f->config != NULL && f->config->cdev != NULL) + return acc_ctrlrequest(f->config->cdev, ctrl); + else + return -1; +} + +static struct usb_function *acc_alloc(struct usb_function_instance *fi) +{ + struct acc_dev *dev = _acc_dev; + + pr_info("acc_alloc\n"); + + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind_configfs; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + dev->function.free_func = acc_free; + dev->function.setup = acc_ctrlrequest_configfs; + + return &dev->function; +} +DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 644f6be05281..39645be93502 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -21,10 +21,17 @@ #include <sound/initval.h> #include <sound/pcm.h> +#include <linux/usb.h> +#include <linux/usb_usual.h> +#include <linux/usb/ch9.h> +#include <linux/configfs.h> +#include <linux/usb/composite.h> +#include <linux/module.h> +#include <linux/moduleparam.h> #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 384 +#define IN_EP_MAX_PACKET_SIZE 256 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 @@ -32,6 +39,7 @@ #define AUDIO_AC_INTERFACE 0 #define AUDIO_AS_INTERFACE 1 #define AUDIO_NUM_INTERFACES 2 +#define MAX_INST_NAME_LEN 40 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -259,6 +267,7 @@ struct audio_dev { ktime_t start_time; /* number of frames sent since start_time */ s64 frames_sent; + struct audio_source_config *config; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -268,6 +277,36 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f) /*-------------------------------------------------------------------------*/ +struct audio_source_instance { + struct usb_function_instance func_inst; + const char *name; + struct audio_source_config *config; + struct device *audio_device; +}; + +static void audio_source_attr_release(struct config_item *item); + +static struct configfs_item_operations audio_source_item_ops = { + .release = audio_source_attr_release, +}; + +static struct config_item_type audio_source_func_type = { + .ct_item_ops = &audio_source_item_ops, + .ct_owner = THIS_MODULE, +}; + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +/*--------------------------------------------------------------------------*/ + static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); @@ -561,6 +600,13 @@ static void audio_build_desc(struct audio_dev *audio) memcpy(sam_freq, &rate, 3); } + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config); +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi); + + /* audio function driver setup/binding */ static int audio_bind(struct usb_configuration *c, struct usb_function *f) @@ -571,6 +617,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct usb_request *req; int i; + int err; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + err = snd_card_setup(c, config); + if (err) + return err; + } audio_build_desc(audio); @@ -580,12 +638,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; ac_interface_desc.bInterfaceNumber = status; + /* AUDIO_AC_INTERFACE */ + ac_header_desc.baInterfaceNr[0] = status; + status = usb_interface_id(c, f); if (status < 0) goto fail; as_interface_alt_0_desc.bInterfaceNumber = status; as_interface_alt_1_desc.bInterfaceNumber = status; + /* AUDIO_AS_INTERFACE */ + ac_header_desc.baInterfaceNr[1] = status; + status = -ENODEV; /* allocate our endpoint */ @@ -630,6 +694,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio->pcm = NULL; audio->substream = NULL; audio->in_ep = NULL; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + config->card = -1; + config->device = -1; + } } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -773,8 +847,6 @@ int audio_source_bind_config(struct usb_configuration *c, struct audio_source_config *config) { struct audio_dev *audio; - struct snd_card *card; - struct snd_pcm *pcm; int err; config->card = -1; @@ -782,16 +854,41 @@ int audio_source_bind_config(struct usb_configuration *c, audio = &_audio_dev; - err = snd_card_new(NULL, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); + err = snd_card_setup(c, config); if (err) return err; - snd_card_set_dev(card, &c->cdev->gadget->dev); + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + return 0; + +add_fail: + snd_card_free(audio->card); + return err; +} + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + audio = &_audio_dev; + + err = snd_card_new(&c->cdev->gadget->dev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + return err; err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); if (err) goto pcm_fail; + pcm->private_data = audio; pcm->info_flags = 0; audio->pcm = pcm; @@ -811,18 +908,147 @@ int audio_source_bind_config(struct usb_configuration *c, if (err) goto register_fail; - err = usb_add_function(c, &audio->func); - if (err) - goto add_fail; - config->card = pcm->card->number; config->device = pcm->device; audio->card = card; return 0; -add_fail: register_fail: pcm_fail: snd_card_free(audio->card); return err; } + +static struct audio_source_instance *to_audio_source_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), struct audio_source_instance, + func_inst.group); +} + +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi) +{ + return container_of(fi, struct audio_source_instance, func_inst); +} + +static void audio_source_attr_release(struct config_item *item) +{ + struct audio_source_instance *fi_audio = to_audio_source_instance(item); + + usb_put_function_instance(&fi_audio->func_inst); +} + +static int audio_source_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct audio_source_instance *fi_audio; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_audio = to_fi_audio_source(fi); + fi_audio->name = ptr; + + return 0; +} + +static void audio_source_free_inst(struct usb_function_instance *fi) +{ + struct audio_source_instance *fi_audio; + + fi_audio = to_fi_audio_source(fi); + device_destroy(fi_audio->audio_device->class, + fi_audio->audio_device->devt); + kfree(fi_audio->name); + kfree(fi_audio->config); +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audio_source_instance *fi_audio = dev_get_drvdata(dev); + struct audio_source_config *config = fi_audio->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +struct device *create_function_device(char *name); + +static struct usb_function_instance *audio_source_alloc_inst(void) +{ + struct audio_source_instance *fi_audio; + struct device_attribute **attrs; + struct device_attribute *attr; + struct device *dev; + void *err_ptr; + int err = 0; + + fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); + if (!fi_audio) + return ERR_PTR(-ENOMEM); + + fi_audio->func_inst.set_inst_name = audio_source_set_inst_name; + fi_audio->func_inst.free_func_inst = audio_source_free_inst; + + fi_audio->config = kzalloc(sizeof(struct audio_source_config), + GFP_KERNEL); + if (!fi_audio->config) { + err_ptr = ERR_PTR(-ENOMEM); + goto fail_audio; + } + + config_group_init_type_name(&fi_audio->func_inst.group, "", + &audio_source_func_type); + dev = create_function_device("f_audio_source"); + + if (IS_ERR(dev)) { + err_ptr = dev; + goto fail_audio_config; + } + + fi_audio->config->card = -1; + fi_audio->config->device = -1; + fi_audio->audio_device = dev; + + attrs = audio_source_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + err_ptr = ERR_PTR(-EINVAL); + goto fail_device; + } + } + + dev_set_drvdata(dev, fi_audio); + _audio_dev.config = fi_audio->config; + + return &fi_audio->func_inst; + +fail_device: + device_destroy(dev->class, dev->devt); +fail_audio_config: + kfree(fi_audio->config); +fail_audio: + kfree(fi_audio); + return err_ptr; + +} + +static struct usb_function *audio_source_alloc(struct usb_function_instance *fi) +{ + return &_audio_dev.func; +} + +DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst, + audio_source_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 620aeaaf2d72..82f6b2ebaebb 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -35,9 +35,14 @@ #include <linux/usb_usual.h> #include <linux/usb/ch9.h> #include <linux/usb/f_mtp.h> +#include <linux/configfs.h> +#include <linux/usb/composite.h> + +#include "configfs.h" #define MTP_BULK_BUFFER_SIZE 16384 #define INTR_BUFFER_SIZE 28 +#define MAX_INST_NAME_LEN 40 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -66,8 +71,9 @@ /* constants for device status */ #define MTP_RESPONSE_OK 0x2001 #define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define DRIVER_NAME "mtp" -static const char mtp_shortname[] = "mtp_usb"; +static const char mtp_shortname[] = DRIVER_NAME "_usb"; struct mtp_dev { struct usb_function function; @@ -280,6 +286,12 @@ struct mtp_data_header { __le32 transaction_id; }; +struct mtp_instance { + struct usb_function_instance func_inst; + const char *name; + struct mtp_dev *dev; +}; + /* temporary variable used between mtp_open() and mtp_gadget_bind() */ static struct mtp_dev *_mtp_dev; @@ -456,7 +468,7 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, return 0; fail: - printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + pr_err("mtp_bind() could not allocate requests\n"); return -1; } @@ -1099,6 +1111,13 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f) return id; mtp_interface_desc.bInterfaceNumber = id; + if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; + mtp_interface_desc.iInterface = ret; + } /* allocate endpoints */ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, &mtp_fullspeed_out_desc, &mtp_intr_desc); @@ -1126,6 +1145,7 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1213,7 +1233,7 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) } dev->cdev = c->cdev; - dev->function.name = "mtp"; + dev->function.name = DRIVER_NAME; dev->function.strings = mtp_strings; if (ptp_config) { dev->function.fs_descriptors = fs_ptp_descs; @@ -1230,12 +1250,16 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) return usb_add_function(c, &dev->function); } -static int mtp_setup(void) +static int __mtp_setup(struct mtp_instance *fi_mtp) { struct mtp_dev *dev; int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (fi_mtp != NULL) + fi_mtp->dev = dev; + if (!dev) return -ENOMEM; @@ -1273,6 +1297,17 @@ err1: return ret; } +static int mtp_setup(void) +{ + return __mtp_setup(NULL); +} + +static int mtp_setup_configfs(struct mtp_instance *fi_mtp) +{ + return __mtp_setup(fi_mtp); +} + + static void mtp_cleanup(void) { struct mtp_dev *dev = _mtp_dev; @@ -1285,3 +1320,138 @@ static void mtp_cleanup(void) _mtp_dev = NULL; kfree(dev); } + +static struct mtp_instance *to_mtp_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct mtp_instance, + func_inst.group); +} + +static void mtp_attr_release(struct config_item *item) +{ + struct mtp_instance *fi_mtp = to_mtp_instance(item); + usb_put_function_instance(&fi_mtp->func_inst); +} + +static struct configfs_item_operations mtp_item_ops = { + .release = mtp_attr_release, +}; + +static struct config_item_type mtp_func_type = { + .ct_item_ops = &mtp_item_ops, + .ct_owner = THIS_MODULE, +}; + + +static struct mtp_instance *to_fi_mtp(struct usb_function_instance *fi) +{ + return container_of(fi, struct mtp_instance, func_inst); +} + +static int mtp_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct mtp_instance *fi_mtp; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_mtp = to_fi_mtp(fi); + fi_mtp->name = ptr; + + return 0; +} + +static void mtp_free_inst(struct usb_function_instance *fi) +{ + struct mtp_instance *fi_mtp; + + fi_mtp = to_fi_mtp(fi); + kfree(fi_mtp->name); + mtp_cleanup(); + kfree(fi_mtp); +} + +struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) +{ + struct mtp_instance *fi_mtp; + int ret = 0; + + fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL); + if (!fi_mtp) + return ERR_PTR(-ENOMEM); + fi_mtp->func_inst.set_inst_name = mtp_set_inst_name; + fi_mtp->func_inst.free_func_inst = mtp_free_inst; + + if (mtp_config) { + ret = mtp_setup_configfs(fi_mtp); + if (ret) { + kfree(fi_mtp); + pr_err("Error setting MTP\n"); + return ERR_PTR(ret); + } + } else + fi_mtp->dev = _mtp_dev; + + config_group_init_type_name(&fi_mtp->func_inst.group, + "", &mtp_func_type); + + return &fi_mtp->func_inst; +} +EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); + +static struct usb_function_instance *mtp_alloc_inst(void) +{ + return alloc_inst_mtp_ptp(true); +} + +static int mtp_ctrlreq_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + return mtp_ctrlrequest(f->config->cdev, ctrl); +} + +static void mtp_free(struct usb_function *f) +{ + /*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, + bool mtp_config) +{ + struct mtp_instance *fi_mtp = to_fi_mtp(fi); + struct mtp_dev *dev = fi_mtp->dev; + + dev->function.name = DRIVER_NAME; + dev->function.strings = mtp_strings; + if (mtp_config) { + dev->function.fs_descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } else { + dev->function.fs_descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_descs; + } + dev->function.bind = mtp_function_bind; + dev->function.unbind = mtp_function_unbind; + dev->function.set_alt = mtp_function_set_alt; + dev->function.disable = mtp_function_disable; + dev->function.setup = mtp_ctrlreq_configfs; + dev->function.free_func = mtp_free; + + return &dev->function; +} +EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); + +static struct usb_function *mtp_alloc(struct usb_function_instance *fi) +{ + return function_alloc_mtp_ptp(fi, true); +} + +DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_mtp.h b/drivers/usb/gadget/function/f_mtp.h index 7adb1ff08eff..7adb1ff08eff 100644 --- a/drivers/usb/gadget/f_mtp.h +++ b/drivers/usb/gadget/function/f_mtp.h diff --git a/drivers/usb/gadget/f_ptp.c b/drivers/usb/gadget/function/f_ptp.c index da3e4d53e085..da3e4d53e085 100644 --- a/drivers/usb/gadget/f_ptp.c +++ b/drivers/usb/gadget/function/f_ptp.c diff --git a/drivers/usb/gadget/functionf_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 4e409261939a..4e409261939a 100644 --- a/drivers/usb/gadget/functionf_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c diff --git a/include/linux/atomic.h b/include/linux/atomic.h index fef3a809e7cf..5b08a8540ecf 100644 --- a/include/linux/atomic.h +++ b/include/linux/atomic.h @@ -3,42 +3,6 @@ #define _LINUX_ATOMIC_H #include <asm/atomic.h> -/* - * Provide __deprecated wrappers for the new interface, avoid flag day changes. - * We need the ugly external functions to break header recursion hell. - */ -#ifndef smp_mb__before_atomic_inc -static inline void __deprecated smp_mb__before_atomic_inc(void) -{ - extern void __smp_mb__before_atomic(void); - __smp_mb__before_atomic(); -} -#endif - -#ifndef smp_mb__after_atomic_inc -static inline void __deprecated smp_mb__after_atomic_inc(void) -{ - extern void __smp_mb__after_atomic(void); - __smp_mb__after_atomic(); -} -#endif - -#ifndef smp_mb__before_atomic_dec -static inline void __deprecated smp_mb__before_atomic_dec(void) -{ - extern void __smp_mb__before_atomic(void); - __smp_mb__before_atomic(); -} -#endif - -#ifndef smp_mb__after_atomic_dec -static inline void __deprecated smp_mb__after_atomic_dec(void) -{ - extern void __smp_mb__after_atomic(void); - __smp_mb__after_atomic(); -} -#endif - /** * atomic_add_unless - add unless the number is already a given value * @v: pointer of type atomic_t diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 38b5f5c88c18..5d858e02997f 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -35,26 +35,6 @@ extern unsigned long __sw_hweight64(__u64 w); */ #include <asm/bitops.h> -/* - * Provide __deprecated wrappers for the new interface, avoid flag day changes. - * We need the ugly external functions to break header recursion hell. - */ -#ifndef smp_mb__before_clear_bit -static inline void __deprecated smp_mb__before_clear_bit(void) -{ - extern void __smp_mb__before_atomic(void); - __smp_mb__before_atomic(); -} -#endif - -#ifndef smp_mb__after_clear_bit -static inline void __deprecated smp_mb__after_clear_bit(void) -{ - extern void __smp_mb__after_atomic(void); - __smp_mb__after_atomic(); -} -#endif - #define for_each_set_bit(bit, addr, size) \ for ((bit) = find_first_bit((addr), (size)); \ (bit) < (size); \ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 89aba433ac2f..e786956e9dc8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -90,22 +90,6 @@ #define CREATE_TRACE_POINTS #include <trace/events/sched.h> -#ifdef smp_mb__before_atomic -void __smp_mb__before_atomic(void) -{ - smp_mb__before_atomic(); -} -EXPORT_SYMBOL(__smp_mb__before_atomic); -#endif - -#ifdef smp_mb__after_atomic -void __smp_mb__after_atomic(void) -{ - smp_mb__after_atomic(); -} -EXPORT_SYMBOL(__smp_mb__after_atomic); -#endif - void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period) { unsigned long delta; |