/**
 * @file   kpd_functions.c
 *
 * Implementation of Keypad functions.
 * These functions implement the keypad processing.
 *
 * @author   Laurent Sollier (l-sollier@ti.com)
 * @version 0.1
 */

/*
 * History:
 *
 *   Date          Author       Modification
 *  ----------------------------------------
 *  10/10/2001     L Sollier    Create
 *
 *
 * (C) Copyright 2001 by Texas Instruments Incorporated, All Rights Reserved
 */

#include "kpd/kpd_i.h"
#include "kpd/kpd_virtual_key_table_mgt.h"
#include "kpd/kpd_env.h"
#include "kpd/kpd_messages_i.h"

#include "rvf/rvf_api.h"
#include "rvm/rvm_use_id_list.h"

extern T_KPD_ENV_CTRL_BLK* kpd_env_ctrl_blk;


/* Define the max of subscribers supported by the keypad driver */
#define MAX_SUBSCRIBERS 32


#define KEY_MASK_SIZE_FOR_SN_MODE 20

/* This structure gather general informations about keypad */
typedef struct {  UINT32               reserved_subscriber_id;
                  UINT8                keypad_mode;
                  char                 sn_mode_notified_keys[KEY_MASK_SIZE_FOR_SN_MODE];
                  T_SUBSCRIBER_ID      owner_keypad_id;
               } T_KEYPAD_DRIVER_INFOS;

/* Keypad informations */
static T_KEYPAD_DRIVER_INFOS keypad_driver_infos;

/* Mutex used to protect reserved_subscriber_id variable */
static T_RVF_MUTEX mutex;


/**
 * @name Functions implementation
 *
 */
/*@{*/


/**
 * function: kpd_initialize_keypad_driver
 */
T_RV_RET kpd_initialize_keypad_driver(void)
{
   T_RV_RET ret = RV_OK;
   UINT8 i;

   /* Initialization of keypad_driver_infos */
   keypad_driver_infos.reserved_subscriber_id = 0;
   keypad_driver_infos.keypad_mode = MN_MODE;
   for (i = 0; i < KEY_MASK_SIZE_FOR_SN_MODE; i++)
      keypad_driver_infos.sn_mode_notified_keys[i] = 0;
   keypad_driver_infos.owner_keypad_id = 0;

   /* Initialize ASCII table */
   if (kpd_initialize_ascii_table() != RV_OK)
      ret = RV_INTERNAL_ERR;

   /* Check if number max of subscriber is supported by the driver */
   else if (KPD_MAX_SUBSCRIBER > MAX_SUBSCRIBERS)
      ret = RV_INTERNAL_ERR;

   /* Check validity of the vpm table */
   else if (kpd_vpm_table_is_valid() == FALSE)
      ret = RV_INTERNAL_ERR;

   /* Mutex initialization */
   else if (rvf_initialize_mutex(&mutex) != RVF_OK)
      ret =  RV_INTERNAL_ERR;

   /* Hardware initialization */
   kpd_initialize_keypad_hardware();

   if (ret ==  RV_INTERNAL_ERR)
      KPD_SEND_TRACE("Keypad driver initialization failed", RV_TRACE_LEVEL_ERROR);

   return ret;
}

/**
 * function: kpd_kill_keypad_driver
 */
T_RV_RET kpd_kill_keypad_driver(void)
{
   return rvf_delete_mutex(&mutex);
}


/**
 * function: kpd_add_subscriber
 */
