#===========================================================================
#
#                              AtCmdLib.pm
#
# LIBRARY PURPOSE
#   The purpose of this library is to provide routines for interfacing to
#   the AT commands of the Globalstar GSP-1620 Data modem.
#
#   Note that Perl uses the $ as a metacharacter to name scalar variables.
#   For this reason, the $ metacharacter needs to be escaped with the \
#   character to make it a literal character in the AT command strings.
#
# EXTERNALIZED INTERFACES:
#
# LIMITATIONS:
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#  (c) COPYRIGHT 2001 QUALCOMM, Incorporated. All Rights Reserved
#                     QUALCOMM Proprietary
#
#  Export of this technology or software is regulated by the U.S. Government.
#  Diversion contrary to U.S. law prohibited.
#
#  All ideas, data and information contained in or disclosed by
#  this document are confidential and proprietary information of
#  QUALCOMM Incorporated and all rights therein are expressly reserved.
#  By accepting this material the recipient agrees that this material
#  and the information contained therein are held in confidence and in
#  trust and will not be used, copied, reproduced in whole or in part,
#  nor its contents revealed in any manner to others without the express
#  written permission of QUALCOMM Incorporated.
#
#===========================================================================

package AtCmdLib;

use Exporter;
@ISA = qw (Exporter);

@EXPORT = qw
(
  at_response
  at_qcpls
  at_qcstatus
  at_dial
  at_sms_info
  at_sms_move
  at_sms_lock
  at_sms_print
  at_hook_control
  at_enable_sms_alert
  at_disable_sms_alert
  at_enable_service_alert
  at_disable_service_alert
  at_wait_for_alert
  at_time_of_day
  print_status
  print_sms_message_hash
  at_cmd_lib_debug
);

#===========================================================================
#               INCLUDE FILES
#===========================================================================

use strict;
use CommLib;

#===========================================================================
#               DEFINITIONS AND DECLARATIONS
#===========================================================================

use subs qw
(
  at_command
  at_response
  at_qcpls
  at_qcstatus
  at_dial
  at_hook_control
  at_sms_info
  at_sms_move
  at_sms_lock
  at_sms_print
  at_enable_sms_alert
  at_disable_sms_alert
  at_enable_service_alert
  at_disable_service_alert
  at_wait_for_alert
  at_time_of_day
  build_hash
  print_status
  print_sms_message_hash
);

# Flag to control debug information.  Set flag to 1 to view what is
# being received on the AT command port.

my $at_cmd_lib_debug = 0;

#===========================================================================
#               FUNCTION DEFINITIONS
#===========================================================================

#===========================================================================
# Function:     at_command
#
# Description:
#   Sends an AT command to the current active AT port and waits for a
#   response from the modem.  The returned response can be controlled
#   based on the response_type parameter.
#
# Parameters:
#   at_command    - The AT command string to send to the modem.
#   response_type - 0 = Return the response string (OK, ERROR, etc).
#                   1 = Return the response as a list.
#
# Returns:
#   The default is to return the response string which is the result code
#   for the AT command.  This is typically something like OK or ERROR.  If
#   the response is requested as a list, then the response consists of a
#   list of strings.  The first element in the list will be the AT command
#   echoed back (if enabled) followed by any response data, followed by
#   the result code (the last element in the list).  An undef value can be
#   returned in the case where no response is received from the modem.
#===========================================================================

sub at_command
{
  my ( $at_command, $response_type ) = @_;

  $response_type = 0 if ( !defined($response_type) );

  #------------------------------------------------------------------------
  # Send the AT command and wait for the response.
  #------------------------------------------------------------------------

  comm_send_command($at_command);
  sleep 1;

  my $response = at_response();

  #----------------------------------------------------------------------
  # For response type 0, just return the response code which should be
  # the last element in the list.
  #----------------------------------------------------------------------

  if ( defined($response) && $response_type == 0 )
  {
    $response = pop(@$response);
  }

  return $response;
}

#===========================================================================
# Function:     at_response
#
# Description:
#   Gets the response from the last AT command.  The response consists of
#   a list of strings.  The first element in the list will be the AT command
#   echoed back (if enabled) followed by any response data, followed by
#   the response string (the last element in the list).
#
# Parameters:
#   None
#
# Returns:
#   Returns a list containing the response data.
#===========================================================================

