pupnp (libupnp) snapshot from SourceForge: git clone git://pupnp.git.sourceforge...
[igd2-for-linux:pandonghui1211s-igd2-for-linux.git] / pupnp_branch-1.6.x / upnp / sample / tvctrlpt / upnp_tv_ctrlpt.c
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2000-2003 Intel Corporation 
4 // All rights reserved. 
5 //
6 // Redistribution and use in source and binary forms, with or without 
7 // modification, are permitted provided that the following conditions are met: 
8 //
9 // * Redistributions of source code must retain the above copyright notice, 
10 // this list of conditions and the following disclaimer. 
11 // * Redistributions in binary form must reproduce the above copyright notice, 
12 // this list of conditions and the following disclaimer in the documentation 
13 // and/or other materials provided with the distribution. 
14 // * Neither name of Intel Corporation nor the names of its contributors 
15 // may be used to endorse or promote products derived from this software 
16 // without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
22 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
23 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
25 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
26 // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 ///////////////////////////////////////////////////////////////////////////
31
32 #include "upnp_tv_ctrlpt.h"
33
34 /*
35    Mutex for protecting the global device list
36    in a multi-threaded, asynchronous environment.
37    All functions should lock this mutex before reading
38    or writing the device list. 
39  */
40 ithread_mutex_t DeviceListMutex;
41
42 UpnpClient_Handle ctrlpt_handle = -1;
43
44 char TvDeviceType[] = "urn:schemas-upnp-org:device:tvdevice:1";
45 char *TvServiceType[] = {
46     "urn:schemas-upnp-org:service:tvcontrol:1",
47     "urn:schemas-upnp-org:service:tvpicture:1"
48 };
49 char *TvServiceName[] = { "Control", "Picture" };
50
51 /*
52    Global arrays for storing variable names and counts for 
53    TvControl and TvPicture services 
54  */
55 char *TvVarName[TV_SERVICE_SERVCOUNT][TV_MAXVARS] = {
56     {"Power", "Channel", "Volume", ""},
57     {"Color", "Tint", "Contrast", "Brightness"}
58 };
59 char TvVarCount[TV_SERVICE_SERVCOUNT] =
60     { TV_CONTROL_VARCOUNT, TV_PICTURE_VARCOUNT };
61
62 /*
63    Timeout to request during subscriptions 
64  */
65 int default_timeout = 1801;
66
67 /*
68    The first node in the global device list, or NULL if empty 
69  */
70 struct TvDeviceNode *GlobalDeviceList = NULL;
71
72 /********************************************************************************
73  * TvCtrlPointDeleteNode
74  *
75  * Description: 
76  *       Delete a device node from the global device list.  Note that this
77  *       function is NOT thread safe, and should be called from another
78  *       function that has already locked the global device list.
79  *
80  * Parameters:
81  *   node -- The device node
82  *
83  ********************************************************************************/
84 int
85 TvCtrlPointDeleteNode( struct TvDeviceNode *node )
86 {
87     int rc,
88       service,
89       var;
90
91     if( NULL == node ) {
92         SampleUtil_Print( "ERROR: TvCtrlPointDeleteNode: Node is empty" );
93         return TV_ERROR;
94     }
95
96     for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
97         /*
98            If we have a valid control SID, then unsubscribe 
99          */
100         if( strcmp( node->device.TvService[service].SID, "" ) != 0 ) {
101             rc = UpnpUnSubscribe( ctrlpt_handle,
102                                   node->device.TvService[service].SID );
103             if( UPNP_E_SUCCESS == rc ) {
104                 SampleUtil_Print
105                     ( "Unsubscribed from Tv %s EventURL with SID=%s",
106                       TvServiceName[service],
107                       node->device.TvService[service].SID );
108             } else {
109                 SampleUtil_Print
110                     ( "Error unsubscribing to Tv %s EventURL -- %d",
111                       TvServiceName[service], rc );
112             }
113         }
114
115         for( var = 0; var < TvVarCount[service]; var++ ) {
116             if( node->device.TvService[service].VariableStrVal[var] ) {
117                 free( node->device.TvService[service].
118                       VariableStrVal[var] );
119             }
120         }
121     }
122
123     //Notify New Device Added
124     SampleUtil_StateUpdate( NULL, NULL, node->device.UDN, DEVICE_REMOVED );
125     free( node );
126     node = NULL;
127
128     return TV_SUCCESS;
129 }
130
131 /********************************************************************************
132  * TvCtrlPointRemoveDevice
133  *
134  * Description: 
135  *       Remove a device from the global device list.
136  *
137  * Parameters:
138  *   UDN -- The Unique Device Name for the device to remove
139  *
140  ********************************************************************************/
141 int
142 TvCtrlPointRemoveDevice( char *UDN )
143 {
144     struct TvDeviceNode *curdevnode,
145      *prevdevnode;
146
147     ithread_mutex_lock( &DeviceListMutex );
148
149     curdevnode = GlobalDeviceList;
150     if( !curdevnode ) {
151         SampleUtil_Print
152             ( "WARNING: TvCtrlPointRemoveDevice: Device list empty" );
153     } else {
154         if( 0 == strcmp( curdevnode->device.UDN, UDN ) ) {
155             GlobalDeviceList = curdevnode->next;
156             TvCtrlPointDeleteNode( curdevnode );
157         } else {
158             prevdevnode = curdevnode;
159             curdevnode = curdevnode->next;
160
161             while( curdevnode ) {
162                 if( strcmp( curdevnode->device.UDN, UDN ) == 0 ) {
163                     prevdevnode->next = curdevnode->next;
164                     TvCtrlPointDeleteNode( curdevnode );
165                     break;
166                 }
167
168                 prevdevnode = curdevnode;
169                 curdevnode = curdevnode->next;
170             }
171         }
172     }
173
174     ithread_mutex_unlock( &DeviceListMutex );
175
176     return TV_SUCCESS;
177 }
178
179 /********************************************************************************
180  * TvCtrlPointRemoveAll
181  *
182  * Description: 
183  *       Remove all devices from the global device list.
184  *
185  * Parameters:
186  *   None
187  *
188  ********************************************************************************/
189 int
190 TvCtrlPointRemoveAll( void )
191 {
192     struct TvDeviceNode *curdevnode,
193      *next;
194
195     ithread_mutex_lock( &DeviceListMutex );
196
197     curdevnode = GlobalDeviceList;
198     GlobalDeviceList = NULL;
199
200     while( curdevnode ) {
201         next = curdevnode->next;
202         TvCtrlPointDeleteNode( curdevnode );
203         curdevnode = next;
204     }
205
206     ithread_mutex_unlock( &DeviceListMutex );
207
208     return TV_SUCCESS;
209 }
210
211 /********************************************************************************
212  * TvCtrlPointRefresh
213  *
214  * Description: 
215  *       Clear the current global device list and issue new search
216  *       requests to build it up again from scratch.
217  *
218  * Parameters:
219  *   None
220  *
221  ********************************************************************************/
222 int
223 TvCtrlPointRefresh( void )
224 {
225     int rc;
226
227     TvCtrlPointRemoveAll();
228
229     /*
230        Search for all devices of type tvdevice version 1, 
231        waiting for up to 5 seconds for the response 
232      */
233     rc = UpnpSearchAsync( ctrlpt_handle, 5, TvDeviceType, NULL );
234     if( UPNP_E_SUCCESS != rc ) {
235         SampleUtil_Print( "Error sending search request%d", rc );
236         return TV_ERROR;
237     }
238
239     return TV_SUCCESS;
240 }
241
242 /********************************************************************************
243  * TvCtrlPointGetVar
244  *
245  * Description: 
246  *       Send a GetVar request to the specified service of a device.
247  *
248  * Parameters:
249  *   service -- The service
250  *   devnum -- The number of the device (order in the list,
251  *             starting with 1)
252  *   varname -- The name of the variable to request.
253  *
254  ********************************************************************************/
255 int
256 TvCtrlPointGetVar( int service,
257                    int devnum,
258                    char *varname )
259 {
260     struct TvDeviceNode *devnode;
261     int rc;
262
263     ithread_mutex_lock( &DeviceListMutex );
264
265     rc = TvCtrlPointGetDevice( devnum, &devnode );
266
267     if( TV_SUCCESS == rc ) {
268         rc = UpnpGetServiceVarStatusAsync( ctrlpt_handle,
269                                            devnode->device.
270                                            TvService[service].ControlURL,
271                                            varname,
272                                            TvCtrlPointCallbackEventHandler,
273                                            NULL );
274         if( rc != UPNP_E_SUCCESS ) {
275             SampleUtil_Print
276                 ( "Error in UpnpGetServiceVarStatusAsync -- %d", rc );
277             rc = TV_ERROR;
278         }
279     }
280
281     ithread_mutex_unlock( &DeviceListMutex );
282
283     return rc;
284 }
285
286 int
287 TvCtrlPointGetPower( int devnum )
288 {
289     return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Power" );
290 }
291
292 int
293 TvCtrlPointGetChannel( int devnum )
294 {
295     return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Channel" );
296 }
297
298 int
299 TvCtrlPointGetVolume( int devnum )
300 {
301     return TvCtrlPointGetVar( TV_SERVICE_CONTROL, devnum, "Volume" );
302 }
303
304 int
305 TvCtrlPointGetColor( int devnum )
306 {
307     return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Color" );
308 }
309
310 int
311 TvCtrlPointGetTint( int devnum )
312 {
313     return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Tint" );
314 }
315
316 int
317 TvCtrlPointGetContrast( int devnum )
318 {
319     return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Contrast" );
320 }
321
322 int
323 TvCtrlPointGetBrightness( int devnum )
324 {
325     return TvCtrlPointGetVar( TV_SERVICE_PICTURE, devnum, "Brightness" );
326 }
327
328 /********************************************************************************
329  * TvCtrlPointSendAction
330  *
331  * Description: 
332  *       Send an Action request to the specified service of a device.
333  *
334  * Parameters:
335  *   service -- The service
336  *   devnum -- The number of the device (order in the list,
337  *             starting with 1)
338  *   actionname -- The name of the action.
339  *   param_name -- An array of parameter names
340  *   param_val -- The corresponding parameter values
341  *   param_count -- The number of parameters
342  *
343  ********************************************************************************/
344 int
345 TvCtrlPointSendAction( int service,
346                        int devnum,
347                        char *actionname,
348                        char **param_name,
349                        char **param_val,
350                        int param_count )
351 {
352     struct TvDeviceNode *devnode;
353     IXML_Document *actionNode = NULL;
354     int rc = TV_SUCCESS;
355     int param;
356
357     ithread_mutex_lock( &DeviceListMutex );
358
359     rc = TvCtrlPointGetDevice( devnum, &devnode );
360     if( TV_SUCCESS == rc ) {
361         if( 0 == param_count ) {
362             actionNode =
363                 UpnpMakeAction( actionname, TvServiceType[service], 0,
364                                 NULL );
365         } else {
366             for( param = 0; param < param_count; param++ ) {
367                 if( UpnpAddToAction
368                     ( &actionNode, actionname, TvServiceType[service],
369                       param_name[param],
370                       param_val[param] ) != UPNP_E_SUCCESS ) {
371                     SampleUtil_Print
372                         ( "ERROR: TvCtrlPointSendAction: Trying to add action param" );
373                     //return -1; // TBD - BAD! leaves mutex locked
374                 }
375             }
376         }
377
378         rc = UpnpSendActionAsync( ctrlpt_handle,
379                                   devnode->device.TvService[service].
380                                   ControlURL, TvServiceType[service],
381                                   NULL, actionNode,
382                                   TvCtrlPointCallbackEventHandler, NULL );
383
384         if( rc != UPNP_E_SUCCESS ) {
385             SampleUtil_Print( "Error in UpnpSendActionAsync -- %d", rc );
386             rc = TV_ERROR;
387         }
388     }
389
390     ithread_mutex_unlock( &DeviceListMutex );
391
392     if( actionNode )
393         ixmlDocument_free( actionNode );
394
395     return rc;
396 }
397
398 /********************************************************************************
399  * TvCtrlPointSendActionNumericArg
400  *
401  * Description:Send an action with one argument to a device in the global device list.
402  *
403  * Parameters:
404  *   devnum -- The number of the device (order in the list, starting with 1)
405  *   service -- TV_SERVICE_CONTROL or TV_SERVICE_PICTURE
406  *   actionName -- The device action, i.e., "SetChannel"
407  *   paramName -- The name of the parameter that is being passed
408  *   paramValue -- Actual value of the parameter being passed
409  *
410  ********************************************************************************/
411 int
412 TvCtrlPointSendActionNumericArg( int devnum,
413                                  int service,
414                                  char *actionName,
415                                  char *paramName,
416                                  int paramValue )
417 {
418     char param_val_a[50];
419     char *param_val = param_val_a;
420
421     sprintf( param_val_a, "%d", paramValue );
422
423     return TvCtrlPointSendAction( service, devnum, actionName, &paramName,
424                                   &param_val, 1 );
425 }
426
427 int
428 TvCtrlPointSendPowerOn( int devnum )
429 {
430     return TvCtrlPointSendAction( TV_SERVICE_CONTROL, devnum, "PowerOn",
431                                   NULL, NULL, 0 );
432 }
433
434 int
435 TvCtrlPointSendPowerOff( int devnum )
436 {
437     return TvCtrlPointSendAction( TV_SERVICE_CONTROL, devnum, "PowerOff",
438                                   NULL, NULL, 0 );
439 }
440
441 int
442 TvCtrlPointSendSetChannel( int devnum,
443                            int channel )
444 {
445     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_CONTROL,
446                                             "SetChannel", "Channel",
447                                             channel );
448 }
449
450 int
451 TvCtrlPointSendSetVolume( int devnum,
452                           int volume )
453 {
454     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_CONTROL,
455                                             "SetVolume", "Volume",
456                                             volume );
457 }
458
459 int
460 TvCtrlPointSendSetColor( int devnum,
461                          int color )
462 {
463     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE,
464                                             "SetColor", "Color", color );
465 }
466
467 int
468 TvCtrlPointSendSetTint( int devnum,
469                         int tint )
470 {
471     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE,
472                                             "SetTint", "Tint", tint );
473 }
474
475 int
476 TvCtrlPointSendSetContrast( int devnum,
477                             int contrast )
478 {
479     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE,
480                                             "SetContrast", "Contrast",
481                                             contrast );
482 }
483
484 int
485 TvCtrlPointSendSetBrightness( int devnum,
486                               int brightness )
487 {
488     return TvCtrlPointSendActionNumericArg( devnum, TV_SERVICE_PICTURE,
489                                             "SetBrightness", "Brightness",
490                                             brightness );
491 }
492
493 /********************************************************************************
494  * TvCtrlPointGetDevice
495  *
496  * Description: 
497  *       Given a list number, returns the pointer to the device
498  *       node at that position in the global device list.  Note
499  *       that this function is not thread safe.  It must be called 
500  *       from a function that has locked the global device list.
501  *
502  * Parameters:
503  *   devnum -- The number of the device (order in the list,
504  *             starting with 1)
505  *   devnode -- The output device node pointer
506  *
507  ********************************************************************************/
508 int
509 TvCtrlPointGetDevice( int devnum,
510                       struct TvDeviceNode **devnode )
511 {
512     int count = devnum;
513     struct TvDeviceNode *tmpdevnode = NULL;
514
515     if( count )
516         tmpdevnode = GlobalDeviceList;
517
518     while( --count && tmpdevnode ) {
519         tmpdevnode = tmpdevnode->next;
520     }
521
522     if( !tmpdevnode ) {
523         SampleUtil_Print( "Error finding TvDevice number -- %d", devnum );
524         return TV_ERROR;
525     }
526
527     *devnode = tmpdevnode;
528     return TV_SUCCESS;
529 }
530
531 /********************************************************************************
532  * TvCtrlPointPrintList
533  *
534  * Description: 
535  *       Print the universal device names for each device in the global device list
536  *
537  * Parameters:
538  *   None
539  *
540  ********************************************************************************/
541 int
542 TvCtrlPointPrintList()
543 {
544     struct TvDeviceNode *tmpdevnode;
545     int i = 0;
546
547     ithread_mutex_lock( &DeviceListMutex );
548
549     SampleUtil_Print( "TvCtrlPointPrintList:" );
550     tmpdevnode = GlobalDeviceList;
551     while( tmpdevnode ) {
552         SampleUtil_Print( " %3d -- %s", ++i, tmpdevnode->device.UDN );
553         tmpdevnode = tmpdevnode->next;
554     }
555     SampleUtil_Print( "" );
556     ithread_mutex_unlock( &DeviceListMutex );
557
558     return TV_SUCCESS;
559 }
560
561 /********************************************************************************
562  * TvCtrlPointPrintDevice
563  *
564  * Description: 
565  *       Print the identifiers and state table for a device from
566  *       the global device list.
567  *
568  * Parameters:
569  *   devnum -- The number of the device (order in the list,
570  *             starting with 1)
571  *
572  ********************************************************************************/
573 int
574 TvCtrlPointPrintDevice( int devnum )
575 {
576     struct TvDeviceNode *tmpdevnode;
577     int i = 0,
578       service,
579       var;
580     char spacer[15];
581
582     if( devnum <= 0 ) {
583         SampleUtil_Print
584             ( "Error in TvCtrlPointPrintDevice: invalid devnum = %d",
585               devnum );
586         return TV_ERROR;
587     }
588
589     ithread_mutex_lock( &DeviceListMutex );
590
591     SampleUtil_Print( "TvCtrlPointPrintDevice:" );
592     tmpdevnode = GlobalDeviceList;
593     while( tmpdevnode ) {
594         i++;
595         if( i == devnum )
596             break;
597         tmpdevnode = tmpdevnode->next;
598     }
599
600     if( !tmpdevnode ) {
601         SampleUtil_Print
602             ( "Error in TvCtrlPointPrintDevice: invalid devnum = %d  --  actual device count = %d",
603               devnum, i );
604     } else {
605         SampleUtil_Print( "  TvDevice -- %d", devnum );
606         SampleUtil_Print( "    |                  " );
607         SampleUtil_Print( "    +- UDN        = %s",
608                           tmpdevnode->device.UDN );
609         SampleUtil_Print( "    +- DescDocURL     = %s",
610                           tmpdevnode->device.DescDocURL );
611         SampleUtil_Print( "    +- FriendlyName   = %s",
612                           tmpdevnode->device.FriendlyName );
613         SampleUtil_Print( "    +- PresURL        = %s",
614                           tmpdevnode->device.PresURL );
615         SampleUtil_Print( "    +- Adver. TimeOut = %d",
616                           tmpdevnode->device.AdvrTimeOut );
617
618         for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
619             if( service < TV_SERVICE_SERVCOUNT - 1 )
620                 sprintf( spacer, "    |    " );
621             else
622                 sprintf( spacer, "         " );
623             SampleUtil_Print( "    |                  " );
624             SampleUtil_Print( "    +- Tv %s Service",
625                               TvServiceName[service] );
626             SampleUtil_Print( "%s+- ServiceId       = %s", spacer,
627                               tmpdevnode->device.TvService[service].
628                               ServiceId );
629             SampleUtil_Print( "%s+- ServiceType     = %s", spacer,
630                               tmpdevnode->device.TvService[service].
631                               ServiceType );
632             SampleUtil_Print( "%s+- EventURL        = %s", spacer,
633                               tmpdevnode->device.TvService[service].
634                               EventURL );
635             SampleUtil_Print( "%s+- ControlURL      = %s", spacer,
636                               tmpdevnode->device.TvService[service].
637                               ControlURL );
638             SampleUtil_Print( "%s+- SID             = %s", spacer,
639                               tmpdevnode->device.TvService[service].SID );
640             SampleUtil_Print( "%s+- ServiceStateTable", spacer );
641
642             for( var = 0; var < TvVarCount[service]; var++ ) {
643                 SampleUtil_Print( "%s     +- %-10s = %s", spacer,
644                                   TvVarName[service][var],
645                                   tmpdevnode->device.TvService[service].
646                                   VariableStrVal[var] );
647             }
648         }
649     }
650
651     SampleUtil_Print( "" );
652     ithread_mutex_unlock( &DeviceListMutex );
653
654     return TV_SUCCESS;
655 }
656
657 /********************************************************************************
658  * TvCtrlPointAddDevice
659  *
660  * Description: 
661  *       If the device is not already included in the global device list,
662  *       add it.  Otherwise, update its advertisement expiration timeout.
663  *
664  * Parameters:
665  *   DescDoc -- The description document for the device
666  *   location -- The location of the description document URL
667  *   expires -- The expiration time for this advertisement
668  *
669  ********************************************************************************/
670 void
671 TvCtrlPointAddDevice( IXML_Document * DescDoc,
672                       char *location,
673                       int expires )
674 {
675     char *deviceType = NULL;
676     char *friendlyName = NULL;
677     char presURL[200];
678     char *baseURL = NULL;
679     char *relURL = NULL;
680     char *UDN = NULL;
681     char *serviceId[TV_SERVICE_SERVCOUNT] = { NULL, NULL };
682     char *eventURL[TV_SERVICE_SERVCOUNT] = { NULL, NULL };
683     char *controlURL[TV_SERVICE_SERVCOUNT] = { NULL, NULL };
684     Upnp_SID eventSID[TV_SERVICE_SERVCOUNT];
685     int TimeOut[TV_SERVICE_SERVCOUNT] =
686         { default_timeout, default_timeout };
687     struct TvDeviceNode *deviceNode;
688     struct TvDeviceNode *tmpdevnode;
689     int ret = 1;
690     int found = 0;
691     int service,
692       var;
693
694     ithread_mutex_lock( &DeviceListMutex );
695
696     /*
697        Read key elements from description document 
698      */
699     UDN = SampleUtil_GetFirstDocumentItem( DescDoc, "UDN" );
700     deviceType = SampleUtil_GetFirstDocumentItem( DescDoc, "deviceType" );
701     friendlyName =
702         SampleUtil_GetFirstDocumentItem( DescDoc, "friendlyName" );
703     baseURL = SampleUtil_GetFirstDocumentItem( DescDoc, "URLBase" );
704     relURL = SampleUtil_GetFirstDocumentItem( DescDoc, "presentationURL" );
705
706     ret =
707         UpnpResolveURL( ( baseURL ? baseURL : location ), relURL,
708                         presURL );
709
710     if( UPNP_E_SUCCESS != ret )
711         SampleUtil_Print( "Error generating presURL from %s + %s", baseURL,
712                           relURL );
713
714     if( strcmp( deviceType, TvDeviceType ) == 0 ) {
715         SampleUtil_Print( "Found Tv device" );
716
717         // Check if this device is already in the list
718         tmpdevnode = GlobalDeviceList;
719         while( tmpdevnode ) {
720             if( strcmp( tmpdevnode->device.UDN, UDN ) == 0 ) {
721                 found = 1;
722                 break;
723             }
724             tmpdevnode = tmpdevnode->next;
725         }
726
727         if( found ) {
728             // The device is already there, so just update 
729             // the advertisement timeout field
730             tmpdevnode->device.AdvrTimeOut = expires;
731         } else {
732             for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
733                 if( SampleUtil_FindAndParseService
734                     ( DescDoc, location, TvServiceType[service],
735                       &serviceId[service], &eventURL[service],
736                       &controlURL[service] ) ) {
737                     SampleUtil_Print( "Subscribing to EventURL %s...",
738                                       eventURL[service] );
739
740                     ret =
741                         UpnpSubscribe( ctrlpt_handle, eventURL[service],
742                                        &TimeOut[service],
743                                        eventSID[service] );
744
745                     if( ret == UPNP_E_SUCCESS ) {
746                         SampleUtil_Print
747                             ( "Subscribed to EventURL with SID=%s",
748                               eventSID[service] );
749                     } else {
750                         SampleUtil_Print
751                             ( "Error Subscribing to EventURL -- %d", ret );
752                         strcpy( eventSID[service], "" );
753                     }
754                 } else {
755                     SampleUtil_Print( "Error: Could not find Service: %s",
756                                       TvServiceType[service] );
757                 }
758             }
759
760             /*
761                Create a new device node 
762              */
763             deviceNode =
764                 ( struct TvDeviceNode * )
765                 malloc( sizeof( struct TvDeviceNode ) );
766             strcpy( deviceNode->device.UDN, UDN );
767             strcpy( deviceNode->device.DescDocURL, location );
768             strcpy( deviceNode->device.FriendlyName, friendlyName );
769             strcpy( deviceNode->device.PresURL, presURL );
770             deviceNode->device.AdvrTimeOut = expires;
771
772             for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
773                 strcpy( deviceNode->device.TvService[service].ServiceId,
774                         serviceId[service] );
775                 strcpy( deviceNode->device.TvService[service].ServiceType,
776                         TvServiceType[service] );
777                 strcpy( deviceNode->device.TvService[service].ControlURL,
778                         controlURL[service] );
779                 strcpy( deviceNode->device.TvService[service].EventURL,
780                         eventURL[service] );
781                 strcpy( deviceNode->device.TvService[service].SID,
782                         eventSID[service] );
783
784                 for( var = 0; var < TvVarCount[service]; var++ ) {
785                     deviceNode->device.TvService[service].
786                         VariableStrVal[var] =
787                         ( char * )malloc( TV_MAX_VAL_LEN );
788                     strcpy( deviceNode->device.TvService[service].
789                             VariableStrVal[var], "" );
790                 }
791             }
792
793             deviceNode->next = NULL;
794
795             // Insert the new device node in the list
796             if( ( tmpdevnode = GlobalDeviceList ) ) {
797
798                 while( tmpdevnode ) {
799                     if( tmpdevnode->next ) {
800                         tmpdevnode = tmpdevnode->next;
801                     } else {
802                         tmpdevnode->next = deviceNode;
803                         break;
804                     }
805                 }
806             } else {
807                 GlobalDeviceList = deviceNode;
808             }
809
810             //Notify New Device Added
811             SampleUtil_StateUpdate( NULL, NULL, deviceNode->device.UDN,
812                                     DEVICE_ADDED );
813         }
814     }
815
816     ithread_mutex_unlock( &DeviceListMutex );
817
818     if( deviceType )
819         free( deviceType );
820     if( friendlyName )
821         free( friendlyName );
822     if( UDN )
823         free( UDN );
824     if( baseURL )
825         free( baseURL );
826     if( relURL )
827         free( relURL );
828
829     for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
830         if( serviceId[service] )
831             free( serviceId[service] );
832         if( controlURL[service] )
833             free( controlURL[service] );
834         if( eventURL[service] )
835             free( eventURL[service] );
836     }
837 }
838
839 /********************************************************************************
840  * TvStateUpdate
841  *
842  * Description: 
843  *       Update a Tv state table.  Called when an event is
844  *       received.  Note: this function is NOT thread save.  It must be
845  *       called from another function that has locked the global device list.
846  *
847  * Parameters:
848  *   UDN     -- The UDN of the parent device.
849  *   Service -- The service state table to update
850  *   ChangedVariables -- DOM document representing the XML received
851  *                       with the event
852  *   State -- pointer to the state table for the Tv  service
853  *            to update
854  *
855  ********************************************************************************/
856 void
857 TvStateUpdate( char *UDN,
858                int Service,
859                IXML_Document * ChangedVariables,
860                char **State )
861 {
862     IXML_NodeList *properties,
863      *variables;
864     IXML_Element *property,
865      *variable;
866     int length,
867       length1;
868     int i,
869       j;
870     char *tmpstate = NULL;
871
872     SampleUtil_Print( "Tv State Update (service %d): ", Service );
873
874     /*
875        Find all of the e:property tags in the document 
876      */
877     properties =
878         ixmlDocument_getElementsByTagName( ChangedVariables,
879                                            "e:property" );
880     if( NULL != properties ) {
881         length = ixmlNodeList_length( properties );
882         for( i = 0; i < length; i++ ) { /* Loop through each property change found */
883             property =
884                 ( IXML_Element * ) ixmlNodeList_item( properties, i );
885
886             /*
887                For each variable name in the state table, check if this
888                is a corresponding property change 
889              */
890             for( j = 0; j < TvVarCount[Service]; j++ ) {
891                 variables =
892                     ixmlElement_getElementsByTagName( property,
893                                                       TvVarName[Service]
894                                                       [j] );
895
896                 /*
897                    If a match is found, extract the value, and update the state table 
898                  */
899                 if( variables ) {
900                     length1 = ixmlNodeList_length( variables );
901                     if( length1 ) {
902                         variable =
903                             ( IXML_Element * )
904                             ixmlNodeList_item( variables, 0 );
905                         tmpstate = SampleUtil_GetElementValue( variable );
906
907                         if( tmpstate ) {
908                             strcpy( State[j], tmpstate );
909                             SampleUtil_Print
910                                 ( " Variable Name: %s New Value:'%s'",
911                                   TvVarName[Service][j], State[j] );
912                         }
913
914                         if( tmpstate )
915                             free( tmpstate );
916                         tmpstate = NULL;
917                     }
918
919                     ixmlNodeList_free( variables );
920                     variables = NULL;
921                 }
922             }
923
924         }
925         ixmlNodeList_free( properties );
926     }
927 }
928
929 /********************************************************************************
930  * TvCtrlPointHandleEvent
931  *
932  * Description: 
933  *       Handle a UPnP event that was received.  Process the event and update
934  *       the appropriate service state table.
935  *
936  * Parameters:
937  *   sid -- The subscription id for the event
938  *   eventkey -- The eventkey number for the event
939  *   changes -- The DOM document representing the changes
940  *
941  ********************************************************************************/
942 void
943 TvCtrlPointHandleEvent( Upnp_SID sid,
944                         int evntkey,
945                         IXML_Document * changes )
946 {
947     struct TvDeviceNode *tmpdevnode;
948     int service;
949
950     ithread_mutex_lock( &DeviceListMutex );
951
952     tmpdevnode = GlobalDeviceList;
953     while( tmpdevnode ) {
954         for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
955             if( strcmp( tmpdevnode->device.TvService[service].SID, sid ) ==
956                 0 ) {
957                 SampleUtil_Print( "Received Tv %s Event: %d for SID %s",
958                                   TvServiceName[service], evntkey, sid );
959
960                 TvStateUpdate( tmpdevnode->device.UDN, service, changes,
961                                ( char ** )&tmpdevnode->device.
962                                TvService[service].VariableStrVal );
963                 break;
964             }
965         }
966         tmpdevnode = tmpdevnode->next;
967     }
968
969     ithread_mutex_unlock( &DeviceListMutex );
970 }
971
972 /********************************************************************************
973  * TvCtrlPointHandleSubscribeUpdate
974  *
975  * Description: 
976  *       Handle a UPnP subscription update that was received.  Find the 
977  *       service the update belongs to, and update its subscription
978  *       timeout.
979  *
980  * Parameters:
981  *   eventURL -- The event URL for the subscription
982  *   sid -- The subscription id for the subscription
983  *   timeout  -- The new timeout for the subscription
984  *
985  ********************************************************************************/
986 void
987 TvCtrlPointHandleSubscribeUpdate( char *eventURL,
988                                   Upnp_SID sid,
989                                   int timeout )
990 {
991     struct TvDeviceNode *tmpdevnode;
992     int service;
993
994     ithread_mutex_lock( &DeviceListMutex );
995
996     tmpdevnode = GlobalDeviceList;
997     while( tmpdevnode ) {
998         for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
999
1000             if( strcmp
1001                 ( tmpdevnode->device.TvService[service].EventURL,
1002                   eventURL ) == 0 ) {
1003                 SampleUtil_Print
1004                     ( "Received Tv %s Event Renewal for eventURL %s",
1005                       TvServiceName[service], eventURL );
1006                 strcpy( tmpdevnode->device.TvService[service].SID, sid );
1007                 break;
1008             }
1009         }
1010
1011         tmpdevnode = tmpdevnode->next;
1012     }
1013
1014     ithread_mutex_unlock( &DeviceListMutex );
1015 }
1016
1017 void
1018 TvCtrlPointHandleGetVar( char *controlURL,
1019                          char *varName,
1020                          DOMString varValue )
1021 {
1022
1023     struct TvDeviceNode *tmpdevnode;
1024     int service;
1025
1026     ithread_mutex_lock( &DeviceListMutex );
1027
1028     tmpdevnode = GlobalDeviceList;
1029     while( tmpdevnode ) {
1030         for( service = 0; service < TV_SERVICE_SERVCOUNT; service++ ) {
1031             if( strcmp
1032                 ( tmpdevnode->device.TvService[service].ControlURL,
1033                   controlURL ) == 0 ) {
1034                 SampleUtil_StateUpdate( varName, varValue,
1035                                         tmpdevnode->device.UDN,
1036                                         GET_VAR_COMPLETE );
1037                 break;
1038             }
1039         }
1040         tmpdevnode = tmpdevnode->next;
1041     }
1042
1043     ithread_mutex_unlock( &DeviceListMutex );
1044 }
1045
1046 /********************************************************************************
1047  * TvCtrlPointCallbackEventHandler
1048  *
1049  * Description: 
1050  *       The callback handler registered with the SDK while registering
1051  *       the control point.  Detects the type of callback, and passes the 
1052  *       request on to the appropriate function.
1053  *
1054  * Parameters:
1055  *   EventType -- The type of callback event
1056  *   Event -- Data structure containing event data
1057  *   Cookie -- Optional data specified during callback registration
1058  *
1059  ********************************************************************************/
1060 int
1061 TvCtrlPointCallbackEventHandler( Upnp_EventType EventType,
1062                                  void *Event,
1063                                  void *Cookie )
1064 {
1065     SampleUtil_PrintEvent( EventType, Event );
1066
1067     switch ( EventType ) {
1068             /*
1069                SSDP Stuff 
1070              */
1071         case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1072         case UPNP_DISCOVERY_SEARCH_RESULT:
1073             {
1074                 struct Upnp_Discovery *d_event =
1075                     ( struct Upnp_Discovery * )Event;
1076                 IXML_Document *DescDoc = NULL;
1077                 int ret;
1078
1079                 if( d_event->ErrCode != UPNP_E_SUCCESS ) {
1080                     SampleUtil_Print( "Error in Discovery Callback -- %d",
1081                                       d_event->ErrCode );
1082                 }
1083
1084                 if( ( ret =
1085                       UpnpDownloadXmlDoc( d_event->Location,
1086                                           &DescDoc ) ) !=
1087                     UPNP_E_SUCCESS ) {
1088                     SampleUtil_Print
1089                         ( "Error obtaining device description from %s -- error = %d",
1090                           d_event->Location, ret );
1091                 } else {
1092                     TvCtrlPointAddDevice( DescDoc, d_event->Location,
1093                                           d_event->Expires );
1094                 }
1095
1096                 if( DescDoc )
1097                     ixmlDocument_free( DescDoc );
1098
1099                 TvCtrlPointPrintList();
1100                 break;
1101             }
1102
1103         case UPNP_DISCOVERY_SEARCH_TIMEOUT:
1104             /*
1105                Nothing to do here... 
1106              */
1107             break;
1108
1109         case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
1110             {
1111                 struct Upnp_Discovery *d_event =
1112                     ( struct Upnp_Discovery * )Event;
1113
1114                 if( d_event->ErrCode != UPNP_E_SUCCESS ) {
1115                     SampleUtil_Print
1116                         ( "Error in Discovery ByeBye Callback -- %d",
1117                           d_event->ErrCode );
1118                 }
1119
1120                 SampleUtil_Print( "Received ByeBye for Device: %s",
1121                                   d_event->DeviceId );
1122                 TvCtrlPointRemoveDevice( d_event->DeviceId );
1123
1124                 SampleUtil_Print( "After byebye:" );
1125                 TvCtrlPointPrintList();
1126
1127                 break;
1128             }
1129
1130             /*
1131                SOAP Stuff 
1132              */
1133         case UPNP_CONTROL_ACTION_COMPLETE:
1134             {
1135                 struct Upnp_Action_Complete *a_event =
1136                     ( struct Upnp_Action_Complete * )Event;
1137
1138                 if( a_event->ErrCode != UPNP_E_SUCCESS ) {
1139                     SampleUtil_Print
1140                         ( "Error in  Action Complete Callback -- %d",
1141                           a_event->ErrCode );
1142                 }
1143
1144                 /*
1145                    No need for any processing here, just print out results.  Service state
1146                    table updates are handled by events. 
1147                  */
1148
1149                 break;
1150             }
1151
1152         case UPNP_CONTROL_GET_VAR_COMPLETE:
1153             {
1154                 struct Upnp_State_Var_Complete *sv_event =
1155                     ( struct Upnp_State_Var_Complete * )Event;
1156
1157                 if( sv_event->ErrCode != UPNP_E_SUCCESS ) {
1158                     SampleUtil_Print
1159                         ( "Error in Get Var Complete Callback -- %d",
1160                           sv_event->ErrCode );
1161                 } else {
1162                     TvCtrlPointHandleGetVar( sv_event->CtrlUrl,
1163                                              sv_event->StateVarName,
1164                                              sv_event->CurrentVal );
1165                 }
1166
1167                 break;
1168             }
1169
1170             /*
1171                GENA Stuff 
1172              */
1173         case UPNP_EVENT_RECEIVED:
1174             {
1175                 struct Upnp_Event *e_event = ( struct Upnp_Event * )Event;
1176
1177                 TvCtrlPointHandleEvent( e_event->Sid, e_event->EventKey,
1178                                         e_event->ChangedVariables );
1179                 break;
1180             }
1181
1182         case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1183         case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1184         case UPNP_EVENT_RENEWAL_COMPLETE:
1185             {
1186                 struct Upnp_Event_Subscribe *es_event =
1187                     ( struct Upnp_Event_Subscribe * )Event;
1188
1189                 if( es_event->ErrCode != UPNP_E_SUCCESS ) {
1190                     SampleUtil_Print
1191                         ( "Error in Event Subscribe Callback -- %d",
1192                           es_event->ErrCode );
1193                 } else {
1194                     TvCtrlPointHandleSubscribeUpdate( es_event->
1195                                                       PublisherUrl,
1196                                                       es_event->Sid,
1197                                                       es_event->TimeOut );
1198                 }
1199
1200                 break;
1201             }
1202
1203         case UPNP_EVENT_AUTORENEWAL_FAILED:
1204         case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
1205             {
1206                 int TimeOut = default_timeout;
1207                 Upnp_SID newSID;
1208                 int ret;
1209
1210                 struct Upnp_Event_Subscribe *es_event =
1211                     ( struct Upnp_Event_Subscribe * )Event;
1212
1213                 ret =
1214                     UpnpSubscribe( ctrlpt_handle, es_event->PublisherUrl,
1215                                    &TimeOut, newSID );
1216
1217                 if( ret == UPNP_E_SUCCESS ) {
1218                     SampleUtil_Print( "Subscribed to EventURL with SID=%s",
1219                                       newSID );
1220                     TvCtrlPointHandleSubscribeUpdate( es_event->
1221                                                       PublisherUrl, newSID,
1222                                                       TimeOut );
1223                 } else {
1224                     SampleUtil_Print
1225                         ( "Error Subscribing to EventURL -- %d", ret );
1226                 }
1227                 break;
1228             }
1229
1230             /*
1231                ignore these cases, since this is not a device 
1232              */
1233         case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1234         case UPNP_CONTROL_GET_VAR_REQUEST:
1235         case UPNP_CONTROL_ACTION_REQUEST:
1236             break;
1237     }
1238
1239     return 0;
1240 }
1241
1242 /********************************************************************************
1243  * TvCtrlPointVerifyTimeouts
1244  *
1245  * Description: 
1246  *       Checks the advertisement  each device
1247  *        in the global device list.  If an advertisement expires,
1248  *       the device is removed from the list.  If an advertisement is about to
1249  *       expire, a search request is sent for that device.  
1250  *
1251  * Parameters:
1252  *    incr -- The increment to subtract from the timeouts each time the
1253  *            function is called.
1254  *
1255  ********************************************************************************/
1256 void
1257 TvCtrlPointVerifyTimeouts( int incr )
1258 {
1259     struct TvDeviceNode *prevdevnode,
1260      *curdevnode;
1261     int ret;
1262
1263     ithread_mutex_lock( &DeviceListMutex );
1264
1265     prevdevnode = NULL;
1266     curdevnode = GlobalDeviceList;
1267
1268     while( curdevnode ) {
1269         curdevnode->device.AdvrTimeOut -= incr;
1270         //SampleUtil_Print("Advertisement Timeout: %d\n", curdevnode->device.AdvrTimeOut);
1271
1272         if( curdevnode->device.AdvrTimeOut <= 0 ) {
1273             /*
1274                This advertisement has expired, so we should remove the device
1275                from the list 
1276              */
1277
1278             if( GlobalDeviceList == curdevnode )
1279                 GlobalDeviceList = curdevnode->next;
1280             else
1281                 prevdevnode->next = curdevnode->next;
1282             TvCtrlPointDeleteNode( curdevnode );
1283             if( prevdevnode )
1284                 curdevnode = prevdevnode->next;
1285             else
1286                 curdevnode = GlobalDeviceList;
1287         } else {
1288
1289             if( curdevnode->device.AdvrTimeOut < 2 * incr ) {
1290                 /*
1291                    This advertisement is about to expire, so send
1292                    out a search request for this device UDN to 
1293                    try to renew 
1294                  */
1295                 ret = UpnpSearchAsync( ctrlpt_handle, incr,
1296                                        curdevnode->device.UDN, NULL );
1297                 if( ret != UPNP_E_SUCCESS )
1298                     SampleUtil_Print
1299                         ( "Error sending search request for Device UDN: %s -- err = %d",
1300                           curdevnode->device.UDN, ret );
1301             }
1302
1303             prevdevnode = curdevnode;
1304             curdevnode = curdevnode->next;
1305         }
1306
1307     }
1308     ithread_mutex_unlock( &DeviceListMutex );
1309
1310 }
1311
1312 /********************************************************************************
1313  * TvCtrlPointTimerLoop
1314  *
1315  * Description: 
1316  *       Function that runs in its own thread and monitors advertisement
1317  *       and subscription timeouts for devices in the global device list.
1318  *
1319  * Parameters:
1320  *    None
1321  *
1322  ********************************************************************************/
1323 void *
1324 TvCtrlPointTimerLoop( void *args )
1325 {
1326     int incr = 30;              // how often to verify the timeouts, in seconds
1327
1328     while( 1 ) {
1329         isleep( incr );
1330         TvCtrlPointVerifyTimeouts( incr );
1331     }
1332
1333     return NULL;
1334 }
1335
1336 /********************************************************************************
1337  * TvCtrlPointStart
1338  *
1339  * Description: 
1340  *              Call this function to initialize the UPnP library and start the TV Control
1341  *              Point.  This function creates a timer thread and provides a callback
1342  *              handler to process any UPnP events that are received.
1343  *
1344  * Parameters:
1345  *              None
1346  *
1347  * Returns:
1348  *              TV_SUCCESS if everything went well, else TV_ERROR
1349  *
1350  ********************************************************************************/
1351 int
1352 TvCtrlPointStart( print_string printFunctionPtr,
1353                   state_update updateFunctionPtr )
1354 {
1355     ithread_t timer_thread;
1356     int rc;
1357     unsigned short port = 0;
1358     char *ip_address = NULL;
1359
1360     SampleUtil_Initialize( printFunctionPtr );
1361     SampleUtil_RegisterUpdateFunction( updateFunctionPtr );
1362
1363     ithread_mutex_init( &DeviceListMutex, 0 );
1364
1365     SampleUtil_Print(
1366         "Initializing UPnP Sdk with\n"
1367         "\tipaddress = %s port = %u\n",
1368         ip_address, port );
1369
1370     rc = UpnpInit( ip_address, port );
1371     if( UPNP_E_SUCCESS != rc ) {
1372         SampleUtil_Print( "WinCEStart: UpnpInit() Error: %d", rc );
1373         UpnpFinish();
1374         return TV_ERROR;
1375     }
1376
1377     if( NULL == ip_address ) {
1378         ip_address = UpnpGetServerIpAddress();
1379     }
1380     if( 0 == port ) {
1381         port = UpnpGetServerPort();
1382     }
1383
1384     SampleUtil_Print(
1385         "UPnP Initialized\n"
1386         "\tipaddress= %s port = %u\n",
1387         ip_address, port );
1388
1389     SampleUtil_Print( "Registering Control Point" );
1390     rc = UpnpRegisterClient( TvCtrlPointCallbackEventHandler,
1391                              &ctrlpt_handle, &ctrlpt_handle );
1392     if( UPNP_E_SUCCESS != rc ) {
1393         SampleUtil_Print( "Error registering CP: %d", rc );
1394         UpnpFinish();
1395         return TV_ERROR;
1396     }
1397
1398     SampleUtil_Print( "Control Point Registered" );
1399
1400     TvCtrlPointRefresh();
1401
1402     // start a timer thread
1403     ithread_create( &timer_thread, NULL, TvCtrlPointTimerLoop, NULL );
1404
1405     return TV_SUCCESS;
1406 }
1407
1408 int
1409 TvCtrlPointStop( void )
1410 {
1411     TvCtrlPointRemoveAll();
1412     UpnpUnRegisterClient( ctrlpt_handle );
1413     UpnpFinish();
1414     SampleUtil_Finish();
1415
1416     return TV_SUCCESS;
1417 }