diff options
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/bpf-lirc.c | 24 | ||||
-rw-r--r-- | drivers/media/rc/imon.c | 4 | ||||
-rw-r--r-- | drivers/media/rc/imon_raw.c | 47 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-xbox-dvd.c | 63 | ||||
-rw-r--r-- | drivers/media/rc/mceusb.c | 9 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 8 | ||||
-rw-r--r-- | drivers/media/rc/xbox_remote.c | 306 |
10 files changed, 447 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/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c index 8b97fd1f0cea..390a722e6211 100644 --- a/drivers/media/rc/bpf-lirc.c +++ b/drivers/media/rc/bpf-lirc.c @@ -59,6 +59,28 @@ static const struct bpf_func_proto rc_keydown_proto = { .arg4_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_rc_pointer_rel, u32*, sample, s32, rel_x, s32, rel_y) +{ + struct ir_raw_event_ctrl *ctrl; + + ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample); + + input_report_rel(ctrl->dev->input_dev, REL_X, rel_x); + input_report_rel(ctrl->dev->input_dev, REL_Y, rel_y); + input_sync(ctrl->dev->input_dev); + + return 0; +} + +static const struct bpf_func_proto rc_pointer_rel_proto = { + .func = bpf_rc_pointer_rel, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + static const struct bpf_func_proto * lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { @@ -67,6 +89,8 @@ lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &rc_repeat_proto; case BPF_FUNC_rc_keydown: return &rc_keydown_proto; + case BPF_FUNC_rc_pointer_rel: + return &rc_pointer_rel_proto; case BPF_FUNC_map_lookup_elem: return &bpf_map_lookup_elem_proto; case BPF_FUNC_map_update_elem: 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"); |