T_RV_RET kpd_add_subscriber(T_SUBSCRIBER_ID* subscriber_id)
{
   UINT8 i;
   UINT8 nb_subscriber = 0;
   T_RV_RET ret = RV_OK;

   rvf_lock_mutex(&mutex);

   /* Check number of subscribers */
   for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
      if (keypad_driver_infos.reserved_subscriber_id & (1<<i))
         nb_subscriber++;

   if (nb_subscriber >= KPD_MAX_SUBSCRIBER)
   {
      KPD_SEND_TRACE("KPD: Max of subscriber reached", RV_TRACE_LEVEL_WARNING);
      ret = RV_INTERNAL_ERR;
   }
   else
   {
      for (i = 0; i < KPD_MAX_SUBSCRIBER; i++)
         if ( (keypad_driver_infos.reserved_subscriber_id & (1<<i)) == 0)
         {
            keypad_driver_infos.reserved_subscriber_id |= 1<<i;
            *subscriber_id = i;
            break;
         }
   }

   rvf_unlock_mutex(&mutex);

   return ret;
}

/**
 * function: kpd_remove_subscriber
 */
T_RV_RET kpd_remove_subscriber(T_SUBSCRIBER_ID subscriber_id)
{
   T_RV_RET ret = RV_OK;

   rvf_lock_mutex(&mutex);

   /* Check if subscriber id is correct */
   if (keypad_driver_infos.reserved_subscriber_id & (1<<subscriber_id) )
   {
      /* Unreserve the id */
      keypad_driver_infos.reserved_subscriber_id &= ~(1<<subscriber_id);
   }
   else
   {
      KPD_SEND_TRACE("KPD: Subscriber Id unknown", RV_TRACE_LEVEL_ERROR);
      ret = RV_INVALID_PARAMETER;
   }

   rvf_unlock_mutex(&mutex);

   return ret;
}

/**
 * function: kpd_subscriber_id_used
 */
BOOL kpd_subscriber_id_used(T_KPD_SUBSCRIBER subscriber, T_SUBSCRIBER_ID* subscriber_id)
{
   BOOL ret = FALSE;
   T_SUBSCRIBER_ID id;

   rvf_lock_mutex(&mutex);

   if (subscriber != 0)
   {
      id = ((T_SUBSCRIBER*) subscriber)->subscriber_id;
      *subscriber_id = id;

      /* Check if subscriber id is correct */
      if ( keypad_driver_infos.reserved_subscriber_id & (1<<id) )
         ret = TRUE;
   }

   rvf_unlock_mutex(&mutex);

   return ret;
}


/**
 * function: kpd_send_key_event_message
 */
void kpd_send_key_event_message(T_KPD_PHYSICAL_KEY_ID physical_key_pressed_id,
                                T_KPD_KEY_STATE state,
                                T_KPD_PRESS_STATE press_state,
                                T_KPD_MODE mode,
                                T_RV_RETURN return_path)
{
   T_RVF_MB_STATUS mb_status;
   T_KPD_KEY_EVENT_MSG* key_event;
   char* ascii_code;


   /* Subscriber must be notified by the pressed key */
   mb_status = rvf_get_buf (kpd_env_ctrl_blk->prim_id, sizeof(T_KPD_KEY_EVENT_MSG), (void **) &key_event);   

   if (mb_status != RVF_RED) /* Memory allocation success */
   {
      /* Fill the message */
      key_event->hdr.msg_id = KPD_KEY_EVENT_MSG;
      key_event->key_info.virtual_key_id = kpd_get_virtual_key(physical_key_pressed_id,
                                                               mode);
      key_event->key_info.state = state;
      key_event->key_info.press_state = press_state;
      kpd_get_ascii_key_value(physical_key_pressed_id,
                              mode,
                              &ascii_code);
      key_event->key_info.ascii_value_p = ascii_code;
      
      KPD_SEND_TRACE_PARAM("KPD: Virtual key Id sent: ", key_event->key_info.virtual_key_id, RV_TRACE_LEVEL_DEBUG_HIGH);

      /* Send message to the client */
      if (return_path.callback_func != 0)
      {
         return_path.callback_func((void*) key_event);
         rvf_free_buf(key_event);
      }
      else
      {
         rvf_send_msg(return_path.addr_id, key_event);
      }
   }
   else
   {
      KPD_SEND_TRACE("KPD: Memory allocation error", RV_TRACE_LEVEL_ERROR);
   }
}


