summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c31
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h4
-rw-r--r--drivers/usb/gadget/function/f_uvc.c31
-rw-r--r--drivers/usb/gadget/function/f_uvc.h6
-rw-r--r--drivers/usb/gadget/function/u_uvc.h5
-rw-r--r--drivers/usb/gadget/function/uvc.h53
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c12
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h12
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c3
-rw-r--r--drivers/usb/gadget/function/uvc_video.h2
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2
-rw-r--r--drivers/usb/gadget/legacy/webcam.c4
-rw-r--r--drivers/usb/gadget/udc/Kconfig1
-rw-r--r--drivers/usb/gadget/udc/core.c18
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c84
16 files changed, 170 insertions, 102 deletions
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index efba66ca0719..025129942894 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1217,8 +1217,8 @@ static void purge_configs_funcs(struct gadget_info *gi)
list_move_tail(&f->list, &cfg->func_list);
if (f->unbind) {
dev_dbg(&gi->cdev.gadget->dev,
- "unbind function '%s'/%p\n",
- f->name, f);
+ "unbind function '%s'/%p\n",
+ f->name, f);
f->unbind(c, f);
}
}
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index acecd13dcbd9..ca8a4b53c59f 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -206,7 +206,6 @@
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
-#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/limits.h>
@@ -312,8 +311,6 @@ struct fsg_common {
void *private_data;
char inquiry_string[INQUIRY_STRING_LEN];
-
- struct kref ref;
};
struct fsg_dev {
@@ -2551,25 +2548,11 @@ static DEVICE_ATTR(file, 0, file_show, file_store);
/****************************** FSG COMMON ******************************/
-static void fsg_common_release(struct kref *ref);
-
static void fsg_lun_release(struct device *dev)
{
/* Nothing needs to be done */
}
-void fsg_common_get(struct fsg_common *common)
-{
- kref_get(&common->ref);
-}
-EXPORT_SYMBOL_GPL(fsg_common_get);
-
-void fsg_common_put(struct fsg_common *common)
-{
- kref_put(&common->ref, fsg_common_release);
-}
-EXPORT_SYMBOL_GPL(fsg_common_put);
-
static struct fsg_common *fsg_common_setup(struct fsg_common *common)
{
if (!common) {
@@ -2582,7 +2565,6 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common)
}
init_rwsem(&common->filesem);
spin_lock_init(&common->lock);
- kref_init(&common->ref);
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->io_wait);
init_waitqueue_head(&common->fsg_wait);
@@ -2870,9 +2852,8 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
}
EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
-static void fsg_common_release(struct kref *ref)
+static void fsg_common_release(struct fsg_common *common)
{
- struct fsg_common *common = container_of(ref, struct fsg_common, ref);
int i;
/* If the thread isn't already dead, tell it to exit now */
@@ -3308,7 +3289,9 @@ static ssize_t fsg_opts_num_buffers_store(struct config_item *item,
if (ret)
goto end;
- fsg_common_set_num_buffers(opts->common, num);
+ ret = fsg_common_set_num_buffers(opts->common, num);
+ if (ret)
+ goto end;
ret = len;
end:
@@ -3344,7 +3327,7 @@ static void fsg_free_inst(struct usb_function_instance *fi)
struct fsg_opts *opts;
opts = fsg_opts_from_func_inst(fi);
- fsg_common_put(opts->common);
+ fsg_common_release(opts->common);
kfree(opts);
}
@@ -3368,7 +3351,7 @@ static struct usb_function_instance *fsg_alloc_inst(void)
rc = fsg_common_set_num_buffers(opts->common,
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
if (rc)
- goto release_opts;
+ goto release_common;
pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
@@ -3391,6 +3374,8 @@ static struct usb_function_instance *fsg_alloc_inst(void)
release_buffers:
fsg_common_free_buffers(opts->common);
+release_common:
+ kfree(opts->common);
release_opts:
kfree(opts);
return ERR_PTR(rc);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index 58857fcf199f..3b8c4ce2a40a 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -115,10 +115,6 @@ fsg_opts_from_func_inst(const struct usb_function_instance *fi)
return container_of(fi, struct fsg_opts, func_inst);
}
-void fsg_common_get(struct fsg_common *common);
-
-void fsg_common_put(struct fsg_common *common);
-
void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 439eba660e95..d8ce7868fe22 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -6,16 +6,17 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/g_uvc.h>
#include <linux/usb/video.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
@@ -30,6 +31,8 @@
#include "uvc_video.h"
unsigned int uvc_gadget_trace_param;
+module_param_named(trace, uvc_gadget_trace_param, uint, 0644);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Function descriptors
@@ -410,10 +413,21 @@ uvc_function_disconnect(struct uvc_device *uvc)
* USB probe and disconnect
*/
+static ssize_t function_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uvc_device *uvc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", uvc->func.fi->group.cg_item.ci_name);
+}
+
+static DEVICE_ATTR_RO(function_name);
+
static int
uvc_register_video(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
+ int ret;
/* TODO reference counting. */
uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
@@ -426,7 +440,17 @@ uvc_register_video(struct uvc_device *uvc)
video_set_drvdata(&uvc->vdev, uvc);
- return video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = device_create_file(&uvc->vdev.dev, &dev_attr_function_name);
+ if (ret < 0) {
+ video_unregister_device(&uvc->vdev);
+ return ret;
+ }
+
+ return 0;
}
#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
@@ -864,6 +888,7 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
INFO(cdev, "%s\n", __func__);
+ device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index 81defe4557fe..a81a17765558 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -9,10 +9,7 @@
#ifndef _F_UVC_H_
#define _F_UVC_H_
-#include <linux/usb/composite.h>
-#include <linux/usb/video.h>
-
-#include "uvc.h"
+struct uvc_device;
void uvc_function_setup_continue(struct uvc_device *uvc);
@@ -21,4 +18,3 @@ void uvc_function_connect(struct uvc_device *uvc);
void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* _F_UVC_H_ */
-
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index d00d3ded71c0..2ed292e94fbc 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -13,6 +13,7 @@
#ifndef U_UVC_H
#define U_UVC_H
+#include <linux/mutex.h>
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
@@ -20,7 +21,6 @@
struct f_uvc_opts {
struct usb_function_instance func_inst;
- unsigned int uvc_gadget_trace_param;
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
@@ -80,7 +80,4 @@ struct f_uvc_opts {
int refcnt;
};
-void uvc_set_trace_param(unsigned int trace);
-
#endif /* U_UVC_H */
-
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index a64e07e61f8c..93cf78b420fe 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -9,52 +9,26 @@
#ifndef _UVC_GADGET_H_
#define _UVC_GADGET_H_
-#include <linux/ioctl.h>
-#include <linux/types.h>
-#include <linux/usb/ch9.h>
-
-#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0)
-#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1)
-#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2)
-#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3)
-#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4)
-#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
-#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
-
-struct uvc_request_data {
- __s32 length;
- __u8 data[60];
-};
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/usb/composite.h>
+#include <linux/videodev2.h>
-struct uvc_event {
- union {
- enum usb_device_speed speed;
- struct usb_ctrlrequest req;
- struct uvc_request_data data;
- };
-};
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
-#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data)
+#include "uvc_queue.h"
-#define UVC_INTF_CONTROL 0
-#define UVC_INTF_STREAMING 1
+struct usb_ep;
+struct usb_request;
+struct uvc_descriptor_header;
/* ------------------------------------------------------------------------
* Debugging, printing and logging
*/
-#ifdef __KERNEL__
-
-#include <linux/usb.h> /* For usb_endpoint_* */
-#include <linux/usb/composite.h>
-#include <linux/usb/gadget.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-device.h>
-
-#include "uvc_queue.h"
-
#define UVC_TRACE_PROBE (1 << 0)
#define UVC_TRACE_DESCR (1 << 1)
#define UVC_TRACE_CONTROL (1 << 2)
@@ -184,7 +158,4 @@ extern void uvc_endpoint_stream(struct uvc_device *dev);
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);
-#endif /* __KERNEL__ */
-
#endif /* _UVC_GADGET_H_ */
-
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index c9b8cc4aae5a..b51f0d278826 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -31,7 +31,11 @@ static struct configfs_attribute prefix##attr_##cname = { \
.show = prefix##cname##_show, \
}
-static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uvc_opts,
+ func_inst.group);
+}
/* control/header/<NAME> */
DECLARE_UVC_HEADER_DESCRIPTOR(1);
@@ -2105,12 +2109,6 @@ static const struct config_item_type uvcg_streaming_grp_type = {
.ct_owner = THIS_MODULE,
};
-static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
-{
- return container_of(to_config_group(item), struct f_uvc_opts,
- func_inst.group);
-}
-
static void uvc_attr_release(struct config_item *item)
{
struct f_uvc_opts *opts = to_f_uvc_opts(item);
diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index f9f65b5c1062..2f0fff769843 100644
--- a/drivers/usb/gadget/function/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
@@ -2,13 +2,15 @@
#ifndef _UVC_QUEUE_H_
#define _UVC_QUEUE_H_
-#ifdef __KERNEL__
-
-#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/poll.h>
-#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+
#include <media/videobuf2-v4l2.h>
+struct file;
+struct mutex;
+
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of video buffers. */
@@ -91,7 +93,5 @@ struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
-#endif /* __KERNEL__ */
-
#endif /* _UVC_QUEUE_H_ */
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 9a9019625496..7f1ca3b57823 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -6,10 +6,11 @@
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
-#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/usb/g_uvc.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
index 6c20aa75f966..7d77122b0ff9 100644
--- a/drivers/usb/gadget/function/uvc_video.h
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -12,6 +12,8 @@
#ifndef __UVC_VIDEO_H__
#define __UVC_VIDEO_H__
+struct uvc_video;
+
int uvcg_video_pump(struct uvc_video *video);
int uvcg_video_enable(struct uvc_video *video, int enable);
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 682bf99dcf76..40870227999a 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -41,7 +41,7 @@ static struct usb_device_descriptor usbg_device_desc = {
#define USB_G_STR_CONFIG USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string usbg_us_strings[] = {
- [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor",
+ [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufacturer",
[USB_GADGET_PRODUCT_IDX].s = "Target Product",
[USB_GADGET_SERIAL_IDX].s = "000000000001",
[USB_G_STR_CONFIG].s = "default config",
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 6b86568c9157..a9f8eb8e1c76 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -30,9 +30,6 @@ static unsigned int streaming_maxburst;
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
-static unsigned int trace;
-module_param(trace, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -379,7 +376,6 @@ webcam_bind(struct usb_composite_dev *cdev)
uvc_opts->streaming_interval = streaming_interval;
uvc_opts->streaming_maxpacket = streaming_maxpacket;
uvc_opts->streaming_maxburst = streaming_maxburst;
- uvc_set_trace_param(trace);
uvc_opts->fs_control = uvc_fs_control_cls;
uvc_opts->ss_control = uvc_ss_control_cls;
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 1df4dedffe86..0a16cbd4e528 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -193,6 +193,7 @@ config USB_RENESAS_USB3
tristate 'Renesas USB3.0 Peripheral controller'
depends on ARCH_RENESAS || COMPILE_TEST
depends on EXTCON
+ select USB_ROLE_SWITCH
help
Renesas USB3.0 Peripheral controller is a USB peripheral controller
that supports super, high, and full speed USB 3.0 data transfers.
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index cab5e4f09924..af88b48c1cea 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -87,6 +87,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
* configurable, with more generic names like "ep-a". (remember that for
* USB, "in" means "towards the USB master".)
*
+ * This routine must be called in process context.
+ *
* returns zero, or a negative error code.
*/
int usb_ep_enable(struct usb_ep *ep)
@@ -119,6 +121,8 @@ EXPORT_SYMBOL_GPL(usb_ep_enable);
* gadget drivers must call usb_ep_enable() again before queueing
* requests to the endpoint.
*
+ * This routine must be called in process context.
+ *
* returns zero, or a negative error code.
*/
int usb_ep_disable(struct usb_ep *ep)
@@ -241,6 +245,8 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request);
* Note that @req's ->complete() callback must never be called from
* within usb_ep_queue() as that can create deadlock situations.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. Endpoints that are not enabled
* report errors; errors will also be
* reported when the usb peripheral is disconnected.
@@ -284,6 +290,8 @@ EXPORT_SYMBOL_GPL(usb_ep_queue);
* at the head of the queue) except as part of disconnecting from usb. Such
* restrictions prevent drivers from supporting configuration changes,
* even to configuration zero (a "chapter 9" requirement).
+ *
+ * This routine may be called in interrupt context.
*/
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
@@ -311,6 +319,8 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue);
* current altsetting, see usb_ep_clear_halt(). When switching altsettings,
* it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. On success, this call sets
* underlying hardware state that blocks data transfers.
* Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
@@ -336,6 +346,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt);
* for endpoints that aren't reconfigured, after clearing any other state
* in the endpoint's i/o queue.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero, or a negative error code. On success, this call clears
* the underlying hardware state reflecting endpoint halt and data toggle.
* Note that some hardware can't support this request (like pxa2xx_udc),
@@ -360,6 +372,8 @@ EXPORT_SYMBOL_GPL(usb_ep_clear_halt);
* requests. If the gadget driver clears the halt status, it will
* automatically unwedge the endpoint.
*
+ * This routine may be called in interrupt context.
+ *
* Returns zero on success, else negative errno.
*/
int usb_ep_set_wedge(struct usb_ep *ep)
@@ -388,6 +402,8 @@ EXPORT_SYMBOL_GPL(usb_ep_set_wedge);
* written OUT to it by the host. Drivers that need precise handling for
* fault reporting or recovery may need to use this call.
*
+ * This routine may be called in interrupt context.
+ *
* This returns the number of such bytes in the fifo, or a negative
* errno if the endpoint doesn't use a FIFO or doesn't support such
* precise handling.
@@ -415,6 +431,8 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_status);
* an endpoint fifo after abnormal transaction terminations. The call
* must never be used except when endpoint is not being used for any
* protocol translation.
+ *
+ * This routine may be called in interrupt context.
*/
void usb_ep_fifo_flush(struct usb_ep *ep)
{
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index 7cf98c793e04..1f879b3f2c96 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -23,6 +23,8 @@
#include <linux/uaccess.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/of.h>
+#include <linux/usb/role.h>
/* register definitions */
#define USB3_AXI_INT_STA 0x008
@@ -335,6 +337,11 @@ struct renesas_usb3 {
struct phy *phy;
struct dentry *dentry;
+ struct usb_role_switch *role_sw;
+ struct device *host_dev;
+ struct work_struct role_work;
+ enum usb_role role;
+
struct renesas_usb3_ep *usb3_ep;
int num_usb3_eps;
@@ -651,6 +658,14 @@ static void usb3_check_vbus(struct renesas_usb3 *usb3)
}
}
+static void renesas_usb3_role_work(struct work_struct *work)
+{
+ struct renesas_usb3 *usb3 =
+ container_of(work, struct renesas_usb3, role_work);
+
+ usb_role_switch_set_role(usb3->role_sw, usb3->role);
+}
+
static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
{
if (host)
@@ -659,6 +674,16 @@ static void usb3_set_mode(struct renesas_usb3 *usb3, bool host)
usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
}
+static void usb3_set_mode_by_role_sw(struct renesas_usb3 *usb3, bool host)
+{
+ if (usb3->role_sw) {
+ usb3->role = host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+ schedule_work(&usb3->role_work);
+ } else {
+ usb3_set_mode(usb3, host);
+ }
+}
+
static void usb3_vbus_out(struct renesas_usb3 *usb3, bool enable)
{
if (enable)
@@ -672,7 +697,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
unsigned long flags;
spin_lock_irqsave(&usb3->lock, flags);
- usb3_set_mode(usb3, host);
+ usb3_set_mode_by_role_sw(usb3, host);
usb3_vbus_out(usb3, a_dev);
/* for A-Peripheral or forced B-device mode */
if ((!host && a_dev) ||
@@ -2302,6 +2327,41 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
.set_selfpowered = renesas_usb3_set_selfpowered,
};
+static enum usb_role renesas_usb3_role_switch_get(struct device *dev)
+{
+ struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ enum usb_role cur_role;
+
+ pm_runtime_get_sync(dev);
+ cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+ pm_runtime_put(dev);
+
+ return cur_role;
+}
+
+static int renesas_usb3_role_switch_set(struct device *dev,
+ enum usb_role role)
+{
+ struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ struct device *host = usb3->host_dev;
+ enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+
+ pm_runtime_get_sync(dev);
+ if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) {
+ device_release_driver(host);
+ usb3_set_mode(usb3, false);
+ } else if (cur_role == USB_ROLE_DEVICE && role == USB_ROLE_HOST) {
+ /* Must set the mode before device_attach of the host */
+ usb3_set_mode(usb3, true);
+ /* This device_attach() might sleep */
+ if (device_attach(host) < 0)
+ dev_err(dev, "device_attach(host) failed\n");
+ }
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -2405,6 +2465,8 @@ static int renesas_usb3_remove(struct platform_device *pdev)
debugfs_remove_recursive(usb3->dentry);
device_remove_file(&pdev->dev, &dev_attr_role);
+ usb_role_switch_unregister(usb3->role_sw);
+
usb_del_gadget_udc(&usb3->gadget);
renesas_usb3_dma_free_prd(usb3, &pdev->dev);
@@ -2562,6 +2624,12 @@ static const unsigned int renesas_usb3_cable[] = {
EXTCON_NONE,
};
+static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = {
+ .set = renesas_usb3_role_switch_set,
+ .get = renesas_usb3_role_switch_get,
+ .allow_userspace_control = true,
+};
+
static int renesas_usb3_probe(struct platform_device *pdev)
{
struct renesas_usb3 *usb3;
@@ -2647,6 +2715,20 @@ static int renesas_usb3_probe(struct platform_device *pdev)
if (ret < 0)
goto err_dev_create;
+ INIT_WORK(&usb3->role_work, renesas_usb3_role_work);
+ usb3->role_sw = usb_role_switch_register(&pdev->dev,
+ &renesas_usb3_role_switch_desc);
+ if (!IS_ERR(usb3->role_sw)) {
+ usb3->host_dev = usb_of_get_companion_dev(&pdev->dev);
+ if (!usb3->host_dev) {
+ /* If not found, this driver will not use a role sw */
+ usb_role_switch_unregister(usb3->role_sw);
+ usb3->role_sw = NULL;
+ }
+ } else {
+ usb3->role_sw = NULL;
+ }
+
usb3->workaround_for_vbus = priv->workaround_for_vbus;
renesas_usb3_debugfs_init(usb3, &pdev->dev);