linux/drivers/media/rc/imon_raw.c
<<
>>
Prefs
   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\xC2\xB5s 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);
 210