From patchwork Wed Oct 9 13:43:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bastien Nocera X-Patchwork-Id: 11181233 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 13E2F13BD for ; Wed, 9 Oct 2019 13:43:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EECEF218AC for ; Wed, 9 Oct 2019 13:43:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731300AbfJINnr (ORCPT ); Wed, 9 Oct 2019 09:43:47 -0400 Received: from relay8-d.mail.gandi.net ([217.70.183.201]:54835 "EHLO relay8-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731072AbfJINnr (ORCPT ); Wed, 9 Oct 2019 09:43:47 -0400 X-Originating-IP: 83.155.44.161 Received: from localhost.localdomain (mon69-7-83-155-44-161.fbx.proxad.net [83.155.44.161]) (Authenticated sender: hadess@hadess.net) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 7F1021BF217; Wed, 9 Oct 2019 13:43:44 +0000 (UTC) From: Bastien Nocera To: linux-usb@vger.kernel.org Cc: Greg Kroah-Hartman , Benjamin Tissoires Subject: [PATCH 1/5] USB: Export generic USB device driver functions Date: Wed, 9 Oct 2019 15:43:38 +0200 Message-Id: <20191009134342.6476-2-hadess@hadess.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191009134342.6476-1-hadess@hadess.net> References: <20191009134342.6476-1-hadess@hadess.net> MIME-Version: 1.0 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org This will make it possible to implement device drivers which extend the generic driver without needing to reimplement it. Signed-off-by: Bastien Nocera --- drivers/usb/core/generic.c | 20 ++++++++++++-------- drivers/usb/core/usb.h | 4 ++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 38f8b3e31762..7454c74d43ee 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,7 +195,7 @@ int usb_choose_configuration(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_choose_configuration); -static int generic_probe(struct usb_device *udev) +int usb_generic_driver_probe(struct usb_device *udev) { int err, c; @@ -221,8 +221,9 @@ static int generic_probe(struct usb_device *udev) return 0; } +EXPORT_SYMBOL_GPL(usb_generic_driver_probe); -static void generic_disconnect(struct usb_device *udev) +void usb_generic_driver_disconnect(struct usb_device *udev) { usb_notify_remove_device(udev); @@ -231,10 +232,11 @@ static void generic_disconnect(struct usb_device *udev) if (udev->actconfig) usb_set_configuration(udev, -1); } +EXPORT_SYMBOL_GPL(usb_generic_driver_disconnect); #ifdef CONFIG_PM -static int generic_suspend(struct usb_device *udev, pm_message_t msg) +int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg) { int rc; @@ -261,8 +263,9 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) usbfs_notify_suspend(udev); return rc; } +EXPORT_SYMBOL_GPL(usb_generic_driver_suspend); -static int generic_resume(struct usb_device *udev, pm_message_t msg) +int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg) { int rc; @@ -280,16 +283,17 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg) usbfs_notify_resume(udev); return rc; } +EXPORT_SYMBOL_GPL(usb_generic_driver_resume); #endif /* CONFIG_PM */ struct usb_device_driver usb_generic_driver = { .name = "usb", - .probe = generic_probe, - .disconnect = generic_disconnect, + .probe = usb_generic_driver_probe, + .disconnect = usb_generic_driver_disconnect, #ifdef CONFIG_PM - .suspend = generic_suspend, - .resume = generic_resume, + .suspend = usb_generic_driver_suspend, + .resume = usb_generic_driver_resume, #endif .supports_autosuspend = 1, }; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index cf4783cf661a..7423c4c5700f 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -47,6 +47,10 @@ extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); +extern int usb_generic_driver_probe(struct usb_device *udev); +extern void usb_generic_driver_disconnect(struct usb_device *udev); +extern int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg); +extern int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg); static inline unsigned usb_get_max_power(struct usb_device *udev, struct usb_host_config *c) From patchwork Wed Oct 9 13:43:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bastien Nocera X-Patchwork-Id: 11181231 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7018A76 for ; Wed, 9 Oct 2019 13:43:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5B73F218AC for ; Wed, 9 Oct 2019 13:43:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731229AbfJINnq (ORCPT ); Wed, 9 Oct 2019 09:43:46 -0400 Received: from relay8-d.mail.gandi.net ([217.70.183.201]:54387 "EHLO relay8-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731037AbfJINnq (ORCPT ); Wed, 9 Oct 2019 09:43:46 -0400 X-Originating-IP: 83.155.44.161 Received: from localhost.localdomain (mon69-7-83-155-44-161.fbx.proxad.net [83.155.44.161]) (Authenticated sender: hadess@hadess.net) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id 3DDA11BF213; Wed, 9 Oct 2019 13:43:45 +0000 (UTC) From: Bastien Nocera To: linux-usb@vger.kernel.org Cc: Greg Kroah-Hartman , Benjamin Tissoires Subject: [PATCH 2/5] USB: Make it possible to "subclass" usb_device_driver Date: Wed, 9 Oct 2019 15:43:39 +0200 Message-Id: <20191009134342.6476-3-hadess@hadess.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191009134342.6476-1-hadess@hadess.net> References: <20191009134342.6476-1-hadess@hadess.net> MIME-Version: 1.0 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org The kernel currenly has only 2 usb_device_drivers, one generic one, one that completely replaces the generic one to make USB devices usable over a network. Use the newly exported generic driver functions when a driver declares to want them run, in addition to its own code. This makes it possible to write drivers that extend the generic USB driver. Signed-off-by: Bastien Nocera --- drivers/usb/core/driver.c | 36 ++++++++++++++++++++++++++++++------ include/linux/usb.h | 1 + 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2b27d232d7a7..863e380a272b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -261,10 +261,17 @@ static int usb_probe_device(struct device *dev) */ if (!udriver->supports_autosuspend) error = usb_autoresume_device(udev); + if (error) + return error; - if (!error) - error = udriver->probe(udev); - return error; + if (udriver->generic_init) + error = usb_generic_driver_probe(udev); + if (error) + return error; + + if (udriver->probe) + return udriver->probe(udev); + return 0; } /* called from driver core with dev locked */ @@ -273,7 +280,10 @@ static int usb_unbind_device(struct device *dev) struct usb_device *udev = to_usb_device(dev); struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); - udriver->disconnect(udev); + if (udriver->generic_init) + usb_generic_driver_disconnect(udev); + if (udriver->disconnect) + udriver->disconnect(udev); if (!udriver->supports_autosuspend) usb_autosuspend_device(udev); return 0; @@ -886,6 +896,14 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver, if (usb_disabled()) return -ENODEV; + if (new_udriver->probe == NULL && + !new_udriver->generic_init) { + printk(KERN_ERR "%s: error %d registering device " + " driver %s, no probe() function\n", + usbcore_name, retval, new_udriver->name); + return -EINVAL; + } + new_udriver->drvwrap.for_devices = 1; new_udriver->drvwrap.driver.name = new_udriver->name; new_udriver->drvwrap.driver.bus = &usb_bus_type; @@ -1149,7 +1167,10 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) udev->do_remote_wakeup = 0; udriver = &usb_generic_driver; } - status = udriver->suspend(udev, msg); + if (udriver->generic_init) + status = usb_generic_driver_suspend (udev, msg); + if (status == 0 && udriver->suspend) + status = udriver->suspend(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -1181,7 +1202,10 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); - status = udriver->resume(udev, msg); + if (udriver->generic_init) + status = usb_generic_driver_resume (udev, msg); + if (status == 0 && udriver->resume) + status = udriver->resume(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); diff --git a/include/linux/usb.h b/include/linux/usb.h index e656e7b4b1e4..fb9ad3511e55 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1242,6 +1242,7 @@ struct usb_device_driver { const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; unsigned int supports_autosuspend:1; + unsigned int generic_init:1; }; #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ drvwrap.driver) From patchwork Wed Oct 9 13:43:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bastien Nocera X-Patchwork-Id: 11181235 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1E1AF13BD for ; Wed, 9 Oct 2019 13:43:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F239420B7C for ; Wed, 9 Oct 2019 13:43:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731316AbfJINns (ORCPT ); Wed, 9 Oct 2019 09:43:48 -0400 Received: from relay8-d.mail.gandi.net ([217.70.183.201]:47059 "EHLO relay8-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731037AbfJINns (ORCPT ); Wed, 9 Oct 2019 09:43:48 -0400 X-Originating-IP: 83.155.44.161 Received: from localhost.localdomain (mon69-7-83-155-44-161.fbx.proxad.net [83.155.44.161]) (Authenticated sender: hadess@hadess.net) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id EC0B51BF204; Wed, 9 Oct 2019 13:43:45 +0000 (UTC) From: Bastien Nocera To: linux-usb@vger.kernel.org Cc: Greg Kroah-Hartman , Benjamin Tissoires Subject: [PATCH 3/5] USB: Implement usb_device_match_id() Date: Wed, 9 Oct 2019 15:43:40 +0200 Message-Id: <20191009134342.6476-4-hadess@hadess.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191009134342.6476-1-hadess@hadess.net> References: <20191009134342.6476-1-hadess@hadess.net> MIME-Version: 1.0 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Match a usb_device with a table of IDs. Signed-off-by: Bastien Nocera --- drivers/usb/core/driver.c | 15 +++++++++++++++ include/linux/usb.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 863e380a272b..50f92da8afcf 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -800,6 +800,21 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface, } EXPORT_SYMBOL_GPL(usb_match_id); +const struct usb_device_id *usb_device_match_id(struct usb_device *udev, + const struct usb_device_id *id) +{ + if (!id) + return NULL; + + for (; id->idVendor || id->idProduct ; id++) { + if (usb_match_device(udev, id)) + return id; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_device_match_id); + static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ diff --git a/include/linux/usb.h b/include/linux/usb.h index fb9ad3511e55..66bd4344e298 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -864,6 +864,8 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id); extern int usb_match_one_id(struct usb_interface *interface, const struct usb_device_id *id); +const struct usb_device_id *usb_device_match_id(struct usb_device *udev, + const struct usb_device_id *id); extern int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *)); extern struct usb_interface *usb_find_interface(struct usb_driver *drv, From patchwork Wed Oct 9 13:43:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bastien Nocera X-Patchwork-Id: 11181237 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A38431864 for ; Wed, 9 Oct 2019 13:43:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8E38F218DE for ; Wed, 9 Oct 2019 13:43:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731343AbfJINnt (ORCPT ); Wed, 9 Oct 2019 09:43:49 -0400 Received: from relay8-d.mail.gandi.net ([217.70.183.201]:34351 "EHLO relay8-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731072AbfJINnt (ORCPT ); Wed, 9 Oct 2019 09:43:49 -0400 X-Originating-IP: 83.155.44.161 Received: from localhost.localdomain (mon69-7-83-155-44-161.fbx.proxad.net [83.155.44.161]) (Authenticated sender: hadess@hadess.net) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id EDD831BF20F; Wed, 9 Oct 2019 13:43:46 +0000 (UTC) From: Bastien Nocera To: linux-usb@vger.kernel.org Cc: Greg Kroah-Hartman , Benjamin Tissoires Subject: [PATCH 4/5] USB: Select better matching USB drivers when available Date: Wed, 9 Oct 2019 15:43:41 +0200 Message-Id: <20191009134342.6476-5-hadess@hadess.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191009134342.6476-1-hadess@hadess.net> References: <20191009134342.6476-1-hadess@hadess.net> MIME-Version: 1.0 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Now that USB device drivers can reuse code from the generic USB device driver, we need to make sure that they get selected rather than the generic driver. Add an id_table and match vfunc to the usb_device_driver struct, which will get used to select a better matching driver at ->probe time. This is a similar mechanism to that used in the HID drivers, with the generic driver being selected unless there's a better matching one found in the registered drivers (see hid_generic_match() in drivers/hid/hid-generic.c). Signed-off-by: Bastien Nocera --- drivers/usb/core/driver.c | 15 +++++++++++++-- drivers/usb/core/generic.c | 29 +++++++++++++++++++++++++++++ include/linux/usb.h | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 50f92da8afcf..27ce63ed902d 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -819,13 +819,24 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { + struct usb_device *udev; + struct usb_device_driver *udrv; /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) return 0; - /* TODO: Add real matching code */ - return 1; + udev = to_usb_device(dev); + udrv = to_usb_device_driver(drv); + + if (udrv->id_table && + usb_device_match_id(udev, udrv->id_table) != NULL) { + return 1; + } + + if (udrv->match) + return udrv->match(udev); + return 0; } else if (is_usb_interface(dev)) { struct usb_interface *intf; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7454c74d43ee..89f9c026a4d1 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,6 +195,34 @@ int usb_choose_configuration(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_choose_configuration); +static int __check_usb_generic(struct device_driver *drv, void *data) +{ + struct usb_device *udev = data; + struct usb_device_driver *udrv; + + if (!is_usb_device_driver(drv)) + return 0; + udrv = to_usb_device_driver(drv); + if (udrv == &usb_generic_driver) + return 0; + if (!udrv->id_table) + return 0; + + return usb_device_match_id(udev, udrv->id_table) != NULL; +} + +static bool usb_generic_driver_match(struct usb_device *udev) +{ + /* + * If any other driver wants the device, leave the device to this other + * driver. + */ + if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_usb_generic)) + return false; + + return true; +} + int usb_generic_driver_probe(struct usb_device *udev) { int err, c; @@ -289,6 +317,7 @@ EXPORT_SYMBOL_GPL(usb_generic_driver_resume); struct usb_device_driver usb_generic_driver = { .name = "usb", + .match = usb_generic_driver_match, .probe = usb_generic_driver_probe, .disconnect = usb_generic_driver_disconnect, #ifdef CONFIG_PM diff --git a/include/linux/usb.h b/include/linux/usb.h index 66bd4344e298..df5604f41118 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1236,6 +1236,7 @@ struct usb_driver { struct usb_device_driver { const char *name; + bool (*match) (struct usb_device *udev); int (*probe) (struct usb_device *udev); void (*disconnect) (struct usb_device *udev); @@ -1243,6 +1244,7 @@ struct usb_device_driver { int (*resume) (struct usb_device *udev, pm_message_t message); const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; + const struct usb_device_id *id_table; unsigned int supports_autosuspend:1; unsigned int generic_init:1; }; From patchwork Wed Oct 9 13:43:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Bastien Nocera X-Patchwork-Id: 11181239 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 81C1076 for ; Wed, 9 Oct 2019 13:43:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5B464218DE for ; Wed, 9 Oct 2019 13:43:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731345AbfJINnw (ORCPT ); Wed, 9 Oct 2019 09:43:52 -0400 Received: from relay8-d.mail.gandi.net ([217.70.183.201]:47059 "EHLO relay8-d.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731336AbfJINnw (ORCPT ); Wed, 9 Oct 2019 09:43:52 -0400 X-Originating-IP: 83.155.44.161 Received: from localhost.localdomain (mon69-7-83-155-44-161.fbx.proxad.net [83.155.44.161]) (Authenticated sender: hadess@hadess.net) by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id CFE281BF210; Wed, 9 Oct 2019 13:43:47 +0000 (UTC) From: Bastien Nocera To: linux-usb@vger.kernel.org Cc: Greg Kroah-Hartman , Benjamin Tissoires Subject: [PATCH 5/5] USB: Add driver to control USB fast charge for iOS devices Date: Wed, 9 Oct 2019 15:43:42 +0200 Message-Id: <20191009134342.6476-6-hadess@hadess.net> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191009134342.6476-1-hadess@hadess.net> References: <20191009134342.6476-1-hadess@hadess.net> MIME-Version: 1.0 Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org iOS devices will not draw more than 500mA unless instructed to do so. Setting the charge type power supply property to "fast" tells the device to start drawing more power, using the same procedure that official "MFi" chargers would. Signed-off-by: Bastien Nocera --- drivers/usb/misc/Kconfig | 10 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/apple-mfi-fastcharge.c | 500 ++++++++++++++++++++++++ 3 files changed, 511 insertions(+) create mode 100644 drivers/usb/misc/apple-mfi-fastcharge.c diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index bdae62b2ffe0..f52a49478f1c 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -147,6 +147,16 @@ config USB_APPLEDISPLAY Say Y here if you want to control the backlight of Apple Cinema Displays over USB. This driver provides a sysfs interface. +config APPLE_MFI_FASTCHARGE + tristate "Fast charge control for iOS devices" + select POWER_SUPPLY + help + Say Y here if you want to control whether iOS devices will + fast charge from the USB interface, as implemented in "MFi" + chargers. + + It is safe to say M here. + source "drivers/usb/misc/sisusbvga/Kconfig" config USB_LD diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 109f54f5b9aa..b75106cf3948 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o +obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c new file mode 100644 index 000000000000..e28300018adc --- /dev/null +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Fast-charge control for Apple "MFi" devices + * + * Copyright (C) 2019 Bastien Nocera + */ + +/* Standard include files */ +#include +#include +#include +#include + +MODULE_AUTHOR("Bastien Nocera "); +MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices"); +MODULE_LICENSE("GPL"); + +#define TRICKLE_CURRENT_MA 0 +#define FAST_CURRENT_MA 2500 + +#define APPLE_VENDOR_ID 0x05ac /* Apple */ +#define INTERFACE_NUMBER 0 + +/* The product ID is defined as starting with 0x12nn, as per the + * "Choosing an Apple Device USB Configuration" section in + * release R9 (2012) of the "MFi Accessory Hardware Specification" + * + * To distinguish an Apple device, a USB host can check the device + * descriptor of attached USB devices for the following fields: + * ■ Vendor ID: 0x05AC + * ■ Product ID: 0x12nn + * + * The table is generated with the following lua program: + * i = 0x1200 + * while i <= 0x12ff do + * print(string.format("\t{ USB_DEVICE(APPLE_VENDOR_ID, 0x%x) },", i)) + * i = i + 1 + * end + */ + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(APPLE_VENDOR_ID, 0x1200) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1201) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1202) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1203) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1204) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1205) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1206) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1207) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1208) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1209) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x120f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1210) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1211) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1212) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1213) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1214) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1215) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1216) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1217) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1218) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1219) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x121f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1220) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1221) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1222) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1223) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1224) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1225) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1226) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1227) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1228) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1229) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x122f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1230) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1231) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1232) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1233) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1234) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1235) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1236) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1237) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1238) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1239) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x123f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1240) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1241) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1242) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1243) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1244) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1245) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1246) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1247) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1248) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1249) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x124f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1250) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1251) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1252) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1253) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1254) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1255) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1256) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1257) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1258) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1259) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x125f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1260) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1261) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1262) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1263) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1264) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1265) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1266) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1267) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1268) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1269) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x126f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1270) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1271) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1272) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1273) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1274) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1275) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1276) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1277) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1278) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1279) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x127f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1280) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1281) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1282) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1283) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1284) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1285) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1286) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1287) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1288) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1289) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x128f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1290) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1291) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1292) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1293) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1294) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1295) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1296) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1297) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1298) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x1299) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129a) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129b) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129c) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129d) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129e) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x129f) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12a9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12aa) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ab) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ac) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ad) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ae) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12af) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12b9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ba) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12bb) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12bc) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12bd) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12be) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12bf) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12c9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ca) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12cb) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12cc) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12cd) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ce) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12cf) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12d9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12da) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12db) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12dc) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12dd) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12de) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12df) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12e9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ea) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12eb) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ec) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ed) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ee) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ef) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f0) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f1) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f2) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f3) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f4) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f5) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f6) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f7) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f8) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12f9) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12fa) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12fb) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12fc) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12fd) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12fe) }, + { USB_DEVICE(APPLE_VENDOR_ID, 0x12ff) }, + { }, +}; + +MODULE_DEVICE_TABLE (usb, id_table); + +/* Driver-local specific stuff */ +struct mfi_device { + struct usb_device *udev; + struct power_supply *battery; + int charge_type; +}; + +static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi, + const union power_supply_propval *val) +{ + int current_ma; + int retval; + + if (mfi->charge_type == val->intval) { + dev_dbg(&mfi->udev->dev, "charge type %d already set\n", mfi->charge_type); + return 0; + } + + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: + current_ma = TRICKLE_CURRENT_MA; + break; + case POWER_SUPPLY_CHARGE_TYPE_FAST: + current_ma = FAST_CURRENT_MA; + break; + default: + return -EINVAL; + } + + retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0), + 0x40, /* Vendor-defined USB get enabled capabilities request. */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + current_ma, /* wValue, current offset */ + current_ma, /* wIndex, current offset */ + NULL, 0, USB_CTRL_GET_TIMEOUT); + if (retval) { + dev_dbg(&mfi->udev->dev, "retval = %d\n", retval); + return retval; + } + + mfi->charge_type = val->intval; + + return 0; +} + +static int apple_mfi_fc_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct mfi_device *mfi = power_supply_get_drvdata(psy); + + dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = mfi->charge_type; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + default: + return -ENODATA; + } + + return 0; +} + +static int apple_mfi_fc_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mfi_device *mfi = power_supply_get_drvdata(psy); + int ret; + + dev_dbg(&mfi->udev->dev, "prop: %d\n", psp); + + ret = pm_runtime_get_sync(&mfi->udev->dev); + if (ret < 0) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = apple_mfi_fc_set_charge_type(mfi, val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_mark_last_busy(&mfi->udev->dev); + pm_runtime_put_autosuspend(&mfi->udev->dev); + + return ret; +} + +static int apple_mfi_fc_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + return 1; + default: + return 0; + } +} + +static enum power_supply_property apple_mfi_fc_properties[] = { + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_SCOPE +}; + +static const struct power_supply_desc apple_mfi_fc_desc = { + .name = "apple_mfi_fastcharge", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = apple_mfi_fc_properties, + .num_properties = ARRAY_SIZE(apple_mfi_fc_properties), + .get_property = apple_mfi_fc_get_property, + .set_property = apple_mfi_fc_set_property, + .property_is_writeable = apple_mfi_fc_property_is_writeable +}; + +static int mfi_fc_probe(struct usb_device *udev) +{ + struct power_supply_config battery_cfg = {}; + struct mfi_device *mfi = NULL; + int err; + + dev_err(&udev->dev, "mfi_fc_probe\n"); + + mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); + if (!mfi) { + err = -ENOMEM; + goto error; + } + + battery_cfg.drv_data = mfi; + + mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + mfi->battery = power_supply_register(&udev->dev, &apple_mfi_fc_desc, + &battery_cfg); + if (IS_ERR(mfi->battery)) { + dev_err(&udev->dev, "Can't register battery\n"); + err = PTR_ERR(mfi->battery); + goto error; + } + + mfi->udev = usb_get_dev(udev); + dev_set_drvdata(&udev->dev, mfi); + dev_err(&udev->dev, "Registered fast charge\n"); + + return 0; + +error: + kfree(mfi); + return err; +} + +static void mfi_fc_disconnect(struct usb_device *udev) +{ + struct mfi_device *mfi; + + dev_err(&udev->dev, "De-registering fast charge\n"); + + mfi = dev_get_drvdata(&udev->dev); + if (mfi->battery) + power_supply_unregister(mfi->battery); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(mfi->udev); + kfree(mfi); +} + +static struct usb_device_driver mfi_fc_driver = { + .name = "apple-mfi-fastcharge", + .probe = mfi_fc_probe, + .disconnect = mfi_fc_disconnect, + .id_table = id_table, + .generic_init = 1, +}; + +static int __init mfi_fc_driver_init(void) +{ + int ret; + + pr_err("mfi_fc_driver_init\n"); + + ret = usb_register_device_driver(&mfi_fc_driver, THIS_MODULE); + if (ret) + pr_err("usb_register failed %d\n", ret); + + return ret; +} + +static void __exit mfi_fc_driver_exit(void) +{ + pr_err("mfi_fc_driver_exit\n"); + usb_deregister_device_driver(&mfi_fc_driver); +} + +module_init(mfi_fc_driver_init); +module_exit(mfi_fc_driver_exit);