1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Driver for Xbox DVD Movie Playback Kit |
3 | // Copyright (c) 2018 by Benjamin Valentin <benpicco@googlemail.com> |
4 | |
5 | /* |
6 | * Xbox DVD Movie Playback Kit USB IR dongle support |
7 | * |
8 | * The driver was derived from the ati_remote driver 2.2.1 |
9 | * and used information from lirc_xbox.c |
10 | * |
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 Dergachev |
14 | * 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 Information |
24 | */ |
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 Dongles |
34 | */ |
35 | #define VENDOR_GAMESTER 0x040b |
36 | #define VENDOR_MICROSOFT 0x045e |
37 | |
38 | static 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 | |
50 | MODULE_DEVICE_TABLE(usb, xbox_remote_table); |
51 | |
52 | struct 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 | |
64 | static 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(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 | |
79 | static void xbox_remote_rc_close(struct rc_dev *rdev) |
80 | { |
81 | struct xbox_remote *xbox_remote = rdev->priv; |
82 | |
83 | usb_kill_urb(urb: xbox_remote->irq_urb); |
84 | } |
85 | |
86 | /* |
87 | * xbox_remote_report_input |
88 | */ |
89 | static 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] = 0x00 |
96 | * data[1] = length - always 0x06 |
97 | * data[2] = the key code |
98 | * data[3] = high part of key code |
99 | * 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(dev: xbox_remote->rdev, protocol: RC_PROTO_XBOX_DVD, |
111 | le16_to_cpup(p: (__le16 *)(data + 2)), toggle: 0); |
112 | } |
113 | |
114 | /* |
115 | * xbox_remote_irq_in |
116 | */ |
117 | static 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 | |
146 | static 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(dev: xbox_remote->udev, id: &rdev->input_id); |
163 | rdev->dev.parent = &xbox_remote->interface->dev; |
164 | } |
165 | |
166 | static 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(urb: xbox_remote->irq_urb, dev: udev, pipe, transfer_buffer: xbox_remote->inbuf, |
178 | buffer_length: maxp, complete_fn: xbox_remote_irq_in, context: xbox_remote, |
179 | interval: endpoint_in->bInterval); |
180 | } |
181 | |
182 | /* |
183 | * xbox_remote_probe |
184 | */ |
185 | static 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(epd: 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(size: 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(iso_packets: 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(dev: udev, buf: xbox_remote->rc_phys, size: sizeof(xbox_remote->rc_phys)); |
231 | |
232 | strlcat(p: xbox_remote->rc_phys, q: "/input0" , avail: sizeof(xbox_remote->rc_phys)); |
233 | |
234 | snprintf(buf: xbox_remote->rc_name, size: sizeof(xbox_remote->rc_name), fmt: "%s%s%s" , |
235 | udev->manufacturer ?: "" , |
236 | udev->manufacturer && udev->product ? " " : "" , |
237 | udev->product ?: "" ); |
238 | |
239 | if (!strlen(xbox_remote->rc_name)) |
240 | snprintf(buf: xbox_remote->rc_name, size: 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(dev: xbox_remote->rdev); |
254 | if (err) |
255 | goto exit_kill_urbs; |
256 | |
257 | usb_set_intfdata(intf: interface, data: xbox_remote); |
258 | |
259 | return 0; |
260 | |
261 | exit_kill_urbs: |
262 | usb_kill_urb(urb: xbox_remote->irq_urb); |
263 | exit_free_buffers: |
264 | usb_free_urb(urb: xbox_remote->irq_urb); |
265 | exit_free_dev_rdev: |
266 | rc_free_device(dev: rc_dev); |
267 | kfree(objp: xbox_remote); |
268 | |
269 | return err; |
270 | } |
271 | |
272 | /* |
273 | * xbox_remote_disconnect |
274 | */ |
275 | static void xbox_remote_disconnect(struct usb_interface *interface) |
276 | { |
277 | struct xbox_remote *xbox_remote; |
278 | |
279 | xbox_remote = usb_get_intfdata(intf: interface); |
280 | usb_set_intfdata(intf: interface, NULL); |
281 | if (!xbox_remote) { |
282 | dev_warn(&interface->dev, "%s - null device?\n" , __func__); |
283 | return; |
284 | } |
285 | |
286 | usb_kill_urb(urb: xbox_remote->irq_urb); |
287 | rc_unregister_device(dev: xbox_remote->rdev); |
288 | usb_free_urb(urb: xbox_remote->irq_urb); |
289 | kfree(objp: xbox_remote); |
290 | } |
291 | |
292 | /* usb specific object to register with the usb subsystem */ |
293 | static 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 | |
300 | module_usb_driver(xbox_remote_driver); |
301 | |
302 | MODULE_AUTHOR(DRIVER_AUTHOR); |
303 | MODULE_DESCRIPTION(DRIVER_DESC); |
304 | MODULE_LICENSE("GPL" ); |
305 | |