Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Fast-charge control for Apple "MFi" devices
0004  *
0005  * Copyright (C) 2019 Bastien Nocera <hadess@hadess.net>
0006  */
0007 
0008 /* Standard include files */
0009 #include <linux/module.h>
0010 #include <linux/power_supply.h>
0011 #include <linux/slab.h>
0012 #include <linux/usb.h>
0013 
0014 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
0015 MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices");
0016 MODULE_LICENSE("GPL");
0017 
0018 #define TRICKLE_CURRENT_MA      0
0019 #define FAST_CURRENT_MA         2500
0020 
0021 #define APPLE_VENDOR_ID         0x05ac  /* Apple */
0022 
0023 /* The product ID is defined as starting with 0x12nn, as per the
0024  * "Choosing an Apple Device USB Configuration" section in
0025  * release R9 (2012) of the "MFi Accessory Hardware Specification"
0026  *
0027  * To distinguish an Apple device, a USB host can check the device
0028  * descriptor of attached USB devices for the following fields:
0029  * ■ Vendor ID: 0x05AC
0030  * ■ Product ID: 0x12nn
0031  *
0032  * Those checks will be done in .match() and .probe().
0033  */
0034 
0035 static const struct usb_device_id mfi_fc_id_table[] = {
0036     { .idVendor = APPLE_VENDOR_ID,
0037       .match_flags = USB_DEVICE_ID_MATCH_VENDOR },
0038     {},
0039 };
0040 
0041 MODULE_DEVICE_TABLE(usb, mfi_fc_id_table);
0042 
0043 /* Driver-local specific stuff */
0044 struct mfi_device {
0045     struct usb_device *udev;
0046     struct power_supply *battery;
0047     int charge_type;
0048 };
0049 
0050 static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi,
0051                     const union power_supply_propval *val)
0052 {
0053     int current_ma;
0054     int retval;
0055     __u8 request_type;
0056 
0057     if (mfi->charge_type == val->intval) {
0058         dev_dbg(&mfi->udev->dev, "charge type %d already set\n",
0059                 mfi->charge_type);
0060         return 0;
0061     }
0062 
0063     switch (val->intval) {
0064     case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
0065         current_ma = TRICKLE_CURRENT_MA;
0066         break;
0067     case POWER_SUPPLY_CHARGE_TYPE_FAST:
0068         current_ma = FAST_CURRENT_MA;
0069         break;
0070     default:
0071         return -EINVAL;
0072     }
0073 
0074     request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
0075     retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0),
0076                  0x40, /* Vendor‐defined power request */
0077                  request_type,
0078                  current_ma, /* wValue, current offset */
0079                  current_ma, /* wIndex, current offset */
0080                  NULL, 0, USB_CTRL_GET_TIMEOUT);
0081     if (retval) {
0082         dev_dbg(&mfi->udev->dev, "retval = %d\n", retval);
0083         return retval;
0084     }
0085 
0086     mfi->charge_type = val->intval;
0087 
0088     return 0;
0089 }
0090 
0091 static int apple_mfi_fc_get_property(struct power_supply *psy,
0092         enum power_supply_property psp,
0093         union power_supply_propval *val)
0094 {
0095     struct mfi_device *mfi = power_supply_get_drvdata(psy);
0096 
0097     dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
0098 
0099     switch (psp) {
0100     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0101         val->intval = mfi->charge_type;
0102         break;
0103     case POWER_SUPPLY_PROP_SCOPE:
0104         val->intval = POWER_SUPPLY_SCOPE_DEVICE;
0105         break;
0106     default:
0107         return -ENODATA;
0108     }
0109 
0110     return 0;
0111 }
0112 
0113 static int apple_mfi_fc_set_property(struct power_supply *psy,
0114         enum power_supply_property psp,
0115         const union power_supply_propval *val)
0116 {
0117     struct mfi_device *mfi = power_supply_get_drvdata(psy);
0118     int ret;
0119 
0120     dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
0121 
0122     ret = pm_runtime_get_sync(&mfi->udev->dev);
0123     if (ret < 0) {
0124         pm_runtime_put_noidle(&mfi->udev->dev);
0125         return ret;
0126     }
0127 
0128     switch (psp) {
0129     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0130         ret = apple_mfi_fc_set_charge_type(mfi, val);
0131         break;
0132     default:
0133         ret = -EINVAL;
0134     }
0135 
0136     pm_runtime_mark_last_busy(&mfi->udev->dev);
0137     pm_runtime_put_autosuspend(&mfi->udev->dev);
0138 
0139     return ret;
0140 }
0141 
0142 static int apple_mfi_fc_property_is_writeable(struct power_supply *psy,
0143                           enum power_supply_property psp)
0144 {
0145     switch (psp) {
0146     case POWER_SUPPLY_PROP_CHARGE_TYPE:
0147         return 1;
0148     default:
0149         return 0;
0150     }
0151 }
0152 
0153 static enum power_supply_property apple_mfi_fc_properties[] = {
0154     POWER_SUPPLY_PROP_CHARGE_TYPE,
0155     POWER_SUPPLY_PROP_SCOPE
0156 };
0157 
0158 static const struct power_supply_desc apple_mfi_fc_desc = {
0159     .name                   = "apple_mfi_fastcharge",
0160     .type                   = POWER_SUPPLY_TYPE_BATTERY,
0161     .properties             = apple_mfi_fc_properties,
0162     .num_properties         = ARRAY_SIZE(apple_mfi_fc_properties),
0163     .get_property           = apple_mfi_fc_get_property,
0164     .set_property           = apple_mfi_fc_set_property,
0165     .property_is_writeable  = apple_mfi_fc_property_is_writeable
0166 };
0167 
0168 static bool mfi_fc_match(struct usb_device *udev)
0169 {
0170     int idProduct;
0171 
0172     idProduct = le16_to_cpu(udev->descriptor.idProduct);
0173     /* See comment above mfi_fc_id_table[] */
0174     return (idProduct >= 0x1200 && idProduct <= 0x12ff);
0175 }
0176 
0177 static int mfi_fc_probe(struct usb_device *udev)
0178 {
0179     struct power_supply_config battery_cfg = {};
0180     struct mfi_device *mfi = NULL;
0181     int err;
0182 
0183     if (!mfi_fc_match(udev))
0184         return -ENODEV;
0185 
0186     mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL);
0187     if (!mfi)
0188         return -ENOMEM;
0189 
0190     battery_cfg.drv_data = mfi;
0191 
0192     mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
0193     mfi->battery = power_supply_register(&udev->dev,
0194                         &apple_mfi_fc_desc,
0195                         &battery_cfg);
0196     if (IS_ERR(mfi->battery)) {
0197         dev_err(&udev->dev, "Can't register battery\n");
0198         err = PTR_ERR(mfi->battery);
0199         kfree(mfi);
0200         return err;
0201     }
0202 
0203     mfi->udev = usb_get_dev(udev);
0204     dev_set_drvdata(&udev->dev, mfi);
0205 
0206     return 0;
0207 }
0208 
0209 static void mfi_fc_disconnect(struct usb_device *udev)
0210 {
0211     struct mfi_device *mfi;
0212 
0213     mfi = dev_get_drvdata(&udev->dev);
0214     if (mfi->battery)
0215         power_supply_unregister(mfi->battery);
0216     dev_set_drvdata(&udev->dev, NULL);
0217     usb_put_dev(mfi->udev);
0218     kfree(mfi);
0219 }
0220 
0221 static struct usb_device_driver mfi_fc_driver = {
0222     .name =     "apple-mfi-fastcharge",
0223     .probe =    mfi_fc_probe,
0224     .disconnect =   mfi_fc_disconnect,
0225     .id_table = mfi_fc_id_table,
0226     .match =    mfi_fc_match,
0227     .generic_subclass = 1,
0228 };
0229 
0230 static int __init mfi_fc_driver_init(void)
0231 {
0232     return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE);
0233 }
0234 
0235 static void __exit mfi_fc_driver_exit(void)
0236 {
0237     usb_deregister_device_driver(&mfi_fc_driver);
0238 }
0239 
0240 module_init(mfi_fc_driver_init);
0241 module_exit(mfi_fc_driver_exit);