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

imon_raw.c (4771B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// Copyright (C) 2018 Sean Young <sean@mess.org>
      4
      5#include <linux/module.h>
      6#include <linux/usb.h>
      7#include <linux/usb/input.h>
      8#include <media/rc-core.h>
      9
     10/* Each bit is 250us */
     11#define BIT_DURATION 250
     12
     13struct imon {
     14	struct device *dev;
     15	struct urb *ir_urb;
     16	struct rc_dev *rcdev;
     17	__be64 ir_buf;
     18	char phys[64];
     19};
     20
     21/*
     22 * The first 5 bytes of data represent IR pulse or space. Each bit, starting
     23 * from highest bit in the first byte, represents 250µs of data. It is 1
     24 * for space and 0 for pulse.
     25 *
     26 * The station sends 10 packets, and the 7th byte will be number 1 to 10, so
     27 * when we receive 10 we assume all the data has arrived.
     28 */
     29static void imon_ir_data(struct imon *imon)
     30{
     31	struct ir_raw_event rawir = {};
     32	u64 data = be64_to_cpu(imon->ir_buf);
     33	u8 packet_no = data & 0xff;
     34	int offset = 40;
     35	int bit;
     36
     37	if (packet_no == 0xff)
     38		return;
     39
     40	dev_dbg(imon->dev, "data: %*ph", 8, &imon->ir_buf);
     41
     42	/*
     43	 * Only the first 5 bytes contain IR data. Right shift so we move
     44	 * the IR bits to the lower 40 bits.
     45	 */
     46	data >>= 24;
     47
     48	do {
     49		/*
     50		 * Find highest set bit which is less or equal to offset
     51		 *
     52		 * offset is the bit above (base 0) where we start looking.
     53		 *
     54		 * data & (BIT_ULL(offset) - 1) masks off any unwanted bits,
     55		 * so we have just bits less than offset.
     56		 *
     57		 * fls will tell us the highest bit set plus 1 (or 0 if no
     58		 * bits are set).
     59		 */
     60		rawir.pulse = !rawir.pulse;
     61		bit = fls64(data & (BIT_ULL(offset) - 1));
     62		if (bit < offset) {
     63			dev_dbg(imon->dev, "%s: %d bits",
     64				rawir.pulse ? "pulse" : "space", offset - bit);
     65			rawir.duration = (offset - bit) * BIT_DURATION;
     66			ir_raw_event_store_with_filter(imon->rcdev, &rawir);
     67
     68			offset = bit;
     69		}
     70
     71		data = ~data;
     72	} while (offset > 0);
     73
     74	if (packet_no == 0x0a && !imon->rcdev->idle) {
     75		ir_raw_event_set_idle(imon->rcdev, true);
     76		ir_raw_event_handle(imon->rcdev);
     77	}
     78}
     79
     80static void imon_ir_rx(struct urb *urb)
     81{
     82	struct imon *imon = urb->context;
     83	int ret;
     84
     85	switch (urb->status) {
     86	case 0:
     87		imon_ir_data(imon);
     88		break;
     89	case -ECONNRESET:
     90	case -ENOENT:
     91	case -ESHUTDOWN:
     92		usb_unlink_urb(urb);
     93		return;
     94	case -EPIPE:
     95	default:
     96		dev_dbg(imon->dev, "error: urb status = %d", urb->status);
     97		break;
     98	}
     99
    100	ret = usb_submit_urb(urb, GFP_ATOMIC);
    101	if (ret && ret != -ENODEV)
    102		dev_warn(imon->dev, "failed to resubmit urb: %d", ret);
    103}
    104
    105static int imon_probe(struct usb_interface *intf,
    106		      const struct usb_device_id *id)
    107{
    108	struct usb_endpoint_descriptor *ir_ep = NULL;
    109	struct usb_host_interface *idesc;
    110	struct usb_device *udev;
    111	struct rc_dev *rcdev;
    112	struct imon *imon;
    113	int i, ret;
    114
    115	udev = interface_to_usbdev(intf);
    116	idesc = intf->cur_altsetting;
    117
    118	for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
    119		struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc;
    120
    121		if (usb_endpoint_is_int_in(ep)) {
    122			ir_ep = ep;
    123			break;
    124		}
    125	}
    126
    127	if (!ir_ep) {
    128		dev_err(&intf->dev, "IR endpoint missing");
    129		return -ENODEV;
    130	}
    131
    132	imon = devm_kmalloc(&intf->dev, sizeof(*imon), GFP_KERNEL);
    133	if (!imon)
    134		return -ENOMEM;
    135
    136	imon->ir_urb = usb_alloc_urb(0, GFP_KERNEL);
    137	if (!imon->ir_urb)
    138		return -ENOMEM;
    139
    140	imon->dev = &intf->dev;
    141	usb_fill_int_urb(imon->ir_urb, udev,
    142			 usb_rcvintpipe(udev, ir_ep->bEndpointAddress),
    143			 &imon->ir_buf, sizeof(imon->ir_buf),
    144			 imon_ir_rx, imon, ir_ep->bInterval);
    145
    146	rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW);
    147	if (!rcdev) {
    148		ret = -ENOMEM;
    149		goto free_urb;
    150	}
    151
    152	usb_make_path(udev, imon->phys, sizeof(imon->phys));
    153
    154	rcdev->device_name = "iMON Station";
    155	rcdev->driver_name = KBUILD_MODNAME;
    156	rcdev->input_phys = imon->phys;
    157	usb_to_input_id(udev, &rcdev->input_id);
    158	rcdev->dev.parent = &intf->dev;
    159	rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
    160	rcdev->map_name = RC_MAP_IMON_RSC;
    161	rcdev->rx_resolution = BIT_DURATION;
    162	rcdev->priv = imon;
    163
    164	ret = devm_rc_register_device(&intf->dev, rcdev);
    165	if (ret)
    166		goto free_urb;
    167
    168	imon->rcdev = rcdev;
    169
    170	ret = usb_submit_urb(imon->ir_urb, GFP_KERNEL);
    171	if (ret)
    172		goto free_urb;
    173
    174	usb_set_intfdata(intf, imon);
    175
    176	return 0;
    177
    178free_urb:
    179	usb_free_urb(imon->ir_urb);
    180	return ret;
    181}
    182
    183static void imon_disconnect(struct usb_interface *intf)
    184{
    185	struct imon *imon = usb_get_intfdata(intf);
    186
    187	usb_kill_urb(imon->ir_urb);
    188	usb_free_urb(imon->ir_urb);
    189}
    190
    191static const struct usb_device_id imon_table[] = {
    192	/* SoundGraph iMON (IR only) -- sg_imon.inf */
    193	{ USB_DEVICE(0x04e8, 0xff30) },
    194	{}
    195};
    196
    197static struct usb_driver imon_driver = {
    198	.name = KBUILD_MODNAME,
    199	.probe = imon_probe,
    200	.disconnect = imon_disconnect,
    201	.id_table = imon_table
    202};
    203
    204module_usb_driver(imon_driver);
    205
    206MODULE_DESCRIPTION("Early raw iMON IR devices");
    207MODULE_AUTHOR("Sean Young <sean@mess.org>");
    208MODULE_LICENSE("GPL");
    209MODULE_DEVICE_TABLE(usb, imon_table);