text: /*
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   */
  
  #ifndef USBG_FUNCTION_MS__
  #define USBG_FUNCTION_MS__
  
  #include <usbg/usbg.h>
  
  #include <malloc.h>
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  typedef struct usbg_f_ms usbg_f_ms;
  
  struct usbg_f_ms_lun_attrs {
  	int id;
  	bool cdrom;
  	bool ro;
  	bool nofua;
  	bool removable;
  	const char *file;
  };
  
  struct usbg_f_ms_attrs {
  	bool stall;
  	int nluns;
  	struct usbg_f_ms_lun_attrs **luns;
  };
  
  enum usbg_f_ms_lun_attr {
  	USBG_F_MS_LUN_ATTR_MIN = 0,
  	USBG_F_MS_LUN_CDROM = USBG_F_MS_LUN_ATTR_MIN,
  	USBG_F_MS_LUN_RO,
  	USBG_F_MS_LUN_NOFUA,
  	USBG_F_MS_LUN_REMOVABLE,
  	USBG_F_MS_LUN_FILE,
  	USBG_F_MS_LUN_ATTR_MAX
  };
  
  union usbg_f_ms_lun_attr_val {
  	bool cdrom;
  	bool ro;
  	bool nofua;
  	bool removable;
  	const char *file;
  };
  
  #define USBG_F_MS_LUN_BOOL_TO_ATTR_VAL(WHAT)		\
  	USBG_TO_UNION(usbg_f_ms_lun_attr_val, cdrom, WHAT)
  
  #define USBG_F_MS_LUN_CCHAR_PTR_TO_ATTR_VAL(WHAT)		\
  	USBG_TO_UNION(usbg_f_ms_lun_attr_val, file, WHAT)
  
  
  
  /**
   * @brief Cast from generic function to mass storage function
   * @param[in] f function to be converted to ms funciton.
   *         Should be one of types:
   *         ecm, subset, ncm, eem, rndis
   * @return Converted ms function or NULL if function hasn't suitable type
   */
  usbg_f_ms *usbg_to_ms_function(usbg_function *f);
  
  /**
   * @brief Cast form ms function to generic one
   * @param[in] mf function to be converted to generic one
   * @return Generic usbg function
   */
  usbg_function *usbg_from_ms_function(usbg_f_ms *mf);
  
  /**
   * @brief Create new LUN in choosen funcion
   * @note LUN ids should be contiguous (so id for new LUN should be
   *         current value nluns)
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun to be created
   * @param[in] lattrs LUN attributes to be set (may be NULL to use defaults)
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_create_lun(usbg_f_ms *mf, int lun_id,
  			 struct usbg_f_ms_lun_attrs *lattrs);
  
  /**
   * @brief Remove LUN from choosen funcion
   * @note LUN ids should be contiguous (so you should remove LUN with id equal
   *        to nluns - 1)
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun to be created
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_rm_lun(usbg_f_ms *mf, int lun_id);
  
  /**
   * @brief Get attributes of choosen LUN
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] lattrs Structure to be filled with data
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_get_lun_attrs(usbg_f_ms *mf, int lun_id,
  			struct usbg_f_ms_lun_attrs *lattrs);
  
  /**
   * @brief Set attributes of choosen LUN
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] lattrs Lun attributes to be set
   * @return 0 on success usbg_error if error occurred.
   * @note The id value in lattrs is ignored by this function
   */
  int usbg_f_ms_set_lun_attrs(usbg_f_ms *mf, int lun_id,
  			    const struct usbg_f_ms_lun_attrs *lattrs);
  
  /**
   * @brief Cleanup LUN attributes structure after usage
   * @param[in] attrs to be cleaned up
   */
  static inline void usbg_f_ms_cleanup_lun_attrs(struct usbg_f_ms_lun_attrs *lattrs)
  {
  	if (lattrs) {
  		free((char *)lattrs->file);
  		lattrs->file = NULL;
  	}
  }
  
  /**
   * @brief Get the value of single LUN attribute
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] lattr Code of attribute which value should be taken
   * @param[out] val Current value of this attribute
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_get_lun_attr_val(usbg_f_ms *mf, int lun_id,
  			       enum usbg_f_ms_lun_attr lattr,
  			       union usbg_f_ms_lun_attr_val *val);
  
  /**
   * @brief Set the value of single LUN attribute
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] lattr Code of attribute which value should be set
   * @param[in] val Value of attribute which should be set
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_set_lun_attr_val(usbg_f_ms *mf, int lun_id,
  			       enum usbg_f_ms_lun_attr lattr,
  			       const union usbg_f_ms_lun_attr_val val);
  
  /**
   * @brief Get the value which determines if lun is visible as a cdrom
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] cdrom Current value of stall attribute
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_get_lun_cdrom(usbg_f_ms *mf, int lun_id,
  					  bool *cdrom)
  {
  	return usbg_f_ms_get_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_CDROM,
  					  (union usbg_f_ms_lun_attr_val *)cdrom);
  }
  
  /**
   * @brief Set the value which determines if lun is visible as a cdrom
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] cdrom Value of stall attribute which should be set
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_set_lun_cdrom(usbg_f_ms *mf, int lun_id,
  					  bool cdrom)
  {
  	return usbg_f_ms_set_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_CDROM,
  					  USBG_F_MS_LUN_BOOL_TO_ATTR_VAL(cdrom));
  }
  
  /**
   * @brief Get the value which determines if lun is read only
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] ro Indicates if LUN is read only
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_get_lun_ro(usbg_f_ms *mf, int lun_id, bool *ro)
  {
  	return usbg_f_ms_get_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_RO,
  					  (union usbg_f_ms_lun_attr_val *)ro);
  }
  
  /**
   * @brief Set the value which determines if lun read only
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] ro True if LUN should be read only
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_set_lun_ro(usbg_f_ms *mf, int lun_id, bool ro)
  {
  	return usbg_f_ms_set_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_RO,
  					  USBG_F_MS_LUN_BOOL_TO_ATTR_VAL(ro));
  }
  
  /**
   * @brief Get the value which determines if lun should ignore
   *        the FUA (Force Unit Access) flag in SCSI commands
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] nofua Indicates if FUA bit is ignored
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_get_lun_nofua(usbg_f_ms *mf, int lun_id,
  					  bool *nofua)
  {
  	return usbg_f_ms_get_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_NOFUA,
  					  (union usbg_f_ms_lun_attr_val *)nofua);
  }
  
  /**
   * @brief Set the value which determines if lun should ignore
   *        the FUA (Force Unit Access) flag in SCSI commands
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] nofua True if LUN should ignore FUA bit
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_set_lun_nofua(usbg_f_ms *mf, int lun_id,
  				bool nofua)
  {
  	return usbg_f_ms_set_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_NOFUA,
  					  USBG_F_MS_LUN_BOOL_TO_ATTR_VAL(nofua));
  }
  
  /**
   * @brief Get the value which determines if lun is removable
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] removable Indicates if LUN is removable
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_get_lun_removable(usbg_f_ms *mf, int lun_id,
  				bool *removable)
  {
  	return usbg_f_ms_get_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_REMOVABLE,
  					  (union usbg_f_ms_lun_attr_val *)removable);
  }
  
  /**
   * @brief Set the value which determines if lun removable
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] removable True if LUN should be removable
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_set_lun_removable(usbg_f_ms *mf, int lun_id,
  				bool removable)
  {
  	return usbg_f_ms_set_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_REMOVABLE,
  					  USBG_F_MS_LUN_BOOL_TO_ATTR_VAL(removable));
  }
  
  /**
   * @brief Get the name of file which is used as backend storage by this LUN
   *         into newly allocated storage
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] file Newly allocated string name of file used as backend
   *         storage
   * @return 0 on success usbg_error if error occurred.
   * @note returned file should be free() by caller
   */
  static inline int usbg_f_ms_get_lun_file(usbg_f_ms *mf, int lun_id,
  			       char **file)
  {
  	return usbg_f_ms_get_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_FILE,
  					  (union usbg_f_ms_lun_attr_val *)file);
  }
  
  /**
   * @brief Get the name of file which is used as backend storage by this LUN
   *         into user buffer
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[out] buf Place where file should be stored
   * @param[in] len Size of buffer
   * @return Number of bytes placed into buf (excluding '\0') or the number of
   *         characters which would have been written to the buffer if enough
   *         space had been available. (just like snprintf() family). This may
   *         also return negative error code from usbg_error.
   */
  int usbg_f_ms_get_lun_file_s(usbg_f_ms *mf, int lun_id,
  			     char *buf, int len);
  
  /**
   * @brief Set the name of file which is used as backend storage by this LUN
   * @param[in] mf Pointer to ms function
   * @param[in] lun_id ID of lun
   * @param[in] file Name of file which should be used as backend storage
   * @return 0 on success usbg_error if error occurred.
   */
  static inline int usbg_f_ms_set_lun_file(usbg_f_ms *mf, int lun_id,
  			   const char *file)
  {
  	return usbg_f_ms_set_lun_attr_val(mf, lun_id, USBG_F_MS_LUN_FILE,
  					  USBG_F_MS_LUN_CCHAR_PTR_TO_ATTR_VAL(file));
  }
  
  /**
   * @brief Get attributes of given ms function
   * @param[in] mf Pointer to ms function
   * @param[out] attrs Structure to be filled with data
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_get_attrs(usbg_f_ms *mf, struct usbg_f_ms_attrs *attrs);
  
  /**
   * @brief Set attributes of given ms function
   * @param[in] mf Pointer to ms function
   * @param[in] attrs to be set
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_set_attrs(usbg_f_ms *mf, const struct usbg_f_ms_attrs *attrs);
  
  /**
   * @brief Cleanup attributes structure after usage
   * @param[in] attrs to be cleaned up
   */
  static inline void usbg_f_ms_cleanup_attrs(struct usbg_f_ms_attrs *attrs)
  {
  	int i;
  
  	if (!attrs)
  		return;
  
  	for (i = 0; i < attrs->nluns; ++i) {
  		if (!attrs->luns[i])
  			continue;
  
  		usbg_f_ms_cleanup_lun_attrs(attrs->luns[i]);
  		free(attrs->luns[i]);
  	}
  	free(attrs->luns);
  	attrs->luns = NULL;
  }
  
  /**
   * @brief Get the value of device side MAC address
   * @param[in] mf Pointer to ms function
   * @param[out] stall Current value of stall attribute
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_get_stall(usbg_f_ms *mf, bool *stall);
  
  /**
   * @brief Set the value of device side MAC address
   * @param[in] mf Pointer to ms function
   * @param[in] stall Value of stall attribute which should be set
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_set_stall(usbg_f_ms *mf, bool stall);
  
  /**
   * @brief Get the value of device side MAC address
   * @param[in] mf Pointer to ms function
   * @param[out] nluns Current number of LUNs
   * @return 0 on success usbg_error if error occurred.
   */
  int usbg_f_ms_get_nluns(usbg_f_ms *mf, int *nluns);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* USBG_FUNCTION_MS__ */
  