[PATCH] usb: misc: supports Apple Carplay driver

Chunfeng Yun chunfeng.yun at mediatek.com
Tue Mar 13 23:02:36 PDT 2018


The driver is used to support Apple carplay feature by a debugfs
interface which can force the driver to send a USB Vendor Request
of "Apple Device to Host Mode Switch" to switch Apple Device
into host mode.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/misc/Kconfig   |    9 ++
 drivers/usb/misc/Makefile  |    1 +
 drivers/usb/misc/carplay.c |  205 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 215 insertions(+)
 create mode 100644 drivers/usb/misc/carplay.c

diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 68d2f2c..c010c95 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -275,3 +275,12 @@ config USB_CHAOSKEY
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called chaoskey.
+
+config USB_CARPLAY
+	tristate "USB carplay driver support"
+	help
+	  The driver is used to support Apple carplay feature.
+	  It is realized by sending a USB Vendor Request of "Apple Device to
+	  Host Mode Switch" to switch Apple Device into host mode.
+	  When the users want to use carplay, they can force the driver to send
+	  this Vendor Request by a debugfs interface.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 109f54f..94380e7 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_USB_HSIC_USB3503)		+= usb3503.o
 obj-$(CONFIG_USB_HSIC_USB4604)		+= usb4604.o
 obj-$(CONFIG_USB_CHAOSKEY)		+= chaoskey.o
 
+obj-$(CONFIG_USB_CARPLAY)		+= carplay.o
 obj-$(CONFIG_USB_SISUSBVGA)		+= sisusbvga/
 obj-$(CONFIG_USB_LINK_LAYER_TEST)	+= lvstest.o
diff --git a/drivers/usb/misc/carplay.c b/drivers/usb/misc/carplay.c
new file mode 100644
index 0000000..bfd41f3
--- /dev/null
+++ b/drivers/usb/misc/carplay.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * carplay.c - carplay usb driver
+ *
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.com>
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+/*
+ * usage:
+ * The requirement for the platform using Carplay feature is that support
+ * the USB Dual Role Switch feature, and must have a USB-A receptacle
+ * that is capable of functioning in both USB Host and USB Device roles.
+ *
+ * 1. Apple iphone is enumerated as a usb device
+ * 2. switch Apple iphone to host mode, by, e.g.
+ *    echo host > /sys/kernel/debug/usb/carplay.1-1/mode
+ * 3. switch the platform to device mode, but meanwhile should keep vbus alive;
+ * 4. use carplay feature after the platform is enumerated as a usb device;
+ * 5. when unplug usb cable, switch the platform back to host mode.
+ *
+ * step 2 is supported by this driver;
+ * step 1, 3, 4, 5 should be supported by the USB Dual-Role Controller Driver
+ *    on the platform.
+ *
+ * For more detailed information, please refer to "Chapter 46. USB Role Switch"
+ * in MFI Accessroy Interface Specification.pdf
+ */
+
+#define CARPLAY_NAME "carplay"
+#define VENDER_REQ_DEV_TO_HOST 0x51
+
+struct usb_carplay {
+	struct usb_interface *intf;
+	struct usb_device *udev;
+	struct dentry *droot;
+	struct device *idev;
+	bool is_host;
+};
+
+static int carplay_switch_to_host(struct usb_carplay *ucp)
+{
+	struct usb_device *udev = ucp->udev;
+	int retval;
+
+	if (!ucp->udev)
+		return -ENODEV;
+
+	retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			VENDER_REQ_DEV_TO_HOST, USB_TYPE_VENDOR,
+			1, 0, NULL, 0, USB_CTRL_GET_TIMEOUT);
+
+	dev_dbg(ucp->idev, "%s retval = %d\n", __func__, retval);
+
+	if (retval != 0) {
+		dev_err(ucp->idev, "%s fail retval = %d\n", __func__, retval);
+		return retval;
+	}
+	ucp->is_host = true;
+
+	return 0;
+}
+
+static int carplay_mode_show(struct seq_file *sf, void *unused)
+{
+	struct usb_carplay *ucp = sf->private;
+
+	seq_printf(sf, "current mode: %s\n(usage: echo host > mode)\n",
+		ucp->is_host ? "host" : "device");
+
+	return 0;
+}
+
+static int carplay_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, carplay_mode_show, inode->i_private);
+}
+
+static ssize_t carplay_mode_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file *sf = file->private_data;
+	struct usb_carplay *ucp = sf->private;
+	char buf[16];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "host", 4) && !ucp->is_host) {
+		carplay_switch_to_host(ucp);
+	} else {
+		dev_err(ucp->idev, "wrong setting\n");
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static const struct file_operations carplay_mode_fops = {
+	.open = carplay_mode_open,
+	.write = carplay_mode_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static struct dentry *carplay_debugfs_init(struct usb_carplay *ucp)
+{
+	struct dentry *root;
+	const char *udev_name = dev_name(&ucp->udev->dev);
+	char name[16];
+
+	snprintf(name, sizeof(name), "%s.%s", CARPLAY_NAME, udev_name);
+	root = debugfs_create_dir(name, usb_debug_root);
+	if (!root) {
+		dev_err(ucp->idev, "create debugfs root failed\n");
+		return root;
+	}
+	ucp->droot = root;
+
+	return debugfs_create_file("mode", 0664, root, ucp,
+			&carplay_mode_fops);
+}
+
+static void carplay_debugfs_exit(struct usb_carplay *ucp)
+{
+	debugfs_remove_recursive(ucp->droot);
+}
+
+static int carplay_probe(struct usb_interface *intf,
+	const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct usb_carplay *ucp;
+	struct dentry *de;
+
+	udev = interface_to_usbdev(intf);
+
+	ucp = kzalloc(sizeof(*ucp), GFP_KERNEL);
+	if (!ucp)
+		return -ENOMEM;
+
+	ucp->udev = usb_get_dev(udev);
+	ucp->intf = intf;
+	ucp->idev = &intf->dev;
+	usb_set_intfdata(intf, ucp);
+	ucp->is_host = false;
+
+	de = carplay_debugfs_init(ucp);
+	if (IS_ERR_OR_NULL(de)) {
+		usb_set_intfdata(intf, NULL);
+		usb_put_dev(ucp->udev);
+		kfree(ucp);
+		return -ENOMEM;
+	}
+
+	dev_info(ucp->idev, "carplay attached\n");
+	return 0;
+}
+
+static void carplay_disconnect(struct usb_interface *intf)
+{
+	struct usb_carplay *ucp = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+	usb_put_dev(ucp->udev);
+	carplay_debugfs_exit(ucp);
+	kfree(ucp);
+	dev_info(&intf->dev, "carplay disconnected\n");
+}
+
+static const struct usb_device_id carplay_id_table[] = {
+	/* generic EZ-USB FX2 controller (or development board) */
+	{ USB_DEVICE(0x05ac, 0x12a8) },
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, carplay_id_table);
+
+static struct usb_driver carplay_driver = {
+	.name = CARPLAY_NAME,
+	.id_table = carplay_id_table,
+	.probe = carplay_probe,
+	.disconnect = carplay_disconnect,
+};
+
+module_usb_driver(carplay_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun at mediatek.com>");
+MODULE_DESCRIPTION("USB Carplay Driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5




More information about the Linux-mediatek mailing list