/*
  *  USB ATI Remote support
  *
- *                Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
+ *                Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>
  *  Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
  *  Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
  *
        const char *(*get_default_keymap)(struct usb_interface *interface);
 };
 
+static const char *get_medion_keymap(struct usb_interface *interface)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+
+       /* The receiver shipped with the "Digitainer" variant helpfully has
+        * a single additional bit set in its descriptor. */
+       if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP)
+               return RC_MAP_MEDION_X10_DIGITAINER;
+
+       return RC_MAP_MEDION_X10;
+}
+
 static const struct ati_receiver_type type_ati         = { .default_keymap = RC_MAP_ATI_X10 };
-static const struct ati_receiver_type type_medion      = { .default_keymap = RC_MAP_MEDION_X10 };
+static const struct ati_receiver_type type_medion      = { .get_default_keymap = get_medion_keymap };
 static const struct ati_receiver_type type_firefly     = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
 
 static struct usb_device_id ati_remote_table[] = {
        int acc;
        int remote_num;
        unsigned char scancode;
+       u32 wheel_keycode = KEY_RESERVED;
        int i;
 
        /*
         */
        scancode = data[2] & 0x7f;
 
-       /* Look up event code index in the mouse translation table. */
-       for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
-               if (scancode == ati_remote_tbl[i].data) {
-                       index = i;
-                       break;
+       dbginfo(&ati_remote->interface->dev,
+               "channel 0x%02x; key data %02x, scancode %02x\n",
+               remote_num, data[2], scancode);
+
+       if (scancode >= 0x70) {
+               /*
+                * This is either a mouse or scrollwheel event, depending on
+                * the remote/keymap.
+                * Get the keycode assigned to scancode 0x78/0x70. If it is
+                * set, assume this is a scrollwheel up/down event.
+                */
+               wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev,
+                                                       scancode & 0x78);
+
+               if (wheel_keycode == KEY_RESERVED) {
+                       /* scrollwheel was not mapped, assume mouse */
+
+                       /* Look up event code index in the mouse translation table. */
+                       for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+                               if (scancode == ati_remote_tbl[i].data) {
+                                       index = i;
+                                       break;
+                               }
+                       }
                }
        }
 
