563 lines
16 KiB
C
563 lines
16 KiB
C
/*****************************************************************
|
|
* CAPI 2.0 Library *
|
|
* All parts are distributed under the terms of GPL. See COPYING *
|
|
*****************************************************************/
|
|
|
|
/**
|
|
* \file capi_mod_fritzbox.c
|
|
* \brief Capi Over TCP for FRITZ!Box or compatible routers
|
|
* \author Jan-Michael Brummer, based upon the work by Marco Zissen
|
|
*/
|
|
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <linux/capi.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include "capi20.h"
|
|
#include "capi_mod.h"
|
|
#include "capi_mod_fritzbox.h"
|
|
|
|
/**
|
|
* \brief Add standard fritzbox capi over tcp header to buffer pointer
|
|
* \param ppnPtr data buffer pointer
|
|
* \param nLen length of message
|
|
* \param nApplId application id
|
|
* \param nCommand packet command
|
|
*/
|
|
static void fritzboxSetHeader( unsigned char **ppnPtr, short nLen, short nApplId, short nCommand ) {
|
|
/* CAPI-Over-TCP Header (3 Bytes): */
|
|
/* TYPE: Message */
|
|
put_byte( ppnPtr, 0x80 );
|
|
/* Length of whole message */
|
|
put_word( ppnPtr, nLen );
|
|
|
|
/* CAPI-Message Header: */
|
|
/* Length */
|
|
put_word( ppnPtr, nLen );
|
|
/* Application ID */
|
|
put_word( ppnPtr, nApplId );
|
|
/* Command */
|
|
put_netword( ppnPtr, nCommand );
|
|
/* Message-Number 0x01 */
|
|
put_word( ppnPtr, 0x01 );
|
|
/* Default */
|
|
put_byte( ppnPtr, 0x02 );
|
|
put_byte( ppnPtr, 0x00 );
|
|
}
|
|
|
|
/**
|
|
* \brief Send message to socket and wait for response
|
|
* \param nHandle socket handle
|
|
* \param pnBuffer data buffer pointer
|
|
* \param nWrite number of bytes to write from pnBuffer
|
|
* \return number of bytes read
|
|
*/
|
|
static int fritzboxRemoteCommand( int nHandle, unsigned char *pnBuffer, int nWrite ) {
|
|
unsigned char anTemp[ 2 ];
|
|
int nNum, nType, nLen;
|
|
|
|
/* write message to socket */
|
|
nNum = write( nHandle, pnBuffer, nWrite );
|
|
if ( nNum != nWrite ) {
|
|
return 0;
|
|
}
|
|
|
|
/* try to read header */
|
|
nNum = read( nHandle, anTemp, 2 );
|
|
if ( nNum == 2 ) {
|
|
nType = anTemp[ 0 ];
|
|
nLen = anTemp[ 1 ];
|
|
|
|
/* ok, we have a header... now the rest */
|
|
nNum = read( nHandle, pnBuffer, nLen );
|
|
if ( nNum != nLen ) {
|
|
CapiDebug( 1, "[%s]: read got %x, want %x", __FUNCTION__, nNum, nLen );
|
|
return 0;
|
|
}
|
|
|
|
return nNum;
|
|
} else {
|
|
CapiDebug( 1, "[%s]: read got %x, want 2", __FUNCTION__, nNum );
|
|
}
|
|
|
|
/* uhm, error. return 0 */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Create a socket to hostname:port
|
|
* \return socket number
|
|
*/
|
|
static int fritzboxOpenSocket( void ) {
|
|
int nHandle;
|
|
struct hostent *psHostInfo;
|
|
struct sockaddr_in sAddr;
|
|
|
|
/* Create new socket */
|
|
nHandle = socket( PF_INET, SOCK_STREAM, 0 );
|
|
if ( nHandle == -1 ) {
|
|
CapiDebug( 1, "[%s]: Could not create socket! (%d)", __FUNCTION__, nHandle );
|
|
return nHandle;
|
|
}
|
|
|
|
sAddr.sin_family = PF_INET;
|
|
sAddr.sin_port = htons( getPort() );
|
|
/* Retrieve hostname information */
|
|
psHostInfo = gethostbyname( getHostName() );
|
|
if ( psHostInfo != NULL ) {
|
|
sAddr.sin_addr = *( struct in_addr * ) psHostInfo -> h_addr;
|
|
|
|
/* Connect socket to address */
|
|
if ( !connect( nHandle, ( struct sockaddr * ) &sAddr, sizeof( sAddr ) ) ) {
|
|
/* no errors, return handle */
|
|
return nHandle;
|
|
}
|
|
CapiDebug( 1, "[%s]: Could not connect to port %d on '%s'. CapiOverTCP enabled??", __FUNCTION__, getPort(), getHostName() );
|
|
} else {
|
|
CapiDebug( 1, "[%s]: Could not resolve host '%s'", __FUNCTION__, getHostName() );
|
|
}
|
|
|
|
/* error occured, either we could not connect to port or we couldn't find hostname
|
|
* close socket and return -1 as error
|
|
*/
|
|
close( nHandle );
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if fritzbox interface is available
|
|
* \return file descriptor of socket (see fritzboxOpenSocket for more informations)
|
|
*/
|
|
static unsigned fritzboxIsInstalled( void ) {
|
|
unsigned nHandle;
|
|
char *pnHostName;
|
|
|
|
/* Check if we have valid data for hostname and port */
|
|
pnHostName = getHostName();
|
|
if ( strlen( pnHostName ) <= 0 ) {
|
|
setHostName( "fritz.box" );
|
|
}
|
|
if ( getPort() == -1 ) {
|
|
setPort( 5031 );
|
|
}
|
|
|
|
/* we check if we can open a new socket to hostname:port */
|
|
nHandle = fritzboxOpenSocket();
|
|
|
|
/* return new handle or error code */
|
|
return nHandle;
|
|
}
|
|
|
|
/**
|
|
* \brief Register at fritzbox
|
|
* \param nMaxB3Connection maximum b3 connection
|
|
* \param nMaxB3Blks maximum b3 blocks
|
|
* \param nMaxSizeB3 maximum b3 size
|
|
* \param pnApplId pointer where we store the new application id
|
|
* \return new socket handle
|
|
*/
|
|
static unsigned fritzboxRegister( unsigned nMaxB3Connection, unsigned nMaxB3Blks, unsigned nMaxSizeB3, unsigned *pnApplId ) {
|
|
unsigned char anMessage[ 256 ];
|
|
unsigned char *pnPtr = anMessage;
|
|
int nSock, nError;
|
|
|
|
/* Safety: Set application id to invalid (-1) */
|
|
*pnApplId = -1;
|
|
|
|
/* open a new socket for communication */
|
|
nSock = fritzboxOpenSocket();
|
|
if ( nSock < 0 ) {
|
|
return nSock;
|
|
}
|
|
|
|
/* Setup registration message packet */
|
|
fritzboxSetHeader( &pnPtr, 20, 0x0000, CAPICMD( 0x20, CAPI_REQ ) );
|
|
|
|
/* Parameter length */
|
|
put_byte( &pnPtr, 0x09 );
|
|
|
|
/* The Parameters: */
|
|
|
|
/* Buffer pointer (currently not used), 2bytes*/
|
|
put_byte( &pnPtr, 0x00 );
|
|
put_byte( &pnPtr, 0x00 );
|
|
/* Message buffer size */
|
|
put_byte( &pnPtr, 0x06 );
|
|
/* Maximum number of logical connections */
|
|
put_word( &pnPtr, nMaxB3Connection );
|
|
/* Number of data blocks available simultaneously */
|
|
put_word( &pnPtr, nMaxB3Blks );
|
|
/* Maximum size of a data block */
|
|
put_word( &pnPtr, nMaxSizeB3 );
|
|
/* Capi version?? */
|
|
|
|
/* Send message to socket and wait for answer */
|
|
if ( !( fritzboxRemoteCommand( nSock, anMessage, 23 ) ) ) {
|
|
/* Error occured, close socket and return error code -2 */
|
|
CapiDebug( 1, "Error: Unable to register CAPI! (ApplId: %d, MaxB3Con: %d, MaxB3Blks: %d, MaxB3Size: %d)", *pnApplId, nMaxB3Connection, nMaxB3Blks, nMaxSizeB3 );
|
|
close( nSock );
|
|
return -2;
|
|
}
|
|
|
|
nError = CAPIMSG_U16( anMessage, 16 );
|
|
if ( nError != 0 ) {
|
|
CapiDebug( 1, "Error: Unable to register CAPI! Error %x\n", nError );
|
|
close( nSock );
|
|
return -3;
|
|
}
|
|
|
|
/* Fine, we have a new application id, set it to pnApplId and return socket handle */
|
|
*pnApplId = CAPIMSG_APPID( anMessage );
|
|
CapiDebug( 1, "Successfully registered CAPI (ApplId: %d, MaxB3Con: %d, MaxB3Blks: %d, MaxB3Size: %d)", *pnApplId, nMaxB3Connection, nMaxB3Blks, nMaxSizeB3 );
|
|
|
|
return nSock;
|
|
}
|
|
|
|
/**
|
|
* \brief Release fritzbox connection
|
|
* \param nSock socket handle
|
|
* \param nApplId registered application id
|
|
* \return error code (CapiNoError or CapiRegNotInstalled on error)
|
|
*/
|
|
static unsigned fritzboxRelease( int nSock, int nApplId ) {
|
|
unsigned char anMessage[ 256 ];
|
|
unsigned char *pnPtr = anMessage;
|
|
|
|
/* Setup release message packet */
|
|
fritzboxSetHeader( &pnPtr, 14, nApplId, CAPICMD( 0x20, CAPI_REQ ) );
|
|
|
|
/* Parameter length */
|
|
put_byte( &pnPtr, 0x03 );
|
|
|
|
/* Parameters */
|
|
put_byte( &pnPtr, 0x01 );
|
|
put_byte( &pnPtr, 0x00 );
|
|
put_byte( &pnPtr, 0x00 );
|
|
|
|
/* write message to socket */
|
|
if( write( nSock, anMessage, 17 ) != 17 ) {
|
|
return 0;
|
|
}
|
|
|
|
/* try to read header */
|
|
read( nSock, anMessage, sizeof( anMessage ) );
|
|
|
|
/* well done, return no error */
|
|
CapiDebug( 3, "Successfully released CAPI (ApplId: %d)", nApplId );
|
|
|
|
return CapiNoError;
|
|
}
|
|
|
|
/**
|
|
* \brief Put capi message to fritzbox
|
|
* \param nSock socket handle
|
|
* \param nApplId application id
|
|
* \param pnMsg message pointer
|
|
* \return error code (CapiNoError or CapiMsgOSResourceErr on error)
|
|
*/
|
|
static unsigned fritzboxPutMessage( int nSock, unsigned nApplId, unsigned char *pnMsg ) {
|
|
unsigned char anSendBuffer[ SEND_BUFSIZ ];
|
|
int nNum;
|
|
int nLen = CAPIMSG_LEN( pnMsg );
|
|
int nCommand = CAPIMSG_COMMAND( pnMsg );
|
|
int nSubCommand = CAPIMSG_SUBCOMMAND( pnMsg );
|
|
|
|
nLen = capi_processMessage( pnMsg, nApplId, nCommand, nSubCommand, nLen );
|
|
|
|
/* Create capi over tcp header (0x80, 2 bytes length) */
|
|
anSendBuffer[ 0 ] = 0x80;
|
|
anSendBuffer[ 1 ] = nLen & 0xFF;
|
|
anSendBuffer[ 2 ] = ( nLen >> 8 ) & 0xFF;
|
|
|
|
if ( nCommand == CAPI_DATA_B3 && nSubCommand == CAPI_REQ ) {
|
|
/* Special case: Set buffer address to zero */
|
|
pnMsg[ 12 ] = 0x00;
|
|
pnMsg[ 13 ] = 0x00;
|
|
pnMsg[ 14 ] = 0x00;
|
|
pnMsg[ 15 ] = 0x00;
|
|
}
|
|
|
|
/* attach capi message after header */
|
|
memcpy( anSendBuffer + 3, pnMsg, sizeof( anSendBuffer ) - 3 );
|
|
nLen += 3;
|
|
|
|
/* write data to socket */
|
|
nNum = write( nSock, anSendBuffer, nLen );
|
|
if ( nNum != nLen ) {
|
|
CapiDebug( 3, "Error: Unable send CAPI_PUT_MESSAGE (nApplId: %d, Ctrl: %d, Cmd: %d, SubCmd: %d)", nApplId, anSendBuffer[ 11 ] & 0x7F, CAPIMSG_COMMAND( pnMsg ), CAPIMSG_SUBCOMMAND( pnMsg ) );
|
|
return CapiMsgOSResourceErr;
|
|
}
|
|
|
|
CapiDebug( 3, "CAPI_PUT_MESSAGE (nApplId: %d, Ctrl: %d, Cmd: %d, SubCmd: %d)", nApplId, anSendBuffer[ 11 ] & 0x7F, CAPIMSG_COMMAND( pnMsg ), CAPIMSG_SUBCOMMAND( pnMsg ) );
|
|
return CapiNoError;
|
|
}
|
|
|
|
/**
|
|
* \brief Read data from socket
|
|
* \param nSock socket handle
|
|
* \param pnBuffer buffer pointer
|
|
* \param nLen length of buffer
|
|
* \return read len
|
|
*/
|
|
static int readSocket( int nSock, unsigned char *pnBuffer, int nLen ) {
|
|
unsigned char anTemp[ 4096 ], *pnPtr;
|
|
int nTotLen, nReadLen, nActLen, nOrigLen, nType;
|
|
time_t nTime;
|
|
|
|
/* try to read the CAPI over TCP Header, 3 bytes */
|
|
if ( read( nSock, anTemp, 3 ) == 3 ) {
|
|
pnPtr = anTemp;
|
|
|
|
/* Get message type and the total length of message */
|
|
nType = CAPIMSG_U8( anTemp, 0 );
|
|
nTotLen = nOrigLen = CAPIMSG_U16( anTemp, 1 );
|
|
nTime = time( NULL );
|
|
nReadLen = 0;
|
|
|
|
/* read message */
|
|
while ( ( ( nActLen = read( nSock, &anTemp[ nReadLen ], nTotLen ) ) < nTotLen ) && ( time( NULL ) < ( nTime + 5 ) ) ) {
|
|
if ( nActLen > 0 ) {
|
|
nTotLen -= nActLen;
|
|
nReadLen += nActLen;
|
|
}
|
|
|
|
nActLen = 0;
|
|
}
|
|
|
|
if ( nActLen > 0 ) {
|
|
nReadLen += nActLen;
|
|
}
|
|
|
|
if ( nReadLen != nOrigLen ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( nLen == 0 ) {
|
|
nLen = nOrigLen;
|
|
}
|
|
|
|
nOrigLen = ( nLen < nReadLen ) ? nLen : nReadLen;
|
|
|
|
/* copy informations to buffer, cut to nLen if needed */
|
|
memcpy( pnBuffer, anTemp, nOrigLen );
|
|
return nOrigLen;
|
|
}
|
|
|
|
/* could not read header, return 0 */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Get message from fritzbox
|
|
* \param nSock socket handle
|
|
* \param nApplId application id
|
|
* \param ppnBuffer pointer to data buffer pointer (where we store the data)
|
|
* \return error code
|
|
*/
|
|
static unsigned fritzboxGetMessage( int nSock, unsigned nApplId, unsigned char **ppnBuffer ) {
|
|
int nRet;
|
|
unsigned char *pnBuffer;
|
|
unsigned nOffset;
|
|
size_t nBufSize;
|
|
|
|
/* try to get a new buffer from queue */
|
|
if ( ( *ppnBuffer = pnBuffer = ( unsigned char * ) capi_get_buffer( nApplId, &nBufSize, &nOffset ) ) == 0 ) {
|
|
CapiDebug( 1, "[%s]: could not get buffer\n", __FUNCTION__ );
|
|
return CapiMsgOSResourceErr;
|
|
}
|
|
|
|
/* Get CAPI-over-TCP Header */
|
|
nRet = readSocket( nSock, pnBuffer, nBufSize );
|
|
if ( nRet > 0 ) {
|
|
/* workaround for old driver */
|
|
CAPIMSG_SETAPPID( pnBuffer, nApplId );
|
|
|
|
/* DATA_B3? Then set buffer address */
|
|
if ( CAPIMSG_COMMAND( pnBuffer ) == CAPI_DATA_B3 && CAPIMSG_SUBCOMMAND( pnBuffer ) == CAPI_IND ) {
|
|
capi_save_datahandle( nApplId, nOffset, CAPIMSG_U16( pnBuffer, 18 ), CAPIMSG_U32( pnBuffer, 8 ) );
|
|
/* patch datahandle */
|
|
capimsg_setu16( pnBuffer, 18, nOffset );
|
|
|
|
if ( sizeof( void * ) == 4 ) {
|
|
/* 32bit case */
|
|
u_int32_t nData = ( ( ( u_int32_t ) pnBuffer ) + CAPIMSG_LEN( pnBuffer ) );
|
|
|
|
pnBuffer[ 12 ] = ( ( unsigned char ) nData & 0xFF );
|
|
pnBuffer[ 13 ] = ( ( unsigned char )( nData >> 8 ) & 0xFF );
|
|
pnBuffer[ 14 ] = ( ( unsigned char )( nData >> 16 ) & 0xFF );
|
|
pnBuffer[ 15 ] = ( ( unsigned char )( nData >> 24 ) & 0xFF );
|
|
} else {
|
|
/* 64bit case */
|
|
u_int64_t nData;
|
|
|
|
if ( CAPIMSG_LEN( pnBuffer ) < 30 ) {
|
|
memmove( pnBuffer + 30, pnBuffer + CAPIMSG_LEN( pnBuffer ), CAPIMSG_DATALEN( pnBuffer ) );
|
|
pnBuffer[ 0 ] = 30;
|
|
pnBuffer[ 1 ] = 0;
|
|
}
|
|
|
|
nData = ( ( ( ulong ) pnBuffer ) + CAPIMSG_LEN( pnBuffer ) );
|
|
pnBuffer[ 12 ] = pnBuffer[ 13 ] = pnBuffer[ 14 ] = pnBuffer[ 15 ] = 0;
|
|
pnBuffer[ 22 ] = nData & 0xFF;
|
|
pnBuffer[ 23 ] = ( nData >> 8 ) & 0xFF;
|
|
pnBuffer[ 24 ] = ( nData >> 16 ) & 0xFF;
|
|
pnBuffer[ 25 ] = ( nData >> 24 ) & 0xFF;
|
|
pnBuffer[ 26 ] = ( nData >> 32 ) & 0xFF;
|
|
pnBuffer[ 27 ] = ( nData >> 40 ) & 0xFF;
|
|
pnBuffer[ 28 ] = ( nData >> 48 ) & 0xFF;
|
|
pnBuffer[ 29 ] = ( nData >> 56 ) & 0xFF;
|
|
}
|
|
|
|
CapiDebug( 3, "CAPI_GET_MESSAGE (nApplId: %d, Ctrl: %d)", nApplId, pnBuffer[ 8 ] & 0x7F );
|
|
|
|
/* keep buffer */
|
|
return CapiNoError;
|
|
}
|
|
|
|
/* buffer is not needed, return it */
|
|
capi_return_buffer( nApplId, nOffset );
|
|
|
|
if ( ( CAPIMSG_COMMAND( pnBuffer ) == CAPI_DISCONNECT ) && ( CAPIMSG_SUBCOMMAND( pnBuffer ) == CAPI_IND ) ) {
|
|
/* we got a disconnect, cleanup buffers */
|
|
cleanup_buffers_for_plci( nApplId, CAPIMSG_U32( pnBuffer, 8 ) );
|
|
}
|
|
|
|
return CapiNoError;
|
|
}
|
|
|
|
/* uh, error occured while reading capi message, return buffer and check for errors */
|
|
capi_return_buffer( nApplId, nOffset );
|
|
if ( nRet == 0 ) {
|
|
return CapiReceiveQueueEmpty;
|
|
}
|
|
|
|
switch ( errno ) {
|
|
case EMSGSIZE:
|
|
nRet = CapiIllCmdOrSubcmdOrMsgToSmall;
|
|
break;
|
|
case EAGAIN:
|
|
nRet = CapiReceiveQueueEmpty;
|
|
break;
|
|
default:
|
|
nRet = CapiMsgOSResourceErr;
|
|
break;
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
/**
|
|
* \brief Get manufactor informations
|
|
* \param nHandle socket handle
|
|
* \param nController controller id
|
|
* \param pnBuffer buffer pointer we write our informations to
|
|
* \return pnBuffer
|
|
*/
|
|
static unsigned char *fritzboxGetManufactor( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
|
|
/* set manufactor informations into request buffer */
|
|
memcpy( pnBuffer, CAPI20_FB_MANUF, strlen( CAPI20_FB_MANUF ) );
|
|
pnBuffer[ strlen( CAPI20_FB_MANUF ) ] = '\0';
|
|
|
|
return pnBuffer;
|
|
}
|
|
|
|
/**
|
|
* \brief Get version informations
|
|
* \param nHandle socket handle
|
|
* \param nController controller id
|
|
* \param pnBuffer buffer pointer we write our informations to
|
|
* \return pnBuffer
|
|
*/
|
|
static unsigned char *fritzboxGetVersion( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
|
|
capi_version cversion;
|
|
|
|
/* set version information */
|
|
cversion.majorversion = 2;
|
|
cversion.minorversion = 0;
|
|
cversion.majormanuversion = CAPI20_FB_VERSION_MAJOR;
|
|
cversion.minormanuversion = CAPI20_FB_VERSION_MINOR;
|
|
|
|
memcpy( pnBuffer, &cversion, sizeof( capi_version ) );
|
|
|
|
return pnBuffer;
|
|
}
|
|
|
|
/**
|
|
* \brief Get serial number informations
|
|
* \param nHandle socket handle
|
|
* \param nController controller id
|
|
* \param pnBuffer buffer pointer we write our informations to
|
|
* \return pnBuffer
|
|
*/
|
|
static unsigned char *fritzboxGetSerialNumber( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
|
|
/* set serial number */
|
|
memcpy( pnBuffer, CAPI20_FB_SERIAL, strlen( CAPI20_FB_SERIAL ) );
|
|
pnBuffer[ strlen( CAPI20_FB_SERIAL ) ] = 0;
|
|
|
|
return pnBuffer;
|
|
}
|
|
|
|
/**
|
|
* \brief Get profile from fritzbox
|
|
* \param nHandle socket handle
|
|
* \param nControllerId controller
|
|
* \param pnBuf buffer
|
|
* \return error code
|
|
*/
|
|
static unsigned fritzboxGetProfile( int nHandle, unsigned nController, unsigned char *pnBuf ) {
|
|
unsigned char anMessage[ 256 ];
|
|
unsigned char *pnPtr = anMessage;
|
|
|
|
memset( pnPtr, 0, sizeof( anMessage ) );
|
|
|
|
/* we want a get profile message */
|
|
fritzboxSetHeader( &pnPtr, 0x12, 0x0000, CAPICMD( 0x20, CAPI_REQ ) );
|
|
|
|
/* Parameter length */
|
|
put_byte( &pnPtr, 0x05 );
|
|
|
|
/* Default (0x02, 0x00) */
|
|
put_byte( &pnPtr, 0x02 );
|
|
put_byte( &pnPtr, 0x00 );
|
|
/* Param size */
|
|
put_byte( &pnPtr, 0x02 );
|
|
/* Controller */
|
|
put_word( &pnPtr, nController );
|
|
|
|
/* send message packet and read answer */
|
|
if ( !( fritzboxRemoteCommand( nHandle, anMessage, 21 ) ) ) {
|
|
/* wuuh, an error occurred, return error */
|
|
return CapiMsgOSResourceErr;
|
|
}
|
|
|
|
/* copy capi profile information to buffer */
|
|
memcpy( pnBuf, anMessage + 18, sizeof( struct capi_profile ) );
|
|
|
|
return CapiNoError;
|
|
}
|
|
|
|
/** Module operations structure */
|
|
static struct sModuleOperations sFritzBox = {
|
|
fritzboxIsInstalled,
|
|
fritzboxRegister,
|
|
fritzboxRelease,
|
|
fritzboxPutMessage,
|
|
fritzboxGetMessage,
|
|
fritzboxGetManufactor,
|
|
fritzboxGetVersion,
|
|
fritzboxGetSerialNumber,
|
|
fritzboxGetProfile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
MODULE_INIT( "fritzbox", &sFritzBox );
|