aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2018-01-05 19:58:51 +0000
committerSean Young <sean@mess.org>2018-01-14 20:15:40 +0000
commitbd4ca2f02f47233ca29f18dd2731b0d64d2d5e62 (patch)
tree8678591f26185581e62b2128222266ce64d97b11
parent24334ed68ad530e569fad1d10b95f50391182b94 (diff)
media: rc: new driver for early iMon devices
These devices were supported by the lirc_imon.c driver which was removed from staging in commit. Signed-off-by: Sean Young <sean@mess.org>
-rw-r--r--drivers/media/rc/Kconfig13
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/imon_raw.c221
3 files changed, 235 insertions, 0 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 0f863822889e..7919f4a36ad2 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -185,6 +185,19 @@ config IR_IMON
To compile this driver as a module, choose M here: the
module will be called imon.
+config IR_IMON_RAW
+ tristate "SoundGraph iMON Receiver and Display (early raw IR models)"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a SoundGraph iMON (aka Antec Veris)
+ IR Receiver and/or LCD/VFD/VGA display.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imon_raw.
+
+
config IR_MCEUSB
tristate "Windows Media Center Ed. eHome Infrared Transceiver"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 0e857816ac2d..e098e127b26a 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o
obj-$(CONFIG_IR_IMON) += imon.o
+obj-$(CONFIG_IR_IMON_RAW) += imon_raw.o
obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c
new file mode 100644
index 000000000000..4adb1fd3b05b
--- /dev/null
+++ b/drivers/media/rc/imon_raw.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#define FLAG_IRONLY 1
+#define FLAG_VFD_6P 2
+
+/* Each bit is 250us */
+#define BIT_DURATION 250000
+
+struct imon {
+ struct device *dev;
+ struct usb_device *usbdev;
+ struct urb *ir_urb;
+ struct rc_dev *rcdev;
+ u8 ir_buf[8];
+ bool vfd_proto_6p;
+ char phys[64];
+};
+
+/*
+ * ffs/find_next_bit() searches in the wrong direction, so open-code our own.
+ */
+static int is_bit_set(u8 *buf, int bit)
+{
+ return buf[bit / 8] & (0x80 >> (bit & 7));
+}
+
+static void imon_ir_data(struct imon *imon)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ int offset = 0, size = 5 * 8;
+ int bit;
+
+ dev_dbg(imon->dev, "data: %*ph", 8, imon->ir_buf);
+
+ while (offset < size) {
+ bit = offset;
+ while (!is_bit_set(imon->ir_buf, bit) && bit < size)
+ bit++;
+ dev_dbg(imon->dev, "zero: %d", bit - offset);
+ if (bit > offset) {
+ rawir.pulse = true;
+ rawir.duration = (bit - offset) * BIT_DURATION;
+ ir_raw_event_store_with_filter(imon->rcdev, &rawir);
+ }
+
+ if (bit >= size)
+ break;
+
+ offset = bit;
+ while (is_bit_set(imon->ir_buf, bit) && bit < size)
+ bit++;
+ dev_dbg(imon->dev, "set: %d", bit - offset);
+
+ rawir.pulse = false;
+ rawir.duration = (bit - offset) * BIT_DURATION;
+ ir_raw_event_store_with_filter(imon->rcdev, &rawir);
+
+ offset = bit;
+ }
+
+ if (imon->ir_buf[7] == 0x0a) {
+ ir_raw_event_set_idle(imon->rcdev, true);
+ ir_raw_event_handle(imon->rcdev);
+ }
+}
+
+static void imon_ir_rx(struct urb *urb)
+{
+ struct imon *imon = urb->context;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ if (imon->ir_buf[7] != 0xff)
+ imon_ir_data(imon);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(imon->dev, "error: urb status = %d", urb->status);
+ break;
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret && ret != -ENODEV)
+ dev_warn(imon->dev, "failed to resubmit urb: %d", ret);
+}
+
+static int imon_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ir_ep = NULL;
+ struct usb_endpoint_descriptor *vfd_ep = NULL;
+ struct usb_host_interface *idesc;
+ struct usb_device *udev;
+ struct rc_dev *rcdev;
+ struct imon *imon;
+ int i, ret;
+
+ udev = interface_to_usbdev(intf);
+ idesc = intf->cur_altsetting;
+
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
+ struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(ep) && !ir_ep)
+ ir_ep = ep;
+ else if (usb_endpoint_is_int_out(ep) && !vfd_ep)
+ vfd_ep = ep;
+ }
+
+ if (!ir_ep) {
+ dev_err(&intf->dev, "IR endpoint missing");
+ return -ENODEV;
+ }
+
+ if ((id->driver_info & FLAG_IRONLY) &&
+ idesc->desc.bInterfaceNumber != 0)
+ vfd_ep = NULL;
+
+ imon = devm_kzalloc(&intf->dev, sizeof(*imon), GFP_KERNEL);
+ if (!imon)
+ return -ENOMEM;
+
+ imon->ir_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!imon->ir_urb)
+ return -ENOMEM;
+
+ imon->dev = &intf->dev;
+ imon->usbdev = udev;
+ usb_fill_int_urb(imon->ir_urb, udev,
+ usb_rcvintpipe(udev, ir_ep->bEndpointAddress),
+ imon->ir_buf, sizeof(imon->ir_buf),
+ imon_ir_rx, imon, ir_ep->bInterval);
+
+ rcdev = devm_rc_allocate_device(&intf->dev, RC_DRIVER_IR_RAW);
+ if (!rcdev)
+ return -ENOMEM;
+
+ usb_make_path(udev, imon->phys, sizeof(imon->phys));
+
+ rcdev->device_name = "iMON raw Remote";
+ rcdev->driver_name = KBUILD_MODNAME;
+ rcdev->input_phys = imon->phys;
+ usb_to_input_id(udev, &rcdev->input_id);
+ rcdev->dev.parent = &intf->dev;
+ rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+ rcdev->map_name = RC_MAP_RC6_MCE;
+ rcdev->rx_resolution = BIT_DURATION;
+ rcdev->priv = imon;
+
+ ret = devm_rc_register_device(&intf->dev, rcdev);
+ if (ret)
+ return ret;
+
+ imon->rcdev = rcdev;
+
+ ret = usb_submit_urb(imon->ir_urb, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ if (id->driver_info & FLAG_VFD_6P)
+ imon->vfd_proto_6p = true;
+
+ if (ep_vfd) {
+ // register vfd
+ }
+
+ usb_set_intfdata(intf, imon);
+
+ return 0;
+}
+
+static void imon_disconnect(struct usb_interface *intf)
+{
+ struct imon *imon = usb_get_intfdata(intf);
+
+ usb_kill_urb(imon->ir_urb);
+ usb_free_urb(imon->ir_urb);
+}
+
+static const struct usb_device_id imon_table[] = {
+ /* TriGem iMON (IR only) -- TG_iMON.inf */
+ { USB_DEVICE(0x0aa8, 0x8001), .driver_info = FLAG_IRONLY },
+
+ /* SoundGraph iMON (IR only) -- sg_imon.inf */
+ { USB_DEVICE(0x04e8, 0xff30), .driver_info = FLAG_IRONLY },
+
+ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
+ { USB_DEVICE(0x0aa8, 0xffda), .driver_info = FLAG_VFD_6P },
+
+ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
+ { USB_DEVICE(0x15c2, 0xffda) },
+
+ {}
+};
+
+static struct usb_driver imon_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = imon_probe,
+ .disconnect = imon_disconnect,
+ .id_table = imon_table
+};
+
+module_usb_driver(imon_driver);
+
+MODULE_DESCRIPTION("Early iMON raw IR devices");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_table);

Privacy Policy