aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/rc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/rc')
-rw-r--r--drivers/media/rc/Kconfig12
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/imon.c4
-rw-r--r--drivers/media/rc/imon_raw.c47
-rw-r--r--drivers/media/rc/keymaps/Makefile1
-rw-r--r--drivers/media/rc/keymaps/rc-xbox-dvd.c63
-rw-r--r--drivers/media/rc/mceusb.c9
-rw-r--r--drivers/media/rc/rc-main.c8
-rw-r--r--drivers/media/rc/xbox_remote.c306
9 files changed, 423 insertions, 28 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 1021c08a9ba4..8a216068a35a 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -493,6 +493,18 @@ config IR_TANGO
The HW decoder supports NEC, RC-5, RC-6 IR protocols.
When compiled as a module, look for tango-ir.
+config RC_XBOX_DVD
+ tristate "Xbox DVD Movie Playback Kit"
+ depends on RC_CORE
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use the Xbox DVD Movie Playback Kit.
+ These are IR remotes with USB receivers for the Original Xbox (2001).
+
+ To compile this driver as a module, choose M here: the module will be
+ called xbox_remote.
+
config IR_ZX
tristate "ZTE ZX IR remote control"
depends on RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index e0340d043fe8..92c163816849 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -48,3 +48,4 @@ obj-$(CONFIG_IR_SIR) += sir_ir.o
obj-$(CONFIG_IR_MTK) += mtk-cir.o
obj-$(CONFIG_IR_ZX) += zx-irdec.o
obj-$(CONFIG_IR_TANGO) += tango-ir.o
+obj-$(CONFIG_RC_XBOX_DVD) += xbox_remote.o
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 1041c056854d..989d2554ec72 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -772,9 +772,9 @@ static ssize_t show_associate_remote(struct device *d,
mutex_lock(&ictx->lock);
if (ictx->rf_isassociating)
- strcpy(buf, "associating\n");
+ strscpy(buf, "associating\n", PAGE_SIZE);
else
- strcpy(buf, "closed\n");
+ strscpy(buf, "closed\n", PAGE_SIZE);
dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n");
mutex_unlock(&ictx->lock);
diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c
index 7796098d9c30..25e56c5b13c0 100644
--- a/drivers/media/rc/imon_raw.c
+++ b/drivers/media/rc/imon_raw.c
@@ -14,51 +14,50 @@ struct imon {
struct device *dev;
struct urb *ir_urb;
struct rc_dev *rcdev;
- u8 ir_buf[8];
+ u8 ir_buf[8] __aligned(__alignof__(u64));
char phys[64];
};
/*
- * ffs/find_next_bit() searches in the wrong direction, so open-code our own.
+ * The first 5 bytes of data represent IR pulse or space. Each bit, starting
+ * from highest bit in the first byte, represents 250µs of data. It is 1
+ * for space and 0 for pulse.
+ *
+ * The station sends 10 packets, and the 7th byte will be number 1 to 10, so
+ * when we receive 10 we assume all the data has arrived.
*/
-static inline int is_bit_set(const u8 *buf, int bit)
-{
- return buf[bit / 8] & (0x80 >> (bit & 7));
-}
-
static void imon_ir_data(struct imon *imon)
{
struct ir_raw_event rawir = {};
- int offset = 0, size = 5 * 8;
+ u64 d = be64_to_cpup((__be64 *)imon->ir_buf) >> 24;
+ int offset = 40;
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, "pulse: %d bits", bit - offset);
- if (bit > offset) {
+ do {
+ bit = fls64(d & (BIT_ULL(offset) - 1));
+ if (bit < offset) {
+ dev_dbg(imon->dev, "pulse: %d bits", offset - bit);
rawir.pulse = true;
- rawir.duration = (bit - offset) * BIT_DURATION;
+ rawir.duration = (offset - bit) * BIT_DURATION;
ir_raw_event_store_with_filter(imon->rcdev, &rawir);
- }
- if (bit >= size)
- break;
+ if (bit == 0)
+ break;
- offset = bit;
- while (is_bit_set(imon->ir_buf, bit) && bit < size)
- bit++;
- dev_dbg(imon->dev, "space: %d bits", bit - offset);
+ offset = bit;
+ }
+
+ bit = fls64(~d & (BIT_ULL(offset) - 1));
+ dev_dbg(imon->dev, "space: %d bits", offset - bit);
rawir.pulse = false;
- rawir.duration = (bit - offset) * BIT_DURATION;
+ rawir.duration = (offset - bit) * BIT_DURATION;
ir_raw_event_store_with_filter(imon->rcdev, &rawir);
offset = bit;
- }
+ } while (offset > 0);
if (imon->ir_buf[7] == 0x0a) {
ir_raw_event_set_idle(imon->rcdev, true);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index d6b913a3032d..5b1399af6b3a 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -116,4 +116,5 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-winfast.o \
rc-winfast-usbii-deluxe.o \
rc-su3000.o \
+ rc-xbox-dvd.o \
rc-zx-irdec.o
diff --git a/drivers/media/rc/keymaps/rc-xbox-dvd.c b/drivers/media/rc/keymaps/rc-xbox-dvd.c
new file mode 100644
index 000000000000..af387244636b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-xbox-dvd.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Keytable for Xbox DVD remote
+// Copyright (c) 2018 by Benjamin Valentin <benpicco@googlemail.com>
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* based on lircd.conf.xbox */
+static struct rc_map_table xbox_dvd[] = {
+ {0xa0b, KEY_OK},
+ {0xaa6, KEY_UP},
+ {0xaa7, KEY_DOWN},
+ {0xaa8, KEY_RIGHT},
+ {0xaa9, KEY_LEFT},
+ {0xac3, KEY_INFO},
+
+ {0xac6, KEY_9},
+ {0xac7, KEY_8},
+ {0xac8, KEY_7},
+ {0xac9, KEY_6},
+ {0xaca, KEY_5},
+ {0xacb, KEY_4},
+ {0xacc, KEY_3},
+ {0xacd, KEY_2},
+ {0xace, KEY_1},
+ {0xacf, KEY_0},
+
+ {0xad5, KEY_ANGLE},
+ {0xad8, KEY_BACK},
+ {0xadd, KEY_PREVIOUSSONG},
+ {0xadf, KEY_NEXTSONG},
+ {0xae0, KEY_STOP},
+ {0xae2, KEY_REWIND},
+ {0xae3, KEY_FASTFORWARD},
+ {0xae5, KEY_TITLE},
+ {0xae6, KEY_PAUSE},
+ {0xaea, KEY_PLAY},
+ {0xaf7, KEY_MENU},
+};
+
+static struct rc_map_list xbox_dvd_map = {
+ .map = {
+ .scan = xbox_dvd,
+ .size = ARRAY_SIZE(xbox_dvd),
+ .rc_proto = RC_PROTO_UNKNOWN,
+ .name = RC_MAP_XBOX_DVD,
+ }
+};
+
+static int __init init_rc_map(void)
+{
+ return rc_map_register(&xbox_dvd_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+ rc_map_unregister(&xbox_dvd_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index c9293696dc2d..8d7d3ef88862 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -432,6 +432,15 @@ static const struct usb_device_id mceusb_dev_table[] = {
.driver_info = HAUPPAUGE_CX_HYBRID_TV },
{ USB_DEVICE(VENDOR_HAUPPAUGE, 0xb139),
.driver_info = HAUPPAUGE_CX_HYBRID_TV },
+ /* Hauppauge WinTV-HVR-935C - based on cx231xx */
+ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb151),
+ .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+ /* Hauppauge WinTV-HVR-955Q - based on cx231xx */
+ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb123),
+ .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+ /* Hauppauge WinTV-HVR-975 - based on cx231xx */
+ { USB_DEVICE(VENDOR_HAUPPAUGE, 0xb150),
+ .driver_info = HAUPPAUGE_CX_HYBRID_TV },
{ USB_DEVICE(VENDOR_PCTV, 0x0259),
.driver_info = HAUPPAUGE_CX_HYBRID_TV },
{ USB_DEVICE(VENDOR_PCTV, 0x025e),
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 552bbe82a160..66a174979b3c 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -695,7 +695,8 @@ void rc_repeat(struct rc_dev *dev)
(dev->last_toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0)
};
- ir_lirc_scancode_event(dev, &sc);
+ if (dev->allowed_protocols != RC_PROTO_BIT_CEC)
+ ir_lirc_scancode_event(dev, &sc);
spin_lock_irqsave(&dev->keylock, flags);
@@ -735,7 +736,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol,
.keycode = keycode
};
- ir_lirc_scancode_event(dev, &sc);
+ if (dev->allowed_protocols != RC_PROTO_BIT_CEC)
+ ir_lirc_scancode_event(dev, &sc);
if (new_event && dev->keypressed)
ir_do_keyup(dev, false);
@@ -1950,6 +1952,8 @@ void rc_unregister_device(struct rc_dev *dev)
rc_free_rx_device(dev);
mutex_lock(&dev->lock);
+ if (dev->users && dev->close)
+ dev->close(dev);
dev->registered = false;
mutex_unlock(&dev->lock);
diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c
new file mode 100644
index 000000000000..f959cbb94744
--- /dev/null
+++ b/drivers/media/rc/xbox_remote.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Driver for Xbox DVD Movie Playback Kit
+// Copyright (c) 2018 by Benjamin Valentin <benpicco@googlemail.com>
+
+/*
+ * Xbox DVD Movie Playback Kit USB IR dongle support
+ *
+ * The driver was derived from the ati_remote driver 2.2.1
+ * and used information from lirc_xbox.c
+ *
+ * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Copyright (c) 2002 Vladimir Dergachev
+ * Copyright (c) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+/*
+ * Module and Version Information
+ */
+#define DRIVER_VERSION "1.0.0"
+#define DRIVER_AUTHOR "Benjamin Valentin <benpicco@googlemail.com>"
+#define DRIVER_DESC "Xbox DVD USB Remote Control"
+
+#define NAME_BUFSIZE 80 /* size of product name, path buffers */
+#define DATA_BUFSIZE 8 /* size of URB data buffers */
+
+/*
+ * USB vendor ids for XBOX DVD Dongles
+ */
+#define VENDOR_GAMESTER 0x040b
+#define VENDOR_MICROSOFT 0x045e
+
+static const struct usb_device_id xbox_remote_table[] = {
+ /* Gamester Xbox DVD Movie Playback Kit IR */
+ {
+ USB_DEVICE(VENDOR_GAMESTER, 0x6521),
+ },
+ /* Microsoft Xbox DVD Movie Playback Kit IR */
+ {
+ USB_DEVICE(VENDOR_MICROSOFT, 0x0284),
+ },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, xbox_remote_table);
+
+struct xbox_remote {
+ struct rc_dev *rdev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *irq_urb;
+ unsigned char inbuf[DATA_BUFSIZE] __aligned(sizeof(u16));
+
+ char rc_name[NAME_BUFSIZE];
+ char rc_phys[NAME_BUFSIZE];
+};
+
+static int xbox_remote_rc_open(struct rc_dev *rdev)
+{
+ struct xbox_remote *xbox_remote = rdev->priv;
+
+ /* On first open, submit the read urb which was set up previously. */
+ xbox_remote->irq_urb->dev = xbox_remote->udev;
+ if (usb_submit_urb(xbox_remote->irq_urb, GFP_KERNEL)) {
+ dev_err(&xbox_remote->interface->dev,
+ "%s: usb_submit_urb failed!\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void xbox_remote_rc_close(struct rc_dev *rdev)
+{
+ struct xbox_remote *xbox_remote = rdev->priv;
+
+ usb_kill_urb(xbox_remote->irq_urb);
+}
+
+/*
+ * xbox_remote_report_input
+ */
+static void xbox_remote_input_report(struct urb *urb)
+{
+ struct xbox_remote *xbox_remote = urb->context;
+ unsigned char *data = xbox_remote->inbuf;
+
+ /*
+ * data[0] = 0x00
+ * data[1] = length - always 0x06
+ * data[2] = the key code
+ * data[3] = high part of key code
+ * data[4] = last_press_ms (low)
+ * data[5] = last_press_ms (high)
+ */
+
+ /* Deal with strange looking inputs */
+ if (urb->actual_length != 6 || urb->actual_length != data[1]) {
+ dev_warn(&urb->dev->dev, "Weird data, len=%d: %*ph\n",
+ urb->actual_length, urb->actual_length, data);
+ return;
+ }
+
+ rc_keydown(xbox_remote->rdev, RC_PROTO_UNKNOWN,
+ le16_to_cpup((__le16 *)(data + 2)), 0);
+}
+
+/*
+ * xbox_remote_irq_in
+ */
+static void xbox_remote_irq_in(struct urb *urb)
+{
+ struct xbox_remote *xbox_remote = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0: /* success */
+ xbox_remote_input_report(urb);
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&xbox_remote->interface->dev,
+ "%s: urb error status, unlink?\n",
+ __func__);
+ return;
+ default: /* error */
+ dev_dbg(&xbox_remote->interface->dev,
+ "%s: Nonzero urb status %d\n",
+ __func__, urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&xbox_remote->interface->dev,
+ "%s: usb_submit_urb()=%d\n",
+ __func__, retval);
+}
+
+static void xbox_remote_rc_init(struct xbox_remote *xbox_remote)
+{
+ struct rc_dev *rdev = xbox_remote->rdev;
+
+ rdev->priv = xbox_remote;
+ rdev->allowed_protocols = RC_PROTO_BIT_UNKNOWN;
+ rdev->driver_name = "xbox_remote";
+
+ rdev->open = xbox_remote_rc_open;
+ rdev->close = xbox_remote_rc_close;
+
+ rdev->device_name = xbox_remote->rc_name;
+ rdev->input_phys = xbox_remote->rc_phys;
+
+ usb_to_input_id(xbox_remote->udev, &rdev->input_id);
+ rdev->dev.parent = &xbox_remote->interface->dev;
+}
+
+static int xbox_remote_initialize(struct xbox_remote *xbox_remote,
+ struct usb_endpoint_descriptor *endpoint_in)
+{
+ struct usb_device *udev = xbox_remote->udev;
+ int pipe, maxp;
+
+ /* Set up irq_urb */
+ pipe = usb_rcvintpipe(udev, endpoint_in->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(xbox_remote->irq_urb, udev, pipe, xbox_remote->inbuf,
+ maxp, xbox_remote_irq_in, xbox_remote,
+ endpoint_in->bInterval);
+
+ return 0;
+}
+
+/*
+ * xbox_remote_probe
+ */
+static int xbox_remote_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_host = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint_in;
+ struct xbox_remote *xbox_remote;
+ struct rc_dev *rc_dev;
+ int err = -ENOMEM;
+
+ // why is there also a device with no endpoints?
+ if (iface_host->desc.bNumEndpoints == 0)
+ return -ENODEV;
+
+ if (iface_host->desc.bNumEndpoints != 1) {
+ pr_err("%s: Unexpected desc.bNumEndpoints: %d\n",
+ __func__, iface_host->desc.bNumEndpoints);
+ return -ENODEV;
+ }
+
+ endpoint_in = &iface_host->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ pr_err("%s: Unexpected endpoint_in\n", __func__);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
+ pr_err("%s: endpoint_in message size==0?\n", __func__);
+ return -ENODEV;
+ }
+
+ xbox_remote = kzalloc(sizeof(*xbox_remote), GFP_KERNEL);
+ rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
+ if (!xbox_remote || !rc_dev)
+ goto exit_free_dev_rdev;
+
+ /* Allocate URB buffer */
+ xbox_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xbox_remote->irq_urb)
+ goto exit_free_buffers;
+
+ xbox_remote->udev = udev;
+ xbox_remote->rdev = rc_dev;
+ xbox_remote->interface = interface;
+
+ usb_make_path(udev, xbox_remote->rc_phys, sizeof(xbox_remote->rc_phys));
+
+ strlcat(xbox_remote->rc_phys, "/input0", sizeof(xbox_remote->rc_phys));
+
+ snprintf(xbox_remote->rc_name, sizeof(xbox_remote->rc_name), "%s%s%s",
+ udev->manufacturer ?: "",
+ udev->manufacturer && udev->product ? " " : "",
+ udev->product ?: "");
+
+ if (!strlen(xbox_remote->rc_name))
+ snprintf(xbox_remote->rc_name, sizeof(xbox_remote->rc_name),
+ DRIVER_DESC "(%04x,%04x)",
+ le16_to_cpu(xbox_remote->udev->descriptor.idVendor),
+ le16_to_cpu(xbox_remote->udev->descriptor.idProduct));
+
+ rc_dev->map_name = RC_MAP_XBOX_DVD; /* default map */
+
+ xbox_remote_rc_init(xbox_remote);
+
+ /* Device Hardware Initialization */
+ err = xbox_remote_initialize(xbox_remote, endpoint_in);
+ if (err)
+ goto exit_kill_urbs;
+
+ /* Set up and register rc device */
+ err = rc_register_device(xbox_remote->rdev);
+ if (err)
+ goto exit_kill_urbs;
+
+ usb_set_intfdata(interface, xbox_remote);
+
+ return 0;
+
+exit_kill_urbs:
+ usb_kill_urb(xbox_remote->irq_urb);
+exit_free_buffers:
+ usb_free_urb(xbox_remote->irq_urb);
+exit_free_dev_rdev:
+ rc_free_device(rc_dev);
+ kfree(xbox_remote);
+
+ return err;
+}
+
+/*
+ * xbox_remote_disconnect
+ */
+static void xbox_remote_disconnect(struct usb_interface *interface)
+{
+ struct xbox_remote *xbox_remote;
+
+ xbox_remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!xbox_remote) {
+ dev_warn(&interface->dev, "%s - null device?\n", __func__);
+ return;
+ }
+
+ usb_kill_urb(xbox_remote->irq_urb);
+ rc_unregister_device(xbox_remote->rdev);
+ usb_free_urb(xbox_remote->irq_urb);
+ kfree(xbox_remote);
+}
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver xbox_remote_driver = {
+ .name = "xbox_remote",
+ .probe = xbox_remote_probe,
+ .disconnect = xbox_remote_disconnect,
+ .id_table = xbox_remote_table,
+};
+
+module_usb_driver(xbox_remote_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");