/* * btncpyctl.c - tool to manipulate the /dev/btncpy device on a ZyXEL nas * * Copyright (c) 2014 Mijzelf. * * 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. * */ /* * A tool to intercept the Copy button function. But it can do more, * like dis- and enabling WOL on a 325. The tool is based on the * ZyXEL GPL kernel sources, which implements 'the other end'. * * Change Log: * --------------- * * Version 1.1 2015-11-02 * * Added: * - Version * - An option to choose a different device node. The NAS500 uses /dev/gpio * instead of /dev/btncpy * * Version 1.0 2014-11-09 * * Initial version * */ #include #include #include #include #include #include #include #define PROGRAM_VERSION "1.1 (2015-11-02)" #define BTNCPY_IOC_MAGIC 'g' #define BTNCPY_IOC_SET_NUM _IO(BTNCPY_IOC_MAGIC, 1) #define LED_SET_CTL_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 2) #define LED_GET_CTL_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 3) #define BUZ_SET_CTL_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 4) #define SPR_HD0_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 5) #define GPOUT_BLK_QUERY_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 6) #define BTN_POWER_ENABLE _IO(BTNCPY_IOC_MAGIC, 7) #define BTN_RESET_ENABLE _IO(BTNCPY_IOC_MAGIC, 8) #define BUTTON_TEST_IN_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 9) #define BUTTON_TEST_OUT_IOC_NUM _IO(BTNCPY_IOC_MAGIC, 10) #define WOL_ICO_ENABLE _IO(BTNCPY_IOC_MAGIC, 22) #define POWER_RESUME_SET _IO(BTNCPY_IOC_MAGIC, 32) #define POWER_RESUME_CLR _IO(BTNCPY_IOC_MAGIC, 33) #define LED_COLOR_BITS 0 // 0,1 #define LED_STATE_BITS 2 // 2,3 #define LED_NUM_BITS 4 // 4,5 #define LED_STATE_MAP_ADDR(led_num, led_state, led_color) \ ((led_num & 0xf) << LED_NUM_BITS) | ((led_state & 0x3) << LED_STATE_BITS) | ((led_color & 0x3) << LED_COLOR_BITS) #define GET_LED_INDEX(map_addr) ((map_addr >> LED_NUM_BITS) & 0xf) #define GET_LED_COLOR(map_addr) (map_addr & 0x3) #define GET_LED_STATE(map_addr) ((map_addr >> LED_STATE_BITS) & 0x3) #define BZ_TIMER_PERIOD (HZ/2) #define TIME_BITS 5 #define FREQ_BITS 4 #define STATE_BITS 2 #define TIME_MASK (0x1F << (FREQ_BITS + STATE_BITS)) #define FREQ_MASK (0xF << STATE_BITS) #define STATE_MASK 0x3 #define GET_TIME(addr) ((addr & TIME_MASK) >> (FREQ_BITS + STATE_BITS)) #define GET_FREQ(addr) ((addr & FREQ_MASK) >> STATE_BITS) #define GET_STATE(addr) (addr & STATE_MASK) #define BUZ_GPIO_MAP_ADDR(time, freq, state) \ ((time & 0x1f) << (FREQ_BITS + STATE_BITS) | ((freq & 0xf) << STATE_BITS) | (state & 0x3)) typedef struct { int cmd, numargs, retvalue; const char *name, *desc; } COMMAND; static const COMMAND _commands[] = { { BTNCPY_IOC_SET_NUM, 1, 0, "BTNCPY_IOC_SET_NUM", "pid\n" "\t\tSet the pid of the btncpy daemon. A signal 10 is send when the copy button is pressed shortly,\n" "\t\ta signal 12 is send when the button is pressed long. (In both cases at release of the button." }, { LED_SET_CTL_IOC_NUM, 3, 0, "LED_SET_CTL_IOC_NUM", "index color state\n" "\t\tSet led. (index < 16, color < 4, state < 4)" }, { LED_GET_CTL_IOC_NUM, 1, 1, "LED_GET_CTL_IOC_NUM", "index\n" "\t\tGet led status" }, { BUZ_SET_CTL_IOC_NUM, 3, 0, "BUZ_SET_CTL_IOC_NUM", "time freq state\n" "\t\tSet buzzer state (time < 32, freq < 16, state < 4)" }, { BUTTON_TEST_IN_IOC_NUM, 2, 0, "BUTTON_TEST_IN_IOC_NUM", "pid button\n" "\t\tSend signal 10 to pid when button is pressed (0 reset, 1 copy, 2 power)\n" "\t\tNote: this resets the 'BTNCPY_IOC_SET_NUM' pid" }, { BUTTON_TEST_OUT_IOC_NUM, 0, 0, "BUTTON_TEST_OUT_IOC_NUM", "\n" "\t\tResets 'BUTTON_TEST_OUT_IOC_NUM'" }, { SPR_HD0_IOC_NUM, 1, 0, "SPR_HD0_IOC_NUM", "arg\n\t\t?" }, { GPOUT_BLK_QUERY_IOC_NUM, 1, 1, "GPOUT_BLK_QUERY_IOC_NUM", "arg\n\t\t?" }, { BTN_POWER_ENABLE, 1, 0, "BTN_POWER_ENABLE", "0|1\n" "\t\tDis- or enable power button?" }, { BTN_RESET_ENABLE, 1, 0, "BTN_RESET_ENABLE", "0|1\n" "\t\tDis- or enable reset button?" }, { WOL_ICO_ENABLE, 1, 0, "WOL_ICO_ENABLE", "0|1\n" "\t\tDis- or enable wol on shutdown." }, { POWER_RESUME_SET, 0, 0, "POWER_RESUME_SET", "\n" "\t\tSet in low power mode?" }, { POWER_RESUME_CLR, 0, 0, "POWER_RESUME_CLR", "\n" "\t\tRelease from low power mode?" }, { 0, } }; void help() { int i; printf( "A tool to control the /dev/btncpy device on a ZyXEL nas.\n" "version " PROGRAM_VERSION "\n" "\n" "Usage: btncpyctl command \n" "Commands:\n" ); for( i=0; _commands[ i ].cmd; i++ ) printf( "\t%s %s\n", _commands[ i ].name, _commands[ i ].desc ); printf( "Options:\n" "\t-d : Alternate device node to use.\n" "\t\tBy default /dev/btncpy. Should be /dev/gpio on a NAS5xx\n" "\t-q: Quiet mode.\n" "\t-v: Print version and exit.\n" "\t-h: This page.\n" "\n" "Note: not all devices support all commands\n" ); exit( 0 ); } const COMMAND *FindCommand( const char *name ) { const COMMAND *pret = 0; int i; size_t len = strlen( name ); if( 0 == len ) return 0; for( i=0; _commands[ i ].cmd; i++ ) if( 0 == strncasecmp( name, _commands[ i ].name, len ) ) { if( 0 != pret ) { printf( "Ambiguous command %s\n", name ); return 0; } pret = &_commands[ i ]; } if( pret ) return pret; printf( "Unrecognized command %s\n", name ); return 0; } int main( int argc, char **argv ) { int i, quiet=0, args[ 4 ] = { 0, }, numargs=0; const COMMAND *pcommand=0; char *devicenode=strdup("/dev/btncpy"); for( i=1; i= argc ) { printf( "-d needs an argument\n" ); help(); break; } devicenode=strdup( argv[ i + 1 ] ); i++; break; case 'h': case '?': help(); break; case 'v': printf( PROGRAM_VERSION "\n" ); exit( 0 ); case 'q': quiet=1; break; default: printf( "Unknown option %c\n", argv[ i ][ 1 ] ); help(); break; } } else if( 0 == pcommand ) { pcommand = FindCommand( argv[ i ] ); if( 0 == pcommand ) { help(); } } else { if( numargs >= pcommand->numargs ) printf( "%s doesn't take %d args\n", pcommand->name, numargs + 1 ); args[ numargs++ ] = atoi( argv[ i ] ); } } if( 0 == pcommand ) { help(); } else { if( numargs != pcommand->numargs ) { printf( "%s needs %d arguments\n", pcommand->name, pcommand->numargs ); help(); } int fdes = open( devicenode, O_RDWR ), ret; if( 0 > fdes ) { if( !quiet ) printf( "Cannot open %s. Error %d\n", devicenode, fdes ); return fdes; } switch( pcommand->cmd ) { default: ret = ioctl( fdes, pcommand->cmd, args[ 0 ] ); break; case LED_SET_CTL_IOC_NUM: ret = ioctl( fdes, pcommand->cmd, LED_STATE_MAP_ADDR( args[ 0 ], args[ 1 ], args[ 2 ] ) ); break; case BUTTON_TEST_IN_IOC_NUM: ret = ioctl( fdes, pcommand->cmd, (args[ 0 ] << 3) | (args[ 1 ] & 0x7) ); break; case BUZ_SET_CTL_IOC_NUM: ret = ioctl( fdes, pcommand->cmd, BUZ_GPIO_MAP_ADDR( args[ 0 ], args[ 1 ], args[ 2 ] ) ); } close( fdes ); if( pcommand->retvalue ) { if( !quiet ) printf( "Return value is %d\n", ret ); return ret; } else { if( 0 == ret ) { if( !quiet ) printf( "Succeeded\n" ); return 0; } if( !quiet ) printf( "Failed. Error %d\n", ret ); return ret; } } return 0; }