/*++ * linux/drivers/input/rmtctl/wmt-rmtctl.c * WonderMedia input remote control driver * * Copyright c 2010 WonderMedia Technologies, Inc. * * 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, see . * * WonderMedia Technologies, Inc. * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wmt-rmtctl.h" enum CIR_CODEC_TYPE { CIR_CODEC_NEC = 0, CIR_CODEC_TOSHIBA = 1, CIR_CODEC_PHILIPS_RC6 = 2, CIR_CODEC_MAX, }; struct cir_param { enum CIR_CODEC_TYPE codec; unsigned int param[7]; unsigned int repeat_timeout; }; struct cir_vendor_info { char *vendor_name; enum CIR_CODEC_TYPE codec; unsigned int vendor_code; unsigned int wakeup_code; unsigned int key_codes[258]; // usb-keyboard standard }; #define RMTCTL_DEBUG 0 #define REL_DELTA 20 #define WMT_RMTCTL_VENDOR_ENV "wmt.io.rmtctl.vendorcode" struct rmtctl_led{ int gpio; int active; int on; int enable; struct timer_list timer; }; struct rmtctl_rel { int on; unsigned int code; struct timer_list timer; struct input_dev *input; }; struct rmtctl_priv { enum CIR_CODEC_TYPE codec; int vendor_index; int table_index; unsigned int saved_vcode; unsigned int vendor_code; unsigned int scan_code; struct input_dev *idev; struct timer_list timer; struct delayed_work delaywork; struct rmtctl_led led;//led control struct rmtctl_rel rel_dev;//remote cursor removing }; static struct cir_param rmtctl_params[] = { // NEC : |9ms carrier wave| + |4.5ms interval| [CIR_CODEC_NEC] = { .codec = CIR_CODEC_NEC, .param = { 0x10a, 0x8e, 0x42, 0x55, 0x9, 0x13, 0x13 }, .repeat_timeout = 17965000, }, // TOSHIBA : |4.5ms carrier wave| + |4.5ms interval| [CIR_CODEC_TOSHIBA] = { .codec = CIR_CODEC_TOSHIBA, .param = { 0x8e, 0x8e, 0x42, 0x55, 0x9, 0x13, 0x13 }, .repeat_timeout = 17965000, }, [CIR_CODEC_PHILIPS_RC6] = { .codec = CIR_CODEC_PHILIPS_RC6, .param = { 0x7, 0x15, 0x23, 0x31, 0x40, 0x4C, 0x5B }, .repeat_timeout = 17965000, }, }; static struct cir_vendor_info rmtctl_vendors[] = { //Add by Flourise Kevin for FuRuiYing IR keypad 2014.12.07 Start { .vendor_name = "FRY-01", .codec = CIR_CODEC_NEC, .vendor_code = 0x00FF, .wakeup_code = 0x0F, .key_codes = { //No.1 Line [0x0F] = KEY_POWER, /* power down */ [0x52] = KEY_HOME, /* Home */ [0x10] = KEY_MENU, /* menu */ [0x58] = KEY_MUTE, /* mute */ //No.2 Line [0x5E] = KEY_KATAKANA, /* cursor */ [0x4D] = KEY_UP, /* up */ [0x11] = KEY_PREVIOUSSONG, /*previous song*/ [0x5C] = KEY_VOLUMEUP, /*volume Up*/ //No.3 Line [0x57] = KEY_LEFT, /*left*/ [0x5B] = KEY_ENTER, /* OK */ [0x5F] = KEY_RIGHT, /*Right*/ [0x54] = KEY_VOLUMEDOWN, /*volume down*/ //No.4 Line [0x56] = KEY_TAB, /*table key*/ [0x5A] = KEY_DOWN, /*Down*/ [0x12] = KEY_NEXTSONG, /*next song*/ [0x53] = KEY_BACK, /* back */ //No.5 Line [0x17] = KEY_1, [0x1B] = KEY_2, [0x1F] = KEY_3, [0x16] = KEY_4, //No.6 Line [0x1A] = KEY_5, [0x1E] = KEY_6, [0x15] = KEY_7, [0x19] = KEY_8, //No.7 Line [0x1D] = KEY_9, [0x18] = KEY_0, [0x14] = KEY_DOT, /*dot //small point*/ [0x1c] = KEY_BACKSPACE, /* backspace */ //No.8 Line [0x13] = KEY_PAUSE, /*vod */ [0x50] = KEY_PLAY, /*live */ [0x59] = KEY_PAUSE, /*pause */ [0x55] = KEY_PLAY, /*play */ }, }, //Add by Flourise Kevin for FuRuiYing IR keypad 2014.12.07 End //Add by Flourise Kevin for FuRuiYing IR keypad 2014.11.29 Start { .vendor_name = "Ly-01", .codec = CIR_CODEC_NEC, .vendor_code = 0x00FF, .wakeup_code = 0x18, .key_codes = { [0x18] = KEY_POWER, /* power down */ [0xF2] = KEY_SWITCHVIDEOMODE, /*Cycle between available vide outputs (Monitor/LCD/TV-out/etc) */ [0x54] = KEY_1, [0x48] = KEY_2, [0x07] = KEY_3, [0x50] = KEY_4, [0x12] = KEY_5, [0x11] = KEY_6, [0x4c] = KEY_7, [0x0e] = KEY_8, [0x0d] = KEY_9, // [0x41] = KEY_SW_INPUT_TYPE, /*switch input type*/ [0x0c] = KEY_0, [0x4b] = KEY_BACKSPACE, /* backspace */ [0x06] = KEY_HOME, /* Home */ [0x40] = KEY_BACK, /* back */ [0x46] = KEY_UP, /* up */ [0x47] = KEY_LEFT, /*left*/ [0x55] = KEY_ENTER, /* OK */ [0x15] = KEY_RIGHT, /*Right*/ [0x16] = KEY_DOWN, /*Down*/ [0x04] = KEY_MENU, /* menu */ [0x17] = KEY_KATAKANA, /* cursor */ [0x14] = KEY_VOLUMEDOWN, /*volume down*/ [0x08] = KEY_MUTE, /* mute */ [0x10] = KEY_VOLUMEUP, /*volume Up*/ // [0x56] = KEY_TV, /*start TV*/ // [0x57] = KEY_QQ, /*start QQ*/ [0x1f] = KEY_PREVIOUSSONG, /*previous song*/ [0x5b] = KEY_NEXTSONG, /*next song*/ }, }, //Add by Flourise Kevin for FuRuiYing IR keypad 2014.11.29 End // SRC1804 0 { .vendor_name = "SRC1804", .codec = CIR_CODEC_NEC, .vendor_code = 0x02fd, .wakeup_code = 0x57, .key_codes = { [0x57] = KEY_POWER, /* power down */ [0x56] = KEY_VOLUMEDOWN, /* vol- */ [0x14] = KEY_VOLUMEUP, /* vol+ */ [0x53] = KEY_HOME, /* home */ [0x11] = KEY_MENU, /* menu */ [0x10] = KEY_BACK, /* back */ [0x4b] = KEY_ZOOMOUT, /* zoom out */ [0x08] = KEY_ZOOMIN, /* zoom in */ [0x0d] = KEY_UP, /* up */ [0x4e] = KEY_LEFT, /* left */ [0x19] = KEY_ENTER, /* OK */ [0x0c] = KEY_RIGHT, /* right */ [0x4f] = KEY_DOWN, /* down */ [0x09] = KEY_PAGEUP, /* page up */ [0x47] = KEY_PREVIOUSSONG, /* rewind */ [0x05] = KEY_PAGEDOWN, /* page down */ [0x04] = KEY_NEXTSONG /* forward */ }, }, // IH8950 1 { .vendor_name = "IH8950", .codec = CIR_CODEC_NEC, .vendor_code = 0x0909, .wakeup_code = 0xdc, .key_codes = { [0xdc] = KEY_POWER, /* power down */ [0x81] = KEY_VOLUMEDOWN, /* vol- */ [0x80] = KEY_VOLUMEUP, /* vol+ */ [0x82] = KEY_HOME, /* home */ [0xc5] = KEY_BACK, /* back */ [0xca] = KEY_UP, /* up */ [0x99] = KEY_LEFT, /* left */ [0xce] = KEY_ENTER, /* OK */ [0xc1] = KEY_RIGHT, /* right */ [0xd2] = KEY_DOWN, /* down */ [0x9c] = KEY_MUTE, [0x95] = KEY_PLAYPAUSE, [0x88] = KEY_MENU, }, }, // sunday 2 { .vendor_name = "sunday", .codec = CIR_CODEC_NEC, .vendor_code = 0x02fd, .wakeup_code = 0x1a, .key_codes = { [0x1a] = KEY_POWER, /* power down */ [0x16] = KEY_VOLUMEDOWN, /* vol- */ [0x44] = KEY_VOLUMEUP, /* vol+ */ [0x59] = KEY_HOME, /* home */ [0x1b] = KEY_BACK, /* back */ [0x06] = KEY_UP, /* up */ [0x5d] = KEY_LEFT, /* left */ [0x1e] = KEY_ENTER, /* OK */ [0x5c] = KEY_RIGHT, /* right */ [0x1f] = KEY_DOWN, /* down */ [0x55] = KEY_PLAYPAUSE, [0x54] = KEY_REWIND, [0x17] = KEY_FASTFORWARD, [0x58] = KEY_AGAIN, /* recent app */ }, }, /* F1 - F12 */ // KT-9211 3 { .vendor_name = "KT-9211", .codec = CIR_CODEC_NEC, .vendor_code = 0x02fd, .wakeup_code = 0x57, .key_codes = { [0x57] = KEY_POWER, /* power down */ [0x56] = KEY_VOLUMEDOWN, /* volume- */ [0x15] = KEY_MUTE, /* mute */ [0x14] = KEY_VOLUMEUP, /* volume+ */ [0x0d] = KEY_UP, /* up */ [0x4e] = KEY_LEFT, /* left */ [0x19] = KEY_ENTER, /* OK */ [0x0c] = KEY_RIGHT, /* right */ [0x4f] = KEY_DOWN, /* down */ [0x53] = KEY_HOME, /* home */ [0x09] = KEY_F5, /* my photo in default */ [0x11] = KEY_F12, /* setting apk in default */ [0x47] = KEY_F3, /* My Music in default */ [0x10] = KEY_BACK, /* Back */ [0x08] = KEY_F4, /* my video in default */ [0x17] = KEY_F1, /* web browser in default */ //Following items are configured manually. [0x05] = KEY_F10, /* file browser in default */ [0x04] = KEY_F2, /* camera in default */ [0x4b] = KEY_F11, /* calendar in default */ [0x16] = KEY_SCREEN, /* calculator in default */ [0x18] = KEY_F9, /* recorder in default */ }, }, // KT-8830 4 { .vendor_name = "KT-8830", .codec = CIR_CODEC_NEC, .vendor_code = 0x866b, .wakeup_code = 0x1c, .key_codes = { [0x1c] = KEY_POWER, [0x14] = KEY_MUTE, [0x09] = KEY_1, [0x1d] = KEY_2, [0x1f] = KEY_3, [0x0d] = KEY_4, [0x19] = KEY_5, [0x1b] = KEY_6, [0x11] = KEY_7, [0x15] = KEY_8, [0x17] = KEY_9, [0x12] = KEY_0, [0x16] = KEY_VOLUMEDOWN, [0x04] = KEY_VOLUMEUP, [0x40] = KEY_HOME, [0x4c] = KEY_BACK, [0x00] = KEY_F12, // setup [0x13] = KEY_MENU, [0x03] = KEY_UP, [0x0e] = KEY_LEFT, [0x1a] = KEY_RIGHT, [0x02] = KEY_DOWN, [0x07] = KEY_ENTER, [0x47] = KEY_F4, // video [0x10] = KEY_F3, // music [0x0f] = KEY_FASTFORWARD, [0x43] = KEY_REWIND, [0x18] = KEY_F1, // browser [0x0b] = KEY_YEN, // multi-functions [0x4e] = KEY_PLAYPAUSE, }, }, // Haier-OTT 5 { .vendor_name = "Haier-OTT", .codec = CIR_CODEC_NEC, .vendor_code = 0xb34c, .wakeup_code = 0xdc, .key_codes = { [0xdc] = KEY_POWER, [0x9c] = KEY_MUTE, [0xca] = KEY_UP, [0x99] = KEY_LEFT, [0xc1] = KEY_RIGHT, [0xd2] = KEY_DOWN, [0xce] = KEY_ENTER, [0x80] = KEY_VOLUMEDOWN, [0x81] = KEY_VOLUMEUP, [0xc5] = KEY_HOME, [0x95] = KEY_BACK, [0x88] = KEY_MENU, [0x82] = KEY_F12, /* setting apk in default */ }, }, // GPRC-11933 6 { .vendor_name = "GPRC-11933", .codec = CIR_CODEC_NEC, .vendor_code = 0x4040, .wakeup_code = 0x4D, .key_codes = { [0x4D] = KEY_POWER, /* power down */ [0x53] = KEY_PLAYPAUSE, [0x5B] = KEY_F3, /* My Music in default */ [0x57] = KEY_F1, /* web browser in default */ [0x54] = KEY_F10, /* file browser in default */ [0x17] = KEY_VOLUMEDOWN, /* volume- */ [0x43] = KEY_MUTE, /* mute */ [0x18] = KEY_VOLUMEUP, /* volume+ */ [0x1F] = KEY_PREVIOUSSONG, /* rewind */ [0x1E] = KEY_NEXTSONG, /* forward */ [0x0B] = KEY_UP, /* up */ [0x10] = KEY_LEFT, /* left */ [0x0D] = KEY_ENTER, /* OK */ [0x11] = KEY_RIGHT, /* right */ [0x0E] = KEY_DOWN, /* down */ [0x1A] = KEY_HOME, /* home */ [0x42] = KEY_BACK, /* Back */ [0x45] = KEY_F12, /* setting apk in default */ [0x47] = KEY_KATAKANA, /* multi-functions */ [0x01] = KEY_1, [0x02] = KEY_2, [0x03] = KEY_3, [0x04] = KEY_4, [0x05] = KEY_5, [0x06] = KEY_6, [0x07] = KEY_7, [0x08] = KEY_8, [0x09] = KEY_9, [0x00] = KEY_0, [0x44] = KEY_MENU, [0x0C] = KEY_BACKSPACE, }, }, // TS-Y118 7 { .vendor_name = "TS-Y118", .codec = CIR_CODEC_NEC, .vendor_code = 0xb34c, .wakeup_code = 0xdc, .key_codes = { [0xdc] = KEY_POWER, [0x9c] = KEY_MUTE, [0x8d] = KEY_F12, // apk-settings [0x88] = KEY_HOME, [0xca] = KEY_UP, [0xd2] = KEY_DOWN, [0x99] = KEY_LEFT, [0xc1] = KEY_RIGHT, [0xce] = KEY_ENTER, [0x95] = KEY_PLAYPAUSE, [0xc5] = KEY_BACK, [0x80] = KEY_VOLUMEUP, [0x81] = KEY_VOLUMEDOWN, [0xdd] = KEY_PAGEUP, [0x8c] = KEY_PAGEDOWN, [0x92] = KEY_1, [0x93] = KEY_2, [0xcc] = KEY_3, [0x8e] = KEY_4, [0x8f] = KEY_5, [0xc8] = KEY_6, [0x8a] = KEY_7, [0x8b] = KEY_8, [0xc4] = KEY_9, [0x87] = KEY_0, }, }, // Hisense 8 { .vendor_name = "Hisense", .codec = CIR_CODEC_NEC, .vendor_code = 0x00ff, .wakeup_code = 0x14, .key_codes = { [0x14] = KEY_POWER, [0x1c] = KEY_MUTE, [0x40] = KEY_PAGEUP, [0x44] = KEY_PAGEDOWN, [0x0b] = KEY_VOLUMEUP, [0x58] = KEY_VOLUMEDOWN, [0x01] = KEY_MENU, [0x03] = KEY_UP, [0x02] = KEY_DOWN, [0x0e] = KEY_LEFT, [0x1a] = KEY_RIGHT, [0x07] = KEY_ENTER, [0x48] = KEY_HOME, [0x5c] = KEY_BACK, [0x09] = KEY_1, [0x1d] = KEY_2, [0x1f] = KEY_3, [0x0d] = KEY_4, [0x19] = KEY_5, [0x1b] = KEY_6, [0x11] = KEY_7, [0x15] = KEY_8, [0x17] = KEY_9, [0x12] = KEY_0, [0x06] = KEY_DOT, [0x16] = KEY_DELETE, [0x0c] = KEY_ZOOMIN, [0x4c] = KEY_F11, [0x13] = KEY_YEN, }, }, // Mountain 9 { .vendor_name = "TV BOX Mountain", .codec = CIR_CODEC_NEC, .vendor_code = 0x00df, .wakeup_code = 0x1c, .key_codes = { [0x1c] = KEY_POWER, /* power down */ [0x08] = KEY_MUTE, /* volume mute */ [0x1a] = KEY_UP, /* up */ [0x47] = KEY_LEFT, /* left */ [0x06] = KEY_ENTER, /* OK */ [0x07] = KEY_RIGHT, /* right */ [0x48] = KEY_DOWN, /* down */ [0x4f] = KEY_VOLUMEDOWN, /* vol- */ [0x4b] = KEY_VOLUMEUP, /* vol+ */ [0x0a] = KEY_BACK, /* back */ [0x03] = KEY_HOME, /* home */ [0x42] = KEY_KATAKANA, /* TV */ [0x55] = KEY_MENU, /* menu */ }, }, // GPRC-11933 10 { .vendor_name = "GPRC-11933A", .codec = CIR_CODEC_NEC, .vendor_code = 0x00FF, .wakeup_code = 0x57, .key_codes = { [0x57] = KEY_POWER, /* power down */ [0x5B] = KEY_MUTE, /* mute */ [0x16] = KEY_F3, /* My Musicin default */ [0x5A] = KEY_F1, /* web browser in default */ [0x52] = KEY_PLAYPAUSE, [0x50] = KEY_KATAKANA, /* cursor */ [0x0F] = KEY_PREVIOUSSONG, /* rewind */ [0x4C] = KEY_NEXTSONG, /* forward */ [0x58] = KEY_VOLUMEDOWN, /* volume- */ [0x1B] = KEY_VOLUMEUP, /* volume+ */ [0x4F] = KEY_F12, /* setting apk in default */ [0x1A] = KEY_MENU, [0x43] = KEY_UP, /* up */ [0x06] = KEY_LEFT, /* left */ [0x02] = KEY_ENTER, /* OK */ [0x0E] = KEY_RIGHT, /* right */ [0x0A] = KEY_DOWN, /* down */ [0x4E] = KEY_HOME, /* home */ [0x4D] = KEY_BACK, /* back */ [0x10] = KEY_1, [0x11] = KEY_2, [0x12] = KEY_3, [0x13] = KEY_4, [0x14] = KEY_5, [0x15] = KEY_6, [0x17] = KEY_7, [0x18] = KEY_8, [0x19] = KEY_9, [0x1D] = KEY_0, [0x1C] = KEY_F1, [0x1E] = KEY_BACKSPACE, }, }, //China Telecom White 11 { .vendor_name = "TS-Y118A", .codec = CIR_CODEC_NEC, .vendor_code = 0xb34c, .wakeup_code = 0xdc, .key_codes = { [0xdc] = KEY_POWER, [0x98] =KEY_AUDIO, [0x9c] = KEY_MUTE, [0x8d] = KEY_SETUP, [0xd6] = KEY_LAST, [0xcd] = KEY_RED, [0x91] = KEY_GREEN, [0x83] = KEY_YELLOW, [0xc3] = KEY_BLUE, [0x88] = KEY_HOMEPAGE, [0xca] = KEY_UP, [0x82] = KEY_HOME, [0x99] = KEY_LEFT, [0xce] = KEY_SELECT, [0xc1] = KEY_RIGHT, [0x95] = KEY_PLAYPAUSE, [0xd2] = KEY_DOWN, [0xc5] = KEY_BACK, [0x80] = KEY_VOLUMEUP, [0x81] = KEY_VOLUMEDOWN, [0xdd] = KEY_PAGEUP, [0x8c] = KEY_PAGEDOWN, [0x85] = KEY_CHANNELUP, [0x86] = KEY_CHANNELDOWN, [0x92] = KEY_1, [0x93] = KEY_2, [0xcc] = KEY_3, [0x8e] = KEY_4, [0x8f] = KEY_5, [0xc8] = KEY_6, [0x8a] = KEY_7, [0x8b] = KEY_8, [0xc4] = KEY_9, [0x87] = KEY_0, [0xda] = KEY_NUMERIC_STAR, [0xd0] = KEY_NUMERIC_POUND, }, }, //China Telecom Black 12 { .vendor_name = "TS-Y118B", .codec = CIR_CODEC_NEC, .vendor_code = 0xb24d, .wakeup_code = 0xdc, .key_codes = { [0xdc] = KEY_POWER, [0x98] =KEY_AUDIO, [0x9c] = KEY_MUTE, [0x8d] = KEY_SETUP, [0xd6] = KEY_LAST, [0xcd] = KEY_RED, [0x91] = KEY_GREEN, [0x83] = KEY_YELLOW, [0xc3] = KEY_BLUE, [0x88] = KEY_HOMEPAGE, [0xca] = KEY_UP, [0x82] = KEY_HOME, [0x99] = KEY_LEFT, [0xce] = KEY_SELECT, [0xc1] = KEY_RIGHT, [0x95] = KEY_PLAYPAUSE, [0xd2] = KEY_DOWN, [0xc5] = KEY_BACK, [0x80] = KEY_VOLUMEUP, [0x81] = KEY_VOLUMEDOWN, [0xdd] = KEY_PAGEUP, [0x8c] = KEY_PAGEDOWN, [0x85] = KEY_CHANNELUP, [0x86] = KEY_CHANNELDOWN, [0x92] = KEY_1, [0x93] = KEY_2, [0xcc] = KEY_3, [0x8e] = KEY_4, [0x8f] = KEY_5, [0xc8] = KEY_6, [0x8a] = KEY_7, [0x8b] = KEY_8, [0xc4] = KEY_9, [0x87] = KEY_0, [0xda] = KEY_NUMERIC_STAR, [0xd0] = KEY_NUMERIC_POUND, }, }, // Jensen 13 { .vendor_name = "Jensen", .codec = CIR_CODEC_NEC, .vendor_code = 0x00ff, .wakeup_code = 0x46, .key_codes = { [0x08] = KEY_RESERVED, [0x5a] = KEY_VOLUMEUP, [0x4a] = KEY_VOLUMEDOWN, [0x19] = KEY_UP, [0x1c] = KEY_DOWN, [0x0c] = KEY_LEFT, [0x5e] = KEY_RIGHT, [0x18] = KEY_ENTER, [0x0d] = KEY_BACK, [0x45] = KEY_1, [0x46] = KEY_POWER, [0x47] = KEY_3, [0x44] = KEY_4, [0x40] = KEY_F1, [0x43] = KEY_6, [0x07] = KEY_7, [0x15] = KEY_BACK, [0x09] = KEY_9, [0x16] = KEY_0, [0x42] = KEY_PREVIOUSSONG, /* rewind */ [0x52] = KEY_NEXTSONG, /* forward */ }, }, // Foxconn 14 { .vendor_name = "CIR-9F", .codec = CIR_CODEC_NEC, .vendor_code = 0x009f, .wakeup_code = 0x57, .key_codes = { [0x57] = KEY_POWER, /* power */ [0x5d] = KEY_VOLUMEDOWN, /* vol- */ [0xff] = KEY_VOLUMEUP, /* vol+ */ [0x47] = KEY_HOME, /* home */ [0x16] = KEY_MENU, /* menu */ [0x4f] = KEY_BACK, /* back */ [0x43] = KEY_UP, /* up */ [0x06] = KEY_LEFT, /* left */ [0x02] = KEY_ENTER, /* PLAYPAUSE */ //[0x02] = KEY_PLAYPAUSE, /* PLAYPAUSE */ [0x0e] = KEY_RIGHT, /* right */ [0x0a] = KEY_DOWN, /* down */ [0x0b] = KEY_REWIND, /* rewind data is 0x36*/ [0x0f] = KEY_FASTFORWARD, /* forward */ [0x5b] = KEY_ENTER /* OK - notify */ }, }, // Philips 15 { .vendor_name = "PHILIPS", .codec = CIR_CODEC_PHILIPS_RC6, .vendor_code = 60, .wakeup_code = 12, .key_codes = { [12] = KEY_POWER, [146] = KEY_HOME, [84] = KEY_MENU, [92] = KEY_ENTER, [88] = KEY_UP, [89] = KEY_DOWN, [90] = KEY_LEFT, [91] = KEY_RIGHT, [83] = BTN_DEAD, [131] = KEY_BACK, }, }, //Ronsheng 16 { .vendor_name = "Ronsheng", .codec = CIR_CODEC_NEC, .vendor_code = 0x00FF, .wakeup_code = 0x18, .key_codes = { [0x18] = KEY_POWER, /* power down */ [0x56] = KEY_F3, /* My Musicin default */ [0x57] = KEY_F1, /* web browser in default */ [0x1f] = KEY_PREVIOUSSONG, /* rewind */ [0x5b] = KEY_NEXTSONG, /* forward */ [0x14] = KEY_VOLUMEDOWN, /* volume- */ [0x08] = KEY_MUTE, /* mute */ [0x10] = KEY_VOLUMEUP, /* volume+ */ [0x17] = KEY_KATAKANA, /* cursor */ [0x04] = KEY_MENU, [0x46] = KEY_UP, /* up */ [0x47] = KEY_LEFT, /* left */ [0x55] = KEY_ENTER, /* OK */ [0x15] = KEY_RIGHT, /* right */ [0x16] = KEY_DOWN, /* down */ [0x06] = KEY_HOME, /* home */ [0x40] = KEY_BACK, /* back */ [0x54] = KEY_1, [0x48] = KEY_2, [0x07] = KEY_3, [0x50] = KEY_4, [0x12] = KEY_5, [0x11] = KEY_6, [0x4c] = KEY_7, [0x0e] = KEY_8, [0x0d] = KEY_9, [0x0c] = KEY_0, [0x41] = KEY_RESERVED, [0x4b] = KEY_BACKSPACE, }, }, // GPRC-11933 17 { .vendor_name = "GPRC-11933", .codec = CIR_CODEC_NEC, .vendor_code = 0x4040, .wakeup_code = 0x4D, .key_codes = { [0x4D] = KEY_POWER, /* power down */ [0x53] = KEY_F1, [0x5B] = KEY_F2, [0x57] = KEY_F3, [0x54] = KEY_F4, [0x17] = KEY_VOLUMEDOWN, /* volume- */ [0x43] = KEY_MUTE, /* mute */ [0x18] = KEY_VOLUMEUP, /* volume+ */ [0x1F] = KEY_F12, /* setting */ [0x1E] = KEY_F5, [0x0B] = KEY_UP, /* up */ [0x10] = KEY_LEFT, /* left */ [0x0D] = KEY_ENTER, /* OK */ [0x11] = KEY_RIGHT, /* right */ [0x0E] = KEY_DOWN, /* down */ [0x1A] = KEY_HOME, /* home */ [0x47] = KEY_BACK, /* Back */ [0x45] = KEY_MENU, /* MENU */ [0x42] = KEY_KATAKANA, /* multi-functions */ [0x01] = KEY_1, [0x02] = KEY_2, [0x03] = KEY_3, [0x04] = KEY_4, [0x05] = KEY_5, [0x06] = KEY_6, [0x07] = KEY_7, [0x08] = KEY_8, [0x09] = KEY_9, [0x00] = KEY_0, [0x44] = KEY_AGAIN, [0x0C] = KEY_BACKSPACE, }, }, }; extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); extern int wmt_setsyspara(char *varname, char *varval); static int rmtctl_report_rel(struct rmtctl_rel *rel_dev,const unsigned int code ) { switch (code) { case KEY_UP: input_report_rel(rel_dev->input, REL_X, 0); input_report_rel(rel_dev->input, REL_Y, -REL_DELTA); input_sync(rel_dev->input); break; case KEY_DOWN: input_report_rel(rel_dev->input, REL_X, 0); input_report_rel(rel_dev->input, REL_Y, REL_DELTA); input_sync(rel_dev->input); break; case KEY_LEFT: input_report_rel(rel_dev->input, REL_X, -REL_DELTA); input_report_rel(rel_dev->input, REL_Y, 0); input_sync(rel_dev->input); break; case KEY_RIGHT: input_report_rel(rel_dev->input, REL_X, REL_DELTA); input_report_rel(rel_dev->input, REL_Y, 0); input_sync(rel_dev->input); break; default: break; } return 0; } static void rmtctl_report_event(unsigned int *code, int repeat) { static unsigned int last_code = KEY_RESERVED; int ret; struct rmtctl_priv *priv = container_of(code, struct rmtctl_priv, scan_code); ret = del_timer(&priv->timer); if (!repeat) { // new code coming if (ret == 1) { // del_timer() of active timer returns 1 means that active timer has been stoped by new key, // so report last key up event if (RMTCTL_DEBUG) printk("[%d] up reported caused by new key[%d]\n", last_code, *code); if (priv->rel_dev.on) { switch (*code) { case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: del_timer(&priv->rel_dev.timer); //stop report mouse break; case KEY_ENTER: input_report_key(priv->rel_dev.input, BTN_LEFT, 0); input_sync(priv->rel_dev.input); del_timer(&priv->rel_dev.timer); //stop report mouse break; default: input_report_key(priv->idev, last_code, 0); input_sync(priv->idev); break; } } else { input_report_key(priv->idev, last_code, 0); input_sync(priv->idev); } if (*code == KEY_KATAKANA) { if (priv->rel_dev.on == 0) { input_report_rel(priv->rel_dev.input, REL_X, 1); input_report_rel(priv->rel_dev.input, REL_Y, 1); input_sync(priv->rel_dev.input); priv->rel_dev.on = 1; } else priv->rel_dev.on = 0; } } if (RMTCTL_DEBUG) printk("[%d] down\n", *code); // report key down event, mod_timer() to report key up event later for measure key repeat. if (priv->rel_dev.on) { switch (*code) { case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: rmtctl_report_rel(&priv->rel_dev,*code); break; case KEY_ENTER: input_report_key(priv->rel_dev.input, BTN_LEFT, 1); input_sync(priv->rel_dev.input); break; default: input_event(priv->idev, EV_KEY, *code, 1); input_sync(priv->idev); break; } } else { input_event(priv->idev, EV_KEY, *code, 1); input_sync(priv->idev); } if(priv->led.enable){ priv->led.on = 0; gpio_direction_output(priv->led.gpio, !priv->led.active); mod_timer(&priv->led.timer, jiffies+HZ/20); } priv->timer.data = (unsigned long)code; mod_timer(&priv->timer, jiffies + 250*HZ/1000); } else { // report key up event after report repeat event according to usb-keyboard standard priv->timer.data = (unsigned long)code; mod_timer(&priv->timer, jiffies + 250*HZ/1000); // detect 'repeat' flag, report repeat event if (*code != KEY_POWER && *code != KEY_END) { if (RMTCTL_DEBUG) printk("[%d] repeat\n", *code); if (priv->rel_dev.on) { switch (*code) { case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: case KEY_ENTER: priv->rel_dev.code = *code; mod_timer(&priv->rel_dev.timer, jiffies+HZ/100); //ready to repeat report mouse event break; default: input_event(priv->idev, EV_KEY, *code, 2); input_sync(priv->idev); break; } } else { input_event(priv->idev, EV_KEY, *code, 2); input_sync(priv->idev); } } } last_code = *code; } /** no hw repeat detect, so we measure repeat timeout event by timer */ static void rmtctl_report_event_without_hwrepeat(unsigned int *code) { static unsigned int last_code = KEY_RESERVED; static unsigned long last_jiffies = 0; int repeat = (jiffies_to_msecs(jiffies - last_jiffies) <250 && *code==last_code) ? 1 : 0; last_code = *code; last_jiffies = jiffies; rmtctl_report_event(code, repeat); } static void rmtctl_timer_handler(unsigned long data) { struct rmtctl_priv *priv = container_of((void *)data, struct rmtctl_priv, scan_code); unsigned int code = *(unsigned int *)data; // report key up event if (RMTCTL_DEBUG) printk("[%d] up reported by timer\n", code); if (priv->rel_dev.on) { switch (code) { case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: del_timer(&priv->rel_dev.timer); break; case KEY_ENTER: input_report_key(priv->rel_dev.input, BTN_LEFT, 0); input_sync(priv->rel_dev.input); break; default: input_report_key(priv->idev, code, 0); input_sync(priv->idev); break; } } else { input_report_key(priv->idev, code, 0); input_sync(priv->idev); } if (code == KEY_KATAKANA) { if (priv->rel_dev.on == 0) { input_report_rel(priv->rel_dev.input, REL_X, 1); input_report_rel(priv->rel_dev.input, REL_Y, 1); input_sync(priv->rel_dev.input); priv->rel_dev.on = 1; } else priv->rel_dev.on = 0; } if(priv->led.enable){ del_timer_sync(&priv->led.timer); gpio_direction_output(priv->led.gpio,priv->led.active); } } static irqreturn_t rmtctl_interrupt(int irq, void *dev_id) { unsigned int status, ir_data, ir_code,vendor, repeat; unsigned char *key; int i; struct rmtctl_priv *priv = (struct rmtctl_priv *)dev_id; /* get IR status. */ status = REG32_VAL(IRSTS); /* check 'IR received data' flag. */ if ((status & 0x1) == 0x0) { printk("IR IRQ was triggered without data received. (0x%x)\n", status); return IRQ_NONE; } /* read IR data. */ ir_data = REG32_VAL(IRDATA(0)) ; key = (char *) &ir_data; /* clear INT status*/ REG32_VAL(IRSTS)=0x1 ; if (RMTCTL_DEBUG){ printk("ir_data = 0x%08x, status = 0x%x \n", ir_data, status); } if(priv->codec == CIR_CODEC_PHILIPS_RC6){ vendor = key[1]; ir_code = key[0]; //trailer = key[2] & 0x01; } else{//NEC /* get vendor ID. */ vendor = (key[0] << 8) | (key[1]); /* check if key is valid. Key[3] is XORed t o key[2]. */ if (key[2] & key[3]) { printk("Invalid IR key received. (0x%x, 0x%x)\n", key[2], key[3]); return IRQ_NONE; } /* keycode mapping. */ ir_code = key[2]; } if ( priv->vendor_index >= 0 ) { if (vendor == rmtctl_vendors[priv->vendor_index].vendor_code && rmtctl_vendors[priv->vendor_index].codec == priv->codec) { priv->table_index = priv->vendor_index; priv->scan_code = rmtctl_vendors[priv->vendor_index].key_codes[ir_code]; } else{ if(vendor != priv->vendor_code){ for (i = 0; i < ARRAY_SIZE(rmtctl_vendors); i++) { if (vendor == rmtctl_vendors[i].vendor_code && rmtctl_vendors[i].codec == priv->codec) { priv->table_index = i; priv->scan_code = rmtctl_vendors[i].key_codes[ir_code]; break; } } if(i==ARRAY_SIZE(rmtctl_vendors)) return IRQ_HANDLED; } else{ priv->scan_code = rmtctl_vendors[priv->table_index].key_codes[ir_code]; } } } else { if(vendor != priv->vendor_code){ for (i = 0; i < ARRAY_SIZE(rmtctl_vendors); i++) { if (vendor == rmtctl_vendors[i].vendor_code && rmtctl_vendors[i].codec == priv->codec) { priv->table_index = i; priv->scan_code = rmtctl_vendors[i].key_codes[ir_code]; break; } } if(i==ARRAY_SIZE(rmtctl_vendors)) return IRQ_HANDLED; } else{ priv->scan_code = rmtctl_vendors[priv->table_index].key_codes[ir_code]; } } //switch to a new remote controller shoud reset cursor mode if (priv->vendor_code != vendor) priv->rel_dev.on = 0; if ((status & 0x2) || (priv->scan_code == KEY_RESERVED)) { /* ignore repeated or reserved keys. */ } else if (priv->codec == CIR_CODEC_NEC) { /* check 'IR code repeat' flag. */ repeat = status & 0x10; rmtctl_report_event(&priv->scan_code, repeat); } else if(priv->codec == CIR_CODEC_PHILIPS_RC6){ //repeat = !(trailer^key[2]); //trailer = key[2]; //printk("RCV philips rc6,repeat=%d\n",repeat); rmtctl_report_event_without_hwrepeat(&priv->scan_code); } else { rmtctl_report_event_without_hwrepeat(&priv->scan_code); } if (priv->vendor_code != vendor) { priv->vendor_code = vendor; /* save new vendor code to env *1.remote controller configed index is active, no need to save it's vendor code *2.current vendor code is saved, no need to save again */ if(priv->table_index != priv->vendor_index && vendor != priv->saved_vcode){ priv->saved_vcode = vendor; //schedule_delayed_work(&priv->delaywork, msecs_to_jiffies(20)); } } return IRQ_HANDLED; } static void rmtctl_hw_suspend(unsigned long data) { unsigned int wakeup_code; int i; struct rmtctl_priv *priv = (struct rmtctl_priv *)data; if (priv->vendor_index >= 0 && priv->vendor_index == priv->table_index) { i = priv->vendor_index; } else { for (i = 0; i < ARRAY_SIZE(rmtctl_vendors); i++) if (priv->vendor_code == rmtctl_vendors[i].vendor_code && priv->codec == rmtctl_vendors[i].codec) break; if (i == ARRAY_SIZE(rmtctl_vendors)) goto out; } if(priv->codec == CIR_CODEC_PHILIPS_RC6){ wakeup_code = ((unsigned char)(rmtctl_vendors[i].vendor_code & 0xff) << 8) | (unsigned char)(rmtctl_vendors[i].wakeup_code) ; REG32_VAL(WAKEUP_CMD2(0)) = 0x10000| wakeup_code; }else{ wakeup_code = ((unsigned char)(~rmtctl_vendors[i].wakeup_code) << 24) | ((unsigned char)(rmtctl_vendors[i].wakeup_code) << 16 ) | ((unsigned char)(rmtctl_vendors[i].vendor_code & 0xff) << 8) | ((unsigned char)((rmtctl_vendors[i].vendor_code) >> 8) << 0 ); } REG32_VAL(WAKEUP_CMD1(0)) = wakeup_code; REG32_VAL(WAKEUP_CMD1(1)) = 0x0; REG32_VAL(WAKEUP_CMD1(2)) = 0x0; REG32_VAL(WAKEUP_CMD1(3)) = 0x0; REG32_VAL(WAKEUP_CMD1(4)) = 0x0; out: REG32_VAL(WAKEUP_CTRL) = 0x101; } static void rmtctl_hw_init(unsigned long data) { unsigned int st; int i, retries = 0; struct rmtctl_priv *priv = (struct rmtctl_priv *)data; /*setting shared pin*/ GPIO_CTRL_GP62_WAKEUP_SUS_BYTE_VAL &= 0xFFFD; PULL_EN_GP62_WAKEUP_SUS_BYTE_VAL |= 0x02; PULL_CTRL_GP62_WAKEUP_SUS_BYTE_VAL |= 0x02; /* turn off CIR SW reset. */ REG32_VAL(IRSWRST) = 1; REG32_VAL(IRSWRST) = 0; for (i = 0; i < ARRAY_SIZE(rmtctl_params[priv->codec].param); i++) REG32_VAL(PARAMETER(i)) = rmtctl_params[priv->codec].param[i]; if (priv->codec == CIR_CODEC_NEC || priv->codec == CIR_CODEC_TOSHIBA) { printk("NEC\n"); REG32_VAL(NEC_REPEAT_TIME_OUT_CTRL) = 0x1; REG32_VAL(NEC_REPEAT_TIME_OUT_COUNT) = rmtctl_params[priv->codec].repeat_timeout; REG32_VAL(IRCTL) = 0X100; //NEC repeat key } else if(priv->codec == CIR_CODEC_PHILIPS_RC6){ printk("PHILIPS RC6\n"); REG32_VAL(IRCTL) =0x40; //PHILIPS RC6 REG32_VAL(INT_MASK_COUNT) = 40*1000000*1/4; //REG32_VAL(IRCTL_2) =0xFE;//Enable PHILIPS RC6 mode 0,Mask mode1-7 ? }else{ REG32_VAL(IRCTL) = 0; //NEC repeat key REG32_VAL(INT_MASK_COUNT) = 28*1000000*1/4; //0x47868C0/4;//count for 1 sec 0x47868C0 } REG32_VAL(IRCTL) |= (0x1<<25); REG32_VAL(INT_MASK_CTRL) = 0x1; /* IR_EN */ REG32_VAL(IRCTL) |= 0x1; while(retries++ < 100 && !(REG32_VAL(IRCTL)&0x01)){ REG32_VAL(IRCTL) |= 0x1; udelay(5); } /* read CIR status to clear IR interrupt. */ st = REG32_VAL(IRSTS); } static void rmtctl_refresh_vendorcode(struct work_struct *work) { unsigned char data[64]; struct rmtctl_priv *priv = container_of(work, struct rmtctl_priv, delaywork.work); sprintf(data, "0x%x", priv->vendor_code); wmt_setsyspara(WMT_RMTCTL_VENDOR_ENV, data); } static void rel_handler(unsigned long data) { struct rmtctl_rel *rel = (struct rmtctl_rel *)data; struct rmtctl_priv *priv = container_of(rel, struct rmtctl_priv, rel_dev); rmtctl_report_rel(&priv->rel_dev, rel->code); mod_timer(&rel->timer, jiffies+HZ/15); // report rate 15pps } static void rmtctl_rel_init(struct rmtctl_rel *rel) { struct input_dev *input; input = input_allocate_device(); input->name = "rmtctl-rel"; /* register the device as mouse */ set_bit(EV_REL, input->evbit); set_bit(REL_X, input->relbit); set_bit(REL_Y, input->relbit); /* register device's buttons and keys */ set_bit(EV_KEY, input->evbit); set_bit(BTN_LEFT, input->keybit); set_bit(BTN_RIGHT, input->keybit); input_register_device(input); rel->input = input; init_timer(&rel->timer); rel->timer.data = (unsigned long)rel; rel->timer.function = rel_handler; } void led_handler(unsigned long data) { struct rmtctl_led *led = (struct rmtctl_led*)data; gpio_direction_output(led->gpio, led->on++%2); mod_timer(&led->timer, jiffies+HZ/20); return; } static void rmtctl_led_init(struct rmtctl_led *led) { init_timer(&led->timer); led->timer.data = (unsigned long)led; led->timer.function = led_handler; gpio_direction_output(led->gpio, led->active); return; } static int rmtctl_probe(struct platform_device *dev) { int i,ivendor, ikeycode; char buf[64]; int varlen = sizeof(buf); struct rmtctl_priv *priv; priv = kzalloc(sizeof(struct rmtctl_priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; platform_set_drvdata(dev, priv); // get codec type of cir device if (wmt_getsyspara("wmt.io.rmtctl", buf, &varlen) == 0) { sscanf(buf, "%d", &priv->codec); if (priv->codec < CIR_CODEC_NEC || priv->codec >= CIR_CODEC_MAX){ kfree(priv); return -EINVAL; } } else { kfree(priv); return -EINVAL; } // using assigned cir device? if (wmt_getsyspara("wmt.io.rmtctl.index", buf, &varlen) == 0) { sscanf(buf, "%d", &priv->vendor_index); if (priv->vendor_index >= ARRAY_SIZE(rmtctl_vendors) || rmtctl_vendors[priv->vendor_index].codec != priv->codec) priv->vendor_index = -1; priv->table_index = priv->vendor_index; } else priv->vendor_index = -1; // get last vendor code saved in uboot environment #if 0 if (wmt_getsyspara(WMT_RMTCTL_VENDOR_ENV, buf, &varlen) == 0){ sscanf(buf, "0x%x", &priv->vendor_code); priv->saved_vcode = priv->vendor_code; for (i = 0; i < ARRAY_SIZE(rmtctl_vendors); i++) { if (priv->vendor_code == rmtctl_vendors[i].vendor_code && rmtctl_vendors[i].codec == priv->codec) { priv->table_index = i; break; } } }else #endif priv->vendor_code = 0xffff; if (wmt_getsyspara("wmt.io.rmtctl.led", buf, &varlen) == 0) { sscanf(buf, "%d:%d", &priv->led.gpio, &priv->led.active); priv->led.enable = 1; } /* register an input device. */ if ((priv->idev = input_allocate_device()) == NULL) return -ENOMEM; set_bit(EV_KEY, priv->idev->evbit); for (ivendor = 0; ivendor < ARRAY_SIZE(rmtctl_vendors); ivendor++) { if (priv->codec == rmtctl_vendors[ivendor].codec) { for (ikeycode = 0; ikeycode < ARRAY_SIZE(rmtctl_vendors[ivendor].key_codes); ikeycode++) { if (rmtctl_vendors[ivendor].key_codes[ikeycode]) { set_bit(rmtctl_vendors[ivendor].key_codes[ikeycode], priv->idev->keybit); } } } } priv->idev->name = "rmtctl"; priv->idev->phys = "rmtctl"; input_register_device(priv->idev); rmtctl_rel_init(&priv->rel_dev); if(priv->led.enable){ gpio_request(priv->led.gpio,"rmtctl-led"); rmtctl_led_init(&priv->led); } INIT_DELAYED_WORK(&priv->delaywork, rmtctl_refresh_vendorcode); init_timer(&priv->timer); priv->timer.data = (unsigned long)priv->idev; priv->timer.function = rmtctl_timer_handler; /* Register an ISR */ request_irq(IRQ_CIR, rmtctl_interrupt, IRQF_SHARED, "rmtctl", priv); /* Initial H/W */ rmtctl_hw_init((unsigned long)priv); if (RMTCTL_DEBUG) printk("WonderMedia rmtctl driver v0.98 initialized: ok\n"); return 0; } static int rmtctl_remove(struct platform_device *dev) { struct rmtctl_priv *priv = platform_get_drvdata(dev); if (RMTCTL_DEBUG) printk("rmtctl_remove\n"); del_timer_sync(&priv->timer); cancel_delayed_work_sync(&priv->delaywork); del_timer_sync(&priv->rel_dev.timer); input_unregister_device(priv->rel_dev.input); input_free_device(priv->rel_dev.input); if(priv->led.enable){ del_timer_sync(&priv->led.timer); gpio_direction_output(priv->led.gpio, priv->led.active); gpio_free(priv->led.gpio); } free_irq(IRQ_CIR, priv); input_unregister_device(priv->idev); input_free_device(priv->idev); kfree(priv); return 0; } #ifdef CONFIG_PM static int rmtctl_suspend(struct platform_device *dev, pm_message_t state) { struct rmtctl_priv *priv = platform_get_drvdata(dev); del_timer_sync(&priv->timer); cancel_delayed_work_sync(&priv->delaywork); del_timer_sync(&priv->rel_dev.timer); if(priv->led.enable){ priv->led.on = 0; del_timer(&priv->led.timer); gpio_direction_output(priv->led.gpio, priv->led.active); } /* Nothing to suspend? */ rmtctl_hw_init((unsigned long)priv); rmtctl_hw_suspend((unsigned long)priv); disable_irq(IRQ_CIR); /* Enable rmt wakeup */ PMWE_VAL |= (1 << WKS_CIR); return 0; } static int rmtctl_resume(struct platform_device *dev) { volatile unsigned int regval; int i =0 ; struct rmtctl_priv *priv = platform_get_drvdata(dev); /* Initial H/W */ REG32_VAL(WAKEUP_CTRL) &=~ BIT0; for (i=0;i<10;i++) { regval = REG32_VAL(WAKEUP_STS) ; if (regval & BIT0){ REG32_VAL(WAKEUP_STS) |= BIT4; }else{ break; } msleep_interruptible(5); } regval = REG32_VAL(WAKEUP_STS) ; if (regval & BIT0) printk("CIR resume NG WAKEUP_STS 0x%08x \n",regval); rmtctl_hw_init((unsigned long)priv); if(priv->led.enable) rmtctl_led_init(&priv->led); enable_irq(IRQ_CIR); return 0; } #else #define rmtctl_suspend NULL #define rmtctl_resume NULL #endif static struct platform_driver rmtctl_driver = { .driver.name = "wmt-rmtctl", //.bus = &platform_bus_type, //.probe = rmtctl_probe, .remove = rmtctl_remove, .suspend = rmtctl_suspend, .resume = rmtctl_resume }; static void rmtctl_release(struct device *dev) { /* Nothing to release? */ } static u64 rmtctl_dmamask = 0xffffffff; static struct platform_device rmtctl_device = { .name = "wmt-rmtctl", .id = 0, .dev = { .release = rmtctl_release, .dma_mask = &rmtctl_dmamask, }, .num_resources = 0, .resource = NULL, }; static int __init rmtctl_init(void) { int ret; if (platform_device_register(&rmtctl_device)) return -1; ret = platform_driver_probe(&rmtctl_driver, rmtctl_probe); return ret; } static void __exit rmtctl_exit(void) { platform_driver_unregister(&rmtctl_driver); platform_device_unregister(&rmtctl_device); } module_init(rmtctl_init); module_exit(rmtctl_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("WonderMedia Technologies, Inc."); MODULE_DESCRIPTION("WMT [Remoter] driver");