2 * Copyright (c) 2010, AaYJFG - XBMC community
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the XBMC community nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL AaYJFG BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
39 #include <libusb-1.0/libusb.h>
41 #include "xbmcclient.h"
45 #define msg(x...) do { fprintf(stdout, x); } while (0)
46 #define err(x...) do { fprintf(stderr, x); } while (0)
52 err("*** POSIX ERROR *** in %s:%d [%s]: ", __FILE__, __LINE__, #x); \
62 err("*** USB ERROR *** in %s:%d [%s]: %d\n", __FILE__, __LINE__, #x, __err); \
66 static CXBMCClient xbmc
;
67 static bool disconnected
= false;
68 static bool quit
= false;
71 static void print_ir_code(const char *prefix
, struct libusb_transfer
*transfer
)
73 msg("** %s : ", prefix
);
74 for (int i
= 0; i
< transfer
->actual_length
; i
++)
75 msg("%02x ", transfer
->buffer
[i
] & 0xff);
80 static void xbmc_action (const char *action)
82 msg("## send action '%s'\n", action);
83 xbmc.SendACTION(action);
86 static void xbmc_action_button (const char *btn
)
88 msg("## send action '%s' (button)\n", btn
);
89 xbmc
.SendACTION(btn
, ACTION_BUTTON
);
92 static void xbmc_key (const char *key
)
94 msg("## send key (R1) '%s'\n", key
);
95 xbmc
.SendButton(key
, "R1", BTN_NO_REPEAT
);
98 static void xbmc_keyr(const char *key
)
100 msg("## send repeat key (R1) '%s'\n", key
);
101 xbmc
.SendButton(key
, "R1", BTN_DOWN
);
104 static void xbmc_key_kb (const char *key)
106 msg("## send key (KB) '%s'\n", key);
107 xbmc.SendButton(key, "KB", BTN_NO_REPEAT);
110 static void xbmc_keyr_kb(const char *key)
112 msg("## send repeat key (KB) '%s'\n", key);
113 xbmc.SendButton(key, "KB", BTN_DOWN);
116 static void xbmc_release_button()
118 msg("## send key released\n");
119 xbmc
.SendButton(0x01, BTN_UP
);
122 static void emit_left_click (void)
124 msg("## send left click\n");
125 xbmc_action_button("select");
128 static void emit_right_click (void)
130 msg("## send right click\n");
131 xbmc_action_button("contextmenu");
134 static void emit_info(void)
136 msg("## send info click\n");
140 static void emit_mouse (int dx
, int dy
)
142 if (dx
!= 0 || dy
!= 0) {
143 const int maxcoord
= 65535;
144 static int x
= maxcoord
/ 2;
145 static int y
= maxcoord
/ 2;
156 msg("## mouse dx %d, dy %d -- x %d y %d\n", dx
, dy
, x
, y
);
157 xbmc
.SendMOUSE(x
, y
);
161 static void transfer0x81_cb (struct libusb_transfer
*transfer
)
163 static const struct {
167 void (*key_fn
) (const char *key
);
169 } translation_table
[] = {
170 { 0x03, 0x17, false, xbmc_key
, "mytv" }, /* (yellow) TV */
171 { 0x01, 0x10, false, xbmc_key
, "mymusic" }, /* (blue) Music */
172 { 0x01, 0x0c, false, xbmc_key
, "mypictures" }, /* (green) Pictures */
173 { 0x01, 0x08, false, xbmc_key
, "myvideo" }, /* (red) Videos */
174 { 0x01, 0x12, false, xbmc_key
, "recordedtv" },
175 { 0x01, 0x0a, false, xbmc_key
, "guide" },
176 { 0x01, 0x17, false, xbmc_key
, "livetv" },
177 { 0x03, 0x10, false, xbmc_key
, "menu" },
178 { 0x03, 0x05, false, xbmc_key
, "reverse" },
179 { 0x03, 0x09, false, xbmc_key
, "forward" },
180 { 0x01, 0x15, false, xbmc_key
, "record" },
181 { 0x00, 0x2a, false, xbmc_key
, "back" },
182 { 0x00, 0x28, false, xbmc_key
, "select" },
183 { 0x00, 0x4f, true, xbmc_keyr
, "right" },
184 { 0x00, 0x50, true, xbmc_keyr
, "left" },
185 { 0x00, 0x51, true, xbmc_keyr
, "down" },
186 { 0x00, 0x52, true, xbmc_keyr
, "up" },
187 { 0x00, 0x4b, true, xbmc_keyr
, "pageplus" },
188 { 0x00, 0x4e, true, xbmc_keyr
, "pageminus" },
189 { 0x0c, 0x28, false, xbmc_key
, "start" },
190 { 0x00, 0x59, false, xbmc_key
, "one" },
191 { 0x00, 0x5a, false, xbmc_key
, "two" },
192 { 0x00, 0x5b, false, xbmc_key
, "three" },
193 { 0x00, 0x5c, false, xbmc_key
, "four" },
194 { 0x00, 0x5d, false, xbmc_key
, "five" },
195 { 0x00, 0x5e, false, xbmc_key
, "six" },
196 { 0x00, 0x5f, false, xbmc_key
, "seven" },
197 { 0x00, 0x60, false, xbmc_key
, "eight" },
198 { 0x00, 0x61, false, xbmc_key
, "nine" },
199 { 0x00, 0x62, false, xbmc_key
, "zero" },
200 { 0x00, 0x55, false, xbmc_key
, "star" },
201 { 0x04, 0x5d, false, xbmc_key
, "hash" },
202 { 0x00, 0x29, false, xbmc_key
, "clear" },
203 { 0x04, 0x3d, false, xbmc_key
, "subtitle" }, /* close button */
204 { 0x04, 0x5b, false, NULL
, "Alt+3 [discard]" }, /* discard - hash */
205 { 0x00, 0x53, false, NULL
, "NumLock [discard]" }, /* discard - key down */
206 { 0x0c, 0x00, false, NULL
, "start [discard]" }, /* discard - mypictures*/
207 { 0x08, 0x00, false, NULL
, "start [discard]" }, /* discard - start*/
208 { 0x04, 0x00, false, NULL
, "Alt [discard]" }, /* discard - hash, close*/
209 { 0x03, 0x00, false, NULL
, "Shift [discard]" }, /* discard - mytv, menu, rewind, fastforward*/
210 { 0x01, 0x00, false, NULL
, "Ctrl [discard]" }, /* discard - key down */
212 static bool repeating
= false;
213 if (transfer
->status
== LIBUSB_TRANSFER_NO_DEVICE
) {
217 if (transfer
->actual_length
== 8) {
218 char m
= transfer
->buffer
[0];
219 char c
= transfer
->buffer
[2];
220 if (m
== 0x00 && c
== 0x00) {
222 xbmc_release_button();
225 for (unsigned int i
= 0; i
< sizeof(translation_table
)/sizeof(translation_table
[0]); i
++) {
226 if (translation_table
[i
].modifier
== m
&& translation_table
[i
].code
== c
) {
227 print_ir_code(translation_table
[i
].key
, transfer
);
228 if (translation_table
[i
].key_fn
!= NULL
) {
229 translation_table
[i
].key_fn(translation_table
[i
].key
);
230 repeating
= translation_table
[i
].can_repeat
;
236 if (transfer
->actual_length
> 0) {
237 msg("*** unknown keycode: ");
238 for (int i
= 0; i
< transfer
->actual_length
; i
++)
239 msg("%02x ", transfer
->buffer
[i
] & 0xff);
243 UCHK(libusb_submit_transfer(transfer
));
246 static void transfer0x82_cb (struct libusb_transfer
*transfer
)
248 static const struct {
251 void (*key_fn
) (const char *key
);
253 } translation_table
[] = {
254 { { 0x03, 0x02, 0x55, 0x55, 0x55 }, false, xbmc_key
, "power" },
255 { { 0x02, 0x02, 0x00, 0x00, 0x55 }, false, xbmc_key
, "display" },
256 { { 0x02, 0x80, 0x00, 0x00, 0x55 }, false, xbmc_key
, "skipminus" },
257 { { 0x02, 0x00, 0x02, 0x00, 0x55 }, false, xbmc_key
, "skipplus" },
258 { { 0x02, 0x00, 0x00, 0x01, 0x55 }, false, xbmc_key
, "stop" },
259 { { 0x02, 0x00, 0x00, 0x02, 0x55 }, false, xbmc_key
, "pause" },
260 { { 0x02, 0x00, 0x01, 0x00, 0x55 }, false, xbmc_key
, "mute" },
261 { { 0x02, 0x10, 0x00, 0x00, 0x55 }, true, xbmc_keyr
, "volumeplus" },
262 { { 0x02, 0x00, 0x10, 0x00, 0x55 }, true, xbmc_keyr
, "volumeminus" },
264 static bool repeating
= false;
265 static int rclick_pending
= 0;
266 if (transfer
->status
== LIBUSB_TRANSFER_NO_DEVICE
) {
270 if (transfer
->status
== LIBUSB_TRANSFER_TIMED_OUT
) {
271 if (rclick_pending
== 1) {
272 print_ir_code("info", transfer
);
278 if (transfer
->actual_length
== 5) {
279 const char keycode_up
[5] = { 0x02, 0x00, 0x00, 0x00, 0x55 };
280 const char keycode_up_power
[5] = { 0x03, 0x00, 0x55, 0x55, 0x55 };
281 const char keycode_up_mouse
[5] = { 0x01, 0x00, 0x00, 0x00, 0x00 };
282 if (memcmp(transfer
->buffer
, keycode_up
, 5) == 0 ||
283 memcmp(transfer
->buffer
, keycode_up_power
, 5) == 0 ||
284 memcmp(transfer
->buffer
, keycode_up_mouse
, 5) == 0) {
286 xbmc_release_button();
287 if (rclick_pending
> 0 && --rclick_pending
== 0) {
288 print_ir_code("right click", transfer
);
293 if (transfer
->buffer
[0] == 0x01) {
294 if (transfer
->buffer
[1] == 0x00) {
295 char dx
= transfer
->buffer
[2];
296 char dy
= transfer
->buffer
[3];
297 if (dx
!= 0 || dy
!= 0) {
298 print_ir_code("mouse", transfer
);
302 else if (transfer
->buffer
[1] == 0x01) {
303 print_ir_code("left click", transfer
);
306 else if (transfer
->buffer
[1] == 0x02) {
307 print_ir_code("catching right click/info", transfer
);
314 for (unsigned int i
= 0; i
< sizeof(translation_table
)/sizeof(translation_table
[0]); i
++) {
315 if (memcmp(translation_table
[i
].code
, transfer
->buffer
, 5) == 0) {
316 print_ir_code(translation_table
[i
].key
, transfer
);
317 if (translation_table
[i
].key_fn
!= NULL
) {
318 translation_table
[i
].key_fn(translation_table
[i
].key
);
319 repeating
= translation_table
[i
].can_repeat
;
324 msg("*** unknown keycode: ");
325 for (int i
= 0; i
< transfer
->actual_length
; i
++)
326 msg("%02x ", transfer
->buffer
[i
] & 0xff);
330 UCHK(libusb_submit_transfer(transfer
));
333 static void handle_exit(int sig
)
338 void print_usage(char* progname
)
340 msg("HAMA MCE remote event client for XBMC\n");
341 msg("\tUsage: %s [options]\n", progname
);
342 msg("\t\t -h --help\t\tdisplay usage summary\n");
343 msg("\t\t -v --version\t\tdisplay version\n");
344 msg("\t\t -d --daemon\t\trun in background\n");
347 int main (int argc
, char **argv
)
349 bool daemonize
= false;
353 static struct option long_options
[] =
355 {"help", no_argument
, NULL
, 'h'},
356 {"version", no_argument
, NULL
, 'v'},
357 {"daemon", no_argument
, NULL
, 'd'},
358 {"fork", no_argument
, NULL
, 'f'},
362 c
= getopt_long(argc
, argv
, "hvdf", long_options
, NULL
);
367 print_usage(argv
[0]);
370 msg("HAMA MCE remote event client v%s for XBMC\n", VERSION
);
376 err("%s: The --fork argument is deprectiated, change it to -d or --daemon\n", argv
[0]);
380 print_usage(argv
[0]);
385 if (optind
< (argc
- 1)) {
386 err("%s: too many arguments\n", argv
[0]);
391 sigemptyset(&sa
.sa_mask
);
392 sa
.sa_handler
= handle_exit
;
393 PCHK(sigaction(SIGINT
, &sa
, NULL
));
394 PCHK(sigaction(SIGTERM
, &sa
, NULL
));
397 libusb_device_handle
*dev
;
398 struct libusb_transfer
*transfer0x81
= libusb_alloc_transfer(0);
399 struct libusb_transfer
*transfer0x82
= libusb_alloc_transfer(0);
400 unsigned char buf0x81
[8];
401 unsigned char buf0x82
[5];
403 UCHK(libusb_init(&ctx
));
405 if (!(dev
= libusb_open_device_with_vid_pid(ctx
, 0x05a4, 0x9881))) {
406 err("%s: No HAMA MCE remote control found.\n", argv
[0]);
410 int exit_code
= EXIT_SUCCESS
;
412 if (libusb_kernel_driver_active(dev
, 0))
413 UCHK(libusb_detach_kernel_driver(dev
, 0));
414 if (libusb_kernel_driver_active(dev
, 1))
415 UCHK(libusb_detach_kernel_driver(dev
, 1));
416 UCHK(libusb_claim_interface(dev
, 0));
417 UCHK(libusb_claim_interface(dev
, 1));
419 libusb_fill_interrupt_transfer(transfer0x81
, dev
, 0x81, buf0x81
, sizeof(buf0x81
), transfer0x81_cb
, NULL
, 215);
420 UCHK(libusb_submit_transfer(transfer0x81
));
422 libusb_fill_interrupt_transfer(transfer0x82
, dev
, 0x82, buf0x82
, sizeof(buf0x82
), transfer0x82_cb
, NULL
, 200);
423 UCHK(libusb_submit_transfer(transfer0x82
));
425 msg("Connected HAMA MCE Remote\n");
426 xbmc
.SendHELO("HAMA MCE Remote", ICON_NONE
);
429 if (daemon(0,0) == -1) {
430 err("Failed to fork\n");
432 exit_code
= EXIT_FAILURE
;
437 while (!(disconnected
|| quit
)) {
438 UCHK(libusb_handle_events(ctx
));
442 msg("Disconnected HAMA MCE Remote\n");
443 xbmc
.SendNOTIFICATION("Disconnected", "HAMA MCE Remote", ICON_NONE
);
446 msg("Closing HAMA MCE Remote\n");
447 xbmc
.SendNOTIFICATION("Closing", "HAMA MCE Remote", ICON_NONE
);
451 libusb_free_transfer(transfer0x81
);
452 libusb_free_transfer(transfer0x82
);
455 // Release the remote back to the system
456 UCHK(libusb_release_interface(dev
, 0));
457 UCHK(libusb_release_interface(dev
, 1));
458 UCHK(libusb_attach_kernel_driver(dev
, 0));
459 UCHK(libusb_attach_kernel_driver(dev
, 1));
468 /* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */