From patchwork Wed Mar 14 06:02:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?Q2h1bmZlbmcgWXVuICjkupHmmKXls7Ap?= X-Patchwork-Id: 10281513 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2EA8F60211 for ; Wed, 14 Mar 2018 06:04:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1CB75286FB for ; Wed, 14 Mar 2018 06:04:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0F259286FD; Wed, 14 Mar 2018 06:04:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 8F16B286FB for ; Wed, 14 Mar 2018 06:04:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=jghu8RhJ5vvD1PK84MiK50fU6zOLiIMC8AidF2ag0AY=; b=Xc00NOXbqC9obO hTUt9m3F4EvYxuQyMUniswLuA96iYqa21dUlAvug9Jwi7jUOGdaOPxI6ZQWrsmsv+EDQHMEhYkDG/ rh3qyWVpJWkZ6NABM7dZbRRRW7u//EQWECOJwBSzoJfbohNkr/lViJ5IQi8Sd/d7TjxuD43HLiLeT /StoK2XNsa6ix0U0GJdudu5M2B6madzVyNhmY061Ry02QJLwBvQBWS552w3S4wCk91Pd4EfQOsXZ6 HsWBtHs/xWBqK70FG9RHpDwjcsx/pLNioFzoZOuIoXUFXjCY35uvOZ8de9ijEcnN/GzXRU6MMuzdA vwwj/Rdw+g/Naz3gZJKQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1evzWL-0007C1-Fm; Wed, 14 Mar 2018 06:04:17 +0000 Received: from [1.203.163.78] (helo=mailgw01.mediatek.com) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1evzVT-0006aQ-HU; Wed, 14 Mar 2018 06:03:25 +0000 X-UUID: a352ee4261e3465e80877d0821e7c2a2-20180314 Received: from mtkcas34.mediatek.inc [(172.27.4.250)] by mailgw01.mediatek.com (envelope-from ) (mailgw01.mediatek.com ESMTP with TLS) with ESMTP id 453249713; Wed, 14 Mar 2018 14:02:49 +0800 Received: from MTKCAS06.mediatek.inc (172.21.101.30) by MTKMBS31N1.mediatek.inc (172.27.4.69) with Microsoft SMTP Server (TLS) id 15.0.1210.3; Wed, 14 Mar 2018 14:02:48 +0800 Received: from localhost.localdomain (10.17.3.153) by MTKCAS06.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1210.3 via Frontend Transport; Wed, 14 Mar 2018 14:02:47 +0800 From: Chunfeng Yun To: Greg Kroah-Hartman Subject: [PATCH] usb: misc: supports Apple Carplay driver Date: Wed, 14 Mar 2018 14:02:36 +0800 Message-ID: <1521007356-12306-2-git-send-email-chunfeng.yun@mediatek.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1521007356-12306-1-git-send-email-chunfeng.yun@mediatek.com> References: <1521007356-12306-1-git-send-email-chunfeng.yun@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180313_230323_949500_D3DAE90F X-CRM114-Status: GOOD ( 19.29 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kate Stewart , Heikki Krogerus , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Serge Semin , Chunfeng Yun , Alan Stern , linux-arm-kernel@lists.infradead.org, Matthias Brugger , linux-mediatek@lists.infradead.org, Thomas Gleixner , Guenter Roeck Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+patchwork-linux-mediatek=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 "); +MODULE_DESCRIPTION("USB Carplay Driver"); +MODULE_LICENSE("GPL");