Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Common/core components for the Surface System Aggregator Module (SSAM) HID
0004  * transport driver. Provides support for integrated HID devices on Microsoft
0005  * Surface models.
0006  *
0007  * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
0008  */
0009 
0010 #include <asm/unaligned.h>
0011 #include <linux/hid.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/types.h>
0015 #include <linux/usb/ch9.h>
0016 
0017 #include <linux/surface_aggregator/controller.h>
0018 
0019 #include "surface_hid_core.h"
0020 
0021 
0022 /* -- Utility functions. ---------------------------------------------------- */
0023 
0024 static bool surface_hid_is_hot_removed(struct surface_hid_device *shid)
0025 {
0026     /*
0027      * Non-ssam client devices, i.e. platform client devices, cannot be
0028      * hot-removed.
0029      */
0030     if (!is_ssam_device(shid->dev))
0031         return false;
0032 
0033     return ssam_device_is_hot_removed(to_ssam_device(shid->dev));
0034 }
0035 
0036 
0037 /* -- Device descriptor access. --------------------------------------------- */
0038 
0039 static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid)
0040 {
0041     int status;
0042 
0043     if (surface_hid_is_hot_removed(shid))
0044         return -ENODEV;
0045 
0046     status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID,
0047             (u8 *)&shid->hid_desc, sizeof(shid->hid_desc));
0048     if (status)
0049         return status;
0050 
0051     if (shid->hid_desc.desc_len != sizeof(shid->hid_desc)) {
0052         dev_err(shid->dev, "unexpected HID descriptor length: got %u, expected %zu\n",
0053             shid->hid_desc.desc_len, sizeof(shid->hid_desc));
0054         return -EPROTO;
0055     }
0056 
0057     if (shid->hid_desc.desc_type != HID_DT_HID) {
0058         dev_err(shid->dev, "unexpected HID descriptor type: got %#04x, expected %#04x\n",
0059             shid->hid_desc.desc_type, HID_DT_HID);
0060         return -EPROTO;
0061     }
0062 
0063     if (shid->hid_desc.num_descriptors != 1) {
0064         dev_err(shid->dev, "unexpected number of descriptors: got %u, expected 1\n",
0065             shid->hid_desc.num_descriptors);
0066         return -EPROTO;
0067     }
0068 
0069     if (shid->hid_desc.report_desc_type != HID_DT_REPORT) {
0070         dev_err(shid->dev, "unexpected report descriptor type: got %#04x, expected %#04x\n",
0071             shid->hid_desc.report_desc_type, HID_DT_REPORT);
0072         return -EPROTO;
0073     }
0074 
0075     return 0;
0076 }
0077 
0078 static int surface_hid_load_device_attributes(struct surface_hid_device *shid)
0079 {
0080     int status;
0081 
0082     if (surface_hid_is_hot_removed(shid))
0083         return -ENODEV;
0084 
0085     status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS,
0086             (u8 *)&shid->attrs, sizeof(shid->attrs));
0087     if (status)
0088         return status;
0089 
0090     if (get_unaligned_le32(&shid->attrs.length) != sizeof(shid->attrs)) {
0091         dev_err(shid->dev, "unexpected attribute length: got %u, expected %zu\n",
0092             get_unaligned_le32(&shid->attrs.length), sizeof(shid->attrs));
0093         return -EPROTO;
0094     }
0095 
0096     return 0;
0097 }
0098 
0099 
0100 /* -- Transport driver (common). -------------------------------------------- */
0101 
0102 static int surface_hid_start(struct hid_device *hid)
0103 {
0104     struct surface_hid_device *shid = hid->driver_data;
0105 
0106     return ssam_notifier_register(shid->ctrl, &shid->notif);
0107 }
0108 
0109 static void surface_hid_stop(struct hid_device *hid)
0110 {
0111     struct surface_hid_device *shid = hid->driver_data;
0112     bool hot_removed;
0113 
0114     /*
0115      * Communication may fail for devices that have been hot-removed. This
0116      * also includes unregistration of HID events, so we need to check this
0117      * here. Only if the device has not been marked as hot-removed, we can
0118      * safely disable events.
0119      */
0120     hot_removed = surface_hid_is_hot_removed(shid);
0121 
0122     /* Note: This call will log errors for us, so ignore them here. */
0123     __ssam_notifier_unregister(shid->ctrl, &shid->notif, !hot_removed);
0124 }
0125 
0126 static int surface_hid_open(struct hid_device *hid)
0127 {
0128     return 0;
0129 }
0130 
0131 static void surface_hid_close(struct hid_device *hid)
0132 {
0133 }
0134 
0135 static int surface_hid_parse(struct hid_device *hid)
0136 {
0137     struct surface_hid_device *shid = hid->driver_data;
0138     size_t len = get_unaligned_le16(&shid->hid_desc.report_desc_len);
0139     u8 *buf;
0140     int status;
0141 
0142     if (surface_hid_is_hot_removed(shid))
0143         return -ENODEV;
0144 
0145     buf = kzalloc(len, GFP_KERNEL);
0146     if (!buf)
0147         return -ENOMEM;
0148 
0149     status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_REPORT, buf, len);
0150     if (!status)
0151         status = hid_parse_report(hid, buf, len);
0152 
0153     kfree(buf);
0154     return status;
0155 }
0156 
0157 static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportnum, u8 *buf,
0158                    size_t len, unsigned char rtype, int reqtype)
0159 {
0160     struct surface_hid_device *shid = hid->driver_data;
0161 
0162     if (surface_hid_is_hot_removed(shid))
0163         return -ENODEV;
0164 
0165     if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT)
0166         return shid->ops.output_report(shid, reportnum, buf, len);
0167 
0168     else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT)
0169         return shid->ops.get_feature_report(shid, reportnum, buf, len);
0170 
0171     else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT)
0172         return shid->ops.set_feature_report(shid, reportnum, buf, len);
0173 
0174     return -EIO;
0175 }
0176 
0177 static struct hid_ll_driver surface_hid_ll_driver = {
0178     .start       = surface_hid_start,
0179     .stop        = surface_hid_stop,
0180     .open        = surface_hid_open,
0181     .close       = surface_hid_close,
0182     .parse       = surface_hid_parse,
0183     .raw_request = surface_hid_raw_request,
0184 };
0185 
0186 
0187 /* -- Common device setup. -------------------------------------------------- */
0188 
0189 int surface_hid_device_add(struct surface_hid_device *shid)
0190 {
0191     int status;
0192 
0193     status = surface_hid_load_hid_descriptor(shid);
0194     if (status)
0195         return status;
0196 
0197     status = surface_hid_load_device_attributes(shid);
0198     if (status)
0199         return status;
0200 
0201     shid->hid = hid_allocate_device();
0202     if (IS_ERR(shid->hid))
0203         return PTR_ERR(shid->hid);
0204 
0205     shid->hid->dev.parent = shid->dev;
0206     shid->hid->bus = BUS_HOST;
0207     shid->hid->vendor = get_unaligned_le16(&shid->attrs.vendor);
0208     shid->hid->product = get_unaligned_le16(&shid->attrs.product);
0209     shid->hid->version = get_unaligned_le16(&shid->hid_desc.hid_version);
0210     shid->hid->country = shid->hid_desc.country_code;
0211 
0212     snprintf(shid->hid->name, sizeof(shid->hid->name), "Microsoft Surface %04X:%04X",
0213          shid->hid->vendor, shid->hid->product);
0214 
0215     strscpy(shid->hid->phys, dev_name(shid->dev), sizeof(shid->hid->phys));
0216 
0217     shid->hid->driver_data = shid;
0218     shid->hid->ll_driver = &surface_hid_ll_driver;
0219 
0220     status = hid_add_device(shid->hid);
0221     if (status)
0222         hid_destroy_device(shid->hid);
0223 
0224     return status;
0225 }
0226 EXPORT_SYMBOL_GPL(surface_hid_device_add);
0227 
0228 void surface_hid_device_destroy(struct surface_hid_device *shid)
0229 {
0230     hid_destroy_device(shid->hid);
0231 }
0232 EXPORT_SYMBOL_GPL(surface_hid_device_destroy);
0233 
0234 
0235 /* -- PM ops. --------------------------------------------------------------- */
0236 
0237 #ifdef CONFIG_PM_SLEEP
0238 
0239 static int surface_hid_suspend(struct device *dev)
0240 {
0241     struct surface_hid_device *d = dev_get_drvdata(dev);
0242 
0243     return hid_driver_suspend(d->hid, PMSG_SUSPEND);
0244 }
0245 
0246 static int surface_hid_resume(struct device *dev)
0247 {
0248     struct surface_hid_device *d = dev_get_drvdata(dev);
0249 
0250     return hid_driver_resume(d->hid);
0251 }
0252 
0253 static int surface_hid_freeze(struct device *dev)
0254 {
0255     struct surface_hid_device *d = dev_get_drvdata(dev);
0256 
0257     return hid_driver_suspend(d->hid, PMSG_FREEZE);
0258 }
0259 
0260 static int surface_hid_poweroff(struct device *dev)
0261 {
0262     struct surface_hid_device *d = dev_get_drvdata(dev);
0263 
0264     return hid_driver_suspend(d->hid, PMSG_HIBERNATE);
0265 }
0266 
0267 static int surface_hid_restore(struct device *dev)
0268 {
0269     struct surface_hid_device *d = dev_get_drvdata(dev);
0270 
0271     return hid_driver_reset_resume(d->hid);
0272 }
0273 
0274 const struct dev_pm_ops surface_hid_pm_ops = {
0275     .freeze   = surface_hid_freeze,
0276     .thaw     = surface_hid_resume,
0277     .suspend  = surface_hid_suspend,
0278     .resume   = surface_hid_resume,
0279     .poweroff = surface_hid_poweroff,
0280     .restore  = surface_hid_restore,
0281 };
0282 EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
0283 
0284 #else /* CONFIG_PM_SLEEP */
0285 
0286 const struct dev_pm_ops surface_hid_pm_ops = { };
0287 EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
0288 
0289 #endif /* CONFIG_PM_SLEEP */
0290 
0291 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
0292 MODULE_DESCRIPTION("HID transport driver core for Surface System Aggregator Module");
0293 MODULE_LICENSE("GPL");