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 /*!
33  * \addtogroup UpnpSamples
34  *
35  * @{
36  *
37  * \name Control Point Sample Module
38  *
39  * @{
40  *
41  * \file
42  */
43 
44 #include "tv_ctrlpt.h"
45 
46 #include "upnp.h"
47 
48 /*!
49  * Mutex for protecting the global device list in a multi-threaded,
50  * asynchronous environment. All functions should lock this mutex before
51  * reading or writing the device list.
52  */
53 ithread_mutex_t DeviceListMutex;
54 
55 UpnpClient_Handle ctrlpt_handle = -1;
56 
57 /*! Device type for tv device. */
58 const char TvDeviceType[] = "urn:schemas-upnp-org:device:tvdevice:1";
59 
60 /*! Service names.*/
61 const char *TvServiceName[] = {"Control", "Picture"};
62 
63 /*!
64    Global arrays for storing variable names and counts for
65    TvControl and TvPicture services
66  */
67 const char *TvVarName[TV_SERVICE_SERVCOUNT][TV_MAXVARS] = {
68 	{"Power", "Channel", "Volume", ""},
69 	{"Color", "Tint", "Contrast", "Brightness"}};
70 char TvVarCount[TV_SERVICE_SERVCOUNT] = {
71 	TV_CONTROL_VARCOUNT, TV_PICTURE_VARCOUNT};
72 
73 /*!
74    Timeout to request during subscriptions
75  */
76 int default_timeout = 1801;
77 
78 /*!
79    The first node in the global device list, or NULL if empty
80  */
81 struct TvDeviceNode *GlobalDeviceList = NULL;
82 
83 /********************************************************************************
84  * TvCtrlPointDeleteNode
85  *
86  * Description:
87  *       Delete a device node from the global device list.  Note that this
88  *       function is NOT thread safe, and should be called from another
89  *       function that has already locked the global device list.
90  *
91  * Parameters:
92  *   node -- The device node
93  *
94  ********************************************************************************/
TvCtrlPointDeleteNode(struct TvDeviceNode * node)95 int TvCtrlPointDeleteNode(struct TvDeviceNode *node)
96 {
97 	int rc, service, var;
98 
99 	if (NULL == node) {
100 		SampleUtil_Print(
101 			"ERROR: TvCtrlPointDeleteNode: Node is empty\n");
102 		return TV_ERROR;
103 	}
104 
105 	for (service = 0; service < TV_SERVICE_SERVCOUNT; service++) {
106 		/*
107 		   If we have a valid control SID, then unsubscribe
108 		 */
109 		if (strcmp(node->device.TvService[service].SID, "") != 0) {
110 			rc = UpnpUnSubscribe(ctrlpt_handle,
111 				node->device.TvService[service].SID);
112 			if (UPNP_E_SUCCESS == rc) {
113 				SampleUtil_Print("Unsubscribed from Tv %s "
114 						 "EventURL with SID=%s\n",
115 					TvServiceName[service],
116 					node->device.TvService[service].SID);
117 			} else {
118 				SampleUtil_Print("Error unsubscribing to Tv %s "
119 						 "EventURL -- %d\n",
120 					TvServiceName[service],
121 					rc);
122 			}
123 		}
124 
125 		for (var = 0; var < TvVarCount[service]; var++) {
126 			if (node->device.TvService[service]
127 					.VariableStrVal[var]) {
128 				free(node->device.TvService[service]
129 						.VariableStrVal[var]);
130 			}
131 		}
132 	}
133 
134 	/*Notify New Device Added */
135 	SampleUtil_StateUpdate(NULL, NULL, node->device.UDN, DEVICE_REMOVED);
136 	free(node);
137 	node = NULL;
138 
139 	return TV_SUCCESS;
140 }
141 
142 /********************************************************************************
143  * TvCtrlPointRemoveDevice
144  *
145  * Description:
146  *       Remove a device from the global device list.
147  *
148  * Parameters:
149  *   UDN -- The Unique Device Name for the device to remove
150  *
151  ********************************************************************************/
TvCtrlPointRemoveDevice(const char * UDN)152 int TvCtrlPointRemoveDevice(const char *UDN)
153 {
154 	struct TvDeviceNode *curdevnode;
155 	struct TvDeviceNode *prevdevnode;
156 
157 	ithread_mutex_lock(&DeviceListMutex);
158 
159 	curdevnode = GlobalDeviceList;
160 	if (!curdevnode) {
161 		SampleUtil_Print("WARNING: TvCtrlPointRemoveDevice: Device "
162 				 "list empty\n");
163 	} else {
164 		if (0 == strcmp(curdevnode->device.UDN, UDN)) {
165 			GlobalDeviceList = curdevnode->next;
166 			TvCtrlPointDeleteNode(curdevnode);
167 		} else {
168 			prevdevnode = curdevnode;
169 			curdevnode = curdevnode->next;
170 			while (curdevnode) {
171 				if (strcmp(curdevnode->device.UDN, UDN) == 0) {
172 					prevdevnode->next = curdevnode->next;
173 					TvCtrlPointDeleteNode(curdevnode);
174 					break;
175 				}
176 				prevdevnode = curdevnode;
177 				curdevnode = curdevnode->next;
178 			}
179 		}
180 	}
181 
182 	ithread_mutex_unlock(&DeviceListMutex);
183 
184 	return TV_SUCCESS;
185 }
186 
187 /********************************************************************************
188  * TvCtrlPointRemoveAll
189  *
190  * Description:
191  *       Remove all devices from the global device list.
192  *
193  * Parameters:
194  *   None
195  *
196  ********************************************************************************/
TvCtrlPointRemoveAll(void)197 int TvCtrlPointRemoveAll(void)
198 {
199 	struct TvDeviceNode *curdevnode, *next;
200 
201 	ithread_mutex_lock(&DeviceListMutex);
202 
203 	curdevnode = GlobalDeviceList;
204 	GlobalDeviceList = NULL;
205 
206 	while (curdevnode) {
207 		next = curdevnode->next;
208 		TvCtrlPointDeleteNode(curdevnode);
209 		curdevnode = next;
210 	}
211 
212 	ithread_mutex_unlock(&DeviceListMutex);
213 
214 	return TV_SUCCESS;
215 }
216 
217 /********************************************************************************
218  * TvCtrlPointRefresh
219  *
220  * Description:
221  *       Clear the current global device list and issue new search
222  *	 requests to build it up again from scratch.
223  *
224  * Parameters:
225  *   None
226  *
227  ********************************************************************************/
TvCtrlPointRefresh(void)228 int TvCtrlPointRefresh(void)
229 {
230 	int rc;
231 
232 	TvCtrlPointRemoveAll();
233 	/* Search for all devices of type tvdevice version 1,
234 	 * waiting for up to 5 seconds for the response */
235 	rc = UpnpSearchAsync(ctrlpt_handle, 5, TvDeviceType, NULL);
236 	if (UPNP_E_SUCCESS != rc) {
237 		SampleUtil_Print("Error sending search request%d\n", rc);
238 
239 		return TV_ERROR;
240 	}
241 
242 	return TV_SUCCESS;
243 }
244 
245 /********************************************************************************
246  * TvCtrlPointGetVar
247  *
248  * Description:
249  *       Send a GetVar request to the specified service of a device.
250  *
251  * Parameters:
252  *   service -- The service
253  *   devnum -- The number of the device (order in the list,
254  *             starting with 1)
255  *   varname -- The name of the variable to request.
256  *
257  ********************************************************************************/
TvCtrlPointGetVar(int service,int devnum,const char * varname)258 int TvCtrlPointGetVar(int service, int devnum, const 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.TvService[service].ControlURL,
270 			varname,
271 			TvCtrlPointCallbackEventHandler,
272 			NULL);
273 		if (rc != UPNP_E_SUCCESS) {
274 			SampleUtil_Print(
275 				"Error in UpnpGetServiceVarStatusAsync -- %d\n",
276 				rc);
277 			rc = TV_ERROR;
278 		}
279 	}
280 
281 	ithread_mutex_unlock(&DeviceListMutex);
282 
283 	return rc;
284 }
285 
TvCtrlPointGetPower(int devnum)286 int TvCtrlPointGetPower(int devnum)
287 {
288 	return TvCtrlPointGetVar(TV_SERVICE_CONTROL, devnum, "Power");
289 }
290 
TvCtrlPointGetChannel(int devnum)291 int TvCtrlPointGetChannel(int devnum)
292 {
293 	return TvCtrlPointGetVar(TV_SERVICE_CONTROL, devnum, "Channel");
294 }
295 
TvCtrlPointGetVolume(int devnum)296 int TvCtrlPointGetVolume(int devnum)
297 {
298 	return TvCtrlPointGetVar(TV_SERVICE_CONTROL, devnum, "Volume");
299 }
300 
TvCtrlPointGetColor(int devnum)301 int TvCtrlPointGetColor(int devnum)
302 {
303 	return TvCtrlPointGetVar(TV_SERVICE_PICTURE, devnum, "Color");
304 }
305 
TvCtrlPointGetTint(int devnum)306 int TvCtrlPointGetTint(int devnum)
307 {
308 	return TvCtrlPointGetVar(TV_SERVICE_PICTURE, devnum, "Tint");
309 }
310 
TvCtrlPointGetContrast(int devnum)311 int TvCtrlPointGetContrast(int devnum)
312 {
313 	return TvCtrlPointGetVar(TV_SERVICE_PICTURE, devnum, "Contrast");
314 }
315 
TvCtrlPointGetBrightness(int devnum)316 int TvCtrlPointGetBrightness(int devnum)
317 {
318 	return TvCtrlPointGetVar(TV_SERVICE_PICTURE, devnum, "Brightness");
319 }
320 
321 /********************************************************************************
322  * TvCtrlPointSendAction
323  *
324  * Description:
325  *       Send an Action request to the specified service of a device.
326  *
327  * Parameters:
328  *   service -- The service
329  *   devnum -- The number of the device (order in the list,
330  *             starting with 1)
331  *   actionname -- The name of the action.
332  *   param_name -- An array of parameter names
333  *   param_val -- The corresponding parameter values
334  *   param_count -- The number of parameters
335  *
336  ********************************************************************************/
TvCtrlPointSendAction(int service,int devnum,const char * actionname,const char ** param_name,char ** param_val,int param_count)337 int TvCtrlPointSendAction(int service,
338 	int devnum,
339 	const char *actionname,
340 	const char **param_name,
341 	char **param_val,
342 	int param_count)
343 {
344 	struct TvDeviceNode *devnode;
345 	IXML_Document *actionNode = NULL;
346 	int rc = TV_SUCCESS;
347 	int param;
348 
349 	ithread_mutex_lock(&DeviceListMutex);
350 
351 	rc = TvCtrlPointGetDevice(devnum, &devnode);
352 	if (TV_SUCCESS == rc) {
353 		if (0 == param_count) {
354 			actionNode = UpnpMakeAction(
355 				actionname, TvServiceType[service], 0, NULL);
356 		} else {
357 			for (param = 0; param < param_count; param++) {
358 				if (UpnpAddToAction(&actionNode,
359 					    actionname,
360 					    TvServiceType[service],
361 					    param_name[param],
362 					    param_val[param]) !=
363 					UPNP_E_SUCCESS) {
364 					SampleUtil_Print(
365 						"ERROR: TvCtrlPointSendAction: "
366 						"Trying to add action param\n");
367 					/*return -1; // TBD - BAD! leaves mutex
368 					 * locked */
369 				}
370 			}
371 		}
372 
373 		rc = UpnpSendActionAsync(ctrlpt_handle,
374 			devnode->device.TvService[service].ControlURL,
375 			TvServiceType[service],
376 			NULL,
377 			actionNode,
378 			TvCtrlPointCallbackEventHandler,
379 			NULL);
380 
381 		if (rc != UPNP_E_SUCCESS) {
382 			SampleUtil_Print(
383 				"Error in UpnpSendActionAsync -- %d\n", rc);
384 			rc = TV_ERROR;
385 		}
386 	}
387 
388 	ithread_mutex_unlock(&DeviceListMutex);
389 
390 	if (actionNode)
391 		ixmlDocument_free(actionNode);
392 
393 	return rc;
394 }
395 
396 /********************************************************************************
397  * TvCtrlPointSendActionNumericArg
398  *
399  * Description:Send an action with one argument to a device in the global device
400  *list.
401  *
402  * Parameters:
403  *   devnum -- The number of the device (order in the list, starting with 1)
404  *   service -- TV_SERVICE_CONTROL or TV_SERVICE_PICTURE
405  *   actionName -- The device action, i.e., "SetChannel"
406  *   paramName -- The name of the parameter that is being passed
407  *   paramValue -- Actual value of the parameter being passed
408  *
409  ********************************************************************************/
TvCtrlPointSendActionNumericArg(int devnum,int service,const char * actionName,const char * paramName,int paramValue)410 int TvCtrlPointSendActionNumericArg(int devnum,
411 	int service,
412 	const char *actionName,
413 	const char *paramName,
414 	int paramValue)
415 {
416 	char param_val_a[50];
417 	char *param_val = param_val_a;
418 
419 	sprintf(param_val_a, "%d", paramValue);
420 	return TvCtrlPointSendAction(
421 		service, devnum, actionName, &paramName, &param_val, 1);
422 }
423 
TvCtrlPointSendPowerOn(int devnum)424 int TvCtrlPointSendPowerOn(int devnum)
425 {
426 	return TvCtrlPointSendAction(
427 		TV_SERVICE_CONTROL, devnum, "PowerOn", NULL, NULL, 0);
428 }
429 
TvCtrlPointSendPowerOff(int devnum)430 int TvCtrlPointSendPowerOff(int devnum)
431 {
432 	return TvCtrlPointSendAction(
433 		TV_SERVICE_CONTROL, devnum, "PowerOff", NULL, NULL, 0);
434 }
435 
TvCtrlPointSendSetChannel(int devnum,int channel)436 int TvCtrlPointSendSetChannel(int devnum, int channel)
437 {
438 	return TvCtrlPointSendActionNumericArg(
439 		devnum, TV_SERVICE_CONTROL, "SetChannel", "Channel", channel);
440 }
441 
TvCtrlPointSendSetVolume(int devnum,int volume)442 int TvCtrlPointSendSetVolume(int devnum, int volume)
443 {
444 	return TvCtrlPointSendActionNumericArg(
445 		devnum, TV_SERVICE_CONTROL, "SetVolume", "Volume", volume);
446 }
447 
TvCtrlPointSendSetColor(int devnum,int color)448 int TvCtrlPointSendSetColor(int devnum, int color)
449 {
450 	return TvCtrlPointSendActionNumericArg(
451 		devnum, TV_SERVICE_PICTURE, "SetColor", "Color", color);
452 }
453 
TvCtrlPointSendSetTint(int devnum,int tint)454 int TvCtrlPointSendSetTint(int devnum, int tint)
455 {
456 	return TvCtrlPointSendActionNumericArg(
457 		devnum, TV_SERVICE_PICTURE, "SetTint", "Tint", tint);
458 }
459 
TvCtrlPointSendSetContrast(int devnum,int contrast)460 int TvCtrlPointSendSetContrast(int devnum, int contrast)
461 {
462 	return TvCtrlPointSendActionNumericArg(devnum,
463 		TV_SERVICE_PICTURE,
464 		"SetContrast",
465 		"Contrast",
466 		contrast);
467 }
468 
TvCtrlPointSendSetBrightness(int devnum,int brightness)469 int TvCtrlPointSendSetBrightness(int devnum, int brightness)
470 {
471 	return TvCtrlPointSendActionNumericArg(devnum,
472 		TV_SERVICE_PICTURE,
473 		"SetBrightness",
474 		"Brightness",
475 		brightness);
476 }
477 
478 /********************************************************************************
479  * TvCtrlPointGetDevice
480  *
481  * Description:
482  *       Given a list number, returns the pointer to the device
483  *       node at that position in the global device list.  Note
484  *       that this function is not thread safe.  It must be called
485  *       from a function that has locked the global device list.
486  *
487  * Parameters:
488  *   devnum -- The number of the device (order in the list,
489  *             starting with 1)
490  *   devnode -- The output device node pointer
491  *
492  ********************************************************************************/
TvCtrlPointGetDevice(int devnum,struct TvDeviceNode ** devnode)493 int TvCtrlPointGetDevice(int devnum, struct TvDeviceNode **devnode)
494 {
495 	int count = devnum;
496 	struct TvDeviceNode *tmpdevnode = NULL;
497 
498 	if (count)
499 		tmpdevnode = GlobalDeviceList;
500 	while (--count && tmpdevnode) {
501 		tmpdevnode = tmpdevnode->next;
502 	}
503 	if (!tmpdevnode) {
504 		SampleUtil_Print(
505 			"Error finding TvDevice number -- %d\n", devnum);
506 		return TV_ERROR;
507 	}
508 	*devnode = tmpdevnode;
509 
510 	return TV_SUCCESS;
511 }
512 
513 /********************************************************************************
514  * TvCtrlPointPrintList
515  *
516  * Description:
517  *       Print the universal device names for each device in the global device
518  *list
519  *
520  * Parameters:
521  *   None
522  *
523  ********************************************************************************/
TvCtrlPointPrintList()524 int TvCtrlPointPrintList()
525 {
526 	struct TvDeviceNode *tmpdevnode;
527 	int i = 0;
528 
529 	ithread_mutex_lock(&DeviceListMutex);
530 
531 	SampleUtil_Print("TvCtrlPointPrintList:\n");
532 	tmpdevnode = GlobalDeviceList;
533 	while (tmpdevnode) {
534 		SampleUtil_Print(" %3d -- %s\n", ++i, tmpdevnode->device.UDN);
535 		tmpdevnode = tmpdevnode->next;
536 	}
537 	SampleUtil_Print("\n");
538 	ithread_mutex_unlock(&DeviceListMutex);
539 
540 	return TV_SUCCESS;
541 }
542 
543 /********************************************************************************
544  * TvCtrlPointPrintDevice
545  *
546  * Description:
547  *       Print the identifiers and state table for a device from
548  *       the global device list.
549  *
550  * Parameters:
551  *   devnum -- The number of the device (order in the list,
552  *             starting with 1)
553  *
554  ********************************************************************************/
TvCtrlPointPrintDevice(int devnum)555 int TvCtrlPointPrintDevice(int devnum)
556 {
557 	struct TvDeviceNode *tmpdevnode;
558 	int i = 0, service, var;
559 	char spacer[15];
560 
561 	if (devnum <= 0) {
562 		SampleUtil_Print("Error in TvCtrlPointPrintDevice: "
563 				 "invalid devnum = %d\n",
564 			devnum);
565 		return TV_ERROR;
566 	}
567 
568 	ithread_mutex_lock(&DeviceListMutex);
569 
570 	SampleUtil_Print("TvCtrlPointPrintDevice:\n");
571 	tmpdevnode = GlobalDeviceList;
572 	while (tmpdevnode) {
573 		i++;
574 		if (i == devnum)
575 			break;
576 		tmpdevnode = tmpdevnode->next;
577 	}
578 	if (!tmpdevnode) {
579 		SampleUtil_Print(
580 			"Error in TvCtrlPointPrintDevice: "
581 			"invalid devnum = %d  --  actual device count = %d\n",
582 			devnum,
583 			i);
584 	} else {
585 		SampleUtil_Print("  TvDevice -- %d\n"
586 				 "    |                  \n"
587 				 "    +- UDN        = %s\n"
588 				 "    +- DescDocURL     = %s\n"
589 				 "    +- FriendlyName   = %s\n"
590 				 "    +- PresURL        = %s\n"
591 				 "    +- Adver. TimeOut = %d\n",
592 			devnum,
593 			tmpdevnode->device.UDN,
594 			tmpdevnode->device.DescDocURL,
595 			tmpdevnode->device.FriendlyName,
596 			tmpdevnode->device.PresURL,
597 			tmpdevnode->device.AdvrTimeOut);
598 		for (service = 0; service < TV_SERVICE_SERVCOUNT; service++) {
599 			if (service < TV_SERVICE_SERVCOUNT - 1)
600 				sprintf(spacer, "    |    ");
601 			else
602 				sprintf(spacer, "         ");
603 			SampleUtil_Print("    |                  \n"
604 					 "    +- Tv %s Service\n"
605 					 "%s+- ServiceId       = %s\n"
606 					 "%s+- ServiceType     = %s\n"
607 					 "%s+- EventURL        = %s\n"
608 					 "%s+- ControlURL      = %s\n"
609 					 "%s+- SID             = %s\n"
610 					 "%s+- ServiceStateTable\n",
611 				TvServiceName[service],
612 				spacer,
613 				tmpdevnode->device.TvService[service].ServiceId,
614 				spacer,
615 				tmpdevnode->device.TvService[service]
616 					.ServiceType,
617 				spacer,
618 				tmpdevnode->device.TvService[service].EventURL,
619 				spacer,
620 				tmpdevnode->device.TvService[service]
621 					.ControlURL,
622 				spacer,
623 				tmpdevnode->device.TvService[service].SID,
624 				spacer);
625 			for (var = 0; var < TvVarCount[service]; var++) {
626 				SampleUtil_Print("%s     +- %-10s = %s\n",
627 					spacer,
628 					TvVarName[service][var],
629 					tmpdevnode->device.TvService[service]
630 						.VariableStrVal[var]);
631 			}
632 		}
633 	}
634 	SampleUtil_Print("\n");
635 	ithread_mutex_unlock(&DeviceListMutex);
636 
637 	return TV_SUCCESS;
638 }
639 
640 /********************************************************************************
641  * TvCtrlPointAddDevice
642  *
643  * Description:
644  *       If the device is not already included in the global device list,
645  *       add it.  Otherwise, update its advertisement expiration timeout.
646  *
647  * Parameters:
648  *   DescDoc -- The description document for the device
649  *   location -- The location of the description document URL
650  *   expires -- The expiration time for this advertisement
651  *
652  ********************************************************************************/
TvCtrlPointAddDevice(IXML_Document * DescDoc,const char * location,int expires)653 void TvCtrlPointAddDevice(
654 	IXML_Document *DescDoc, const char *location, int expires)
655 {
656 	char *deviceType = NULL;
657 	char *friendlyName = NULL;
658 	char *presURL = NULL;
659 	char *baseURL = NULL;
660 	char *relURL = NULL;
661 	char *UDN = NULL;
662 	char *serviceId[TV_SERVICE_SERVCOUNT] = {NULL, NULL};
663 	char *eventURL[TV_SERVICE_SERVCOUNT] = {NULL, NULL};
664 	char *controlURL[TV_SERVICE_SERVCOUNT] = {NULL, NULL};
665 	Upnp_SID eventSID[TV_SERVICE_SERVCOUNT];
666 	int TimeOut[TV_SERVICE_SERVCOUNT];
667 	struct TvDeviceNode *deviceNode;
668 	struct TvDeviceNode *tmpdevnode;
669 	int ret = 1;
670 	int found = 0;
671 	int service;
672 	int var;
673 
674 	TimeOut[0] = default_timeout;
675 	TimeOut[1] = default_timeout;
676 	ithread_mutex_lock(&DeviceListMutex);
677 
678 	/* Read key elements from description document */
679 	UDN = SampleUtil_GetFirstDocumentItem(DescDoc, "UDN");
680 	deviceType = SampleUtil_GetFirstDocumentItem(DescDoc, "deviceType");
681 	friendlyName = SampleUtil_GetFirstDocumentItem(DescDoc, "friendlyName");
682 	baseURL = SampleUtil_GetFirstDocumentItem(DescDoc, "URLBase");
683 	relURL = SampleUtil_GetFirstDocumentItem(DescDoc, "presentationURL");
684 
685 	ret = UpnpResolveURL2((baseURL ? baseURL : location), relURL, &presURL);
686 
687 	if (UPNP_E_SUCCESS != ret)
688 		SampleUtil_Print("Error generating presURL from %s + %s\n",
689 			baseURL,
690 			relURL);
691 
692 	if (strcmp(deviceType, TvDeviceType) == 0) {
693 		SampleUtil_Print("Found Tv device\n");
694 
695 		/* Check if this device is already in the list */
696 		tmpdevnode = GlobalDeviceList;
697 		while (tmpdevnode) {
698 			if (strcmp(tmpdevnode->device.UDN, UDN) == 0) {
699 				found = 1;
700 				break;
701 			}
702 			tmpdevnode = tmpdevnode->next;
703 		}
704 
705 		if (found) {
706 			/* The device is already there, so just update  */
707 			/* the advertisement timeout field */
708 			tmpdevnode->device.AdvrTimeOut = expires;
709 		} else {
710 			for (service = 0; service < TV_SERVICE_SERVCOUNT;
711 				service++) {
712 				if (SampleUtil_FindAndParseService(DescDoc,
713 					    location,
714 					    TvServiceType[service],
715 					    &serviceId[service],
716 					    &eventURL[service],
717 					    &controlURL[service])) {
718 					SampleUtil_Print("Subscribing to "
719 							 "EventURL %s...\n",
720 						eventURL[service]);
721 					ret = UpnpSubscribe(ctrlpt_handle,
722 						eventURL[service],
723 						&TimeOut[service],
724 						eventSID[service]);
725 					if (ret == UPNP_E_SUCCESS) {
726 						SampleUtil_Print(
727 							"Subscribed to "
728 							"EventURL with "
729 							"SID=%s\n",
730 							eventSID[service]);
731 					} else {
732 						SampleUtil_Print(
733 							"Error Subscribing to "
734 							"EventURL -- %d\n",
735 							ret);
736 						strcpy(eventSID[service], "");
737 					}
738 				} else {
739 					SampleUtil_Print("Error: Could not "
740 							 "find Service: %s\n",
741 						TvServiceType[service]);
742 				}
743 			}
744 			/* Create a new device node */
745 			deviceNode = (struct TvDeviceNode *)malloc(
746 				sizeof(struct TvDeviceNode));
747 			strcpy(deviceNode->device.UDN, UDN);
748 			strcpy(deviceNode->device.DescDocURL, location);
749 			strcpy(deviceNode->device.FriendlyName, friendlyName);
750 			strcpy(deviceNode->device.PresURL, presURL);
751 			deviceNode->device.AdvrTimeOut = expires;
752 			for (service = 0; service < TV_SERVICE_SERVCOUNT;
753 				service++) {
754 				if (serviceId[service] == NULL) {
755 					/* not found */
756 					continue;
757 				}
758 				strcpy(deviceNode->device.TvService[service]
759 						.ServiceId,
760 					serviceId[service]);
761 				strcpy(deviceNode->device.TvService[service]
762 						.ServiceType,
763 					TvServiceType[service]);
764 				strcpy(deviceNode->device.TvService[service]
765 						.ControlURL,
766 					controlURL[service]);
767 				strcpy(deviceNode->device.TvService[service]
768 						.EventURL,
769 					eventURL[service]);
770 				strcpy(deviceNode->device.TvService[service]
771 						.SID,
772 					eventSID[service]);
773 				for (var = 0; var < TvVarCount[service];
774 					var++) {
775 					deviceNode->device.TvService[service]
776 						.VariableStrVal[var] =
777 						(char *)malloc(TV_MAX_VAL_LEN);
778 					strcpy(deviceNode->device
779 							.TvService[service]
780 							.VariableStrVal[var],
781 						"");
782 				}
783 			}
784 			deviceNode->next = NULL;
785 			/* Insert the new device node in the list */
786 			if ((tmpdevnode = GlobalDeviceList)) {
787 				while (tmpdevnode) {
788 					if (tmpdevnode->next) {
789 						tmpdevnode = tmpdevnode->next;
790 					} else {
791 						tmpdevnode->next = deviceNode;
792 						break;
793 					}
794 				}
795 			} else {
796 				GlobalDeviceList = deviceNode;
797 			}
798 			/*Notify New Device Added */
799 			SampleUtil_StateUpdate(NULL,
800 				NULL,
801 				deviceNode->device.UDN,
802 				DEVICE_ADDED);
803 		}
804 	}
805 
806 	ithread_mutex_unlock(&DeviceListMutex);
807 
808 	if (deviceType)
809 		free(deviceType);
810 	if (friendlyName)
811 		free(friendlyName);
812 	if (UDN)
813 		free(UDN);
814 	if (baseURL)
815 		free(baseURL);
816 	if (relURL)
817 		free(relURL);
818 	if (presURL)
819 		free(presURL);
820 	for (service = 0; service < TV_SERVICE_SERVCOUNT; service++) {
821 		if (serviceId[service])
822 			free(serviceId[service]);
823 		if (controlURL[service])
824 			free(controlURL[service]);
825 		if (eventURL[service])
826 			free(eventURL[service]);
827 	}
828 }
829 
TvStateUpdate(char * UDN,int Service,IXML_Document * ChangedVariables,char ** State)830 void TvStateUpdate(
831 	char *UDN, int Service, IXML_Document *ChangedVariables, char **State)
832 {
833 	IXML_NodeList *properties;
834 	IXML_NodeList *variables;
835 	IXML_Element *property;
836 	IXML_Element *variable;
837 	long unsigned int length;
838 	long unsigned int length1;
839 	long unsigned int i;
840 	int j;
841 	char *tmpstate = NULL;
842 	(void)UDN;
843 
844 	SampleUtil_Print("Tv State Update (service %d):\n", Service);
845 	/* Find all of the e:property tags in the document */
846 	properties = ixmlDocument_getElementsByTagName(
847 		ChangedVariables, "e:property");
848 	if (properties) {
849 		length = ixmlNodeList_length(properties);
850 		for (i = 0; i < length; i++) {
851 			/* Loop through each property change found */
852 			property = (IXML_Element *)ixmlNodeList_item(
853 				properties, i);
854 			/* For each variable name in the state table,
855 			 * check if this is a corresponding property change */
856 			for (j = 0; j < TvVarCount[Service]; j++) {
857 				variables = ixmlElement_getElementsByTagName(
858 					property, TvVarName[Service][j]);
859 				/* If a match is found, extract
860 				 * the value, and update the state table */
861 				if (variables) {
862 					length1 =
863 						ixmlNodeList_length(variables);
864 					if (length1) {
865 						variable = (IXML_Element *)
866 							ixmlNodeList_item(
867 								variables, 0);
868 						tmpstate =
869 							SampleUtil_GetElementValue(
870 								variable);
871 						if (tmpstate) {
872 							strcpy(State[j],
873 								tmpstate);
874 							SampleUtil_Print(
875 								" Variable "
876 								"Name: %s New "
877 								"Value:'%s'\n",
878 								TvVarName
879 									[Service]
880 									[j],
881 								State[j]);
882 						}
883 						if (tmpstate)
884 							free(tmpstate);
885 						tmpstate = NULL;
886 					}
887 					ixmlNodeList_free(variables);
888 					variables = NULL;
889 				}
890 			}
891 		}
892 		ixmlNodeList_free(properties);
893 	}
894 	return;
895 }
896 
897 /********************************************************************************
898  * TvCtrlPointHandleEvent
899  *
900  * Description:
901  *       Handle a UPnP event that was received.  Process the event and update
902  *       the appropriate service state table.
903  *
904  * Parameters:
905  *   sid -- The subscription id for the event
906  *   eventkey -- The eventkey number for the event
907  *   changes -- The DOM document representing the changes
908  *
909  ********************************************************************************/
TvCtrlPointHandleEvent(const char * sid,int evntkey,IXML_Document * changes)910 void TvCtrlPointHandleEvent(
911 	const char *sid, int evntkey, IXML_Document *changes)
912 {
913 	struct TvDeviceNode *tmpdevnode;
914 	int service;
915 
916 	ithread_mutex_lock(&DeviceListMutex);
917 
918 	tmpdevnode = GlobalDeviceList;
919 	while (tmpdevnode) {
920 		for (service = 0; service < TV_SERVICE_SERVCOUNT; ++service) {
921 			if (strcmp(tmpdevnode->device.TvService[service].SID,
922 				    sid) == 0) {
923 				SampleUtil_Print(
924 					"Received Tv %s Event: %d for SID %s\n",
925 					TvServiceName[service],
926 					evntkey,
927 					sid);
928 				TvStateUpdate(tmpdevnode->device.UDN,
929 					service,
930 					changes,
931 					(char **)&tmpdevnode->device
932 						.TvService[service]
933 						.VariableStrVal);
934 				break;
935 			}
936 		}
937 		tmpdevnode = tmpdevnode->next;
938 	}
939 
940 	ithread_mutex_unlock(&DeviceListMutex);
941 }
942 
943 /********************************************************************************
944  * TvCtrlPointHandleSubscribeUpdate
945  *
946  * Description:
947  *       Handle a UPnP subscription update that was received.  Find the
948  *       service the update belongs to, and update its subscription
949  *       timeout.
950  *
951  * Parameters:
952  *   eventURL -- The event URL for the subscription
953  *   sid -- The subscription id for the subscription
954  *   timeout  -- The new timeout for the subscription
955  *
956  ********************************************************************************/
TvCtrlPointHandleSubscribeUpdate(const char * eventURL,const Upnp_SID sid,int timeout)957 void TvCtrlPointHandleSubscribeUpdate(
958 	const char *eventURL, const Upnp_SID sid, int timeout)
959 {
960 	struct TvDeviceNode *tmpdevnode;
961 	int service;
962 	(void)timeout;
963 
964 	ithread_mutex_lock(&DeviceListMutex);
965 
966 	tmpdevnode = GlobalDeviceList;
967 	while (tmpdevnode) {
968 		for (service = 0; service < TV_SERVICE_SERVCOUNT; service++) {
969 			if (strcmp(tmpdevnode->device.TvService[service]
970 					    .EventURL,
971 				    eventURL) == 0) {
972 				SampleUtil_Print("Received Tv %s Event Renewal "
973 						 "for eventURL %s\n",
974 					TvServiceName[service],
975 					eventURL);
976 				strcpy(tmpdevnode->device.TvService[service]
977 						.SID,
978 					sid);
979 				break;
980 			}
981 		}
982 
983 		tmpdevnode = tmpdevnode->next;
984 	}
985 
986 	ithread_mutex_unlock(&DeviceListMutex);
987 
988 	return;
989 }
990 
TvCtrlPointHandleGetVar(const char * controlURL,const char * varName,const DOMString varValue)991 void TvCtrlPointHandleGetVar(
992 	const char *controlURL, const char *varName, const DOMString varValue)
993 {
994 
995 	struct TvDeviceNode *tmpdevnode;
996 	int service;
997 
998 	ithread_mutex_lock(&DeviceListMutex);
999 
1000 	tmpdevnode = GlobalDeviceList;
1001 	while (tmpdevnode) {
1002 		for (service = 0; service < TV_SERVICE_SERVCOUNT; service++) {
1003 			if (strcmp(tmpdevnode->device.TvService[service]
1004 					    .ControlURL,
1005 				    controlURL) == 0) {
1006 				SampleUtil_StateUpdate(varName,
1007 					varValue,
1008 					tmpdevnode->device.UDN,
1009 					GET_VAR_COMPLETE);
1010 				break;
1011 			}
1012 		}
1013 		tmpdevnode = tmpdevnode->next;
1014 	}
1015 
1016 	ithread_mutex_unlock(&DeviceListMutex);
1017 }
1018 
1019 /********************************************************************************
1020  * TvCtrlPointCallbackEventHandler
1021  *
1022  * Description:
1023  *       The callback handler registered with the SDK while registering
1024  *       the control point.  Detects the type of callback, and passes the
1025  *       request on to the appropriate function.
1026  *
1027  * Parameters:
1028  *   EventType -- The type of callback event
1029  *   Event -- Data structure containing event data
1030  *   Cookie -- Optional data specified during callback registration
1031  *
1032  ********************************************************************************/
TvCtrlPointCallbackEventHandler(Upnp_EventType EventType,const void * Event,void * Cookie)1033 int TvCtrlPointCallbackEventHandler(
1034 	Upnp_EventType EventType, const void *Event, void *Cookie)
1035 {
1036 	int errCode = 0;
1037 	(void)Cookie;
1038 
1039 	SampleUtil_PrintEvent(EventType, Event);
1040 	switch (EventType) {
1041 	/* SSDP Stuff */
1042 	case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1043 	case UPNP_DISCOVERY_SEARCH_RESULT: {
1044 		const UpnpDiscovery *d_event = (UpnpDiscovery *)Event;
1045 		IXML_Document *DescDoc = NULL;
1046 		const char *location = NULL;
1047 		int errCode = UpnpDiscovery_get_ErrCode(d_event);
1048 
1049 		if (errCode != UPNP_E_SUCCESS) {
1050 			SampleUtil_Print(
1051 				"Error in Discovery Callback -- %d\n", errCode);
1052 		}
1053 
1054 		location = UpnpString_get_String(
1055 			UpnpDiscovery_get_Location(d_event));
1056 		errCode = UpnpDownloadXmlDoc(location, &DescDoc);
1057 		if (errCode != UPNP_E_SUCCESS) {
1058 			SampleUtil_Print("Error obtaining device description "
1059 					 "from %s -- error = %d\n",
1060 				location,
1061 				errCode);
1062 		} else {
1063 			TvCtrlPointAddDevice(DescDoc,
1064 				location,
1065 				UpnpDiscovery_get_Expires(d_event));
1066 		}
1067 		if (DescDoc) {
1068 			ixmlDocument_free(DescDoc);
1069 		}
1070 		TvCtrlPointPrintList();
1071 		break;
1072 	}
1073 	case UPNP_DISCOVERY_SEARCH_TIMEOUT:
1074 		/* Nothing to do here... */
1075 		break;
1076 	case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
1077 		UpnpDiscovery *d_event = (UpnpDiscovery *)Event;
1078 		int errCode = UpnpDiscovery_get_ErrCode(d_event);
1079 		const char *deviceId = UpnpString_get_String(
1080 			UpnpDiscovery_get_DeviceID(d_event));
1081 
1082 		if (errCode != UPNP_E_SUCCESS) {
1083 			SampleUtil_Print(
1084 				"Error in Discovery ByeBye Callback -- %d\n",
1085 				errCode);
1086 		}
1087 		SampleUtil_Print("Received ByeBye for Device: %s\n", deviceId);
1088 		TvCtrlPointRemoveDevice(deviceId);
1089 		SampleUtil_Print("After byebye:\n");
1090 		TvCtrlPointPrintList();
1091 		break;
1092 	}
1093 	/* SOAP Stuff */
1094 	case UPNP_CONTROL_ACTION_COMPLETE: {
1095 		UpnpActionComplete *a_event = (UpnpActionComplete *)Event;
1096 		int errCode = UpnpActionComplete_get_ErrCode(a_event);
1097 		if (errCode != UPNP_E_SUCCESS) {
1098 			SampleUtil_Print(
1099 				"Error in  Action Complete Callback -- %d\n",
1100 				errCode);
1101 		}
1102 		/* No need for any processing here, just print out results.
1103 		 * Service state table updates are handled by events. */
1104 		break;
1105 	}
1106 	case UPNP_CONTROL_GET_VAR_COMPLETE: {
1107 		UpnpStateVarComplete *sv_event = (UpnpStateVarComplete *)Event;
1108 		int errCode = UpnpStateVarComplete_get_ErrCode(sv_event);
1109 		if (errCode != UPNP_E_SUCCESS) {
1110 			SampleUtil_Print(
1111 				"Error in Get Var Complete Callback -- %d\n",
1112 				errCode);
1113 		} else {
1114 			TvCtrlPointHandleGetVar(
1115 				UpnpString_get_String(
1116 					UpnpStateVarComplete_get_CtrlUrl(
1117 						sv_event)),
1118 				UpnpString_get_String(
1119 					UpnpStateVarComplete_get_StateVarName(
1120 						sv_event)),
1121 				UpnpStateVarComplete_get_CurrentVal(sv_event));
1122 		}
1123 		break;
1124 	}
1125 	/* GENA Stuff */
1126 	case UPNP_EVENT_RECEIVED: {
1127 		UpnpEvent *e_event = (UpnpEvent *)Event;
1128 		TvCtrlPointHandleEvent(UpnpEvent_get_SID_cstr(e_event),
1129 			UpnpEvent_get_EventKey(e_event),
1130 			UpnpEvent_get_ChangedVariables(e_event));
1131 		break;
1132 	}
1133 	case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1134 	case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1135 	case UPNP_EVENT_RENEWAL_COMPLETE: {
1136 		UpnpEventSubscribe *es_event = (UpnpEventSubscribe *)Event;
1137 
1138 		errCode = UpnpEventSubscribe_get_ErrCode(es_event);
1139 		if (errCode != UPNP_E_SUCCESS) {
1140 			SampleUtil_Print(
1141 				"Error in Event Subscribe Callback -- %d\n",
1142 				errCode);
1143 		} else {
1144 			TvCtrlPointHandleSubscribeUpdate(
1145 				UpnpString_get_String(
1146 					UpnpEventSubscribe_get_PublisherUrl(
1147 						es_event)),
1148 				UpnpString_get_String(
1149 					UpnpEventSubscribe_get_SID(es_event)),
1150 				UpnpEventSubscribe_get_TimeOut(es_event));
1151 		}
1152 		break;
1153 	}
1154 	case UPNP_EVENT_AUTORENEWAL_FAILED:
1155 	case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
1156 		UpnpEventSubscribe *es_event = (UpnpEventSubscribe *)Event;
1157 		int TimeOut = default_timeout;
1158 		Upnp_SID newSID;
1159 
1160 		errCode = UpnpSubscribe(ctrlpt_handle,
1161 			UpnpString_get_String(
1162 				UpnpEventSubscribe_get_PublisherUrl(es_event)),
1163 			&TimeOut,
1164 			newSID);
1165 		if (errCode == UPNP_E_SUCCESS) {
1166 			SampleUtil_Print(
1167 				"Subscribed to EventURL with SID=%s\n", newSID);
1168 			TvCtrlPointHandleSubscribeUpdate(
1169 				UpnpString_get_String(
1170 					UpnpEventSubscribe_get_PublisherUrl(
1171 						es_event)),
1172 				newSID,
1173 				TimeOut);
1174 		} else {
1175 			SampleUtil_Print(
1176 				"Error Subscribing to EventURL -- %d\n",
1177 				errCode);
1178 		}
1179 		break;
1180 	}
1181 	/* ignore these cases, since this is not a device */
1182 	case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1183 	case UPNP_CONTROL_GET_VAR_REQUEST:
1184 	case UPNP_CONTROL_ACTION_REQUEST:
1185 		break;
1186 	}
1187 
1188 	return 0;
1189 }
1190 
TvCtrlPointVerifyTimeouts(int incr)1191 void TvCtrlPointVerifyTimeouts(int incr)
1192 {
1193 	struct TvDeviceNode *prevdevnode;
1194 	struct TvDeviceNode *curdevnode;
1195 	int ret;
1196 
1197 	ithread_mutex_lock(&DeviceListMutex);
1198 
1199 	prevdevnode = NULL;
1200 	curdevnode = GlobalDeviceList;
1201 	while (curdevnode) {
1202 		curdevnode->device.AdvrTimeOut -= incr;
1203 		/*SampleUtil_Print("Advertisement Timeout: %d\n",
1204 		 * curdevnode->device.AdvrTimeOut); */
1205 		if (curdevnode->device.AdvrTimeOut <= 0) {
1206 			/* This advertisement has expired, so we should remove
1207 			 * the device from the list */
1208 			if (GlobalDeviceList == curdevnode)
1209 				GlobalDeviceList = curdevnode->next;
1210 			else
1211 				prevdevnode->next = curdevnode->next;
1212 			TvCtrlPointDeleteNode(curdevnode);
1213 			if (prevdevnode)
1214 				curdevnode = prevdevnode->next;
1215 			else
1216 				curdevnode = GlobalDeviceList;
1217 		} else {
1218 			if (curdevnode->device.AdvrTimeOut < 2 * incr) {
1219 				/* This advertisement is about to expire, so
1220 				 * send out a search request for this device
1221 				 * UDN to try to renew */
1222 				ret = UpnpSearchAsync(ctrlpt_handle,
1223 					incr,
1224 					curdevnode->device.UDN,
1225 					NULL);
1226 				if (ret != UPNP_E_SUCCESS)
1227 					SampleUtil_Print(
1228 						"Error sending search request "
1229 						"for Device UDN: %s -- err = "
1230 						"%d\n",
1231 						curdevnode->device.UDN,
1232 						ret);
1233 			}
1234 			prevdevnode = curdevnode;
1235 			curdevnode = curdevnode->next;
1236 		}
1237 	}
1238 
1239 	ithread_mutex_unlock(&DeviceListMutex);
1240 }
1241 
1242 /*!
1243  * \brief Function that runs in its own thread and monitors advertisement
1244  * and subscription timeouts for devices in the global device list.
1245  */
1246 static int TvCtrlPointTimerLoopRun = 1;
TvCtrlPointTimerLoop(void * args)1247 void *TvCtrlPointTimerLoop(void *args)
1248 {
1249 	/* how often to verify the timeouts, in seconds */
1250 	int incr = 30;
1251 	(void)args;
1252 
1253 	while (TvCtrlPointTimerLoopRun) {
1254 		isleep((unsigned int)incr);
1255 		TvCtrlPointVerifyTimeouts(incr);
1256 	}
1257 
1258 	return NULL;
1259 }
1260 
1261 /*!
1262  * \brief Call this function to initialize the UPnP library and start the TV
1263  * Control Point.  This function creates a timer thread and provides a
1264  * callback handler to process any UPnP events that are received.
1265  *
1266  * \return TV_SUCCESS if everything went well, else TV_ERROR.
1267  */
TvCtrlPointStart(char * iface,state_update updateFunctionPtr,int combo)1268 int TvCtrlPointStart(char *iface,
1269 	state_update updateFunctionPtr,
1270 	int combo)
1271 {
1272 	ithread_t timer_thread;
1273 	int rc;
1274 	unsigned short port = 0;
1275 
1276 	SampleUtil_RegisterUpdateFunction(updateFunctionPtr);
1277 
1278 	ithread_mutex_init(&DeviceListMutex, 0);
1279 
1280 	SampleUtil_Print("Initializing UPnP Sdk with\n"
1281 			 "\tinterface = %s port = %u\n",
1282 		iface ? iface : "{NULL}",
1283 		port);
1284 
1285 	rc = UpnpInit2(iface, port);
1286 	if (rc != UPNP_E_SUCCESS) {
1287 		SampleUtil_Print("WinCEStart: UpnpInit2() Error: %d\n", rc);
1288 		if (!combo) {
1289 			UpnpFinish();
1290 
1291 			return TV_ERROR;
1292 		}
1293 	}
1294 
1295 	SampleUtil_Print("UPnP Initialized\n"
1296 			 "\tipv4 address = %s port = %u\n"
1297 			 "\tipv6 address = %s port = %u\n"
1298 			 "\tipv6ulagua address = %s port = %u\n",
1299 		UpnpGetServerIpAddress(), UpnpGetServerPort(),
1300 		UpnpGetServerIp6Address(), UpnpGetServerPort6(),
1301 		UpnpGetServerUlaGuaIp6Address(), UpnpGetServerUlaGuaPort6());
1302 	SampleUtil_Print("Registering Control Point\n");
1303 	rc = UpnpRegisterClient(TvCtrlPointCallbackEventHandler,
1304 		&ctrlpt_handle,
1305 		&ctrlpt_handle);
1306 	if (rc != UPNP_E_SUCCESS) {
1307 		SampleUtil_Print("Error registering CP: %d\n", rc);
1308 		UpnpFinish();
1309 
1310 		return TV_ERROR;
1311 	}
1312 
1313 	SampleUtil_Print("Control Point Registered\n");
1314 
1315 	TvCtrlPointRefresh();
1316 
1317 	/* start a timer thread */
1318 	ithread_create(&timer_thread, NULL, TvCtrlPointTimerLoop, NULL);
1319 	ithread_detach(timer_thread);
1320 
1321 	return TV_SUCCESS;
1322 }
1323 
TvCtrlPointStop(void)1324 int TvCtrlPointStop(void)
1325 {
1326 	TvCtrlPointTimerLoopRun = 0;
1327 	TvCtrlPointRemoveAll();
1328 	UpnpUnRegisterClient(ctrlpt_handle);
1329 	UpnpFinish();
1330 	SampleUtil_Finish();
1331 
1332 	return TV_SUCCESS;
1333 }
1334 
TvCtrlPointPrintShortHelp(void)1335 void TvCtrlPointPrintShortHelp(void)
1336 {
1337 	SampleUtil_Print("Commands:\n"
1338 			 "  Help\n"
1339 			 "  HelpFull\n"
1340 			 "  ListDev\n"
1341 			 "  Refresh\n"
1342 			 "  PrintDev      <devnum>\n"
1343 			 "  PowerOn       <devnum>\n"
1344 			 "  PowerOff      <devnum>\n"
1345 			 "  SetChannel    <devnum> <channel>\n"
1346 			 "  SetVolume     <devnum> <volume>\n"
1347 			 "  SetColor      <devnum> <color>\n"
1348 			 "  SetTint       <devnum> <tint>\n"
1349 			 "  SetContrast   <devnum> <contrast>\n"
1350 			 "  SetBrightness <devnum> <brightness>\n"
1351 			 "  CtrlAction    <devnum> <action>\n"
1352 			 "  PictAction    <devnum> <action>\n"
1353 			 "  CtrlGetVar    <devnum> <varname>\n"
1354 			 "  PictGetVar    <devnum> <action>\n"
1355 			 "  Exit\n");
1356 }
1357 
TvCtrlPointPrintLongHelp(void)1358 void TvCtrlPointPrintLongHelp(void)
1359 {
1360 	SampleUtil_Print(
1361 		"\n"
1362 		"******************************\n"
1363 		"* TV Control Point Help Info *\n"
1364 		"******************************\n"
1365 		"\n"
1366 		"This sample control point application automatically searches\n"
1367 		"for and subscribes to the services of television device "
1368 		"emulator\n"
1369 		"devices, described in the tvdevicedesc.xml description "
1370 		"document.\n"
1371 		"It also registers itself as a tv device.\n"
1372 		"\n"
1373 		"Commands:\n"
1374 		"  Help\n"
1375 		"       Print this help info.\n"
1376 		"  ListDev\n"
1377 		"       Print the current list of TV Device Emulators that "
1378 		"this\n"
1379 		"         control point is aware of.  Each device is preceded "
1380 		"by a\n"
1381 		"         device number which corresponds to the devnum "
1382 		"argument of\n"
1383 		"         commands listed below.\n"
1384 		"  Refresh\n"
1385 		"       Delete all of the devices from the device list and "
1386 		"issue new\n"
1387 		"         search request to rebuild the list from scratch.\n"
1388 		"  PrintDev       <devnum>\n"
1389 		"       Print the state table for the device <devnum>.\n"
1390 		"         e.g., 'PrintDev 1' prints the state table for the "
1391 		"first\n"
1392 		"         device in the device list.\n"
1393 		"  PowerOn        <devnum>\n"
1394 		"       Sends the PowerOn action to the Control Service of\n"
1395 		"         device <devnum>.\n"
1396 		"  PowerOff       <devnum>\n"
1397 		"       Sends the PowerOff action to the Control Service of\n"
1398 		"         device <devnum>.\n"
1399 		"  SetChannel     <devnum> <channel>\n"
1400 		"       Sends the SetChannel action to the Control Service of\n"
1401 		"         device <devnum>, requesting the channel to be "
1402 		"changed\n"
1403 		"         to <channel>.\n"
1404 		"  SetVolume      <devnum> <volume>\n"
1405 		"       Sends the SetVolume action to the Control Service of\n"
1406 		"         device <devnum>, requesting the volume to be "
1407 		"changed\n"
1408 		"         to <volume>.\n"
1409 		"  SetColor       <devnum> <color>\n"
1410 		"       Sends the SetColor action to the Control Service of\n"
1411 		"         device <devnum>, requesting the color to be changed\n"
1412 		"         to <color>.\n"
1413 		"  SetTint        <devnum> <tint>\n"
1414 		"       Sends the SetTint action to the Control Service of\n"
1415 		"         device <devnum>, requesting the tint to be changed\n"
1416 		"         to <tint>.\n"
1417 		"  SetContrast    <devnum> <contrast>\n"
1418 		"       Sends the SetContrast action to the Control Service "
1419 		"of\n"
1420 		"         device <devnum>, requesting the contrast to be "
1421 		"changed\n"
1422 		"         to <contrast>.\n"
1423 		"  SetBrightness  <devnum> <brightness>\n"
1424 		"       Sends the SetBrightness action to the Control Service "
1425 		"of\n"
1426 		"         device <devnum>, requesting the brightness to be "
1427 		"changed\n"
1428 		"         to <brightness>.\n"
1429 		"  CtrlAction     <devnum> <action>\n"
1430 		"       Sends an action request specified by the string "
1431 		"<action>\n"
1432 		"         to the Control Service of device <devnum>.  This "
1433 		"command\n"
1434 		"         only works for actions that have no arguments.\n"
1435 		"         (e.g., \"CtrlAction 1 IncreaseChannel\")\n"
1436 		"  PictAction     <devnum> <action>\n"
1437 		"       Sends an action request specified by the string "
1438 		"<action>\n"
1439 		"         to the Picture Service of device <devnum>.  This "
1440 		"command\n"
1441 		"         only works for actions that have no arguments.\n"
1442 		"         (e.g., \"PictAction 1 DecreaseContrast\")\n"
1443 		"  CtrlGetVar     <devnum> <varname>\n"
1444 		"       Requests the value of a variable specified by the "
1445 		"string <varname>\n"
1446 		"         from the Control Service of device <devnum>.\n"
1447 		"         (e.g., \"CtrlGetVar 1 Volume\")\n"
1448 		"  PictGetVar     <devnum> <action>\n"
1449 		"       Requests the value of a variable specified by the "
1450 		"string <varname>\n"
1451 		"         from the Picture Service of device <devnum>.\n"
1452 		"         (e.g., \"PictGetVar 1 Tint\")\n"
1453 		"  Exit\n"
1454 		"       Exits the control point application.\n");
1455 }
1456 
1457 /*! Tags for valid commands issued at the command prompt. */
1458 enum cmdloop_tvcmds
1459 {
1460 	PRTHELP = 0,
1461 	PRTFULLHELP,
1462 	POWON,
1463 	POWOFF,
1464 	SETCHAN,
1465 	SETVOL,
1466 	SETCOL,
1467 	SETTINT,
1468 	SETCONT,
1469 	SETBRT,
1470 	CTRLACTION,
1471 	PICTACTION,
1472 	CTRLGETVAR,
1473 	PICTGETVAR,
1474 	PRTDEV,
1475 	LSTDEV,
1476 	REFRESH,
1477 	EXITCMD
1478 };
1479 
1480 /*! Data structure for parsing commands from the command line. */
1481 struct cmdloop_commands
1482 {
1483 	/* the string  */
1484 	const char *str;
1485 	/* the command */
1486 	int cmdnum;
1487 	/* the number of arguments */
1488 	int numargs;
1489 	/* the args */
1490 	const char *args;
1491 } cmdloop_commands;
1492 
1493 /*! Mappings between command text names, command tag,
1494  * and required command arguments for command line
1495  * commands */
1496 static struct cmdloop_commands cmdloop_cmdlist[] = {{"Help", PRTHELP, 1, ""},
1497 	{"HelpFull", PRTFULLHELP, 1, ""},
1498 	{"ListDev", LSTDEV, 1, ""},
1499 	{"Refresh", REFRESH, 1, ""},
1500 	{"PrintDev", PRTDEV, 2, "<devnum>"},
1501 	{"PowerOn", POWON, 2, "<devnum>"},
1502 	{"PowerOff", POWOFF, 2, "<devnum>"},
1503 	{"SetChannel", SETCHAN, 3, "<devnum> <channel (int)>"},
1504 	{"SetVolume", SETVOL, 3, "<devnum> <volume (int)>"},
1505 	{"SetColor", SETCOL, 3, "<devnum> <color (int)>"},
1506 	{"SetTint", SETTINT, 3, "<devnum> <tint (int)>"},
1507 	{"SetContrast", SETCONT, 3, "<devnum> <contrast (int)>"},
1508 	{"SetBrightness", SETBRT, 3, "<devnum> <brightness (int)>"},
1509 	{"CtrlAction", CTRLACTION, 2, "<devnum> <action (string)>"},
1510 	{"PictAction", PICTACTION, 2, "<devnum> <action (string)>"},
1511 	{"CtrlGetVar", CTRLGETVAR, 2, "<devnum> <varname (string)>"},
1512 	{"PictGetVar", PICTGETVAR, 2, "<devnum> <varname (string)>"},
1513 	{"Exit", EXITCMD, 1, ""}};
1514 
TvCtrlPointPrintCommands(void)1515 void TvCtrlPointPrintCommands(void)
1516 {
1517 	int i;
1518 	int numofcmds = (sizeof cmdloop_cmdlist) / sizeof(cmdloop_commands);
1519 
1520 	SampleUtil_Print("Valid Commands:\n");
1521 	for (i = 0; i < numofcmds; ++i) {
1522 		SampleUtil_Print("  %-14s %s\n",
1523 			cmdloop_cmdlist[i].str,
1524 			cmdloop_cmdlist[i].args);
1525 	}
1526 	SampleUtil_Print("\n");
1527 }
1528 
TvCtrlPointCommandLoop(void * args)1529 void *TvCtrlPointCommandLoop(void *args)
1530 {
1531 	char cmdline[100];
1532 	char *s;
1533 	(void)args;
1534 
1535 	while (1) {
1536 		SampleUtil_Print("\n>> ");
1537 		s = fgets(cmdline, 100, stdin);
1538 		if (!s)
1539 			break;
1540 		TvCtrlPointProcessCommand(cmdline);
1541 	}
1542 
1543 	return NULL;
1544 }
1545 
TvCtrlPointProcessCommand(char * cmdline)1546 int TvCtrlPointProcessCommand(char *cmdline)
1547 {
1548 	char cmd[100];
1549 	char strarg[100];
1550 	int arg_val_err = -99999;
1551 	int arg1 = arg_val_err;
1552 	int arg2 = arg_val_err;
1553 	int cmdnum = -1;
1554 	int numofcmds = (sizeof cmdloop_cmdlist) / sizeof(cmdloop_commands);
1555 	int cmdfound = 0;
1556 	int i;
1557 	int rc;
1558 	int invalidargs = 0;
1559 	int validargs;
1560 
1561 	validargs = sscanf(cmdline, "%s %d %d", cmd, &arg1, &arg2);
1562 	for (i = 0; i < numofcmds; ++i) {
1563 		if (strcasecmp(cmd, cmdloop_cmdlist[i].str) == 0) {
1564 			cmdnum = cmdloop_cmdlist[i].cmdnum;
1565 			cmdfound++;
1566 			if (validargs != cmdloop_cmdlist[i].numargs)
1567 				invalidargs++;
1568 			break;
1569 		}
1570 	}
1571 	if (!cmdfound) {
1572 		SampleUtil_Print("Command not found; try 'Help'\n");
1573 		return TV_SUCCESS;
1574 	}
1575 	if (invalidargs) {
1576 		SampleUtil_Print("Invalid arguments; try 'Help'\n");
1577 		return TV_SUCCESS;
1578 	}
1579 	switch (cmdnum) {
1580 	case PRTHELP:
1581 		TvCtrlPointPrintShortHelp();
1582 		break;
1583 	case PRTFULLHELP:
1584 		TvCtrlPointPrintLongHelp();
1585 		break;
1586 	case POWON:
1587 		TvCtrlPointSendPowerOn(arg1);
1588 		break;
1589 	case POWOFF:
1590 		TvCtrlPointSendPowerOff(arg1);
1591 		break;
1592 	case SETCHAN:
1593 		TvCtrlPointSendSetChannel(arg1, arg2);
1594 		break;
1595 	case SETVOL:
1596 		TvCtrlPointSendSetVolume(arg1, arg2);
1597 		break;
1598 	case SETCOL:
1599 		TvCtrlPointSendSetColor(arg1, arg2);
1600 		break;
1601 	case SETTINT:
1602 		TvCtrlPointSendSetTint(arg1, arg2);
1603 		break;
1604 	case SETCONT:
1605 		TvCtrlPointSendSetContrast(arg1, arg2);
1606 		break;
1607 	case SETBRT:
1608 		TvCtrlPointSendSetBrightness(arg1, arg2);
1609 		break;
1610 	case CTRLACTION:
1611 		/* re-parse commandline since second arg is string. */
1612 		validargs = sscanf(cmdline, "%s %d %s", cmd, &arg1, strarg);
1613 		if (validargs == 3)
1614 			TvCtrlPointSendAction(TV_SERVICE_CONTROL,
1615 				arg1,
1616 				strarg,
1617 				NULL,
1618 				NULL,
1619 				0);
1620 		else
1621 			invalidargs++;
1622 		break;
1623 	case PICTACTION:
1624 		/* re-parse commandline since second arg is string. */
1625 		validargs = sscanf(cmdline, "%s %d %s", cmd, &arg1, strarg);
1626 		if (validargs == 3)
1627 			TvCtrlPointSendAction(TV_SERVICE_PICTURE,
1628 				arg1,
1629 				strarg,
1630 				NULL,
1631 				NULL,
1632 				0);
1633 		else
1634 			invalidargs++;
1635 		break;
1636 	case CTRLGETVAR:
1637 		/* re-parse commandline since second arg is string. */
1638 		validargs = sscanf(cmdline, "%s %d %s", cmd, &arg1, strarg);
1639 		if (validargs == 3)
1640 			TvCtrlPointGetVar(TV_SERVICE_CONTROL, arg1, strarg);
1641 		else
1642 			invalidargs++;
1643 		break;
1644 	case PICTGETVAR:
1645 		/* re-parse commandline since second arg is string. */
1646 		validargs = sscanf(cmdline, "%s %d %s", cmd, &arg1, strarg);
1647 		if (validargs == 3)
1648 			TvCtrlPointGetVar(TV_SERVICE_PICTURE, arg1, strarg);
1649 		else
1650 			invalidargs++;
1651 		break;
1652 	case PRTDEV:
1653 		TvCtrlPointPrintDevice(arg1);
1654 		break;
1655 	case LSTDEV:
1656 		TvCtrlPointPrintList();
1657 		break;
1658 	case REFRESH:
1659 		TvCtrlPointRefresh();
1660 		break;
1661 	case EXITCMD:
1662 		rc = TvCtrlPointStop();
1663 		exit(rc);
1664 		break;
1665 	default:
1666 		SampleUtil_Print("Command not implemented; see 'Help'\n");
1667 		break;
1668 	}
1669 	if (invalidargs)
1670 		SampleUtil_Print("Invalid args in command; see 'Help'\n");
1671 
1672 	return TV_SUCCESS;
1673 }
1674 
1675 /*! @} Control Point Sample Module */
1676 
1677 /*! @} UpnpSamples */
1678