-       if (index >= 0) {
-               dbginfo(&ati_remote->interface->dev,
-                       "channel 0x%02x; mouse data %02x; index %d; keycode %d\n",
-                       remote_num, data[2], index, ati_remote_tbl[index].code);
-               if (!dev)
-                       return; /* no mouse device */
-       } else
-               dbginfo(&ati_remote->interface->dev,
-                       "channel 0x%02x; key data %02x, scancode %02x\n",
-                       remote_num, data[2], scancode);
-
-
        if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
                input_event(dev, ati_remote_tbl[index].type,
                        ati_remote_tbl[index].code,
 
                if (index < 0) {
                        /* Not a mouse event, hand it to rc-core. */
-
-                       /*
-                        * We don't use the rc-core repeat handling yet as
-                        * it would cause ghost repeats which would be a
-                        * regression for this driver.
-                        */
-                       rc_keydown_notimeout(ati_remote->rdev, scancode,
-                                            data[2]);
-                       rc_keyup(ati_remote->rdev);
+                       int count = 1;
+
+                       if (wheel_keycode != KEY_RESERVED) {
+                               /*
+                                * This is a scrollwheel event, send the
+                                * scroll up (0x78) / down (0x70) scancode
+                                * repeatedly as many times as indicated by
+                                * rest of the scancode.
+                                */
+                               count = (scancode & 0x07) + 1;
+                               scancode &= 0x78;
+                       }
+
+                       while (count--) {
+                               /*
+                               * We don't use the rc-core repeat handling yet as
+                               * it would cause ghost repeats which would be a
+                               * regression for this driver.
+                               */
+                               rc_keydown_notimeout(ati_remote->rdev, scancode,
+                                                    data[2]);
+                               rc_keyup(ati_remote->rdev);
+                       }
                        return;
                }
 
 
                        rc-lme2510.o \
                        rc-manli.o \
                        rc-medion-x10.o \
+                       rc-medion-x10-digitainer.o \
                        rc-msi-digivox-ii.o \
                        rc-msi-digivox-iii.o \
                        rc-msi-tvanywhere.o \
 
--- /dev/null
+/*
+ * Medion X10 RF remote keytable (Digitainer variant)
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for a variant that has a distinctive scrollwheel instead of
+ * up/down buttons (tested with P/N 40009936 / 20018268), reportedly
+ * originally shipped with Medion Digitainer but now sold separately simply as
+ * an "X10" remote.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_digitainer[] = {
+       { 0x02, KEY_POWER },
+
+       { 0x2c, KEY_TV },
+       { 0x2d, KEY_VIDEO },
+       { 0x04, KEY_DVD },    /* CD/DVD */
+       { 0x16, KEY_TEXT },   /* "teletext" icon, i.e. a screen with lines */
+       { 0x06, KEY_AUDIO },
+       { 0x2e, KEY_RADIO },
+       { 0x31, KEY_EPG },    /* a screen with an open book */
+       { 0x05, KEY_IMAGES }, /* Photo */
+       { 0x2f, KEY_INFO },
+
+       { 0x78, KEY_UP },     /* scrollwheel up 1 notch */
+       /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */
+
+       { 0x70, KEY_DOWN },   /* scrollwheel down 1 notch */
+       /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */
+
+       { 0x19, KEY_MENU },
+       { 0x1d, KEY_LEFT },
+       { 0x1e, KEY_OK },     /* scrollwheel press */
+       { 0x1f, KEY_RIGHT },
+       { 0x20, KEY_BACK },
+
+       { 0x09, KEY_VOLUMEUP },
+       { 0x08, KEY_VOLUMEDOWN },
+       { 0x00, KEY_MUTE },
+
+       { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */
+
+       { 0x0b, KEY_CHANNELUP },
+       { 0x0c, KEY_CHANNELDOWN },
+       { 0x1c, KEY_LAST },
+
+       { 0x32, KEY_RED },    /* also Audio */
+       { 0x33, KEY_GREEN },  /* also Subtitle */
+       { 0x34, KEY_YELLOW }, /* also Angle */
+       { 0x35, KEY_BLUE },   /* also Title */
+
+       { 0x28, KEY_STOP },
+       { 0x29, KEY_PAUSE },
+       { 0x25, KEY_PLAY },
+       { 0x21, KEY_PREVIOUS },
+       { 0x18, KEY_CAMERA },
+       { 0x23, KEY_NEXT },
+       { 0x24, KEY_REWIND },
+       { 0x27, KEY_RECORD },
+       { 0x26, KEY_FORWARD },
+
+       { 0x0d, KEY_1 },
+       { 0x0e, KEY_2 },
+       { 0x0f, KEY_3 },
+       { 0x10, KEY_4 },
+       { 0x11, KEY_5 },
+       { 0x12, KEY_6 },
+       { 0x13, KEY_7 },
+       { 0x14, KEY_8 },
+       { 0x15, KEY_9 },
+       { 0x17, KEY_0 },
+};
+
+static struct rc_map_list medion_x10_digitainer_map = {
+       .map = {
+               .scan    = medion_x10_digitainer,
+               .size    = ARRAY_SIZE(medion_x10_digitainer),
+               .rc_type = RC_TYPE_OTHER,
+               .name    = RC_MAP_MEDION_X10_DIGITAINER,
+       }
+};
+
+static int __init init_rc_map_medion_x10_digitainer(void)
+{
+       return rc_map_register(&medion_x10_digitainer_map);
+}
+
+static void __exit exit_rc_map_medion_x10_digitainer(void)
+{
+       rc_map_unregister(&medion_x10_digitainer_map);
+}
+
+module_init(init_rc_map_medion_x10_digitainer)
+module_exit(exit_rc_map_medion_x10_digitainer)
+
+MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
 
 #define RC_MAP_LME2510                   "rc-lme2510"
 #define RC_MAP_MANLI                     "rc-manli"
 #define RC_MAP_MEDION_X10                "rc-medion-x10"
+#define RC_MAP_MEDION_X10_DIGITAINER     "rc-medion-x10-digitainer"
 #define RC_MAP_MSI_DIGIVOX_II            "rc-msi-digivox-ii"
 #define RC_MAP_MSI_DIGIVOX_III           "rc-msi-digivox-iii"
 #define RC_MAP_MSI_TVANYWHERE_PLUS       "rc-msi-tvanywhere-plus"