/**
 * function: kpd_send_status_message
 */
void kpd_send_status_message(UINT8 operation, UINT8 status_value, T_RV_RETURN return_path)
{
   T_RVF_MB_STATUS mb_status;
   T_KPD_STATUS_MSG* msg_status;

   /* Reserve memory for message */
   mb_status = rvf_get_buf (kpd_env_ctrl_blk->prim_id, sizeof(T_KPD_STATUS_MSG), (void **) &msg_status);

   if (mb_status != RVF_RED) /* Memory allocation success */
   {
      /* Fill the message */
      msg_status->hdr.msg_id = KPD_STATUS_MSG;
      msg_status->operation = operation;
      msg_status->status_value = status_value;

      /* Send message to the client */
      if (return_path.callback_func != 0)
      {
         return_path.callback_func((void*) msg_status);
         rvf_free_buf(msg_status);

      }
      else
      {
         rvf_send_msg(return_path.addr_id, msg_status);
      }
      KPD_SEND_TRACE_PARAM("KPD: Sent status message, Id:", status_value, RV_TRACE_LEVEL_DEBUG_LOW);
   }
   else
   {
      KPD_SEND_TRACE("KPD: Memory allocation error", RV_TRACE_LEVEL_ERROR);
   }
}


/**
 * function: kpd_is_key_in_sn_mode
 */
BOOL kpd_is_key_in_sn_mode(T_KPD_PHYSICAL_KEY_ID physical_key_pressed_id)
{
   return ( (keypad_driver_infos.keypad_mode == SN_MODE)
          && (keypad_driver_infos.sn_mode_notified_keys[physical_key_pressed_id >> 3] & (1<<(physical_key_pressed_id & 0x07))) );
}


/**
 * function: kpd_set_keys_in_sn_mode
 */
void kpd_set_keys_in_sn_mode(T_KPD_VIRTUAL_KEY_TABLE* keys_owner, T_KPD_MODE mode)
{
   UINT8 i;
   INT8 position;

   for (i = 0; i < keys_owner->nb_notified_keys; i++)
   {
      /* Retrieve position in vpm table */
      kpd_retrieve_virtual_key_position(keys_owner->notified_keys[i],
                                        mode,
                                        &position);

      keypad_driver_infos.sn_mode_notified_keys[position >> 3] |= (1<<(position & 0x07));
   }
}


/**
 * function: kpd_is_owner_keypad
 */
BOOL kpd_is_owner_keypad(T_SUBSCRIBER_ID subscriber_id)
{
   return ( (keypad_driver_infos.keypad_mode == SN_MODE)
          && (keypad_driver_infos.owner_keypad_id == subscriber_id) );
}


/**
 * function: kpd_get_keypad_mode
 */
UINT8 kpd_get_keypad_mode(void)
{
   return keypad_driver_infos.keypad_mode;
}

/**
 * function: kpd_set_keypad_mode
 */
void kpd_set_keypad_mode(UINT8 mode)
{
   UINT8 i;

   keypad_driver_infos.keypad_mode = mode;

   if (mode == MN_MODE)
   {
      for (i = 0; i < KEY_MASK_SIZE_FOR_SN_MODE; i++)
         keypad_driver_infos.sn_mode_notified_keys[i] = 0;
   }
}

/**
 * function: kpd_get_owner_keypad_id
 */
T_SUBSCRIBER_ID kpd_get_owner_keypad_id(void)
{
   return keypad_driver_infos.owner_keypad_id;
}

/**
 * function: kpd_set_owner_keypad_id
 */
void kpd_set_owner_keypad_id(T_SUBSCRIBER_ID subscriber_id)
{
   keypad_driver_infos.owner_keypad_id = subscriber_id;
}


/*@}*/