sub at_response
{
  #------------------------------------------------------------------------
  # Get the AT response
  #------------------------------------------------------------------------

  my $at_response = comm_get_response();

  #----------------------------------------------------------------------
  # If the response only contains the AT command sent, then try to read
  # again to pick up the rest of the response.  This can happen for AT
  # commands that take longer to complete (i.e. ATD).
  #----------------------------------------------------------------------

  my @lines = split /[\n\r]/, $at_response;

  if ( $#lines == 0 && $lines[0] =~ /^AT/ )
  {
    $at_response .= comm_get_response();
  }

  #------------------------------------------------------------------------
  # Generate the list, removing any blank lines.
  #------------------------------------------------------------------------

  return build_list($at_response);
}

#===========================================================================
# Function:     at_wait_for_alert
#
# Description:
#   Waits on the Comm port for an alert.  Only specific alerts are
#   recognized and any other data is ignored.
#
# Parameters:
#   None
#
# Returns:
#   Returns the alert string.
#===========================================================================

sub at_wait_for_alert
{
  my @valid_alert = ( "SELF TEST RESULT", "SMS", "RING", "SERVICE" );

  #------------------------------------------------------------------------
  # Get the AT response
  #------------------------------------------------------------------------

  my $at_alert = comm_get_response();

  # Add check for a valid alert

  return $at_alert;
}


#===========================================================================
# Function:     at_dial
#
# Description:
#   Sends an ATDT command to the modem to originate a call to the specified
#   number.
#
# Parameters:
#   dialed_number - The number to be called.
#
# Returns:
#   Returns the response string which should be:
#
#     CONNECT     - Connection is successfully established.
#     ERROR       - Invalid dial string.
#     BUSY        - The modem is currently on a call.
#     NO CARRIER  - Service is not available
#     NO DIALTONE - Data port is not active
#===========================================================================

sub at_dial
{
  my ( $dialed_number ) = @_;

  #------------------------------------------------------------------------
  # Send the ATDT command and wait for the response.
  #------------------------------------------------------------------------

  return at_command("ATDT$dialed_number");
}

#===========================================================================
# Function:     at_hook_control
#
# Description:
#   Sends a ATH command to the modem to set the hook state of the modem.
#   This is basically used to end the call in progress.
#
# Parameters:
#   hook_state    - 0   = terminate call and return modem to command mode.
#                   777 = terminate call but instruct gateway to transition
#                         into dormant mode (maintaining the PPP state).
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_hook_control
{
  my ( $hook_state ) = @_;

  #------------------------------------------------------------------------
  # Send the ATH command
  #------------------------------------------------------------------------

  return at_command("ATH$hook_state");
}

#===========================================================================
# Function:     at_qcstatus
#
# Description:
#   Sends a AT$QCSTATUS command to the modem and returns a hash containing
#   the response data.
#
# Parameters:
#   None
#
# Returns:
#   Returns a hash containing the response data or undef if there is
#   no response data (i.e., an ERROR status was returned).
#===========================================================================

sub at_qcstatus
{
  #------------------------------------------------------------------------
  # Send the QCSTATUS comamnd
  #------------------------------------------------------------------------

  my $response = at_command("AT\$QCSTATUS", 1);

  #------------------------------------------------------------------------
  # Return the hash built from the response data.
  #------------------------------------------------------------------------

  return build_hash( $response );
}

#===========================================================================
# Function:     at_qcpls
#
# Description:
#   Sends a AT$QCPLS command to the modem to request position location
#   information.  This routine returns a HASH with the following information.
#
#    Hash key     Data contents
#    --------     -----------------------------------------------
#    W            Latitude as DDD MM SS (Degrees, Minutes, and Seconds)
#    N            Longitude as DDD MM SS (Degrees, Minutes, and Seconds)
#    TIME         The time of the measurement as (DD MM YYYY HH:MM:SS)
#    ERR          The estimated accuracy of the position.
#
#   Note that the TIME field is the time of the measurement.  For the 
#   current position (call is in progress) this should be the current time.
#   For the last known position, this is the time when the measurement was
#   taken.  TIME is specified in UTC.
#
# Parameters:
#   position_type   0 = current position, 1 = last obtained position.
#
# Returns:
#   Returns a hash containing the response data or undef if there is
#   no response data (i.e., an ERROR status was returned).
#===========================================================================

sub at_qcpls
{
  my ( $position_type, $time_format ) = @_;

  #------------------------------------------------------------------------
  # Set the defaults
  #------------------------------------------------------------------------

  $time_format = 1 if ( !defined($time_format) );
  $position_type = 0 if ( !defined($position_type) );

  #------------------------------------------------------------------------
  # Send the QCSTATUS comamnd
  #------------------------------------------------------------------------

  my $response = at_command("AT\$QCPLS=$position_type,$time_format", 1);

  #------------------------------------------------------------------------
  # Return the hash built from the response data.
  #------------------------------------------------------------------------

  return build_hash( $response );
}

#===========================================================================
# Function:     at_sms_info
#
# Description:
#   Sends the AT$QCSMSI command to the modem and returns the current state
#   of the SMS database.
#
# Parameters:
#   None
#
# Returns:
#   Returns a hash containing the fields from the response data or undef if
#   the response returns an error.
#===========================================================================

sub at_sms_info
{
  return build_hash( at_command("AT\$QCSMSI", 1) );
}

#===========================================================================
# Function:     at_sms_move
#
# Description:
#   Sends the AT$QCSMSM command to the modem to traverse the list of SMS
#   messages.  The current message can be optionally deleted.  The default
#   behavior is to move to the next message in the list without deleting the
#   current message.
#
# Parameters:
#   move_to   - 0 = Move to the next position in the list
#               1 = Move to the previous position in the list
#               2 = Move to the top of the list
#               3 = Move to the end of the list
#
#   delete    - 0 = Do NOT delete the current message before moving
#               1 = DO delete the current message before moving
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_sms_move
{
  my ( $move_to, $delete ) = @_;

  $delete = 0 if ( !defined($delete) );
  $move_to = 0 if ( !defined($move_to) );

  return at_command("AT\$QCSMSM=$move_to,$delete");
}

#===========================================================================
# Function:     at_sms_lock
#
# Description:
#   Sends the AT$QCSMSL command to the modem to lock or unlock the current
#   message.  The lock is to prevent read messages from being automatically
#   deleted when space is needed for a new incoming message.  The default
#   behavior is to lock the current message.
#
# Parameters:
#   move_to   - 0 = Unlock the current message (clear the lock flag)
#               1 = lock the current message (set the lock flag)
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_sms_lock
{
  my ( $lock ) = @_;

  $lock = 1 if ( !defined($lock) );

  return at_command("AT\$QCSMSL=$lock");
}

#===========================================================================
# Function:     at_sms_print
#
# Description:
#   Sends the AT$QCSMSP command to the modem to read the contents of the
#   current SMS message.  The message includes header information like a
#   time stamp, lock flag, message type, priority, etc.
#
# Parameters:
#   time_format -  Specifies the format of the message time stamp.
#
#     0 = <YYYY>:<DOY> <HH:MM:SS>
#     1 = <DD> <MM> <YYYY> <HH:MM:SS>
#
# Returns:
#   Returns a hash containing the fields from the response data or undef if
#   the response returns an error.
#===========================================================================

sub at_sms_print
{
  my ( $time_format ) = @_;

  $time_format = 0 if ( !defined($time_format) );

  my $response = at_command("AT\$QCSMSP=$time_format", 1);

  #------------------------------------------------------------------------
  # Return the hash built from the response data.
  #------------------------------------------------------------------------

  return build_hash( $response );
}

#===========================================================================
# Function:     at_enable_sms_alert
#
# Description:
#   Enables the SMS alert.
#
# Parameters:
#   None
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_enable_sms_alert
{
  return at_command("AT\$QCSMSA=1");
}

#===========================================================================
# Function:     at_disable_sms_alert
#
# Description:
#   Disables the SMS alert.
#
# Parameters:
#   None
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_disable_sms_alert
{
  return at_command("AT\$QCSMSA=0");
}

#===========================================================================
# Function:     at_enable_service_alert
#
# Description:
#   Enables the service alert.
#
# Parameters:
#   None
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_enable_service_alert
{
  return at_command("AT\$QCSA=1");
}

#===========================================================================
# Function:     at_disable_service_alert
#
# Description:
#   Disables the service alert.
#
# Parameters:
#   None
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub at_disable_service_alert
{
  return at_command("AT\$QCSA=0");
}

#===========================================================================
# Function:     at_time_of_day
#
# Description:
#   Sends the AT$QCSMSM command to the modem to traverse the list of SMS
#   messages.  The current message can be optionally deleted.
#
# Parameters:
#   time_format -  Specifies the format of the message time stamp.
#
#     0 = <YYYY>:<DOY> <HH:MM:SS>
#     1 = <DD> <MM> <YYYY> <HH:MM:SS>
#
# Returns:
#   Returns the time of day string or undef if an error was detected.
#===========================================================================

sub at_time_of_day
{
}

#===========================================================================
# Function:     build_list
#
# Description:
#   This routine builds a list from the response strings read from the comm
#   port.  The input data is split using the expected termination characters.
#
# Parameters:
#   at_data -- Data read from the comm. port
#
# Returns:
#   Returns a list containing the response data.
#===========================================================================

sub build_list
{
  my ( $at_data ) = @_;

  #------------------------------------------------------------------------
  # Generate the list, removing any blank lines.
  #------------------------------------------------------------------------

  my $line;
  my $resp;
  my @lines = split /[\n\r]/, $at_data;

  foreach $line (@lines)
  {
    if ($line !~ /^\s*$/)
    {
      push @$resp, $line;
    }
  }

  #------------------------------------------------------------------------
  # Debug code
  #------------------------------------------------------------------------
  if ($at_cmd_lib_debug > 0)
  {
    my $i = 0;
    my $element;

    foreach $element (@$resp)
    {
      printf "%.05d - %s\n", ++$i, $element; 
    }
    print "\n";
  }

  return $resp;
}

#===========================================================================
# Function:     build_hash
#
# Description:
#   Builds a hash from an AT response where each line is the "xxx: yyy"
#   format.
#
# Parameters:
#   at_response - A list containing the response strings from the last
#                 AT command.
#
# Returns:
#   Returns a hash containing the the result data or undef if there is no
#   result data because the ERROR response was returned. 
#===========================================================================

sub build_hash
{
  my ( $at_response ) = @_;

  my $line;
  my $response;

  foreach $line (@$at_response)
  {
    if ($line =~ "([a-zA-Z _]+):[ ]*(.+)")
    {
      $response->{$1} = $2;
    }
  }

  return $response;
}

#===========================================================================
# Function:     print_hash
#
# Description:
#   Used to display the contents of a hash which is returned as a result of
#   an AT command.  The hash is displayed as the key/value pair.
#
# Parameters:
#   hash          - The hash to be displayed
#   keys_to_print - A list of the hash keys to be printed.  The hash is 
#                   displayed in the same order as the keys are defined
#                   in the list.
#
# Returns:
#   Returns the response string which should be OK or ERROR.
#===========================================================================

sub print_hash
{
  my ( $hash, $keys_to_print ) = @_;

  my $key;

  foreach $key (@$keys_to_print)
  {
    if (defined($hash->{$key}))
    {
      print "$key: $hash->{$key}\n";
    }
  }
}

sub print_status
{
  my ( $status ) = @_;

  my $keys_to_print = [ "SERVICE MODE", "PROVIDER",     "GATEWAY",
                        "RSSI",         "REGISTRATION", "ROAMING",
                        "CALL STATE",   "CALL TYPE",    "CALL DURATION"
                      ];

  print_hash($status, $keys_to_print);
}

sub print_sms_message_hash
{
  my ( $sms_hash ) = @_;

  my $keys_to_print = [ "NEW",      "LOCKED", "NUMBER", "TYPE",
                        "PRIORITY", "TIME",   "LENGTH", "MESSAGE"
                      ];

  print_hash($sms_hash, $keys_to_print);
}

1;

