cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

surface_hid_core.c (6893B)


      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
     24static 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
     60static 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
     81static 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
     88static 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
     96static int surface_hid_open(struct hid_device *hid)
     97{
     98	return 0;
     99}
    100
    101static void surface_hid_close(struct hid_device *hid)
    102{
    103}
    104
    105static 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
    124static 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
    141static 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
    153int 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}
    190EXPORT_SYMBOL_GPL(surface_hid_device_add);
    191
    192void surface_hid_device_destroy(struct surface_hid_device *shid)
    193{
    194	hid_destroy_device(shid->hid);
    195}
    196EXPORT_SYMBOL_GPL(surface_hid_device_destroy);
    197
    198
    199/* -- PM ops. --------------------------------------------------------------- */
    200
    201#ifdef CONFIG_PM_SLEEP
    202
    203static int surface_hid_suspend(struct device *dev)
    204{
    205	struct surface_hid_device *d = dev_get_drvdata(dev);
    206
    207	return hid_driver_suspend(d->hid, PMSG_SUSPEND);
    208}
    209
    210static int surface_hid_resume(struct device *dev)
    211{
    212	struct surface_hid_device *d = dev_get_drvdata(dev);
    213
    214	return hid_driver_resume(d->hid);
    215}
    216
    217static int surface_hid_freeze(struct device *dev)
    218{
    219	struct surface_hid_device *d = dev_get_drvdata(dev);
    220
    221	return hid_driver_suspend(d->hid, PMSG_FREEZE);
    222}
    223
    224static int surface_hid_poweroff(struct device *dev)
    225{
    226	struct surface_hid_device *d = dev_get_drvdata(dev);
    227
    228	return hid_driver_suspend(d->hid, PMSG_HIBERNATE);
    229}
    230
    231static int surface_hid_restore(struct device *dev)
    232{
    233	struct surface_hid_device *d = dev_get_drvdata(dev);
    234
    235	return hid_driver_reset_resume(d->hid);
    236}
    237
    238const struct dev_pm_ops surface_hid_pm_ops = {
    239	.freeze   = surface_hid_freeze,
    240	.thaw     = surface_hid_resume,
    241	.suspend  = surface_hid_suspend,
    242	.resume   = surface_hid_resume,
    243	.poweroff = surface_hid_poweroff,
    244	.restore  = surface_hid_restore,
    245};
    246EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
    247
    248#else /* CONFIG_PM_SLEEP */
    249
    250const struct dev_pm_ops surface_hid_pm_ops = { };
    251EXPORT_SYMBOL_GPL(surface_hid_pm_ops);
    252
    253#endif /* CONFIG_PM_SLEEP */
    254
    255MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
    256MODULE_DESCRIPTION("HID transport driver core for Surface System Aggregator Module");
    257MODULE_LICENSE("GPL");