/******************************************************************************* * * Copyright (c) 2000-2003 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ #include "upnp_tv_device.h" #include #define DEFAULT_WEB_DIR "./web" #define DESC_URL_SIZE 200 /*! Device type for tv device. */ extern const char TvDeviceType[]; /*! Service types for tv services. */ extern const char *TvServiceType[]; /*! Global arrays for storing Tv Control Service variable names, values, * and defaults. */ const char *tvc_varname[] = { "Power", "Channel", "Volume" }; char tvc_varval[TV_CONTROL_VARCOUNT][TV_MAX_VAL_LEN]; const char *tvc_varval_def[] = { "1", "1", "5" }; /*! Global arrays for storing Tv Picture Service variable names, values, * and defaults. */ const char *tvp_varname[] = { "Color", "Tint", "Contrast", "Brightness" }; char tvp_varval[TV_PICTURE_VARCOUNT][TV_MAX_VAL_LEN]; const char *tvp_varval_def[] = { "5", "5", "5", "5" }; /*! The amount of time (in seconds) before advertisements will expire. */ int default_advr_expire = 100; /*! Global structure for storing the state table for this device. */ struct TvService tv_service_table[2]; /*! Device handle supplied by UPnP SDK. */ UpnpDevice_Handle device_handle = -1; /*! Mutex for protecting the global state table data * in a multi-threaded, asynchronous environment. * All functions should lock this mutex before reading * or writing the state table data. */ ithread_mutex_t TVDevMutex; /*! Color constants */ #define MAX_COLOR 10 #define MIN_COLOR 1 /*! Brightness constants */ #define MAX_BRIGHTNESS 10 #define MIN_BRIGHTNESS 1 /*! Power constants */ #define POWER_ON 1 #define POWER_OFF 0 /*! Tint constants */ #define MAX_TINT 10 #define MIN_TINT 1 /*! Volume constants */ #define MAX_VOLUME 10 #define MIN_VOLUME 1 /*! Contrast constants */ #define MAX_CONTRAST 10 #define MIN_CONTRAST 1 /*! Channel constants */ #define MAX_CHANNEL 100 #define MIN_CHANNEL 1 /*! * \brief Initializes the service table for the specified service. */ static int SetServiceTable( /*! [in] one of TV_SERVICE_CONTROL or, TV_SERVICE_PICTURE. */ IN int serviceType, /*! [in] UDN of device containing service. */ IN const char *UDN, /*! [in] serviceId of service. */ IN const char *serviceId, /*! [in] service type (as specified in Description Document) . */ IN const char *serviceTypeS, /*! [in,out] service containing table to be set. */ INOUT struct TvService *out) { unsigned int i = 0; strcpy( out->UDN, UDN ); strcpy( out->ServiceId, serviceId ); strcpy( out->ServiceType, serviceTypeS ); switch ( serviceType ) { case TV_SERVICE_CONTROL: out->VariableCount = TV_CONTROL_VARCOUNT; for( i = 0; i < tv_service_table[TV_SERVICE_CONTROL].VariableCount; i++ ) { tv_service_table[TV_SERVICE_CONTROL].VariableName[i] = tvc_varname[i]; tv_service_table[TV_SERVICE_CONTROL].VariableStrVal[i] = tvc_varval[i]; strcpy( tv_service_table[TV_SERVICE_CONTROL]. VariableStrVal[i], tvc_varval_def[i] ); } break; case TV_SERVICE_PICTURE: out->VariableCount = TV_PICTURE_VARCOUNT; for( i = 0; i < tv_service_table[TV_SERVICE_PICTURE].VariableCount; i++ ) { tv_service_table[TV_SERVICE_PICTURE].VariableName[i] = tvp_varname[i]; tv_service_table[TV_SERVICE_PICTURE].VariableStrVal[i] = tvp_varval[i]; strcpy( tv_service_table[TV_SERVICE_PICTURE]. VariableStrVal[i], tvp_varval_def[i] ); } break; default: assert( 0 ); } return SetActionTable( serviceType, out ); } int SetActionTable(int serviceType, struct TvService *out) { if( serviceType == TV_SERVICE_CONTROL ) { out->ActionNames[0] = "PowerOn"; out->actions[0] = TvDevicePowerOn; out->ActionNames[1] = "PowerOff"; out->actions[1] = TvDevicePowerOff; out->ActionNames[2] = "SetChannel"; out->actions[2] = TvDeviceSetChannel; out->ActionNames[3] = "IncreaseChannel"; out->actions[3] = TvDeviceIncreaseChannel; out->ActionNames[4] = "DecreaseChannel"; out->actions[4] = TvDeviceDecreaseChannel; out->ActionNames[5] = "SetVolume"; out->actions[5] = TvDeviceSetVolume; out->ActionNames[6] = "IncreaseVolume"; out->actions[6] = TvDeviceIncreaseVolume; out->ActionNames[7] = "DecreaseVolume"; out->actions[7] = TvDeviceDecreaseVolume; out->ActionNames[8] = NULL; return 1; } else if( serviceType == TV_SERVICE_PICTURE ) { out->ActionNames[0] = "SetColor"; out->ActionNames[1] = "IncreaseColor"; out->ActionNames[2] = "DecreaseColor"; out->actions[0] = TvDeviceSetColor; out->actions[1] = TvDeviceIncreaseColor; out->actions[2] = TvDeviceDecreaseColor; out->ActionNames[3] = "SetTint"; out->ActionNames[4] = "IncreaseTint"; out->ActionNames[5] = "DecreaseTint"; out->actions[3] = TvDeviceSetTint; out->actions[4] = TvDeviceIncreaseTint; out->actions[5] = TvDeviceDecreaseTint; out->ActionNames[6] = "SetBrightness"; out->ActionNames[7] = "IncreaseBrightness"; out->ActionNames[8] = "DecreaseBrightness"; out->actions[6] = TvDeviceSetBrightness; out->actions[7] = TvDeviceIncreaseBrightness; out->actions[8] = TvDeviceDecreaseBrightness; out->ActionNames[9] = "SetContrast"; out->ActionNames[10] = "IncreaseContrast"; out->ActionNames[11] = "DecreaseContrast"; out->actions[9] = TvDeviceSetContrast; out->actions[10] = TvDeviceIncreaseContrast; out->actions[11] = TvDeviceDecreaseContrast; return 1; } return 0; } int TvDeviceStateTableInit(char *DescDocURL) { IXML_Document *DescDoc = NULL; int ret = UPNP_E_SUCCESS; char *servid_ctrl = NULL, *evnturl_ctrl = NULL, *ctrlurl_ctrl = NULL; char *servid_pict = NULL, *evnturl_pict = NULL, *ctrlurl_pict = NULL; char *udn = NULL; /*Download description document */ if( UpnpDownloadXmlDoc( DescDocURL, &DescDoc ) != UPNP_E_SUCCESS ) { SampleUtil_Print( "TvDeviceStateTableInit -- Error Parsing %s\n", DescDocURL ); ret = UPNP_E_INVALID_DESC; goto error_handler; } udn = SampleUtil_GetFirstDocumentItem( DescDoc, "UDN" ); /* Find the Tv Control Service identifiers */ if( !SampleUtil_FindAndParseService( DescDoc, DescDocURL, TvServiceType[TV_SERVICE_CONTROL], &servid_ctrl, &evnturl_ctrl, &ctrlurl_ctrl ) ) { SampleUtil_Print( "TvDeviceStateTableInit -- Error: Could not find" " Service: %s\n", TvServiceType[TV_SERVICE_CONTROL] ); ret = UPNP_E_INVALID_DESC; goto error_handler; } /*set control service table */ SetServiceTable( TV_SERVICE_CONTROL, udn, servid_ctrl, TvServiceType[TV_SERVICE_CONTROL], &tv_service_table[TV_SERVICE_CONTROL] ); /* Find the Tv Picture Service identifiers */ if( !SampleUtil_FindAndParseService( DescDoc, DescDocURL, TvServiceType[TV_SERVICE_PICTURE], &servid_pict, &evnturl_pict, &ctrlurl_pict ) ) { SampleUtil_Print( "TvDeviceStateTableInit -- Error: Could not find" " Service: %s\n", TvServiceType[TV_SERVICE_PICTURE] ); ret = UPNP_E_INVALID_DESC; goto error_handler; } /*set picture service table */ SetServiceTable( TV_SERVICE_PICTURE, udn, servid_pict, TvServiceType[TV_SERVICE_PICTURE], &tv_service_table[TV_SERVICE_PICTURE] ); error_handler: /*clean up */ if( udn ) free( udn ); if( servid_ctrl ) free( servid_ctrl ); if( evnturl_ctrl ) free( evnturl_ctrl ); if( ctrlurl_ctrl ) free( ctrlurl_ctrl ); if( servid_pict ) free( servid_pict ); if( evnturl_pict ) free( evnturl_pict ); if( ctrlurl_pict ) free( ctrlurl_pict ); if( DescDoc ) ixmlDocument_free( DescDoc ); return ( ret ); } int TvDeviceHandleSubscriptionRequest(struct Upnp_Subscription_Request *sr_event) { unsigned int i = 0; int cmp1 = 0; int cmp2 = 0; const char *l_serviceId = NULL; const char *l_udn = NULL; const char *l_sid = NULL; /* lock state mutex */ ithread_mutex_lock(&TVDevMutex); l_serviceId = sr_event->ServiceId; l_udn = sr_event->UDN; l_sid = sr_event->Sid; for (i = 0; i < TV_SERVICE_SERVCOUNT; ++i) { cmp1 = strcmp(l_udn, tv_service_table[i].UDN); cmp2 = strcmp(l_serviceId, tv_service_table[i].ServiceId); if (cmp1 == 0 && cmp2 == 0) { #if 0 PropSet = NULL; for (j = 0; j< tv_service_table[i].VariableCount; ++j) { /* add each variable to the property set */ /* for initial state dump */ UpnpAddToPropertySet( &PropSet, tv_service_table[i].VariableName[j], tv_service_table[i].VariableStrVal[j]); } /* dump initial state */ UpnpAcceptSubscriptionExt( device_handle, l_udn, l_serviceId, PropSet, l_sid); /* free document */ Document_free(PropSet); #endif UpnpAcceptSubscription( device_handle, l_udn, l_serviceId, (const char **)tv_service_table[i]. VariableName, (const char **)tv_service_table[i]. VariableStrVal, tv_service_table[i].VariableCount, l_sid); } } ithread_mutex_unlock(&TVDevMutex); return 1; } int TvDeviceHandleGetVarRequest(struct Upnp_State_Var_Request *cgv_event) { unsigned int i = 0; unsigned int j = 0; int getvar_succeeded = 0; cgv_event->CurrentVal = NULL; ithread_mutex_lock(&TVDevMutex); for (i = 0; i < TV_SERVICE_SERVCOUNT; i++) { /* check udn and service id */ const char *devUDN = cgv_event->DevUDN; const char *serviceID = cgv_event->ServiceID; if (strcmp(devUDN, tv_service_table[i].UDN) == 0 && strcmp(serviceID, tv_service_table[i].ServiceId) == 0) { /* check variable name */ for (j = 0; j < tv_service_table[i].VariableCount; j++) { const char *stateVarName = cgv_event->StateVarName; if (strcmp(stateVarName, tv_service_table[i].VariableName[j]) == 0) { getvar_succeeded = 1; cgv_event->CurrentVal = ixmlCloneDOMString( tv_service_table[i].VariableStrVal[j]); break; } } } } if (getvar_succeeded) { cgv_event->ErrCode = UPNP_E_SUCCESS; } else { SampleUtil_Print( "Error in UPNP_CONTROL_GET_VAR_REQUEST callback:\n" " Unknown variable name = %s\n", cgv_event->StateVarName); cgv_event->ErrCode = 404; strcpy(cgv_event->ErrStr, "Invalid Variable"); } ithread_mutex_unlock(&TVDevMutex); return cgv_event->ErrCode == UPNP_E_SUCCESS; } int TvDeviceHandleActionRequest(struct Upnp_Action_Request *ca_event) { /* Defaults if action not found. */ int action_found = 0; int i = 0; int service = -1; int retCode = 0; const char *errorString = NULL; const char *devUDN = NULL; const char *serviceID = NULL; const char *actionName = NULL; ca_event->ErrCode = 0; ca_event->ActionResult = NULL; devUDN = ca_event->DevUDN; serviceID = ca_event->ServiceID; actionName = ca_event->ActionName; if (strcmp(devUDN, tv_service_table[TV_SERVICE_CONTROL].UDN) == 0 && strcmp(serviceID, tv_service_table[TV_SERVICE_CONTROL].ServiceId) == 0) { /* Request for action in the TvDevice Control Service. */ service = TV_SERVICE_CONTROL; } else if (strcmp(devUDN, tv_service_table[TV_SERVICE_PICTURE].UDN) == 0 && strcmp(serviceID, tv_service_table[TV_SERVICE_PICTURE].ServiceId) == 0) { /* Request for action in the TvDevice Picture Service. */ service = TV_SERVICE_PICTURE; } /* Find and call appropriate procedure based on action name. * Each action name has an associated procedure stored in the * service table. These are set at initialization. */ for (i = 0; i < TV_MAXACTIONS && tv_service_table[service].ActionNames[i] != NULL; i++) { if (!strcmp(actionName, tv_service_table[service].ActionNames[i])) { if (!strcmp(tv_service_table[TV_SERVICE_CONTROL]. VariableStrVal[TV_CONTROL_POWER], "1") || !strcmp(actionName, "PowerOn")) { retCode = tv_service_table[service].actions[i]( ca_event->ActionRequest, &ca_event->ActionResult, &errorString); } else { errorString = "Power is Off"; retCode = UPNP_E_INTERNAL_ERROR; } action_found = 1; break; } } if (!action_found) { ca_event->ActionResult = NULL; strcpy(ca_event->ErrStr, "Invalid Action"); ca_event->ErrCode = 401; } else { if (retCode == UPNP_E_SUCCESS) { ca_event->ErrCode = UPNP_E_SUCCESS; } else { /* copy the error string */ strcpy(ca_event->ErrStr, errorString); switch (retCode) { case UPNP_E_INVALID_PARAM: ca_event->ErrCode = 402; break; case UPNP_E_INTERNAL_ERROR: default: ca_event->ErrCode = 501; break; } } } return ca_event->ErrCode; } int TvDeviceSetServiceTableVar(unsigned int service, unsigned int variable, char *value) { /*IXML_Document *PropSet= NULL; */ if( ( service >= TV_SERVICE_SERVCOUNT ) || ( variable >= tv_service_table[service].VariableCount ) || ( strlen( value ) >= TV_MAX_VAL_LEN ) ) { return ( 0 ); } ithread_mutex_lock( &TVDevMutex ); strcpy( tv_service_table[service].VariableStrVal[variable], value ); #if 0 /*Using utility api */ PropSet= UpnpCreatePropertySet(1,tv_service_table[service]. VariableName[variable], tv_service_table[service]. VariableStrVal[variable]); UpnpNotifyExt(device_handle, tv_service_table[service].UDN, tv_service_table[service].ServiceId,PropSet); /*Free created property set */ Document_free(PropSet); #endif UpnpNotify( device_handle, tv_service_table[service].UDN, tv_service_table[service].ServiceId, ( const char ** )&tv_service_table[service]. VariableName[variable], ( const char ** )&tv_service_table[service]. VariableStrVal[variable], 1 ); ithread_mutex_unlock( &TVDevMutex ); return ( 1 ); } /*! * \brief Turn the power on/off, update the TvDevice control service * state table, and notify all subscribed control points of the * updated state. */ static int TvDeviceSetPower( /*! [in] If 1, turn power on. If 0, turn power off. */ int on) { char value[TV_MAX_VAL_LEN]; int ret = 0; if( on != POWER_ON && on != POWER_OFF ) { SampleUtil_Print( "error: can't set power to value %d\n", on ); return 0; } /* Vendor-specific code to turn the power on/off goes here. */ sprintf( value, "%d", on ); ret = TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_POWER, value ); return ret; } int TvDevicePowerOn(IXML_Document *in, IXML_Document **out, const char **errorString) { ( *out ) = NULL; ( *errorString ) = NULL; if( TvDeviceSetPower( POWER_ON ) ) { /*create a response */ if( UpnpAddToActionResponse( out, "PowerOn", TvServiceType[TV_SERVICE_CONTROL], "Power", "1" ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDevicePowerOff(IXML_Document *in, IXML_Document **out, const char **errorString) { ( *out ) = NULL; ( *errorString ) = NULL; if( TvDeviceSetPower( POWER_OFF ) ) { /*create a response */ if( UpnpAddToActionResponse( out, "PowerOff", TvServiceType[TV_SERVICE_CONTROL], "Power", "0" ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; in = in; } int TvDeviceSetChannel(IXML_Document *in, IXML_Document **out, const char **errorString) { char *value = NULL; int channel = 0; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Channel" ) ) ) { ( *errorString ) = "Invalid Channel"; return UPNP_E_INVALID_PARAM; } channel = atoi( value ); if( channel < MIN_CHANNEL || channel > MAX_CHANNEL ) { free( value ); SampleUtil_Print( "error: can't change to channel %d\n", channel ); ( *errorString ) = "Invalid Channel"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here. */ if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_CHANNEL, value ) ) { if( UpnpAddToActionResponse( out, "SetChannel", TvServiceType[TV_SERVICE_CONTROL], "NewChannel", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } int IncrementChannel(int incr, IN IXML_Document *in, IXML_Document **out, const char **errorString) { int curchannel; int newchannel; const char *actionName = NULL; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseChannel"; } else { actionName = "DecreaseChannel"; } ithread_mutex_lock( &TVDevMutex ); curchannel = atoi( tv_service_table[TV_SERVICE_CONTROL]. VariableStrVal[TV_CONTROL_CHANNEL] ); ithread_mutex_unlock( &TVDevMutex ); newchannel = curchannel + incr; if( newchannel < MIN_CHANNEL || newchannel > MAX_CHANNEL ) { SampleUtil_Print( "error: can't change to channel %d\n", newchannel ); ( *errorString ) = "Invalid Channel"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here. */ sprintf( value, "%d", newchannel ); if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_CHANNEL, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_CONTROL], "Channel", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDeviceDecreaseChannel(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementChannel(-1, in, out, errorString); } int TvDeviceIncreaseChannel(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementChannel(1, in, out, errorString); } int TvDeviceSetVolume(IXML_Document *in, IXML_Document **out, const char **errorString) { char *value = NULL; int volume = 0; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Volume" ) ) ) { ( *errorString ) = "Invalid Volume"; return UPNP_E_INVALID_PARAM; } volume = atoi( value ); if( volume < MIN_VOLUME || volume > MAX_VOLUME ) { SampleUtil_Print( "error: can't change to volume %d\n", volume ); ( *errorString ) = "Invalid Volume"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the volume goes here */ if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_VOLUME, value ) ) { if( UpnpAddToActionResponse( out, "SetVolume", TvServiceType[TV_SERVICE_CONTROL], "NewVolume", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } /*! * \brief Increment the volume. Read the current volume from the state table, * add the increment, and then change the volume. */ static int IncrementVolume( /*! [in] The increment by which to change the volume. */ int incr, /*! [in] Action request document. */ IXML_Document *in, /*! [out] Action result document. */ IXML_Document **out, /*! [out] Error string in case action was unsuccessful. */ const char **errorString) { int curvolume; int newvolume; const char *actionName = NULL; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseVolume"; } else { actionName = "DecreaseVolume"; } ithread_mutex_lock( &TVDevMutex ); curvolume = atoi( tv_service_table[TV_SERVICE_CONTROL]. VariableStrVal[TV_CONTROL_VOLUME] ); ithread_mutex_unlock( &TVDevMutex ); newvolume = curvolume + incr; if( newvolume < MIN_VOLUME || newvolume > MAX_VOLUME ) { SampleUtil_Print( "error: can't change to volume %d\n", newvolume ); ( *errorString ) = "Invalid Volume"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here */ sprintf( value, "%d", newvolume ); if( TvDeviceSetServiceTableVar( TV_SERVICE_CONTROL, TV_CONTROL_VOLUME, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_CONTROL], "Volume", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDeviceIncreaseVolume(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementVolume( 1, in, out, errorString ); } int TvDeviceDecreaseVolume(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementVolume( -1, in, out, errorString ); } int TvDeviceSetColor(IXML_Document *in, IXML_Document **out, const char **errorString) { char *value = NULL; int color = 0; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Color" ) ) ) { ( *errorString ) = "Invalid Color"; return UPNP_E_INVALID_PARAM; } color = atoi( value ); if( color < MIN_COLOR || color > MAX_COLOR ) { SampleUtil_Print( "error: can't change to color %d\n", color ); ( *errorString ) = "Invalid Color"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the volume goes here */ if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_COLOR, value ) ) { if( UpnpAddToActionResponse( out, "SetColor", TvServiceType[TV_SERVICE_PICTURE], "NewColor", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } /*! * \brief Increment the color. Read the current color from the state * table, add the increment, and then change the color. */ static int IncrementColor( /*! [in] The increment by which to change the volume. */ int incr, /*! [in] Action request document. */ IXML_Document *in, /*! [out] Action result document. */ IXML_Document **out, /*! [out] Error string in case action was unsuccessful. */ const char **errorString) { int curcolor; int newcolor; const char *actionName; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseColor"; } else { actionName = "DecreaseColor"; } ithread_mutex_lock( &TVDevMutex ); curcolor = atoi( tv_service_table[TV_SERVICE_PICTURE]. VariableStrVal[TV_PICTURE_COLOR] ); ithread_mutex_unlock( &TVDevMutex ); newcolor = curcolor + incr; if( newcolor < MIN_COLOR || newcolor > MAX_COLOR ) { SampleUtil_Print( "error: can't change to color %d\n", newcolor ); ( *errorString ) = "Invalid Color"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here */ sprintf( value, "%d", newcolor ); if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_COLOR, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_PICTURE], "Color", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDeviceDecreaseColor(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementColor(-1, in, out, errorString); } int TvDeviceIncreaseColor(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementColor(1, in, out, errorString); } int TvDeviceSetTint(IXML_Document *in, IXML_Document **out, const char **errorString) { char *value = NULL; int tint = -1; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Tint" ) ) ) { ( *errorString ) = "Invalid Tint"; return UPNP_E_INVALID_PARAM; } tint = atoi( value ); if( tint < MIN_TINT || tint > MAX_TINT ) { SampleUtil_Print( "error: can't change to tint %d\n", tint ); ( *errorString ) = "Invalid Tint"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the volume goes here */ if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_TINT, value ) ) { if( UpnpAddToActionResponse( out, "SetTint", TvServiceType[TV_SERVICE_PICTURE], "NewTint", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } /****************************************************************************** * IncrementTint * * Description: * Increment the tint. Read the current tint from the state * table, add the increment, and then change the tint. * * Parameters: * incr -- The increment by which to change the tint. * * IXML_Document * in - action request document * IXML_Document **out - action result document * char **errorString - errorString (in case action was unsuccessful) *****************************************************************************/ int IncrementTint(IN int incr, IN IXML_Document *in, OUT IXML_Document **out, OUT const char **errorString) { int curtint; int newtint; const char *actionName = NULL; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseTint"; } else { actionName = "DecreaseTint"; } ithread_mutex_lock( &TVDevMutex ); curtint = atoi( tv_service_table[TV_SERVICE_PICTURE]. VariableStrVal[TV_PICTURE_TINT] ); ithread_mutex_unlock( &TVDevMutex ); newtint = curtint + incr; if( newtint < MIN_TINT || newtint > MAX_TINT ) { SampleUtil_Print( "error: can't change to tint %d\n", newtint ); ( *errorString ) = "Invalid Tint"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here */ sprintf( value, "%d", newtint ); if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_TINT, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_PICTURE], "Tint", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } /****************************************************************************** * TvDeviceIncreaseTint * * Description: * Increase tint. * * Parameters: * * IXML_Document * in - action request document * IXML_Document **out - action result document * char **errorString - errorString (in case action was unsuccessful) * *****************************************************************************/ int TvDeviceIncreaseTint( IN IXML_Document *in, OUT IXML_Document **out, OUT const char **errorString) { return IncrementTint( 1, in, out, errorString ); } /****************************************************************************** * TvDeviceDecreaseTint * * Description: * Decrease tint. * * Parameters: * * IXML_Document * in - action request document * IXML_Document **out - action result document * char **errorString - errorString (in case action was unsuccessful) * *****************************************************************************/ int TvDeviceDecreaseTint( IN IXML_Document *in, OUT IXML_Document **out, OUT const char **errorString) { return IncrementTint( -1, in, out, errorString ); } /***************************************************************************** * TvDeviceSetContrast * * Description: * Change the contrast, update the TvDevice picture service * state table, and notify all subscribed control points of the * updated state. * * Parameters: * * IXML_Document * in - action request document * IXML_Document **out - action result document * char **errorString - errorString (in case action was unsuccessful) * ****************************************************************************/ int TvDeviceSetContrast( IN IXML_Document *in, OUT IXML_Document **out, OUT const char **errorString) { char *value = NULL; int contrast = -1; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Contrast" ) ) ) { ( *errorString ) = "Invalid Contrast"; return UPNP_E_INVALID_PARAM; } contrast = atoi( value ); if( contrast < MIN_CONTRAST || contrast > MAX_CONTRAST ) { SampleUtil_Print( "error: can't change to contrast %d\n", contrast ); ( *errorString ) = "Invalid Contrast"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the volume goes here */ if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_CONTRAST, value ) ) { if( UpnpAddToActionResponse( out, "SetContrast", TvServiceType[TV_SERVICE_PICTURE], "NewContrast", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } /*! * \brief Increment the contrast. Read the current contrast from the state * table, add the increment, and then change the contrast. */ static int IncrementContrast( /*! [in] The increment by which to change the volume. */ int incr, /*! [in] Action request document. */ IXML_Document *in, /*! [out] Action result document. */ IXML_Document **out, /*! [out] Error string in case action was unsuccessful. */ const char **errorString) { int curcontrast; int newcontrast; const char *actionName = NULL; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseContrast"; } else { actionName = "DecreaseContrast"; } ithread_mutex_lock( &TVDevMutex ); curcontrast = atoi( tv_service_table[TV_SERVICE_PICTURE]. VariableStrVal[TV_PICTURE_CONTRAST] ); ithread_mutex_unlock( &TVDevMutex ); newcontrast = curcontrast + incr; if( newcontrast < MIN_CONTRAST || newcontrast > MAX_CONTRAST ) { SampleUtil_Print( "error: can't change to contrast %d\n", newcontrast ); ( *errorString ) = "Invalid Contrast"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here. */ sprintf( value, "%d", newcontrast ); if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_CONTRAST, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_PICTURE], "Contrast", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDeviceIncreaseContrast(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementContrast(1, in, out, errorString); } int TvDeviceDecreaseContrast(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementContrast(-1, in, out, errorString); } int TvDeviceSetBrightness(IXML_Document *in, IXML_Document **out, const char **errorString) { char *value = NULL; int brightness = -1; ( *out ) = NULL; ( *errorString ) = NULL; if( !( value = SampleUtil_GetFirstDocumentItem( in, "Brightness" ) ) ) { ( *errorString ) = "Invalid Brightness"; return UPNP_E_INVALID_PARAM; } brightness = atoi( value ); if( brightness < MIN_BRIGHTNESS || brightness > MAX_BRIGHTNESS ) { SampleUtil_Print( "error: can't change to brightness %d\n", brightness ); ( *errorString ) = "Invalid Brightness"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the volume goes here. */ if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_BRIGHTNESS, value ) ) { if( UpnpAddToActionResponse( out, "SetBrightness", TvServiceType[TV_SERVICE_PICTURE], "NewBrightness", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; free( value ); return UPNP_E_INTERNAL_ERROR; } free( value ); return UPNP_E_SUCCESS; } else { free( value ); ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } } /*! * \brief Increment the brightness. Read the current brightness from the state * table, add the increment, and then change the brightness. */ static int IncrementBrightness( /*! [in] The increment by which to change the brightness. */ int incr, /*! [in] action request document. */ IXML_Document *in, /*! [out] action result document. */ IXML_Document **out, /*! [out] errorString (in case action was unsuccessful). */ const char **errorString) { int curbrightness; int newbrightness; const char *actionName = NULL; char value[TV_MAX_VAL_LEN]; if( incr > 0 ) { actionName = "IncreaseBrightness"; } else { actionName = "DecreaseBrightness"; } ithread_mutex_lock( &TVDevMutex ); curbrightness = atoi( tv_service_table[TV_SERVICE_PICTURE]. VariableStrVal[TV_PICTURE_BRIGHTNESS] ); ithread_mutex_unlock( &TVDevMutex ); newbrightness = curbrightness + incr; if( newbrightness < MIN_BRIGHTNESS || newbrightness > MAX_BRIGHTNESS ) { SampleUtil_Print( "error: can't change to brightness %d\n", newbrightness ); ( *errorString ) = "Invalid Brightness"; return UPNP_E_INVALID_PARAM; } /* Vendor-specific code to set the channel goes here. */ sprintf( value, "%d", newbrightness ); if( TvDeviceSetServiceTableVar( TV_SERVICE_PICTURE, TV_PICTURE_BRIGHTNESS, value ) ) { if( UpnpAddToActionResponse( out, actionName, TvServiceType[TV_SERVICE_PICTURE], "Brightness", value ) != UPNP_E_SUCCESS ) { ( *out ) = NULL; ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } return UPNP_E_SUCCESS; } else { ( *errorString ) = "Internal Error"; return UPNP_E_INTERNAL_ERROR; } in = in; } int TvDeviceIncreaseBrightness(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementBrightness(1, in, out, errorString); } int TvDeviceDecreaseBrightness(IXML_Document *in, IXML_Document **out, const char **errorString) { return IncrementBrightness(-1, in, out, errorString); } int TvDeviceCallbackEventHandler(Upnp_EventType EventType, void *Event, void *Cookie) { switch (EventType) { case UPNP_EVENT_SUBSCRIPTION_REQUEST: TvDeviceHandleSubscriptionRequest((struct Upnp_Subscription_Request *)Event); break; case UPNP_CONTROL_GET_VAR_REQUEST: TvDeviceHandleGetVarRequest((struct Upnp_State_Var_Request *)Event); break; case UPNP_CONTROL_ACTION_REQUEST: TvDeviceHandleActionRequest((struct Upnp_Action_Request *)Event); break; /* ignore these cases, since this is not a control point */ case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: case UPNP_DISCOVERY_SEARCH_RESULT: case UPNP_DISCOVERY_SEARCH_TIMEOUT: case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: case UPNP_CONTROL_ACTION_COMPLETE: case UPNP_CONTROL_GET_VAR_COMPLETE: case UPNP_EVENT_RECEIVED: case UPNP_EVENT_RENEWAL_COMPLETE: case UPNP_EVENT_SUBSCRIBE_COMPLETE: case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: break; default: SampleUtil_Print("Error in TvDeviceCallbackEventHandler: unknown event type %d\n", EventType); } /* Print a summary of the event received */ SampleUtil_PrintEvent(EventType, Event); return 0; Cookie = Cookie; } int TvDeviceStop(void) { UpnpUnRegisterRootDevice( device_handle ); UpnpFinish(); SampleUtil_Finish(); ithread_mutex_destroy( &TVDevMutex ); return UPNP_E_SUCCESS; } int TvDeviceStart(char *ip_address, unsigned short port, const char *desc_doc_name, const char *web_dir_path, print_string pfun) { int ret = UPNP_E_SUCCESS; char desc_doc_url[DESC_URL_SIZE]; ithread_mutex_init( &TVDevMutex, NULL ); SampleUtil_Initialize( pfun ); SampleUtil_Print( "Initializing UPnP Sdk with\n" "\tipaddress = %s port = %u\n", ip_address ? ip_address : "{NULL}", port); ret = UpnpInit( ip_address, port ); if( ret != UPNP_E_SUCCESS ) { SampleUtil_Print( "Error with UpnpInit -- %d\n", ret ); UpnpFinish(); return ret; } ip_address = UpnpGetServerIpAddress(); port = UpnpGetServerPort(); SampleUtil_Print( "UPnP Initialized\n" "\tipaddress = %s port = %u\n", ip_address ? ip_address : "{NULL}", port); if (!desc_doc_name) { desc_doc_name = "tvcombodesc.xml"; } if (!web_dir_path) { web_dir_path = DEFAULT_WEB_DIR; } snprintf( desc_doc_url, DESC_URL_SIZE, "http://%s:%d/%s", ip_address, port, desc_doc_name ); SampleUtil_Print( "Specifying the webserver root directory -- %s\n", web_dir_path ); ret = UpnpSetWebServerRootDir( web_dir_path ); if( ret != UPNP_E_SUCCESS ) { SampleUtil_Print( "Error specifying webserver root directory -- %s: %d\n", web_dir_path, ret ); UpnpFinish(); return ret; } SampleUtil_Print( "Registering the RootDevice\n" "\t with desc_doc_url: %s\n", desc_doc_url ); ret = UpnpRegisterRootDevice( desc_doc_url, TvDeviceCallbackEventHandler, &device_handle, &device_handle ); if( ret != UPNP_E_SUCCESS ) { SampleUtil_Print( "Error registering the rootdevice : %d\n", ret ); UpnpFinish(); return ret; } else { SampleUtil_Print( "RootDevice Registered\n" "Initializing State Table\n"); TvDeviceStateTableInit( desc_doc_url ); SampleUtil_Print("State Table Initialized\n"); ret = UpnpSendAdvertisement( device_handle, default_advr_expire ); if( ret != UPNP_E_SUCCESS ) { SampleUtil_Print( "Error sending advertisements : %d\n", ret ); UpnpFinish(); return ret; } SampleUtil_Print("Advertisements Sent\n"); } return UPNP_E_SUCCESS; }