text: /* Copyright 2012, Google Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   *    * Redistributions of source code must retain the above copyright
   *      notice, this list of conditions and the following disclaimer.
   *    * Redistributions in binary form must reproduce the above
   *      copyright notice, this list of conditions and the following
   *      disclaimer in the documentation and/or other materials provided
   *      with the distribution.
   *    * Neither the name of Google Inc. nor the names of its
   *      contributors may be used to endorse or promote products derived
   *      from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * platform.c: platform interface routines
   */
  
  #include <inttypes.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  
  #include "mosys/alloc.h"
  #include "mosys/callbacks.h"
  #include "mosys/globals.h"
  #include "mosys/intf_list.h"
  #include "mosys/kv_pair.h"
  #include "mosys/log.h"
  #include "mosys/platform.h"
  #include "mosys/output.h"
  
  #include "lib/string.h"
  
  #ifndef LINE_MAX
  #define LINE_MAX 64
  #endif
  
  /*
   * mosys_platform_setup  -  identify platform, setup interfaces and commands
   *
   * @p_opt:	Optional platform name given to bypass auto-detection
   * 
   * returns pointer to identified platform interface
   * returns NULL if platform not identified or other error
   */
  struct platform_intf *mosys_platform_setup(const char *p_opt)
  {
  	struct platform_intf **_intf, *intf;
  	struct platform_intf *ret = NULL;
  	int intf_found = 0;
  
  	/* attempt to probe platform name and match it to an interface */
  	for (_intf = platform_intf_list; _intf && *_intf; _intf++) {
  		intf = *_intf;
  
  		lprintf(LOG_DEBUG, "Checking platform %s\n", intf->name);
  
  		/* use common operations by default */
  		intf->op = &platform_common_op;
  
  		/* platform name specified by user */
  		if (p_opt) {
  			if (strlfind(p_opt, &intf->id_list[0], 0)) {
  				intf_found = 1;
  				break;
  			} else {
  				continue;
  			}
  		}
  
  		/* auto-detect */
  		if (intf->probe) {
  			int rc = intf->probe(intf);
  
  			if (rc < 0) {
  				lprintf(LOG_DEBUG, "Error encountered when "
  					"probing %s\n", intf->name);
  				continue;
  			} else if (rc > 0) {
  				lprintf(LOG_DEBUG, "Platform %s found (via "
  				"probing)\n", intf->name);
  				intf_found = 1;
  				break;
  			}
  		}
  	}
  
  	if (!intf_found)
  		goto mosys_platform_setup_exit;
  
  	/* call platform-specific setup if found */
  	if (intf->setup && intf->setup(intf) < 0)
  		goto mosys_platform_setup_exit;
  
  	/* prepare interface operations */
  	if (intf_op_setup(intf) < 0) {
  		if (intf->destroy)
  			intf->destroy(intf);
  		intf_op_destroy(intf);
  		goto mosys_platform_setup_exit;
  	}
  
  	/* call platform-specific post-setup if found */
  	if (intf->setup_post &&
  	    intf->setup_post(intf) < 0) {
  		if (intf->destroy)
  			intf->destroy(intf);
  		goto mosys_platform_setup_exit;
  	}
  
  	ret = intf;
  
  mosys_platform_setup_exit:
  	return ret;
  }
  
  /*
   * mosys_platform_destroy  -  clean up platform interface
   *
   * @intf:	platform interface
   */
  void mosys_platform_destroy(struct platform_intf *intf)
  {
  	/* Call the destroy callbacks and cleanup afterwards. */
  //	invoke_destroy_callbacks();
  //	cleanup_destroy_callbacks();
  
  	if (intf) {
  		/* cleanup interface */
  		if (intf->destroy)
  			intf->destroy(intf);
  
  		/* cleanup interface operations */
  		intf_op_destroy(intf);
  
  	}
  }
  
  /*
   * platform_cmd_usage  -  print usage text for command
   *
   * @cmd:	command pointer
   */
  void platform_cmd_usage(struct platform_cmd *cmd)
  {
  	printf("usage: %s %s\n\n", cmd->name, cmd->usage ? : "");
  }
  
  /*
   * tree_sub -	subcommand handler for print_tree
   *
   * @intf:	platform interface
   * @cmd:	command to iterate thru
   * @depth:	depth of recursion (number of leading tabs to prepend)
   * @str:	string with full command name
   *
   * returns 0 to indicate success
   * returns <0 to indicate failure (or end of command hierarchy)
   */
  static int tree_subcommand(struct platform_intf *intf,
  			   struct platform_cmd *cmd,
  			   int depth, char *str)
  {
  	struct platform_cmd *sub;
  	char *tabs;
  	int index;
  
  	tabs = mosys_malloc(depth + 1);
  	memset(tabs, '\t', depth);
  	tabs[depth] = '\0';
  
  	for (sub = cmd->arg.sub, index = 1; sub->name != NULL; sub++, index++) {
  		printf("%s", tabs);
  
  		if (sub->type == ARG_TYPE_SUB) {
  			int pos = 0, len = 0;
  
  			if (mosys_get_verbosity() >= LOG_NOTICE) {
  				printf("[branch] %s ", str);
  
  				/* add full command info */
  				pos = strlen(str);
  				len = LINE_MAX - strlen(str);
  				snprintf(str + pos, len, " %s", sub->name);
  			}
  
  			printf("%s\n", sub->name);
  
  			/* continue descending into the hierarchy */
  			tree_subcommand(intf, sub, depth + 1, str);
  
  			if (mosys_get_verbosity() >= LOG_NOTICE) {
  				memset(str + pos, 0, len);
  			}
  		} else if (sub->type == ARG_TYPE_GETTER) {
  			if (mosys_get_verbosity() >= LOG_NOTICE)
  				printf("[leaf] %s ", str);
  			printf("%s\n", sub->name);
  		} else if (sub->type == ARG_TYPE_SETTER) {
  			if (mosys_get_verbosity() >= LOG_NOTICE)
  				printf("[flur] %s ", str);
  			printf("%s\n", sub->name);
  		} else {
  			// not a subcommand or function?!?
  			return -1;
  		}
  
  	}
  
  	free(tabs);
  	return 0;
  }
  
  /*
   * print_tree - print command tree for this platform
   *
   * @intf:	platform interface
   */
  void print_tree(struct platform_intf *intf)
  {
  	int root;
  	char str[LINE_MAX];
  
  	for (root = 0; intf->sub[root] != NULL; root++) {
  		if (mosys_get_verbosity() >= LOG_NOTICE) {
  			printf("[root] ");
  			snprintf(str, sizeof(str), "mosys %s",
  						   intf->sub[root]->name);
  		} else {
  			snprintf(str, sizeof(str), "%s", intf->sub[root]->name);
  		}
  		printf("%s\n", str);
  
  		if (tree_subcommand(intf, intf->sub[root], 1, str) < 0)
  			lprintf(LOG_DEBUG, "tree walking failed", str);
  	}
  }
  
  /*
   * print_platforms - print platform ids
   *
   * returns 0 to indicate success
   * returns <0 to indicate failure
   */
  int print_platforms() {
  	struct platform_intf **_intf, *intf;
  	const char **id;
  	int rc;
  
  	/* go through all supported interfaces */
  	for (_intf = platform_intf_list; _intf && *_intf; _intf++) {
  		intf = *_intf;
  
  		if (intf->type == PLATFORM_DEFAULT)
  			continue;
  
  		for (id = intf->id_list; id && *id; id++) {
  			struct kv_pair *kv;
  
  			kv = kv_pair_new();
  			kv_pair_add(kv, "id", *id);
  			rc = kv_pair_print(kv);
  			kv_pair_free(kv);
  			if (rc)
  				return rc;
  		}
  	}
  
  	return rc;
  }
  