1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Common/core components for the Surface System Aggregator Module (SSAM) HID 4 * transport driver. Provides support for integrated HID devices on Microsoft 5 * Surface models. 6 * 7 * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com> 8 */ 9 10 #include <asm/unaligned.h> 11 #include <linux/hid.h> 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/types.h> 15 #include <linux/usb/ch9.h> 16 17 #include <linux/surface_aggregator/controller.h> 18 19 #include "surface_hid_core.h" 20 21 22 /* -- Device descriptor access. --------------------------------------------- */ 23 24 static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid) 25 { 26 int status; 27 28 status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID, 29 (u8 *)&shid->hid_desc, sizeof(shid->hid_desc)); 30 if (status) 31 return status; 32 33 if (shid->hid_desc.desc_len != sizeof(shid->hid_desc)) { 34 dev_err(shid->dev, "unexpected HID descriptor length: got %u, expected %zu\n", 35 shid->hid_desc.desc_len, sizeof(shid->hid_desc)); 36 return -EPROTO; 37 } 38 39 if (shid->hid_desc.desc_type != HID_DT_HID) { 40 dev_err(shid->dev, "unexpected HID descriptor type: got %#04x, expected %#04x\n", 41 shid->hid_desc.desc_type, HID_DT_HID); 42 return -EPROTO; 43 } 44 45 if (shid->hid_desc.num_descriptors != 1) { 46 dev_err(shid->dev, "unexpected number of descriptors: got %u, expected 1\n", 47 shid->hid_desc.num_descriptors); 48 return -EPROTO; 49 } 50 51 if (shid->hid_desc.report_desc_type != HID_DT_REPORT) { 52 dev_err(shid->dev, "unexpected report descriptor type: got %#04x, expected %#04x\n", 53 shid->hid_desc.report_desc_type, HID_DT_REPORT); 54 return -EPROTO; 55 } 56 57 return 0; 58 } 59 60 static int surface_hid_load_device_attributes(struct surface_hid_device *shid) 61 { 62 int status; 63 64 status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS, 65 (u8 *)&shid->attrs, sizeof(shid->attrs)); 66 if (status) 67 return status; 68 69 if (get_unaligned_le32(&shid->attrs.length) != sizeof(shid->attrs)) { 70 dev_err(shid->dev, "unexpected attribute length: got %u, expected %zu\n", 71 get_unaligned_le32(&shid->attrs.length), sizeof(shid->attrs)); 72 return -EPROTO; 73 } 74 75 return 0; 76 } 77 78 79 /* -- Transport driver (common). -------------------------------------------- */ 80 81 static int surface_hid_start(struct hid_device *hid) 82 { 83 struct surface_hid_device *shid = hid->driver_data; 84 85 return ssam_notifier_register(shid->ctrl, &shid->notif); 86 } 87 88 static void surface_hid_stop(struct hid_device *hid) 89 { 90 struct surface_hid_device *shid = hid->driver_data; 91 92 /* Note: This call will log errors for us, so ignore them here. */ 93 ssam_notifier_unregister(shid->ctrl, &shid->notif); 94 } 95 96 static int surface_hid_open(struct hid_device *hid) 97 { 98 return 0; 99 } 100 101 static void surface_hid_close(struct hid_device *hid) 102 { 103 } 104 105 static int surface_hid_parse(struct hid_device *hid) 106 { 107 struct surface_hid_device *shid = hid->driver_data; 108 size_t len = get_unaligned_le16(&shid->hid_desc.report_desc_len); 109 u8 *buf; 110 int status; 111 112 buf = kzalloc(len, GFP_KERNEL); 113 if (!buf) 114 return -ENOMEM; 115 116 status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_REPORT, buf, len); 117 if (!status) 118 status = hid_parse_report(hid, buf, len); 119 120 kfree(buf); 121 return status; 122 } 123 124 static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportnum, u8 *buf, 125 size_t len, unsigned char rtype, int reqtype) 126 { 127 struct surface_hid_device *shid = hid->driver_data; 128 129 if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) 130 return shid->ops.output_report(shid, reportnum, buf, len); 131 132 else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) 133 return shid->ops.get_feature_report(shid, reportnum, buf, len); 134 135 else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) 136 return shid->ops.set_feature_report(shid, reportnum, buf, len); 137 138 return -EIO; 139 } 140 141 static struct hid_ll_driver surface_hid_ll_driver = { 142 .start = surface_hid_start, 143 .stop = surface_hid_stop, 144 .open = surface_hid_open, 145 .close = surface_hid_close, 146 .parse = surface_hid_parse, 147 .raw_request = surface_hid_raw_request, 148 }; 149 150 151 /* -- Common device setup. -------------------------------------------------- */ 152 153 int surface_hid_device_add(struct surface_hid_device *shid) 154 { 155 int status; 156 157 status = surface_hid_load_hid_descriptor(shid); 158 if (status) 159 return status; 160 161 status = surface_hid_load_device_attributes(shid); 162 if (status) 163 return status; 164 165 shid->hid = hid_allocate_device(); 166 if (IS_ERR(shid->hid)) 167 return PTR_ERR(shid->hid); 168 169 shid->hid->dev.parent = shid->dev; 170 shid->hid->bus = BUS_HOST; 171 shid->hid->vendor = get_unaligned_le16(&shid->attrs.vendor); 172 shid->hid->product = get_unaligned_le16(&shid->attrs.product); 173 shid->hid->version = get_unaligned_le16(&shid->hid_desc.hid_version); 174 shid->hid->country = shid->hid_desc.country_code; 175 176 snprintf(shid->hid->name, sizeof(shid->hid->name), "Microsoft Surface %04X:%04X", 177 shid->hid->vendor, shid->hid->product); 178 179 strscpy(shid->hid->phys, dev_name(shid->dev), sizeof(shid->hid->phys)); 180 181 shid->hid->driver_data = shid; 182 shid->hid->ll_driver = &surface_hid_ll_driver; 183 184 status = hid_add_device(shid->hid); 185 if (status) 186 hid_destroy_device(shid->hid); 187 188 return status; 189 } 190 EXPORT_SYMBOL_GPL(surface_hid_device_add); 191 192 void surface_hid_device_destroy(struct surface_hid_device *shid) 193 { 194 hid_destroy_device(shid->hid); 195 } 196 EXPORT_SYMBOL_GPL(surface_hid_device_destroy); 197 198 199 /* -- PM ops. --------------------------------------------------------------- */ 200 201 #ifdef CONFIG_PM_SLEEP 202 203 static int surface_hid_suspend(struct device *dev) 204 { 205 struct surface_hid_device *d = dev_get_drvdata(dev); 206 207 if (d->hid->driver && d->hid->driver->suspend) 208 return d->hid->driver->suspend(d->hid, PMSG_SUSPEND); 209 210 return 0; 211 } 212 213 static int surface_hid_resume(struct device *dev) 214 { 215 struct surface_hid_device *d = dev_get_drvdata(dev); 216 217 if (d->hid->driver && d->hid->driver->resume) 218 return d->hid->driver->resume(d->hid); 219 220 return 0; 221 } 222 223 static int surface_hid_freeze(struct device *dev) 224 { 225 struct surface_hid_device *d = dev_get_drvdata(dev); 226 227 if (d->hid->driver && d->hid->driver->suspend) 228 return d->hid->driver->suspend(d->hid, PMSG_FREEZE); 229 230 return 0; 231 } 232 233 static int surface_hid_poweroff(struct device *dev) 234 { 235 struct surface_hid_device *d = dev_get_drvdata(dev); 236 237 if (d->hid->driver && d->hid->driver->suspend) 238 return d->hid->driver->suspend(d->hid, PMSG_HIBERNATE); 239 240 return 0; 241 } 242 243 static int surface_hid_restore(struct device *dev) 244 { 245 struct surface_hid_device *d = dev_get_drvdata(dev); 246 247 if (d->hid->driver && d->hid->driver->reset_resume) 248 return d->hid->driver->reset_resume(d->hid); 249 250 return 0; 251 } 252 253 const struct dev_pm_ops surface_hid_pm_ops = { 254 .freeze = surface_hid_freeze, 255 .thaw = surface_hid_resume, 256 .suspend = surface_hid_suspend, 257 .resume = surface_hid_resume, 258 .poweroff = surface_hid_poweroff, 259 .restore = surface_hid_restore, 260 }; 261 EXPORT_SYMBOL_GPL(surface_hid_pm_ops); 262 263 #else /* CONFIG_PM_SLEEP */ 264 265 const struct dev_pm_ops surface_hid_pm_ops = { }; 266 EXPORT_SYMBOL_GPL(surface_hid_pm_ops); 267 268 #endif /* CONFIG_PM_SLEEP */ 269 270 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 271 MODULE_DESCRIPTION("HID transport driver core for Surface System Aggregator Module"); 272 MODULE_LICENSE("GPL"); 273