/*
 * socket.c - User level <socket> library implementation.
 * 
 * Author     Alain Greiner (2016,2017,2018,2019,2020)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <socket.h>
#include <hal_user.h>
#include <hal_shared_types.h>
#include <stdio.h>
#include <syscalls_numbers.h>
#include <shared_socket.h>

///////////////////////
int socket( int domain,
            int type,
            int protocol )
{
    if( protocol != 0 )
    {
        printf("\[ERROR] in %s : <protocol> argument must be 0\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_CREATE,
                             (reg_t)domain,
                             (reg_t)type,
                             0 );
}  // end socket()

////////////////////////////
int bind( int          fdid,
          sockaddr_t * local_addr,
          int          addr_length )
{
    if( addr_length != sizeof( sockaddr_t) )
    {
        printf("\[ERROR] in %s : illegal <addr_length> argument\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_BIND,
                             (reg_t)fdid,
                             (reg_t)local_addr,
                             0 );
}  // end bind()

///////////////////////
int listen( int   fdid,
            int   backlog )
{
    return hal_user_syscall( SYS_SOCKET,
                             SOCK_LISTEN,
                             (reg_t)fdid,
                             (reg_t)backlog,
                             0 );
}  // end listen()

///////////////////////////////
int connect( int          fdid,
             sockaddr_t * remote_addr,
             int          addr_length )
{
    if( addr_length != sizeof( sockaddr_t) )
    {
        printf("\[ERROR] in %s : illegal <addr_length> argument\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_CONNECT,
                             (reg_t)fdid,
                             (reg_t)remote_addr,
                             0 );
}  // end connect()

//////////////////////////////
int accept( int          fdid,
            sockaddr_t * remote_addr,
            int        * addr_length )
{
    if( *addr_length != sizeof( sockaddr_t) )
    {
        printf("\[ERROR] in %s : illegal <addr_length> argument\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_ACCEPT,
                             (reg_t)fdid,
                             (reg_t)remote_addr,
                             0 );
}  // end accept()

////////////////////////////
int send( int          fdid,
          void       * buffer,
          int          length,
          int          flags )
{
    if( flags != 0 )
    {
        printf("\[ERROR] in %s : the <flags> argument must be 0\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_SEND,
                             (reg_t)fdid,
                             (reg_t)buffer,
                             (reg_t)length );
}  // end send()   

////////////////////////////
int recv( int          fdid,
          void       * buffer,
          int          length,
          int          flags )
{
    if( flags != 0 )
    {
        printf("\[ERROR] in %s : the <flags> argument must be 0\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_RECV,
                             (reg_t)fdid,
                             (reg_t)buffer,
                             (reg_t)length );
}  // end recv()

//////////////////////////////
int sendto( int          fdid, 
            void       * buffer,
            int          length,
            int          flags,
            sockaddr_t * remote_addr,
            int          addr_length )
{
    if( flags != 0 )
    {
        printf("\[ERROR] in %s : the <flags> argument must be 0\n", __FUNCTION__ );
        return -1;
    }

    if( remote_addr == NULL ) 
    {
        printf("\[ERROR] in %s : <remote_addr> argument cannot be NULL\n", __FUNCTION__ );
        return -1;
    }

    if( addr_length != sizeof( sockaddr_t) )
    {
        printf("\[ERROR] in %s : illegal <addr_length> argument\n", __FUNCTION__ );
        return -1;
    }

    if( (length & 0x0000FFFF) != length )
    {
        printf("\[ERROR] in %s : illegal <length> argument\n", __FUNCTION__ );
        return -1;
    }

    if( (fdid & 0x0000FFFF) != fdid )
    {
        printf("\[ERROR] in %s : illegal <fdid> argument\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_SENDTO,
                             (reg_t)((fdid & 0xFFFF) | (length << 16)),
                             (reg_t)buffer,
                             (reg_t)remote_addr );
}  // end sendto()

////////////////////////////////
int recvfrom( int          fdid,
              void       * buffer,
              int          length,
              int          flags,
              sockaddr_t * remote_addr,
              int        * addr_length )
{
    if( flags != 0 )
    {
        printf("\[ERROR] in %s : the <flags> argument must be 0\n", __FUNCTION__ );
        return -1;
    }

    if( remote_addr == NULL ) 
    {
        printf("\[ERROR] in %s : <remote_addr> argument cannot be NULL\n", __FUNCTION__ );
        return -1;
    }

    if( *addr_length != sizeof( sockaddr_t) )
    {
        printf("\[ERROR] in %s : illegal <addr_length> argument\n", __FUNCTION__ );
        return -1;
    }

    if( (length & 0x0000FFFF) != length )
    {
        printf("\[ERROR] in %s : illegal <length> argument\n", __FUNCTION__ );
        return -1;
    }

    if( (fdid & 0x0000FFFF) != fdid )
    {
        printf("\[ERROR] in %s : illegal <fdid> argument\n", __FUNCTION__ );
        return -1;
    }

    return hal_user_syscall( SYS_SOCKET,
                             SOCK_RECVFROM,
                             (reg_t)((fdid & 0xFFFF) | (length << 16)),
                             (reg_t)buffer,
                             (reg_t)remote_addr );
}  // end recvfrom()



