305 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// Driver for Xbox DVD Movie Playback Kit3// Copyright (c) 2018 by Benjamin Valentin <benpicco@googlemail.com>4 5/*6 * Xbox DVD Movie Playback Kit USB IR dongle support7 *8 * The driver was derived from the ati_remote driver 2.2.19 * and used information from lirc_xbox.c10 *11 * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>12 * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>13 * Copyright (c) 2002 Vladimir Dergachev14 * Copyright (c) 2003-2004 Paul Miller <pmiller9@users.sourceforge.net>15 */16 17#include <linux/slab.h>18#include <linux/module.h>19#include <linux/usb/input.h>20#include <media/rc-core.h>21 22/*23 * Module and Version Information24 */25#define DRIVER_VERSION "1.0.0"26#define DRIVER_AUTHOR "Benjamin Valentin <benpicco@googlemail.com>"27#define DRIVER_DESC "Xbox DVD USB Remote Control"28 29#define NAME_BUFSIZE 80 /* size of product name, path buffers */30#define DATA_BUFSIZE 8 /* size of URB data buffers */31 32/*33 * USB vendor ids for XBOX DVD Dongles34 */35#define VENDOR_GAMESTER 0x040b36#define VENDOR_MICROSOFT 0x045e37 38static const struct usb_device_id xbox_remote_table[] = {39 /* Gamester Xbox DVD Movie Playback Kit IR */40 {41 USB_DEVICE(VENDOR_GAMESTER, 0x6521),42 },43 /* Microsoft Xbox DVD Movie Playback Kit IR */44 {45 USB_DEVICE(VENDOR_MICROSOFT, 0x0284),46 },47 {} /* Terminating entry */48};49 50MODULE_DEVICE_TABLE(usb, xbox_remote_table);51 52struct xbox_remote {53 struct rc_dev *rdev;54 struct usb_device *udev;55 struct usb_interface *interface;56 57 struct urb *irq_urb;58 unsigned char inbuf[DATA_BUFSIZE] __aligned(sizeof(u16));59 60 char rc_name[NAME_BUFSIZE];61 char rc_phys[NAME_BUFSIZE];62};63 64static int xbox_remote_rc_open(struct rc_dev *rdev)65{66 struct xbox_remote *xbox_remote = rdev->priv;67 68 /* On first open, submit the read urb which was set up previously. */69 xbox_remote->irq_urb->dev = xbox_remote->udev;70 if (usb_submit_urb(xbox_remote->irq_urb, GFP_KERNEL)) {71 dev_err(&xbox_remote->interface->dev,72 "%s: usb_submit_urb failed!\n", __func__);73 return -EIO;74 }75 76 return 0;77}78 79static void xbox_remote_rc_close(struct rc_dev *rdev)80{81 struct xbox_remote *xbox_remote = rdev->priv;82 83 usb_kill_urb(xbox_remote->irq_urb);84}85 86/*87 * xbox_remote_report_input88 */89static void xbox_remote_input_report(struct urb *urb)90{91 struct xbox_remote *xbox_remote = urb->context;92 unsigned char *data = xbox_remote->inbuf;93 94 /*95 * data[0] = 0x0096 * data[1] = length - always 0x0697 * data[2] = the key code98 * data[3] = high part of key code99 * data[4] = last_press_ms (low)100 * data[5] = last_press_ms (high)101 */102 103 /* Deal with strange looking inputs */104 if (urb->actual_length != 6 || urb->actual_length != data[1]) {105 dev_warn(&urb->dev->dev, "Weird data, len=%d: %*ph\n",106 urb->actual_length, urb->actual_length, data);107 return;108 }109 110 rc_keydown(xbox_remote->rdev, RC_PROTO_XBOX_DVD,111 le16_to_cpup((__le16 *)(data + 2)), 0);112}113 114/*115 * xbox_remote_irq_in116 */117static void xbox_remote_irq_in(struct urb *urb)118{119 struct xbox_remote *xbox_remote = urb->context;120 int retval;121 122 switch (urb->status) {123 case 0: /* success */124 xbox_remote_input_report(urb);125 break;126 case -ECONNRESET: /* unlink */127 case -ENOENT:128 case -ESHUTDOWN:129 dev_dbg(&xbox_remote->interface->dev,130 "%s: urb error status, unlink?\n",131 __func__);132 return;133 default: /* error */134 dev_dbg(&xbox_remote->interface->dev,135 "%s: Nonzero urb status %d\n",136 __func__, urb->status);137 }138 139 retval = usb_submit_urb(urb, GFP_ATOMIC);140 if (retval)141 dev_err(&xbox_remote->interface->dev,142 "%s: usb_submit_urb()=%d\n",143 __func__, retval);144}145 146static void xbox_remote_rc_init(struct xbox_remote *xbox_remote)147{148 struct rc_dev *rdev = xbox_remote->rdev;149 150 rdev->priv = xbox_remote;151 rdev->allowed_protocols = RC_PROTO_BIT_XBOX_DVD;152 rdev->driver_name = "xbox_remote";153 154 rdev->open = xbox_remote_rc_open;155 rdev->close = xbox_remote_rc_close;156 157 rdev->device_name = xbox_remote->rc_name;158 rdev->input_phys = xbox_remote->rc_phys;159 160 rdev->timeout = MS_TO_US(10);161 162 usb_to_input_id(xbox_remote->udev, &rdev->input_id);163 rdev->dev.parent = &xbox_remote->interface->dev;164}165 166static void xbox_remote_initialize(struct xbox_remote *xbox_remote,167 struct usb_endpoint_descriptor *endpoint_in)168{169 struct usb_device *udev = xbox_remote->udev;170 int pipe, maxp;171 172 /* Set up irq_urb */173 pipe = usb_rcvintpipe(udev, endpoint_in->bEndpointAddress);174 maxp = usb_maxpacket(udev, pipe);175 maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;176 177 usb_fill_int_urb(xbox_remote->irq_urb, udev, pipe, xbox_remote->inbuf,178 maxp, xbox_remote_irq_in, xbox_remote,179 endpoint_in->bInterval);180}181 182/*183 * xbox_remote_probe184 */185static int xbox_remote_probe(struct usb_interface *interface,186 const struct usb_device_id *id)187{188 struct usb_device *udev = interface_to_usbdev(interface);189 struct usb_host_interface *iface_host = interface->cur_altsetting;190 struct usb_endpoint_descriptor *endpoint_in;191 struct xbox_remote *xbox_remote;192 struct rc_dev *rc_dev;193 int err = -ENOMEM;194 195 // why is there also a device with no endpoints?196 if (iface_host->desc.bNumEndpoints == 0)197 return -ENODEV;198 199 if (iface_host->desc.bNumEndpoints != 1) {200 pr_err("%s: Unexpected desc.bNumEndpoints: %d\n",201 __func__, iface_host->desc.bNumEndpoints);202 return -ENODEV;203 }204 205 endpoint_in = &iface_host->endpoint[0].desc;206 207 if (!usb_endpoint_is_int_in(endpoint_in)) {208 pr_err("%s: Unexpected endpoint_in\n", __func__);209 return -ENODEV;210 }211 if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {212 pr_err("%s: endpoint_in message size==0?\n", __func__);213 return -ENODEV;214 }215 216 xbox_remote = kzalloc(sizeof(*xbox_remote), GFP_KERNEL);217 rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);218 if (!xbox_remote || !rc_dev)219 goto exit_free_dev_rdev;220 221 /* Allocate URB buffer */222 xbox_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);223 if (!xbox_remote->irq_urb)224 goto exit_free_buffers;225 226 xbox_remote->udev = udev;227 xbox_remote->rdev = rc_dev;228 xbox_remote->interface = interface;229 230 usb_make_path(udev, xbox_remote->rc_phys, sizeof(xbox_remote->rc_phys));231 232 strlcat(xbox_remote->rc_phys, "/input0", sizeof(xbox_remote->rc_phys));233 234 snprintf(xbox_remote->rc_name, sizeof(xbox_remote->rc_name), "%s%s%s",235 udev->manufacturer ?: "",236 udev->manufacturer && udev->product ? " " : "",237 udev->product ?: "");238 239 if (!strlen(xbox_remote->rc_name))240 snprintf(xbox_remote->rc_name, sizeof(xbox_remote->rc_name),241 DRIVER_DESC "(%04x,%04x)",242 le16_to_cpu(xbox_remote->udev->descriptor.idVendor),243 le16_to_cpu(xbox_remote->udev->descriptor.idProduct));244 245 rc_dev->map_name = RC_MAP_XBOX_DVD; /* default map */246 247 xbox_remote_rc_init(xbox_remote);248 249 /* Device Hardware Initialization */250 xbox_remote_initialize(xbox_remote, endpoint_in);251 252 /* Set up and register rc device */253 err = rc_register_device(xbox_remote->rdev);254 if (err)255 goto exit_kill_urbs;256 257 usb_set_intfdata(interface, xbox_remote);258 259 return 0;260 261exit_kill_urbs:262 usb_kill_urb(xbox_remote->irq_urb);263exit_free_buffers:264 usb_free_urb(xbox_remote->irq_urb);265exit_free_dev_rdev:266 rc_free_device(rc_dev);267 kfree(xbox_remote);268 269 return err;270}271 272/*273 * xbox_remote_disconnect274 */275static void xbox_remote_disconnect(struct usb_interface *interface)276{277 struct xbox_remote *xbox_remote;278 279 xbox_remote = usb_get_intfdata(interface);280 usb_set_intfdata(interface, NULL);281 if (!xbox_remote) {282 dev_warn(&interface->dev, "%s - null device?\n", __func__);283 return;284 }285 286 usb_kill_urb(xbox_remote->irq_urb);287 rc_unregister_device(xbox_remote->rdev);288 usb_free_urb(xbox_remote->irq_urb);289 kfree(xbox_remote);290}291 292/* usb specific object to register with the usb subsystem */293static struct usb_driver xbox_remote_driver = {294 .name = "xbox_remote",295 .probe = xbox_remote_probe,296 .disconnect = xbox_remote_disconnect,297 .id_table = xbox_remote_table,298};299 300module_usb_driver(xbox_remote_driver);301 302MODULE_AUTHOR(DRIVER_AUTHOR);303MODULE_DESCRIPTION(DRIVER_DESC);304MODULE_LICENSE("GPL");305